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
diagnoseImplicitBindingNotFound(CallInst * ImplBindingCall)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
assignBindings(Module & M,DXILResourceBindingInfo & DRBI,DXILResourceTypeMap & DRTM)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->getOperand(5)}); /* name */
116 IB.Call->replaceAllUsesWith(NewCall);
117 IB.Call->eraseFromParent();
118 Changed = true;
119 }
120
121 for (Function *F : FunctionsToMaybeRemove) {
122 if (F->user_empty()) {
123 F->eraseFromParent();
124 Changed = true;
125 }
126 }
127
128 DRBI.setHasImplicitBinding(!AllBindingsAssigned);
129 return Changed;
130 }
131
132 } // end anonymous namespace
133
run(Module & M,ModuleAnalysisManager & AM)134 PreservedAnalyses DXILResourceImplicitBinding::run(Module &M,
135 ModuleAnalysisManager &AM) {
136
137 DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M);
138 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
139
140 if (!DRBI.hasImplicitBinding())
141 return PreservedAnalyses::all();
142
143 if (!assignBindings(M, DRBI, DRTM))
144 return PreservedAnalyses::all();
145
146 PreservedAnalyses PA;
147 PA.preserve<DXILResourceBindingAnalysis>();
148 PA.preserve<DXILResourceTypeAnalysis>();
149 return PA;
150 }
151
152 namespace {
153
154 class DXILResourceImplicitBindingLegacy : public ModulePass {
155 public:
DXILResourceImplicitBindingLegacy()156 DXILResourceImplicitBindingLegacy() : ModulePass(ID) {}
157
runOnModule(Module & M)158 bool runOnModule(Module &M) override {
159 DXILResourceTypeMap &DRTM =
160 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
161 DXILResourceBindingInfo &DRBI =
162 getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
163
164 if (DRBI.hasImplicitBinding())
165 return assignBindings(M, DRBI, DRTM);
166 return false;
167 }
168
169 static char ID; // Pass identification.
getAnalysisUsage(llvm::AnalysisUsage & AU) const170 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
171 AU.addRequired<DXILResourceTypeWrapperPass>();
172 AU.addRequired<DXILResourceBindingWrapperPass>();
173 AU.addPreserved<DXILResourceTypeWrapperPass>();
174 AU.addPreserved<DXILResourceBindingWrapperPass>();
175 }
176 };
177
178 char DXILResourceImplicitBindingLegacy::ID = 0;
179 } // end anonymous namespace
180
181 INITIALIZE_PASS_BEGIN(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
182 "DXIL Resource Implicit Binding", false, false)
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)183 INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
184 INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
185 INITIALIZE_PASS_END(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
186 "DXIL Resource Implicit Binding", false, false)
187
188 ModulePass *llvm::createDXILResourceImplicitBindingLegacyPass() {
189 return new DXILResourceImplicitBindingLegacy();
190 }
191