Skip to content

Commit 04c8f0d

Browse files
committed
IRGen: Don't reify internal vtable entries that are marked overridden.
Private and internal classes shouldn't have ABI constraints on their concrete vtable layout, so if methods don't have overrides in practice, we can elide their vtable entries.
1 parent db9cf4c commit 04c8f0d

File tree

17 files changed

+221
-55
lines changed

17 files changed

+221
-55
lines changed

include/swift/SIL/SILModule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ class SILModule {
595595
bool deserializeLazily=true);
596596

597597
/// Look up the VTable mapped to the given ClassDecl. Returns null on failure.
598-
SILVTable *lookUpVTable(const ClassDecl *C);
598+
SILVTable *lookUpVTable(const ClassDecl *C, bool deserializeLazily = true);
599599

600600
/// Attempt to lookup the function corresponding to \p Member in the class
601601
/// hierarchy of \p Class.

include/swift/SIL/SILVTable.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,12 @@ class SILVTableEntry {
4949
/// The function which implements the method for the class and the entry kind.
5050
llvm::PointerIntPair<SILFunction *, 2, unsigned> ImplAndKind;
5151

52+
bool IsNonOverridden;
53+
5254
public:
5355
enum Kind : uint8_t {
5456
/// The vtable entry is for a method defined directly in this class.
5557
Normal,
56-
/// The vtable entry is for a method defined directly in this class, and is
57-
/// never overridden by subclasses.
58-
NormalNonOverridden,
5958
/// The vtable entry is inherited from the superclass.
6059
Inherited,
6160
/// The vtable entry is inherited from the superclass, and overridden
@@ -67,14 +66,19 @@ class SILVTableEntry {
6766

6867
SILVTableEntry() : ImplAndKind(nullptr, Kind::Normal) {}
6968

70-
SILVTableEntry(SILDeclRef Method, SILFunction *Implementation, Kind TheKind)
71-
: Method(Method), ImplAndKind(Implementation, TheKind) {}
69+
SILVTableEntry(SILDeclRef Method, SILFunction *Implementation, Kind TheKind,
70+
bool NonOverridden)
71+
: Method(Method), ImplAndKind(Implementation, TheKind),
72+
IsNonOverridden(NonOverridden) {}
7273

7374
SILDeclRef getMethod() const { return Method; }
7475

7576
Kind getKind() const { return Kind(ImplAndKind.getInt()); }
7677
void setKind(Kind kind) { ImplAndKind.setInt(kind); }
7778

79+
bool isNonOverridden() const { return IsNonOverridden; }
80+
void setNonOverridden(bool value) { IsNonOverridden = value; }
81+
7882
SILFunction *getImplementation() const { return ImplAndKind.getPointer(); }
7983
};
8084

lib/IRGen/ClassMetadataVisitor.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "swift/AST/ASTContext.h"
2222
#include "swift/AST/SubstitutionMap.h"
2323
#include "swift/SIL/SILDeclRef.h"
24+
#include "swift/SIL/SILModule.h"
25+
#include "swift/SIL/SILVTable.h"
2426
#include "swift/SIL/SILVTableVisitor.h"
2527
#include "IRGen.h"
2628
#include "NominalMetadataVisitor.h"
@@ -30,6 +32,15 @@ namespace irgen {
3032

3133
class IRGenModule;
3234

35+
/// Returns true if the given SILVTable entry needs to be reified as a runtime
36+
/// vtable entry.
37+
///
38+
/// Methods that have no overrides, and no ABI constraints that require a
39+
/// vtable to be present, can be left out of the runtime vtable for classes.
40+
bool methodRequiresReifiedVTableEntry(IRGenModule &IGM,
41+
const SILVTable *vtable,
42+
SILDeclRef method);
43+
3344
/// A CRTP class for laying out class metadata. Note that this does
3445
/// *not* handle the metadata template stuff.
3546
template <class Impl> class ClassMetadataVisitor
@@ -43,9 +54,13 @@ template <class Impl> class ClassMetadataVisitor
4354

4455
/// The most-derived class.
4556
ClassDecl *const Target;
57+
58+
/// SILVTable entry for the class.
59+
const SILVTable *VTable;
4660

4761
ClassMetadataVisitor(IRGenModule &IGM, ClassDecl *target)
48-
: super(IGM), Target(target) {}
62+
: super(IGM), Target(target),
63+
VTable(IGM.getSILModule().lookUpVTable(target, /*deserialize*/ false)) {}
4964

5065
public:
5166
void layout() {
@@ -152,8 +167,15 @@ template <class Impl> class ClassMetadataVisitor
152167
// Add vtable entries.
153168
asImpl().addVTableEntries(theClass);
154169
}
155-
156-
private:
170+
171+
friend SILVTableVisitor<Impl>;
172+
void addMethod(SILDeclRef declRef) {
173+
// Does this method require a reified runtime vtable entry?
174+
if (methodRequiresReifiedVTableEntry(IGM, VTable, declRef)) {
175+
asImpl().addReifiedVTableEntry(declRef);
176+
}
177+
}
178+
157179
void addFieldEntries(Decl *field) {
158180
if (auto var = dyn_cast<VarDecl>(field)) {
159181
asImpl().addFieldOffset(var);
@@ -194,7 +216,7 @@ class ClassMetadataScanner : public ClassMetadataVisitor<Impl> {
194216
void addClassAddressPoint() { addInt32(); }
195217
void addClassCacheData() { addPointer(); addPointer(); }
196218
void addClassDataPointer() { addPointer(); }
197-
void addMethod(SILDeclRef declRef) {
219+
void addReifiedVTableEntry(SILDeclRef declRef) {
198220
addPointer();
199221
}
200222
void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {}

lib/IRGen/GenMeta.cpp

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,7 +1437,13 @@ namespace {
14371437
}
14381438

14391439
void addMethod(SILDeclRef fn) {
1440-
VTableEntries.push_back(fn);
1440+
if (methodRequiresReifiedVTableEntry(IGM, VTable, fn)) {
1441+
VTableEntries.push_back(fn);
1442+
} else if (getType()->getEffectiveAccess() >= AccessLevel::Public) {
1443+
// Emit a stub method descriptor and lookup function for nonoverridden
1444+
// methods so that resilient code sequences can still use them.
1445+
emitNonoverriddenMethod(fn);
1446+
}
14411447
}
14421448

14431449
void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {
@@ -1521,9 +1527,6 @@ namespace {
15211527
}
15221528

15231529
void addVTable() {
1524-
if (VTableEntries.empty())
1525-
return;
1526-
15271530
LLVM_DEBUG(
15281531
llvm::dbgs() << "VTable entries for " << getType()->getName() << ":\n";
15291532
for (auto entry : VTableEntries) {
@@ -1533,6 +1536,9 @@ namespace {
15331536
}
15341537
);
15351538

1539+
if (VTableEntries.empty())
1540+
return;
1541+
15361542
// Only emit a method lookup function if the class is resilient
15371543
// and has a non-empty vtable.
15381544
if (IGM.hasResilientMetadata(getType(), ResilienceExpansion::Minimal))
@@ -1595,8 +1601,25 @@ namespace {
15951601
IGM.emitDispatchThunk(fn);
15961602
}
15971603
}
1604+
1605+
void emitNonoverriddenMethod(SILDeclRef fn) {
1606+
// TODO: Emit a freestanding method descriptor structure, and a method
1607+
// lookup function, to present the ABI of an overridable method even
1608+
// though the method has no real overrides currently.
1609+
}
15981610

15991611
void addOverrideTable() {
1612+
LLVM_DEBUG(
1613+
llvm::dbgs() << "Override Table entries for " << getType()->getName() << ":\n";
1614+
for (auto entry : OverrideTableEntries) {
1615+
llvm::dbgs() << " ";
1616+
entry.first.print(llvm::dbgs());
1617+
llvm::dbgs() << " -> ";
1618+
entry.second.print(llvm::dbgs());
1619+
llvm::dbgs() << '\n';
1620+
}
1621+
);
1622+
16001623
if (OverrideTableEntries.empty())
16011624
return;
16021625

@@ -2702,12 +2725,12 @@ namespace {
27022725
using super::asImpl;
27032726
using super::IGM;
27042727
using super::Target;
2728+
using super::VTable;
27052729

27062730
ConstantStructBuilder &B;
27072731

27082732
const ClassLayout &FieldLayout;
27092733
const ClassMetadataLayout &MetadataLayout;
2710-
const SILVTable *VTable;
27112734

27122735
Size AddressPoint;
27132736

@@ -2717,8 +2740,7 @@ namespace {
27172740
const ClassLayout &fieldLayout)
27182741
: super(IGM, theClass), B(builder),
27192742
FieldLayout(fieldLayout),
2720-
MetadataLayout(IGM.getClassMetadataLayout(theClass)),
2721-
VTable(IGM.getSILModule().lookUpVTable(theClass)) {}
2743+
MetadataLayout(IGM.getClassMetadataLayout(theClass)) {}
27222744

27232745
public:
27242746
SILType getLoweredType() {
@@ -2854,7 +2876,7 @@ namespace {
28542876
PointerAuthEntity::Special::HeapDestructor);
28552877
} else {
28562878
// In case the optimizer removed the function. See comment in
2857-
// addMethod().
2879+
// addReifiedVTableEntry().
28582880
B.addNullPointer(IGM.FunctionPtrTy);
28592881
}
28602882
}
@@ -2973,7 +2995,7 @@ namespace {
29732995
B.add(data);
29742996
}
29752997

2976-
void addMethod(SILDeclRef fn) {
2998+
void addReifiedVTableEntry(SILDeclRef fn) {
29772999
// Find the vtable entry.
29783000
assert(VTable && "no vtable?!");
29793001
auto entry = VTable->getEntry(IGM.getSILModule(), fn);
@@ -5075,3 +5097,33 @@ void IRGenModule::emitOpaqueTypeDecl(OpaqueTypeDecl *D) {
50755097
// Emit the opaque type descriptor.
50765098
OpaqueTypeDescriptorBuilder(*this, D).emit();
50775099
}
5100+
5101+
bool irgen::methodRequiresReifiedVTableEntry(IRGenModule &IGM,
5102+
const SILVTable *vtable,
5103+
SILDeclRef method) {
5104+
auto &M = IGM.getSILModule();
5105+
auto entry = vtable->getEntry(IGM.getSILModule(), method);
5106+
if (!entry) {
5107+
return true;
5108+
}
5109+
5110+
// We may be able to elide the vtable entry, ABI permitting, if it's not
5111+
// overridden.
5112+
if (!entry->isNonOverridden()) {
5113+
return true;
5114+
}
5115+
5116+
// Does the ABI require a vtable entry to exist? If the class is public,
5117+
// and it's either marked fragile or part of a non-resilient module, then
5118+
// other modules will directly address vtable offsets and we can't remove
5119+
// vtable entries.
5120+
if (vtable->getClass()->getEffectiveAccess() >= AccessLevel::Public) {
5121+
// TODO: Check whether we use a resilient ABI to access this
5122+
// class's methods. We can drop unnecessary vtable entries if we do;
5123+
// otherwise fixed vtable offsets are part of the ABI.
5124+
return true;
5125+
}
5126+
5127+
// Otherwise, we can leave this method out of the runtime vtable.
5128+
return false;
5129+
}

lib/IRGen/MetadataLayout.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,12 +330,12 @@ ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl)
330330
super::addGenericArgument(requirement, forClass);
331331
}
332332

333-
void addMethod(SILDeclRef fn) {
333+
void addReifiedVTableEntry(SILDeclRef fn) {
334334
if (fn.getDecl()->getDeclContext() == Target) {
335335
++Layout.NumImmediateMembers;
336336
Layout.MethodInfos.try_emplace(fn, getNextOffset());
337337
}
338-
super::addMethod(fn);
338+
super::addReifiedVTableEntry(fn);
339339
}
340340

341341
void noteStartOfFieldOffsets(ClassDecl *forClass) {

lib/SIL/IR/SILModule.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,8 @@ void SILModule::eraseGlobalVariable(SILGlobalVariable *G) {
476476
getSILGlobalList().erase(G);
477477
}
478478

479-
SILVTable *SILModule::lookUpVTable(const ClassDecl *C) {
479+
SILVTable *SILModule::lookUpVTable(const ClassDecl *C,
480+
bool deserializeLazily) {
480481
if (!C)
481482
return nullptr;
482483

@@ -485,6 +486,9 @@ SILVTable *SILModule::lookUpVTable(const ClassDecl *C) {
485486
if (R != VTableMap.end())
486487
return R->second;
487488

489+
if (!deserializeLazily)
490+
return nullptr;
491+
488492
// If that fails, try to deserialize it. If that fails, return nullptr.
489493
SILVTable *Vtbl = getSILLoader()->lookupVTable(C);
490494
if (!Vtbl)

lib/SIL/IR/SILPrinter.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3131,16 +3131,17 @@ void SILVTable::print(llvm::raw_ostream &OS, bool Verbose) const {
31313131
switch (entry.getKind()) {
31323132
case SILVTable::Entry::Kind::Normal:
31333133
break;
3134-
case SILVTable::Entry::Kind::NormalNonOverridden:
3135-
OS << " [nonoverridden]";
3136-
break;
31373134
case SILVTable::Entry::Kind::Inherited:
31383135
OS << " [inherited]";
31393136
break;
31403137
case SILVTable::Entry::Kind::Override:
31413138
OS << " [override]";
31423139
break;
31433140
}
3141+
if (entry.isNonOverridden()) {
3142+
OS << " [nonoverridden]";
3143+
}
3144+
31443145
OS << "\t// " << demangleSymbol(entry.getImplementation()->getName());
31453146
OS << "\n";
31463147
}

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6083,7 +6083,8 @@ bool SILParserState::parseSILVTable(Parser &P) {
60836083
}
60846084

60856085
auto Kind = SILVTable::Entry::Kind::Normal;
6086-
if (P.Tok.is(tok::l_square)) {
6086+
bool NonOverridden = false;
6087+
while (P.Tok.is(tok::l_square)) {
60876088
P.consumeToken(tok::l_square);
60886089
if (P.Tok.isNot(tok::identifier)) {
60896090
P.diagnose(P.Tok.getLoc(), diag::sil_vtable_bad_entry_kind);
@@ -6098,7 +6099,7 @@ bool SILParserState::parseSILVTable(Parser &P) {
60986099
Kind = SILVTable::Entry::Kind::Inherited;
60996100
} else if (P.Tok.getText() == "nonoverridden") {
61006101
P.consumeToken();
6101-
Kind = SILVTable::Entry::Kind::NormalNonOverridden;
6102+
NonOverridden = true;
61026103
} else {
61036104
P.diagnose(P.Tok.getLoc(), diag::sil_vtable_bad_entry_kind);
61046105
return true;
@@ -6108,7 +6109,7 @@ bool SILParserState::parseSILVTable(Parser &P) {
61086109
return true;
61096110
}
61106111

6111-
vtableEntries.emplace_back(Ref, Func, Kind);
6112+
vtableEntries.emplace_back(Ref, Func, Kind, NonOverridden);
61126113
} while (P.Tok.isNot(tok::r_brace) && P.Tok.isNot(tok::eof));
61136114
}
61146115

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5425,7 +5425,6 @@ void SILVTable::verify(const SILModule &M) const {
54255425
bool validKind;
54265426
switch (entry.getKind()) {
54275427
case Entry::Normal:
5428-
case Entry::NormalNonOverridden:
54295428
validKind = true;
54305429
break;
54315430

@@ -5450,21 +5449,26 @@ void SILVTable::verify(const SILModule &M) const {
54505449

54515450
switch (entry.getKind()) {
54525451
case Entry::Normal:
5453-
case Entry::NormalNonOverridden:
54545452
assert(!superEntry && "non-root vtable entry must be inherited or override");
54555453
break;
54565454

54575455
case Entry::Inherited:
5456+
if (!superEntry)
5457+
break;
5458+
5459+
assert(entry.isNonOverridden() == superEntry->isNonOverridden()
5460+
&& "inherited vtable entry must share overridden-ness of superclass entry");
54585461
break;
54595462

54605463
case Entry::Override:
5464+
assert(!entry.isNonOverridden()
5465+
&& "override entry can't claim to be nonoverridden");
54615466
if (!superEntry)
54625467
break;
54635468

54645469
// The superclass entry must not prohibit overrides.
5465-
assert(
5466-
superEntry->getKind() != Entry::NormalNonOverridden &&
5467-
"vtable entry overrides an entry that claims to have no overrides");
5470+
assert(!superEntry->isNonOverridden()
5471+
&& "vtable entry overrides an entry that claims to have no overrides");
54685472
// TODO: Check the root vtable entry for the method too.
54695473
break;
54705474
}

0 commit comments

Comments
 (0)