@@ -99,6 +99,9 @@ STATISTIC(SkippedCallsCloning,
99
99
" Number of calls skipped during cloning due to unexpected operand" );
100
100
STATISTIC (MismatchedCloneAssignments,
101
101
" Number of callsites assigned to call multiple non-matching clones" );
102
+ STATISTIC (TotalMergeInvokes, " Number of merge invocations for nodes" );
103
+ STATISTIC (TotalMergeIters, " Number of merge iterations for nodes" );
104
+ STATISTIC (MaxMergeIters, " Max merge iterations for nodes" );
102
105
103
106
static cl::opt<std::string> DotFilePathPrefix (
104
107
" memprof-dot-file-path-prefix" , cl::init(" " ), cl::Hidden,
@@ -109,6 +112,11 @@ static cl::opt<bool> ExportToDot("memprof-export-to-dot", cl::init(false),
109
112
cl::Hidden,
110
113
cl::desc(" Export graph to dot files." ));
111
114
115
+ // TODO: Remove this option once new handling is validated more widely.
116
+ static cl::opt<bool > DoMergeIteration (
117
+ " memprof-merge-iteration" , cl::init(true ), cl::Hidden,
118
+ cl::desc(" Iteratively apply merging on a node to catch new callers" ));
119
+
112
120
// How much of the graph to export to dot.
113
121
enum DotScope {
114
122
All, // The full CCG graph.
@@ -3995,7 +4003,7 @@ IndexCallsiteContextGraph::getAllocationCallType(const CallInfo &Call) const {
3995
4003
3996
4004
void ModuleCallsiteContextGraph::updateCall (CallInfo &CallerCall,
3997
4005
FuncInfo CalleeFunc) {
3998
- auto *CurF = cast<CallBase> (CallerCall.call ())-> getCalledFunction ( );
4006
+ auto *CurF = getCalleeFunc (CallerCall.call ());
3999
4007
auto NewCalleeCloneNo = CalleeFunc.cloneNo ();
4000
4008
if (isMemProfClone (*CurF)) {
4001
4009
// If we already assigned this callsite to call a specific non-default
@@ -4191,16 +4199,36 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones(
4191
4199
if (!Inserted.second )
4192
4200
return ;
4193
4201
4194
- // Make a copy since the recursive call may move a caller edge to a new
4195
- // callee, messing up the iterator.
4196
- auto CallerEdges = Node->CallerEdges ;
4197
- for (auto CallerEdge : CallerEdges) {
4198
- // Skip any caller edge moved onto a different callee during recursion.
4199
- if (CallerEdge->Callee != Node)
4200
- continue ;
4201
- mergeClones (CallerEdge->Caller , Visited, ContextIdToAllocationNode);
4202
+ // Iteratively perform merging on this node to handle new caller nodes created
4203
+ // during the recursive traversal. We could do something more elegant such as
4204
+ // maintain a worklist, but this is a simple approach that doesn't cause a
4205
+ // measureable compile time effect, as most nodes don't have many caller
4206
+ // edges to check.
4207
+ bool FoundUnvisited = true ;
4208
+ unsigned Iters = 0 ;
4209
+ while (FoundUnvisited) {
4210
+ Iters++;
4211
+ FoundUnvisited = false ;
4212
+ // Make a copy since the recursive call may move a caller edge to a new
4213
+ // callee, messing up the iterator.
4214
+ auto CallerEdges = Node->CallerEdges ;
4215
+ for (auto CallerEdge : CallerEdges) {
4216
+ // Skip any caller edge moved onto a different callee during recursion.
4217
+ if (CallerEdge->Callee != Node)
4218
+ continue ;
4219
+ // If we found an unvisited caller, note that we should check the caller
4220
+ // edges again as mergeClones may add or change caller nodes.
4221
+ if (DoMergeIteration && !Visited.contains (CallerEdge->Caller ))
4222
+ FoundUnvisited = true ;
4223
+ mergeClones (CallerEdge->Caller , Visited, ContextIdToAllocationNode);
4224
+ }
4202
4225
}
4203
4226
4227
+ TotalMergeInvokes++;
4228
+ TotalMergeIters += Iters;
4229
+ if (Iters > MaxMergeIters)
4230
+ MaxMergeIters = Iters;
4231
+
4204
4232
// Merge for this node after we handle its callers.
4205
4233
mergeNodeCalleeClones (Node, Visited, ContextIdToAllocationNode);
4206
4234
}
0 commit comments