Skip to content

Commit e3ce94d

Browse files
committed
Introduce a PruneVTables pass to mark non-overridden entries.
Start with some high-level checks of whether a declaration is formally final, or has no visible overrides in the domain of the program visible to the SIL module. We can eventually adopt more of the logic from the Devirtualizer pass to tell whether call sites are "effectively final", and maybe make the Devirtualizer consume that information applied by this pass.
1 parent 8e6840d commit e3ce94d

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)