[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
This commit is contained in:
Helena Kotas 2025-05-12 23:00:00 -07:00 committed by GitHub
parent 383a825d6d
commit 03934d0a21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 496 additions and 0 deletions

View File

@ -652,12 +652,15 @@ public:
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 @@ public:
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 @@ public:
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;
};

View File

@ -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 &);

View File

@ -27,6 +27,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeCallGraphViewerPass(Registry);
initializeCycleInfoWrapperPassPass(Registry);
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
initializeDXILResourceWrapperPassPass(Registry);
initializeDXILResourceBindingWrapperPassPass(Registry);
initializeDXILResourceTypeWrapperPassPass(Registry);
initializeDXILResourceWrapperPassPass(Registry);

View File

@ -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;

View File

@ -32,6 +32,7 @@ add_llvm_target(DirectXCodeGen
DXILPrepare.cpp
DXILPrettyPrinter.cpp
DXILResourceAccess.cpp
DXILResourceImplicitBinding.cpp
DXILShaderFlags.cpp
DXILTranslateMetadata.cpp
DXILRootSignature.cpp

View File

@ -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();
}

View File

@ -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

View File

@ -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 &);

View File

@ -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()))

View File

@ -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 @@ public:
FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
void addCodeGenPrepare() override {
addPass(createDXILFinalizeLinkageLegacyPass());
addPass(createDXILResourceImplicitBindingLegacyPass());
addPass(createDXILIntrinsicExpansionLegacyPass());
addPass(createDXILCBufferAccessLegacyPass());
addPass(createDXILDataScalarizationLegacyPass());

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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