Skip to content

Commit b3f7c8d

Browse files
authored
Make VFE / WME / conditional records work even with ObjC interop and with reflection metadata, take #2 (swiftlang#39878)
1 parent dd798e3 commit b3f7c8d

12 files changed

+199
-87
lines changed

lib/IRGen/GenDecl.cpp

Lines changed: 77 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,8 @@ static void collectGlobalList(IRGenModule &IGM,
547547
static llvm::GlobalVariable *
548548
emitGlobalList(IRGenModule &IGM, ArrayRef<llvm::WeakTrackingVH> handles,
549549
StringRef name, StringRef section,
550-
llvm::GlobalValue::LinkageTypes linkage,
551-
llvm::Type *eltTy,
552-
bool isConstant) {
550+
llvm::GlobalValue::LinkageTypes linkage, llvm::Type *eltTy,
551+
bool isConstant, bool asContiguousArray) {
553552
// Do nothing if the list is empty.
554553
if (handles.empty()) return nullptr;
555554

@@ -558,6 +557,31 @@ emitGlobalList(IRGenModule &IGM, ArrayRef<llvm::WeakTrackingVH> handles,
558557
// so that the linker doesn't accidentally put padding in the list.
559558
Alignment alignment = IGM.getPointerAlignment();
560559

560+
if (!asContiguousArray) {
561+
// Emit as individual globals, which is required for conditional runtime
562+
// records to work.
563+
for (auto &handle : handles) {
564+
llvm::Constant *elt = cast<llvm::Constant>(&*handle);
565+
std::string eltName = name.str() + "_" + elt->getName().str();
566+
if (elt->getType() != eltTy)
567+
elt = llvm::ConstantExpr::getBitCast(elt, eltTy);
568+
auto var = new llvm::GlobalVariable(IGM.Module, eltTy, isConstant,
569+
linkage, elt, eltName);
570+
var->setSection(section);
571+
var->setAlignment(llvm::MaybeAlign(alignment.getValue()));
572+
disableAddressSanitizer(IGM, var);
573+
if (llvm::GlobalValue::isLocalLinkage(linkage))
574+
IGM.addUsedGlobal(var);
575+
576+
if (IGM.IRGen.Opts.ConditionalRuntimeRecords) {
577+
// Allow dead-stripping `var` (the runtime record from the global list)
578+
// when `handle` / `elt` (the underlaying entity) is not referenced.
579+
IGM.appendLLVMUsedConditionalEntry(var, elt->stripPointerCasts());
580+
}
581+
}
582+
return nullptr;
583+
}
584+
561585
// We have an array of value handles, but we need an array of constants.
562586
SmallVector<llvm::Constant*, 8> elts;
563587
elts.reserve(handles.size());
@@ -1037,36 +1061,41 @@ void IRGenModule::emitGlobalLists() {
10371061
if (ObjCInterop) {
10381062
// Objective-C class references go in a variable with a meaningless
10391063
// name but a magic section.
1040-
emitGlobalList(*this, ObjCClasses, "objc_classes",
1041-
GetObjCSectionName("__objc_classlist",
1042-
"regular,no_dead_strip"),
1043-
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
1064+
emitGlobalList(
1065+
*this, ObjCClasses, "objc_classes",
1066+
GetObjCSectionName("__objc_classlist", "regular,no_dead_strip"),
1067+
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
1068+
/*asContiguousArray*/ false);
10441069

10451070
// So do resilient class stubs.
1046-
emitGlobalList(*this, ObjCClassStubs, "objc_class_stubs",
1047-
GetObjCSectionName("__objc_stublist",
1048-
"regular,no_dead_strip"),
1049-
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
1071+
emitGlobalList(
1072+
*this, ObjCClassStubs, "objc_class_stubs",
1073+
GetObjCSectionName("__objc_stublist", "regular,no_dead_strip"),
1074+
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
1075+
/*asContiguousArray*/ true);
10501076

10511077
// So do categories.
1052-
emitGlobalList(*this, ObjCCategories, "objc_categories",
1053-
GetObjCSectionName("__objc_catlist",
1054-
"regular,no_dead_strip"),
1055-
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
1078+
emitGlobalList(
1079+
*this, ObjCCategories, "objc_categories",
1080+
GetObjCSectionName("__objc_catlist", "regular,no_dead_strip"),
1081+
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
1082+
/*asContiguousArray*/ true);
10561083

10571084
// And categories on class stubs.
1058-
emitGlobalList(*this, ObjCCategoriesOnStubs, "objc_categories_stubs",
1059-
GetObjCSectionName("__objc_catlist2",
1060-
"regular,no_dead_strip"),
1061-
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
1062-
1063-
// Emit nonlazily realized class references in a second magic section to make
1064-
// sure they are realized by the Objective-C runtime before any instances
1065-
// are allocated.
1066-
emitGlobalList(*this, ObjCNonLazyClasses, "objc_non_lazy_classes",
1067-
GetObjCSectionName("__objc_nlclslist",
1068-
"regular,no_dead_strip"),
1069-
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
1085+
emitGlobalList(
1086+
*this, ObjCCategoriesOnStubs, "objc_categories_stubs",
1087+
GetObjCSectionName("__objc_catlist2", "regular,no_dead_strip"),
1088+
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
1089+
/*asContiguousArray*/ true);
1090+
1091+
// Emit nonlazily realized class references in a second magic section to
1092+
// make sure they are realized by the Objective-C runtime before any
1093+
// instances are allocated.
1094+
emitGlobalList(
1095+
*this, ObjCNonLazyClasses, "objc_non_lazy_classes",
1096+
GetObjCSectionName("__objc_nlclslist", "regular,no_dead_strip"),
1097+
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
1098+
/*asContiguousArray*/ true);
10701099
}
10711100

10721101
// @llvm.used
@@ -1076,15 +1105,15 @@ void IRGenModule::emitGlobalLists() {
10761105
emitGlobalList(*this, LLVMUsed, "llvm.used", "llvm.metadata",
10771106
llvm::GlobalValue::AppendingLinkage,
10781107
Int8PtrTy,
1079-
false);
1108+
/*isConstant*/false, /*asContiguousArray*/true);
10801109

10811110
// Collect llvm.compiler.used globals already in the module (coming
10821111
// from ClangCodeGen).
10831112
collectGlobalList(*this, LLVMCompilerUsed, "llvm.compiler.used");
10841113
emitGlobalList(*this, LLVMCompilerUsed, "llvm.compiler.used", "llvm.metadata",
10851114
llvm::GlobalValue::AppendingLinkage,
10861115
Int8PtrTy,
1087-
false);
1116+
/*isConstant*/false, /*asContiguousArray*/true);
10881117
}
10891118

10901119
static bool hasCodeCoverageInstrumentation(SILFunction &f, SILModule &m) {
@@ -3709,39 +3738,34 @@ IRGenModule::emitDirectRelativeReference(llvm::Constant *target,
37093738

37103739
/// Expresses that `var` is removable (dead-strippable) when `dependsOn` is not
37113740
/// referenced.
3712-
static void appendLLVMUsedConditionalEntry(IRGenModule &IGM,
3713-
llvm::GlobalVariable *var,
3714-
llvm::Constant *dependsOn) {
3741+
void IRGenModule::appendLLVMUsedConditionalEntry(llvm::GlobalVariable *var,
3742+
llvm::Constant *dependsOn) {
37153743
llvm::Metadata *metadata[] = {
37163744
// (1) which variable is being conditionalized, "target"
37173745
llvm::ConstantAsMetadata::get(var),
37183746
// (2) type, not relevant for a single-edge condition
37193747
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
3720-
llvm::Type::getInt32Ty(IGM.Module.getContext()), 0)),
3748+
llvm::Type::getInt32Ty(Module.getContext()), 0)),
37213749
// (3) the "edge" that holds the target alive, if it's missing the target
37223750
// is allowed to be removed
3723-
llvm::MDNode::get(IGM.Module.getContext(),
3751+
llvm::MDNode::get(Module.getContext(),
37243752
{
37253753
llvm::ConstantAsMetadata::get(dependsOn),
37263754
}),
37273755
};
37283756
auto *usedConditional =
3729-
IGM.Module.getOrInsertNamedMetadata("llvm.used.conditional");
3730-
usedConditional->addOperand(
3731-
llvm::MDNode::get(IGM.Module.getContext(), metadata));
3757+
Module.getOrInsertNamedMetadata("llvm.used.conditional");
3758+
usedConditional->addOperand(llvm::MDNode::get(Module.getContext(), metadata));
37323759
}
37333760

37343761
/// Expresses that `var` is removable (dead-strippable) when either the protocol
37353762
/// from `record` is not referenced or the type from `record` is not referenced.
3736-
static void
3737-
appendLLVMUsedConditionalEntry(IRGenModule &IGM, llvm::GlobalVariable *var,
3738-
const ConformanceDescription &record) {
3739-
auto *protocol =
3740-
IGM.getAddrOfProtocolDescriptor(record.conformance->getProtocol())
3741-
->stripPointerCasts();
3742-
auto *type = IGM.getAddrOfTypeContextDescriptor(
3743-
record.conformance->getType()->getAnyNominal(),
3744-
DontRequireMetadata)
3763+
void IRGenModule::appendLLVMUsedConditionalEntry(
3764+
llvm::GlobalVariable *var, const ProtocolConformance *conformance) {
3765+
auto *protocol = getAddrOfProtocolDescriptor(conformance->getProtocol())
3766+
->stripPointerCasts();
3767+
auto *type = getAddrOfTypeContextDescriptor(
3768+
conformance->getType()->getAnyNominal(), DontRequireMetadata)
37453769
->stripPointerCasts();
37463770

37473771
llvm::Metadata *metadata[] = {
@@ -3750,18 +3774,17 @@ appendLLVMUsedConditionalEntry(IRGenModule &IGM, llvm::GlobalVariable *var,
37503774
// (2) type, "1" = if either edge is missing, the target is allowed to be
37513775
// removed.
37523776
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
3753-
llvm::Type::getInt32Ty(IGM.Module.getContext()), 1)),
3777+
llvm::Type::getInt32Ty(Module.getContext()), 1)),
37543778
// (3) list of edges
3755-
llvm::MDNode::get(IGM.Module.getContext(),
3779+
llvm::MDNode::get(Module.getContext(),
37563780
{
37573781
llvm::ConstantAsMetadata::get(protocol),
37583782
llvm::ConstantAsMetadata::get(type),
37593783
}),
37603784
};
37613785
auto *usedConditional =
3762-
IGM.Module.getOrInsertNamedMetadata("llvm.used.conditional");
3763-
usedConditional->addOperand(
3764-
llvm::MDNode::get(IGM.Module.getContext(), metadata));
3786+
Module.getOrInsertNamedMetadata("llvm.used.conditional");
3787+
usedConditional->addOperand(llvm::MDNode::get(Module.getContext(), metadata));
37653788
}
37663789

37673790
/// Emit the protocol descriptors list and return it (if asContiguousArray is
@@ -3843,7 +3866,7 @@ llvm::Constant *IRGenModule::emitSwiftProtocols(bool asContiguousArray) {
38433866
if (IRGen.Opts.ConditionalRuntimeRecords) {
38443867
// Allow dead-stripping `var` (the protocol record) when the protocol
38453868
// (descriptorRef) is not referenced.
3846-
appendLLVMUsedConditionalEntry(*this, var, descriptorRef.getValue());
3869+
appendLLVMUsedConditionalEntry(var, descriptorRef.getValue());
38473870
}
38483871
}
38493872

@@ -3937,7 +3960,7 @@ llvm::Constant *IRGenModule::emitProtocolConformances(bool asContiguousArray) {
39373960
if (IRGen.Opts.ConditionalRuntimeRecords) {
39383961
// Allow dead-stripping `var` (the conformance record) when the protocol
39393962
// or type (from the conformance) is not referenced.
3940-
appendLLVMUsedConditionalEntry(*this, var, record);
3963+
appendLLVMUsedConditionalEntry(var, record.conformance);
39413964
}
39423965
}
39433966

@@ -4052,7 +4075,7 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords(bool asContiguousArray) {
40524075
if (IRGen.Opts.ConditionalRuntimeRecords) {
40534076
// Allow dead-stripping `var` (the type record) when the type (`ref`) is
40544077
// not referenced.
4055-
appendLLVMUsedConditionalEntry(*this, var, ref.getValue());
4078+
appendLLVMUsedConditionalEntry(var, ref.getValue());
40564079
}
40574080
}
40584081

lib/IRGen/GenReflection.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -676,11 +676,20 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder {
676676

677677
llvm::GlobalVariable *emit() {
678678
auto section = IGM.getAssociatedTypeMetadataSectionName();
679-
return ReflectionMetadataBuilder::emit(
680-
[&](IRGenModule &IGM, ConstantInit init) -> llvm::Constant* {
681-
return IGM.getAddrOfReflectionAssociatedTypeDescriptor(Conformance,init);
682-
},
683-
section);
679+
llvm::GlobalVariable *var = ReflectionMetadataBuilder::emit(
680+
[&](IRGenModule &IGM, ConstantInit init) -> llvm::Constant * {
681+
return IGM.getAddrOfReflectionAssociatedTypeDescriptor(Conformance,
682+
init);
683+
},
684+
section);
685+
686+
if (IGM.IRGen.Opts.ConditionalRuntimeRecords) {
687+
// Allow dead-stripping `var` (the reflection record) when the protocol
688+
// or type (from the conformance) is not referenced.
689+
IGM.appendLLVMUsedConditionalEntry(var, Conformance);
690+
}
691+
692+
return var;
684693
}
685694
};
686695

@@ -856,12 +865,21 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
856865

857866
llvm::GlobalVariable *emit() {
858867
auto section = IGM.getFieldTypeMetadataSectionName();
859-
return ReflectionMetadataBuilder::emit(
860-
[&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* {
861-
return IGM.getAddrOfReflectionFieldDescriptor(
862-
NTD->getDeclaredType()->getCanonicalType(), definition);
863-
},
864-
section);
868+
llvm::GlobalVariable *var = ReflectionMetadataBuilder::emit(
869+
[&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant * {
870+
return IGM.getAddrOfReflectionFieldDescriptor(
871+
NTD->getDeclaredType()->getCanonicalType(), definition);
872+
},
873+
section);
874+
875+
if (IGM.IRGen.Opts.ConditionalRuntimeRecords) {
876+
// Allow dead-stripping `var` (the reflection record) when the type
877+
// (NTD) is not referenced.
878+
auto ref = IGM.getTypeEntityReference(const_cast<NominalTypeDecl *>(NTD));
879+
IGM.appendLLVMUsedConditionalEntry(var, ref.getValue());
880+
}
881+
882+
return var;
865883
}
866884
};
867885

lib/IRGen/IRGenModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,11 @@ private: \
14621462

14631463
TypeEntityReference getTypeEntityReference(GenericTypeDecl *D);
14641464

1465+
void appendLLVMUsedConditionalEntry(llvm::GlobalVariable *var,
1466+
llvm::Constant *dependsOn);
1467+
void appendLLVMUsedConditionalEntry(llvm::GlobalVariable *var,
1468+
const ProtocolConformance *conformance);
1469+
14651470
llvm::Constant *
14661471
getAddrOfTypeMetadata(CanType concreteType,
14671472
TypeMetadataCanonicality canonicality =

test/IRGen/MachO-objc-sections.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class D {
2121
// CHECK-MACHO: @"$s4main1CCMf" = {{.*}}, section "__DATA,__objc_data, regular"
2222
// CHECK-MACHO: @"\01l_OBJC_LABEL_PROTOCOL_$_P" = {{.*}}, section "__DATA,__objc_protolist,coalesced,no_dead_strip"
2323
// CHECK-MACHO: @"\01l_OBJC_PROTOCOL_REFERENCE_$_P" = {{.*}}, section "__DATA,__objc_protorefs,coalesced,no_dead_strip"
24-
// CHECK-MACHO: @objc_classes = {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip"
24+
// CHECK-MACHO: @"objc_classes_$s4main1CCN" = {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip"
25+
// CHECK-MACHO: @"objc_classes_$s4main1DCN" = {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip"
2526
// CHECK-MACHO: @objc_categories = {{.*}}, section "__DATA,__objc_catlist,regular,no_dead_strip"
2627
// CHECK-MACHO: @objc_non_lazy_classes = {{.*}}, section "__DATA,__objc_nlclslist,regular,no_dead_strip"
2728

test/IRGen/conditional-dead-strip-exec.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33

44
// RUN: %empty-directory(%t)
55

6-
// RUN: %target-build-swift -Xfrontend -disable-objc-interop \
7-
// RUN: -Xfrontend -disable-reflection-metadata -Xfrontend -disable-reflection-names \
8-
// RUN: -Xfrontend -conditional-runtime-records \
9-
// RUN: %s -emit-ir -o %t/main.ll
6+
// RUN: %target-build-swift -Xfrontend -conditional-runtime-records %s -emit-ir -o %t/main.ll
107

118
// RUN: %target-clang %t/main.ll -isysroot %sdk -L%swift_obj_root/lib/swift/%target-sdk-name -flto -o %t/main
129
// RUN: %target-run %t/main | %FileCheck %s
@@ -48,6 +45,10 @@ class UsedClass : UnusedProto, ActuallyUsedProto {
4845
public func bark() { print("UsedClass.bark") }
4946
}
5047

48+
// (9) unused protocol with associated type
49+
protocol ProtoWithAssocType { associatedtype T }
50+
struct Implementor : ProtoWithAssocType { typealias T = Int }
51+
5152
print("Hello!")
5253
func1_used()
5354
let o = UsedClass()
@@ -71,6 +72,11 @@ p.bark()
7172
// (2)
7273
// NM-NOT: $s4main10func2_deadyyF
7374

75+
// (9)
76+
// NM-NOT: $s4main11ImplementorVAA18ProtoWithAssocTypeAAMA
77+
// NM-NOT: $s4main11ImplementorVMf
78+
// NM-NOT: $s4main11ImplementorVMn
79+
7480
// (4)
7581
// NM-NOT: $s4main11TheProtocolMp
7682

test/IRGen/conditional-dead-strip-ir.swift

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
// enum, protocol, and protocol conformance records as conditionally removable
33
// via !llvm.used.conditional metadata.
44

5-
// RUN: %target-build-swift -Xfrontend -disable-objc-interop -Xfrontend -conditional-runtime-records \
6-
// RUN: %s -emit-ir -o - | %FileCheck %s
5+
// RUN: %target-build-swift -Xfrontend -conditional-runtime-records -Xfrontend -disable-objc-interop %s -emit-ir -o - | %FileCheck %s
76

87
public protocol TheProtocol {
98
}
@@ -25,19 +24,25 @@ public enum Enum {
2524
// CHECK-SAME: @"$s4main4EnumOHn"
2625
// CHECK-SAME: ], section "llvm.metadata"
2726

28-
// CHECK: !llvm.used.conditional = !{[[C1:!.*]], [[C2:!.*]], [[C3:!.*]], [[C4:!.*]], [[C5:!.*]]}
27+
// CHECK: !llvm.used.conditional = !{[[M1:!.*]], [[M2:!.*]], [[M3:!.*]], [[M4:!.*]], [[C1:!.*]], [[C2:!.*]], [[C3:!.*]], [[C4:!.*]], [[C5:!.*]]}
2928

30-
// CHECK: [[C1]] = !{{{.*}} @"$s4main11TheProtocolHr", i32 0, [[C1A:!.*]]}
31-
// CHECK: [[C1A]] = !{{{.*}} @"$s4main11TheProtocolMp"}
29+
// CHECK-DAG: [[M1]] = !{{{.*}} @"$s4main11TheProtocol_pMF", i32 0, [[M1A:!.*]]}
30+
// CHECK-DAG: [[M1A]] = !{{{.*}} @"$s4main11TheProtocolMp"
31+
// CHECK-DAG: [[M2]] = !{{{.*}} @"$s4main5ClassCMF", i32 0, [[M2A:!.*]]}
32+
// CHECK-DAG: [[M2A]] = !{{{.*}} @"$s4main5ClassCMn"
33+
// CHECK-DAG: [[M3]] = !{{{.*}} @"$s4main6StructVMF", i32 0, [[M3A:!.*]]}
34+
// CHECK-DAG: [[M3A]] = !{{{.*}} @"$s4main6StructVMn"
35+
// CHECK-DAG: [[M4]] = !{{{.*}} @"$s4main4EnumOMF", i32 0, [[M4A:!.*]]}
36+
// CHECK-DAG: [[M4A]] = !{{{.*}} @"$s4main4EnumOMn"
3237

33-
// CHECK: [[C2]] = !{{{.*}} @"$s4main5ClassCAA11TheProtocolAAHc", i32 1, [[C2A:!.*]]}
34-
// CHECK: [[C2A]] = !{{{.*}} @"$s4main11TheProtocolMp", {{.*}} @"$s4main5ClassCMn"}
38+
// CHECK-DAG: [[C1]] = !{{{.*}} @"$s4main11TheProtocolHr", i32 0, [[C1A:!.*]]}
39+
// CHECK-DAG: [[C1A]] = !{{{.*}} @"$s4main11TheProtocolMp"}
3540

36-
// CHECK: [[C3]] = !{{{.*}} @"$s4main5ClassCHn", i32 0, [[C3A:!.*]]}
37-
// CHECK: [[C3A]] = !{{{.*}} @"$s4main5ClassCMn"}
41+
// CHECK-DAG: [[C2]] = !{{{.*}} @"$s4main5ClassCAA11TheProtocolAAHc", i32 1, [[C2A:!.*]]}
42+
// CHECK-DAG: [[C2A]] = !{{{.*}} @"$s4main11TheProtocolMp", {{.*}} @"$s4main5ClassCMn"}
3843

39-
// CHECK: [[C4]] = !{{{.*}} @"$s4main6StructVHn", i32 0, [[C4A:!.*]]}
40-
// CHECK: [[C4A]] = !{{{.*}} @"$s4main6StructVMn"}
44+
// CHECK-DAG: [[C3]] = !{{{.*}} @"$s4main5ClassCHn", i32 0, [[M2A:!.*]]}
4145

42-
// CHECK: [[C5]] = !{{{.*}} @"$s4main4EnumOHn", i32 0, [[C5A:!.*]]}
43-
// CHECK: [[C5A]] = !{{{.*}} @"$s4main4EnumOMn"}
46+
// CHECK-DAG: [[C4]] = !{{{.*}} @"$s4main6StructVHn", i32 0, [[M3A:!.*]]}
47+
48+
// CHECK-DAG: [[C5]] = !{{{.*}} @"$s4main4EnumOHn", i32 0, [[M4A:!.*]]}

0 commit comments

Comments
 (0)