#include "llvm/Analysis/CallGraphSCCPass.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/IR/AbstractCallSite.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManagers.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/OptBisect.h"
#include "llvm/IR/PassTimingInfo.h"
#include "llvm/IR/PrintPasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <string>
#include <utility>
#include <vector>
using namespace llvm;
#define DEBUG_TYPE "cgscc-passmgr"
namespace llvm {
cl::opt<unsigned> MaxDevirtIterations("max-devirt-iterations", cl::ReallyHidden,
cl::init(4));
}
STATISTIC(MaxSCCIterations, "Maximum CGSCCPassMgr iterations on one SCC");
namespace {
class CGPassManager : public ModulePass, public PMDataManager {
public:
static char ID;
explicit CGPassManager() : ModulePass(ID) {}
bool runOnModule(Module &M) override;
using ModulePass::doInitialization;
using ModulePass::doFinalization;
bool doInitialization(CallGraph &CG);
bool doFinalization(CallGraph &CG);
void getAnalysisUsage(AnalysisUsage &Info) const override {
Info.addRequired<CallGraphWrapperPass>();
Info.setPreservesAll();
}
StringRef getPassName() const override { return "CallGraph Pass Manager"; }
PMDataManager *getAsPMDataManager() override { return this; }
Pass *getAsPass() override { return this; }
void dumpPassStructure(unsigned Offset) override {
errs().indent(Offset*2) << "Call Graph SCC Pass Manager\n";
for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) {
Pass *P = getContainedPass(Index);
P->dumpPassStructure(Offset + 1);
dumpLastUses(P, Offset+1);
}
}
Pass *getContainedPass(unsigned N) {
assert(N < PassVector.size() && "Pass number out of range!");
return static_cast<Pass *>(PassVector[N]);
}
PassManagerType getPassManagerType() const override {
return PMT_CallGraphPassManager;
}
private:
bool RunAllPassesOnSCC(CallGraphSCC &CurSCC, CallGraph &CG,
bool &DevirtualizedCall);
bool RunPassOnSCC(Pass *P, CallGraphSCC &CurSCC,
CallGraph &CG, bool &CallGraphUpToDate,
bool &DevirtualizedCall);
bool RefreshCallGraph(const CallGraphSCC &CurSCC, CallGraph &CG,
bool IsCheckingMode);
};
}
char CGPassManager::ID = 0;
bool CGPassManager::RunPassOnSCC(Pass *P, CallGraphSCC &CurSCC,
CallGraph &CG, bool &CallGraphUpToDate,
bool &DevirtualizedCall) {
bool Changed = false;
PMDataManager *PM = P->getAsPMDataManager();
Module &M = CG.getModule();
if (!PM) {
CallGraphSCCPass *CGSP = (CallGraphSCCPass *)P;
if (!CallGraphUpToDate) {
DevirtualizedCall |= RefreshCallGraph(CurSCC, CG, false);
CallGraphUpToDate = true;
}
{
unsigned InstrCount, SCCCount = 0;
StringMap<std::pair<unsigned, unsigned>> FunctionToInstrCount;
bool EmitICRemark = M.shouldEmitInstrCountChangedRemark();
TimeRegion PassTimer(getPassTimer(CGSP));
if (EmitICRemark)
InstrCount = initSizeRemarkInfo(M, FunctionToInstrCount);
Changed = CGSP->runOnSCC(CurSCC);
if (EmitICRemark) {
SCCCount = M.getInstructionCount();
if (SCCCount != InstrCount) {
int64_t Delta =
static_cast<int64_t>(SCCCount) - static_cast<int64_t>(InstrCount);
emitInstrCountChangedRemark(P, M, Delta, InstrCount,
FunctionToInstrCount);
InstrCount = SCCCount;
}
}
}
#ifndef NDEBUG
if (Changed)
RefreshCallGraph(CurSCC, CG, true);
#endif
return Changed;
}
assert(PM->getPassManagerType() == PMT_FunctionPassManager &&
"Invalid CGPassManager member");
FPPassManager *FPP = (FPPassManager*)P;
for (CallGraphNode *CGN : CurSCC) {
if (Function *F = CGN->getFunction()) {
dumpPassInfo(P, EXECUTION_MSG, ON_FUNCTION_MSG, F->getName());
{
TimeRegion PassTimer(getPassTimer(FPP));
Changed |= FPP->runOnFunction(*F);
}
F->getContext().yield();
}
}
if (Changed && CallGraphUpToDate) {
LLVM_DEBUG(dbgs() << "CGSCCPASSMGR: Pass Dirtied SCC: " << P->getPassName()
<< '\n');
CallGraphUpToDate = false;
}
return Changed;
}
bool CGPassManager::RefreshCallGraph(const CallGraphSCC &CurSCC, CallGraph &CG,
bool CheckingMode) {
DenseMap<Value *, CallGraphNode *> Calls;
LLVM_DEBUG(dbgs() << "CGSCCPASSMGR: Refreshing SCC with " << CurSCC.size()
<< " nodes:\n";
for (CallGraphNode *CGN
: CurSCC) CGN->dump(););
bool MadeChange = false;
bool DevirtualizedCall = false;
unsigned FunctionNo = 0;
for (CallGraphSCC::iterator SCCIdx = CurSCC.begin(), E = CurSCC.end();
SCCIdx != E; ++SCCIdx, ++FunctionNo) {
CallGraphNode *CGN = *SCCIdx;
Function *F = CGN->getFunction();
if (!F || F->isDeclaration()) continue;
unsigned NumDirectRemoved = 0, NumIndirectRemoved = 0;
CallGraphNode::iterator CGNEnd = CGN->end();
auto RemoveAndCheckForDone = [&](CallGraphNode::iterator I) {
bool WasLast = I + 1 == CGNEnd;
CGN->removeCallEdge(I);
if (WasLast)
return true;
CGNEnd = CGN->end();
return false;
};
for (CallGraphNode::iterator I = CGN->begin(); I != CGNEnd;) {
if (!I->first) {
if (CheckingMode) {
++I;
continue;
}
if (RemoveAndCheckForDone(I))
break;
continue;
}
auto *Call = dyn_cast_or_null<CallBase>(*I->first);
if (!Call ||
Calls.count(Call) ||
(Call->getCalledFunction() &&
Call->getCalledFunction()->isIntrinsic() &&
Intrinsic::isLeaf(Call->getCalledFunction()->getIntrinsicID()))) {
assert(!CheckingMode &&
"CallGraphSCCPass did not update the CallGraph correctly!");
if (!I->second->getFunction())
++NumIndirectRemoved;
else
++NumDirectRemoved;
if (RemoveAndCheckForDone(I))
break;
continue;
}
assert(!Calls.count(Call) && "Call site occurs in node multiple times");
if (Call) {
Function *Callee = Call->getCalledFunction();
if (!Callee || !(Callee->isIntrinsic()))
Calls.insert(std::make_pair(Call, I->second));
}
++I;
}
unsigned NumDirectAdded = 0, NumIndirectAdded = 0;
for (BasicBlock &BB : *F)
for (Instruction &I : BB) {
auto *Call = dyn_cast<CallBase>(&I);
if (!Call)
continue;
Function *Callee = Call->getCalledFunction();
if (Callee && Callee->isIntrinsic())
continue;
if (!CheckingMode) {
forEachCallbackFunction(*Call, [&](Function *CB) {
CGN->addCalledFunction(nullptr, CG.getOrInsertFunction(CB));
});
}
DenseMap<Value *, CallGraphNode *>::iterator ExistingIt =
Calls.find(Call);
if (ExistingIt != Calls.end()) {
CallGraphNode *ExistingNode = ExistingIt->second;
Calls.erase(ExistingIt);
if (ExistingNode->getFunction() == Call->getCalledFunction())
continue;
if (CheckingMode && Call->getCalledFunction() &&
ExistingNode->getFunction() == nullptr)
continue;
assert(!CheckingMode &&
"CallGraphSCCPass did not update the CallGraph correctly!");
CallGraphNode *CalleeNode;
if (Function *Callee = Call->getCalledFunction()) {
CalleeNode = CG.getOrInsertFunction(Callee);
if (!ExistingNode->getFunction()) {
DevirtualizedCall = true;
LLVM_DEBUG(dbgs() << " CGSCCPASSMGR: Devirtualized call to '"
<< Callee->getName() << "'\n");
}
} else {
CalleeNode = CG.getCallsExternalNode();
}
CGN->replaceCallEdge(*Call, *Call, CalleeNode);
MadeChange = true;
continue;
}
assert(!CheckingMode &&
"CallGraphSCCPass did not update the CallGraph correctly!");
CallGraphNode *CalleeNode;
if (Function *Callee = Call->getCalledFunction()) {
CalleeNode = CG.getOrInsertFunction(Callee);
++NumDirectAdded;
} else {
CalleeNode = CG.getCallsExternalNode();
++NumIndirectAdded;
}
CGN->addCalledFunction(Call, CalleeNode);
MadeChange = true;
}
if (NumIndirectRemoved > NumIndirectAdded &&
NumDirectRemoved < NumDirectAdded)
DevirtualizedCall = true;
assert(Calls.empty() && "Dangling pointers found in call sites map");
if ((FunctionNo & 15) == 15)
Calls.clear();
}
LLVM_DEBUG(if (MadeChange) {
dbgs() << "CGSCCPASSMGR: Refreshed SCC is now:\n";
for (CallGraphNode *CGN : CurSCC)
CGN->dump();
if (DevirtualizedCall)
dbgs() << "CGSCCPASSMGR: Refresh devirtualized a call!\n";
} else {
dbgs() << "CGSCCPASSMGR: SCC Refresh didn't change call graph.\n";
});
(void)MadeChange;
return DevirtualizedCall;
}
bool CGPassManager::RunAllPassesOnSCC(CallGraphSCC &CurSCC, CallGraph &CG,
bool &DevirtualizedCall) {
bool Changed = false;
bool CallGraphUpToDate = true;
for (unsigned PassNo = 0, e = getNumContainedPasses();
PassNo != e; ++PassNo) {
Pass *P = getContainedPass(PassNo);
if (isPassDebuggingExecutionsOrMore()) {
std::string Functions;
#ifndef NDEBUG
raw_string_ostream OS(Functions);
ListSeparator LS;
for (const CallGraphNode *CGN : CurSCC) {
OS << LS;
CGN->print(OS);
}
OS.flush();
#endif
dumpPassInfo(P, EXECUTION_MSG, ON_CG_MSG, Functions);
}
dumpRequiredSet(P);
initializeAnalysisImpl(P);
#ifdef EXPENSIVE_CHECKS
uint64_t RefHash = P->structuralHash(CG.getModule());
#endif
bool LocalChanged =
RunPassOnSCC(P, CurSCC, CG, CallGraphUpToDate, DevirtualizedCall);
Changed |= LocalChanged;
#ifdef EXPENSIVE_CHECKS
if (!LocalChanged && (RefHash != P->structuralHash(CG.getModule()))) {
llvm::errs() << "Pass modifies its input and doesn't report it: "
<< P->getPassName() << "\n";
llvm_unreachable("Pass modifies its input and doesn't report it");
}
#endif
if (LocalChanged)
dumpPassInfo(P, MODIFICATION_MSG, ON_CG_MSG, "");
dumpPreservedSet(P);
verifyPreservedAnalysis(P);
if (LocalChanged)
removeNotPreservedAnalysis(P);
recordAvailableAnalysis(P);
removeDeadPasses(P, "", ON_CG_MSG);
}
if (!CallGraphUpToDate)
DevirtualizedCall |= RefreshCallGraph(CurSCC, CG, false);
return Changed;
}
bool CGPassManager::runOnModule(Module &M) {
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
bool Changed = doInitialization(CG);
scc_iterator<CallGraph*> CGI = scc_begin(&CG);
CallGraphSCC CurSCC(CG, &CGI);
while (!CGI.isAtEnd()) {
const std::vector<CallGraphNode *> &NodeVec = *CGI;
CurSCC.initialize(NodeVec);
++CGI;
unsigned Iteration = 0;
bool DevirtualizedCall = false;
do {
LLVM_DEBUG(if (Iteration) dbgs()
<< " SCCPASSMGR: Re-visiting SCC, iteration #" << Iteration
<< '\n');
DevirtualizedCall = false;
Changed |= RunAllPassesOnSCC(CurSCC, CG, DevirtualizedCall);
} while (Iteration++ < MaxDevirtIterations && DevirtualizedCall);
if (DevirtualizedCall)
LLVM_DEBUG(dbgs() << " CGSCCPASSMGR: Stopped iteration after "
<< Iteration
<< " times, due to -max-devirt-iterations\n");
MaxSCCIterations.updateMax(Iteration);
}
Changed |= doFinalization(CG);
return Changed;
}
bool CGPassManager::doInitialization(CallGraph &CG) {
bool Changed = false;
for (unsigned i = 0, e = getNumContainedPasses(); i != e; ++i) {
if (PMDataManager *PM = getContainedPass(i)->getAsPMDataManager()) {
assert(PM->getPassManagerType() == PMT_FunctionPassManager &&
"Invalid CGPassManager member");
Changed |= ((FPPassManager*)PM)->doInitialization(CG.getModule());
} else {
Changed |= ((CallGraphSCCPass*)getContainedPass(i))->doInitialization(CG);
}
}
return Changed;
}
bool CGPassManager::doFinalization(CallGraph &CG) {
bool Changed = false;
for (unsigned i = 0, e = getNumContainedPasses(); i != e; ++i) {
if (PMDataManager *PM = getContainedPass(i)->getAsPMDataManager()) {
assert(PM->getPassManagerType() == PMT_FunctionPassManager &&
"Invalid CGPassManager member");
Changed |= ((FPPassManager*)PM)->doFinalization(CG.getModule());
} else {
Changed |= ((CallGraphSCCPass*)getContainedPass(i))->doFinalization(CG);
}
}
return Changed;
}
void CallGraphSCC::ReplaceNode(CallGraphNode *Old, CallGraphNode *New) {
assert(Old != New && "Should not replace node with self");
for (unsigned i = 0; ; ++i) {
assert(i != Nodes.size() && "Node not in SCC");
if (Nodes[i] != Old) continue;
if (New)
Nodes[i] = New;
else
Nodes.erase(Nodes.begin() + i);
break;
}
scc_iterator<CallGraph*> *CGI = (scc_iterator<CallGraph*>*)Context;
CGI->ReplaceNode(Old, New);
}
void CallGraphSCC::DeleteNode(CallGraphNode *Old) {
ReplaceNode(Old, nullptr);
}
void CallGraphSCCPass::assignPassManager(PMStack &PMS,
PassManagerType PreferredType) {
while (!PMS.empty() &&
PMS.top()->getPassManagerType() > PMT_CallGraphPassManager)
PMS.pop();
assert(!PMS.empty() && "Unable to handle Call Graph Pass");
CGPassManager *CGP;
if (PMS.top()->getPassManagerType() == PMT_CallGraphPassManager)
CGP = (CGPassManager*)PMS.top();
else {
assert(!PMS.empty() && "Unable to create Call Graph Pass Manager");
PMDataManager *PMD = PMS.top();
CGP = new CGPassManager();
PMTopLevelManager *TPM = PMD->getTopLevelManager();
TPM->addIndirectPassManager(CGP);
Pass *P = CGP;
TPM->schedulePass(P);
PMS.push(CGP);
}
CGP->add(this);
}
void CallGraphSCCPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<CallGraphWrapperPass>();
AU.addPreserved<CallGraphWrapperPass>();
}
namespace {
class PrintCallGraphPass : public CallGraphSCCPass {
std::string Banner;
raw_ostream &OS;
public:
static char ID;
PrintCallGraphPass(const std::string &B, raw_ostream &OS)
: CallGraphSCCPass(ID), Banner(B), OS(OS) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
bool runOnSCC(CallGraphSCC &SCC) override {
bool BannerPrinted = false;
auto PrintBannerOnce = [&]() {
if (BannerPrinted)
return;
OS << Banner;
BannerPrinted = true;
};
bool NeedModule = llvm::forcePrintModuleIR();
if (isFunctionInPrintList("*") && NeedModule) {
PrintBannerOnce();
OS << "\n";
SCC.getCallGraph().getModule().print(OS, nullptr);
return false;
}
bool FoundFunction = false;
for (CallGraphNode *CGN : SCC) {
if (Function *F = CGN->getFunction()) {
if (!F->isDeclaration() && isFunctionInPrintList(F->getName())) {
FoundFunction = true;
if (!NeedModule) {
PrintBannerOnce();
F->print(OS);
}
}
} else if (isFunctionInPrintList("*")) {
PrintBannerOnce();
OS << "\nPrinting <null> Function\n";
}
}
if (NeedModule && FoundFunction) {
PrintBannerOnce();
OS << "\n";
SCC.getCallGraph().getModule().print(OS, nullptr);
}
return false;
}
StringRef getPassName() const override { return "Print CallGraph IR"; }
};
}
char PrintCallGraphPass::ID = 0;
Pass *CallGraphSCCPass::createPrinterPass(raw_ostream &OS,
const std::string &Banner) const {
return new PrintCallGraphPass(Banner, OS);
}
static std::string getDescription(const CallGraphSCC &SCC) {
std::string Desc = "SCC (";
ListSeparator LS;
for (CallGraphNode *CGN : SCC) {
Desc += LS;
Function *F = CGN->getFunction();
if (F)
Desc += F->getName();
else
Desc += "<<null function>>";
}
Desc += ")";
return Desc;
}
bool CallGraphSCCPass::skipSCC(CallGraphSCC &SCC) const {
OptPassGate &Gate =
SCC.getCallGraph().getModule().getContext().getOptPassGate();
return Gate.isEnabled() && !Gate.shouldRunPass(this, getDescription(SCC));
}
char DummyCGSCCPass::ID = 0;
INITIALIZE_PASS(DummyCGSCCPass, "DummyCGSCCPass", "DummyCGSCCPass", false,
false)