[llvm-branch-commits] [llvm] [DirectX] Implement DXILResourceImplicitBinding pass (PR #138043)
Helena Kotas via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri May 9 10:44:27 PDT 2025
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/138043
>From f58e2d5c079f31d7a4d99d481a569ad4c754c4e7 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 30 Apr 2025 15:08:04 -0700
Subject: [PATCH 1/3] [HLSL] Implementation of DXILResourceImplicitBinding pass
This pass takes advantage of the DXILResourceBinding analysis and assigns
register slots to resources that do not have explicit binding.
Part 2/2 of #136786
Closes #136786
---
llvm/include/llvm/Analysis/DXILResource.h | 8 +
llvm/include/llvm/InitializePasses.h | 1 +
llvm/lib/Analysis/Analysis.cpp | 1 +
llvm/lib/Analysis/DXILResource.cpp | 53 +++++
llvm/lib/Target/DirectX/CMakeLists.txt | 1 +
.../DirectX/DXILResourceImplicitBinding.cpp | 182 ++++++++++++++++++
.../DirectX/DXILResourceImplicitBinding.h | 29 +++
llvm/lib/Target/DirectX/DirectX.h | 6 +
.../Target/DirectX/DirectXPassRegistry.def | 1 +
.../Target/DirectX/DirectXTargetMachine.cpp | 3 +
.../CodeGen/DirectX/ImplicitBinding/arrays.ll | 41 ++++
.../ImplicitBinding/multiple-spaces.ll | 44 +++++
.../CodeGen/DirectX/ImplicitBinding/simple.ll | 30 +++
.../ImplicitBinding/unbounded-arrays-error.ll | 34 ++++
.../ImplicitBinding/unbounded-arrays.ll | 42 ++++
llvm/test/CodeGen/DirectX/llc-pipeline.ll | 2 +
16 files changed, 478 insertions(+)
create mode 100644 llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
create mode 100644 llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll
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: @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/ImplicitBinding/simple.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
new file mode 100644
index 0000000000000..e942ff652f82a
--- /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, 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, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_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, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 5, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_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, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 6, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_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 a2412b6324a05..4ed01a05116f8 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
>From b65d3bcee09367a8db25c7f398a394a3de4dc634 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 30 Apr 2025 22:36:43 -0700
Subject: [PATCH 2/3] fix bug & update tests
---
llvm/lib/Analysis/DXILResource.cpp | 4 +-
.../CodeGen/DirectX/ImplicitBinding/arrays.ll | 6 ++
.../ImplicitBinding/multiple-spaces.ll | 58 +++++++++++--------
.../CodeGen/DirectX/ImplicitBinding/simple.ll | 18 +++---
4 files changed, 52 insertions(+), 34 deletions(-)
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 34355c81e77b0..0d8f82e138066 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -1036,7 +1036,9 @@ bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
// single resource or fixed-size array
for (BindingRange &R : FreeRanges) {
- if (R.UpperBound - R.LowerBound + 1 < (uint32_t)Size)
+ // 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;
*RegSlot = R.LowerBound;
// This might create a range where (LowerBound == UpperBound + 1), but
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
index 6196472cb8374..7c656becef11f 100644
--- a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
@@ -35,6 +35,12 @@ define void @test_arrays() {
; 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
index fce904e45ba09..b22d7fbff2f0c 100644
--- a/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
@@ -1,44 +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] : 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
+; 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(u1);
+; RWBuffer<float> A : register(u5);
%bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+ @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 1, i32 1, i32 0, i1 false)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
-; RWBuffer<float> B[];
-%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+; 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", 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)
+; 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<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> 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<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)
+; 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 0, i32 2, i32 3, i32 1, i1 false)
+; 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
index e942ff652f82a..83a1077a71ca5 100644
--- a/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
@@ -5,23 +5,23 @@ 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, 0)
+ %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, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+; 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, 0)
+ %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, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, 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, 0)
+ %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, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 2, 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
>From 8a2770801c06e9f68bd332246417ccf7cbb1b2b4 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 9 May 2025 10:44:06 -0700
Subject: [PATCH 3/3] code review feedback - use std::optional, renames
---
llvm/include/llvm/Analysis/DXILResource.h | 6 +--
llvm/lib/Analysis/DXILResource.cpp | 48 +++++++++----------
.../DirectX/DXILResourceImplicitBinding.cpp | 17 ++++---
3 files changed, 35 insertions(+), 36 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index fd34fdfda4c1c..7ab069d94742f 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -633,7 +633,7 @@ class DXILResourceBindingInfo {
FreeRanges.emplace_back(0, UINT32_MAX);
}
// Size == -1 means unbounded array
- bool findAvailableBinding(int32_t Size, uint32_t *RegSlot);
+ std::optional<uint32_t> findAvailableBinding(int32_t Size);
};
struct BindingSpaces {
@@ -678,8 +678,8 @@ class DXILResourceBindingInfo {
}
// Size == -1 means unbounded array
- bool findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
- int32_t Size, uint32_t *RegSlot);
+ std::optional<uint32_t> findAvailableBinding(dxil::ResourceClass RC,
+ uint32_t Space, int32_t Size);
friend class DXILResourceBindingAnalysis;
friend class DXILResourceBindingWrapperPass;
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 7d721d4b16297..ce809c2080805 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,12 +995,12 @@ 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) {
+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, RegSlot);
+ return RS.findAvailableBinding(Size);
}
DXILResourceBindingInfo::RegisterSpace &
@@ -1015,12 +1015,13 @@ DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
return Spaces.emplace_back(Space);
}
-bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
- int32_t Size, uint32_t *RegSlot) {
+std::optional<uint32_t>
+DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
assert((Size == -1 || Size > 0) && "invalid size");
+ std::optional<uint32_t> RegSlot;
if (FreeRanges.empty())
- return false;
+ return RegSlot;
// unbounded array
if (Size == -1) {
@@ -1028,25 +1029,24 @@ bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
if (Last.UpperBound != UINT32_MAX)
// this space is already occupied by an unbounded array
return false;
- *RegSlot = Last.LowerBound;
+ RegSlot = Last.LowerBound;
FreeRanges.pop_back();
- return true;
- }
-
- // 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;
- *RegSlot = R.LowerBound;
- // This might create a range where (LowerBound == UpperBound + 1), but
- // that's ok.
- R.LowerBound += Size;
- return true;
+ } else {
+ // 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;
+ 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;
+ break;
+ }
}
-
- return false;
+ return RegSlot;
}
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
index 80293ad7c9716..73eedaf51b425 100644
--- a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
@@ -40,13 +40,13 @@ static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) {
static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
DXILResourceTypeMap &DRTM) {
- struct ImplBindingCall {
+ struct ImplicitBindingCall {
int OrderID;
CallInst *Call;
- ImplBindingCall(int OrderID, CallInst *Call)
+ ImplicitBindingCall(int OrderID, CallInst *Call)
: OrderID(OrderID), Call(Call) {}
};
- SmallVector<ImplBindingCall> Calls;
+ SmallVector<ImplicitBindingCall> Calls;
SmallVector<Function *> FunctionsToMaybeRemove;
// collect all of the llvm.dx.resource.handlefromImplicitbinding calls
@@ -78,7 +78,7 @@ static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
bool AllBindingsAssigned = true;
bool Changed = false;
- for (auto &IB : Calls) {
+ for (ImplicitBindingCall &IB : Calls) {
IRBuilder<> Builder(IB.Call);
if (IB.OrderID != LastOrderID) {
@@ -91,15 +91,14 @@ static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
int32_t Size =
cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue();
- uint32_t RegSlot;
- RegSlotOp = nullptr;
- if (!DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size,
- &RegSlot)) {
+ 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);
+ RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot.value());
}
if (!RegSlotOp)
More information about the llvm-branch-commits
mailing list