Skip to content

Commit 5cdff63

Browse files
authored
Merge pull request #68615 from kubamracek/embedded-mandatory-vtable-iterate
[embedded] Perform VTable specialization iteratively as part of MandatoryPerformanceOptimizations
2 parents 9b31e9a + 1723a73 commit 5cdff63

File tree

8 files changed

+216
-36
lines changed

8 files changed

+216
-36
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
4949
if !context.loadFunction(function: f, loadCalleesRecursively: true) {
5050
return
5151
}
52-
optimize(function: f, context)
53-
worklist.add(calleesOf: f)
52+
53+
optimize(function: f, context, &worklist)
5454
}
5555
}
5656
}
@@ -60,7 +60,7 @@ fileprivate struct PathFunctionTuple: Hashable {
6060
var function: Function
6161
}
6262

63-
private func optimize(function: Function, _ context: FunctionPassContext) {
63+
private func optimize(function: Function, _ context: FunctionPassContext, _ worklist: inout FunctionWorklist) {
6464
var alreadyInlinedFunctions: Set<PathFunctionTuple> = Set()
6565

6666
var changed = true
@@ -75,6 +75,21 @@ private func optimize(function: Function, _ context: FunctionPassContext) {
7575
switch instruction {
7676
case let apply as FullApplySite:
7777
inlineAndDevirtualize(apply: apply, alreadyInlinedFunctions: &alreadyInlinedFunctions, context, simplifyCtxt)
78+
79+
// Embedded Swift specific transformations
80+
case let alloc as AllocRefInst:
81+
if context.options.enableEmbeddedSwift {
82+
specializeVTableAndAddEntriesToWorklist(for: alloc.type, in: function, context, &worklist)
83+
}
84+
case let metatype as MetatypeInst:
85+
if context.options.enableEmbeddedSwift {
86+
specializeVTableAndAddEntriesToWorklist(for: metatype.type, in: function, context, &worklist)
87+
}
88+
case let classMethod as ClassMethodInst:
89+
if context.options.enableEmbeddedSwift {
90+
_ = context.specializeClassMethodInst(classMethod)
91+
}
92+
7893
default:
7994
break
8095
}
@@ -88,6 +103,18 @@ private func optimize(function: Function, _ context: FunctionPassContext) {
88103
changed = context.optimizeMemoryAccesses(in: function) || changed
89104
_ = context.eliminateDeadAllocations(in: function)
90105
}
106+
107+
worklist.add(calleesOf: function)
108+
}
109+
110+
private func specializeVTableAndAddEntriesToWorklist(for type: Type, in function: Function, _ context: FunctionPassContext, _ worklist: inout FunctionWorklist) {
111+
guard let vtable = context.specializeVTable(for: type, in: function) else {
112+
return
113+
}
114+
115+
for entry in vtable.entries {
116+
worklist.pushIfNotVisited(entry.function)
117+
}
91118
}
92119

93120
private func inlineAndDevirtualize(apply: FullApplySite, alreadyInlinedFunctions: inout Set<PathFunctionTuple>,

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

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

270+
func specializeVTable(for type: Type, in function: Function) -> VTable? {
271+
guard let vtablePtr = _bridged.specializeVTableForType(type.bridged, function.bridged) else {
272+
return nil
273+
}
274+
return VTable(bridged: BridgedVTable(vTable: vtablePtr))
275+
}
276+
277+
func specializeClassMethodInst(_ cm: ClassMethodInst) -> Bool {
278+
if _bridged.specializeClassMethodInst(cm.bridged) {
279+
notifyInstructionsChanged()
280+
notifyCallsChanged()
281+
return true
282+
}
283+
return false
284+
}
285+
270286
func specializeApplies(in function: Function, isMandatory: Bool) -> Bool {
271287
if _bridged.specializeAppliesInFunction(function.bridged, isMandatory) {
272288
notifyInstructionsChanged()

include/swift/SILOptimizer/OptimizerBridging.h

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

257+
SWIFT_IMPORT_UNSAFE
258+
swift::SILVTable * _Nullable specializeVTableForType(swift::SILType type, BridgedFunction function) const;
259+
260+
bool specializeClassMethodInst(BridgedInstruction cm) const;
261+
257262
bool specializeAppliesInFunction(BridgedFunction function, bool isMandatory) const;
258263

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

include/swift/SILOptimizer/Utils/InstOptUtils.h

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

600+
SILVTable *specializeVTableForType(SILType type, SILModule &mod, SILTransform *transform);
601+
602+
bool specializeClassMethodInst(ClassMethodInst *cm);
603+
600604
bool specializeAppliesInFunction(SILFunction &F,
601605
SILTransform *transform,
602606
bool isMandatory);

lib/SILOptimizer/PassManager/PassManager.cpp

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

1548+
swift::SILVTable * BridgedPassContext::specializeVTableForType(swift::SILType type, BridgedFunction function) const {
1549+
return ::specializeVTableForType(type, function.getFunction()->getModule(), invocation->getTransform());
1550+
}
1551+
1552+
bool BridgedPassContext::specializeClassMethodInst(BridgedInstruction cm) const {
1553+
return ::specializeClassMethodInst(cm.getAs<ClassMethodInst>());
1554+
}
1555+
15481556
bool BridgedPassContext::specializeAppliesInFunction(BridgedFunction function, bool isMandatory) const {
15491557
return ::specializeAppliesInFunction(*function.getFunction(), invocation->getTransform(), isMandatory);
15501558
}

lib/SILOptimizer/Transforms/VTableSpecializer.cpp

Lines changed: 47 additions & 33 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 SILFunction *specializeVTableMethod(SILFunction *origMethod,
57+
SubstitutionMap subs,
58+
SILModule &module,
59+
SILTransform *transform);
60+
61+
static bool specializeVTablesInFunction(SILFunction &func, SILModule &module,
62+
SILTransform *transform) {
6163
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-
}
64+
if (func.getLoweredFunctionType()->isPolymorphic())
65+
return changed;
66+
67+
for (SILBasicBlock &block : func) {
68+
for (SILInstruction &inst : block) {
69+
if (auto *allocRef = dyn_cast<AllocRefInst>(&inst)) {
70+
changed |= (specializeVTableForType(allocRef->getType(), module,
71+
transform) != nullptr);
72+
} else if (auto *metatype = dyn_cast<MetatypeInst>(&inst)) {
73+
changed |= (specializeVTableForType(
74+
metatype->getType().getInstanceTypeOfMetatype(&func),
75+
module, transform) != nullptr);
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,13 +104,13 @@ bool VTableSpecializer::specializeVTables(SILModule &module) {
92104
return changed;
93105
}
94106

95-
bool VTableSpecializer::specializeVTableFor(SILType classTy,
96-
SILModule &module) {
107+
SILVTable *swift::specializeVTableForType(SILType classTy, SILModule &module,
108+
SILTransform *transform) {
97109
CanType astType = classTy.getASTType();
98110
BoundGenericClassType *genClassTy = dyn_cast<BoundGenericClassType>(astType);
99-
if (!genClassTy) return false;
111+
if (!genClassTy) return nullptr;
100112

101-
if (module.lookUpSpecializedVTable(classTy)) return false;
113+
if (module.lookUpSpecializedVTable(classTy)) return nullptr;
102114

103115
LLVM_DEBUG(llvm::errs() << "specializeVTableFor "
104116
<< genClassTy->getDecl()->getName() << ' '
@@ -120,19 +132,21 @@ 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()));
127139
}
128140

129-
SILVTable::create(module, classDecl, classTy, IsNotSerialized, newEntries);
130-
return true;
141+
SILVTable *vtable = SILVTable::create(module, classDecl, classTy,
142+
IsNotSerialized, newEntries);
143+
return vtable;
131144
}
132145

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

@@ -149,7 +163,7 @@ SILFunction *VTableSpecializer::specializeVTableMethod(SILFunction *origMethod,
149163
llvm::report_fatal_error("cannot specialize vtable method");
150164
}
151165

152-
SILOptFunctionBuilder FunctionBuilder(*this);
166+
SILOptFunctionBuilder FunctionBuilder(*transform);
153167

154168
GenericFuncSpecializer FuncSpecializer(FunctionBuilder, origMethod, subs,
155169
ReInfo, /*isMandatory=*/true);
@@ -172,7 +186,10 @@ SILFunction *VTableSpecializer::specializeVTableMethod(SILFunction *origMethod,
172186
return SpecializedF;
173187
}
174188

175-
bool VTableSpecializer::specializeClassMethodInst(ClassMethodInst *cm) {
189+
bool swift::specializeClassMethodInst(ClassMethodInst *cm) {
190+
SILFunction *f = cm->getFunction();
191+
SILModule &m = f->getModule();
192+
176193
SILValue instance = cm->getOperand();
177194
SILType classTy = instance->getType();
178195
CanType astType = classTy.getASTType();
@@ -184,9 +201,6 @@ bool VTableSpecializer::specializeClassMethodInst(ClassMethodInst *cm) {
184201
classDecl->getParentModule(), classDecl);
185202

186203
SILType funcTy = cm->getType();
187-
188-
SILFunction *f = cm->getFunction();
189-
SILModule &m = f->getModule();
190204
SILType substitutedType =
191205
funcTy.substGenericArgs(m, subs, TypeExpansionContext::minimal());
192206

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <strings.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
#include <stdbool.h>
5+
6+
size_t _swiftEmptyArrayStorage[] = { /*isa*/0, /*refcount*/-1, /*count*/0, /*flags*/1 };
7+
8+
typedef struct HeapObject {
9+
void *metadata;
10+
size_t refcount;
11+
} HeapObject;
12+
13+
void *swift_allocObject(void *metadata, size_t requiredSize, size_t requiredAlignmentMask) {
14+
void *r = NULL;
15+
posix_memalign(&r, requiredAlignmentMask + 1, requiredSize);
16+
bzero(r, requiredSize);
17+
HeapObject *object = r;
18+
object->metadata = metadata;
19+
object->refcount = 1;
20+
return object;
21+
}
22+
23+
void swift_deallocClassInstance(HeapObject *object, size_t allocatedSize, size_t allocatedAlignMask) {
24+
free(object);
25+
}
26+
27+
HeapObject *swift_initStackObject(void *metadata, HeapObject *object) {
28+
object->metadata = metadata;
29+
object->refcount = -1;
30+
return object;
31+
}
32+
33+
bool swift_isUniquelyReferenced_nonNull_native(HeapObject *object) {
34+
return object->refcount == 1;
35+
}
36+
37+
void swift_release(HeapObject *object) {
38+
if (object->refcount == -1) return;
39+
40+
object->refcount -= 1;
41+
if (object->refcount == 0) {
42+
free(object);
43+
}
44+
}
45+
46+
HeapObject *swift_retain(HeapObject *object) {
47+
if (object->refcount == -1) return object;
48+
49+
object->refcount += 1;
50+
return object;
51+
}

test/embedded/arrays.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -enable-experimental-feature Embedded -c -o %t/main.o
3+
// RUN: %target-clang -x c -c %S/Inputs/tiny-runtime-dummy-refcounting.c -o %t/runtime.o
4+
// RUN: %target-clang -x c -c %S/Inputs/print.c -o %t/print.o
5+
// RUN: %target-clang %t/main.o %t/runtime.o %t/print.o -o %t/a.out -dead_strip
6+
// RUN: %target-run %t/a.out | %FileCheck %s
7+
8+
// REQUIRES: executable_test
9+
// REQUIRES: VENDOR=apple
10+
// REQUIRES: OS=macosx
11+
12+
@_silgen_name("putchar")
13+
func putchar(_: UInt8)
14+
15+
public func print(_ s: StaticString, terminator: StaticString = "\n") {
16+
var p = s.utf8Start
17+
while p.pointee != 0 {
18+
putchar(p.pointee)
19+
p += 1
20+
}
21+
p = terminator.utf8Start
22+
while p.pointee != 0 {
23+
putchar(p.pointee)
24+
p += 1
25+
}
26+
}
27+
28+
@_silgen_name("print_long")
29+
func print_long(_: Int)
30+
31+
public func print(_ n: Int, terminator: StaticString = "\n") {
32+
print_long(n)
33+
print("", terminator: terminator)
34+
}
35+
36+
func print(_ array: [Int]) {
37+
print("[", terminator: "")
38+
for i in 0 ..< array.count {
39+
print_long(array[i])
40+
if i != array.count - 1 { print(", ", terminator: "") }
41+
}
42+
print("]")
43+
}
44+
45+
func test() {
46+
var a = [1, 2, 3]
47+
a.append(8)
48+
a.append(contentsOf: [5, 4])
49+
let b = a.sorted()
50+
var c = b
51+
c = c.reversed().filter { $0 % 2 == 0 }
52+
print(c) // CHECK: [8, 4, 2]
53+
}
54+
55+
test()

0 commit comments

Comments
 (0)