The `DXILResourceImplicitBinding` pass uses the results of `DXILResourceBindingAnalysis` to assigns register slots to resources that do not have explicit binding. It replaces all `llvm.dx.resource.handlefromimplicitbinding` calls with `llvm.dx.resource.handlefrombinding` using the newly assigned binding. If a binding cannot be found for a resource, the pass will raise a diagnostic error. Currently this diagnostic message does not include the resource name, which will be addressed in a separate task (#137868). Part 2/2 of #136786 Closes #136786
181 lines
5.9 KiB
C++
181 lines
5.9 KiB
C++
//===- 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)}); /* non-uniform flag */
|
|
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())
|
|
if (assignBindings(M, DRBI, DRTM))
|
|
return PreservedAnalyses::none();
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
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>();
|
|
}
|
|
};
|
|
|
|
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();
|
|
}
|