Skip to content

Commit 626ed22

Browse files
committed
[CallGraph] Refine call graph for indirect calls with !callees metadata
For indirect call sites having a small set of possible callees, !callees metadata can be used to indicate what those callees are. This patch updates the call graph and lazy call graph analyses so that they consider this metadata when encountering call sites. For the call graph, it adds a new external call graph node to the graph for each unique !callees metadata node. A call graph edge connects an indirect call site with the external node associated with the !callees metadata that is attached to it. And there is an edge from this external node to each of the callees indicated by the metadata. Similarly, for the lazy call graph, the patch adds Ref edges from a caller to the possible callees indicated by the metadata. The primary purpose of the patch is to facilitate iterating over the functions in a module such that all of the callees indicated by a given !callees metadata node will be visited prior to the functions containing call sites annotated by that node. This property is required by optimizations performing a bottom-up traversal of the SCC DAG. For example, the inliner can be made to inline through an indirect call. If the call site is annotated with !callees metadata, this patch ensures that the inliner will have visited all of the callees prior to the caller, allowing it to reliably compute the cost of inlining one or more of the potential callees. Original patch by @mssimpso. I've made some small changes to get it to apply, build, and pass tests on the top of tree, as well as some minor tweaks to formatting and functionality. Subscribers: mehdi_amini, hiraditya, llvm-commits, mssimpso Tags: #llvm Differential Revision: https://reviews.llvm.org/D39339 llvm-svn: 369025
1 parent 213d8a9 commit 626ed22

File tree

8 files changed

+159
-7
lines changed

8 files changed

+159
-7
lines changed

llvm/include/llvm/Analysis/CallGraph.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#define LLVM_ANALYSIS_CALLGRAPH_H
4747

4848
#include "llvm/ADT/GraphTraits.h"
49+
#include "llvm/ADT/MapVector.h"
4950
#include "llvm/ADT/STLExtras.h"
5051
#include "llvm/IR/Function.h"
5152
#include "llvm/IR/InstrTypes.h"
@@ -76,9 +77,22 @@ class CallGraph {
7677
using FunctionMapTy =
7778
std::map<const Function *, std::unique_ptr<CallGraphNode>>;
7879

80+
/// \brief A type for maintaining dummy nodes.
81+
///
82+
/// Dummy nodes (i.e., nodes having a null function) include, for example,
83+
/// those created to represent !callees metadata. We use a void pointer as
84+
/// the key to allow for various kinds of dummy nodes. We use a MapVector to
85+
/// ensure a deterministic iteration order (there's no good way to sort dummy
86+
/// nodes). A deterministic iteration order is primarily useful for printing.
87+
using DummyNodeMapTy =
88+
MapVector<const MDNode *, std::unique_ptr<CallGraphNode>>;
89+
7990
/// A map from \c Function* to \c CallGraphNode*.
8091
FunctionMapTy FunctionMap;
8192

93+
/// \brief A map for maintaining dummy nodes.
94+
DummyNodeMapTy DummyNodeMap;
95+
8296
/// This node has edges to all external functions and those internal
8397
/// functions that have their address taken.
8498
CallGraphNode *ExternalCallingNode;
@@ -155,6 +169,9 @@ class CallGraph {
155169
/// Similar to operator[], but this will insert a new CallGraphNode for
156170
/// \c F if one does not already exist.
157171
CallGraphNode *getOrInsertFunction(const Function *F);
172+
173+
/// \brief Return the dummy node associated with the given metadata node.
174+
CallGraphNode *getOrInsertNodeForCalleesMD(MDNode *Callees);
158175
};
159176

160177
/// A node in the call graph for a module.

llvm/include/llvm/IR/CallSite.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,30 @@ class CallSiteBase {
663663
return false;
664664
}
665665

666+
/// Return the set of functions this call site is known to call. For direct
667+
/// calls, the returned set contains the called function. For indirect calls,
668+
/// this function collects known callees from !callees metadata, if present.
669+
SmallVector<FunTy *, 1> getKnownCallees() {
670+
SmallVector<FunTy *, 1> Callees;
671+
672+
if (getCalledFunction()) {
673+
// If the call site is direct, just add the called function to the set.
674+
Callees.push_back(getCalledFunction());
675+
return Callees;
676+
}
677+
678+
InstrTy *Inst = getInstruction();
679+
if (auto *Node = Inst->getMetadata(LLVMContext::MD_callees)) {
680+
// Otherwise, if the call site is indirect, collect the known callees from
681+
// !callees metadata if present.
682+
for (const MDOperand &Op : Node->operands())
683+
if (auto *MDConstant = mdconst::extract_or_null<Constant>(Op))
684+
Callees.push_back(cast<FunTy>(MDConstant));
685+
}
686+
687+
return Callees;
688+
}
689+
666690
private:
667691
IterTy getCallee() const {
668692
return cast<CallBase>(getInstruction())->op_end() - 1;

llvm/lib/Analysis/CGSCCPassManager.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ LazyCallGraph::SCC &llvm::updateCGAndAnalysisManagerForFunctionPass(
449449
// irrelevant.
450450
for (Instruction &I : instructions(F))
451451
if (auto CS = CallSite(&I))
452-
if (Function *Callee = CS.getCalledFunction())
452+
for (Function *Callee : CS.getKnownCallees()) {
453453
if (Visited.insert(Callee).second && !Callee->isDeclaration()) {
454454
Node &CalleeN = *G.lookup(*Callee);
455455
Edge *E = N->lookup(CalleeN);
@@ -467,6 +467,7 @@ LazyCallGraph::SCC &llvm::updateCGAndAnalysisManagerForFunctionPass(
467467
if (!E->isCall())
468468
PromotedRefTargets.insert(&CalleeN);
469469
}
470+
}
470471

471472
// Now walk all references.
472473
for (Instruction &I : instructions(F))

llvm/lib/Analysis/CallGraph.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ CallGraph::CallGraph(Module &M)
3737

3838
CallGraph::CallGraph(CallGraph &&Arg)
3939
: M(Arg.M), FunctionMap(std::move(Arg.FunctionMap)),
40+
DummyNodeMap(std::move(Arg.DummyNodeMap)),
4041
ExternalCallingNode(Arg.ExternalCallingNode),
4142
CallsExternalNode(std::move(Arg.CallsExternalNode)) {
4243
Arg.FunctionMap.clear();
@@ -53,6 +54,8 @@ CallGraph::~CallGraph() {
5354
#ifndef NDEBUG
5455
for (auto &I : FunctionMap)
5556
I.second->allReferencesDropped();
57+
for (auto &N : DummyNodeMap)
58+
N.second->allReferencesDropped();
5659
#endif
5760
}
5861

@@ -74,7 +77,14 @@ void CallGraph::addToCallGraph(Function *F) {
7477
for (Instruction &I : BB) {
7578
if (auto *Call = dyn_cast<CallBase>(&I)) {
7679
const Function *Callee = Call->getCalledFunction();
77-
if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID()))
80+
MDNode *CalleesMD = I.getMetadata(LLVMContext::MD_callees);
81+
if (!Callee && CalleesMD)
82+
// If an indirect call site has !callees metadata indicating its
83+
// possible callees, we add an edge from the call site to a dummy
84+
// node. When we construct the dummy node, we add edges from it to
85+
// the functions indicated in the !callees metadata.
86+
Node->addCalledFunction(Call, getOrInsertNodeForCalleesMD(CalleesMD));
87+
else if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID()))
7888
// Indirect calls of intrinsics are not allowed so no need to check.
7989
// We can be more precise here by using TargetArg returned by
8090
// Intrinsic::isLeaf.
@@ -105,6 +115,11 @@ void CallGraph::print(raw_ostream &OS) const {
105115

106116
for (CallGraphNode *CN : Nodes)
107117
CN->print(OS);
118+
119+
// The iteration order of the DummyNodeMap is deterministic, so we don't need
120+
// to sort the nodes. Just print them.
121+
for (auto &Entry : DummyNodeMap)
122+
Entry.second->print(OS);
108123
}
109124

110125
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -120,6 +135,12 @@ LLVM_DUMP_METHOD void CallGraph::dump() const { print(dbgs()); }
120135
Function *CallGraph::removeFunctionFromModule(CallGraphNode *CGN) {
121136
assert(CGN->empty() && "Cannot remove function from call "
122137
"graph if it references other functions!");
138+
139+
// If any dummy node references the node for the given function, we first
140+
// need to remove those edges.
141+
for (auto &Entry : DummyNodeMap)
142+
Entry.second->removeAnyCallEdgeTo(CGN);
143+
123144
Function *F = CGN->getFunction(); // Get the function for the call graph node
124145
FunctionMap.erase(F); // Remove the call graph node from the map
125146

@@ -154,6 +175,21 @@ CallGraphNode *CallGraph::getOrInsertFunction(const Function *F) {
154175
return CGN.get();
155176
}
156177

178+
CallGraphNode *CallGraph::getOrInsertNodeForCalleesMD(MDNode *Callees) {
179+
auto &CGN = DummyNodeMap[Callees];
180+
if (CGN)
181+
return CGN.get();
182+
CGN = llvm::make_unique<CallGraphNode>(nullptr);
183+
for (const MDOperand &Op : Callees->operands())
184+
if (auto *MDConstant = mdconst::extract_or_null<Constant>(Op)) {
185+
auto *F = cast<Function>(MDConstant);
186+
187+
assert(!F->isIntrinsic());
188+
CGN->addCalledFunction(nullptr, getOrInsertFunction(F));
189+
}
190+
return CGN.get();
191+
}
192+
157193
//===----------------------------------------------------------------------===//
158194
// Implementations of the CallGraphNode class methods.
159195
//
@@ -171,7 +207,7 @@ void CallGraphNode::print(raw_ostream &OS) const {
171207
if (Function *FI = I.second->getFunction())
172208
OS << "function '" << FI->getName() <<"'\n";
173209
else
174-
OS << "external node\n";
210+
OS << "<<null function>><<" << I.second << ">>\n";
175211
}
176212
OS << '\n';
177213
}

llvm/lib/Analysis/LazyCallGraph.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,14 @@ LazyCallGraph::EdgeSequence &LazyCallGraph::Node::populateSlow() {
100100
for (BasicBlock &BB : *F)
101101
for (Instruction &I : BB) {
102102
if (auto CS = CallSite(&I))
103-
if (Function *Callee = CS.getCalledFunction())
103+
for (Function *Callee : CS.getKnownCallees())
104104
if (!Callee->isDeclaration())
105105
if (Callees.insert(Callee).second) {
106106
Visited.insert(Callee);
107+
auto EdgeK = CS.getCalledFunction() ? LazyCallGraph::Edge::Call
108+
: LazyCallGraph::Edge::Ref;
107109
addEdge(Edges->Edges, Edges->EdgeIndexMap, G->get(*Callee),
108-
LazyCallGraph::Edge::Call);
110+
EdgeK);
109111
}
110112

111113
for (Value *Op : I.operand_values())
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
; RUN: opt < %s -print-callgraph -disable-output 2>&1 | FileCheck %s
2+
3+
; CHECK: Call graph node <<null function>><<{{.+}}>> #uses=0
4+
; CHECK-DAG: CS<0x0> calls function 'main'
5+
; CHECK-DAG: CS<0x0> calls function 'add'
6+
; CHECK-DAG: CS<0x0> calls function 'sub'
7+
;
8+
; CHECK: Call graph node for function: 'add'<<{{.+}}>> #uses=2
9+
;
10+
; CHECK: Call graph node for function: 'main'<<{{.+}}>> #uses=1
11+
; CHECK-NEXT: CS<{{.+}}> calls <<null function>><<[[CALLEES:.+]]>>
12+
;
13+
; CHECK: Call graph node for function: 'sub'<<{{.+}}>> #uses=2
14+
;
15+
; CHECK: Call graph node <<null function>><<[[CALLEES]]>> #uses=1
16+
; CHECK-DAG: CS<0x0> calls function 'add'
17+
; CHECK-DAG: CS<0x0> calls function 'sub'
18+
19+
define i64 @main(i64 %x, i64 %y, i64 (i64, i64)* %binop) {
20+
%tmp0 = call i64 %binop(i64 %x, i64 %y), !callees !0
21+
ret i64 %tmp0
22+
}
23+
24+
define i64 @add(i64 %x, i64 %y) {
25+
%tmp0 = add i64 %x, %y
26+
ret i64 %tmp0
27+
}
28+
29+
define i64 @sub(i64 %x, i64 %y) {
30+
%tmp0 = sub i64 %x, %y
31+
ret i64 %tmp0
32+
}
33+
34+
!0 = !{i64 (i64, i64)* @add, i64 (i64, i64)* @sub}

llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ entry:
2626
; CHECK: CS<0x0> calls function 'f'
2727

2828
; CHECK: Call graph node for function: 'calls_patchpoint'
29-
; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls external node
29+
; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls <<null function>>
3030

3131
; CHECK: Call graph node for function: 'calls_statepoint'
32-
; CHECK-NEXT: CS<[[addr_0:[^>]+]]> calls external node
32+
; CHECK-NEXT: CS<[[addr_0:[^>]+]]> calls <<null function>>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
; RUN: opt < %s -passes=print-lcg -disable-output 2>&1 | FileCheck %s
2+
3+
; CHECK: Edges in function: main
4+
; CHECK-DAG: ref -> add
5+
; CHECK-DAG: ref -> sub
6+
;
7+
; CHECK: Edges in function: add
8+
;
9+
; CHECK: Edges in function: sub
10+
;
11+
; CHECK: RefSCC with 1 call SCCs:
12+
; CHECK-NEXT: SCC with 1 functions:
13+
; CHECK-NEXT: sub
14+
;
15+
; CHECK: RefSCC with 1 call SCCs:
16+
; CHECK-NEXT: SCC with 1 functions:
17+
; CHECK-NEXT: add
18+
;
19+
; CHECK: RefSCC with 1 call SCCs:
20+
; CHECK-NEXT: SCC with 1 functions:
21+
; CHECK-NEXT: main
22+
23+
define i64 @main(i64 %x, i64 %y, i64 (i64, i64)* %binop) {
24+
%tmp0 = call i64 %binop(i64 %x, i64 %y), !callees !0
25+
ret i64 %tmp0
26+
}
27+
28+
define i64 @add(i64 %x, i64 %y) {
29+
%tmp0 = add i64 %x, %y
30+
ret i64 %tmp0
31+
}
32+
33+
define i64 @sub(i64 %x, i64 %y) {
34+
%tmp0 = sub i64 %x, %y
35+
ret i64 %tmp0
36+
}
37+
38+
!0 = !{i64 (i64, i64)* @add, i64 (i64, i64)* @sub}

0 commit comments

Comments
 (0)