diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index ef309be45f0a4..97a935ff1185a 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -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(t) || isa(t) || isa(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 @@ class LinkEntity { } static LinkEntity forValueWitnessTable(CanType type) { - assert(!isEmbedded(type)); + assert(!isEmbeddedWithoutEmbeddedExitentials(type)); LinkEntity entity; entity.setForType(Kind::ValueWitnessTable, type); return entity; @@ -1128,7 +1143,7 @@ class LinkEntity { } static LinkEntity forProtocolWitnessTable(const ProtocolConformance *C) { - if (isEmbedded(C)) { + if (isEmbeddedWithoutEmbeddedExitentials(C)) { assert(C->getProtocol()->requiresClass()); } diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index e82edee616fba..d52331e3d5d65 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -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), diff --git a/lib/IRGen/ClassMetadataVisitor.h b/lib/IRGen/ClassMetadataVisitor.h index ff55aa8161f03..0c8fad95291f1 100644 --- a/lib/IRGen/ClassMetadataVisitor.h +++ b/lib/IRGen/ClassMetadataVisitor.h @@ -77,6 +77,8 @@ template class ClassMetadataVisitor // 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 @@ template class ClassMetadataVisitor "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(); diff --git a/lib/IRGen/EnumMetadataVisitor.h b/lib/IRGen/EnumMetadataVisitor.h index a334bd27f0955..7ce2b81ae9eef 100644 --- a/lib/IRGen/EnumMetadataVisitor.h +++ b/lib/IRGen/EnumMetadataVisitor.h @@ -43,6 +43,14 @@ template class EnumMetadataVisitor : 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"); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 7188bc566a556..2683be010be4c 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -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(type) || isa(type))) { + bool isGeneric = cast(type)->isGenericContext(); + HasLazyMetadata[type] = !isGeneric; + + return !isGeneric; + } + auto canBeLazy = [&]() -> bool { auto *dc = type->getDeclContext(); if (isa(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() || concreteType->is()) { + if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + adjustmentIndex = 0; + defaultVarTy = EmbeddedExistentialsMetadataStructTy; + } else if (concreteType->isAny() || concreteType->isAnyObject() || concreteType->isVoid() || concreteType->is() || concreteType->is()) { defaultVarTy = FullExistentialTypeMetadataStructTy; adjustmentIndex = MetadataAdjustmentIndex::NoTypeLayoutString; } else if (fullMetadata) { @@ -5433,6 +5470,18 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType, } } } + + if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + if ((isa(nominal) || isa(nominal)) && + nominal->isGenericContext()) { + IRGen.noteUseOfSpecializedValueMetadata(concreteType); + } + } + } + + if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials) && + isa(concreteType)) { + IRGen.noteUseOfSpecializedValueMetadata(concreteType); } if (shouldPrespecializeGenericMetadata()) { diff --git a/lib/IRGen/GenExistential.cpp b/lib/IRGen/GenExistential.cpp index 1c39124ce0e60..89e017d922d96 100644 --- a/lib/IRGen/GenExistential.cpp +++ b/lib/IRGen/GenExistential.cpp @@ -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,7 +2897,11 @@ static llvm::Function *getDestroyBoxedOpaqueExistentialBufferFunction( Builder.CreateBitCast(buffer.getAddress(), IGM.PtrTy); auto *reference = Builder.CreateLoad(Address( referenceAddr, IGM.RefCountedPtrTy, buffer.getAlignment())); - IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity()); + if (IGF.IGM.Context.LangOpts + .hasFeature(Feature::EmbeddedExistentials)) { + IGF.emitReleaseBox(reference); + } else + IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity()); Builder.CreateRetVoid(); } diff --git a/lib/IRGen/GenHeap.cpp b/lib/IRGen/GenHeap.cpp index 6b41ddebf8742..111077347f1c0 100644 --- a/lib/IRGen/GenHeap.cpp +++ b/lib/IRGen/GenHeap.cpp @@ -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); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 7467f91c14ede..95fe6280af9aa 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -75,6 +75,7 @@ #include "ScalarTypeInfo.h" #include "StructLayout.h" #include "StructMetadataVisitor.h" +#include "TupleMetadataVisitor.h" #include "GenMeta.h" @@ -2969,7 +2970,10 @@ void irgen::emitLazyTypeContextDescriptor(IRGenModule &IGM, } void irgen::emitLazyTypeMetadata(IRGenModule &IGM, NominalTypeDecl *type) { - eraseExistingTypeContextDescriptor(IGM, 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)) { emitForeignTypeMetadata(IGM, 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( 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(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,11 +6228,16 @@ void irgen::emitStructMetadata(IRGenModule &IGM, StructDecl *structDecl) { builder.createMetadataAccessFunction(); } else { StructMetadataBuilder builder(IGM, structDecl, init); - builder.layout(); + if (hasEmbeddedExistentialFeature) { + builder.embeddedLayout(); + } else { + builder.layout(); + } isPattern = false; canBeConstant = builder.canBeConstant(); - builder.createMetadataAccessFunction(); + if (!hasEmbeddedExistentialFeature) + builder.createMetadataAccessFunction(); } CanType declaredType = structDecl->getDeclaredType()->getCanonicalType(); @@ -6224,13 +6259,74 @@ void irgen::emitSpecializedGenericStructMetadata(IRGenModule &IGM, CanType type, bool isPattern = false; SpecializedGenericStructMetadataBuilder builder(IGM, type, decl, init); - builder.layout(); + 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 { + using super = TupleMetadataVisitor; + + ConstantStructBuilder &B; + +protected: + + using super::asImpl; + using super::IGM; + using super::Target; + +public: + TupleMetadataBuilder(IRGenModule &IGM, CanType tupleTy, ConstantStructBuilder &B) : + super(IGM, cast(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(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 @@ -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,11 +6714,16 @@ void irgen::emitEnumMetadata(IRGenModule &IGM, EnumDecl *theEnum) { builder.createMetadataAccessFunction(); } else { EnumMetadataBuilder builder(IGM, theEnum, init); - builder.layout(); + if (hasEmbeddedExistentialFeature) { + builder.embeddedLayout(); + } else { + builder.layout(); + } isPattern = false; canBeConstant = builder.canBeConstant(); - builder.createMetadataAccessFunction(); + if (!hasEmbeddedExistentialFeature) + builder.createMetadataAccessFunction(); } CanType declaredType = theEnum->getDeclaredType()->getCanonicalType(); @@ -6640,7 +6744,11 @@ void irgen::emitSpecializedGenericEnumMetadata(IRGenModule &IGM, CanType type, init.setPacked(true); SpecializedGenericEnumMetadataBuilder builder(IGM, type, decl, init); - builder.layout(); + if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + builder.embeddedLayout(); + } else { + builder.layout(); + } bool canBeConstant = builder.canBeConstant(); IGM.defineTypeMetadata(type, /*isPattern=*/false, canBeConstant, diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index d6672cbe3ead1..e512ccfccb9c5 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -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, @@ -194,6 +198,12 @@ namespace irgen { // Class metadata has two words of head-allocated data: the destructor // 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. diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index ba8a02bb801ae..4824098395ade 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -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 @@ class AccessorConformanceInfo : public ConformanceInfo { 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 @@ class AccessorConformanceInfo : public ConformanceInfo { #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 @@ class AccessorConformanceInfo : public ConformanceInfo { 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( diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index 85dde1655f8e6..eba14a65d42a8 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -1380,17 +1380,24 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM, CanType abstractType, bool &canBeConstant) { std::optional boundGenericCharacteristics; - if (auto boundGenericType = dyn_cast(abstractType)) { - CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType); - - auto concreteLoweredType = IGM.getLoweredType(concreteFormalType); - const auto *boundConcreteTI = &IGM.getTypeInfo(concreteLoweredType); - auto packing = boundConcreteTI->getFixedPacking(IGM); - boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI, - packing}; - - abstractType = - boundGenericType->getDecl()->getDeclaredType()->getCanonicalType(); + // 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(abstractType)) { + CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType); + + auto concreteLoweredType = IGM.getLoweredType(concreteFormalType); + const auto *boundConcreteTI = &IGM.getTypeInfo(concreteLoweredType); + auto packing = boundConcreteTI->getFixedPacking(IGM); + boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI, + packing}; + abstractType = + boundGenericType->getDecl()->getDeclaredType()->getCanonicalType(); + } } CanType concreteFormalType = getFormalTypeInPrimaryContext(abstractType); @@ -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(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. diff --git a/lib/IRGen/IRGenFunction.h b/lib/IRGen/IRGenFunction.h index e96f4fae589b7..c25fb901e8b77 100644 --- a/lib/IRGen/IRGenFunction.h +++ b/lib/IRGen/IRGenFunction.h @@ -626,6 +626,9 @@ class IRGenFunction { 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); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 47827766f9833..2ad34f9765ff5 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -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()); } diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index c2c86292b540f..a0aa597431b58 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -349,8 +349,9 @@ class IRGenerator { llvm::SmallPtrSet LazilyEmittedClassMetadata; llvm::SmallVector LazySpecializedClassMetadata; + llvm::SmallVector LazySpecializedValueMetadata; - llvm::SmallPtrSet LazilyEmittedSpecializedClassMetadata; + llvm::SmallPtrSet LazilyEmittedSpecializedMetadata; llvm::SmallVector ClassesForEagerInitialization; @@ -509,6 +510,7 @@ class IRGenerator { void noteUseOfClassMetadata(CanType classType); void noteUseOfSpecializedClassMetadata(CanType classType); + void noteUseOfSpecializedValueMetadata(CanType valueType); void noteUseOfTypeMetadata(NominalTypeDecl *type) { noteUseOfTypeGlobals(type, true, RequireMetadata); @@ -828,6 +830,7 @@ class IRGenModule { 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 { ... } diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 249709a8da563..9c860a9c67a60 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -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(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 diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 92c10012361c4..a3f6de5114a93 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -886,6 +886,17 @@ bool irgen::isCompleteSpecializedNominalTypeMetadataStaticallyAddressable( return false; } + if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials) && + (isa(type) || isa(type) || + isa(type) || isa(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(type) || - type->getASTContext().LangOpts.hasFeature(Feature::Embedded)) { + langOpts.hasFeature(Feature::Embedded)) { return emitDirectTypeMetadataRef(*this, type, request); } diff --git a/lib/IRGen/StructMetadataVisitor.h b/lib/IRGen/StructMetadataVisitor.h index 6ffca8fe0cd22..37c4676580575 100644 --- a/lib/IRGen/StructMetadataVisitor.h +++ b/lib/IRGen/StructMetadataVisitor.h @@ -43,6 +43,14 @@ template class StructMetadataVisitor : 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"); diff --git a/lib/IRGen/TupleMetadataVisitor.h b/lib/IRGen/TupleMetadataVisitor.h index 02dbb07b50926..e801965823ec9 100644 --- a/lib/IRGen/TupleMetadataVisitor.h +++ b/lib/IRGen/TupleMetadataVisitor.h @@ -39,6 +39,14 @@ template struct TupleMetadataVisitor : super(IGM), Target(target) {} public: + + void embeddedLayout() { + // The embedded layout consists of: + // -1 : vwt + // 0 : metadata flags + super::layout(); + } + void layout() { super::layout(); diff --git a/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h b/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h index f9a20694b1e9e..f73a40e63ae91 100644 --- a/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h +++ b/stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h @@ -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 diff --git a/stdlib/public/core/EmbeddedRuntime.swift b/stdlib/public/core/EmbeddedRuntime.swift index badedc60645b7..299b1d82a8f75 100644 --- a/stdlib/public/core/EmbeddedRuntime.swift +++ b/stdlib/public/core/EmbeddedRuntime.swift @@ -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.size + MemoryLayout.size + let headerAlignMask = unsafe MemoryLayout.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(buffer) + let box = unsafe addrOfHeapObjectPtr.pointee + let headerSize = unsafe MemoryLayout.size + MemoryLayout.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?, n: UInt32) { +func swift_release_n_(object: UnsafeMutablePointer?, n: UInt32, isBoxRelease: Bool = false) { guard let object = unsafe object else { return } @@ -413,12 +467,25 @@ func swift_release_n_(object: UnsafeMutablePointer?, n: UInt32) { let doNotFree = unsafe (loadedRefcount & HeapObject.doNotFreeBit) != 0 unsafe storeRelaxed(refcount, newValue: HeapObject.immortalRefCount | (doNotFree ? HeapObject.doNotFreeBit : 0)) - unsafe _swift_embedded_invoke_heap_object_destroy(object) + 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(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) diff --git a/test/embedded/existential.swift b/test/embedded/existential.swift index 9073258edcc02..dd850b85f4175 100644 --- a/test/embedded/existential.swift +++ b/test/embedded/existential.swift @@ -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 { + var x: T? = nil + func foo() {} + deinit { + print("deinit called") + } +} + +struct StructWithClass { + var c = GC() +} + +struct GenericStructWithClass { + var c = GC() + var d = GC() +} + +enum EnumWithClass { + case a + case c(GC) +} + +enum GenericEnumWithClass { + case a + case c(GC) +} // 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() + let _: any Any = 3 + let _: any Any = StructWithClass() + let _: any Any = GenericStructWithClass() + let _: any Any = EnumWithClass.c(GC()) + let _: any Any = GenericEnumWithClass.c(GC()) + 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 } }