Skip to content

Commit 5c8fe55

Browse files
committed
embedded: fix several issues with vtable specialization
* fix a false error if a derived class has different generic parameters than its base class * fix a similar problem if a non-generic class derives from a generic class * fix a compiler crash for calling a class method on a class metatype rdar://137692055
1 parent e0533e6 commit 5c8fe55

File tree

4 files changed

+148
-80
lines changed

4 files changed

+148
-80
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@ 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-
4136
worklist.addAllNonGenericFunctions(of: moduleContext)
4237
} else {
4338
worklist.addAllPerformanceAnnotatedFunctions(of: moduleContext)
@@ -102,15 +97,18 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
10297
// Embedded Swift specific transformations
10398
case let alloc as AllocRefInst:
10499
if context.options.enableEmbeddedSwift {
105-
specializeVTableAndAddEntriesToWorklist(for: alloc.type, in: function,
106-
errorLocation: alloc.location,
107-
moduleContext, &worklist)
100+
specializeVTable(forClassType: alloc.type, errorLocation: alloc.location, moduleContext) {
101+
worklist.pushIfNotVisited($0)
102+
}
108103
}
109104
case let metatype as MetatypeInst:
110105
if context.options.enableEmbeddedSwift {
111-
specializeVTableAndAddEntriesToWorklist(for: metatype.type, in: function,
112-
errorLocation: metatype.location,
113-
moduleContext, &worklist)
106+
let instanceType = metatype.type.loweredInstanceTypeOfMetatype(in: function)
107+
if instanceType.isClass {
108+
specializeVTable(forClassType: instanceType, errorLocation: metatype.location, moduleContext) {
109+
worklist.pushIfNotVisited($0)
110+
}
111+
}
114112
}
115113
case let classMethod as ClassMethodInst:
116114
if context.options.enableEmbeddedSwift {
@@ -166,29 +164,6 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
166164
}
167165
}
168166

169-
private func specializeVTableAndAddEntriesToWorklist(for type: Type, in function: Function,
170-
errorLocation: Location,
171-
_ moduleContext: ModulePassContext,
172-
_ worklist: inout FunctionWorklist) {
173-
let vTablesCountBefore = moduleContext.vTables.count
174-
175-
guard specializeVTable(forClassType: type, errorLocation: errorLocation, moduleContext) != nil else {
176-
return
177-
}
178-
179-
// More than one new vtable might have been created (superclasses), process them all
180-
let vTables = moduleContext.vTables
181-
for i in vTablesCountBefore ..< vTables.count {
182-
for entry in vTables[i].entries
183-
// A new vtable can still contain a generic function if the method couldn't be specialized for some reason
184-
// and an error has been printed. Exclude generic functions to not run into an assert later.
185-
where !entry.implementation.isGeneric
186-
{
187-
worklist.pushIfNotVisited(entry.implementation)
188-
}
189-
}
190-
}
191-
192167
private func inlineAndDevirtualize(apply: FullApplySite, alreadyInlinedFunctions: inout Set<PathFunctionTuple>,
193168
_ context: FunctionPassContext, _ simplifyCtxt: SimplifyContext) {
194169
// De-virtualization and inlining in/into a "serialized" function might create function references to functions

SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift

Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,65 +13,92 @@
1313
import AST
1414
import SIL
1515

16-
@discardableResult
1716
func specializeVTable(forClassType classType: Type,
1817
errorLocation: Location,
19-
_ context: ModulePassContext) -> VTable?
18+
_ context: ModulePassContext,
19+
notifyNewFunction: (Function) -> ())
2020
{
21-
guard let nominal = classType.nominal,
22-
let classDecl = nominal as? ClassDecl,
23-
classType.isGenericAtAnyLevel else
24-
{
25-
return nil
26-
}
21+
var specializer = VTableSpecializer(errorLocation: errorLocation, context)
22+
specializer.specializeVTable(forClassType: classType, notifyNewFunction)
23+
}
2724

28-
if context.lookupSpecializedVTable(for: classType) != nil {
29-
return nil
30-
}
25+
private struct VTableSpecializer {
26+
let errorLocation: Location
27+
let context: ModulePassContext
3128

32-
guard let origVTable = context.lookupVTable(for: classDecl) else {
33-
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType)
34-
return nil
35-
}
29+
// The type of the first class in the hierarchy which implements a method
30+
private var baseTypesOfMethods = Dictionary<Function, Type>()
3631

37-
let classContextSubs = classType.contextSubstitutionMap
32+
init(errorLocation: Location, _ context: ModulePassContext) {
33+
self.errorLocation = errorLocation
34+
self.context = context
35+
}
3836

39-
let newEntries = origVTable.entries.map { origEntry in
40-
if !origEntry.implementation.isGeneric {
41-
return origEntry
37+
mutating func specializeVTable(forClassType classType: Type, _ notifyNewFunction: (Function) -> ()) {
38+
// First handle super classes.
39+
// This is also required for non-generic classes - in case a superclass is generic, e.g.
40+
// `class Derived : Base<Int> {}` - for two reasons:
41+
// * A vtable of a derived class references the vtable of the super class. And of course the referenced
42+
// super-class vtable needs to be a specialized vtable.
43+
// * Even a non-generic derived class can contain generic methods of the base class in case a base-class
44+
// method is not overridden.
45+
//
46+
if let superClassTy = classType.superClassType {
47+
specializeVTable(forClassType: superClassTy, notifyNewFunction)
4248
}
43-
let methodSubs = classContextSubs.getMethodSubstitutions(for: origEntry.implementation)
4449

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+
let classDecl = classType.nominal! as! ClassDecl
51+
guard let origVTable = context.lookupVTable(for: classDecl) else {
52+
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType)
53+
return
5054
}
5155

52-
context.deserializeAllCallees(of: specializedMethod, mode: .allFunctions)
53-
specializedMethod.set(linkage: .public, context)
54-
specializedMethod.set(isSerialized: false, context)
56+
for entry in origVTable.entries {
57+
if baseTypesOfMethods[entry.implementation] == nil {
58+
baseTypesOfMethods[entry.implementation] = classType
59+
}
60+
}
5561

56-
return VTable.Entry(kind: origEntry.kind, isNonOverridden: origEntry.isNonOverridden,
57-
methodDecl: origEntry.methodDecl, implementation: specializedMethod)
62+
if classType.isGenericAtAnyLevel {
63+
if context.lookupSpecializedVTable(for: classType) != nil {
64+
// We already specialized the vtable
65+
return
66+
}
67+
let newEntries = specializeEntries(of: origVTable, notifyNewFunction)
68+
context.createSpecializedVTable(entries: newEntries, for: classType, isSerialized: false)
69+
} else {
70+
if !origVTable.entries.contains(where: { $0.implementation.isGeneric }) {
71+
// The vtable (of the non-generic class) doesn't contain any generic functions (from a generic base class).
72+
return
73+
}
74+
let newEntries = specializeEntries(of: origVTable, notifyNewFunction)
75+
context.replaceVTableEntries(of: origVTable, with: newEntries)
76+
}
5877
}
5978

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-
}
79+
private func specializeEntries(of vTable: VTable, _ notifyNewFunction: (Function) -> ()) -> [VTable.Entry] {
80+
return vTable.entries.compactMap { entry in
81+
if !entry.implementation.isGeneric {
82+
return entry
83+
}
84+
let baseType = baseTypesOfMethods[entry.implementation]!
85+
let classContextSubs = baseType.contextSubstitutionMap
86+
let methodSubs = classContextSubs.getMethodSubstitutions(for: entry.implementation)
87+
88+
guard !methodSubs.conformances.contains(where: {!$0.isValid}),
89+
let specializedMethod = context.specialize(function: entry.implementation, for: methodSubs) else
90+
{
91+
context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function)
92+
return nil
93+
}
94+
notifyNewFunction(specializedMethod)
95+
96+
context.deserializeAllCallees(of: specializedMethod, mode: .allFunctions)
97+
specializedMethod.set(linkage: .public, context)
98+
specializedMethod.set(isSerialized: false, context)
6699

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.isGenericAtAnyLevel
73-
{
74-
specializeVTable(forClassType: superClassTy, errorLocation: vtable.class.location, moduleContext)
100+
return VTable.Entry(kind: entry.kind, isNonOverridden: entry.isNonOverridden,
101+
methodDecl: entry.methodDecl, implementation: specializedMethod)
75102
}
76103
}
77104
}

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,9 @@ bool swift::specializeClassMethodInst(ClassMethodInst *cm) {
23242324

23252325
SILValue instance = cm->getOperand();
23262326
SILType classTy = instance->getType();
2327+
if (classTy.is<MetatypeType>())
2328+
classTy = classTy.getLoweredInstanceTypeOfMetatype(cm->getFunction());
2329+
23272330
CanType astType = classTy.getASTType();
23282331
if (!astType->isSpecialized())
23292332
return false;

test/embedded/generic-classes2.swift

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,77 @@ func callee<T>(_ t: T.Type) {
1818

1919
public func test() {
2020
callee(Int.self)
21+
print("OK!")
22+
}
23+
24+
public class Base<T: BinaryInteger> {
25+
public func foo(_ t: T) {
26+
print(t)
27+
}
28+
}
29+
30+
public class Derived: Base<Int> {
31+
}
32+
33+
34+
func testNonGenericDerived(_ d: Derived) {
35+
d.foo(27)
36+
}
37+
38+
public protocol P {
39+
func mask()
40+
}
41+
42+
public struct ConcreteP: P {
43+
var x: Int
44+
public func mask() {
45+
print(x)
46+
}
47+
}
48+
49+
class BaseClass<SomeP: P> {
50+
func foo(_ p: SomeP) {
51+
p.mask()
52+
}
53+
}
54+
55+
final class SubClass<T, SomeP: P>: BaseClass<SomeP> {
56+
}
57+
58+
public class BaseWithClassMethod<T: BinaryInteger> {
59+
class func foo(_ t: T) {
60+
print("BaseWithClassMethod")
61+
print(t)
62+
}
63+
}
64+
65+
public class DerivedWithClassMethod<T: BinaryInteger> : BaseWithClassMethod<T> {
66+
override class func foo(_ t: T) {
67+
print("DerivedWithClassMethod")
68+
print(t)
69+
}
70+
}
71+
72+
func testClassMethod() -> BaseWithClassMethod<Int>.Type {
73+
return DerivedWithClassMethod<Int>.self
2174
}
2275

2376
@main
2477
struct Main {
2578
static func main() {
79+
// CHECK: OK!
2680
test()
27-
print("OK!")
81+
82+
// CHECK: 27
83+
testNonGenericDerived(Derived())
84+
85+
let x = SubClass<Int, ConcreteP>()
86+
// CHECK: 42
87+
x.foo(ConcreteP(x: 42))
88+
89+
let t = testClassMethod()
90+
// CHECK: 123
91+
t.foo(123)
2892
}
2993
}
3094

31-
// CHECK: OK!

0 commit comments

Comments
 (0)