diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 931a45cb4fa2c..223b3c2162180 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -1051,16 +1051,22 @@ void IRGenModule::emitClassDecl(ClassDecl *D) { auto &resilientLayout = classTI.getClassLayout(*this, selfType, /*forBackwardDeployment=*/false); + auto isEmbeddedWithExistentials = + Context.LangOpts.hasFeature(Feature::EmbeddedExistentials); + // As a matter of policy, class metadata is never emitted lazily for now. - assert(!IRGen.hasLazyMetadata(D)); + assert(isEmbeddedWithExistentials || !IRGen.hasLazyMetadata(D)); // Emit the class metadata. if (!D->getASTContext().LangOpts.hasFeature(Feature::Embedded)) { emitClassMetadata(*this, D, fragileLayout, resilientLayout); emitFieldDescriptor(D); } else { - if (!D->isGenericContext()) { - emitEmbeddedClassMetadata(*this, D, fragileLayout); + if (!isEmbeddedWithExistentials && !D->isGenericContext()) { + emitEmbeddedClassMetadata(*this, D); + } else { + // We create all metadata lazily in embedded with existentials mode. + return; } } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 1a6921606165b..a8bb024b1dce3 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -5308,7 +5308,8 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata( return cast(addr); } - + bool hasEmbeddedExistentials = + Context.LangOpts.hasFeature(Feature::EmbeddedExistentials); auto entity = (isPrespecialized && !irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable( @@ -5324,7 +5325,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata( if (Context.LangOpts.hasFeature(Feature::Embedded)) { entity = LinkEntity::forTypeMetadata(concreteType, TypeMetadataAddress::AddressPoint); - if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) + if (hasEmbeddedExistentials) entity = LinkEntity::forTypeMetadata(concreteType, TypeMetadataAddress::FullMetadata); } @@ -5350,7 +5351,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata( markGlobalAsUsedBasedOnLinkage(*this, link, var); if (Context.LangOpts.hasFeature(Feature::Embedded) && - !Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + !hasEmbeddedExistentials) { return var; } @@ -5361,14 +5362,13 @@ 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 && - !Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) + if (!isObjCImpl && !hasEmbeddedExistentials) 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 && !Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) || + if ((requiresForeignTypeMetadata(nominal) && !hasEmbeddedExistentials) || + (isPrespecialized && !hasEmbeddedExistentials) || isObjCImpl) return var; @@ -5382,7 +5382,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata( } } - if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + if (hasEmbeddedExistentials) { adjustmentIndex = MetadataAdjustmentIndex::EmbeddedWithExistentials; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 2c83c1d410aa8..0f73cecb1293e 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5565,8 +5565,7 @@ static void emitEmbeddedVTable(IRGenModule &IGM, CanType classTy, (void)var; } -void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl, - const ClassLayout &fragileLayout) { +void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl) { PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl); assert(!classDecl->isForeign()); CanType declaredType = classDecl->getDeclaredType()->getCanonicalType(); @@ -5578,7 +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)) { + + auto isEmbeddedWithExistentials = IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials); + if (isEmbeddedWithExistentials) { entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::FullMetadata); } auto *existingVar = cast( @@ -5587,6 +5588,11 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) { return; } + if (isEmbeddedWithExistentials) { + emitEmbeddedClassMetadata(IGM, classTy->getClassOrBoundGenericClass()); + return; + } + auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext(); PrettyStackTraceType stackTraceRAII( context, "emitting lazy class metadata for", classTy); @@ -7196,17 +7202,23 @@ void irgen::emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl) { auto init = builder.beginStruct(); init.setPacked(true); + auto isEmbedded = + IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials); + if (auto classDecl = dyn_cast(decl)) { if (classDecl->isForeignReferenceType()) { + assert(!isEmbedded && "emitting foregin reference type not supported"); ForeignReferenceTypeMetadataBuilder builder(IGM, classDecl, init); builder.layout(); IGM.defineTypeMetadata(type, /*isPattern=*/false, builder.canBeConstant(), init.finishAndCreateFuture()); - builder.createMetadataAccessFunction(); + if (!isEmbedded) + builder.createMetadataAccessFunction(); } else { assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType); + assert(!isEmbedded && "emitting foregin cf class type not supported"); ForeignClassMetadataBuilder builder(IGM, classDecl, init); builder.layout(); @@ -7214,28 +7226,37 @@ void irgen::emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl) { IGM.defineTypeMetadata(type, /*isPattern=*/false, builder.canBeConstant(), init.finishAndCreateFuture()); - builder.createMetadataAccessFunction(); + if (!isEmbedded) + builder.createMetadataAccessFunction(); } } else if (auto structDecl = dyn_cast(decl)) { assert(isa(structDecl->getModuleScopeContext())); ForeignStructMetadataBuilder builder(IGM, structDecl, init); - builder.layout(); + if (isEmbedded) + builder.embeddedLayout(); + else + builder.layout(); IGM.defineTypeMetadata(type, /*isPattern=*/false, builder.canBeConstant(), init.finishAndCreateFuture()); - builder.createMetadataAccessFunction(); + if (!isEmbedded) + builder.createMetadataAccessFunction(); } else if (auto enumDecl = dyn_cast(decl)) { assert(enumDecl->hasClangNode()); - + ForeignEnumMetadataBuilder builder(IGM, enumDecl, init); - builder.layout(); + if (isEmbedded) + builder.embeddedLayout(); + else + builder.layout(); IGM.defineTypeMetadata(type, /*isPattern=*/false, builder.canBeConstant(), init.finishAndCreateFuture()); - builder.createMetadataAccessFunction(); + if (!isEmbedded) + builder.createMetadataAccessFunction(); } else { llvm_unreachable("foreign metadata for unexpected type?!"); } diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 08d354a099613..cdee9e309239c 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -61,8 +61,7 @@ namespace irgen { /// Emit "embedded Swift" class metadata (a simple vtable) for the given class /// declaration. - void emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *theClass, - const ClassLayout &fragileLayout); + void emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *theClass); /// Emit the constant initializer of the type metadata candidate for /// the given foreign class declaration. diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 98aa309152d1e..d16f61aa6fe17 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -629,6 +629,21 @@ SILDeclRef LinkEntity::getSILDeclRef() const { return ref; } +static bool isLazyEmissionOfPublicSymbolInMultipleModulesPossible(CanType ty) { + // In embedded existenitals mode we generate lazy public metadata on demand + // which makes it non unique. + if (ty->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials)) { + if (auto nominal = ty->getAnyNominal()) { + if (SILDeclRef::declHasNonUniqueDefinition(nominal)) { + return true; + } + } else { + return true; + } + } + return false; +} + SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { // For when `this` is a protocol conformance of some kind. auto getLinkageAsConformance = [&] { @@ -658,6 +673,11 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::ValueWitnessTable: { auto type = getType(); + // In embedded existenitals mode we generate lazy public metadata on demand + // which makes it non unique. + if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(type)) + return SILLinkage::Shared; + // Builtin types, (), () -> () and so on are in the runtime. if (!type.getAnyNominal()) return getSILLinkage(FormalLinkage::PublicUnique, forDefinition); @@ -696,12 +716,10 @@ 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()) || - isa(getType()))) { + // In embedded existenitals mode we generate lazy public metadata on demand + // which makes it non unique. + if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(getType())) return SILLinkage::Shared; - } auto *nominal = getType().getAnyNominal(); switch (getMetadataAddress()) { diff --git a/test/embedded/Inputs/existential_foreign.h b/test/embedded/Inputs/existential_foreign.h new file mode 100644 index 0000000000000..a5d6547df2d76 --- /dev/null +++ b/test/embedded/Inputs/existential_foreign.h @@ -0,0 +1,34 @@ +#pragma once + +typedef struct { + unsigned long long f1; + unsigned long long f2; +} SomeCStruct; + +static inline SomeCStruct createSomeCStruct() { + SomeCStruct s; + s.f1 = 1; + s.f2 = 2; + return s; +} + +typedef enum { + caseA, + caseB, + caseC +} SomeCEnum; + +static inline SomeCEnum createSomeCEnum() { + return caseA; +} + +#define SWIFT_ENUM(_type, _name) enum _name : _type + +typedef SWIFT_ENUM(unsigned short, SomeNSEnum) { + someCaseA, + someCaseB, +} SomeNSEnum; + +static inline SomeNSEnum createSomeNSEnum() { + return someCaseB; +} diff --git a/test/embedded/existential_foreign.swift b/test/embedded/existential_foreign.swift new file mode 100644 index 0000000000000..05cbb637bec38 --- /dev/null +++ b/test/embedded/existential_foreign.swift @@ -0,0 +1,102 @@ +// RUN: %target-run-simple-swift(-import-objc-header %S/Inputs/existential_foreign.h -enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s --check-prefix=OUTPUT +// RUN: %target-run-simple-swift(-import-objc-header %S/Inputs/existential_foreign.h -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 + +protocol P { + func printme() +} + +protocol Q { + associatedtype Printable : P + + func getPrintable() -> Printable +} + +extension SomeCStruct : P { + func printme() { + print("SomeCStruct: \(self.f1), \(self.f2)") + } +} + +extension SomeCEnum : P { + func printme() { + switch self { + case caseA: + print("SomeCEnum: .caseA") + case caseB: + print("SomeCEnum: .caseB") + case caseC: + print("SomeCEnum: .caseC") + default: + print("SomeCEnum: default") + } + } +} + +struct SomeCStructContainer : Q { + let s: SomeCStruct + + init() { + self.s = createSomeCStruct() + } + + func getPrintable() -> SomeCStruct { + return s + } +} + +struct SomeCEnumContainer : Q { + let s: SomeCEnum + + init() { + self.s = createSomeCEnum() + } + + func getPrintable() -> SomeCEnum { + return s + } +} + +extension SomeNSEnum : P { + func printme() { + switch self { + case .someCaseA: + print("SomeNSEnum: .someCaseA") + case .someCaseB: + print("SomeNSEnum: .someCaseB") + } + } +} + +struct SomeNSEnumContainer : Q { + let s: SomeNSEnum + + init() { + self.s = createSomeNSEnum() + } + + func getPrintable() -> SomeNSEnum { + return s + } +} + +@main +struct Main { + static func main() { + + let a: [any Q] = [ SomeCStructContainer(), SomeCEnumContainer(), SomeNSEnumContainer() ] + + for x0 in a { + let x = x0.getPrintable() + x.printme() + // OUTPUT: SomeCStruct: 1, 2 + // OUTPUT: SomeCEnum: .caseA + // OUTPUT: SomeNSEnum: .someCaseB + } + } +} diff --git a/test/embedded/lazy_metadata.swift b/test/embedded/lazy_metadata.swift new file mode 100644 index 0000000000000..6a213e75773f1 --- /dev/null +++ b/test/embedded/lazy_metadata.swift @@ -0,0 +1,109 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -wmo -module-name A -emit-irgen -o %t/A1.ll %t/A.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library +// RUN: %FileCheck --check-prefix=A1 %s < %t/A1.ll + +// RUN: %target-swift-frontend -wmo -module-name A -num-threads 1 -emit-ir -o %t/A2.ll -o %t/B2.ll %t/A.swift %t/B.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library +// RUN: %FileCheck --check-prefix=A2 %s < %t/A2.ll +// RUN: %FileCheck --check-prefix=B2 %s < %t/B2.ll + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/A.swiftmodule -wmo -module-name A -emit-ir -o %t/A3.ll %t/A.swift %t/B.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library +// RUN: %FileCheck --check-prefix=A3 %s < %t/A3.ll + +// RUN: %target-swift-frontend -wmo -I %t -module-name C -emit-irgen -o %t/C4.ll %t/C.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library +// RUN: %FileCheck --check-prefix=C4 %s < %t/C4.ll + +// REQUIRES: swift_in_compiler +// REQUIRES: optimized_stdlib +// REQUIRES: swift_feature_Embedded +// REQUIRES: swift_feature_EmbeddedExistentials + + +//--- A.swift +public class SomeClass { +} + +public struct SomeStruct { + var x = 1 + var y = 2 +} + +public enum SomeEnum { + case a(Int) + case b(Float) +} + +//--- B.swift +public func getSomeClass() -> SomeClass { + return SomeClass() +} + +public func getSomeStructAsAny() -> Any { + return SomeStruct() +} + +public func getSomeStruct() -> SomeStruct { + return SomeStruct() +} + +public func getSomeEnumAsAny() -> Any { + return SomeEnum.a(1) +} + +public func getSomeEnum()-> SomeEnum { + return SomeEnum.b(2.0) +} + +//--- C.swift +import A +public func useMetadata() -> Any { + return getSomeClass() +} + +public func useMetadata2() -> Any { + return getSomeStruct() +} + +public func useMetadata3() -> Any { + return getSomeEnum() +} + +// Test no reference of metadata. +// A1-NOT: CMf + +// Test reference of metadata. Because we are the defining module metadata is +// visible outside of the image per current policy. +// In multiple llvm modules per SIL module mode "Private" SIL linkage becomes +// hidden linkonce_odr. +// A2: @"$e1A8SomeEnumOMf" = linkonce_odr hidden constant +// A2: @"$e1A10SomeStructVMf" = linkonce_odr hidden constant +// A2: @"$e1A9SomeClassCMf" = linkonce_odr hidden global + +// A2: @"$e1A8SomeEnumON" = {{.*}}alias{{.*}}e1A8SomeEnumOMf +// A2: @"$e1A10SomeStructVN" = {{.*}}alias{{.*}}e1A10SomeStructVMf +// A2: @"$e1A9SomeClassCN" = {{.*}}alias{{.*}}e1A9SomeClassCMf + +// B2: @"$e1A9SomeClassCN" = {{.*}}external{{.*}} global +// B2: @"$e1A10SomeStructVN" = {{.*}}external{{.*}} global +// B2: @"$e1A8SomeEnumON" = {{.*}}external{{.*}} global +// B2: call {{.*}} @"$e1A9SomeClassCACycfC"(ptr swiftself @"$e1A9SomeClassCN") +// B2: store{{.*}}e1A10SomeStructVN +// B2: store{{.*}}e1A8SomeEnumON + +// Test reference of metadata. Because we are the defining module metadata is +// visible outside of the image per current policy. +// In single llvm module per SIL module "Private" SIL linkage makes more +// intuitive sense. +// A3: @"$e1A8SomeEnumOMf" = {{.*}}internal constant +// A3: @"$e1A10SomeStructVMf" = {{.*}}internal constant +// A3: @"$e1A9SomeClassCMf" = {{.*}}internal global +// A3: @"$e1A9SomeClassCN" = {{.*}}alias{{.*}}e1A9SomeClassCMf +// A3: call {{.*}} @"$e1A9SomeClassCACycfC"({{.*}}getelementptr{{.*}}@"$e1A9SomeClassCMf" + + +// Test "external" reference of metadata (defines metadata from another module +// in current module as linkonce). +// C4: @"$e1A8SomeEnumOMf" = linkonce_odr hidden constant +// C4: @"$e1A10SomeStructVMf" = linkonce_odr hidden constant +// C4: @"$e1A9SomeClassCMf" = linkonce_odr hidden global