[llvm] [SPIR-V] Add SPV_INTEL_unstructured_loop_controls extension (PR #178799)

Dmitry Sidorov via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 2 04:53:24 PST 2026


https://github.com/MrSidims updated https://github.com/llvm/llvm-project/pull/178799

>From c0feae4d91a144e9b310e18f0bbaad88d451d115 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Fri, 30 Jan 2026 01:31:24 +0100
Subject: [PATCH 1/4] [SPIR-V] Add SPV_INTEL_unstructured_loop_controls
 extension

For compute we don't run structurizer hence we won't be able to preserve
loop metadata via LoopMerge instruction. So SPV_INTEL_unstructured_loop_controls
is the only way we can preserve the info in unstructured control flow.
---
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |   1 +
 .../SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp   |   3 +-
 llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp    |   4 +-
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp |  36 ++++++
 llvm/lib/Target/SPIRV/SPIRVInstrInfo.td       |   2 +
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |   7 ++
 llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp |   5 +
 .../lib/Target/SPIRV/SPIRVSymbolicOperands.td |   1 +
 llvm/lib/Target/SPIRV/SPIRVUtils.cpp          |  30 +++--
 llvm/lib/Target/SPIRV/SPIRVUtils.h            |   2 +
 .../loop-unroll.ll                            | 103 ++++++++++++++++++
 11 files changed, 182 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 6124ce594d1ea..c5d4febb7a08d 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -35,6 +35,7 @@ let TargetPrefix = "spv" in {
   def int_spv_ptrcast : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>]>;
   def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
   def int_spv_loop_merge : Intrinsic<[], [llvm_vararg_ty]>;
+  def int_spv_loop_control_intel : Intrinsic<[], [llvm_vararg_ty]>;
   def int_spv_selection_merge : Intrinsic<[], [llvm_any_ty, llvm_i32_ty], [ImmArg<ArgIndex<1>>]>;
   def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
   def int_spv_unreachable : Intrinsic<[], []>;
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
index 42de884840dc9..66500f5626fd1 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
@@ -155,7 +155,8 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
           break;
         case SPIRV::OpExecutionMode:
         case SPIRV::OpExecutionModeId:
-        case SPIRV::OpLoopMerge: {
+        case SPIRV::OpLoopMerge:
+        case SPIRV::OpLoopControlINTEL: {
           // Print any literals after the OPERAND_UNKNOWN argument normally.
           printRemainingVariableOps(MI, NumFixedOps, OS);
           break;
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index 5a70c95bc6fd3..0abdc3b71c4ec 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -181,7 +181,9 @@ static const std::map<StringRef, SPIRV::Extension::Extension>
          SPIRV::Extension::Extension::
              SPV_ALTERA_arbitrary_precision_fixed_point},
         {"SPV_EXT_image_raw10_raw12",
-         SPIRV::Extension::Extension::SPV_EXT_image_raw10_raw12}};
+         SPIRV::Extension::Extension::SPV_EXT_image_raw10_raw12},
+        {"SPV_INTEL_unstructured_loop_controls",
+         SPIRV::Extension::Extension::SPV_INTEL_unstructured_loop_controls}};
 
 bool SPIRVExtensionsParser::parse(cl::Option &O, StringRef ArgName,
                                   StringRef ArgValue,
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 0ae30a2cdf1ac..c6b46be2eb3f9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -207,6 +207,8 @@ class SPIRVEmitIntrinsics
 
   void useRoundingMode(ConstrainedFPIntrinsic *FPI, IRBuilder<> &B);
 
+  void emitUnstructuredLoopControls(Function &F, IRBuilder<> &B);
+
   // Tries to walk the type accessed by the given GEP instruction.
   // For each nested type access, one of the 2 callbacks is called:
   //  - OnLiteralIndexing when the index is a known constant value.
@@ -2878,6 +2880,38 @@ SPIRVEmitIntrinsics::simplifyZeroLengthArrayGepInst(GetElementPtrInst *GEP) {
   return nullptr;
 }
 
+void SPIRVEmitIntrinsics::emitUnstructuredLoopControls(Function &F,
+                                                       IRBuilder<> &B) {
+  const SPIRVSubtarget *ST = TM->getSubtargetImpl(F);
+  // Shaders use SPIRVStructurizer which emits OpLoopMerge via spv_loop_merge.
+  if (ST->isShader())
+    return;
+  if (!ST->canUseExtension(
+          SPIRV::Extension::SPV_INTEL_unstructured_loop_controls))
+    return;
+
+  for (BasicBlock &BB : F) {
+    Instruction *Term = BB.getTerminator();
+    MDNode *LoopMD = Term->getMetadata(LLVMContext::MD_loop);
+    if (!LoopMD)
+      continue;
+
+    SmallVector<unsigned, 1> Ops =
+        getSpirvLoopControlOperandsFromLoopMetadata(LoopMD);
+    unsigned LC = Ops[0];
+    if (LC == SPIRV::LoopControl::None)
+      continue;
+
+    // Emit intrinsic: loop control mask + optional parameters.
+    B.SetInsertPoint(Term);
+    SmallVector<Value *, 4> IntrArgs;
+    IntrArgs.push_back(ConstantInt::get(B.getInt32Ty(), LC));
+    for (unsigned I = 1; I < Ops.size(); ++I)
+      IntrArgs.push_back(ConstantInt::get(B.getInt32Ty(), Ops[I]));
+    B.CreateIntrinsic(Intrinsic::spv_loop_control_intel, {}, IntrArgs);
+  }
+}
+
 bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
   if (Func.isDeclaration())
     return false;
@@ -2994,6 +3028,8 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
     processInstrAfterVisit(I, B);
   }
 
+  emitUnstructuredLoopControls(Func, B);
+
   return true;
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index 7ca1384abc950..678672c3651d5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -622,6 +622,8 @@ def OpPhi: Op<245, (outs ID:$res), (ins TYPE:$type, ID:$var0, ID:$block0, variab
                   "$res = OpPhi $type $var0 $block0">;
 def OpLoopMerge: Op<246, (outs), (ins unknown:$merge, unknown:$continue, LoopControl:$lc, variable_ops),
                   "OpLoopMerge $merge $continue $lc">;
+def OpLoopControlINTEL: Op<5887, (outs), (ins LoopControl:$lc, variable_ops),
+                  "OpLoopControlINTEL $lc">;
 def OpSelectionMerge: Op<247, (outs), (ins unknown:$merge, SelectionControl:$sc),
                   "OpSelectionMerge $merge $sc">;
 def OpLabel: Op<248, (outs ID:$label), (ins), "$label = OpLabel">;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 915db6824d7c6..ac02283079fae 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3644,6 +3644,13 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     }
     return MIB.constrainAllUses(TII, TRI, RBI);
   }
+  case Intrinsic::spv_loop_control_intel: {
+    auto MIB =
+        BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLoopControlINTEL));
+    for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i)
+      MIB.addImm(foldImm(I.getOperand(i), MRI));
+    return MIB.constrainAllUses(TII, TRI, RBI);
+  }
   case Intrinsic::spv_selection_merge: {
     auto MIB =
         BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSelectionMerge));
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 69195500502ca..d6cffa7c3970e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -2331,6 +2331,11 @@ void addInstrRequirements(const MachineInstr &MI,
     Reqs.addCapability(SPIRV::Capability::DerivativeControl);
     break;
   }
+  case SPIRV::OpLoopControlINTEL: {
+    Reqs.addExtension(SPIRV::Extension::SPV_INTEL_unstructured_loop_controls);
+    Reqs.addCapability(SPIRV::Capability::UnstructuredLoopControlsINTEL);
+    break;
+  }
 
   default:
     break;
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 6f50f6a6421e1..56035cccca99d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -622,6 +622,7 @@ defm BFloat16CooperativeMatrixKHR : CapabilityOperand<5118, 0, 0, [SPV_KHR_bfloa
 defm BlockingPipesALTERA : CapabilityOperand<5945, 0, 0, [SPV_ALTERA_blocking_pipes], []>;
 defm ArbitraryPrecisionFixedPointALTERA : CapabilityOperand<5922, 0, 0, [SPV_ALTERA_arbitrary_precision_fixed_point], []>;
 defm ArbitraryPrecisionFloatingPointALTERA : CapabilityOperand<5845, 0, 0,[SPV_ALTERA_arbitrary_precision_floating_point], []>;
+defm UnstructuredLoopControlsINTEL : CapabilityOperand<5886, 0, 0, [SPV_INTEL_unstructured_loop_controls], []>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define SourceLanguage enum values and at the same time
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index 9c0473e69e076..9147611de495d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -986,25 +986,31 @@ createContinuedInstructions(MachineIRBuilder &MIRBuilder, unsigned Opcode,
   return Instructions;
 }
 
-SmallVector<unsigned, 1> getSpirvLoopControlOperandsFromLoopMetadata(Loop *L) {
+SmallVector<unsigned, 1>
+getSpirvLoopControlOperandsFromLoopMetadata(MDNode *LoopMD) {
   unsigned LC = SPIRV::LoopControl::None;
   // Currently used only to store PartialCount value. Later when other
   // LoopControls are added - this map should be sorted before making
   // them loop_merge operands to satisfy 3.23. Loop Control requirements.
   std::vector<std::pair<unsigned, unsigned>> MaskToValueMap;
-  if (getBooleanLoopAttribute(L, "llvm.loop.unroll.disable")) {
+  if (findOptionMDForLoopID(LoopMD, "llvm.loop.unroll.disable")) {
     LC |= SPIRV::LoopControl::DontUnroll;
   } else {
-    if (getBooleanLoopAttribute(L, "llvm.loop.unroll.enable") ||
-        getBooleanLoopAttribute(L, "llvm.loop.unroll.full")) {
+    if (findOptionMDForLoopID(LoopMD, "llvm.loop.unroll.enable") ||
+        findOptionMDForLoopID(LoopMD, "llvm.loop.unroll.full")) {
       LC |= SPIRV::LoopControl::Unroll;
     }
-    std::optional<int> Count =
-        getOptionalIntLoopAttribute(L, "llvm.loop.unroll.count");
-    if (Count && Count != 1) {
-      LC |= SPIRV::LoopControl::PartialCount;
-      MaskToValueMap.emplace_back(
-          std::make_pair(SPIRV::LoopControl::PartialCount, *Count));
+    if (MDNode *CountMD =
+            findOptionMDForLoopID(LoopMD, "llvm.loop.unroll.count")) {
+      if (auto *CI =
+              mdconst::extract_or_null<ConstantInt>(CountMD->getOperand(1))) {
+        unsigned Count = CI->getZExtValue();
+        if (Count != 1) {
+          LC |= SPIRV::LoopControl::PartialCount;
+          MaskToValueMap.emplace_back(
+              std::make_pair(SPIRV::LoopControl::PartialCount, Count));
+        }
+      }
     }
   }
   SmallVector<unsigned, 1> Result = {LC};
@@ -1013,6 +1019,10 @@ SmallVector<unsigned, 1> getSpirvLoopControlOperandsFromLoopMetadata(Loop *L) {
   return Result;
 }
 
+SmallVector<unsigned, 1> getSpirvLoopControlOperandsFromLoopMetadata(Loop *L) {
+  return getSpirvLoopControlOperandsFromLoopMetadata(L->getLoopID());
+}
+
 const std::set<unsigned> &getTypeFoldingSupportedOpcodes() {
   // clang-format off
   static const std::set<unsigned> TypeFoldingSupportingOpcs = {
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index 33e28627bcf89..50042c90a86d3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -571,6 +571,8 @@ bool isTypeFoldingSupported(unsigned Opcode);
 
 // Get loop controls from llvm.loop. metadata.
 SmallVector<unsigned, 1> getSpirvLoopControlOperandsFromLoopMetadata(Loop *L);
+SmallVector<unsigned, 1>
+getSpirvLoopControlOperandsFromLoopMetadata(MDNode *LoopMD);
 
 // Traversing [g]MIR accounting for pseudo-instructions.
 MachineInstr *passCopy(MachineInstr *Def, const MachineRegisterInfo *MRI);
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
new file mode 100644
index 0000000000000..64e9d85f5bd60
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
@@ -0,0 +1,103 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_unstructured_loop_controls %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-NO-EXT
+
+; Check that extension and capability are emitted when extension is enabled
+; CHECK-SPIRV-DAG: OpCapability UnstructuredLoopControlsINTEL
+; CHECK-SPIRV-DAG: OpExtension "SPV_INTEL_unstructured_loop_controls"
+
+; Check that OpLoopControlINTEL is NOT emitted when extension is not enabled
+; CHECK-NO-EXT-NOT: OpLoopControlINTEL
+
+; Test 1: llvm.loop.unroll.enable -> OpLoopControlINTEL Unroll
+; CHECK-SPIRV: test_unroll_enable
+; CHECK-SPIRV: OpLoopControlINTEL Unroll
+; CHECK-SPIRV-NEXT: OpBranchConditional
+
+define spir_kernel void @test_unroll_enable(ptr addrspace(1) %dst) {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %ptr = getelementptr inbounds i32, ptr addrspace(1) %dst, i32 %i
+  store i32 %i, ptr addrspace(1) %ptr, align 4
+  %inc = add nuw nsw i32 %i, 1
+  %cmp = icmp ult i32 %inc, 10
+  br i1 %cmp, label %for.body, label %for.end, !llvm.loop !0
+
+for.end:
+  ret void
+}
+
+; Test 2: llvm.loop.unroll.disable -> OpLoopControlINTEL DontUnroll
+; CHECK-SPIRV: test_unroll_disable
+; CHECK-SPIRV: OpLoopControlINTEL DontUnroll
+; CHECK-SPIRV-NEXT: OpBranchConditional
+
+define spir_kernel void @test_unroll_disable(ptr addrspace(1) %dst) {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %ptr = getelementptr inbounds i32, ptr addrspace(1) %dst, i32 %i
+  store i32 %i, ptr addrspace(1) %ptr, align 4
+  %inc = add nuw nsw i32 %i, 1
+  %cmp = icmp ult i32 %inc, 10
+  br i1 %cmp, label %for.body, label %for.end, !llvm.loop !1
+
+for.end:
+  ret void
+}
+
+; Test 3: llvm.loop.unroll.count N -> OpLoopControlINTEL PartialCount N
+; CHECK-SPIRV: test_unroll_count
+; CHECK-SPIRV: OpLoopControlINTEL PartialCount 4
+; CHECK-SPIRV-NEXT: OpBranchConditional
+
+define spir_kernel void @test_unroll_count(ptr addrspace(1) %dst) {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %ptr = getelementptr inbounds i32, ptr addrspace(1) %dst, i32 %i
+  store i32 %i, ptr addrspace(1) %ptr, align 4
+  %inc = add nuw nsw i32 %i, 1
+  %cmp = icmp ult i32 %inc, 10
+  br i1 %cmp, label %for.body, label %for.end, !llvm.loop !2
+
+for.end:
+  ret void
+}
+
+; Test 4: llvm.loop.unroll.full -> OpLoopControlINTEL Unroll
+; CHECK-SPIRV: test_unroll_full
+; CHECK-SPIRV: OpLoopControlINTEL Unroll
+; CHECK-SPIRV-NEXT: OpBranchConditional
+
+define spir_kernel void @test_unroll_full(ptr addrspace(1) %dst) {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %ptr = getelementptr inbounds i32, ptr addrspace(1) %dst, i32 %i
+  store i32 %i, ptr addrspace(1) %ptr, align 4
+  %inc = add nuw nsw i32 %i, 1
+  %cmp = icmp ult i32 %inc, 10
+  br i1 %cmp, label %for.body, label %for.end, !llvm.loop !3
+
+for.end:
+  ret void
+}
+
+!0 = distinct !{!0, !4}
+!1 = distinct !{!1, !5}
+!2 = distinct !{!2, !6}
+!3 = distinct !{!3, !7}
+
+!4 = !{!"llvm.loop.unroll.enable"}
+!5 = !{!"llvm.loop.unroll.disable"}
+!6 = !{!"llvm.loop.unroll.count", i32 4}
+!7 = !{!"llvm.loop.unroll.full"}

>From bed74c89f4ef52594706f4ef56c385819af00418 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Fri, 30 Jan 2026 19:19:56 +0100
Subject: [PATCH 2/4] comments

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index c6b46be2eb3f9..bf6d1d7a7328c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2905,10 +2905,10 @@ void SPIRVEmitIntrinsics::emitUnstructuredLoopControls(Function &F,
     // Emit intrinsic: loop control mask + optional parameters.
     B.SetInsertPoint(Term);
     SmallVector<Value *, 4> IntrArgs;
-    IntrArgs.push_back(ConstantInt::get(B.getInt32Ty(), LC));
+    IntrArgs.push_back(B.getInt32(LC));
     for (unsigned I = 1; I < Ops.size(); ++I)
-      IntrArgs.push_back(ConstantInt::get(B.getInt32Ty(), Ops[I]));
-    B.CreateIntrinsic(Intrinsic::spv_loop_control_intel, {}, IntrArgs);
+      IntrArgs.push_back(B.getInt32(Ops[I]));
+    B.CreateIntrinsic(Intrinsic::spv_loop_control_intel, IntrArgs);
   }
 }
 

>From 0948b8b7fc25c8d5bac85739e3e2749fef49cb93 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Fri, 30 Jan 2026 20:57:28 +0100
Subject: [PATCH 3/4] address comments

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp   |  4 ++--
 .../loop-unroll.ll                                   | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ac02283079fae..20972d9325473 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3647,8 +3647,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   case Intrinsic::spv_loop_control_intel: {
     auto MIB =
         BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLoopControlINTEL));
-    for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i)
-      MIB.addImm(foldImm(I.getOperand(i), MRI));
+    for (unsigned J = 1; J < I.getNumExplicitOperands(); ++J)
+      MIB.addImm(foldImm(I.getOperand(J), MRI));
     return MIB.constrainAllUses(TII, TRI, RBI);
   }
   case Intrinsic::spv_selection_merge: {
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
index 64e9d85f5bd60..557ce3610cca0 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
@@ -1,14 +1,14 @@
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_unstructured_loop_controls %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
 ; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-NO-EXT
 
-; Check that extension and capability are emitted when extension is enabled
+; Check that extension and capability are emitted when extension is enabled.
 ; CHECK-SPIRV-DAG: OpCapability UnstructuredLoopControlsINTEL
 ; CHECK-SPIRV-DAG: OpExtension "SPV_INTEL_unstructured_loop_controls"
 
-; Check that OpLoopControlINTEL is NOT emitted when extension is not enabled
+; Check that OpLoopControlINTEL is NOT emitted when extension is not enabled.
 ; CHECK-NO-EXT-NOT: OpLoopControlINTEL
 
-; Test 1: llvm.loop.unroll.enable -> OpLoopControlINTEL Unroll
+; Test 1: llvm.loop.unroll.enable -> OpLoopControlINTEL Unroll.
 ; CHECK-SPIRV: test_unroll_enable
 ; CHECK-SPIRV: OpLoopControlINTEL Unroll
 ; CHECK-SPIRV-NEXT: OpBranchConditional
@@ -29,7 +29,7 @@ for.end:
   ret void
 }
 
-; Test 2: llvm.loop.unroll.disable -> OpLoopControlINTEL DontUnroll
+; Test 2: llvm.loop.unroll.disable -> OpLoopControlINTEL DontUnroll.
 ; CHECK-SPIRV: test_unroll_disable
 ; CHECK-SPIRV: OpLoopControlINTEL DontUnroll
 ; CHECK-SPIRV-NEXT: OpBranchConditional
@@ -50,7 +50,7 @@ for.end:
   ret void
 }
 
-; Test 3: llvm.loop.unroll.count N -> OpLoopControlINTEL PartialCount N
+; Test 3: llvm.loop.unroll.count N -> OpLoopControlINTEL PartialCount N.
 ; CHECK-SPIRV: test_unroll_count
 ; CHECK-SPIRV: OpLoopControlINTEL PartialCount 4
 ; CHECK-SPIRV-NEXT: OpBranchConditional
@@ -71,7 +71,7 @@ for.end:
   ret void
 }
 
-; Test 4: llvm.loop.unroll.full -> OpLoopControlINTEL Unroll
+; Test 4: llvm.loop.unroll.full -> OpLoopControlINTEL Unroll.
 ; CHECK-SPIRV: test_unroll_full
 ; CHECK-SPIRV: OpLoopControlINTEL Unroll
 ; CHECK-SPIRV-NEXT: OpBranchConditional

>From fe0c282745ea7d6bc01050189c1bae985550b4b4 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Mon, 2 Feb 2026 05:26:18 -0600
Subject: [PATCH 4/4] add spirv-val

---
 .../SPV_INTEL_unstructured_loop_controls/loop-unroll.ll          | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
index 557ce3610cca0..b3aed5b4f2339 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_unstructured_loop_controls/loop-unroll.ll
@@ -1,5 +1,6 @@
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_unstructured_loop_controls %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
 ; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-NO-EXT
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_unstructured_loop_controls %s -o - -filetype=obj | spirv-val %}
 
 ; Check that extension and capability are emitted when extension is enabled.
 ; CHECK-SPIRV-DAG: OpCapability UnstructuredLoopControlsINTEL



More information about the llvm-commits mailing list