Skip to content

Commit 082e7c4

Browse files
[MemProf] Remove empty edges once after cloning (llvm#85320)
Restructure the handling of edges that become empty during the cloning process. Instead of removing them as they become empty (no context ids and alloc type), do this once after all cloning is complete. This has no effect on the cloning result, but prepares for a follow on change that does improve the cloning. The structural change here reduces the diffs for the follow on change, which would be much more difficult with the previous handling.
1 parent 7545c63 commit 082e7c4

File tree

1 file changed

+54
-30
lines changed

1 file changed

+54
-30
lines changed

llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,12 @@ class CallsiteContextGraph {
310310
// True if this node was effectively removed from the graph, in which case
311311
// its context id set, caller edges, and callee edges should all be empty.
312312
bool isRemoved() const {
313-
assert(ContextIds.empty() ==
314-
(CalleeEdges.empty() && CallerEdges.empty()));
313+
// Note that we can have non-empty context ids with empty caller and
314+
// callee edges if the graph ends up with a single node.
315+
if (ContextIds.empty())
316+
assert(CalleeEdges.empty() && CallerEdges.empty() &&
317+
"Context ids empty but at least one of callee and caller edges "
318+
"were not!");
315319
return ContextIds.empty();
316320
}
317321

@@ -353,9 +357,12 @@ class CallsiteContextGraph {
353357
}
354358
};
355359

356-
/// Helper to remove callee edges that have allocation type None (due to not
360+
/// Helpers to remove callee edges that have allocation type None (due to not
357361
/// carrying any context ids) after transformations.
358362
void removeNoneTypeCalleeEdges(ContextNode *Node);
363+
void
364+
recursivelyRemoveNoneTypeCalleeEdges(ContextNode *Node,
365+
DenseSet<const ContextNode *> &Visited);
359366

360367
protected:
361368
/// Get a list of nodes corresponding to the stack ids in the given callsite
@@ -2431,11 +2438,43 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
24312438
}
24322439
}
24332440

2441+
template <typename DerivedCCG, typename FuncTy, typename CallTy>
2442+
void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
2443+
recursivelyRemoveNoneTypeCalleeEdges(
2444+
ContextNode *Node, DenseSet<const ContextNode *> &Visited) {
2445+
auto Inserted = Visited.insert(Node);
2446+
if (!Inserted.second)
2447+
return;
2448+
2449+
removeNoneTypeCalleeEdges(Node);
2450+
2451+
for (auto *Clone : Node->Clones)
2452+
recursivelyRemoveNoneTypeCalleeEdges(Clone, Visited);
2453+
2454+
// The recursive call may remove some of this Node's caller edges.
2455+
// Iterate over a copy and skip any that were removed.
2456+
auto CallerEdges = Node->CallerEdges;
2457+
for (auto &Edge : CallerEdges) {
2458+
// Skip any that have been removed by an earlier recursive call.
2459+
if (Edge->Callee == nullptr && Edge->Caller == nullptr) {
2460+
assert(!std::count(Node->CallerEdges.begin(), Node->CallerEdges.end(),
2461+
Edge));
2462+
continue;
2463+
}
2464+
recursivelyRemoveNoneTypeCalleeEdges(Edge->Caller, Visited);
2465+
}
2466+
}
2467+
24342468
template <typename DerivedCCG, typename FuncTy, typename CallTy>
24352469
void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones() {
24362470
DenseSet<const ContextNode *> Visited;
24372471
for (auto &Entry : AllocationCallToContextNodeMap)
24382472
identifyClones(Entry.second, Visited);
2473+
Visited.clear();
2474+
for (auto &Entry : AllocationCallToContextNodeMap)
2475+
recursivelyRemoveNoneTypeCalleeEdges(Entry.second, Visited);
2476+
if (VerifyCCG)
2477+
check();
24392478
}
24402479

24412480
// helper function to check an AllocType is cold or notcold or both.
@@ -2450,7 +2489,7 @@ template <typename DerivedCCG, typename FuncTy, typename CallTy>
24502489
void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
24512490
ContextNode *Node, DenseSet<const ContextNode *> &Visited) {
24522491
if (VerifyNodes)
2453-
checkNode<DerivedCCG, FuncTy, CallTy>(Node);
2492+
checkNode<DerivedCCG, FuncTy, CallTy>(Node, /*CheckEdges=*/false);
24542493
assert(!Node->CloneOf);
24552494

24562495
// If Node as a null call, then either it wasn't found in the module (regular
@@ -2511,8 +2550,16 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
25112550
std::stable_sort(Node->CallerEdges.begin(), Node->CallerEdges.end(),
25122551
[&](const std::shared_ptr<ContextEdge> &A,
25132552
const std::shared_ptr<ContextEdge> &B) {
2514-
assert(checkColdOrNotCold(A->AllocTypes) &&
2515-
checkColdOrNotCold(B->AllocTypes));
2553+
// Nodes with non-empty context ids should be sorted before
2554+
// those with empty context ids.
2555+
if (A->ContextIds.empty())
2556+
// Either B ContextIds are non-empty (in which case we
2557+
// should return false because B < A), or B ContextIds
2558+
// are empty, in which case they are equal, and we should
2559+
// maintain the original relative ordering.
2560+
return false;
2561+
if (B->ContextIds.empty())
2562+
return true;
25162563

25172564
if (A->AllocTypes == B->AllocTypes)
25182565
// Use the first context id for each edge as a
@@ -2591,39 +2638,16 @@ void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
25912638
Node->AllocTypes != (uint8_t)AllocationType::None);
25922639
// Sanity check that no alloc types on clone or its edges are None.
25932640
assert(Clone->AllocTypes != (uint8_t)AllocationType::None);
2594-
assert(llvm::none_of(
2595-
Clone->CallerEdges, [&](const std::shared_ptr<ContextEdge> &E) {
2596-
return E->AllocTypes == (uint8_t)AllocationType::None;
2597-
}));
25982641
}
25992642

2600-
// Cloning may have resulted in some cloned callee edges with type None,
2601-
// because they aren't carrying any contexts. Remove those edges.
2602-
for (auto *Clone : Node->Clones) {
2603-
removeNoneTypeCalleeEdges(Clone);
2604-
if (VerifyNodes)
2605-
checkNode<DerivedCCG, FuncTy, CallTy>(Clone);
2606-
}
26072643
// We should still have some context ids on the original Node.
26082644
assert(!Node->ContextIds.empty());
26092645

2610-
// Remove any callee edges that ended up with alloc type None after creating
2611-
// clones and updating callee edges.
2612-
removeNoneTypeCalleeEdges(Node);
2613-
26142646
// Sanity check that no alloc types on node or edges are None.
26152647
assert(Node->AllocTypes != (uint8_t)AllocationType::None);
2616-
assert(llvm::none_of(Node->CalleeEdges,
2617-
[&](const std::shared_ptr<ContextEdge> &E) {
2618-
return E->AllocTypes == (uint8_t)AllocationType::None;
2619-
}));
2620-
assert(llvm::none_of(Node->CallerEdges,
2621-
[&](const std::shared_ptr<ContextEdge> &E) {
2622-
return E->AllocTypes == (uint8_t)AllocationType::None;
2623-
}));
26242648

26252649
if (VerifyNodes)
2626-
checkNode<DerivedCCG, FuncTy, CallTy>(Node);
2650+
checkNode<DerivedCCG, FuncTy, CallTy>(Node, /*CheckEdges=*/false);
26272651
}
26282652

26292653
void ModuleCallsiteContextGraph::updateAllocationCall(

0 commit comments

Comments
 (0)