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