mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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
656 lines
23 KiB
C++
656 lines
23 KiB
C++
//===--- DeadFunctionElimination.cpp - Eliminate dead functions -----------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "sil-dead-function-elimination"
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
|
#include "swift/SIL/PatternMatch.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILVisitor.h"
|
|
#include "swift/SILOptimizer/Utils/Local.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/Support/Debug.h"
|
|
using namespace swift;
|
|
|
|
STATISTIC(NumDeadFunc, "Number of dead functions eliminated");
|
|
STATISTIC(NumEliminatedExternalDefs, "Number of external function definitions eliminated");
|
|
|
|
namespace {
|
|
|
|
/// This is a base class for passes that are based on function liveness
|
|
/// computations like e.g. dead function elimination.
|
|
/// It provides a common logic for computing live (i.e. reachable) functions.
|
|
class FunctionLivenessComputation {
|
|
protected:
|
|
/// Stores which functions implement a vtable or witness table method.
|
|
struct MethodInfo {
|
|
|
|
MethodInfo() : isAnchor(false) {}
|
|
|
|
/// All functions which implement the method. Together with the class for
|
|
/// which the function implements the method. In case of a witness method,
|
|
/// the class pointer is null.
|
|
SmallVector<std::pair<SILFunction *, ClassDecl *>, 8> implementingFunctions;
|
|
|
|
/// True, if the whole method is alive, e.g. because it's class is visible
|
|
/// from outside. This implies that all implementing functions are alive.
|
|
bool isAnchor;
|
|
};
|
|
|
|
SILModule *Module;
|
|
|
|
llvm::DenseMap<AbstractFunctionDecl *, MethodInfo *> MethodInfos;
|
|
llvm::SpecificBumpPtrAllocator<MethodInfo> MethodInfoAllocator;
|
|
|
|
llvm::SmallSetVector<SILFunction *, 16> Worklist;
|
|
|
|
llvm::SmallPtrSet<SILFunction *, 32> AliveFunctions;
|
|
|
|
/// Checks is a function is alive, e.g. because it is visible externally.
|
|
bool isAnchorFunction(SILFunction *F) {
|
|
|
|
// Remove internal functions that are not referenced by anything.
|
|
if (isPossiblyUsedExternally(F->getLinkage(), Module->isWholeModule()))
|
|
return true;
|
|
|
|
// ObjC functions are called through the runtime and are therefore alive
|
|
// even if not referenced inside SIL.
|
|
if (F->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod)
|
|
return true;
|
|
|
|
// If function is marked as "keep-as-public", don't remove it.
|
|
// Change its linkage to public, so that other applications can refer to it.
|
|
// It is important that this transformation is done at the end of
|
|
// a pipeline, as it may break some optimizations.
|
|
if (F->isKeepAsPublic()) {
|
|
F->setLinkage(SILLinkage::Public);
|
|
DEBUG(llvm::dbgs() << "DFE: Preserve the specialization "
|
|
<< F->getName() << '\n');
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Gets or creates the MethodInfo for a vtable or witness table method.
|
|
/// \p decl The method declaration. In case of a vtable method this is always
|
|
/// the most overridden method.
|
|
MethodInfo *getMethodInfo(AbstractFunctionDecl *decl) {
|
|
MethodInfo *&entry = MethodInfos[decl];
|
|
if (entry == nullptr) {
|
|
entry = new (MethodInfoAllocator.Allocate()) MethodInfo();
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/// Adds a function which implements a vtable or witness method. If it's a
|
|
/// vtable method, \p C is the class for which the function implements the
|
|
/// method. For witness methods \C is null.
|
|
void addImplementingFunction(MethodInfo *mi, SILFunction *F, ClassDecl *C) {
|
|
if (mi->isAnchor)
|
|
ensureAlive(F);
|
|
mi->implementingFunctions.push_back(std::make_pair(F, C));
|
|
}
|
|
|
|
/// Returns true if a function is marked as alive.
|
|
bool isAlive(SILFunction *F) { return AliveFunctions.count(F) != 0; }
|
|
|
|
/// Marks a function as alive.
|
|
void makeAlive(SILFunction *F) {
|
|
AliveFunctions.insert(F);
|
|
assert(F && "function does not exist");
|
|
Worklist.insert(F);
|
|
}
|
|
|
|
/// Marks a function as alive if it is not alive yet.
|
|
void ensureAlive(SILFunction *F) {
|
|
if (!isAlive(F))
|
|
makeAlive(F);
|
|
}
|
|
|
|
/// Returns true if \a Derived is the same as \p Base or derived from it.
|
|
static bool isDerivedOrEqual(ClassDecl *Derived, ClassDecl *Base) {
|
|
for (;;) {
|
|
if (Derived == Base)
|
|
return true;
|
|
if (!Derived->hasSuperclass())
|
|
break;
|
|
Derived = Derived->getSuperclass()->getClassOrBoundGenericClass();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Returns true if the implementation of method \p FD in class \p ImplCl
|
|
/// may be called when the type of the class_method's operand is \p MethodCl.
|
|
/// Both, \p MethodCl and \p ImplCl, may by null if not known or if it's a
|
|
/// protocol method.
|
|
static bool canHaveSameImplementation(FuncDecl *FD, ClassDecl *MethodCl,
|
|
ClassDecl *ImplCl) {
|
|
if (!FD || !MethodCl || !ImplCl)
|
|
return true;
|
|
|
|
// All implementations of derived classes may be called.
|
|
if (isDerivedOrEqual(ImplCl, MethodCl))
|
|
return true;
|
|
|
|
// Check if the method implementation is the same in a super class, i.e.
|
|
// it is not overridden in the derived class.
|
|
auto *Impl1 = MethodCl->findImplementingMethod(FD);
|
|
assert(Impl1);
|
|
auto *Impl2 = ImplCl->findImplementingMethod(FD);
|
|
assert(Impl2);
|
|
|
|
return Impl1 == Impl2;
|
|
}
|
|
|
|
/// Marks the implementing functions of the method \p FD as alive. If it is a
|
|
/// class method, \p MethodCl is the type of the class_method instruction's
|
|
/// operand.
|
|
void ensureAlive(MethodInfo *mi, FuncDecl *FD, ClassDecl *MethodCl) {
|
|
for (auto &Pair : mi->implementingFunctions) {
|
|
SILFunction *FImpl = Pair.first;
|
|
if (!isAlive(FImpl) &&
|
|
canHaveSameImplementation(FD, MethodCl, Pair.second)) {
|
|
makeAlive(FImpl);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Gets the base implementation of a method.
|
|
/// We always use the most overridden function to describe a method.
|
|
AbstractFunctionDecl *getBase(AbstractFunctionDecl *FD) {
|
|
while (FD->getOverriddenDecl()) {
|
|
FD = FD->getOverriddenDecl();
|
|
}
|
|
return FD;
|
|
}
|
|
|
|
/// Scans all references inside a function.
|
|
void scanFunction(SILFunction *F) {
|
|
for (SILBasicBlock &BB : *F) {
|
|
for (SILInstruction &I : BB) {
|
|
if (auto *MI = dyn_cast<MethodInst>(&I)) {
|
|
auto *funcDecl = getBase(
|
|
cast<AbstractFunctionDecl>(MI->getMember().getDecl()));
|
|
MethodInfo *mi = getMethodInfo(funcDecl);
|
|
ClassDecl *MethodCl = nullptr;
|
|
if (MI->getNumOperands() == 1)
|
|
MethodCl = MI->getOperand(0)->getType().getClassOrBoundGenericClass();
|
|
ensureAlive(mi, dyn_cast<FuncDecl>(funcDecl), MethodCl);
|
|
} else if (auto *FRI = dyn_cast<FunctionRefInst>(&I)) {
|
|
ensureAlive(FRI->getReferencedFunction());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Retrieve the visibility information from the AST.
|
|
bool isVisibleExternally(ValueDecl *decl) {
|
|
Accessibility accessibility = decl->getEffectiveAccess();
|
|
SILLinkage linkage;
|
|
switch (accessibility) {
|
|
case Accessibility::Private:
|
|
case Accessibility::FilePrivate:
|
|
linkage = SILLinkage::Private;
|
|
break;
|
|
case Accessibility::Internal:
|
|
linkage = SILLinkage::Hidden;
|
|
break;
|
|
case Accessibility::Public:
|
|
case Accessibility::Open:
|
|
linkage = SILLinkage::Public;
|
|
break;
|
|
}
|
|
if (isPossiblyUsedExternally(linkage, Module->isWholeModule()))
|
|
return true;
|
|
|
|
// If a vtable or witness table (method) is only visible in another module
|
|
// it can be accessed inside that module and we don't see this access.
|
|
// We hit this case e.g. if a table is imported from the stdlib.
|
|
if (decl->getDeclContext()->getParentModule() != Module->getSwiftModule())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Find anchors in vtables and witness tables, if required.
|
|
virtual void findAnchorsInTables() = 0;
|
|
|
|
/// Find all functions which are alive from the beginning.
|
|
/// For example, functions which may be referenced externally.
|
|
void findAnchors() {
|
|
|
|
findAnchorsInTables();
|
|
|
|
for (SILFunction &F : *Module) {
|
|
if (isAnchorFunction(&F)) {
|
|
DEBUG(llvm::dbgs() << " anchor function: " << F.getName() << "\n");
|
|
ensureAlive(&F);
|
|
}
|
|
|
|
if (!F.shouldOptimize()) {
|
|
DEBUG(llvm::dbgs() << " anchor a no optimization function: " << F.getName() << "\n");
|
|
ensureAlive(&F);
|
|
}
|
|
}
|
|
|
|
for (SILGlobalVariable &G : Module->getSILGlobalList()) {
|
|
if (SILFunction *initFunc = G.getInitializer()) {
|
|
ensureAlive(initFunc);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
FunctionLivenessComputation(SILModule *module) :
|
|
Module(module) {}
|
|
|
|
/// The main entry point of the optimization.
|
|
bool findAliveFunctions() {
|
|
|
|
DEBUG(llvm::dbgs() << "running function elimination\n");
|
|
|
|
// Find everything which may not be eliminated, e.g. because it is accessed
|
|
// externally.
|
|
findAnchors();
|
|
|
|
// The core of the algorithm: Mark functions as alive which can be reached
|
|
// from the anchors.
|
|
while (!Worklist.empty()) {
|
|
SILFunction *F = Worklist.back();
|
|
Worklist.pop_back();
|
|
scanFunction(F);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual ~FunctionLivenessComputation() {}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DeadFunctionElimination
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class DeadFunctionElimination : FunctionLivenessComputation {
|
|
|
|
/// DeadFunctionElimination pass takes functions
|
|
/// reachable via vtables and witness_tables into account
|
|
/// when computing a function liveness information.
|
|
void findAnchorsInTables() override {
|
|
// Check vtable methods.
|
|
for (SILVTable &vTable : Module->getVTableList()) {
|
|
for (auto &entry : vTable.getEntries()) {
|
|
// Destructors are alive because they are called from swift_release
|
|
if (entry.Method.kind == SILDeclRef::Kind::Deallocator ||
|
|
entry.Method.kind == SILDeclRef::Kind::IVarDestroyer) {
|
|
ensureAlive(entry.Implementation);
|
|
continue;
|
|
}
|
|
|
|
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(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).
|
|
|| isVisibleExternally(fd)
|
|
// Declarations are always accessible externally, so they are alive.
|
|
|| !F->isDefinition()) {
|
|
ensureAlive(mi, nullptr, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check witness methods.
|
|
for (SILWitnessTable &WT : Module->getWitnessTableList()) {
|
|
bool tableIsAlive = isVisibleExternally(WT.getConformance()->getProtocol());
|
|
for (const SILWitnessTable::Entry &entry : WT.getEntries()) {
|
|
if (entry.getKind() != SILWitnessTable::Method)
|
|
continue;
|
|
|
|
auto methodWitness = entry.getMethodWitness();
|
|
auto *fd = cast<AbstractFunctionDecl>(methodWitness.Requirement.
|
|
getDecl());
|
|
assert(fd == getBase(fd) && "key in witness table is overridden");
|
|
SILFunction *F = methodWitness.Witness;
|
|
if (!F)
|
|
continue;
|
|
|
|
MethodInfo *mi = getMethodInfo(fd);
|
|
addImplementingFunction(mi, F, nullptr);
|
|
if (tableIsAlive || !F->isDefinition())
|
|
ensureAlive(mi, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
// Check default witness methods.
|
|
for (SILDefaultWitnessTable &WT : Module->getDefaultWitnessTableList()) {
|
|
for (const SILDefaultWitnessTable::Entry &entry : WT.getEntries()) {
|
|
if (!entry.isValid())
|
|
continue;
|
|
|
|
SILFunction *F = entry.getWitness();
|
|
auto *fd = cast<AbstractFunctionDecl>(entry.getRequirement().getDecl());
|
|
|
|
MethodInfo *mi = getMethodInfo(fd);
|
|
addImplementingFunction(mi, F, nullptr);
|
|
ensureAlive(mi, nullptr, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Removes all dead methods from vtables and witness tables.
|
|
void removeDeadEntriesFromTables() {
|
|
for (SILVTable &vTable : Module->getVTableList()) {
|
|
vTable.removeEntries_if([this](SILVTable::Entry &entry) -> bool {
|
|
if (!isAlive(entry.Implementation)) {
|
|
DEBUG(llvm::dbgs() << " erase dead vtable method " <<
|
|
entry.Implementation->getName() << "\n");
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
auto &WitnessTables = Module->getWitnessTableList();
|
|
for (auto WI = WitnessTables.begin(), EI = WitnessTables.end(); WI != EI;) {
|
|
SILWitnessTable *WT = &*WI;
|
|
WI++;
|
|
WT->clearMethods_if([this](const SILWitnessTable::MethodWitness &MW) -> bool {
|
|
if (!isAlive(MW.Witness)) {
|
|
DEBUG(llvm::dbgs() << " erase dead witness method " <<
|
|
MW.Witness->getName() << "\n");
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
}
|
|
|
|
public:
|
|
DeadFunctionElimination(SILModule *module)
|
|
: FunctionLivenessComputation(module) {}
|
|
|
|
/// The main entry point of the optimization.
|
|
void eliminateFunctions(SILModuleTransform *DFEPass) {
|
|
|
|
DEBUG(llvm::dbgs() << "running dead function elimination\n");
|
|
findAliveFunctions();
|
|
|
|
removeDeadEntriesFromTables();
|
|
|
|
// First drop all references so that we don't get problems with non-zero
|
|
// reference counts of dead functions.
|
|
std::vector<SILFunction *> DeadFunctions;
|
|
for (SILFunction &F : *Module) {
|
|
if (!isAlive(&F)) {
|
|
F.dropAllReferences();
|
|
DeadFunctions.push_back(&F);
|
|
}
|
|
}
|
|
|
|
// Next step: delete all dead functions.
|
|
auto InvalidateEverything = SILAnalysis::InvalidationKind::Everything;
|
|
while (!DeadFunctions.empty()) {
|
|
SILFunction *F = DeadFunctions.back();
|
|
DeadFunctions.pop_back();
|
|
|
|
DEBUG(llvm::dbgs() << " erase dead function " << F->getName() << "\n");
|
|
NumDeadFunc++;
|
|
DFEPass->invalidateAnalysisForDeadFunction(F, InvalidateEverything);
|
|
Module->eraseFunction(F);
|
|
}
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ExternalFunctionDefinitionsElimination
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
/// This pass performs removal of external function definitions for a sake of
|
|
/// reducing the amount of code to run through IRGen. It is supposed to run very
|
|
/// late in the pipeline, after devirtualization, inlining and specialization
|
|
/// passes.
|
|
///
|
|
/// NOTE:
|
|
/// Overall, this pass does not try to remove any information which can be
|
|
/// useful for LLVM code generation, e.g. for analysis of function's
|
|
/// side-effects. Therefore it does not remove bodies of any external functions
|
|
/// that are alive, because LLVM may analyze their bodies to determine their
|
|
/// side-effects and use it to achieve a better optimization.
|
|
///
|
|
/// Current implementation does not consider functions which are reachable only
|
|
/// via vtables or witness_tables as alive and removes their bodies, because
|
|
/// even if they would be kept around, LLVM does not know how to look at
|
|
/// function definitions through Swift's vtables and witness_tables.
|
|
///
|
|
/// TODO:
|
|
/// Once there is a proper support for IPO in Swift compiler and/or there is
|
|
/// a way to communicate function's side-effects without providing its body
|
|
/// (e.g. by means of SILFunction flags, attributes, etc), it should be
|
|
/// safe to remove bodies of all external definitions.
|
|
|
|
class ExternalFunctionDefinitionsElimination : FunctionLivenessComputation {
|
|
|
|
/// ExternalFunctionDefinitionsElimination pass does not take functions
|
|
/// reachable via vtables and witness_tables into account when computing
|
|
/// a function liveness information. The only exceptions are external
|
|
/// transparent functions, because bodies of external transparent functions
|
|
/// should never be removed.
|
|
void findAnchorsInTables() override {
|
|
// Check vtable methods.
|
|
for (SILVTable &vTable : Module->getVTableList()) {
|
|
for (auto &entry : vTable.getEntries()) {
|
|
SILFunction *F = entry.Implementation;
|
|
if (F->isTransparent() && isAvailableExternally(F->getLinkage()))
|
|
ensureAlive(F);
|
|
}
|
|
}
|
|
|
|
// Check witness methods.
|
|
for (SILWitnessTable &WT : Module->getWitnessTableList()) {
|
|
isVisibleExternally(WT.getConformance()->getProtocol());
|
|
for (const SILWitnessTable::Entry &entry : WT.getEntries()) {
|
|
if (entry.getKind() != SILWitnessTable::Method)
|
|
continue;
|
|
|
|
auto methodWitness = entry.getMethodWitness();
|
|
SILFunction *F = methodWitness.Witness;
|
|
if (!F)
|
|
continue;
|
|
if (F->isTransparent() && isAvailableExternally(F->getLinkage()))
|
|
ensureAlive(F);
|
|
}
|
|
}
|
|
|
|
// Check default witness methods.
|
|
for (SILDefaultWitnessTable &WT : Module->getDefaultWitnessTableList()) {
|
|
for (const SILDefaultWitnessTable::Entry &entry : WT.getEntries()) {
|
|
if (!entry.isValid())
|
|
continue;
|
|
|
|
SILFunction *F = entry.getWitness();
|
|
if (F->isTransparent() && isAvailableExternally(F->getLinkage()))
|
|
ensureAlive(F);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool findAliveFunctions() {
|
|
/// TODO: Once there is a proper support for IPO,
|
|
/// bodies of all external functions can be removed.
|
|
/// Therefore there is no need for a liveness computation.
|
|
/// The next line can be just replaced by:
|
|
/// return false;
|
|
|
|
// Keep all transparent functions alive. This is important because we have
|
|
// to generate code for transparent functions.
|
|
// Here we handle the special case if a transparent function is referenced
|
|
// from a non-externally-available function (i.e. a function for which we
|
|
// generate code). And those function is only reachable through a
|
|
// vtable/witness-table. In such a case we would not visit the transparent
|
|
// function in findAliveFunctions() because we don't consider vtables/
|
|
// witness-tables as anchors.
|
|
for (SILFunction &F : *Module) {
|
|
if (isAvailableExternally(F.getLinkage()))
|
|
continue;
|
|
|
|
for (SILBasicBlock &BB : F) {
|
|
for (SILInstruction &I : BB) {
|
|
if (auto *FRI = dyn_cast<FunctionRefInst>(&I)) {
|
|
SILFunction *RefF = FRI->getReferencedFunction();
|
|
if (RefF->isTransparent() && RefF->isFragile())
|
|
ensureAlive(RefF);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FunctionLivenessComputation::findAliveFunctions();
|
|
}
|
|
|
|
/// Try to convert definition into declaration.
|
|
/// Returns true if function was erased from the module.
|
|
bool tryToConvertExternalDefinitionIntoDeclaration(SILFunction *F) {
|
|
// Bail if it is a declaration already
|
|
if (!F->isDefinition())
|
|
return false;
|
|
// Bail if there is no external implementation of this function.
|
|
if (!F->isAvailableExternally())
|
|
return false;
|
|
// Bail if has a shared visibility, as there are no guarantees
|
|
// that an implementation is available elsewhere.
|
|
if (hasSharedVisibility(F->getLinkage()))
|
|
return false;
|
|
// Make this definition a declaration by removing the body of a function.
|
|
|
|
DEBUG(llvm::dbgs() << " removed external function " << F->getName()
|
|
<< "\n");
|
|
F->dropAllReferences();
|
|
auto &Blocks = F->getBlocks();
|
|
Blocks.clear();
|
|
assert(F->isExternalDeclaration() &&
|
|
"Function should be an external declaration");
|
|
NumEliminatedExternalDefs++;
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
ExternalFunctionDefinitionsElimination(SILModule *module)
|
|
: FunctionLivenessComputation(module) {}
|
|
|
|
/// Eliminate bodies of external functions which are not alive.
|
|
///
|
|
/// Bodies of alive functions should not be removed, as LLVM may
|
|
/// still need them for analyzing their side-effects.
|
|
void eliminateFunctions(SILModuleTransform *DFEPass) {
|
|
|
|
findAliveFunctions();
|
|
// Get rid of definitions for all global functions that are not marked as
|
|
// alive.
|
|
for (auto FI = Module->begin(), EI = Module->end(); FI != EI;) {
|
|
SILFunction *F = &*FI;
|
|
++FI;
|
|
// Do not remove bodies of any functions that are alive.
|
|
if (!isAlive(F)) {
|
|
if (tryToConvertExternalDefinitionIntoDeclaration(F)) {
|
|
DFEPass->invalidateAnalysisForDeadFunction(F,
|
|
SILAnalysis::InvalidationKind::Everything);
|
|
if (F->getRefCount() == 0)
|
|
F->getModule().eraseFunction(F);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Pass Definition and Entry Points
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class SILDeadFuncElimination : public SILModuleTransform {
|
|
void run() override {
|
|
DEBUG(llvm::dbgs() << "Running DeadFuncElimination\n");
|
|
|
|
// The deserializer caches functions that it deserializes so that if it is
|
|
// asked to deserialize that function again, it does not do extra work. This
|
|
// causes the function's reference count to be incremented causing it to be
|
|
// alive unnecessarily. We invalidate the SILLoaderCaches here so that we
|
|
// can eliminate such functions.
|
|
getModule()->invalidateSILLoaderCaches();
|
|
|
|
DeadFunctionElimination deadFunctionElimination(getModule());
|
|
deadFunctionElimination.eliminateFunctions(this);
|
|
}
|
|
|
|
StringRef getName() override { return "Dead Function Elimination"; }
|
|
};
|
|
|
|
class SILExternalFuncDefinitionsElimination : public SILModuleTransform {
|
|
void run() override {
|
|
DEBUG(llvm::dbgs() << "Running ExternalFunctionDefinitionsElimination\n");
|
|
|
|
// The deserializer caches functions that it deserializes so that if it is
|
|
// asked to deserialize that function again, it does not do extra work. This
|
|
// causes the function's reference count to be incremented causing it to be
|
|
// alive unnecessarily. We invalidate the SILLoaderCaches here so that we
|
|
// can eliminate the definitions of such functions.
|
|
getModule()->invalidateSILLoaderCaches();
|
|
|
|
ExternalFunctionDefinitionsElimination EFDFE(getModule());
|
|
EFDFE.eliminateFunctions(this);
|
|
}
|
|
|
|
StringRef getName() override {
|
|
return "External Function Definitions Elimination";
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
SILTransform *swift::createDeadFunctionElimination() {
|
|
return new SILDeadFuncElimination();
|
|
}
|
|
|
|
SILTransform *swift::createExternalFunctionDefinitionsElimination() {
|
|
return new SILExternalFuncDefinitionsElimination();
|
|
}
|
|
|
|
void swift::performSILDeadFunctionElimination(SILModule *M) {
|
|
SILPassManager PM(M);
|
|
llvm::SmallVector<PassKind, 1> Pass = {PassKind::DeadFunctionElimination};
|
|
PM.executePassPipelinePlan(
|
|
SILPassPipelinePlan::getPassPipelineForKinds(Pass));
|
|
}
|