xref: /freebsd/contrib/llvm-project/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp (revision e3f4a63af63bea70bc86b6c790b14aa5ee99fcd0)
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->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 
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:
156   DXILResourceImplicitBindingLegacy() : ModulePass(ID) {}
157 
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.
170   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)
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