[llvm] [SPIR-V] Enable structurizer for kernel environment (PR #166079)
Dmitry Sidorov via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 3 03:15:25 PST 2025
https://github.com/MrSidims updated https://github.com/llvm/llvm-project/pull/166079
>From a81d55a6084725cfba67aa922b2b92a090563687 Mon Sep 17 00:00:00 2001
From: "Sidorov, Dmitry" <dmitry.sidorov at intel.com>
Date: Sun, 2 Nov 2025 18:24:56 +0100
Subject: [PATCH 1/2] [SPIR-V] Enable structurizer for kernel environment
---
llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp | 2 +
.../CodeGen/SPIRV/structurizer/kernel-loop.ll | 80 +++++++++++++++++++
2 files changed, 82 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/structurizer/kernel-loop.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 7dd0b95cd9763..eba99f6dacb59 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -181,7 +181,9 @@ void SPIRVPassConfig::addISelPrepare() {
// If an address space cast is not removed while targeting Vulkan, lowering
// will fail during MIR lowering.
addPass(createInferAddressSpacesPass());
+ }
+ if (TM.getSubtargetImpl()->isShader() || TM.getSubtargetImpl()->isKernel()) {
// 1. Simplify loop for subsequent transformations. After this steps, loops
// have the following properties:
// - loops have a single entry edge (pre-header to loop header).
diff --git a/llvm/test/CodeGen/SPIRV/structurizer/kernel-loop.ll b/llvm/test/CodeGen/SPIRV/structurizer/kernel-loop.ll
new file mode 100644
index 0000000000000..56dc916ac7bd3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/structurizer/kernel-loop.ll
@@ -0,0 +1,80 @@
+; RUN: llc -mtriple=spirv64-unknown-opencl -O0 %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-opencl %s -o - -filetype=obj | spirv-val %}
+
+; This test verifies that the structurizer pass runs for OpenCL kernels,
+; generating structured control flow with OpLoopMerge instructions and
+; translating loop metadata to appropriate LoopControl operands.
+
+; CHECK: OpEntryPoint Kernel %[[#kernel_unroll:]] "test_kernel_unroll"
+; CHECK: OpEntryPoint Kernel %[[#kernel_dontunroll:]] "test_kernel_dontunroll"
+
+; Verify unroll metadata is translated to Unroll LoopControl
+; CHECK: %[[#kernel_unroll]] = OpFunction
+; CHECK: OpLabel
+; CHECK: OpLoopMerge %[[#]] %[[#]] Unroll
+; CHECK: OpFunctionEnd
+
+; Verify dont_unroll metadata is translated to DontUnroll LoopControl
+; CHECK: %[[#kernel_dontunroll]] = OpFunction
+; CHECK: OpLabel
+; CHECK: OpLoopMerge %[[#]] %[[#]] DontUnroll
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_kernel_unroll(ptr addrspace(1) %out) {
+entry:
+ %i = alloca i32, align 4
+ store i32 0, ptr %i, align 4
+ br label %for.cond
+
+for.cond:
+ %0 = load i32, ptr %i, align 4
+ %cmp = icmp slt i32 %0, 10
+ br i1 %cmp, label %for.body, label %for.end
+
+for.body:
+ %1 = load i32, ptr %i, align 4
+ %arrayidx = getelementptr inbounds i32, ptr addrspace(1) %out, i64 0
+ store i32 %1, ptr addrspace(1) %arrayidx, align 4
+ br label %for.inc
+
+for.inc:
+ %2 = load i32, ptr %i, align 4
+ %inc = add nsw i32 %2, 1
+ store i32 %inc, ptr %i, align 4
+ br label %for.cond, !llvm.loop !0
+
+for.end:
+ ret void
+}
+
+define spir_kernel void @test_kernel_dontunroll(ptr addrspace(1) %out) {
+entry:
+ %i = alloca i32, align 4
+ store i32 0, ptr %i, align 4
+ br label %for.cond
+
+for.cond:
+ %0 = load i32, ptr %i, align 4
+ %cmp = icmp slt i32 %0, 10
+ br i1 %cmp, label %for.body, label %for.end
+
+for.body:
+ %1 = load i32, ptr %i, align 4
+ %arrayidx = getelementptr inbounds i32, ptr addrspace(1) %out, i64 0
+ store i32 %1, ptr addrspace(1) %arrayidx, align 4
+ br label %for.inc
+
+for.inc:
+ %2 = load i32, ptr %i, align 4
+ %inc = add nsw i32 %2, 1
+ store i32 %inc, ptr %i, align 4
+ br label %for.cond, !llvm.loop !2
+
+for.end:
+ ret void
+}
+
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.unroll.full"}
+!2 = distinct !{!2, !3}
+!3 = !{!"llvm.loop.unroll.disable"}
>From 743574c083459e20bb9b460460a7990671ea9c6d Mon Sep 17 00:00:00 2001
From: "Sidorov, Dmitry" <dmitry.sidorov at intel.com>
Date: Mon, 3 Nov 2025 03:14:41 -0800
Subject: [PATCH 2/2] Remove dead check
Signed-off-by: Sidorov, Dmitry <dmitry.sidorov at intel.com>
---
llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp | 46 ++++++++++----------
1 file changed, 22 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index eba99f6dacb59..504f3ffa842fe 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -183,30 +183,28 @@ void SPIRVPassConfig::addISelPrepare() {
addPass(createInferAddressSpacesPass());
}
- if (TM.getSubtargetImpl()->isShader() || TM.getSubtargetImpl()->isKernel()) {
- // 1. Simplify loop for subsequent transformations. After this steps, loops
- // have the following properties:
- // - loops have a single entry edge (pre-header to loop header).
- // - all loop exits are dominated by the loop pre-header.
- // - loops have a single back-edge.
- addPass(createLoopSimplifyPass());
-
- // 2. Removes registers whose lifetime spans across basic blocks. Also
- // removes phi nodes. This will greatly simplify the next steps.
- addPass(createRegToMemWrapperPass());
-
- // 3. Merge the convergence region exit nodes into one. After this step,
- // regions are single-entry, single-exit. This will help determine the
- // correct merge block.
- addPass(createSPIRVMergeRegionExitTargetsPass());
-
- // 4. Structurize.
- addPass(createSPIRVStructurizerPass());
-
- // 5. Reduce the amount of variables required by pushing some operations
- // back to virtual registers.
- addPass(createPromoteMemoryToRegisterPass());
- }
+ // 1. Simplify loop for subsequent transformations. After this steps, loops
+ // have the following properties:
+ // - loops have a single entry edge (pre-header to loop header).
+ // - all loop exits are dominated by the loop pre-header.
+ // - loops have a single back-edge.
+ addPass(createLoopSimplifyPass());
+
+ // 2. Removes registers whose lifetime spans across basic blocks. Also
+ // removes phi nodes. This will greatly simplify the next steps.
+ addPass(createRegToMemWrapperPass());
+
+ // 3. Merge the convergence region exit nodes into one. After this step,
+ // regions are single-entry, single-exit. This will help determine the
+ // correct merge block.
+ addPass(createSPIRVMergeRegionExitTargetsPass());
+
+ // 4. Structurize.
+ addPass(createSPIRVStructurizerPass());
+
+ // 5. Reduce the amount of variables required by pushing some operations
+ // back to virtual registers.
+ addPass(createPromoteMemoryToRegisterPass());
addPass(createSPIRVStripConvergenceIntrinsicsPass());
addPass(createSPIRVLegalizeImplicitBindingPass());
More information about the llvm-commits
mailing list