Skip to content

Commit 72277ec

Browse files
committed
Introduce a CallGraph updater helper class
The CallGraphUpdater is a helper that simplifies the process of updating the call graph, both old and new style, while running an CGSCC pass. The uses are contained in different commits, e.g. D70767. More functionality is added as we need it. Reviewed By: modocache, hfinkel Differential Revision: https://reviews.llvm.org/D70927
1 parent f8c9ceb commit 72277ec

File tree

13 files changed

+635
-6
lines changed

13 files changed

+635
-6
lines changed

llvm/include/llvm/Analysis/CallGraph.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,6 @@ class CallGraph {
9494
/// callers from the old function to the new.
9595
void spliceFunction(const Function *From, const Function *To);
9696

97-
/// Add a function to the call graph, and link the node to all of the
98-
/// functions that it calls.
99-
void addToCallGraph(Function *F);
100-
10197
public:
10298
explicit CallGraph(Module &M);
10399
CallGraph(CallGraph &&Arg);
@@ -158,6 +154,13 @@ class CallGraph {
158154
/// Similar to operator[], but this will insert a new CallGraphNode for
159155
/// \c F if one does not already exist.
160156
CallGraphNode *getOrInsertFunction(const Function *F);
157+
158+
/// Populate \p CGN based on the calls inside the associated function.
159+
void populateCallGraphNode(CallGraphNode *CGN);
160+
161+
/// Add a function to the call graph, and link the node to all of the
162+
/// functions that it calls.
163+
void addToCallGraph(Function *F);
161164
};
162165

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

llvm/include/llvm/Analysis/LazyCallGraph.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,9 @@ class LazyCallGraph {
10581058
/// fully visited by the DFS prior to calling this routine.
10591059
void removeDeadFunction(Function &F);
10601060

1061+
/// Introduce a node for the function \p NewF in the SCC \p C.
1062+
void addNewFunctionIntoSCC(Function &NewF, SCC &C);
1063+
10611064
///@}
10621065

10631066
///@{
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===- CallGraphUpdater.h - A (lazy) call graph update helper ---*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
///
10+
/// This file provides interfaces used to manipulate a call graph, regardless
11+
/// if it is a "old style" CallGraph or an "new style" LazyCallGraph.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_TRANSFORMS_UTILS_CALLGRAPHUPDATER_H
16+
#define LLVM_TRANSFORMS_UTILS_CALLGRAPHUPDATER_H
17+
18+
#include "llvm/Analysis/CGSCCPassManager.h"
19+
#include "llvm/Analysis/CallGraph.h"
20+
#include "llvm/Analysis/CallGraphSCCPass.h"
21+
#include "llvm/Analysis/LazyCallGraph.h"
22+
23+
namespace llvm {
24+
25+
/// Wrapper to unify "old style" CallGraph and "new style" LazyCallGraph. This
26+
/// simplifies the interface and the call sites, e.g., new and old pass manager
27+
/// passes can share the same code.
28+
class CallGraphUpdater {
29+
/// Containers for functions which we did replace or want to delete when
30+
/// `finalize` is called. This can happen explicitly or as part of the
31+
/// destructor. Dead functions in comdat sections are tracked seperatly
32+
/// because a function with discardable linakage in a COMDAT should only
33+
/// be dropped if the entire COMDAT is dropped, see git ac07703842cf.
34+
///{
35+
SmallPtrSet<Function *, 16> ReplacedFunctions;
36+
SmallVector<Function *, 16> DeadFunctions;
37+
SmallVector<Function *, 16> DeadFunctionsInComdats;
38+
///}
39+
40+
/// Old PM variables
41+
///{
42+
CallGraph *CG = nullptr;
43+
CallGraphSCC *CGSCC = nullptr;
44+
///}
45+
46+
/// New PM variables
47+
///{
48+
LazyCallGraph *LCG = nullptr;
49+
LazyCallGraph::SCC *SCC = nullptr;
50+
CGSCCAnalysisManager *AM = nullptr;
51+
CGSCCUpdateResult *UR = nullptr;
52+
///}
53+
54+
public:
55+
CallGraphUpdater() {}
56+
~CallGraphUpdater() { finalize(); }
57+
58+
/// Initializers for usage outside of a CGSCC pass, inside a CGSCC pass in
59+
/// the old and new pass manager (PM).
60+
///{
61+
void initialize(CallGraph &CG, CallGraphSCC &SCC) {
62+
this->CG = &CG;
63+
this->CGSCC = &SCC;
64+
}
65+
void initialize(LazyCallGraph &LCG, LazyCallGraph::SCC &SCC,
66+
CGSCCAnalysisManager &AM, CGSCCUpdateResult &UR) {
67+
this->LCG = &LCG;
68+
this->SCC = &SCC;
69+
this->AM = &AM;
70+
this->UR = &UR;
71+
}
72+
///}
73+
74+
/// Finalizer that will trigger actions like function removal from the CG.
75+
bool finalize();
76+
77+
/// Remove \p Fn from the call graph.
78+
void removeFunction(Function &Fn);
79+
80+
/// After an CGSCC pass changes a function in ways that affect the call
81+
/// graph, this method can be called to update it.
82+
void reanalyzeFunction(Function &Fn);
83+
84+
/// If a new function was created by outlining, this method can be called
85+
/// to update the call graph for the new function. Note that the old one
86+
/// still needs to be re-analyzed or manually updated.
87+
void registerOutlinedFunction(Function &NewFn);
88+
89+
/// Replace \p OldFn in the call graph (and SCC) with \p NewFn. The uses
90+
/// outside the call graph and the function \p OldFn are not modified.
91+
/// Note that \p OldFn is also removed from the call graph
92+
/// (\see removeFunction).
93+
void replaceFunctionWith(Function &OldFn, Function &NewFn);
94+
95+
/// Remove the call site \p CS from the call graph.
96+
void removeCallSite(CallBase &CS);
97+
98+
/// Replace \p OldCS with the new call site \p NewCS.
99+
/// \return True if the replacement was successful, otherwise False. In the
100+
/// latter case the parent function of \p OldCB needs to be re-analyzed.
101+
bool replaceCallSite(CallBase &OldCS, CallBase &NewCS);
102+
};
103+
104+
} // end namespace llvm
105+
106+
#endif // LLVM_TRANSFORMS_UTILS_CALLGRAPHUPDATER_H

llvm/lib/Analysis/CallGraph.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ void CallGraph::addToCallGraph(Function *F) {
7474
if (!F->hasLocalLinkage() || F->hasAddressTaken())
7575
ExternalCallingNode->addCalledFunction(nullptr, Node);
7676

77+
populateCallGraphNode(Node);
78+
}
79+
80+
void CallGraph::populateCallGraphNode(CallGraphNode *Node) {
81+
Function *F = Node->getFunction();
82+
7783
// If this function is not defined in this translation unit, it could call
7884
// anything.
7985
if (F->isDeclaration() && !F->isIntrinsic())

llvm/lib/Analysis/CallGraphSCCPass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,10 @@ void CallGraphSCC::ReplaceNode(CallGraphNode *Old, CallGraphNode *New) {
549549
for (unsigned i = 0; ; ++i) {
550550
assert(i != Nodes.size() && "Node not in SCC");
551551
if (Nodes[i] != Old) continue;
552-
Nodes[i] = New;
552+
if (New)
553+
Nodes[i] = New;
554+
else
555+
Nodes.erase(Nodes.begin() + i);
553556
break;
554557
}
555558

llvm/lib/Analysis/LazyCallGraph.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,15 @@ void LazyCallGraph::removeDeadFunction(Function &F) {
15661566
// allocators.
15671567
}
15681568

1569+
void LazyCallGraph::addNewFunctionIntoSCC(Function &NewF, SCC &C) {
1570+
Node &CGNode = get(NewF);
1571+
CGNode.DFSNumber = CGNode.LowLink = -1;
1572+
CGNode.populate();
1573+
C.Nodes.push_back(&CGNode);
1574+
SCCMap[&CGNode] = &C;
1575+
NodeMap[&NewF] = &CGNode;
1576+
}
1577+
15691578
LazyCallGraph::Node &LazyCallGraph::insertInto(Function &F, Node *&MappedN) {
15701579
return *new (MappedN = BPA.Allocate()) Node(*this, F);
15711580
}

llvm/lib/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMTransformUtils
77
BuildLibCalls.cpp
88
BypassSlowDivision.cpp
99
CallPromotionUtils.cpp
10+
CallGraphUpdater.cpp
1011
CanonicalizeAliases.cpp
1112
CloneFunction.cpp
1213
CloneModule.cpp
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//===- CallGraphUpdater.cpp - A (lazy) call graph update helper -----------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
///
10+
/// This file provides interfaces used to manipulate a call graph, regardless
11+
/// if it is a "old style" CallGraph or an "new style" LazyCallGraph.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#include "llvm/Transforms/Utils/CallGraphUpdater.h"
16+
#include "llvm/ADT/STLExtras.h"
17+
#include "llvm/Transforms/Utils/ModuleUtils.h"
18+
19+
using namespace llvm;
20+
21+
bool CallGraphUpdater::finalize() {
22+
if (!DeadFunctionsInComdats.empty()) {
23+
filterDeadComdatFunctions(*DeadFunctionsInComdats.front()->getParent(),
24+
DeadFunctionsInComdats);
25+
DeadFunctions.append(DeadFunctionsInComdats.begin(),
26+
DeadFunctionsInComdats.end());
27+
}
28+
29+
for (Function *DeadFn : DeadFunctions) {
30+
DeadFn->removeDeadConstantUsers();
31+
32+
if (CG) {
33+
CallGraphNode *OldCGN = CG->getOrInsertFunction(DeadFn);
34+
CG->getExternalCallingNode()->removeAnyCallEdgeTo(OldCGN);
35+
OldCGN->removeAllCalledFunctions();
36+
DeadFn->replaceAllUsesWith(UndefValue::get(DeadFn->getType()));
37+
38+
assert(OldCGN->getNumReferences() == 0);
39+
40+
delete CG->removeFunctionFromModule(OldCGN);
41+
continue;
42+
}
43+
44+
// The old style call graph (CG) has a value handle we do not want to
45+
// replace with undef so we do this here.
46+
DeadFn->replaceAllUsesWith(UndefValue::get(DeadFn->getType()));
47+
48+
if (LCG && !ReplacedFunctions.count(DeadFn)) {
49+
// Taken mostly from the inliner:
50+
FunctionAnalysisManager &FAM =
51+
AM->getResult<FunctionAnalysisManagerCGSCCProxy>(*SCC, *LCG)
52+
.getManager();
53+
54+
LazyCallGraph::Node &N = LCG->get(*DeadFn);
55+
auto *DeadSCC = LCG->lookupSCC(N);
56+
assert(DeadSCC && DeadSCC->size() == 1 &&
57+
&DeadSCC->begin()->getFunction() == DeadFn);
58+
auto &DeadRC = DeadSCC->getOuterRefSCC();
59+
60+
FAM.clear(*DeadFn, DeadFn->getName());
61+
AM->clear(*DeadSCC, DeadSCC->getName());
62+
LCG->removeDeadFunction(*DeadFn);
63+
64+
// Mark the relevant parts of the call graph as invalid so we don't visit
65+
// them.
66+
UR->InvalidatedSCCs.insert(DeadSCC);
67+
UR->InvalidatedRefSCCs.insert(&DeadRC);
68+
}
69+
70+
// The function is now really dead and de-attached from everything.
71+
DeadFn->eraseFromParent();
72+
}
73+
74+
bool Changed = !DeadFunctions.empty();
75+
DeadFunctionsInComdats.clear();
76+
DeadFunctions.clear();
77+
return Changed;
78+
}
79+
80+
void CallGraphUpdater::reanalyzeFunction(Function &Fn) {
81+
if (CG) {
82+
CallGraphNode *OldCGN = CG->getOrInsertFunction(&Fn);
83+
OldCGN->removeAllCalledFunctions();
84+
CG->populateCallGraphNode(OldCGN);
85+
} else if (LCG) {
86+
LazyCallGraph::Node &N = LCG->get(Fn);
87+
LazyCallGraph::SCC *C = LCG->lookupSCC(N);
88+
updateCGAndAnalysisManagerForCGSCCPass(*LCG, *C, N, *AM, *UR);
89+
}
90+
}
91+
92+
void CallGraphUpdater::registerOutlinedFunction(Function &NewFn) {
93+
if (CG)
94+
CG->addToCallGraph(&NewFn);
95+
else if (LCG)
96+
LCG->addNewFunctionIntoSCC(NewFn, *SCC);
97+
}
98+
99+
void CallGraphUpdater::removeFunction(Function &DeadFn) {
100+
DeadFn.deleteBody();
101+
DeadFn.setLinkage(GlobalValue::ExternalLinkage);
102+
if (DeadFn.hasComdat())
103+
DeadFunctionsInComdats.push_back(&DeadFn);
104+
else
105+
DeadFunctions.push_back(&DeadFn);
106+
}
107+
108+
void CallGraphUpdater::replaceFunctionWith(Function &OldFn, Function &NewFn) {
109+
ReplacedFunctions.insert(&OldFn);
110+
if (CG) {
111+
// Update the call graph for the newly promoted function.
112+
// CG->spliceFunction(&OldFn, &NewFn);
113+
CallGraphNode *OldCGN = (*CG)[&OldFn];
114+
CallGraphNode *NewCGN = CG->getOrInsertFunction(&NewFn);
115+
NewCGN->stealCalledFunctionsFrom(OldCGN);
116+
117+
// And update the SCC we're iterating as well.
118+
CGSCC->ReplaceNode(OldCGN, NewCGN);
119+
} else if (LCG) {
120+
// Directly substitute the functions in the call graph.
121+
LazyCallGraph::Node &OldLCGN = LCG->get(OldFn);
122+
SCC->getOuterRefSCC().replaceNodeFunction(OldLCGN, NewFn);
123+
}
124+
removeFunction(OldFn);
125+
}
126+
127+
bool CallGraphUpdater::replaceCallSite(CallBase &OldCS, CallBase &NewCS) {
128+
// This is only necessary in the (old) CG.
129+
if (!CG)
130+
return true;
131+
132+
Function *Caller = OldCS.getCaller();
133+
CallGraphNode *NewCalleeNode =
134+
CG->getOrInsertFunction(NewCS.getCalledFunction());
135+
CallGraphNode *CallerNode = (*CG)[Caller];
136+
if (llvm::none_of(*CallerNode, [&OldCS](const CallGraphNode::CallRecord &CR) {
137+
return CR.first == &OldCS;
138+
}))
139+
return false;
140+
CallerNode->replaceCallEdge(OldCS, NewCS, NewCalleeNode);
141+
return true;
142+
}
143+
144+
void CallGraphUpdater::removeCallSite(CallBase &CS) {
145+
// This is only necessary in the (old) CG.
146+
if (!CG)
147+
return;
148+
149+
Function *Caller = CS.getCaller();
150+
CallGraphNode *CallerNode = (*CG)[Caller];
151+
CallerNode->removeCallEdgeFor(CS);
152+
}

0 commit comments

Comments
 (0)