Files
swift-mirror/lib/IRGen/GenPack.cpp
2025-10-22 13:00:38 -07:00

1469 lines
56 KiB
C++

//===--- GenPack.cpp - Swift IR Generation For Variadic Generics ----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements IR generation for type and value packs in Swift.
//
//===----------------------------------------------------------------------===//
#include "GenPack.h"
#include "GenProto.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/PackConformance.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/IRGen/GenericRequirement.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILType.h"
#include "llvm/IR/DerivedTypes.h"
#include "GenTuple.h"
#include "GenType.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "MetadataRequest.h"
#include "ResilientTypeInfo.h"
using namespace swift;
using namespace irgen;
static void cleanupTypeMetadataPackImpl(IRGenFunction &IGF, StackAddress pack,
llvm::Value *shape);
static void cleanupWitnessTablePackImpl(IRGenFunction &IGF, StackAddress pack,
llvm::Value *shape);
static CanPackArchetypeType
getForwardedPackArchetypeType(CanPackType packType) {
if (auto expansion = packType.unwrapSingletonPackExpansion())
return dyn_cast<PackArchetypeType>(expansion.getPatternType());
return CanPackArchetypeType();
}
static MetadataResponse
tryGetLocalPackTypeMetadata(IRGenFunction &IGF, CanPackType packType,
DynamicMetadataRequest request) {
if (auto result = IGF.tryGetLocalTypeMetadata(packType, request))
return result;
if (auto packArchetypeType = getForwardedPackArchetypeType(packType)) {
if (auto result = IGF.tryGetLocalTypeMetadata(packArchetypeType, request))
return result;
}
return MetadataResponse();
}
static llvm::Value *tryGetLocalPackTypeData(IRGenFunction &IGF,
CanPackType packType,
LocalTypeDataKind localDataKind) {
if (auto *wtable = IGF.tryGetLocalTypeData(packType, localDataKind))
return wtable;
if (auto packArchetypeType = getForwardedPackArchetypeType(packType)) {
// Also unwrap the pack conformance, if there is one.
if (localDataKind.isPackProtocolConformance()) {
localDataKind = LocalTypeDataKind::forProtocolWitnessTable(
localDataKind.getPackProtocolConformance()
->getPatternConformances()[0]);
}
if (auto *wtable =
IGF.tryGetLocalTypeData(packArchetypeType, localDataKind))
return wtable;
}
return nullptr;
}
static void accumulateSum(IRGenFunction &IGF, llvm::Value *&result,
llvm::Value *value) {
if (result == nullptr) {
result = value;
return;
}
result = IGF.Builder.CreateAdd(result, value);
}
llvm::Value *
irgen::emitIndexOfStructuralPackComponent(IRGenFunction &IGF,
CanPackType packType,
unsigned structuralIndex) {
assert(structuralIndex < packType->getNumElements());
unsigned numFixedComponents = 0;
llvm::Value *length = nullptr;
for (unsigned i = 0; i < structuralIndex; ++i) {
auto componentType = packType.getElementType(i);
if (auto expansion = dyn_cast<PackExpansionType>(componentType)) {
auto countType = expansion.getCountType();
auto expansionLength = IGF.emitPackShapeExpression(countType);
accumulateSum(IGF, length, expansionLength);
} else {
numFixedComponents++;
}
}
if (numFixedComponents > 0 || !length) {
auto fixedLength =
llvm::ConstantInt::get(IGF.IGM.SizeTy, numFixedComponents);
accumulateSum(IGF, length, fixedLength);
}
assert(length);
return length;
}
using PackExplosionCallback = void (CanType eltTy,
unsigned scalarIndex,
llvm::Value *dynamicIndex,
llvm::Value *dynamicLength);
static std::pair<unsigned, llvm::Value *>
visitPackExplosion(IRGenFunction &IGF, CanPackType type,
llvm::function_ref<PackExplosionCallback> callback) {
llvm::Value *result = nullptr;
// If shape(T) == t and shape(U) == u, the shape expression for a pack
// {T..., Int, T..., U..., String} becomes 't + t + u + 2'.
unsigned scalarElements = 0;
for (auto elt : type.getElementTypes()) {
if (auto expansionType = dyn_cast<PackExpansionType>(elt)) {
auto reducedShape = expansionType.getCountType();
auto *eltCount = IGF.emitPackShapeExpression(reducedShape);
callback(elt, scalarElements, result, eltCount);
accumulateSum(IGF, result, eltCount);
continue;
}
callback(elt, scalarElements, result, nullptr);
++scalarElements;
}
return std::make_pair(scalarElements, result);
}
llvm::Value *IRGenFunction::emitPackShapeExpression(CanType type) {
type = type->getReducedShape()->getCanonicalType();
auto kind = LocalTypeDataKind::forPackShapeExpression();
llvm::Value *result = tryGetLocalTypeData(type, kind);
if (result != nullptr)
return result;
auto pair = visitPackExplosion(
*this, cast<PackType>(type),
[&](CanType, unsigned, llvm::Value *, llvm::Value *) {});
if (pair.first > 0) {
auto *constant = llvm::ConstantInt::get(IGM.SizeTy, pair.first);
accumulateSum(*this, pair.second, constant);
} else if (pair.second == nullptr) {
pair.second = llvm::ConstantInt::get(IGM.SizeTy, 0);
}
setScopedLocalTypeData(type, kind, pair.second);
return pair.second;
}
MetadataResponse
irgen::emitPackArchetypeMetadataRef(IRGenFunction &IGF,
CanPackArchetypeType type,
DynamicMetadataRequest request) {
if (auto result = IGF.tryGetLocalTypeMetadata(type, request))
return result;
auto packType = CanPackType::getSingletonPackExpansion(type);
auto response = emitTypeMetadataPackRef(IGF, packType, request);
IGF.setScopedLocalTypeMetadata(type, response);
return response;
}
static Address emitFixedSizeMetadataPackRef(IRGenFunction &IGF,
CanPackType packType,
DynamicMetadataRequest request) {
assert(!packType->containsPackExpansionType());
unsigned elementCount = packType->getNumElements();
auto allocType = llvm::ArrayType::get(
IGF.IGM.TypeMetadataPtrTy, elementCount);
auto pack = IGF.createAlloca(allocType, IGF.IGM.getPointerAlignment());
IGF.Builder.CreateLifetimeStart(pack,
IGF.IGM.getPointerSize() * elementCount);
for (unsigned i : indices(packType->getElementTypes())) {
Address slot = IGF.Builder.CreateStructGEP(
pack, i, IGF.IGM.getPointerSize());
auto metadata = IGF.emitTypeMetadataRef(
packType.getElementType(i), request).getMetadata();
IGF.Builder.CreateStore(metadata, slot);
}
return pack;
}
llvm::Value *irgen::maskMetadataPackPointer(IRGenFunction &IGF,
llvm::Value *patternPack) {
// If the pack is on the heap, the LSB is set, so mask it off.
patternPack =
IGF.Builder.CreatePtrToInt(patternPack, IGF.IGM.SizeTy);
patternPack =
IGF.Builder.CreateAnd(patternPack, llvm::ConstantInt::get(IGF.IGM.SizeTy, -2));
patternPack =
IGF.Builder.CreateIntToPtr(patternPack, IGF.IGM.TypeMetadataPtrPtrTy);
return patternPack;
}
/// Use this to index into packs to correctly handle on-heap packs.
static llvm::Value *loadMetadataAtIndex(IRGenFunction &IGF,
llvm::Value *patternPack,
llvm::Value *index) {
patternPack = maskMetadataPackPointer(IGF, patternPack);
Address patternPackAddress(patternPack, IGF.IGM.TypeMetadataPtrTy,
IGF.IGM.getPointerAlignment());
// Load the metadata pack element from the current source index.
Address fromPtr(
IGF.Builder.CreateInBoundsGEP(patternPackAddress.getElementType(),
patternPackAddress.getAddress(), index),
patternPackAddress.getElementType(), patternPackAddress.getAlignment());
return IGF.Builder.CreateLoad(fromPtr);
}
static llvm::Value *bindMetadataAtIndex(IRGenFunction &IGF,
CanType elementArchetype,
llvm::Value *patternPack,
llvm::Value *index,
DynamicMetadataRequest request) {
if (auto response = IGF.tryGetLocalTypeMetadata(elementArchetype, request))
return response.getMetadata();
llvm::Value *metadata = loadMetadataAtIndex(IGF, patternPack, index);
// Bind the metadata pack element to the element archetype.
IGF.setScopedLocalTypeMetadata(elementArchetype,
MetadataResponse::forComplete(metadata));
return metadata;
}
/// Use this to index into packs to correctly handle on-heap packs.
static llvm::Value *loadWitnessTableAtIndex(IRGenFunction &IGF,
llvm::Value *wtablePack,
llvm::Value *index) {
// If the pack is on the heap, the LSB is set, so mask it off.
wtablePack =
IGF.Builder.CreatePtrToInt(wtablePack, IGF.IGM.SizeTy);
wtablePack =
IGF.Builder.CreateAnd(wtablePack, llvm::ConstantInt::get(IGF.IGM.SizeTy, -2));
wtablePack =
IGF.Builder.CreateIntToPtr(wtablePack, IGF.IGM.WitnessTablePtrPtrTy);
Address patternPackAddress(wtablePack, IGF.IGM.WitnessTablePtrTy,
IGF.IGM.getPointerAlignment());
// Load the witness table pack element from the current source index.
Address fromPtr(
IGF.Builder.CreateInBoundsGEP(patternPackAddress.getElementType(),
patternPackAddress.getAddress(), index),
patternPackAddress.getElementType(), patternPackAddress.getAlignment());
return IGF.Builder.CreateLoad(fromPtr);
}
static llvm::Value *bindWitnessTableAtIndex(IRGenFunction &IGF,
CanType elementArchetype,
ProtocolConformanceRef conf,
llvm::Value *wtablePack,
llvm::Value *index) {
auto key = LocalTypeDataKind::forProtocolWitnessTable(conf);
if (auto *wtable = IGF.tryGetLocalTypeData(elementArchetype, key))
return wtable;
auto *wtable = loadWitnessTableAtIndex(IGF, wtablePack, index);
// Bind the witness table pack element to the element archetype.
IGF.setScopedLocalTypeData(elementArchetype, key, wtable);
return wtable;
}
/// Find the pack archetype for the given interface type in the given
/// opened element context, which is known to be a forwarding context.
static CanPackArchetypeType
getMappedPackArchetypeType(const OpenedElementContext &context, CanType ty) {
auto packType = cast<PackType>(
context.environment->maybeApplyOuterContextSubstitutions(ty)
->getCanonicalType());
auto archetype = getForwardedPackArchetypeType(packType);
assert(archetype);
return archetype;
}
static void bindElementSignatureRequirementsAtIndex(
IRGenFunction &IGF, OpenedElementContext const &context, llvm::Value *index,
DynamicMetadataRequest request) {
enumerateGenericSignatureRequirements(
context.signature, [&](GenericRequirement requirement) {
switch (requirement.getKind()) {
case GenericRequirement::Kind::Shape:
case GenericRequirement::Kind::Metadata:
case GenericRequirement::Kind::WitnessTable:
case GenericRequirement::Kind::Value:
break;
case GenericRequirement::Kind::MetadataPack: {
auto ty = requirement.getTypeParameter();
auto patternPackArchetype = getMappedPackArchetypeType(context, ty);
auto response =
IGF.emitTypeMetadataRef(patternPackArchetype, request);
auto elementArchetype =
context.environment
->mapContextualPackTypeIntoElementContext(
patternPackArchetype)
->getCanonicalType();
auto *patternPack = response.getMetadata();
auto elementMetadata = bindMetadataAtIndex(
IGF, elementArchetype, patternPack, index, request);
assert(elementMetadata);
(void)elementMetadata;
break;
}
case GenericRequirement::Kind::WitnessTablePack: {
auto ty = requirement.getTypeParameter();
auto proto = requirement.getProtocol();
auto patternPackArchetype = getMappedPackArchetypeType(context, ty);
auto elementArchetype =
context.environment
->mapContextualPackTypeIntoElementContext(
patternPackArchetype)
->getCanonicalType();
llvm::Value *_metadata = nullptr;
auto packConformance = ProtocolConformanceRef::forAbstract(
patternPackArchetype, proto);
auto *wtablePack = emitWitnessTableRef(IGF, patternPackArchetype,
&_metadata, packConformance);
auto elementConformance = ProtocolConformanceRef::forAbstract(
elementArchetype, proto);
auto *wtable = bindWitnessTableAtIndex(
IGF, elementArchetype, elementConformance, wtablePack, index);
assert(wtable);
(void)wtable;
break;
}
}
});
}
static llvm::Value *emitPackExpansionElementMetadata(
IRGenFunction &IGF, OpenedElementContext context, CanType patternTy,
llvm::Value *index, DynamicMetadataRequest request) {
bindElementSignatureRequirementsAtIndex(IGF, context, index, request);
// Replace pack archetypes with element archetypes in the pattern type.
auto instantiatedPatternTy =
context.environment
->mapContextualPackTypeIntoElementContext(patternTy);
// Emit the element metadata.
auto element = IGF.emitTypeMetadataRef(instantiatedPatternTy, request)
.getMetadata();
return element;
}
/// Store the values corresponding to the specified pack expansion \p
/// expansionTy for each index in its range [dynamicIndex, dynamicIndex +
/// dynamicLength) produced by the provided function \p elementForIndex into
/// the indicated buffer \p pack.
static void emitPackExpansionPack(
IRGenFunction &IGF, Address pack,
llvm::Value *dynamicIndex, llvm::Value *dynamicLength,
function_ref<llvm::Value *(llvm::Value *)> elementForIndex) {
auto *prev = IGF.Builder.GetInsertBlock();
auto *check = IGF.createBasicBlock("pack-expansion-check");
auto *loop = IGF.createBasicBlock("pack-expansion-loop");
auto *rest = IGF.createBasicBlock("pack-expansion-rest");
IGF.Builder.CreateBr(check);
IGF.Builder.emitBlock(check);
// An index into the source metadata pack.
auto *phi = IGF.Builder.CreatePHI(IGF.IGM.SizeTy, 2);
phi->addIncoming(llvm::ConstantInt::get(IGF.IGM.SizeTy, 0), prev);
// If we reach the end, jump to the continuation block.
auto *cond = IGF.Builder.CreateICmpULT(phi, dynamicLength);
IGF.Builder.CreateCondBr(cond, loop, rest);
IGF.Builder.emitBlock(loop);
ConditionalDominanceScope condition(IGF);
IGF.withLocalStackPackAllocs([&]() {
auto *element = elementForIndex(phi);
// Store the element metadata into to the current destination index.
auto *eltIndex = IGF.Builder.CreateAdd(dynamicIndex, phi);
Address eltPtr(IGF.Builder.CreateInBoundsGEP(pack.getElementType(),
pack.getAddress(), eltIndex),
pack.getElementType(), pack.getAlignment());
IGF.Builder.CreateStore(element, eltPtr);
});
// Increment our counter.
auto *next = IGF.Builder.CreateAdd(phi,
llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
phi->addIncoming(next, IGF.Builder.GetInsertBlock());
// Repeat the loop.
IGF.Builder.CreateBr(check);
// Fall through.
IGF.Builder.emitBlock(rest);
}
static void emitPackExpansionMetadataPack(IRGenFunction &IGF, Address pack,
CanPackExpansionType expansionTy,
llvm::Value *dynamicIndex,
llvm::Value *dynamicLength,
DynamicMetadataRequest request) {
emitPackExpansionPack(
IGF, pack, dynamicIndex, dynamicLength, [&](auto *index) {
auto context =
OpenedElementContext::createForContextualExpansion(IGF.IGM.Context, expansionTy);
auto patternTy = expansionTy.getPatternType();
return emitPackExpansionElementMetadata(IGF, context, patternTy, index,
request);
});
}
std::pair<StackAddress, llvm::Value *>
irgen::emitTypeMetadataPack(IRGenFunction &IGF, CanPackType packType,
DynamicMetadataRequest request) {
auto *shape = IGF.emitPackShapeExpression(packType);
if (auto *constantInt = dyn_cast<llvm::ConstantInt>(shape)) {
assert(packType->getNumElements() == constantInt->getValue());
auto pack =
StackAddress(emitFixedSizeMetadataPackRef(IGF, packType, request));
IGF.recordStackPackMetadataAlloc(pack, constantInt);
return {pack, constantInt};
}
assert(packType->containsPackExpansionType());
auto pack =
IGF.emitDynamicAlloca(IGF.IGM.TypeMetadataPtrTy, shape,
IGF.IGM.getPointerAlignment(), AllowsTaskAlloc);
auto visitFn =
[&](CanType eltTy, unsigned staticIndex,
llvm::Value *dynamicIndex,
llvm::Value *dynamicLength) {
if (staticIndex != 0 || dynamicIndex == nullptr) {
auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, staticIndex);
accumulateSum(IGF, dynamicIndex, constant);
}
if (auto expansionTy = dyn_cast<PackExpansionType>(eltTy)) {
emitPackExpansionMetadataPack(IGF, pack.getAddress(), expansionTy,
dynamicIndex, dynamicLength, request);
} else {
Address eltPtr(
IGF.Builder.CreateInBoundsGEP(pack.getAddress().getElementType(),
pack.getAddressPointer(),
dynamicIndex),
pack.getAddress().getElementType(),
pack.getAlignment());
auto metadata = IGF.emitTypeMetadataRef(eltTy, request).getMetadata();
IGF.Builder.CreateStore(metadata, eltPtr);
}
};
visitPackExplosion(IGF, packType, visitFn);
IGF.recordStackPackMetadataAlloc(pack, shape);
return {pack, shape};
}
static std::optional<unsigned> countForShape(llvm::Value *shape) {
if (auto *constant = dyn_cast<llvm::ConstantInt>(shape))
return constant->getValue().getZExtValue();
return std::nullopt;
}
MetadataResponse
irgen::emitTypeMetadataPackRef(IRGenFunction &IGF, CanPackType packType,
DynamicMetadataRequest request) {
if (auto result = tryGetLocalPackTypeMetadata(IGF, packType, request))
return result;
StackAddress pack;
llvm::Value *shape;
std::tie(pack, shape) = emitTypeMetadataPack(IGF, packType, request);
auto *metadata = pack.getAddress().getAddress();
metadata = IGF.Builder.CreatePointerCast(metadata, IGF.IGM.PtrTy);
if (!IGF.canStackPromotePackMetadata()) {
metadata = IGF.Builder.CreateCall(
IGF.IGM.getAllocateMetadataPackFunctionPointer(), {metadata, shape});
cleanupTypeMetadataPack(IGF, pack, shape);
}
auto response = MetadataResponse::forComplete(metadata);
IGF.setScopedLocalTypeMetadata(packType, response);
return response;
}
static Address emitFixedSizeWitnessTablePack(IRGenFunction &IGF,
CanPackType packType,
PackConformance *packConformance) {
assert(!packType->containsPackExpansionType());
unsigned elementCount = packType->getNumElements();
auto allocType =
llvm::ArrayType::get(IGF.IGM.WitnessTablePtrTy, elementCount);
auto pack = IGF.createAlloca(allocType, IGF.IGM.getPointerAlignment());
IGF.Builder.CreateLifetimeStart(pack,
IGF.IGM.getPointerSize() * elementCount);
for (unsigned i : indices(packType->getElementTypes())) {
Address slot =
IGF.Builder.CreateStructGEP(pack, i, IGF.IGM.getPointerSize());
auto conformance = packConformance->getPatternConformances()[i];
llvm::Value *_metadata = nullptr;
auto *wtable =
emitWitnessTableRef(IGF, packType.getElementType(i),
/*srcMetadataCache=*/&_metadata, conformance);
IGF.Builder.CreateStore(wtable, slot);
}
return pack;
}
static llvm::Value *emitPackExpansionElementWitnessTable(
IRGenFunction &IGF, OpenedElementContext context, CanType patternTy,
ProtocolConformanceRef conformance, llvm::Value **srcMetadataCache,
llvm::Value *index) {
bindElementSignatureRequirementsAtIndex(IGF, context, index,
MetadataState::Complete);
// Replace pack archetypes with element archetypes in the pattern type.
auto instantiatedPatternTy =
context.environment->mapContextualPackTypeIntoElementContext(patternTy);
auto instantiatedConformance =
lookupConformance(instantiatedPatternTy, conformance.getProtocol());
// Emit the element witness table.
auto *wtable = emitWitnessTableRef(IGF, instantiatedPatternTy,
srcMetadataCache, instantiatedConformance);
return wtable;
}
static void emitPackExpansionWitnessTablePack(
IRGenFunction &IGF, Address pack, CanPackExpansionType expansionTy,
ProtocolConformanceRef conformance, llvm::Value *dynamicIndex,
llvm::Value *dynamicLength) {
emitPackExpansionPack(
IGF, pack, dynamicIndex, dynamicLength, [&](auto *index) {
llvm::Value *_metadata = nullptr;
auto context =
OpenedElementContext::createForContextualExpansion(IGF.IGM.Context, expansionTy);
auto patternTy = expansionTy.getPatternType();
return emitPackExpansionElementWitnessTable(
IGF, context, patternTy, conformance,
/*srcMetadataCache=*/&_metadata, index);
});
}
std::pair<StackAddress, llvm::Value *>
irgen::emitWitnessTablePack(IRGenFunction &IGF, CanPackType packType,
PackConformance *packConformance) {
auto *shape = IGF.emitPackShapeExpression(packType);
if (auto *constantInt = dyn_cast<llvm::ConstantInt>(shape)) {
assert(packType->getNumElements() == constantInt->getValue());
auto pack = StackAddress(
emitFixedSizeWitnessTablePack(IGF, packType, packConformance));
IGF.recordStackPackWitnessTableAlloc(pack, constantInt);
return {pack, constantInt};
}
assert(packType->containsPackExpansionType());
auto pack =
IGF.emitDynamicAlloca(IGF.IGM.WitnessTablePtrTy, shape,
IGF.IGM.getPointerAlignment(), AllowsTaskAlloc);
auto index = 0;
auto visitFn = [&](CanType eltTy, unsigned staticIndex,
llvm::Value *dynamicIndex, llvm::Value *dynamicLength) {
if (staticIndex != 0 || dynamicIndex == nullptr) {
auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, staticIndex);
accumulateSum(IGF, dynamicIndex, constant);
}
auto conformance = packConformance->getPatternConformances()[index];
if (auto expansionTy = dyn_cast<PackExpansionType>(eltTy)) {
emitPackExpansionWitnessTablePack(IGF, pack.getAddress(), expansionTy,
conformance, dynamicIndex,
dynamicLength);
} else {
Address eltPtr(
IGF.Builder.CreateInBoundsGEP(pack.getAddress().getElementType(),
pack.getAddressPointer(), dynamicIndex),
pack.getAddress().getElementType(), pack.getAlignment());
llvm::Value *_metadata = nullptr;
auto *wtable = emitWitnessTableRef(
IGF, eltTy, /*srcMetadataCache=*/&_metadata, conformance);
IGF.Builder.CreateStore(wtable, eltPtr);
}
++index;
};
visitPackExplosion(IGF, packType, visitFn);
IGF.recordStackPackWitnessTableAlloc(pack, shape);
return {pack, shape};
}
static void cleanupWitnessTablePackImpl(IRGenFunction &IGF, StackAddress pack,
llvm::Value *shape) {
if (pack.getExtraInfo()) {
IGF.emitDeallocateDynamicAlloca(pack);
} else if (auto count = countForShape(shape)) {
IGF.Builder.CreateLifetimeEnd(pack.getAddress(),
IGF.IGM.getPointerSize() * (count.value()));
}
}
void irgen::cleanupWitnessTablePack(IRGenFunction &IGF, StackAddress pack,
llvm::Value *shape) {
cleanupWitnessTablePackImpl(IGF, pack, shape);
IGF.eraseStackPackWitnessTableAlloc(pack, shape);
}
void irgen::cleanupStackAllocPacks(IRGenFunction &IGF,
ArrayRef<StackPackAlloc> allocs) {
for (auto alloc : llvm::reverse(allocs)) {
StackAddress addr;
uint8_t kind;
llvm::Value *shape;
std::tie(addr, shape, kind) = alloc;
switch ((GenericRequirement::Kind)kind) {
case GenericRequirement::Kind::MetadataPack:
cleanupTypeMetadataPackImpl(IGF, addr, shape);
break;
case GenericRequirement::Kind::WitnessTablePack:
cleanupWitnessTablePackImpl(IGF, addr, shape);
break;
default:
llvm_unreachable("bad requirement in stack pack alloc");
}
}
}
void IRGenFunction::recordStackPackMetadataAlloc(StackAddress addr,
llvm::Value *shape) {
OutstandingStackPackAllocs.insert(
{addr, shape, (uint8_t)GenericRequirement::Kind::MetadataPack});
}
void IRGenFunction::eraseStackPackMetadataAlloc(StackAddress addr,
llvm::Value *shape) {
auto removed = OutstandingStackPackAllocs.remove(
{addr, shape, (uint8_t)GenericRequirement::Kind::MetadataPack});
assert(removed && "erased stack pack metadata addr that wasn't recorded!?");
(void)removed;
}
void IRGenFunction::recordStackPackWitnessTableAlloc(StackAddress addr,
llvm::Value *shape) {
OutstandingStackPackAllocs.insert(
{addr, shape, (uint8_t)GenericRequirement::Kind::WitnessTablePack});
}
void IRGenFunction::eraseStackPackWitnessTableAlloc(StackAddress addr,
llvm::Value *shape) {
auto removed = OutstandingStackPackAllocs.remove(
{addr, shape, (uint8_t)GenericRequirement::Kind::WitnessTablePack});
assert(removed && "erased stack pack metadata addr that wasn't recorded!?");
(void)removed;
}
void IRGenFunction::withLocalStackPackAllocs(llvm::function_ref<void()> fn) {
auto oldSize = OutstandingStackPackAllocs.size();
fn();
SmallVector<StackPackAlloc, 2> allocs;
for (auto index = oldSize, size = OutstandingStackPackAllocs.size();
index < size; ++index) {
allocs.push_back(OutstandingStackPackAllocs[index]);
}
while (OutstandingStackPackAllocs.size() > oldSize) {
OutstandingStackPackAllocs.pop_back();
}
cleanupStackAllocPacks(*this, allocs);
}
llvm::Value *irgen::emitWitnessTablePackRef(IRGenFunction &IGF,
CanPackType packType,
PackConformance *conformance) {
assert(Lowering::TypeConverter::protocolRequiresWitnessTable(
conformance->getProtocol()) &&
"looking up witness table for protocol that doesn't have one");
if (auto *wtable = tryGetLocalPackTypeData(
IGF, packType,
LocalTypeDataKind::forAbstractProtocolWitnessTable(
conformance->getProtocol())))
return wtable;
auto localDataKind =
LocalTypeDataKind::forProtocolWitnessTablePack(conformance);
if (auto *wtable = tryGetLocalPackTypeData(IGF, packType, localDataKind))
return wtable;
StackAddress pack;
llvm::Value *shape;
std::tie(pack, shape) = emitWitnessTablePack(IGF, packType, conformance);
auto *result = pack.getAddress().getAddress();
result = IGF.Builder.CreatePointerCast(result, IGF.IGM.PtrTy);
if (!IGF.canStackPromotePackMetadata()) {
result = IGF.Builder.CreateCall(
IGF.IGM.getAllocateWitnessTablePackFunctionPointer(), {result, shape});
cleanupWitnessTablePack(IGF, pack, shape);
}
IGF.setScopedLocalTypeData(packType, localDataKind, result);
return result;
}
llvm::Value *irgen::emitTypeMetadataPackElementRef(
IRGenFunction &IGF, CanPackType packType,
ArrayRef<ProtocolConformanceRef> conformances, llvm::Value *index,
DynamicMetadataRequest request,
llvm::SmallVectorImpl<llvm::Value *> &wtables) {
// If the packs have already been materialized, just gep into them.
auto materializedMetadataPack =
tryGetLocalPackTypeMetadata(IGF, packType, request);
llvm::SmallVector<llvm::Value *> materializedWtablePacks;
for (auto conformance : conformances) {
auto *wtablePack = tryGetLocalPackTypeData(
IGF, packType,
LocalTypeDataKind::forProtocolWitnessTable(conformance));
materializedWtablePacks.push_back(wtablePack);
}
if (materializedMetadataPack &&
llvm::all_of(materializedWtablePacks,
[](auto *wtablePack) { return wtablePack; })) {
auto *metadataPack = materializedMetadataPack.getMetadata();
auto *metadata = loadMetadataAtIndex(IGF, metadataPack, index);
for (auto *wtablePack : materializedWtablePacks) {
auto *wtable = loadWitnessTableAtIndex(IGF, wtablePack, index);
wtables.push_back(wtable);
}
return metadata;
}
// Otherwise, in general, there's no already available array of metadata
// which can be indexed into.
auto *shape = IGF.emitPackShapeExpression(packType);
// If the shape and the index are both constant, the type for which metadata
// will be emitted is statically available.
auto *constantShape = dyn_cast<llvm::ConstantInt>(shape);
auto *constantIndex = dyn_cast<llvm::ConstantInt>(index);
if (constantShape && constantIndex) {
assert(packType->getNumElements() == constantShape->getValue());
auto index = constantIndex->getValue().getZExtValue();
assert(packType->getNumElements() > index);
auto ty = packType.getElementType(index);
auto response = IGF.emitTypeMetadataRef(ty, request);
auto *metadata = response.getMetadata();
for (auto conformance : conformances) {
auto patternConformance = conformance.getPack()
->getPatternConformances()[index];
auto *wtable =
emitWitnessTableRef(IGF, ty, /*srcMetadataCache=*/&metadata,
patternConformance);
wtables.push_back(wtable);
}
return metadata;
}
// A pack consists of types and pack expansion types. An example:
// {repeat each T, Int, repeat each T, repeat each U, String},
// The above type has length 5. The type "repeat each U" is at index 3.
//
// A pack _explosion_ is notionally obtained by flat-mapping the pack by the
// the operation of "listing elements" in pack expansion types.
//
// The explosion of the example pack looks like
// {T_0, T_1, ..., Int, T_0, T_1, ..., U_0, U_1, ..., String}
// ^^^^^^^^^^^^^
// the runtime components of "each T"
//
// We have an index into the explosion,
//
// {T_0, T_1, ..., Int, T_0, T_1, ..., U_0, U_1, ... String}
// ------------%index------------>
//
// and we need to obtain the element in the explosion corresponding to it.
//
// {T_0, T_1, ..., Int, T_0, T_1, ..., T_k, ..., U_0, U_1, ... String}
// ------------%index---------------> ^^^
//
// Unfortunately, the explosion has not (the first check in this function)
// been materialized--and doing so is likely wasteful--so we can't simply
// index into some array.
//
// Instead, _notionally_, we will "compute"
// (1) the index into the _pack_ and
// {repeat each T, Int, repeat each T, repeat each U, String}
// ------%outer------> ^^^^^^^^^^^^^
// (2) the index within the elements of the pack expansion type
// {T_0, T_2, ..., T_k, ...}
// ----%inner---> ^^^
//
// In fact, we won't ever materialize %outer into any register. Instead, we
// can just brach to materializing the metadata (and witness tables) once
// we've determined which outer element's range contains %index.
//
// As for %inner, it will only be materialized in those blocks corresponding
// to pack expansions.
//
// Create the following control flow:
//
// +-------+ t_0 is not t_N _is_ an
// |entry: | an expansion expansion
// |... | +----------+ +----------+ +----------+
// |... | --> |check_0: | -> ... -> |check_N: | -> |trap: |
// | | | %i == %u0| | %i < %uN | | llvm.trap|
// +-------+ +----------+ +----------+ +----------+
// %outer = 0 %outer = N
// | |
// V V
// +----------+ +-----------------------+
// |emit_1: | |emit_N: |
// | %inner=0 | | %inner = %index - %lN |
// | %m_1 = | | %m_N = |
// | %wt_1_1= | | %wt_1_N = |
// | %wt_k_1= | | %wt_k_N = |
// +----------+ +-----------------------+
// | |
// V V
// +-------------------------------------------
// |exit:
// | %m = phi [ %m_1, %emit_1 ],
// | ...
// | [ %m_N, %emit_N ]
// | %wt_1 = phi [ %wt_1_1, %emit_1 ],
// | ...
// | [ %m_1_N, %emit_N ]
// | ...
// | %wt_k = phi [ %wt_k_1, %emit_1 ],
// | ...
// | [ %m_k_N, %emit_N ]
auto *current = IGF.Builder.GetInsertBlock();
// Terminate the block that branches to continue checking or metadata/wtable
// emission depending on whether the index is in the pack expansion's bounds.
auto emitCheckBranch = [&IGF](llvm::Value *condition,
llvm::BasicBlock *inBounds,
llvm::BasicBlock *outOfBounds) {
if (condition) {
IGF.Builder.CreateCondBr(condition, inBounds, outOfBounds);
} else {
assert(!inBounds &&
"no condition to check but a materialization block!?");
IGF.Builder.CreateBr(outOfBounds);
}
};
// The block which emission will continue in after we finish emitting
// metadata/wtables for this element.
auto *exit = IGF.createBasicBlock("pack-index-element-exit");
IGF.Builder.emitBlock(exit);
auto *metadataPhi = IGF.Builder.CreatePHI(IGF.IGM.TypeMetadataPtrTy,
packType.getElementTypes().size());
llvm::SmallVector<llvm::PHINode *, 2> wtablePhis;
wtablePhis.reserve(conformances.size());
for (auto idx : indices(conformances)) {
(void)idx;
wtablePhis.push_back(IGF.Builder.CreatePHI(
IGF.IGM.WitnessTablePtrTy, packType.getElementTypes().size()));
}
IGF.Builder.SetInsertPoint(current);
// The previous checkBounds' block's comparision of %index. Use it to emit a
// branch to the current block or the previous block's metadata/wtable
// emission block.
llvm::Value *previousCondition = nullptr;
// The previous type's materialize block. Use it as the inBounds target when
// branching from the previous block.
llvm::BasicBlock *previousInBounds = nullptr;
// The lower bound of indices for the current pack expansion. Inclusive.
llvm::Value *lowerBound = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) {
auto elementTy = packType.getElementType(i);
// The block within which it will be checked whether %index corresponds to
// an element of the pack expansion elementTy.
auto *checkBounds = IGF.createBasicBlock("pack-index-element-bounds");
// Finish emitting the previous block, either entry or check_i-1.
//
// Branch from the previous bounds-check block either to this bounds-check
// block or to the previous metadata/wtable emission block.
emitCheckBranch(previousCondition, previousInBounds, checkBounds);
// (1) Emit check_i {{
IGF.Builder.emitBlock(checkBounds);
ConditionalDominanceScope dominanceScope(IGF);
// The upper bound for the current pack expansion. Exclusive.
llvm::Value *upperBound = nullptr;
llvm::Value *condition = nullptr;
if (auto expansionTy = dyn_cast<PackExpansionType>(elementTy)) {
auto reducedShape = expansionTy.getCountType();
auto *length = IGF.emitPackShapeExpression(reducedShape);
upperBound = IGF.Builder.CreateAdd(lowerBound, length);
// %index < %upperBound
//
// It's not necessary to check that %index >= %lowerBound. Either
// elementTy is the first element type in packType or we branched here
// from some series of checkBounds blocks in each of which it was
// determined that %index is greater than the indices of the
// corresponding element type.
condition = IGF.Builder.CreateICmpULT(index, upperBound);
} else {
upperBound = IGF.Builder.CreateAdd(
lowerBound, llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
// %index == %lowerBound
condition = IGF.Builder.CreateICmpEQ(lowerBound, index);
}
// }} Finished emitting check_i, except for the terminator which will be
// emitted in the next iteration once the new outOfBounds block is
// available.
// (2) Emit emit_i {{
// The block within which the metadata/wtables corresponding to %inner will
// be materialized.
auto *materialize = IGF.createBasicBlock("pack-index-element-metadata");
IGF.Builder.emitBlock(materialize);
IGF.withLocalStackPackAllocs([&]() {
llvm::Value *metadata = nullptr;
llvm::SmallVector<llvm::Value *, 2> wtables;
wtables.reserve(conformances.size());
if (auto expansionTy = dyn_cast<PackExpansionType>(elementTy)) {
// Actually materialize %inner. Then use it to get the metadata from
// the pack expansion at that index.
auto *relativeIndex = IGF.Builder.CreateSub(index, lowerBound);
auto context = OpenedElementContext::createForContextualExpansion(
IGF.IGM.Context, expansionTy);
auto patternTy = expansionTy.getPatternType();
metadata = emitPackExpansionElementMetadata(IGF, context, patternTy,
relativeIndex, request);
for (auto conformance : conformances) {
auto patternConformance =
conformance.getPack()->getPatternConformances()[i];
auto *wtable = emitPackExpansionElementWitnessTable(
IGF, context, patternTy, patternConformance, &metadata,
relativeIndex);
wtables.push_back(wtable);
}
} else {
metadata = IGF.emitTypeMetadataRef(elementTy, request).getMetadata();
for (auto conformance : conformances) {
auto patternConformance =
conformance.getPack()->getPatternConformances()[i];
llvm::Value *_metadata = nullptr;
auto *wtable = emitWitnessTableRef(IGF, elementTy,
/*srcMetadataCache=*/&_metadata,
patternConformance);
wtables.push_back(wtable);
}
}
metadataPhi->addIncoming(metadata, IGF.Builder.GetInsertBlock());
for (auto i : indices(wtables)) {
auto *wtable = wtables[i];
auto *wtablePhi = wtablePhis[i];
wtablePhi->addIncoming(wtable, IGF.Builder.GetInsertBlock());
}
});
IGF.Builder.CreateBr(exit);
// }} Finished emitting emit_i.
// Switch back to emitting check_i. The next iteration will emit its
// terminator.
IGF.Builder.SetInsertPoint(checkBounds);
// Set up the values for the next iteration.
previousInBounds = materialize;
previousCondition = condition;
lowerBound = upperBound;
}
auto *trap = IGF.createBasicBlock("pack-index-element-trap");
emitCheckBranch(previousCondition, previousInBounds, trap);
IGF.Builder.emitBlock(trap);
IGF.emitTrap("Variadic generic index out of bounds",
/*EmitUnreachable=*/true);
IGF.Builder.SetInsertPoint(exit);
for (auto *wtablePhi : wtablePhis) {
wtables.push_back(wtablePhi);
}
return metadataPhi;
}
void irgen::bindOpenedElementArchetypesAtIndex(IRGenFunction &IGF,
GenericEnvironment *environment,
llvm::Value *index) {
ASSERT(environment->getKind() == GenericEnvironment::Kind::Element);
// Record the generic type parameters of interest.
llvm::SmallPtrSet<CanType, 2> openablePackParams;
environment->forEachPackElementGenericTypeParam([&](auto *genericParam) {
openablePackParams.insert(genericParam->getCanonicalType());
});
auto subs = environment->getOuterSubstitutions();
// Find the archetypes and conformances which must be bound.
llvm::SmallSetVector<CanType, 2> types;
llvm::DenseMap<CanType, llvm::SmallVector<ProtocolConformanceRef, 2>>
conformancesForType;
auto isDerivedFromPackElementGenericTypeParam = [&](CanType ty) -> bool {
// Is this type itself an openable pack parameter OR a dependent type of
// one?
return openablePackParams.contains(
ty->getRootGenericParam()->getCanonicalType());
};
enumerateGenericSignatureRequirements(
environment->getGenericSignature().getCanonicalSignature(),
[&](GenericRequirement requirement) {
switch (requirement.getKind()) {
case GenericRequirement::Kind::MetadataPack: {
auto ty = requirement.getTypeParameter();
if (!isDerivedFromPackElementGenericTypeParam(ty))
return;
types.insert(ty);
conformancesForType.insert({ty, {}});
break;
}
case GenericRequirement::Kind::WitnessTablePack: {
auto ty = requirement.getTypeParameter();
if (!isDerivedFromPackElementGenericTypeParam(ty))
return;
types.insert(ty);
auto iterator = conformancesForType.insert({ty, {}}).first;
auto conformance = subs.lookupConformance(
ty, requirement.getProtocol());
iterator->getSecond().push_back(conformance);
break;
}
case GenericRequirement::Kind::Shape:
case GenericRequirement::Kind::Metadata:
case GenericRequirement::Kind::WitnessTable:
case GenericRequirement::Kind::Value:
break;
}
});
// For each archetype to be bound, find the corresponding conformances and
// bind the metadata and wtables.
for (auto ty : types) {
auto conformances = conformancesForType.find(ty)->getSecond();
auto archetype = cast<ElementArchetypeType>(
environment->mapPackTypeIntoElementContext(ty)->getCanonicalType());
auto packType = cast<PackType>(ty.subst(subs)->getCanonicalType());
llvm::SmallVector<llvm::Value *, 2> wtables;
auto *metadata = emitTypeMetadataPackElementRef(
IGF, packType, conformances, index, MetadataState::Complete, wtables);
auto reqt = GenericRequirement::forMetadata(archetype);
bindGenericRequirement(IGF, reqt, metadata, MetadataState::Complete,
SubstitutionMap());
assert(conformances.size() == wtables.size());
for (unsigned i : indices(wtables)) {
auto reqt = GenericRequirement::forWitnessTable(
archetype, conformances[i].getProtocol());
bindGenericRequirement(IGF, reqt, wtables[i], MetadataState::Complete,
SubstitutionMap());
}
}
}
static void cleanupTypeMetadataPackImpl(IRGenFunction &IGF, StackAddress pack,
llvm::Value *shape) {
if (pack.getExtraInfo()) {
IGF.emitDeallocateDynamicAlloca(pack);
} else if (auto count = countForShape(shape)) {
IGF.Builder.CreateLifetimeEnd(pack.getAddress(),
IGF.IGM.getPointerSize() * (*count));
}
}
void irgen::cleanupTypeMetadataPack(IRGenFunction &IGF, StackAddress pack,
llvm::Value *shape) {
cleanupTypeMetadataPackImpl(IGF, pack, shape);
IGF.eraseStackPackMetadataAlloc(pack, shape);
}
Address irgen::emitStorageAddressOfPackElement(IRGenFunction &IGF, Address pack,
llvm::Value *index,
SILType elementType,
CanSILPackType packType) {
// When we have an indirect pack, the elements are pointers, so we can
// simply index into that flat array.
assert(elementType.isAddress() && "direct packs not currently supported");
auto elementSize = getPackElementSize(IGF.IGM, packType);
auto elementAddress = IGF.Builder.CreateArrayGEP(pack, index, elementSize);
return IGF.Builder.CreateElementBitCast(elementAddress, IGF.IGM.PtrTy);
}
Size irgen::getPackElementSize(IRGenModule &IGM, CanSILPackType ty) {
assert(ty->isElementAddress() && "not implemented for direct packs");
return IGM.getPointerSize();
}
StackAddress irgen::allocatePack(IRGenFunction &IGF, CanSILPackType packType) {
auto *shape = IGF.emitPackShapeExpression(packType);
auto elementSize = getPackElementSize(IGF.IGM, packType);
if (auto *constantInt = dyn_cast<llvm::ConstantInt>(shape)) {
assert(packType->getNumElements() == constantInt->getValue());
(void)constantInt;
assert(!packType->containsPackExpansionType());
unsigned elementCount = packType->getNumElements();
auto allocType = llvm::ArrayType::get(
IGF.IGM.OpaquePtrTy, elementCount);
auto addr = IGF.createAlloca(allocType, IGF.IGM.getPointerAlignment());
IGF.Builder.CreateLifetimeStart(addr, elementSize * elementCount);
// We have an [N x opaque*]*; we need an opaque**.
addr = IGF.Builder.CreateElementBitCast(addr, IGF.IGM.OpaquePtrTy);
return addr;
}
assert(packType->containsPackExpansionType());
auto addr =
IGF.emitDynamicAlloca(IGF.IGM.OpaquePtrTy, shape,
IGF.IGM.getPointerAlignment(), AllowsTaskAlloc);
return addr;
}
void irgen::deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType packType) {
if (packType->containsPackExpansionType()) {
IGF.emitDeallocateDynamicAlloca(addr);
return;
}
auto elementSize = getPackElementSize(IGF.IGM, packType);
auto elementCount = packType->getNumElements();
IGF.Builder.CreateLifetimeEnd(addr.getAddress(),
elementSize * elementCount);
}
static unsigned getConstantLabelsLength(CanTupleType type) {
unsigned total = 0;
for (auto elt : type->getElements()) {
if (elt.getType()->is<PackExpansionType>()) {
assert(!elt.hasName());
continue;
}
if (elt.hasName()) {
assert(!elt.getType()->is<PackExpansionType>());
total += elt.getName().getLength();
}
++total;
}
return total;
}
/// Emit the dynamic label string for a tuple type containing pack
/// expansions.
///
/// The basic idea is that the static label string is "stretched out".
/// Pack expansion elements are unlabeled, so they appear as a single
/// blank space in the static label string. We replace this with the
/// appropriate number of blank spaces, given the dynamic length of
/// the pack.
std::optional<StackAddress>
irgen::emitDynamicTupleTypeLabels(IRGenFunction &IGF, CanTupleType type,
CanPackType packType,
llvm::Value *shapeExpression) {
bool hasLabels = false;
for (auto elt : type->getElements()) {
hasLabels |= elt.hasName();
}
if (!hasLabels)
return std::nullopt;
// Elements of pack expansion type are unlabeled, so the length of
// the label string is the number of elements in the pack, plus the
// sum of the lengths of the labels.
llvm::Value *labelLength = llvm::ConstantInt::get(
IGF.IGM.SizeTy, getConstantLabelsLength(type));
labelLength = IGF.Builder.CreateAdd(shapeExpression, labelLength);
// Leave root for a null byte at the end.
labelLength = IGF.Builder.CreateAdd(labelLength,
llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
// Allocate space for the label string; we fill it in below.
StackAddress labelString =
IGF.emitDynamicAlloca(IGF.IGM.Int8Ty, labelLength,
IGF.IGM.getPointerAlignment(), AllowsTaskAlloc);
// Get the static label string, where each pack expansion is one element.
auto *staticLabelString = getTupleLabelsString(IGF.IGM, type);
// The position in the static label string for to the current element.
unsigned staticPosition = 0;
// The position in the dynamic label string for to the current element.
llvm::Value *dynamicPosition = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
// Number of expansions we've seen so far.
unsigned numExpansions = 0;
// Was there at least one label?
bool sawLabel = false;
auto visitFn = [&](CanType eltTy,
unsigned scalarIndex,
llvm::Value *dynamicIndex,
llvm::Value *dynamicLength) {
auto elt = type->getElements()[scalarIndex + numExpansions];
assert(eltTy == CanType(elt.getType()));
// The destination address, where we put the current element's label.
auto eltAddr = IGF.Builder.CreateArrayGEP(labelString.getAddress(),
dynamicPosition, Size(1));
// If we're looking at a pack expansion, insert the appropriate
// number of blank spaces in the dynamic label string.
if (isa<PackExpansionType>(eltTy)) {
assert(!elt.hasName() && "Pack expansions cannot have labels");
// Fill the dynamic label string with a blank label for each
// dynamic element.
IGF.Builder.CreateMemSet(
eltAddr, llvm::ConstantInt::get(IGF.IGM.Int8Ty, ' '),
dynamicLength);
// We consumed one static label.
staticPosition += 1;
// We produced some number of dynamic labels.
dynamicPosition = IGF.Builder.CreateAdd(dynamicPosition, dynamicLength);
// We consumed an expansion.
numExpansions += 1;
return;
}
// Otherwise, we have a single scalar element, which deposits a single
// label in the dynamic label string.
unsigned length = 0;
// Scalar elements may have labels.
if (elt.hasName()) {
// Index into the static label string.
llvm::Constant *indices[] = {
llvm::ConstantInt::get(IGF.IGM.SizeTy, staticPosition)
};
// The source address in the static label string.
Address srcAddr(
llvm::ConstantExpr::getInBoundsGetElementPtr(
IGF.IGM.Int8Ty, staticLabelString,
indices),
IGF.IGM.Int8Ty, Alignment(1));
// The number of bytes to copy; add one for the space at the end.
length = elt.getName().getLength() + 1;
// Desposit the label for this element in the dynamic label string.
IGF.Builder.CreateMemCpy(eltAddr, srcAddr, Size(length));
sawLabel = true;
} else {
length = 1;
// There is no label. The static label string stores a blank space,
// and we need to update the dynamic string for the same.
IGF.Builder.CreateStore(
llvm::ConstantInt::get(IGF.IGM.Int8Ty, ' '),
eltAddr);
}
// We consumed one static label.
staticPosition += length;
// We produced one dynamic label.
auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, length);
accumulateSum(IGF, dynamicPosition, constant);
};
(void) visitPackExplosion(IGF, packType, visitFn);
// Null-terminate the dynamic label string.
auto eltAddr = IGF.Builder.CreateArrayGEP(labelString.getAddress(),
dynamicPosition, Size(1));
IGF.Builder.CreateStore(
llvm::ConstantInt::get(IGF.IGM.Int8Ty, '\0'),
eltAddr);
assert(sawLabel);
(void) sawLabel;
return labelString;
}
StackAddress
irgen::emitDynamicFunctionParameterFlags(IRGenFunction &IGF,
AnyFunctionType::CanParamArrayRef params,
CanPackType packType,
llvm::Value *shapeExpression) {
auto array = IGF.emitDynamicAlloca(IGF.IGM.Int32Ty, shapeExpression,
Alignment(4), AllowsTaskAlloc);
unsigned numExpansions = 0;
auto visitFn = [&](CanType eltTy,
unsigned scalarIndex,
llvm::Value *dynamicIndex,
llvm::Value *dynamicLength) {
if (scalarIndex != 0 || dynamicIndex == nullptr) {
auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, scalarIndex);
accumulateSum(IGF, dynamicIndex, constant);
}
auto elt = params[scalarIndex + numExpansions];
auto flags = getABIParameterFlags(elt.getParameterFlags());
auto flagsVal = llvm::ConstantInt::get(
IGF.IGM.Int32Ty, flags.getIntValue());
assert(eltTy == elt.getPlainType());
// If we're looking at a pack expansion, insert the appropriate
// number of flags fields.
if (isa<PackExpansionType>(eltTy)) {
emitPackExpansionPack(IGF, array.getAddress(),
dynamicIndex, dynamicLength,
[&](llvm::Value *) -> llvm::Value * {
return flagsVal;
});
// We consumed an expansion.
numExpansions += 1;
return;
}
// The destination address, where we put the current element's flags field.
Address eltAddr(
IGF.Builder.CreateInBoundsGEP(array.getAddress().getElementType(),
array.getAddressPointer(),
dynamicIndex),
array.getAddress().getElementType(),
array.getAlignment());
// Otherwise, we have a single scalar element, which deposits a single
// flags field.
IGF.Builder.CreateStore(flagsVal, eltAddr);
};
(void) visitPackExplosion(IGF, packType, visitFn);
return array;
}
std::pair<StackAddress, llvm::Value *>
irgen::emitInducedTupleTypeMetadataPack(
IRGenFunction &IGF, llvm::Value *tupleMetadata) {
auto *shape = emitTupleTypeMetadataLength(IGF, tupleMetadata);
auto pack =
IGF.emitDynamicAlloca(IGF.IGM.TypeMetadataPtrTy, shape,
IGF.IGM.getPointerAlignment(), AllowsTaskAlloc);
auto elementForIndex =
[&](llvm::Value *index) -> llvm::Value * {
return irgen::emitTupleTypeMetadataElementType(IGF, tupleMetadata, index);
};
auto *index = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
emitPackExpansionPack(IGF, pack.getAddress(), index, shape,
elementForIndex);
IGF.recordStackPackMetadataAlloc(pack, shape);
return {pack, shape};
}
MetadataResponse
irgen::emitInducedTupleTypeMetadataPackRef(
IRGenFunction &IGF, CanPackType packType,
llvm::Value *tupleMetadata) {
StackAddress pack;
llvm::Value *shape;
std::tie(pack, shape) = emitInducedTupleTypeMetadataPack(
IGF, tupleMetadata);
auto *metadata = pack.getAddress().getAddress();
if (!IGF.canStackPromotePackMetadata()) {
metadata = IGF.Builder.CreateCall(
IGF.IGM.getAllocateMetadataPackFunctionPointer(), {metadata, shape});
cleanupTypeMetadataPack(IGF, pack, shape);
}
return MetadataResponse::forComplete(metadata);
}