Skip to content

Commit 7ffd270

Browse files
committed
embedded: move the VTableSpecializer pass into MandatoryPerformanceOptimizations
MandatoryPerformanceOptimizations already did most of the vtable specialization work. So it makes sense to remove the VTableSpecializerPass completely and do everything in MandatoryPerformanceOptimizations.
1 parent 3775a35 commit 7ffd270

File tree

13 files changed

+156
-350
lines changed

13 files changed

+156
-350
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,23 @@ let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance-
3333
// For embedded Swift, optimize all the functions (there cannot be any
3434
// generics, type metadata, etc.)
3535
if moduleContext.options.enableEmbeddedSwift {
36+
// We need to specialize all vtables which are referenced from non-generic contexts. Beside
37+
// `alloc_ref`s of generic classes in non-generic functions, we also need to specialize generic
38+
// superclasses of non-generic classes. E.g. `class Derived : Base<Int> {}`
39+
specializeVTablesOfSuperclasses(moduleContext)
40+
3641
worklist.addAllNonGenericFunctions(of: moduleContext)
3742
} else {
3843
worklist.addAllPerformanceAnnotatedFunctions(of: moduleContext)
3944
worklist.addAllAnnotatedGlobalInitOnceFunctions(of: moduleContext)
4045
}
4146

4247
optimizeFunctionsTopDown(using: &worklist, moduleContext)
48+
49+
if moduleContext.options.enableEmbeddedSwift {
50+
// Print errors for generic functions in vtables, which is not allowed in embedded Swift.
51+
checkVTablesForGenericFunctions(moduleContext)
52+
}
4353
}
4454

4555
private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
@@ -92,11 +102,15 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
92102
// Embedded Swift specific transformations
93103
case let alloc as AllocRefInst:
94104
if context.options.enableEmbeddedSwift {
95-
specializeVTableAndAddEntriesToWorklist(for: alloc.type, in: function, context, moduleContext, &worklist)
105+
specializeVTableAndAddEntriesToWorklist(for: alloc.type, in: function,
106+
errorLocation: alloc.location,
107+
moduleContext, &worklist)
96108
}
97109
case let metatype as MetatypeInst:
98110
if context.options.enableEmbeddedSwift {
99-
specializeVTableAndAddEntriesToWorklist(for: metatype.type, in: function, context, moduleContext, &worklist)
111+
specializeVTableAndAddEntriesToWorklist(for: metatype.type, in: function,
112+
errorLocation: metatype.location,
113+
moduleContext, &worklist)
100114
}
101115
case let classMethod as ClassMethodInst:
102116
if context.options.enableEmbeddedSwift {
@@ -144,18 +158,23 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
144158
}
145159

146160
private func specializeVTableAndAddEntriesToWorklist(for type: Type, in function: Function,
147-
_ context: FunctionPassContext, _ moduleContext: ModulePassContext,
161+
errorLocation: Location,
162+
_ moduleContext: ModulePassContext,
148163
_ worklist: inout FunctionWorklist) {
149164
let vTablesCountBefore = moduleContext.vTables.count
150165

151-
guard context.specializeVTable(for: type, in: function) != nil else {
166+
guard specializeVTable(forClassType: type, errorLocation: errorLocation, moduleContext) != nil else {
152167
return
153168
}
154169

155170
// More than one new vtable might have been created (superclasses), process them all
156171
let vTables = moduleContext.vTables
157172
for i in vTablesCountBefore ..< vTables.count {
158-
for entry in vTables[i].entries {
173+
for entry in vTables[i].entries
174+
// A new vtable can still contain a generic function if the method couldn't be specialized for some reason
175+
// and an error has been printed. Exclude generic functions to not run into an assert later.
176+
where !entry.implementation.isGeneric
177+
{
159178
worklist.pushIfNotVisited(entry.implementation)
160179
}
161180
}
@@ -240,6 +259,14 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
240259
return false
241260
}
242261

262+
private func checkVTablesForGenericFunctions(_ context: ModulePassContext) {
263+
for vTable in context.vTables where !vTable.class.isGenericAtAnyLevel {
264+
for entry in vTable.entries where entry.implementation.isGeneric {
265+
context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function)
266+
}
267+
}
268+
}
269+
243270
private extension FullApplySite {
244271
func resultIsUsedInGlobalInitialization() -> SmallProjectionPath? {
245272
guard parentFunction.isGlobalInitOnceFunction,

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,6 @@ struct FunctionPassContext : MutatingContext {
339339
return false
340340
}
341341

342-
func specializeVTable(for type: Type, in function: Function) -> VTable? {
343-
guard let vtablePtr = _bridged.specializeVTableForType(type.bridged, function.bridged) else {
344-
return nil
345-
}
346-
return VTable(bridged: BridgedVTable(vTable: vtablePtr))
347-
}
348-
349342
func specializeClassMethodInst(_ cm: ClassMethodInst) -> Bool {
350343
if _bridged.specializeClassMethodInst(cm.bridged) {
351344
notifyInstructionsChanged()

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ swift_compiler_sources(Optimizer
1717
EscapeUtils.swift
1818
ForwardingUtils.swift
1919
FunctionSignatureTransforms.swift
20+
GenericSpecialization.swift
2021
LifetimeDependenceUtils.swift
2122
LocalVariableUtils.swift
2223
OptUtils.swift
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===--- GenericSpecialization.swift ---------------------------------------==//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
import SIL
14+
15+
@discardableResult
16+
func specializeVTable(forClassType classType: Type,
17+
errorLocation: Location,
18+
_ context: ModulePassContext) -> VTable?
19+
{
20+
if !classType.isClass {
21+
return nil
22+
}
23+
if !classType.isGenericAtAnyLevel {
24+
return nil
25+
}
26+
let classDecl = classType.nominal
27+
28+
if context.lookupSpecializedVTable(for: classType) != nil {
29+
return nil
30+
}
31+
32+
guard let origVTable = context.lookupVTable(for: classDecl) else {
33+
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType)
34+
return nil
35+
}
36+
37+
let classContextSubs = classType.contextSubstitutionMap
38+
39+
let newEntries = origVTable.entries.map { origEntry in
40+
if !origEntry.implementation.isGeneric {
41+
return origEntry
42+
}
43+
let methodSubs = classContextSubs.getMethodSubstitutions(for: origEntry.implementation)
44+
45+
guard !methodSubs.conformances.contains(where: {!$0.isValid}),
46+
let specializedMethod = context.specialize(function: origEntry.implementation, for: methodSubs) else
47+
{
48+
context.diagnosticEngine.diagnose(origEntry.methodDecl.location.sourceLoc, .non_final_generic_class_function)
49+
return origEntry
50+
}
51+
52+
context.deserializeAllCallees(of: specializedMethod, mode: .allFunctions)
53+
specializedMethod.set(linkage: .public, context)
54+
specializedMethod.set(isSerialized: false, context)
55+
56+
return VTable.Entry(kind: origEntry.kind, isNonOverridden: origEntry.isNonOverridden,
57+
methodDecl: origEntry.methodDecl, implementation: specializedMethod)
58+
}
59+
60+
let specializedVTable = context.createSpecializedVTable(entries: newEntries, for: classType, isSerialized: false)
61+
if let superClassTy = classType.superClassType {
62+
specializeVTable(forClassType: superClassTy, errorLocation: classDecl.location, context)
63+
}
64+
return specializedVTable
65+
}
66+
67+
func specializeVTablesOfSuperclasses(_ moduleContext: ModulePassContext) {
68+
for vtable in moduleContext.vTables {
69+
if !vtable.isSpecialized,
70+
!vtable.class.isGenericAtAnyLevel,
71+
let superClassTy = vtable.class.superClassType,
72+
superClassTy.nominal.isGenericAtAnyLevel
73+
{
74+
specializeVTable(forClassType: superClassTy, errorLocation: vtable.class.location, moduleContext)
75+
}
76+
}
77+
}

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,6 @@ struct BridgedPassContext {
252252
SWIFT_IMPORT_UNSAFE DevirtResult tryDevirtualizeApply(BridgedInstruction apply, bool isMandatory) const;
253253
bool tryOptimizeKeypath(BridgedInstruction apply) const;
254254
SWIFT_IMPORT_UNSAFE OptionalBridgedValue constantFoldBuiltin(BridgedInstruction builtin) const;
255-
SWIFT_IMPORT_UNSAFE swift::SILVTable * _Nullable specializeVTableForType(BridgedType type,
256-
BridgedFunction function) const;
257255
SWIFT_IMPORT_UNSAFE OptionalBridgedFunction specializeFunction(BridgedFunction function,
258256
BridgedSubstitutionMap substitutions) const;
259257
void deserializeAllCallees(BridgedFunction function, bool deserializeAll) const;

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,6 @@ SWIFT_FUNCTION_PASS(DeadStoreElimination, "dead-store-elimination",
258258
"Dead Store Elimination")
259259
PASS(GenericSpecializer, "generic-specializer",
260260
"Generic Function Specialization on Static Types")
261-
PASS(VTableSpecializer, "vtable-specializer",
262-
"Generic Specialization on VTables")
263261
PASS(ExistentialSpecializer, "existential-specializer",
264262
"Existential Specializer")
265263
PASS(SILSkippingChecker, "check-sil-skipping",

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,8 +606,6 @@ bool optimizeMemoryAccesses(SILFunction *fn, DominanceInfo *domInfo);
606606
/// See the PredictableDeadAllocationElimination pass.
607607
bool eliminateDeadAllocations(SILFunction *fn, DominanceInfo *domInfo);
608608

609-
SILVTable *specializeVTableForType(SILType type, SILModule &mod, SILTransform *transform);
610-
611609
bool specializeClassMethodInst(ClassMethodInst *cm);
612610

613611
bool specializeAppliesInFunction(SILFunction &F,

lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@ class PerformanceDiagnosticsPass : public SILModuleTransform {
914914
for (SILFunction *function : constructorsAndDestructors) {
915915
diagnoser.visitFunctionEmbeddedSwift(function);
916916
}
917-
}
917+
}
918918
}
919919
};
920920

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,12 +1735,6 @@ bool BridgedPassContext::canMakeStaticObjectReadOnly(BridgedType type) const {
17351735
return false;
17361736
}
17371737

1738-
swift::SILVTable * BridgedPassContext::specializeVTableForType(BridgedType type, BridgedFunction function) const {
1739-
return ::specializeVTableForType(type.unbridged(),
1740-
function.getFunction()->getModule(),
1741-
invocation->getTransform());
1742-
}
1743-
17441738
OptionalBridgedFunction BridgedPassContext::specializeFunction(BridgedFunction function,
17451739
BridgedSubstitutionMap substitutions) const {
17461740
swift::SILModule *mod = invocation->getPassManager()->getModule();

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,6 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
255255
// Canonical swift requires all non cond_br critical edges to be split.
256256
P.addSplitNonCondBrCriticalEdges();
257257

258-
// For embedded Swift: Specialize generic class vtables.
259-
P.addVTableSpecializer();
260-
261258
P.addMandatoryPerformanceOptimizations();
262259
P.addOnoneSimplification();
263260
P.addAllocVectorLowering();

0 commit comments

Comments
 (0)