xref: /freebsd/contrib/llvm-project/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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