[llvm] [SPIR-V] Emit builtin variable OpVariable into entry block (PR #189958)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 2 02:24:51 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/189958
>From 51ebeca2ea6ba10a0ba16eebcb8e4f2533a51586 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 1 Apr 2026 15:25:39 +0200
Subject: [PATCH 1/6] [SPIR-V] Emit builtin variable OpVariable into entry
block
Move the OpVariable and decoration emission for builtin variables into the first MBB so that VReg definition dependencies remain valid across all BBs
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 10 ++++++++--
.../test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll | 6 ++----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index db5218ec73bb7..4c5c37e6d7ddc 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -518,11 +518,17 @@ static Register buildBuiltinVariableLoad(
VariableType, MIRBuilder, SPIRV::StorageClass::Input);
GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Set up the global OpVariable with the necessary builtin decorations.
+ // Emit the OpVariable and its decorations into the first MBB to ensure
+ // VReg definition dependencies are valid across all MBBs.
+ MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
+ MachineIRBuilder FirstBlockBuilder;
+ FirstBlockBuilder.setMF(MIRBuilder.getMF());
+ FirstBlockBuilder.setMBB(EntryBB);
+
Register Variable = GR->buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst, LinkageTy,
- MIRBuilder, false);
+ FirstBlockBuilder, false);
// Load the value from the global variable.
Register LoadedRegister =
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll
index 3e68161105cc1..0c5b03895f579 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll
@@ -1,7 +1,5 @@
-; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
-
-; TODO: This test currently fails with LLVM_ENABLE_EXPENSIVE_CHECKS enabled
-; XFAIL: expensive_checks
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: %if spirv-tools %{ llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
; CHECK-SPIRV-DAG: %[[#]] = OpGroupAsyncCopy %[[#]] %[[#Scope:]]
; CHECK-SPIRV-DAG: %[[#Scope]] = OpConstant %[[#]]
>From fc61b8d39ff5171b38be2b57013afa05a247e22f Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 1 Apr 2026 16:53:56 +0200
Subject: [PATCH 2/6] add spir-val todo
---
llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll
index 0c5b03895f579..facab89b1ad1e 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/OpGroupAsyncCopy.ll
@@ -1,5 +1,6 @@
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
-; RUN: %if spirv-tools %{ llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; TODO: enable spirv-val: OpGroupAsyncCopy event arg needs OpTypeEvent, not OpTypePointer
+; RUNx: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
; CHECK-SPIRV-DAG: %[[#]] = OpGroupAsyncCopy %[[#]] %[[#Scope:]]
; CHECK-SPIRV-DAG: %[[#Scope]] = OpConstant %[[#]]
>From 082b75606132a682d27d33bc69c4f782b3596dcd Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 1 Apr 2026 18:20:18 +0200
Subject: [PATCH 3/6] Address review comments
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 26 ++++++---
.../CodeGen/SPIRV/builtin_intrinsics_32.ll | 56 +++++++++---------
.../CodeGen/SPIRV/builtin_intrinsics_64.ll | 58 +++++++++----------
.../hlsl-intrinsics/builtin-var-dominance.ll | 48 +++++++++++++++
.../transcoding/builtin-var-dominance.ll | 33 +++++++++++
5 files changed, 156 insertions(+), 65 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll
create mode 100644 llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9e75932b20f59..b3be6543a8f72 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -6114,12 +6114,17 @@ bool SPIRVInstructionSelector::loadVec3BuiltinInputID(
MIRBuilder.getMRI()->setType(NewRegister, LLT::pointer(0, 64));
GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Build global variable with the necessary decorations for the input ID
- // builtin variable.
+ // Emit the OpVariable into the first MBB before its terminator to ensure
+ // the def dominates all uses across all MBBs.
+ MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
+ MachineIRBuilder FirstBlockBuilder;
+ FirstBlockBuilder.setMF(MIRBuilder.getMF());
+ FirstBlockBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
+
Register Variable = GR.buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr,
- SPIRV::StorageClass::Input, nullptr, true, std::nullopt, MIRBuilder,
- false);
+ SPIRV::StorageClass::Input, nullptr, true, std::nullopt,
+ FirstBlockBuilder, false);
// Create new register for loading value.
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
@@ -6167,12 +6172,17 @@ bool SPIRVInstructionSelector::loadBuiltinInputID(
GR.getPointerSize()));
GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Build global variable with the necessary decorations for the input ID
- // builtin variable.
+ // Emit the OpVariable into the first MBB to ensure the def dominates all
+ // uses across all MBBs.
+ MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
+ MachineIRBuilder FirstBlockBuilder;
+ FirstBlockBuilder.setMF(MIRBuilder.getMF());
+ FirstBlockBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
+
Register Variable = GR.buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr,
- SPIRV::StorageClass::Input, nullptr, true, std::nullopt, MIRBuilder,
- false);
+ SPIRV::StorageClass::Input, nullptr, true, std::nullopt,
+ FirstBlockBuilder, false);
// Load uint value from the global variable.
auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
diff --git a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll
index c348c934e85ff..6abe8ce53bb98 100644
--- a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll
+++ b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll
@@ -4,34 +4,34 @@
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
target triple = "spirv32-unknown-unknown"
-; CHECK: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
-; CHECK: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
-; CHECK: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
-; CHECK: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
-; CHECK: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
-; CHECK: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
-; CHECK: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
-; CHECK: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
-; CHECK: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
-; CHECK: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
-; CHECK: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
-; CHECK: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
-; CHECK: [[I32:%[0-9]*]] = OpTypeInt 32 0
-; CHECK: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
-; CHECK: [[I32V3:%[0-9]*]] = OpTypeVector [[I32]] 3
-; CHECK: [[I32V3PTR:%[0-9]*]] = OpTypePointer Input [[I32V3]]
-; CHECK: [[NumWorkgroups]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[WorkgroupSize]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[WorkgroupId]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[LocalInvocationId]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[GlobalInvocationId]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[GlobalSize]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[GlobalOffset]] = OpVariable [[I32V3PTR]] Input
-; CHECK: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
-; CHECK: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
-; CHECK: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
-; CHECK: [[SubgroupId]] = OpVariable [[I32PTR]] Input
-; CHECK: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
+; CHECK-DAG: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
+; CHECK-DAG: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
+; CHECK-DAG: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
+; CHECK-DAG: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
+; CHECK-DAG: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
+; CHECK-DAG: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
+; CHECK-DAG: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
+; CHECK-DAG: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
+; CHECK-DAG: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
+; CHECK-DAG: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
+; CHECK-DAG: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
+; CHECK-DAG: [[I32:%[0-9]*]] = OpTypeInt 32 0
+; CHECK-DAG: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
+; CHECK-DAG: [[I32V3:%[0-9]*]] = OpTypeVector [[I32]] 3
+; CHECK-DAG: [[I32V3PTR:%[0-9]*]] = OpTypePointer Input [[I32V3]]
+; CHECK-DAG: [[NumWorkgroups]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[WorkgroupSize]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[WorkgroupId]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[LocalInvocationId]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[GlobalInvocationId]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[GlobalSize]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[GlobalOffset]] = OpVariable [[I32V3PTR]] Input
+; CHECK-DAG: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[SubgroupId]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
@G_spv_num_workgroups_0 = global i32 0
@G_spv_num_workgroups_1 = global i32 0
diff --git a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll
index 45227c31c6ad8..f9b703e681cf5 100644
--- a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll
+++ b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll
@@ -4,35 +4,35 @@
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
target triple = "spirv64-unknown-unknown"
-; CHECK: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
-; CHECK: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
-; CHECK: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
-; CHECK: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
-; CHECK: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
-; CHECK: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
-; CHECK: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
-; CHECK: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
-; CHECK: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
-; CHECK: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
-; CHECK: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
-; CHECK: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
-; CHECK: [[I32:%[0-9]*]] = OpTypeInt 32 0
-; CHECK: [[I64:%[0-9]*]] = OpTypeInt 64 0
-; CHECK: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
-; CHECK: [[I64V3:%[0-9]*]] = OpTypeVector [[I64]] 3
-; CHECK: [[I64V3PTR:%[0-9]*]] = OpTypePointer Input [[I64V3]]
-; CHECK: [[NumWorkgroups]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[WorkgroupSize]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[WorkgroupId]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[LocalInvocationId]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[GlobalInvocationId]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[GlobalSize]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[GlobalOffset]] = OpVariable [[I64V3PTR]] Input
-; CHECK: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
-; CHECK: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
-; CHECK: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
-; CHECK: [[SubgroupId]] = OpVariable [[I32PTR]] Input
-; CHECK: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
+; CHECK-DAG: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
+; CHECK-DAG: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
+; CHECK-DAG: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
+; CHECK-DAG: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
+; CHECK-DAG: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
+; CHECK-DAG: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
+; CHECK-DAG: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
+; CHECK-DAG: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
+; CHECK-DAG: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
+; CHECK-DAG: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
+; CHECK-DAG: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
+; CHECK-DAG: [[I32:%[0-9]*]] = OpTypeInt 32 0
+; CHECK-DAG: [[I64:%[0-9]*]] = OpTypeInt 64 0
+; CHECK-DAG: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
+; CHECK-DAG: [[I64V3:%[0-9]*]] = OpTypeVector [[I64]] 3
+; CHECK-DAG: [[I64V3PTR:%[0-9]*]] = OpTypePointer Input [[I64V3]]
+; CHECK-DAG: [[NumWorkgroups]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[WorkgroupSize]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[WorkgroupId]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[LocalInvocationId]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[GlobalInvocationId]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[GlobalSize]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[GlobalOffset]] = OpVariable [[I64V3PTR]] Input
+; CHECK-DAG: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[SubgroupId]] = OpVariable [[I32PTR]] Input
+; CHECK-DAG: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
@G_spv_num_workgroups_0 = global i64 0
@G_spv_num_workgroups_1 = global i64 0
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll
new file mode 100644
index 0000000000000..12912236d2942
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll
@@ -0,0 +1,48 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-vulkan-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - -filetype=obj | spirv-val %}
+
+;; Verify that builtin variable OpVariable is emitted in the entry block
+;; on the shader (ISel) path, so its def dominates all uses when the
+;; same builtin is accessed in multiple non-entry blocks.
+
+; CHECK-DAG: OpDecorate %[[#VarID:]] BuiltIn LocalInvocationId
+; CHECK-DAG: %[[#VarID]] = OpVariable %[[#]] Input
+
+define internal spir_func void @main_inner(<3 x i32> noundef %ID) {
+entry:
+ ret void
+}
+
+define void @main.1() #0 {
+entry:
+ %cmp = icmp sgt i32 1, 0
+ br i1 %cmp, label %then, label %else
+
+then:
+ %0 = call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
+ %1 = insertelement <3 x i32> poison, i32 %0, i64 0
+ %2 = call i32 @llvm.spv.thread.id.in.group.i32(i32 1)
+ %3 = insertelement <3 x i32> %1, i32 %2, i64 1
+ %4 = call i32 @llvm.spv.thread.id.in.group.i32(i32 2)
+ %5 = insertelement <3 x i32> %3, i32 %4, i64 2
+ call void @main_inner(<3 x i32> %5)
+ br label %exit
+
+else:
+ %6 = call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
+ %7 = insertelement <3 x i32> poison, i32 %6, i64 0
+ %8 = call i32 @llvm.spv.thread.id.in.group.i32(i32 1)
+ %9 = insertelement <3 x i32> %7, i32 %8, i64 1
+ %10 = call i32 @llvm.spv.thread.id.in.group.i32(i32 2)
+ %11 = insertelement <3 x i32> %9, i32 %10, i64 2
+ call void @main_inner(<3 x i32> %11)
+ br label %exit
+
+exit:
+ ret void
+}
+
+declare i32 @llvm.spv.thread.id.in.group.i32(i32) #1
+
+attributes #0 = { norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
+attributes #1 = { nounwind willreturn memory(none) }
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll b/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll
new file mode 100644
index 0000000000000..f6046be91d96c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll
@@ -0,0 +1,33 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+;; Verify that builtin variable OpVariable is emitted in the entry block
+;; so its def dominates all uses, even when the same builtin is called
+;; in multiple non-entry blocks.
+
+; CHECK-DAG: OpDecorate %[[#VarID:]] BuiltIn LocalInvocationId
+; CHECK-DAG: %[[#VarID]] = OpVariable %[[#]] Input
+
+define spir_kernel void @test(ptr addrspace(1) %out, i32 %n) {
+entry:
+ %cmp = icmp sgt i32 %n, 0
+ br i1 %cmp, label %then, label %else
+
+then:
+ %id0 = call spir_func i32 @_Z12get_local_idj(i32 0)
+ store i32 %id0, ptr addrspace(1) %out, align 4
+ br label %exit
+
+else:
+ %id1 = call spir_func i32 @_Z12get_local_idj(i32 0)
+ %neg = sub i32 0, %id1
+ store i32 %neg, ptr addrspace(1) %out, align 4
+ br label %exit
+
+exit:
+ ret void
+}
+
+declare spir_func i32 @_Z12get_local_idj(i32)
>From 0eb19ebfad994d5f0a78bd95c92762b967f04445 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 1 Apr 2026 18:27:43 +0200
Subject: [PATCH 4/6] Address review comments 2
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 12 ++--
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 28 ++++-----
.../CodeGen/SPIRV/builtin_intrinsics_32.ll | 56 +++++++++---------
.../CodeGen/SPIRV/builtin_intrinsics_64.ll | 58 +++++++++----------
4 files changed, 77 insertions(+), 77 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 4c5c37e6d7ddc..24017636c0522 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -518,17 +518,17 @@ static Register buildBuiltinVariableLoad(
VariableType, MIRBuilder, SPIRV::StorageClass::Input);
GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Emit the OpVariable and its decorations into the first MBB to ensure
- // VReg definition dependencies are valid across all MBBs.
+ // Emit the OpVariable into the entry block to ensure the def dominates
+ // all uses across all MBBs.
MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
- MachineIRBuilder FirstBlockBuilder;
- FirstBlockBuilder.setMF(MIRBuilder.getMF());
- FirstBlockBuilder.setMBB(EntryBB);
+ MachineIRBuilder GVBuilder(MIRBuilder.getState());
+ if (&GVBuilder.getMBB() != &EntryBB)
+ GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
Register Variable = GR->buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst, LinkageTy,
- FirstBlockBuilder, false);
+ GVBuilder, false);
// Load the value from the global variable.
Register LoadedRegister =
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index b3be6543a8f72..9e204efb29a39 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -6114,17 +6114,17 @@ bool SPIRVInstructionSelector::loadVec3BuiltinInputID(
MIRBuilder.getMRI()->setType(NewRegister, LLT::pointer(0, 64));
GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Emit the OpVariable into the first MBB before its terminator to ensure
- // the def dominates all uses across all MBBs.
+ // Emit the OpVariable into the entry block to ensure the def dominates
+ // all uses across all MBBs.
MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
- MachineIRBuilder FirstBlockBuilder;
- FirstBlockBuilder.setMF(MIRBuilder.getMF());
- FirstBlockBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
+ MachineIRBuilder GVBuilder(MIRBuilder.getState());
+ if (&GVBuilder.getMBB() != &EntryBB)
+ GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
Register Variable = GR.buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr,
- SPIRV::StorageClass::Input, nullptr, true, std::nullopt,
- FirstBlockBuilder, false);
+ SPIRV::StorageClass::Input, nullptr, true, std::nullopt, GVBuilder,
+ false);
// Create new register for loading value.
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
@@ -6172,17 +6172,17 @@ bool SPIRVInstructionSelector::loadBuiltinInputID(
GR.getPointerSize()));
GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Emit the OpVariable into the first MBB to ensure the def dominates all
- // uses across all MBBs.
+ // Emit the OpVariable into the entry block to ensure the def dominates
+ // all uses across all MBBs.
MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
- MachineIRBuilder FirstBlockBuilder;
- FirstBlockBuilder.setMF(MIRBuilder.getMF());
- FirstBlockBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
+ MachineIRBuilder GVBuilder(MIRBuilder.getState());
+ if (&GVBuilder.getMBB() != &EntryBB)
+ GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
Register Variable = GR.buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr,
- SPIRV::StorageClass::Input, nullptr, true, std::nullopt,
- FirstBlockBuilder, false);
+ SPIRV::StorageClass::Input, nullptr, true, std::nullopt, GVBuilder,
+ false);
// Load uint value from the global variable.
auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
diff --git a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll
index 6abe8ce53bb98..c348c934e85ff 100644
--- a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll
+++ b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_32.ll
@@ -4,34 +4,34 @@
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
target triple = "spirv32-unknown-unknown"
-; CHECK-DAG: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
-; CHECK-DAG: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
-; CHECK-DAG: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
-; CHECK-DAG: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
-; CHECK-DAG: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
-; CHECK-DAG: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
-; CHECK-DAG: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
-; CHECK-DAG: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
-; CHECK-DAG: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
-; CHECK-DAG: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
-; CHECK-DAG: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
-; CHECK-DAG: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
-; CHECK-DAG: [[I32:%[0-9]*]] = OpTypeInt 32 0
-; CHECK-DAG: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
-; CHECK-DAG: [[I32V3:%[0-9]*]] = OpTypeVector [[I32]] 3
-; CHECK-DAG: [[I32V3PTR:%[0-9]*]] = OpTypePointer Input [[I32V3]]
-; CHECK-DAG: [[NumWorkgroups]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[WorkgroupSize]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[WorkgroupId]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[LocalInvocationId]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[GlobalInvocationId]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[GlobalSize]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[GlobalOffset]] = OpVariable [[I32V3PTR]] Input
-; CHECK-DAG: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[SubgroupId]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
+; CHECK: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
+; CHECK: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
+; CHECK: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
+; CHECK: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
+; CHECK: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
+; CHECK: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
+; CHECK: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
+; CHECK: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
+; CHECK: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
+; CHECK: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
+; CHECK: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
+; CHECK: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
+; CHECK: [[I32:%[0-9]*]] = OpTypeInt 32 0
+; CHECK: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
+; CHECK: [[I32V3:%[0-9]*]] = OpTypeVector [[I32]] 3
+; CHECK: [[I32V3PTR:%[0-9]*]] = OpTypePointer Input [[I32V3]]
+; CHECK: [[NumWorkgroups]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[WorkgroupSize]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[WorkgroupId]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[LocalInvocationId]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[GlobalInvocationId]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[GlobalSize]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[GlobalOffset]] = OpVariable [[I32V3PTR]] Input
+; CHECK: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
+; CHECK: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
+; CHECK: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
+; CHECK: [[SubgroupId]] = OpVariable [[I32PTR]] Input
+; CHECK: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
@G_spv_num_workgroups_0 = global i32 0
@G_spv_num_workgroups_1 = global i32 0
diff --git a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll
index f9b703e681cf5..45227c31c6ad8 100644
--- a/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll
+++ b/llvm/test/CodeGen/SPIRV/builtin_intrinsics_64.ll
@@ -4,35 +4,35 @@
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
target triple = "spirv64-unknown-unknown"
-; CHECK-DAG: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
-; CHECK-DAG: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
-; CHECK-DAG: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
-; CHECK-DAG: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
-; CHECK-DAG: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
-; CHECK-DAG: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
-; CHECK-DAG: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
-; CHECK-DAG: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
-; CHECK-DAG: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
-; CHECK-DAG: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
-; CHECK-DAG: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
-; CHECK-DAG: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
-; CHECK-DAG: [[I32:%[0-9]*]] = OpTypeInt 32 0
-; CHECK-DAG: [[I64:%[0-9]*]] = OpTypeInt 64 0
-; CHECK-DAG: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
-; CHECK-DAG: [[I64V3:%[0-9]*]] = OpTypeVector [[I64]] 3
-; CHECK-DAG: [[I64V3PTR:%[0-9]*]] = OpTypePointer Input [[I64V3]]
-; CHECK-DAG: [[NumWorkgroups]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[WorkgroupSize]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[WorkgroupId]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[LocalInvocationId]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[GlobalInvocationId]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[GlobalSize]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[GlobalOffset]] = OpVariable [[I64V3PTR]] Input
-; CHECK-DAG: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[SubgroupId]] = OpVariable [[I32PTR]] Input
-; CHECK-DAG: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
+; CHECK: OpDecorate [[NumWorkgroups:%[0-9]*]] BuiltIn NumWorkgroups
+; CHECK: OpDecorate [[WorkgroupSize:%[0-9]*]] BuiltIn WorkgroupSize
+; CHECK: OpDecorate [[WorkgroupId:%[0-9]*]] BuiltIn WorkgroupId
+; CHECK: OpDecorate [[LocalInvocationId:%[0-9]*]] BuiltIn LocalInvocationId
+; CHECK: OpDecorate [[GlobalInvocationId:%[0-9]*]] BuiltIn GlobalInvocationId
+; CHECK: OpDecorate [[GlobalSize:%[0-9]*]] BuiltIn GlobalSize
+; CHECK: OpDecorate [[GlobalOffset:%[0-9]*]] BuiltIn GlobalOffset
+; CHECK: OpDecorate [[SubgroupSize:%[0-9]*]] BuiltIn SubgroupSize
+; CHECK: OpDecorate [[SubgroupMaxSize:%[0-9]*]] BuiltIn SubgroupMaxSize
+; CHECK: OpDecorate [[NumSubgroups:%[0-9]*]] BuiltIn NumSubgroups
+; CHECK: OpDecorate [[SubgroupId:%[0-9]*]] BuiltIn SubgroupId
+; CHECK: OpDecorate [[SubgroupLocalInvocationId:%[0-9]*]] BuiltIn SubgroupLocalInvocationId
+; CHECK: [[I32:%[0-9]*]] = OpTypeInt 32 0
+; CHECK: [[I64:%[0-9]*]] = OpTypeInt 64 0
+; CHECK: [[I32PTR:%[0-9]*]] = OpTypePointer Input [[I32]]
+; CHECK: [[I64V3:%[0-9]*]] = OpTypeVector [[I64]] 3
+; CHECK: [[I64V3PTR:%[0-9]*]] = OpTypePointer Input [[I64V3]]
+; CHECK: [[NumWorkgroups]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[WorkgroupSize]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[WorkgroupId]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[LocalInvocationId]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[GlobalInvocationId]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[GlobalSize]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[GlobalOffset]] = OpVariable [[I64V3PTR]] Input
+; CHECK: [[SubgroupSize]] = OpVariable [[I32PTR]] Input
+; CHECK: [[SubgroupMaxSize]] = OpVariable [[I32PTR]] Input
+; CHECK: [[NumSubgroups]] = OpVariable [[I32PTR]] Input
+; CHECK: [[SubgroupId]] = OpVariable [[I32PTR]] Input
+; CHECK: [[SubgroupLocalInvocationId]] = OpVariable [[I32PTR]] Input
@G_spv_num_workgroups_0 = global i64 0
@G_spv_num_workgroups_1 = global i64 0
>From dc4def62678d6eea6bd44b7a8d923debb232bb8e Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 2 Apr 2026 06:26:01 +0200
Subject: [PATCH 5/6] Update comments in tests
---
.../CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll | 6 +++---
.../test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll
index 12912236d2942..019a808d13269 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/builtin-var-dominance.ll
@@ -1,9 +1,9 @@
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-vulkan-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - -filetype=obj | spirv-val %}
-;; Verify that builtin variable OpVariable is emitted in the entry block
-;; on the shader (ISel) path, so its def dominates all uses when the
-;; same builtin is accessed in multiple non-entry blocks.
+;; Verify correct translation when the same builtin is called from multiple
+;; non-entry blocks. The fix is validated by -verify-machineinstrs that fails
+;; if the OpVariable's VReg definition does not dominate all its uses in MIR.
; CHECK-DAG: OpDecorate %[[#VarID:]] BuiltIn LocalInvocationId
; CHECK-DAG: %[[#VarID]] = OpVariable %[[#]] Input
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll b/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll
index f6046be91d96c..c1f60c1d73733 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/builtin-var-dominance.ll
@@ -3,9 +3,9 @@
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-;; Verify that builtin variable OpVariable is emitted in the entry block
-;; so its def dominates all uses, even when the same builtin is called
-;; in multiple non-entry blocks.
+;; Verify correct translation when the same builtin is called from multiple
+;; non-entry blocks. The fix is validated by -verify-machineinstrs that fails
+;; if the OpVariable's VReg definition does not dominate all its uses in MIR.
; CHECK-DAG: OpDecorate %[[#VarID:]] BuiltIn LocalInvocationId
; CHECK-DAG: %[[#VarID]] = OpVariable %[[#]] Input
>From 08473c73a8336fb54db2c34b14a2f63684df72d8 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 2 Apr 2026 11:23:26 +0200
Subject: [PATCH 6/6] Make fix in common location
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 10 ++-------
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 9 +++++++-
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 22 +++++--------------
3 files changed, 16 insertions(+), 25 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 24017636c0522..db5218ec73bb7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -518,17 +518,11 @@ static Register buildBuiltinVariableLoad(
VariableType, MIRBuilder, SPIRV::StorageClass::Input);
GR->assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Emit the OpVariable into the entry block to ensure the def dominates
- // all uses across all MBBs.
- MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
- MachineIRBuilder GVBuilder(MIRBuilder.getState());
- if (&GVBuilder.getMBB() != &EntryBB)
- GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
-
+ // Set up the global OpVariable with the necessary builtin decorations.
Register Variable = GR->buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltinValue), nullptr,
SPIRV::StorageClass::Input, nullptr, /* isConst= */ isConst, LinkageTy,
- GVBuilder, false);
+ MIRBuilder, false);
// Load the value from the global variable.
Register LoadedRegister =
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index fa5279c6869c8..50f672768f141 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -834,7 +834,14 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
return ResVReg;
}
- auto MIB = MIRBuilder.buildInstr(SPIRV::OpVariable)
+ // Emit the OpVariable into the entry block to ensure the def dominates
+ // all uses across all MBBs.
+ MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
+ MachineIRBuilder GVBuilder(MIRBuilder.getState());
+ if (&GVBuilder.getMBB() != &EntryBB)
+ GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
+
+ auto MIB = GVBuilder.buildInstr(SPIRV::OpVariable)
.addDef(ResVReg)
.addUse(getSPIRVTypeID(BaseType))
.addImm(static_cast<uint32_t>(Storage));
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9e204efb29a39..9e75932b20f59 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -6114,16 +6114,11 @@ bool SPIRVInstructionSelector::loadVec3BuiltinInputID(
MIRBuilder.getMRI()->setType(NewRegister, LLT::pointer(0, 64));
GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Emit the OpVariable into the entry block to ensure the def dominates
- // all uses across all MBBs.
- MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
- MachineIRBuilder GVBuilder(MIRBuilder.getState());
- if (&GVBuilder.getMBB() != &EntryBB)
- GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
-
+ // Build global variable with the necessary decorations for the input ID
+ // builtin variable.
Register Variable = GR.buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr,
- SPIRV::StorageClass::Input, nullptr, true, std::nullopt, GVBuilder,
+ SPIRV::StorageClass::Input, nullptr, true, std::nullopt, MIRBuilder,
false);
// Create new register for loading value.
@@ -6172,16 +6167,11 @@ bool SPIRVInstructionSelector::loadBuiltinInputID(
GR.getPointerSize()));
GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
- // Emit the OpVariable into the entry block to ensure the def dominates
- // all uses across all MBBs.
- MachineBasicBlock &EntryBB = MIRBuilder.getMF().front();
- MachineIRBuilder GVBuilder(MIRBuilder.getState());
- if (&GVBuilder.getMBB() != &EntryBB)
- GVBuilder.setInsertPt(EntryBB, EntryBB.getFirstTerminator());
-
+ // Build global variable with the necessary decorations for the input ID
+ // builtin variable.
Register Variable = GR.buildGlobalVariable(
NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr,
- SPIRV::StorageClass::Input, nullptr, true, std::nullopt, GVBuilder,
+ SPIRV::StorageClass::Input, nullptr, true, std::nullopt, MIRBuilder,
false);
// Load uint value from the global variable.
More information about the llvm-commits
mailing list