Skip to content

Make VFE / WME / conditional records work even with ObjC interop and with reflection metadata, take #2 #39878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 77 additions & 54 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,9 +547,8 @@ static void collectGlobalList(IRGenModule &IGM,
static llvm::GlobalVariable *
emitGlobalList(IRGenModule &IGM, ArrayRef<llvm::WeakTrackingVH> handles,
StringRef name, StringRef section,
llvm::GlobalValue::LinkageTypes linkage,
llvm::Type *eltTy,
bool isConstant) {
llvm::GlobalValue::LinkageTypes linkage, llvm::Type *eltTy,
bool isConstant, bool asContiguousArray) {
// Do nothing if the list is empty.
if (handles.empty()) return nullptr;

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

if (!asContiguousArray) {
// Emit as individual globals, which is required for conditional runtime
// records to work.
for (auto &handle : handles) {
llvm::Constant *elt = cast<llvm::Constant>(&*handle);
std::string eltName = name.str() + "_" + elt->getName().str();
if (elt->getType() != eltTy)
elt = llvm::ConstantExpr::getBitCast(elt, eltTy);
auto var = new llvm::GlobalVariable(IGM.Module, eltTy, isConstant,
linkage, elt, eltName);
var->setSection(section);
var->setAlignment(llvm::MaybeAlign(alignment.getValue()));
disableAddressSanitizer(IGM, var);
if (llvm::GlobalValue::isLocalLinkage(linkage))
IGM.addUsedGlobal(var);

if (IGM.IRGen.Opts.ConditionalRuntimeRecords) {
// Allow dead-stripping `var` (the runtime record from the global list)
// when `handle` / `elt` (the underlaying entity) is not referenced.
IGM.appendLLVMUsedConditionalEntry(var, elt->stripPointerCasts());
}
}
return nullptr;
}

// We have an array of value handles, but we need an array of constants.
SmallVector<llvm::Constant*, 8> elts;
elts.reserve(handles.size());
Expand Down Expand Up @@ -1037,36 +1061,41 @@ void IRGenModule::emitGlobalLists() {
if (ObjCInterop) {
// Objective-C class references go in a variable with a meaningless
// name but a magic section.
emitGlobalList(*this, ObjCClasses, "objc_classes",
GetObjCSectionName("__objc_classlist",
"regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
emitGlobalList(
*this, ObjCClasses, "objc_classes",
GetObjCSectionName("__objc_classlist", "regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
/*asContiguousArray*/ false);

// So do resilient class stubs.
emitGlobalList(*this, ObjCClassStubs, "objc_class_stubs",
GetObjCSectionName("__objc_stublist",
"regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
emitGlobalList(
*this, ObjCClassStubs, "objc_class_stubs",
GetObjCSectionName("__objc_stublist", "regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
/*asContiguousArray*/ true);

// So do categories.
emitGlobalList(*this, ObjCCategories, "objc_categories",
GetObjCSectionName("__objc_catlist",
"regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
emitGlobalList(
*this, ObjCCategories, "objc_categories",
GetObjCSectionName("__objc_catlist", "regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
/*asContiguousArray*/ true);

// And categories on class stubs.
emitGlobalList(*this, ObjCCategoriesOnStubs, "objc_categories_stubs",
GetObjCSectionName("__objc_catlist2",
"regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);

// Emit nonlazily realized class references in a second magic section to make
// sure they are realized by the Objective-C runtime before any instances
// are allocated.
emitGlobalList(*this, ObjCNonLazyClasses, "objc_non_lazy_classes",
GetObjCSectionName("__objc_nlclslist",
"regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
emitGlobalList(
*this, ObjCCategoriesOnStubs, "objc_categories_stubs",
GetObjCSectionName("__objc_catlist2", "regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
/*asContiguousArray*/ true);

// Emit nonlazily realized class references in a second magic section to
// make sure they are realized by the Objective-C runtime before any
// instances are allocated.
emitGlobalList(
*this, ObjCNonLazyClasses, "objc_non_lazy_classes",
GetObjCSectionName("__objc_nlclslist", "regular,no_dead_strip"),
llvm::GlobalValue::InternalLinkage, Int8PtrTy, /*isConstant*/ false,
/*asContiguousArray*/ true);
}

// @llvm.used
Expand All @@ -1076,15 +1105,15 @@ void IRGenModule::emitGlobalLists() {
emitGlobalList(*this, LLVMUsed, "llvm.used", "llvm.metadata",
llvm::GlobalValue::AppendingLinkage,
Int8PtrTy,
false);
/*isConstant*/false, /*asContiguousArray*/true);

// Collect llvm.compiler.used globals already in the module (coming
// from ClangCodeGen).
collectGlobalList(*this, LLVMCompilerUsed, "llvm.compiler.used");
emitGlobalList(*this, LLVMCompilerUsed, "llvm.compiler.used", "llvm.metadata",
llvm::GlobalValue::AppendingLinkage,
Int8PtrTy,
false);
/*isConstant*/false, /*asContiguousArray*/true);
}

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

/// Expresses that `var` is removable (dead-strippable) when `dependsOn` is not
/// referenced.
static void appendLLVMUsedConditionalEntry(IRGenModule &IGM,
llvm::GlobalVariable *var,
llvm::Constant *dependsOn) {
void IRGenModule::appendLLVMUsedConditionalEntry(llvm::GlobalVariable *var,
llvm::Constant *dependsOn) {
llvm::Metadata *metadata[] = {
// (1) which variable is being conditionalized, "target"
llvm::ConstantAsMetadata::get(var),
// (2) type, not relevant for a single-edge condition
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
llvm::Type::getInt32Ty(IGM.Module.getContext()), 0)),
llvm::Type::getInt32Ty(Module.getContext()), 0)),
// (3) the "edge" that holds the target alive, if it's missing the target
// is allowed to be removed
llvm::MDNode::get(IGM.Module.getContext(),
llvm::MDNode::get(Module.getContext(),
{
llvm::ConstantAsMetadata::get(dependsOn),
}),
};
auto *usedConditional =
IGM.Module.getOrInsertNamedMetadata("llvm.used.conditional");
usedConditional->addOperand(
llvm::MDNode::get(IGM.Module.getContext(), metadata));
Module.getOrInsertNamedMetadata("llvm.used.conditional");
usedConditional->addOperand(llvm::MDNode::get(Module.getContext(), metadata));
}

/// Expresses that `var` is removable (dead-strippable) when either the protocol
/// from `record` is not referenced or the type from `record` is not referenced.
static void
appendLLVMUsedConditionalEntry(IRGenModule &IGM, llvm::GlobalVariable *var,
const ConformanceDescription &record) {
auto *protocol =
IGM.getAddrOfProtocolDescriptor(record.conformance->getProtocol())
->stripPointerCasts();
auto *type = IGM.getAddrOfTypeContextDescriptor(
record.conformance->getType()->getAnyNominal(),
DontRequireMetadata)
void IRGenModule::appendLLVMUsedConditionalEntry(
llvm::GlobalVariable *var, const ProtocolConformance *conformance) {
auto *protocol = getAddrOfProtocolDescriptor(conformance->getProtocol())
->stripPointerCasts();
auto *type = getAddrOfTypeContextDescriptor(
conformance->getType()->getAnyNominal(), DontRequireMetadata)
->stripPointerCasts();

llvm::Metadata *metadata[] = {
Expand All @@ -3750,18 +3774,17 @@ appendLLVMUsedConditionalEntry(IRGenModule &IGM, llvm::GlobalVariable *var,
// (2) type, "1" = if either edge is missing, the target is allowed to be
// removed.
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
llvm::Type::getInt32Ty(IGM.Module.getContext()), 1)),
llvm::Type::getInt32Ty(Module.getContext()), 1)),
// (3) list of edges
llvm::MDNode::get(IGM.Module.getContext(),
llvm::MDNode::get(Module.getContext(),
{
llvm::ConstantAsMetadata::get(protocol),
llvm::ConstantAsMetadata::get(type),
}),
};
auto *usedConditional =
IGM.Module.getOrInsertNamedMetadata("llvm.used.conditional");
usedConditional->addOperand(
llvm::MDNode::get(IGM.Module.getContext(), metadata));
Module.getOrInsertNamedMetadata("llvm.used.conditional");
usedConditional->addOperand(llvm::MDNode::get(Module.getContext(), metadata));
}

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

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

Expand Down Expand Up @@ -4052,7 +4075,7 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords(bool asContiguousArray) {
if (IRGen.Opts.ConditionalRuntimeRecords) {
// Allow dead-stripping `var` (the type record) when the type (`ref`) is
// not referenced.
appendLLVMUsedConditionalEntry(*this, var, ref.getValue());
appendLLVMUsedConditionalEntry(var, ref.getValue());
}
}

Expand Down
40 changes: 29 additions & 11 deletions lib/IRGen/GenReflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,11 +676,20 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder {

llvm::GlobalVariable *emit() {
auto section = IGM.getAssociatedTypeMetadataSectionName();
return ReflectionMetadataBuilder::emit(
[&](IRGenModule &IGM, ConstantInit init) -> llvm::Constant* {
return IGM.getAddrOfReflectionAssociatedTypeDescriptor(Conformance,init);
},
section);
llvm::GlobalVariable *var = ReflectionMetadataBuilder::emit(
[&](IRGenModule &IGM, ConstantInit init) -> llvm::Constant * {
return IGM.getAddrOfReflectionAssociatedTypeDescriptor(Conformance,
init);
},
section);

if (IGM.IRGen.Opts.ConditionalRuntimeRecords) {
// Allow dead-stripping `var` (the reflection record) when the protocol
// or type (from the conformance) is not referenced.
IGM.appendLLVMUsedConditionalEntry(var, Conformance);
}

return var;
}
};

Expand Down Expand Up @@ -856,12 +865,21 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {

llvm::GlobalVariable *emit() {
auto section = IGM.getFieldTypeMetadataSectionName();
return ReflectionMetadataBuilder::emit(
[&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* {
return IGM.getAddrOfReflectionFieldDescriptor(
NTD->getDeclaredType()->getCanonicalType(), definition);
},
section);
llvm::GlobalVariable *var = ReflectionMetadataBuilder::emit(
[&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant * {
return IGM.getAddrOfReflectionFieldDescriptor(
NTD->getDeclaredType()->getCanonicalType(), definition);
},
section);

if (IGM.IRGen.Opts.ConditionalRuntimeRecords) {
// Allow dead-stripping `var` (the reflection record) when the type
// (NTD) is not referenced.
auto ref = IGM.getTypeEntityReference(const_cast<NominalTypeDecl *>(NTD));
IGM.appendLLVMUsedConditionalEntry(var, ref.getValue());
}

return var;
}
};

Expand Down
5 changes: 5 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,11 @@ private: \

TypeEntityReference getTypeEntityReference(GenericTypeDecl *D);

void appendLLVMUsedConditionalEntry(llvm::GlobalVariable *var,
llvm::Constant *dependsOn);
void appendLLVMUsedConditionalEntry(llvm::GlobalVariable *var,
const ProtocolConformance *conformance);

llvm::Constant *
getAddrOfTypeMetadata(CanType concreteType,
TypeMetadataCanonicality canonicality =
Expand Down
3 changes: 2 additions & 1 deletion test/IRGen/MachO-objc-sections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class D {
// CHECK-MACHO: @"$s4main1CCMf" = {{.*}}, section "__DATA,__objc_data, regular"
// CHECK-MACHO: @"\01l_OBJC_LABEL_PROTOCOL_$_P" = {{.*}}, section "__DATA,__objc_protolist,coalesced,no_dead_strip"
// CHECK-MACHO: @"\01l_OBJC_PROTOCOL_REFERENCE_$_P" = {{.*}}, section "__DATA,__objc_protorefs,coalesced,no_dead_strip"
// CHECK-MACHO: @objc_classes = {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip"
// CHECK-MACHO: @"objc_classes_$s4main1CCN" = {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip"
// CHECK-MACHO: @"objc_classes_$s4main1DCN" = {{.*}}, section "__DATA,__objc_classlist,regular,no_dead_strip"
// CHECK-MACHO: @objc_categories = {{.*}}, section "__DATA,__objc_catlist,regular,no_dead_strip"
// CHECK-MACHO: @objc_non_lazy_classes = {{.*}}, section "__DATA,__objc_nlclslist,regular,no_dead_strip"

14 changes: 10 additions & 4 deletions test/IRGen/conditional-dead-strip-exec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@

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

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

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

// (9) unused protocol with associated type
protocol ProtoWithAssocType { associatedtype T }
struct Implementor : ProtoWithAssocType { typealias T = Int }

print("Hello!")
func1_used()
let o = UsedClass()
Expand All @@ -71,6 +72,11 @@ p.bark()
// (2)
// NM-NOT: $s4main10func2_deadyyF

// (9)
// NM-NOT: $s4main11ImplementorVAA18ProtoWithAssocTypeAAMA
// NM-NOT: $s4main11ImplementorVMf
// NM-NOT: $s4main11ImplementorVMn

// (4)
// NM-NOT: $s4main11TheProtocolMp

Expand Down
31 changes: 18 additions & 13 deletions test/IRGen/conditional-dead-strip-ir.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// enum, protocol, and protocol conformance records as conditionally removable
// via !llvm.used.conditional metadata.

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

public protocol TheProtocol {
}
Expand All @@ -25,19 +24,25 @@ public enum Enum {
// CHECK-SAME: @"$s4main4EnumOHn"
// CHECK-SAME: ], section "llvm.metadata"

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

// CHECK: [[C1]] = !{{{.*}} @"$s4main11TheProtocolHr", i32 0, [[C1A:!.*]]}
// CHECK: [[C1A]] = !{{{.*}} @"$s4main11TheProtocolMp"}
// CHECK-DAG: [[M1]] = !{{{.*}} @"$s4main11TheProtocol_pMF", i32 0, [[M1A:!.*]]}
// CHECK-DAG: [[M1A]] = !{{{.*}} @"$s4main11TheProtocolMp"
// CHECK-DAG: [[M2]] = !{{{.*}} @"$s4main5ClassCMF", i32 0, [[M2A:!.*]]}
// CHECK-DAG: [[M2A]] = !{{{.*}} @"$s4main5ClassCMn"
// CHECK-DAG: [[M3]] = !{{{.*}} @"$s4main6StructVMF", i32 0, [[M3A:!.*]]}
// CHECK-DAG: [[M3A]] = !{{{.*}} @"$s4main6StructVMn"
// CHECK-DAG: [[M4]] = !{{{.*}} @"$s4main4EnumOMF", i32 0, [[M4A:!.*]]}
// CHECK-DAG: [[M4A]] = !{{{.*}} @"$s4main4EnumOMn"

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

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

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

// CHECK: [[C5]] = !{{{.*}} @"$s4main4EnumOHn", i32 0, [[C5A:!.*]]}
// CHECK: [[C5A]] = !{{{.*}} @"$s4main4EnumOMn"}
// CHECK-DAG: [[C4]] = !{{{.*}} @"$s4main6StructVHn", i32 0, [[M3A:!.*]]}

// CHECK-DAG: [[C5]] = !{{{.*}} @"$s4main4EnumOHn", i32 0, [[M4A:!.*]]}
Loading