Skip to content

Commit 03934d0

Browse files
authored
[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
1 parent 383a825 commit 03934d0

16 files changed

+496
-0
lines changed

llvm/include/llvm/Analysis/DXILResource.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,12 +652,15 @@ class DXILResourceBindingInfo {
652652
RegisterSpace(uint32_t Space) : Space(Space) {
653653
FreeRanges.emplace_back(0, UINT32_MAX);
654654
}
655+
// Size == -1 means unbounded array
656+
std::optional<uint32_t> findAvailableBinding(int32_t Size);
655657
};
656658

657659
struct BindingSpaces {
658660
dxil::ResourceClass RC;
659661
llvm::SmallVector<RegisterSpace> Spaces;
660662
BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
663+
RegisterSpace &getOrInsertSpace(uint32_t Space);
661664
};
662665

663666
private:
@@ -678,6 +681,7 @@ class DXILResourceBindingInfo {
678681
OverlappingBinding(false) {}
679682

680683
bool hasImplicitBinding() const { return ImplicitBinding; }
684+
void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; }
681685
bool hasOverlappingBinding() const { return OverlappingBinding; }
682686

683687
BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
@@ -695,6 +699,10 @@ class DXILResourceBindingInfo {
695699
llvm_unreachable("Invalid resource class");
696700
}
697701

702+
// Size == -1 means unbounded array
703+
std::optional<uint32_t> findAvailableBinding(dxil::ResourceClass RC,
704+
uint32_t Space, int32_t Size);
705+
698706
friend class DXILResourceBindingAnalysis;
699707
friend class DXILResourceBindingWrapperPass;
700708
};

llvm/include/llvm/InitializePasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ void initializeDCELegacyPassPass(PassRegistry &);
8585
void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
8686
void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
8787
void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
88+
void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
8889
void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
8990
void initializeDXILResourceWrapperPassPass(PassRegistry &);
9091
void initializeDeadMachineInstructionElimPass(PassRegistry &);

llvm/lib/Analysis/Analysis.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
2727
initializeCallGraphViewerPass(Registry);
2828
initializeCycleInfoWrapperPassPass(Registry);
2929
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
30+
initializeDXILResourceWrapperPassPass(Registry);
3031
initializeDXILResourceBindingWrapperPassPass(Registry);
3132
initializeDXILResourceTypeWrapperPassPass(Registry);
3233
initializeDXILResourceWrapperPassPass(Registry);

llvm/lib/Analysis/DXILResource.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/Support/FormatVariadic.h"
2424
#include <climits>
2525
#include <cstdint>
26+
#include <optional>
2627

2728
#define DEBUG_TYPE "dxil-resource"
2829

@@ -998,6 +999,62 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
998999
}
9991000
}
10001001

1002+
// returns std::nulopt if binding could not be found in given space
1003+
std::optional<uint32_t>
1004+
DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
1005+
uint32_t Space, int32_t Size) {
1006+
BindingSpaces &BS = getBindingSpaces(RC);
1007+
RegisterSpace &RS = BS.getOrInsertSpace(Space);
1008+
return RS.findAvailableBinding(Size);
1009+
}
1010+
1011+
DXILResourceBindingInfo::RegisterSpace &
1012+
DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
1013+
for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
1014+
if (I->Space == Space)
1015+
return *I;
1016+
if (I->Space < Space)
1017+
continue;
1018+
return *Spaces.insert(I, Space);
1019+
}
1020+
return Spaces.emplace_back(Space);
1021+
}
1022+
1023+
std::optional<uint32_t>
1024+
DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
1025+
assert((Size == -1 || Size > 0) && "invalid size");
1026+
1027+
if (FreeRanges.empty())
1028+
return std::nullopt;
1029+
1030+
// unbounded array
1031+
if (Size == -1) {
1032+
BindingRange &Last = FreeRanges.back();
1033+
if (Last.UpperBound != UINT32_MAX)
1034+
// this space is already occupied by an unbounded array
1035+
return std::nullopt;
1036+
uint32_t RegSlot = Last.LowerBound;
1037+
FreeRanges.pop_back();
1038+
return RegSlot;
1039+
}
1040+
1041+
// single resource or fixed-size array
1042+
for (BindingRange &R : FreeRanges) {
1043+
// compare the size as uint64_t to prevent overflow for range (0,
1044+
// UINT32_MAX)
1045+
if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
1046+
continue;
1047+
uint32_t RegSlot = R.LowerBound;
1048+
// This might create a range where (LowerBound == UpperBound + 1). When
1049+
// that happens, the next time this function is called the range will
1050+
// skipped over by the check above (at this point Size is always > 0).
1051+
R.LowerBound += Size;
1052+
return RegSlot;
1053+
}
1054+
1055+
return std::nullopt;
1056+
}
1057+
10011058
//===----------------------------------------------------------------------===//
10021059

10031060
AnalysisKey DXILResourceTypeAnalysis::Key;

llvm/lib/Target/DirectX/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ add_llvm_target(DirectXCodeGen
3232
DXILPrepare.cpp
3333
DXILPrettyPrinter.cpp
3434
DXILResourceAccess.cpp
35+
DXILResourceImplicitBinding.cpp
3536
DXILShaderFlags.cpp
3637
DXILTranslateMetadata.cpp
3738
DXILRootSignature.cpp
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//===- DXILResourceImplicitBinding.cpp -----------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DXILResourceImplicitBinding.h"
10+
#include "DirectX.h"
11+
#include "llvm/ADT/APInt.h"
12+
#include "llvm/ADT/STLExtras.h"
13+
#include "llvm/Analysis/DXILResource.h"
14+
#include "llvm/IR/Analysis.h"
15+
#include "llvm/IR/Constants.h"
16+
#include "llvm/IR/DiagnosticInfo.h"
17+
#include "llvm/IR/Function.h"
18+
#include "llvm/IR/IRBuilder.h"
19+
#include "llvm/IR/Instructions.h"
20+
#include "llvm/IR/IntrinsicsDirectX.h"
21+
#include "llvm/IR/Module.h"
22+
#include "llvm/InitializePasses.h"
23+
#include <cstdint>
24+
25+
#define DEBUG_TYPE "dxil-resource-implicit-binding"
26+
27+
using namespace llvm;
28+
using namespace llvm::dxil;
29+
30+
namespace {
31+
32+
static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) {
33+
Function *F = ImplBindingCall->getFunction();
34+
LLVMContext &Context = F->getParent()->getContext();
35+
// FIXME: include the name of the resource in the error message
36+
// (llvm/llvm-project#137868)
37+
Context.diagnose(
38+
DiagnosticInfoGenericWithLoc("resource cannot be allocated", *F,
39+
ImplBindingCall->getDebugLoc(), DS_Error));
40+
}
41+
42+
static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
43+
DXILResourceTypeMap &DRTM) {
44+
struct ImplicitBindingCall {
45+
int OrderID;
46+
CallInst *Call;
47+
ImplicitBindingCall(int OrderID, CallInst *Call)
48+
: OrderID(OrderID), Call(Call) {}
49+
};
50+
SmallVector<ImplicitBindingCall> Calls;
51+
SmallVector<Function *> FunctionsToMaybeRemove;
52+
53+
// collect all of the llvm.dx.resource.handlefromImplicitbinding calls
54+
for (Function &F : M.functions()) {
55+
if (!F.isDeclaration())
56+
continue;
57+
58+
if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefromimplicitbinding)
59+
continue;
60+
61+
for (User *U : F.users()) {
62+
if (CallInst *CI = dyn_cast<CallInst>(U)) {
63+
int OrderID = cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
64+
Calls.emplace_back(OrderID, CI);
65+
}
66+
}
67+
FunctionsToMaybeRemove.emplace_back(&F);
68+
}
69+
70+
// sort all the collected implicit bindings by OrderID
71+
llvm::stable_sort(
72+
Calls, [](auto &LHS, auto &RHS) { return LHS.OrderID < RHS.OrderID; });
73+
74+
// iterate over sorted calls, find binding for each new OrderID and replace
75+
// each call with dx_resource_handlefrombinding using the new binding
76+
int LastOrderID = -1;
77+
llvm::TargetExtType *HandleTy = nullptr;
78+
ConstantInt *RegSlotOp = nullptr;
79+
bool AllBindingsAssigned = true;
80+
bool Changed = false;
81+
82+
for (ImplicitBindingCall &IB : Calls) {
83+
IRBuilder<> Builder(IB.Call);
84+
85+
if (IB.OrderID != LastOrderID) {
86+
LastOrderID = IB.OrderID;
87+
HandleTy = cast<TargetExtType>(IB.Call->getType());
88+
ResourceTypeInfo &RTI = DRTM[HandleTy];
89+
90+
uint32_t Space =
91+
cast<ConstantInt>(IB.Call->getArgOperand(1))->getZExtValue();
92+
int32_t Size =
93+
cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue();
94+
95+
std::optional<uint32_t> RegSlot =
96+
DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size);
97+
if (!RegSlot) {
98+
diagnoseImplicitBindingNotFound(IB.Call);
99+
AllBindingsAssigned = false;
100+
continue;
101+
}
102+
RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot.value());
103+
}
104+
105+
if (!RegSlotOp)
106+
continue;
107+
108+
auto *NewCall = Builder.CreateIntrinsic(
109+
HandleTy, Intrinsic::dx_resource_handlefrombinding,
110+
{IB.Call->getOperand(1), /* space */
111+
RegSlotOp, /* register slot */
112+
IB.Call->getOperand(2), /* size */
113+
IB.Call->getOperand(3), /* index */
114+
IB.Call->getOperand(4)}); /* non-uniform flag */
115+
IB.Call->replaceAllUsesWith(NewCall);
116+
IB.Call->eraseFromParent();
117+
Changed = true;
118+
}
119+
120+
for (Function *F : FunctionsToMaybeRemove) {
121+
if (F->user_empty()) {
122+
F->eraseFromParent();
123+
Changed = true;
124+
}
125+
}
126+
127+
DRBI.setHasImplicitBinding(!AllBindingsAssigned);
128+
return Changed;
129+
}
130+
131+
} // end anonymous namespace
132+
133+
PreservedAnalyses DXILResourceImplicitBinding::run(Module &M,
134+
ModuleAnalysisManager &AM) {
135+
136+
DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M);
137+
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
138+
if (DRBI.hasImplicitBinding())
139+
if (assignBindings(M, DRBI, DRTM))
140+
return PreservedAnalyses::none();
141+
return PreservedAnalyses::all();
142+
}
143+
144+
namespace {
145+
146+
class DXILResourceImplicitBindingLegacy : public ModulePass {
147+
public:
148+
DXILResourceImplicitBindingLegacy() : ModulePass(ID) {}
149+
150+
bool runOnModule(Module &M) override {
151+
DXILResourceTypeMap &DRTM =
152+
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
153+
DXILResourceBindingInfo &DRBI =
154+
getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
155+
156+
if (DRBI.hasImplicitBinding())
157+
return assignBindings(M, DRBI, DRTM);
158+
return false;
159+
}
160+
161+
static char ID; // Pass identification.
162+
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
163+
AU.addRequired<DXILResourceTypeWrapperPass>();
164+
AU.addRequired<DXILResourceBindingWrapperPass>();
165+
}
166+
};
167+
168+
char DXILResourceImplicitBindingLegacy::ID = 0;
169+
} // end anonymous namespace
170+
171+
INITIALIZE_PASS_BEGIN(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
172+
"DXIL Resource Implicit Binding", false, false)
173+
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
174+
INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
175+
INITIALIZE_PASS_END(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
176+
"DXIL Resource Implicit Binding", false, false)
177+
178+
ModulePass *llvm::createDXILResourceImplicitBindingLegacyPass() {
179+
return new DXILResourceImplicitBindingLegacy();
180+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===- DXILResourceImplicitBindings.h --_____________-----------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// \file Assign register slots to resources without explicit binding.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
14+
#define LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
15+
16+
#include "llvm/IR/PassManager.h"
17+
#include "llvm/Pass.h"
18+
19+
namespace llvm {
20+
21+
class DXILResourceImplicitBinding
22+
: public PassInfoMixin<DXILResourceImplicitBinding> {
23+
public:
24+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
25+
};
26+
27+
} // namespace llvm
28+
29+
#endif // LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H

llvm/lib/Target/DirectX/DirectX.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ void initializeDXILResourceAccessLegacyPass(PassRegistry &);
7878
/// Pass to update resource accesses to use load/store directly.
7979
FunctionPass *createDXILResourceAccessLegacyPass();
8080

81+
/// Initializer for DXILResourceImplicitBindingLegacyPass
82+
void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
83+
84+
/// Pass to assign register slots to resources without binding.
85+
ModulePass *createDXILResourceImplicitBindingLegacyPass();
86+
8187
/// Initializer for DXILTranslateMetadata.
8288
void initializeDXILTranslateMetadataLegacyPass(PassRegistry &);
8389

llvm/lib/Target/DirectX/DirectXPassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ MODULE_PASS("dxil-intrinsic-expansion", DXILIntrinsicExpansion())
3030
MODULE_PASS("dxil-op-lower", DXILOpLowering())
3131
MODULE_PASS("dxil-pretty-printer", DXILPrettyPrinterPass(dbgs()))
3232
MODULE_PASS("dxil-translate-metadata", DXILTranslateMetadata())
33+
MODULE_PASS("dxil-resource-implicit-binding", DXILResourceImplicitBinding())
3334
MODULE_PASS("dxil-post-optimization-validation", DXILPostOptimizationValidation())
3435
// TODO: rename to print<foo> after NPM switch
3536
MODULE_PASS("print-dx-shader-flags", dxil::ShaderFlagsAnalysisPrinter(dbgs()))

llvm/lib/Target/DirectX/DirectXTargetMachine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "DXILPostOptimizationValidation.h"
2323
#include "DXILPrettyPrinter.h"
2424
#include "DXILResourceAccess.h"
25+
#include "DXILResourceImplicitBinding.h"
2526
#include "DXILRootSignature.h"
2627
#include "DXILShaderFlags.h"
2728
#include "DXILTranslateMetadata.h"
@@ -63,6 +64,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() {
6364
initializeDXContainerGlobalsPass(*PR);
6465
initializeDXILOpLoweringLegacyPass(*PR);
6566
initializeDXILResourceAccessLegacyPass(*PR);
67+
initializeDXILResourceImplicitBindingLegacyPass(*PR);
6668
initializeDXILTranslateMetadataLegacyPass(*PR);
6769
initializeDXILPostOptimizationValidationLegacyPass(*PR);
6870
initializeShaderFlagsAnalysisWrapperPass(*PR);
@@ -101,6 +103,7 @@ class DirectXPassConfig : public TargetPassConfig {
101103
FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
102104
void addCodeGenPrepare() override {
103105
addPass(createDXILFinalizeLinkageLegacyPass());
106+
addPass(createDXILResourceImplicitBindingLegacyPass());
104107
addPass(createDXILIntrinsicExpansionLegacyPass());
105108
addPass(createDXILCBufferAccessLegacyPass());
106109
addPass(createDXILDataScalarizationLegacyPass());

0 commit comments

Comments
 (0)