Skip to content

Commit 1a9fe1f

Browse files
committed
SILGen: Use SILVTableVisitor instead of bespoke member traversal
This changes the order in which declarations are emitted. It also means we no longer emit a vtable entry for the materializeForSet of dynamic storage. Neither of these are intended to have any functional effect.
1 parent b42675c commit 1a9fe1f

13 files changed

+176
-224
lines changed

lib/SIL/SILDeclRef.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -772,12 +772,14 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const {
772772
// @NSManaged property, then it won't be in the vtable.
773773
if (overridden.getDecl()->hasClangNode())
774774
return SILDeclRef();
775-
if (overridden.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
775+
if (overridden.getDecl()->isDynamic())
776776
return SILDeclRef();
777777
if (auto *ovFD = dyn_cast<FuncDecl>(overridden.getDecl()))
778778
if (auto *asd = ovFD->getAccessorStorageDecl()) {
779779
if (asd->hasClangNode())
780780
return SILDeclRef();
781+
if (asd->isDynamic())
782+
return SILDeclRef();
781783
}
782784

783785
// If we overrode a decl from an extension, it won't be in a vtable

lib/SILGen/SILGenType.cpp

+61-133
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/ProtocolConformance.h"
2525
#include "swift/AST/TypeMemberVisitor.h"
2626
#include "swift/SIL/SILArgument.h"
27+
#include "swift/SIL/SILVTableVisitor.h"
2728
#include "swift/SIL/TypeLowering.h"
2829

2930
using namespace swift;
@@ -146,154 +147,83 @@ bool SILGenModule::requiresObjCMethodEntryPoint(ConstructorDecl *constructor) {
146147
namespace {
147148

148149
/// An ASTVisitor for populating SILVTable entries from ClassDecl members.
149-
class SILGenVTable : public Lowering::ASTVisitor<SILGenVTable> {
150+
class SILGenVTable : public SILVTableVisitor<SILGenVTable> {
150151
public:
151152
SILGenModule &SGM;
152153
ClassDecl *theClass;
153154
std::vector<SILVTable::Entry> vtableEntries;
154155

155156
SILGenVTable(SILGenModule &SGM, ClassDecl *theClass)
156157
: SGM(SGM), theClass(theClass)
157-
{
158-
// Populate the superclass members, if any.
159-
Type super = theClass->getSuperclass();
160-
if (super && super->getClassOrBoundGenericClass())
161-
visitAncestor(super->getClassOrBoundGenericClass());
162-
}
158+
{ }
163159

164-
~SILGenVTable() {
165-
// Create the vtable.
166-
SILVTable::create(SGM.M, theClass, vtableEntries);
167-
}
160+
void emitVTable() {
161+
// Populate the superclass members, if any.
162+
visitAncestor(theClass);
168163

169-
void visitAncestor(ClassDecl *ancestor) {
170-
// Recursively visit all our ancestors.
171-
Type super = ancestor->getSuperclass();
172-
if (super && super->getClassOrBoundGenericClass())
173-
visitAncestor(super->getClassOrBoundGenericClass());
174-
175-
// Only visit the members for a class defined natively.
176-
if (!ancestor->hasClangNode()) {
177-
for (auto member : ancestor->getMembers())
178-
visit(member);
179-
}
180-
}
164+
auto *dtor = theClass->getDestructor();
165+
assert(dtor);
181166

182-
// Add an entry to the vtable.
183-
void addEntry(SILDeclRef member) {
184-
/// Get the function to reference from the vtable.
185-
auto getVtableEntry = [&](SILDeclRef entry) -> SILVTable::Entry {
186-
// If the member is dynamic, reference its dynamic dispatch thunk so that
187-
// it will be redispatched, funneling the method call through the runtime
188-
// hook point.
189-
// TODO: Dynamic thunks could conceivably require reabstraction too.
190-
if (member.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
191-
return { entry,
192-
SGM.getDynamicThunk(member, SGM.Types.getConstantInfo(member)),
193-
SILLinkage::Public };
194-
195-
// The derived method may require thunking to match up to the ABI of the
196-
// base method.
197-
SILLinkage implLinkage;
198-
SILFunction *implFn = SGM.emitVTableMethod(member, entry, implLinkage);
199-
return { entry, implFn, stripExternalFromLinkage(implLinkage) };
200-
};
201-
202-
// Try to find an overridden entry.
203-
// NB: Mutates vtableEntries in-place
204-
// FIXME: O(n^2)
205-
if (auto overridden = member.getNextOverriddenVTableEntry()) {
206-
for (SILVTable::Entry &entry : vtableEntries) {
207-
SILDeclRef ref = overridden;
208-
209-
do {
210-
// Replace the overridden member.
211-
if (entry.Method == ref) {
212-
// The entry is keyed by the least derived method.
213-
entry = getVtableEntry(ref);
214-
return;
215-
}
216-
} while ((ref = ref.getOverridden()));
217-
}
218-
llvm_unreachable("no overridden vtable entry?!");
219-
}
167+
// Add the deallocating destructor to the vtable just for the purpose
168+
// that it is referenced and cannot be eliminated by dead function removal.
169+
// In reality, the deallocating destructor is referenced directly from
170+
// the HeapMetadata for the class.
171+
if (!dtor->hasClangNode())
172+
addMethod(SILDeclRef(dtor, SILDeclRef::Kind::Deallocator));
220173

221-
// If this is a final member and isn't overriding something, we don't need
222-
// to add it to the vtable.
223-
if (member.getDecl()->isFinal())
224-
return;
225-
// If this is dynamic and isn't overriding a non-dynamic method, it'll
226-
// always be accessed by objc_msgSend, so we don't need to add it to the
227-
// vtable.
228-
if (member.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
229-
return;
174+
if (SGM.requiresIVarDestroyer(theClass))
175+
addMethod(SILDeclRef(theClass, SILDeclRef::Kind::IVarDestroyer));
230176

231-
// Otherwise, introduce a new vtable entry.
232-
vtableEntries.push_back(getVtableEntry(member));
177+
// Create the vtable.
178+
SILVTable::create(SGM.M, theClass, vtableEntries);
233179
}
234180

235-
// Default for members that don't require vtable entries.
236-
void visitDecl(Decl*) {}
237-
238-
void visitFuncDecl(FuncDecl *fd) {
239-
// ObjC decls don't go in vtables.
240-
if (fd->hasClangNode())
241-
return;
242-
243-
// Observers don't get separate vtable entries.
244-
if (fd->isObservingAccessor())
245-
return;
181+
void visitAncestor(ClassDecl *ancestor) {
182+
auto superTy = ancestor->getSuperclass();
183+
if (superTy)
184+
visitAncestor(superTy->getClassOrBoundGenericClass());
246185

247-
addEntry(SILDeclRef(fd));
186+
addVTableEntries(ancestor);
248187
}
249188

250-
void visitConstructorDecl(ConstructorDecl *cd) {
251-
// Stub constructors don't get an entry, unless they were synthesized to
252-
// override a non-required designated initializer in the superclass.
253-
if (cd->hasStubImplementation() && !cd->getOverriddenDecl())
254-
return;
255-
256-
// Required constructors (or overrides thereof) have their allocating entry
257-
// point in the vtable.
258-
bool isRequired = false;
259-
auto override = cd;
260-
while (override) {
261-
if (override->isRequired()) {
262-
isRequired = true;
263-
break;
264-
}
265-
override = override->getOverriddenDecl();
189+
SILVTable::Entry getVTableEntry(SILDeclRef baseRef, SILDeclRef declRef) {
190+
// If the member is dynamic, reference its dynamic dispatch thunk so that
191+
// it will be redispatched, funneling the method call through the runtime
192+
// hook point.
193+
// TODO: Dynamic thunks could conceivably require reabstraction too.
194+
if (declRef.getDecl()->isDynamic()) {
195+
return { baseRef,
196+
SGM.getDynamicThunk(declRef, SGM.Types.getConstantInfo(declRef)),
197+
SILLinkage::Public };
266198
}
267-
if (isRequired) {
268-
addEntry(SILDeclRef(cd, SILDeclRef::Kind::Allocator));
269-
}
270-
271-
// All constructors have their initializing constructor in the
272-
// vtable, which can be used by a convenience initializer.
273-
addEntry(SILDeclRef(cd, SILDeclRef::Kind::Initializer));
274-
}
275199

276-
void visitVarDecl(VarDecl *vd) {
277-
// Note: dynamically-dispatched properties have their getter and setter
278-
// added to the vtable when they are visited.
200+
// The derived method may require thunking to match up to the ABI of the
201+
// base method.
202+
SILLinkage implLinkage;
203+
SILFunction *implFn = SGM.emitVTableMethod(declRef, baseRef, implLinkage);
204+
return { baseRef, implFn, stripExternalFromLinkage(implLinkage) };
279205
}
280206

281-
void visitDestructorDecl(DestructorDecl *dd) {
282-
if (dd->getParent()->getAsClassOrClassExtensionContext() == theClass) {
283-
// Add the deallocating destructor to the vtable just for the purpose
284-
// that it is referenced and cannot be eliminated by dead function removal.
285-
// In reality, the deallocating destructor is referenced directly from
286-
// the HeapMetadata for the class.
287-
addEntry(SILDeclRef(dd, SILDeclRef::Kind::Deallocator));
288-
289-
if (SGM.requiresIVarDestroyer(theClass))
290-
addEntry(SILDeclRef(theClass, SILDeclRef::Kind::IVarDestroyer));
207+
// Try to find an overridden entry.
208+
void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {
209+
// NB: Mutates vtableEntries in-place
210+
// FIXME: O(n^2)
211+
for (SILVTable::Entry &entry : vtableEntries) {
212+
// Replace the overridden member.
213+
if (entry.Method == baseRef) {
214+
// The entry is keyed by the least derived method.
215+
entry = getVTableEntry(baseRef, declRef);
216+
return;
217+
}
291218
}
219+
baseRef.dump();
220+
declRef.dump();
221+
llvm_unreachable("no overridden vtable entry?!");
292222
}
293223

294-
void visitSubscriptDecl(SubscriptDecl *sd) {
295-
// Note: dynamically-dispatched properties have their getter and setter
296-
// added to the vtable when they are visited.
224+
// Add an entry to the vtable.
225+
void addMethod(SILDeclRef member) {
226+
vtableEntries.push_back(getVTableEntry(member, member));
297227
}
298228
};
299229

@@ -319,24 +249,22 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
319249
public:
320250
SILGenModule &SGM;
321251
NominalTypeDecl *theType;
322-
Optional<SILGenVTable> genVTable;
323252

324253
SILGenType(SILGenModule &SGM, NominalTypeDecl *theType)
325254
: SGM(SGM), theType(theType) {}
326255

327256
/// Emit SIL functions for all the members of the type.
328257
void emitType() {
329-
// Start building a vtable if this is a class.
330-
if (auto theClass = dyn_cast<ClassDecl>(theType))
331-
genVTable.emplace(SGM, theClass);
332-
333-
for (Decl *member : theType->getMembers()) {
334-
if (genVTable)
335-
genVTable->visit(member);
336-
258+
for (Decl *member : theType->getMembers())
337259
visit(member);
260+
261+
// Build a vtable if this is a class.
262+
if (auto theClass = dyn_cast<ClassDecl>(theType)) {
263+
SILGenVTable genVTable(SGM, theClass);
264+
genVTable.emitVTable();
338265
}
339266

267+
// Build a default witness table if this is a protocol.
340268
if (auto protocol = dyn_cast<ProtocolDecl>(theType)) {
341269
if (!protocol->isObjC())
342270
SGM.emitDefaultWitnessTable(protocol);

test/DebugInfo/initializer.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ protocol Named {
66
var name : String { get }
77
}
88

9-
// initializer.Person.init (initializer.Person.Type)() -> initializer.Person
10-
// CHECK: define hidden {{.*}}%T11initializer6PersonC* @_T011initializer6PersonCACycfc(%T11initializer6PersonC*{{.*}}) {{.*}} {
11-
129
// initializer.Person.__allocating_init (initializer.Person.Type)() -> initializer.Person
1310
// CHECK: define hidden {{.*}}%T11initializer6PersonC* @_T011initializer6PersonCACycfC(%swift.type*{{.*}}) {{.*}} {
1411
// CHECK: call {{.*}}%T11initializer6PersonC* @_T011initializer6PersonCACycfc(%T11initializer6PersonC* {{.*}}%3), !dbg ![[ALLOCATING_INIT:.*]]
1512

13+
// initializer.Person.init (initializer.Person.Type)() -> initializer.Person
14+
// CHECK: define hidden {{.*}}%T11initializer6PersonC* @_T011initializer6PersonCACycfc(%T11initializer6PersonC*{{.*}}) {{.*}} {
15+
1616
// CHECK-DAG: ![[ALLOCATING_INIT]] = !DILocation(line: 0, scope
1717
class Person : Named {
1818
var name : String { get { return "No Name" } }

test/SILGen/SILDeclRef.swift

+9-9
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,16 @@ public func testBase(b: Base) -> Int32 {
5555
// Check that vtables and witness tables contain SILDeclRefs with signatures.
5656

5757
// CHECK: sil_vtable Base {
58-
// CHECK: #Base.foo!1: (Base) -> () -> Int32 : _T010SILDeclRef4BaseC3foos5Int32VyF // Base.foo() -> Int32
59-
// CHECK: #Base.foo!1: (Base) -> (Int32) -> () : _T010SILDeclRef4BaseC3fooys5Int32V1n_tF // Base.foo(n : Int32) -> ()
60-
// CHECK: #Base.foo!1: (Base) -> (Float) -> Int32 : _T010SILDeclRef4BaseC3foos5Int32VSf1f_tF // Base.foo(f : Float) -> Int32
61-
// CHECK: #Base.deinit!deallocator: _T010SILDeclRef4BaseCfD // Base.__deallocating_deinit
62-
// CHECK: #Base.init!initializer.1: (Base.Type) -> () -> Base : _T010SILDeclRef4BaseCACycfc // Base.init() -> Base
63-
// CHECK: }
58+
// CHECK-NEXT: #Base.foo!1: (Base) -> () -> Int32 : _T010SILDeclRef4BaseC3foos5Int32VyF // Base.foo() -> Int32
59+
// CHECK-NEXT: #Base.foo!1: (Base) -> (Int32) -> () : _T010SILDeclRef4BaseC3fooys5Int32V1n_tF // Base.foo(n : Int32) -> ()
60+
// CHECK-NEXT: #Base.foo!1: (Base) -> (Float) -> Int32 : _T010SILDeclRef4BaseC3foos5Int32VSf1f_tF // Base.foo(f : Float) -> Int32
61+
// CHECK-NEXT: #Base.init!initializer.1: (Base.Type) -> () -> Base : _T010SILDeclRef4BaseCACycfc // Base.init() -> Base
62+
// CHECK-NEXT: #Base.deinit!deallocator: _T010SILDeclRef4BaseCfD // Base.__deallocating_deinit
63+
// CHECK-NEXT: }
6464

6565
// CHECK:sil_witness_table [fragile] Base: P module SILDeclRef {
66-
// CHECK: method #P.foo!1: <Self where Self : P> (Self) -> () -> Int32 : @_T010SILDeclRef4BaseCAA1PA2aDP3foos5Int32VyFTW // protocol witness for P.foo() -> Int32 in conformance Base
67-
// CHECK: method #P.foo!1: <Self where Self : P> (Self) -> (Int32) -> () : @_T010SILDeclRef4BaseCAA1PA2aDP3fooys5Int32V1n_tFTW // protocol witness for P.foo(n : Int32) -> () in conformance Base
68-
// CHECK: }
66+
// CHECK-NEXT: method #P.foo!1: <Self where Self : P> (Self) -> () -> Int32 : @_T010SILDeclRef4BaseCAA1PA2aDP3foos5Int32VyFTW // protocol witness for P.foo() -> Int32 in conformance Base
67+
// CHECK-NEXT: method #P.foo!1: <Self where Self : P> (Self) -> (Int32) -> () : @_T010SILDeclRef4BaseCAA1PA2aDP3fooys5Int32V1n_tFTW // protocol witness for P.foo(n : Int32) -> () in conformance Base
68+
// CHECK-NEXT: }
6969

7070

test/SILGen/complete_object_init.swift

+8-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
struct X { }
44

55
class A {
6-
// CHECK-LABEL: sil hidden @_T020complete_object_init1AC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (@owned A) -> @owned A
6+
// CHECK-LABEL: sil hidden @_T020complete_object_init1AC{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@thick A.Type) -> @owned A
7+
// CHECK: bb0([[SELF_META:%[0-9]+]] : $@thick A.Type):
8+
// CHECK: [[SELF:%[0-9]+]] = alloc_ref_dynamic [[SELF_META]] : $@thick A.Type, $A
9+
// CHECK: [[OTHER_INIT:%[0-9]+]] = function_ref @_T020complete_object_init1AC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (@owned A) -> @owned A
10+
// CHECK: [[RESULT:%[0-9]+]] = apply [[OTHER_INIT]]([[SELF]]) : $@convention(method) (@owned A) -> @owned A
11+
// CHECK: return [[RESULT]] : $A
12+
13+
// CHECK-LABEL: sil hidden @_T020complete_object_init1AC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (@owned A) -> @owned A
714
// CHECK: bb0([[SELF_PARAM:%[0-9]+]] : $A):
815
// CHECK: [[SELF_BOX:%[0-9]+]] = alloc_box ${ var A }
916
// CHECK: [[PB:%.*]] = project_box [[SELF_BOX]]
@@ -20,13 +27,7 @@ class A {
2027
// CHECK: destroy_value [[SELF_BOX]] : ${ var A }
2128
// CHECK: return [[RESULT]] : $A
2229

23-
// CHECK-LABEL: sil hidden @_T020complete_object_init1AC{{[_0-9a-zA-Z]*}}fC : $@convention(method) (@thick A.Type) -> @owned A
2430
convenience init() {
25-
// CHECK: bb0([[SELF_META:%[0-9]+]] : $@thick A.Type):
26-
// CHECK: [[SELF:%[0-9]+]] = alloc_ref_dynamic [[SELF_META]] : $@thick A.Type, $A
27-
// CHECK: [[OTHER_INIT:%[0-9]+]] = function_ref @_T020complete_object_init1AC{{[_0-9a-zA-Z]*}}fc : $@convention(method) (@owned A) -> @owned A
28-
// CHECK: [[RESULT:%[0-9]+]] = apply [[OTHER_INIT]]([[SELF]]) : $@convention(method) (@owned A) -> @owned A
29-
// CHECK: return [[RESULT]] : $A
3031
self.init(x: X())
3132
}
3233

test/SILGen/dynamic.swift

+22-3
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,21 @@ public class Sub : Base {
448448
override var x: Bool { return false || super.x }
449449
}
450450

451+
public class BaseExt : NSObject {}
452+
453+
extension BaseExt {
454+
public var count: Int {
455+
return 0
456+
}
457+
}
458+
459+
public class SubExt : BaseExt {
460+
public override var count: Int {
461+
return 1
462+
}
463+
}
464+
465+
// Vtable contains entries for native and @objc methods, but not dynamic ones
451466
// CHECK-LABEL: sil_vtable Foo {
452467
// CHECK-NEXT: #Foo.init!initializer.1: {{.*}} : _T07dynamic3FooCACSi6native_tcfc
453468
// CHECK-NEXT: #Foo.nativeMethod!1: {{.*}} : _T07dynamic3FooC12nativeMethodyyF
@@ -465,13 +480,17 @@ public class Sub : Base {
465480
// CHECK-NEXT: #Foo.subscript!getter.1: {{.*}} : _T07dynamic3FooC9subscriptSis9AnyObject_p4objc_tcfg // dynamic.Foo.subscript.getter : (objc : Swift.AnyObject) -> Swift.Int
466481
// CHECK-NEXT: #Foo.subscript!setter.1: {{.*}} : _T07dynamic3FooC9subscriptSis9AnyObject_p4objc_tcfs // dynamic.Foo.subscript.setter : (objc : Swift.AnyObject) -> Swift.Int
467482
// CHECK-NEXT: #Foo.subscript!materializeForSet
468-
// CHECK-NEXT: #Foo.dynamicProp!materializeForSet.1: (Foo) -> (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer) -> (Builtin.RawPointer, Builtin.RawPointer?) : _T07dynamic3FooC0A4PropSifm // dynamic.Foo.dynamicProp.materializeForSet : Swift.Int
469-
// CHECK-NEXT: #Foo.subscript!materializeForSet.1: (Foo) -> (Builtin.RawPointer, inout Builtin.UnsafeValueBuffer, Int) -> (Builtin.RawPointer, Builtin.RawPointer?) : _T07dynamic3FooC9subscriptS2iAA_tcfm // dynamic.Foo.subscript.materializeForSet : (dynamic : Swift.Int) -> Swift.Int
470483
// CHECK-NEXT: #Foo.overriddenByDynamic!1: {{.*}} : _T07dynamic3FooC19overriddenByDynamic{{[_0-9a-zA-Z]*}}
471484
// CHECK-NEXT: #Foo.deinit!deallocator: {{.*}}
472485
// CHECK-NEXT: }
473486

474487
// Vtable uses a dynamic thunk for dynamic overrides
475488
// CHECK-LABEL: sil_vtable Subclass {
476-
// CHECK-LABEL: #Foo.overriddenByDynamic!1: {{.*}} : public _T07dynamic8SubclassC19overriddenByDynamic{{[_0-9a-zA-Z]*}}FTD
489+
// CHECK: #Foo.overriddenByDynamic!1: {{.*}} : public _T07dynamic8SubclassC19overriddenByDynamic{{[_0-9a-zA-Z]*}}FTD
490+
// CHECK: }
477491

492+
// No vtable entry for override of @objc extension property
493+
// CHECK-LABEL: sil_vtable SubExt {
494+
// CHECK-NEXT: #BaseExt.init!initializer.1: (BaseExt.Type) -> () -> BaseExt : _T07dynamic6SubExtCACycfc // dynamic.SubExt.init () -> dynamic.SubExt
495+
// CHECK-NEXT: #SubExt.deinit!deallocator: _T07dynamic6SubExtCfD // dynamic.SubExt.__deallocating_deinit
496+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)