diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 931a45cb4fa2c..3e1ca248809a1 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 lazyly in embedded with existentials mode. + return; } } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 2c83c1d410aa8..c18d65cd604ab 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5398,6 +5398,7 @@ diagnoseUnsupportedObjCImplLayout(IRGenModule &IGM, ClassDecl *classDecl, void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl, const ClassLayout &fragileLayout, const ClassLayout &resilientLayout) { + assert(!classDecl->isForeign()); PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl); @@ -5565,8 +5566,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 +5578,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 +5589,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); 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/lazy_metadata.swift b/test/embedded/lazy_metadata.swift new file mode 100644 index 0000000000000..a6136f7c06e1f --- /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