Skip to content

Commit 1eb3a05

Browse files
committed
DeadFunctionElimination: don’t eliminate public methods which are called via a thunk.
For this we need to store the linkage of the “original” method implementation in the vtable. Otherwise DeadFunctionElimination thinks that the method implementation is not public but private (which is the linkage of the thunk). The big part of this change is to extend SILVTable to store the linkage (+ serialization, printing, etc.). fixes rdar://problem/29841635
1 parent 7d427aa commit 1eb3a05

File tree

20 files changed

+193
-112
lines changed

20 files changed

+193
-112
lines changed

docs/SIL.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ VTables
10281028
decl ::= sil-vtable
10291029
sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'
10301030

1031-
sil-vtable-entry ::= sil-decl-ref ':' sil-function-name
1031+
sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-name
10321032

10331033
SIL represents dynamic dispatch for class methods using the `class_method`_,
10341034
`super_method`_, and `dynamic_method`_ instructions. The potential destinations
@@ -1084,6 +1084,9 @@ visible through that class (in the example above, ``B``'s vtable references
10841084
that can be used to look up overridden methods in the SIL vtable for a derived
10851085
class (such as ``C.bas`` in ``C``'s vtable).
10861086

1087+
In case the SIL function is a thunk, the function name is preceded with the
1088+
linkage of the original implementing function.
1089+
10871090
Witness Tables
10881091
~~~~~~~~~~~~~~
10891092
::

include/swift/SIL/SILVTable.h

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,28 @@ class SILVTable : public llvm::ilist_node<SILVTable>,
4242
public:
4343
// TODO: Entry should include substitutions needed to invoke an overridden
4444
// generic base class method.
45-
using Pair = std::pair<SILDeclRef, SILFunction*>;
45+
struct Entry {
46+
47+
Entry() : Implementation(nullptr), Linkage(SILLinkage::Private) { }
48+
49+
Entry(SILDeclRef Method, SILFunction *Implementation, SILLinkage Linkage) :
50+
Method(Method), Implementation(Implementation), Linkage(Linkage) { }
51+
52+
/// The declaration reference to the least-derived method visible through
53+
/// the class.
54+
SILDeclRef Method;
55+
56+
/// The function which implements the method for the class.
57+
SILFunction *Implementation;
58+
59+
/// The linkage of the implementing function.
60+
///
61+
/// This is usuallly the same as
62+
/// stripExternalFromLinkage(Implementation->getLinkage())
63+
/// except if Implementation is a thunk (which has private or shared
64+
/// linkage).
65+
SILLinkage Linkage;
66+
};
4667

4768
// Disallow copying into temporary objects.
4869
SILVTable(const SILVTable &other) = delete;
@@ -56,10 +77,10 @@ class SILVTable : public llvm::ilist_node<SILVTable>,
5677
unsigned NumEntries;
5778

5879
/// Tail-allocated SILVTable entries.
59-
Pair Entries[1];
80+
Entry Entries[1];
6081

6182
/// Private constructor. Create SILVTables by calling SILVTable::create.
62-
SILVTable(ClassDecl *c, ArrayRef<Pair> entries);
83+
SILVTable(ClassDecl *c, ArrayRef<Entry> entries);
6384

6485
public:
6586
~SILVTable();
@@ -68,24 +89,24 @@ class SILVTable : public llvm::ilist_node<SILVTable>,
6889
/// The SILDeclRef keys should reference the most-overridden members available
6990
/// through the class.
7091
static SILVTable *create(SILModule &M, ClassDecl *Class,
71-
ArrayRef<Pair> Entries);
92+
ArrayRef<Entry> Entries);
7293

7394
/// Return the class that the vtable represents.
7495
ClassDecl *getClass() const { return Class; }
7596

7697
/// Return all of the method entries.
77-
ArrayRef<Pair> getEntries() const { return {Entries, NumEntries}; }
98+
ArrayRef<Entry> getEntries() const { return {Entries, NumEntries}; }
7899

79100
/// Look up the implementation function for the given method.
80101
SILFunction *getImplementation(SILModule &M, SILDeclRef method) const;
81102

82103
/// Removes entries from the vtable.
83104
/// \p predicate Returns true if the passed entry should be removed.
84105
template <typename Predicate> void removeEntries_if(Predicate predicate) {
85-
Pair *end = std::remove_if(Entries, Entries + NumEntries,
86-
[&](Pair &entry) -> bool {
106+
Entry *end = std::remove_if(Entries, Entries + NumEntries,
107+
[&](Entry &entry) -> bool {
87108
if (predicate(entry)) {
88-
entry.second->decrementRefCount();
109+
entry.Implementation->decrementRefCount();
89110
removeFromVTableCache(entry);
90111
return true;
91112
}
@@ -102,7 +123,7 @@ class SILVTable : public llvm::ilist_node<SILVTable>,
102123
void dump() const;
103124

104125
private:
105-
void removeFromVTableCache(Pair &entry);
126+
void removeFromVTableCache(Entry &entry);
106127
};
107128

108129
} // end swift namespace

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
5454
/// in source control, you should also update the comment to briefly
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
57-
const uint16_t VERSION_MINOR = 304; // Last change: Archetype generic env
57+
const uint16_t VERSION_MINOR = 305; // Last change: linkage in SILVTable::Entry
5858

5959
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6060
using DeclIDField = BCFixed<31>;

lib/Parse/ParseSIL.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4272,7 +4272,7 @@ bool Parser::parseSILVTable() {
42724272
Lexer::SILBodyRAII Tmp(*L);
42734273
Scope S(this, ScopeKind::TopLevel);
42744274
// Parse the entry list.
4275-
std::vector<SILVTable::Pair> vtableEntries;
4275+
std::vector<SILVTable::Entry> vtableEntries;
42764276
if (Tok.isNot(tok::r_brace)) {
42774277
do {
42784278
SILDeclRef Ref;
@@ -4281,10 +4281,12 @@ bool Parser::parseSILVTable() {
42814281
if (VTableState.parseSILDeclRef(Ref))
42824282
return true;
42834283
SILFunction *Func = nullptr;
4284+
Optional<SILLinkage> Linkage = SILLinkage::Private;
42844285
if (Tok.is(tok::kw_nil)) {
42854286
consumeToken();
42864287
} else {
42874288
if (parseToken(tok::colon, diag::expected_sil_vtable_colon) ||
4289+
parseSILLinkage(Linkage, *this) ||
42884290
VTableState.parseSILIdentifier(FuncName, FuncLoc,
42894291
diag::expected_sil_value_name))
42904292
return true;
@@ -4293,8 +4295,10 @@ bool Parser::parseSILVTable() {
42934295
diagnose(FuncLoc, diag::sil_vtable_func_not_found, FuncName);
42944296
return true;
42954297
}
4298+
if (!Linkage)
4299+
Linkage = stripExternalFromLinkage(Func->getLinkage());
42964300
}
4297-
vtableEntries.emplace_back(Ref, Func);
4301+
vtableEntries.emplace_back(Ref, Func, Linkage.getValue());
42984302
} while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof));
42994303
}
43004304

lib/SIL/Linker.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ SILVTable *SILLinkerVisitor::processClassDecl(const ClassDecl *C) {
139139
// Otherwise, add all the vtable functions in Vtbl to the function
140140
// processing list...
141141
for (auto &E : Vtbl->getEntries())
142-
Worklist.push_back(E.second);
142+
Worklist.push_back(E.Implementation);
143143

144144
// And then transitively deserialize all SIL referenced by those functions.
145145
process();
@@ -162,9 +162,9 @@ bool SILLinkerVisitor::linkInVTable(ClassDecl *D) {
162162
// for processing.
163163
bool Result = false;
164164
for (auto P : Vtbl->getEntries()) {
165-
if (P.second->isExternalDeclaration()) {
165+
if (P.Implementation->isExternalDeclaration()) {
166166
Result = true;
167-
addFunctionToWorklist(P.second);
167+
addFunctionToWorklist(P.Implementation);
168168
}
169169
}
170170
return Result;

lib/SIL/SILPrinter.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2141,9 +2141,14 @@ void SILVTable::print(llvm::raw_ostream &OS, bool Verbose) const {
21412141
OS << "sil_vtable " << getClass()->getName() << " {\n";
21422142
for (auto &entry : getEntries()) {
21432143
OS << " ";
2144-
entry.first.print(OS);
2145-
OS << ": " << entry.second->getName()
2146-
<< "\t// " << demangleSymbol(entry.second->getName()) << "\n";
2144+
entry.Method.print(OS);
2145+
OS << ": ";
2146+
if (entry.Linkage !=
2147+
stripExternalFromLinkage(entry.Implementation->getLinkage())) {
2148+
OS << getLinkageString(entry.Linkage);
2149+
}
2150+
OS << entry.Implementation->getName()
2151+
<< "\t// " << demangleSymbol(entry.Implementation->getName()) << "\n";
21472152
}
21482153
OS << "}\n\n";
21492154
}

lib/SIL/SILVTable.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@
2424
using namespace swift;
2525

2626
SILVTable *SILVTable::create(SILModule &M, ClassDecl *Class,
27-
ArrayRef<Pair> Entries) {
27+
ArrayRef<Entry> Entries) {
2828
// SILVTable contains one element declared in Entries. We must allocate
2929
// space for it, because its default ctor will write to it.
3030
unsigned NumTailElements = std::max((unsigned)Entries.size(), 1U)-1;
31-
void *buf = M.allocate(sizeof(SILVTable) + sizeof(Pair) * NumTailElements,
31+
void *buf = M.allocate(sizeof(SILVTable) + sizeof(Entry) * NumTailElements,
3232
alignof(SILVTable));
3333
SILVTable *vt = ::new (buf) SILVTable(Class, Entries);
3434
M.vtables.push_back(vt);
3535
M.VTableMap[Class] = vt;
3636
// Update the Module's cache with new vtable + vtable entries:
37-
for (auto &entry : Entries) {
38-
M.VTableEntryCache.insert({{vt, entry.first}, entry.second});
37+
for (const Entry &entry : Entries) {
38+
M.VTableEntryCache.insert({{vt, entry.Method}, entry.Implementation});
3939
}
4040
return vt;
4141
}
@@ -44,33 +44,33 @@ SILFunction *
4444
SILVTable::getImplementation(SILModule &M, SILDeclRef method) const {
4545
SILDeclRef m = method;
4646
do {
47-
auto entry = M.VTableEntryCache.find({this, m});
48-
if (entry != M.VTableEntryCache.end()) {
49-
return (*entry).second;
47+
auto entryIter = M.VTableEntryCache.find({this, m});
48+
if (entryIter != M.VTableEntryCache.end()) {
49+
return (*entryIter).second;
5050
}
5151
} while ((m = m.getOverridden()));
5252
return nullptr;
5353
}
5454

55-
void SILVTable::removeFromVTableCache(Pair &entry) {
56-
SILModule &M = entry.second->getModule();
57-
M.VTableEntryCache.erase({this, entry.first});
55+
void SILVTable::removeFromVTableCache(Entry &entry) {
56+
SILModule &M = entry.Implementation->getModule();
57+
M.VTableEntryCache.erase({this, entry.Method});
5858
}
5959

60-
SILVTable::SILVTable(ClassDecl *c, ArrayRef<Pair> entries)
60+
SILVTable::SILVTable(ClassDecl *c, ArrayRef<Entry> entries)
6161
: Class(c), NumEntries(entries.size())
6262
{
63-
memcpy(Entries, entries.begin(), sizeof(Pair) * NumEntries);
63+
memcpy(Entries, entries.begin(), sizeof(Entry) * NumEntries);
6464

6565
// Bump the reference count of functions referenced by this table.
66-
for (auto entry : getEntries()) {
67-
entry.second->incrementRefCount();
66+
for (const Entry &entry : getEntries()) {
67+
entry.Implementation->incrementRefCount();
6868
}
6969
}
7070

7171
SILVTable::~SILVTable() {
7272
// Drop the reference count of functions referenced by this table.
73-
for (auto entry : getEntries()) {
74-
entry.second->decrementRefCount();
73+
for (const Entry &entry : getEntries()) {
74+
entry.Implementation->decrementRefCount();
7575
}
7676
}

lib/SIL/SILVerifier.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3592,17 +3592,17 @@ void SILVTable::verify(const SILModule &M) const {
35923592
#ifndef NDEBUG
35933593
for (auto &entry : getEntries()) {
35943594
// All vtable entries must be decls in a class context.
3595-
assert(entry.first.hasDecl() && "vtable entry is not a decl");
3596-
auto baseInfo = M.Types.getConstantInfo(entry.first);
3597-
ValueDecl *decl = entry.first.getDecl();
3595+
assert(entry.Method.hasDecl() && "vtable entry is not a decl");
3596+
auto baseInfo = M.Types.getConstantInfo(entry.Method);
3597+
ValueDecl *decl = entry.Method.getDecl();
35983598

35993599
assert((!isa<FuncDecl>(decl)
36003600
|| !cast<FuncDecl>(decl)->isObservingAccessor())
36013601
&& "observing accessors shouldn't have vtable entries");
36023602

36033603
// For ivar destroyers, the decl is the class itself.
36043604
ClassDecl *theClass;
3605-
if (entry.first.kind == SILDeclRef::Kind::IVarDestroyer)
3605+
if (entry.Method.kind == SILDeclRef::Kind::IVarDestroyer)
36063606
theClass = dyn_cast<ClassDecl>(decl);
36073607
else
36083608
theClass = dyn_cast<ClassDecl>(decl->getDeclContext());
@@ -3622,22 +3622,22 @@ void SILVTable::verify(const SILModule &M) const {
36223622
assert(c && "vtable entry must refer to a member of the vtable's class");
36233623

36243624
// All function vtable entries must be at their natural uncurry level.
3625-
assert(!entry.first.isCurried && "vtable entry must not be curried");
3625+
assert(!entry.Method.isCurried && "vtable entry must not be curried");
36263626

36273627
// Foreign entry points shouldn't appear in vtables.
3628-
assert(!entry.first.isForeign && "vtable entry must not be foreign");
3628+
assert(!entry.Method.isForeign && "vtable entry must not be foreign");
36293629

36303630
// The vtable entry must be ABI-compatible with the overridden vtable slot.
36313631
SmallString<32> baseName;
36323632
{
36333633
llvm::raw_svector_ostream os(baseName);
3634-
entry.first.print(os);
3634+
entry.Method.print(os);
36353635
}
36363636

3637-
SILVerifier(*entry.second)
3637+
SILVerifier(*entry.Implementation)
36383638
.requireABICompatibleFunctionTypes(
36393639
baseInfo.getSILType().castTo<SILFunctionType>(),
3640-
entry.second->getLoweredFunctionType(),
3640+
entry.Implementation->getLoweredFunctionType(),
36413641
"vtable entry for " + baseName + " must be ABI-compatible");
36423642
}
36433643
#endif
@@ -3747,9 +3747,9 @@ void SILModule::verify() const {
37473747
vt.verify(*this);
37483748
// Check if there is a cache entry for each vtable entry
37493749
for (auto entry : vt.getEntries()) {
3750-
if (VTableEntryCache.find({&vt, entry.first}) == VTableEntryCache.end()) {
3751-
llvm::errs() << "Vtable entry for function: " << entry.second->getName()
3752-
<< "not in cache!\n";
3750+
if (VTableEntryCache.find({&vt, entry.Method}) == VTableEntryCache.end()) {
3751+
llvm::errs() << "Vtable entry for function: "
3752+
<< entry.Implementation->getName() << "not in cache!\n";
37533753
assert(false && "triggering standard assertion failure routine");
37543754
}
37553755
EntriesSZ++;

lib/SILGen/SILGen.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
172172
/// diverges from the overridden base method. If no thunking is needed,
173173
/// returns a static reference to the derived method.
174174
SILFunction *emitVTableMethod(SILDeclRef derived,
175-
SILDeclRef base);
175+
SILDeclRef base,
176+
SILLinkage &implLinkage);
176177

177178
/// True if a function has been emitted for a given SILDeclRef.
178179
bool hasFunction(SILDeclRef constant);

lib/SILGen/SILGenFunction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
651651
/// \param inputSubstType Formal AST type of base class method
652652
/// \param outputSubstType Formal AST type of derived class method
653653
void emitVTableThunk(SILDeclRef derived,
654+
SILFunction *implFn,
654655
AbstractionPattern inputOrigType,
655656
CanAnyFunctionType inputSubstType,
656657
CanAnyFunctionType outputSubstType);

lib/SILGen/SILGenPoly.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2891,6 +2891,7 @@ SILGenFunction::emitTransformedValue(SILLocation loc, RValue &&v,
28912891

28922892
void
28932893
SILGenFunction::emitVTableThunk(SILDeclRef derived,
2894+
SILFunction *implFn,
28942895
AbstractionPattern inputOrigType,
28952896
CanAnyFunctionType inputSubstType,
28962897
CanAnyFunctionType outputSubstType) {
@@ -2902,7 +2903,6 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived,
29022903
cleanupLoc.markAutoGenerated();
29032904
Scope scope(Cleanups, cleanupLoc);
29042905

2905-
auto implFn = SGM.getFunction(derived, NotForDefinition);
29062906
auto fTy = implFn->getLoweredFunctionType();
29072907

29082908
ArrayRef<Substitution> subs;

0 commit comments

Comments
 (0)