Skip to content

Commit 8c5d7ee

Browse files
authored
Merge pull request #76669 from eeckstein/class-existentials
embedded: support class existentials with generic classes
2 parents 5cc52dc + 6e61c20 commit 8c5d7ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1181
-556
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/FunctionUses.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,21 +128,21 @@ struct FunctionUses {
128128

129129
for vTable in context.vTables {
130130
for entry in vTable.entries {
131-
markUnknown(entry.function)
131+
markUnknown(entry.implementation)
132132
}
133133
}
134134

135135
for witnessTable in context.witnessTables {
136136
for entry in witnessTable.entries {
137-
if entry.kind == .Method, let f = entry.methodFunction {
137+
if entry.kind == .method, let f = entry.methodFunction {
138138
markUnknown(f)
139139
}
140140
}
141141
}
142142

143143
for witnessTable in context.defaultWitnessTables {
144144
for entry in witnessTable.entries {
145-
if entry.kind == .Method, let f = entry.methodFunction {
145+
if entry.kind == .method, let f = entry.methodFunction {
146146
markUnknown(f)
147147
}
148148
}

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyRefCasts.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,10 @@ private func insertCompensatingInstructions(for inst: Instruction, in failureBlo
116116
let newInst: SingleValueInstruction
117117
switch inst {
118118
case let ier as InitExistentialRefInst:
119-
newInst = builder.createInitExistentialRef(instance: newArg, existentialType: ier.type, useConformancesOf: ier)
119+
newInst = builder.createInitExistentialRef(instance: newArg,
120+
existentialType: ier.type,
121+
formalConcreteType: ier.formalConcreteType,
122+
conformances: ier.conformances)
120123
case let uc as UpcastInst:
121124
newInst = builder.createUpcast(from: newArg, to: uc.type)
122125
default:

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 81 additions & 6 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,17 +102,26 @@ 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 {
103117
_ = context.specializeClassMethodInst(classMethod)
104118
}
105119

120+
case let initExRef as InitExistentialRefInst:
121+
if context.options.enableEmbeddedSwift {
122+
specializeWitnessTables(for: initExRef, moduleContext, &worklist)
123+
}
124+
106125
// We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers.
107126
case let destroyValue as DestroyValueInst:
108127
if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) {
@@ -144,19 +163,24 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
144163
}
145164

146165
private func specializeVTableAndAddEntriesToWorklist(for type: Type, in function: Function,
147-
_ context: FunctionPassContext, _ moduleContext: ModulePassContext,
166+
errorLocation: Location,
167+
_ moduleContext: ModulePassContext,
148168
_ worklist: inout FunctionWorklist) {
149169
let vTablesCountBefore = moduleContext.vTables.count
150170

151-
guard context.specializeVTable(for: type, in: function) != nil else {
171+
guard specializeVTable(forClassType: type, errorLocation: errorLocation, moduleContext) != nil else {
152172
return
153173
}
154174

155175
// More than one new vtable might have been created (superclasses), process them all
156176
let vTables = moduleContext.vTables
157177
for i in vTablesCountBefore ..< vTables.count {
158-
for entry in vTables[i].entries {
159-
worklist.pushIfNotVisited(entry.function)
178+
for entry in vTables[i].entries
179+
// A new vtable can still contain a generic function if the method couldn't be specialized for some reason
180+
// and an error has been printed. Exclude generic functions to not run into an assert later.
181+
where !entry.implementation.isGeneric
182+
{
183+
worklist.pushIfNotVisited(entry.implementation)
160184
}
161185
}
162186
}
@@ -240,6 +264,45 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
240264
return false
241265
}
242266

267+
private func specializeWitnessTables(for initExRef: InitExistentialRefInst, _ context: ModulePassContext,
268+
_ worklist: inout FunctionWorklist)
269+
{
270+
for conformance in initExRef.conformances where conformance.isConcrete {
271+
let origWitnessTable = context.lookupWitnessTable(for: conformance)
272+
if conformance.isSpecialized {
273+
if origWitnessTable == nil {
274+
let wt = specializeWitnessTable(forConformance: conformance, errorLocation: initExRef.location, context)
275+
worklist.addWitnessMethods(of: wt)
276+
}
277+
} else if let origWitnessTable {
278+
checkForGenericMethods(in: origWitnessTable, errorLocation: initExRef.location, context)
279+
}
280+
}
281+
}
282+
283+
private func checkForGenericMethods(in witnessTable: WitnessTable,
284+
errorLocation: Location,
285+
_ context: ModulePassContext)
286+
{
287+
for entry in witnessTable.entries where entry.kind == .method {
288+
if let method = entry.methodFunction,
289+
method.isGeneric
290+
{
291+
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_witness_method,
292+
entry.methodRequirement)
293+
return
294+
}
295+
}
296+
}
297+
298+
private func checkVTablesForGenericFunctions(_ context: ModulePassContext) {
299+
for vTable in context.vTables where !vTable.class.isGenericAtAnyLevel {
300+
for entry in vTable.entries where entry.implementation.isGeneric {
301+
context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function)
302+
}
303+
}
304+
}
305+
243306
private extension FullApplySite {
244307
func resultIsUsedInGlobalInitialization() -> SmallProjectionPath? {
245308
guard parentFunction.isGlobalInitOnceFunction,
@@ -445,6 +508,18 @@ fileprivate struct FunctionWorklist {
445508
}
446509
}
447510

511+
mutating func addWitnessMethods(of witnessTable: WitnessTable) {
512+
for entry in witnessTable.entries where entry.kind == .method {
513+
if let method = entry.methodFunction,
514+
// A new witness table can still contain a generic function if the method couldn't be specialized for
515+
// some reason and an error has been printed. Exclude generic functions to not run into an assert later.
516+
!method.isGeneric
517+
{
518+
pushIfNotVisited(method)
519+
}
520+
}
521+
}
522+
448523
mutating func pushIfNotVisited(_ element: Function) {
449524
if pushedFunctions.insert(element).inserted {
450525
functions.append(element)

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ extension Context {
6161
}
6262
}
6363

64+
func lookupWitnessTable(for conformance: ProtocolConformance) -> WitnessTable? {
65+
return _bridged.lookupWitnessTable(conformance.bridged).witnessTable
66+
}
67+
68+
func lookupVTable(for classDecl: NominalTypeDecl) -> VTable? {
69+
return _bridged.lookupVTable(classDecl.bridged).vTable
70+
}
71+
72+
func lookupSpecializedVTable(for classType: Type) -> VTable? {
73+
return _bridged.lookupSpecializedVTable(classType.bridged).vTable
74+
}
75+
6476
func notifyNewFunction(function: Function, derivedFrom: Function) {
6577
_bridged.addFunctionToPassManagerWorklist(function.bridged, derivedFrom.bridged)
6678
}
@@ -221,7 +233,7 @@ extension MutatingContext {
221233
}
222234

223235
func getContextSubstitutionMap(for type: Type) -> SubstitutionMap {
224-
SubstitutionMap(_bridged.getContextSubstitutionMap(type.bridged))
236+
SubstitutionMap(bridged: _bridged.getContextSubstitutionMap(type.bridged))
225237
}
226238

227239
func notifyInstructionsChanged() {
@@ -327,13 +339,6 @@ struct FunctionPassContext : MutatingContext {
327339
return false
328340
}
329341

330-
func specializeVTable(for type: Type, in function: Function) -> VTable? {
331-
guard let vtablePtr = _bridged.specializeVTableForType(type.bridged, function.bridged) else {
332-
return nil
333-
}
334-
return VTable(bridged: BridgedVTable(vTable: vtablePtr))
335-
}
336-
337342
func specializeClassMethodInst(_ cm: ClassMethodInst) -> Bool {
338343
if _bridged.specializeClassMethodInst(cm.bridged) {
339344
notifyInstructionsChanged()

SwiftCompilerSources/Sources/Optimizer/PassManager/ModulePassContext.swift

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@ struct ModulePassContext : Context, CustomStringConvertible {
5454
}
5555

5656
struct VTableArray : BridgedRandomAccessCollection {
57-
fileprivate let bridged: BridgedPassContext.VTableArray
57+
fileprivate let bridgedCtxt: BridgedPassContext
5858

5959
var startIndex: Int { return 0 }
60-
var endIndex: Int { return bridged.count }
60+
var endIndex: Int { return bridgedCtxt.getNumVTables() }
6161

6262
subscript(_ index: Int) -> VTable {
6363
assert(index >= startIndex && index < endIndex)
64-
return VTable(bridged: BridgedVTable(vTable: bridged.base![index]))
64+
return VTable(bridged: bridgedCtxt.getVTable(index))
6565
}
6666
}
6767

@@ -101,9 +101,7 @@ struct ModulePassContext : Context, CustomStringConvertible {
101101
GlobalVariableList(first: _bridged.getFirstGlobalInModule().globalVar)
102102
}
103103

104-
var vTables: VTableArray {
105-
VTableArray(bridged: _bridged.getVTables())
106-
}
104+
var vTables: VTableArray { VTableArray(bridgedCtxt: _bridged) }
107105

108106
var witnessTables: WitnessTableList {
109107
WitnessTableList(first: _bridged.getFirstWitnessTableInModule().witnessTable)
@@ -131,6 +129,44 @@ struct ModulePassContext : Context, CustomStringConvertible {
131129
return function.isDefinition
132130
}
133131

132+
func specialize(function: Function, for substitutions: SubstitutionMap) -> Function? {
133+
return _bridged.specializeFunction(function.bridged, substitutions.bridged).function
134+
}
135+
136+
enum DeserializationMode {
137+
case allFunctions
138+
case onlySharedFunctions
139+
}
140+
141+
func deserializeAllCallees(of function: Function, mode: DeserializationMode) {
142+
_bridged.deserializeAllCallees(function.bridged, mode == .allFunctions ? true : false)
143+
}
144+
145+
@discardableResult
146+
func createWitnessTable(entries: [WitnessTable.Entry],
147+
conformance: ProtocolConformance,
148+
linkage: Linkage,
149+
serialized: Bool) -> WitnessTable
150+
{
151+
let bridgedEntries = entries.map { $0.bridged }
152+
let bridgedWitnessTable = bridgedEntries.withBridgedArrayRef {
153+
_bridged.createWitnessTable(linkage.bridged, serialized, conformance.bridged, $0)
154+
}
155+
return WitnessTable(bridged: bridgedWitnessTable)
156+
}
157+
158+
@discardableResult
159+
func createSpecializedVTable(entries: [VTable.Entry],
160+
for classType: Type,
161+
isSerialized: Bool) -> VTable
162+
{
163+
let bridgedEntries = entries.map { $0.bridged }
164+
let bridgedVTable = bridgedEntries.withBridgedArrayRef {
165+
_bridged.createSpecializedVTable(classType.bridged, isSerialized, $0)
166+
}
167+
return VTable(bridged: bridgedVTable)
168+
}
169+
134170
func createEmptyFunction(
135171
name: String,
136172
parameters: [ParameterInfo],
@@ -176,4 +212,8 @@ extension Function {
176212
func set(linkage: Linkage, _ context: ModulePassContext) {
177213
bridged.setLinkage(linkage.bridged)
178214
}
215+
216+
func set(isSerialized: Bool, _ context: ModulePassContext) {
217+
bridged.setIsSerialized(isSerialized)
218+
}
179219
}

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

SwiftCompilerSources/Sources/Optimizer/Utilities/DiagnosticEngine.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ASTBridging
1414

1515
import Basic
16+
import SIL
1617

1718
public typealias DiagID = BridgedDiagID
1819

@@ -29,6 +30,16 @@ extension Int: DiagnosticArgument {
2930
fn(BridgedDiagnosticArgument(self))
3031
}
3132
}
33+
extension Type: DiagnosticArgument {
34+
public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) {
35+
fn(bridged.asDiagnosticArgument())
36+
}
37+
}
38+
extension DeclRef: DiagnosticArgument {
39+
public func _withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) {
40+
fn(bridged.asDiagnosticArgument())
41+
}
42+
}
3243

3344
public struct DiagnosticFixIt {
3445
public let start: SourceLoc

0 commit comments

Comments
 (0)