[llvm-branch-commits] [llvm] [AMDGPU][SplitModule] Add entry points for unreachable call cycles (PR #230984)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Oct 11 08:06:41 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Shilei Tian (shiltian)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/230984.diff


2 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp (+40-1) 
- (modified) llvm/test/tools/llvm-split/AMDGPU/non-kernels-unreachable-cycles.ll (+15-6) 


``````````diff
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 4c8febcb0f158..3b5e5e36b368e 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=amdgcn-amd-amdhsa < %s 2>&1 | FileCheck --check-prefix=CRASH %s
+; RUN: llvm-split -o %t -j 3 -mtriple=amdgcn-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

``````````

</details>


https://github.com/llvm/llvm-project/pull/230984


More information about the llvm-branch-commits mailing list