mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #85551 from aschwaighofer/wip_embedded_exit
Preliminary support for existential in embedded Swift
This commit is contained in:
@@ -89,11 +89,18 @@ enum class TypeMetadataAddress {
|
||||
inline bool isEmbedded(CanType t) {
|
||||
return t->getASTContext().LangOpts.hasFeature(Feature::Embedded);
|
||||
}
|
||||
|
||||
inline bool isEmbeddedWithoutEmbeddedExitentials(CanType t) {
|
||||
auto &langOpts = t->getASTContext().LangOpts;
|
||||
return langOpts.hasFeature(Feature::Embedded) &&
|
||||
!langOpts.hasFeature(Feature::EmbeddedExistentials);
|
||||
}
|
||||
// Metadata is not generated and not allowed to be referenced in Embedded Swift,
|
||||
// expect for classes (both generic and non-generic), dynamic self, and
|
||||
// class-bound existentials.
|
||||
inline bool isMetadataAllowedInEmbedded(CanType t) {
|
||||
bool embeddedExistentials =
|
||||
t->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials);
|
||||
|
||||
if (isa<ClassType>(t) || isa<BoundGenericClassType>(t) ||
|
||||
isa<DynamicSelfType>(t)) {
|
||||
return true;
|
||||
@@ -106,6 +113,9 @@ inline bool isMetadataAllowedInEmbedded(CanType t) {
|
||||
if (archeTy->requiresClass())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (embeddedExistentials)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -117,6 +127,11 @@ inline bool isEmbedded(const ProtocolConformance *c) {
|
||||
return c->getType()->getASTContext().LangOpts.hasFeature(Feature::Embedded);
|
||||
}
|
||||
|
||||
inline bool isEmbeddedWithoutEmbeddedExitentials(const ProtocolConformance *c) {
|
||||
return isEmbedded(c) && !c->getType()->getASTContext().
|
||||
LangOpts.hasFeature(Feature::EmbeddedExistentials);
|
||||
}
|
||||
|
||||
/// A link entity is some sort of named declaration, combined with all
|
||||
/// the information necessary to distinguish specific implementations
|
||||
/// of the declaration from each other.
|
||||
@@ -1098,7 +1113,7 @@ public:
|
||||
}
|
||||
|
||||
static LinkEntity forValueWitnessTable(CanType type) {
|
||||
assert(!isEmbedded(type));
|
||||
assert(!isEmbeddedWithoutEmbeddedExitentials(type));
|
||||
LinkEntity entity;
|
||||
entity.setForType(Kind::ValueWitnessTable, type);
|
||||
return entity;
|
||||
@@ -1128,7 +1143,7 @@ public:
|
||||
}
|
||||
|
||||
static LinkEntity forProtocolWitnessTable(const ProtocolConformance *C) {
|
||||
if (isEmbedded(C)) {
|
||||
if (isEmbeddedWithoutEmbeddedExitentials(C)) {
|
||||
assert(C->getProtocol()->requiresClass());
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,14 @@ FUNCTION(NativeStrongReleaseDirect, Swift, swift_releaseDirect, SwiftDirectRR_CC
|
||||
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
|
||||
UNKNOWN_MEMEFFECTS)
|
||||
|
||||
// void swift_releaseBox(void *ptr);
|
||||
FUNCTION(ReleaseBox, Swift, swift_releaseBox, C_CC, AlwaysAvailable,
|
||||
RETURNS(VoidTy),
|
||||
ARGS(RefCountedPtrTy),
|
||||
ATTRS(NoUnwind),
|
||||
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
|
||||
UNKNOWN_MEMEFFECTS)
|
||||
|
||||
// void *swift_retain_n(void *ptr, int32_t n);
|
||||
FUNCTION(NativeStrongRetainN, Swift, swift_retain_n, C_CC, AlwaysAvailable,
|
||||
RETURNS(RefCountedPtrTy),
|
||||
|
||||
@@ -77,6 +77,8 @@ public:
|
||||
// The regular `layout` method can be used for layout tasks for which the
|
||||
// actual superclass pointer is not relevant.
|
||||
void layoutEmbedded(CanType classTy) {
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
asImpl().addValueWitnessTable();
|
||||
asImpl().noteAddressPoint();
|
||||
asImpl().addEmbeddedSuperclass(classTy);
|
||||
asImpl().addDestructorFunction();
|
||||
@@ -89,6 +91,8 @@ public:
|
||||
"Adjustment index must be synchronized with this layout");
|
||||
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
asImpl().addValueWitnessTable();
|
||||
asImpl().noteAddressPoint();
|
||||
asImpl().addSuperclass();
|
||||
asImpl().addDestructorFunction();
|
||||
|
||||
@@ -43,6 +43,14 @@ protected:
|
||||
: super(IGM), Target(target) {}
|
||||
|
||||
public:
|
||||
|
||||
void embeddedLayout() {
|
||||
// The embedded layout consists of:
|
||||
// + // -1 : vwt
|
||||
// + // 0 : metadata flags
|
||||
super::layout();
|
||||
}
|
||||
|
||||
void layout() {
|
||||
static_assert(MetadataAdjustmentIndex::ValueType == 2,
|
||||
"Adjustment index must be synchronized with this layout");
|
||||
|
||||
@@ -1361,7 +1361,9 @@ deleteAndReenqueueForEmissionValuesDependentOnCanonicalPrespecializedMetadataRec
|
||||
void IRGenerator::emitLazyDefinitions() {
|
||||
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
|
||||
// In embedded Swift, the compiler cannot emit any metadata, etc.
|
||||
assert(LazyTypeMetadata.empty());
|
||||
// Other than to support existentials.
|
||||
assert(LazyTypeMetadata.empty() ||
|
||||
SIL.getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials));
|
||||
assert(LazySpecializedTypeMetadataRecords.empty());
|
||||
assert(LazyTypeContextDescriptors.empty());
|
||||
assert(LazyOpaqueTypeDescriptors.empty());
|
||||
@@ -1388,7 +1390,8 @@ void IRGenerator::emitLazyDefinitions() {
|
||||
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
|
||||
!LazyMetadataAccessors.empty() ||
|
||||
!LazyClassMetadata.empty() ||
|
||||
!LazySpecializedClassMetadata.empty()
|
||||
!LazySpecializedClassMetadata.empty() ||
|
||||
!LazySpecializedValueMetadata.empty()
|
||||
) {
|
||||
// Emit any lazy type metadata we require.
|
||||
while (!LazyTypeMetadata.empty()) {
|
||||
@@ -1514,6 +1517,12 @@ void IRGenerator::emitLazyDefinitions() {
|
||||
CurrentIGMPtr IGM = getGenModule(classType->getClassOrBoundGenericClass());
|
||||
emitLazySpecializedClassMetadata(*IGM.get(), classType);
|
||||
}
|
||||
|
||||
while(!LazySpecializedValueMetadata.empty()) {
|
||||
CanType valueType = LazySpecializedValueMetadata.pop_back_val();
|
||||
CurrentIGMPtr IGM = getGenModule(valueType->getNominalOrBoundGenericNominal());
|
||||
emitLazySpecializedValueMetadata(*IGM.get(), valueType);
|
||||
}
|
||||
}
|
||||
|
||||
FinishedEmittingLazyDefinitions = true;
|
||||
@@ -1580,6 +1589,14 @@ bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
|
||||
if (found != HasLazyMetadata.end())
|
||||
return found->second;
|
||||
|
||||
if (SIL.getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
||||
(isa<StructDecl>(type) || isa<EnumDecl>(type))) {
|
||||
bool isGeneric = cast<NominalTypeDecl>(type)->isGenericContext();
|
||||
HasLazyMetadata[type] = !isGeneric;
|
||||
|
||||
return !isGeneric;
|
||||
}
|
||||
|
||||
auto canBeLazy = [&]() -> bool {
|
||||
auto *dc = type->getDeclContext();
|
||||
if (isa<ClangModuleUnit>(dc->getModuleScopeContext())) {
|
||||
@@ -1628,11 +1645,17 @@ void IRGenerator::noteUseOfClassMetadata(CanType classType) {
|
||||
}
|
||||
|
||||
void IRGenerator::noteUseOfSpecializedClassMetadata(CanType classType) {
|
||||
if (LazilyEmittedSpecializedClassMetadata.insert(classType.getPointer()).second) {
|
||||
if (LazilyEmittedSpecializedMetadata.insert(classType.getPointer()).second) {
|
||||
LazySpecializedClassMetadata.push_back(classType);
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenerator::noteUseOfSpecializedValueMetadata(CanType valueType) {
|
||||
if (LazilyEmittedSpecializedMetadata.insert(valueType.getPointer()).second) {
|
||||
LazySpecializedValueMetadata.push_back(valueType);
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type,
|
||||
bool isUseOfMetadata,
|
||||
RequireMetadata_t requireMetadata) {
|
||||
@@ -5298,6 +5321,9 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
|
||||
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
|
||||
entity = LinkEntity::forTypeMetadata(concreteType,
|
||||
TypeMetadataAddress::AddressPoint);
|
||||
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
entity = LinkEntity::forTypeMetadata(concreteType,
|
||||
TypeMetadataAddress::FullMetadata);
|
||||
}
|
||||
|
||||
auto DbgTy = DebugTypeInfo::getGlobalMetadata(MetatypeType::get(concreteType),
|
||||
@@ -5320,7 +5346,8 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
|
||||
LinkInfo link = LinkInfo::get(*this, entity, ForDefinition);
|
||||
markGlobalAsUsedBasedOnLinkage(*this, link, var);
|
||||
|
||||
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
|
||||
if (Context.LangOpts.hasFeature(Feature::Embedded) &&
|
||||
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
return var;
|
||||
}
|
||||
|
||||
@@ -5331,12 +5358,15 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
|
||||
if (auto nominal = concreteType->getAnyNominal()) {
|
||||
// Keep type metadata around for all types (except @_objcImplementation,
|
||||
// since we're using ObjC metadata for that).
|
||||
if (!isObjCImpl)
|
||||
if (!isObjCImpl &&
|
||||
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
addRuntimeResolvableType(nominal);
|
||||
|
||||
// Don't define the alias for foreign type metadata, prespecialized
|
||||
// generic metadata, or @_objcImplementation classes, since they're not ABI.
|
||||
if (requiresForeignTypeMetadata(nominal) || isPrespecialized || isObjCImpl)
|
||||
if (requiresForeignTypeMetadata(nominal) ||
|
||||
(isPrespecialized && !Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) ||
|
||||
isObjCImpl)
|
||||
return var;
|
||||
|
||||
// Native Swift class metadata has a destructor before the address point.
|
||||
@@ -5349,6 +5379,10 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
|
||||
}
|
||||
}
|
||||
|
||||
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
adjustmentIndex = MetadataAdjustmentIndex::EmbeddedWithExistentials;
|
||||
}
|
||||
|
||||
llvm::Constant *indices[] = {
|
||||
llvm::ConstantInt::get(Int32Ty, 0),
|
||||
llvm::ConstantInt::get(Int32Ty, adjustmentIndex)};
|
||||
@@ -5390,7 +5424,10 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
|
||||
|
||||
llvm::Type *defaultVarTy;
|
||||
unsigned adjustmentIndex;
|
||||
if (concreteType->isAny() || concreteType->isAnyObject() || concreteType->isVoid() || concreteType->is<TupleType>() || concreteType->is<BuiltinType>()) {
|
||||
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
adjustmentIndex = 0;
|
||||
defaultVarTy = EmbeddedExistentialsMetadataStructTy;
|
||||
} else if (concreteType->isAny() || concreteType->isAnyObject() || concreteType->isVoid() || concreteType->is<TupleType>() || concreteType->is<BuiltinType>()) {
|
||||
defaultVarTy = FullExistentialTypeMetadataStructTy;
|
||||
adjustmentIndex = MetadataAdjustmentIndex::NoTypeLayoutString;
|
||||
} else if (fullMetadata) {
|
||||
@@ -5433,6 +5470,18 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
if ((isa<StructDecl>(nominal) || isa<EnumDecl>(nominal)) &&
|
||||
nominal->isGenericContext()) {
|
||||
IRGen.noteUseOfSpecializedValueMetadata(concreteType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
||||
isa<TupleType>(concreteType)) {
|
||||
IRGen.noteUseOfSpecializedValueMetadata(concreteType);
|
||||
}
|
||||
|
||||
if (shouldPrespecializeGenericMetadata()) {
|
||||
|
||||
@@ -2314,7 +2314,21 @@ Address irgen::emitAllocateBoxedOpaqueExistentialBuffer(
|
||||
if (fixedTI->getFixedPacking(IGF.IGM) == FixedPacking::OffsetZero) {
|
||||
return valueTI.getAddressForPointer(IGF.Builder.CreateBitCast(
|
||||
existentialBuffer.getAddress(), IGF.IGM.PtrTy));
|
||||
} else if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
llvm::Value *box, *address;
|
||||
auto *metadata = existLayout.loadMetadataRef(IGF, existentialContainer);
|
||||
IGF.emitAllocBoxCall(metadata, box, address);
|
||||
llvm::Value *addressInBox =
|
||||
IGF.Builder.CreateBitCast(address, IGF.IGM.OpaquePtrTy);
|
||||
IGF.Builder.CreateStore(
|
||||
box, Address(IGF.Builder.CreateBitCast(
|
||||
existentialBuffer.getAddress(), IGF.IGM.PtrTy),
|
||||
IGF.IGM.RefCountedPtrTy,
|
||||
existLayout.getAlignment(IGF.IGM)));
|
||||
|
||||
return valueTI.getAddressForPointer(addressInBox);
|
||||
}
|
||||
|
||||
// Otherwise, allocate a box with enough storage.
|
||||
Address addr = emitAllocateExistentialBoxInBuffer(
|
||||
IGF, valueType, existentialBuffer, genericEnv, "exist.box.addr",
|
||||
@@ -2883,6 +2897,10 @@ static llvm::Function *getDestroyBoxedOpaqueExistentialBufferFunction(
|
||||
Builder.CreateBitCast(buffer.getAddress(), IGM.PtrTy);
|
||||
auto *reference = Builder.CreateLoad(Address(
|
||||
referenceAddr, IGM.RefCountedPtrTy, buffer.getAlignment()));
|
||||
if (IGF.IGM.Context.LangOpts
|
||||
.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
IGF.emitReleaseBox(reference);
|
||||
} else
|
||||
IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity());
|
||||
|
||||
Builder.CreateRetVoid();
|
||||
|
||||
@@ -1274,6 +1274,12 @@ void IRGenFunction::emitNativeStrongRelease(llvm::Value *value,
|
||||
emitUnaryRefCountCall(*this, function, value);
|
||||
}
|
||||
|
||||
void IRGenFunction::emitReleaseBox(llvm::Value *value) {
|
||||
if (doesNotRequireRefCounting(value))
|
||||
return;
|
||||
emitUnaryRefCountCall(*this, IGM.getReleaseBoxFn(), value);
|
||||
}
|
||||
|
||||
void IRGenFunction::emitNativeSetDeallocating(llvm::Value *value) {
|
||||
if (doesNotRequireRefCounting(value)) return;
|
||||
emitUnaryRefCountCall(*this, IGM.getNativeSetDeallocatingFn(), value);
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
#include "ScalarTypeInfo.h"
|
||||
#include "StructLayout.h"
|
||||
#include "StructMetadataVisitor.h"
|
||||
#include "TupleMetadataVisitor.h"
|
||||
|
||||
#include "GenMeta.h"
|
||||
|
||||
@@ -2969,6 +2970,9 @@ void irgen::emitLazyTypeContextDescriptor(IRGenModule &IGM,
|
||||
}
|
||||
|
||||
void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) {
|
||||
// Embedded existentials emit very spares metadata records and don't have type
|
||||
// context descriptors.
|
||||
if (!type->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
eraseExistingTypeContextDescriptor(IGM, type);
|
||||
|
||||
if (requiresForeignTypeMetadata(type)) {
|
||||
@@ -4265,6 +4269,9 @@ namespace {
|
||||
auto type = (Target->checkAncestry(AncestryFlags::ObjC)
|
||||
? IGM.Context.getAnyObjectType()
|
||||
: IGM.Context.TheNativeObjectType);
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
return irgen::emitValueWitnessTable(IGM, type, false, false);
|
||||
}
|
||||
auto wtable = IGM.getAddrOfValueWitnessTable(type);
|
||||
return ConstantReference(wtable,
|
||||
swift::irgen::ConstantReference::Direct);
|
||||
@@ -5570,6 +5577,9 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
|
||||
// Might already be emitted, skip if that's the case.
|
||||
auto entity =
|
||||
LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::FullMetadata);
|
||||
}
|
||||
auto *existingVar = cast<llvm::GlobalVariable>(
|
||||
IGM.getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo()));
|
||||
if (!existingVar->isDeclaration()) {
|
||||
@@ -5597,6 +5607,23 @@ void irgen::emitLazySpecializedClassMetadata(IRGenModule &IGM,
|
||||
emitEmbeddedVTable(IGM, classTy, vtable);
|
||||
}
|
||||
|
||||
void irgen::emitLazySpecializedValueMetadata(IRGenModule &IGM,
|
||||
CanType valueTy) {
|
||||
auto &context = valueTy->getASTContext();
|
||||
PrettyStackTraceType stackTraceRAII(
|
||||
context, "emitting lazy specialized value metadata for", valueTy);
|
||||
|
||||
if (isa<TupleType>(valueTy)) {
|
||||
emitLazyTupleMetadata(IGM, valueTy);
|
||||
} else if (valueTy->getStructOrBoundGenericStruct()) {
|
||||
emitSpecializedGenericStructMetadata(IGM, valueTy,
|
||||
*valueTy.getStructOrBoundGenericStruct());
|
||||
} else {
|
||||
emitSpecializedGenericEnumMetadata(IGM, valueTy,
|
||||
*valueTy.getEnumOrBoundGenericEnum());
|
||||
}
|
||||
}
|
||||
|
||||
void irgen::emitSpecializedGenericClassMetadata(IRGenModule &IGM, CanType type,
|
||||
ClassDecl &decl) {
|
||||
assert(decl.isGenericContext());
|
||||
@@ -6189,7 +6216,10 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) {
|
||||
|
||||
bool isPattern;
|
||||
bool canBeConstant;
|
||||
bool hasEmbeddedExistentialFeature =
|
||||
IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
|
||||
if (structDecl->isGenericContext()) {
|
||||
assert(!hasEmbeddedExistentialFeature);
|
||||
GenericStructMetadataBuilder builder(IGM, structDecl, init);
|
||||
builder.layout();
|
||||
isPattern = true;
|
||||
@@ -6198,10 +6228,15 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) {
|
||||
builder.createMetadataAccessFunction();
|
||||
} else {
|
||||
StructMetadataBuilder builder(IGM, structDecl, init);
|
||||
if (hasEmbeddedExistentialFeature) {
|
||||
builder.embeddedLayout();
|
||||
} else {
|
||||
builder.layout();
|
||||
}
|
||||
isPattern = false;
|
||||
canBeConstant = builder.canBeConstant();
|
||||
|
||||
if (!hasEmbeddedExistentialFeature)
|
||||
builder.createMetadataAccessFunction();
|
||||
}
|
||||
|
||||
@@ -6224,13 +6259,74 @@ void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, CanType type,
|
||||
bool isPattern = false;
|
||||
|
||||
SpecializedGenericStructMetadataBuilder builder(IGM, type, decl, init);
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
builder.embeddedLayout();
|
||||
} else {
|
||||
builder.layout();
|
||||
}
|
||||
|
||||
bool canBeConstant = builder.canBeConstant();
|
||||
IGM.defineTypeMetadata(type, isPattern, canBeConstant,
|
||||
init.finishAndCreateFuture());
|
||||
}
|
||||
|
||||
// Tuples (currently only used in embedded existentials mode)
|
||||
//
|
||||
namespace {
|
||||
class TupleMetadataBuilder : public TupleMetadataVisitor<TupleMetadataBuilder> {
|
||||
using super = TupleMetadataVisitor<TupleMetadataBuilder>;
|
||||
|
||||
ConstantStructBuilder &B;
|
||||
|
||||
protected:
|
||||
|
||||
using super::asImpl;
|
||||
using super::IGM;
|
||||
using super::Target;
|
||||
|
||||
public:
|
||||
TupleMetadataBuilder(IRGenModule &IGM, CanType tupleTy, ConstantStructBuilder &B) :
|
||||
super(IGM, cast<TupleType>(tupleTy)), B(B) {}
|
||||
|
||||
ConstantReference emitValueWitnessTable(bool relativeReference) {
|
||||
return irgen::emitValueWitnessTable(IGM, Target->getCanonicalType(),
|
||||
false, relativeReference);
|
||||
}
|
||||
|
||||
void addMetadataFlags() {
|
||||
B.addInt(IGM.MetadataKindTy, unsigned(MetadataKind::Tuple));
|
||||
}
|
||||
|
||||
void addValueWitnessTable() {
|
||||
auto vwtPointer = emitValueWitnessTable(false).getValue();
|
||||
B.addSignedPointer(vwtPointer,
|
||||
IGM.getOptions().PointerAuth.ValueWitnessTable,
|
||||
PointerAuthEntity());
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
void irgen::emitLazyTupleMetadata(IRGenModule &IGM, CanType tupleTy) {
|
||||
assert(IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials));
|
||||
assert(isa<TupleType>(tupleTy));
|
||||
|
||||
Type ty = tupleTy.getPointer();
|
||||
auto &context = ty->getASTContext();
|
||||
PrettyStackTraceType stackTraceRAII(
|
||||
context, "emitting prespecialized metadata for", ty);
|
||||
|
||||
ConstantInitBuilder initBuilder(IGM);
|
||||
auto init = initBuilder.beginStruct();
|
||||
init.setPacked(true);
|
||||
|
||||
bool isPattern = false;
|
||||
bool canBeConstant = true;
|
||||
|
||||
TupleMetadataBuilder builder(IGM, tupleTy, init);
|
||||
builder.embeddedLayout();
|
||||
|
||||
IGM.defineTypeMetadata(tupleTy, isPattern, canBeConstant,
|
||||
init.finishAndCreateFuture());
|
||||
}
|
||||
// Enums
|
||||
|
||||
static std::optional<Size>
|
||||
@@ -6606,7 +6702,10 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) {
|
||||
|
||||
bool isPattern;
|
||||
bool canBeConstant;
|
||||
bool hasEmbeddedExistentialFeature =
|
||||
IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
|
||||
if (theEnum->isGenericContext()) {
|
||||
assert(!hasEmbeddedExistentialFeature);
|
||||
GenericEnumMetadataBuilder builder(IGM, theEnum, init);
|
||||
builder.layout();
|
||||
isPattern = true;
|
||||
@@ -6615,10 +6714,15 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) {
|
||||
builder.createMetadataAccessFunction();
|
||||
} else {
|
||||
EnumMetadataBuilder builder(IGM, theEnum, init);
|
||||
if (hasEmbeddedExistentialFeature) {
|
||||
builder.embeddedLayout();
|
||||
} else {
|
||||
builder.layout();
|
||||
}
|
||||
isPattern = false;
|
||||
canBeConstant = builder.canBeConstant();
|
||||
|
||||
if (!hasEmbeddedExistentialFeature)
|
||||
builder.createMetadataAccessFunction();
|
||||
}
|
||||
|
||||
@@ -6640,7 +6744,11 @@ void irgen::emitSpecializedGenericEnumMetadata(IRGenModule &IGM, CanType type,
|
||||
init.setPacked(true);
|
||||
|
||||
SpecializedGenericEnumMetadataBuilder builder(IGM, type, decl, init);
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
builder.embeddedLayout();
|
||||
} else {
|
||||
builder.layout();
|
||||
}
|
||||
|
||||
bool canBeConstant = builder.canBeConstant();
|
||||
IGM.defineTypeMetadata(type, /*isPattern=*/false, canBeConstant,
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace irgen {
|
||||
void emitLazyClassMetadata(IRGenModule &IGM, CanType classType);
|
||||
|
||||
void emitLazySpecializedClassMetadata(IRGenModule &IGM, CanType classType);
|
||||
void emitLazySpecializedValueMetadata(IRGenModule &IGM, CanType valueTy);
|
||||
|
||||
void emitLazyCanonicalSpecializedMetadataAccessor(IRGenModule &IGM,
|
||||
CanType theType);
|
||||
@@ -110,6 +111,9 @@ namespace irgen {
|
||||
void emitSpecializedGenericEnumMetadata(IRGenModule &IGM, CanType type,
|
||||
EnumDecl &decl);
|
||||
|
||||
/// Emit the metadata associated with a given tuple type.
|
||||
void emitLazyTupleMetadata(IRGenModule &IGM, CanType type);
|
||||
|
||||
/// Emit the metadata associated with a given instantiation of a generic
|
||||
// class.
|
||||
void emitSpecializedGenericClassMetadata(IRGenModule &IGM, CanType type,
|
||||
@@ -195,6 +199,12 @@ namespace irgen {
|
||||
// and the value witness table.
|
||||
Class = 3,
|
||||
|
||||
// In Embedded with existentials all metadata is a record with a value
|
||||
// witness prepended.
|
||||
// -1: vwt
|
||||
// 0: metadata flags (unused)
|
||||
EmbeddedWithExistentials = 1,
|
||||
|
||||
// Struct and enum metadata have one word of head-allocated data:
|
||||
// the value witness table.
|
||||
ValueType = 2,
|
||||
|
||||
@@ -962,13 +962,17 @@ namespace {
|
||||
|
||||
void addAssociatedType(AssociatedTypeDecl *assocType) {
|
||||
// In Embedded Swift witness tables don't have associated-types entries.
|
||||
if (assocType->getASTContext().LangOpts.hasFeature(Feature::Embedded))
|
||||
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) {
|
||||
if (req.getAssociation()->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
|
||||
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
|
||||
@@ -1728,7 +1732,9 @@ public:
|
||||
SILEntries = SILEntries.slice(1);
|
||||
|
||||
// In Embedded Swift witness tables don't have associated-types entries.
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded))
|
||||
auto &langOpts = IGM.Context.LangOpts;
|
||||
if (langOpts.hasFeature(Feature::Embedded) &&
|
||||
!langOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
return;
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -1745,6 +1751,17 @@ public:
|
||||
#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,
|
||||
@@ -1768,8 +1785,9 @@ public:
|
||||
auto &entry = SILEntries.front();
|
||||
(void)entry;
|
||||
SILEntries = SILEntries.slice(1);
|
||||
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded) &&
|
||||
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
|
||||
@@ -2731,7 +2749,8 @@ static void addWTableTypeMetadata(IRGenModule &IGM,
|
||||
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())
|
||||
if (!wt->getConformance()->getProtocol()->requiresClass() &&
|
||||
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3752,7 +3771,9 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
|
||||
auto proto = conformance.getProtocol();
|
||||
|
||||
// In Embedded Swift, only class-bound wtables are allowed.
|
||||
if (srcType->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
|
||||
auto &langOpts = srcType->getASTContext().LangOpts;
|
||||
if (langOpts.hasFeature(Feature::Embedded) &&
|
||||
!langOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
assert(proto->requiresClass());
|
||||
}
|
||||
|
||||
@@ -4531,6 +4552,28 @@ irgen::emitAssociatedTypeMetadataRef(IRGenFunction &IGF,
|
||||
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(
|
||||
|
||||
@@ -1380,6 +1380,13 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM,
|
||||
CanType abstractType,
|
||||
bool &canBeConstant) {
|
||||
std::optional<BoundGenericTypeCharacteristics> boundGenericCharacteristics;
|
||||
// Embedded existentials need fully "specialized" value witness table
|
||||
// functions: i.e the metadata argument must remain unused.
|
||||
// For the non-embedded existentials path, when there is a bound generic type,
|
||||
// that is we have a specialized generic type, we have decided for code size
|
||||
// reasons to continue using "generic" value witness table functions i.e the
|
||||
// same once used for runtime instantiated generic metadata.
|
||||
if (!IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
if (auto boundGenericType = dyn_cast<BoundGenericType>(abstractType)) {
|
||||
CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType);
|
||||
|
||||
@@ -1388,10 +1395,10 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM,
|
||||
auto packing = boundConcreteTI->getFixedPacking(IGM);
|
||||
boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI,
|
||||
packing};
|
||||
|
||||
abstractType =
|
||||
boundGenericType->getDecl()->getDeclaredType()->getCanonicalType();
|
||||
}
|
||||
}
|
||||
CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType);
|
||||
|
||||
auto concreteLoweredType = IGM.getLoweredType(concreteFormalType);
|
||||
@@ -1533,13 +1540,22 @@ ConstantReference irgen::emitValueWitnessTable(IRGenModule &IGM,
|
||||
bool isPattern,
|
||||
bool relativeReference) {
|
||||
// See if we can use a prefab witness table from the runtime.
|
||||
if (!isPattern) {
|
||||
if (!isPattern &&
|
||||
!IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
if (auto known = getAddrOfKnownValueWitnessTable(IGM, abstractType,
|
||||
relativeReference)) {
|
||||
return known;
|
||||
}
|
||||
}
|
||||
|
||||
// There might already be a definition emitted in embedded mode.
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
auto addr = IGM.getAddrOfValueWitnessTable(abstractType);
|
||||
if (!cast<llvm::GlobalVariable>(addr)->isDeclaration()) {
|
||||
return {addr, ConstantReference::Direct};
|
||||
}
|
||||
}
|
||||
|
||||
// We should never be making a pattern if the layout isn't fixed.
|
||||
// The reverse can be true for types whose layout depends on
|
||||
// resilient types.
|
||||
|
||||
@@ -626,6 +626,9 @@ public:
|
||||
void emitNativeStrongRelease(llvm::Value *value, Atomicity atomicity);
|
||||
void emitNativeSetDeallocating(llvm::Value *value);
|
||||
|
||||
// Routines to deal with box (embedded) runtime calls.
|
||||
void emitReleaseBox(llvm::Value *value);
|
||||
|
||||
// Routines for the ObjC reference-counting style.
|
||||
void emitObjCStrongRetain(llvm::Value *value);
|
||||
llvm::Value *emitObjCRetainCall(llvm::Value *value);
|
||||
|
||||
@@ -392,6 +392,11 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
|
||||
TypeMetadataStructTy
|
||||
});
|
||||
|
||||
EmbeddedExistentialsMetadataStructTy = createStructType(*this, "swift.embedded_existential_type", {
|
||||
WitnessTablePtrTy,
|
||||
TypeMetadataStructTy
|
||||
});
|
||||
|
||||
// A full heap metadata is basically just an additional small prefix
|
||||
// on a full metadata, used for metadata corresponding to heap
|
||||
// allocations.
|
||||
@@ -1481,7 +1486,9 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
|
||||
|
||||
void IRGenerator::addLazyWitnessTable(const ProtocolConformance *Conf) {
|
||||
// In Embedded Swift, only class-bound wtables are allowed.
|
||||
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
|
||||
auto &langOpts = SIL.getASTContext().LangOpts;
|
||||
if (langOpts.hasFeature(Feature::Embedded) &&
|
||||
!langOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
assert(Conf->getProtocol()->requiresClass());
|
||||
}
|
||||
|
||||
|
||||
@@ -349,8 +349,9 @@ private:
|
||||
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedClassMetadata;
|
||||
|
||||
llvm::SmallVector<CanType, 4> LazySpecializedClassMetadata;
|
||||
llvm::SmallVector<CanType, 4> LazySpecializedValueMetadata;
|
||||
|
||||
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedSpecializedClassMetadata;
|
||||
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedSpecializedMetadata;
|
||||
|
||||
llvm::SmallVector<ClassDecl *, 4> ClassesForEagerInitialization;
|
||||
|
||||
@@ -513,6 +514,7 @@ public:
|
||||
|
||||
void noteUseOfClassMetadata(CanType classType);
|
||||
void noteUseOfSpecializedClassMetadata(CanType classType);
|
||||
void noteUseOfSpecializedValueMetadata(CanType valueType);
|
||||
|
||||
void noteUseOfTypeMetadata(NominalTypeDecl *type) {
|
||||
noteUseOfTypeGlobals(type, true, RequireMetadata);
|
||||
@@ -832,6 +834,7 @@ public:
|
||||
llvm::StructType *TupleTypeMetadataTy; /// %swift.tuple_type
|
||||
llvm::StructType *FullHeapMetadataStructTy; /// %swift.full_heapmetadata = type { ... }
|
||||
llvm::StructType *FullBoxMetadataStructTy; /// %swift.full_boxmetadata = type { ... }
|
||||
llvm::StructType *EmbeddedExistentialsMetadataStructTy;
|
||||
llvm::StructType *FullTypeMetadataStructTy; /// %swift.full_type = type { ... }
|
||||
llvm::StructType *FullExistentialTypeMetadataStructTy; /// %swift.full_existential_type = type { ... }
|
||||
llvm::StructType *FullForeignTypeMetadataStructTy; /// %swift.full_foreign_type = type { ... }
|
||||
|
||||
@@ -696,6 +696,12 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
|
||||
if (isForcedShared())
|
||||
return SILLinkage::Shared;
|
||||
|
||||
// In embedded existenitals mode we generate metadata for tuple types.
|
||||
if (getType()->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
||||
isa<TupleType>(getType())) {
|
||||
return SILLinkage::Shared;
|
||||
}
|
||||
|
||||
auto *nominal = getType().getAnyNominal();
|
||||
switch (getMetadataAddress()) {
|
||||
case TypeMetadataAddress::FullMetadata:
|
||||
@@ -1120,6 +1126,9 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const {
|
||||
case Kind::NoncanonicalSpecializedGenericTypeMetadata:
|
||||
switch (getMetadataAddress()) {
|
||||
case TypeMetadataAddress::FullMetadata:
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
return IGM.EmbeddedExistentialsMetadataStructTy;
|
||||
}
|
||||
if (getType().getClassOrBoundGenericClass())
|
||||
return IGM.FullHeapMetadataStructTy;
|
||||
else
|
||||
|
||||
@@ -886,6 +886,17 @@ bool irgen::isCompleteSpecializedNominalTypeMetadataStaticallyAddressable(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
||||
(isa<StructType>(type) || isa<BoundGenericStructType>(type) ||
|
||||
isa<EnumType>(type) || isa<BoundGenericEnumType>(type))) {
|
||||
if (type->hasArchetype()) {
|
||||
llvm::errs() << "Cannot get metadata of generic class for type "
|
||||
<< type << "\n";
|
||||
llvm::report_fatal_error("cannot get metadata for type with archetype");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prespecialized struct/enum metadata gets no dedicated accessor yet and so
|
||||
// cannot do the work of registering the generic arguments which are classes
|
||||
// with the ObjC runtime. Concretely, the following cannot be prespecialized
|
||||
@@ -1252,6 +1263,10 @@ static MetadataResponse emitFixedArrayMetadataRef(IRGenFunction &IGF,
|
||||
static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
|
||||
CanTupleType type,
|
||||
DynamicMetadataRequest request) {
|
||||
if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
|
||||
return MetadataResponse::forComplete(IGF.IGM.getAddrOfTypeMetadata(type));
|
||||
}
|
||||
|
||||
if (type->containsPackExpansionType())
|
||||
return emitDynamicTupleTypeMetadataRef(IGF, type, request);
|
||||
|
||||
@@ -3372,7 +3387,9 @@ llvm::Value *IRGenFunction::emitTypeMetadataRef(CanType type) {
|
||||
MetadataResponse
|
||||
IRGenFunction::emitTypeMetadataRef(CanType type,
|
||||
DynamicMetadataRequest request) {
|
||||
if (type->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
|
||||
auto &langOpts = type->getASTContext().LangOpts;
|
||||
if (langOpts.hasFeature(Feature::Embedded) &&
|
||||
!langOpts.hasFeature(Feature::EmbeddedExistentials) &&
|
||||
!isMetadataAllowedInEmbedded(type)) {
|
||||
llvm::errs() << "Metadata pointer requested in embedded Swift for type "
|
||||
<< type << "\n";
|
||||
@@ -3392,7 +3409,7 @@ IRGenFunction::emitTypeMetadataRef(CanType type,
|
||||
if (type->hasArchetype() ||
|
||||
!shouldTypeMetadataAccessUseAccessor(IGM, type) ||
|
||||
isa<PackType>(type) ||
|
||||
type->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
|
||||
langOpts.hasFeature(Feature::Embedded)) {
|
||||
return emitDirectTypeMetadataRef(*this, type, request);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,14 @@ protected:
|
||||
: super(IGM), Target(target) {}
|
||||
|
||||
public:
|
||||
|
||||
void embeddedLayout() {
|
||||
// The embedded layout consists of:
|
||||
// -1 : vwt
|
||||
// 0 : metadata flags
|
||||
super::layout();
|
||||
}
|
||||
|
||||
void layout() {
|
||||
static_assert(MetadataAdjustmentIndex::ValueType == 2,
|
||||
"Adjustment index must be synchronized with this layout");
|
||||
|
||||
@@ -39,6 +39,14 @@ protected:
|
||||
: super(IGM), Target(target) {}
|
||||
|
||||
public:
|
||||
|
||||
void embeddedLayout() {
|
||||
// The embedded layout consists of:
|
||||
// -1 : vwt
|
||||
// 0 : metadata flags
|
||||
super::layout();
|
||||
}
|
||||
|
||||
void layout() {
|
||||
super::layout();
|
||||
|
||||
|
||||
@@ -83,6 +83,73 @@ static inline void _swift_embedded_set_heap_object_metadata_pointer(void *object
|
||||
((EmbeddedHeapObject *)object)->metadata = metadata;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
void *initializeBufferWithCopyOfBufferFn;
|
||||
#if __has_feature(ptrauth_calls)
|
||||
void (* __ptrauth(0, 1, 0x04f8) destroyFn)(void *, void*);
|
||||
#else
|
||||
void (*destroyFn)(void *, void*);
|
||||
#endif
|
||||
#if __has_feature(ptrauth_calls)
|
||||
void* (* __ptrauth(0, 1, 0xe3ba) initializeWithCopyFn)(void*, void*, void*);
|
||||
#else
|
||||
void* (*initializeWithCopyFn)(void*, void*, void*);
|
||||
#endif
|
||||
void *assignWithCopyFn;
|
||||
void *initializeWithTakeFn;
|
||||
void *assignWithTakeFn;
|
||||
void *getEnumTagSinglePayloadFn;
|
||||
void *storeEnumTagSinglePayload;
|
||||
__swift_size_t size;
|
||||
__swift_size_t stride;
|
||||
unsigned flags;
|
||||
} EmbeddedValueWitnessTable;
|
||||
|
||||
typedef struct {
|
||||
#if __has_feature(ptrauth_calls)
|
||||
EmbeddedValueWitnessTable * __ptrauth(2, 1, 0x2e3f) vwt;
|
||||
#else
|
||||
EmbeddedValueWitnessTable *vwt;
|
||||
#endif
|
||||
} EmbeddedMetaDataPrefix;
|
||||
|
||||
static inline __swift_size_t
|
||||
_swift_embedded_metadata_get_size(void *metadata) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
return fullmeta->vwt->size;
|
||||
}
|
||||
|
||||
static inline __swift_size_t
|
||||
_swift_embedded_metadata_get_align_mask_impl(EmbeddedMetaDataPrefix *fullMetadata) {
|
||||
unsigned flags = fullMetadata->vwt->flags;
|
||||
unsigned embeddedValueWitnessTableFlagsMask = 0xFF;
|
||||
|
||||
return flags & embeddedValueWitnessTableFlagsMask;
|
||||
}
|
||||
|
||||
static inline __swift_size_t
|
||||
_swift_embedded_metadata_get_align_mask(void *metadata) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
return _swift_embedded_metadata_get_align_mask_impl(fullmeta);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_swift_embedded_invoke_box_destroy(void *object) {
|
||||
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
__swift_size_t alignMask = _swift_embedded_metadata_get_align_mask_impl(fullmeta);
|
||||
__swift_size_t headerSize = sizeof(void*) + sizeof(__swift_size_t);
|
||||
__swift_size_t startOfBoxedValue = (headerSize + alignMask) & ~alignMask;
|
||||
void *addrInBox = (void *)(((unsigned char *)object) + startOfBoxedValue);
|
||||
fullmeta->vwt->destroyFn(addrInBox, metadata);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_swift_embedded_initialize_box(void *metadata, void *newObjectAddr, void *oldObjectAddr) {
|
||||
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
|
||||
fullmeta->vwt->initializeWithCopyFn(newObjectAddr, oldObjectAddr, metadata);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -261,6 +261,60 @@ public func swift_allocEmptyBox() -> Builtin.RawPointer {
|
||||
return box
|
||||
}
|
||||
|
||||
/// The embedded swift_allocBox version is different to the standad one in that
|
||||
/// we want to avoid building metadata for the box type. Instead we store the
|
||||
/// metadata of the contained type in the heap object. To make this work when
|
||||
/// destroying the box the release needs to be special i.e `swift_releaseBox`.
|
||||
/// It does not call the the heap object metadata's destroy function. Rather, it
|
||||
/// knows that the allocBox's metadata is the contained objects and calls an
|
||||
/// appropriate implementation: `_swift_embedded_invoke_box_destroy`.
|
||||
/// Therefore, one cannot not use `swift_release` but rather must use
|
||||
/// `swift_releaseBox` to implement the "release" function of a box object.
|
||||
|
||||
@_silgen_name("swift_allocBox")
|
||||
public func swift_allocBox(_ metadata: Builtin.RawPointer) -> (Builtin.RawPointer, Builtin.RawPointer) {
|
||||
let alignMask = Int(unsafe _swift_embedded_metadata_get_align_mask(UnsafeMutableRawPointer(metadata)))
|
||||
let size = Int(unsafe _swift_embedded_metadata_get_size(UnsafeMutableRawPointer(metadata)))
|
||||
let headerSize = unsafe MemoryLayout<Int>.size + MemoryLayout<UnsafeRawPointer>.size
|
||||
let headerAlignMask = unsafe MemoryLayout<UnsafeRawPointer>.alignment - 1
|
||||
let startOfBoxedValue = ((headerSize + alignMask) & ~alignMask)
|
||||
let requiredSize: Int = startOfBoxedValue + size
|
||||
let requiredAlignmentMask: Int = alignMask | headerAlignMask
|
||||
|
||||
let p = unsafe swift_slowAlloc(requiredSize, requiredAlignmentMask)!
|
||||
let object = unsafe p.assumingMemoryBound(to: HeapObject.self)
|
||||
|
||||
unsafe _swift_embedded_set_heap_object_metadata_pointer(object, UnsafeMutableRawPointer(metadata))
|
||||
unsafe object.pointee.refcount = 1
|
||||
|
||||
let boxedValueAddr = unsafe UnsafeMutableRawPointer(p).advanced(by: startOfBoxedValue)
|
||||
|
||||
return (object._rawValue, boxedValueAddr._rawValue)
|
||||
}
|
||||
|
||||
@c
|
||||
public func swift_deallocBox(_ object: Builtin.RawPointer) {
|
||||
unsafe free(UnsafeMutableRawPointer(object))
|
||||
}
|
||||
|
||||
@_silgen_name("swift_makeBoxUnique")
|
||||
public func swifft_makeBoxUnique(buffer: Builtin.RawPointer, metadata: Builtin.RawPointer, alignMask: Int) -> (Builtin.RawPointer, Builtin.RawPointer){
|
||||
let addrOfHeapObjectPtr = unsafe UnsafeMutablePointer<Builtin.RawPointer>(buffer)
|
||||
let box = unsafe addrOfHeapObjectPtr.pointee
|
||||
let headerSize = unsafe MemoryLayout<Int>.size + MemoryLayout<UnsafeRawPointer>.size
|
||||
let startOfBoxedValue = ((headerSize + alignMask) & ~alignMask)
|
||||
let oldObjectAddr = unsafe UnsafeMutableRawPointer(box) + startOfBoxedValue
|
||||
|
||||
if !swift_isUniquelyReferenced_native(object: box) {
|
||||
let refAndObjectAddr = swift_allocBox(metadata)
|
||||
unsafe _swift_embedded_initialize_box(UnsafeMutableRawPointer(metadata), UnsafeMutableRawPointer(refAndObjectAddr.1), oldObjectAddr)
|
||||
swift_releaseBox(box)
|
||||
unsafe addrOfHeapObjectPtr.pointee = refAndObjectAddr.0
|
||||
return refAndObjectAddr
|
||||
} else {
|
||||
return (box, oldObjectAddr._rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Refcounting
|
||||
|
||||
@@ -388,7 +442,7 @@ public func swift_release_n(object: Builtin.RawPointer, n: UInt32) {
|
||||
unsafe swift_release_n_(object: o, n: n)
|
||||
}
|
||||
|
||||
func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
|
||||
func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32, isBoxRelease: Bool = false) {
|
||||
guard let object = unsafe object else {
|
||||
return
|
||||
}
|
||||
@@ -413,12 +467,25 @@ func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
|
||||
let doNotFree = unsafe (loadedRefcount & HeapObject.doNotFreeBit) != 0
|
||||
unsafe storeRelaxed(refcount, newValue: HeapObject.immortalRefCount | (doNotFree ? HeapObject.doNotFreeBit : 0))
|
||||
|
||||
if isBoxRelease {
|
||||
unsafe _swift_embedded_invoke_box_destroy(object)
|
||||
} else {
|
||||
unsafe _swift_embedded_invoke_heap_object_destroy(object)
|
||||
}
|
||||
} else if resultingRefcount < 0 {
|
||||
fatalError("negative refcount")
|
||||
}
|
||||
}
|
||||
|
||||
@c
|
||||
public func swift_releaseBox(_ object: Builtin.RawPointer) {
|
||||
if !isValidPointerForNativeRetain(object: object) {
|
||||
fatalError("not a valid pointer for releaseBox")
|
||||
}
|
||||
let o = unsafe UnsafeMutablePointer<HeapObject>(object)
|
||||
unsafe swift_release_n_(object: o, n: 1, isBoxRelease: true)
|
||||
}
|
||||
|
||||
@c
|
||||
public func swift_bridgeObjectRelease(object: Builtin.RawPointer) {
|
||||
swift_bridgeObjectRelease_n(object: object, n: 1)
|
||||
|
||||
@@ -1,23 +1,225 @@
|
||||
// RUN: %target-swift-frontend -enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo -emit-sil %s | %FileCheck %s
|
||||
// RUN: %target-run-simple-swift(-enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s --check-prefix=OUTPUT
|
||||
// RUN: %target-run-simple-swift(-enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo -O) | %FileCheck %s --check-prefix=OUTPUT
|
||||
|
||||
// REQUIRES: swift_in_compiler
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: optimized_stdlib
|
||||
// REQUIRES: swift_feature_Embedded
|
||||
// REQUIRES: swift_feature_EmbeddedExistentials
|
||||
|
||||
class C {}
|
||||
class CP {
|
||||
}
|
||||
|
||||
class C : CP {
|
||||
func foo() { }
|
||||
}
|
||||
|
||||
class GC<T> {
|
||||
var x: T? = nil
|
||||
func foo() {}
|
||||
deinit {
|
||||
print("deinit called")
|
||||
}
|
||||
}
|
||||
|
||||
struct StructWithClass {
|
||||
var c = GC<Int>()
|
||||
}
|
||||
|
||||
struct GenericStructWithClass<T> {
|
||||
var c = GC<T>()
|
||||
var d = GC<T>()
|
||||
}
|
||||
|
||||
enum EnumWithClass {
|
||||
case a
|
||||
case c(GC<Int>)
|
||||
}
|
||||
|
||||
enum GenericEnumWithClass<T> {
|
||||
case a
|
||||
case c(GC<T>)
|
||||
}
|
||||
|
||||
// CHECK: sil @$e11existential4testyyF
|
||||
// CHECK: init_existential_addr
|
||||
// CHECK: } // end sil function '$e11existential4testyyF'
|
||||
|
||||
// There are 8 class instances that are destroyed.
|
||||
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT: deinit called
|
||||
|
||||
// OUTPUT-NOT: deinit called
|
||||
|
||||
func test() {
|
||||
let any: any Any = C()
|
||||
let _: any Any = GC<Int>()
|
||||
let _: any Any = 3
|
||||
let _: any Any = StructWithClass()
|
||||
let _: any Any = GenericStructWithClass<Int>()
|
||||
let _: any Any = EnumWithClass.c(GC<Int>())
|
||||
let _: any Any = GenericEnumWithClass.c(GC<Int>())
|
||||
let _: any Any = (3, 4)
|
||||
let _: any Any = (StructWithClass(), StructWithClass())
|
||||
// outline storage case
|
||||
let _: any Any = (StructWithClass(), StructWithClass(), StructWithClass(), StructWithClass())
|
||||
}
|
||||
|
||||
protocol Basic {
|
||||
func a()
|
||||
}
|
||||
|
||||
protocol Derived : Basic {
|
||||
func b()
|
||||
}
|
||||
|
||||
class Implementor : Derived {
|
||||
func a() { print("a") }
|
||||
func b() { print("b") }
|
||||
}
|
||||
|
||||
extension Int : Derived {
|
||||
func a() { print("a Int \(self)") }
|
||||
func b() { print("b Int \(self)") }
|
||||
}
|
||||
|
||||
struct MyStruct : Derived {
|
||||
var x = 5
|
||||
func a() { print("a MyStruct \(self.x)") }
|
||||
func b() { print("b MyStruct") }
|
||||
}
|
||||
|
||||
struct LargeMyStruct : Derived {
|
||||
var x = (1, 2, 3, 4, 5)
|
||||
var refCounted = StructWithClass()
|
||||
|
||||
func a() { print("a LargeMyStruct \(self.x.4)") }
|
||||
func b() { print("b LargeMyStruct") }
|
||||
}
|
||||
|
||||
enum MyEnum : Derived {
|
||||
case a
|
||||
case b(Int)
|
||||
|
||||
func a() {
|
||||
print("a MyEnum ")
|
||||
switch self {
|
||||
case .a: break
|
||||
case .b(let x):
|
||||
print(x)
|
||||
}
|
||||
}
|
||||
|
||||
func b() {
|
||||
print("b MyEnum ")
|
||||
}
|
||||
}
|
||||
|
||||
func test2(_ p: any Derived) {
|
||||
p.a()
|
||||
p.b()
|
||||
}
|
||||
|
||||
|
||||
protocol ValuePrinter {
|
||||
func printValue()
|
||||
mutating func mutate()
|
||||
}
|
||||
protocol WithAssoc {
|
||||
associatedtype Assoc : ValuePrinter
|
||||
func a() -> Assoc
|
||||
}
|
||||
|
||||
extension Int : ValuePrinter {
|
||||
func printValue() {
|
||||
print("my value: \(self)")
|
||||
}
|
||||
mutating func mutate() {
|
||||
self = 8
|
||||
print("my value (mutating expect 8): \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
extension LargeMyStruct : ValuePrinter {
|
||||
func printValue() {
|
||||
print("my value of LargeMyStruct: \(self.x.4)")
|
||||
}
|
||||
mutating func mutate() {
|
||||
self.x = (6, 7, 8, 9, 10)
|
||||
print("my value of LargeMyStruct (mutating expect 10): \(self.x.4)")
|
||||
}
|
||||
}
|
||||
|
||||
struct ConformWithAssoc : WithAssoc {
|
||||
var x = 1
|
||||
func a() -> Int {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
struct ConformWithLargeAssoc : WithAssoc {
|
||||
var x = LargeMyStruct()
|
||||
|
||||
func a() -> LargeMyStruct {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
func test3(_ p: any WithAssoc) {
|
||||
let x = p.a()
|
||||
x.printValue()
|
||||
}
|
||||
|
||||
func test4(_ p: any WithAssoc) {
|
||||
var x = p.a()
|
||||
let c = x
|
||||
x.mutate()
|
||||
c.printValue()
|
||||
}
|
||||
|
||||
@main
|
||||
struct Main {
|
||||
static func main() {
|
||||
test()
|
||||
|
||||
test2(Implementor())
|
||||
// OUTPUT: a
|
||||
// OUTPUT: b
|
||||
test2(5)
|
||||
// OUTPUT: a Int 5
|
||||
// OUTPUT: b Int 5
|
||||
test2(MyStruct())
|
||||
// OUTPUT: a MyStruct 5
|
||||
// OUTPUT: b MyStruct
|
||||
test2(MyEnum.b(5))
|
||||
// OUTPUT: a MyEnum
|
||||
// OUTPUT: 5
|
||||
// OUTPUT: b MyEnum
|
||||
test3(ConformWithAssoc())
|
||||
// OUTPUT: my value: 1
|
||||
test3(ConformWithLargeAssoc())
|
||||
// OUTPUT: my value of LargeMyStruct: 5
|
||||
// OUTPUT: deinit called
|
||||
test4(ConformWithAssoc())
|
||||
// OUTPUT: my value (mutating expect 8): 8
|
||||
// OUTPUT: my value: 1
|
||||
test4(ConformWithLargeAssoc())
|
||||
// OUTPUT: my value of LargeMyStruct (mutating expect 10): 10
|
||||
// OUTPUT: my value of LargeMyStruct: 5
|
||||
// OUTPUT: deinit called
|
||||
// OUTPUT-NOT: deinit called
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user