Skip to content

Commit 0c7aded

Browse files
Merge pull request #85871 from aschwaighofer/lazy_metadata_emission_embedded_exist
[embedded] Lazily emit class metadata in embedded mode and apply shared linkage
2 parents 4754eea + 93e494b commit 0c7aded

File tree

5 files changed

+151
-13
lines changed

5 files changed

+151
-13
lines changed

lib/IRGen/GenClass.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,16 +1051,22 @@ void IRGenModule::emitClassDecl(ClassDecl *D) {
10511051
auto &resilientLayout =
10521052
classTI.getClassLayout(*this, selfType, /*forBackwardDeployment=*/false);
10531053

1054+
auto isEmbeddedWithExistentials =
1055+
Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
1056+
10541057
// As a matter of policy, class metadata is never emitted lazily for now.
1055-
assert(!IRGen.hasLazyMetadata(D));
1058+
assert(isEmbeddedWithExistentials || !IRGen.hasLazyMetadata(D));
10561059

10571060
// Emit the class metadata.
10581061
if (!D->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
10591062
emitClassMetadata(*this, D, fragileLayout, resilientLayout);
10601063
emitFieldDescriptor(D);
10611064
} else {
1062-
if (!D->isGenericContext()) {
1063-
emitEmbeddedClassMetadata(*this, D, fragileLayout);
1065+
if (!isEmbeddedWithExistentials && !D->isGenericContext()) {
1066+
emitEmbeddedClassMetadata(*this, D);
1067+
} else {
1068+
// We create all metadata lazily in embedded with existentials mode.
1069+
return;
10641070
}
10651071
}
10661072

lib/IRGen/GenMeta.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5565,8 +5565,7 @@ static void emitEmbeddedVTable(IRGenModule &IGM, CanType classTy,
55655565
(void)var;
55665566
}
55675567

5568-
void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
5569-
const ClassLayout &fragileLayout) {
5568+
void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl) {
55705569
PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl);
55715570
assert(!classDecl->isForeign());
55725571
CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();
@@ -5578,7 +5577,9 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
55785577
// Might already be emitted, skip if that's the case.
55795578
auto entity =
55805579
LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
5581-
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
5580+
5581+
auto isEmbeddedWithExistentials = IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
5582+
if (isEmbeddedWithExistentials) {
55825583
entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::FullMetadata);
55835584
}
55845585
auto *existingVar = cast<llvm::GlobalVariable>(
@@ -5587,6 +5588,11 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
55875588
return;
55885589
}
55895590

5591+
if (isEmbeddedWithExistentials) {
5592+
emitEmbeddedClassMetadata(IGM, classTy->getClassOrBoundGenericClass());
5593+
return;
5594+
}
5595+
55905596
auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext();
55915597
PrettyStackTraceType stackTraceRAII(
55925598
context, "emitting lazy class metadata for", classTy);

lib/IRGen/GenMeta.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ namespace irgen {
6161

6262
/// Emit "embedded Swift" class metadata (a simple vtable) for the given class
6363
/// declaration.
64-
void emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *theClass,
65-
const ClassLayout &fragileLayout);
64+
void emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *theClass);
6665

6766
/// Emit the constant initializer of the type metadata candidate for
6867
/// the given foreign class declaration.

lib/IRGen/Linking.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,21 @@ SILDeclRef LinkEntity::getSILDeclRef() const {
629629
return ref;
630630
}
631631

632+
static bool isLazyEmissionOfPublicSymbolInMultipleModulesPossible(CanType ty) {
633+
// In embedded existenitals mode we generate lazy public metadata on demand
634+
// which makes it non unique.
635+
if (ty->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
636+
if (auto nominal = ty->getAnyNominal()) {
637+
if (SILDeclRef::declHasNonUniqueDefinition(nominal)) {
638+
return true;
639+
}
640+
} else {
641+
return true;
642+
}
643+
}
644+
return false;
645+
}
646+
632647
SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
633648
// For when `this` is a protocol conformance of some kind.
634649
auto getLinkageAsConformance = [&] {
@@ -658,6 +673,11 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
658673
case Kind::ValueWitnessTable: {
659674
auto type = getType();
660675

676+
// In embedded existenitals mode we generate lazy public metadata on demand
677+
// which makes it non unique.
678+
if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(type))
679+
return SILLinkage::Shared;
680+
661681
// Builtin types, (), () -> () and so on are in the runtime.
662682
if (!type.getAnyNominal())
663683
return getSILLinkage(FormalLinkage::PublicUnique, forDefinition);
@@ -696,12 +716,10 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
696716
if (isForcedShared())
697717
return SILLinkage::Shared;
698718

699-
// In embedded existenitals mode we generate metadata for tuple types.
700-
if (getType()->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
701-
(isa<TupleType>(getType()) ||
702-
isa<FunctionType>(getType()))) {
719+
// In embedded existenitals mode we generate lazy public metadata on demand
720+
// which makes it non unique.
721+
if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(getType()))
703722
return SILLinkage::Shared;
704-
}
705723

706724
auto *nominal = getType().getAnyNominal();
707725
switch (getMetadataAddress()) {

test/embedded/lazy_metadata.swift

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// 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
5+
// RUN: %FileCheck --check-prefix=A1 %s < %t/A1.ll
6+
7+
// 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
8+
// RUN: %FileCheck --check-prefix=A2 %s < %t/A2.ll
9+
// RUN: %FileCheck --check-prefix=B2 %s < %t/B2.ll
10+
11+
// 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
12+
// RUN: %FileCheck --check-prefix=A3 %s < %t/A3.ll
13+
14+
// 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
15+
// RUN: %FileCheck --check-prefix=C4 %s < %t/C4.ll
16+
17+
// REQUIRES: swift_in_compiler
18+
// REQUIRES: optimized_stdlib
19+
// REQUIRES: swift_feature_Embedded
20+
// REQUIRES: swift_feature_EmbeddedExistentials
21+
22+
23+
//--- A.swift
24+
public class SomeClass {
25+
}
26+
27+
public struct SomeStruct {
28+
var x = 1
29+
var y = 2
30+
}
31+
32+
public enum SomeEnum {
33+
case a(Int)
34+
case b(Float)
35+
}
36+
37+
//--- B.swift
38+
public func getSomeClass() -> SomeClass {
39+
return SomeClass()
40+
}
41+
42+
public func getSomeStructAsAny() -> Any {
43+
return SomeStruct()
44+
}
45+
46+
public func getSomeStruct() -> SomeStruct {
47+
return SomeStruct()
48+
}
49+
50+
public func getSomeEnumAsAny() -> Any {
51+
return SomeEnum.a(1)
52+
}
53+
54+
public func getSomeEnum()-> SomeEnum {
55+
return SomeEnum.b(2.0)
56+
}
57+
58+
//--- C.swift
59+
import A
60+
public func useMetadata() -> Any {
61+
return getSomeClass()
62+
}
63+
64+
public func useMetadata2() -> Any {
65+
return getSomeStruct()
66+
}
67+
68+
public func useMetadata3() -> Any {
69+
return getSomeEnum()
70+
}
71+
72+
// Test no reference of metadata.
73+
// A1-NOT: CMf
74+
75+
// Test reference of metadata. Because we are the defining module metadata is
76+
// visible outside of the image per current policy.
77+
// In multiple llvm modules per SIL module mode "Private" SIL linkage becomes
78+
// hidden linkonce_odr.
79+
// A2: @"$e1A8SomeEnumOMf" = linkonce_odr hidden constant
80+
// A2: @"$e1A10SomeStructVMf" = linkonce_odr hidden constant
81+
// A2: @"$e1A9SomeClassCMf" = linkonce_odr hidden global
82+
83+
// A2: @"$e1A8SomeEnumON" = {{.*}}alias{{.*}}e1A8SomeEnumOMf
84+
// A2: @"$e1A10SomeStructVN" = {{.*}}alias{{.*}}e1A10SomeStructVMf
85+
// A2: @"$e1A9SomeClassCN" = {{.*}}alias{{.*}}e1A9SomeClassCMf
86+
87+
// B2: @"$e1A9SomeClassCN" = {{.*}}external{{.*}} global
88+
// B2: @"$e1A10SomeStructVN" = {{.*}}external{{.*}} global
89+
// B2: @"$e1A8SomeEnumON" = {{.*}}external{{.*}} global
90+
// B2: call {{.*}} @"$e1A9SomeClassCACycfC"(ptr swiftself @"$e1A9SomeClassCN")
91+
// B2: store{{.*}}e1A10SomeStructVN
92+
// B2: store{{.*}}e1A8SomeEnumON
93+
94+
// Test reference of metadata. Because we are the defining module metadata is
95+
// visible outside of the image per current policy.
96+
// In single llvm module per SIL module "Private" SIL linkage makes more
97+
// intuitive sense.
98+
// A3: @"$e1A8SomeEnumOMf" = {{.*}}internal constant
99+
// A3: @"$e1A10SomeStructVMf" = {{.*}}internal constant
100+
// A3: @"$e1A9SomeClassCMf" = {{.*}}internal global
101+
// A3: @"$e1A9SomeClassCN" = {{.*}}alias{{.*}}e1A9SomeClassCMf
102+
// A3: call {{.*}} @"$e1A9SomeClassCACycfC"({{.*}}getelementptr{{.*}}@"$e1A9SomeClassCMf"
103+
104+
105+
// Test "external" reference of metadata (defines metadata from another module
106+
// in current module as linkonce).
107+
// C4: @"$e1A8SomeEnumOMf" = linkonce_odr hidden constant
108+
// C4: @"$e1A10SomeStructVMf" = linkonce_odr hidden constant
109+
// C4: @"$e1A9SomeClassCMf" = linkonce_odr hidden global

0 commit comments

Comments
 (0)