Skip to content

Commit 2c0efaa

Browse files
committed
MandatoryPerformanceOptimizations: specialize witness tables to support class existentials with generic classes
1 parent 2a0e81e commit 2c0efaa

File tree

4 files changed

+60
-3
lines changed

4 files changed

+60
-3
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
112112
_ = context.specializeClassMethodInst(classMethod)
113113
}
114114

115+
case let initExRef as InitExistentialRefInst:
116+
if context.options.enableEmbeddedSwift {
117+
specializeWitnessTables(for: initExRef, moduleContext, &worklist)
118+
}
119+
115120
// We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers.
116121
case let destroyValue as DestroyValueInst:
117122
if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) {
@@ -254,6 +259,31 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
254259
return false
255260
}
256261

262+
private func specializeWitnessTables(for initExRef: InitExistentialRefInst, _ context: ModulePassContext,
263+
_ worklist: inout FunctionWorklist)
264+
{
265+
let conformingType = initExRef.instance.type
266+
assert(conformingType.isClass)
267+
268+
for conformance in initExRef.conformances {
269+
if conformance.isConcrete,
270+
conformance.isSpecialized,
271+
context.lookupWitnessTable(for: conformance) == nil
272+
{
273+
let wt = specializeWitnessTable(forConformance: conformance, context)
274+
for entry in wt.entries where entry.kind == .method {
275+
if let method = entry.methodFunction,
276+
// A new witness table can still contain a generic function if the method couldn't be specialized for
277+
// some reason and an error has been printed. Exclude generic functions to not run into an assert later.
278+
!method.isGeneric
279+
{
280+
worklist.pushIfNotVisited(method)
281+
}
282+
}
283+
}
284+
}
285+
}
286+
257287
private extension FullApplySite {
258288
func resultIsUsedInGlobalInitialization() -> SmallProjectionPath? {
259289
guard parentFunction.isGlobalInitOnceFunction,

SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,31 @@ func specializeVTablesOfSuperclasses(_ moduleContext: ModulePassContext) {
7575
}
7676
}
7777
}
78+
79+
func specializeWitnessTable(forConformance conformance: ProtocolConformance,
80+
_ context: ModulePassContext) -> WitnessTable
81+
{
82+
let genericConformance = conformance.genericConformance
83+
guard let witnessTable = context.lookupWitnessTable(for: genericConformance) else {
84+
fatalError("no witness table found")
85+
}
86+
assert(witnessTable.isDefinition, "No witness table available")
87+
88+
let newEntries = witnessTable.entries.map { origEntry in
89+
switch origEntry.kind {
90+
case .method:
91+
let methodDecl = origEntry.methodRequirement
92+
guard let specializedMethod = context.specialize(function: origEntry.methodFunction!,
93+
for: conformance.specializedSubstitutions) else {
94+
context.diagnosticEngine.diagnose(methodDecl.location.sourceLoc, .cannot_specialize_witness_method)
95+
return origEntry
96+
}
97+
return WitnessTable.Entry(methodRequirement: methodDecl, methodFunction: specializedMethod)
98+
default:
99+
// TODO: handle other witness table entry kinds
100+
fatalError("unsupported witness table etnry")
101+
}
102+
return origEntry
103+
}
104+
return context.createWitnessTable(entries: newEntries, conformance: conformance, linkage: .shared, serialized: false)
105+
}

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ ERROR(global_must_be_compile_time_const,none,
377377
"global variable must be a compile-time constant", ())
378378
ERROR(non_final_generic_class_function,none,
379379
"classes cannot have non-final generic fuctions in embedded Swift", ())
380+
ERROR(cannot_specialize_witness_method,none,
381+
"generic method cannot be used in existential type", ())
380382
ERROR(cannot_specialize_class,none,
381383
"cannot specialize %0 because class definition is not available (make sure to build with -wmo)", (Type))
382384
ERROR(embedded_swift_existential_type,none,

test/embedded/existential-class-bound4.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
// REQUIRES: optimized_stdlib
66
// REQUIRES: OS=macosx || OS=linux-gnu
77

8-
// Generic classes don't work yet.
9-
// XFAIL: *
10-
118
protocol ClassBound: AnyObject {
129
func foo()
1310
func bar()

0 commit comments

Comments
 (0)