mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-23 02:29:18 +00:00
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@203435 91177308-0d34-0410-b5e6-96231b3b80d8
182 lines
6.7 KiB
C++
182 lines
6.7 KiB
C++
//===- LazyCallGraph.cpp - Analysis of a Module's call graph --------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Analysis/LazyCallGraph.h"
|
|
#include "llvm/ADT/SCCIterator.h"
|
|
#include "llvm/IR/CallSite.h"
|
|
#include "llvm/IR/InstVisitor.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/PassManager.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static void findCallees(
|
|
SmallVectorImpl<Constant *> &Worklist, SmallPtrSetImpl<Constant *> &Visited,
|
|
SmallVectorImpl<PointerUnion<Function *, LazyCallGraph::Node *>> &Callees,
|
|
SmallPtrSetImpl<Function *> &CalleeSet) {
|
|
while (!Worklist.empty()) {
|
|
Constant *C = Worklist.pop_back_val();
|
|
|
|
if (Function *F = dyn_cast<Function>(C)) {
|
|
// Note that we consider *any* function with a definition to be a viable
|
|
// edge. Even if the function's definition is subject to replacement by
|
|
// some other module (say, a weak definition) there may still be
|
|
// optimizations which essentially speculate based on the definition and
|
|
// a way to check that the specific definition is in fact the one being
|
|
// used. For example, this could be done by moving the weak definition to
|
|
// a strong (internal) definition and making the weak definition be an
|
|
// alias. Then a test of the address of the weak function against the new
|
|
// strong definition's address would be an effective way to determine the
|
|
// safety of optimizing a direct call edge.
|
|
if (!F->isDeclaration() && CalleeSet.insert(F))
|
|
Callees.push_back(F);
|
|
continue;
|
|
}
|
|
|
|
for (Value *Op : C->operand_values())
|
|
if (Visited.insert(cast<Constant>(Op)))
|
|
Worklist.push_back(cast<Constant>(Op));
|
|
}
|
|
}
|
|
|
|
LazyCallGraph::Node::Node(LazyCallGraph &G, Function &F) : G(G), F(F) {
|
|
SmallVector<Constant *, 16> Worklist;
|
|
SmallPtrSet<Constant *, 16> Visited;
|
|
// Find all the potential callees in this function. First walk the
|
|
// instructions and add every operand which is a constant to the worklist.
|
|
for (BasicBlock &BB : F)
|
|
for (Instruction &I : BB)
|
|
for (Value *Op : I.operand_values())
|
|
if (Constant *C = dyn_cast<Constant>(Op))
|
|
if (Visited.insert(C))
|
|
Worklist.push_back(C);
|
|
|
|
// We've collected all the constant (and thus potentially function or
|
|
// function containing) operands to all of the instructions in the function.
|
|
// Process them (recursively) collecting every function found.
|
|
findCallees(Worklist, Visited, Callees, CalleeSet);
|
|
}
|
|
|
|
LazyCallGraph::Node::Node(LazyCallGraph &G, const Node &OtherN)
|
|
: G(G), F(OtherN.F), CalleeSet(OtherN.CalleeSet) {
|
|
// Loop over the other node's callees, adding the Function*s to our list
|
|
// directly, and recursing to add the Node*s.
|
|
Callees.reserve(OtherN.Callees.size());
|
|
for (const auto &OtherCallee : OtherN.Callees)
|
|
if (Function *Callee = OtherCallee.dyn_cast<Function *>())
|
|
Callees.push_back(Callee);
|
|
else
|
|
Callees.push_back(G.copyInto(*OtherCallee.get<Node *>()));
|
|
}
|
|
|
|
LazyCallGraph::Node::Node(LazyCallGraph &G, Node &&OtherN)
|
|
: G(G), F(OtherN.F), Callees(std::move(OtherN.Callees)),
|
|
CalleeSet(std::move(OtherN.CalleeSet)) {
|
|
// Loop over our Callees. They've been moved from another node, but we need
|
|
// to move the Node*s to live under our bump ptr allocator.
|
|
for (auto &Callee : Callees)
|
|
if (Node *ChildN = Callee.dyn_cast<Node *>())
|
|
Callee = G.moveInto(std::move(*ChildN));
|
|
}
|
|
|
|
LazyCallGraph::LazyCallGraph(Module &M) : M(M) {
|
|
for (Function &F : M)
|
|
if (!F.isDeclaration() && !F.hasLocalLinkage())
|
|
if (EntryNodeSet.insert(&F))
|
|
EntryNodes.push_back(&F);
|
|
|
|
// Now add entry nodes for functions reachable via initializers to globals.
|
|
SmallVector<Constant *, 16> Worklist;
|
|
SmallPtrSet<Constant *, 16> Visited;
|
|
for (GlobalVariable &GV : M.globals())
|
|
if (GV.hasInitializer())
|
|
if (Visited.insert(GV.getInitializer()))
|
|
Worklist.push_back(GV.getInitializer());
|
|
|
|
findCallees(Worklist, Visited, EntryNodes, EntryNodeSet);
|
|
}
|
|
|
|
LazyCallGraph::LazyCallGraph(const LazyCallGraph &G)
|
|
: M(G.M), EntryNodeSet(G.EntryNodeSet) {
|
|
EntryNodes.reserve(G.EntryNodes.size());
|
|
for (const auto &EntryNode : G.EntryNodes)
|
|
if (Function *Callee = EntryNode.dyn_cast<Function *>())
|
|
EntryNodes.push_back(Callee);
|
|
else
|
|
EntryNodes.push_back(copyInto(*EntryNode.get<Node *>()));
|
|
}
|
|
|
|
// FIXME: This would be crazy simpler if BumpPtrAllocator were movable without
|
|
// invalidating any of the allocated memory. We should make that be the case at
|
|
// some point and delete this.
|
|
LazyCallGraph::LazyCallGraph(LazyCallGraph &&G)
|
|
: M(G.M), EntryNodes(std::move(G.EntryNodes)),
|
|
EntryNodeSet(std::move(G.EntryNodeSet)) {
|
|
// Loop over our EntryNodes. They've been moved from another graph, so we
|
|
// need to move the Node*s to live under our bump ptr allocator. We can just
|
|
// do this in-place.
|
|
for (auto &Entry : EntryNodes)
|
|
if (Node *EntryN = Entry.dyn_cast<Node *>())
|
|
Entry = moveInto(std::move(*EntryN));
|
|
}
|
|
|
|
LazyCallGraph::Node *LazyCallGraph::insertInto(Function &F, Node *&MappedN) {
|
|
return new (MappedN = BPA.Allocate()) Node(*this, F);
|
|
}
|
|
|
|
LazyCallGraph::Node *LazyCallGraph::copyInto(const Node &OtherN) {
|
|
Node *&N = NodeMap[&OtherN.F];
|
|
if (N)
|
|
return N;
|
|
|
|
return new (N = BPA.Allocate()) Node(*this, OtherN);
|
|
}
|
|
|
|
LazyCallGraph::Node *LazyCallGraph::moveInto(Node &&OtherN) {
|
|
Node *&N = NodeMap[&OtherN.F];
|
|
if (N)
|
|
return N;
|
|
|
|
return new (N = BPA.Allocate()) Node(*this, std::move(OtherN));
|
|
}
|
|
|
|
char LazyCallGraphAnalysis::PassID;
|
|
|
|
LazyCallGraphPrinterPass::LazyCallGraphPrinterPass(raw_ostream &OS) : OS(OS) {}
|
|
|
|
static void printNodes(raw_ostream &OS, LazyCallGraph::Node &N,
|
|
SmallPtrSetImpl<LazyCallGraph::Node *> &Printed) {
|
|
// Recurse depth first through the nodes.
|
|
for (LazyCallGraph::Node *ChildN : N)
|
|
if (Printed.insert(ChildN))
|
|
printNodes(OS, *ChildN, Printed);
|
|
|
|
OS << " Call edges in function: " << N.getFunction().getName() << "\n";
|
|
for (LazyCallGraph::iterator I = N.begin(), E = N.end(); I != E; ++I)
|
|
OS << " -> " << I->getFunction().getName() << "\n";
|
|
|
|
OS << "\n";
|
|
}
|
|
|
|
PreservedAnalyses LazyCallGraphPrinterPass::run(Module *M,
|
|
ModuleAnalysisManager *AM) {
|
|
LazyCallGraph &G = AM->getResult<LazyCallGraphAnalysis>(M);
|
|
|
|
OS << "Printing the call graph for module: " << M->getModuleIdentifier()
|
|
<< "\n\n";
|
|
|
|
SmallPtrSet<LazyCallGraph::Node *, 16> Printed;
|
|
for (LazyCallGraph::Node *N : G)
|
|
if (Printed.insert(N))
|
|
printNodes(OS, *N, Printed);
|
|
|
|
return PreservedAnalyses::all();
|
|
}
|