Skip to content

Commit 30fa52b

Browse files
committed
Add a -emit-dead-strippable-symbols flag that emits functions/variable/metadata in a dead_strip-friendly way. This enables static linking to remove unused functions and classes even across modules.
1 parent 8051532 commit 30fa52b

File tree

9 files changed

+336
-56
lines changed

9 files changed

+336
-56
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ class IRGenOptions {
250250
/// Emit names of struct stored properties and enum cases.
251251
unsigned EnableReflectionNames : 1;
252252

253+
/// Emit metadata, variables and functions in a way that a linker can remove
254+
/// them if they're unused, even across modules (when linking statically).
255+
unsigned EmitDeadStrippableSymbols : 1;
256+
253257
/// Emit mangled names of anonymous context descriptors.
254258
unsigned EnableAnonymousContextMangledNames : 1;
255259

@@ -348,7 +352,8 @@ class IRGenOptions {
348352
PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None),
349353
LLVMLTOKind(IRGenLLVMLTOKind::None), HasValueNamesSetting(false),
350354
ValueNames(false), EnableReflectionMetadata(true),
351-
EnableReflectionNames(true), EnableAnonymousContextMangledNames(false),
355+
EnableReflectionNames(true), EmitDeadStrippableSymbols(false),
356+
EnableAnonymousContextMangledNames(false),
352357
ForcePublicLinkage(false), LazyInitializeClassMetadata(false),
353358
LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false),
354359
PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true),

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,11 @@ def emit_sorted_sil : Flag<["-"], "emit-sorted-sil">,
604604
HelpText<"When printing SIL, print out all sil entities sorted by name to "
605605
"ease diffing">;
606606

607+
def emit_dead_strippable_symbols : Flag<["-"], "emit-dead-strippable-symbols">,
608+
HelpText<"Emit metadata, variables and functions in a way that a linker can "
609+
"remove them if they're unused, even across modules (when linking "
610+
"statically).">;
611+
607612
def emit_syntax : Flag<["-"], "emit-syntax">,
608613
HelpText<"Parse input file(s) and emit the Syntax tree(s) as JSON">, ModeOpt;
609614

lib/Frontend/CompilerInvocation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,12 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
15461546
Opts.EnableReflectionNames = false;
15471547
}
15481548

1549+
if (Args.hasArg(OPT_emit_dead_strippable_symbols)) {
1550+
if (!Opts.UseJIT) {
1551+
Opts.EmitDeadStrippableSymbols = true;
1552+
}
1553+
}
1554+
15491555
if (Args.hasArg(OPT_force_public_linkage)) {
15501556
Opts.ForcePublicLinkage = true;
15511557
}

lib/IRGen/GenDecl.cpp

Lines changed: 139 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,8 +1991,9 @@ void irgen::updateLinkageForDefinition(IRGenModule &IGM,
19911991

19921992
// Everything externally visible is considered used in Swift.
19931993
// That mostly means we need to be good at not marking things external.
1994-
if (LinkInfo::isUsed(IRL))
1995-
IGM.addUsedGlobal(global);
1994+
if (LinkInfo::isUsed(IRL) && !IGM.IRGen.Opts.EmitDeadStrippableSymbols) {
1995+
IGM.addUsedGlobal(global);
1996+
}
19961997
}
19971998

19981999
LinkInfo LinkInfo::get(IRGenModule &IGM, const LinkEntity &entity,
@@ -2085,7 +2086,11 @@ llvm::Function *irgen::createFunction(IRGenModule &IGM,
20852086
// Everything externally visible is considered used in Swift.
20862087
// That mostly means we need to be good at not marking things external.
20872088
if (linkInfo.isUsed()) {
2088-
IGM.addUsedGlobal(fn);
2089+
// Don't allow dead_strip'ing for "main".
2090+
if (name == SWIFT_ENTRY_POINT_FUNCTION ||
2091+
!IGM.IRGen.Opts.EmitDeadStrippableSymbols) {
2092+
IGM.addUsedGlobal(fn);
2093+
}
20892094
}
20902095

20912096
return fn;
@@ -2132,7 +2137,7 @@ llvm::GlobalVariable *swift::irgen::createVariable(
21322137

21332138
// Everything externally visible is considered used in Swift.
21342139
// That mostly means we need to be good at not marking things external.
2135-
if (linkInfo.isUsed()) {
2140+
if (linkInfo.isUsed() && !IGM.IRGen.Opts.EmitDeadStrippableSymbols) {
21362141
IGM.addUsedGlobal(var);
21372142
}
21382143

@@ -3350,6 +3355,47 @@ llvm::Constant *IRGenModule::emitSwiftProtocols() {
33503355
if (SwiftProtocols.empty())
33513356
return nullptr;
33523357

3358+
StringRef sectionName;
3359+
switch (TargetInfo.OutputObjectFormat) {
3360+
case llvm::Triple::UnknownObjectFormat:
3361+
llvm_unreachable("Don't know how to emit protocols for "
3362+
"the selected object format.");
3363+
case llvm::Triple::MachO:
3364+
sectionName = IRGen.Opts.EmitDeadStrippableSymbols
3365+
? "__TEXT, __swift5_protos, regular, live_support"
3366+
: "__TEXT, __swift5_protos, regular, no_dead_strip";
3367+
break;
3368+
case llvm::Triple::ELF:
3369+
case llvm::Triple::Wasm:
3370+
sectionName = "swift5_protocols";
3371+
break;
3372+
case llvm::Triple::XCOFF:
3373+
case llvm::Triple::COFF:
3374+
sectionName = ".sw5prt$B";
3375+
break;
3376+
}
3377+
3378+
if (IRGen.Opts.EmitDeadStrippableSymbols) {
3379+
for (auto *protocol : SwiftProtocols) {
3380+
auto ref = getTypeEntityReference(protocol);
3381+
auto name = "\x01l_protocol_" + std::string(ref.getValue()->getName());
3382+
auto var = new llvm::GlobalVariable(
3383+
Module, ProtocolRecordTy, /*isConstant*/ true,
3384+
llvm::GlobalValue::PrivateLinkage, /*initializer*/ nullptr, name);
3385+
3386+
llvm::Constant *relativeAddr =
3387+
emitDirectRelativeReference(ref.getValue(), var, {0});
3388+
llvm::Constant *recordFields[] = {relativeAddr};
3389+
auto record = llvm::ConstantStruct::get(ProtocolRecordTy, recordFields);
3390+
var->setInitializer(record);
3391+
var->setSection(sectionName);
3392+
var->setAlignment(llvm::MaybeAlign(4));
3393+
3394+
disableAddressSanitizer(*this, var);
3395+
}
3396+
return nullptr;
3397+
}
3398+
33533399
// Define the global variable for the protocol list.
33543400
ConstantInitBuilder builder(*this);
33553401
auto recordsArray = builder.beginArray(ProtocolRecordTy);
@@ -3374,24 +3420,6 @@ llvm::Constant *IRGenModule::emitSwiftProtocols() {
33743420
/*isConstant*/ true,
33753421
llvm::GlobalValue::PrivateLinkage);
33763422

3377-
StringRef sectionName;
3378-
switch (TargetInfo.OutputObjectFormat) {
3379-
case llvm::Triple::UnknownObjectFormat:
3380-
llvm_unreachable("Don't know how to emit protocols for "
3381-
"the selected object format.");
3382-
case llvm::Triple::MachO:
3383-
sectionName = "__TEXT, __swift5_protos, regular, no_dead_strip";
3384-
break;
3385-
case llvm::Triple::ELF:
3386-
case llvm::Triple::Wasm:
3387-
sectionName = "swift5_protocols";
3388-
break;
3389-
case llvm::Triple::XCOFF:
3390-
case llvm::Triple::COFF:
3391-
sectionName = ".sw5prt$B";
3392-
break;
3393-
}
3394-
33953423
var->setSection(sectionName);
33963424

33973425
disableAddressSanitizer(*this, var);
@@ -3413,6 +3441,49 @@ llvm::Constant *IRGenModule::emitProtocolConformances() {
34133441
if (ProtocolConformances.empty())
34143442
return nullptr;
34153443

3444+
StringRef sectionName;
3445+
switch (TargetInfo.OutputObjectFormat) {
3446+
case llvm::Triple::UnknownObjectFormat:
3447+
llvm_unreachable("Don't know how to emit protocol conformances for "
3448+
"the selected object format.");
3449+
case llvm::Triple::MachO:
3450+
sectionName = IRGen.Opts.EmitDeadStrippableSymbols
3451+
? "__TEXT, __swift5_proto, regular, live_support"
3452+
: "__TEXT, __swift5_proto, regular, no_dead_strip";
3453+
break;
3454+
case llvm::Triple::ELF:
3455+
case llvm::Triple::Wasm:
3456+
sectionName = "swift5_protocol_conformances";
3457+
break;
3458+
case llvm::Triple::XCOFF:
3459+
case llvm::Triple::COFF:
3460+
sectionName = ".sw5prtc$B";
3461+
break;
3462+
}
3463+
3464+
if (IRGen.Opts.EmitDeadStrippableSymbols) {
3465+
for (const auto &record : ProtocolConformances) {
3466+
auto conformance = record.conformance;
3467+
auto entity = LinkEntity::forProtocolConformanceDescriptor(conformance);
3468+
auto name =
3469+
"\x01l_protocol_conformance_" + std::string(entity.mangleAsString());
3470+
auto var = new llvm::GlobalVariable(
3471+
Module, RelativeAddressTy, /*isConstant*/ true,
3472+
llvm::GlobalValue::PrivateLinkage, /*initializer*/ nullptr, name);
3473+
3474+
auto descriptor =
3475+
getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo());
3476+
llvm::Constant *relativeAddr =
3477+
emitDirectRelativeReference(descriptor, var, {});
3478+
var->setInitializer(relativeAddr);
3479+
var->setSection(sectionName);
3480+
var->setAlignment(llvm::MaybeAlign(4));
3481+
3482+
disableAddressSanitizer(*this, var);
3483+
}
3484+
return nullptr;
3485+
}
3486+
34163487
// Define the global variable for the conformance list.
34173488
ConstantInitBuilder builder(*this);
34183489
auto descriptorArray = builder.beginArray(RelativeAddressTy);
@@ -3434,24 +3505,6 @@ llvm::Constant *IRGenModule::emitProtocolConformances() {
34343505
/*isConstant*/ true,
34353506
llvm::GlobalValue::PrivateLinkage);
34363507

3437-
StringRef sectionName;
3438-
switch (TargetInfo.OutputObjectFormat) {
3439-
case llvm::Triple::UnknownObjectFormat:
3440-
llvm_unreachable("Don't know how to emit protocol conformances for "
3441-
"the selected object format.");
3442-
case llvm::Triple::MachO:
3443-
sectionName = "__TEXT, __swift5_proto, regular, no_dead_strip";
3444-
break;
3445-
case llvm::Triple::ELF:
3446-
case llvm::Triple::Wasm:
3447-
sectionName = "swift5_protocol_conformances";
3448-
break;
3449-
case llvm::Triple::XCOFF:
3450-
case llvm::Triple::COFF:
3451-
sectionName = ".sw5prtc$B";
3452-
break;
3453-
}
3454-
34553508
var->setSection(sectionName);
34563509

34573510
disableAddressSanitizer(*this, var);
@@ -3466,7 +3519,9 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords() {
34663519
std::string sectionName;
34673520
switch (TargetInfo.OutputObjectFormat) {
34683521
case llvm::Triple::MachO:
3469-
sectionName = "__TEXT, __swift5_types, regular, no_dead_strip";
3522+
sectionName = IRGen.Opts.EmitDeadStrippableSymbols
3523+
? "__TEXT, __swift5_types, regular, live_support"
3524+
: "__TEXT, __swift5_types, regular, no_dead_strip";
34703525
break;
34713526
case llvm::Triple::ELF:
34723527
case llvm::Triple::Wasm:
@@ -3485,6 +3540,45 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords() {
34853540
if (RuntimeResolvableTypes.empty())
34863541
return nullptr;
34873542

3543+
auto generateRecord = [this](TypeEntityReference ref,
3544+
llvm::GlobalVariable *var,
3545+
ArrayRef<unsigned> baseIndices) {
3546+
// Form the relative address, with the type reference kind in the low bits.
3547+
llvm::Constant *relativeAddr =
3548+
emitDirectRelativeReference(ref.getValue(), var, baseIndices);
3549+
unsigned lowBits = static_cast<unsigned>(ref.getKind());
3550+
if (lowBits != 0) {
3551+
relativeAddr = llvm::ConstantExpr::getAdd(
3552+
relativeAddr, llvm::ConstantInt::get(RelativeAddressTy, lowBits));
3553+
}
3554+
3555+
llvm::Constant *recordFields[] = {relativeAddr};
3556+
auto record = llvm::ConstantStruct::get(TypeMetadataRecordTy, recordFields);
3557+
return record;
3558+
};
3559+
3560+
if (IRGen.Opts.EmitDeadStrippableSymbols) {
3561+
for (auto type : RuntimeResolvableTypes) {
3562+
auto ref = getTypeEntityReference(type);
3563+
auto name =
3564+
"\x01l_type_metadata_" + std::string(ref.getValue()->getName());
3565+
auto var = new llvm::GlobalVariable(
3566+
Module, TypeMetadataRecordTy, /*isConstant*/ true,
3567+
llvm::GlobalValue::PrivateLinkage, /*initializer*/ nullptr, name);
3568+
3569+
auto record = generateRecord(ref, var, {0});
3570+
var->setInitializer(record);
3571+
var->setSection(sectionName);
3572+
var->setAlignment(llvm::MaybeAlign(4));
3573+
3574+
disableAddressSanitizer(*this, var);
3575+
3576+
addCompilerUsedGlobal(var);
3577+
}
3578+
3579+
return nullptr;
3580+
}
3581+
34883582
// Define the global variable for the conformance list.
34893583
// We have to do this before defining the initializer since the entries will
34903584
// contain offsets relative to themselves.
@@ -3502,20 +3596,8 @@ llvm::Constant *IRGenModule::emitTypeMetadataRecords() {
35023596
SmallVector<llvm::Constant *, 8> elts;
35033597
for (auto type : RuntimeResolvableTypes) {
35043598
auto ref = getTypeEntityReference(type);
3505-
3506-
// Form the relative address, with the type reference kind in the low bits.
35073599
unsigned arrayIdx = elts.size();
3508-
llvm::Constant *relativeAddr =
3509-
emitDirectRelativeReference(ref.getValue(), var, { arrayIdx, 0 });
3510-
unsigned lowBits = static_cast<unsigned>(ref.getKind());
3511-
if (lowBits != 0) {
3512-
relativeAddr = llvm::ConstantExpr::getAdd(relativeAddr,
3513-
llvm::ConstantInt::get(RelativeAddressTy, lowBits));
3514-
}
3515-
3516-
llvm::Constant *recordFields[] = { relativeAddr };
3517-
auto record = llvm::ConstantStruct::get(TypeMetadataRecordTy,
3518-
recordFields);
3600+
auto record = generateRecord(ref, var, { arrayIdx, 0 });
35193601
elts.push_back(record);
35203602
}
35213603

@@ -3890,7 +3972,9 @@ llvm::GlobalValue *IRGenModule::defineAlias(LinkEntity entity,
38903972
ApplyIRLinkage({link.getLinkage(), link.getVisibility(), link.getDLLStorage()})
38913973
.to(alias);
38923974

3893-
if (link.isUsed()) {
3975+
// Everything externally visible is considered used in Swift.
3976+
// That mostly means we need to be good at not marking things external.
3977+
if (link.isUsed() && !IRGen.Opts.EmitDeadStrippableSymbols) {
38943978
addUsedGlobal(alias);
38953979
}
38963980

test/IRGen/dead_strip.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Checks that applying -dead_strip linker flag actually removes unused classes and functions from static libraries.
2+
3+
// REQUIRES: OS=macosx
4+
5+
// RUN: echo "-target x86_64-apple-macos11.0 -swift-version 5 -parse-as-library -working-directory %t" >> %t-commonflags
6+
// RUN: echo "-Xfrontend -disable-objc-interop" >> %t-commonflags
7+
// RUN: echo "-Xfrontend -disable-reflection-metadata -Xfrontend -disable-reflection-names" >> %t-commonflags
8+
9+
// RUN: %empty-directory(%t)
10+
// RUN: %target-swiftc_driver @%t-commonflags -emit-library -static -DMODULE -emit-module %s -module-name MyModule
11+
// RUN: %target-swiftc_driver @%t-commonflags %s -I%t -L%t -lMyModule -Xlinker -dead_strip -module-name main -o %t/main
12+
// RUN: %llvm-nm --defined-only %t/main | %FileCheck %s
13+
14+
// RUN: %empty-directory(%t)
15+
// RUN: %target-swiftc_driver @%t-commonflags -emit-library -static -DMODULE -emit-module %s -module-name MyModule -Xfrontend -emit-dead-strippable-symbols
16+
// RUN: %target-swiftc_driver @%t-commonflags %s -I%t -L%t -lMyModule -Xlinker -dead_strip -module-name main -o %t/main
17+
// RUN: %llvm-nm --defined-only %t/main | %FileCheck %s -check-prefix CHECK-DEADSTRIPPABLE
18+
19+
#if MODULE
20+
21+
public protocol MyProtocol {
22+
}
23+
public class MyClass: MyProtocol {
24+
}
25+
26+
public func fooFromModule() { print("fooFromModule") }
27+
public func barFromModule() { print("barFromModule") }
28+
29+
#else
30+
31+
import MyModule
32+
33+
@_cdecl("main")
34+
func main() {
35+
print("Hello!")
36+
fooFromModule()
37+
}
38+
39+
#endif
40+
41+
// CHECK: _$s8MyModule07barFromB0yyF
42+
// CHECK: _$s8MyModule07fooFromB0yyF
43+
// CHECK: _$s8MyModule0A5ClassCAA0A8ProtocolAAMc
44+
// CHECK: _$s8MyModule0A5ClassCAA0A8ProtocolAAWP
45+
// CHECK: _$s8MyModule0A5ClassCACycfC
46+
// CHECK: _$s8MyModule0A5ClassCACycfCTq
47+
// CHECK: _$s8MyModule0A5ClassCACycfc
48+
// CHECK: _$s8MyModule0A5ClassCMa
49+
// CHECK: _$s8MyModule0A5ClassCMf
50+
// CHECK: _$s8MyModule0A5ClassCMn
51+
// CHECK: _$s8MyModule0A5ClassCN
52+
// CHECK: _$s8MyModule0A5ClassCfD
53+
// CHECK: _$s8MyModule0A5ClassCfd
54+
// CHECK: _$s8MyModule0A8ProtocolMp
55+
// CHECK: _$s8MyModuleMXM
56+
57+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule07barFromB0yyF
58+
// CHECK-DEADSTRIPPABLE: _$s8MyModule07fooFromB0yyF
59+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCAA0A8ProtocolAAMc
60+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCAA0A8ProtocolAAWP
61+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCACycfC
62+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCACycfCTq
63+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCACycfc
64+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCMa
65+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCMf
66+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCMn
67+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCN
68+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCfD
69+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A5ClassCfd
70+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModule0A8ProtocolMp
71+
// CHECK-DEADSTRIPPABLE-NOT: _$s8MyModuleMXM

0 commit comments

Comments
 (0)