Skip to content

Commit ee8cdac

Browse files
committed
DeadFunctionElimination: Also eliminate dead global variables.
It makes sense to to this in a single pass, because there might be dead cycles for globals and functions, e.g. if a global references a function in its static initializer and the function references that global. Another improvement: eliminate dead global-initializers. Before we had an explicit SIL representation of statically initialized globals, we had to keep them alive. rdar://32956923
1 parent ae313fe commit ee8cdac

File tree

6 files changed

+115
-17
lines changed

6 files changed

+115
-17
lines changed

include/swift/SIL/SILGlobalVariable.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ class SILGlobalVariable
128128
SILLinkage getLinkage() const { return SILLinkage(Linkage); }
129129
void setLinkage(SILLinkage linkage) { Linkage = unsigned(linkage); }
130130

131+
/// Returns true if the linkage of the SILFunction indicates that the global
132+
/// might be referenced from outside the current compilation unit.
133+
bool isPossiblyUsedExternally() const;
134+
131135
/// Get this global variable's serialized attribute.
132136
IsSerialized_t isSerialized() const;
133137
void setSerialized(IsSerialized_t isSerialized);

lib/SIL/IR/SILGlobalVariable.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ SILGlobalVariable::~SILGlobalVariable() {
6262
StaticInitializerBlock.clearStaticInitializerBlock(Module);
6363
}
6464

65+
bool SILGlobalVariable::isPossiblyUsedExternally() const {
66+
SILLinkage linkage = getLinkage();
67+
return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule());
68+
}
69+
6570
/// Get this global variable's fragile attribute.
6671
IsSerialized_t SILGlobalVariable::isSerialized() const {
6772
return Serialized ? IsSerialized : IsNotSerialized;

lib/SILOptimizer/IPO/DeadFunctionElimination.cpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
using namespace swift;
2828

2929
STATISTIC(NumDeadFunc, "Number of dead functions eliminated");
30+
STATISTIC(NumDeadGlobals, "Number of dead global variables eliminated");
3031

3132
namespace {
3233

@@ -115,11 +116,6 @@ class FunctionLivenessComputation {
115116
if (F->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod)
116117
return true;
117118

118-
// Global initializers are always emitted into the defining module and
119-
// their bodies are never SIL serialized.
120-
if (F->isGlobalInit())
121-
return true;
122-
123119
return false;
124120
}
125121

@@ -145,6 +141,11 @@ class FunctionLivenessComputation {
145141
return AliveFunctionsAndTables.count(WT) != 0;
146142
}
147143

144+
/// Returns true if a global variable is marked as alive.
145+
bool isAlive(SILGlobalVariable *global) {
146+
return AliveFunctionsAndTables.count(global) != 0;
147+
}
148+
148149
/// Marks a function as alive.
149150
void makeAlive(SILFunction *F) {
150151
AliveFunctionsAndTables.insert(F);
@@ -204,6 +205,16 @@ class FunctionLivenessComputation {
204205
}
205206
}
206207

208+
/// Marks the \p global and all functions, which are referenced from its
209+
/// initializer as alive.
210+
void makeAlive(SILGlobalVariable *global) {
211+
AliveFunctionsAndTables.insert(global);
212+
for (const SILInstruction &initInst : *global) {
213+
if (auto *fRef = dyn_cast<FunctionRefInst>(&initInst))
214+
ensureAlive(fRef->getReferencedFunction());
215+
}
216+
}
217+
207218
/// Marks the declarations referenced by a key path pattern as alive if they
208219
/// aren't yet.
209220
void
@@ -239,6 +250,12 @@ class FunctionLivenessComputation {
239250
makeAlive(F);
240251
}
241252

253+
/// Marks a global variable as alive if it is not alive yet.
254+
void ensureAlive(SILGlobalVariable *global) {
255+
if (!isAlive(global))
256+
makeAlive(global);
257+
}
258+
242259
/// Marks a witness table as alive if it is not alive yet.
243260
void ensureAliveConformance(const ProtocolConformance *C) {
244261
SILWitnessTable *WT = Module->lookUpWitnessTable(C,
@@ -342,6 +359,10 @@ class FunctionLivenessComputation {
342359
} else if (auto *KPI = dyn_cast<KeyPathInst>(&I)) {
343360
for (auto &component : KPI->getPattern()->getComponents())
344361
ensureKeyPathComponentIsAlive(component);
362+
} else if (auto *GA = dyn_cast<GlobalAddrInst>(&I)) {
363+
ensureAlive(GA->getReferencedGlobal());
364+
} else if (auto *GV = dyn_cast<GlobalValueInst>(&I)) {
365+
ensureAlive(GV->getReferencedGlobal());
345366
}
346367
}
347368
}
@@ -407,6 +428,11 @@ class FunctionLivenessComputation {
407428
ensureAlive(&F);
408429
}
409430
}
431+
432+
for (SILGlobalVariable &global : Module->getSILGlobals()) {
433+
if (global.isPossiblyUsedExternally())
434+
ensureAlive(&global);
435+
}
410436
}
411437

412438
public:
@@ -675,13 +701,21 @@ class DeadFunctionElimination : FunctionLivenessComputation {
675701

676702
// First drop all references so that we don't get problems with non-zero
677703
// reference counts of dead functions.
678-
std::vector<SILFunction *> DeadFunctions;
704+
llvm::SmallVector<SILFunction *, 16> DeadFunctions;
705+
llvm::SmallVector<SILGlobalVariable *, 16> DeadGlobals;
679706
for (SILFunction &F : *Module) {
680707
if (!isAlive(&F)) {
681708
F.dropAllReferences();
682709
DeadFunctions.push_back(&F);
683710
}
684711
}
712+
713+
for (SILGlobalVariable &global : Module->getSILGlobals()) {
714+
if (!isAlive(&global)) {
715+
global.dropAllReferences();
716+
DeadGlobals.push_back(&global);
717+
}
718+
}
685719

686720
// Next step: delete dead witness tables.
687721
SILModule::WitnessTableListType &WTables = Module->getWitnessTableList();
@@ -706,6 +740,11 @@ class DeadFunctionElimination : FunctionLivenessComputation {
706740
DFEPass->notifyWillDeleteFunction(F);
707741
Module->eraseFunction(F);
708742
}
743+
for (SILGlobalVariable *deadGlobal : DeadGlobals) {
744+
++NumDeadGlobals;
745+
Module->eraseGlobalVariable(deadGlobal);
746+
}
747+
709748
if (changedTables)
710749
DFEPass->invalidateFunctionTables();
711750
}

test/SILOptimizer/dead_function_elimination.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,39 @@ internal func donotEliminate() {
168168
return
169169
}
170170

171+
func callingGlobalFuncPtr() {
172+
GFStr.globalFuncPtr()
173+
}
174+
175+
func aliveReferencedFunc() {
176+
}
177+
178+
struct GFStr {
179+
static var globalFuncPtr: () -> () = callingGlobalFuncPtr
180+
static var aliveFuncPtr: () -> () = aliveReferencedFunc
181+
}
182+
183+
public func keepPtrAlive() {
184+
GFStr.aliveFuncPtr()
185+
}
186+
171187
// CHECK-NOT: sil {{.*}}inCycleA
172188
// CHECK-NOT: sil {{.*}}inCycleB
173189
// CHECK-NOT: sil {{.*}}DeadMethod
174190
// CHECK-NOT: sil {{.*}}DeadWitness
175191
// CHECK-NOT: sil {{.*}}publicClassMethod
192+
// CHECK-NOT: sil {{.*}}callingGlobalFuncPtr
193+
// CHECK-NOT: sil_global {{.*}}globalFuncPtr
176194

177195
// CHECK-TESTING: sil {{.*}}inCycleA
178196
// CHECK-TESTING: sil {{.*}}inCycleB
179197
// CHECK-TESTING: sil {{.*}}DeadMethod
180198
// CHECK-TESTING: sil {{.*}}publicClassMethod
181199
// CHECK-TESTING: sil {{.*}}DeadWitness
182200

201+
// CHECK-LABEL: sil_global hidden @$s25dead_function_elimination5GFStrV12aliveFuncPtryycvpZ
183202
// CHECK-LABEL: @$s25dead_function_elimination14donotEliminateyyF
203+
// CHECK-LABEL: sil @$s25dead_function_elimination12keepPtrAliveyyF
184204

185205
// CHECK-LABEL: sil_vtable Base
186206
// CHECK: aliveMethod

test/SILOptimizer/remove_unused_func.sil

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-deadfuncelim | %FileCheck --check-prefix=KEEP %s
12
// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-deadfuncelim | %FileCheck %s
23

34
sil_stage canonical
@@ -6,7 +7,7 @@ import Builtin
67
import Swift
78

89
// This function needs to be removed.
9-
// CHECK-NOT: @remove_me
10+
// KEEP-NOT: @remove_me
1011

1112
sil private @remove_me : $@convention(thin) (Builtin.Int64) -> Builtin.Int64 {
1213
bb0(%0 : $Builtin.Int64):
@@ -15,3 +16,32 @@ bb0(%0 : $Builtin.Int64):
1516
%4 = tuple_extract %3 : $(Builtin.Int64, Builtin.Int1), 0
1617
return %4: $Builtin.Int64
1718
}
19+
20+
sil_global @globalFunctionPointer : $@callee_guaranteed () -> () = {
21+
%0 = function_ref @alivePrivateFunc : $@convention(thin) () -> ()
22+
%initval = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_guaranteed () -> ()
23+
}
24+
25+
// CHECK-LABEL: sil private @alivePrivateFunc
26+
sil private @alivePrivateFunc : $@convention(thin) () -> () {
27+
bb0:
28+
%0 = tuple ()
29+
return %0 : $()
30+
}
31+
32+
// KEEP-NOT: @privateFunctionPointer
33+
sil_global private @privateFunctionPointer : $@callee_guaranteed () -> () = {
34+
%0 = function_ref @deadPrivateFunc : $@convention(thin) () -> ()
35+
%initval = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_guaranteed () -> ()
36+
}
37+
38+
// KEEP-NOT: @deadPrivateFunc
39+
sil private @deadPrivateFunc : $@convention(thin) () -> () {
40+
bb0:
41+
%0 = global_addr @privateFunctionPointer : $*@callee_guaranteed () -> ()
42+
%1 = load %0 : $*@callee_guaranteed () -> ()
43+
%2 = apply %1() : $@callee_guaranteed () -> ()
44+
%3 = tuple ()
45+
return %3 : $()
46+
}
47+

test/SILOptimizer/sil_combine_protocol_conf.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend %s -O -wmo -emit-sil -Xllvm -sil-disable-pass=DeadFunctionElimination | %FileCheck %s
1+
// RUN: %target-swift-frontend %s -O -wmo -emit-sil | %FileCheck %s
22

33
// case 1: class protocol -- should optimize
44
internal protocol SomeProtocol : class {
@@ -95,7 +95,7 @@ internal class Klass2: MultipleConformanceProtocol {
9595
}
9696

9797

98-
internal class Other {
98+
public class Other {
9999
let x:SomeProtocol
100100
let y:SomeNonClassProtocol
101101
let z:DerivedProtocol
@@ -113,7 +113,7 @@ internal class Other {
113113
self.s = s;
114114
}
115115

116-
// CHECK-LABEL: sil hidden [noinline] @$s25sil_combine_protocol_conf5OtherC11doWorkClassSiyF : $@convention(method) (@guaranteed Other) -> Int {
116+
// CHECK-LABEL: sil [noinline] @$s25sil_combine_protocol_conf5OtherC11doWorkClassSiyF : $@convention(method) (@guaranteed Other) -> Int {
117117
// CHECK: bb0
118118
// CHECK: debug_value
119119
// CHECK: integer_literal
@@ -162,7 +162,7 @@ internal class Other {
162162
// CHECK: struct
163163
// CHECK: return
164164
// CHECK: } // end sil function '$s25sil_combine_protocol_conf5OtherC11doWorkClassSiyF'
165-
@inline(never) func doWorkClass () ->Int {
165+
@inline(never) public func doWorkClass () ->Int {
166166
return self.x.foo(x:self.x) // optimize
167167
+ self.y.bar(x:self.y) // optimize
168168
+ self.z.foo() // do not optimize
@@ -221,7 +221,7 @@ struct GenericOuter<T> {
221221
}
222222
}
223223

224-
internal class OtherClass {
224+
public class OtherClass {
225225
var arg1: PropProtocol
226226
var arg2: GenericPropProtocol
227227
var arg3: NestedPropProtocol
@@ -234,7 +234,7 @@ internal class OtherClass {
234234
self.arg4 = arg4
235235
}
236236

237-
// CHECK-LABEL: sil hidden [noinline] @$s25sil_combine_protocol_conf10OtherClassC12doWorkStructSiyF : $@convention(method) (@guaranteed OtherClass) -> Int {
237+
// CHECK-LABEL: sil [noinline] @$s25sil_combine_protocol_conf10OtherClassC12doWorkStructSiyF : $@convention(method) (@guaranteed OtherClass) -> Int {
238238
// CHECK: bb0([[ARG:%.*]] :
239239
// CHECK: debug_value
240240
// CHECK: [[R1:%.*]] = ref_element_addr [[ARG]] : $OtherClass, #OtherClass.arg1
@@ -279,7 +279,7 @@ internal class OtherClass {
279279
// CHECK: struct
280280
// CHECK: return
281281
// CHECK: } // end sil function '$s25sil_combine_protocol_conf10OtherClassC12doWorkStructSiyF'
282-
@inline(never) func doWorkStruct () -> Int{
282+
@inline(never) public func doWorkStruct () -> Int{
283283
return self.arg1.val // optimize
284284
+ self.arg2.val // do not optimize
285285
+ self.arg3.val // optimize
@@ -322,7 +322,7 @@ internal enum AGenericEnum<T> : AGenericProtocol {
322322
}
323323
}
324324

325-
internal class OtherKlass {
325+
public class OtherKlass {
326326
var arg1: AProtocol
327327
var arg2: AGenericProtocol
328328

@@ -331,7 +331,7 @@ internal class OtherKlass {
331331
self.arg2 = arg2
332332
}
333333

334-
// CHECK-LABEL: sil hidden [noinline] @$s25sil_combine_protocol_conf10OtherKlassC10doWorkEnumSiyF : $@convention(method) (@guaranteed OtherKlass) -> Int {
334+
// CHECK-LABEL: sil [noinline] @$s25sil_combine_protocol_conf10OtherKlassC10doWorkEnumSiyF : $@convention(method) (@guaranteed OtherKlass) -> Int {
335335
// CHECK: bb0([[ARG:%.*]] :
336336
// CHECK: debug_value
337337
// CHECK: integer_literal
@@ -350,7 +350,7 @@ internal class OtherKlass {
350350
// CHECK: struct
351351
// CHECK: return
352352
// CHECK: } // end sil function '$s25sil_combine_protocol_conf10OtherKlassC10doWorkEnumSiyF'
353-
@inline(never) func doWorkEnum() -> Int {
353+
@inline(never) public func doWorkEnum() -> Int {
354354
return self.arg1.val // optimize
355355
+ self.arg2.val // do not optimize
356356
}

0 commit comments

Comments
 (0)