Skip to content

Commit bdbd059

Browse files
authored
Merge pull request #7148 from swiftix/default-witness-tables-methods-elimination
[sil-dead-funciton-elimination] Eliminate unused default methods impementations from default witness tables
2 parents 978b15f + a422f8a commit bdbd059

File tree

3 files changed

+89
-10
lines changed

3 files changed

+89
-10
lines changed

include/swift/SIL/SILDefaultWitnessTable.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class SILDefaultWitnessTable : public llvm::ilist_node<SILDefaultWitnessTable>,
5757
: Requirement(Requirement), Witness(Witness) {}
5858

5959
bool isValid() const {
60-
return !Requirement.isNull();
60+
return !Requirement.isNull() && Witness;
6161
}
6262

6363
const SILDeclRef &getRequirement() const {
@@ -68,6 +68,12 @@ class SILDefaultWitnessTable : public llvm::ilist_node<SILDefaultWitnessTable>,
6868
assert(Witness != nullptr);
6969
return Witness;
7070
}
71+
void removeWitnessMethod() {
72+
if (Witness) {
73+
Witness->decrementRefCount();
74+
}
75+
Witness = nullptr;
76+
}
7177
};
7278

7379
private:
@@ -131,6 +137,19 @@ class SILDefaultWitnessTable : public llvm::ilist_node<SILDefaultWitnessTable>,
131137
/// Return the AST ProtocolDecl this default witness table is associated with.
132138
const ProtocolDecl *getProtocol() const { return Protocol; }
133139

140+
/// Clears methods in witness entries.
141+
/// \p predicate Returns true if the passed entry should be set to null.
142+
template <typename Predicate> void clearMethods_if(Predicate predicate) {
143+
for (Entry &entry : Entries) {
144+
if (!entry.isValid())
145+
continue;
146+
auto *MW = entry.getWitness();
147+
if (MW && predicate(MW)) {
148+
entry.removeWitnessMethod();
149+
}
150+
}
151+
}
152+
134153
/// Return the minimum witness table size, in words.
135154
///
136155
/// This will not change if requirements with default implementations are

lib/SILOptimizer/IPO/DeadFunctionElimination.cpp

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ class FunctionLivenessComputation {
339339
}
340340

341341
/// Retrieve the visibility information from the AST.
342-
bool isVisibleExternally(ValueDecl *decl) {
342+
bool isVisibleExternally(const ValueDecl *decl) {
343343
Accessibility accessibility = decl->getEffectiveAccess();
344344
SILLinkage linkage;
345345
switch (accessibility) {
@@ -558,15 +558,27 @@ class DeadFunctionElimination : FunctionLivenessComputation {
558558
makeAlive(&WT);
559559
}
560560
}
561+
561562
// Check default witness methods.
562563
for (SILDefaultWitnessTable &WT : Module->getDefaultWitnessTableList()) {
563-
for (const SILDefaultWitnessTable::Entry &entry : WT.getEntries()) {
564-
if (!entry.isValid())
565-
continue;
564+
if (isVisibleExternally(WT.getProtocol())) {
565+
// The default witness table is visible from "outside". Therefore all
566+
// methods might be called and we mark all methods as alive.
567+
for (const SILDefaultWitnessTable::Entry &entry : WT.getEntries()) {
568+
if (!entry.isValid())
569+
continue;
566570

567-
auto *fd = cast<AbstractFunctionDecl>(entry.getRequirement().getDecl());
568-
MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true);
569-
ensureAliveProtocolMethod(mi);
571+
auto *fd =
572+
cast<AbstractFunctionDecl>(entry.getRequirement().getDecl());
573+
assert(fd == getBase(fd) &&
574+
"key in default witness table is overridden");
575+
SILFunction *F = entry.getWitness();
576+
if (!F)
577+
continue;
578+
579+
MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true);
580+
ensureAliveProtocolMethod(mi);
581+
}
570582
}
571583
}
572584
}
@@ -597,6 +609,24 @@ class DeadFunctionElimination : FunctionLivenessComputation {
597609
return false;
598610
});
599611
}
612+
613+
auto DefaultWitnessTables = Module->getDefaultWitnessTables();
614+
for (auto WI = DefaultWitnessTables.begin(),
615+
EI = DefaultWitnessTables.end();
616+
WI != EI;) {
617+
SILDefaultWitnessTable *WT = &*WI;
618+
WI++;
619+
WT->clearMethods_if([this](SILFunction *MW) -> bool {
620+
if (!MW)
621+
return false;
622+
if (!isAlive(MW)) {
623+
DEBUG(llvm::dbgs() << " erase dead default witness method "
624+
<< MW->getName() << "\n");
625+
return true;
626+
}
627+
return false;
628+
});
629+
}
600630
}
601631

602632
public:

test/SILOptimizer/dead_function_elimination.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,23 @@ public class PublicCl {
113113
// Check if unused witness table methods are removed.
114114

115115
protocol Prot {
116-
func aliveWitness()
116+
func aliveWitness()
117117

118-
func DeadWitness()
118+
func DeadWitness()
119+
120+
func aliveDefaultWitness()
121+
122+
func DeadDefaultWitness()
123+
}
124+
125+
extension Prot {
126+
@inline(never)
127+
func aliveDefaultWitness() {
128+
}
129+
130+
@inline(never)
131+
func DeadDefaultWitness() {
132+
}
119133
}
120134

121135
struct Adopt : Prot {
@@ -134,6 +148,11 @@ func testProtocols(_ p: Prot) {
134148
p.aliveWitness()
135149
}
136150

151+
@inline(never)
152+
@_semantics("optimize.sil.never") // avoid devirtualization
153+
func testDefaultWitnessMethods(_ p: Prot) {
154+
p.aliveDefaultWitness()
155+
}
137156

138157
@_semantics("optimize.sil.never") // avoid devirtualization
139158
public func callTest() {
@@ -198,3 +217,14 @@ internal func donotEliminate() {
198217
// CHECK-TESTING-LABEL: sil_witness_table [fragile] Adopt: Prot
199218
// CHECK-TESTING: DeadWitness{{.*}}: @{{.*}}DeadWitness
200219

220+
// CHECK-LABEL: sil_default_witness_table hidden Prot
221+
// CHECK: no_default
222+
// CHECK: no_default
223+
// CHECK: method #Prot.aliveDefaultWitness!1: {{.*}} : @{{.*}}aliveDefaultWitness
224+
// CHECK: no_default
225+
226+
// CHECK-TESTING-LABEL: sil_default_witness_table Prot
227+
// CHECK-TESTING: no_default
228+
// CHECK-TESTING: no_default
229+
// CHECK-TESTING: method #Prot.aliveDefaultWitness!1: {{.*}} : @{{.*}}aliveDefaultWitness
230+
// CHECK-TESTING: method #Prot.DeadDefaultWitness!1: {{.*}} : @{{.*}}DeadDefaultWitness

0 commit comments

Comments
 (0)