[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:
parent
383a825d6d
commit
03934d0a21
@ -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;
|
||||
};
|
||||
|
@ -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 &);
|
||||
|
@ -27,6 +27,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
|
||||
initializeCallGraphViewerPass(Registry);
|
||||
initializeCycleInfoWrapperPassPass(Registry);
|
||||
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
|
||||
initializeDXILResourceWrapperPassPass(Registry);
|
||||
initializeDXILResourceBindingWrapperPassPass(Registry);
|
||||
initializeDXILResourceTypeWrapperPassPass(Registry);
|
||||
initializeDXILResourceWrapperPassPass(Registry);
|
||||
|
@ -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;
|
||||
|
@ -32,6 +32,7 @@ add_llvm_target(DirectXCodeGen
|
||||
DXILPrepare.cpp
|
||||
DXILPrettyPrinter.cpp
|
||||
DXILResourceAccess.cpp
|
||||
DXILResourceImplicitBinding.cpp
|
||||
DXILShaderFlags.cpp
|
||||
DXILTranslateMetadata.cpp
|
||||
DXILRootSignature.cpp
|
||||
|
180
llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
Normal file
180
llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
Normal 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();
|
||||
}
|
29
llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
Normal file
29
llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
Normal 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
|
@ -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 &);
|
||||
|
||||
|
@ -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()))
|
||||
|
@ -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());
|
||||
|
47
llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
Normal file
47
llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
Normal 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
|
||||
}
|
||||
|
54
llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
Normal file
54
llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
Normal 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
|
||||
}
|
30
llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
Normal file
30
llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
Normal 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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user