[llvm] [llvm][SplitModuleByCategory] Fix infinite loop on cyclic global uses (PR #206862)
Yury Plyakhin via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 17:00:24 PDT 2026
https://github.com/YuriPlyakhin created https://github.com/llvm/llvm-project/pull/206862
The dependency graph construction in `addUserToGraphRecursively` walked the users of globals without visited set. A cycle in the use graph caused it to push the same users forever, hanging the splitter.
Track visited users so each is processed once.
Co-Authored-By: Claude
>From ece56cfde00f78bafcff064331128ff22a24d1c9 Mon Sep 17 00:00:00 2001
From: "Plyakhin, Yury" <yury.plyakhin at intel.com>
Date: Wed, 1 Jul 2026 01:55:13 +0200
Subject: [PATCH] [llvm][SplitModuleByCategory] Fix infinite loop on cyclic
global uses
The dependency graph construction in addUserToGraphRecursively walked
the users of globals without visited set. A cycle in the use graph caused
it to push the same users forever, hanging the splitter.
Track visited users so each is processed once.
Co-Authored-By: Claude
---
.../Transforms/Utils/SplitModuleByCategory.h | 4 +-
.../Utils/SplitModuleByCategory.cpp | 5 +-
.../SplitByCategory/recursion-and-cycles.ll | 47 +++++++++++++++++++
3 files changed, 53 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/tools/llvm-split/SplitByCategory/recursion-and-cycles.ll
diff --git a/llvm/include/llvm/Transforms/Utils/SplitModuleByCategory.h b/llvm/include/llvm/Transforms/Utils/SplitModuleByCategory.h
index 0ab2f455052f9..545899e7290ef 100644
--- a/llvm/include/llvm/Transforms/Utils/SplitModuleByCategory.h
+++ b/llvm/include/llvm/Transforms/Utils/SplitModuleByCategory.h
@@ -53,8 +53,8 @@ class Function;
/// there is a function B with signature S. An "A" -> "B" edge will be added
/// to the graph;
///
-/// FIXME: For now, the algorithm assumes no recursion in the input Module. This
-/// will be addressed in the near future.
+/// Recursion and other cycles in the input Module (e.g. directly or mutually
+/// recursive functions, or self-referencing global variables) are supported.
LLVM_ABI Error splitModuleTransitiveFromEntryPoints(
std::unique_ptr<Module> M,
function_ref<std::optional<int>(const Function &F)> EntryPointCategorizer,
diff --git a/llvm/lib/Transforms/Utils/SplitModuleByCategory.cpp b/llvm/lib/Transforms/Utils/SplitModuleByCategory.cpp
index a8b705ac8148d..b3d69d3affc06 100644
--- a/llvm/lib/Transforms/Utils/SplitModuleByCategory.cpp
+++ b/llvm/lib/Transforms/Utils/SplitModuleByCategory.cpp
@@ -159,7 +159,9 @@ class DependencyGraph {
private:
void addUserToGraphRecursively(const User *Root, const GlobalValue *V) {
SmallVector<const User *, 8> WorkList;
+ SmallPtrSet<const User *, 8> Visited;
WorkList.push_back(Root);
+ Visited.insert(Root);
while (!WorkList.empty()) {
const User *U = WorkList.pop_back_val();
@@ -173,7 +175,8 @@ class DependencyGraph {
// bitcast or gep). We trace users of this constant further to reach
// global objects they are used by and add them to the graph.
for (const User *UU : U->users())
- WorkList.push_back(UU);
+ if (Visited.insert(UU).second)
+ WorkList.push_back(UU);
} else {
llvm_unreachable("Unhandled type of function user");
}
diff --git a/llvm/test/tools/llvm-split/SplitByCategory/recursion-and-cycles.ll b/llvm/test/tools/llvm-split/SplitByCategory/recursion-and-cycles.ll
new file mode 100644
index 0000000000000..52dd6bf6c70ad
--- /dev/null
+++ b/llvm/test/tools/llvm-split/SplitByCategory/recursion-and-cycles.ll
@@ -0,0 +1,47 @@
+; Check that Module splitting produces correct output in the presence of cycles
+; in the dependency graph.
+
+; RUN: llvm-split -split-by-category=kernel -S < %s -o %t
+; RUN: FileCheck %s -input-file=%t_0.ll --check-prefix CHECK0 \
+; RUN: --implicit-check-not @gptr --implicit-check-not @kernel_A
+; RUN: FileCheck %s -input-file=%t_1.ll --check-prefix CHECK1 \
+; RUN: --implicit-check-not @self --implicit-check-not @kernel_B
+
+; CHECK0-DAG: define spir_func void @ping()
+; CHECK0-DAG: define spir_func void @pong()
+; CHECK0-DAG: define spir_func void @self()
+; CHECK0-DAG: define spir_kernel void @kernel_B()
+
+; CHECK1-DAG: @gptr = private global ptr @gptr
+; CHECK1-DAG: define spir_func void @ping()
+; CHECK1-DAG: define spir_func void @pong()
+; CHECK1-DAG: define spir_kernel void @kernel_A()
+
+ at gptr = private global ptr @gptr
+
+define spir_func void @ping() {
+ call spir_func void @pong()
+ ret void
+}
+
+define spir_func void @pong() {
+ call spir_func void @ping()
+ ret void
+}
+
+define spir_func void @self() {
+ call spir_func void @self()
+ ret void
+}
+
+define spir_kernel void @kernel_A() {
+ call spir_func void @ping()
+ %v = load ptr, ptr @gptr
+ ret void
+}
+
+define spir_kernel void @kernel_B() {
+ call spir_func void @ping()
+ call spir_func void @self()
+ ret void
+}
More information about the llvm-commits
mailing list