270 lines
9.1 KiB
C++
270 lines
9.1 KiB
C++
//===- SPIRVLegalizeImplicitBinding.cpp - Legalize implicit bindings ----*- C++
|
|
//-*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This pass legalizes the @llvm.spv.resource.handlefromimplicitbinding
|
|
// intrinsic by replacing it with a call to
|
|
// @llvm.spv.resource.handlefrombinding.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SPIRV.h"
|
|
#include "llvm/ADT/BitVector.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/InstVisitor.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/IntrinsicsSPIRV.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Pass.h"
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
class SPIRVLegalizeImplicitBinding : public ModulePass {
|
|
public:
|
|
static char ID;
|
|
SPIRVLegalizeImplicitBinding() : ModulePass(ID) {}
|
|
StringRef getPassName() const override {
|
|
return "SPIRV Legalize Implicit Binding";
|
|
}
|
|
bool runOnModule(Module &M) override;
|
|
|
|
private:
|
|
void collectBindingInfo(Module &M);
|
|
uint32_t getAndReserveFirstUnusedBinding(uint32_t DescSet);
|
|
void replaceImplicitBindingCalls(Module &M);
|
|
void replaceResourceHandleCall(Module &M, CallInst *OldCI,
|
|
uint32_t NewBinding);
|
|
void replaceCounterHandleCall(Module &M, CallInst *OldCI,
|
|
uint32_t NewBinding);
|
|
void verifyUniqueOrderIdPerResource(SmallVectorImpl<CallInst *> &Calls);
|
|
|
|
// A map from descriptor set to a bit vector of used binding numbers.
|
|
std::vector<BitVector> UsedBindings;
|
|
// A list of all implicit binding calls, to be sorted by order ID.
|
|
SmallVector<CallInst *, 16> ImplicitBindingCalls;
|
|
};
|
|
|
|
struct BindingInfoCollector : public InstVisitor<BindingInfoCollector> {
|
|
std::vector<BitVector> &UsedBindings;
|
|
SmallVector<CallInst *, 16> &ImplicitBindingCalls;
|
|
|
|
BindingInfoCollector(std::vector<BitVector> &UsedBindings,
|
|
SmallVector<CallInst *, 16> &ImplicitBindingCalls)
|
|
: UsedBindings(UsedBindings), ImplicitBindingCalls(ImplicitBindingCalls) {
|
|
}
|
|
|
|
void addBinding(uint32_t DescSet, uint32_t Binding) {
|
|
if (UsedBindings.size() <= DescSet) {
|
|
UsedBindings.resize(DescSet + 1);
|
|
UsedBindings[DescSet].resize(64);
|
|
}
|
|
if (UsedBindings[DescSet].size() <= Binding) {
|
|
UsedBindings[DescSet].resize(2 * Binding + 1);
|
|
}
|
|
UsedBindings[DescSet].set(Binding);
|
|
}
|
|
|
|
void visitCallInst(CallInst &CI) {
|
|
if (CI.getIntrinsicID() == Intrinsic::spv_resource_handlefrombinding) {
|
|
const uint32_t DescSet =
|
|
cast<ConstantInt>(CI.getArgOperand(0))->getZExtValue();
|
|
const uint32_t Binding =
|
|
cast<ConstantInt>(CI.getArgOperand(1))->getZExtValue();
|
|
addBinding(DescSet, Binding);
|
|
} else if (CI.getIntrinsicID() ==
|
|
Intrinsic::spv_resource_handlefromimplicitbinding) {
|
|
ImplicitBindingCalls.push_back(&CI);
|
|
} else if (CI.getIntrinsicID() ==
|
|
Intrinsic::spv_resource_counterhandlefrombinding) {
|
|
const uint32_t DescSet =
|
|
cast<ConstantInt>(CI.getArgOperand(2))->getZExtValue();
|
|
const uint32_t Binding =
|
|
cast<ConstantInt>(CI.getArgOperand(1))->getZExtValue();
|
|
addBinding(DescSet, Binding);
|
|
} else if (CI.getIntrinsicID() ==
|
|
Intrinsic::spv_resource_counterhandlefromimplicitbinding) {
|
|
ImplicitBindingCalls.push_back(&CI);
|
|
}
|
|
}
|
|
};
|
|
|
|
static uint32_t getOrderId(const CallInst *CI) {
|
|
uint32_t OrderIdArgIdx = 0;
|
|
switch (CI->getIntrinsicID()) {
|
|
case Intrinsic::spv_resource_handlefromimplicitbinding:
|
|
OrderIdArgIdx = 0;
|
|
break;
|
|
case Intrinsic::spv_resource_counterhandlefromimplicitbinding:
|
|
OrderIdArgIdx = 1;
|
|
break;
|
|
default:
|
|
llvm_unreachable("CallInst is not an implicit binding intrinsic");
|
|
}
|
|
return cast<ConstantInt>(CI->getArgOperand(OrderIdArgIdx))->getZExtValue();
|
|
}
|
|
|
|
static uint32_t getDescSet(const CallInst *CI) {
|
|
uint32_t DescSetArgIdx;
|
|
switch (CI->getIntrinsicID()) {
|
|
case Intrinsic::spv_resource_handlefromimplicitbinding:
|
|
case Intrinsic::spv_resource_handlefrombinding:
|
|
DescSetArgIdx = 1;
|
|
break;
|
|
case Intrinsic::spv_resource_counterhandlefromimplicitbinding:
|
|
case Intrinsic::spv_resource_counterhandlefrombinding:
|
|
DescSetArgIdx = 2;
|
|
break;
|
|
default:
|
|
llvm_unreachable("CallInst is not an implicit binding intrinsic");
|
|
}
|
|
return cast<ConstantInt>(CI->getArgOperand(DescSetArgIdx))->getZExtValue();
|
|
}
|
|
|
|
void SPIRVLegalizeImplicitBinding::collectBindingInfo(Module &M) {
|
|
BindingInfoCollector InfoCollector(UsedBindings, ImplicitBindingCalls);
|
|
InfoCollector.visit(M);
|
|
|
|
// Sort the collected calls by their order ID.
|
|
std::sort(ImplicitBindingCalls.begin(), ImplicitBindingCalls.end(),
|
|
[](const CallInst *A, const CallInst *B) {
|
|
return getOrderId(A) < getOrderId(B);
|
|
});
|
|
}
|
|
|
|
void SPIRVLegalizeImplicitBinding::verifyUniqueOrderIdPerResource(
|
|
SmallVectorImpl<CallInst *> &Calls) {
|
|
// Check that the order Id is unique per resource.
|
|
for (uint32_t i = 1; i < Calls.size(); ++i) {
|
|
const uint32_t OrderA = getOrderId(Calls[i - 1]);
|
|
const uint32_t OrderB = getOrderId(Calls[i]);
|
|
if (OrderA == OrderB) {
|
|
const uint32_t DescSetA = getDescSet(Calls[i - 1]);
|
|
const uint32_t DescSetB = getDescSet(Calls[i]);
|
|
if (DescSetA != DescSetB) {
|
|
report_fatal_error("Implicit binding calls with the same order ID must "
|
|
"have the same descriptor set");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t SPIRVLegalizeImplicitBinding::getAndReserveFirstUnusedBinding(
|
|
uint32_t DescSet) {
|
|
if (UsedBindings.size() <= DescSet) {
|
|
UsedBindings.resize(DescSet + 1);
|
|
UsedBindings[DescSet].resize(64);
|
|
}
|
|
|
|
int NewBinding = UsedBindings[DescSet].find_first_unset();
|
|
if (NewBinding == -1) {
|
|
NewBinding = UsedBindings[DescSet].size();
|
|
UsedBindings[DescSet].resize(2 * NewBinding + 1);
|
|
}
|
|
|
|
UsedBindings[DescSet].set(NewBinding);
|
|
return NewBinding;
|
|
}
|
|
|
|
void SPIRVLegalizeImplicitBinding::replaceImplicitBindingCalls(Module &M) {
|
|
uint32_t lastOrderId = -1;
|
|
uint32_t lastBindingNumber = -1;
|
|
|
|
for (CallInst *OldCI : ImplicitBindingCalls) {
|
|
const uint32_t OrderId = getOrderId(OldCI);
|
|
uint32_t BindingNumber;
|
|
if (OrderId == lastOrderId) {
|
|
BindingNumber = lastBindingNumber;
|
|
} else {
|
|
const uint32_t DescSet = getDescSet(OldCI);
|
|
BindingNumber = getAndReserveFirstUnusedBinding(DescSet);
|
|
}
|
|
|
|
if (OldCI->getIntrinsicID() ==
|
|
Intrinsic::spv_resource_handlefromimplicitbinding) {
|
|
replaceResourceHandleCall(M, OldCI, BindingNumber);
|
|
} else {
|
|
assert(OldCI->getIntrinsicID() ==
|
|
Intrinsic::spv_resource_counterhandlefromimplicitbinding &&
|
|
"Unexpected implicit binding intrinsic");
|
|
replaceCounterHandleCall(M, OldCI, BindingNumber);
|
|
}
|
|
lastOrderId = OrderId;
|
|
lastBindingNumber = BindingNumber;
|
|
}
|
|
}
|
|
|
|
bool SPIRVLegalizeImplicitBinding::runOnModule(Module &M) {
|
|
collectBindingInfo(M);
|
|
if (ImplicitBindingCalls.empty()) {
|
|
return false;
|
|
}
|
|
verifyUniqueOrderIdPerResource(ImplicitBindingCalls);
|
|
|
|
replaceImplicitBindingCalls(M);
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
char SPIRVLegalizeImplicitBinding::ID = 0;
|
|
|
|
INITIALIZE_PASS(SPIRVLegalizeImplicitBinding, "legalize-spirv-implicit-binding",
|
|
"Legalize SPIR-V implicit bindings", false, false)
|
|
|
|
ModulePass *llvm::createSPIRVLegalizeImplicitBindingPass() {
|
|
return new SPIRVLegalizeImplicitBinding();
|
|
}
|
|
|
|
void SPIRVLegalizeImplicitBinding::replaceResourceHandleCall(
|
|
Module &M, CallInst *OldCI, uint32_t NewBinding) {
|
|
IRBuilder<> Builder(OldCI);
|
|
const uint32_t DescSet =
|
|
cast<ConstantInt>(OldCI->getArgOperand(1))->getZExtValue();
|
|
|
|
SmallVector<Value *, 8> Args;
|
|
Args.push_back(Builder.getInt32(DescSet));
|
|
Args.push_back(Builder.getInt32(NewBinding));
|
|
|
|
// Copy the remaining arguments from the old call.
|
|
for (uint32_t i = 2; i < OldCI->arg_size(); ++i) {
|
|
Args.push_back(OldCI->getArgOperand(i));
|
|
}
|
|
|
|
Function *NewFunc = Intrinsic::getOrInsertDeclaration(
|
|
&M, Intrinsic::spv_resource_handlefrombinding, OldCI->getType());
|
|
CallInst *NewCI = Builder.CreateCall(NewFunc, Args);
|
|
NewCI->setCallingConv(OldCI->getCallingConv());
|
|
|
|
OldCI->replaceAllUsesWith(NewCI);
|
|
OldCI->eraseFromParent();
|
|
}
|
|
|
|
void SPIRVLegalizeImplicitBinding::replaceCounterHandleCall(
|
|
Module &M, CallInst *OldCI, uint32_t NewBinding) {
|
|
IRBuilder<> Builder(OldCI);
|
|
const uint32_t DescSet =
|
|
cast<ConstantInt>(OldCI->getArgOperand(2))->getZExtValue();
|
|
|
|
SmallVector<Value *, 8> Args;
|
|
Args.push_back(OldCI->getArgOperand(0));
|
|
Args.push_back(Builder.getInt32(NewBinding));
|
|
Args.push_back(Builder.getInt32(DescSet));
|
|
|
|
Type *Tys[] = {OldCI->getType(), OldCI->getArgOperand(0)->getType()};
|
|
Function *NewFunc = Intrinsic::getOrInsertDeclaration(
|
|
&M, Intrinsic::spv_resource_counterhandlefrombinding, Tys);
|
|
CallInst *NewCI = Builder.CreateCall(NewFunc, Args);
|
|
NewCI->setCallingConv(OldCI->getCallingConv());
|
|
|
|
OldCI->replaceAllUsesWith(NewCI);
|
|
OldCI->eraseFromParent();
|
|
}
|