https://github.com/shiltian updated https://github.com/llvm/llvm-project/pull/230984
>From c219f17f69c358c206cc461b26299c39e9091140 Mon Sep 17 00:00:00 2001 From: Shilei Tian <[email protected]> Date: Sun, 11 Oct 2026 10:51:01 -0400 Subject: [PATCH] [AMDGPU][SplitModule] Add entry points for unreachable call cycles A function in a call cycle always has an incoming direct call, so `SplitGraph` never makes it an entry point. If no kernel and no other entry point reaches the cycle, the cycle is assigned to no partition. This can hit an assertion in `verifyGraph`. In this PR, we visit the unreached nodes in reverse post-order of the direct call edges, and make each node that is still unreached an entry point. Only the outermost unreached cycles get one, so their callees are not copied into other partitions. --- llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp | 41 ++++++++++++++++++- .../AMDGPU/non-kernels-unreachable-cycles.ll | 21 +++++++--- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp b/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp index 702383b76d1ee..e4cf47dbfad94 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp @@ -576,14 +576,53 @@ void SplitGraph::buildGraph(CallGraph &CG) { CandidateEntryPoints.push_back(N); } + BitVector ReachableNodes = NodesReachableByKernels; for (Node *N : CandidateEntryPoints) { // This can be another entry point if it's not reachable by a kernel // TODO: We could sort all of the possible new entries in a stable order // (e.g. by cost), then consume them one by one until // NodesReachableByKernels is all 1s. It'd allow us to avoid // considering some nodes as non-entries in some specific cases. - if (!NodesReachableByKernels.test(N->getID())) + if (!NodesReachableByKernels.test(N->getID())) { N->markAsGraphEntry(); + N->getDependencies(ReachableNodes); + } + } + + // A function in a call cycle always has an incoming DirectCall edge, so it + // is never a candidate above. If no entry point reaches a cycle, one of its + // functions must become an entry point. Visit the unreached nodes in reverse + // post-order of the DirectCall edges, so that callers come before callees. + // This way, a function that an unreached cycle calls does not become an + // entry point of its own. + SmallVector<Node *> PostOrder; + BitVector Visited = ReachableNodes; + SmallVector<std::pair<Node *, edges_iterator>> Stack; + for (Node *Root : Nodes) { + if (Visited.test(Root->getID())) + continue; + Visited.set(Root->getID()); + Stack.emplace_back(Root, Root->outgoing_edges().begin()); + while (!Stack.empty()) { + auto &[N, It] = Stack.back(); + if (It == N->outgoing_edges().end()) { + PostOrder.push_back(N); + Stack.pop_back(); + continue; + } + const Edge *E = *It++; + if (E->Kind != EdgeKind::DirectCall || Visited.test(E->Dst->getID())) + continue; + Visited.set(E->Dst->getID()); + Stack.emplace_back(E->Dst, E->Dst->outgoing_edges().begin()); + } + } + + for (Node *N : reverse(PostOrder)) { + if (ReachableNodes.test(N->getID())) + continue; + N->markAsGraphEntry(); + N->getDependencies(ReachableNodes); } #ifndef NDEBUG diff --git a/llvm/test/tools/llvm-split/AMDGPU/non-kernels-unreachable-cycles.ll b/llvm/test/tools/llvm-split/AMDGPU/non-kernels-unreachable-cycles.ll index 3cfa1c7c3e479..f52b3088eada2 100644 --- a/llvm/test/tools/llvm-split/AMDGPU/non-kernels-unreachable-cycles.ll +++ b/llvm/test/tools/llvm-split/AMDGPU/non-kernels-unreachable-cycles.ll @@ -1,12 +1,21 @@ -; REQUIRES: asserts -; RUN: not --crash llvm-split -o %t -j 3 -mtriple=amdgpu-amd-amdhsa < %s 2>&1 | FileCheck --check-prefix=CRASH %s +; RUN: llvm-split -o %t -j 3 -mtriple=amdgpu-amd-amdhsa < %s +; RUN: llvm-dis -o - %t0 | FileCheck --check-prefix=CHECK0 --implicit-check-not=define %s +; RUN: llvm-dis -o - %t1 | FileCheck --check-prefix=CHECK1 --implicit-check-not=define %s +; RUN: llvm-dis -o - %t2 | FileCheck --check-prefix=CHECK2 --implicit-check-not=define %s ; No entry point reaches @self_rec or the cycle of @mutual_a and @mutual_b, and -; each of these functions has an incoming direct call. FIXME: These cycles get -; no entry point, so the graph verifier fails. Without assertions, no partition -; defines these functions. +; each of these functions has an incoming direct call. Check that each cycle is +; still assigned to a partition. Only @self_rec calls @helper, and @helper is +; defined first. Check that @helper does not become an entry point of its own, +; so it is defined only once. -; CRASH: not all nodes are reachable through the graph's entry points! +; CHECK0: define amdgpu_kernel void @kernel + +; CHECK1: define void @mutual_a +; CHECK1: define void @mutual_b + +; CHECK2: define internal void @helper +; CHECK2: define void @self_rec define internal void @helper(ptr %p) { store i32 0, ptr %p _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
