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