[llvm] 03934d0 - [DirectX] Implement DXILResourceImplicitBinding pass (#138043)

via llvm-commits llvm-commits at lists.llvm.org
Mon May 12 23:00:03 PDT 2025


Author: Helena Kotas
Date: 2025-05-12T23:00:00-07:00
New Revision: 03934d0a213e479622c7875a2a59ae64b4f2213f

URL: https://github.com/llvm/llvm-project/commit/03934d0a213e479622c7875a2a59ae64b4f2213f
DIFF: https://github.com/llvm/llvm-project/commit/03934d0a213e479622c7875a2a59ae64b4f2213f.diff

LOG: [DirectX] Implement DXILResourceImplicitBinding pass (#138043)

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

Added: 
    llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
    llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
    llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
    llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
    llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
    llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
    llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll

Modified: 
    llvm/include/llvm/Analysis/DXILResource.h
    llvm/include/llvm/InitializePasses.h
    llvm/lib/Analysis/Analysis.cpp
    llvm/lib/Analysis/DXILResource.cpp
    llvm/lib/Target/DirectX/CMakeLists.txt
    llvm/lib/Target/DirectX/DirectX.h
    llvm/lib/Target/DirectX/DirectXPassRegistry.def
    llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
    llvm/test/CodeGen/DirectX/llc-pipeline.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index ec41188ff0848..b6efd82bb308e 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -652,12 +652,15 @@ class DXILResourceBindingInfo {
     RegisterSpace(uint32_t Space) : Space(Space) {
       FreeRanges.emplace_back(0, UINT32_MAX);
     }
+    // Size == -1 means unbounded array
+    std::optional<uint32_t> findAvailableBinding(int32_t Size);
   };
 
   struct BindingSpaces {
     dxil::ResourceClass RC;
     llvm::SmallVector<RegisterSpace> Spaces;
     BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
+    RegisterSpace &getOrInsertSpace(uint32_t Space);
   };
 
 private:
@@ -678,6 +681,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) {
@@ -695,6 +699,10 @@ class DXILResourceBindingInfo {
     llvm_unreachable("Invalid resource class");
   }
 
+  // Size == -1 means unbounded array
+  std::optional<uint32_t> findAvailableBinding(dxil::ResourceClass RC,
+                                               uint32_t Space, int32_t Size);
+
   friend class DXILResourceBindingAnalysis;
   friend class DXILResourceBindingWrapperPass;
 };

diff  --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index 5a282d17b72c8..0e58caf6478a4 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 baba7d2c75e58..36b3901246285 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -23,6 +23,7 @@
 #include "llvm/Support/FormatVariadic.h"
 #include <climits>
 #include <cstdint>
+#include <optional>
 
 #define DEBUG_TYPE "dxil-resource"
 
@@ -998,6 +999,62 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
   }
 }
 
+// returns std::nulopt if binding could not be found in given space
+std::optional<uint32_t>
+DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
+                                              uint32_t Space, int32_t Size) {
+  BindingSpaces &BS = getBindingSpaces(RC);
+  RegisterSpace &RS = BS.getOrInsertSpace(Space);
+  return RS.findAvailableBinding(Size);
+}
+
+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);
+}
+
+std::optional<uint32_t>
+DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
+  assert((Size == -1 || Size > 0) && "invalid size");
+
+  if (FreeRanges.empty())
+    return std::nullopt;
+
+  // unbounded array
+  if (Size == -1) {
+    BindingRange &Last = FreeRanges.back();
+    if (Last.UpperBound != UINT32_MAX)
+      // this space is already occupied by an unbounded array
+      return std::nullopt;
+    uint32_t RegSlot = Last.LowerBound;
+    FreeRanges.pop_back();
+    return RegSlot;
+  }
+
+  // single resource or fixed-size array
+  for (BindingRange &R : FreeRanges) {
+    // compare the size as uint64_t to prevent overflow for range (0,
+    // UINT32_MAX)
+    if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
+      continue;
+    uint32_t RegSlot = R.LowerBound;
+    // This might create a range where (LowerBound == UpperBound + 1). When
+    // that happens, the next time this function is called the range will
+    // skipped over by the check above (at this point Size is always > 0).
+    R.LowerBound += Size;
+    return RegSlot;
+  }
+
+  return std::nullopt;
+}
+
 //===----------------------------------------------------------------------===//
 
 AnalysisKey DXILResourceTypeAnalysis::Key;

diff  --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt
index 01e0ef7e9bbc9..c7c09caf43667 100644
--- a/llvm/lib/Target/DirectX/CMakeLists.txt
+++ b/llvm/lib/Target/DirectX/CMakeLists.txt
@@ -32,6 +32,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..92454bea51e31
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
@@ -0,0 +1,180 @@
+//===- 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/Analysis.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 ImplicitBindingCall {
+    int OrderID;
+    CallInst *Call;
+    ImplicitBindingCall(int OrderID, CallInst *Call)
+        : OrderID(OrderID), Call(Call) {}
+  };
+  SmallVector<ImplicitBindingCall> 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 (ImplicitBindingCall &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();
+
+      std::optional<uint32_t> RegSlot =
+          DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size);
+      if (!RegSlot) {
+        diagnoseImplicitBindingNotFound(IB.Call);
+        AllBindingsAssigned = false;
+        continue;
+      }
+      RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot.value());
+    }
+
+    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) {
+
+  DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M);
+  DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
+  if (DRBI.hasImplicitBinding())
+    if (assignBindings(M, DRBI, DRTM))
+      return PreservedAnalyses::none();
+  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 f52c581e8f308..e31c2ffa4f761 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 2d57483d7e8e3..d5069541642b5 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())
 MODULE_PASS("dxil-post-optimization-validation", DXILPostOptimizationValidation())
 // TODO: rename to print<foo> after NPM switch
 MODULE_PASS("print-dx-shader-flags", dxil::ShaderFlagsAnalysisPrinter(dbgs()))

diff  --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
index 19cfa89bb75ee..0d2730f820748 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
@@ -22,6 +22,7 @@
 #include "DXILPostOptimizationValidation.h"
 #include "DXILPrettyPrinter.h"
 #include "DXILResourceAccess.h"
+#include "DXILResourceImplicitBinding.h"
 #include "DXILRootSignature.h"
 #include "DXILShaderFlags.h"
 #include "DXILTranslateMetadata.h"
@@ -63,6 +64,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() {
   initializeDXContainerGlobalsPass(*PR);
   initializeDXILOpLoweringLegacyPass(*PR);
   initializeDXILResourceAccessLegacyPass(*PR);
+  initializeDXILResourceImplicitBindingLegacyPass(*PR);
   initializeDXILTranslateMetadataLegacyPass(*PR);
   initializeDXILPostOptimizationValidationLegacyPass(*PR);
   initializeShaderFlagsAnalysisWrapperPass(*PR);
@@ -101,6 +103,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..7c656becef11f
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
@@ -0,0 +1,47 @@
+; 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)
+
+; another access to resource array B to make sure it gets the same binding
+  %bufB2 = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 10, i32 0, i32 4, 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 3, i32 4, i32 0, 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..b22d7fbff2f0c
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
@@ -0,0 +1,54 @@
+; 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] : registcer(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(u5);
+  %bufA = call target("dx.TypedBuffer", float, 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: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
+
+; RWBuffer<int> B[];
+%bufB = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, 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 6, i32 -1, i32 0, i1 false)
+
+; RWBuffer<float> C[4] : register(space5);
+%bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 5, i32 4, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 5, i32 0, i32 4, i32 0, i1 false)
+
+; RWBuffer<int> D[] : register(space5);
+  %bufD = call target("dx.TypedBuffer", i32, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 102, i32 5, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 5, i32 4, i32 -1, i32 0, i1 false)
+
+; RWBuffer<float> E[3] : register(space10); // gets u0, space10
+%bufE = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 103, i32 10, i32 4, 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 10, i32 0, i32 4, i32 0, i1 false)
+
+; StructuredBuffer<int> F : register(space3); // gets t0 in space3
+%bufF = call target("dx.RawBuffer", i32, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 104, i32 3, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", i32, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 3, i32 0, i32 1, i32 0, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+  ret void
+}

diff  --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
new file mode 100644
index 0000000000000..83a1077a71ca5
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
@@ -0,0 +1,30 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_simple_binding() {
+
+; StructuredBuffer<float> A : register(t1);
+  %bufA = call target("dx.RawBuffer", float, 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.RawBuffer", float, 0, 0) 
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; StructuredBuffer<float> B; // gets register(t0, space0)
+  %bufB = call target("dx.RawBuffer", float, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 5, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+
+; StructuredBuffer<float> C; // gets register(t2, space0)
+  %bufC = call target("dx.RawBuffer", float, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 6, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+
+  ret void
+}
+

diff  --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
new file mode 100644
index 0000000000000..af61941d9791f
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
@@ -0,0 +1,34 @@
+; RUN: not opt -S -dxil-resource-implicit-binding %s  2>&1 | FileCheck %s
+
+; Resources defined
+; RWBuffer<float> A : register(u1);
+; RWBuffer<float> B[];     // gets u6 (unbounded range)
+; RWBuffer<float> C : register(u5);
+; RWBuffer<float> D[4];    // error - does not fit in the remaining descriptor ranges in space0 
+
+; CHECK: error:
+; CHECK-SAME: resource cannot be allocated
+
+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)
+
+; 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)
+
+; 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)
+
+; RWBuffer<float> D[4];
+%bufD = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 0, i32 4, i32 1, i1 false)
+
+  ret void
+}
+

diff  --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll
new file mode 100644
index 0000000000000..762cbc10b6e66
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll
@@ -0,0 +1,42 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined
+; RWBuffer<float> A : register(u1);
+; RWBuffer<float> B[];     // gets u6 (unbounded range)
+; RWBuffer<int> C : register(u5);
+; RWBuffer<float> D[3];    // gets u2 because it fits between A and C but not before A
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_unbounded_arrays() {
+
+; 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: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
+
+; ; RWBuffer<float> D[3];
+  %bufD = call target("dx.TypedBuffer", float, 1, 0, 0)
+      @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 0, i32 3, i32 1, 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 2, i32 3, i32 1, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+  ret void
+}
+

diff  --git a/llvm/test/CodeGen/DirectX/llc-pipeline.ll b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
index 705e05ced9aae..06aa6adbf0fb7 100644
--- a/llvm/test/CodeGen/DirectX/llc-pipeline.ll
+++ b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
@@ -14,6 +14,8 @@
 
 ; CHECK-NEXT: ModulePass Manager
 ; CHECK-NEXT:   DXIL Finalize Linkage
+; CHECK-NEXT:   DXIL Resource Binding Analysis
+; CHECK-NEXT:   DXIL Resource Implicit Binding
 ; CHECK-NEXT:   DXIL Intrinsic Expansion
 ; CHECK-NEXT:   DXIL CBuffer Access
 ; CHECK-NEXT:   DXIL Data Scalarization


        


More information about the llvm-commits mailing list