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 findAvailableBinding(int32_t Size); }; struct BindingSpaces { dxil::ResourceClass RC; llvm::SmallVector 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 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 #include +#include #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 +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 +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 + +#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 Calls; + SmallVector 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(U)) { + int OrderID = cast(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(IB.Call->getType()); + ResourceTypeInfo &RTI = DRTM[HandleTy]; + + uint32_t Space = + cast(IB.Call->getArgOperand(1))->getZExtValue(); + int32_t Size = + cast(IB.Call->getArgOperand(2))->getZExtValue(); + + std::optional 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(M); + DXILResourceTypeMap &DRTM = AM.getResult(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().getResourceTypeMap(); + DXILResourceBindingInfo &DRBI = + getAnalysis().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(); + AU.addRequired(); + } +}; + +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 { +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 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 A : register(u2); +; RWBuffer B[4]; // gets u3 because it does not fit before A (range 4) +; RWBuffer C[2]; // gets u0 because it fits before A (range 2) +; RWBuffer E[5]; // gets u7 which is right after B (range 5) + +target triple = "dxil-pc-shadermodel6.6-compute" + +define void @test_arrays() { + +; RWBuffer 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 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 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 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 A : register(u5); // defaults to space0 +; RWBuffer B[]; // gets u6 (unbounded range) +; RWBuffer C[4] : registcer(space5); // gets u0 in space5 +; RWBuffer D[] : register(space5); // gets u4 in space5 +; RWBuffer E[3] : register(space10); // gets u0, space10 +; StructuredBuffer F : register(space3); // gets t0 in space3 + +target triple = "dxil-pc-shadermodel6.6-compute" + +define void @test_many_spaces() { + +; RWBuffer 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 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 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 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 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 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 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 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 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 A : register(u1); +; RWBuffer B[]; // gets u6 (unbounded range) +; RWBuffer C : register(u5); +; RWBuffer 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 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 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 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 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 A : register(u1); +; RWBuffer B[]; // gets u6 (unbounded range) +; RWBuffer C : register(u5); +; RWBuffer 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 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 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 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 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