mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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
This commit is contained in:
@@ -1028,7 +1028,7 @@ VTables
|
||||
decl ::= sil-vtable
|
||||
sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'
|
||||
|
||||
sil-vtable-entry ::= sil-decl-ref ':' sil-function-name
|
||||
sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-name
|
||||
|
||||
SIL represents dynamic dispatch for class methods using the `class_method`_,
|
||||
`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
|
||||
that can be used to look up overridden methods in the SIL vtable for a derived
|
||||
class (such as ``C.bas`` in ``C``'s vtable).
|
||||
|
||||
In case the SIL function is a thunk, the function name is preceded with the
|
||||
linkage of the original implementing function.
|
||||
|
||||
Witness Tables
|
||||
~~~~~~~~~~~~~~
|
||||
::
|
||||
|
||||
@@ -42,7 +42,28 @@ class SILVTable : public llvm::ilist_node<SILVTable>,
|
||||
public:
|
||||
// TODO: Entry should include substitutions needed to invoke an overridden
|
||||
// generic base class method.
|
||||
using Pair = std::pair<SILDeclRef, SILFunction*>;
|
||||
struct Entry {
|
||||
|
||||
Entry() : Implementation(nullptr), Linkage(SILLinkage::Private) { }
|
||||
|
||||
Entry(SILDeclRef Method, SILFunction *Implementation, SILLinkage Linkage) :
|
||||
Method(Method), Implementation(Implementation), Linkage(Linkage) { }
|
||||
|
||||
/// The declaration reference to the least-derived method visible through
|
||||
/// the class.
|
||||
SILDeclRef Method;
|
||||
|
||||
/// The function which implements the method for the class.
|
||||
SILFunction *Implementation;
|
||||
|
||||
/// The linkage of the implementing function.
|
||||
///
|
||||
/// This is usuallly the same as
|
||||
/// stripExternalFromLinkage(Implementation->getLinkage())
|
||||
/// except if Implementation is a thunk (which has private or shared
|
||||
/// linkage).
|
||||
SILLinkage Linkage;
|
||||
};
|
||||
|
||||
// Disallow copying into temporary objects.
|
||||
SILVTable(const SILVTable &other) = delete;
|
||||
@@ -56,10 +77,10 @@ private:
|
||||
unsigned NumEntries;
|
||||
|
||||
/// Tail-allocated SILVTable entries.
|
||||
Pair Entries[1];
|
||||
Entry Entries[1];
|
||||
|
||||
/// Private constructor. Create SILVTables by calling SILVTable::create.
|
||||
SILVTable(ClassDecl *c, ArrayRef<Pair> entries);
|
||||
SILVTable(ClassDecl *c, ArrayRef<Entry> entries);
|
||||
|
||||
public:
|
||||
~SILVTable();
|
||||
@@ -68,13 +89,13 @@ public:
|
||||
/// The SILDeclRef keys should reference the most-overridden members available
|
||||
/// through the class.
|
||||
static SILVTable *create(SILModule &M, ClassDecl *Class,
|
||||
ArrayRef<Pair> Entries);
|
||||
ArrayRef<Entry> Entries);
|
||||
|
||||
/// Return the class that the vtable represents.
|
||||
ClassDecl *getClass() const { return Class; }
|
||||
|
||||
/// Return all of the method entries.
|
||||
ArrayRef<Pair> getEntries() const { return {Entries, NumEntries}; }
|
||||
ArrayRef<Entry> getEntries() const { return {Entries, NumEntries}; }
|
||||
|
||||
/// Look up the implementation function for the given method.
|
||||
SILFunction *getImplementation(SILModule &M, SILDeclRef method) const;
|
||||
@@ -82,10 +103,10 @@ public:
|
||||
/// Removes entries from the vtable.
|
||||
/// \p predicate Returns true if the passed entry should be removed.
|
||||
template <typename Predicate> void removeEntries_if(Predicate predicate) {
|
||||
Pair *end = std::remove_if(Entries, Entries + NumEntries,
|
||||
[&](Pair &entry) -> bool {
|
||||
Entry *end = std::remove_if(Entries, Entries + NumEntries,
|
||||
[&](Entry &entry) -> bool {
|
||||
if (predicate(entry)) {
|
||||
entry.second->decrementRefCount();
|
||||
entry.Implementation->decrementRefCount();
|
||||
removeFromVTableCache(entry);
|
||||
return true;
|
||||
}
|
||||
@@ -102,7 +123,7 @@ public:
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
void removeFromVTableCache(Pair &entry);
|
||||
void removeFromVTableCache(Entry &entry);
|
||||
};
|
||||
|
||||
} // end swift namespace
|
||||
|
||||
@@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
|
||||
/// in source control, you should also update the comment to briefly
|
||||
/// describe what change you made. The content of this comment isn't important;
|
||||
/// it just ensures a conflict if two people change the module format.
|
||||
const uint16_t VERSION_MINOR = 304; // Last change: Archetype generic env
|
||||
const uint16_t VERSION_MINOR = 305; // Last change: linkage in SILVTable::Entry
|
||||
|
||||
using DeclID = PointerEmbeddedInt<unsigned, 31>;
|
||||
using DeclIDField = BCFixed<31>;
|
||||
|
||||
@@ -4272,7 +4272,7 @@ bool Parser::parseSILVTable() {
|
||||
Lexer::SILBodyRAII Tmp(*L);
|
||||
Scope S(this, ScopeKind::TopLevel);
|
||||
// Parse the entry list.
|
||||
std::vector<SILVTable::Pair> vtableEntries;
|
||||
std::vector<SILVTable::Entry> vtableEntries;
|
||||
if (Tok.isNot(tok::r_brace)) {
|
||||
do {
|
||||
SILDeclRef Ref;
|
||||
@@ -4281,10 +4281,12 @@ bool Parser::parseSILVTable() {
|
||||
if (VTableState.parseSILDeclRef(Ref))
|
||||
return true;
|
||||
SILFunction *Func = nullptr;
|
||||
Optional<SILLinkage> Linkage = SILLinkage::Private;
|
||||
if (Tok.is(tok::kw_nil)) {
|
||||
consumeToken();
|
||||
} else {
|
||||
if (parseToken(tok::colon, diag::expected_sil_vtable_colon) ||
|
||||
parseSILLinkage(Linkage, *this) ||
|
||||
VTableState.parseSILIdentifier(FuncName, FuncLoc,
|
||||
diag::expected_sil_value_name))
|
||||
return true;
|
||||
@@ -4293,8 +4295,10 @@ bool Parser::parseSILVTable() {
|
||||
diagnose(FuncLoc, diag::sil_vtable_func_not_found, FuncName);
|
||||
return true;
|
||||
}
|
||||
if (!Linkage)
|
||||
Linkage = stripExternalFromLinkage(Func->getLinkage());
|
||||
}
|
||||
vtableEntries.emplace_back(Ref, Func);
|
||||
vtableEntries.emplace_back(Ref, Func, Linkage.getValue());
|
||||
} while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof));
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ SILVTable *SILLinkerVisitor::processClassDecl(const ClassDecl *C) {
|
||||
// Otherwise, add all the vtable functions in Vtbl to the function
|
||||
// processing list...
|
||||
for (auto &E : Vtbl->getEntries())
|
||||
Worklist.push_back(E.second);
|
||||
Worklist.push_back(E.Implementation);
|
||||
|
||||
// And then transitively deserialize all SIL referenced by those functions.
|
||||
process();
|
||||
@@ -162,9 +162,9 @@ bool SILLinkerVisitor::linkInVTable(ClassDecl *D) {
|
||||
// for processing.
|
||||
bool Result = false;
|
||||
for (auto P : Vtbl->getEntries()) {
|
||||
if (P.second->isExternalDeclaration()) {
|
||||
if (P.Implementation->isExternalDeclaration()) {
|
||||
Result = true;
|
||||
addFunctionToWorklist(P.second);
|
||||
addFunctionToWorklist(P.Implementation);
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
|
||||
@@ -2141,9 +2141,14 @@ void SILVTable::print(llvm::raw_ostream &OS, bool Verbose) const {
|
||||
OS << "sil_vtable " << getClass()->getName() << " {\n";
|
||||
for (auto &entry : getEntries()) {
|
||||
OS << " ";
|
||||
entry.first.print(OS);
|
||||
OS << ": " << entry.second->getName()
|
||||
<< "\t// " << demangleSymbol(entry.second->getName()) << "\n";
|
||||
entry.Method.print(OS);
|
||||
OS << ": ";
|
||||
if (entry.Linkage !=
|
||||
stripExternalFromLinkage(entry.Implementation->getLinkage())) {
|
||||
OS << getLinkageString(entry.Linkage);
|
||||
}
|
||||
OS << entry.Implementation->getName()
|
||||
<< "\t// " << demangleSymbol(entry.Implementation->getName()) << "\n";
|
||||
}
|
||||
OS << "}\n\n";
|
||||
}
|
||||
|
||||
@@ -24,18 +24,18 @@
|
||||
using namespace swift;
|
||||
|
||||
SILVTable *SILVTable::create(SILModule &M, ClassDecl *Class,
|
||||
ArrayRef<Pair> Entries) {
|
||||
ArrayRef<Entry> Entries) {
|
||||
// SILVTable contains one element declared in Entries. We must allocate
|
||||
// space for it, because its default ctor will write to it.
|
||||
unsigned NumTailElements = std::max((unsigned)Entries.size(), 1U)-1;
|
||||
void *buf = M.allocate(sizeof(SILVTable) + sizeof(Pair) * NumTailElements,
|
||||
void *buf = M.allocate(sizeof(SILVTable) + sizeof(Entry) * NumTailElements,
|
||||
alignof(SILVTable));
|
||||
SILVTable *vt = ::new (buf) SILVTable(Class, Entries);
|
||||
M.vtables.push_back(vt);
|
||||
M.VTableMap[Class] = vt;
|
||||
// Update the Module's cache with new vtable + vtable entries:
|
||||
for (auto &entry : Entries) {
|
||||
M.VTableEntryCache.insert({{vt, entry.first}, entry.second});
|
||||
for (const Entry &entry : Entries) {
|
||||
M.VTableEntryCache.insert({{vt, entry.Method}, entry.Implementation});
|
||||
}
|
||||
return vt;
|
||||
}
|
||||
@@ -44,33 +44,33 @@ SILFunction *
|
||||
SILVTable::getImplementation(SILModule &M, SILDeclRef method) const {
|
||||
SILDeclRef m = method;
|
||||
do {
|
||||
auto entry = M.VTableEntryCache.find({this, m});
|
||||
if (entry != M.VTableEntryCache.end()) {
|
||||
return (*entry).second;
|
||||
auto entryIter = M.VTableEntryCache.find({this, m});
|
||||
if (entryIter != M.VTableEntryCache.end()) {
|
||||
return (*entryIter).second;
|
||||
}
|
||||
} while ((m = m.getOverridden()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SILVTable::removeFromVTableCache(Pair &entry) {
|
||||
SILModule &M = entry.second->getModule();
|
||||
M.VTableEntryCache.erase({this, entry.first});
|
||||
void SILVTable::removeFromVTableCache(Entry &entry) {
|
||||
SILModule &M = entry.Implementation->getModule();
|
||||
M.VTableEntryCache.erase({this, entry.Method});
|
||||
}
|
||||
|
||||
SILVTable::SILVTable(ClassDecl *c, ArrayRef<Pair> entries)
|
||||
SILVTable::SILVTable(ClassDecl *c, ArrayRef<Entry> entries)
|
||||
: Class(c), NumEntries(entries.size())
|
||||
{
|
||||
memcpy(Entries, entries.begin(), sizeof(Pair) * NumEntries);
|
||||
memcpy(Entries, entries.begin(), sizeof(Entry) * NumEntries);
|
||||
|
||||
// Bump the reference count of functions referenced by this table.
|
||||
for (auto entry : getEntries()) {
|
||||
entry.second->incrementRefCount();
|
||||
for (const Entry &entry : getEntries()) {
|
||||
entry.Implementation->incrementRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
SILVTable::~SILVTable() {
|
||||
// Drop the reference count of functions referenced by this table.
|
||||
for (auto entry : getEntries()) {
|
||||
entry.second->decrementRefCount();
|
||||
for (const Entry &entry : getEntries()) {
|
||||
entry.Implementation->decrementRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3592,9 +3592,9 @@ void SILVTable::verify(const SILModule &M) const {
|
||||
#ifndef NDEBUG
|
||||
for (auto &entry : getEntries()) {
|
||||
// All vtable entries must be decls in a class context.
|
||||
assert(entry.first.hasDecl() && "vtable entry is not a decl");
|
||||
auto baseInfo = M.Types.getConstantInfo(entry.first);
|
||||
ValueDecl *decl = entry.first.getDecl();
|
||||
assert(entry.Method.hasDecl() && "vtable entry is not a decl");
|
||||
auto baseInfo = M.Types.getConstantInfo(entry.Method);
|
||||
ValueDecl *decl = entry.Method.getDecl();
|
||||
|
||||
assert((!isa<FuncDecl>(decl)
|
||||
|| !cast<FuncDecl>(decl)->isObservingAccessor())
|
||||
@@ -3602,7 +3602,7 @@ void SILVTable::verify(const SILModule &M) const {
|
||||
|
||||
// For ivar destroyers, the decl is the class itself.
|
||||
ClassDecl *theClass;
|
||||
if (entry.first.kind == SILDeclRef::Kind::IVarDestroyer)
|
||||
if (entry.Method.kind == SILDeclRef::Kind::IVarDestroyer)
|
||||
theClass = dyn_cast<ClassDecl>(decl);
|
||||
else
|
||||
theClass = dyn_cast<ClassDecl>(decl->getDeclContext());
|
||||
@@ -3622,22 +3622,22 @@ void SILVTable::verify(const SILModule &M) const {
|
||||
assert(c && "vtable entry must refer to a member of the vtable's class");
|
||||
|
||||
// All function vtable entries must be at their natural uncurry level.
|
||||
assert(!entry.first.isCurried && "vtable entry must not be curried");
|
||||
assert(!entry.Method.isCurried && "vtable entry must not be curried");
|
||||
|
||||
// Foreign entry points shouldn't appear in vtables.
|
||||
assert(!entry.first.isForeign && "vtable entry must not be foreign");
|
||||
assert(!entry.Method.isForeign && "vtable entry must not be foreign");
|
||||
|
||||
// The vtable entry must be ABI-compatible with the overridden vtable slot.
|
||||
SmallString<32> baseName;
|
||||
{
|
||||
llvm::raw_svector_ostream os(baseName);
|
||||
entry.first.print(os);
|
||||
entry.Method.print(os);
|
||||
}
|
||||
|
||||
SILVerifier(*entry.second)
|
||||
SILVerifier(*entry.Implementation)
|
||||
.requireABICompatibleFunctionTypes(
|
||||
baseInfo.getSILType().castTo<SILFunctionType>(),
|
||||
entry.second->getLoweredFunctionType(),
|
||||
entry.Implementation->getLoweredFunctionType(),
|
||||
"vtable entry for " + baseName + " must be ABI-compatible");
|
||||
}
|
||||
#endif
|
||||
@@ -3747,9 +3747,9 @@ void SILModule::verify() const {
|
||||
vt.verify(*this);
|
||||
// Check if there is a cache entry for each vtable entry
|
||||
for (auto entry : vt.getEntries()) {
|
||||
if (VTableEntryCache.find({&vt, entry.first}) == VTableEntryCache.end()) {
|
||||
llvm::errs() << "Vtable entry for function: " << entry.second->getName()
|
||||
<< "not in cache!\n";
|
||||
if (VTableEntryCache.find({&vt, entry.Method}) == VTableEntryCache.end()) {
|
||||
llvm::errs() << "Vtable entry for function: "
|
||||
<< entry.Implementation->getName() << "not in cache!\n";
|
||||
assert(false && "triggering standard assertion failure routine");
|
||||
}
|
||||
EntriesSZ++;
|
||||
|
||||
@@ -172,7 +172,8 @@ public:
|
||||
/// diverges from the overridden base method. If no thunking is needed,
|
||||
/// returns a static reference to the derived method.
|
||||
SILFunction *emitVTableMethod(SILDeclRef derived,
|
||||
SILDeclRef base);
|
||||
SILDeclRef base,
|
||||
SILLinkage &implLinkage);
|
||||
|
||||
/// True if a function has been emitted for a given SILDeclRef.
|
||||
bool hasFunction(SILDeclRef constant);
|
||||
|
||||
@@ -651,6 +651,7 @@ public:
|
||||
/// \param inputSubstType Formal AST type of base class method
|
||||
/// \param outputSubstType Formal AST type of derived class method
|
||||
void emitVTableThunk(SILDeclRef derived,
|
||||
SILFunction *implFn,
|
||||
AbstractionPattern inputOrigType,
|
||||
CanAnyFunctionType inputSubstType,
|
||||
CanAnyFunctionType outputSubstType);
|
||||
|
||||
@@ -2891,6 +2891,7 @@ SILGenFunction::emitTransformedValue(SILLocation loc, RValue &&v,
|
||||
|
||||
void
|
||||
SILGenFunction::emitVTableThunk(SILDeclRef derived,
|
||||
SILFunction *implFn,
|
||||
AbstractionPattern inputOrigType,
|
||||
CanAnyFunctionType inputSubstType,
|
||||
CanAnyFunctionType outputSubstType) {
|
||||
@@ -2902,7 +2903,6 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived,
|
||||
cleanupLoc.markAutoGenerated();
|
||||
Scope scope(Cleanups, cleanupLoc);
|
||||
|
||||
auto implFn = SGM.getFunction(derived, NotForDefinition);
|
||||
auto fTy = implFn->getLoweredFunctionType();
|
||||
|
||||
ArrayRef<Substitution> subs;
|
||||
|
||||
@@ -57,10 +57,14 @@ SILFunction *SILGenModule::getDynamicThunk(SILDeclRef constant,
|
||||
}
|
||||
|
||||
SILFunction *
|
||||
SILGenModule::emitVTableMethod(SILDeclRef derived, SILDeclRef base) {
|
||||
SILGenModule::emitVTableMethod(SILDeclRef derived, SILDeclRef base,
|
||||
SILLinkage &implLinkage) {
|
||||
SILFunction *implFn = getFunction(derived, NotForDefinition);
|
||||
implLinkage = implFn->getLinkage();
|
||||
|
||||
// As a fast path, if there is no override, definitely no thunk is necessary.
|
||||
if (derived == base)
|
||||
return getFunction(derived, NotForDefinition);
|
||||
return implFn;
|
||||
|
||||
// Determine the derived thunk type by lowering the derived type against the
|
||||
// abstraction pattern of the base.
|
||||
@@ -74,7 +78,7 @@ SILGenModule::emitVTableMethod(SILDeclRef derived, SILDeclRef base) {
|
||||
// member type. If the override is ABI compatible, we do not need
|
||||
// a thunk.
|
||||
if (overrideInfo == derivedInfo)
|
||||
return getFunction(derived, NotForDefinition);
|
||||
return implFn;
|
||||
|
||||
// Generate the thunk name.
|
||||
// TODO: If we allocated a new vtable slot for the derived method, then
|
||||
@@ -98,7 +102,7 @@ SILGenModule::emitVTableMethod(SILDeclRef derived, SILDeclRef base) {
|
||||
thunk->setDebugScope(new (M) SILDebugScope(loc, thunk));
|
||||
|
||||
SILGenFunction(*this, *thunk)
|
||||
.emitVTableThunk(derived, basePattern,
|
||||
.emitVTableThunk(derived, implFn, basePattern,
|
||||
overrideInfo.LoweredInterfaceType,
|
||||
derivedInfo.LoweredInterfaceType);
|
||||
|
||||
@@ -147,7 +151,7 @@ class SILGenVTable : public Lowering::ASTVisitor<SILGenVTable> {
|
||||
public:
|
||||
SILGenModule &SGM;
|
||||
ClassDecl *theClass;
|
||||
std::vector<SILVTable::Pair> vtableEntries;
|
||||
std::vector<SILVTable::Entry> vtableEntries;
|
||||
|
||||
SILGenVTable(SILGenModule &SGM, ClassDecl *theClass)
|
||||
: SGM(SGM), theClass(theClass)
|
||||
@@ -179,31 +183,35 @@ public:
|
||||
// Add an entry to the vtable.
|
||||
void addEntry(SILDeclRef member) {
|
||||
/// Get the function to reference from the vtable.
|
||||
auto getVtableEntryFn = [&](SILDeclRef entry) -> SILFunction* {
|
||||
auto getVtableEntry = [&](SILDeclRef entry) -> SILVTable::Entry {
|
||||
// If the member is dynamic, reference its dynamic dispatch thunk so that
|
||||
// it will be redispatched, funneling the method call through the runtime
|
||||
// hook point.
|
||||
// TODO: Dynamic thunks could conceivably require reabstraction too.
|
||||
if (member.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
|
||||
return SGM.getDynamicThunk(member, SGM.Types.getConstantInfo(member));
|
||||
return { entry,
|
||||
SGM.getDynamicThunk(member, SGM.Types.getConstantInfo(member)),
|
||||
SILLinkage::Public };
|
||||
|
||||
// The derived method may require thunking to match up to the ABI of the
|
||||
// base method.
|
||||
return SGM.emitVTableMethod(member, entry);
|
||||
SILLinkage implLinkage;
|
||||
SILFunction *implFn = SGM.emitVTableMethod(member, entry, implLinkage);
|
||||
return { entry, implFn, stripExternalFromLinkage(implLinkage) };
|
||||
};
|
||||
|
||||
// Try to find an overridden entry.
|
||||
// NB: Mutates vtableEntries in-place
|
||||
// FIXME: O(n^2)
|
||||
if (auto overridden = member.getNextOverriddenVTableEntry()) {
|
||||
for (SILVTable::Pair &entry : vtableEntries) {
|
||||
for (SILVTable::Entry &entry : vtableEntries) {
|
||||
SILDeclRef ref = overridden;
|
||||
|
||||
do {
|
||||
// Replace the overridden member.
|
||||
if (entry.first == ref) {
|
||||
if (entry.Method == ref) {
|
||||
// The entry is keyed by the least derived method.
|
||||
entry = {ref, getVtableEntryFn(ref)};
|
||||
entry = getVtableEntry(ref);
|
||||
return;
|
||||
}
|
||||
} while ((ref = ref.getOverridden()));
|
||||
@@ -222,7 +230,7 @@ public:
|
||||
return;
|
||||
|
||||
// Otherwise, introduce a new vtable entry.
|
||||
vtableEntries.emplace_back(member, getVtableEntryFn(member));
|
||||
vtableEntries.push_back(getVtableEntry(member));
|
||||
}
|
||||
|
||||
// Default for members that don't require vtable entries.
|
||||
|
||||
@@ -297,21 +297,21 @@ class DeadFunctionElimination : FunctionLivenessComputation {
|
||||
for (SILVTable &vTable : Module->getVTableList()) {
|
||||
for (auto &entry : vTable.getEntries()) {
|
||||
// Destructors are alive because they are called from swift_release
|
||||
if (entry.first.kind == SILDeclRef::Kind::Deallocator ||
|
||||
entry.first.kind == SILDeclRef::Kind::IVarDestroyer) {
|
||||
ensureAlive(entry.second);
|
||||
if (entry.Method.kind == SILDeclRef::Kind::Deallocator ||
|
||||
entry.Method.kind == SILDeclRef::Kind::IVarDestroyer) {
|
||||
ensureAlive(entry.Implementation);
|
||||
continue;
|
||||
}
|
||||
|
||||
SILFunction *F = entry.second;
|
||||
auto *fd = cast<AbstractFunctionDecl>(entry.first.getDecl());
|
||||
SILFunction *F = entry.Implementation;
|
||||
auto *fd = cast<AbstractFunctionDecl>(entry.Method.getDecl());
|
||||
fd = getBase(fd);
|
||||
MethodInfo *mi = getMethodInfo(fd);
|
||||
addImplementingFunction(mi, F, vTable.getClass());
|
||||
|
||||
if (// A conservative approach: if any of the overridden functions is
|
||||
// visible externally, we mark the whole method as alive.
|
||||
isPossiblyUsedExternally(F->getLinkage(), Module->isWholeModule())
|
||||
isPossiblyUsedExternally(entry.Linkage, Module->isWholeModule())
|
||||
// We also have to check the method declaration's accessibility.
|
||||
// Needed if it's a public base method declared in another
|
||||
// compilation unit (for this we have no SILFunction).
|
||||
@@ -364,10 +364,10 @@ class DeadFunctionElimination : FunctionLivenessComputation {
|
||||
/// Removes all dead methods from vtables and witness tables.
|
||||
void removeDeadEntriesFromTables() {
|
||||
for (SILVTable &vTable : Module->getVTableList()) {
|
||||
vTable.removeEntries_if([this](SILVTable::Pair &entry) -> bool {
|
||||
if (!isAlive(entry.second)) {
|
||||
vTable.removeEntries_if([this](SILVTable::Entry &entry) -> bool {
|
||||
if (!isAlive(entry.Implementation)) {
|
||||
DEBUG(llvm::dbgs() << " erase dead vtable method " <<
|
||||
entry.second->getName() << "\n");
|
||||
entry.Implementation->getName() << "\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -467,7 +467,7 @@ class ExternalFunctionDefinitionsElimination : FunctionLivenessComputation {
|
||||
// Check vtable methods.
|
||||
for (SILVTable &vTable : Module->getVTableList()) {
|
||||
for (auto &entry : vTable.getEntries()) {
|
||||
SILFunction *F = entry.second;
|
||||
SILFunction *F = entry.Implementation;
|
||||
if (F->isTransparent() && isAvailableExternally(F->getLinkage()))
|
||||
ensureAlive(F);
|
||||
}
|
||||
|
||||
@@ -2173,7 +2173,7 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) {
|
||||
return nullptr;
|
||||
kind = SILCursor.readRecord(entry.ID, scratch);
|
||||
|
||||
std::vector<SILVTable::Pair> vtableEntries;
|
||||
std::vector<SILVTable::Entry> vtableEntries;
|
||||
// Another SIL_VTABLE record means the end of this VTable.
|
||||
while (kind != SIL_VTABLE && kind != SIL_WITNESS_TABLE &&
|
||||
kind != SIL_DEFAULT_WITNESS_TABLE &&
|
||||
@@ -2182,12 +2182,22 @@ SILVTable *SILDeserializer::readVTable(DeclID VId) {
|
||||
"Content of Vtable should be in SIL_VTABLE_ENTRY.");
|
||||
ArrayRef<uint64_t> ListOfValues;
|
||||
DeclID NameID;
|
||||
VTableEntryLayout::readRecord(scratch, NameID, ListOfValues);
|
||||
unsigned RawLinkage;
|
||||
VTableEntryLayout::readRecord(scratch, NameID, RawLinkage, ListOfValues);
|
||||
|
||||
Optional<SILLinkage> Linkage = fromStableSILLinkage(RawLinkage);
|
||||
if (!Linkage) {
|
||||
DEBUG(llvm::dbgs() << "invalid linkage code " << RawLinkage
|
||||
<< " for VTable Entry\n");
|
||||
MF->error();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SILFunction *Func = getFuncForReference(MF->getIdentifier(NameID).str());
|
||||
if (Func) {
|
||||
unsigned NextValueIndex = 0;
|
||||
vtableEntries.emplace_back(getSILDeclRef(MF, ListOfValues,
|
||||
NextValueIndex), Func);
|
||||
vtableEntries.emplace_back(getSILDeclRef(MF, ListOfValues, NextValueIndex),
|
||||
Func, Linkage.getValue());
|
||||
}
|
||||
|
||||
// Fetch the next record.
|
||||
|
||||
@@ -170,6 +170,7 @@ namespace sil_block {
|
||||
using VTableEntryLayout = BCRecordLayout<
|
||||
SIL_VTABLE_ENTRY,
|
||||
DeclIDField, // SILFunction name
|
||||
SILLinkageField, // Linkage
|
||||
BCArray<ValueIDField> // SILDeclRef
|
||||
>;
|
||||
|
||||
|
||||
@@ -1746,13 +1746,14 @@ void SILSerializer::writeSILVTable(const SILVTable &vt) {
|
||||
|
||||
for (auto &entry : vt.getEntries()) {
|
||||
SmallVector<ValueID, 4> ListOfValues;
|
||||
handleSILDeclRef(S, entry.first, ListOfValues);
|
||||
addReferencedSILFunction(entry.second, true);
|
||||
handleSILDeclRef(S, entry.Method, ListOfValues);
|
||||
addReferencedSILFunction(entry.Implementation, true);
|
||||
// Each entry is a pair of SILDeclRef and SILFunction.
|
||||
VTableEntryLayout::emitRecord(Out, ScratchRecord,
|
||||
SILAbbrCodes[VTableEntryLayout::Code],
|
||||
// SILFunction name
|
||||
S.addIdentifierRef(Ctx.getIdentifier(entry.second->getName())),
|
||||
S.addIdentifierRef(Ctx.getIdentifier(entry.Implementation->getName())),
|
||||
toStableSILLinkage(entry.Linkage),
|
||||
ListOfValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1569,10 +1569,12 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil_vtable Foo {
|
||||
// CHECK: #Foo.subscript!getter.1: _TFC3tmp3Foog9subscriptFTVs5Int32S1__S1_
|
||||
// CHECK: #Foo.subscript!getter.1: hidden _TFC3tmp3Foog9subscriptFTVs5Int32S1__S1_
|
||||
// CHECK: #Foo.subscript!setter.1: _TFC3tmp3Foos9subscriptFTVs5Int32S1__S1_
|
||||
// CHECK: }
|
||||
sil_vtable Foo {
|
||||
#Foo.subscript!getter.1: _TFC3tmp3Foog9subscriptFTVs5Int32S1__S1_
|
||||
#Foo.subscript!setter.1: _TFC3tmp3Foos9subscriptFTVs5Int32S1__S1_
|
||||
// Override the linke to check if it is parsed correctly.
|
||||
#Foo.subscript!getter.1: hidden _TFC3tmp3Foog9subscriptFTVs5Int32S1__S1_
|
||||
// The public linkage is the same as the function's linkage and should not be printed.
|
||||
#Foo.subscript!setter.1: public _TFC3tmp3Foos9subscriptFTVs5Int32S1__S1_
|
||||
}
|
||||
|
||||
@@ -468,6 +468,6 @@ public class Sub : Base {
|
||||
|
||||
// Vtable uses a dynamic thunk for dynamic overrides
|
||||
// CHECK-LABEL: sil_vtable Subclass {
|
||||
// CHECK-LABEL: #Foo.overriddenByDynamic!1: _TTDFC7dynamic8Subclass19overriddenByDynamic
|
||||
// CHECK-LABEL: #Foo.overriddenByDynamic!1: public _TTDFC7dynamic8Subclass19overriddenByDynamic
|
||||
|
||||
|
||||
|
||||
@@ -193,60 +193,60 @@ class Noot : Aap {
|
||||
// CHECK: [[OUTER:%.*]] = convert_function [[INNER]] : $@callee_owned () -> @owned Noot to $@callee_owned () -> @owned Optional<Aap>
|
||||
// CHECK: return [[OUTER]]
|
||||
// CHECK-LABEL: sil_vtable D {
|
||||
// CHECK: #B.iuo!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.iuo!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f2!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f3!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g2!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g3!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h2!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h3!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i2!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i3!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
|
||||
// CHECK-LABEL: sil_vtable E {
|
||||
// CHECK: #B.iuo!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.iuo!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f2!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f3!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g2!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g3!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.g4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h2!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h3!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.h4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i2!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i3!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.i4!1: _TF{{[A-Z0-9a-z_]*}}1D
|
||||
|
||||
// CHECK-LABEL: sil_vtable F {
|
||||
// CHECK: #B.iuo!1: _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.iuo!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1D
|
||||
// CHECK: #B.f!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.f2!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.f3!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.f4!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g2!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g3!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.g4!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h2!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h3!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.h4!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i2!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i3!1: _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i2!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i3!1: hidden _TTVF{{[A-Z0-9a-z_]*}}1F
|
||||
// CHECK: #B.i4!1: _TF{{[A-Z0-9a-z_]*}}1F
|
||||
|
||||
// CHECK-LABEL: sil_vtable NoThrowVariance {
|
||||
|
||||
24
test/SILOptimizer/alive_method_with_thunk.swift
Normal file
24
test/SILOptimizer/alive_method_with_thunk.swift
Normal file
@@ -0,0 +1,24 @@
|
||||
// RUN: %target-swift-frontend -O %s -parse-as-library -emit-sil | %FileCheck %s
|
||||
|
||||
public class BaseClass<T> {
|
||||
func doSomething(_ value: T) -> Int {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
public class DerivedClass: BaseClass<Double> {
|
||||
|
||||
// Don't eliminate this public method, which is called via a thunk
|
||||
public override func doSomething(_ value: Double) -> Int {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK: sil_vtable BaseClass {
|
||||
// CHECK: #BaseClass.doSomething!1: _{{.*}}// BaseClass.doSomething(A) -> Int
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: sil_vtable DerivedClass {
|
||||
// CHECK: #BaseClass.doSomething!1: public _{{.*}}// override DerivedClass.doSomething(Double) -> Int
|
||||
// CHECK: }
|
||||
|
||||
Reference in New Issue
Block a user