[llvm] [SPIR-V] Prevent adding duplicate binding instructions for implicit binding (PR #161299)

Lucie Choi via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 1 20:48:13 PDT 2025


https://github.com/luciechoi updated https://github.com/llvm/llvm-project/pull/161299

>From f77469748ce91a350c6234f9c7167974053965c8 Mon Sep 17 00:00:00 2001
From: luciechoi <ychoi0407 at gmail.com>
Date: Mon, 29 Sep 2025 23:27:39 +0000
Subject: [PATCH 1/3] [SPIR-V] Prevent duplicate binding for implicit binding

---
 .../SPIRV/SPIRVLegalizeImplicitBinding.cpp    | 13 ++++-
 .../SPIRV/hlsl-resources/ImplicitBinding.ll   | 50 ++++++++++---------
 2 files changed, 39 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
index aea3397ad2fd6..6c725387bbe2e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
@@ -112,11 +112,22 @@ uint32_t SPIRVLegalizeImplicitBinding::getAndReserveFirstUnusedBinding(
 }
 
 void SPIRVLegalizeImplicitBinding::replaceImplicitBindingCalls(Module &M) {
+  std::unordered_map<uint32_t, uint32_t> OrderIdToBinding;
+
   for (CallInst *OldCI : ImplicitBindingCalls) {
     IRBuilder<> Builder(OldCI);
+    const uint32_t OrderId =
+        cast<ConstantInt>(OldCI->getArgOperand(0))->getZExtValue();
     const uint32_t DescSet =
         cast<ConstantInt>(OldCI->getArgOperand(1))->getZExtValue();
-    const uint32_t NewBinding = getAndReserveFirstUnusedBinding(DescSet);
+
+    // Reuse an existing binding for this order ID, if one was already assigned.
+    // Otherwise, assign a new binding.
+    const uint32_t NewBinding =
+        (OrderIdToBinding.find(OrderId) != OrderIdToBinding.end())
+            ? OrderIdToBinding[OrderId]
+            : getAndReserveFirstUnusedBinding(DescSet);
+    OrderIdToBinding[OrderId] = NewBinding;
 
     SmallVector<Value *, 8> Args;
     Args.push_back(Builder.getInt32(DescSet));
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll
index cd524980ed275..1c235bd4c6e3e 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll
@@ -44,30 +44,34 @@ entry:
   %3 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 0, i32 2, i32 1, i32 0, ptr nonnull @.str.6)
   %4 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 10, i32 1, i32 1, i32 0, ptr nonnull @.str.8)
   %5 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 2, i32 10, i32 1, i32 0, ptr nonnull @.str.10)
-  %6 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 3, i32 10, i32 1, i32 0, ptr nonnull @.str.12)
+  %6 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 3, i32 10, i32 2, i32 0, ptr nonnull @.str.12)
   %7 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 10, i32 2, i32 1, i32 0, ptr nonnull @.str.14)
-  %8 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %1, i32 0)
-  %9 = load i32, ptr addrspace(11) %8, align 4
-  %10 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %2, i32 0)
-  %11 = load i32, ptr addrspace(11) %10, align 4
-  %add.i = add nsw i32 %11, %9
-  %12 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %3, i32 0)
-  %13 = load i32, ptr addrspace(11) %12, align 4
-  %add4.i = add nsw i32 %add.i, %13
-  %14 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %4, i32 0)
-  %15 = load i32, ptr addrspace(11) %14, align 4
-  %add6.i = add nsw i32 %add4.i, %15
-  %16 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %5, i32 0)
-  %17 = load i32, ptr addrspace(11) %16, align 4
-  %add8.i = add nsw i32 %add6.i, %17
-  %18 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %6, i32 0)
-  %19 = load i32, ptr addrspace(11) %18, align 4
-  %add10.i = add nsw i32 %add8.i, %19
-  %20 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %7, i32 0)
-  %21 = load i32, ptr addrspace(11) %20, align 4
-  %add12.i = add nsw i32 %add10.i, %21
-  %22 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %0, i32 0)
-  store i32 %add12.i, ptr addrspace(11) %22, align 4
+  %8 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 3, i32 10, i32 2, i32 1, ptr nonnull @.str.12)
+  %9 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %1, i32 0)
+  %10 = load i32, ptr addrspace(11) %9, align 4
+  %11 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %2, i32 0)
+  %12 = load i32, ptr addrspace(11) %11, align 4
+  %add.i = add nsw i32 %12, %10
+  %13 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %3, i32 0)
+  %14 = load i32, ptr addrspace(11) %13, align 4
+  %add4.i = add nsw i32 %add.i, %14
+  %15 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %4, i32 0)
+  %16 = load i32, ptr addrspace(11) %15, align 4
+  %add6.i = add nsw i32 %add4.i, %16
+  %17 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %5, i32 0)
+  %18 = load i32, ptr addrspace(11) %17, align 4
+  %add8.i = add nsw i32 %add6.i, %18
+  %19 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %6, i32 0)
+  %20 = load i32, ptr addrspace(11) %19, align 4
+  %add10.i = add nsw i32 %add8.i, %20
+  %21 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %7, i32 0)
+  %22 = load i32, ptr addrspace(11) %21, align 4
+  %add12.i = add nsw i32 %add10.i, %22
+  %23 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %8, i32 0)
+  %24 = load i32, ptr addrspace(11) %23, align 4
+  %add14.i = add nsw i32 %add12.i, %24
+  %25 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %0, i32 0)
+  store i32 %add14.i, ptr addrspace(11) %25, align 4
   ret void
 }
 

>From fea1ab623e92f757fcdcb18ec205ffd6f257da36 Mon Sep 17 00:00:00 2001
From: luciechoi <ychoi0407 at gmail.com>
Date: Tue, 30 Sep 2025 20:31:03 +0000
Subject: [PATCH 2/3] Add a check to test unique implicit binding numbers.

---
 .../SPIRV/SPIRVLegalizeImplicitBinding.cpp    | 39 ++++++++++++++++---
 .../SPIRV/hlsl-resources/ImplicitBinding.ll   |  5 ++-
 .../UniqueImplicitBindingNumber.ll            | 20 ++++++++++
 3 files changed, 56 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
index 6c725387bbe2e..8b9f44a9af1db 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
@@ -92,6 +92,32 @@ void SPIRVLegalizeImplicitBinding::collectBindingInfo(Module &M) {
             cast<ConstantInt>(B->getArgOperand(OrderIdArgIdx))->getZExtValue();
         return OrderA < OrderB;
       });
+
+  // Check that the order Id is unique per resource.
+  for (uint32_t i = 1; i < ImplicitBindingCalls.size(); ++i) {
+    const uint32_t OrderIdArgIdx = 0;
+    const uint32_t DescSetArgIdx = 1;
+    const uint32_t OrderA =
+        cast<ConstantInt>(
+            ImplicitBindingCalls[i - 1]->getArgOperand(OrderIdArgIdx))
+            ->getZExtValue();
+    const uint32_t OrderB =
+        cast<ConstantInt>(ImplicitBindingCalls[i]->getArgOperand(OrderIdArgIdx))
+            ->getZExtValue();
+    if (OrderA == OrderB) {
+      const uint32_t DescSetA =
+          cast<ConstantInt>(
+              ImplicitBindingCalls[i - 1]->getArgOperand(DescSetArgIdx))
+              ->getZExtValue();
+      const uint32_t DescSetB =
+          cast<ConstantInt>(
+              ImplicitBindingCalls[i]->getArgOperand(DescSetArgIdx))
+              ->getZExtValue();
+      assert(DescSetA == DescSetB &&
+             "If two implicit binding calls have the same order ID, they must "
+             "also have the same descriptor set.");
+    }
+  }
 }
 
 uint32_t SPIRVLegalizeImplicitBinding::getAndReserveFirstUnusedBinding(
@@ -112,7 +138,8 @@ uint32_t SPIRVLegalizeImplicitBinding::getAndReserveFirstUnusedBinding(
 }
 
 void SPIRVLegalizeImplicitBinding::replaceImplicitBindingCalls(Module &M) {
-  std::unordered_map<uint32_t, uint32_t> OrderIdToBinding;
+  uint32_t lastOrderId = -1;
+  uint32_t lastBindingNumber = -1;
 
   for (CallInst *OldCI : ImplicitBindingCalls) {
     IRBuilder<> Builder(OldCI);
@@ -123,11 +150,11 @@ void SPIRVLegalizeImplicitBinding::replaceImplicitBindingCalls(Module &M) {
 
     // Reuse an existing binding for this order ID, if one was already assigned.
     // Otherwise, assign a new binding.
-    const uint32_t NewBinding =
-        (OrderIdToBinding.find(OrderId) != OrderIdToBinding.end())
-            ? OrderIdToBinding[OrderId]
-            : getAndReserveFirstUnusedBinding(DescSet);
-    OrderIdToBinding[OrderId] = NewBinding;
+    const uint32_t NewBinding = (lastOrderId == OrderId)
+                                    ? lastBindingNumber
+                                    : getAndReserveFirstUnusedBinding(DescSet);
+    lastOrderId = OrderId;
+    lastBindingNumber = NewBinding;
 
     SmallVector<Value *, 8> Args;
     Args.push_back(Builder.getInt32(DescSet));
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll
index 1c235bd4c6e3e..2964da9058104 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/ImplicitBinding.ll
@@ -32,6 +32,7 @@
 ; CHECK-DAG: OpDecorate [[g]] Binding 0
 ; CHECK-DAG: OpDecorate [[h]] DescriptorSet 10
 ; CHECK-DAG: OpDecorate [[h]] Binding 3
+; CHECK-NOT: OpDecorate [[h]] Binding 4
 ; CHECK-DAG: OpDecorate [[i]] DescriptorSet 10
 ; CHECK-DAG: OpDecorate [[i]] Binding 2
 
@@ -45,8 +46,8 @@ entry:
   %4 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 10, i32 1, i32 1, i32 0, ptr nonnull @.str.8)
   %5 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 2, i32 10, i32 1, i32 0, ptr nonnull @.str.10)
   %6 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 3, i32 10, i32 2, i32 0, ptr nonnull @.str.12)
-  %7 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 10, i32 2, i32 1, i32 0, ptr nonnull @.str.14)
-  %8 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 3, i32 10, i32 2, i32 1, ptr nonnull @.str.12)
+  %7 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 3, i32 10, i32 2, i32 1, ptr nonnull @.str.12)
+  %8 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 10, i32 2, i32 1, i32 0, ptr nonnull @.str.14)
   %9 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %1, i32 0)
   %10 = load i32, ptr addrspace(11) %9, align 4
   %11 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %2, i32 0)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll
new file mode 100644
index 0000000000000..77485aa73780f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll
@@ -0,0 +1,20 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - 
+; This is to test that all resources, even if they have different descriptor sets, get unique binding numbers
+; XFAIL: *
+
+ at .str = private unnamed_addr constant [2 x i8] c"b\00", align 1
+ at .str.2 = private unnamed_addr constant [2 x i8] c"c\00", align 1
+
+define void @main() local_unnamed_addr #0 {
+entry:
+  %0 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+  %1 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %0, i32 0)
+  %2 = load i32, ptr addrspace(11) %1, align 4
+  %3 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 0, i32 1, i32 1, i32 0, ptr nonnull @.str.2)
+  %4 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.SignedImage_i32_5_2_0_0_2_0t(target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) %3, i32 0)
+  store i32 %2, ptr addrspace(11) %4, align 4
+  ret void
+}
+
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }

>From 5c21e85f81db3c7dd71a8635210fc670ac844182 Mon Sep 17 00:00:00 2001
From: luciechoi <ychoi0407 at gmail.com>
Date: Thu, 2 Oct 2025 03:42:32 +0000
Subject: [PATCH 3/3] Refactor and modify unit test to be 'expectedly failed'

---
 .../SPIRV/SPIRVLegalizeImplicitBinding.cpp    | 25 +++++++++++--------
 .../UniqueImplicitBindingNumber.ll            |  5 ++--
 2 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
index 8b9f44a9af1db..205895e48a379 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
@@ -39,6 +39,7 @@ class SPIRVLegalizeImplicitBinding : public ModulePass {
   void collectBindingInfo(Module &M);
   uint32_t getAndReserveFirstUnusedBinding(uint32_t DescSet);
   void replaceImplicitBindingCalls(Module &M);
+  void verifyUniqueOrderIdPerResource(SmallVectorImpl<CallInst *> &Calls);
 
   // A map from descriptor set to a bit vector of used binding numbers.
   std::vector<BitVector> UsedBindings;
@@ -92,30 +93,31 @@ void SPIRVLegalizeImplicitBinding::collectBindingInfo(Module &M) {
             cast<ConstantInt>(B->getArgOperand(OrderIdArgIdx))->getZExtValue();
         return OrderA < OrderB;
       });
+}
 
+void SPIRVLegalizeImplicitBinding::verifyUniqueOrderIdPerResource(
+    SmallVectorImpl<CallInst *> &Calls) {
   // Check that the order Id is unique per resource.
-  for (uint32_t i = 1; i < ImplicitBindingCalls.size(); ++i) {
+  for (uint32_t i = 1; i < Calls.size(); ++i) {
     const uint32_t OrderIdArgIdx = 0;
     const uint32_t DescSetArgIdx = 1;
     const uint32_t OrderA =
-        cast<ConstantInt>(
-            ImplicitBindingCalls[i - 1]->getArgOperand(OrderIdArgIdx))
+        cast<ConstantInt>(Calls[i - 1]->getArgOperand(OrderIdArgIdx))
             ->getZExtValue();
     const uint32_t OrderB =
-        cast<ConstantInt>(ImplicitBindingCalls[i]->getArgOperand(OrderIdArgIdx))
+        cast<ConstantInt>(Calls[i]->getArgOperand(OrderIdArgIdx))
             ->getZExtValue();
     if (OrderA == OrderB) {
       const uint32_t DescSetA =
-          cast<ConstantInt>(
-              ImplicitBindingCalls[i - 1]->getArgOperand(DescSetArgIdx))
+          cast<ConstantInt>(Calls[i - 1]->getArgOperand(DescSetArgIdx))
               ->getZExtValue();
       const uint32_t DescSetB =
-          cast<ConstantInt>(
-              ImplicitBindingCalls[i]->getArgOperand(DescSetArgIdx))
+          cast<ConstantInt>(Calls[i]->getArgOperand(DescSetArgIdx))
               ->getZExtValue();
-      assert(DescSetA == DescSetB &&
-             "If two implicit binding calls have the same order ID, they must "
-             "also have the same descriptor set.");
+      if (DescSetA != DescSetB) {
+        report_fatal_error("Implicit binding calls with the same order ID must "
+                           "have the same descriptor set");
+      }
     }
   }
 }
@@ -180,6 +182,7 @@ bool SPIRVLegalizeImplicitBinding::runOnModule(Module &M) {
   if (ImplicitBindingCalls.empty()) {
     return false;
   }
+  verifyUniqueOrderIdPerResource(ImplicitBindingCalls);
 
   replaceImplicitBindingCalls(M);
   return true;
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll
index 77485aa73780f..c968c99e4d58a 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/UniqueImplicitBindingNumber.ll
@@ -1,6 +1,5 @@
-; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - 
-; This is to test that all resources, even if they have different descriptor sets, get unique binding numbers
-; XFAIL: *
+; RUN: not llc -O0 -mtriple=spirv32-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
+; CHECK-ERROR: LLVM ERROR: Implicit binding calls with the same order ID must have the same descriptor set
 
 @.str = private unnamed_addr constant [2 x i8] c"b\00", align 1
 @.str.2 = private unnamed_addr constant [2 x i8] c"c\00", align 1



More information about the llvm-commits mailing list