[llvm-branch-commits] [llvm] [SPIRV] Refactor implicit binding legalization (PR #221662)

Deric C. via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Sep 10 11:57:41 PDT 2026


================
@@ -182,43 +139,121 @@ uint32_t SPIRVLegalizeImplicitBindingImpl::getAndReserveFirstUnusedBinding(
   return NewBinding;
 }
 
-void SPIRVLegalizeImplicitBindingImpl::replaceImplicitBindingCalls(Module &M) {
-  uint32_t lastOrderId = -1;
-  uint32_t lastBindingNumber = -1;
+// Replace the implicit binding call with a new call using explicit binding.
+static void replaceWithHandleFromBinding(Module &M, CallInst *CI,
+                                         uint32_t DescSet, uint32_t Binding,
+                                         Value *IndexOp, Value *RangeOp,
+                                         Value *Name) {
+  IRBuilder<> Builder(CI);
+  Value *DescSetOp = Builder.getInt32(DescSet);
+  Value *BindingOp = Builder.getInt32(Binding);
+  Function *NewFunc = Intrinsic::getOrInsertDeclaration(
+      &M, Intrinsic::spv_resource_handlefrombinding, {CI->getType()});
+  CallInst *NewCI = Builder.CreateCall(
+      NewFunc, {DescSetOp, BindingOp, IndexOp, RangeOp, Name});
+  NewCI->setCallingConv(CI->getCallingConv());
+  CI->replaceAllUsesWith(NewCI);
+  CI->eraseFromParent();
+}
 
-  for (CallInst *OldCI : ImplicitBindingCalls) {
-    const uint32_t OrderId = getOrderId(OldCI);
-    uint32_t BindingNumber;
-    if (OrderId == lastOrderId) {
-      BindingNumber = lastBindingNumber;
-    } else {
-      const uint32_t DescSet = getDescSet(OldCI);
-      BindingNumber = getAndReserveFirstUnusedBinding(DescSet);
+// Replace the implicit counter binding call with a new call using explicit
+// binding.
+static void replaceWithCounterHandleFromBinding(Module &M, CallInst *CI,
+                                                Value *MainHandle,
+                                                uint32_t DescSet,
+                                                uint32_t Binding) {
+
+  assert(CI->getIntrinsicID() ==
+             Intrinsic::spv_resource_counterhandlefromimplicitbinding &&
+         "unexpected implicit binding intrinsic");
+  IRBuilder<> Builder(CI);
+  Value *DescSetOp = Builder.getInt32(DescSet);
+  Value *BindingOp = Builder.getInt32(Binding);
+  Type *OverloadTys[] = {CI->getType(), CI->getArgOperand(0)->getType()};
+  Function *NewFunc = Intrinsic::getOrInsertDeclaration(
+      &M, Intrinsic::spv_resource_counterhandlefrombinding, OverloadTys);
+  CallInst *NewCI =
+      Builder.CreateCall(NewFunc, {MainHandle, DescSetOp, BindingOp});
+  NewCI->setCallingConv(CI->getCallingConv());
+  CI->replaceAllUsesWith(NewCI);
+  CI->eraseFromParent();
+}
+
+bool SPIRVLegalizeImplicitBindingImpl::replaceImplicitBindingCalls(Module &M) {
+  // Collect all implicit binding calls.
+  SmallVector<std::pair<uint32_t, CallInst *>> IBCalls;
+  bool Changed = false;
+  for (Function &F : M) {
+    if (!F.isDeclaration())
+      continue;
+
+    uint32_t OrderIdIdx;
+    if (F.getIntrinsicID() == Intrinsic::spv_resource_handlefromimplicitbinding)
+      OrderIdIdx = 0;
+    else if (F.getIntrinsicID() ==
+             Intrinsic::spv_resource_counterhandlefromimplicitbinding)
+      OrderIdIdx = 1;
+    else
+      continue;
+
+    for (User *U : F.users()) {
+      if (CallInst *CI = dyn_cast<CallInst>(U)) {
+        ConstantInt *OrderId = cast<ConstantInt>(CI->getArgOperand(OrderIdIdx));
+        IBCalls.emplace_back(OrderId->getZExtValue(), CI);
+      }
     }
+  }
 
-    if (OldCI->getIntrinsicID() ==
-        Intrinsic::spv_resource_handlefromimplicitbinding) {
-      replaceResourceHandleCall(M, OldCI, BindingNumber);
+  // Sort the collected calls by their order ID.
+  llvm::sort(IBCalls, [](const std::pair<uint32_t, CallInst *> &A,
+                         const std::pair<uint32_t, CallInst *> &B) {
+    return A.first < B.first;
+  });
+
+  // Assign bindings based on the order ID. Same order ID gets the same binding.
+  // Also make sure that calls with the same order ID have the same descriptor
+  // set.
+  uint32_t LastOrderId = -1;
+  uint32_t LastBinding = -1;
+  uint32_t LastDescSet = -1;
+  for (auto &[OrderId, CI] : IBCalls) {
+    uint32_t Binding;
+    uint32_t DescSet = getDescSet(CI);
+    if (OrderId == LastOrderId) {
+      if (DescSet != LastDescSet)
+        report_fatal_error("Implicit binding calls with the same order ID must "
+                           "have the same descriptor set");
+      Binding = LastBinding;
     } else {
-      assert(OldCI->getIntrinsicID() ==
-                 Intrinsic::spv_resource_counterhandlefromimplicitbinding &&
-             "Unexpected implicit binding intrinsic");
-      replaceCounterHandleCall(M, OldCI, BindingNumber);
+      Binding = getAndReserveFirstUnusedBinding(DescSet);
     }
-    lastOrderId = OrderId;
-    lastBindingNumber = BindingNumber;
+
+    // Replace the implicit binding call with an explicit binding call.
+    if (CI->getIntrinsicID() ==
+        Intrinsic::spv_resource_handlefromimplicitbinding)
+      replaceWithHandleFromBinding(M, CI, DescSet, Binding,
+                                   CI->getArgOperand(2), CI->getArgOperand(3),
+                                   CI->getArgOperand(4));
+    else
+      replaceWithCounterHandleFromBinding(M, CI, CI->getArgOperand(0), DescSet,
+                                          Binding);
----------------
Icohedron wrote:

[SUGGESTION] These callsites are the only ones that call the `replaceWith...FromBinding` functions, and both functions take `CI` in addition to `CI`'s operands.
You could remove the function parameters that correspond to `CI`'s operands and instead let the callee use `CI->getArgOperand(N)` to retrieve the values it needs.

The `replaceWithCounterHandleFromBinding` function in particular calls `CI->getArgOperand(0)` again to build the overload type despite the caller having already provided it in the form of the `MainHandle` parameter.

https://github.com/llvm/llvm-project/pull/221662


More information about the llvm-branch-commits mailing list