| //===- DXILResourceImplicitBinding.cpp -----------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DXILResourceImplicitBinding.h" | |
| #include "DirectX.h" | |
| #include "llvm/ADT/APInt.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/Analysis/DXILResource.h" | |
| #include "llvm/IR/Analysis.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/DiagnosticInfo.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/IntrinsicsDirectX.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/InitializePasses.h" | |
| #include <cstdint> | |
| #define DEBUG_TYPE "dxil-resource-implicit-binding" | |
| using namespace llvm; | |
| using namespace llvm::dxil; | |
| namespace { | |
| static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) { | |
| Function *F = ImplBindingCall->getFunction(); | |
| LLVMContext &Context = F->getParent()->getContext(); | |
| // FIXME: include the name of the resource in the error message | |
| // (llvm/llvm-project#137868) | |
| Context.diagnose( | |
| DiagnosticInfoGenericWithLoc("resource cannot be allocated", *F, | |
| ImplBindingCall->getDebugLoc(), DS_Error)); | |
| } | |
| static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI, | |
| DXILResourceTypeMap &DRTM) { | |
| struct ImplicitBindingCall { | |
| int OrderID; | |
| CallInst *Call; | |
| ImplicitBindingCall(int OrderID, CallInst *Call) | |
| : OrderID(OrderID), Call(Call) {} | |
| }; | |
| SmallVector<ImplicitBindingCall> Calls; | |
| SmallVector<Function *> FunctionsToMaybeRemove; | |
| // collect all of the llvm.dx.resource.handlefromImplicitbinding calls | |
| for (Function &F : M.functions()) { | |
| if (!F.isDeclaration()) | |
| continue; | |
| if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefromimplicitbinding) | |
| continue; | |
| for (User *U : F.users()) { | |
| if (CallInst *CI = dyn_cast<CallInst>(U)) { | |
| int OrderID = cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue(); | |
| Calls.emplace_back(OrderID, CI); | |
| } | |
| } | |
| FunctionsToMaybeRemove.emplace_back(&F); | |
| } | |
| // sort all the collected implicit bindings by OrderID | |
| llvm::stable_sort( | |
| Calls, [](auto &LHS, auto &RHS) { return LHS.OrderID < RHS.OrderID; }); | |
| // iterate over sorted calls, find binding for each new OrderID and replace | |
| // each call with dx_resource_handlefrombinding using the new binding | |
| int LastOrderID = -1; | |
| llvm::TargetExtType *HandleTy = nullptr; | |
| ConstantInt *RegSlotOp = nullptr; | |
| bool AllBindingsAssigned = true; | |
| bool Changed = false; | |
| for (ImplicitBindingCall &IB : Calls) { | |
| IRBuilder<> Builder(IB.Call); | |
| if (IB.OrderID != LastOrderID) { | |
| LastOrderID = IB.OrderID; | |
| HandleTy = cast<TargetExtType>(IB.Call->getType()); | |
| ResourceTypeInfo &RTI = DRTM[HandleTy]; | |
| uint32_t Space = | |
| cast<ConstantInt>(IB.Call->getArgOperand(1))->getZExtValue(); | |
| int32_t Size = | |
| cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue(); | |
| std::optional<uint32_t> RegSlot = | |
| DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size); | |
| if (!RegSlot) { | |
| diagnoseImplicitBindingNotFound(IB.Call); | |
| AllBindingsAssigned = false; | |
| continue; | |
| } | |
| RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot.value()); | |
| } | |
| if (!RegSlotOp) | |
| continue; | |
| auto *NewCall = Builder.CreateIntrinsic( | |
| HandleTy, Intrinsic::dx_resource_handlefrombinding, | |
| {IB.Call->getOperand(1), /* space */ | |
| RegSlotOp, /* register slot */ | |
| IB.Call->getOperand(2), /* size */ | |
| IB.Call->getOperand(3), /* index */ | |
| IB.Call->getOperand(4)}); /* name */ | |
| IB.Call->replaceAllUsesWith(NewCall); | |
| IB.Call->eraseFromParent(); | |
| Changed = true; | |
| } | |
| for (Function *F : FunctionsToMaybeRemove) { | |
| if (F->user_empty()) { | |
| F->eraseFromParent(); | |
| Changed = true; | |
| } | |
| } | |
| DRBI.setHasImplicitBinding(!AllBindingsAssigned); | |
| return Changed; | |
| } | |
| } // end anonymous namespace | |
| PreservedAnalyses DXILResourceImplicitBinding::run(Module &M, | |
| ModuleAnalysisManager &AM) { | |
| DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M); | |
| DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M); | |
| if (!DRBI.hasImplicitBinding()) | |
| return PreservedAnalyses::all(); | |
| if (!assignBindings(M, DRBI, DRTM)) | |
| return PreservedAnalyses::all(); | |
| PreservedAnalyses PA; | |
| PA.preserve<DXILResourceBindingAnalysis>(); | |
| PA.preserve<DXILResourceTypeAnalysis>(); | |
| return PA; | |
| } | |
| namespace { | |
| class DXILResourceImplicitBindingLegacy : public ModulePass { | |
| public: | |
| DXILResourceImplicitBindingLegacy() : ModulePass(ID) {} | |
| bool runOnModule(Module &M) override { | |
| DXILResourceTypeMap &DRTM = | |
| getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); | |
| DXILResourceBindingInfo &DRBI = | |
| getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo(); | |
| if (DRBI.hasImplicitBinding()) | |
| return assignBindings(M, DRBI, DRTM); | |
| return false; | |
| } | |
| static char ID; // Pass identification. | |
| void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { | |
| AU.addRequired<DXILResourceTypeWrapperPass>(); | |
| AU.addRequired<DXILResourceBindingWrapperPass>(); | |
| AU.addPreserved<DXILResourceTypeWrapperPass>(); | |
| AU.addPreserved<DXILResourceBindingWrapperPass>(); | |
| } | |
| }; | |
| char DXILResourceImplicitBindingLegacy::ID = 0; | |
| } // end anonymous namespace | |
| INITIALIZE_PASS_BEGIN(DXILResourceImplicitBindingLegacy, DEBUG_TYPE, | |
| "DXIL Resource Implicit Binding", false, false) | |
| INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass) | |
| INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass) | |
| INITIALIZE_PASS_END(DXILResourceImplicitBindingLegacy, DEBUG_TYPE, | |
| "DXIL Resource Implicit Binding", false, false) | |
| ModulePass *llvm::createDXILResourceImplicitBindingLegacyPass() { | |
| return new DXILResourceImplicitBindingLegacy(); | |
| } |