mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We need to add a version of swift_allocBox to the embedded runtime implementation for outline storage
4701 lines
182 KiB
C++
4701 lines
182 KiB
C++
//===--- GenProto.cpp - Swift IR Generation for Protocols -----------------===//
|
|
//
|
|
// 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 protocols in Swift.
|
|
//
|
|
// Protocols serve two masters: generic algorithms and existential
|
|
// types. In either case, the size and structure of a type is opaque
|
|
// to the code manipulating a value. Local values of the type must
|
|
// be stored in fixed-size buffers (which can overflow to use heap
|
|
// allocation), and basic operations on the type must be dynamically
|
|
// delegated to a collection of information that "witnesses" the
|
|
// truth that a particular type implements the protocol.
|
|
//
|
|
// In the comments throughout this file, three type names are used:
|
|
// 'B' is the type of a fixed-size buffer
|
|
// 'T' is the type which implements a protocol
|
|
// 'W' is the type of a witness to the protocol
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/ConformanceLookup.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/DiagnosticsIRGen.h"
|
|
#include "swift/AST/GenericEnvironment.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/IRGenOptions.h"
|
|
#include "swift/AST/PackConformance.h"
|
|
#include "swift/AST/PrettyStackTrace.h"
|
|
#include "swift/AST/SubstitutionMap.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Platform.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "swift/IRGen/Linking.h"
|
|
#include "swift/SIL/SILDeclRef.h"
|
|
#include "swift/SIL/SILDefaultWitnessTable.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SIL/SILValue.h"
|
|
#include "swift/SIL/SILWitnessTable.h"
|
|
#include "swift/SIL/SILWitnessVisitor.h"
|
|
#include "swift/SIL/TypeLowering.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "CallEmission.h"
|
|
#include "ConformanceDescription.h"
|
|
#include "ConstantBuilder.h"
|
|
#include "EntryPointArgumentEmission.h"
|
|
#include "EnumPayload.h"
|
|
#include "Explosion.h"
|
|
#include "FixedTypeInfo.h"
|
|
#include "Fulfillment.h"
|
|
#include "GenArchetype.h"
|
|
#include "GenCall.h"
|
|
#include "GenCast.h"
|
|
#include "GenClass.h"
|
|
#include "GenEnum.h"
|
|
#include "GenHeap.h"
|
|
#include "GenMeta.h"
|
|
#include "GenOpaque.h"
|
|
#include "GenPack.h"
|
|
#include "GenPointerAuth.h"
|
|
#include "GenPoly.h"
|
|
#include "GenTuple.h"
|
|
#include "GenType.h"
|
|
#include "GenericRequirement.h"
|
|
#include "IRGenDebugInfo.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenMangler.h"
|
|
#include "IRGenModule.h"
|
|
#include "LoadableTypeInfo.h"
|
|
#include "MetadataPath.h"
|
|
#include "MetadataRequest.h"
|
|
#include "NecessaryBindings.h"
|
|
#include "ProtocolInfo.h"
|
|
#include "TypeInfo.h"
|
|
|
|
#include "GenProto.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
namespace {
|
|
|
|
/// A class for computing how to pass arguments to a polymorphic
|
|
/// function. The subclasses of this are the places which need to
|
|
/// be updated if the convention changes.
|
|
class PolymorphicConvention {
|
|
protected:
|
|
IRGenModule &IGM;
|
|
ModuleDecl &M;
|
|
CanSILFunctionType FnType;
|
|
|
|
CanGenericSignature Generics;
|
|
|
|
std::vector<MetadataSource> Sources;
|
|
|
|
FulfillmentMap Fulfillments;
|
|
|
|
GenericSignature::RequiredProtocols getRequiredProtocols(Type t) {
|
|
// FIXME: We need to rework this to use archetypes instead of interface
|
|
// types, or fix the bad interaction between interface type substitution
|
|
// and concretized conformance requirements. Then we can remove the hack
|
|
// from getReducedType() to handle this case, and also stop calling
|
|
// getReducedType() here.
|
|
t = Generics.getReducedType(t);
|
|
if (!t->isTypeParameter())
|
|
return {};
|
|
|
|
return Generics->getRequiredProtocols(t);
|
|
}
|
|
|
|
CanType getSuperclassBound(Type t) {
|
|
// See above.
|
|
t = Generics.getReducedType(t);
|
|
if (!t->isTypeParameter())
|
|
return CanType();
|
|
|
|
if (auto superclassTy = Generics->getSuperclassBound(t))
|
|
return superclassTy->getCanonicalType();
|
|
return CanType();
|
|
}
|
|
|
|
public:
|
|
PolymorphicConvention(IRGenModule &IGM, CanSILFunctionType fnType, bool considerParameterSources);
|
|
|
|
ArrayRef<MetadataSource> getSources() const { return Sources; }
|
|
|
|
void enumerateRequirements(const RequirementCallback &callback);
|
|
|
|
void enumerateUnfulfilledRequirements(const RequirementCallback &callback);
|
|
|
|
/// Returns a Fulfillment for a type parameter requirement, or
|
|
/// nullptr if it's unfulfilled.
|
|
const Fulfillment *getFulfillmentForTypeMetadata(CanType type) const;
|
|
|
|
/// Returns a Fulfillment for a pack shape, or nullptr if it's
|
|
/// unfulfilled.
|
|
const Fulfillment *getFulfillmentForShape(CanType type) const;
|
|
|
|
/// Return the source of type metadata at a particular source index.
|
|
const MetadataSource &getSource(size_t SourceIndex) const {
|
|
return Sources[SourceIndex];
|
|
}
|
|
|
|
private:
|
|
template <typename ...Args>
|
|
void considerNewTypeSource(IsExact_t isExact, MetadataSource::Kind kind,
|
|
CanType type, Args... args);
|
|
bool considerType(CanType type, IsExact_t isExact,
|
|
unsigned sourceIndex, MetadataPath &&path);
|
|
bool considerTupleType(CanTupleType type, IsExact_t isExact,
|
|
unsigned sourceIndex, MetadataPath &&path);
|
|
|
|
/// Testify to generic parameters in the Self type of a protocol
|
|
/// witness method.
|
|
void considerWitnessSelf(CanSILFunctionType fnType);
|
|
|
|
/// Testify to generic parameters in the Self type of an @objc
|
|
/// generic or protocol method.
|
|
void considerObjCGenericSelf(CanSILFunctionType fnType);
|
|
|
|
void considerParameter(SILParameterInfo param, unsigned paramIndex,
|
|
bool isSelfParameter);
|
|
|
|
void addSelfMetadataFulfillment(CanType arg);
|
|
void addSelfWitnessTableFulfillment(CanType arg,
|
|
ProtocolConformanceRef conformance);
|
|
|
|
void addPseudogenericFulfillments();
|
|
|
|
struct FulfillmentMapCallback : FulfillmentMap::InterestingKeysCallback {
|
|
PolymorphicConvention &Self;
|
|
FulfillmentMapCallback(PolymorphicConvention &self) : Self(self) {}
|
|
|
|
bool isInterestingType(CanType type) const override {
|
|
return type->isTypeParameter();
|
|
}
|
|
bool hasInterestingType(CanType type) const override {
|
|
return type->hasTypeParameter();
|
|
}
|
|
bool isInterestingPackExpansion(CanPackExpansionType type) const override {
|
|
return type.getPatternType()->isTypeParameter();
|
|
}
|
|
bool hasLimitedInterestingConformances(CanType type) const override {
|
|
return true;
|
|
}
|
|
GenericSignature::RequiredProtocols
|
|
getInterestingConformances(CanType type) const override {
|
|
return Self.getRequiredProtocols(type);
|
|
}
|
|
CanType getSuperclassBound(CanType type) const override {
|
|
return Self.getSuperclassBound(type);
|
|
}
|
|
};
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
PolymorphicConvention::PolymorphicConvention(IRGenModule &IGM,
|
|
CanSILFunctionType fnType,
|
|
bool considerParameterSources = true)
|
|
: IGM(IGM), M(*IGM.getSwiftModule()), FnType(fnType),
|
|
Generics(fnType->getInvocationGenericSignature()) {
|
|
auto rep = fnType->getRepresentation();
|
|
|
|
if (fnType->isPseudogeneric()) {
|
|
// Protocol witnesses still get Self metadata no matter what. The type
|
|
// parameters of Self are pseudogeneric, though.
|
|
if (rep == SILFunctionTypeRepresentation::WitnessMethod)
|
|
considerWitnessSelf(fnType);
|
|
|
|
addPseudogenericFulfillments();
|
|
return;
|
|
}
|
|
|
|
if (rep == SILFunctionTypeRepresentation::WitnessMethod) {
|
|
// Protocol witnesses always derive all polymorphic parameter information
|
|
// from the Self and Self witness table arguments. We also *cannot* consider
|
|
// other arguments; doing so would potentially make the signature
|
|
// incompatible with other witnesses for the same method.
|
|
considerWitnessSelf(fnType);
|
|
} else if (rep == SILFunctionTypeRepresentation::ObjCMethod) {
|
|
// Objective-C thunks for generic methods also always derive all
|
|
// polymorphic parameter information from the Self argument.
|
|
considerObjCGenericSelf(fnType);
|
|
} else {
|
|
// We don't need to pass anything extra as long as all of the
|
|
// archetypes (and their requirements) are producible from
|
|
// arguments.
|
|
unsigned selfIndex = ~0U;
|
|
auto params = fnType->getParameters();
|
|
|
|
if (considerParameterSources) {
|
|
// Consider 'self' first.
|
|
if (fnType->hasSelfParam()) {
|
|
selfIndex = params.size() - 1;
|
|
considerParameter(params[selfIndex], selfIndex, true);
|
|
}
|
|
|
|
// Now consider the rest of the parameters.
|
|
for (auto index : indices(params)) {
|
|
if (index != selfIndex)
|
|
considerParameter(params[index], index, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PolymorphicConvention::addPseudogenericFulfillments() {
|
|
enumerateRequirements([&](GenericRequirement reqt) {
|
|
auto erasedTypeParam = Generics->getExistentialType(reqt.getTypeParameter())
|
|
->getCanonicalType();
|
|
Sources.emplace_back(MetadataSource::Kind::ErasedTypeMetadata,
|
|
reqt.getTypeParameter(), erasedTypeParam);
|
|
|
|
MetadataPath path;
|
|
Fulfillments.addFulfillment(reqt,
|
|
Sources.size() - 1, std::move(path),
|
|
MetadataState::Complete);
|
|
});
|
|
}
|
|
|
|
void
|
|
irgen::enumerateGenericSignatureRequirements(CanGenericSignature signature,
|
|
const RequirementCallback &callback) {
|
|
if (!signature) return;
|
|
|
|
for (auto type : signature->getShapeClasses())
|
|
callback(GenericRequirement::forShape(type));
|
|
|
|
// Get all of the type metadata.
|
|
signature->forEachParam([&](GenericTypeParamType *gp, bool canonical) {
|
|
if (gp->isValue() && canonical) {
|
|
callback(GenericRequirement::forValue(CanType(gp)));
|
|
return;
|
|
}
|
|
|
|
if (canonical)
|
|
callback(GenericRequirement::forMetadata(CanType(gp)));
|
|
});
|
|
|
|
// Get the protocol conformances.
|
|
for (auto &reqt : signature.getRequirements()) {
|
|
switch (reqt.getKind()) {
|
|
// Ignore these; they don't introduce extra requirements.
|
|
case RequirementKind::SameShape:
|
|
case RequirementKind::Superclass:
|
|
case RequirementKind::SameType:
|
|
case RequirementKind::Layout:
|
|
continue;
|
|
|
|
case RequirementKind::Conformance: {
|
|
auto type = CanType(reqt.getFirstType());
|
|
auto protocol = reqt.getProtocolDecl();
|
|
if (Lowering::TypeConverter::protocolRequiresWitnessTable(protocol)) {
|
|
callback(GenericRequirement::forWitnessTable(type, protocol));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
llvm_unreachable("bad requirement kind");
|
|
}
|
|
}
|
|
|
|
void
|
|
PolymorphicConvention::enumerateRequirements(const RequirementCallback &callback) {
|
|
return enumerateGenericSignatureRequirements(Generics, callback);
|
|
}
|
|
|
|
void PolymorphicConvention::
|
|
enumerateUnfulfilledRequirements(const RequirementCallback &callback) {
|
|
enumerateRequirements([&](GenericRequirement requirement) {
|
|
if (!Fulfillments.getFulfillment(requirement))
|
|
callback(requirement);
|
|
});
|
|
}
|
|
|
|
template <typename ...Args>
|
|
void PolymorphicConvention::considerNewTypeSource(IsExact_t isExact,
|
|
MetadataSource::Kind kind,
|
|
CanType type,
|
|
Args... args) {
|
|
if (!Fulfillments.isInterestingTypeForFulfillments(type)) return;
|
|
|
|
// Prospectively add a source.
|
|
Sources.emplace_back(kind, type, std::forward<Args>(args)...);
|
|
|
|
// Consider the source.
|
|
if (!considerType(type, isExact, Sources.size() - 1, MetadataPath())) {
|
|
// If it wasn't used in any fulfillments, remove it.
|
|
Sources.pop_back();
|
|
}
|
|
}
|
|
|
|
bool PolymorphicConvention::considerType(CanType type, IsExact_t isExact,
|
|
unsigned sourceIndex,
|
|
MetadataPath &&path) {
|
|
FulfillmentMapCallback callbacks(*this);
|
|
return Fulfillments.searchTypeMetadata(IGM, type, isExact,
|
|
MetadataState::Complete, sourceIndex,
|
|
std::move(path), callbacks);
|
|
}
|
|
|
|
bool PolymorphicConvention::considerTupleType(CanTupleType type, IsExact_t isExact,
|
|
unsigned sourceIndex,
|
|
MetadataPath &&path) {
|
|
FulfillmentMapCallback callbacks(*this);
|
|
return Fulfillments.searchTupleTypeMetadata(IGM, type, isExact,
|
|
MetadataState::Complete, sourceIndex,
|
|
std::move(path), callbacks);
|
|
}
|
|
|
|
void PolymorphicConvention::considerWitnessSelf(CanSILFunctionType fnType) {
|
|
CanType selfTy = fnType->getSelfInstanceType(
|
|
IGM.getSILModule(), IGM.getMaximalTypeExpansionContext());
|
|
auto conformance = fnType->getWitnessMethodConformanceOrInvalid();
|
|
|
|
// First, bind type metadata for Self.
|
|
Sources.emplace_back(MetadataSource::Kind::SelfMetadata, selfTy);
|
|
|
|
if (auto tupleTy = dyn_cast<TupleType>(selfTy)) {
|
|
considerTupleType(tupleTy, IsInexact, Sources.size() - 1, MetadataPath());
|
|
} else {
|
|
if (isa<GenericTypeParamType>(selfTy)) {
|
|
// The Self type is abstract, so we can fulfill its metadata from
|
|
// the Self metadata parameter.
|
|
addSelfMetadataFulfillment(selfTy);
|
|
}
|
|
|
|
considerType(selfTy, IsInexact, Sources.size() - 1, MetadataPath());
|
|
}
|
|
|
|
// The witness table for the Self : P conformance can be
|
|
// fulfilled from the Self witness table parameter.
|
|
Sources.emplace_back(MetadataSource::Kind::SelfWitnessTable, selfTy);
|
|
addSelfWitnessTableFulfillment(selfTy, conformance);
|
|
}
|
|
|
|
void PolymorphicConvention::considerObjCGenericSelf(CanSILFunctionType fnType) {
|
|
// If this is a static method, get the instance type.
|
|
CanType selfTy = fnType->getSelfInstanceType(
|
|
IGM.getSILModule(), IGM.getMaximalTypeExpansionContext());
|
|
unsigned paramIndex = fnType->getParameters().size() - 1;
|
|
|
|
// Bind type metadata for Self.
|
|
Sources.emplace_back(MetadataSource::Kind::ClassPointer, selfTy, paramIndex);
|
|
|
|
if (isa<GenericTypeParamType>(selfTy))
|
|
addSelfMetadataFulfillment(selfTy);
|
|
else
|
|
considerType(selfTy, IsInexact,
|
|
Sources.size() - 1, MetadataPath());
|
|
}
|
|
|
|
void PolymorphicConvention::considerParameter(SILParameterInfo param,
|
|
unsigned paramIndex,
|
|
bool isSelfParameter) {
|
|
auto type = param.getArgumentType(IGM.getSILModule(), FnType,
|
|
IGM.getMaximalTypeExpansionContext());
|
|
switch (param.getConvention()) {
|
|
// Indirect parameters do give us a value we can use, but right now
|
|
// we don't bother, for no good reason. But if this is 'self',
|
|
// consider passing an extra metatype.
|
|
case ParameterConvention::Indirect_In:
|
|
case ParameterConvention::Indirect_In_Guaranteed:
|
|
case ParameterConvention::Indirect_Inout:
|
|
case ParameterConvention::Indirect_InoutAliasable:
|
|
case ParameterConvention::Indirect_In_CXX:
|
|
if (!isSelfParameter) return;
|
|
if (type->getNominalOrBoundGenericNominal()) {
|
|
considerNewTypeSource(IsExact,
|
|
MetadataSource::Kind::GenericLValueMetadata,
|
|
type, paramIndex);
|
|
}
|
|
return;
|
|
|
|
case ParameterConvention::Pack_Guaranteed:
|
|
case ParameterConvention::Pack_Owned:
|
|
case ParameterConvention::Pack_Inout:
|
|
// Ignore packs as sources of metadata.
|
|
// In principle, we could recurse into non-expansion components,
|
|
// but what situation would we be in where we had concrete
|
|
// components of a pack and weren't ABI-constrained to ignore them?
|
|
return;
|
|
|
|
case ParameterConvention::Direct_Owned:
|
|
case ParameterConvention::Direct_Unowned:
|
|
case ParameterConvention::Direct_Guaranteed:
|
|
// Classes are sources of metadata.
|
|
if (type->getClassOrBoundGenericClass()) {
|
|
considerNewTypeSource(IsInexact, MetadataSource::Kind::ClassPointer,
|
|
type, paramIndex);
|
|
return;
|
|
}
|
|
|
|
if (isa<GenericTypeParamType>(type)) {
|
|
if (auto superclassTy = getSuperclassBound(type)) {
|
|
considerNewTypeSource(IsInexact, MetadataSource::Kind::ClassPointer,
|
|
superclassTy, paramIndex);
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
// Thick metatypes are sources of metadata.
|
|
if (auto metatypeTy = dyn_cast<MetatypeType>(type)) {
|
|
if (metatypeTy->getRepresentation() != MetatypeRepresentation::Thick)
|
|
return;
|
|
|
|
// Thick metatypes for Objective-C parameterized classes are not
|
|
// sources of metadata.
|
|
CanType objTy = metatypeTy.getInstanceType();
|
|
if (auto classDecl = objTy->getClassOrBoundGenericClass())
|
|
if (classDecl->isTypeErasedGenericClass())
|
|
return;
|
|
|
|
considerNewTypeSource(IsInexact, MetadataSource::Kind::Metadata, objTy,
|
|
paramIndex);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
llvm_unreachable("bad parameter convention");
|
|
}
|
|
|
|
void PolymorphicConvention::addSelfMetadataFulfillment(CanType arg) {
|
|
unsigned source = Sources.size() - 1;
|
|
Fulfillments.addFulfillment(GenericRequirement::forMetadata(arg),
|
|
source, MetadataPath(), MetadataState::Complete);
|
|
}
|
|
|
|
void PolymorphicConvention::addSelfWitnessTableFulfillment(
|
|
CanType arg, ProtocolConformanceRef conformance) {
|
|
auto proto = conformance.getProtocol();
|
|
unsigned source = Sources.size() - 1;
|
|
Fulfillments.addFulfillment(GenericRequirement::forWitnessTable(arg, proto),
|
|
source, MetadataPath(), MetadataState::Complete);
|
|
|
|
if (conformance.isConcrete()) {
|
|
FulfillmentMapCallback callbacks(*this);
|
|
Fulfillments.searchConformance(IGM, conformance.getConcrete(), source,
|
|
MetadataPath(), callbacks);
|
|
}
|
|
}
|
|
|
|
const Fulfillment *
|
|
PolymorphicConvention::getFulfillmentForTypeMetadata(CanType type) const {
|
|
return Fulfillments.getTypeMetadata(type);
|
|
}
|
|
|
|
const Fulfillment *
|
|
PolymorphicConvention::getFulfillmentForShape(CanType type) const {
|
|
return Fulfillments.getShape(type);
|
|
}
|
|
|
|
void irgen::enumerateGenericParamFulfillments(IRGenModule &IGM,
|
|
CanSILFunctionType fnType,
|
|
GenericParamFulfillmentCallback callback) {
|
|
PolymorphicConvention convention(IGM, fnType);
|
|
|
|
// Check if any requirements were fulfilled by metadata stored inside a
|
|
// captured value.
|
|
auto generics = fnType->getInvocationGenericSignature();
|
|
|
|
for (auto shapeClass : generics->getShapeClasses()) {
|
|
auto fulfillment
|
|
= convention.getFulfillmentForShape(shapeClass);
|
|
if (fulfillment == nullptr)
|
|
continue;
|
|
|
|
auto &source = convention.getSource(fulfillment->SourceIndex);
|
|
callback(GenericRequirement::forShape(shapeClass),
|
|
source, fulfillment->Path);
|
|
}
|
|
|
|
for (auto genericParam : generics.getGenericParams()) {
|
|
auto genericParamType = genericParam->getCanonicalType();
|
|
|
|
auto fulfillment
|
|
= convention.getFulfillmentForTypeMetadata(genericParamType);
|
|
if (fulfillment == nullptr)
|
|
continue;
|
|
|
|
auto &source = convention.getSource(fulfillment->SourceIndex);
|
|
callback(GenericRequirement::forMetadata(genericParamType),
|
|
source, fulfillment->Path);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A class for binding type parameters of a generic function.
|
|
class EmitPolymorphicParameters : public PolymorphicConvention {
|
|
IRGenFunction &IGF;
|
|
SILFunction &Fn;
|
|
|
|
public:
|
|
EmitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn);
|
|
|
|
void emit(EntryPointArgumentEmission &emission,
|
|
WitnessMetadata *witnessMetadata,
|
|
const GetParameterFn &getParameter);
|
|
|
|
private:
|
|
CanType getTypeInContext(CanType type) const;
|
|
|
|
CanType getArgTypeInContext(unsigned paramIndex) const;
|
|
|
|
/// Fulfill local type data from any extra information associated with
|
|
/// the given source.
|
|
void bindExtraSource(const MetadataSource &source,
|
|
EntryPointArgumentEmission &emission,
|
|
WitnessMetadata *witnessMetadata);
|
|
|
|
void bindParameterSources(const GetParameterFn &getParameter);
|
|
|
|
void bindParameterSource(SILParameterInfo param, unsigned paramIndex,
|
|
const GetParameterFn &getParameter) ;
|
|
// Did the convention decide that the parameter at the given index
|
|
// was a class-pointer source?
|
|
bool isClassPointerSource(unsigned paramIndex);
|
|
|
|
// If we are building a protocol witness thunks for
|
|
// `DistributedActorSystem.remoteCall` or
|
|
// `DistributedTargetInvocationEncoder.record{Argument, ReturnType}`
|
|
// `DistributedTargetInvocationDecoder.decodeNextArgument`
|
|
// `DistributedTargetInvocationResultHandler.onReturn`
|
|
// requirements we need to supply witness tables associated with `Res`,
|
|
// `Argument`, `R` generic parameters which are not expressible on the
|
|
// protocol requirement because they come from `SerializationRequirement`
|
|
// associated type.
|
|
void injectAdHocDistributedRequirements();
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
EmitPolymorphicParameters::EmitPolymorphicParameters(IRGenFunction &IGF,
|
|
SILFunction &Fn)
|
|
: PolymorphicConvention(IGF.IGM, Fn.getLoweredFunctionType()),
|
|
IGF(IGF), Fn(Fn) {}
|
|
|
|
|
|
CanType EmitPolymorphicParameters::getTypeInContext(CanType type) const {
|
|
return Fn.mapTypeIntoEnvironment(type)->getCanonicalType();
|
|
}
|
|
|
|
CanType EmitPolymorphicParameters::getArgTypeInContext(unsigned paramIndex) const {
|
|
return getTypeInContext(FnType->getParameters()[paramIndex].getArgumentType(
|
|
IGM.getSILModule(), FnType, IGM.getMaximalTypeExpansionContext()));
|
|
}
|
|
|
|
void EmitPolymorphicParameters::bindExtraSource(
|
|
const MetadataSource &source, EntryPointArgumentEmission &emission,
|
|
WitnessMetadata *witnessMetadata) {
|
|
switch (source.getKind()) {
|
|
case MetadataSource::Kind::Metadata:
|
|
case MetadataSource::Kind::ClassPointer:
|
|
// Ignore these, we'll get to them when we walk the parameter list.
|
|
return;
|
|
|
|
case MetadataSource::Kind::GenericLValueMetadata: {
|
|
CanType argTy = getArgTypeInContext(source.getParamIndex());
|
|
|
|
llvm::Value *metadata = emission.getNextPolymorphicParameterAsMetadata();
|
|
setTypeMetadataName(IGF.IGM, metadata, argTy);
|
|
|
|
IGF.bindLocalTypeDataFromTypeMetadata(argTy, IsExact, metadata,
|
|
MetadataState::Complete);
|
|
return;
|
|
}
|
|
|
|
case MetadataSource::Kind::SelfMetadata: {
|
|
assert(witnessMetadata && "no metadata for witness method");
|
|
llvm::Value *metadata = witnessMetadata->SelfMetadata;
|
|
assert(metadata && "no Self metadata for witness method");
|
|
|
|
// Mark this as the cached metatype for Self.
|
|
auto selfTy = FnType->getSelfInstanceType(
|
|
IGM.getSILModule(), IGM.getMaximalTypeExpansionContext());
|
|
CanType argTy = getTypeInContext(selfTy);
|
|
setTypeMetadataName(IGF.IGM, metadata, argTy);
|
|
auto *CD = selfTy.getClassOrBoundGenericClass();
|
|
// The self metadata here corresponds to the conforming type.
|
|
// For an inheritable conformance, that may be a subclass of the static
|
|
// type, and so the self metadata will be inexact. Currently, all
|
|
// conformances are inheritable.
|
|
IGF.bindLocalTypeDataFromTypeMetadata(
|
|
argTy, (!CD || CD->isFinal()) ? IsExact : IsInexact, metadata,
|
|
MetadataState::Complete);
|
|
return;
|
|
}
|
|
|
|
case MetadataSource::Kind::SelfWitnessTable: {
|
|
assert(witnessMetadata && "no metadata for witness method");
|
|
llvm::Value *selfTable = witnessMetadata->SelfWitnessTable;
|
|
assert(selfTable && "no Self witness table for witness method");
|
|
|
|
// Mark this as the cached witness table for Self.
|
|
auto conformance = FnType->getWitnessMethodConformanceOrInvalid();
|
|
auto selfProto = conformance.getProtocol();
|
|
|
|
auto selfTy = FnType->getSelfInstanceType(
|
|
IGM.getSILModule(), IGM.getMaximalTypeExpansionContext());
|
|
CanType argTy = getTypeInContext(selfTy);
|
|
|
|
setProtocolWitnessTableName(IGF.IGM, selfTable, argTy, selfProto);
|
|
IGF.setUnscopedLocalTypeData(
|
|
argTy,
|
|
LocalTypeDataKind::forProtocolWitnessTable(conformance),
|
|
selfTable);
|
|
|
|
if (conformance.isConcrete()) {
|
|
IGF.bindLocalTypeDataFromSelfWitnessTable(
|
|
conformance.getConcrete(),
|
|
selfTable,
|
|
[this](CanType type) {
|
|
return getTypeInContext(type);
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
case MetadataSource::Kind::ErasedTypeMetadata: {
|
|
ArtificialLocation Loc(IGF.getDebugScope(), IGF.IGM.DebugInfo.get(),
|
|
IGF.Builder);
|
|
CanType argTy = getTypeInContext(source.Type);
|
|
llvm::Value *metadata = IGF.emitTypeMetadataRef(source.getFixedType());
|
|
setTypeMetadataName(IGF.IGM, metadata, argTy);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(argTy, IsExact, metadata,
|
|
MetadataState::Complete);
|
|
return;
|
|
}
|
|
}
|
|
llvm_unreachable("bad source kind!");
|
|
}
|
|
|
|
void EmitPolymorphicParameters::bindParameterSources(const GetParameterFn &getParameter) {
|
|
auto params = FnType->getParameters();
|
|
|
|
// Bind things from 'self' preferentially.
|
|
if (FnType->hasSelfParam()) {
|
|
bindParameterSource(params.back(), params.size() - 1, getParameter);
|
|
params = params.drop_back();
|
|
}
|
|
|
|
for (unsigned index : indices(params)) {
|
|
bindParameterSource(params[index], index, getParameter);
|
|
}
|
|
}
|
|
|
|
void EmitPolymorphicParameters::
|
|
bindParameterSource(SILParameterInfo param, unsigned paramIndex,
|
|
const GetParameterFn &getParameter) {
|
|
// Ignore indirect parameters for now. This is potentially dumb.
|
|
if (IGF.IGM.silConv.isSILIndirect(param))
|
|
return;
|
|
|
|
CanType paramType = getArgTypeInContext(paramIndex);
|
|
|
|
// If the parameter is a thick metatype, bind it directly.
|
|
// TODO: objc metatypes?
|
|
if (auto metatype = dyn_cast<MetatypeType>(paramType)) {
|
|
if (metatype->getRepresentation() == MetatypeRepresentation::Thick) {
|
|
paramType = metatype.getInstanceType();
|
|
llvm::Value *metadata = getParameter(paramIndex);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
|
|
MetadataState::Complete);
|
|
} else if (metatype->getRepresentation() == MetatypeRepresentation::ObjC) {
|
|
paramType = metatype.getInstanceType();
|
|
llvm::Value *objcMetatype = getParameter(paramIndex);
|
|
auto *metadata = emitObjCMetadataRefForMetadata(IGF, objcMetatype);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
|
|
MetadataState::Complete);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If the parameter is a class type, we only consider it interesting
|
|
// if the convention decided it was actually a source.
|
|
// TODO: if the class pointer is guaranteed, we can do this lazily,
|
|
// at which point it might make sense to do it for a wider selection
|
|
// of types.
|
|
if (isClassPointerSource(paramIndex)) {
|
|
llvm::Value *instanceRef = getParameter(paramIndex);
|
|
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
|
|
llvm::Value *metadata =
|
|
emitDynamicTypeOfHeapObject(IGF, instanceRef,
|
|
MetatypeRepresentation::Thick,
|
|
instanceType,
|
|
/*allow artificial subclasses*/ true);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
|
|
MetadataState::Complete);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool EmitPolymorphicParameters::isClassPointerSource(unsigned paramIndex) {
|
|
for (auto &source : getSources()) {
|
|
if (source.getKind() == MetadataSource::Kind::ClassPointer &&
|
|
source.getParamIndex() == paramIndex) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void EmitPolymorphicParameters::injectAdHocDistributedRequirements() {
|
|
// FIXME: We need a better way to recognize that function is
|
|
// a thunk for witness of `remoteCall` requirement.
|
|
if (!Fn.hasLocation())
|
|
return;
|
|
|
|
auto loc = Fn.getLocation();
|
|
|
|
auto *funcDecl = dyn_cast_or_null<FuncDecl>(loc.getAsDeclContext());
|
|
if (!(funcDecl && funcDecl->hasGenericParamList()))
|
|
return;
|
|
|
|
if (!funcDecl->isDistributedWitnessWithAdHocSerializationRequirement())
|
|
return;
|
|
|
|
Type genericParam;
|
|
|
|
auto sig = funcDecl->getGenericSignature();
|
|
|
|
// DistributedActorSystem.remoteCall
|
|
if (funcDecl->isDistributedActorSystemRemoteCall(
|
|
/*isVoidReturn=*/false)) {
|
|
genericParam = funcDecl->getResultInterfaceType();
|
|
} else {
|
|
// DistributedTargetInvocationEncoder.record{Argument, ReturnType}
|
|
// DistributedTargetInvocationDecoder.decodeNextArgument
|
|
// DistributedTargetInvocationResultHandler.onReturn
|
|
genericParam = sig.getInnermostGenericParams().front();
|
|
}
|
|
|
|
if (!genericParam)
|
|
return;
|
|
|
|
auto protocols = sig->getRequiredProtocols(genericParam);
|
|
if (protocols.empty())
|
|
return;
|
|
|
|
auto archetypeTy = getTypeInContext(genericParam->getCanonicalType());
|
|
llvm::Value *metadata = IGF.emitTypeMetadataRef(archetypeTy);
|
|
|
|
for (auto *proto : protocols) {
|
|
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(proto))
|
|
continue;
|
|
|
|
// Lookup the witness table for this protocol dynamically via
|
|
// swift_conformsToProtocol(<<archetype>>, <<protocol>>)
|
|
auto *witnessTable = IGF.Builder.CreateCall(
|
|
IGM.getConformsToProtocolFunctionPointer(),
|
|
{metadata, IGM.getAddrOfProtocolDescriptor(proto)});
|
|
|
|
IGF.setUnscopedLocalTypeData(
|
|
archetypeTy,
|
|
LocalTypeDataKind::forAbstractProtocolWitnessTable(proto),
|
|
witnessTable);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A class for binding type parameters of a generic function.
|
|
class BindPolymorphicParameter : public PolymorphicConvention {
|
|
IRGenFunction &IGF;
|
|
CanSILFunctionType &SubstFnType;
|
|
|
|
public:
|
|
BindPolymorphicParameter(IRGenFunction &IGF, CanSILFunctionType &origFnType,
|
|
CanSILFunctionType &SubstFnType)
|
|
: PolymorphicConvention(IGF.IGM, origFnType), IGF(IGF),
|
|
SubstFnType(SubstFnType) {}
|
|
|
|
void emit(Explosion &in, unsigned paramIndex);
|
|
|
|
private:
|
|
// Did the convention decide that the parameter at the given index
|
|
// was a class-pointer source?
|
|
bool isClassPointerSource(unsigned paramIndex);
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
bool BindPolymorphicParameter::isClassPointerSource(unsigned paramIndex) {
|
|
for (auto &source : getSources()) {
|
|
if (source.getKind() == MetadataSource::Kind::ClassPointer &&
|
|
source.getParamIndex() == paramIndex) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void BindPolymorphicParameter::emit(Explosion &nativeParam, unsigned paramIndex) {
|
|
if (!isClassPointerSource(paramIndex))
|
|
return;
|
|
|
|
assert(nativeParam.size() == 1);
|
|
auto paramType = SubstFnType->getParameters()[paramIndex].getArgumentType(
|
|
IGM.getSILModule(), SubstFnType, IGM.getMaximalTypeExpansionContext());
|
|
llvm::Value *instanceRef = nativeParam.getAll()[0];
|
|
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
|
|
llvm::Value *metadata =
|
|
emitDynamicTypeOfHeapObject(IGF, instanceRef,
|
|
MetatypeRepresentation::Thick,
|
|
instanceType,
|
|
/* allow artificial subclasses */ true);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
|
|
MetadataState::Complete);
|
|
}
|
|
|
|
void irgen::bindPolymorphicParameter(IRGenFunction &IGF,
|
|
CanSILFunctionType &OrigFnType,
|
|
CanSILFunctionType &SubstFnType,
|
|
Explosion &nativeParam,
|
|
unsigned paramIndex) {
|
|
BindPolymorphicParameter(IGF, OrigFnType, SubstFnType)
|
|
.emit(nativeParam, paramIndex);
|
|
}
|
|
|
|
static bool shouldSetName(IRGenModule &IGM, llvm::Value *value, CanType type) {
|
|
// If value names are globally disabled, honor that.
|
|
if (!IGM.EnableValueNames) return false;
|
|
|
|
// Suppress value names for values with local archetypes
|
|
if (type->hasLocalArchetype()) return false;
|
|
|
|
// If the value already has a name, honor that.
|
|
if (value->hasName()) return false;
|
|
|
|
// Only do this for local values.
|
|
return (isa<llvm::Instruction>(value) || isa<llvm::Argument>(value));
|
|
}
|
|
|
|
void irgen::setTypeMetadataName(IRGenModule &IGM, llvm::Value *metadata,
|
|
CanType type) {
|
|
if (!shouldSetName(IGM, metadata, type)) return;
|
|
|
|
SmallString<128> name; {
|
|
llvm::raw_svector_ostream out(name);
|
|
type.print(out);
|
|
}
|
|
metadata->setName(type->getString());
|
|
}
|
|
|
|
void irgen::setProtocolWitnessTableName(IRGenModule &IGM, llvm::Value *wtable,
|
|
CanType type,
|
|
ProtocolDecl *requirement) {
|
|
if (!shouldSetName(IGM, wtable, type)) return;
|
|
|
|
SmallString<128> name; {
|
|
llvm::raw_svector_ostream out(name);
|
|
type.print(out);
|
|
out << '.' << requirement->getNameStr();
|
|
}
|
|
wtable->setName(name);
|
|
}
|
|
|
|
namespace {
|
|
/// A class which lays out a witness table in the abstract.
|
|
class WitnessTableLayout : public SILWitnessVisitor<WitnessTableLayout> {
|
|
SmallVector<WitnessTableEntry, 16> Entries;
|
|
bool requirementSignatureOnly;
|
|
|
|
public:
|
|
explicit WitnessTableLayout(ProtocolInfoKind resultKind) {
|
|
switch (resultKind) {
|
|
case ProtocolInfoKind::RequirementSignature:
|
|
requirementSignatureOnly = true;
|
|
break;
|
|
case ProtocolInfoKind::Full:
|
|
requirementSignatureOnly = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool shouldVisitRequirementSignatureOnly() {
|
|
return requirementSignatureOnly;
|
|
}
|
|
|
|
void addProtocolConformanceDescriptor() { }
|
|
|
|
/// The next witness is an out-of-line base protocol.
|
|
void addOutOfLineBaseProtocol(ProtocolDecl *baseProto) {
|
|
Entries.push_back(WitnessTableEntry::forOutOfLineBase(baseProto));
|
|
}
|
|
|
|
void addMethod(SILDeclRef func) {
|
|
// If this assert needs to be changed, be sure to also change
|
|
// ProtocolDescriptorBuilder::getRequirementInfo.
|
|
assert((isa<ConstructorDecl>(func.getDecl())
|
|
? (func.kind == SILDeclRef::Kind::Allocator)
|
|
: (func.kind == SILDeclRef::Kind::Func)) &&
|
|
"unexpected kind for protocol witness declaration ref");
|
|
Entries.push_back(WitnessTableEntry::forFunction(func));
|
|
}
|
|
|
|
void addPlaceholder(MissingMemberDecl *placeholder) {
|
|
for (auto i : range(placeholder->getNumberOfVTableEntries())) {
|
|
(void)i;
|
|
Entries.push_back(WitnessTableEntry::forPlaceholder());
|
|
}
|
|
}
|
|
|
|
void addAssociatedType(AssociatedTypeDecl *assocType) {
|
|
// In Embedded Swift witness tables don't have associated-types entries.
|
|
auto &langOpts = assocType->getASTContext().LangOpts;
|
|
if (langOpts.hasFeature(Feature::Embedded) &&
|
|
!langOpts.hasFeature(Feature::EmbeddedExistentials))
|
|
return;
|
|
Entries.push_back(WitnessTableEntry::forAssociatedType(assocType));
|
|
}
|
|
|
|
void addAssociatedConformance(const AssociatedConformance &req) {
|
|
auto &langOpts = req.getAssociation()->getASTContext().LangOpts;
|
|
if (langOpts.hasFeature(Feature::Embedded) &&
|
|
!langOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
|
!req.getAssociatedRequirement()->requiresClass()) {
|
|
// If it's not a class protocol, the associated type can never be used to create
|
|
// an existential. Therefore this witness entry is never used at runtime
|
|
// in embedded swift.
|
|
return;
|
|
}
|
|
Entries.push_back(WitnessTableEntry::forAssociatedConformance(req));
|
|
}
|
|
|
|
ArrayRef<WitnessTableEntry> getEntries() const { return Entries; }
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Return true if the witness table requires runtime instantiation to
|
|
/// handle resiliently-added requirements with default implementations.
|
|
///
|
|
/// If disableOptimizations is true, skip optimizations that treat
|
|
/// formally-resilient conformances as non-resilient.
|
|
bool IRGenModule::isResilientConformance(
|
|
const NormalProtocolConformance *conformance,
|
|
bool disableOptimizations
|
|
) {
|
|
// If the protocol is not resilient, the conformance is not resilient
|
|
// either.
|
|
bool shouldTreatProtocolNonResilient =
|
|
IRGen.Opts.UseFragileResilientProtocolWitnesses;
|
|
if (!conformance->getProtocol()->isResilient() ||
|
|
shouldTreatProtocolNonResilient)
|
|
return false;
|
|
|
|
auto *conformanceModule = conformance->getDeclContext()->getParentModule();
|
|
|
|
// If the protocol and the conformance are both in the current module,
|
|
// they're not resilient.
|
|
if (conformanceModule == getSwiftModule() &&
|
|
conformanceModule == conformance->getProtocol()->getParentModule())
|
|
return false;
|
|
|
|
// If the protocol WAS from the current module (@_originallyDefinedIn), we
|
|
// consider the conformance non-resilient, because we used to consider it
|
|
// non-resilient before the symbol moved. This is to ensure ABI stability
|
|
// across module boundaries.
|
|
if (conformanceModule == getSwiftModule() &&
|
|
conformanceModule->getName().str() ==
|
|
conformance->getProtocol()->getAlternateModuleName())
|
|
return false;
|
|
|
|
// If the protocol and the conformance are in the same module and the
|
|
// conforming type is not generic, they're not resilient.
|
|
//
|
|
// This is an optimization -- a conformance of a non-generic type cannot
|
|
// resiliently become dependent.
|
|
if (!conformance->getDeclContext()->isGenericContext() &&
|
|
conformanceModule == conformance->getProtocol()->getParentModule() &&
|
|
!disableOptimizations)
|
|
return false;
|
|
|
|
// We have a resilient conformance.
|
|
return true;
|
|
}
|
|
|
|
bool IRGenModule::isResilientConformance(const RootProtocolConformance *root,
|
|
bool disableOptimizations) {
|
|
if (auto normal = dyn_cast<NormalProtocolConformance>(root))
|
|
return isResilientConformance(normal, disableOptimizations);
|
|
// Self-conformances never require this.
|
|
return false;
|
|
}
|
|
|
|
/// Whether this protocol conformance has a dependent type witness.
|
|
static bool hasDependentTypeWitness(
|
|
const NormalProtocolConformance *conformance) {
|
|
auto DC = conformance->getDeclContext();
|
|
// If the conforming type isn't dependent, the below check is never true.
|
|
if (!DC->isGenericContext())
|
|
return false;
|
|
|
|
auto genericSig = conformance->getGenericSignature();
|
|
|
|
// Check whether any of the associated types are dependent.
|
|
if (conformance->forEachTypeWitness(
|
|
[&](AssociatedTypeDecl *requirement, Type type,
|
|
TypeDecl *explicitDecl) -> bool {
|
|
// Skip associated types that don't have witness table entries.
|
|
if (!requirement->getOverriddenDecls().empty())
|
|
return false;
|
|
|
|
// RESILIENCE: this could be an opaque conformance
|
|
return type->getReducedType(genericSig)->hasTypeParameter();
|
|
},
|
|
/*useResolver=*/true)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) {
|
|
if (auto normal = dyn_cast<NormalProtocolConformance>(conformance))
|
|
return normal->isSynthesizedNonUnique();
|
|
return false;
|
|
}
|
|
|
|
/// Determine whether a protocol can ever have a dependent conformance.
|
|
static bool protocolCanHaveDependentConformance(
|
|
ProtocolDecl *proto,
|
|
bool isResilient
|
|
) {
|
|
// Objective-C protocols have never been able to have a dependent conformance.
|
|
if (proto->isObjC())
|
|
return false;
|
|
|
|
// Prior to Swift 6.0, only Objective-C protocols were never able to have
|
|
// a dependent conformance. This is overly pessimistic when the protocol
|
|
// is a marker protocol (since they don't have requirements), but we must
|
|
// retain backward compatibility with binaries built for earlier deployment
|
|
// targets that concluded that these protocols might involve dependent
|
|
// conformances. Only do this for resilient protocols.
|
|
if (isResilient && proto->isSpecificProtocol(KnownProtocolKind::Sendable)) {
|
|
ASTContext &ctx = proto->getASTContext();
|
|
if (auto runtimeCompatVersion = getSwiftRuntimeCompatibilityVersionForTarget(
|
|
ctx.LangOpts.Target)) {
|
|
if (runtimeCompatVersion < llvm::VersionTuple(6, 0))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return Lowering::TypeConverter::protocolRequiresWitnessTable(proto);
|
|
}
|
|
|
|
static bool isDependentConformance(
|
|
IRGenModule &IGM,
|
|
const RootProtocolConformance *rootConformance,
|
|
bool isResilient,
|
|
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> &visited){
|
|
// Self-conformances are never dependent.
|
|
auto conformance = dyn_cast<NormalProtocolConformance>(rootConformance);
|
|
if (!conformance)
|
|
return false;
|
|
|
|
if (IGM.getOptions().LazyInitializeProtocolConformances) {
|
|
const auto *MD = rootConformance->getDeclContext()->getParentModule();
|
|
if (!(MD == IGM.getSwiftModule() || MD->isStaticLibrary()))
|
|
return true;
|
|
}
|
|
|
|
// Check whether we've visited this conformance already. If so,
|
|
// optimistically assume it's fine --- we want the maximal fixed point.
|
|
if (!visited.insert(conformance).second)
|
|
return false;
|
|
|
|
// If the conformance is resilient or synthesized, this is always true.
|
|
if (IGM.isResilientConformance(conformance)
|
|
|| isSynthesizedNonUnique(conformance))
|
|
return true;
|
|
|
|
// Check whether any of the conformances are dependent.
|
|
auto proto = conformance->getProtocol();
|
|
for (const auto &req : proto->getRequirementSignature().getRequirements()) {
|
|
if (req.getKind() != RequirementKind::Conformance)
|
|
continue;
|
|
|
|
auto assocProtocol = req.getProtocolDecl();
|
|
if (!protocolCanHaveDependentConformance(
|
|
assocProtocol, isResilient))
|
|
continue;
|
|
|
|
auto assocConformance =
|
|
conformance->getAssociatedConformance(req.getFirstType(), assocProtocol);
|
|
|
|
// We might be presented with a broken AST.
|
|
if (assocConformance.isInvalid())
|
|
return false;
|
|
|
|
if (assocConformance.isAbstract() ||
|
|
isDependentConformance(IGM,
|
|
assocConformance.getConcrete()
|
|
->getRootConformance(),
|
|
isResilient,
|
|
visited))
|
|
return true;
|
|
}
|
|
|
|
if (hasDependentTypeWitness(conformance))
|
|
return true;
|
|
|
|
// Check if there are any conditional conformances. Other forms of conditional
|
|
// requirements don't exist in the witness table.
|
|
return SILWitnessTable::enumerateWitnessTableConditionalConformances(
|
|
conformance, [](unsigned, CanType, ProtocolDecl *) { return true; });
|
|
}
|
|
|
|
static bool hasConditionalConformances(IRGenModule &IGM,
|
|
const RootProtocolConformance *rootConformance,
|
|
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> visited) {
|
|
// Self-conformances are never dependent.
|
|
auto conformance = dyn_cast<NormalProtocolConformance>(rootConformance);
|
|
if (!conformance)
|
|
return false;
|
|
|
|
// Check whether we've visited this conformance already. If so,
|
|
// optimistically assume it's fine --- we want the maximal fixed point.
|
|
if (!visited.insert(conformance).second)
|
|
return false;
|
|
|
|
// Check whether any of the conformances are dependent.
|
|
auto proto = conformance->getProtocol();
|
|
for (const auto &req : proto->getRequirementSignature().getRequirements()) {
|
|
if (req.getKind() != RequirementKind::Conformance)
|
|
continue;
|
|
|
|
auto assocProtocol = req.getProtocolDecl();
|
|
if (assocProtocol->isObjC())
|
|
continue;
|
|
|
|
auto assocConformance =
|
|
conformance->getAssociatedConformance(req.getFirstType(), assocProtocol);
|
|
|
|
// We might be presented with a broken AST.
|
|
if (assocConformance.isInvalid())
|
|
return false;
|
|
|
|
if (assocConformance.isAbstract())
|
|
continue;
|
|
|
|
if (hasConditionalConformances(IGM,
|
|
assocConformance.getConcrete()
|
|
->getRootConformance(),
|
|
visited))
|
|
return true;
|
|
}
|
|
|
|
// Check if there are any conditional conformances. Other forms of conditional
|
|
// requirements don't exist in the witness table.
|
|
return SILWitnessTable::enumerateWitnessTableConditionalConformances(
|
|
conformance, [](unsigned, CanType, ProtocolDecl *) { return true; });
|
|
}
|
|
static bool hasConditionalConformances(IRGenModule &IGM,
|
|
const RootProtocolConformance *conformance) {
|
|
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> visited;
|
|
return hasConditionalConformances(IGM, conformance, visited);
|
|
}
|
|
/// Is there anything about the given conformance that requires witness
|
|
/// tables to be dependently-generated?
|
|
bool IRGenModule::isDependentConformance(
|
|
const RootProtocolConformance *conformance) {
|
|
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> visited;
|
|
return ::isDependentConformance(
|
|
*this, conformance,
|
|
isResilientConformance(conformance, /*disableOptimizations=*/true),
|
|
visited);
|
|
}
|
|
|
|
static llvm::Value *
|
|
emitConditionalConformancesBuffer(IRGenFunction &IGF,
|
|
const ProtocolConformance *substConformance) {
|
|
auto rootConformance =
|
|
dyn_cast<NormalProtocolConformance>(substConformance->getRootConformance());
|
|
|
|
// Not a normal conformance means no conditional requirements means no need
|
|
// for a buffer.
|
|
if (!rootConformance)
|
|
return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy);
|
|
|
|
// Pointers to the witness tables, in the right order, which will be included
|
|
// in the buffer that gets passed to the witness table accessor.
|
|
llvm::SmallVector<llvm::Value *, 4> tables;
|
|
|
|
auto subMap = substConformance->getSubstitutionMap();
|
|
SILWitnessTable::enumerateWitnessTableConditionalConformances(
|
|
rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) {
|
|
auto substType = type.subst(subMap)->getCanonicalType();
|
|
auto reqConformance = subMap.lookupConformance(type, proto);
|
|
assert(reqConformance && "conditional conformance must be valid");
|
|
|
|
tables.push_back(emitWitnessTableRef(IGF, substType, reqConformance));
|
|
return /*finished?*/ false;
|
|
});
|
|
|
|
// No conditional requirements means no need for a buffer.
|
|
if (tables.empty()) {
|
|
return llvm::UndefValue::get(IGF.IGM.WitnessTablePtrPtrTy);
|
|
}
|
|
|
|
auto buffer = IGF.createAlloca(
|
|
llvm::ArrayType::get(IGF.IGM.WitnessTablePtrTy, tables.size()),
|
|
IGF.IGM.getPointerAlignment(), "conditional.requirement.buffer");
|
|
buffer = IGF.Builder.CreateStructGEP(buffer, 0, Size(0));
|
|
|
|
// Write each of the conditional witness tables into the buffer.
|
|
for (auto idx : indices(tables)) {
|
|
auto slot =
|
|
IGF.Builder.CreateConstArrayGEP(buffer, idx, IGF.IGM.getPointerSize());
|
|
auto wtable =
|
|
IGF.Builder.CreateBitCast(tables[idx], IGF.IGM.WitnessTablePtrTy);
|
|
IGF.Builder.CreateStore(wtable, slot);
|
|
}
|
|
|
|
return buffer.getAddress();
|
|
}
|
|
|
|
static llvm::Value *emitWitnessTableAccessorCall(
|
|
IRGenFunction &IGF, CanType conformingType,
|
|
const ProtocolConformance *conformance,
|
|
llvm::Value **srcMetadataCache) {
|
|
auto conformanceDescriptor =
|
|
IGF.IGM.getAddrOfProtocolConformanceDescriptor(
|
|
conformance->getRootConformance());
|
|
|
|
// Emit the source metadata if we haven't yet.
|
|
if (!*srcMetadataCache) {
|
|
// Witness table accesses only require abstract type metadata; this
|
|
// is so that we can create the witness tables without introducing
|
|
// cycle problems.
|
|
*srcMetadataCache = IGF.emitAbstractTypeMetadataRef(conformingType);
|
|
}
|
|
|
|
auto conditionalTables =
|
|
emitConditionalConformancesBuffer(IGF, conformance);
|
|
|
|
auto call = IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables ?
|
|
IGF.Builder.CreateCall(
|
|
IGF.IGM.getGetWitnessTableRelativeFunctionPointer(),
|
|
{conformanceDescriptor, *srcMetadataCache, conditionalTables}) :
|
|
IGF.Builder.CreateCall(
|
|
IGF.IGM.getGetWitnessTableFunctionPointer(),
|
|
{conformanceDescriptor, *srcMetadataCache, conditionalTables});
|
|
|
|
call->setCallingConv(IGF.IGM.DefaultCC);
|
|
call->setDoesNotThrow();
|
|
|
|
return call;
|
|
}
|
|
|
|
/// Fetch the lazy access function for the given conformance of the
|
|
/// given type.
|
|
static llvm::Function *
|
|
getWitnessTableLazyAccessFunction(IRGenModule &IGM,
|
|
CanType conformingType,
|
|
const ProtocolConformance *conformance) {
|
|
assert(!conformingType->hasArchetype());
|
|
|
|
auto rootConformance = conformance->getRootNormalConformance();
|
|
llvm::Function *accessor = IGM.getAddrOfWitnessTableLazyAccessFunction(
|
|
rootConformance, conformingType, ForDefinition);
|
|
|
|
// If we're not supposed to define the accessor, or if we already
|
|
// have defined it, just return the pointer.
|
|
if (!accessor->empty())
|
|
return accessor;
|
|
|
|
if (IGM.getOptions().optimizeForSize())
|
|
accessor->addFnAttr(llvm::Attribute::NoInline);
|
|
|
|
// Okay, define the accessor.
|
|
auto cacheVariable =
|
|
cast<llvm::GlobalVariable>(IGM.getAddrOfWitnessTableLazyCacheVariable(
|
|
rootConformance, conformingType, ForDefinition));
|
|
emitCacheAccessFunction(
|
|
IGM, accessor, cacheVariable, IGM.WitnessTablePtrTy, CacheStrategy::Lazy,
|
|
[&](IRGenFunction &IGF, Explosion ¶ms) {
|
|
llvm::Value *conformingMetadataCache = nullptr;
|
|
return MetadataResponse::forComplete(emitWitnessTableAccessorCall(
|
|
IGF, conformingType, conformance, &conformingMetadataCache));
|
|
});
|
|
|
|
return accessor;
|
|
}
|
|
|
|
static const ProtocolConformance *
|
|
mapConformanceIntoContext(const RootProtocolConformance *conf) {
|
|
if (auto *genericEnv = conf->getDeclContext()->getGenericEnvironmentOfContext())
|
|
return conf->subst(genericEnv->getForwardingSubstitutionMap()).getConcrete();
|
|
return conf;
|
|
}
|
|
|
|
WitnessIndex ProtocolInfo::getAssociatedTypeIndex(
|
|
IRGenModule &IGM,
|
|
AssociatedTypeDecl *assocType) const {
|
|
assert(!IGM.isResilient(assocType->getProtocol(),
|
|
ResilienceExpansion::Maximal) &&
|
|
"Cannot ask for the associated type index of non-resilient protocol");
|
|
for (auto &witness : getWitnessEntries()) {
|
|
if (witness.matchesAssociatedType(assocType))
|
|
return getNonBaseWitnessIndex(&witness);
|
|
}
|
|
llvm_unreachable("didn't find entry for associated type");
|
|
}
|
|
|
|
static llvm::Constant *
|
|
getConstantSignedRelativeProtocolWitnessTable(IRGenModule &IGM,
|
|
llvm::Value *table) {
|
|
auto constantTable = cast<llvm::Constant>(table);
|
|
auto &schema = IGM.getOptions().PointerAuth.RelativeProtocolWitnessTable;
|
|
constantTable =
|
|
IGM.getConstantSignedPointer(constantTable, schema, PointerAuthEntity(),
|
|
/*storageAddress*/ nullptr);
|
|
return constantTable;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Conformance info for a witness table that can be directly generated.
|
|
class DirectConformanceInfo : public ConformanceInfo {
|
|
friend ProtocolInfo;
|
|
|
|
const RootProtocolConformance *RootConformance;
|
|
public:
|
|
DirectConformanceInfo(const RootProtocolConformance *C)
|
|
: RootConformance(C) {}
|
|
|
|
llvm::Value *getTable(IRGenFunction &IGF,
|
|
llvm::Value **conformingMetadataCache) const override {
|
|
return IGF.IGM.getAddrOfWitnessTable(RootConformance);
|
|
}
|
|
|
|
llvm::Constant *tryGetConstantTable(IRGenModule &IGM,
|
|
CanType conformingType) const override {
|
|
if (IGM.getOptions().LazyInitializeProtocolConformances) {
|
|
const auto *MD = RootConformance->getDeclContext()->getParentModule();
|
|
// If the protocol conformance is defined in the current module or the
|
|
// module will be statically linked, then we can statically initialize the
|
|
// conformance as we know that the protocol conformance is guaranteed to
|
|
// be present.
|
|
if (!(MD == IGM.getSwiftModule() || MD->isStaticLibrary()))
|
|
return nullptr;
|
|
}
|
|
return IGM.getAddrOfWitnessTable(RootConformance);
|
|
}
|
|
};
|
|
|
|
/// Conformance info for a witness table that can be directly generated.
|
|
class SpecializedConformanceInfo : public ConformanceInfo {
|
|
friend ProtocolInfo;
|
|
|
|
const ProtocolConformance *Conformance;
|
|
public:
|
|
SpecializedConformanceInfo(const ProtocolConformance *C)
|
|
: Conformance(C) {}
|
|
|
|
llvm::Value *getTable(IRGenFunction &IGF,
|
|
llvm::Value **conformingMetadataCache) const override {
|
|
return IGF.IGM.getAddrOfWitnessTable(Conformance);
|
|
}
|
|
|
|
llvm::Constant *tryGetConstantTable(IRGenModule &IGM,
|
|
CanType conformingType) const override {
|
|
return IGM.getAddrOfWitnessTable(Conformance);
|
|
}
|
|
};
|
|
|
|
/// Conformance info for a witness table that is (or may be) dependent.
|
|
class AccessorConformanceInfo : public ConformanceInfo {
|
|
friend ProtocolInfo;
|
|
|
|
const ProtocolConformance *Conformance;
|
|
|
|
public:
|
|
AccessorConformanceInfo(const ProtocolConformance *C) : Conformance(C) {}
|
|
|
|
llvm::Value *getTable(IRGenFunction &IGF,
|
|
llvm::Value **typeMetadataCache) const override {
|
|
auto conformingType = Conformance->getType()->getCanonicalType();
|
|
|
|
if (isa<NormalProtocolConformance>(Conformance)) {
|
|
conformingType = conformingType->getReducedType(
|
|
Conformance->getGenericSignature());
|
|
}
|
|
|
|
// If we're looking up a dependent type, we can't cache the result.
|
|
if (conformingType->hasArchetype() ||
|
|
conformingType->hasDynamicSelfType()) {
|
|
return emitWitnessTableAccessorCall(IGF, conformingType, Conformance,
|
|
typeMetadataCache);
|
|
}
|
|
|
|
// Otherwise, call a lazy-cache function.
|
|
auto accessor =
|
|
getWitnessTableLazyAccessFunction(IGF.IGM, conformingType, Conformance);
|
|
llvm::CallInst *call =
|
|
IGF.Builder.CreateCall(accessor->getFunctionType(), accessor, {});
|
|
call->setCallingConv(IGF.IGM.DefaultCC);
|
|
call->setDoesNotAccessMemory();
|
|
call->setDoesNotThrow();
|
|
|
|
return call;
|
|
}
|
|
|
|
llvm::Constant *tryGetConstantTable(IRGenModule &IGM,
|
|
CanType conformingType) const override {
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
/// A base class for some code shared between fragile and resilient witness
|
|
/// table layout.
|
|
class WitnessTableBuilderBase {
|
|
protected:
|
|
IRGenModule &IGM;
|
|
SILWitnessTable *SILWT;
|
|
const RootProtocolConformance &Conformance;
|
|
const ProtocolConformance &ConformanceInContext;
|
|
CanType ConcreteType;
|
|
|
|
std::optional<FulfillmentMap> Fulfillments;
|
|
|
|
WitnessTableBuilderBase(IRGenModule &IGM, SILWitnessTable *SILWT)
|
|
: IGM(IGM), SILWT(SILWT),
|
|
Conformance(*SILWT->getConformance()->getRootConformance()),
|
|
ConformanceInContext(*mapConformanceIntoContext(SILWT->getConformance()->getRootConformance())),
|
|
ConcreteType(Conformance.getDeclContext()
|
|
->mapTypeIntoEnvironment(Conformance.getType())
|
|
->getCanonicalType()) {}
|
|
|
|
void defineAssociatedTypeWitnessTableAccessFunction(
|
|
AssociatedConformance requirement,
|
|
ProtocolConformanceRef conformance);
|
|
|
|
llvm::Constant *getAssociatedConformanceWitness(
|
|
AssociatedConformance requirement,
|
|
ProtocolConformanceRef conformance);
|
|
|
|
const FulfillmentMap &getFulfillmentMap() {
|
|
if (Fulfillments) return *Fulfillments;
|
|
|
|
Fulfillments.emplace();
|
|
if (ConcreteType->hasArchetype()) {
|
|
struct Callback : FulfillmentMap::InterestingKeysCallback {
|
|
bool isInterestingType(CanType type) const override {
|
|
return isa<ArchetypeType>(type);
|
|
}
|
|
bool hasInterestingType(CanType type) const override {
|
|
return type->hasArchetype();
|
|
}
|
|
bool isInterestingPackExpansion(CanPackExpansionType type) const override {
|
|
return isa<PackArchetypeType>(type.getPatternType());
|
|
}
|
|
bool hasLimitedInterestingConformances(CanType type) const override {
|
|
return false;
|
|
}
|
|
GenericSignature::RequiredProtocols
|
|
getInterestingConformances(CanType type) const override {
|
|
llvm_unreachable("no limits");
|
|
}
|
|
CanType getSuperclassBound(CanType type) const override {
|
|
if (auto superclassTy = cast<ArchetypeType>(type)->getSuperclass())
|
|
return superclassTy->getCanonicalType();
|
|
return CanType();
|
|
}
|
|
} callback;
|
|
Fulfillments->searchTypeMetadata(IGM, ConcreteType, IsExact,
|
|
MetadataState::Abstract,
|
|
/*sourceIndex*/ 0, MetadataPath(),
|
|
callback);
|
|
}
|
|
return *Fulfillments;
|
|
}
|
|
};
|
|
|
|
/// A fragile witness table is emitted to look like one in memory, except
|
|
/// possibly with some blank slots which are filled in by an instantiation
|
|
/// function.
|
|
class FragileWitnessTableBuilder : public WitnessTableBuilderBase,
|
|
public SILWitnessVisitor<FragileWitnessTableBuilder> {
|
|
ConstantArrayBuilder &Table;
|
|
unsigned TableSize = ~0U; // will get overwritten unconditionally
|
|
SmallVector<std::pair<size_t, const ConformanceInfo *>, 4>
|
|
SpecializedBaseConformances;
|
|
|
|
ArrayRef<SILWitnessTable::Entry> SILEntries;
|
|
ArrayRef<ProtocolConformanceRef> SILConditionalConformances;
|
|
|
|
const ProtocolInfo &PI;
|
|
|
|
SmallVector<size_t, 4> ConditionalRequirementPrivateDataIndices;
|
|
bool isRelative;
|
|
|
|
void addConditionalConformances() {
|
|
for (auto reqtIndex : indices(SILConditionalConformances)) {
|
|
// We don't actually need to know anything about the specific
|
|
// conformances here, just make sure we get right private data slots.
|
|
ConditionalRequirementPrivateDataIndices.push_back(reqtIndex);
|
|
}
|
|
}
|
|
|
|
public:
|
|
FragileWitnessTableBuilder(IRGenModule &IGM, ConstantArrayBuilder &table,
|
|
SILWitnessTable *SILWT, bool isRelative)
|
|
: WitnessTableBuilderBase(IGM, SILWT), Table(table),
|
|
SILEntries(SILWT->getEntries()),
|
|
SILConditionalConformances(SILWT->getConditionalConformances()),
|
|
PI(IGM.getProtocolInfo(SILWT->getConformance()->getProtocol(),
|
|
ProtocolInfoKind::Full)),
|
|
isRelative(isRelative) {}
|
|
|
|
/// The number of entries in the witness table.
|
|
unsigned getTableSize() const { return TableSize; }
|
|
|
|
/// The top-level entry point.
|
|
void build() {
|
|
addConditionalConformances();
|
|
visitProtocolDecl(Conformance.getProtocol());
|
|
TableSize = Table.size();
|
|
}
|
|
|
|
/// Add reference to the protocol conformance descriptor that generated
|
|
/// this table.
|
|
void addProtocolConformanceDescriptor() {
|
|
// In Embedded Swift, there are no protocol conformance descriptors. Emit
|
|
// a null pointer instead to keep the same layout as regular Swift.
|
|
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
|
|
Table.addNullPointer(IGM.Int8PtrTy);
|
|
return;
|
|
}
|
|
|
|
auto descriptor =
|
|
IGM.getAddrOfProtocolConformanceDescriptor(&Conformance);
|
|
if (isRelative)
|
|
Table.addRelativeAddress(descriptor);
|
|
else
|
|
Table.add(descriptor);
|
|
}
|
|
|
|
/// A base protocol is witnessed by a pointer to the conformance
|
|
/// of this type to that protocol.
|
|
void addOutOfLineBaseProtocol(ProtocolDecl *baseProto) {
|
|
auto &entry = SILEntries.front();
|
|
SILEntries = SILEntries.slice(1);
|
|
|
|
assert(entry.getKind() == SILWitnessTable::BaseProtocol
|
|
&& "sil witness table does not match protocol");
|
|
assert(entry.getBaseProtocolWitness().Requirement == baseProto
|
|
&& "sil witness table does not match protocol");
|
|
auto piIndex = PI.getBaseIndex(baseProto);
|
|
assert((size_t)piIndex.getValue() ==
|
|
Table.size() - WitnessTableFirstRequirementOffset &&
|
|
"offset doesn't match ProtocolInfo layout");
|
|
|
|
// TODO: Use the witness entry instead of falling through here.
|
|
|
|
// Look for conformance info.
|
|
ProtocolConformance *astConf = nullptr;
|
|
if (isa<SpecializedProtocolConformance>(SILWT->getConformance())) {
|
|
astConf = entry.getBaseProtocolWitness().Witness;
|
|
ASSERT(isa<SpecializedProtocolConformance>(astConf));
|
|
} else {
|
|
astConf = ConformanceInContext.getInheritedConformance(baseProto);
|
|
assert(astConf->getType()->isEqual(ConcreteType));
|
|
}
|
|
const ConformanceInfo &conf = IGM.getConformanceInfo(baseProto, astConf);
|
|
|
|
// If we can emit the base witness table as a constant, do so.
|
|
llvm::Constant *baseWitness = conf.tryGetConstantTable(IGM, ConcreteType);
|
|
|
|
if (baseWitness && isRelative) {
|
|
Table.addRelativeAddress(baseWitness);
|
|
return;
|
|
} else if (baseWitness) {
|
|
Table.add(baseWitness);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, we'll need to derive it at instantiation time.
|
|
SpecializedBaseConformances.push_back({Table.size(), &conf});
|
|
|
|
if (isRelative) {
|
|
Table.addInt32(0);
|
|
return;
|
|
}
|
|
Table.addNullPointer(IGM.Int8PtrTy);
|
|
}
|
|
|
|
void addMethod(SILDeclRef requirement) {
|
|
auto &entry = SILEntries.front();
|
|
SILEntries = SILEntries.slice(1);
|
|
|
|
bool isAsyncRequirement = requirement.hasAsync();
|
|
bool isCalleeAllocatedCoroutineRequirement =
|
|
requirement.isCalleeAllocatedCoroutine();
|
|
assert(!(isAsyncRequirement && isCalleeAllocatedCoroutineRequirement) &&
|
|
"async yield_once coroutines aren't implemented");
|
|
|
|
#ifndef NDEBUG
|
|
assert(entry.getKind() == SILWitnessTable::Method
|
|
&& "sil witness table does not match protocol");
|
|
assert(entry.getMethodWitness().Requirement == requirement
|
|
&& "sil witness table does not match protocol");
|
|
auto piIndex = PI.getFunctionIndex(requirement);
|
|
assert((size_t)piIndex.getValue() ==
|
|
Table.size() - WitnessTableFirstRequirementOffset &&
|
|
"offset doesn't match ProtocolInfo layout");
|
|
#endif
|
|
|
|
SILFunction *Func = entry.getMethodWitness().Witness;
|
|
llvm::Constant *witness = nullptr;
|
|
if (Func) {
|
|
assert(Func->isAsync() == isAsyncRequirement);
|
|
assert(Func->isCalleeAllocatedCoroutine() ==
|
|
isCalleeAllocatedCoroutineRequirement);
|
|
if (Func->isAsync()) {
|
|
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
|
|
} else if (Func->isCalleeAllocatedCoroutine()) {
|
|
witness = IGM.getAddrOfCoroFunctionPointer(Func);
|
|
} else {
|
|
|
|
auto *conformance = dyn_cast<NormalProtocolConformance>(&Conformance);
|
|
auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition);
|
|
if (IGM.getOptions().UseProfilingMarkerThunks &&
|
|
conformance &&
|
|
conformance->getDeclContext()->
|
|
getSelfNominalTypeDecl()->isGenericContext() &&
|
|
!Func->getLoweredFunctionType()->isCoroutine())
|
|
witness = IGM.getAddrOfWitnessTableProfilingThunk(f, *conformance);
|
|
else
|
|
witness = f;
|
|
}
|
|
} else {
|
|
// The method is removed by dead method elimination.
|
|
// It should be never called. We add a pointer to an error function.
|
|
if (isAsyncRequirement) {
|
|
witness = llvm::ConstantExpr::getBitCast(
|
|
IGM.getDeletedAsyncMethodErrorAsyncFunctionPointer(),
|
|
IGM.FunctionPtrTy);
|
|
} else if (isCalleeAllocatedCoroutineRequirement) {
|
|
witness = llvm::ConstantExpr::getBitCast(
|
|
IGM.getDeletedCalleeAllocatedCoroutineMethodErrorCoroFunctionPointer(),
|
|
IGM.CoroFunctionPointerPtrTy);
|
|
} else {
|
|
witness = llvm::ConstantExpr::getBitCast(
|
|
IGM.getDeletedMethodErrorFn(), IGM.FunctionPtrTy);
|
|
}
|
|
}
|
|
witness = llvm::ConstantExpr::getBitCast(witness, IGM.Int8PtrTy);
|
|
|
|
if (isRelative) {
|
|
Table.addRelativeAddress(witness);
|
|
return;
|
|
}
|
|
|
|
PointerAuthSchema schema =
|
|
isAsyncRequirement
|
|
? IGM.getOptions().PointerAuth.AsyncProtocolWitnesses
|
|
: isCalleeAllocatedCoroutineRequirement
|
|
? IGM.getOptions().PointerAuth.CoroProtocolWitnesses
|
|
: IGM.getOptions().PointerAuth.ProtocolWitnesses;
|
|
Table.addSignedPointer(witness, schema, requirement);
|
|
|
|
return;
|
|
}
|
|
|
|
void addPlaceholder(MissingMemberDecl *placeholder) {
|
|
llvm_unreachable("cannot emit a witness table with placeholders in it");
|
|
}
|
|
|
|
void addAssociatedType(AssociatedTypeDecl *assocType) {
|
|
auto &entry = SILEntries.front();
|
|
SILEntries = SILEntries.slice(1);
|
|
|
|
// In Embedded Swift witness tables don't have associated-types entries.
|
|
auto &langOpts = IGM.Context.LangOpts;
|
|
if (langOpts.hasFeature(Feature::Embedded) &&
|
|
!langOpts.hasFeature(Feature::EmbeddedExistentials))
|
|
return;
|
|
|
|
#ifndef NDEBUG
|
|
assert(entry.getKind() == SILWitnessTable::AssociatedType
|
|
&& "sil witness table does not match protocol");
|
|
assert(entry.getAssociatedTypeWitness().Requirement == assocType
|
|
&& "sil witness table does not match protocol");
|
|
auto piIndex = PI.getAssociatedTypeIndex(IGM, assocType);
|
|
assert((size_t)piIndex.getValue() ==
|
|
Table.size() - WitnessTableFirstRequirementOffset &&
|
|
"offset doesn't match ProtocolInfo layout");
|
|
#else
|
|
(void)entry;
|
|
#endif
|
|
|
|
auto typeWitness = Conformance.getTypeWitness(assocType);
|
|
|
|
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
|
// In Embedded Swift associated type witness point to the metadata.
|
|
llvm::Constant *witnessEntry = IGM.getAddrOfTypeMetadata(
|
|
typeWitness->getCanonicalType());
|
|
auto &schema = IGM.getOptions().PointerAuth
|
|
.ProtocolAssociatedTypeAccessFunctions;
|
|
Table.addSignedPointer(witnessEntry, schema, assocType);
|
|
return;
|
|
}
|
|
|
|
llvm::Constant *typeWitnessAddr =
|
|
IGM.getAssociatedTypeWitness(
|
|
typeWitness,
|
|
Conformance.getDeclContext()->getGenericSignatureOfContext(),
|
|
/*inProtocolContext=*/false);
|
|
typeWitnessAddr = llvm::ConstantExpr::getBitCast(typeWitnessAddr, IGM.Int8PtrTy);
|
|
|
|
if (isRelative) {
|
|
Table.addRelativeAddress(typeWitnessAddr);
|
|
return;
|
|
}
|
|
|
|
auto &schema = IGM.getOptions().PointerAuth
|
|
.ProtocolAssociatedTypeAccessFunctions;
|
|
Table.addSignedPointer(typeWitnessAddr, schema, assocType);
|
|
}
|
|
|
|
void addAssociatedConformance(AssociatedConformance requirement) {
|
|
// FIXME: Add static witness tables for type conformances.
|
|
|
|
auto &entry = SILEntries.front();
|
|
(void)entry;
|
|
SILEntries = SILEntries.slice(1);
|
|
auto &langOpts = IGM.Context.LangOpts;
|
|
if (langOpts.hasFeature(Feature::Embedded) &&
|
|
!langOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
|
!requirement.getAssociatedRequirement()->requiresClass()) {
|
|
// If it's not a class protocol, the associated type can never be used to create
|
|
// an existential. Therefore this witness entry is never used at runtime
|
|
// in embedded swift.
|
|
return;
|
|
}
|
|
|
|
ProtocolConformanceRef associatedConformance =
|
|
ConformanceInContext.getAssociatedConformance(
|
|
requirement.getAssociation(),
|
|
requirement.getAssociatedRequirement());
|
|
|
|
#ifndef NDEBUG
|
|
assert(entry.getKind() == SILWitnessTable::AssociatedConformance
|
|
&& "sil witness table does not match protocol");
|
|
#endif
|
|
|
|
auto associatedWitness = entry.getAssociatedConformanceWitness();
|
|
|
|
#ifndef NDEBUG
|
|
assert(associatedWitness.Requirement == requirement.getAssociation()
|
|
&& "sil witness table does not match protocol");
|
|
auto piIndex = PI.getAssociatedConformanceIndex(requirement);
|
|
assert((size_t)piIndex.getValue() ==
|
|
Table.size() - WitnessTableFirstRequirementOffset &&
|
|
"offset doesn't match ProtocolInfo layout");
|
|
#endif
|
|
|
|
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
|
|
// In Embedded Swift associated-conformance entries simply point to the witness table
|
|
// of the associated conformance.
|
|
ProtocolConformance *assocConf = associatedWitness.Witness.getConcrete();
|
|
llvm::Constant *witnessEntry = IGM.getAddrOfWitnessTable(assocConf);
|
|
auto &schema = IGM.getOptions().PointerAuth
|
|
.ProtocolAssociatedTypeWitnessTableAccessFunctions;
|
|
Table.addSignedPointer(witnessEntry, schema, requirement);
|
|
return;
|
|
}
|
|
|
|
llvm::Constant *witnessEntry =
|
|
getAssociatedConformanceWitness(requirement, associatedConformance);
|
|
|
|
if (isRelative) {
|
|
Table.addRelativeAddress(witnessEntry);
|
|
return;
|
|
}
|
|
|
|
auto &schema = IGM.getOptions().PointerAuth
|
|
.ProtocolAssociatedTypeWitnessTableAccessFunctions;
|
|
Table.addSignedPointer(witnessEntry, schema, requirement);
|
|
}
|
|
|
|
/// Build the instantiation function that runs at the end of witness
|
|
/// table specialization.
|
|
llvm::Function *buildInstantiationFunction();
|
|
};
|
|
|
|
/// A resilient witness table consists of a list of descriptor/witness pairs,
|
|
/// and a runtime function builds the actual witness table in memory, placing
|
|
/// entries in the correct oder and filling in default implementations as
|
|
/// needed.
|
|
class ResilientWitnessTableBuilder : public WitnessTableBuilderBase {
|
|
public:
|
|
ResilientWitnessTableBuilder(IRGenModule &IGM, SILWitnessTable *SILWT)
|
|
: WitnessTableBuilderBase(IGM, SILWT) {}
|
|
|
|
/// Collect the set of resilient witnesses, which will become part of the
|
|
/// protocol conformance descriptor.
|
|
void collectResilientWitnesses(
|
|
SmallVectorImpl<llvm::Constant *> &resilientWitnesses);
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
llvm::Constant *IRGenModule::getAssociatedTypeWitness(Type type,
|
|
GenericSignature sig,
|
|
bool inProtocolContext) {
|
|
// FIXME: If we can directly reference constant type metadata, do so.
|
|
|
|
// Form a reference to the mangled name for this type.
|
|
assert(!type->hasArchetype() && "type cannot contain archetypes");
|
|
auto role = inProtocolContext
|
|
? MangledTypeRefRole::DefaultAssociatedTypeWitness
|
|
: MangledTypeRefRole::Metadata;
|
|
auto typeRef = getTypeRef(type, sig, role).first;
|
|
|
|
// Set the low bit to indicate that this is a mangled name.
|
|
auto witness = llvm::ConstantExpr::getBitCast(typeRef, Int8PtrTy);
|
|
unsigned bit = ProtocolRequirementFlags::AssociatedTypeMangledNameBit;
|
|
auto bitConstant = llvm::ConstantInt::get(IntPtrTy, bit);
|
|
return llvm::ConstantExpr::getInBoundsGetElementPtr(Int8Ty, witness,
|
|
bitConstant);
|
|
}
|
|
|
|
static void buildAssociatedTypeValueName(CanType depAssociatedType,
|
|
SmallString<128> &name) {
|
|
if (auto memberType = dyn_cast<DependentMemberType>(depAssociatedType)) {
|
|
buildAssociatedTypeValueName(memberType.getBase(), name);
|
|
name += '.';
|
|
name += memberType->getName().str();
|
|
} else {
|
|
assert(isa<GenericTypeParamType>(depAssociatedType)); // Self
|
|
}
|
|
}
|
|
|
|
llvm::Constant *WitnessTableBuilderBase::getAssociatedConformanceWitness(
|
|
AssociatedConformance requirement,
|
|
ProtocolConformanceRef conformance) {
|
|
defineAssociatedTypeWitnessTableAccessFunction(requirement, conformance);
|
|
assert(isa<NormalProtocolConformance>(Conformance) && "has associated type");
|
|
auto conf = cast<NormalProtocolConformance>(&Conformance);
|
|
return IGM.getMangledAssociatedConformance(conf, requirement);
|
|
}
|
|
|
|
void WitnessTableBuilderBase::defineAssociatedTypeWitnessTableAccessFunction(
|
|
AssociatedConformance requirement,
|
|
ProtocolConformanceRef associatedConformance) {
|
|
auto associatedType = associatedConformance.getType()->getCanonicalType();
|
|
bool hasArchetype = associatedType->hasArchetype();
|
|
bool isOpaqueArchetype = isa<OpaqueTypeArchetypeType>(associatedType);
|
|
|
|
assert(isa<NormalProtocolConformance>(Conformance) && "has associated type");
|
|
|
|
// Emit an access function.
|
|
llvm::Function *accessor =
|
|
IGM.getAddrOfAssociatedTypeWitnessTableAccessFunction(
|
|
cast<NormalProtocolConformance>(&Conformance),
|
|
requirement);
|
|
|
|
IRGenFunction IGF(IGM, accessor);
|
|
if (IGM.DebugInfo)
|
|
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);
|
|
|
|
if (IGM.getOptions().optimizeForSize())
|
|
accessor->addFnAttr(llvm::Attribute::NoInline);
|
|
|
|
Explosion parameters = IGF.collectParameters();
|
|
|
|
llvm::Value *associatedTypeMetadata = parameters.claimNext();
|
|
|
|
// We use a non-standard name for the type that states the association
|
|
// requirement rather than the concrete type.
|
|
if (IGM.EnableValueNames) {
|
|
SmallString<128> name;
|
|
name += ConcreteType->getString();
|
|
buildAssociatedTypeValueName(requirement.getAssociation(), name);
|
|
associatedTypeMetadata->setName(name);
|
|
}
|
|
|
|
llvm::Value *self = parameters.claimNext();
|
|
setTypeMetadataName(IGM, self, ConcreteType);
|
|
|
|
Address destTable(parameters.claimNext(), IGM.WitnessTableTy,
|
|
IGM.getPointerAlignment());
|
|
setProtocolWitnessTableName(IGM, destTable.getAddress(), ConcreteType,
|
|
Conformance.getProtocol());
|
|
|
|
ProtocolDecl *associatedProtocol = requirement.getAssociatedRequirement();
|
|
|
|
const ConformanceInfo *conformanceI = nullptr;
|
|
|
|
if (associatedConformance.isConcrete()) {
|
|
conformanceI = &IGM.getConformanceInfo(associatedProtocol,
|
|
associatedConformance.getConcrete());
|
|
|
|
// If we can emit a constant table, do so.
|
|
if (auto constantTable =
|
|
conformanceI->tryGetConstantTable(IGM, associatedType)) {
|
|
constantTable =
|
|
getConstantSignedRelativeProtocolWitnessTable(IGM, constantTable);
|
|
IGF.Builder.CreateRet(constantTable);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If there are no archetypes, return a reference to the table.
|
|
if (!hasArchetype && !isOpaqueArchetype) {
|
|
auto wtable = conformanceI->getTable(IGF, &associatedTypeMetadata);
|
|
IGF.Builder.CreateRet(wtable);
|
|
return;
|
|
}
|
|
|
|
IGF.bindLocalTypeDataFromSelfWitnessTable(
|
|
&Conformance,
|
|
destTable.getAddress(),
|
|
[&](CanType type) {
|
|
return Conformance.getDeclContext()->mapTypeIntoEnvironment(type)
|
|
->getCanonicalType();
|
|
});
|
|
|
|
// If the witness table is directly fulfillable from the type, do so.
|
|
if (auto fulfillment =
|
|
getFulfillmentMap().getWitnessTable(associatedType,
|
|
associatedProtocol)) {
|
|
// We don't know that 'self' is any better than an abstract metadata here.
|
|
auto source = MetadataResponse::forBounded(self, MetadataState::Abstract);
|
|
|
|
llvm::Value *wtable =
|
|
fulfillment->Path.followFromTypeMetadata(IGF, ConcreteType, source,
|
|
MetadataState::Complete,
|
|
/*cache*/ nullptr)
|
|
.getMetadata();
|
|
IGF.Builder.CreateRet(wtable);
|
|
return;
|
|
}
|
|
|
|
// Bind local type data from the metadata arguments.
|
|
IGF.bindLocalTypeDataFromTypeMetadata(associatedType, IsExact,
|
|
associatedTypeMetadata,
|
|
MetadataState::Abstract);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(ConcreteType, IsExact, self,
|
|
MetadataState::Abstract);
|
|
|
|
// Find abstract conformances.
|
|
// TODO: provide an API to find the best metadata path to the conformance
|
|
// and decide whether it's expensive enough to be worth caching.
|
|
if (!conformanceI) {
|
|
assert(associatedConformance.isAbstract());
|
|
auto wtable =
|
|
emitArchetypeWitnessTableRef(IGF, cast<ArchetypeType>(associatedType),
|
|
associatedConformance.getProtocol());
|
|
IGF.Builder.CreateRet(wtable);
|
|
return;
|
|
}
|
|
|
|
// Handle concrete conformances involving archetypes.
|
|
auto wtable = conformanceI->getTable(IGF, &associatedTypeMetadata);
|
|
IGF.Builder.CreateRet(wtable);
|
|
}
|
|
|
|
void ResilientWitnessTableBuilder::collectResilientWitnesses(
|
|
SmallVectorImpl<llvm::Constant *> &resilientWitnesses) {
|
|
assert(isa<NormalProtocolConformance>(Conformance) &&
|
|
"resilient conformance should always be normal");
|
|
auto &conformance = cast<NormalProtocolConformance>(Conformance);
|
|
|
|
assert(resilientWitnesses.empty());
|
|
for (auto &entry : SILWT->getEntries()) {
|
|
// Associated type witness.
|
|
if (entry.getKind() == SILWitnessTable::AssociatedType) {
|
|
// Associated type witness.
|
|
auto assocType = entry.getAssociatedTypeWitness().Requirement;
|
|
auto associate = conformance.getTypeWitness(assocType);
|
|
|
|
llvm::Constant *witness =
|
|
IGM.getAssociatedTypeWitness(
|
|
associate,
|
|
conformance.getDeclContext()->getGenericSignatureOfContext(),
|
|
/*inProtocolContext=*/false);
|
|
resilientWitnesses.push_back(witness);
|
|
continue;
|
|
}
|
|
|
|
// Associated conformance access function.
|
|
if (entry.getKind() == SILWitnessTable::AssociatedConformance) {
|
|
const auto &witness = entry.getAssociatedConformanceWitness();
|
|
|
|
ProtocolConformanceRef associatedConformance =
|
|
ConformanceInContext.getAssociatedConformance(witness.Requirement,
|
|
witness.Witness.getProtocol());
|
|
|
|
AssociatedConformance requirement(SILWT->getProtocol(),
|
|
witness.Requirement,
|
|
witness.Witness.getProtocol());
|
|
|
|
llvm::Constant *witnessEntry =
|
|
getAssociatedConformanceWitness(requirement, associatedConformance);
|
|
resilientWitnesses.push_back(witnessEntry);
|
|
continue;
|
|
}
|
|
|
|
// Inherited conformance witnesses.
|
|
if (entry.getKind() == SILWitnessTable::BaseProtocol) {
|
|
const auto &witness = entry.getBaseProtocolWitness();
|
|
auto baseProto = witness.Requirement;
|
|
auto proto = SILWT->getProtocol();
|
|
CanType selfType = proto->getSelfInterfaceType()->getCanonicalType();
|
|
AssociatedConformance requirement(proto, selfType, baseProto);
|
|
ProtocolConformanceRef inheritedConformance =
|
|
ConformanceInContext.getAssociatedConformance(selfType, baseProto);
|
|
llvm::Constant *witnessEntry =
|
|
getAssociatedConformanceWitness(requirement, inheritedConformance);
|
|
resilientWitnesses.push_back(witnessEntry);
|
|
continue;
|
|
}
|
|
|
|
if (entry.getKind() != SILWitnessTable::Method)
|
|
continue;
|
|
|
|
SILFunction *Func = entry.getMethodWitness().Witness;
|
|
llvm::Constant *witness;
|
|
bool isGenericConformance =
|
|
conformance.getDeclContext()->getSelfNominalTypeDecl()->isGenericContext();
|
|
if (Func) {
|
|
if (Func->isAsync())
|
|
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
|
|
else if (Func->getLoweredFunctionType()->isCalleeAllocatedCoroutine())
|
|
witness = IGM.getAddrOfCoroFunctionPointer(Func);
|
|
else {
|
|
auto f = IGM.getAddrOfSILFunction(Func, NotForDefinition);
|
|
if (isGenericConformance && IGM.getOptions().UseProfilingMarkerThunks &&
|
|
!Func->getLoweredFunctionType()->isCoroutine())
|
|
witness = IGM.getAddrOfWitnessTableProfilingThunk(f, conformance);
|
|
else witness = f;
|
|
}
|
|
} else {
|
|
// The method is removed by dead method elimination.
|
|
// It should be never called. We add a null pointer.
|
|
witness = nullptr;
|
|
}
|
|
resilientWitnesses.push_back(witness);
|
|
}
|
|
}
|
|
|
|
llvm::Function *FragileWitnessTableBuilder::buildInstantiationFunction() {
|
|
// We need an instantiation function if any base conformance
|
|
// is non-dependent.
|
|
if (SpecializedBaseConformances.empty())
|
|
return nullptr;
|
|
|
|
assert(isa<NormalProtocolConformance>(Conformance) &&
|
|
"self-conformance requiring instantiation function?");
|
|
|
|
llvm::Function *fn =
|
|
IGM.getAddrOfGenericWitnessTableInstantiationFunction(
|
|
cast<NormalProtocolConformance>(&Conformance));
|
|
IRGenFunction IGF(IGM, fn);
|
|
if (IGM.DebugInfo)
|
|
IGM.DebugInfo->emitArtificialFunction(IGF, fn);
|
|
|
|
auto PointerAlignment = IGM.getPointerAlignment();
|
|
auto PointerSize = IGM.getPointerSize();
|
|
|
|
// Break out the parameters.
|
|
Explosion params = IGF.collectParameters();
|
|
Address wtable(params.claimNext(), IGM.WitnessTableTy, PointerAlignment);
|
|
llvm::Value *metadata = params.claimNext();
|
|
IGF.bindLocalTypeDataFromTypeMetadata(ConcreteType, IsExact, metadata,
|
|
MetadataState::Complete);
|
|
llvm::Value *instantiationArgs = params.claimNext();
|
|
Address conditionalTables(
|
|
IGF.Builder.CreateBitCast(instantiationArgs,
|
|
IGF.IGM.WitnessTablePtrPtrTy),
|
|
IGM.WitnessTablePtrTy, PointerAlignment);
|
|
|
|
// Register local type data for the conditional conformance witness tables.
|
|
for (auto idx : indices(ConditionalRequirementPrivateDataIndices)) {
|
|
Address conditionalTablePtr =
|
|
IGF.Builder.CreateConstArrayGEP(conditionalTables, idx, PointerSize);
|
|
auto conditionalTable = IGF.Builder.CreateLoad(conditionalTablePtr);
|
|
|
|
const auto &condConformance = SILConditionalConformances[idx];
|
|
CanType reqTypeInContext =
|
|
Conformance.getDeclContext()
|
|
->mapTypeIntoEnvironment(condConformance.getType())
|
|
->getCanonicalType();
|
|
if (auto archetype = dyn_cast<ArchetypeType>(reqTypeInContext)) {
|
|
auto condProto = condConformance.getProtocol();
|
|
IGF.setUnscopedLocalTypeData(
|
|
archetype,
|
|
LocalTypeDataKind::forAbstractProtocolWitnessTable(condProto),
|
|
conditionalTable);
|
|
}
|
|
}
|
|
|
|
// Initialize all the specialized base conformances.
|
|
for (auto &base : SpecializedBaseConformances) {
|
|
// Ask the ConformanceInfo to emit the wtable.
|
|
llvm::Value *baseWTable =
|
|
base.second->getTable(IGF, &metadata);
|
|
|
|
baseWTable = IGF.Builder.CreateBitCast(baseWTable, IGM.Int8PtrTy);
|
|
|
|
// Store that to the appropriate slot in the new witness table.
|
|
Address slot =
|
|
IGF.Builder.CreateConstArrayGEP(wtable, base.first, PointerSize);
|
|
IGF.Builder.CreateStore(baseWTable, slot);
|
|
}
|
|
|
|
|
|
IGF.Builder.CreateRetVoid();
|
|
|
|
return fn;
|
|
}
|
|
|
|
/// Produce a reference for the conforming entity of a protocol conformance
|
|
/// descriptor, which is usually the conforming concrete type.
|
|
static TypeEntityReference getConformingEntityReference(
|
|
IRGenModule &IGM, const RootProtocolConformance *conformance) {
|
|
// This is a conformance of a protocol to another protocol. Module it as
|
|
// an extension.
|
|
if (isa<NormalProtocolConformance>(conformance) &&
|
|
cast<NormalProtocolConformance>(conformance)->isConformanceOfProtocol()) {
|
|
auto ext = cast<ExtensionDecl>(conformance->getDeclContext());
|
|
auto linkEntity = LinkEntity::forExtensionDescriptor(ext);
|
|
IGM.IRGen.noteUseOfExtensionDescriptor(ext);
|
|
return IGM.getContextDescriptorEntityReference(linkEntity);
|
|
}
|
|
|
|
return IGM.getTypeEntityReference(
|
|
conformance->getDeclContext()->getSelfNominalTypeDecl());
|
|
}
|
|
|
|
namespace {
|
|
/// Builds a protocol conformance descriptor.
|
|
class ProtocolConformanceDescriptorBuilder {
|
|
IRGenModule &IGM;
|
|
ConstantStructBuilder &B;
|
|
const RootProtocolConformance *Conformance;
|
|
SILWitnessTable *SILWT;
|
|
ConformanceDescription Description;
|
|
ConformanceFlags Flags;
|
|
|
|
using PlaceholderPosition =
|
|
ConstantAggregateBuilderBase::PlaceholderPosition;
|
|
std::optional<PlaceholderPosition> FlagsPP;
|
|
|
|
public:
|
|
ProtocolConformanceDescriptorBuilder(
|
|
IRGenModule &IGM,
|
|
ConstantStructBuilder &B,
|
|
const ConformanceDescription &description)
|
|
: IGM(IGM), B(B), Conformance(description.conformance),
|
|
SILWT(description.wtable), Description(description) { }
|
|
|
|
void layout() {
|
|
addProtocol();
|
|
addConformingType();
|
|
addWitnessTable();
|
|
addFlags();
|
|
addContext();
|
|
addConditionalRequirements();
|
|
addResilientWitnesses();
|
|
addGenericWitnessTable();
|
|
addGlobalActorIsolation();
|
|
|
|
// We fill the flags last, since we continue filling them in
|
|
// after the call to addFlags() deposits the placeholder.
|
|
B.fillPlaceholderWithInt(*FlagsPP, IGM.Int32Ty,
|
|
Flags.getIntValue());
|
|
|
|
B.suggestType(IGM.ProtocolConformanceDescriptorTy);
|
|
}
|
|
|
|
void addProtocol() {
|
|
// Relative reference to the protocol descriptor.
|
|
auto protocol = Conformance->getProtocol();
|
|
auto descriptorRef = IGM.getAddrOfLLVMVariableOrGOTEquivalent(
|
|
LinkEntity::forProtocolDescriptor(protocol));
|
|
B.addRelativeAddress(descriptorRef);
|
|
}
|
|
|
|
void addConformingType() {
|
|
// Add a relative reference to the type, with the type reference
|
|
// kind stored in the flags.
|
|
auto ref = getConformingEntityReference(IGM, Conformance);
|
|
B.addRelativeAddress(ref.getValue());
|
|
Flags = Flags.withTypeReferenceKind(ref.getKind());
|
|
}
|
|
|
|
void addWitnessTable() {
|
|
// Relative reference to the witness table.
|
|
B.addRelativeAddressOrNull(Description.pattern);
|
|
}
|
|
|
|
void addFlags() {
|
|
// Miscellaneous flags.
|
|
if (auto conf = dyn_cast<NormalProtocolConformance>(Conformance)) {
|
|
auto isolation = conf->getIsolation();
|
|
Flags = Flags.withIsRetroactive(conf->isRetroactive());
|
|
Flags = Flags.withIsSynthesizedNonUnique(conf->isSynthesizedNonUnique());
|
|
Flags = Flags.withIsConformanceOfProtocol(conf->isConformanceOfProtocol());
|
|
Flags = Flags.withHasGlobalActorIsolation(isolation.isGlobalActor());
|
|
} else {
|
|
Flags = Flags.withIsRetroactive(false)
|
|
.withIsSynthesizedNonUnique(false);
|
|
}
|
|
|
|
// Add a placeholder for the flags.
|
|
FlagsPP = B.addPlaceholderWithSize(IGM.Int32Ty);
|
|
}
|
|
|
|
void addContext() {
|
|
auto normal = dyn_cast<NormalProtocolConformance>(Conformance);
|
|
if (!normal || !normal->isRetroactive())
|
|
return;
|
|
|
|
auto moduleContext =
|
|
normal->getDeclContext()->getModuleScopeContext();
|
|
ConstantReference moduleContextRef =
|
|
IGM.getAddrOfParentContextDescriptor(moduleContext,
|
|
/*fromAnonymousContext=*/false);
|
|
B.addRelativeAddress(moduleContextRef);
|
|
}
|
|
|
|
void addConditionalRequirements() {
|
|
auto normal = dyn_cast<NormalProtocolConformance>(Conformance);
|
|
if (!normal)
|
|
return;
|
|
|
|
// Compute the inverse requirements from the generic signature where the
|
|
// conformance occurs.
|
|
SmallVector<Requirement, 2> condReqs;
|
|
SmallVector<InverseRequirement, 2> inverses;
|
|
if (auto genericSig =
|
|
normal->getDeclContext()->getGenericSignatureOfContext()) {
|
|
genericSig->getRequirementsWithInverses(condReqs, inverses);
|
|
}
|
|
condReqs.clear();
|
|
|
|
for (auto condReq : normal->getConditionalRequirements()) {
|
|
// We don't need to collect conditional requirements for invertible
|
|
// protocol requirements here, since they are encoded in the inverse
|
|
// list above.
|
|
if (!condReq.isInvertibleProtocolRequirement()) {
|
|
condReqs.push_back(condReq);
|
|
}
|
|
}
|
|
if (condReqs.empty()) {
|
|
// For a protocol P that conforms to another protocol, introduce a
|
|
// conditional requirement for that P's Self: P. This aligns with
|
|
// SILWitnessTable::enumerateWitnessTableConditionalConformances().
|
|
if (auto selfProto = normal->getDeclContext()->getSelfProtocolDecl()) {
|
|
auto selfType = selfProto->getSelfInterfaceType()->getCanonicalType();
|
|
condReqs.emplace_back(RequirementKind::Conformance, selfType,
|
|
selfProto->getDeclaredInterfaceType());
|
|
}
|
|
|
|
if (condReqs.empty() && inverses.empty())
|
|
return;
|
|
}
|
|
|
|
auto nominal = normal->getDeclContext()->getSelfNominalTypeDecl();
|
|
auto sig = nominal->getGenericSignatureOfContext();
|
|
auto metadata = irgen::addGenericRequirements(
|
|
IGM, B, sig, condReqs, inverses);
|
|
|
|
Flags = Flags.withNumConditionalRequirements(metadata.NumRequirements);
|
|
Flags = Flags.withNumConditionalPackDescriptors(
|
|
metadata.GenericPackArguments.size());
|
|
|
|
// Collect the shape classes from the nominal type's generic signature.
|
|
sig->forEachParam([&](GenericTypeParamType *param, bool canonical) {
|
|
if (canonical && param->isParameterPack()) {
|
|
auto reducedShape = sig->getReducedShape(param)->getCanonicalType();
|
|
if (reducedShape->isEqual(param))
|
|
metadata.ShapeClasses.push_back(reducedShape);
|
|
}
|
|
});
|
|
|
|
irgen::addGenericPackShapeDescriptors(
|
|
IGM, B, metadata.ShapeClasses,
|
|
metadata.GenericPackArguments);
|
|
}
|
|
|
|
void addResilientWitnesses() {
|
|
if (Description.resilientWitnesses.empty())
|
|
return;
|
|
assert(!IGM.IRGen.Opts.UseFragileResilientProtocolWitnesses);
|
|
|
|
Flags = Flags.withHasResilientWitnesses(true);
|
|
|
|
// TargetResilientWitnessesHeader
|
|
ArrayRef<llvm::Constant *> witnesses = Description.resilientWitnesses;
|
|
B.addInt32(witnesses.size());
|
|
for (const auto &entry : SILWT->getEntries()) {
|
|
// Add the requirement descriptor.
|
|
if (entry.getKind() == SILWitnessTable::AssociatedType) {
|
|
// Associated type descriptor.
|
|
auto assocType = entry.getAssociatedTypeWitness().Requirement;
|
|
auto assocTypeDescriptor =
|
|
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
|
|
LinkEntity::forAssociatedTypeDescriptor(assocType));
|
|
B.addRelativeAddress(assocTypeDescriptor);
|
|
} else if (entry.getKind() == SILWitnessTable::AssociatedConformance) {
|
|
// Associated conformance descriptor.
|
|
const auto &witness = entry.getAssociatedConformanceWitness();
|
|
|
|
AssociatedConformance requirement(SILWT->getProtocol(),
|
|
witness.Requirement,
|
|
witness.Witness.getProtocol());
|
|
auto assocConformanceDescriptor =
|
|
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
|
|
LinkEntity::forAssociatedConformanceDescriptor(requirement));
|
|
B.addRelativeAddress(assocConformanceDescriptor);
|
|
} else if (entry.getKind() == SILWitnessTable::BaseProtocol) {
|
|
// Associated conformance descriptor for a base protocol.
|
|
const auto &witness = entry.getBaseProtocolWitness();
|
|
auto proto = SILWT->getProtocol();
|
|
BaseConformance requirement(proto, witness.Requirement);
|
|
auto baseConformanceDescriptor =
|
|
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
|
|
LinkEntity::forBaseConformanceDescriptor(requirement));
|
|
B.addRelativeAddress(baseConformanceDescriptor);
|
|
} else if (entry.getKind() == SILWitnessTable::Method) {
|
|
// Method descriptor.
|
|
auto declRef = entry.getMethodWitness().Requirement;
|
|
auto requirement =
|
|
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
|
|
LinkEntity::forMethodDescriptor(declRef));
|
|
B.addRelativeAddress(requirement);
|
|
} else {
|
|
// Not part of the resilient witness table.
|
|
continue;
|
|
}
|
|
|
|
// Add the witness.
|
|
llvm::Constant *witness = witnesses.front();
|
|
if (auto *fn = llvm::dyn_cast<llvm::Function>(witness)) {
|
|
B.addCompactFunctionReference(fn);
|
|
} else {
|
|
B.addRelativeAddress(witness);
|
|
}
|
|
witnesses = witnesses.drop_front();
|
|
}
|
|
assert(witnesses.empty() && "Wrong # of resilient witnesses");
|
|
}
|
|
|
|
void addGenericWitnessTable() {
|
|
if (!Description.requiresSpecialization)
|
|
return;
|
|
|
|
Flags = Flags.withHasGenericWitnessTable(true);
|
|
|
|
// WitnessTableSizeInWords
|
|
B.addInt(IGM.Int16Ty, Description.witnessTableSize);
|
|
// WitnessTablePrivateSizeInWordsAndRequiresInstantiation
|
|
B.addInt(IGM.Int16Ty,
|
|
(Description.witnessTablePrivateSize << 1) |
|
|
Description.requiresSpecialization);
|
|
// Instantiation function
|
|
B.addCompactFunctionReferenceOrNull(Description.instantiationFn);
|
|
|
|
// Private data
|
|
if (IGM.IRGen.Opts.NoPreallocatedInstantiationCaches) {
|
|
B.addInt32(0);
|
|
} else {
|
|
auto privateDataTy =
|
|
llvm::ArrayType::get(IGM.Int8PtrTy,
|
|
swift::NumGenericMetadataPrivateDataWords);
|
|
auto privateDataInit = llvm::Constant::getNullValue(privateDataTy);
|
|
|
|
IRGenMangler mangler(IGM.Context);
|
|
auto symbolName =
|
|
mangler.mangleProtocolConformanceInstantiationCache(Conformance);
|
|
|
|
auto privateData =
|
|
new llvm::GlobalVariable(IGM.Module, privateDataTy,
|
|
/*constant*/ false,
|
|
llvm::GlobalVariable::InternalLinkage,
|
|
privateDataInit, symbolName);
|
|
B.addRelativeAddress(privateData);
|
|
}
|
|
}
|
|
|
|
void addGlobalActorIsolation() {
|
|
if (!Flags.hasGlobalActorIsolation())
|
|
return;
|
|
|
|
auto normal = cast<NormalProtocolConformance>(Conformance);
|
|
auto nominal = normal->getDeclContext()->getSelfNominalTypeDecl();
|
|
|
|
// Add global actor type.
|
|
auto sig = nominal->getGenericSignatureOfContext();
|
|
auto isolation = Conformance->getIsolation();
|
|
assert(isolation.isGlobalActor());
|
|
Type globalActorType = isolation.getGlobalActor();
|
|
auto globalActorTypeName = IGM.getTypeRef(
|
|
globalActorType, sig, MangledTypeRefRole::Metadata).first;
|
|
B.addRelativeAddress(globalActorTypeName);
|
|
|
|
// Add conformance of the global actor type to the GlobalActor protocol.
|
|
SmallVector<ProtocolConformance *, 1> globalActorConformances;
|
|
auto globalActorProtocol =
|
|
IGM.Context.getProtocol(KnownProtocolKind::GlobalActor);
|
|
auto globalActorConformance = lookupConformance(
|
|
globalActorType, globalActorProtocol);
|
|
|
|
auto rootGlobalActorConformance = globalActorConformance.getConcrete()
|
|
->getRootConformance();
|
|
IGM.IRGen.addLazyWitnessTable(rootGlobalActorConformance);
|
|
|
|
auto globalActorConformanceDescriptor =
|
|
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
|
|
LinkEntity::forProtocolConformanceDescriptor(
|
|
rootGlobalActorConformance));
|
|
B.addRelativeAddress(globalActorConformanceDescriptor);
|
|
}
|
|
|
|
};
|
|
}
|
|
|
|
void IRGenModule::emitProtocolConformance(
|
|
const ConformanceDescription &record) {
|
|
auto conformance = record.conformance;
|
|
|
|
// Emit additional metadata to be used by reflection.
|
|
emitAssociatedTypeMetadataRecord(conformance);
|
|
|
|
// Form the protocol conformance descriptor.
|
|
ConstantInitBuilder initBuilder(*this);
|
|
auto init = initBuilder.beginStruct();
|
|
ProtocolConformanceDescriptorBuilder builder(*this, init, record);
|
|
builder.layout();
|
|
|
|
auto var =
|
|
cast<llvm::GlobalVariable>(
|
|
getAddrOfProtocolConformanceDescriptor(conformance,
|
|
init.finishAndCreateFuture()));
|
|
var->setConstant(true);
|
|
setTrueConstGlobal(var);
|
|
}
|
|
|
|
void IRGenerator::ensureRelativeSymbolCollocation(SILWitnessTable &wt) {
|
|
if (!CurrentIGM)
|
|
return;
|
|
|
|
// Only resilient conformances use relative pointers for witness methods.
|
|
if (wt.isDeclaration() || isAvailableExternally(wt.getLinkage()) ||
|
|
!CurrentIGM->isResilientConformance(wt.getConformance()->getRootConformance()))
|
|
return;
|
|
|
|
for (auto &entry : wt.getEntries()) {
|
|
if (entry.getKind() != SILWitnessTable::Method)
|
|
continue;
|
|
auto *witness = entry.getMethodWitness().Witness;
|
|
if (witness)
|
|
forceLocalEmitOfLazyFunction(witness);
|
|
}
|
|
}
|
|
|
|
void IRGenerator::ensureRelativeSymbolCollocation(SILDefaultWitnessTable &wt) {
|
|
if (!CurrentIGM)
|
|
return;
|
|
|
|
for (auto &entry : wt.getEntries()) {
|
|
if (entry.getKind() != SILWitnessTable::Method)
|
|
continue;
|
|
auto *witness = entry.getMethodWitness().Witness;
|
|
if (witness)
|
|
forceLocalEmitOfLazyFunction(witness);
|
|
}
|
|
}
|
|
|
|
/// Do a memoized witness-table layout for a protocol.
|
|
const ProtocolInfo &IRGenModule::getProtocolInfo(ProtocolDecl *protocol,
|
|
ProtocolInfoKind kind) {
|
|
// If the protocol is resilient, we cannot know the full witness table layout.
|
|
assert(!isResilient(protocol, ResilienceExpansion::Maximal) ||
|
|
kind == ProtocolInfoKind::RequirementSignature);
|
|
|
|
return Types.getProtocolInfo(protocol, kind);
|
|
}
|
|
|
|
/// Do a memoized witness-table layout for a protocol.
|
|
const ProtocolInfo &TypeConverter::getProtocolInfo(ProtocolDecl *protocol,
|
|
ProtocolInfoKind kind) {
|
|
// Check whether we've already translated this protocol.
|
|
auto it = Protocols.find(protocol);
|
|
if (it != Protocols.end() && it->getSecond()->getKind() >= kind)
|
|
return *it->getSecond();
|
|
|
|
// If not, lay out the protocol's witness table, if it needs one.
|
|
WitnessTableLayout layout(kind);
|
|
if (Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
|
|
layout.visitProtocolDecl(protocol);
|
|
|
|
// Create a ProtocolInfo object from the layout.
|
|
std::unique_ptr<ProtocolInfo> info = ProtocolInfo::create(layout.getEntries(),
|
|
kind);
|
|
|
|
// Verify that we haven't generated an incompatible layout.
|
|
if (it != Protocols.end()) {
|
|
ArrayRef<WitnessTableEntry> originalEntries =
|
|
it->second->getWitnessEntries();
|
|
ArrayRef<WitnessTableEntry> newEntries = info->getWitnessEntries();
|
|
assert(newEntries.size() >= originalEntries.size());
|
|
assert(newEntries.take_front(originalEntries.size()) == originalEntries);
|
|
(void)originalEntries;
|
|
(void)newEntries;
|
|
}
|
|
|
|
// Memoize.
|
|
std::unique_ptr<const ProtocolInfo> &cachedInfo = Protocols[protocol];
|
|
cachedInfo = std::move(info);
|
|
|
|
// Done.
|
|
return *cachedInfo;
|
|
}
|
|
|
|
/// Allocate a new ProtocolInfo.
|
|
std::unique_ptr<ProtocolInfo>
|
|
ProtocolInfo::create(ArrayRef<WitnessTableEntry> table, ProtocolInfoKind kind) {
|
|
size_t bufferSize = totalSizeToAlloc<WitnessTableEntry>(table.size());
|
|
void *buffer = ::operator new(bufferSize);
|
|
return std::unique_ptr<ProtocolInfo>(new(buffer) ProtocolInfo(table, kind));
|
|
}
|
|
|
|
// Provide a unique home for the ConformanceInfo vtable.
|
|
void ConformanceInfo::anchor() {}
|
|
|
|
/// Find the conformance information for a protocol.
|
|
const ConformanceInfo &
|
|
IRGenModule::getConformanceInfo(const ProtocolDecl *protocol,
|
|
const ProtocolConformance *conformance) {
|
|
assert(conformance->getProtocol() == protocol &&
|
|
"conformance is for wrong protocol");
|
|
|
|
auto checkCache =
|
|
[this](const ProtocolConformance *conf) -> const ConformanceInfo * {
|
|
// Check whether we've already cached this.
|
|
auto it = Conformances.find(conf);
|
|
if (it != Conformances.end())
|
|
return it->second.get();
|
|
|
|
return nullptr;
|
|
};
|
|
|
|
if (auto found = checkCache(conformance))
|
|
return *found;
|
|
|
|
const ConformanceInfo *info;
|
|
|
|
auto *specConf = conformance;
|
|
if (auto *inheritedC = dyn_cast<InheritedProtocolConformance>(conformance)) {
|
|
SILWitnessTable *wt = getSILModule().lookUpWitnessTable(inheritedC);
|
|
if (wt && wt->getConformance() == inheritedC) {
|
|
info = new SpecializedConformanceInfo(inheritedC);
|
|
Conformances.try_emplace(conformance, info);
|
|
return *info;
|
|
}
|
|
specConf = inheritedC->getInheritedConformance();
|
|
}
|
|
|
|
// If there is a specialized SILWitnessTable for the specialized conformance,
|
|
// directly use it.
|
|
if (auto *sc = dyn_cast<SpecializedProtocolConformance>(specConf)) {
|
|
SILWitnessTable *wt = getSILModule().lookUpWitnessTable(specConf);
|
|
if (wt && wt->getConformance() == sc) {
|
|
info = new SpecializedConformanceInfo(sc);
|
|
Conformances.try_emplace(conformance, info);
|
|
return *info;
|
|
}
|
|
}
|
|
|
|
// Drill down to the root normal
|
|
auto rootConformance = conformance->getRootConformance();
|
|
|
|
// If the conformance is dependent in any way, we need to unique it.
|
|
// Under a relative protocol witness table implementation conformances are
|
|
// always direct.
|
|
//
|
|
// FIXME: Both implementations of ConformanceInfo are trivially-destructible,
|
|
// so in theory we could allocate them on a BumpPtrAllocator. But there's not
|
|
// a good one for us to use. (The ASTContext's outlives the IRGenModule in
|
|
// batch mode.)
|
|
if (!IRGen.Opts.UseRelativeProtocolWitnessTables &&
|
|
(isDependentConformance(rootConformance) ||
|
|
// Foreign types need to go through the accessor to unique the witness
|
|
// table.
|
|
isSynthesizedNonUnique(rootConformance))) {
|
|
info = new AccessorConformanceInfo(conformance);
|
|
Conformances.try_emplace(conformance, info);
|
|
} else if(IRGen.Opts.UseRelativeProtocolWitnessTables &&
|
|
hasConditionalConformances(*this, rootConformance)) {
|
|
info = new AccessorConformanceInfo(conformance);
|
|
Conformances.try_emplace(conformance, info);
|
|
} else {
|
|
// Otherwise, we can use a direct-referencing conformance, which can get
|
|
// away with the non-specialized conformance.
|
|
if (auto found = checkCache(rootConformance))
|
|
return *found;
|
|
|
|
info = new DirectConformanceInfo(rootConformance);
|
|
Conformances.try_emplace(rootConformance, info);
|
|
}
|
|
|
|
return *info;
|
|
}
|
|
|
|
/// Whether the witness table will be constant.
|
|
static bool isConstantWitnessTable(SILWitnessTable *wt) {
|
|
for (const auto &entry : wt->getEntries()) {
|
|
switch (entry.getKind()) {
|
|
case SILWitnessTable::Invalid:
|
|
case SILWitnessTable::BaseProtocol:
|
|
case SILWitnessTable::Method:
|
|
continue;
|
|
|
|
case SILWitnessTable::AssociatedType:
|
|
case SILWitnessTable::AssociatedConformance:
|
|
// Associated types and conformances are cached in the witness table.
|
|
// FIXME: If we start emitting constant references to here,
|
|
// we will need to ask the witness table builder for this information.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void addWTableTypeMetadata(IRGenModule &IGM,
|
|
llvm::GlobalVariable *global,
|
|
SILWitnessTable *wt) {
|
|
auto conf = wt->getConformance();
|
|
|
|
uint64_t minOffset = UINT64_MAX;
|
|
uint64_t maxOffset = 0;
|
|
for (auto entry : wt->getEntries()) {
|
|
if (entry.getKind() != SILWitnessTable::WitnessKind::Method)
|
|
continue;
|
|
|
|
auto mw = entry.getMethodWitness();
|
|
auto member = mw.Requirement;
|
|
|
|
auto &fnProtoInfo =
|
|
IGM.getProtocolInfo(conf->getProtocol(), ProtocolInfoKind::Full);
|
|
auto index = fnProtoInfo.getFunctionIndex(member).forProtocolWitnessTable();
|
|
auto entrySize = IGM.IRGen.Opts.UseRelativeProtocolWitnessTables ?
|
|
4 : IGM.getPointerSize().getValue();
|
|
auto offset = index.getValue() * entrySize;
|
|
global->addTypeMetadata(offset, typeIdForMethod(IGM, member));
|
|
|
|
minOffset = std::min(minOffset, offset);
|
|
maxOffset = std::max(maxOffset, offset);
|
|
}
|
|
|
|
if (minOffset == UINT64_MAX)
|
|
return;
|
|
|
|
using VCallVisibility = llvm::GlobalObject::VCallVisibility;
|
|
VCallVisibility vis = VCallVisibility::VCallVisibilityPublic;
|
|
auto linkage = stripExternalFromLinkage(wt->getLinkage());
|
|
switch (linkage) {
|
|
case SILLinkage::Private:
|
|
vis = VCallVisibility::VCallVisibilityTranslationUnit;
|
|
break;
|
|
case SILLinkage::Hidden:
|
|
case SILLinkage::Shared:
|
|
vis = VCallVisibility::VCallVisibilityLinkageUnit;
|
|
break;
|
|
case SILLinkage::Public:
|
|
case SILLinkage::PublicExternal:
|
|
case SILLinkage::PublicNonABI:
|
|
case SILLinkage::Package:
|
|
case SILLinkage::PackageExternal:
|
|
case SILLinkage::PackageNonABI:
|
|
case SILLinkage::HiddenExternal:
|
|
if (IGM.getOptions().InternalizeAtLink) {
|
|
vis = VCallVisibility::VCallVisibilityLinkageUnit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
auto relptrSize = IGM.DataLayout.getTypeAllocSize(IGM.Int32Ty).getKnownMinValue();
|
|
IGM.setVCallVisibility(global, vis,
|
|
std::make_pair(minOffset, maxOffset + relptrSize));
|
|
}
|
|
|
|
void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) {
|
|
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
|
|
// In Embedded Swift, only class-bound wtables are allowed.
|
|
if (!wt->getConformance()->getProtocol()->requiresClass() &&
|
|
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
|
return;
|
|
}
|
|
|
|
// Don't emit a witness table if it is a declaration.
|
|
if (wt->isDeclaration())
|
|
return;
|
|
|
|
// Don't emit a witness table that is available externally.
|
|
// It can end up in having duplicate symbols for generated associated type
|
|
// metadata access functions.
|
|
// Also, it is not a big benefit for LLVM to emit such witness tables.
|
|
if (isAvailableExternally(wt->getLinkage()))
|
|
return;
|
|
|
|
// Ensure that relatively-referenced symbols for witness thunks are collocated
|
|
// in the same LLVM module.
|
|
IRGen.ensureRelativeSymbolCollocation(*wt);
|
|
|
|
auto conf = wt->getConformance();
|
|
RootProtocolConformance *rootConf = conf->getRootConformance();
|
|
PrettyStackTraceConformance _st("emitting witness table for", conf);
|
|
|
|
unsigned tableSize = 0;
|
|
llvm::GlobalVariable *global = nullptr;
|
|
llvm::Function *instantiationFunction = nullptr;
|
|
bool isDependent = isDependentConformance(rootConf);
|
|
SmallVector<llvm::Constant *, 4> resilientWitnesses;
|
|
bool isResilient = isResilientConformance(rootConf);
|
|
bool useRelativeProtocolWitnessTable =
|
|
IRGen.Opts.UseRelativeProtocolWitnessTables;
|
|
if (useRelativeProtocolWitnessTable &&
|
|
!conf->getConditionalRequirements().empty()) {
|
|
auto sig = conf->getGenericSignature();
|
|
sig->forEachParam([&](GenericTypeParamType *param, bool canonical) {
|
|
if (param->isParameterPack()) {
|
|
#ifndef NDEBUG
|
|
wt->dump();
|
|
#endif
|
|
llvm::report_fatal_error("use of relative protcol witness tables not supported");
|
|
}});
|
|
}
|
|
if (!isResilient) {
|
|
// Build the witness table.
|
|
ConstantInitBuilder builder(*this);
|
|
auto witnessTableEntryTy = useRelativeProtocolWitnessTable ?
|
|
(llvm::Type*)RelativeAddressTy : (llvm::Type*)Int8PtrTy;
|
|
auto wtableContents = builder.beginArray(witnessTableEntryTy);
|
|
FragileWitnessTableBuilder wtableBuilder(*this, wtableContents, wt,
|
|
useRelativeProtocolWitnessTable);
|
|
wtableBuilder.build();
|
|
|
|
// Produce the initializer value.
|
|
auto initializer = wtableContents.finishAndCreateFuture();
|
|
|
|
auto *normalConf = dyn_cast<NormalProtocolConformance>(conf);
|
|
global = cast<llvm::GlobalVariable>(
|
|
(isDependent && conf->getDeclContext()->isGenericContext() &&
|
|
!useRelativeProtocolWitnessTable && normalConf)
|
|
? getAddrOfWitnessTablePattern(normalConf, initializer)
|
|
: getAddrOfWitnessTable(conf, initializer));
|
|
// Eelative protocol witness tables are always constant. They don't cache
|
|
// results in the table.
|
|
global->setConstant(useRelativeProtocolWitnessTable ||
|
|
isConstantWitnessTable(wt));
|
|
global->setAlignment(
|
|
llvm::MaybeAlign(getWitnessTableAlignment().getValue()));
|
|
|
|
if (getOptions().WitnessMethodElimination) {
|
|
addWTableTypeMetadata(*this, global, wt);
|
|
}
|
|
|
|
tableSize = wtableBuilder.getTableSize();
|
|
instantiationFunction = wtableBuilder.buildInstantiationFunction();
|
|
} else {
|
|
if (IRGen.Opts.UseRelativeProtocolWitnessTables)
|
|
llvm::report_fatal_error("resilient relative protocol witness tables are not supported");
|
|
|
|
// Build the witness table.
|
|
ResilientWitnessTableBuilder wtableBuilder(*this, wt);
|
|
|
|
// Collect the resilient witnesses to go into the conformance descriptor.
|
|
wtableBuilder.collectResilientWitnesses(resilientWitnesses);
|
|
}
|
|
|
|
if (!Context.LangOpts.hasFeature(Feature::Embedded)) {
|
|
// Collect the information that will go into the protocol conformance
|
|
// descriptor.
|
|
unsigned tablePrivateSize = wt->getConditionalConformances().size();
|
|
ConformanceDescription description(rootConf, wt, global, tableSize,
|
|
tablePrivateSize, isDependent);
|
|
|
|
// Build the instantiation function, we if need one.
|
|
description.instantiationFn = instantiationFunction;
|
|
description.resilientWitnesses = std::move(resilientWitnesses);
|
|
|
|
// Record this conformance descriptor.
|
|
addProtocolConformance(std::move(description));
|
|
|
|
IRGen.noteUseOfTypeContextDescriptor(
|
|
conf->getDeclContext()->getSelfNominalTypeDecl(),
|
|
RequireMetadata);
|
|
}
|
|
}
|
|
|
|
/// True if a function's signature in LLVM carries polymorphic parameters.
|
|
/// Generic functions and protocol witnesses carry polymorphic parameters.
|
|
bool irgen::hasPolymorphicParameters(CanSILFunctionType ty) {
|
|
switch (ty->getRepresentation()) {
|
|
case SILFunctionTypeRepresentation::Block:
|
|
// Should never be polymorphic.
|
|
assert(!ty->isPolymorphic() && "polymorphic C function?!");
|
|
return false;
|
|
|
|
case SILFunctionTypeRepresentation::Thick:
|
|
case SILFunctionTypeRepresentation::Thin:
|
|
case SILFunctionTypeRepresentation::Method:
|
|
case SILFunctionTypeRepresentation::Closure:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
|
|
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
|
|
return ty->isPolymorphic();
|
|
|
|
case SILFunctionTypeRepresentation::CFunctionPointer:
|
|
case SILFunctionTypeRepresentation::ObjCMethod:
|
|
case SILFunctionTypeRepresentation::CXXMethod:
|
|
// May be polymorphic at the SIL level, but no type metadata is actually
|
|
// passed.
|
|
return false;
|
|
|
|
case SILFunctionTypeRepresentation::WitnessMethod:
|
|
// Always carries polymorphic parameters for the Self type.
|
|
return true;
|
|
}
|
|
|
|
llvm_unreachable("Not a valid SILFunctionTypeRepresentation.");
|
|
}
|
|
|
|
/// Emit a polymorphic parameters clause, binding all the metadata necessary.
|
|
void EmitPolymorphicParameters::emit(EntryPointArgumentEmission &emission,
|
|
WitnessMetadata *witnessMetadata,
|
|
const GetParameterFn &getParameter) {
|
|
// Collect any early sources and bind local type data from them.
|
|
for (auto &source : getSources()) {
|
|
bindExtraSource(source, emission, witnessMetadata);
|
|
}
|
|
|
|
auto subs = Fn.getForwardingSubstitutionMap();
|
|
|
|
// Collect any concrete type metadata that's been passed separately.
|
|
enumerateUnfulfilledRequirements([&](GenericRequirement requirement) {
|
|
llvm::Value *value = emission.getNextPolymorphicParameter(requirement);
|
|
bindGenericRequirement(IGF, requirement, value, MetadataState::Complete,
|
|
subs);
|
|
});
|
|
|
|
// Bind all the fulfillments we can from the formal parameters.
|
|
bindParameterSources(getParameter);
|
|
|
|
// Inject ad-hoc requirements related to `SerializationRequirement`
|
|
// associated type.
|
|
injectAdHocDistributedRequirements();
|
|
}
|
|
|
|
MetadataResponse
|
|
MetadataPath::followFromTypeMetadata(IRGenFunction &IGF,
|
|
CanType sourceType,
|
|
MetadataResponse source,
|
|
DynamicMetadataRequest request,
|
|
Map<MetadataResponse> *cache) const {
|
|
LocalTypeDataKey key = {
|
|
sourceType,
|
|
LocalTypeDataKind::forFormalTypeMetadata()
|
|
};
|
|
return follow(IGF, key, source, Path.begin(), Path.end(), request, cache);
|
|
}
|
|
|
|
MetadataResponse
|
|
MetadataPath::followFromWitnessTable(IRGenFunction &IGF,
|
|
CanType conformingType,
|
|
ProtocolConformanceRef conformance,
|
|
MetadataResponse source,
|
|
DynamicMetadataRequest request,
|
|
Map<MetadataResponse> *cache) const {
|
|
LocalTypeDataKey key = {
|
|
conformingType,
|
|
LocalTypeDataKind::forProtocolWitnessTable(conformance)
|
|
};
|
|
return follow(IGF, key, source, Path.begin(), Path.end(), request, cache);
|
|
}
|
|
|
|
/// Follow this metadata path.
|
|
///
|
|
/// \param sourceKey - A description of the source value. Not necessarily
|
|
/// an appropriate caching key.
|
|
/// \param cache - If given, this cache will be used to short-circuit
|
|
/// the lookup; otherwise, the global (but dominance-sensitive) cache
|
|
/// in the IRGenFunction will be used. This caching system is somewhat
|
|
/// more efficient than what IGF provides, but it's less general, and it
|
|
/// should probably be removed.
|
|
MetadataResponse MetadataPath::follow(IRGenFunction &IGF,
|
|
LocalTypeDataKey sourceKey,
|
|
MetadataResponse source,
|
|
iterator begin, iterator end,
|
|
DynamicMetadataRequest finalRequest,
|
|
Map<MetadataResponse> *cache) {
|
|
assert(source && "no source metadata value!");
|
|
|
|
// The invariant is that this iterator starts a path from source and
|
|
// that sourceKey is correctly describes it.
|
|
iterator i = begin;
|
|
|
|
// Before we begin emitting code to generate the actual path, try to find
|
|
// the latest point in the path that we've cached a value for.
|
|
|
|
// If the caller gave us a cache to use, check that. This lookup is very
|
|
// efficient and doesn't even require us to parse the prefix.
|
|
if (cache) {
|
|
auto result = cache->findPrefix(begin, end);
|
|
if (result.first) {
|
|
source = *result.first;
|
|
|
|
// If that was the end, there's no more work to do; don't bother
|
|
// adjusting the source key.
|
|
if (result.second == end)
|
|
return source;
|
|
|
|
// Advance the source key past the cached prefix.
|
|
while (i != result.second) {
|
|
Component component = *i++;
|
|
(void) followComponent(IGF, sourceKey, MetadataResponse(), component,
|
|
MetadataState::Abstract);
|
|
}
|
|
}
|
|
|
|
// Otherwise, make a pass over the path looking for available concrete
|
|
// entries in the IGF's local type data cache.
|
|
} else {
|
|
auto skipI = i;
|
|
LocalTypeDataKey skipKey = sourceKey;
|
|
while (skipI != end) {
|
|
Component component = *skipI++;
|
|
(void) followComponent(IGF, skipKey, MetadataResponse(), component,
|
|
MetadataState::Abstract);
|
|
|
|
// Check the cache for a concrete value. We don't want an abstract
|
|
// cache entry because, if one exists, we'll just end up here again
|
|
// recursively.
|
|
auto skipRequest =
|
|
(skipI == end ? finalRequest : MetadataState::Abstract);
|
|
if (auto skipResponse =
|
|
IGF.tryGetConcreteLocalTypeData(skipKey, skipRequest)) {
|
|
// Advance the baseline information for the source to the current
|
|
// point in the path, then continue the search.
|
|
sourceKey = skipKey;
|
|
source = skipResponse;
|
|
i = skipI;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Drill in on the actual source value.
|
|
while (i != end) {
|
|
auto component = *i++;
|
|
|
|
auto componentRequest =
|
|
(i == end ? finalRequest : MetadataState::Abstract);
|
|
source = followComponent(IGF, sourceKey, source,
|
|
component, componentRequest);
|
|
|
|
// If we have a cache, remember this in the cache at the next position.
|
|
if (cache) {
|
|
cache->insertNew(begin, i, source);
|
|
|
|
// Otherwise, insert it into the global cache (at the updated source key).
|
|
} else {
|
|
IGF.setScopedLocalTypeData(sourceKey, source);
|
|
}
|
|
}
|
|
|
|
return source;
|
|
}
|
|
|
|
llvm::Value *IRGenFunction::optionallyLoadFromConditionalProtocolWitnessTable(
|
|
llvm::Value *wtable) {
|
|
if (!IGM.IRGen.Opts.UseRelativeProtocolWitnessTables)
|
|
return wtable;
|
|
|
|
auto *ptrVal = Builder.CreatePtrToInt(wtable, IGM.IntPtrTy);
|
|
auto *one = llvm::ConstantInt::get(IGM.IntPtrTy, 1);
|
|
auto *isCond = Builder.CreateAnd(ptrVal, one);
|
|
auto *isCondBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
|
auto *endBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
|
auto *origBB = Builder.GetInsertBlock();
|
|
isCond = Builder.CreateICmpEQ(isCond, one);
|
|
Builder.CreateCondBr(isCond, isCondBB, endBB);
|
|
|
|
Builder.emitBlock(isCondBB);
|
|
ConditionalDominanceScope condition (*this);
|
|
auto *mask = Builder.CreateNot(one);
|
|
auto *wtableAddr = Builder.CreateAnd(ptrVal, mask);
|
|
wtableAddr = Builder.CreateIntToPtr(wtableAddr, IGM.WitnessTablePtrPtrTy);
|
|
auto *wtableDeref = Builder.CreateLoad(Address(wtableAddr,
|
|
IGM.WitnessTablePtrTy,
|
|
IGM.getPointerAlignment()));
|
|
Builder.CreateBr(endBB);
|
|
|
|
Builder.emitBlock(endBB);
|
|
auto *phi = Builder.CreatePHI(wtable->getType(), 2);
|
|
phi->addIncoming(wtable, origBB);
|
|
phi->addIncoming(wtableDeref, isCondBB);
|
|
if (auto &schema = getOptions().PointerAuth.RelativeProtocolWitnessTable) {
|
|
auto info = PointerAuthInfo::emit(*this, schema, nullptr,
|
|
PointerAuthEntity());
|
|
return emitPointerAuthAuth(*this, phi, info);
|
|
}
|
|
return phi;
|
|
}
|
|
|
|
llvm::Value *irgen::loadParentProtocolWitnessTable(IRGenFunction &IGF,
|
|
llvm::Value *wtable,
|
|
WitnessIndex index) {
|
|
auto &IGM = IGF.IGM;
|
|
if (!IGM.IRGen.Opts.UseRelativeProtocolWitnessTables) {
|
|
auto baseWTable =
|
|
emitInvariantLoadOfOpaqueWitness(IGF,/*isProtocolWitness*/true, wtable,
|
|
index);
|
|
return baseWTable;
|
|
}
|
|
|
|
llvm::SmallString<40> fnName;
|
|
llvm::raw_svector_ostream(fnName)
|
|
<< "__swift_relative_protocol_witness_table_parent_"
|
|
<< index.getValue();
|
|
|
|
auto helperFn = cast<llvm::Function>(IGM.getOrCreateHelperFunction(
|
|
fnName, IGM.WitnessTablePtrTy, {IGM.WitnessTablePtrTy},
|
|
[&](IRGenFunction &subIGF) {
|
|
|
|
auto it = subIGF.CurFn->arg_begin();
|
|
llvm::Value *wtable = &*it;
|
|
auto &Builder = subIGF.Builder;
|
|
auto *ptrVal = Builder.CreatePtrToInt(wtable, IGM.IntPtrTy);
|
|
auto *one = llvm::ConstantInt::get(IGM.IntPtrTy, 1);
|
|
auto *isCond = Builder.CreateAnd(ptrVal, one);
|
|
auto *isNotCondBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
|
auto *isCondBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
|
auto *endBB = llvm::BasicBlock::Create(IGM.getLLVMContext());
|
|
isCond = Builder.CreateICmpEQ(isCond, one);
|
|
Builder.CreateCondBr(isCond, isCondBB, isNotCondBB);
|
|
|
|
Builder.emitBlock(isCondBB);
|
|
auto *mask = Builder.CreateNot(one);
|
|
auto *wtableAddr = Builder.CreateAnd(ptrVal, mask);
|
|
wtableAddr = Builder.CreateIntToPtr(wtableAddr, IGM.WitnessTablePtrTy);
|
|
auto addr = slotForLoadOfOpaqueWitness(subIGF, wtableAddr, index,
|
|
false /*isRelative*/);
|
|
llvm::Value *baseWTable = Builder.CreateLoad(addr);
|
|
baseWTable = subIGF.Builder.CreateBitCast(baseWTable, IGM.WitnessTablePtrTy);
|
|
Builder.CreateBr(endBB);
|
|
|
|
Builder.emitBlock(isNotCondBB);
|
|
if (auto &schema = subIGF.getOptions().PointerAuth.RelativeProtocolWitnessTable) {
|
|
auto info = PointerAuthInfo::emit(subIGF, schema, nullptr,
|
|
PointerAuthEntity());
|
|
wtable = emitPointerAuthAuth(subIGF, wtable, info);
|
|
}
|
|
auto baseWTable2 =
|
|
emitInvariantLoadOfOpaqueWitness(subIGF,/*isProtocolWitness*/true, wtable,
|
|
index);
|
|
baseWTable2 = subIGF.Builder.CreateBitCast(baseWTable2,
|
|
subIGF.IGM.WitnessTablePtrTy);
|
|
if (auto &schema = subIGF.getOptions().PointerAuth.RelativeProtocolWitnessTable) {
|
|
auto info = PointerAuthInfo::emit(subIGF, schema, nullptr,
|
|
PointerAuthEntity());
|
|
baseWTable2 = emitPointerAuthSign(subIGF, baseWTable2, info);
|
|
|
|
baseWTable2 = subIGF.Builder.CreateBitCast(baseWTable2,
|
|
IGM.WitnessTablePtrTy);
|
|
}
|
|
|
|
Builder.CreateBr(endBB);
|
|
|
|
Builder.emitBlock(endBB);
|
|
auto *phi = Builder.CreatePHI(wtable->getType(), 2);
|
|
phi->addIncoming(baseWTable, isCondBB);
|
|
phi->addIncoming(baseWTable2, isNotCondBB);
|
|
Builder.CreateRet(phi);
|
|
|
|
}, true /*noinline*/));
|
|
|
|
auto *call = IGF.Builder.CreateCallWithoutDbgLoc(
|
|
helperFn->getFunctionType(), helperFn, {wtable});
|
|
call->setCallingConv(IGF.IGM.DefaultCC);
|
|
call->setDoesNotThrow();
|
|
return call;
|
|
}
|
|
|
|
llvm::Value *irgen::loadConditionalConformance(IRGenFunction &IGF,
|
|
llvm::Value *wtable,
|
|
WitnessIndex index) {
|
|
|
|
auto &IGM = IGF.IGM;
|
|
if (!IGM.IRGen.Opts.UseRelativeProtocolWitnessTables) {
|
|
return emitInvariantLoadOfOpaqueWitness(IGF, /*isProtocolWitness*/true,
|
|
wtable, index);
|
|
}
|
|
|
|
auto &Builder = IGF.Builder;
|
|
auto *one = llvm::ConstantInt::get(IGM.IntPtrTy, 1);
|
|
auto *mask = Builder.CreateNot(one);
|
|
auto *ptrVal = Builder.CreatePtrToInt(wtable, IGM.IntPtrTy);
|
|
auto *wtableAddr = Builder.CreateAnd(ptrVal, mask);
|
|
wtableAddr = Builder.CreateIntToPtr(wtableAddr, IGM.WitnessTablePtrTy);
|
|
auto addr = slotForLoadOfOpaqueWitness(IGF, wtableAddr, index,
|
|
false /*isRelative*/);
|
|
return Builder.CreateLoad(addr);
|
|
}
|
|
|
|
/// Call an associated-type witness table access function. Does not do
|
|
/// any caching or drill down to implied protocols.
|
|
static llvm::Value *
|
|
emitAssociatedTypeWitnessTableRef(IRGenFunction &IGF,
|
|
llvm::Value *parentMetadata,
|
|
llvm::Value *wtable,
|
|
AssociatedConformance conformance,
|
|
llvm::Value *associatedTypeMetadata) {
|
|
auto sourceProtocol = conformance.getSourceProtocol();
|
|
auto assocConformanceDescriptor =
|
|
IGF.IGM.getAddrOfAssociatedConformanceDescriptor(conformance);
|
|
auto baseDescriptor =
|
|
IGF.IGM.getAddrOfProtocolRequirementsBaseDescriptor(sourceProtocol);
|
|
|
|
auto call = IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables ?
|
|
IGF.Builder.CreateCall(
|
|
IGF.IGM.getGetAssociatedConformanceWitnessRelativeFunctionPointer(),
|
|
{wtable, parentMetadata, associatedTypeMetadata, baseDescriptor,
|
|
assocConformanceDescriptor}) :
|
|
IGF.Builder.CreateCall(
|
|
IGF.IGM.getGetAssociatedConformanceWitnessFunctionPointer(),
|
|
{wtable, parentMetadata, associatedTypeMetadata, baseDescriptor,
|
|
assocConformanceDescriptor});
|
|
call->setDoesNotThrow();
|
|
call->setDoesNotAccessMemory();
|
|
return call;
|
|
}
|
|
|
|
/// Drill down on a single stage of component.
|
|
///
|
|
/// sourceType and sourceDecl will be adjusted to refer to the new
|
|
/// component. Source can be null, in which case this will be the only
|
|
/// thing done.
|
|
MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF,
|
|
LocalTypeDataKey &sourceKey,
|
|
MetadataResponse source,
|
|
Component component,
|
|
DynamicMetadataRequest request) {
|
|
switch (component.getKind()) {
|
|
case Component::Kind::NominalTypeArgument:
|
|
case Component::Kind::NominalTypeArgumentConformance:
|
|
case Component::Kind::NominalTypeArgumentShape:
|
|
case Component::Kind::NominalValueArgument: {
|
|
assert(sourceKey.Kind == LocalTypeDataKind::forFormalTypeMetadata());
|
|
auto type = sourceKey.Type;
|
|
if (auto archetypeTy = dyn_cast<ArchetypeType>(type))
|
|
type = archetypeTy->getSuperclass()->getCanonicalType();
|
|
auto *nominal = type.getAnyNominal();
|
|
auto reqtIndex = component.getPrimaryIndex();
|
|
|
|
GenericTypeRequirements requirements(IGF.IGM, nominal);
|
|
auto &requirement = requirements.getRequirements()[reqtIndex];
|
|
|
|
auto subs = type->getContextSubstitutionMap();
|
|
auto sub = requirement.getTypeParameter().subst(subs)->getCanonicalType();
|
|
|
|
// In either case, we need to change the type.
|
|
sourceKey.Type = sub;
|
|
|
|
// If this is a type argument, we've fully updated sourceKey.
|
|
if (component.getKind() == Component::Kind::NominalTypeArgument) {
|
|
assert(requirement.isAnyMetadata() && "index mismatch!");
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto sourceMetadata = source.getMetadata();
|
|
auto *argMetadata = requirement.isMetadataPack()
|
|
? emitArgumentMetadataPackRef(IGF, nominal, requirements, reqtIndex,
|
|
sourceMetadata)
|
|
: emitArgumentMetadataRef(IGF, nominal, requirements, reqtIndex,
|
|
sourceMetadata);
|
|
setTypeMetadataName(IGF.IGM, argMetadata, sourceKey.Type);
|
|
|
|
// Assume that the argument metadata is complete if the metadata is.
|
|
auto argState = getPresumedMetadataStateForTypeArgument(
|
|
source.getStaticLowerBoundOnState());
|
|
auto response = MetadataResponse::forBounded(argMetadata, argState);
|
|
|
|
// Do a dynamic check if necessary to satisfy the request.
|
|
assert((requirement.isMetadata() || request.isSatisfiedBy(response)) &&
|
|
"checkTypeMetadataState for packs is currently unimplemented");
|
|
return emitCheckTypeMetadataState(IGF, request, response);
|
|
|
|
// If this is a shape class, load the value.
|
|
} else if (component.getKind() == Component::Kind::NominalTypeArgumentShape) {
|
|
assert(requirement.isShape() && "index mismatch!");
|
|
|
|
sourceKey.Kind = LocalTypeDataKind::forPackShapeExpression();
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto sourceMetadata = source.getMetadata();
|
|
auto shape = emitArgumentPackShapeRef(IGF, nominal,
|
|
requirements, reqtIndex,
|
|
sourceMetadata);
|
|
|
|
return MetadataResponse::forComplete(shape);
|
|
|
|
// Otherwise, we need to switch sourceKey.Kind to the appropriate
|
|
// conformance kind.
|
|
} else if (component.getKind() == Component::Kind::NominalTypeArgumentConformance) {
|
|
assert(requirement.isAnyWitnessTable() && "index mismatch!");
|
|
auto conformance = subs.lookupConformance(requirement.getTypeParameter(),
|
|
requirement.getProtocol());
|
|
assert(conformance.getProtocol() == requirement.getProtocol());
|
|
sourceKey.Kind = LocalTypeDataKind::forProtocolWitnessTable(conformance);
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto sourceMetadata = source.getMetadata();
|
|
auto protocol = conformance.getProtocol();
|
|
auto wtable = requirement.isWitnessTablePack()
|
|
? emitArgumentWitnessTablePackRef(IGF, nominal, requirements,
|
|
reqtIndex, sourceMetadata)
|
|
: emitArgumentWitnessTableRef(IGF, nominal, requirements, reqtIndex,
|
|
sourceMetadata);
|
|
setProtocolWitnessTableName(IGF.IGM, wtable, sourceKey.Type, protocol);
|
|
|
|
return MetadataResponse::forComplete(wtable);
|
|
} else if (component.getKind() == Component::Kind::NominalValueArgument) {
|
|
assert(requirement.isValue() && "index mismatch!");
|
|
|
|
sourceKey.Kind = LocalTypeDataKind::forValue();
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto sourceMetadata = source.getMetadata();
|
|
auto value = emitValueGenericRef(IGF, nominal, requirements, reqtIndex,
|
|
sourceMetadata);
|
|
|
|
setTypeMetadataName(IGF.IGM, value, sourceKey.Type);
|
|
|
|
return MetadataResponse::forComplete(value);
|
|
}
|
|
|
|
llvm_unreachable("Bad component kind");
|
|
}
|
|
|
|
case Component::Kind::PackExpansionCount: {
|
|
assert(sourceKey.Kind == LocalTypeDataKind::forPackShapeExpression());
|
|
|
|
auto componentIndex = component.getPrimaryIndex();
|
|
|
|
auto packType = cast<PackType>(sourceKey.Type);
|
|
auto expansion = cast<PackExpansionType>(
|
|
packType.getElementType(componentIndex));
|
|
sourceKey.Type = expansion.getCountType();
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
// Count the number of pack expansions in the pack.
|
|
size_t numExpansions = 0;
|
|
for (auto eltType : packType.getElementTypes()) {
|
|
if (auto eltExpansion = dyn_cast<PackExpansionType>(eltType)) {
|
|
assert(eltExpansion.getCountType() == expansion.getCountType());
|
|
numExpansions++;
|
|
}
|
|
}
|
|
assert(numExpansions >= 1);
|
|
size_t numScalars = packType->getNumElements() - numExpansions;
|
|
|
|
llvm::Value *count = source.getMetadata();
|
|
|
|
// Subtract the number of scalars.
|
|
if (numScalars > 0) {
|
|
count = IGF.Builder.CreateSub(count,
|
|
IGF.IGM.getSize(Size(numScalars)));
|
|
}
|
|
// Divide by the number of pack expansions.
|
|
if (numExpansions > 1) {
|
|
count = IGF.Builder.CreateUDiv(count,
|
|
IGF.IGM.getSize(Size(numExpansions)));
|
|
}
|
|
|
|
return MetadataResponse::forComplete(count);
|
|
}
|
|
|
|
case Component::Kind::PackExpansionPattern: {
|
|
assert(sourceKey.Kind == LocalTypeDataKind::forFormalTypeMetadata() ||
|
|
sourceKey.Kind.isPackProtocolConformance());
|
|
bool isTypeMetadata =
|
|
(sourceKey.Kind == LocalTypeDataKind::forFormalTypeMetadata());
|
|
|
|
auto componentIndex = component.getPrimaryIndex();
|
|
|
|
// Change the key type to the pattern type.
|
|
auto packType = cast<PackType>(sourceKey.Type);
|
|
auto expansion = cast<PackExpansionType>(
|
|
packType.getElementType(componentIndex));
|
|
sourceKey.Type = expansion.getPatternType();
|
|
|
|
// If this is a pack conformance, change the key kind to the
|
|
// appropriate component.
|
|
if (!isTypeMetadata) {
|
|
sourceKey.Kind = LocalTypeDataKind::forProtocolWitnessTable(
|
|
sourceKey.Kind.getPackProtocolConformance()
|
|
->getPatternConformances()[componentIndex]);
|
|
}
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
// Compute the offset of the start of the pack component.
|
|
// We can skip even computing this in the very likely case that the
|
|
// component index is zero.
|
|
if (componentIndex == 0) return source;
|
|
auto dynamicIndex =
|
|
emitIndexOfStructuralPackComponent(IGF, packType, componentIndex);
|
|
|
|
// Slice into the pack.
|
|
auto eltTy = (isTypeMetadata ? IGF.IGM.TypeMetadataPtrTy
|
|
: IGF.IGM.WitnessTablePtrTy);
|
|
auto subPack =
|
|
IGF.Builder.CreateInBoundsGEP(eltTy, source.getMetadata(),
|
|
dynamicIndex);
|
|
|
|
// The pack slice has the same state as the containing pack.
|
|
auto state = source.getStaticLowerBoundOnState();
|
|
if (source.hasDynamicState())
|
|
return MetadataResponse(subPack, source.getDynamicState(), state);
|
|
return MetadataResponse::forBounded(subPack, state);
|
|
}
|
|
|
|
case Component::Kind::OutOfLineBaseProtocol: {
|
|
auto protocol = sourceKey.Kind.getConformedProtocol();
|
|
auto &pi = IGF.IGM.getProtocolInfo(protocol,
|
|
ProtocolInfoKind::RequirementSignature);
|
|
|
|
auto &entry = pi.getWitnessEntries()[component.getPrimaryIndex()];
|
|
assert(entry.isOutOfLineBase());
|
|
auto inheritedProtocol = entry.getBase();
|
|
|
|
if (sourceKey.Kind.isConcreteProtocolConformance()) {
|
|
auto inheritedConformance =
|
|
sourceKey.Kind.getConcreteProtocolConformance()
|
|
->getInheritedConformance(inheritedProtocol);
|
|
sourceKey.Kind = LocalTypeDataKind::forConcreteProtocolWitnessTable(
|
|
inheritedConformance);
|
|
} else {
|
|
sourceKey.Kind =
|
|
LocalTypeDataKind::forAbstractProtocolWitnessTable(inheritedProtocol);
|
|
}
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto wtable = source.getMetadata();
|
|
WitnessIndex index(component.getPrimaryIndex(), /*prefix*/ false);
|
|
auto baseWTable = loadParentProtocolWitnessTable(IGF, wtable,
|
|
index.forProtocolWitnessTable());
|
|
baseWTable =
|
|
IGF.Builder.CreateBitCast(baseWTable, IGF.IGM.WitnessTablePtrTy);
|
|
setProtocolWitnessTableName(IGF.IGM, baseWTable, sourceKey.Type,
|
|
inheritedProtocol);
|
|
|
|
return MetadataResponse::forComplete(baseWTable);
|
|
}
|
|
|
|
case Component::Kind::AssociatedConformance: {
|
|
auto sourceType = sourceKey.Type;
|
|
auto sourceConformance = sourceKey.Kind.getProtocolConformance(sourceType);
|
|
auto sourceProtocol = sourceKey.Kind.getConformedProtocol();
|
|
auto &pi = IGF.IGM.getProtocolInfo(sourceProtocol,
|
|
ProtocolInfoKind::RequirementSignature);
|
|
|
|
auto &entry = pi.getWitnessEntries()[component.getPrimaryIndex()];
|
|
assert(entry.isAssociatedConformance());
|
|
auto association = entry.getAssociatedConformancePath();
|
|
auto associatedRequirement = entry.getAssociatedConformanceRequirement();
|
|
|
|
|
|
auto associatedConformance =
|
|
sourceConformance.getAssociatedConformance(association, associatedRequirement);
|
|
auto associatedType = associatedConformance.getType()->getCanonicalType();
|
|
|
|
sourceKey.Type = associatedType;
|
|
sourceKey.Kind =
|
|
LocalTypeDataKind::forProtocolWitnessTable(associatedConformance);
|
|
|
|
assert((associatedConformance.isConcrete() ||
|
|
isa<ArchetypeType>(sourceKey.Type)) &&
|
|
"couldn't find concrete conformance for concrete type");
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
AssociatedConformance associatedConformanceRef(sourceProtocol,
|
|
association,
|
|
associatedRequirement);
|
|
|
|
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
|
|
// In Embedded Swift associated-conformance entries simply point to the witness table
|
|
// of the associated conformance.
|
|
llvm::Value *sourceWTable = source.getMetadata();
|
|
llvm::Value *associatedWTable = emitAssociatedConformanceValue(
|
|
IGF, sourceWTable, associatedConformanceRef);
|
|
return MetadataResponse::forComplete(associatedWTable);
|
|
}
|
|
|
|
// Witness table lookups only require abstract metadata.
|
|
auto *sourceMetadata =
|
|
IGF.emitAbstractTypeMetadataRef(sourceType);
|
|
|
|
// Try to avoid circularity when realizing the associated metadata.
|
|
//
|
|
// Suppose we are asked to produce the witness table for some
|
|
// conformance access path (T : P)(Self.X : Q)(Self.Y : R).
|
|
//
|
|
// The associated conformance accessor for (Self.Y : R) takes the
|
|
// metadata for T.X and T.X : Q as arguments. If T.X is concrete,
|
|
// there are two ways of building it:
|
|
//
|
|
// a) Using the knowledge of the concrete type to build it directly,
|
|
// by first constructing the type metadata for its generic arguments.
|
|
//
|
|
// b) Obtaining T.X from the witness table for T : P. This will also
|
|
// construct the generic type, but from the generic environment
|
|
// of the concrete type of T, and not the abstract environment of
|
|
// our conformance access path.
|
|
//
|
|
// Now, say that T.X == Foo<T.X.Y>, with "Foo<A> where A : R".
|
|
//
|
|
// If approach a) is taken, then constructing Foo<T.X.Y> requires
|
|
// recovering the conformance T.X.Y : R, which recursively evaluates
|
|
// the same conformance access path, eventually causing a stack
|
|
// overflow.
|
|
//
|
|
// With approach b) on the other hand, the type metadata for
|
|
// Foo<T.X.Y> is constructed from the concrete type metadata for T,
|
|
// which must provide some other conformance access path for the
|
|
// conformance to R.
|
|
//
|
|
// This is not very principled, and a remaining issue is with conformance
|
|
// requirements where the subject type consists of multiple terms.
|
|
//
|
|
// A better approach would be for conformance access paths to directly
|
|
// record how type metadata at each intermediate step is constructed.
|
|
llvm::Value *associatedMetadata = nullptr;
|
|
|
|
if (auto response = IGF.tryGetLocalTypeMetadata(associatedType,
|
|
MetadataState::Abstract)) {
|
|
// The associated type metadata was already cached, so we're fine.
|
|
associatedMetadata = response.getMetadata();
|
|
} else {
|
|
// If the associated type is concrete and the parent type is an archetype,
|
|
// it is better to realize the associated type metadata from the witness
|
|
// table of the parent's conformance, instead of realizing the concrete
|
|
// type directly.
|
|
auto depMemType = cast<DependentMemberType>(association);
|
|
CanType baseSubstType =
|
|
depMemType.getBase().subst(
|
|
SubstitutionMap::getProtocolSubstitutions(sourceConformance))
|
|
->getCanonicalType();
|
|
if (auto archetypeType = dyn_cast<ArchetypeType>(baseSubstType)) {
|
|
auto *baseAssocType = depMemType->getAssocType();
|
|
|
|
MetadataResponse response =
|
|
emitAssociatedTypeMetadataRef(IGF, archetypeType, baseAssocType,
|
|
MetadataState::Abstract);
|
|
|
|
// Cache this response in case we have to realize the associated type
|
|
// again later.
|
|
IGF.setScopedLocalTypeMetadata(associatedType, response);
|
|
|
|
associatedMetadata = response.getMetadata();
|
|
} else {
|
|
// Ok, fall back to realizing the (possibly concrete) type.
|
|
// Witness table lookups only require abstract metadata.
|
|
associatedMetadata =
|
|
IGF.emitAbstractTypeMetadataRef(sourceKey.Type);
|
|
}
|
|
}
|
|
|
|
auto sourceWTable = source.getMetadata();
|
|
|
|
auto associatedWTable =
|
|
emitAssociatedTypeWitnessTableRef(IGF, sourceMetadata, sourceWTable,
|
|
associatedConformanceRef,
|
|
associatedMetadata);
|
|
|
|
setProtocolWitnessTableName(IGF.IGM, associatedWTable, sourceKey.Type,
|
|
associatedRequirement);
|
|
|
|
return MetadataResponse::forComplete(associatedWTable);
|
|
}
|
|
|
|
case Component::Kind::ConditionalConformance: {
|
|
auto sourceConformance = sourceKey.Kind.getConcreteProtocolConformance();
|
|
|
|
auto reqtIndex = component.getPrimaryIndex();
|
|
|
|
ProtocolDecl *conformingProto;
|
|
auto found = SILWitnessTable::enumerateWitnessTableConditionalConformances(
|
|
sourceConformance,
|
|
[&](unsigned index, CanType type, ProtocolDecl *proto) {
|
|
if (reqtIndex == index) {
|
|
conformingProto = proto;
|
|
sourceKey.Type = type;
|
|
// done!
|
|
return true;
|
|
}
|
|
return /*finished?*/ false;
|
|
});
|
|
assert(found && "too many conditional conformances");
|
|
(void)found;
|
|
|
|
sourceKey.Kind =
|
|
LocalTypeDataKind::forAbstractProtocolWitnessTable(conformingProto);
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
WitnessIndex index(privateWitnessTableIndexToTableOffset(reqtIndex),
|
|
/*prefix*/ false);
|
|
|
|
auto sourceWTable = source.getMetadata();
|
|
auto capturedWTable = loadConditionalConformance(IGF, sourceWTable,
|
|
index.forProtocolWitnessTable());
|
|
capturedWTable =
|
|
IGF.Builder.CreateBitCast(capturedWTable, IGF.IGM.WitnessTablePtrTy);
|
|
setProtocolWitnessTableName(IGF.IGM, capturedWTable, sourceKey.Type,
|
|
conformingProto);
|
|
|
|
return MetadataResponse::forComplete(capturedWTable);
|
|
}
|
|
|
|
case Component::Kind::TuplePack: {
|
|
assert(component.getPrimaryIndex() == 0);
|
|
|
|
auto tupleType = cast<TupleType>(sourceKey.Type);
|
|
auto packType = tupleType.getInducedPackType();
|
|
|
|
sourceKey.Kind = LocalTypeDataKind::forFormalTypeMetadata();
|
|
sourceKey.Type = packType;
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto sourceMetadata = source.getMetadata();
|
|
return emitInducedTupleTypeMetadataPackRef(IGF, packType,
|
|
sourceMetadata);
|
|
}
|
|
|
|
case Component::Kind::TupleShape: {
|
|
assert(component.getPrimaryIndex() == 0);
|
|
|
|
auto tupleType = cast<TupleType>(sourceKey.Type);
|
|
|
|
sourceKey.Kind = LocalTypeDataKind::forPackShapeExpression();
|
|
sourceKey.Type = tupleType.getInducedPackType();
|
|
|
|
if (!source) return MetadataResponse();
|
|
|
|
auto sourceMetadata = source.getMetadata();
|
|
auto count = irgen::emitTupleTypeMetadataLength(IGF, sourceMetadata);
|
|
|
|
return MetadataResponse::forComplete(count);
|
|
}
|
|
|
|
case Component::Kind::Impossible:
|
|
llvm_unreachable("following an impossible path!");
|
|
|
|
}
|
|
llvm_unreachable("bad metadata path component");
|
|
}
|
|
|
|
void MetadataPath::dump() const {
|
|
auto &out = llvm::errs();
|
|
print(out);
|
|
out << '\n';
|
|
}
|
|
void MetadataPath::print(llvm::raw_ostream &out) const {
|
|
for (auto i = Path.begin(), e = Path.end(); i != e; ++i) {
|
|
if (i != Path.begin()) out << ".";
|
|
auto component = *i;
|
|
switch (component.getKind()) {
|
|
case Component::Kind::OutOfLineBaseProtocol:
|
|
out << "out_of_line_base_protocol[" << component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::AssociatedConformance:
|
|
out << "associated_conformance[" << component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::NominalTypeArgument:
|
|
out << "nominal_type_argument[" << component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::NominalTypeArgumentConformance:
|
|
out << "nominal_type_argument_conformance["
|
|
<< component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::NominalTypeArgumentShape:
|
|
out << "nominal_type_argument_shape["
|
|
<< component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::NominalValueArgument:
|
|
out << "nominal_value_argument["
|
|
<< component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::PackExpansionCount:
|
|
out << "pack_expansion_count[" << component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::PackExpansionPattern:
|
|
out << "pack_expansion_pattern[" << component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::ConditionalConformance:
|
|
out << "conditional_conformance[" << component.getPrimaryIndex() << "]";
|
|
break;
|
|
case Component::Kind::TuplePack:
|
|
out << "tuple_pack";
|
|
break;
|
|
case Component::Kind::TupleShape:
|
|
out << "tuple_shape";
|
|
break;
|
|
case Component::Kind::Impossible:
|
|
out << "impossible";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Collect any required metadata for a witness method from the end of
|
|
/// the given parameter list.
|
|
void irgen::collectTrailingWitnessMetadata(
|
|
IRGenFunction &IGF, SILFunction &fn,
|
|
NativeCCEntryPointArgumentEmission &emission,
|
|
WitnessMetadata &witnessMetadata) {
|
|
assert(fn.getLoweredFunctionType()->getRepresentation()
|
|
== SILFunctionTypeRepresentation::WitnessMethod);
|
|
|
|
llvm::Value *wtable = emission.getSelfWitnessTable();
|
|
assert(wtable->getType() == IGF.IGM.WitnessTablePtrTy &&
|
|
"parameter signature mismatch: witness metadata didn't "
|
|
"end in witness table?");
|
|
wtable->setName("SelfWitnessTable");
|
|
witnessMetadata.SelfWitnessTable = wtable;
|
|
|
|
llvm::Value *metatype = emission.getSelfMetadata();
|
|
assert(metatype->getType() == IGF.IGM.TypeMetadataPtrTy &&
|
|
"parameter signature mismatch: witness metadata didn't "
|
|
"end in metatype?");
|
|
metatype->setName("Self");
|
|
witnessMetadata.SelfMetadata = metatype;
|
|
}
|
|
|
|
/// Perform all the bindings necessary to emit the given declaration.
|
|
void irgen::emitPolymorphicParameters(IRGenFunction &IGF, SILFunction &Fn,
|
|
EntryPointArgumentEmission &emission,
|
|
WitnessMetadata *witnessMetadata,
|
|
const GetParameterFn &getParameter) {
|
|
EmitPolymorphicParameters(IGF, Fn).emit(emission, witnessMetadata,
|
|
getParameter);
|
|
}
|
|
|
|
/// Given an array of polymorphic arguments as might be set up by
|
|
/// GenericArguments, bind the polymorphic parameters.
|
|
void irgen::emitPolymorphicParametersFromArray(IRGenFunction &IGF,
|
|
NominalTypeDecl *typeDecl,
|
|
Address array,
|
|
MetadataState state) {
|
|
GenericTypeRequirements requirements(IGF.IGM, typeDecl);
|
|
|
|
array = IGF.Builder.CreateElementBitCast(array, IGF.IGM.TypeMetadataPtrTy);
|
|
|
|
SubstitutionMap subs;
|
|
if (auto *genericEnv = typeDecl->getGenericEnvironment())
|
|
subs = genericEnv->getForwardingSubstitutionMap();
|
|
|
|
// Okay, bind everything else from the context.
|
|
requirements.bindFromBuffer(IGF, array, state, subs);
|
|
}
|
|
|
|
Size NecessaryBindings::getBufferSize(IRGenModule &IGM) const {
|
|
// We need one pointer for each archetype or witness table.
|
|
return IGM.getPointerSize() * size();
|
|
}
|
|
|
|
void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer,
|
|
MetadataState metadataState) const {
|
|
bindFromGenericRequirementsBuffer(IGF, getRequirements(), buffer,
|
|
metadataState, SubMap);
|
|
}
|
|
|
|
void NecessaryBindings::save(IRGenFunction &IGF, Address buffer,
|
|
std::optional<SubstitutionMap> replacementSubstitutions) const {
|
|
SubstitutionMap subsToPass = replacementSubstitutions.has_value()
|
|
? replacementSubstitutions.value()
|
|
: SubMap;
|
|
emitInitOfGenericRequirementsBuffer(IGF, getRequirements(), buffer,
|
|
MetadataState::Complete, subsToPass,
|
|
/*onHeapPacks=*/!NoEscape);
|
|
}
|
|
|
|
llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
|
|
CanType srcType,
|
|
ProtocolConformanceRef conformance) {
|
|
llvm::Value *srcMetadataCache = nullptr;
|
|
return emitWitnessTableRef(IGF, srcType, &srcMetadataCache, conformance);
|
|
}
|
|
|
|
/// Emit a protocol witness table for a conformance.
|
|
llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
|
|
CanType srcType,
|
|
llvm::Value **srcMetadataCache,
|
|
ProtocolConformanceRef conformance) {
|
|
auto proto = conformance.getProtocol();
|
|
|
|
// In Embedded Swift, only class-bound wtables are allowed.
|
|
auto &langOpts = srcType->getASTContext().LangOpts;
|
|
if (langOpts.hasFeature(Feature::Embedded) &&
|
|
!langOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
|
assert(proto->requiresClass());
|
|
}
|
|
|
|
assert(Lowering::TypeConverter::protocolRequiresWitnessTable(proto)
|
|
&& "protocol does not have witness tables?!");
|
|
|
|
// Look through any opaque types we're allowed to.
|
|
if (srcType->hasOpaqueArchetype()) {
|
|
srcType = IGF.IGM.substOpaqueTypesWithUnderlyingTypes(srcType);
|
|
conformance = IGF.IGM.substOpaqueTypesWithUnderlyingTypes(conformance);
|
|
}
|
|
|
|
// If we don't have concrete conformance information, the type must be
|
|
// an archetype and the conformance must be via one of the protocol
|
|
// requirements of the archetype. Look at what's locally bound.
|
|
ProtocolConformance *concreteConformance;
|
|
if (conformance.isAbstract()) {
|
|
auto archetype = cast<ArchetypeType>(srcType);
|
|
return emitArchetypeWitnessTableRef(IGF, archetype, proto);
|
|
|
|
// All other source types should be concrete enough that we have
|
|
// conformance info for them. However, that conformance info might be
|
|
// more concrete than we're expecting.
|
|
// TODO: make a best effort to devirtualize, maybe?
|
|
} else if (conformance.isPack()) {
|
|
auto pack = cast<PackType>(srcType);
|
|
return emitWitnessTablePackRef(IGF, pack, conformance.getPack());
|
|
} else {
|
|
concreteConformance = conformance.getConcrete();
|
|
}
|
|
assert(concreteConformance->getProtocol() == proto);
|
|
|
|
auto cacheKind =
|
|
LocalTypeDataKind::forConcreteProtocolWitnessTable(concreteConformance);
|
|
|
|
// Check immediately for an existing cache entry.
|
|
auto wtable = IGF.tryGetLocalTypeData(srcType, cacheKind);
|
|
if (wtable) return wtable;
|
|
|
|
auto &conformanceI = IGF.IGM.getConformanceInfo(proto, concreteConformance);
|
|
wtable = conformanceI.getTable(IGF, srcMetadataCache);
|
|
if (isa<llvm::Constant>(wtable))
|
|
wtable = getConstantSignedRelativeProtocolWitnessTable(IGF.IGM, wtable);
|
|
|
|
IGF.setScopedLocalTypeData(srcType, cacheKind, wtable);
|
|
return wtable;
|
|
}
|
|
|
|
static CanType getOrigSelfType(IRGenModule &IGM,
|
|
CanSILFunctionType origFnType) {
|
|
// Grab the apparent 'self' type. If there isn't a 'self' type,
|
|
// we're not going to try to access this anyway.
|
|
assert(!origFnType->getParameters().empty());
|
|
|
|
auto selfParam = origFnType->getParameters().back();
|
|
CanType inputType = selfParam.getArgumentType(
|
|
IGM.getSILModule(), origFnType, IGM.getMaximalTypeExpansionContext());
|
|
// If the parameter is a direct metatype parameter, this is a static method
|
|
// of the instance type. We can assume this because:
|
|
// - metatypes cannot directly conform to protocols
|
|
// - even if they could, they would conform as a value type 'self' and thus
|
|
// be passed indirectly as an @in or @inout parameter.
|
|
if (auto meta = dyn_cast<MetatypeType>(inputType)) {
|
|
if (!selfParam.isFormalIndirect())
|
|
inputType = meta.getInstanceType();
|
|
}
|
|
|
|
return inputType;
|
|
}
|
|
|
|
static CanType getSubstSelfType(IRGenModule &IGM,
|
|
CanSILFunctionType origFnType,
|
|
SubstitutionMap subs) {
|
|
CanType inputType = getOrigSelfType(IGM, origFnType);
|
|
|
|
// Substitute the `self` type.
|
|
// FIXME: This has to be done as a formal AST type substitution rather than
|
|
// a SIL function type substitution, because some nominal types (viz
|
|
// Optional) have type lowering recursively applied to their type parameters.
|
|
// Substituting into the original lowered function type like this is still
|
|
// problematic if we ever allow methods or protocol conformances on structural
|
|
// types; we'd really need to separately record the formal Self type in the
|
|
// SIL function type to make that work, which could be managed by having a
|
|
// "substituted generic signature" concept.
|
|
if (!subs.empty()) {
|
|
inputType = inputType.subst(subs)->getCanonicalType();
|
|
}
|
|
|
|
return inputType;
|
|
}
|
|
|
|
namespace {
|
|
class EmitPolymorphicArguments : public PolymorphicConvention {
|
|
IRGenFunction &IGF;
|
|
public:
|
|
EmitPolymorphicArguments(IRGenFunction &IGF,
|
|
CanSILFunctionType polyFn)
|
|
: PolymorphicConvention(IGF.IGM, polyFn), IGF(IGF) {}
|
|
|
|
void emit(SubstitutionMap subs,
|
|
WitnessMetadata *witnessMetadata, Explosion &out);
|
|
|
|
private:
|
|
void emitEarlySources(SubstitutionMap subs, Explosion &out) {
|
|
for (auto &source : getSources()) {
|
|
switch (source.getKind()) {
|
|
// Already accounted for in the parameters.
|
|
case MetadataSource::Kind::ClassPointer:
|
|
case MetadataSource::Kind::Metadata:
|
|
continue;
|
|
|
|
// Needs a special argument.
|
|
case MetadataSource::Kind::GenericLValueMetadata: {
|
|
out.add(
|
|
IGF.emitTypeMetadataRef(getSubstSelfType(IGF.IGM, FnType, subs)));
|
|
continue;
|
|
}
|
|
|
|
// Witness 'Self' arguments are added as a special case in
|
|
// EmitPolymorphicArguments::emit.
|
|
case MetadataSource::Kind::SelfMetadata:
|
|
case MetadataSource::Kind::SelfWitnessTable:
|
|
continue;
|
|
|
|
// No influence on the arguments.
|
|
case MetadataSource::Kind::ErasedTypeMetadata:
|
|
continue;
|
|
}
|
|
llvm_unreachable("bad source kind!");
|
|
}
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Pass all the arguments necessary for the given function.
|
|
void irgen::emitPolymorphicArguments(IRGenFunction &IGF,
|
|
CanSILFunctionType origFnType,
|
|
SubstitutionMap subs,
|
|
WitnessMetadata *witnessMetadata,
|
|
Explosion &out) {
|
|
EmitPolymorphicArguments(IGF, origFnType).emit(subs, witnessMetadata, out);
|
|
}
|
|
|
|
void EmitPolymorphicArguments::emit(SubstitutionMap subs,
|
|
WitnessMetadata *witnessMetadata,
|
|
Explosion &out) {
|
|
// Add all the early sources.
|
|
emitEarlySources(subs, out);
|
|
|
|
// For now, treat all archetypes independently.
|
|
enumerateUnfulfilledRequirements([&](GenericRequirement requirement) {
|
|
llvm::Value *requiredValue =
|
|
emitGenericRequirementFromSubstitutions(IGF, requirement,
|
|
MetadataState::Complete,
|
|
subs);
|
|
out.add(requiredValue);
|
|
});
|
|
|
|
// For a witness call, add the Self argument metadata arguments last.
|
|
for (auto &source : getSources()) {
|
|
switch (source.getKind()) {
|
|
case MetadataSource::Kind::Metadata:
|
|
case MetadataSource::Kind::ClassPointer:
|
|
// Already accounted for in the arguments.
|
|
continue;
|
|
|
|
case MetadataSource::Kind::GenericLValueMetadata:
|
|
// Added in the early phase.
|
|
continue;
|
|
|
|
case MetadataSource::Kind::SelfMetadata: {
|
|
assert(witnessMetadata && "no metadata structure for witness method");
|
|
auto self = IGF.emitTypeMetadataRef(
|
|
getSubstSelfType(IGF.IGM, FnType, subs));
|
|
witnessMetadata->SelfMetadata = self;
|
|
continue;
|
|
}
|
|
|
|
case MetadataSource::Kind::SelfWitnessTable: {
|
|
// Added later.
|
|
continue;
|
|
}
|
|
|
|
case MetadataSource::Kind::ErasedTypeMetadata:
|
|
// No influence on the arguments.
|
|
continue;
|
|
}
|
|
llvm_unreachable("bad source kind");
|
|
}
|
|
}
|
|
|
|
NecessaryBindings
|
|
NecessaryBindings::forPartialApplyForwarder(IRGenModule &IGM,
|
|
CanSILFunctionType origType,
|
|
SubstitutionMap subs,
|
|
bool noEscape,
|
|
bool considerParameterSources) {
|
|
NecessaryBindings bindings(subs, noEscape);
|
|
bindings.computeBindings(IGM, origType, considerParameterSources);
|
|
return bindings;
|
|
}
|
|
|
|
void NecessaryBindings::computeBindings(
|
|
IRGenModule &IGM, CanSILFunctionType origType,
|
|
bool considerParameterSources) {
|
|
|
|
// Bail out early if we don't have polymorphic parameters.
|
|
if (!hasPolymorphicParameters(origType))
|
|
return;
|
|
|
|
// Figure out what we're actually required to pass:
|
|
PolymorphicConvention convention(IGM, origType, considerParameterSources);
|
|
|
|
// - extra sources
|
|
for (auto &source : convention.getSources()) {
|
|
switch (source.getKind()) {
|
|
case MetadataSource::Kind::Metadata:
|
|
case MetadataSource::Kind::ClassPointer:
|
|
continue;
|
|
|
|
case MetadataSource::Kind::GenericLValueMetadata:
|
|
addRequirement(GenericRequirement::forMetadata(
|
|
getOrigSelfType(IGM, origType)));
|
|
continue;
|
|
|
|
case MetadataSource::Kind::SelfMetadata:
|
|
// Async functions pass the SelfMetadata and SelfWitnessTable parameters
|
|
// along explicitly.
|
|
addRequirement(GenericRequirement::forMetadata(
|
|
getOrigSelfType(IGM, origType)));
|
|
continue;
|
|
|
|
case MetadataSource::Kind::SelfWitnessTable:
|
|
// We'll just pass undef in cases like this.
|
|
continue;
|
|
|
|
case MetadataSource::Kind::ErasedTypeMetadata:
|
|
// Fixed in the body.
|
|
continue;
|
|
}
|
|
llvm_unreachable("bad source kind");
|
|
}
|
|
|
|
// - unfulfilled requirements
|
|
convention.enumerateUnfulfilledRequirements(
|
|
[&](GenericRequirement requirement) {
|
|
addRequirement(requirement);
|
|
});
|
|
}
|
|
|
|
/// The information we need to record in generic type metadata
|
|
/// is the information in the type's generic signature. This is
|
|
/// simply the information that would be passed to a generic function
|
|
/// that takes the (thick) parent metatype as an argument.
|
|
GenericTypeRequirements::GenericTypeRequirements(IRGenModule &IGM,
|
|
NominalTypeDecl *typeDecl)
|
|
: GenericTypeRequirements(IGM, typeDecl->getGenericSignatureOfContext()) {}
|
|
|
|
GenericTypeRequirements::GenericTypeRequirements(IRGenModule &IGM,
|
|
GenericSignature ncGenerics) {
|
|
// We only need to do something here if the declaration context is
|
|
// somehow generic.
|
|
if (!ncGenerics || ncGenerics->areAllParamsConcrete()) return;
|
|
|
|
// Construct a representative function type.
|
|
auto generics = ncGenerics.getCanonicalSignature();
|
|
auto fnType = SILFunctionType::get(
|
|
generics, SILFunctionType::ExtInfo(), SILCoroutineKind::None,
|
|
/*callee*/ ParameterConvention::Direct_Unowned,
|
|
/*params*/ {}, /*yields*/ {},
|
|
/*results*/ {}, /*error*/ std::nullopt,
|
|
/*pattern subs*/ SubstitutionMap(),
|
|
/*invocation subs*/ SubstitutionMap(), IGM.Context);
|
|
|
|
// Figure out what we're actually still required to pass
|
|
PolymorphicConvention convention(IGM, fnType);
|
|
convention.enumerateUnfulfilledRequirements([&](GenericRequirement reqt) {
|
|
assert(generics->isReducedType(reqt.getTypeParameter()));
|
|
Requirements.push_back(reqt);
|
|
});
|
|
|
|
// We do not need to consider extra sources.
|
|
}
|
|
|
|
void GenericTypeRequirements::emitInitOfBuffer(IRGenFunction &IGF,
|
|
SubstitutionMap subs,
|
|
Address buffer) {
|
|
if (Requirements.empty()) return;
|
|
|
|
emitInitOfGenericRequirementsBuffer(IGF, Requirements, buffer,
|
|
MetadataState::Complete, subs);
|
|
}
|
|
|
|
void irgen::emitInitOfGenericRequirementsBuffer(IRGenFunction &IGF,
|
|
ArrayRef<GenericRequirement> requirements,
|
|
Address buffer,
|
|
MetadataState metadataState,
|
|
SubstitutionMap subs,
|
|
bool onHeapPacks) {
|
|
if (requirements.empty()) return;
|
|
|
|
// Cast the buffer to %type**.
|
|
buffer = IGF.Builder.CreateElementBitCast(buffer, IGF.IGM.TypeMetadataPtrTy);
|
|
|
|
for (auto index : indices(requirements)) {
|
|
// GEP to the appropriate slot.
|
|
Address slot = buffer;
|
|
if (index != 0) {
|
|
slot = IGF.Builder.CreateConstArrayGEP(slot, index,
|
|
IGF.IGM.getPointerSize());
|
|
}
|
|
|
|
llvm::Value *value = emitGenericRequirementFromSubstitutions(
|
|
IGF, requirements[index], metadataState, subs, onHeapPacks);
|
|
slot = IGF.Builder.CreateElementBitCast(slot,
|
|
requirements[index].getType(IGF.IGM));
|
|
IGF.Builder.CreateStore(value, slot);
|
|
}
|
|
}
|
|
|
|
llvm::Value *
|
|
irgen::emitGenericRequirementFromSubstitutions(IRGenFunction &IGF,
|
|
GenericRequirement requirement,
|
|
MetadataState metadataState,
|
|
SubstitutionMap subs,
|
|
bool onHeapPacks) {
|
|
CanType depTy = requirement.getTypeParameter();
|
|
CanType argType = depTy.subst(subs)->getCanonicalType();
|
|
|
|
switch (requirement.getKind()) {
|
|
case GenericRequirement::Kind::Shape:
|
|
return IGF.emitPackShapeExpression(argType);
|
|
|
|
case GenericRequirement::Kind::Metadata:
|
|
return IGF.emitTypeMetadataRef(argType, metadataState).getMetadata();
|
|
|
|
case GenericRequirement::Kind::MetadataPack: {
|
|
auto metadata = IGF.emitTypeMetadataRef(argType, metadataState).getMetadata();
|
|
metadata = IGF.Builder.CreateBitCast(metadata, IGF.IGM.TypeMetadataPtrPtrTy);
|
|
|
|
// FIXME: We should track if this pack is already known to be on the heap
|
|
if (onHeapPacks) {
|
|
auto shape = IGF.emitPackShapeExpression(argType);
|
|
metadata = IGF.Builder.CreateCall(IGF.IGM.getAllocateMetadataPackFunctionPointer(),
|
|
{metadata, shape});
|
|
}
|
|
|
|
return metadata;
|
|
}
|
|
|
|
case GenericRequirement::Kind::WitnessTable: {
|
|
auto conformance = subs.lookupConformance(depTy, requirement.getProtocol());
|
|
return emitWitnessTableRef(IGF, argType, conformance);
|
|
}
|
|
|
|
case GenericRequirement::Kind::WitnessTablePack: {
|
|
auto conformance = subs.lookupConformance(depTy, requirement.getProtocol());
|
|
auto wtable = emitWitnessTableRef(IGF, argType, conformance);
|
|
wtable = IGF.Builder.CreateBitCast(wtable, IGF.IGM.WitnessTablePtrPtrTy);
|
|
|
|
// FIXME: We should track if this pack is already known to be on the heap
|
|
if (onHeapPacks) {
|
|
auto shape = IGF.emitPackShapeExpression(argType);
|
|
wtable = IGF.Builder.CreateCall(IGF.IGM.getAllocateWitnessTablePackFunctionPointer(),
|
|
{wtable, shape});
|
|
}
|
|
|
|
return wtable;
|
|
}
|
|
|
|
case GenericRequirement::Kind::Value:
|
|
return IGF.emitValueGenericRef(argType);
|
|
}
|
|
}
|
|
|
|
void GenericTypeRequirements::bindFromBuffer(IRGenFunction &IGF,
|
|
Address buffer,
|
|
MetadataState metadataState,
|
|
SubstitutionMap subs) {
|
|
bindFromGenericRequirementsBuffer(IGF, Requirements, buffer,
|
|
metadataState, subs);
|
|
}
|
|
|
|
void irgen::bindFromGenericRequirementsBuffer(IRGenFunction &IGF,
|
|
ArrayRef<GenericRequirement> requirements,
|
|
Address buffer,
|
|
MetadataState metadataState,
|
|
SubstitutionMap subs) {
|
|
if (requirements.empty()) return;
|
|
|
|
// Cast the buffer to %type**.
|
|
buffer = IGF.Builder.CreateElementBitCast(buffer, IGF.IGM.TypeMetadataPtrTy);
|
|
|
|
for (auto index : indices(requirements)) {
|
|
// GEP to the appropriate slot.
|
|
Address slot = buffer;
|
|
if (index != 0) {
|
|
slot = IGF.Builder.CreateConstArrayGEP(slot, index,
|
|
IGF.IGM.getPointerSize());
|
|
}
|
|
|
|
// Cast if necessary.
|
|
slot = IGF.Builder.CreateElementBitCast(
|
|
slot, requirements[index].getType(IGF.IGM));
|
|
|
|
llvm::Value *value = IGF.Builder.CreateLoad(slot);
|
|
bindGenericRequirement(IGF, requirements[index], value, metadataState, subs);
|
|
}
|
|
}
|
|
|
|
llvm::Type *GenericRequirement::typeForKind(IRGenModule &IGM,
|
|
GenericRequirement::Kind kind) {
|
|
switch (kind) {
|
|
case GenericRequirement::Kind::Shape:
|
|
case GenericRequirement::Kind::Value:
|
|
return IGM.SizeTy;
|
|
case GenericRequirement::Kind::Metadata:
|
|
return IGM.TypeMetadataPtrTy;
|
|
case GenericRequirement::Kind::WitnessTable:
|
|
return IGM.WitnessTablePtrTy;
|
|
case GenericRequirement::Kind::MetadataPack:
|
|
return IGM.TypeMetadataPtrPtrTy;
|
|
case GenericRequirement::Kind::WitnessTablePack:
|
|
return IGM.WitnessTablePtrPtrTy;
|
|
}
|
|
}
|
|
|
|
void irgen::bindGenericRequirement(IRGenFunction &IGF,
|
|
GenericRequirement requirement,
|
|
llvm::Value *value,
|
|
MetadataState metadataState,
|
|
SubstitutionMap subs) {
|
|
// Get the corresponding context type.
|
|
auto type = requirement.getTypeParameter();
|
|
if (subs)
|
|
type = type.subst(subs)->getCanonicalType();
|
|
|
|
// FIXME: Remove this
|
|
bool wasUnwrappedPack = false;
|
|
if (auto packType = dyn_cast<PackType>(type)) {
|
|
if (auto expansionType = packType.unwrapSingletonPackExpansion()) {
|
|
if (auto archetypeType = dyn_cast_or_null<PackArchetypeType>(
|
|
expansionType.getPatternType())) {
|
|
type = archetypeType;
|
|
wasUnwrappedPack = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(value->getType() == requirement.getType(IGF.IGM));
|
|
switch (requirement.getKind()) {
|
|
case GenericRequirement::Kind::Shape: {
|
|
assert(isa<ArchetypeType>(type));
|
|
auto kind = LocalTypeDataKind::forPackShapeExpression();
|
|
IGF.setUnscopedLocalTypeData(type, kind, value);
|
|
break;
|
|
}
|
|
|
|
case GenericRequirement::Kind::Metadata:
|
|
case GenericRequirement::Kind::MetadataPack: {
|
|
setTypeMetadataName(IGF.IGM, value, type);
|
|
IGF.bindLocalTypeDataFromTypeMetadata(type, IsExact, value, metadataState);
|
|
break;
|
|
}
|
|
|
|
case GenericRequirement::Kind::WitnessTable:
|
|
case GenericRequirement::Kind::WitnessTablePack: {
|
|
auto proto = requirement.getProtocol();
|
|
setProtocolWitnessTableName(IGF.IGM, value, type, proto);
|
|
|
|
if (subs) {
|
|
auto conf = subs.lookupConformance(requirement.getTypeParameter(), proto);
|
|
|
|
// FIXME: Remove this
|
|
if (conf.isPack() && isa<PackArchetypeType>(type)) {
|
|
assert(wasUnwrappedPack);
|
|
assert(conf.getPack()->getPatternConformances().size() == 1);
|
|
conf = conf.getPack()->getPatternConformances()[0];
|
|
}
|
|
|
|
auto kind = LocalTypeDataKind::forProtocolWitnessTable(conf);
|
|
IGF.setUnscopedLocalTypeData(type, kind, value);
|
|
} else {
|
|
auto kind = LocalTypeDataKind::forAbstractProtocolWitnessTable(proto);
|
|
IGF.setUnscopedLocalTypeData(type, kind, value);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GenericRequirement::Kind::Value: {
|
|
setTypeMetadataName(IGF.IGM, value, type);
|
|
auto kind = LocalTypeDataKind::forValue();
|
|
IGF.setUnscopedLocalTypeData(type, kind, value);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
/// A class for expanding a polymorphic signature.
|
|
class ExpandPolymorphicSignature : public PolymorphicConvention {
|
|
unsigned numShapes = 0;
|
|
unsigned numTypeMetadataPtrs = 0;
|
|
unsigned numWitnessTablePtrs = 0;
|
|
unsigned numValues = 0;
|
|
|
|
public:
|
|
ExpandPolymorphicSignature(IRGenModule &IGM, CanSILFunctionType fn)
|
|
: PolymorphicConvention(IGM, fn) {}
|
|
|
|
ExpandedSignature
|
|
expand(SmallVectorImpl<llvm::Type *> &out,
|
|
SmallVectorImpl<PolymorphicSignatureExpandedTypeSource> *reqs) {
|
|
auto outStartSize = out.size();
|
|
(void)outStartSize;
|
|
for (auto &source : getSources())
|
|
addEarlySource(source, out, reqs);
|
|
|
|
enumerateUnfulfilledRequirements([&](GenericRequirement reqt) {
|
|
if (reqs)
|
|
reqs->push_back(reqt);
|
|
out.push_back(reqt.getType(IGM));
|
|
switch (reqt.getKind()) {
|
|
case GenericRequirement::Kind::Shape:
|
|
++numShapes;
|
|
break;
|
|
case GenericRequirement::Kind::Metadata:
|
|
case GenericRequirement::Kind::MetadataPack:
|
|
++numTypeMetadataPtrs;
|
|
break;
|
|
case GenericRequirement::Kind::WitnessTable:
|
|
case GenericRequirement::Kind::WitnessTablePack:
|
|
++numWitnessTablePtrs;
|
|
break;
|
|
case GenericRequirement::Kind::Value:
|
|
++numValues;
|
|
break;
|
|
}
|
|
});
|
|
assert((!reqs || reqs->size() == (out.size() - outStartSize)) &&
|
|
"missing type source for type");
|
|
return {numShapes, numTypeMetadataPtrs, numWitnessTablePtrs, numValues};
|
|
}
|
|
|
|
private:
|
|
/// Add signature elements for the source metadata.
|
|
void addEarlySource(
|
|
const MetadataSource &source, SmallVectorImpl<llvm::Type *> &out,
|
|
SmallVectorImpl<PolymorphicSignatureExpandedTypeSource> *reqs) {
|
|
switch (source.getKind()) {
|
|
case MetadataSource::Kind::ClassPointer: return; // already accounted for
|
|
case MetadataSource::Kind::Metadata: return; // already accounted for
|
|
case MetadataSource::Kind::GenericLValueMetadata:
|
|
if (reqs)
|
|
reqs->push_back(source);
|
|
++numTypeMetadataPtrs;
|
|
return out.push_back(IGM.TypeMetadataPtrTy);
|
|
case MetadataSource::Kind::SelfMetadata:
|
|
case MetadataSource::Kind::SelfWitnessTable:
|
|
return; // handled as a special case in expand()
|
|
case MetadataSource::Kind::ErasedTypeMetadata:
|
|
return; // fixed in the body
|
|
}
|
|
llvm_unreachable("bad source kind");
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Given a generic signature, add the argument types required in order to call it.
|
|
ExpandedSignature irgen::expandPolymorphicSignature(
|
|
IRGenModule &IGM, CanSILFunctionType polyFn,
|
|
SmallVectorImpl<llvm::Type *> &out,
|
|
SmallVectorImpl<PolymorphicSignatureExpandedTypeSource> *outReqs) {
|
|
return ExpandPolymorphicSignature(IGM, polyFn).expand(out, outReqs);
|
|
}
|
|
|
|
void irgen::expandTrailingWitnessSignature(IRGenModule &IGM,
|
|
CanSILFunctionType polyFn,
|
|
SmallVectorImpl<llvm::Type*> &out) {
|
|
assert(polyFn->getRepresentation()
|
|
== SILFunctionTypeRepresentation::WitnessMethod);
|
|
|
|
assert(getTrailingWitnessSignatureLength(IGM, polyFn) == 2);
|
|
|
|
// A witness method always provides Self.
|
|
out.push_back(IGM.TypeMetadataPtrTy);
|
|
|
|
// A witness method always provides the witness table for Self.
|
|
out.push_back(IGM.WitnessTablePtrTy);
|
|
}
|
|
|
|
static llvm::Value *emitWTableSlotLoad(IRGenFunction &IGF, llvm::Value *wtable,
|
|
SILDeclRef member, Address slot,
|
|
bool isRelativeTable) {
|
|
if (IGF.IGM.getOptions().WitnessMethodElimination) {
|
|
// For LLVM IR WME, emit a @llvm.type.checked.load with the type of the
|
|
// method.
|
|
auto slotAsPointer = IGF.Builder.CreateElementBitCast(slot, IGF.IGM.Int8Ty);
|
|
auto typeId = typeIdForMethod(IGF.IGM, member);
|
|
|
|
// Arguments for @llvm.type.checked.load: 1) target address, 2) offset -
|
|
// always 0 because target address is directly pointing to the right slot,
|
|
// 3) type identifier, i.e. the mangled name of the *base* method.
|
|
SmallVector<llvm::Value *, 8> args;
|
|
args.push_back(slotAsPointer.getAddress());
|
|
args.push_back(llvm::ConstantInt::get(IGF.IGM.Int32Ty, 0));
|
|
args.push_back(llvm::MetadataAsValue::get(*IGF.IGM.LLVMContext, typeId));
|
|
|
|
// TODO/FIXME: Using @llvm.type.checked.load loses the "invariant" marker
|
|
// which could mean redundant loads don't get removed.
|
|
llvm::Value *checkedLoad =
|
|
isRelativeTable ? IGF.Builder.CreateIntrinsicCall(
|
|
llvm::Intrinsic::type_checked_load_relative, args)
|
|
: IGF.Builder.CreateIntrinsicCall(
|
|
llvm::Intrinsic::type_checked_load, args);
|
|
return IGF.Builder.CreateExtractValue(checkedLoad, 0);
|
|
}
|
|
|
|
if (isRelativeTable)
|
|
return IGF.emitLoadOfRelativePointer(slot, false, IGF.IGM.Int8Ty);
|
|
|
|
// Not doing LLVM IR WME, can just be a direct load.
|
|
return IGF.emitInvariantLoad(slot);
|
|
}
|
|
|
|
static FunctionPointer emitRelativeProtocolWitnessTableAccess(IRGenFunction &IGF,
|
|
WitnessIndex index,
|
|
llvm::Value *wtable,
|
|
SILDeclRef member) {
|
|
auto witnessTableTy = wtable->getType();
|
|
auto &IGM = IGF.IGM;
|
|
llvm::SmallString<40> fnName;
|
|
auto entity = LinkEntity::forMethodDescriptor(member);
|
|
auto mangled = entity.mangleAsString(IGM.Context);
|
|
llvm::raw_svector_ostream(fnName)
|
|
<< "__swift_relative_protocol_witness_table_access_"
|
|
<< index.forProtocolWitnessTable().getValue()
|
|
<< "_" << mangled;
|
|
|
|
auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType(
|
|
IGF.IGM.getMaximalTypeExpansionContext(), member);
|
|
Signature signature = IGF.IGM.getSignature(fnType);
|
|
|
|
auto helperFn = cast<llvm::Function>(IGM.getOrCreateHelperFunction(
|
|
fnName, IGM.Int8PtrTy, {witnessTableTy},
|
|
[&](IRGenFunction &subIGF) {
|
|
|
|
auto it = subIGF.CurFn->arg_begin();
|
|
llvm::Value *wtable = &*it;
|
|
wtable = subIGF.optionallyLoadFromConditionalProtocolWitnessTable(wtable);
|
|
auto slot = slotForLoadOfOpaqueWitness(subIGF, wtable,
|
|
index.forProtocolWitnessTable(),
|
|
true);
|
|
llvm::Value *witnessFnPtr = emitWTableSlotLoad(subIGF, wtable, member, slot,
|
|
true);
|
|
|
|
subIGF.Builder.CreateRet(witnessFnPtr);
|
|
|
|
}, true /*noinline*/));
|
|
|
|
auto *call = IGF.Builder.CreateCallWithoutDbgLoc(
|
|
helperFn->getFunctionType(), helperFn, {wtable});
|
|
call->setCallingConv(IGF.IGM.DefaultCC);
|
|
call->setDoesNotThrow();
|
|
auto fn = IGF.Builder.CreateBitCast(call, IGM.PtrTy);
|
|
return FunctionPointer::createUnsigned(fnType, fn, signature, true);
|
|
}
|
|
|
|
FunctionPointer irgen::emitWitnessMethodValue(IRGenFunction &IGF,
|
|
llvm::Value *wtable,
|
|
SILDeclRef member) {
|
|
auto *fn = cast<AbstractFunctionDecl>(member.getDecl());
|
|
auto proto = cast<ProtocolDecl>(fn->getDeclContext());
|
|
|
|
assert(!IGF.IGM.isResilient(proto, ResilienceExpansion::Maximal));
|
|
|
|
// Find the witness we're interested in.
|
|
auto &fnProtoInfo = IGF.IGM.getProtocolInfo(proto, ProtocolInfoKind::Full);
|
|
auto index = fnProtoInfo.getFunctionIndex(member);
|
|
auto isRelativeTable = IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables;
|
|
if (isRelativeTable) {
|
|
return emitRelativeProtocolWitnessTableAccess(IGF, index, wtable, member);
|
|
}
|
|
|
|
wtable = IGF.optionallyLoadFromConditionalProtocolWitnessTable(wtable);
|
|
auto slot =
|
|
slotForLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable(),
|
|
false/*isRelativeTable*/);
|
|
llvm::Value *witnessFnPtr = emitWTableSlotLoad(IGF, wtable, member, slot,
|
|
false/*isRelativeTable*/);
|
|
|
|
auto fnType = IGF.IGM.getSILTypes().getConstantFunctionType(
|
|
IGF.IGM.getMaximalTypeExpansionContext(), member);
|
|
Signature signature = IGF.IGM.getSignature(fnType);
|
|
witnessFnPtr = IGF.Builder.CreateBitCast(witnessFnPtr, IGF.IGM.PtrTy);
|
|
|
|
auto &schema = fnType->isAsync()
|
|
? IGF.getOptions().PointerAuth.AsyncProtocolWitnesses
|
|
: fnType->isCalleeAllocatedCoroutine()
|
|
? IGF.getOptions().PointerAuth.CoroProtocolWitnesses
|
|
: IGF.getOptions().PointerAuth.ProtocolWitnesses;
|
|
auto authInfo = PointerAuthInfo::emit(IGF, schema, slot.getAddress(), member);
|
|
|
|
return FunctionPointer::createSigned(fnType, witnessFnPtr, authInfo,
|
|
signature);
|
|
}
|
|
|
|
llvm::Value *irgen::emitAssociatedConformanceValue(IRGenFunction &IGF,
|
|
llvm::Value *wtable,
|
|
const AssociatedConformance &conf) {
|
|
auto proto = conf.getSourceProtocol();
|
|
assert(!IGF.IGM.isResilient(proto, ResilienceExpansion::Maximal));
|
|
|
|
// Find the witness we're interested in.
|
|
auto &fnProtoInfo = IGF.IGM.getProtocolInfo(proto, ProtocolInfoKind::Full);
|
|
auto index = fnProtoInfo.getAssociatedConformanceIndex(conf);
|
|
assert(!IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables);
|
|
|
|
wtable = IGF.optionallyLoadFromConditionalProtocolWitnessTable(wtable);
|
|
auto slot =
|
|
slotForLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable(),
|
|
false/*isRelativeTable*/);
|
|
llvm::Value *confPointer = IGF.emitInvariantLoad(slot);
|
|
if (auto &schema = IGF.getOptions().PointerAuth.ProtocolAssociatedTypeWitnessTableAccessFunctions) {
|
|
auto authInfo = PointerAuthInfo::emit(IGF, schema, slot.getAddress(), conf);
|
|
confPointer = emitPointerAuthAuth(IGF, confPointer, authInfo);
|
|
}
|
|
return confPointer;
|
|
}
|
|
|
|
FunctionPointer irgen::emitWitnessMethodValue(
|
|
IRGenFunction &IGF, CanType baseTy, llvm::Value **baseMetadataCache,
|
|
SILDeclRef member, ProtocolConformanceRef conformance) {
|
|
llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache,
|
|
conformance);
|
|
|
|
return emitWitnessMethodValue(IGF, wtable, member);
|
|
}
|
|
|
|
llvm::Value *irgen::computeResilientWitnessTableIndex(
|
|
IRGenFunction &IGF,
|
|
ProtocolDecl *proto,
|
|
llvm::Constant *reqtDescriptor) {
|
|
// The requirement base descriptor refers to the first requirement in the
|
|
// protocol descriptor, offset by the start of the witness table requirements.
|
|
auto requirementsBaseDescriptor =
|
|
IGF.IGM.getAddrOfProtocolRequirementsBaseDescriptor(proto);
|
|
|
|
// Subtract the two pointers to determine the offset to this particular
|
|
// requirement.
|
|
auto baseAddress = IGF.Builder.CreatePtrToInt(requirementsBaseDescriptor,
|
|
IGF.IGM.IntPtrTy);
|
|
auto reqtAddress = IGF.Builder.CreatePtrToInt(reqtDescriptor,
|
|
IGF.IGM.IntPtrTy);
|
|
auto offset = IGF.Builder.CreateSub(reqtAddress, baseAddress);
|
|
|
|
// Determine how to adjust the byte offset we have to make it a witness
|
|
// table offset.
|
|
const auto &dataLayout = IGF.IGM.Module.getDataLayout();
|
|
auto protoReqSize =
|
|
dataLayout.getTypeAllocSizeInBits(IGF.IGM.ProtocolRequirementStructTy);
|
|
auto ptrSize = dataLayout.getTypeAllocSizeInBits(IGF.IGM.Int8PtrTy);
|
|
assert(protoReqSize >= ptrSize && "> 64-bit pointers?");
|
|
assert((protoReqSize % ptrSize == 0) && "Must be evenly divisible");
|
|
(void)ptrSize;
|
|
unsigned factor = protoReqSize / 8;
|
|
auto factorConstant = llvm::ConstantInt::get(IGF.IGM.IntPtrTy, factor);
|
|
return IGF.Builder.CreateUDiv(offset, factorConstant);
|
|
}
|
|
|
|
MetadataResponse
|
|
irgen::emitAssociatedTypeMetadataRef(IRGenFunction &IGF,
|
|
llvm::Value *parentMetadata,
|
|
llvm::Value *wtable,
|
|
AssociatedTypeDecl *assocType,
|
|
DynamicMetadataRequest request) {
|
|
auto &IGM = IGF.IGM;
|
|
|
|
// Im embedded with existentials mode the type metadata is directly referenced
|
|
// by the witness table.
|
|
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
|
auto proto = assocType->getProtocol();
|
|
assert(!IGF.IGM.isResilient(proto, ResilienceExpansion::Maximal));
|
|
assert(!IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables);
|
|
|
|
auto &protoInfo = IGF.IGM.getProtocolInfo(proto, ProtocolInfoKind::Full);
|
|
auto index = protoInfo.getAssociatedTypeIndex(IGM, assocType);
|
|
auto slot =
|
|
slotForLoadOfOpaqueWitness(IGF, wtable, index.forProtocolWitnessTable(),
|
|
false/*isRelativeTable*/);
|
|
llvm::Value *assocTypeMetadata = IGF.emitInvariantLoad(slot);
|
|
|
|
if (auto &schema = IGF.getOptions().PointerAuth.ProtocolAssociatedTypeAccessFunctions) {
|
|
auto authInfo = PointerAuthInfo::emit(IGF, schema, slot.getAddress(), assocType);
|
|
assocTypeMetadata = emitPointerAuthAuth(IGF, assocTypeMetadata, authInfo);
|
|
|
|
}
|
|
|
|
return MetadataResponse::forComplete(assocTypeMetadata);
|
|
}
|
|
// Extract the requirements base descriptor.
|
|
auto reqBaseDescriptor =
|
|
IGM.getAddrOfProtocolRequirementsBaseDescriptor(
|
|
assocType->getProtocol());
|
|
|
|
// Extract the associated type descriptor.
|
|
auto assocTypeDescriptor =
|
|
IGM.getAddrOfAssociatedTypeDescriptor(assocType);
|
|
// Call swift_getAssociatedTypeWitness().
|
|
auto call =
|
|
IGF.IGM.IRGen.Opts.UseRelativeProtocolWitnessTables ?
|
|
IGF.Builder.CreateCall(IGM.getGetAssociatedTypeWitnessRelativeFunctionPointer(),
|
|
{request.get(IGF), wtable, parentMetadata,
|
|
reqBaseDescriptor, assocTypeDescriptor}) :
|
|
IGF.Builder.CreateCall(IGM.getGetAssociatedTypeWitnessFunctionPointer(),
|
|
{request.get(IGF), wtable, parentMetadata,
|
|
reqBaseDescriptor, assocTypeDescriptor});
|
|
call->setDoesNotThrow();
|
|
call->setDoesNotAccessMemory();
|
|
return MetadataResponse::handle(IGF, request, call);
|
|
}
|
|
|
|
Signature
|
|
IRGenModule::getAssociatedTypeWitnessTableAccessFunctionSignature() {
|
|
auto &fnType = AssociatedTypeWitnessTableAccessFunctionTy;
|
|
if (!fnType) {
|
|
// The associated type metadata is passed first so that this function is
|
|
// CC-compatible with a conformance's witness table access function.
|
|
fnType = llvm::FunctionType::get(WitnessTablePtrTy,
|
|
{ TypeMetadataPtrTy,
|
|
TypeMetadataPtrTy,
|
|
WitnessTablePtrTy },
|
|
/*varargs*/ false);
|
|
}
|
|
|
|
auto attrs = llvm::AttributeList().addFnAttribute(getLLVMContext(),
|
|
llvm::Attribute::NoUnwind);
|
|
return Signature(fnType, attrs, SwiftCC);
|
|
}
|
|
|
|
/// Load a reference to the protocol descriptor for the given protocol.
|
|
///
|
|
/// For Swift protocols, this is a constant reference to the protocol descriptor
|
|
/// symbol.
|
|
/// For ObjC protocols, descriptors are uniqued at runtime by the ObjC runtime.
|
|
/// We need to load the unique reference from a global variable fixed up at
|
|
/// startup.
|
|
///
|
|
/// The result is always a ProtocolDescriptorRefTy whose low bit will be
|
|
/// set to indicate when this is an Objective-C protocol.
|
|
llvm::Value *irgen::emitProtocolDescriptorRef(IRGenFunction &IGF,
|
|
ProtocolDecl *protocol) {
|
|
if (!protocol->isObjC()) {
|
|
return IGF.Builder.CreatePtrToInt(
|
|
IGF.IGM.getAddrOfProtocolDescriptor(protocol),
|
|
IGF.IGM.ProtocolDescriptorRefTy);
|
|
}
|
|
|
|
llvm::Value *val = emitReferenceToObjCProtocol(IGF, protocol);
|
|
val = IGF.Builder.CreatePtrToInt(val, IGF.IGM.ProtocolDescriptorRefTy);
|
|
|
|
// Set the low bit to indicate that this is an Objective-C protocol.
|
|
auto *isObjCBit = llvm::ConstantInt::get(IGF.IGM.ProtocolDescriptorRefTy, 1);
|
|
val = IGF.Builder.CreateOr(val, isObjCBit);
|
|
|
|
return val;
|
|
}
|
|
|
|
llvm::Constant *IRGenModule::getAddrOfGenericEnvironment(
|
|
CanGenericSignature signature) {
|
|
if (!signature)
|
|
return nullptr;
|
|
|
|
IRGenMangler mangler(Context);
|
|
auto symbolName = mangler.mangleSymbolNameForGenericEnvironment(signature);
|
|
return getAddrOfStringForMetadataRef(symbolName, /*alignment=*/0, false,
|
|
[&] (ConstantInitBuilder &builder) -> ConstantInitFuture {
|
|
/// Collect the cumulative count of parameters at each level.
|
|
llvm::SmallVector<uint16_t, 4> genericParamCounts;
|
|
unsigned curDepth = 0;
|
|
unsigned genericParamCount = 0;
|
|
for (const auto &gp : signature.getGenericParams()) {
|
|
if (curDepth != gp->getDepth()) {
|
|
genericParamCounts.push_back(genericParamCount);
|
|
curDepth = gp->getDepth();
|
|
}
|
|
|
|
++genericParamCount;
|
|
}
|
|
genericParamCounts.push_back(genericParamCount);
|
|
|
|
auto flags = GenericEnvironmentFlags()
|
|
.withNumGenericParameterLevels(genericParamCounts.size())
|
|
.withNumGenericRequirements(signature.getRequirements().size());
|
|
|
|
ConstantStructBuilder fields = builder.beginStruct();
|
|
fields.setPacked(true);
|
|
|
|
// Flags
|
|
fields.addInt32(flags.getIntValue());
|
|
|
|
// Parameter counts.
|
|
for (auto count : genericParamCounts) {
|
|
fields.addInt16(count);
|
|
}
|
|
|
|
// Generic parameters.
|
|
auto metadata =
|
|
irgen::addGenericParameters(*this, fields, signature, /*implicit=*/false);
|
|
assert(metadata.NumParamsEmitted == metadata.NumParams &&
|
|
"Implicit GenericParamDescriptors not supported here");
|
|
assert(metadata.GenericPackArguments.empty() &&
|
|
"We don't support packs here yet");
|
|
|
|
// Need to pad the structure after generic parameters
|
|
// up to four bytes because generic requirements that
|
|
// follow expect that alignment.
|
|
fields.addAlignmentPadding(Alignment(4));
|
|
|
|
// Generic requirements
|
|
irgen::addGenericRequirements(*this, fields, signature);
|
|
return fields.finishAndCreateFuture();
|
|
});
|
|
}
|