Skip to content

Commit 03f927e

Browse files
committed
[embedded] Perform VTable specialization iteratively as part of MandatoryPerformanceOptimizations
1 parent fca24bd commit 03f927e

File tree

7 files changed

+155
-26
lines changed

7 files changed

+155
-26
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,17 @@ private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
4949
if !context.loadFunction(function: f, loadCalleesRecursively: true) {
5050
return
5151
}
52+
53+
let vTablesFunctionsBefore = Set(moduleContext.vTables.flatMap { $0.entries.map { $0.function } })
54+
5255
optimize(function: f, context)
56+
57+
let vTablesFunctionsAfter = Set(moduleContext.vTables.flatMap { $0.entries.map { $0.function } })
58+
// Optimizing can trigger vtable specialization, add new vtable entries to the worklist
59+
for f in vTablesFunctionsAfter.subtracting(vTablesFunctionsBefore) {
60+
worklist.pushIfNotVisited(f)
61+
}
62+
5363
worklist.add(calleesOf: f)
5464
}
5565
}
@@ -82,6 +92,8 @@ private func optimize(function: Function, _ context: FunctionPassContext) {
8292

8393
_ = context.specializeApplies(in: function, isMandatory: true)
8494

95+
changed = context.specializeVTables(in: function) || changed
96+
8597
removeUnusedMetatypeInstructions(in: function, context)
8698

8799
// If this is a just specialized function, try to optimize copy_addr, etc.

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ struct FunctionPassContext : MutatingContext {
267267
return false
268268
}
269269

270+
func specializeVTables(in function: Function) -> Bool {
271+
if _bridged.specializeVTables(function.bridged) {
272+
notifyInstructionsChanged()
273+
return true
274+
}
275+
return false
276+
}
277+
270278
func specializeApplies(in function: Function, isMandatory: Bool) -> Bool {
271279
if _bridged.specializeAppliesInFunction(function.bridged, isMandatory) {
272280
notifyInstructionsChanged()

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ struct BridgedPassContext {
254254
SWIFT_IMPORT_UNSAFE
255255
OptionalBridgedValue constantFoldBuiltin(BridgedInstruction builtin) const;
256256

257+
bool specializeVTables(BridgedFunction function) const;
258+
257259
bool specializeAppliesInFunction(BridgedFunction function, bool isMandatory) const;
258260

259261
std::string mangleOutlinedVariable(BridgedFunction function) const;

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ bool optimizeMemoryAccesses(SILFunction *fn);
597597
/// See the PredictableDeadAllocationElimination pass.
598598
bool eliminateDeadAllocations(SILFunction *fn);
599599

600+
bool specializeVTables(SILFunction *fn, SILTransform *transform);
601+
600602
bool specializeAppliesInFunction(SILFunction &F,
601603
SILTransform *transform,
602604
bool isMandatory);

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,10 @@ SwiftInt BridgedPassContext::getStaticStride(swift::SILType type) const {
15451545
return integerValueFromConstant(c);
15461546
}
15471547

1548+
bool BridgedPassContext::specializeVTables(BridgedFunction function) const {
1549+
return ::specializeVTables(function.getFunction(), invocation->getTransform());
1550+
}
1551+
15481552
bool BridgedPassContext::specializeAppliesInFunction(BridgedFunction function, bool isMandatory) const {
15491553
return ::specializeAppliesInFunction(*function.getFunction(), invocation->getTransform(), isMandatory);
15501554
}

lib/SILOptimizer/Transforms/VTableSpecializer.cpp

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ namespace {
3838

3939
class VTableSpecializer : public SILModuleTransform {
4040
bool specializeVTables(SILModule &module);
41-
bool specializeVTableFor(SILType classTy, SILModule &module);
42-
SILFunction *specializeVTableMethod(SILFunction *origMethod,
43-
SubstitutionMap subs, SILModule &module);
44-
bool specializeClassMethodInst(ClassMethodInst *cm);
4541

4642
/// The entry point to the transformation.
4743
void run() override {
@@ -57,25 +53,41 @@ class VTableSpecializer : public SILModuleTransform {
5753

5854
} // end anonymous namespace
5955

60-
bool VTableSpecializer::specializeVTables(SILModule &module) {
56+
static bool specializeVTableFor(SILType classTy, SILModule &module,
57+
SILTransform *transform);
58+
static SILFunction *specializeVTableMethod(SILFunction *origMethod,
59+
SubstitutionMap subs,
60+
SILModule &module,
61+
SILTransform *transform);
62+
static bool specializeClassMethodInst(ClassMethodInst *cm);
63+
64+
static bool specializeVTablesInFunction(SILFunction &func, SILModule &module,
65+
SILTransform *transform) {
6166
bool changed = false;
62-
for (SILFunction &func : module) {
63-
if (func.getLoweredFunctionType()->isPolymorphic()) continue;
64-
65-
for (SILBasicBlock &block : func) {
66-
for (SILInstruction &inst : block) {
67-
if (auto *allocRef = dyn_cast<AllocRefInst>(&inst)) {
68-
changed |= specializeVTableFor(allocRef->getType(), module);
69-
} else if (auto *metatype = dyn_cast<MetatypeInst>(&inst)) {
70-
changed |= specializeVTableFor(
71-
metatype->getType().getInstanceTypeOfMetatype(&func), module);
72-
} else if (auto *cm = dyn_cast<ClassMethodInst>(&inst)) {
73-
changed |= specializeClassMethodInst(cm);
74-
}
67+
if (func.getLoweredFunctionType()->isPolymorphic()) return changed;
68+
69+
for (SILBasicBlock &block : func) {
70+
for (SILInstruction &inst : block) {
71+
if (auto *allocRef = dyn_cast<AllocRefInst>(&inst)) {
72+
changed |= specializeVTableFor(allocRef->getType(), module, transform);
73+
} else if (auto *metatype = dyn_cast<MetatypeInst>(&inst)) {
74+
changed |= specializeVTableFor(
75+
metatype->getType().getInstanceTypeOfMetatype(&func), module, transform);
76+
} else if (auto *cm = dyn_cast<ClassMethodInst>(&inst)) {
77+
changed |= specializeClassMethodInst(cm);
7578
}
7679
}
7780
}
7881

82+
return changed;
83+
}
84+
85+
bool VTableSpecializer::specializeVTables(SILModule &module) {
86+
bool changed = false;
87+
for (SILFunction &func : module) {
88+
specializeVTablesInFunction(func, module, this);
89+
}
90+
7991
for (SILVTable *vtable : module.getVTables()) {
8092
if (vtable->getClass()->isGenericContext()) continue;
8193

@@ -92,8 +104,8 @@ bool VTableSpecializer::specializeVTables(SILModule &module) {
92104
return changed;
93105
}
94106

95-
bool VTableSpecializer::specializeVTableFor(SILType classTy,
96-
SILModule &module) {
107+
static bool specializeVTableFor(SILType classTy, SILModule &module,
108+
SILTransform *transform) {
97109
CanType astType = classTy.getASTType();
98110
BoundGenericClassType *genClassTy = dyn_cast<BoundGenericClassType>(astType);
99111
if (!genClassTy) return false;
@@ -120,7 +132,7 @@ bool VTableSpecializer::specializeVTableFor(SILType classTy,
120132
for (const SILVTableEntry &entry : origVtable->getEntries()) {
121133
SILFunction *origMethod = entry.getImplementation();
122134
SILFunction *specializedMethod =
123-
specializeVTableMethod(origMethod, subs, module);
135+
specializeVTableMethod(origMethod, subs, module, transform);
124136
newEntries.push_back(SILVTableEntry(entry.getMethod(), specializedMethod,
125137
entry.getKind(),
126138
entry.isNonOverridden()));
@@ -130,9 +142,10 @@ bool VTableSpecializer::specializeVTableFor(SILType classTy,
130142
return true;
131143
}
132144

133-
SILFunction *VTableSpecializer::specializeVTableMethod(SILFunction *origMethod,
134-
SubstitutionMap subs,
135-
SILModule &module) {
145+
static SILFunction *specializeVTableMethod(SILFunction *origMethod,
146+
SubstitutionMap subs,
147+
SILModule &module,
148+
SILTransform *transform) {
136149
LLVM_DEBUG(llvm::errs() << "specializeVTableMethod " << origMethod->getName()
137150
<< '\n');
138151

@@ -149,7 +162,7 @@ SILFunction *VTableSpecializer::specializeVTableMethod(SILFunction *origMethod,
149162
llvm::report_fatal_error("cannot specialize vtable method");
150163
}
151164

152-
SILOptFunctionBuilder FunctionBuilder(*this);
165+
SILOptFunctionBuilder FunctionBuilder(*transform);
153166

154167
GenericFuncSpecializer FuncSpecializer(FunctionBuilder, origMethod, subs,
155168
ReInfo, /*isMandatory=*/true);
@@ -172,7 +185,7 @@ SILFunction *VTableSpecializer::specializeVTableMethod(SILFunction *origMethod,
172185
return SpecializedF;
173186
}
174187

175-
bool VTableSpecializer::specializeClassMethodInst(ClassMethodInst *cm) {
188+
static bool specializeClassMethodInst(ClassMethodInst *cm) {
176189
SILValue instance = cm->getOperand();
177190
SILType classTy = instance->getType();
178191
CanType astType = classTy.getASTType();
@@ -217,6 +230,12 @@ bool VTableSpecializer::specializeClassMethodInst(ClassMethodInst *cm) {
217230
return true;
218231
}
219232

233+
bool swift::specializeVTables(SILFunction *fn, SILTransform *transform) {
234+
if (!fn->getModule.getASTContext().LangOpts.hasFeature(Feature::Embedded))
235+
return false;
236+
return specializeVTablesInFunction(*fn, fn->getModule(), transform);
237+
}
238+
220239
SILTransform *swift::createVTableSpecializer() {
221240
return new VTableSpecializer();
222241
}

test/embedded/arrays.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %{python} %utils/split_file.py -o %t %s
3+
4+
// RUN: %target-swift-frontend %t/main.swift -enable-experimental-feature Embedded -c -o %t/main.o
5+
// RUN: %target-clang -g -x c %t/runtime.c -c -o %t/runtime.o
6+
// RUN: %target-clang %t/main.o %t/runtime.o -o %t/a.out -dead_strip
7+
// RUN: %target-run %t/a.out | %FileCheck %s
8+
9+
// REQUIRES: executable_test
10+
11+
// BEGIN main.swift
12+
13+
@_silgen_name("putchar")
14+
func putchar(_: UInt8)
15+
16+
public func print(_ s: StaticString, terminator: StaticString = "\n") {
17+
var p = s.utf8Start
18+
while p.pointee != 0 {
19+
putchar(p.pointee)
20+
p += 1
21+
}
22+
p = terminator.utf8Start
23+
while p.pointee != 0 {
24+
putchar(p.pointee)
25+
p += 1
26+
}
27+
}
28+
29+
@_silgen_name("vprintf")
30+
func vprintf(_: UnsafePointer<UInt8>, _: UnsafeRawPointer)
31+
32+
var global: Int = 0
33+
public func print(_ n: Int) {
34+
let f: StaticString = "%d"
35+
global = n
36+
vprintf(f.utf8Start, &global)
37+
}
38+
39+
public func print(_ array: [Int]) {
40+
print("[", terminator: "")
41+
for e in array {
42+
print(e)
43+
print(", ", terminator: "")
44+
}
45+
print("]")
46+
}
47+
48+
func test() {
49+
var a = [1, 2, 3]
50+
a.append(8)
51+
a.append(contentsOf: [5, 4])
52+
let b = a.sorted()
53+
var c = b
54+
c = c.reversed()
55+
print(c) // CHECK: [8, 5, 4, 3, 2, 1, ]
56+
}
57+
58+
test()
59+
60+
// BEGIN runtime.c
61+
62+
#include <stdint.h>
63+
#include <stdlib.h>
64+
#include <stdbool.h>
65+
66+
size_t _swiftEmptyArrayStorage[] = { /*isa*/0, /*refcount*/0, /*count*/0, /*flags*/1 };
67+
68+
void *swift_allocObject(void *metadata, size_t requiredSize, size_t requiredAlignmentMask) {
69+
void *r = NULL;
70+
posix_memalign(&r, requiredAlignmentMask + 1, requiredSize);
71+
return r;
72+
}
73+
74+
void swift_deallocClassInstance() { }
75+
void swift_initStackObject() { }
76+
bool swift_isUniquelyReferenced_nonNull_native(void *) { return true; }
77+
void swift_release() { }
78+
void swift_retain() { }
79+
void swift_setDeallocating() { }
80+
81+
void swift_beginAccess() { }
82+
void swift_endAccess() { }

0 commit comments

Comments
 (0)