Files
swift-mirror/lib/IRGen/MetadataRequest.cpp
Doug Gregor b84f8ab080 Rename "suppressible protocols" to "invertible protocols".
We've decided to use the "invertible protocols" terminology throughout
the runtime and compiler, so move over to that terminology
consistently.
2024-03-29 11:31:48 -07:00

4083 lines
155 KiB
C++

//===--- MetadataRequest.cpp - IR generation for metadata requests --------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 accessing metadata.
//
//===----------------------------------------------------------------------===//
#include "MetadataRequest.h"
#include "Callee.h"
#include "ConstantBuilder.h"
#include "Explosion.h"
#include "ExtendedExistential.h"
#include "FixedTypeInfo.h"
#include "GenArchetype.h"
#include "GenClass.h"
#include "GenMeta.h"
#include "GenPack.h"
#include "GenPointerAuth.h"
#include "GenProto.h"
#include "GenTuple.h"
#include "GenType.h"
#include "GenericArguments.h"
#include "GenericRequirement.h"
#include "IRGenDebugInfo.h"
#include "IRGenFunction.h"
#include "IRGenMangler.h"
#include "IRGenModule.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/CanTypeVisitor.h"
#include "swift/AST/DiagnosticsIRGen.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/IRGen/Linking.h"
#include "swift/SIL/FormalLinkage.h"
#include "swift/SIL/TypeLowering.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ModRef.h"
#include <algorithm>
using namespace swift;
using namespace irgen;
llvm::Value *DynamicMetadataRequest::get(IRGenFunction &IGF) const {
if (isStatic()) {
return IGF.IGM.getSize(Size(StaticRequest.getOpaqueValue()));
} else {
return DynamicRequest;
}
}
llvm::Value *DynamicMetadataRequest::getRequiredState(IRGenFunction &IGF) const{
if (isStatic()) {
return IGF.IGM.getSize(Size(size_t(StaticRequest.getState())));
}
auto request = DynamicRequest;
static_assert(MetadataRequest::State_bit == 0,
"code below is not doing any shifts");
uint32_t mask =
((uint32_t(1) << MetadataRequest::State_width) - 1);
auto requiredState =
IGF.Builder.CreateAnd(request,
llvm::ConstantInt::get(IGF.IGM.SizeTy, mask));
return requiredState;
}
MetadataResponse MetadataResponse::getUndef(IRGenFunction &IGF) {
return forComplete(llvm::UndefValue::get(IGF.IGM.TypeMetadataPtrTy));
}
MetadataResponse
MetadataResponse::handle(IRGenFunction &IGF, DynamicMetadataRequest request,
llvm::Value *pair) {
assert(pair->getType() == IGF.IGM.TypeMetadataResponseTy);
// If the request is statically known to produce a complete result,
// we never even need to extract the status value.
if (request.isStaticallyBlockingComplete()) {
auto value = IGF.Builder.CreateExtractValue(pair, 0);
return MetadataResponse::forComplete(value);
}
// Otherwise, split the response.
auto split = IGF.Builder.CreateSplit<2>(pair);
// If the request has a collector installed, check the dependency now.
if (auto collector = request.getDependencyCollector()) {
collector->checkDependency(IGF, request, split[0], split[1]);
}
// Compute the static lower bound on the metadata's dynamic state.
// This will include any refinements from having branched for the
// dependency collector.
auto staticBound = request.getStaticLowerBoundOnResponseState();
auto response = MetadataResponse(split[0], split[1], staticBound);
return response;
}
llvm::Value *MetadataResponse::combine(IRGenFunction &IGF) const {
assert(isValid());
assert(hasDynamicState() && "cannot combine response without dynamic state");
return IGF.Builder.CreateCombine(IGF.IGM.TypeMetadataResponseTy,
{Metadata, getDynamicState()});
}
void MetadataResponse::ensureDynamicState(IRGenFunction &IGF) & {
assert(isValid());
// If we already have a dynamic state, bail out.
if (hasDynamicState()) return;
// If we're statically known complete, we can just fill in
// MetadataState::Complete.
if (isStaticallyKnownComplete()) {
DynamicState = getCompletedState(IGF.IGM);
return;
}
// Otherwise, we need to check the state dynamically. Do a non-blocking
// request for complete metadata.
auto request = MetadataRequest(MetadataState::Complete,
/*non-blocking*/ true);
*this = emitGetTypeMetadataDynamicState(IGF, request, Metadata);
}
llvm::Constant *MetadataResponse::getCompletedState(IRGenModule &IGM) {
return IGM.getSize(Size(size_t(MetadataState::Complete)));
}
llvm::Value *MetadataDependency::combine(IRGenFunction &IGF) const {
if (isTrivial()) {
return getTrivialCombinedDependency(IGF.IGM);
}
return IGF.Builder.CreateCombine(IGF.IGM.TypeMetadataDependencyTy,
{RequiredMetadata, RequiredState});
}
llvm::Constant *
MetadataDependency::getTrivialCombinedDependency(IRGenModule &IGM) {
return llvm::ConstantAggregateZero::get(IGM.TypeMetadataDependencyTy);
}
void MetadataDependencyCollector::checkDependency(IRGenFunction &IGF,
DynamicMetadataRequest request,
llvm::Value *metadata,
llvm::Value *metadataState) {
// Having either both or neither of the PHIs is normal.
// Having just RequiredState means that we already finalized this collector
// and shouldn't be using it anymore.
assert((!RequiredMetadata || RequiredState) &&
"checking dependencies on a finished collector");
// If the request is statically always satisfied, the operation cannot
// have failed.
if (request.isStaticallyAlwaysSatisfied())
return;
// Otherwise, we need to pull out the response state and compare it against
// the request state.
llvm::Value *requiredState = request.getRequiredState(IGF);
// More advanced metadata states are lower numbers.
static_assert(MetadataStateIsReverseOrdered,
"relying on the ordering of MetadataState here");
auto satisfied = IGF.Builder.CreateICmpULE(metadataState, requiredState);
emitCheckBranch(IGF, satisfied, metadata, requiredState);
}
void MetadataDependencyCollector::collect(IRGenFunction &IGF,
llvm::Value *dependency) {
// Having either both or neither of the PHIs is normal.
// Having just RequiredState means that we already finalized this collector
// and shouldn't be using it anymore.
assert((!RequiredMetadata || RequiredState) &&
"checking dependencies on a finished collector");
assert(dependency->getType() == IGF.IGM.TypeMetadataDependencyTy);
// Split the dependency.
auto metadata = IGF.Builder.CreateExtractValue(dependency, 0);
auto requiredState = IGF.Builder.CreateExtractValue(dependency, 1);
// We have a dependency if the metadata is non-null; otherwise we're
// satisfied and can continue.
auto satisfied = IGF.Builder.CreateIsNull(metadata);
emitCheckBranch(IGF, satisfied, metadata, requiredState);
}
void MetadataDependencyCollector::emitCheckBranch(IRGenFunction &IGF,
llvm::Value *satisfied,
llvm::Value *metadata,
llvm::Value *requiredState) {
// Lazily create the final continuation block and phis.
if (!RequiredMetadata) {
auto contBB = IGF.createBasicBlock("metadata-dependencies.cont");
RequiredMetadata =
llvm::PHINode::Create(IGF.IGM.TypeMetadataPtrTy, 4, "", contBB);
RequiredState = llvm::PHINode::Create(IGF.IGM.SizeTy, 4, "", contBB);
}
// Conditionally branch to the final continuation block.
auto satisfiedBB = IGF.createBasicBlock("dependency-satisfied");
auto curBB = IGF.Builder.GetInsertBlock();
RequiredMetadata->addIncoming(metadata, curBB);
RequiredState->addIncoming(requiredState, curBB);
IGF.Builder.CreateCondBr(satisfied, satisfiedBB,
RequiredMetadata->getParent());
// Otherwise resume emitting code on the main path.
IGF.Builder.emitBlock(satisfiedBB);
}
MetadataDependency MetadataDependencyCollector::finish(IRGenFunction &IGF) {
assert((!RequiredMetadata || RequiredState) &&
"finishing an already-finished collector");
// If we never branched with a dependency, the result is trivial.
if (RequiredMetadata == nullptr)
return MetadataDependency();
llvm::BasicBlock *curBB = IGF.Builder.GetInsertBlock();
assert(curBB);
auto contBB = RequiredMetadata->getParent();
IGF.Builder.CreateBr(contBB);
RequiredMetadata->addIncoming(
llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy),
curBB);
RequiredState->addIncoming(llvm::ConstantInt::get(IGF.IGM.SizeTy, 0), curBB);
IGF.Builder.emitBlock(contBB);
auto result = MetadataDependency(RequiredMetadata, RequiredState);
// Clear RequiredMetadata to tell the destructor that we finished.
// We leave RequiredState in place so that we can detect attempts to
// add
RequiredMetadata = nullptr;
return result;
}
static bool usesExtendedExistentialMetadata(CanType type) {
auto layout = type.getExistentialLayout();
// If there are parameterized protocol types that we want to
// treat as equal to unparameterized protocol types (maybe
// something like `P<some Any>`?), then AST type canonicalization
// should turn them into unparameterized protocol types. If the
// structure makes it to IRGen, we have to honor that decision that
// they represent different types.
return layout.containsParameterized;
}
static std::optional<std::pair<CanExistentialType, /*depth*/ unsigned>>
usesExtendedExistentialMetadata(CanExistentialMetatypeType type) {
unsigned depth = 1;
auto cur = type.getInstanceType();
while (auto metatype = dyn_cast<ExistentialMetatypeType>(cur)) {
cur = metatype.getInstanceType();
depth++;
}
// The only existential types that don't currently use ExistentialType
// are Any and AnyObject, which don't use extended metadata.
if (usesExtendedExistentialMetadata(cur)) {
// HACK: The AST for an existential metatype of a (parameterized) protocol
// still directly wraps the existential type as its instance, which means
// we need to reconstitute the enclosing ExistentialType.
assert(cur->isExistentialType());
if (!cur->is<ExistentialType>()) {
cur = ExistentialType::get(cur)->getCanonicalType();
}
return std::make_pair(cast<ExistentialType>(cur), depth);
}
return std::nullopt;
}
llvm::Constant *IRGenModule::getAddrOfStringForMetadataRef(
StringRef symbolName,
unsigned alignment,
bool shouldSetLowBit,
llvm::function_ref<ConstantInitFuture (ConstantInitBuilder &)> body) {
// Call this to form the return value.
auto returnValue = [&](llvm::Constant *addr) {
if (!shouldSetLowBit)
return addr;
auto bitConstant = llvm::ConstantInt::get(IntPtrTy, 1);
return llvm::ConstantExpr::getGetElementPtr(Int8Ty, addr, bitConstant);
};
// Check whether we already have an entry with this name.
auto &entry = StringsForTypeRef[symbolName];
if (entry.second) {
return returnValue(entry.second);
}
// Construct the initializer.
ConstantInitBuilder builder(*this);
auto finished = body(builder);
auto var = new llvm::GlobalVariable(Module, finished.getType(),
/*constant*/ true,
llvm::GlobalValue::LinkOnceODRLinkage,
nullptr,
symbolName);
ApplyIRLinkage(IRLinkage::InternalLinkOnceODR).to(var);
if (alignment)
var->setAlignment(llvm::MaybeAlign(alignment));
setTrueConstGlobal(var);
var->setSection(getReflectionTypeRefSectionName());
finished.installInGlobal(var);
// Drill down to the i8* at the beginning of the constant.
auto addr = llvm::ConstantExpr::getBitCast(var, Int8PtrTy);
StringsForTypeRef[symbolName] = { var, addr };
return returnValue(addr);
}
llvm::Constant *IRGenModule::getAddrOfStringForTypeRef(StringRef str,
MangledTypeRefRole role){
return getAddrOfStringForTypeRef(SymbolicMangling{str.str(), {}}, role);
}
llvm::Constant *IRGenModule::getAddrOfStringForTypeRef(
const SymbolicMangling &mangling,
MangledTypeRefRole role) {
// Create a symbol name for the symbolic mangling. This is used as the
// uniquing key both for ODR coalescing and within this TU.
IRGenMangler mangler;
std::string symbolName =
mangler.mangleSymbolNameForSymbolicMangling(mangling, role);
// See if we emitted the constant already.
auto &entry = StringsForTypeRef[symbolName];
if (entry.second) {
return entry.second;
}
ConstantInitBuilder B(*this);
auto S = B.beginStruct();
S.setPacked(true);
switch (role) {
case MangledTypeRefRole::DefaultAssociatedTypeWitness:
// The 0xFF prefix identifies a default associated type witness.
S.addInt(Int8Ty,
ProtocolRequirementFlags::AssociatedTypeInProtocolContextByte);
break;
case MangledTypeRefRole::FlatUnique:
assert(mangling.SymbolicReferences.empty());
break;
case MangledTypeRefRole::Metadata:
case MangledTypeRefRole::Reflection:
case MangledTypeRefRole::FieldMetadata:
break;
}
unsigned pos = 0;
for (auto &symbolic : mangling.SymbolicReferences) {
using SymbolicReferent = IRGenMangler::SymbolicReferent;
const SymbolicReferent &referent = symbolic.first;
assert(symbolic.second >= pos
&& "references should be ordered");
if (symbolic.second != pos) {
// Emit the preceding literal chunk.
auto literalChunk = StringRef(mangling.String.data() + pos,
symbolic.second - pos);
auto literal = llvm::ConstantDataArray::getString(getLLVMContext(),
literalChunk,
/*null*/ false);
S.add(literal);
}
ConstantReference ref;
unsigned char kind;
switch (referent.getKind()) {
case SymbolicReferent::NominalType: {
auto type = const_cast<NominalTypeDecl*>(referent.getNominalType());
bool isObjCProtocol = false;
if (auto proto = dyn_cast<ProtocolDecl>(type)) {
if (proto->isObjC()) {
assert(canUseObjCSymbolicReferences());
ref = ConstantReference(
cast<llvm::Constant>(getObjCProtocolRefSymRefDescriptor(proto)),
ConstantReference::Direct);
isObjCProtocol = true;
} else {
// The symbolic reference is to the protocol descriptor of the
// referenced protocol.
ref = getAddrOfLLVMVariableOrGOTEquivalent(
LinkEntity::forProtocolDescriptor(proto));
}
} else {
// The symbolic reference is to the type context descriptor of the
// referenced type.
IRGen.noteUseOfTypeContextDescriptor(type, DontRequireMetadata);
ref = getAddrOfLLVMVariableOrGOTEquivalent(
LinkEntity::forNominalTypeDescriptor(type));
}
// \1 - direct reference, \2 - indirect reference
kind = (ref.isIndirect() ? 0x02 : (isObjCProtocol ? 0x0c : 0x01));
break;
}
case SymbolicReferent::OpaqueType: {
auto opaque = const_cast<OpaqueTypeDecl*>(referent.getOpaqueType());
IRGen.noteUseOfOpaqueTypeDescriptor(opaque);
ref = getAddrOfLLVMVariableOrGOTEquivalent(
LinkEntity::forOpaqueTypeDescriptor(opaque));
kind = (ref.isIndirect() ? 0x02 : 0x01);
break;
}
case SymbolicReferent::ExtendedExistentialTypeShape: {
auto shapeInfo =
ExtendedExistentialTypeShapeInfo::get(
referent.getType()->getCanonicalType());
ref = ConstantReference(
emitExtendedExistentialTypeShape(*this, shapeInfo),
ConstantReference::Direct);
kind = (shapeInfo.isUnique() ? 0x0a : 0x0b);
break;
}
}
// add kind byte
S.add(llvm::ConstantInt::get(Int8Ty, kind));
// add relative reference
S.addRelativeAddress(ref.getValue());
pos = symbolic.second + 5;
}
// Add the last literal bit, if any.
if (pos != mangling.String.size()) {
auto literalChunk = StringRef(mangling.String.data() + pos,
mangling.String.size() - pos);
auto literal = llvm::ConstantDataArray::getString(getLLVMContext(),
literalChunk,
/*null*/ false);
S.add(literal);
}
// And a null terminator!
S.addInt(Int8Ty, 0);
auto finished = S.finishAndCreateFuture();
auto var = new llvm::GlobalVariable(Module, finished.getType(),
/*constant*/ true,
llvm::GlobalValue::LinkOnceODRLinkage,
nullptr,
symbolName);
ApplyIRLinkage(IRLinkage::InternalLinkOnceODR).to(var);
var->setAlignment(llvm::MaybeAlign(2));
setTrueConstGlobal(var);
var->setSection(getReflectionTypeRefSectionName());
finished.installInGlobal(var);
// Drill down to the i8* at the beginning of the constant.
auto addr = llvm::ConstantExpr::getBitCast(var, Int8PtrTy);
entry = {var, addr};
return addr;
}
llvm::Value *irgen::emitObjCMetadataRefForMetadata(IRGenFunction &IGF,
llvm::Value *classPtr) {
assert(IGF.IGM.Context.LangOpts.EnableObjCInterop);
classPtr = IGF.Builder.CreateBitCast(classPtr, IGF.IGM.ObjCClassPtrTy);
// Fetch the metadata for that class.
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetObjCClassMetadataFunctionPointer(), classPtr);
call->setDoesNotThrow();
call->setDoesNotAccessMemory();
return call;
}
/// Emit a reference to the Swift metadata for an Objective-C class.
static llvm::Value *emitObjCMetadataRef(IRGenFunction &IGF,
ClassDecl *theClass) {
// Derive a pointer to the Objective-C class.
auto classPtr = emitObjCHeapMetadataRef(IGF, theClass);
return emitObjCMetadataRefForMetadata(IGF, classPtr);
}
// Get the type that exists at runtime to represent a compile-time type.
CanType IRGenModule::getRuntimeReifiedType(CanType type) {
// Leave type-erased ObjC generics with their generic arguments unbound, since
// the arguments do not exist at runtime.
return CanType(type.transform([&](Type t) -> Type {
if (CanType(t).isTypeErasedGenericClassType()) {
return t->getAnyNominal()->getDeclaredType()->getCanonicalType();
}
return t;
}));
}
CanType IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type) {
// Substitute away opaque types whose underlying types we're allowed to
// assume are constant.
if (type->hasOpaqueArchetype()) {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
getSwiftModule(), ResilienceExpansion::Maximal,
getSILModule().isWholeModule());
auto underlyingTy =
type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes)
->getCanonicalType();
return underlyingTy;
}
return type;
}
SILType IRGenModule::substOpaqueTypesWithUnderlyingTypes(
SILType type, CanGenericSignature genericSig) {
// Substitute away opaque types whose underlying types we're allowed to
// assume are constant.
if (type.getASTType()->hasOpaqueArchetype()) {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
getSwiftModule(), ResilienceExpansion::Maximal,
getSILModule().isWholeModule());
auto underlyingTy =
type.subst(getSILModule(), replacer, replacer, genericSig,
SubstFlags::SubstituteOpaqueArchetypes);
return underlyingTy;
}
return type;
}
std::pair<CanType, ProtocolConformanceRef>
IRGenModule::substOpaqueTypesWithUnderlyingTypes(CanType type,
ProtocolConformanceRef conformance) {
// Substitute away opaque types whose underlying types we're allowed to
// assume are constant.
if (type->hasOpaqueArchetype()) {
ReplaceOpaqueTypesWithUnderlyingTypes replacer(
getSwiftModule(), ResilienceExpansion::Maximal,
getSILModule().isWholeModule());
auto substConformance = conformance.subst(
type, replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes);
auto underlyingTy =
type.subst(replacer, replacer, SubstFlags::SubstituteOpaqueArchetypes)
->getCanonicalType();
return std::make_pair(underlyingTy, substConformance);
}
return std::make_pair(type, conformance);
}
/// Attempts to return a constant heap metadata reference for a
/// class type. This is generally only valid for specific kinds of
/// ObjC reference, like superclasses or category references.
llvm::Constant *
irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
CanType type,
bool allowDynamicUninitialized) {
auto theDecl = type->getClassOrBoundGenericClass();
assert(theDecl && "emitting constant heap metadata ref for non-class type?");
switch (IGM.getClassMetadataStrategy(theDecl)) {
case ClassMetadataStrategy::Resilient:
case ClassMetadataStrategy::Singleton:
if (!allowDynamicUninitialized)
return nullptr;
break;
case ClassMetadataStrategy::Update:
case ClassMetadataStrategy::FixedOrUpdate:
case ClassMetadataStrategy::Fixed:
break;
}
// For imported classes, use the ObjC class symbol.
if (!hasKnownSwiftMetadata(IGM, theDecl))
return IGM.getAddrOfObjCClass(theDecl, NotForDefinition);
return IGM.getAddrOfTypeMetadata(type);
}
/// Attempts to return a constant type metadata reference for a
/// nominal type.
ConstantReference
irgen::tryEmitConstantTypeMetadataRef(IRGenModule &IGM, CanType type,
SymbolReferenceKind refKind) {
if (IGM.isStandardLibrary())
return ConstantReference();
if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type))
return ConstantReference();
return IGM.getAddrOfTypeMetadata(type, refKind);
}
/// Emit a reference to an ObjC class. In general, the only things
/// you're allowed to do with the address of an ObjC class symbol are
/// (1) send ObjC messages to it (in which case the message will be
/// forwarded to the real class, if one exists) or (2) put it in
/// various data sections where the ObjC runtime will properly arrange
/// things. Therefore, we must typically force the initialization of
/// a class when emitting a reference to it.
llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF,
ClassDecl *theClass,
bool allowUninitialized) {
// If the class is visible only through the Objective-C runtime, form the
// appropriate runtime call.
if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::RuntimeOnly) {
SmallString<64> scratch;
auto className =
IGF.IGM.getAddrOfGlobalString(theClass->getObjCRuntimeName(scratch));
return IGF.Builder.CreateCall(IGF.IGM.getLookUpClassFunctionPointer(),
className);
}
assert(!theClass->isForeign());
Address classRef = IGF.IGM.getAddrOfObjCClassRef(theClass);
auto classObject = IGF.Builder.CreateLoad(classRef);
if (allowUninitialized) return classObject;
// TODO: memoize this the same way that we memoize Swift type metadata?
return IGF.Builder.CreateCall(IGF.IGM.getFixedClassInitializationFn(),
classObject);
}
static MetadataResponse emitNominalPrespecializedGenericMetadataRef(
IRGenFunction &IGF, NominalTypeDecl *theDecl, CanType theType,
DynamicMetadataRequest request,
SpecializedMetadataCanonicality canonicality) {
assert(isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IGF.IGM, theType, canonicality));
// We are applying generic parameters to a generic type.
assert(theType->getAnyNominal() == theDecl);
// Check to see if we've maybe got a local reference already.
if (auto cache = IGF.tryGetLocalTypeMetadata(theType, request))
return cache;
switch (canonicality) {
case CanonicalSpecializedMetadata: {
auto metadata = IGF.IGM.getAddrOfTypeMetadata(theType);
return MetadataResponse::forComplete(metadata);
}
case NoncanonicalSpecializedMetadata: {
auto cacheVariable =
IGF.IGM.getAddrOfNoncanonicalSpecializedGenericTypeMetadataCacheVariable(theType);
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetCanonicalSpecializedMetadataFunctionPointer(),
{request.get(IGF),
IGF.IGM.getAddrOfTypeMetadata(theType,
TypeMetadataCanonicality::Noncanonical),
cacheVariable});
call->setDoesNotThrow();
call->setCallingConv(IGF.IGM.SwiftCC);
return MetadataResponse::handle(IGF, request, call);
}
}
llvm_unreachable("unhandled metadata canonicality");
}
static llvm::Value *
emitIdempotentClassMetadataInitialization(IRGenFunction &IGF,
llvm::Value *metadata) {
if (IGF.IGM.ObjCInterop) {
metadata = IGF.Builder.CreateBitCast(metadata, IGF.IGM.ObjCClassPtrTy);
metadata = IGF.Builder.CreateCall(IGF.IGM.getFixedClassInitializationFn(),
metadata);
metadata = IGF.Builder.CreateBitCast(metadata, IGF.IGM.TypeMetadataPtrTy);
}
return metadata;
}
/// Returns a metadata reference for a nominal type.
///
/// This is only valid in a couple of special cases:
/// 1) The nominal type is generic, in which case we emit a call to the
/// generic metadata accessor function, which must be defined separately.
/// 2) The nominal type is a value type with a fixed size from this
/// resilience domain, in which case we can reference the constant
/// metadata directly.
/// 3) The nominal type is a class with known Swift metadata and
/// a fixed layout from this resilience domain, in which case we only
/// need perform idempotent class initialization to realize it
/// in the ObjC runtime.
///
/// In any other case, a metadata accessor should be called instead.
static MetadataResponse emitNominalMetadataRef(IRGenFunction &IGF,
NominalTypeDecl *theDecl,
CanType theType,
DynamicMetadataRequest request) {
assert(!isa<ProtocolDecl>(theDecl));
if (!theDecl->isGenericContext()) {
assert(!IGF.IGM.isResilient(theDecl, ResilienceExpansion::Maximal));
if (auto response = IGF.tryGetLocalTypeMetadata(theType, request)) {
return response;
}
llvm::Value *metadata = IGF.IGM.getAddrOfTypeMetadata(theType);
// We need to realize classes with the ObjC runtime.
if (auto c = dyn_cast<ClassDecl>(theDecl)) {
assert(hasKnownSwiftMetadata(IGF.IGM, c));
metadata = emitIdempotentClassMetadataInitialization(IGF, metadata);
}
auto response = MetadataResponse::forComplete(metadata);
IGF.setScopedLocalTypeMetadata(theType, response);
return response;
}
// We are applying generic parameters to a generic type.
assert(theType->isSpecialized() &&
theType->getAnyNominal() == theDecl);
// Check to see if we've maybe got a local reference already.
if (auto cache = IGF.tryGetLocalTypeMetadata(theType, request))
return cache;
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
MetadataResponse response = emitNominalPrespecializedGenericMetadataRef(
IGF, theDecl, theType, request, CanonicalSpecializedMetadata);
IGF.setScopedLocalTypeMetadata(theType, response);
return response;
}
// Grab the substitutions.
GenericArguments genericArgs;
genericArgs.collect(IGF, theType);
assert((!genericArgs.Values.empty() ||
theDecl->getGenericSignature()->areAllParamsConcrete()) &&
"no generic args?!");
MetadataResponse response;
if (isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IGF.IGM, theType, CanonicalSpecializedMetadata)) {
response = emitNominalPrespecializedGenericMetadataRef(
IGF, theDecl, theType, request, CanonicalSpecializedMetadata);
} else if (isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IGF.IGM, theType, NoncanonicalSpecializedMetadata)) {
response = emitNominalPrespecializedGenericMetadataRef(
IGF, theDecl, theType, request, NoncanonicalSpecializedMetadata);
} else if (auto theClass = dyn_cast<ClassDecl>(theDecl)) {
if (isSpecializedNominalTypeMetadataStaticallyAddressable(
IGF.IGM, theType, CanonicalSpecializedMetadata,
ForUseOnlyFromAccessor)) {
llvm::Function *accessor =
IGF.IGM
.getAddrOfCanonicalSpecializedGenericTypeMetadataAccessFunction(
theType, NotForDefinition);
response =
IGF.emitGenericTypeMetadataAccessFunctionCall(accessor, {}, request);
}
}
if (!response.isValid()) {
// Call the generic metadata accessor function.
llvm::Function *accessor =
IGF.IGM.getAddrOfGenericTypeMetadataAccessFunction(
theDecl, genericArgs.Types, NotForDefinition);
response = IGF.emitGenericTypeMetadataAccessFunctionCall(
accessor, genericArgs.Values, request);
}
IGF.setScopedLocalTypeMetadata(theType, response);
return response;
}
bool irgen::isSpecializedNominalTypeMetadataStaticallyAddressable(
IRGenModule &IGM, CanType type,
SpecializedMetadataCanonicality canonicality,
SpecializedMetadataUsageIsOnlyFromAccessor onlyFromAccessor) {
auto *nominal = type->getAnyNominal();
assert(!isa<ProtocolDecl>(nominal));
assert(nominal->isGenericContext());
if (!IGM.shouldPrespecializeGenericMetadata()) {
return false;
}
if (type->hasArchetype()) {
return false;
}
if (!IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal))
return false;
switch (canonicality) {
case CanonicalSpecializedMetadata:
if (IGM.getSILModule().isWholeModule()) {
// Canonical prespecializations can only be emitted within the module
// where the generic type is itself defined, since it is the module where
// the metadata accessor is defined.
if (IGM.getSwiftModule() != nominal->getModuleContext()) {
return false;
}
} else {
// If whole module optimization is not enabled, we can only construct a
// canonical prespecialization if the usage is in the same *file* as that
// containing the type's decl! The reason is that the generic metadata
// accessor is defined in the IRGenModule corresponding to the source file
// containing the type's decl.
SourceFile *nominalFile = nominal->getDeclContext()->getParentSourceFile();
if (auto *moduleFile = IGM.IRGen.getSourceFile(&IGM)) {
if (nominalFile != moduleFile) {
return false;
}
}
}
break;
case NoncanonicalSpecializedMetadata:
// Non-canonical metadata prespecializations for a type cannot be formed
// within the module that defines that type.
if (IGM.getSwiftModule() == nominal->getModuleContext()) {
return false;
}
if (nominal->isResilient(IGM.getSwiftModule(),
ResilienceExpansion::Maximal)) {
return false;
}
break;
}
if (auto *theClass = dyn_cast<ClassDecl>(nominal)) {
if (theClass->hasResilientMetadata(IGM.getSwiftModule(),
ResilienceExpansion::Maximal)) {
return false;
}
AncestryOptions flags = theClass->checkAncestry();
if (flags & (AncestryOptions(AncestryFlags::ResilientOther) |
AncestryOptions(AncestryFlags::ClangImported))) {
return false;
}
if (auto *theSuperclass = theClass->getSuperclassDecl()) {
auto superclassType =
type->getSuperclass(/*useArchetypes=*/false)->getCanonicalType();
if (!isCanonicalInitializableTypeMetadataStaticallyAddressable(
IGM, superclassType) &&
!tryEmitConstantHeapMetadataRef(
IGM, superclassType,
/*allowDynamicUninitialized=*/false)) {
return false;
}
}
}
// Analyze the substitution map to determine if everything can be referenced
// statically.
auto substitutions =
type->getContextSubstitutionMap(IGM.getSwiftModule(), nominal);
// If we cannot statically reference type metadata for our replacement types,
// we cannot specialize.
for (auto replacementType : substitutions.getReplacementTypes()) {
auto canonicalType = replacementType->getCanonicalType();
if (onlyFromAccessor) {
// If an accessor is being used, then the accessor will be able to
// initialize the arguments, i.e. register classes with the ObjC
// runtime.
if (!irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable(
IGM, canonicalType)) {
return false;
}
} else {
if (!irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable(
IGM, canonicalType)) {
return false;
}
}
}
// If we have to instantiate resilient or dependent witness tables, we
// cannot prespecialize.
for (auto conformance : substitutions.getConformances()) {
auto protocol = conformance.getRequirement();
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
continue;
if (!conformance.isConcrete())
return false;
auto rootConformance = conformance.getConcrete()->getRootConformance();
if (IGM.isDependentConformance(rootConformance) ||
IGM.isResilientConformance(rootConformance))
return false;
}
return true;
}
bool irgen::isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IRGenModule &IGM, CanType type,
SpecializedMetadataCanonicality canonicality) {
if (isa<ClassType>(type) || isa<BoundGenericClassType>(type)) {
// TODO: On platforms without ObjC interop, we can do direct access to
// class metadata.
return false;
}
// Prespecialized struct/enum metadata gets no dedicated accessor yet and so
// cannot do the work of registering the generic arguments which are classes
// with the ObjC runtime. Concretely, the following cannot be prespecialized
// yet:
// Struct<Klass<Int>>
// Enum<Klass<Int>>
return isSpecializedNominalTypeMetadataStaticallyAddressable(
IGM, type, canonicality, NotForUseOnlyFromAccessor);
}
/// Is there a known address for canonical specialized metadata? The metadata
/// there may need initialization before it is complete.
bool irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable(
IRGenModule &IGM, CanType type) {
if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) {
// The address of the complete metadata is the address of the abstract
// metadata.
return true;
}
if (isa<ExistentialType>(type))
return false;
auto *nominal = type->getAnyNominal();
if (nominal && nominal->isGenericContext()) {
// Prespecialized class metadata gets a dedicated accessor which can do
// the work of registering the class and its arguments with the ObjC
// runtime.
// Concretely, Clazz<Klass<Int>> can be prespecialized.
return isSpecializedNominalTypeMetadataStaticallyAddressable(
IGM, type, CanonicalSpecializedMetadata,
ForUseOnlyFromAccessor);
}
return false;
}
bool irgen::isNoncanonicalCompleteTypeMetadataStaticallyAddressable(
IRGenModule &IGM, CanType type) {
// If the canonical metadata record can be statically addressed, then there
// should be no visible non-canonical metadata record to address.
if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type)) {
return false;
}
if (isa<BoundGenericStructType>(type) || isa<BoundGenericEnumType>(type)) {
// Imported type metadata always requires an accessor.
if (isa<ClangModuleUnit>(type->getAnyNominal()->getModuleScopeContext()))
return false;
return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IGM, type, NoncanonicalSpecializedMetadata);
}
return false;
}
/// Is complete metadata for the given type available at a fixed address?
bool irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable(
IRGenModule &IGM, CanType type) {
assert(!type->hasArchetype());
// Value type metadata only requires dynamic initialization on first
// access if it contains a resilient type.
if (isa<StructType>(type) || isa<EnumType>(type)) {
auto nominalType = cast<NominalType>(type);
auto *nominalDecl = nominalType->getDecl();
// Imported type metadata always requires an accessor.
if (isa<ClangModuleUnit>(nominalDecl->getModuleScopeContext()))
return false;
if (nominalDecl->isGenericContext())
return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IGM, type, CanonicalSpecializedMetadata);
auto expansion = ResilienceExpansion::Maximal;
return IGM.getTypeInfoForUnlowered(type).isFixedSize(expansion);
}
// The empty tuple type has a singleton metadata.
if (auto tuple = dyn_cast<TupleType>(type))
return tuple->getNumElements() == 0;
// Any and AnyObject have singleton metadata.
if (type->isAny() || type->isAnyObject())
return true;
// The builtin types generally don't require metadata, but some of them
// have nodes in the runtime anyway.
if (isa<BuiltinType>(type))
return true;
// SIL box types are artificial, but for the purposes of dynamic layout,
// we use the NativeObject metadata.
if (isa<SILBoxType>(type))
return true;
if (isa<BoundGenericStructType>(type) || isa<BoundGenericEnumType>(type)) {
return isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
IGM, type, CanonicalSpecializedMetadata);
}
return false;
}
/// Should requests for the given type's metadata be cached?
bool irgen::shouldCacheTypeMetadataAccess(IRGenModule &IGM, CanType type) {
// DynamicSelfType is actually local.
if (type->hasDynamicSelfType())
return false;
// Nongeneric, nonresilient classes with known Swift metadata need to be
// realized with the Objective-C runtime, but that only requires a single
// runtime call that already has a fast path exit for already-realized
// classes, so we don't need to put up another layer of caching in front.
//
// TODO: On platforms without ObjC interop, we can do direct access to
// Swift metadata without a runtime call at all.
if (auto classDecl = type.getClassOrBoundGenericClass()) {
if (!hasKnownSwiftMetadata(IGM, classDecl))
return true;
if (classDecl->isGenericContext() &&
isSpecializedNominalTypeMetadataStaticallyAddressable(
IGM, type, CanonicalSpecializedMetadata,
ForUseOnlyFromAccessor))
return false;
auto strategy = IGM.getClassMetadataStrategy(classDecl);
return strategy != ClassMetadataStrategy::Fixed;
}
// Trivially accessible metadata does not need a cache.
if (isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type))
return false;
if (isNoncanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type))
return false;
return true;
}
/// Should requests for the given type's metadata go through an accessor?
static bool shouldTypeMetadataAccessUseAccessor(IRGenModule &IGM, CanType type){
// Anything that requires caching should go through an accessor to outline
// the cache check.
if (shouldCacheTypeMetadataAccess(IGM, type))
return true;
// Fixed-metadata classes don't require caching, but we still want to go
// through the accessor to outline the ObjC realization.
// TODO: On non-Apple platforms, fixed classes should not need any
// initialization so should be directly addressable.
if (isa<ClassType>(type)) {
return true;
}
return false;
}
/// Return the standard access strategy for getting a non-dependent
/// type metadata object.
MetadataAccessStrategy irgen::getTypeMetadataAccessStrategy(CanType type) {
// We should not be emitting accessors for partially-substituted
// generic types.
assert(!type->hasArchetype());
// Non-generic structs, enums, and classes are special cases.
//
// Note that while protocol types don't have a metadata pattern,
// we still require an accessor since we actually want to get
// the metadata for the existential type.
//
// This needs to kept in sync with hasRequiredTypeMetadataAccessPattern.
auto nominal = type->getAnyNominal();
if (nominal && !isa<ProtocolDecl>(nominal)) {
// Metadata accessors for fully-substituted generic types are
// emitted with shared linkage.
if (nominal->isGenericContext() && !nominal->isObjC()) {
if (type->isSpecialized())
return MetadataAccessStrategy::NonUniqueAccessor;
assert(type->hasUnboundGenericType());
}
if (type->isForeignReferenceType())
return MetadataAccessStrategy::PublicUniqueAccessor;
if (requiresForeignTypeMetadata(nominal))
return MetadataAccessStrategy::ForeignAccessor;
// If the type doesn't guarantee that it has an access function,
// we might have to use a non-unique accessor.
// Everything else requires accessors.
switch (getDeclLinkage(nominal)) {
case FormalLinkage::PublicUnique:
return MetadataAccessStrategy::PublicUniqueAccessor;
case FormalLinkage::PackageUnique:
return MetadataAccessStrategy::PackageUniqueAccessor;
case FormalLinkage::HiddenUnique:
return MetadataAccessStrategy::HiddenUniqueAccessor;
case FormalLinkage::Private:
return MetadataAccessStrategy::PrivateAccessor;
case FormalLinkage::PublicNonUnique:
return MetadataAccessStrategy::NonUniqueAccessor;
}
llvm_unreachable("bad formal linkage");
}
// Everything else requires a shared accessor function.
return MetadataAccessStrategy::NonUniqueAccessor;
}
static llvm::Constant *emitEmptyTupleTypeMetadataRef(IRGenModule &IGM) {
llvm::Constant *fullMetadata = IGM.getEmptyTupleMetadata();
llvm::Constant *indices[] = {
llvm::ConstantInt::get(IGM.Int32Ty, 0),
llvm::ConstantInt::get(IGM.Int32Ty, 1)
};
return llvm::ConstantExpr::getInBoundsGetElementPtr(
IGM.FullExistentialTypeMetadataStructTy, fullMetadata, indices);
}
/// Emit metadata for a tuple type containing one or more pack expansions, eg
/// (T, repeat each U, v: V, repeat each W).
static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
CanTupleType type,
DynamicMetadataRequest request) {
CanPackType packType = type.getInducedPackType();
// Begin by computing the number of elements in the tuple type.
auto *shapeExpression = IGF.emitPackShapeExpression(packType);
llvm::BasicBlock *trueBB = nullptr, *falseBB = nullptr, *restBB = nullptr;
llvm::BasicBlock *unwrappedBB = nullptr;
llvm::Value *unwrapped = nullptr;
// A tuple type containing zero or one non-pack-expansions might contain
// exactly one element after substitution, in which case the tuple
// "vanishes" and gets unwrapped. This behavior is implemented in both
// compile-time type substitution, and runtime type metadata instantiation,
// ensuring consistent behavior.
//
// FIXME: Inconsistent behavior with one-element labeled tuples.
if (type->getNumScalarElements() <= 1) {
ConditionalDominanceScope scope(IGF);
// Test if the runtime length of the pack type is exactly 1.
auto *one = llvm::ConstantInt::get(IGF.IGM.SizeTy, 1);
auto *isOne = IGF.Builder.CreateICmpEQ(shapeExpression, one);
trueBB = IGF.createBasicBlock("vanishing-tuple");
falseBB = IGF.createBasicBlock("actual-tuple");
IGF.Builder.CreateCondBr(isOne, trueBB, falseBB);
IGF.Builder.emitBlock(trueBB);
// If the length is 1, directly emit the metadata for the first pack element.
ArrayRef<ProtocolConformanceRef> conformances;
llvm::SmallVector<llvm::Value *, 2> wtables;
auto *index = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
auto *value = emitTypeMetadataPackElementRef(
IGF, packType, conformances, index, request, wtables);
// FIXME: Should emitTypeMetadataPackElementRef() preserve the dynamic state?
auto response = MetadataResponse::forBounded(
value, request.getStaticLowerBoundOnResponseState());
response.ensureDynamicState(IGF);
unwrapped = response.combine(IGF);
unwrappedBB = IGF.Builder.GetInsertBlock();
assert(wtables.empty());
restBB = IGF.createBasicBlock("tuple-rest");
IGF.Builder.CreateBr(restBB);
IGF.Builder.emitBlock(falseBB);
}
llvm::CallInst *call = nullptr;
{
ConditionalDominanceScope scope(IGF);
std::optional<StackAddress> labelString =
emitDynamicTupleTypeLabels(IGF, type, packType, shapeExpression);
// Otherwise, we know that either statically or dynamically, we have more than
// one element. Emit the pack.
llvm::Value *shape;
StackAddress addr;
std::tie(addr, shape) =
emitTypeMetadataPack(IGF, packType, MetadataState::Abstract);
auto *pointerToFirst = IGF.Builder.CreatePointerCast(
addr.getAddressPointer(), IGF.IGM.TypeMetadataPtrPtrTy);
auto *flags = shapeExpression;
if (labelString) {
flags = IGF.Builder.CreateOr(flags, llvm::ConstantInt::get(IGF.IGM.SizeTy,
TupleTypeFlags().withNonConstantLabels(true).getIntValue()));
}
// Call swift_getTupleMetadata().
llvm::Value *args[] = {
request.get(IGF),
flags,
pointerToFirst,
(labelString
? labelString->getAddress().getAddress()
: llvm::ConstantPointerNull::get(IGF.IGM.Int8PtrTy)),
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
};
call = IGF.Builder.CreateCall(
IGF.IGM.getGetTupleMetadataFunctionPointer(), args);
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();
cleanupTypeMetadataPack(IGF, addr, shape);
if (labelString)
IGF.emitDeallocateDynamicAlloca(*labelString);
}
// Control flow join with the one-element case.
llvm::Value *result = nullptr;
if (unwrapped != nullptr) {
IGF.Builder.CreateBr(restBB);
IGF.Builder.emitBlock(restBB);
auto *phi = IGF.Builder.CreatePHI(IGF.IGM.TypeMetadataResponseTy, 2);
phi->addIncoming(unwrapped, unwrappedBB);
phi->addIncoming(call, call->getParent());
result = phi;
} else {
result = call;
}
return MetadataResponse::handle(IGF, request, result);
}
static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
CanTupleType type,
DynamicMetadataRequest request) {
if (type->containsPackExpansionType())
return emitDynamicTupleTypeMetadataRef(IGF, type, request);
auto getElementMetadata = [&](CanType type) {
// Just request the elements to be abstract so that we can always build
// the metadata.
// TODO: if we have a collector, or if this is a blocking request, maybe
// we should build a stronger request?
return IGF.emitTypeMetadataRef(type, MetadataState::Abstract).getMetadata();
};
switch (type->getNumElements()) {
case 0:
return MetadataResponse::forComplete(
emitEmptyTupleTypeMetadataRef(IGF.IGM));
case 1:
// For metadata purposes, we consider a singleton tuple to be
// isomorphic to its element type. ???
return IGF.emitTypeMetadataRef(type.getElementType(0), request);
case 2: {
auto elt0Metadata = getElementMetadata(type.getElementType(0));
auto elt1Metadata = getElementMetadata(type.getElementType(1));
llvm::Value *args[] = {
request.get(IGF),
elt0Metadata, elt1Metadata,
getTupleLabelsString(IGF.IGM, type),
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
};
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetTupleMetadata2FunctionPointer(), args);
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();
return MetadataResponse::handle(IGF, request, call);
}
case 3: {
auto elt0Metadata = getElementMetadata(type.getElementType(0));
auto elt1Metadata = getElementMetadata(type.getElementType(1));
auto elt2Metadata = getElementMetadata(type.getElementType(2));
llvm::Value *args[] = {
request.get(IGF),
elt0Metadata, elt1Metadata, elt2Metadata,
getTupleLabelsString(IGF.IGM, type),
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
};
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetTupleMetadata3FunctionPointer(), args);
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();
return MetadataResponse::handle(IGF, request, call);
}
default:
return emitDynamicTupleTypeMetadataRef(IGF, type, request);
}
}
static Address createGenericArgumentsArray(IRGenFunction &IGF,
ArrayRef<llvm::Value *> args) {
// Allocate an array to pass the arguments.
auto argsBufferTy = llvm::ArrayType::get(IGF.IGM.Int8PtrTy, args.size());
auto argsBuffer =
IGF.createAlloca(argsBufferTy, IGF.IGM.getPointerAlignment());
// Mark the beginning of the array lifetime.
IGF.Builder.CreateLifetimeStart(argsBuffer,
IGF.IGM.getPointerSize() * args.size());
// Fill in the buffer.
for (unsigned i : indices(args)) {
Address elt = IGF.Builder.CreateStructGEP(argsBuffer, i,
IGF.IGM.getPointerSize() * i);
auto *arg = IGF.Builder.CreateBitOrPointerCast(args[i], IGF.IGM.Int8PtrTy);
IGF.Builder.CreateStore(arg, elt);
}
return argsBuffer;
}
static void destroyGenericArgumentsArray(IRGenFunction &IGF,
Address argsBuffer,
ArrayRef<llvm::Value *> args) {
IGF.Builder.CreateLifetimeEnd(argsBuffer,
IGF.IGM.getPointerSize() * args.size());
}
static llvm::Value *getFunctionParameterRef(IRGenFunction &IGF,
AnyFunctionType::CanParam param) {
auto type = param.getPlainType()->getCanonicalType();
return IGF.emitAbstractTypeMetadataRef(type);
}
/// Mapping type-level parameter flags to ABI parameter flags.
ParameterFlags irgen::getABIParameterFlags(ParameterTypeFlags flags) {
return ParameterFlags()
.withOwnership(asParameterOwnership(flags.getValueOwnership()))
.withVariadic(flags.isVariadic())
.withAutoClosure(flags.isAutoClosure())
.withNoDerivative(flags.isNoDerivative())
.withIsolated(flags.isIsolated())
.withTransferring(flags.isTransferring());
}
static std::pair<FunctionTypeFlags, ExtendedFunctionTypeFlags>
getFunctionTypeFlags(CanFunctionType type) {
bool hasParameterFlags = false;
for (auto param : type.getParams()) {
if (!getABIParameterFlags(param.getParameterFlags()).isNone()) {
hasParameterFlags = true;
break;
}
}
// Map the convention to a runtime metadata value.
FunctionMetadataConvention metadataConvention;
bool isEscaping = false;
switch (type->getRepresentation()) {
case FunctionTypeRepresentation::Swift:
metadataConvention = FunctionMetadataConvention::Swift;
isEscaping = !type->isNoEscape();
break;
case FunctionTypeRepresentation::Thin:
metadataConvention = FunctionMetadataConvention::Thin;
break;
case FunctionTypeRepresentation::Block:
metadataConvention = FunctionMetadataConvention::Block;
break;
case FunctionTypeRepresentation::CFunctionPointer:
metadataConvention = FunctionMetadataConvention::CFunctionPointer;
break;
}
// Compute the set of suppressed protocols.
InvertibleProtocolSet InvertedProtocols;
for (auto invertibleKind : InvertibleProtocolSet::allKnown()) {
switch (invertibleKind) {
case InvertibleProtocolKind::Copyable: {
// If the function type is noncopyable, note that in the suppressed
// protocols.
auto proto =
type->getASTContext().getProtocol(KnownProtocolKind::Copyable);
if (proto &&
proto->getParentModule()->lookupConformance(type, proto).isInvalid())
InvertedProtocols.insert(invertibleKind);
break;
}
case InvertibleProtocolKind::Escapable:
// We intentionally do not record the "escapable" bit here, because it's
// already in the normal function type flags. The runtime will
// introduce it as necessary.
break;
}
}
auto isolation = type->getIsolation();
auto extFlags = ExtendedFunctionTypeFlags()
.withTypedThrows(!type->getThrownError().isNull())
.withTransferringResult(type->hasTransferringResult())
.withInvertedProtocols(InvertedProtocols);
if (isolation.isErased())
extFlags = extFlags.withIsolatedAny();
auto flags = FunctionTypeFlags()
.withConvention(metadataConvention)
.withAsync(type->isAsync())
.withSendable(type->isSendable())
.withThrows(type->isThrowing())
.withParameterFlags(hasParameterFlags)
.withEscaping(isEscaping)
.withDifferentiable(type->isDifferentiable())
.withGlobalActor(isolation.isGlobalActor())
.withExtendedFlags(extFlags.getIntValue() != 0);
return std::make_pair(flags, extFlags);
}
namespace {
struct FunctionTypeMetadataParamInfo {
StackAddress parameters;
StackAddress paramFlags;
unsigned numParams;
};
}
static FunctionTypeMetadataParamInfo
emitFunctionTypeMetadataParams(IRGenFunction &IGF,
AnyFunctionType::CanParamArrayRef params,
FunctionTypeFlags flags,
DynamicMetadataRequest request,
SmallVectorImpl<llvm::Value *> &arguments) {
FunctionTypeMetadataParamInfo info;
info.numParams = params.size();
ConstantInitBuilder paramFlags(IGF.IGM);
auto flagsArr = paramFlags.beginArray();
if (!params.empty()) {
auto arrayTy =
llvm::ArrayType::get(IGF.IGM.TypeMetadataPtrTy, info.numParams);
info.parameters = StackAddress(IGF.createAlloca(
arrayTy, IGF.IGM.getTypeMetadataAlignment(), "function-parameters"));
IGF.Builder.CreateLifetimeStart(info.parameters.getAddress(),
IGF.IGM.getPointerSize() * info.numParams);
for (unsigned i : indices(params)) {
auto param = params[i];
auto paramFlags = getABIParameterFlags(param.getParameterFlags());
auto argPtr = IGF.Builder.CreateStructGEP(info.parameters.getAddress(), i,
IGF.IGM.getPointerSize());
auto *typeRef = getFunctionParameterRef(IGF, param);
IGF.Builder.CreateStore(typeRef, argPtr);
if (i == 0)
arguments.push_back(argPtr.getAddress());
flagsArr.addInt32(paramFlags.getIntValue());
}
} else {
auto parametersPtr =
llvm::ConstantPointerNull::get(
IGF.IGM.TypeMetadataPtrTy->getPointerTo());
arguments.push_back(parametersPtr);
}
auto *Int32Ptr = IGF.IGM.Int32Ty->getPointerTo();
if (flags.hasParameterFlags()) {
auto *flagsVar = flagsArr.finishAndCreateGlobal(
"parameter-flags", IGF.IGM.getPointerAlignment(),
/* constant */ true);
arguments.push_back(IGF.Builder.CreateBitCast(flagsVar, Int32Ptr));
} else {
flagsArr.abandon();
arguments.push_back(llvm::ConstantPointerNull::get(Int32Ptr));
}
return info;
}
static FunctionTypeMetadataParamInfo
emitDynamicFunctionTypeMetadataParams(IRGenFunction &IGF,
AnyFunctionType::CanParamArrayRef params,
FunctionTypeFlags flags,
CanPackType packType,
DynamicMetadataRequest request,
SmallVectorImpl<llvm::Value *> &arguments) {
assert(!params.empty());
FunctionTypeMetadataParamInfo info;
llvm::Value *shape;
std::tie(info.parameters, shape) = emitTypeMetadataPack(
IGF, packType, MetadataState::Abstract);
arguments.push_back(info.parameters.getAddress().getAddress());
if (flags.hasParameterFlags()) {
info.paramFlags = emitDynamicFunctionParameterFlags(
IGF, params, packType, shape);
arguments.push_back(info.paramFlags.getAddress().getAddress());
} else {
arguments.push_back(llvm::ConstantPointerNull::get(
IGF.IGM.Int32Ty->getPointerTo()));
}
return info;
}
static void cleanupFunctionTypeMetadataParams(IRGenFunction &IGF,
FunctionTypeMetadataParamInfo info) {
if (info.parameters.isValid()) {
if (info.parameters.getExtraInfo()) {
IGF.emitDeallocateDynamicAlloca(info.parameters);
} else {
IGF.Builder.CreateLifetimeEnd(info.parameters.getAddress(),
IGF.IGM.getPointerSize() * info.numParams);
}
}
}
static CanPackType getInducedPackType(AnyFunctionType::CanParamArrayRef params,
ASTContext &ctx) {
SmallVector<CanType, 2> elts;
for (auto param : params)
elts.push_back(param.getPlainType());
return CanPackType::get(ctx, elts);
}
static MetadataResponse emitFunctionTypeMetadataRef(IRGenFunction &IGF,
CanFunctionType type,
DynamicMetadataRequest request) {
auto result =
IGF.emitAbstractTypeMetadataRef(type->getResult()->getCanonicalType());
auto params = type.getParams();
bool hasPackExpansion = type->containsPackExpansionParam();
FunctionTypeFlags flags;
ExtendedFunctionTypeFlags extFlags;
std::tie(flags, extFlags) = getFunctionTypeFlags(type);
llvm::Value *flagsVal = nullptr;
llvm::Value *shapeExpression = nullptr;
CanPackType packType;
if (!hasPackExpansion) {
flags = flags.withNumParameters(params.size());
flagsVal = llvm::ConstantInt::get(IGF.IGM.SizeTy,
flags.getIntValue());
} else {
packType = getInducedPackType(type.getParams(), type->getASTContext());
auto *shapeExpression = IGF.emitPackShapeExpression(packType);
flagsVal = llvm::ConstantInt::get(IGF.IGM.SizeTy,
flags.getIntValue());
flagsVal = IGF.Builder.CreateOr(flagsVal, shapeExpression);
}
auto constructSimpleCall =
[&](llvm::SmallVectorImpl<llvm::Value *> &arguments)
-> FunctionPointer {
assert(!flags.hasParameterFlags());
assert(!shapeExpression);
arguments.push_back(flagsVal);
for (auto param : params) {
arguments.push_back(getFunctionParameterRef(IGF, param));
}
arguments.push_back(result);
switch (params.size()) {
case 0:
return IGF.IGM.getGetFunctionMetadata0FunctionPointer();
case 1:
return IGF.IGM.getGetFunctionMetadata1FunctionPointer();
case 2:
return IGF.IGM.getGetFunctionMetadata2FunctionPointer();
case 3:
return IGF.IGM.getGetFunctionMetadata3FunctionPointer();
default:
llvm_unreachable("supports only 1/2/3 parameter functions");
}
};
switch (params.size()) {
case 0:
case 1:
case 2:
case 3: {
if (!flags.hasParameterFlags() && !type->isDifferentiable() &&
!type->getGlobalActor() && !hasPackExpansion &&
!flags.hasExtendedFlags()) {
llvm::SmallVector<llvm::Value *, 8> arguments;
auto metadataFn = constructSimpleCall(arguments);
auto *call = IGF.Builder.CreateCall(metadataFn, arguments);
call->setDoesNotThrow();
return MetadataResponse::forComplete(call);
}
// If function type has parameter flags or is differentiable or has a
// global actor, emit the most general function to retrieve them.
LLVM_FALLTHROUGH;
}
default:
assert((!params.empty() || type->isDifferentiable() ||
!type->getIsolation().isNonIsolated() ||
type->getThrownError()) &&
"0 parameter case should be specialized unless it is a "
"differentiable function or has a global actor");
llvm::SmallVector<llvm::Value *, 8> arguments;
arguments.push_back(flagsVal);
llvm::Value *diffKindVal = nullptr;
{
FunctionMetadataDifferentiabilityKind metadataDifferentiabilityKind;
switch (type->getDifferentiabilityKind()) {
case DifferentiabilityKind::NonDifferentiable:
metadataDifferentiabilityKind =
FunctionMetadataDifferentiabilityKind::NonDifferentiable;
break;
case DifferentiabilityKind::Normal:
metadataDifferentiabilityKind =
FunctionMetadataDifferentiabilityKind::Normal;
break;
case DifferentiabilityKind::Linear:
metadataDifferentiabilityKind =
FunctionMetadataDifferentiabilityKind::Linear;
break;
case DifferentiabilityKind::Forward:
metadataDifferentiabilityKind =
FunctionMetadataDifferentiabilityKind::Forward;
break;
case DifferentiabilityKind::Reverse:
metadataDifferentiabilityKind =
FunctionMetadataDifferentiabilityKind::Reverse;
break;
}
if (type->isDifferentiable()) {
assert(metadataDifferentiabilityKind.isDifferentiable());
diffKindVal = llvm::ConstantInt::get(
IGF.IGM.SizeTy, metadataDifferentiabilityKind.getIntValue());
} else if (type->getGlobalActor() || flags.hasExtendedFlags()) {
diffKindVal = llvm::ConstantInt::get(
IGF.IGM.SizeTy,
FunctionMetadataDifferentiabilityKind::NonDifferentiable);
}
}
if (diffKindVal) {
arguments.push_back(diffKindVal);
}
FunctionTypeMetadataParamInfo info;
if (!hasPackExpansion) {
assert(!shapeExpression);
info = emitFunctionTypeMetadataParams(IGF, params, flags, request,
arguments);
} else {
info = emitDynamicFunctionTypeMetadataParams(IGF, params, flags, packType,
request, arguments);
}
arguments.push_back(result);
if (Type globalActor = type->getGlobalActor()) {
arguments.push_back(
IGF.emitAbstractTypeMetadataRef(globalActor->getCanonicalType()));
} else if (flags.hasExtendedFlags()) {
arguments.push_back(llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy));
}
if (flags.hasExtendedFlags()) {
auto extFlagsVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty,
extFlags.getIntValue());
arguments.push_back(extFlagsVal);
}
if (Type thrownError = type->getThrownError()) {
arguments.push_back(
IGF.emitAbstractTypeMetadataRef(thrownError->getCanonicalType()));
} else if (flags.hasExtendedFlags()) {
arguments.push_back(llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy));
}
auto getMetadataFn =
flags.hasExtendedFlags()
? IGF.IGM.getGetFunctionMetadataExtendedFunctionPointer()
: type->getGlobalActor()
? (IGF.IGM.isConcurrencyAvailable()
? IGF.IGM
.getGetFunctionMetadataGlobalActorFunctionPointer()
: IGF.IGM
.getGetFunctionMetadataGlobalActorBackDeployFunctionPointer())
: type->isDifferentiable()
? IGF.IGM.getGetFunctionMetadataDifferentiableFunctionPointer()
: IGF.IGM.getGetFunctionMetadataFunctionPointer();
auto call = IGF.Builder.CreateCall(getMetadataFn, arguments);
call->setDoesNotThrow();
cleanupFunctionTypeMetadataParams(IGF, info);
return MetadataResponse::forComplete(call);
}
}
namespace {
/// A visitor class for emitting a reference to a metatype object.
/// This implements a "raw" access, useful for implementing cache
/// functions or for implementing dependent accesses.
///
/// If the access requires runtime initialization, that initialization
/// must be dependency-ordered-before any load that carries a dependency
/// from the resulting metadata pointer.
class EmitTypeMetadataRef
: public CanTypeVisitor<EmitTypeMetadataRef, MetadataResponse,
DynamicMetadataRequest> {
private:
IRGenFunction &IGF;
public:
EmitTypeMetadataRef(IRGenFunction &IGF) : IGF(IGF) {}
MetadataResponse emitDirectMetadataRef(CanType type) {
return MetadataResponse::forComplete(IGF.IGM.getAddrOfTypeMetadata(type));
}
/// The given type should use opaque type info. We assume that
/// the runtime always provides an entry for such a type.
MetadataResponse visitBuiltinIntegerType(CanBuiltinIntegerType type,
DynamicMetadataRequest request) {
// If the size isn't a power up two, round up to the next power of two
// and use the corresponding integer type.
auto &opaqueTI = cast<FixedTypeInfo>(IGF.IGM.getTypeInfoForLowered(type));
unsigned numBits = opaqueTI.getFixedSize().getValueInBits();
if (!llvm::isPowerOf2_32(numBits)) {
numBits = llvm::NextPowerOf2(numBits);
type = CanBuiltinIntegerType(
BuiltinIntegerType::get(numBits, IGF.IGM.Context));
}
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinIntegerLiteralType(CanBuiltinIntegerLiteralType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinNativeObjectType(CanBuiltinNativeObjectType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinBridgeObjectType(CanBuiltinBridgeObjectType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinUnsafeValueBufferType(CanBuiltinUnsafeValueBufferType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinRawPointerType(CanBuiltinRawPointerType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinRawUnsafeContinuationType(CanBuiltinRawUnsafeContinuationType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinJobType(CanBuiltinJobType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinExecutorType(CanBuiltinExecutorType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinPackIndexType(CanBuiltinPackIndexType type,
DynamicMetadataRequest request) {
llvm_unreachable("metadata unsupported for this builtin type");
}
MetadataResponse
visitBuiltinFloatType(CanBuiltinFloatType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse
visitBuiltinVectorType(CanBuiltinVectorType type,
DynamicMetadataRequest request) {
return emitDirectMetadataRef(type);
}
MetadataResponse visitNominalType(CanNominalType type,
DynamicMetadataRequest request) {
assert(!type->isExistentialType());
return emitNominalMetadataRef(IGF, type->getDecl(), type, request);
}
MetadataResponse visitBoundGenericType(CanBoundGenericType type,
DynamicMetadataRequest request) {
assert(!type->isExistentialType());
return emitNominalMetadataRef(IGF, type->getDecl(), type, request);
}
MetadataResponse visitPackType(CanPackType type,
DynamicMetadataRequest request) {
return emitTypeMetadataPackRef(IGF, type, request);
}
MetadataResponse visitSILPackType(CanSILPackType type,
DynamicMetadataRequest request) {
llvm_unreachable("cannot emit metadata for a SIL pack type");
}
MetadataResponse visitPackExpansionType(CanPackExpansionType type,
DynamicMetadataRequest request) {
llvm_unreachable("cannot emit metadata for a pack expansion by itself");
}
MetadataResponse visitPackElementType(CanPackElementType type,
DynamicMetadataRequest request) {
llvm_unreachable("cannot emit metadata for a pack element by itself");
}
MetadataResponse visitTupleType(CanTupleType type,
DynamicMetadataRequest request) {
if (auto cached = tryGetLocal(type, request))
return cached;
auto response = emitTupleTypeMetadataRef(IGF, type, request);
return setLocal(type, response);
}
MetadataResponse visitGenericFunctionType(CanGenericFunctionType type,
DynamicMetadataRequest request) {
IGF.unimplemented(SourceLoc(),
"metadata ref for generic function type");
return MetadataResponse::getUndef(IGF);
}
MetadataResponse visitFunctionType(CanFunctionType type,
DynamicMetadataRequest request) {
if (auto metatype = tryGetLocal(type, request))
return metatype;
auto response = emitFunctionTypeMetadataRef(IGF, type, request);
return setLocal(type, response);
}
MetadataResponse visitMetatypeType(CanMetatypeType type,
DynamicMetadataRequest request) {
// FIXME: We shouldn't accept a lowered metatype here, but we need to
// represent Optional<@objc_metatype T.Type> as an AST type for ABI
// reasons.
// assert(!type->hasRepresentation()
// && "should not be asking for a representation-specific metatype "
// "metadata");
if (auto metatype = tryGetLocal(type, request))
return metatype;
auto instMetadata =
IGF.emitAbstractTypeMetadataRef(type.getInstanceType());
auto fn = IGF.IGM.getGetMetatypeMetadataFunctionPointer();
auto call = IGF.Builder.CreateCall(fn, instMetadata);
call->setDoesNotThrow();
return setLocal(type, MetadataResponse::forComplete(call));
}
MetadataResponse
visitExistentialMetatypeType(CanExistentialMetatypeType type,
DynamicMetadataRequest request) {
if (auto metatype = tryGetLocal(type, request))
return metatype;
// Existential metatypes for extended existentials don't use
// ExistentialMetatypeMetadata.
if (usesExtendedExistentialMetadata(type)) {
auto metadata = emitExtendedExistentialTypeMetadata(type);
return setLocal(type, MetadataResponse::forComplete(metadata));
}
// Otherwise, emit the instance type metadata and wrap it in an
// ExistentialMetatypeMetadata.
auto instMetadata =
IGF.emitAbstractTypeMetadataRef(type.getExistentialInstanceType());
auto fn = IGF.IGM.getGetExistentialMetatypeMetadataFunctionPointer();
auto call = IGF.Builder.CreateCall(fn, instMetadata);
call->setDoesNotThrow();
return setLocal(type, MetadataResponse::forComplete(call));
}
MetadataResponse visitModuleType(CanModuleType type,
DynamicMetadataRequest request) {
IGF.unimplemented(SourceLoc(), "metadata ref for module type");
return MetadataResponse::getUndef(IGF);
}
MetadataResponse visitDynamicSelfType(CanDynamicSelfType type,
DynamicMetadataRequest request) {
return MetadataResponse::forComplete(IGF.getDynamicSelfMetadata());
}
MetadataResponse visitExistentialType(CanExistentialType type,
DynamicMetadataRequest request) {
if (auto metadata = tryGetLocal(type, request))
return metadata;
// These currently aren't wrapped in ExistentialType, but we
// can future-proof against them ending up in this path.
if (type->isAny() || type->isAnyObject())
return emitSingletonExistentialTypeMetadata(type);
auto metadata = emitExistentialTypeMetadata(type);
return setLocal(type, MetadataResponse::forComplete(metadata));
}
MetadataResponse emitSingletonExistentialTypeMetadata(CanType type) {
assert(type->isAny() || type->isAnyObject());
// Any and AnyObject have singleton metadata in the runtime.
llvm::Constant *singletonMetadata = nullptr;
if (type->isAny())
singletonMetadata = IGF.IGM.getAnyExistentialMetadata();
if (type->isAnyObject())
singletonMetadata = IGF.IGM.getAnyObjectExistentialMetadata();
llvm::Constant *indices[] = {
llvm::ConstantInt::get(IGF.IGM.Int32Ty, 0),
llvm::ConstantInt::get(IGF.IGM.Int32Ty, 1)
};
return MetadataResponse::forComplete(
llvm::ConstantExpr::getInBoundsGetElementPtr(
IGF.IGM.FullExistentialTypeMetadataStructTy, singletonMetadata, indices));
}
llvm::Value *emitExistentialTypeMetadata(CanExistentialType type) {
auto layout = type.getExistentialLayout();
if (layout.containsParameterized) {
return emitExtendedExistentialTypeMetadata(type);
}
SmallVector<ProtocolDecl *, 4> protocols;
for (auto proto : layout.getProtocols()) {
if (!proto->isMarkerProtocol())
protocols.push_back(proto);
}
// Collect references to the protocol descriptors.
auto descriptorArrayTy
= llvm::ArrayType::get(IGF.IGM.ProtocolDescriptorRefTy,
protocols.size());
Address descriptorArray = IGF.createAlloca(descriptorArrayTy,
IGF.IGM.getPointerAlignment(),
"protocols");
IGF.Builder.CreateLifetimeStart(descriptorArray,
IGF.IGM.getPointerSize() * protocols.size());
descriptorArray = IGF.Builder.CreateElementBitCast(
descriptorArray, IGF.IGM.ProtocolDescriptorRefTy);
unsigned index = 0;
for (auto *protoDecl : protocols) {
llvm::Value *ref = emitProtocolDescriptorRef(IGF, protoDecl);
Address slot = IGF.Builder.CreateConstArrayGEP(descriptorArray,
index, IGF.IGM.getPointerSize());
IGF.Builder.CreateStore(ref, slot);
++index;
}
// Note: ProtocolClassConstraint::Class is 0, ::Any is 1.
auto classConstraint =
llvm::ConstantInt::get(IGF.IGM.Int1Ty,
!layout.requiresClass());
llvm::Value *superclassConstraint =
llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy);
if (auto superclass = layout.explicitSuperclass) {
superclassConstraint = IGF.emitAbstractTypeMetadataRef(
CanType(superclass));
}
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetExistentialMetadataFunctionPointer(),
{classConstraint, superclassConstraint,
IGF.IGM.getSize(Size(protocols.size())),
descriptorArray.getAddress()});
call->setDoesNotThrow();
IGF.Builder.CreateLifetimeEnd(descriptorArray,
IGF.IGM.getPointerSize() * protocols.size());
return call;
}
llvm::Value *emitExtendedExistentialTypeMetadata(CanType type) {
assert(type.isAnyExistentialType());
auto shapeInfo = ExtendedExistentialTypeShapeInfo::get(type);
llvm::Constant *shape =
emitExtendedExistentialTypeShape(IGF.IGM, shapeInfo);
bool shapeIsUnique = shapeInfo.isUnique();
// Emit a reference to the extended existential shape,
// signed appropriately.
shape = llvm::ConstantExpr::getBitCast(shape, IGF.IGM.Int8PtrTy);
if (auto &schema = shapeIsUnique
? IGF.getOptions().PointerAuth.ExtendedExistentialTypeShape
: IGF.getOptions().PointerAuth.NonUniqueExtendedExistentialTypeShape) {
shape = IGF.IGM.getConstantSignedPointer(shape, schema,
PointerAuthEntity(),
/*address*/ nullptr);
}
// Emit the generalization arguments.
GenericArguments genericArgs;
Address argsBuffer;
llvm::Value *argsPointer;
if (shapeInfo.genSubs.empty()) {
argsPointer = llvm::UndefValue::get(IGF.IGM.Int8PtrPtrTy);
} else {
genericArgs.collect(IGF, shapeInfo.genSubs);
argsBuffer = createGenericArgumentsArray(IGF, genericArgs.Values);
argsPointer =
IGF.Builder.CreateBitCast(argsBuffer.getAddress(),
IGF.IGM.Int8PtrPtrTy);
}
// Call the metadata access function in the runtime.
auto call = IGF.Builder.CreateCall(
shapeIsUnique
? IGF.IGM
.getGetExtendedExistentialTypeMetadataUniqueFunctionPointer()
: IGF.IGM.getGetExtendedExistentialTypeMetadataFunctionPointer(),
{shape, argsPointer});
call->setDoesNotThrow();
// Destroy the generalization arguments array, if we made one.
if (!shapeInfo.genSubs.empty())
destroyGenericArgumentsArray(IGF, argsBuffer, genericArgs.Values);
return call;
}
MetadataResponse visitProtocolType(CanProtocolType type,
DynamicMetadataRequest request) {
assert(false && "constraint type should be wrapped in existential type");
CanExistentialType existential(
ExistentialType::get(type)->castTo<ExistentialType>());
if (auto metatype = tryGetLocal(existential, request))
return metatype;
auto metadata = emitExistentialTypeMetadata(existential);
return setLocal(type, MetadataResponse::forComplete(metadata));
}
MetadataResponse
visitProtocolCompositionType(CanProtocolCompositionType type,
DynamicMetadataRequest request) {
if (type->isAny() || type->isAnyObject())
return emitSingletonExistentialTypeMetadata(type);
assert(false && "constraint type should be wrapped in existential type");
CanExistentialType existential(
ExistentialType::get(type)->castTo<ExistentialType>());
if (auto metatype = tryGetLocal(existential, request))
return metatype;
auto metadata = emitExistentialTypeMetadata(existential);
return setLocal(type, MetadataResponse::forComplete(metadata));
}
MetadataResponse
visitParameterizedProtocolType(CanParameterizedProtocolType type,
DynamicMetadataRequest request) {
llvm_unreachable("constraint type should be wrapped in existential type");
}
MetadataResponse visitReferenceStorageType(CanReferenceStorageType type,
DynamicMetadataRequest request) {
llvm_unreachable("reference storage type should have been converted by "
"SILGen");
}
MetadataResponse visitSILFunctionType(CanSILFunctionType type,
DynamicMetadataRequest request) {
llvm_unreachable("should not be asking for metadata of a lowered SIL "
"function type--SILGen should have used the AST type");
}
MetadataResponse visitSILTokenType(CanSILTokenType type,
DynamicMetadataRequest request) {
llvm_unreachable("should not be asking for metadata of a SILToken type");
}
MetadataResponse
visitSILMoveOnlyWrappedType(CanSILMoveOnlyWrappedType type,
DynamicMetadataRequest request) {
llvm_unreachable("should not be asking for metadata of a move only type");
}
MetadataResponse visitArchetypeType(CanArchetypeType type,
DynamicMetadataRequest request) {
if (auto packArchetypeType = dyn_cast<PackArchetypeType>(type))
return emitPackArchetypeMetadataRef(IGF, packArchetypeType, request);
return emitArchetypeTypeMetadataRef(IGF, type, request);
}
MetadataResponse visitGenericTypeParamType(CanGenericTypeParamType type,
DynamicMetadataRequest request) {
llvm_unreachable("dependent type should have been substituted by Sema or SILGen");
}
MetadataResponse visitDependentMemberType(CanDependentMemberType type,
DynamicMetadataRequest request) {
llvm_unreachable("dependent type should have been substituted by Sema or SILGen");
}
MetadataResponse visitLValueType(CanLValueType type,
DynamicMetadataRequest request) {
llvm_unreachable("lvalue type should have been lowered by SILGen");
}
MetadataResponse visitInOutType(CanInOutType type,
DynamicMetadataRequest request) {
llvm_unreachable("inout type should have been lowered by SILGen");
}
MetadataResponse visitErrorType(CanErrorType type,
DynamicMetadataRequest request) {
llvm_unreachable("error type should not appear in IRGen");
}
// These types are artificial types used for internal purposes and
// should never appear in a metadata request.
#define INTERNAL_ONLY_TYPE(ID) \
MetadataResponse visit##ID##Type(Can##ID##Type type, \
DynamicMetadataRequest request) { \
llvm_unreachable("cannot ask for metadata of compiler-internal type"); \
}
INTERNAL_ONLY_TYPE(SILBlockStorage)
INTERNAL_ONLY_TYPE(BuiltinDefaultActorStorage)
INTERNAL_ONLY_TYPE(BuiltinNonDefaultDistributedActorStorage)
#undef INTERNAL_ONLY_TYPE
MetadataResponse visitSILBoxType(CanSILBoxType type,
DynamicMetadataRequest request) {
// The Builtin.NativeObject metadata can stand in for boxes.
return emitDirectMetadataRef(type->getASTContext().TheNativeObjectType);
}
/// Try to find the metatype in local data.
MetadataResponse tryGetLocal(CanType type, DynamicMetadataRequest request) {
return IGF.tryGetLocalTypeMetadata(type, request);
}
/// Set the metatype in local data.
MetadataResponse setLocal(CanType type, MetadataResponse response) {
IGF.setScopedLocalTypeMetadata(type, response);
return response;
}
};
} // end anonymous namespace
/// Emit a type metadata reference without using an accessor function.
static MetadataResponse emitDirectTypeMetadataRef(IRGenFunction &IGF,
CanType type,
DynamicMetadataRequest request) {
return EmitTypeMetadataRef(IGF).visit(type, request);
}
static bool isLoadFrom(llvm::Value *value, Address address) {
if (auto load = dyn_cast<llvm::LoadInst>(value)) {
return load->getOperand(0) == address.getAddress();
}
return false;
}
/// Emit the body of a cache accessor.
///
/// If cacheVariable is null, we perform the direct access every time.
/// This is used for metadata accessors that come about due to resilience,
/// where the direct access is completely trivial.
void irgen::emitCacheAccessFunction(IRGenModule &IGM, llvm::Function *accessor,
llvm::Constant *cacheVariable,
llvm::Type *cacheTy,
CacheStrategy cacheStrategy,
CacheEmitter getValue, bool isReadNone) {
assert((cacheStrategy == CacheStrategy::None) == (cacheVariable == nullptr));
accessor->setDoesNotThrow();
// Don't inline cache functions, since doing so has little impact on
// overall performance.
accessor->addFnAttr(llvm::Attribute::NoInline);
// Accessor functions don't need frame pointers.
IGM.setHasNoFramePointer(accessor);
IGM.setColocateMetadataSection(accessor);
// This function is logically 'readnone': the caller does not need
// to reason about any side effects or stores it might perform.
if (isReadNone)
accessor->setDoesNotAccessMemory();
IRGenFunction IGF(IGM, accessor);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);
auto parameters = IGF.collectParameters();
bool returnsResponse =
(accessor->getReturnType() == IGM.TypeMetadataResponseTy);
switch (cacheStrategy) {
// If there's no cache variable, just perform the direct access.
case CacheStrategy::None: {
auto response = getValue(IGF, parameters);
llvm::Value *ret;
if (returnsResponse) {
response.ensureDynamicState(IGF);
ret = response.combine(IGF);
} else {
assert(response.isStaticallyKnownComplete());
ret = response.getMetadata();
}
IGF.Builder.CreateRet(ret);
return;
}
// For in-place initialization, drill to the first element of the cache.
case CacheStrategy::SingletonInitialization:
cacheVariable =
llvm::ConstantExpr::getBitCast(cacheVariable,
IGM.TypeMetadataPtrTy->getPointerTo());
break;
case CacheStrategy::Lazy:
break;
}
llvm::Constant *null =
llvm::ConstantPointerNull::get(cast<llvm::PointerType>(cacheTy));
Address cache(cacheVariable, cacheTy, IGM.getPointerAlignment());
// Okay, first thing, check the cache variable.
//
// Conceptually, this needs to establish memory ordering with the
// store we do later in the function: if the metadata value is
// non-null, we must be able to see any stores performed by the
// initialization of the metadata. However, any attempt to read
// from the metadata will be address-dependent on the loaded
// metadata pointer, which is sufficient to provide adequate
// memory ordering guarantees on all the platforms we care about:
// ARM has special rules about address dependencies, and x86's
// memory ordering is strong enough to guarantee the visibility
// even without the address dependency.
//
// And we do not need to worry about the compiler because the
// address dependency naturally forces an order to the memory
// accesses.
//
// Therefore, we can perform a completely naked load here.
// FIXME: Technically should be "consume", but that introduces barriers in the
// current LLVM ARM backend.
auto load = IGF.Builder.CreateLoad(cache);
// Make this barrier explicit when building for TSan to avoid false positives.
if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread)
load->setOrdering(llvm::AtomicOrdering::Acquire);
// Compare the load result against null.
auto isNullBB = IGF.createBasicBlock("cacheIsNull");
auto contBB = IGF.createBasicBlock("cont");
llvm::Value *comparison = IGF.Builder.CreateICmpEQ(load, null);
IGF.Builder.CreateCondBr(comparison, isNullBB, contBB);
auto loadBB = IGF.Builder.GetInsertBlock();
// If the load yielded null, emit the type metadata.
IGF.Builder.emitBlock(isNullBB);
MetadataResponse response = getValue(IGF, parameters);
// Ensure that we have a dynamically-correct state value.
llvm::Constant *completedState = nullptr;
if (returnsResponse) {
completedState = MetadataResponse::getCompletedState(IGM);
response.ensureDynamicState(IGF);
}
auto directResult = response.getMetadata();
// Emit a branch around the caching code if we're working with responses
// and the fetched result is not complete. We can avoid doing this if
// the response is statically known to be complete, and we don't need to
// do it if this is an in-place initialization cache because the store
// is done within the runtime.
llvm::BasicBlock *completionCheckBB = nullptr;
llvm::Value *directState = nullptr;
if (cacheStrategy == CacheStrategy::SingletonInitialization) {
directState = response.getDynamicState();
completionCheckBB = IGF.Builder.GetInsertBlock();
} else {
if (returnsResponse &&
!response.isStaticallyKnownComplete()) {
completionCheckBB = IGF.Builder.GetInsertBlock();
directState = response.getDynamicState();
auto isCompleteBB = IGF.createBasicBlock("is_complete");
auto isComplete =
IGF.Builder.CreateICmpEQ(directState, completedState);
IGF.Builder.CreateCondBr(isComplete, isCompleteBB, contBB);
IGF.Builder.emitBlock(isCompleteBB);
}
// Store it back to the cache variable. This needs to be a store-release
// because it needs to propagate memory visibility to the other threads
// that can access the cache: the initializing stores might be visible
// to this thread, but they aren't transitively guaranteed to be visible
// to other threads unless this is a store-release.
//
// However, we can skip this if the value was actually loaded from the
// cache. This is a simple, if hacky, peephole that's useful for the
// code in emitOnceTypeMetadataAccessFunctionBody.
if (!isLoadFrom(directResult, cache)) {
IGF.Builder.CreateStore(directResult, cache)
->setAtomic(llvm::AtomicOrdering::Release);
}
}
IGF.Builder.CreateBr(contBB);
auto storeBB = IGF.Builder.GetInsertBlock();
// Emit the continuation block.
IGF.Builder.emitBlock(contBB);
// Add a phi for the metadata value.
auto phi = IGF.Builder.CreatePHI(null->getType(), 3);
phi->addIncoming(load, loadBB);
phi->addIncoming(directResult, storeBB);
// Add a phi for the metadata state if we're returning a response.
llvm::Value *stateToReturn = nullptr;
if (directState) {
if (storeBB != completionCheckBB)
phi->addIncoming(directResult, completionCheckBB);
auto completionStatePHI = IGF.Builder.CreatePHI(IGM.SizeTy, 3);
completionStatePHI->addIncoming(completedState, loadBB);
completionStatePHI->addIncoming(directState, completionCheckBB);
if (storeBB != completionCheckBB)
completionStatePHI->addIncoming(completedState, storeBB);
stateToReturn = completionStatePHI;
} else if (returnsResponse) {
stateToReturn = completedState;
}
// Build the return value.
llvm::Value *ret;
if (returnsResponse) {
ret = MetadataResponse(phi, stateToReturn, MetadataState::Abstract)
.combine(IGF);
} else {
ret = phi;
}
IGF.Builder.CreateRet(ret);
}
MetadataResponse
IRGenFunction::emitGenericTypeMetadataAccessFunctionCall(
llvm::Function *accessFunction,
ArrayRef<llvm::Value *> args,
DynamicMetadataRequest request) {
SmallVector<llvm::Value *, 8> callArgs;
// Add the metadata request argument.
callArgs.push_back(request.get(*this));
Address argsBuffer;
bool allocatedArgsBuffer = false;
if (args.size() > NumDirectGenericTypeMetadataAccessFunctionArgs) {
argsBuffer = createGenericArgumentsArray(*this, args);
allocatedArgsBuffer = true;
// Add the buffer to the call arguments.
callArgs.push_back(
Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrPtrTy));
} else {
callArgs.append(args.begin(), args.end());
}
auto call = Builder.CreateCall(accessFunction->getFunctionType(),
accessFunction, callArgs);
call->setDoesNotThrow();
call->setCallingConv(IGM.SwiftCC);
call->setMemoryEffects(allocatedArgsBuffer
? llvm::MemoryEffects::inaccessibleOrArgMemOnly()
: llvm::MemoryEffects::none());
// If we allocated a buffer for the arguments, end its lifetime.
if (allocatedArgsBuffer)
destroyGenericArgumentsArray(*this, argsBuffer, args);
return MetadataResponse::handle(*this, request, call);
}
MetadataResponse irgen::emitGenericTypeMetadataAccessFunction(
IRGenFunction &IGF, Explosion &params, NominalTypeDecl *nominal,
GenericArguments &genericArgs) {
auto &IGM = IGF.IGM;
llvm::Value *descriptor =
IGM.getAddrOfTypeContextDescriptor(nominal, RequireMetadata);
// Sign the descriptor.
auto schema = IGF.IGM.getOptions().PointerAuth.TypeDescriptorsAsArguments;
if (schema) {
auto authInfo = PointerAuthInfo::emit(
IGF, schema, nullptr,
PointerAuthEntity::Special::TypeDescriptorAsArgument);
descriptor = emitPointerAuthSign(IGF, descriptor, authInfo);
}
auto request = params.claimNext();
bool checkPrespecialized =
IGM.IRGen.metadataPrespecializationsForType(nominal).size() > 0;
auto numArguments = genericArgs.Types.size();
llvm::Value *result;
if (numArguments > NumDirectGenericTypeMetadataAccessFunctionArgs) {
// swift_getGenericMetadata's calling convention is already cleverly
// laid out to minimize the assembly language size of the thunk.
// The caller passed us an appropriate buffer with the arguments.
auto argsBuffer =
Address(params.claimNext(), IGM.Int8PtrTy, IGM.getPointerAlignment());
llvm::Value *arguments =
IGF.Builder.CreateBitCast(argsBuffer.getAddress(), IGM.Int8PtrTy);
// Make the call.
llvm::CallInst *call;
if (checkPrespecialized) {
call = IGF.Builder.CreateCall(
IGM.getGetCanonicalPrespecializedGenericMetadataFunctionPointer(),
{request, arguments, descriptor,
IGM.getAddrOfCanonicalPrespecializedGenericTypeCachingOnceToken(
nominal)});
} else {
call = IGF.Builder.CreateCall(IGM.getGetGenericMetadataFunctionPointer(),
{request, arguments, descriptor});
}
call->setDoesNotThrow();
call->setCallingConv(IGM.SwiftCC);
call->setOnlyReadsMemory();
result = call;
} else {
static_assert(NumDirectGenericTypeMetadataAccessFunctionArgs == 3,
"adjust this if you change "
"NumDirectGenericTypeMetadataAccessFunctionArgs");
// Factor out the buffer shuffling for metadata accessors that take their
// arguments directly, so that the accessor function itself only needs to
// materialize the nominal type descriptor and call this thunk.
auto generateThunkFn = [&IGM,
checkPrespecialized](IRGenFunction &subIGF) {
subIGF.CurFn->setOnlyReadsMemory();
subIGF.CurFn->setWillReturn();
subIGF.CurFn->setCallingConv(IGM.SwiftCC);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(subIGF, subIGF.CurFn);
IGM.setHasNoFramePointer(subIGF.CurFn);
auto params = subIGF.collectParameters();
auto request = params.claimNext();
auto arg0 = params.claimNext();
auto arg1 = params.claimNext();
auto arg2 = params.claimNext();
auto descriptor = params.claimNext();
llvm::Value *token = nullptr;
if (checkPrespecialized) {
token = params.claimNext();
}
// Allocate a buffer with enough storage for the arguments.
auto argsBufferTy =
llvm::ArrayType::get(IGM.Int8PtrTy,
NumDirectGenericTypeMetadataAccessFunctionArgs);
auto argsBuffer = subIGF.createAlloca(argsBufferTy,
IGM.getPointerAlignment(),
"generic.arguments");
subIGF.Builder.CreateLifetimeStart(argsBuffer,
IGM.getPointerSize() * NumDirectGenericTypeMetadataAccessFunctionArgs);
auto arg0Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy,
argsBuffer.getAddress(), 0, 0);
subIGF.Builder.CreateStore(arg0, arg0Buf, IGM.getPointerAlignment());
auto arg1Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy,
argsBuffer.getAddress(), 0, 1);
subIGF.Builder.CreateStore(arg1, arg1Buf, IGM.getPointerAlignment());
auto arg2Buf = subIGF.Builder.CreateConstInBoundsGEP2_32(argsBufferTy,
argsBuffer.getAddress(), 0, 2);
subIGF.Builder.CreateStore(arg2, arg2Buf, IGM.getPointerAlignment());
// Make the call.
auto argsAddr = subIGF.Builder.CreateBitCast(argsBuffer.getAddress(),
IGM.Int8PtrTy);
llvm::Value *result;
if (checkPrespecialized) {
result = subIGF.Builder.CreateCall(
IGM.getGetCanonicalPrespecializedGenericMetadataFunctionPointer(),
{request, argsAddr, descriptor, token});
} else {
result = subIGF.Builder.CreateCall(
IGM.getGetGenericMetadataFunctionPointer(),
{request, argsAddr, descriptor});
}
subIGF.Builder.CreateRet(result);
};
llvm::Function *thunkFn;
if (checkPrespecialized) {
thunkFn = cast<llvm::Function>(IGM.getOrCreateHelperFunction(
"__swift_instantiateCanonicalPrespecializedGenericMetadata",
IGM.TypeMetadataResponseTy,
{
IGM.SizeTy, // request
IGM.Int8PtrTy, // arg 0
IGM.Int8PtrTy, // arg 1
IGM.Int8PtrTy, // arg 2
IGM.TypeContextDescriptorPtrTy, // type context descriptor
IGM.OnceTy->getPointerTo() // token pointer
},
generateThunkFn,
/*noinline*/ true));
} else {
thunkFn = cast<llvm::Function>(IGM.getOrCreateHelperFunction(
"__swift_instantiateGenericMetadata", IGM.TypeMetadataResponseTy,
{
IGM.SizeTy, // request
IGM.Int8PtrTy, // arg 0
IGM.Int8PtrTy, // arg 1
IGM.Int8PtrTy, // arg 2
IGM.TypeContextDescriptorPtrTy // type context descriptor
},
generateThunkFn,
/*noinline*/ true));
}
IGM.setColocateMetadataSection(thunkFn);
// Call out to the helper.
auto getNextParam = [&]() -> llvm::Value * {
auto *param = params.claimNext();
if (param->getType()->isPointerTy())
return IGF.Builder.CreateBitCast(param, IGM.Int8PtrTy);
return IGF.Builder.CreateIntToPtr(param, IGM.Int8PtrTy);
};
auto arg0 = numArguments >= 1
? getNextParam()
: llvm::UndefValue::get(IGM.Int8PtrTy);
auto arg1 = numArguments >= 2
? getNextParam()
: llvm::UndefValue::get(IGM.Int8PtrTy);
auto arg2 = numArguments >= 3
? getNextParam()
: llvm::UndefValue::get(IGM.Int8PtrTy);
llvm::CallInst *call;
if (checkPrespecialized) {
auto *token =
IGM.getAddrOfCanonicalPrespecializedGenericTypeCachingOnceToken(
nominal);
call = IGF.Builder.CreateCall(
thunkFn->getFunctionType(), thunkFn,
{request, arg0, arg1, arg2, descriptor, token});
} else {
call = IGF.Builder.CreateCall(thunkFn->getFunctionType(), thunkFn,
{request, arg0, arg1, arg2, descriptor});
}
call->setDoesNotAccessMemory();
call->setDoesNotThrow();
call->setCallingConv(IGM.SwiftCC);
result = call;
}
return MetadataResponse::handle(IGF, DynamicMetadataRequest(request), result);
}
static void
emitIdempotentCanonicalSpecializedClassMetadataInitializationComponent(
IRGenFunction &IGF, CanType theType,
llvm::SmallSet<CanType, 16> &initializedTypes) {
if (initializedTypes.count(theType) > 0) {
return;
}
initializedTypes.insert(theType);
auto *classDecl = theType->getClassOrBoundGenericClass();
assert(classDecl);
if (classDecl->isGenericContext()) {
llvm::Function *accessor =
IGF.IGM.getAddrOfCanonicalSpecializedGenericTypeMetadataAccessFunction(
theType, NotForDefinition);
auto request = DynamicMetadataRequest(MetadataState::Complete);
IGF.emitGenericTypeMetadataAccessFunctionCall(accessor, {}, request);
} else {
llvm::Function *accessor =
IGF.IGM.getAddrOfTypeMetadataAccessFunction(theType, NotForDefinition);
auto request = DynamicMetadataRequest(MetadataState::Complete);
IGF.emitGenericTypeMetadataAccessFunctionCall(accessor, {}, request);
}
}
MetadataResponse
irgen::emitCanonicalSpecializedGenericTypeMetadataAccessFunction(
IRGenFunction &IGF, Explosion &params, CanType theType) {
assert(isa<ClassDecl>(theType->getAnyNominal()));
auto request = params.claimNext();
// The metadata request that is passed to a canonical specialized generic
// metadata accessor is ignored because complete metadata is always returned.
(void)request;
llvm::SmallSet<CanType, 16> initializedTypes;
auto *nominal = theType->getAnyNominal();
assert(nominal);
assert(isa<ClassDecl>(nominal));
assert(nominal->isGenericContext());
assert(!theType->hasUnboundGenericType());
auto requirements = GenericTypeRequirements(IGF.IGM, nominal);
auto substitutions =
theType->getContextSubstitutionMap(IGF.IGM.getSwiftModule(), nominal);
for (auto requirement : requirements.getRequirements()) {
if (requirement.isAnyWitnessTable()) {
continue;
}
assert(requirement.isMetadata()); // FIXME: packs and counts
auto parameter = requirement.getTypeParameter();
auto noncanonicalArgument = parameter.subst(substitutions);
auto argument = noncanonicalArgument->getCanonicalType();
if (auto *classDecl = argument->getClassOrBoundGenericClass()) {
emitIdempotentCanonicalSpecializedClassMetadataInitializationComponent(
IGF, argument, initializedTypes);
}
}
Type superclassType = theType->getSuperclass(/*useArchetypes=*/false);
if (superclassType) {
emitIdempotentCanonicalSpecializedClassMetadataInitializationComponent(
IGF, superclassType->getCanonicalType(), initializedTypes);
}
auto *uninitializedMetadata = IGF.IGM.getAddrOfTypeMetadata(theType);
initializedTypes.insert(theType);
auto *initializedMetadata =
emitIdempotentClassMetadataInitialization(IGF, uninitializedMetadata);
return MetadataResponse::forComplete(initializedMetadata);
}
/// Emit the body of a metadata accessor function for the given type.
///
/// This function is appropriate for ordinary situations where the
/// construction of the metadata value just involves calling idempotent
/// metadata-construction functions. It is not used for the in-place
/// initialization of non-generic nominal type metadata.
static MetadataResponse
emitDirectTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
DynamicMetadataRequest request,
CanType type) {
assert(!type->hasArchetype() &&
"cannot emit metadata accessor for context-dependent type");
// We only take this path for non-generic nominal types.
auto typeDecl = type->getAnyNominal();
if (!typeDecl)
return emitDirectTypeMetadataRef(IGF, type, request);
if (typeDecl->isGenericContext() &&
!(isa<ClassDecl>(typeDecl) &&
isa<ClangModuleUnit>(typeDecl->getModuleScopeContext()))) {
// This is a metadata accessor for a fully substituted generic type.
return emitDirectTypeMetadataRef(IGF, type, request);
}
// We should never be emitting a metadata accessor for resilient nominal
// types outside of their defining module. We'd only do that anyway for
// types that don't guarantee the existence of a non-unique access
// function, and that should never be true of a resilient type with
// external availability.
//
// (The type might still not have a statically-known layout. It just
// can't be resilient at the top level: we have to know its immediate
// members, or we can't even begin to approach the problem of emitting
// metadata for it.)
assert(!IGF.IGM.isResilient(typeDecl, ResilienceExpansion::Maximal));
// We should never be emitting a metadata accessor for foreign type
// metadata using this function.
assert(!requiresForeignTypeMetadata(typeDecl));
if (auto classDecl = dyn_cast<ClassDecl>(typeDecl)) {
// For known-Swift metadata, we can perform a direct reference with
// potentially idempotent initialization.
if (hasKnownSwiftMetadata(IGF.IGM, classDecl))
return emitDirectTypeMetadataRef(IGF, type, request);
// Classes that might not have Swift metadata use a different
// access pattern.
return MetadataResponse::forComplete(emitObjCMetadataRef(IGF, classDecl));
}
// We should not be doing more serious work along this path.
assert(isCanonicalCompleteTypeMetadataStaticallyAddressable(IGF.IGM, type));
// Okay, everything else is built from a Swift metadata object.
llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type);
return MetadataResponse::forComplete(metadata);
}
static llvm::Function *getAccessFunctionPrototype(IRGenModule &IGM,
CanType type,
ForDefinition_t forDefinition) {
assert(!type->hasArchetype());
// Type should be bound unless it's type erased.
assert(type.isTypeErasedGenericClassType()
? !isa<BoundGenericType>(type)
: !isa<UnboundGenericType>(type));
return IGM.getAddrOfTypeMetadataAccessFunction(type, forDefinition);
}
llvm::Function *
irgen::getOtherwiseDefinedTypeMetadataAccessFunction(IRGenModule &IGM,
CanType type) {
return getAccessFunctionPrototype(IGM, type, NotForDefinition);
}
/// Get or create an accessor function to the given non-dependent type.
llvm::Function *
irgen::createTypeMetadataAccessFunction(IRGenModule &IGM, CanType type,
CacheStrategy cacheStrategy,
MetadataAccessGenerator generator,
bool allowExistingDefinition) {
// Get the prototype.
auto accessor = getAccessFunctionPrototype(IGM, type, ForDefinition);
// If we're not supposed to define the accessor, or if we already
// have defined it, just return the pointer.
if (!accessor->empty()) {
assert(allowExistingDefinition &&
"repeat definition of access function!");
return accessor;
}
// Okay, define the accessor.
llvm::Constant *cacheVariable = nullptr;
llvm::Type *cacheTy = nullptr;
// If our preferred access method is to go via an accessor, it means
// there is some non-trivial computation that needs to be cached.
if (!shouldCacheTypeMetadataAccess(IGM, type)) {
cacheStrategy = CacheStrategy::None;
} else {
switch (cacheStrategy) {
// Nothing to do.
case CacheStrategy::None:
break;
// For lazy initialization, the cache variable is just a pointer.
case CacheStrategy::Lazy:
cacheVariable = IGM.getAddrOfTypeMetadataLazyCacheVariable(type);
cacheTy = IGM.TypeMetadataPtrTy;
break;
// For in-place initialization, drill down to the first element.
case CacheStrategy::SingletonInitialization:
cacheVariable = IGM.getAddrOfTypeMetadataSingletonInitializationCache(
type->getAnyNominal(), ForDefinition);
cacheTy = IGM.TypeMetadataPtrTy;
break;
}
if (IGM.getOptions().optimizeForSize())
accessor->addFnAttr(llvm::Attribute::NoInline);
}
emitCacheAccessFunction(IGM, accessor, cacheVariable, cacheTy, cacheStrategy,
[&](IRGenFunction &IGF, Explosion &params) {
auto request =
DynamicMetadataRequest(params.claimNext());
return generator(IGF, request, cacheVariable);
});
return accessor;
}
/// Emit a standard accessor function to the given non-dependent type.
llvm::Function *
irgen::createDirectTypeMetadataAccessFunction(IRGenModule &IGM, CanType type,
bool allowExistingDefinition) {
return createTypeMetadataAccessFunction(IGM, type, CacheStrategy::Lazy,
[&](IRGenFunction &IGF,
DynamicMetadataRequest request,
llvm::Constant *cacheVariable) {
// We should not be called with ForDefinition for nominal types
// that require in-place initialization.
return emitDirectTypeMetadataAccessFunctionBody(IGF, request, type);
}, allowExistingDefinition);
}
/// Get or create an accessor function to the given generic type.
llvm::Function *
irgen::getGenericTypeMetadataAccessFunction(IRGenModule &IGM,
NominalTypeDecl *nominal,
ForDefinition_t shouldDefine) {
assert(nominal->isGenericContext());
assert(!nominal->isTypeErasedGenericClass());
GenericArguments genericArgs;
genericArgs.collectTypes(IGM, nominal);
llvm::Function *accessor =
IGM.getAddrOfGenericTypeMetadataAccessFunction(
nominal, genericArgs.Types, shouldDefine);
if (shouldDefine)
IGM.setColocateMetadataSection(accessor);
// If we're not supposed to define the accessor, or if we already
// have defined it, just return the pointer.
if (!shouldDefine || !accessor->empty())
return accessor;
IGM.IRGen.noteUseOfMetadataAccessor(nominal);
return accessor;
}
static bool shouldAccessByMangledName(IRGenModule &IGM, CanType type) {
// Never access by mangled name if we've been asked not to.
if (IGM.getOptions().DisableConcreteTypeMetadataMangledNameAccessors)
return false;
// Do not access by mangled name if the runtime won't understand it.
if (mangledNameIsUnknownToDeployTarget(IGM, type))
return false;
// A nongeneric nominal type with nontrivial metadata has an accessor
// already we can just call.
if (auto nom = dyn_cast<NominalType>(type)) {
if (!isa<ProtocolDecl>(nom->getDecl())
&& (!nom->getDecl()->isGenericContext()
|| nom->getDecl()->getGenericSignature()->areAllParamsConcrete())) {
return false;
}
}
return true;
// The visitor below can be used to fine-tune a heuristic to decide whether
// demangling might be better for code size than open-coding an access. In
// my experiments on the Swift standard library and Apple SDK overlays,
// always demangling seemed to have the biggest code size benefit.
#if false
// Guess the number of calls and addresses we need to materialize a
// metadata record in code.
struct OpenCodedMetadataAccessWeightVisitor
: CanTypeVisitor<OpenCodedMetadataAccessWeightVisitor>
{
IRGenModule &IGM;
unsigned NumCalls = 0, NumAddresses = 0;
OpenCodedMetadataAccessWeightVisitor(IRGenModule &IGM)
: IGM(IGM) {}
void visitBoundGenericType(CanBoundGenericType bgt) {
// Need to materialize all the arguments, then call the metadata
// accessor.
//
// TODO: Also need to count the parent type's generic arguments.
for (auto arg : bgt->getGenericArgs()) {
visit(arg);
}
NumCalls += 1;
}
void visitNominalType(CanNominalType nom) {
// Some nominal types have trivially-referenceable metadata symbols,
// others may require accessors to trigger instantiation.
//
// TODO: Also need to count the parent type's generic arguments.
if (!shouldCacheTypeMetadataAccess(IGM, nom)) {
NumAddresses += 1;
} else {
NumCalls += 1;
}
}
void visitPackType(CanPackType tup) {
llvm_unreachable("Unimplemented!");
}
void visitPackExpansionType(CanPackExpansionType tup) {
llvm_unreachable("Unimplemented!");
}
void visitPackElementType(CanPackElementType tup) {
llvm_unreachable("Unimplemented!");
}
void visitTupleType(CanTupleType tup) {
// The empty tuple has trivial metadata.
if (tup->getNumElements() == 0) {
NumAddresses += 1;
return;
}
// Need to materialize the element types, then call the getTupleMetadata
// accessor.
for (auto elt : tup.getElementTypes()) {
visit(elt);
}
NumCalls += 1;
}
void visitAnyFunctionType(CanAnyFunctionType fun) {
// Need to materialize the arguments and return, then call the
// getFunctionMetadata accessor.
for (auto arg : fun.getParams()) {
visit(arg.getPlainType());
}
visit(fun.getResult());
NumCalls += 1;
}
void visitMetatypeType(CanMetatypeType meta) {
// Need to materialize the instance type, then call the
// getMetatypeMetadata accessor.
visit(meta.getInstanceType());
NumCalls += 1;
}
void visitProtocolType(CanProtocolType proto) {
// Need to reference the protocol descriptor, then call the
// getExistentialTypeMetadata accessor.
NumAddresses += 1;
NumCalls += 1;
}
void visitBuiltinType(CanBuiltinType b) {
// Builtins always have trivial metadata.
NumAddresses += 1;
}
void visitProtocolCompositionType(CanProtocolCompositionType comp) {
unsigned numMembers = comp->getMembers().size();
// The empty compositions Any and AnyObject are trivial.
if (numMembers == 0) {
NumAddresses += 1;
return;
}
// Need to materialize the base class, if any.
if (comp->getMembers().front()->getClassOrBoundGenericClass()) {
visit(CanType(comp->getMembers().front()));
numMembers -= 1;
}
// Need to reference the protocol descriptors for each protocol.
NumAddresses += numMembers;
// Finally, call the getExistentialTypeMetadata accessor.
NumCalls += 1;
}
void visitExistentialMetatypeType(CanExistentialMetatypeType meta) {
// Extended existential metatypes just emit a different shape
// and don't do any wrapping.
if (auto typeAndDepth = usesExtendedExistentialMetadata(meta)) {
return visit(typeAndDepth.first);
}
// The number of accesses turns out the same as the instance type,
// but instead of getExistentialTypeMetadata, we call
// getExistentialMetatypeMetadata
visit(meta.getInstanceType());
}
// Shouldn't emit metadata for other kinds of types.
void visitType(CanType t) {
llvm_unreachable("unhandled type?!");
}
};
OpenCodedMetadataAccessWeightVisitor visitor(IGM);
visitor.visit(type);
// If we need more than one accessor call, or the access requires too many
// arguments, the mangled name accessor is probably more compact.
return visitor.NumCalls > 1 || visitor.NumAddresses > 1;
#endif
}
static bool canIssueIncompleteMetadataRequests(IRGenModule &IGM) {
// We can only answer blocking complete metadata requests with the <=5.1
// runtime ABI entry points.
auto &context = IGM.getSwiftModule()->getASTContext();
auto deploymentAvailability =
AvailabilityContext::forDeploymentTarget(context);
return deploymentAvailability.isContainedIn(
context.getTypesInAbstractMetadataStateAvailability());
}
/// Emit a call to a type metadata accessor using a mangled name.
static MetadataResponse
emitMetadataAccessByMangledName(IRGenFunction &IGF, CanType type,
DynamicMetadataRequest request) {
assert(!isa<PackType>(type));
auto &IGM = IGF.IGM;
// We can only answer blocking complete metadata requests with the <=5.1
// runtime ABI entry points.
assert((request.isStaticallyBlockingComplete() ||
(request.isStaticallyAbstract() &&
canIssueIncompleteMetadataRequests(IGM))) &&
"can only form complete metadata by mangled name");
llvm::Constant *mangledString;
unsigned mangledStringSize;
std::tie(mangledString, mangledStringSize) =
IGM.getTypeRef(type, CanGenericSignature(), MangledTypeRefRole::Metadata);
// Android AArch64 reserves the top byte of the address for memory tagging
// since Android 11, so only use the bottom 23 bits to store this size
// and the 24th bit to signal that there is a size.
if (IGM.Triple.isAndroid() && IGM.Triple.getArch() == llvm::Triple::aarch64)
assert(mangledStringSize < 0x00800001u &&
"8MB of mangled name ought to be enough for Android AArch64");
else
assert(mangledStringSize < 0x80000000u &&
"2GB of mangled name ought to be enough for anyone");
// Get or create the cache variable if necessary.
auto cache = IGM.getAddrOfTypeMetadataDemanglingCacheVariable(type,
ConstantInit());
if (cast<llvm::GlobalVariable>(cache->stripPointerCasts())->isDeclaration()) {
ConstantInitBuilder builder(IGM);
auto structBuilder = builder.beginStruct();
// A "negative" 64-bit value in the cache indicates the uninitialized state.
// Which word has that bit in the {i32, i32} layout depends on endianness.
if (IGM.getModule()->getDataLayout().isBigEndian()) {
structBuilder.addInt32(-mangledStringSize);
structBuilder.addRelativeAddress(mangledString);
} else {
structBuilder.addRelativeAddress(mangledString);
structBuilder.addInt32(-mangledStringSize);
}
auto init = structBuilder.finishAndCreateFuture();
cache = IGM.getAddrOfTypeMetadataDemanglingCacheVariable(type, init);
}
// Get or create a shared helper function to do the instantiation.
auto instantiationFnName =
request.isStaticallyAbstract()
? "__swift_instantiateConcreteTypeFromMangledNameAbstract"
: "__swift_instantiateConcreteTypeFromMangledName";
auto generateInstantiationFn = [&IGM, request](IRGenFunction &subIGF) {
subIGF.CurFn->setOnlyReadsMemory();
subIGF.CurFn->setWillReturn();
IGM.setHasNoFramePointer(subIGF.CurFn);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(subIGF, subIGF.CurFn);
IGM.setColocateMetadataSection(subIGF.CurFn);
auto params = subIGF.collectParameters();
auto cache = params.claimNext();
// Load the existing cache value.
// Conceptually, this needs to establish memory ordering with the
// store we do later in the function: if the metadata value is
// non-null, we must be able to see any stores performed by the
// initialization of the metadata. However, any attempt to read
// from the metadata will be address-dependent on the loaded
// metadata pointer, which is sufficient to provide adequate
// memory ordering guarantees on all the platforms we care about:
// ARM has special rules about address dependencies, and x86's
// memory ordering is strong enough to guarantee the visibility
// even without the address dependency.
//
// And we do not need to worry about the compiler because the
// address dependency naturally forces an order to the memory
// accesses.
//
// Therefore, we can perform a completely naked load here.
// FIXME: Technically should be "consume", but that introduces barriers
// in the current LLVM ARM backend.
auto cacheWordAddr = subIGF.Builder.CreateBitCast(cache,
IGM.Int64Ty->getPointerTo());
auto load = subIGF.Builder.CreateLoad(
Address(cacheWordAddr, IGM.Int64Ty, Alignment(8)));
// Make this barrier explicit when building for TSan to avoid false positives.
if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread)
load->setOrdering(llvm::AtomicOrdering::Acquire);
else
load->setOrdering(llvm::AtomicOrdering::Monotonic);
// Compare the load result to see if it's negative.
auto isUnfilledBB = subIGF.createBasicBlock("");
auto contBB = subIGF.createBasicBlock("");
llvm::Value *comparison = subIGF.Builder.CreateICmpSLT(load,
llvm::ConstantInt::get(IGM.Int64Ty, 0));
// Check if the 24th bit is set on Android AArch64 and only instantiate the
// type metadata if it is, as otherwise it might be negative only because
// of the memory tag on Android.
if (IGM.Triple.isAndroid() &&
IGM.Triple.getArch() == llvm::Triple::aarch64) {
auto getBitAfterAndroidTag = subIGF.Builder.CreateAnd(
load, llvm::ConstantInt::get(IGM.Int64Ty, 0x0080000000000000));
auto checkNotAndroidTag = subIGF.Builder.CreateICmpNE(
getBitAfterAndroidTag, llvm::ConstantInt::get(IGM.Int64Ty, 0));
comparison = subIGF.Builder.CreateAnd(comparison, checkNotAndroidTag);
}
comparison = subIGF.Builder.CreateExpect(comparison,
llvm::ConstantInt::get(IGM.Int1Ty, 0));
subIGF.Builder.CreateCondBr(comparison, isUnfilledBB, contBB);
auto loadBB = subIGF.Builder.GetInsertBlock();
// If the load is negative, emit the call to instantiate the type
// metadata.
subIGF.Builder.SetInsertPoint(&subIGF.CurFn->back());
subIGF.Builder.emitBlock(isUnfilledBB);
// Break up the loaded value into size and relative address to the
// string.
auto size = subIGF.Builder.CreateAShr(load, 32);
size = subIGF.Builder.CreateTruncOrBitCast(size, IGM.SizeTy);
size = subIGF.Builder.CreateNeg(size);
auto stringAddrOffset = subIGF.Builder.CreateTrunc(load,
IGM.Int32Ty);
stringAddrOffset = subIGF.Builder.CreateSExtOrBitCast(stringAddrOffset,
IGM.SizeTy);
auto stringAddrBase = subIGF.Builder.CreatePtrToInt(cache, IGM.SizeTy);
if (IGM.getModule()->getDataLayout().isBigEndian()) {
stringAddrBase = subIGF.Builder.CreateAdd(stringAddrBase,
llvm::ConstantInt::get(IGM.SizeTy, 4));
}
auto stringAddr = subIGF.Builder.CreateAdd(stringAddrBase,
stringAddrOffset);
stringAddr = subIGF.Builder.CreateIntToPtr(stringAddr, IGM.Int8PtrTy);
llvm::CallInst *call;
bool signedDescriptor = IGM.getAvailabilityContext().isContainedIn(
IGM.Context.getSignedDescriptorAvailability());
if (request.isStaticallyAbstract()) {
call = signedDescriptor ?
subIGF.Builder.CreateCall(
IGM.getGetTypeByMangledNameInContextInMetadataState2FunctionPointer(),
{llvm::ConstantInt::get(IGM.SizeTy, (size_t)MetadataState::Abstract),
stringAddr, size,
// TODO: Use mangled name lookup in generic
// contexts?
llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy),
llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}):
subIGF.Builder.CreateCall(
IGM.getGetTypeByMangledNameInContextInMetadataStateFunctionPointer(),
{llvm::ConstantInt::get(IGM.SizeTy, (size_t)MetadataState::Abstract),
stringAddr, size,
// TODO: Use mangled name lookup in generic
// contexts?
llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy),
llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)});
} else {
call = signedDescriptor ?
subIGF.Builder.CreateCall(
IGM.getGetTypeByMangledNameInContext2FunctionPointer(),
{stringAddr, size,
// TODO: Use mangled name lookup in generic
// contexts?
llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy),
llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)}) :
subIGF.Builder.CreateCall(
IGM.getGetTypeByMangledNameInContextFunctionPointer(),
{stringAddr, size,
// TODO: Use mangled name lookup in generic
// contexts?
llvm::ConstantPointerNull::get(IGM.TypeContextDescriptorPtrTy),
llvm::ConstantPointerNull::get(IGM.Int8PtrPtrTy)});
}
call->setDoesNotThrow();
call->setOnlyReadsMemory();
call->setCallingConv(IGM.SwiftCC);
// Store the result back to the cache. Metadata instantiation should
// already have emitted the necessary barriers to publish the instantiated
// metadata to other threads, so we only need to expose the pointer.
// Worst case, another thread might race with us and reinstantiate the
// exact same metadata pointer.
auto resultWord = subIGF.Builder.CreatePtrToInt(call, IGM.SizeTy);
resultWord = subIGF.Builder.CreateZExtOrBitCast(resultWord, IGM.Int64Ty);
auto store = subIGF.Builder.CreateStore(resultWord, cacheWordAddr,
Alignment(8));
store->setOrdering(llvm::AtomicOrdering::Monotonic);
subIGF.Builder.CreateBr(contBB);
subIGF.Builder.SetInsertPoint(loadBB);
subIGF.Builder.emitBlock(contBB);
auto phi = subIGF.Builder.CreatePHI(IGM.Int64Ty, 2);
phi->addIncoming(load, loadBB);
phi->addIncoming(resultWord, isUnfilledBB);
auto resultAddr = subIGF.Builder.CreateTruncOrBitCast(phi, IGM.SizeTy);
resultAddr = subIGF.Builder.CreateIntToPtr(resultAddr,
IGM.TypeMetadataPtrTy);
subIGF.Builder.CreateRet(resultAddr);
};
auto instantiationFn = cast<llvm::Function>(IGM.getOrCreateHelperFunction(
instantiationFnName, IGF.IGM.TypeMetadataPtrTy, cache->getType(),
generateInstantiationFn,
/*noinline*/ true));
auto call = IGF.Builder.CreateCall(instantiationFn->getFunctionType(),
instantiationFn, cache);
call->setDoesNotThrow();
call->setOnlyReadsMemory();
auto response = MetadataResponse::forComplete(call);
IGF.setScopedLocalTypeMetadata(type, response);
return response;
}
/// Emit a call to the type metadata accessor for the given function.
static MetadataResponse
emitCallToTypeMetadataAccessFunction(IRGenFunction &IGF, CanType type,
DynamicMetadataRequest request) {
// If we already cached the metadata, use it.
if (auto local = IGF.tryGetLocalTypeMetadata(type, request))
return local;
// If the metadata would require multiple runtime calls to build, emit a
// single access by mangled name instead, if we're asking for complete
// metadata.
//
if ((request.isStaticallyBlockingComplete() ||
(request.isStaticallyAbstract() &&
canIssueIncompleteMetadataRequests(IGF.IGM))) &&
shouldAccessByMangledName(IGF.IGM, type)) {
return emitMetadataAccessByMangledName(IGF, type, request);
}
auto *accessor = getOrCreateTypeMetadataAccessFunction(IGF.IGM, type);
llvm::CallInst *call = IGF.Builder.CreateCall(accessor->getFunctionType(),
accessor, {request.get(IGF)});
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotAccessMemory();
call->setDoesNotThrow();
auto response = MetadataResponse::handle(IGF, request, call);
// Save the metadata for future lookups.
IGF.setScopedLocalTypeMetadata(type, response);
return response;
}
llvm::Value *IRGenFunction::emitAbstractTypeMetadataRef(CanType type) {
return emitTypeMetadataRef(type, MetadataState::Abstract).getMetadata();
}
/// Produce the type metadata pointer for the given type.
llvm::Value *IRGenFunction::emitTypeMetadataRef(CanType type) {
return emitTypeMetadataRef(type, MetadataState::Complete).getMetadata();
}
/// Produce the type metadata pointer for the given type.
MetadataResponse
IRGenFunction::emitTypeMetadataRef(CanType type,
DynamicMetadataRequest request) {
if (type->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
!isMetadataAllowedInEmbedded(type)) {
llvm::errs() << "Metadata pointer requested in embedded Swift for type "
<< type << "\n";
llvm::report_fatal_error("metadata used in embedded mode");
}
type = IGM.getRuntimeReifiedType(type);
// Look through any opaque types we're allowed to.
type = IGM.substOpaqueTypesWithUnderlyingTypes(type);
// If we're asking for the metadata of the type that dynamic Self is known
// to be equal to, we can just use the self metadata.
if (SelfTypeIsExact && SelfType == type) {
return MetadataResponse::forComplete(getDynamicSelfMetadata());
}
if (type->hasArchetype() ||
!shouldTypeMetadataAccessUseAccessor(IGM, type) ||
isa<PackType>(type) ||
type->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
return emitDirectTypeMetadataRef(*this, type, request);
}
return emitCallToTypeMetadataAccessFunction(*this, type, request);
}
/// Return the address of a function that will return type metadata
/// for the given non-dependent type.
llvm::Function *irgen::getOrCreateTypeMetadataAccessFunction(IRGenModule &IGM,
CanType type) {
type = IGM.getRuntimeReifiedType(type);
assert(!type->hasArchetype() &&
"cannot create global function to return dependent type metadata");
switch (getTypeMetadataAccessStrategy(type)) {
case MetadataAccessStrategy::ForeignAccessor:
case MetadataAccessStrategy::PublicUniqueAccessor:
case MetadataAccessStrategy::PackageUniqueAccessor:
case MetadataAccessStrategy::HiddenUniqueAccessor:
case MetadataAccessStrategy::PrivateAccessor:
return getOtherwiseDefinedTypeMetadataAccessFunction(IGM, type);
case MetadataAccessStrategy::NonUniqueAccessor:
return createDirectTypeMetadataAccessFunction(IGM, type,
/*allow existing*/true);
}
llvm_unreachable("bad type metadata access strategy");
}
namespace {
/// A visitor class for rewriting a lowered (SIL) type to a formal
/// type with the same type layout that we can fetch metadata for.
/// We need type metadata in order to do value operations on some
/// lowered types (like allocating or copying them), but we can
/// only fetch type metadata for formal types. We can't reliably
/// reverse the type lowering process to get the original formal
/// type, but we should be able to reliably find a formal type with
/// the same layout as a lowered type.
///
/// We can't reliably do this on types expressed in terms of builtin
/// types, because there aren't type metadata for all builtin types.
/// Fortunately, we really shouldn't need type metadata to do value
/// operations on builtin types, and we shouldn't ever see compound
/// types with them that would require metadata to manipulate (like
/// a tuple of a builtin type and a resilient type) --- we can rely
/// on stdlib programmers to not write such types, and we can rely on
/// SIL transformations not introducing them unnecessarily.
///
/// NOTE: If you modify the special cases in this, you should update
/// isTypeMetadataForLayoutAccessible in SIL.cpp.
class GetFormalTypeWithSameLayout
: public CanTypeVisitor<GetFormalTypeWithSameLayout, CanType> {
public:
GetFormalTypeWithSameLayout() {}
/// For most types, we can just emit the usual metadata.
CanType visitType(CanType t) { return t; }
CanType visitBoundGenericEnumType(CanBoundGenericEnumType ty) {
// Optionals have a lowered payload type, so we recurse here.
if (auto objectTy = ty.getOptionalObjectType()) {
auto payloadTy = visit(objectTy);
if (payloadTy == objectTy)
return ty;
auto &C = ty->getASTContext();
auto optDecl = C.getOptionalDecl();
return CanType(BoundGenericEnumType::get(optDecl, Type(), payloadTy));
}
// Otherwise, generic arguments are not lowered.
return ty;
}
CanType visitPackType(CanPackType ty) {
llvm_unreachable("requesting type metadata for a pack type?");
}
CanType visitPackExpansionType(CanPackExpansionType ty) {
CanType pattern = ty.getPatternType();
CanType loweredPattern = visit(ty.getPatternType());
if (pattern == loweredPattern) return ty;
return CanPackExpansionType::get(loweredPattern, ty.getCountType());
}
CanType visitTupleType(CanTupleType ty) {
bool changed = false;
SmallVector<TupleTypeElt, 4> loweredElts;
loweredElts.reserve(ty->getNumElements());
for (auto i : indices(ty->getElementTypes())) {
auto substEltType = ty.getElementType(i);
CanType loweredSubstEltType = visit(substEltType);
changed = (changed || substEltType != loweredSubstEltType);
loweredElts.push_back(ty->getElement(i).getWithType(loweredSubstEltType));
}
if (!changed)
return ty;
return CanTupleType(TupleType::get(loweredElts, ty->getASTContext()));
}
CanType visitAnyFunctionType(CanAnyFunctionType ty) {
llvm_unreachable("not a SIL type");
}
CanType visitSILFunctionType(CanSILFunctionType ty) {
// All function types have the same layout regardless of arguments or
// abstraction level. Use the metadata for () -> () for thick functions,
// or AnyObject for block functions.
auto &C = ty->getASTContext();
switch (ty->getRepresentation()) {
case SILFunctionType::Representation::Thin:
case SILFunctionType::Representation::Method:
case SILFunctionType::Representation::WitnessMethod:
case SILFunctionType::Representation::ObjCMethod:
case SILFunctionType::Representation::CXXMethod:
case SILFunctionType::Representation::CFunctionPointer:
case SILFunctionType::Representation::Closure:
case SILFunctionType::Representation::KeyPathAccessorGetter:
case SILFunctionType::Representation::KeyPathAccessorSetter:
case SILFunctionType::Representation::KeyPathAccessorEquals:
case SILFunctionType::Representation::KeyPathAccessorHash:
// A thin function looks like a plain pointer.
// FIXME: Except for extra inhabitants?
return C.TheRawPointerType;
case SILFunctionType::Representation::Thick: {
// All function types look like () -> ().
// FIXME: It'd be nice not to have to call through the runtime here.
//
// FIXME: Verify ExtInfo state is correct, not working by accident.
CanFunctionType::ExtInfo info;
return CanFunctionType::get({}, C.TheEmptyTupleType, info);
}
case SILFunctionType::Representation::Block:
// All block types look like AnyObject.
return C.getAnyObjectType();
}
llvm_unreachable("Not a valid SILFunctionType.");
}
CanType visitAnyMetatypeType(CanAnyMetatypeType ty) {
assert(ty->hasRepresentation() && "not a lowered metatype");
auto &C = ty->getASTContext();
switch (ty->getRepresentation()) {
case MetatypeRepresentation::Thin:
// Thin metatypes are empty, so they look like the empty tuple type.
return C.TheEmptyTupleType;
case MetatypeRepresentation::Thick:
case MetatypeRepresentation::ObjC:
// Thick and ObjC metatypes look like pointers with extra inhabitants.
// Get the metatype metadata from the runtime.
// FIXME: It'd be nice not to need a runtime call here; we should just
// have a standard aligned-pointer type metadata.
return ty;
}
llvm_unreachable("Not a valid MetatypeRepresentation.");
}
};
} // end anonymous namespace
llvm::Value *IRGenFunction::emitTypeMetadataRefForLayout(SILType type) {
return emitTypeMetadataRefForLayout(type, MetadataState::Complete);
}
llvm::Value *
IRGenFunction::emitTypeMetadataRefForLayout(SILType ty,
DynamicMetadataRequest request) {
assert(request.canResponseStatusBeIgnored());
if (auto response =
tryGetLocalTypeMetadataForLayout(ty.getObjectType(), request)) {
assert(request.canResponseStatusBeIgnored() || !response.isValid());
return response.getMetadata();
}
// Map to a layout equivalent AST type.
auto layoutEquivalentType =
GetFormalTypeWithSameLayout().visit(ty.getASTType());
auto response = emitTypeMetadataRef(layoutEquivalentType, request);
setScopedLocalTypeMetadataForLayout(ty.getObjectType(), response);
return response.getMetadata();
}
namespace {
/// A visitor class for emitting a reference to a type layout struct.
/// There are a few ways we can emit it:
///
/// - If the type is fixed-layout and we have visibility of its value
/// witness table (or one close enough), we can project the layout struct
/// from it.
/// - If the type is fixed layout, we can emit our own copy of the layout
/// struct.
/// - If the type is dynamic-layout, we have to instantiate its metadata
/// and project out its metadata. (FIXME: This leads to deadlocks in
/// recursive cases, though we can avoid many deadlocks because most
/// valid recursive types bottom out in fixed-sized types like classes
/// or pointers.)
class EmitTypeLayoutRef
: public CanTypeVisitor<EmitTypeLayoutRef, llvm::Value *,
DynamicMetadataRequest> {
private:
IRGenFunction &IGF;
public:
EmitTypeLayoutRef(IRGenFunction &IGF) : IGF(IGF) {}
llvm::Value *emitFromValueWitnessTablePointer(llvm::Value *vwtable) {
llvm::Value *indexConstant = llvm::ConstantInt::get(IGF.IGM.Int32Ty,
(unsigned)ValueWitness::First_TypeLayoutWitness);
return IGF.Builder.CreateInBoundsGEP(IGF.IGM.Int8PtrTy, vwtable,
indexConstant);
}
/// Emit the type layout by projecting it from a value witness table to
/// which we have linkage.
llvm::Value *emitFromValueWitnessTable(CanType t) {
auto *vwtable = IGF.IGM.getAddrOfValueWitnessTable(t);
return emitFromValueWitnessTablePointer(vwtable);
}
/// Emit the type layout by projecting it from dynamic type metadata.
llvm::Value *emitFromTypeMetadata(CanType t,
DynamicMetadataRequest request) {
auto *vwtable =
IGF.emitValueWitnessTableRef(IGF.IGM.getLoweredType(t), request);
return emitFromValueWitnessTablePointer(vwtable);
}
/// Given that the type is fixed-layout, emit the type layout by
/// emitting a global layout for it.
llvm::Value *emitFromFixedLayout(CanType t) {
auto layout = tryEmitFromFixedLayout(t);
assert(layout && "type must be fixed-size to call emitFromFixedLayout");
return layout;
}
/// If the type is fixed-layout, emit the type layout by
/// emitting a global layout for it.
llvm::Value *tryEmitFromFixedLayout(CanType t) {
auto &ti = IGF.getTypeInfo(SILType::getPrimitiveObjectType(t));
if (auto fixedTI = dyn_cast<FixedTypeInfo>(&ti))
return IGF.IGM.emitFixedTypeLayout(t, *fixedTI);
return nullptr;
}
bool hasVisibleValueWitnessTable(CanType t) const {
// Some builtin and structural types have value witnesses exported from
// the runtime.
auto &C = IGF.IGM.Context;
if (t == C.TheEmptyTupleType
|| t == C.TheNativeObjectType
|| t == C.TheBridgeObjectType
|| t == C.TheRawPointerType
|| t == C.getAnyObjectType())
return true;
if (auto intTy = dyn_cast<BuiltinIntegerType>(t)) {
auto width = intTy->getWidth();
if (width.isPointerWidth())
return true;
if (width.isFixedWidth()) {
switch (width.getFixedWidth()) {
case 8:
case 16:
case 32:
case 64:
case 128:
case 256:
return true;
default:
return false;
}
}
return false;
}
// TODO: If a nominal type is in the same source file as we're currently
// emitting, we would be able to see its value witness table.
return false;
}
/// Fallback default implementation.
llvm::Value *visitType(CanType t, DynamicMetadataRequest request) {
auto silTy = IGF.IGM.getLoweredType(t);
auto &ti = IGF.getTypeInfo(silTy);
// If the type is in the same source file, or has a common value
// witness table exported from the runtime, we can project from the
// value witness table instead of emitting a new record.
if (hasVisibleValueWitnessTable(t))
return emitFromValueWitnessTable(t);
// If the type is a singleton aggregate, the field's layout is equivalent
// to the aggregate's.
if (SILType singletonFieldTy = getSingletonAggregateFieldType(IGF.IGM,
silTy, ResilienceExpansion::Maximal))
return visit(singletonFieldTy.getASTType(), request);
// If the type is fixed-layout, emit a copy of its layout.
if (auto fixed = dyn_cast<FixedTypeInfo>(&ti))
return IGF.IGM.emitFixedTypeLayout(t, *fixed);
return emitFromTypeMetadata(t, request);
}
llvm::Value *visitAnyFunctionType(CanAnyFunctionType type,
DynamicMetadataRequest request) {
llvm_unreachable("not a SIL type");
}
llvm::Value *visitSILFunctionType(CanSILFunctionType type,
DynamicMetadataRequest request) {
// All function types have the same layout regardless of arguments or
// abstraction level. Use the value witness table for
// @convention(blah) () -> () from the runtime.
auto &C = type->getASTContext();
switch (type->getRepresentation()) {
case SILFunctionType::Representation::Thin:
case SILFunctionType::Representation::Method:
case SILFunctionType::Representation::WitnessMethod:
case SILFunctionType::Representation::ObjCMethod:
case SILFunctionType::Representation::CXXMethod:
case SILFunctionType::Representation::CFunctionPointer:
case SILFunctionType::Representation::Closure:
case SILFunctionType::Representation::KeyPathAccessorGetter:
case SILFunctionType::Representation::KeyPathAccessorSetter:
case SILFunctionType::Representation::KeyPathAccessorEquals:
case SILFunctionType::Representation::KeyPathAccessorHash:
// A thin function looks like a plain pointer.
// FIXME: Except for extra inhabitants?
return emitFromValueWitnessTable(C.TheRawPointerType);
case SILFunctionType::Representation::Thick: {
// All function types look like () -> ().
// FIXME: Verify ExtInfo state is correct, not working by accident.
CanFunctionType::ExtInfo info;
return emitFromValueWitnessTable(
CanFunctionType::get({}, C.TheEmptyTupleType, info));
}
case SILFunctionType::Representation::Block:
// All block types look like AnyObject.
return emitFromValueWitnessTable(C.getAnyObjectType());
}
llvm_unreachable("Not a valid SILFunctionType.");
}
llvm::Value *visitAnyMetatypeType(CanAnyMetatypeType type,
DynamicMetadataRequest request) {
assert(type->hasRepresentation()
&& "not a lowered metatype");
switch (type->getRepresentation()) {
case MetatypeRepresentation::Thin: {
// Thin metatypes are empty, so they look like the empty tuple type.
return emitFromValueWitnessTable(IGF.IGM.Context.TheEmptyTupleType);
}
case MetatypeRepresentation::Thick:
if (isa<ExistentialMetatypeType>(type)) {
return emitFromFixedLayout(type);
}
// Otherwise, this is a metatype that looks like a pointer.
LLVM_FALLTHROUGH;
case MetatypeRepresentation::ObjC:
// Thick metatypes look like pointers with spare bits.
return emitFromValueWitnessTable(
CanMetatypeType::get(IGF.IGM.Context.TheNativeObjectType));
}
llvm_unreachable("Not a valid MetatypeRepresentation.");
}
llvm::Value *visitAnyClassType(ClassDecl *classDecl,
DynamicMetadataRequest request) {
// All class types have the same layout.
auto type = classDecl->getDeclaredType()->getCanonicalType();
switch (type->getReferenceCounting()) {
case ReferenceCounting::Native:
return emitFromValueWitnessTable(IGF.IGM.Context.TheNativeObjectType);
case ReferenceCounting::ObjC:
case ReferenceCounting::Block:
case ReferenceCounting::Unknown:
return emitFromValueWitnessTable(IGF.IGM.Context.getAnyObjectType());
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("classes shouldn't have this kind of refcounting");
case ReferenceCounting::None:
case ReferenceCounting::Custom:
return emitFromValueWitnessTable(IGF.IGM.Context.TheRawPointerType);
}
llvm_unreachable("Not a valid ReferenceCounting.");
}
llvm::Value *visitClassType(CanClassType type,
DynamicMetadataRequest request) {
return visitAnyClassType(type->getClassOrBoundGenericClass(), request);
}
llvm::Value *visitBoundGenericClassType(CanBoundGenericClassType type,
DynamicMetadataRequest request) {
return visitAnyClassType(type->getClassOrBoundGenericClass(), request);
}
llvm::Value *visitPackType(CanPackType type,
DynamicMetadataRequest request) {
llvm_unreachable("");
}
llvm::Value *visitPackExpansionType(CanPackExpansionType type,
DynamicMetadataRequest request) {
llvm_unreachable("");
}
llvm::Value *visitPackElementType(CanPackElementType type,
DynamicMetadataRequest request) {
llvm_unreachable("not implemented for PackElementType");
}
llvm::Value *visitTupleType(CanTupleType type,
DynamicMetadataRequest request) {
// Tuples containing pack expansion types are completely dynamic.
if (type->containsPackExpansionType())
return emitFromTypeMetadata(type, request);
// Single-element tuples have exactly the same layout as their elements.
if (type->getNumElements() == 1) {
return visit(type.getElementType(0), request);
}
// If the type is fixed-layout, use a global layout.
if (auto layout = tryEmitFromFixedLayout(type))
return layout;
// TODO: check for cached VWT / metadata for the type.
// Use swift_getTupleTypeLayout to compute a layout.
// Create a buffer to hold the result. We don't have any reasonable
// way to scope the lifetime of this.
auto resultPtr = IGF.createAlloca(IGF.IGM.FullTypeLayoutTy,
IGF.IGM.getPointerAlignment())
.getAddress();
switch (type->getNumElements()) {
case 0:
case 1:
llvm_unreachable("filtered out above");
case 2: {
auto elt0 = visit(type.getElementType(0), request);
auto elt1 = visit(type.getElementType(1), request);
// Ignore the offset.
auto call =
IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout2FunctionPointer(),
{resultPtr, elt0, elt1});
call->setDoesNotThrow();
break;
}
case 3: {
auto elt0 = visit(type.getElementType(0), request);
auto elt1 = visit(type.getElementType(1), request);
auto elt2 = visit(type.getElementType(2), request);
// Ignore the offsets.
auto call =
IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout3FunctionPointer(),
{resultPtr, elt0, elt1, elt2});
call->setDoesNotThrow();
break;
}
default: {
// Allocate a temporary array for the element layouts.
auto eltLayoutsArraySize =
IGF.IGM.getPointerSize() * type->getNumElements();
auto eltLayoutsArray =
IGF.createAlloca(IGF.IGM.Int8PtrPtrTy,
IGF.IGM.getSize(Size(type->getNumElements())),
IGF.IGM.getPointerAlignment());
IGF.Builder.CreateLifetimeStart(eltLayoutsArray, eltLayoutsArraySize);
// Emit layouts for all the elements and store them into the array.
for (auto i : indices(type.getElementTypes())) {
auto eltLayout = visit(type.getElementType(i), request);
auto eltLayoutSlot =
i == 0 ? eltLayoutsArray
: IGF.Builder.CreateConstArrayGEP(eltLayoutsArray, i,
IGF.IGM.getPointerSize());
IGF.Builder.CreateStore(eltLayout, eltLayoutSlot);
}
// Ignore the offsets.
auto offsetsPtr =
llvm::ConstantPointerNull::get(IGF.IGM.Int32Ty->getPointerTo());
// Flags.
auto flags = TupleTypeFlags().withNumElements(type->getNumElements());
auto flagsValue = IGF.IGM.getSize(Size(flags.getIntValue()));
// Compute the layout.
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetTupleLayoutFunctionPointer(),
{resultPtr, offsetsPtr, flagsValue, eltLayoutsArray.getAddress()});
call->setDoesNotThrow();
// We're done with the buffer.
IGF.Builder.CreateLifetimeEnd(eltLayoutsArray, eltLayoutsArraySize);
break;
}
}
// Cast resultPtr to i8**, our general currency type for type layouts.
resultPtr = IGF.Builder.CreateBitCast(resultPtr, IGF.IGM.Int8PtrPtrTy);
return resultPtr;
}
};
} // end anonymous namespace
llvm::Value *irgen::emitTypeLayoutRef(IRGenFunction &IGF, SILType type,
MetadataDependencyCollector *collector) {
auto request =
DynamicMetadataRequest::getNonBlocking(MetadataState::LayoutComplete,
collector);
assert(request.canResponseStatusBeIgnored());
return EmitTypeLayoutRef(IGF).visit(type.getASTType(), request);
}
/// Given a class metatype, produce the necessary heap metadata
/// reference. This is generally the metatype pointer, but may
/// instead be a reference type.
llvm::Value *irgen::emitClassHeapMetadataRefForMetatype(IRGenFunction &IGF,
llvm::Value *metatype,
CanType type) {
// If the type is known to have Swift metadata, this is trivial.
if (hasKnownSwiftMetadata(IGF.IGM, type))
return metatype;
// Otherwise, we may have to unwrap an ObjC class wrapper.
assert(IGF.IGM.Context.LangOpts.EnableObjCInterop);
metatype = IGF.Builder.CreateBitCast(metatype, IGF.IGM.TypeMetadataPtrTy);
// Fetch the metadata for that class.
auto call = IGF.Builder.CreateCall(
IGF.IGM.getGetObjCClassFromMetadataFunctionPointer(), metatype);
call->setDoesNotThrow();
call->setDoesNotAccessMemory();
return call;
}
/// Produce the heap metadata pointer for the given class type. For
/// Swift-defined types, this is equivalent to the metatype for the
/// class, but for Objective-C-defined types, this is the class
/// object.
llvm::Value *irgen::emitClassHeapMetadataRef(IRGenFunction &IGF, CanType type,
MetadataValueType desiredType,
DynamicMetadataRequest request,
bool allowUninitialized) {
assert(request.canResponseStatusBeIgnored() &&
"emitClassHeapMetadataRef only supports satisfied requests");
assert(type->mayHaveSuperclass() || type->isTypeErasedGenericClassType());
// Archetypes may or may not be ObjC classes and need unwrapping to get at
// the class object.
if (auto archetype = dyn_cast<ArchetypeType>(type)) {
// Look up the Swift metadata from context.
auto archetypeMeta = IGF.emitTypeMetadataRef(type, request).getMetadata();
// Get the class pointer.
auto classPtr = emitClassHeapMetadataRefForMetatype(IGF, archetypeMeta,
archetype);
if (desiredType == MetadataValueType::ObjCClass)
classPtr = IGF.Builder.CreateBitCast(classPtr, IGF.IGM.ObjCClassPtrTy);
return classPtr;
}
if (ClassDecl *theClass = dyn_cast_or_null<ClassDecl>(type->getAnyNominal())) {
if (!hasKnownSwiftMetadata(IGF.IGM, theClass)) {
llvm::Value *result =
emitObjCHeapMetadataRef(IGF, theClass, allowUninitialized);
if (desiredType == MetadataValueType::TypeMetadata)
result = IGF.Builder.CreateBitCast(result, IGF.IGM.TypeMetadataPtrTy);
return result;
}
}
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
llvm::Constant *result = IGF.IGM.getAddrOfTypeMetadata(type);
return result;
}
llvm::Value *result = IGF.emitTypeMetadataRef(type, request).getMetadata();
if (desiredType == MetadataValueType::ObjCClass)
result = IGF.Builder.CreateBitCast(result, IGF.IGM.ObjCClassPtrTy);
return result;
}
/// Emit a metatype value for a known type.
void irgen::emitMetatypeRef(IRGenFunction &IGF, CanMetatypeType type,
Explosion &explosion) {
switch (type->getRepresentation()) {
case MetatypeRepresentation::Thin:
// Thin types have a trivial representation.
break;
case MetatypeRepresentation::Thick:
explosion.add(IGF.emitTypeMetadataRef(type.getInstanceType()));
break;
case MetatypeRepresentation::ObjC:
explosion.add(emitClassHeapMetadataRef(IGF, type.getInstanceType(),
MetadataValueType::ObjCClass,
MetadataState::Complete));
break;
}
}
static bool canCheckStateWithBranch(DynamicMetadataRequest request,
MetadataResponse response) {
assert(request.getDependencyCollector() == nullptr ||
(request.isStatic() && request.getStaticRequest().isNonBlocking()));
return (response.hasDynamicState() &&
request.getDependencyCollector() != nullptr);
}
MetadataResponse
irgen::emitCheckTypeMetadataState(IRGenFunction &IGF,
DynamicMetadataRequest request,
MetadataResponse response) {
// Note that the structure of this function is mirrored in
// getCheckTypeMetadataStateCost.
// If the request is already satisfied by the response, we don't need
// to check anything.
if (request.isSatisfiedBy(response))
return response;
auto metadata = response.getMetadata();
// Try to check the already-fetched dynamic state against the required state.
if (canCheckStateWithBranch(request, response)) {
auto dynamicState = response.getDynamicState();
request.getDependencyCollector()
->checkDependency(IGF, request, metadata, dynamicState);
return MetadataResponse(metadata, dynamicState,
request.getStaticRequest().getState());
}
// Otherwise, we have to ask the runtime.
return emitGetTypeMetadataDynamicState(IGF, request, metadata);
}
OperationCost
irgen::getCheckTypeMetadataStateCost(DynamicMetadataRequest request,
MetadataResponse response) {
if (request.isSatisfiedBy(response))
return OperationCost::Free;
if (canCheckStateWithBranch(request, response))
return OperationCost::Arithmetic;
return OperationCost::Call;
}
/// Call swift_checkMetadataState.
MetadataResponse
irgen::emitGetTypeMetadataDynamicState(IRGenFunction &IGF,
DynamicMetadataRequest request,
llvm::Value *metadata) {
auto call =
IGF.Builder.CreateCall(IGF.IGM.getCheckMetadataStateFunctionPointer(),
{request.get(IGF), metadata});
call->setCallingConv(IGF.IGM.SwiftCC);
return MetadataResponse::handle(IGF, request, call);
}