blob: e56fed87dcf506765fb06a844086e54f0d2aa7fb [file] [log] [blame]
//===--- DeadFunctionElimination.cpp - Eliminate dead functions -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://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() {
// 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.first.kind == SILDeclRef::Kind::Deallocator ||
entry.first.kind == SILDeclRef::Kind::IVarDestroyer) {
ensureAlive(entry.second);
continue;
}
SILFunction *F = entry.second;
auto *fd = cast<AbstractFunctionDecl>(entry.first.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())
// 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::Pair &entry) -> bool {
if (!isAlive(entry.second)) {
DEBUG(llvm::dbgs() << " erase dead vtable method " <<
entry.second->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.
for (SILFunction &F : *Module)
if (!isAlive(&F))
F.dropAllReferences();
// Next step: delete all dead functions.
for (auto FI = Module->begin(), EI = Module->end(); FI != EI;) {
SILFunction *F = &*FI;
++FI;
if (!isAlive(F)) {
DEBUG(llvm::dbgs() << " erase dead function " << F->getName() << "\n");
NumDeadFunc++;
DFEPass->invalidateAnalysisForDeadFunction(F,
SILAnalysis::InvalidationKind::Everything);
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.
void findAnchorsInTables() {
}
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();
}