LLVM 24.0.0git
SPIRVLegalizeImplicitBinding.cpp
Go to the documentation of this file.
1//===- SPIRVLegalizeImplicitBinding.cpp - Legalize implicit bindings ----*- C++
2//-*-===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This pass legalizes the @llvm.spv.resource.handlefromimplicitbinding
11// intrinsic by replacing it with a call to
12// @llvm.spv.resource.handlefrombinding.
13//
14//===----------------------------------------------------------------------===//
15
16#include "SPIRV.h"
17#include "llvm/ADT/BitVector.h"
18#include "llvm/ADT/STLExtras.h"
20#include "llvm/IR/IRBuilder.h"
21#include "llvm/IR/InstVisitor.h"
22#include "llvm/IR/Intrinsics.h"
23#include "llvm/IR/IntrinsicsSPIRV.h"
24#include "llvm/IR/Module.h"
25#include "llvm/Pass.h"
26#include <vector>
27
28using namespace llvm;
29
30namespace {
31class SPIRVLegalizeImplicitBindingImpl {
32public:
33 bool runOnModule(Module &M);
34
35private:
36 void collectBindingInfo(Module &M);
37 uint32_t getAndReserveFirstUnusedBinding(uint32_t DescSet);
38 void replaceImplicitBindingCalls(Module &M);
39 void replaceResourceHandleCall(Module &M, CallInst *OldCI,
40 uint32_t NewBinding);
41 void replaceCounterHandleCall(Module &M, CallInst *OldCI,
42 uint32_t NewBinding);
43 void verifyUniqueOrderIdPerResource(SmallVectorImpl<CallInst *> &Calls);
44
45 // A map from descriptor set to a bit vector of used binding numbers.
46 std::vector<BitVector> UsedBindings;
47 // A list of all implicit binding calls, to be sorted by order ID.
48 SmallVector<CallInst *, 16> ImplicitBindingCalls;
49};
50
51class SPIRVLegalizeImplicitBindingLegacy : public ModulePass {
52public:
53 static char ID;
54 SPIRVLegalizeImplicitBindingLegacy() : ModulePass(ID) {}
55 StringRef getPassName() const override {
56 return "SPIRV Legalize Implicit Binding";
57 }
58 bool runOnModule(Module &M) override {
59 return SPIRVLegalizeImplicitBindingImpl().runOnModule(M);
60 }
61};
62
63struct BindingInfoCollector : public InstVisitor<BindingInfoCollector> {
64 std::vector<BitVector> &UsedBindings;
65 SmallVector<CallInst *, 16> &ImplicitBindingCalls;
66
67 BindingInfoCollector(std::vector<BitVector> &UsedBindings,
68 SmallVector<CallInst *, 16> &ImplicitBindingCalls)
69 : UsedBindings(UsedBindings), ImplicitBindingCalls(ImplicitBindingCalls) {
70 }
71
72 void addBinding(uint32_t DescSet, uint32_t Binding) {
73 if (UsedBindings.size() <= DescSet) {
74 UsedBindings.resize(DescSet + 1);
75 UsedBindings[DescSet].resize(64);
76 }
77 if (UsedBindings[DescSet].size() <= Binding) {
78 UsedBindings[DescSet].resize(2 * Binding + 1);
79 }
80 UsedBindings[DescSet].set(Binding);
81 }
82
83 void visitCallInst(CallInst &CI) {
84 if (CI.getIntrinsicID() == Intrinsic::spv_resource_handlefrombinding) {
85 const uint32_t DescSet =
86 cast<ConstantInt>(CI.getArgOperand(0))->getZExtValue();
87 const uint32_t Binding =
88 cast<ConstantInt>(CI.getArgOperand(1))->getZExtValue();
89 addBinding(DescSet, Binding);
90 } else if (CI.getIntrinsicID() ==
91 Intrinsic::spv_resource_handlefromimplicitbinding) {
92 ImplicitBindingCalls.push_back(&CI);
93 } else if (CI.getIntrinsicID() ==
94 Intrinsic::spv_resource_counterhandlefrombinding) {
95 const uint32_t DescSet =
96 cast<ConstantInt>(CI.getArgOperand(2))->getZExtValue();
97 const uint32_t Binding =
98 cast<ConstantInt>(CI.getArgOperand(1))->getZExtValue();
99 addBinding(DescSet, Binding);
100 } else if (CI.getIntrinsicID() ==
101 Intrinsic::spv_resource_counterhandlefromimplicitbinding) {
102 ImplicitBindingCalls.push_back(&CI);
103 }
104 }
105};
106
107static uint32_t getOrderId(const CallInst *CI) {
108 uint32_t OrderIdArgIdx = 0;
109 switch (CI->getIntrinsicID()) {
110 case Intrinsic::spv_resource_handlefromimplicitbinding:
111 OrderIdArgIdx = 0;
112 break;
113 case Intrinsic::spv_resource_counterhandlefromimplicitbinding:
114 OrderIdArgIdx = 1;
115 break;
116 default:
117 llvm_unreachable("CallInst is not an implicit binding intrinsic");
118 }
119 return cast<ConstantInt>(CI->getArgOperand(OrderIdArgIdx))->getZExtValue();
120}
121
122static uint32_t getDescSet(const CallInst *CI) {
123 uint32_t DescSetArgIdx;
124 switch (CI->getIntrinsicID()) {
125 case Intrinsic::spv_resource_handlefromimplicitbinding:
126 case Intrinsic::spv_resource_handlefrombinding:
127 DescSetArgIdx = 1;
128 break;
129 case Intrinsic::spv_resource_counterhandlefromimplicitbinding:
130 case Intrinsic::spv_resource_counterhandlefrombinding:
131 DescSetArgIdx = 2;
132 break;
133 default:
134 llvm_unreachable("CallInst is not an implicit binding intrinsic");
135 }
136 return cast<ConstantInt>(CI->getArgOperand(DescSetArgIdx))->getZExtValue();
137}
138
139void SPIRVLegalizeImplicitBindingImpl::collectBindingInfo(Module &M) {
140 BindingInfoCollector InfoCollector(UsedBindings, ImplicitBindingCalls);
141 InfoCollector.visit(M);
142
143 // Sort the collected calls by their order ID.
144 llvm::sort(ImplicitBindingCalls, [](const CallInst *A, const CallInst *B) {
145 return getOrderId(A) < getOrderId(B);
146 });
147}
148
149void SPIRVLegalizeImplicitBindingImpl::verifyUniqueOrderIdPerResource(
150 SmallVectorImpl<CallInst *> &Calls) {
151 // Check that the order Id is unique per resource.
152 for (uint32_t i = 1; i < Calls.size(); ++i) {
153 const uint32_t OrderA = getOrderId(Calls[i - 1]);
154 const uint32_t OrderB = getOrderId(Calls[i]);
155 if (OrderA == OrderB) {
156 const uint32_t DescSetA = getDescSet(Calls[i - 1]);
157 const uint32_t DescSetB = getDescSet(Calls[i]);
158 if (DescSetA != DescSetB) {
159 report_fatal_error("Implicit binding calls with the same order ID must "
160 "have the same descriptor set");
161 }
162 }
163 }
164}
165
166uint32_t SPIRVLegalizeImplicitBindingImpl::getAndReserveFirstUnusedBinding(
167 uint32_t DescSet) {
168 if (UsedBindings.size() <= DescSet) {
169 UsedBindings.resize(DescSet + 1);
170 UsedBindings[DescSet].resize(64);
171 }
172
173 int NewBinding = UsedBindings[DescSet].find_first_unset();
174 if (NewBinding == -1) {
175 NewBinding = UsedBindings[DescSet].size();
176 UsedBindings[DescSet].resize(2 * NewBinding + 1);
177 }
178
179 UsedBindings[DescSet].set(NewBinding);
180 return NewBinding;
181}
182
183void SPIRVLegalizeImplicitBindingImpl::replaceImplicitBindingCalls(Module &M) {
184 uint32_t lastOrderId = -1;
185 uint32_t lastBindingNumber = -1;
186
187 for (CallInst *OldCI : ImplicitBindingCalls) {
188 const uint32_t OrderId = getOrderId(OldCI);
189 uint32_t BindingNumber;
190 if (OrderId == lastOrderId) {
191 BindingNumber = lastBindingNumber;
192 } else {
193 const uint32_t DescSet = getDescSet(OldCI);
194 BindingNumber = getAndReserveFirstUnusedBinding(DescSet);
195 }
196
197 if (OldCI->getIntrinsicID() ==
198 Intrinsic::spv_resource_handlefromimplicitbinding) {
199 replaceResourceHandleCall(M, OldCI, BindingNumber);
200 } else {
201 assert(OldCI->getIntrinsicID() ==
202 Intrinsic::spv_resource_counterhandlefromimplicitbinding &&
203 "Unexpected implicit binding intrinsic");
204 replaceCounterHandleCall(M, OldCI, BindingNumber);
205 }
206 lastOrderId = OrderId;
207 lastBindingNumber = BindingNumber;
208 }
209}
210
211bool SPIRVLegalizeImplicitBindingImpl::runOnModule(Module &M) {
212 collectBindingInfo(M);
213 if (ImplicitBindingCalls.empty()) {
214 return false;
215 }
216 verifyUniqueOrderIdPerResource(ImplicitBindingCalls);
217
218 replaceImplicitBindingCalls(M);
219 return true;
220}
221} // namespace
222
223PreservedAnalyses
225 return SPIRVLegalizeImplicitBindingImpl().runOnModule(M)
228}
229
230char SPIRVLegalizeImplicitBindingLegacy::ID = 0;
231
232INITIALIZE_PASS(SPIRVLegalizeImplicitBindingLegacy,
233 "legalize-spirv-implicit-binding",
234 "Legalize SPIR-V implicit bindings", false, false)
235
237 return new SPIRVLegalizeImplicitBindingLegacy();
238}
239
240void SPIRVLegalizeImplicitBindingImpl::replaceResourceHandleCall(
241 Module &M, CallInst *OldCI, uint32_t NewBinding) {
242 IRBuilder<> Builder(OldCI);
243 const uint32_t DescSet =
244 cast<ConstantInt>(OldCI->getArgOperand(1))->getZExtValue();
245
246 SmallVector<Value *, 8> Args;
247 Args.push_back(Builder.getInt32(DescSet));
248 Args.push_back(Builder.getInt32(NewBinding));
249
250 // Copy the remaining arguments from the old call.
251 for (uint32_t i = 2; i < OldCI->arg_size(); ++i) {
252 Args.push_back(OldCI->getArgOperand(i));
253 }
254
256 &M, Intrinsic::spv_resource_handlefrombinding, OldCI->getType());
257 CallInst *NewCI = Builder.CreateCall(NewFunc, Args);
258 NewCI->setCallingConv(OldCI->getCallingConv());
259
260 OldCI->replaceAllUsesWith(NewCI);
261 OldCI->eraseFromParent();
262}
263
264void SPIRVLegalizeImplicitBindingImpl::replaceCounterHandleCall(
265 Module &M, CallInst *OldCI, uint32_t NewBinding) {
266 IRBuilder<> Builder(OldCI);
267 const uint32_t DescSet =
268 cast<ConstantInt>(OldCI->getArgOperand(2))->getZExtValue();
269
270 SmallVector<Value *, 8> Args;
271 Args.push_back(OldCI->getArgOperand(0));
272 Args.push_back(Builder.getInt32(NewBinding));
273 Args.push_back(Builder.getInt32(DescSet));
274
275 Type *Tys[] = {OldCI->getType(), OldCI->getArgOperand(0)->getType()};
277 &M, Intrinsic::spv_resource_counterhandlefrombinding, Tys);
278 CallInst *NewCI = Builder.CreateCall(NewFunc, Args);
279 NewCI->setCallingConv(OldCI->getCallingConv());
280
281 OldCI->replaceAllUsesWith(NewCI);
282 OldCI->eraseFromParent();
283}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements the BitVector class.
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
DXIL Resource Implicit Binding
Module.h This file contains the declarations for the Module class.
Machine Check Debug Module
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
void setCallingConv(CallingConv::ID CC)
CallingConv::ID getCallingConv() const
Value * getArgOperand(unsigned i) const
LLVM_ABI Intrinsic::ID getIntrinsicID() const
Returns the intrinsic ID of the intrinsic called or Intrinsic::not_intrinsic if the called function i...
unsigned arg_size() const
This class represents a function call, abstracting a target machine's calling convention.
Base class for instruction visitors.
Definition InstVisitor.h:78
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
This is an optimization pass for GlobalISel generic memory operations.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1669
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
ModulePass * createSPIRVLegalizeImplicitBindingPass()