Skip to content

Commit 8f0569d

Browse files
authored
Merge pull request swiftlang#32210 from jckarter/prune-vtables-pass
Introduce a `PruneVTables` pass to mark non-overridden entries.
2 parents bded632 + e3ce94d commit 8f0569d

File tree

5 files changed

+231
-1
lines changed

5 files changed

+231
-1
lines changed

include/swift/SIL/SILVTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ class SILVTable : public llvm::ilist_node<SILVTable>,
128128
/// Return all of the method entries.
129129
ArrayRef<Entry> getEntries() const { return {Entries, NumEntries}; }
130130

131+
/// Return all of the method entries mutably.
132+
MutableArrayRef<Entry> getMutableEntries() { return {Entries, NumEntries}; }
133+
131134
/// Look up the implementation function for the given method.
132135
Optional<Entry> getEntry(SILModule &M, SILDeclRef method) const;
133136

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,9 @@ PASS(MandatoryCombine, "mandatory-combine",
340340
"Perform mandatory peephole combines")
341341
PASS(BugReducerTester, "bug-reducer-tester",
342342
"sil-bug-reducer Tool Testing by Asserting on a Sentinel Function")
343-
PASS_RANGE(AllPasses, AADumper, BugReducerTester)
343+
PASS(PruneVTables, "prune-vtables",
344+
"Mark class methods that do not require vtable dispatch")
345+
PASS_RANGE(AllPasses, AADumper, PruneVTables)
344346

345347
#undef IRGEN_PASS
346348
#undef PASS

lib/SILOptimizer/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ target_sources(swiftSILOptimizer PRIVATE
2525
ObjectOutliner.cpp
2626
PerformanceInliner.cpp
2727
PhiArgumentOptimizations.cpp
28+
PruneVTables.cpp
2829
RedundantLoadElimination.cpp
2930
RedundantOverflowCheckRemoval.cpp
3031
ReleaseDevirtualizer.cpp
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===--- PruneVTables.cpp - Prune unnecessary vtable entries --------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Mark sil_vtable entries as [nonoverridden] when possible, so that we know
14+
// at IRGen time they can be elided from runtime vtables.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#define DEBUG_TYPE "prune-vtables"
19+
20+
#include "swift/SILOptimizer/PassManager/Transforms.h"
21+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
22+
23+
using namespace swift;
24+
25+
namespace {
26+
class PruneVTables : public SILModuleTransform {
27+
void runOnVTable(SILModule *M,
28+
SILVTable *vtable) {
29+
for (auto &entry : vtable->getMutableEntries()) {
30+
// We don't need to worry about entries that are inherited, overridden,
31+
// or have already been found to have no overrides.
32+
if (entry.TheKind != SILVTable::Entry::Normal) {
33+
continue;
34+
}
35+
36+
// The destructor entry must remain.
37+
if (entry.Method.kind == SILDeclRef::Kind::Deallocator) {
38+
continue;
39+
}
40+
41+
auto methodDecl = entry.Method.getAbstractFunctionDecl();
42+
if (!methodDecl)
43+
continue;
44+
45+
// Is the method declared final?
46+
if (!methodDecl->isFinal()) {
47+
// Are callees of this entry statically knowable?
48+
if (!calleesAreStaticallyKnowable(*M, entry.Method))
49+
continue;
50+
51+
// Does the method have any overrides in this module?
52+
if (methodDecl->isOverridden())
53+
continue;
54+
}
55+
56+
entry.TheKind = SILVTable::Entry::NormalNonOverridden;
57+
}
58+
}
59+
60+
void run() override {
61+
SILModule *M = getModule();
62+
63+
for (auto &vtable : M->getVTables()) {
64+
runOnVTable(M, &vtable);
65+
}
66+
}
67+
};
68+
}
69+
70+
SILTransform *swift::createPruneVTables() {
71+
return new PruneVTables();
72+
}

test/SILOptimizer/prune-vtables.sil

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// RUN: %target-sil-opt -prune-vtables %s | %FileCheck --check-prefix=NOWMO %s
2+
// RUN: %target-sil-opt -wmo -prune-vtables %s | %FileCheck --check-prefix=WMO %s
3+
4+
sil_stage canonical
5+
6+
private class PrivateA {
7+
func noOverrides() {}
8+
func yesOverrides() {}
9+
final func isFinal() {}
10+
}
11+
12+
sil @PrivateA_noOverrides : $@convention(method) (@guaranteed PrivateA) -> ()
13+
sil @PrivateA_yesOverrides : $@convention(method) (@guaranteed PrivateA) -> ()
14+
sil @PrivateA_isFinal : $@convention(method) (@guaranteed PrivateA) -> ()
15+
16+
// NOWMO-LABEL: sil_vtable PrivateA {
17+
// NOWMO: #PrivateA.noOverrides{{.*}} [nonoverridden]
18+
// NOWMO-NOT: #PrivateA.yesOverrides{{.*}} [nonoverridden]
19+
// NOWMO: #PrivateA.isFinal{{.*}} [nonoverridden]
20+
21+
// WMO-LABEL: sil_vtable PrivateA {
22+
// WMO: #PrivateA.noOverrides{{.*}} [nonoverridden]
23+
// WMO-NOT: #PrivateA.yesOverrides{{.*}} [nonoverridden]
24+
// WMO: #PrivateA.isFinal{{.*}} [nonoverridden]
25+
sil_vtable PrivateA {
26+
#PrivateA.noOverrides: @PrivateA_noOverrides
27+
#PrivateA.yesOverrides: @PrivateA_yesOverrides
28+
#PrivateA.isFinal: @PrivateA_isFinal
29+
}
30+
31+
private class PrivateB: PrivateA {
32+
override func yesOverrides() {}
33+
}
34+
35+
sil @PrivateB_yesOverrides : $@convention(method) (@guaranteed PrivateB) -> ()
36+
37+
sil_vtable PrivateB {
38+
#PrivateA.noOverrides: @PrivateA_noOverrides [inherited]
39+
#PrivateA.yesOverrides: @PrivateB_yesOverrides [override]
40+
#PrivateA.isFinal: @PrivateA_isFinal [inherited]
41+
}
42+
43+
internal class InternalA {
44+
func noOverrides() {}
45+
func yesOverrides() {}
46+
final func isFinal() {}
47+
}
48+
49+
sil @InternalA_noOverrides : $@convention(method) (@guaranteed InternalA) -> ()
50+
sil @InternalA_yesOverrides : $@convention(method) (@guaranteed InternalA) -> ()
51+
sil @InternalA_isFinal : $@convention(method) (@guaranteed InternalA) -> ()
52+
53+
// NOWMO-LABEL: sil_vtable InternalA {
54+
// NOWMO-NOT: #InternalA.noOverrides{{.*}} [nonoverridden]
55+
// NOWMO-NOT: #InternalA.yesOverrides{{.*}} [nonoverridden]
56+
// NOWMO: #InternalA.isFinal{{.*}} [nonoverridden]
57+
58+
// WMO-LABEL: sil_vtable InternalA {
59+
// WMO: #InternalA.noOverrides{{.*}} [nonoverridden]
60+
// WMO-NOT: #InternalA.yesOverrides{{.*}} [nonoverridden]
61+
// WMO: #InternalA.isFinal{{.*}} [nonoverridden]
62+
sil_vtable InternalA {
63+
#InternalA.noOverrides: @InternalA_noOverrides
64+
#InternalA.yesOverrides: @InternalA_yesOverrides
65+
#InternalA.isFinal: @InternalA_isFinal
66+
}
67+
68+
internal class InternalB: InternalA {
69+
override func yesOverrides() {}
70+
}
71+
72+
sil @InternalB_yesOverrides : $@convention(method) (@guaranteed InternalB) -> ()
73+
74+
sil_vtable InternalB {
75+
#InternalA.noOverrides: @InternalA_noOverrides [inherited]
76+
#InternalA.yesOverrides: @InternalB_yesOverrides [override]
77+
#InternalA.isFinal: @InternalA_isFinal [inherited]
78+
}
79+
80+
public class PublicA {
81+
public func noOverrides() {}
82+
public func yesOverrides() {}
83+
public final func isFinal() {}
84+
}
85+
86+
sil @PublicA_noOverrides : $@convention(method) (@guaranteed PublicA) -> ()
87+
sil @PublicA_yesOverrides : $@convention(method) (@guaranteed PublicA) -> ()
88+
sil @PublicA_isFinal : $@convention(method) (@guaranteed PublicA) -> ()
89+
90+
// NOWMO-LABEL: sil_vtable PublicA {
91+
// NOWMO-NOT: #PublicA.noOverrides{{.*}} [nonoverridden]
92+
// NOWMO-NOT: #PublicA.yesOverrides{{.*}} [nonoverridden]
93+
// NOWMO: #PublicA.isFinal{{.*}} [nonoverridden]
94+
95+
// WMO-LABEL: sil_vtable PublicA {
96+
// WMO: #PublicA.noOverrides{{.*}} [nonoverridden]
97+
// WMO-NOT: #PublicA.yesOverrides{{.*}} [nonoverridden]
98+
// WMO: #PublicA.isFinal{{.*}} [nonoverridden]
99+
sil_vtable PublicA {
100+
#PublicA.noOverrides: @PublicA_noOverrides
101+
#PublicA.yesOverrides: @PublicA_yesOverrides
102+
#PublicA.isFinal: @PublicA_isFinal
103+
}
104+
105+
public class PublicB: PublicA {
106+
public override func yesOverrides() {}
107+
}
108+
109+
sil @PublicB_yesOverrides : $@convention(method) (@guaranteed PublicB) -> ()
110+
111+
sil_vtable PublicB {
112+
#PublicA.noOverrides: @PublicA_noOverrides [inherited]
113+
#PublicA.yesOverrides: @PublicB_yesOverrides [override]
114+
#PublicA.isFinal: @PublicA_isFinal [inherited]
115+
}
116+
117+
open class OpenA {
118+
open func noOverrides() {}
119+
open func yesOverrides() {}
120+
public final func isFinal() {}
121+
}
122+
123+
sil @OpenA_noOverrides : $@convention(method) (@guaranteed OpenA) -> ()
124+
sil @OpenA_yesOverrides : $@convention(method) (@guaranteed OpenA) -> ()
125+
sil @OpenA_isFinal : $@convention(method) (@guaranteed OpenA) -> ()
126+
127+
// NOWMO-LABEL: sil_vtable OpenA {
128+
// NOWMO-NOT: #OpenA.noOverrides{{.*}} [nonoverridden]
129+
// NOWMO-NOT: #OpenA.yesOverrides{{.*}} [nonoverridden]
130+
// NOWMO: #OpenA.isFinal{{.*}} [nonoverridden]
131+
132+
// WMO-LABEL: sil_vtable OpenA {
133+
// WMO-NOT: #OpenA.noOverrides{{.*}} [nonoverridden]
134+
// WMO-NOT: #OpenA.yesOverrides{{.*}} [nonoverridden]
135+
// WMO: #OpenA.isFinal{{.*}} [nonoverridden]
136+
sil_vtable OpenA {
137+
#OpenA.noOverrides: @OpenA_noOverrides
138+
#OpenA.yesOverrides: @OpenA_yesOverrides
139+
#OpenA.isFinal: @OpenA_isFinal
140+
}
141+
142+
open class OpenB: OpenA {
143+
open override func yesOverrides() {}
144+
}
145+
146+
sil @OpenB_yesOverrides : $@convention(method) (@guaranteed OpenB) -> ()
147+
148+
sil_vtable OpenB {
149+
#OpenA.noOverrides: @PublicA_noOverrides [inherited]
150+
#OpenA.yesOverrides: @OpenB_yesOverrides [override]
151+
#OpenA.isFinal: @PublicA_isFinal [inherited]
152+
}

0 commit comments

Comments
 (0)