Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[DirectX] Implement DXILResourceImplicitBinding pass #138043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: users/hekota/pr137258-resource-binding-analysis
Choose a base branch
from

Conversation

hekota
Copy link
Member

@hekota hekota commented Apr 30, 2025

The DXILResourceImplicitBinding pass uses the results of DXILResourceBindingAnalysis to assigns register slots to resources that do not have explicit binding. It replaces all llvm.dx.resource.handlefromimplicitbinding calls with llvm.dx.resource.handlefrombinding using the newly assigned binding.

If a binding cannot be found for a resource, the pass will raise a diagnostic error. Currently this diagnostic message does not include the resource name, which will be addressed in a separate task (#137868).

Part 2/2 of #136786
Closes #136786

This pass takes advantage of the DXILResourceBinding analysis and assigns
register slots to resources that do not have explicit binding.

Part 2/2 of llvm#136786

Closes llvm#136786
@llvmbot
Copy link
Member

llvmbot commented Apr 30, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Helena Kotas (hekota)

Changes

The DXILResourceImplicitBinding pass uses the results of DXILResourceBindingAnalysis to assigns register slots to resources that do not have explicit binding. It replaces all llvm.dx.resource.handlefromimplicitbinding calls with llvm.dx.resource.handlefrombinding using the newly assigned binding.

If a binding cannot be found for a resource, the pass will raise a diagnostic error. Currently this diagnostic message does not include the resource name, which will be addressed in a separate task (#137868).

Part 2/2 of #136786
Closes #136786


Patch is 26.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138043.diff

16 Files Affected:

  • (modified) llvm/include/llvm/Analysis/DXILResource.h (+8)
  • (modified) llvm/include/llvm/InitializePasses.h (+1)
  • (modified) llvm/lib/Analysis/Analysis.cpp (+1)
  • (modified) llvm/lib/Analysis/DXILResource.cpp (+53)
  • (modified) llvm/lib/Target/DirectX/CMakeLists.txt (+1)
  • (added) llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp (+182)
  • (added) llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h (+29)
  • (modified) llvm/lib/Target/DirectX/DirectX.h (+6)
  • (modified) llvm/lib/Target/DirectX/DirectXPassRegistry.def (+1)
  • (modified) llvm/lib/Target/DirectX/DirectXTargetMachine.cpp (+3)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll (+41)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll (+44)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll (+30)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll (+34)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll (+42)
  • (modified) llvm/test/CodeGen/DirectX/llc-pipeline.ll (+2)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 569f5346b9e36..fd34fdfda4c1c 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -632,12 +632,15 @@ class DXILResourceBindingInfo {
     RegisterSpace(uint32_t Space) : Space(Space) {
       FreeRanges.emplace_back(0, UINT32_MAX);
     }
+    // Size == -1 means unbounded array
+    bool findAvailableBinding(int32_t Size, uint32_t *RegSlot);
   };
 
   struct BindingSpaces {
     dxil::ResourceClass ResClass;
     llvm::SmallVector<RegisterSpace> Spaces;
     BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {}
+    RegisterSpace &getOrInsertSpace(uint32_t Space);
   };
 
 private:
@@ -658,6 +661,7 @@ class DXILResourceBindingInfo {
         OverlappingBinding(false) {}
 
   bool hasImplicitBinding() const { return ImplicitBinding; }
+  void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; }
   bool hasOverlappingBinding() const { return OverlappingBinding; }
 
   BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
@@ -673,6 +677,10 @@ class DXILResourceBindingInfo {
     }
   }
 
+  // Size == -1 means unbounded array
+  bool findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
+                            int32_t Size, uint32_t *RegSlot);
+
   friend class DXILResourceBindingAnalysis;
   friend class DXILResourceBindingWrapperPass;
 };
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index b84c6d6123d58..71db56d922676 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -85,6 +85,7 @@ void initializeDCELegacyPassPass(PassRegistry &);
 void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
 void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
 void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
+void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
 void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
 void initializeDXILResourceWrapperPassPass(PassRegistry &);
 void initializeDeadMachineInstructionElimPass(PassRegistry &);
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index 484a456f49f1b..9f5daf32be9a0 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -27,6 +27,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
   initializeCallGraphViewerPass(Registry);
   initializeCycleInfoWrapperPassPass(Registry);
   initializeDXILMetadataAnalysisWrapperPassPass(Registry);
+  initializeDXILResourceWrapperPassPass(Registry);
   initializeDXILResourceBindingWrapperPassPass(Registry);
   initializeDXILResourceTypeWrapperPassPass(Registry);
   initializeDXILResourceWrapperPassPass(Registry);
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index ce8e250a32ebe..34355c81e77b0 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,6 +995,59 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
   }
 }
 
+// returns false if binding could not be found in given space
+bool DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
+                                                   uint32_t Space, int32_t Size,
+                                                   uint32_t *RegSlot) {
+  BindingSpaces &BS = getBindingSpaces(RC);
+  RegisterSpace &RS = BS.getOrInsertSpace(Space);
+  return RS.findAvailableBinding(Size, RegSlot);
+}
+
+DXILResourceBindingInfo::RegisterSpace &
+DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
+  for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
+    if (I->Space == Space)
+      return *I;
+    if (I->Space < Space)
+      continue;
+    return *Spaces.insert(I, Space);
+  }
+  return Spaces.emplace_back(Space);
+}
+
+bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
+    int32_t Size, uint32_t *RegSlot) {
+  assert((Size == -1 || Size > 0) && "invalid size");
+
+  if (FreeRanges.empty())
+    return false;
+
+  // unbounded array
+  if (Size == -1) {
+    BindingRange &Last = FreeRanges.back();
+    if (Last.UpperBound != UINT32_MAX)
+      // this space is already occupied by an unbounded array
+      return false;
+    *RegSlot = Last.LowerBound;
+    FreeRanges.pop_back();
+    return true;
+  }
+
+  // single resource or fixed-size array
+  for (BindingRange &R : FreeRanges) {
+    if (R.UpperBound - R.LowerBound + 1 < (uint32_t)Size)
+      continue;
+    *RegSlot = R.LowerBound;
+    // This might create a range where (LowerBound == UpperBound + 1), but
+    // that's ok.
+    R.LowerBound += Size;
+    return true;
+  }
+
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 
 AnalysisKey DXILResourceTypeAnalysis::Key;
diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt
index 65105d3a5f4c3..9f5550a9a9ce8 100644
--- a/llvm/lib/Target/DirectX/CMakeLists.txt
+++ b/llvm/lib/Target/DirectX/CMakeLists.txt
@@ -31,6 +31,7 @@ add_llvm_target(DirectXCodeGen
   DXILPrepare.cpp
   DXILPrettyPrinter.cpp
   DXILResourceAccess.cpp
+  DXILResourceImplicitBinding.cpp
   DXILShaderFlags.cpp
   DXILTranslateMetadata.cpp
   DXILRootSignature.cpp
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
new file mode 100644
index 0000000000000..80293ad7c9716
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
@@ -0,0 +1,182 @@
+//===- DXILResourceImplicitBinding.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DXILResourceImplicitBinding.h"
+#include "DirectX.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicsDirectX.h"
+#include "llvm/IR/Module.h"
+#include "llvm/InitializePasses.h"
+#include <cstdint>
+
+#define DEBUG_TYPE "dxil-resource-implicit-binding"
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+namespace {
+
+static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) {
+  Function *F = ImplBindingCall->getFunction();
+  LLVMContext &Context = F->getParent()->getContext();
+  // FIXME: include the name of the resource in the error message
+  // (llvm/llvm-project#137868)
+  Context.diagnose(
+      DiagnosticInfoGenericWithLoc("resource cannot be allocated", *F,
+                                   ImplBindingCall->getDebugLoc(), DS_Error));
+}
+
+static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
+                           DXILResourceTypeMap &DRTM) {
+  struct ImplBindingCall {
+    int OrderID;
+    CallInst *Call;
+    ImplBindingCall(int OrderID, CallInst *Call)
+        : OrderID(OrderID), Call(Call) {}
+  };
+  SmallVector<ImplBindingCall> Calls;
+  SmallVector<Function *> FunctionsToMaybeRemove;
+
+  // collect all of the llvm.dx.resource.handlefromImplicitbinding calls
+  for (Function &F : M.functions()) {
+    if (!F.isDeclaration())
+      continue;
+
+    if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefromimplicitbinding)
+      continue;
+
+    for (User *U : F.users()) {
+      if (CallInst *CI = dyn_cast<CallInst>(U)) {
+        int OrderID = cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
+        Calls.emplace_back(OrderID, CI);
+      }
+    }
+    FunctionsToMaybeRemove.emplace_back(&F);
+  }
+
+  // sort all the collected implicit bindings by OrderID
+  llvm::stable_sort(
+      Calls, [](auto &LHS, auto &RHS) { return LHS.OrderID < RHS.OrderID; });
+
+  // iterate over sorted calls, find binding for each new OrderID and replace
+  // each call with dx_resource_handlefrombinding using the new binding
+  int LastOrderID = -1;
+  llvm::TargetExtType *HandleTy = nullptr;
+  ConstantInt *RegSlotOp = nullptr;
+  bool AllBindingsAssigned = true;
+  bool Changed = false;
+
+  for (auto &IB : Calls) {
+    IRBuilder<> Builder(IB.Call);
+
+    if (IB.OrderID != LastOrderID) {
+      LastOrderID = IB.OrderID;
+      HandleTy = cast<TargetExtType>(IB.Call->getType());
+      ResourceTypeInfo &RTI = DRTM[HandleTy];
+
+      uint32_t Space =
+          cast<ConstantInt>(IB.Call->getArgOperand(1))->getZExtValue();
+      int32_t Size =
+          cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue();
+
+      uint32_t RegSlot;
+      RegSlotOp = nullptr;
+      if (!DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size,
+                                     &RegSlot)) {
+        diagnoseImplicitBindingNotFound(IB.Call);
+        AllBindingsAssigned = false;
+        continue;
+      }
+      RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot);
+    }
+
+    if (!RegSlotOp)
+      continue;
+
+    auto *NewCall = Builder.CreateIntrinsic(
+        HandleTy, Intrinsic::dx_resource_handlefrombinding,
+        {IB.Call->getOperand(1),   /* space */
+         RegSlotOp,                /* register slot */
+         IB.Call->getOperand(2),   /* size */
+         IB.Call->getOperand(3),   /* index */
+         IB.Call->getOperand(4)}); /* non-uniform flag */
+    IB.Call->replaceAllUsesWith(NewCall);
+    IB.Call->eraseFromParent();
+    Changed = true;
+  }
+
+  for (Function *F : FunctionsToMaybeRemove) {
+    if (F->user_empty()) {
+      F->eraseFromParent();
+      Changed = true;
+    }
+  }
+
+  DRBI.setHasImplicitBinding(!AllBindingsAssigned);
+  return Changed;
+}
+
+} // end anonymous namespace
+
+PreservedAnalyses DXILResourceImplicitBinding::run(Module &M,
+                                                   ModuleAnalysisManager &AM) {
+
+  PreservedAnalyses PA;
+
+  DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M);
+  DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
+  if (DRBI.hasImplicitBinding())
+    if (assignBindings(M, DRBI, DRTM))
+      return PA;
+  return PreservedAnalyses::all();
+}
+
+namespace {
+
+class DXILResourceImplicitBindingLegacy : public ModulePass {
+public:
+  DXILResourceImplicitBindingLegacy() : ModulePass(ID) {}
+
+  bool runOnModule(Module &M) override {
+    DXILResourceTypeMap &DRTM =
+        getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
+    DXILResourceBindingInfo &DRBI =
+        getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
+
+    if (DRBI.hasImplicitBinding())
+      return assignBindings(M, DRBI, DRTM);
+    return false;
+  }
+
+  static char ID; // Pass identification.
+  void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+    AU.addRequired<DXILResourceTypeWrapperPass>();
+    AU.addRequired<DXILResourceBindingWrapperPass>();
+  }
+};
+
+char DXILResourceImplicitBindingLegacy::ID = 0;
+} // end anonymous namespace
+
+INITIALIZE_PASS_BEGIN(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
+                      "DXIL Resource Implicit Binding", false, false)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
+INITIALIZE_PASS_END(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
+                    "DXIL Resource Implicit Binding", false, false)
+
+ModulePass *llvm::createDXILResourceImplicitBindingLegacyPass() {
+  return new DXILResourceImplicitBindingLegacy();
+}
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
new file mode 100644
index 0000000000000..86ca9ec6842a0
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
@@ -0,0 +1,29 @@
+//===- DXILResourceImplicitBindings.h --_____________-----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// \file Assign register slots to resources without explicit binding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
+#define LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+
+namespace llvm {
+
+class DXILResourceImplicitBinding
+    : public PassInfoMixin<DXILResourceImplicitBinding> {
+public:
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
diff --git a/llvm/lib/Target/DirectX/DirectX.h b/llvm/lib/Target/DirectX/DirectX.h
index f64aaaf65d937..647b8ff42867c 100644
--- a/llvm/lib/Target/DirectX/DirectX.h
+++ b/llvm/lib/Target/DirectX/DirectX.h
@@ -78,6 +78,12 @@ void initializeDXILResourceAccessLegacyPass(PassRegistry &);
 /// Pass to update resource accesses to use load/store directly.
 FunctionPass *createDXILResourceAccessLegacyPass();
 
+/// Initializer for DXILResourceImplicitBindingLegacyPass
+void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
+
+/// Pass to assign register slots to resources without binding.
+ModulePass *createDXILResourceImplicitBindingLegacyPass();
+
 /// Initializer for DXILTranslateMetadata.
 void initializeDXILTranslateMetadataLegacyPass(PassRegistry &);
 
diff --git a/llvm/lib/Target/DirectX/DirectXPassRegistry.def b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
index da239402d01eb..ef65b96799eae 100644
--- a/llvm/lib/Target/DirectX/DirectXPassRegistry.def
+++ b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
@@ -30,6 +30,7 @@ MODULE_PASS("dxil-intrinsic-expansion", DXILIntrinsicExpansion())
 MODULE_PASS("dxil-op-lower", DXILOpLowering())
 MODULE_PASS("dxil-pretty-printer", DXILPrettyPrinterPass(dbgs()))
 MODULE_PASS("dxil-translate-metadata", DXILTranslateMetadata())
+MODULE_PASS("dxil-resource-implicit-binding", DXILResourceImplicitBinding())
 // TODO: rename to print<foo> after NPM switch
 MODULE_PASS("print-dx-shader-flags", dxil::ShaderFlagsAnalysisPrinter(dbgs()))
 MODULE_PASS("print<dxil-root-signature>", dxil::RootSignatureAnalysisPrinter(dbgs()))
diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
index d3d1f94f3ab1c..2f2372b779092 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
@@ -21,6 +21,7 @@
 #include "DXILOpLowering.h"
 #include "DXILPrettyPrinter.h"
 #include "DXILResourceAccess.h"
+#include "DXILResourceImplicitBinding.h"
 #include "DXILRootSignature.h"
 #include "DXILShaderFlags.h"
 #include "DXILTranslateMetadata.h"
@@ -62,6 +63,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() {
   initializeDXContainerGlobalsPass(*PR);
   initializeDXILOpLoweringLegacyPass(*PR);
   initializeDXILResourceAccessLegacyPass(*PR);
+  initializeDXILResourceImplicitBindingLegacyPass(*PR);
   initializeDXILTranslateMetadataLegacyPass(*PR);
   initializeShaderFlagsAnalysisWrapperPass(*PR);
   initializeRootSignatureAnalysisWrapperPass(*PR);
@@ -99,6 +101,7 @@ class DirectXPassConfig : public TargetPassConfig {
   FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
   void addCodeGenPrepare() override {
     addPass(createDXILFinalizeLinkageLegacyPass());
+    addPass(createDXILResourceImplicitBindingLegacyPass());
     addPass(createDXILIntrinsicExpansionLegacyPass());
     addPass(createDXILCBufferAccessLegacyPass());
     addPass(createDXILDataScalarizationLegacyPass());
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
new file mode 100644
index 0000000000000..6196472cb8374
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
@@ -0,0 +1,41 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined (with random order of handlefromimplicitbinding calls):
+; RWBuffer<float> A : register(u2);
+; RWBuffer<float> B[4];    // gets u3 because it does not fit before A (range 4)
+; RWBuffer<int> C[2];      // gets u0 because it fits before A (range 2)
+; RWBuffer<float> E[5];    // gets u7 which is right after B (range 5)
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_arrays() {
+
+; RWBuffer<float> A : register(u2);
+  %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> E[2];
+  %bufE = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 30, i32 0, i32 5, i32 4, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 7, i32 5, i32 4, i1 false)
+
+; RWBuffer<float> B[4];
+  %bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 10, i32 0, i32 4, i32 2, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 3, i32 4, i32 2, i1 false)
+
+; RWBuffer<int> C[2];
+  %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 20, i32 0, i32 2, i32 1, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 0, i32 2, i32 1, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+  ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
new file mode 100644
index 0000000000000..fce904e45ba09
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
@@ -0,0 +1,44 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined (with random order of handlefromimplicitbinding calls):
+; RWBuffer<float> A : register(u5);         // defaults to space0
+; RWBuffer<int> B[];                      // gets u6 (unbounded range)
+; RWBuffer<float> C[4] : register(space5);  // gets u0 in space5
+; RWBuffer<int> D[] : register(space5);   // gets u4 in space5
+; RWBuffer<float> E[3] : register(space10); // gets u0, space10
+; StructuredBuffer<int> F : register(space3);; // gets t0 in space3
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_many_spaces() {
+
+; RWBuffer<float> A : register(u1);
+  %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> B[];
+%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 6, i32 -1, i32 0, i1 false)
+
+; RWBuffer<int> C : register(u5);
+  %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME:...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Apr 30, 2025

@llvm/pr-subscribers-backend-directx

Author: Helena Kotas (hekota)

Changes

The DXILResourceImplicitBinding pass uses the results of DXILResourceBindingAnalysis to assigns register slots to resources that do not have explicit binding. It replaces all llvm.dx.resource.handlefromimplicitbinding calls with llvm.dx.resource.handlefrombinding using the newly assigned binding.

If a binding cannot be found for a resource, the pass will raise a diagnostic error. Currently this diagnostic message does not include the resource name, which will be addressed in a separate task (#137868).

Part 2/2 of #136786
Closes #136786


Patch is 26.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138043.diff

16 Files Affected:

  • (modified) llvm/include/llvm/Analysis/DXILResource.h (+8)
  • (modified) llvm/include/llvm/InitializePasses.h (+1)
  • (modified) llvm/lib/Analysis/Analysis.cpp (+1)
  • (modified) llvm/lib/Analysis/DXILResource.cpp (+53)
  • (modified) llvm/lib/Target/DirectX/CMakeLists.txt (+1)
  • (added) llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp (+182)
  • (added) llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h (+29)
  • (modified) llvm/lib/Target/DirectX/DirectX.h (+6)
  • (modified) llvm/lib/Target/DirectX/DirectXPassRegistry.def (+1)
  • (modified) llvm/lib/Target/DirectX/DirectXTargetMachine.cpp (+3)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll (+41)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll (+44)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll (+30)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll (+34)
  • (added) llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll (+42)
  • (modified) llvm/test/CodeGen/DirectX/llc-pipeline.ll (+2)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 569f5346b9e36..fd34fdfda4c1c 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -632,12 +632,15 @@ class DXILResourceBindingInfo {
     RegisterSpace(uint32_t Space) : Space(Space) {
       FreeRanges.emplace_back(0, UINT32_MAX);
     }
+    // Size == -1 means unbounded array
+    bool findAvailableBinding(int32_t Size, uint32_t *RegSlot);
   };
 
   struct BindingSpaces {
     dxil::ResourceClass ResClass;
     llvm::SmallVector<RegisterSpace> Spaces;
     BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {}
+    RegisterSpace &getOrInsertSpace(uint32_t Space);
   };
 
 private:
@@ -658,6 +661,7 @@ class DXILResourceBindingInfo {
         OverlappingBinding(false) {}
 
   bool hasImplicitBinding() const { return ImplicitBinding; }
+  void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; }
   bool hasOverlappingBinding() const { return OverlappingBinding; }
 
   BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
@@ -673,6 +677,10 @@ class DXILResourceBindingInfo {
     }
   }
 
+  // Size == -1 means unbounded array
+  bool findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
+                            int32_t Size, uint32_t *RegSlot);
+
   friend class DXILResourceBindingAnalysis;
   friend class DXILResourceBindingWrapperPass;
 };
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index b84c6d6123d58..71db56d922676 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -85,6 +85,7 @@ void initializeDCELegacyPassPass(PassRegistry &);
 void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
 void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
 void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
+void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
 void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
 void initializeDXILResourceWrapperPassPass(PassRegistry &);
 void initializeDeadMachineInstructionElimPass(PassRegistry &);
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index 484a456f49f1b..9f5daf32be9a0 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -27,6 +27,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
   initializeCallGraphViewerPass(Registry);
   initializeCycleInfoWrapperPassPass(Registry);
   initializeDXILMetadataAnalysisWrapperPassPass(Registry);
+  initializeDXILResourceWrapperPassPass(Registry);
   initializeDXILResourceBindingWrapperPassPass(Registry);
   initializeDXILResourceTypeWrapperPassPass(Registry);
   initializeDXILResourceWrapperPassPass(Registry);
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index ce8e250a32ebe..34355c81e77b0 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,6 +995,59 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
   }
 }
 
+// returns false if binding could not be found in given space
+bool DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
+                                                   uint32_t Space, int32_t Size,
+                                                   uint32_t *RegSlot) {
+  BindingSpaces &BS = getBindingSpaces(RC);
+  RegisterSpace &RS = BS.getOrInsertSpace(Space);
+  return RS.findAvailableBinding(Size, RegSlot);
+}
+
+DXILResourceBindingInfo::RegisterSpace &
+DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
+  for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
+    if (I->Space == Space)
+      return *I;
+    if (I->Space < Space)
+      continue;
+    return *Spaces.insert(I, Space);
+  }
+  return Spaces.emplace_back(Space);
+}
+
+bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
+    int32_t Size, uint32_t *RegSlot) {
+  assert((Size == -1 || Size > 0) && "invalid size");
+
+  if (FreeRanges.empty())
+    return false;
+
+  // unbounded array
+  if (Size == -1) {
+    BindingRange &Last = FreeRanges.back();
+    if (Last.UpperBound != UINT32_MAX)
+      // this space is already occupied by an unbounded array
+      return false;
+    *RegSlot = Last.LowerBound;
+    FreeRanges.pop_back();
+    return true;
+  }
+
+  // single resource or fixed-size array
+  for (BindingRange &R : FreeRanges) {
+    if (R.UpperBound - R.LowerBound + 1 < (uint32_t)Size)
+      continue;
+    *RegSlot = R.LowerBound;
+    // This might create a range where (LowerBound == UpperBound + 1), but
+    // that's ok.
+    R.LowerBound += Size;
+    return true;
+  }
+
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 
 AnalysisKey DXILResourceTypeAnalysis::Key;
diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt
index 65105d3a5f4c3..9f5550a9a9ce8 100644
--- a/llvm/lib/Target/DirectX/CMakeLists.txt
+++ b/llvm/lib/Target/DirectX/CMakeLists.txt
@@ -31,6 +31,7 @@ add_llvm_target(DirectXCodeGen
   DXILPrepare.cpp
   DXILPrettyPrinter.cpp
   DXILResourceAccess.cpp
+  DXILResourceImplicitBinding.cpp
   DXILShaderFlags.cpp
   DXILTranslateMetadata.cpp
   DXILRootSignature.cpp
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
new file mode 100644
index 0000000000000..80293ad7c9716
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
@@ -0,0 +1,182 @@
+//===- DXILResourceImplicitBinding.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DXILResourceImplicitBinding.h"
+#include "DirectX.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicsDirectX.h"
+#include "llvm/IR/Module.h"
+#include "llvm/InitializePasses.h"
+#include <cstdint>
+
+#define DEBUG_TYPE "dxil-resource-implicit-binding"
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+namespace {
+
+static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) {
+  Function *F = ImplBindingCall->getFunction();
+  LLVMContext &Context = F->getParent()->getContext();
+  // FIXME: include the name of the resource in the error message
+  // (llvm/llvm-project#137868)
+  Context.diagnose(
+      DiagnosticInfoGenericWithLoc("resource cannot be allocated", *F,
+                                   ImplBindingCall->getDebugLoc(), DS_Error));
+}
+
+static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
+                           DXILResourceTypeMap &DRTM) {
+  struct ImplBindingCall {
+    int OrderID;
+    CallInst *Call;
+    ImplBindingCall(int OrderID, CallInst *Call)
+        : OrderID(OrderID), Call(Call) {}
+  };
+  SmallVector<ImplBindingCall> Calls;
+  SmallVector<Function *> FunctionsToMaybeRemove;
+
+  // collect all of the llvm.dx.resource.handlefromImplicitbinding calls
+  for (Function &F : M.functions()) {
+    if (!F.isDeclaration())
+      continue;
+
+    if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefromimplicitbinding)
+      continue;
+
+    for (User *U : F.users()) {
+      if (CallInst *CI = dyn_cast<CallInst>(U)) {
+        int OrderID = cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
+        Calls.emplace_back(OrderID, CI);
+      }
+    }
+    FunctionsToMaybeRemove.emplace_back(&F);
+  }
+
+  // sort all the collected implicit bindings by OrderID
+  llvm::stable_sort(
+      Calls, [](auto &LHS, auto &RHS) { return LHS.OrderID < RHS.OrderID; });
+
+  // iterate over sorted calls, find binding for each new OrderID and replace
+  // each call with dx_resource_handlefrombinding using the new binding
+  int LastOrderID = -1;
+  llvm::TargetExtType *HandleTy = nullptr;
+  ConstantInt *RegSlotOp = nullptr;
+  bool AllBindingsAssigned = true;
+  bool Changed = false;
+
+  for (auto &IB : Calls) {
+    IRBuilder<> Builder(IB.Call);
+
+    if (IB.OrderID != LastOrderID) {
+      LastOrderID = IB.OrderID;
+      HandleTy = cast<TargetExtType>(IB.Call->getType());
+      ResourceTypeInfo &RTI = DRTM[HandleTy];
+
+      uint32_t Space =
+          cast<ConstantInt>(IB.Call->getArgOperand(1))->getZExtValue();
+      int32_t Size =
+          cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue();
+
+      uint32_t RegSlot;
+      RegSlotOp = nullptr;
+      if (!DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size,
+                                     &RegSlot)) {
+        diagnoseImplicitBindingNotFound(IB.Call);
+        AllBindingsAssigned = false;
+        continue;
+      }
+      RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot);
+    }
+
+    if (!RegSlotOp)
+      continue;
+
+    auto *NewCall = Builder.CreateIntrinsic(
+        HandleTy, Intrinsic::dx_resource_handlefrombinding,
+        {IB.Call->getOperand(1),   /* space */
+         RegSlotOp,                /* register slot */
+         IB.Call->getOperand(2),   /* size */
+         IB.Call->getOperand(3),   /* index */
+         IB.Call->getOperand(4)}); /* non-uniform flag */
+    IB.Call->replaceAllUsesWith(NewCall);
+    IB.Call->eraseFromParent();
+    Changed = true;
+  }
+
+  for (Function *F : FunctionsToMaybeRemove) {
+    if (F->user_empty()) {
+      F->eraseFromParent();
+      Changed = true;
+    }
+  }
+
+  DRBI.setHasImplicitBinding(!AllBindingsAssigned);
+  return Changed;
+}
+
+} // end anonymous namespace
+
+PreservedAnalyses DXILResourceImplicitBinding::run(Module &M,
+                                                   ModuleAnalysisManager &AM) {
+
+  PreservedAnalyses PA;
+
+  DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M);
+  DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
+  if (DRBI.hasImplicitBinding())
+    if (assignBindings(M, DRBI, DRTM))
+      return PA;
+  return PreservedAnalyses::all();
+}
+
+namespace {
+
+class DXILResourceImplicitBindingLegacy : public ModulePass {
+public:
+  DXILResourceImplicitBindingLegacy() : ModulePass(ID) {}
+
+  bool runOnModule(Module &M) override {
+    DXILResourceTypeMap &DRTM =
+        getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
+    DXILResourceBindingInfo &DRBI =
+        getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
+
+    if (DRBI.hasImplicitBinding())
+      return assignBindings(M, DRBI, DRTM);
+    return false;
+  }
+
+  static char ID; // Pass identification.
+  void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+    AU.addRequired<DXILResourceTypeWrapperPass>();
+    AU.addRequired<DXILResourceBindingWrapperPass>();
+  }
+};
+
+char DXILResourceImplicitBindingLegacy::ID = 0;
+} // end anonymous namespace
+
+INITIALIZE_PASS_BEGIN(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
+                      "DXIL Resource Implicit Binding", false, false)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
+INITIALIZE_PASS_END(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
+                    "DXIL Resource Implicit Binding", false, false)
+
+ModulePass *llvm::createDXILResourceImplicitBindingLegacyPass() {
+  return new DXILResourceImplicitBindingLegacy();
+}
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
new file mode 100644
index 0000000000000..86ca9ec6842a0
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
@@ -0,0 +1,29 @@
+//===- DXILResourceImplicitBindings.h --_____________-----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// \file Assign register slots to resources without explicit binding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
+#define LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+
+namespace llvm {
+
+class DXILResourceImplicitBinding
+    : public PassInfoMixin<DXILResourceImplicitBinding> {
+public:
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
diff --git a/llvm/lib/Target/DirectX/DirectX.h b/llvm/lib/Target/DirectX/DirectX.h
index f64aaaf65d937..647b8ff42867c 100644
--- a/llvm/lib/Target/DirectX/DirectX.h
+++ b/llvm/lib/Target/DirectX/DirectX.h
@@ -78,6 +78,12 @@ void initializeDXILResourceAccessLegacyPass(PassRegistry &);
 /// Pass to update resource accesses to use load/store directly.
 FunctionPass *createDXILResourceAccessLegacyPass();
 
+/// Initializer for DXILResourceImplicitBindingLegacyPass
+void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
+
+/// Pass to assign register slots to resources without binding.
+ModulePass *createDXILResourceImplicitBindingLegacyPass();
+
 /// Initializer for DXILTranslateMetadata.
 void initializeDXILTranslateMetadataLegacyPass(PassRegistry &);
 
diff --git a/llvm/lib/Target/DirectX/DirectXPassRegistry.def b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
index da239402d01eb..ef65b96799eae 100644
--- a/llvm/lib/Target/DirectX/DirectXPassRegistry.def
+++ b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
@@ -30,6 +30,7 @@ MODULE_PASS("dxil-intrinsic-expansion", DXILIntrinsicExpansion())
 MODULE_PASS("dxil-op-lower", DXILOpLowering())
 MODULE_PASS("dxil-pretty-printer", DXILPrettyPrinterPass(dbgs()))
 MODULE_PASS("dxil-translate-metadata", DXILTranslateMetadata())
+MODULE_PASS("dxil-resource-implicit-binding", DXILResourceImplicitBinding())
 // TODO: rename to print<foo> after NPM switch
 MODULE_PASS("print-dx-shader-flags", dxil::ShaderFlagsAnalysisPrinter(dbgs()))
 MODULE_PASS("print<dxil-root-signature>", dxil::RootSignatureAnalysisPrinter(dbgs()))
diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
index d3d1f94f3ab1c..2f2372b779092 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
@@ -21,6 +21,7 @@
 #include "DXILOpLowering.h"
 #include "DXILPrettyPrinter.h"
 #include "DXILResourceAccess.h"
+#include "DXILResourceImplicitBinding.h"
 #include "DXILRootSignature.h"
 #include "DXILShaderFlags.h"
 #include "DXILTranslateMetadata.h"
@@ -62,6 +63,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() {
   initializeDXContainerGlobalsPass(*PR);
   initializeDXILOpLoweringLegacyPass(*PR);
   initializeDXILResourceAccessLegacyPass(*PR);
+  initializeDXILResourceImplicitBindingLegacyPass(*PR);
   initializeDXILTranslateMetadataLegacyPass(*PR);
   initializeShaderFlagsAnalysisWrapperPass(*PR);
   initializeRootSignatureAnalysisWrapperPass(*PR);
@@ -99,6 +101,7 @@ class DirectXPassConfig : public TargetPassConfig {
   FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
   void addCodeGenPrepare() override {
     addPass(createDXILFinalizeLinkageLegacyPass());
+    addPass(createDXILResourceImplicitBindingLegacyPass());
     addPass(createDXILIntrinsicExpansionLegacyPass());
     addPass(createDXILCBufferAccessLegacyPass());
     addPass(createDXILDataScalarizationLegacyPass());
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
new file mode 100644
index 0000000000000..6196472cb8374
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
@@ -0,0 +1,41 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined (with random order of handlefromimplicitbinding calls):
+; RWBuffer<float> A : register(u2);
+; RWBuffer<float> B[4];    // gets u3 because it does not fit before A (range 4)
+; RWBuffer<int> C[2];      // gets u0 because it fits before A (range 2)
+; RWBuffer<float> E[5];    // gets u7 which is right after B (range 5)
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_arrays() {
+
+; RWBuffer<float> A : register(u2);
+  %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> E[2];
+  %bufE = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 30, i32 0, i32 5, i32 4, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 7, i32 5, i32 4, i1 false)
+
+; RWBuffer<float> B[4];
+  %bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 10, i32 0, i32 4, i32 2, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 3, i32 4, i32 2, i1 false)
+
+; RWBuffer<int> C[2];
+  %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 20, i32 0, i32 2, i32 1, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 0, i32 2, i32 1, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+  ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
new file mode 100644
index 0000000000000..fce904e45ba09
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
@@ -0,0 +1,44 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined (with random order of handlefromimplicitbinding calls):
+; RWBuffer<float> A : register(u5);         // defaults to space0
+; RWBuffer<int> B[];                      // gets u6 (unbounded range)
+; RWBuffer<float> C[4] : register(space5);  // gets u0 in space5
+; RWBuffer<int> D[] : register(space5);   // gets u4 in space5
+; RWBuffer<float> E[3] : register(space10); // gets u0, space10
+; StructuredBuffer<int> F : register(space3);; // gets t0 in space3
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_many_spaces() {
+
+; RWBuffer<float> A : register(u1);
+  %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> B[];
+%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 6, i32 -1, i32 0, i1 false)
+
+; RWBuffer<int> C : register(u5);
+  %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME:...
[truncated]

@@ -0,0 +1,44 @@
; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized this test is not complete... working on a fix.

@hekota hekota changed the title [HLSL] Implementation of DXILResourceImplicitBinding pass [DirectX] Implementation of DXILResourceImplicitBinding pass May 1, 2025
@hekota hekota changed the title [DirectX] Implementation of DXILResourceImplicitBinding pass [DirectX] Implement DXILResourceImplicitBinding pass May 1, 2025
hekota added a commit to hekota/llvm-project that referenced this pull request May 7, 2025
…urces

Adds constructor for resources with implicit binding and applies it to
all resources without binding at the global scope. Adds Clang buildin
function __builtin_hlsl_resource_handlefromimplicitbinding that it translated
to llvm.dx|spv.resource.handlefromimplicitbinding calls. Specific bindings are
assigned in DXILResourceImplicitBinding pass.

Depends on llvm#138043

Closes llvm#136784
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants