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 bool replaceImplicitBindingCalls(Module &M);
39
40 // A map from descriptor set to a bit vector of used binding numbers.
41 std::vector<BitVector> UsedBindings;
42
43 // Set to true by collectBindingInfo() if there are any implicit binding
44 // declarations in the module.
45 bool MayHaveImplicitBindings = false;
46};
47
48class SPIRVLegalizeImplicitBindingLegacy : public ModulePass {
49public:
50 static char ID;
51 SPIRVLegalizeImplicitBindingLegacy() : ModulePass(ID) {}
52 StringRef getPassName() const override {
53 return "SPIRV Legalize Implicit Binding";
54 }
55 bool runOnModule(Module &M) override {
56 return SPIRVLegalizeImplicitBindingImpl().runOnModule(M);
57 }
58};
59
60static uint32_t getDescSet(const CallInst *CI) {
61 uint32_t DescSetArgIdx;
62 switch (CI->getIntrinsicID()) {
63 case Intrinsic::spv_resource_handlefromimplicitbinding:
64 DescSetArgIdx = 1;
65 break;
66 case Intrinsic::spv_resource_counterhandlefromimplicitbinding:
67 DescSetArgIdx = 2;
68 break;
69 default:
70 llvm_unreachable("CallInst is not an implicit binding intrinsic");
71 }
72 return cast<ConstantInt>(CI->getArgOperand(DescSetArgIdx))->getZExtValue();
73}
74
75// Collect all of the bindings used by llvm.spv.resource.handlefrombinding
76// and llvm.spv.resource.counterhandlefrombinding calls. Also check if there
77// are any implicit binding calls.
78void SPIRVLegalizeImplicitBindingImpl::collectBindingInfo(Module &M) {
79
80 auto addBinding = [&](uint32_t DescSet, uint32_t Binding) {
81 if (UsedBindings.size() <= DescSet) {
82 UsedBindings.resize(DescSet + 1);
83 UsedBindings[DescSet].resize(64);
84 }
85 if (UsedBindings[DescSet].size() <= Binding) {
86 UsedBindings[DescSet].resize(2 * Binding + 1);
87 }
88 UsedBindings[DescSet].set(Binding);
89 };
90
91 auto collectBinding = [&](Function &F, uint32_t ArgDescSetIdx,
92 uint32_t ArgBindingIdx) {
93 for (User *U : F.users()) {
94 if (CallInst *CI = dyn_cast<CallInst>(U)) {
95 const uint32_t DescSet =
96 cast<ConstantInt>(CI->getArgOperand(ArgDescSetIdx))->getZExtValue();
97 const uint32_t Binding =
98 cast<ConstantInt>(CI->getArgOperand(ArgBindingIdx))->getZExtValue();
99 addBinding(DescSet, Binding);
100 }
101 }
102 };
103
104 for (Function &F : M) {
105 if (!F.isDeclaration())
106 continue;
107
108 switch (F.getIntrinsicID()) {
109 case Intrinsic::spv_resource_handlefrombinding:
110 collectBinding(F, /*ArgDescSetIdx*/ 0, /*ArgBindingIdx*/ 1);
111 break;
112 case Intrinsic::spv_resource_counterhandlefrombinding:
113 collectBinding(F, /*ArgDescSetIdx*/ 1, /*ArgBindingIdx*/ 2);
114 break;
115 case Intrinsic::spv_resource_handlefromimplicitbinding:
116 case Intrinsic::spv_resource_counterhandlefromimplicitbinding:
117 MayHaveImplicitBindings = true;
118 break;
119 default:
120 break;
121 }
122 }
123}
124
125uint32_t SPIRVLegalizeImplicitBindingImpl::getAndReserveFirstUnusedBinding(
126 uint32_t DescSet) {
127 if (UsedBindings.size() <= DescSet) {
128 UsedBindings.resize(DescSet + 1);
129 UsedBindings[DescSet].resize(64);
130 }
131
132 int NewBinding = UsedBindings[DescSet].find_first_unset();
133 if (NewBinding == -1) {
134 NewBinding = UsedBindings[DescSet].size();
135 UsedBindings[DescSet].resize(2 * NewBinding + 1);
136 }
137
138 UsedBindings[DescSet].set(NewBinding);
139 return NewBinding;
140}
141
142// Replace the implicit binding call with a new call using explicit binding.
143static void replaceWithHandleFromBinding(Module &M, CallInst *CI,
144 uint32_t DescSet, uint32_t Binding,
145 Value *IndexOp, Value *RangeOp,
146 Value *Name) {
147 assert(CI->getIntrinsicID() ==
148 Intrinsic::spv_resource_handlefromimplicitbinding &&
149 "unexpected implicit binding intrinsic");
150 IRBuilder<> Builder(CI);
151 Value *DescSetOp = Builder.getInt32(DescSet);
152 Value *BindingOp = Builder.getInt32(Binding);
154 &M, Intrinsic::spv_resource_handlefrombinding, {CI->getType()});
155 CallInst *NewCI = Builder.CreateCall(
156 NewFunc, {DescSetOp, BindingOp, IndexOp, RangeOp, Name});
157 NewCI->setCallingConv(CI->getCallingConv());
158 CI->replaceAllUsesWith(NewCI);
159 CI->eraseFromParent();
160}
161
162// Replace the implicit counter binding call with a new call using explicit
163// binding.
164static void replaceWithCounterHandleFromBinding(Module &M, CallInst *CI,
165 uint32_t DescSet,
166 uint32_t Binding) {
167 assert(CI->getIntrinsicID() ==
168 Intrinsic::spv_resource_counterhandlefromimplicitbinding &&
169 "unexpected implicit binding intrinsic");
170 IRBuilder<> Builder(CI);
171 Value *DescSetOp = Builder.getInt32(DescSet);
172 Value *BindingOp = Builder.getInt32(Binding);
173 Value *MainHandle = CI->getArgOperand(0);
174 Type *OverloadTys[] = {CI->getType(), MainHandle->getType()};
176 &M, Intrinsic::spv_resource_counterhandlefrombinding, OverloadTys);
177 CallInst *NewCI =
178 Builder.CreateCall(NewFunc, {MainHandle, DescSetOp, BindingOp});
179 NewCI->setCallingConv(CI->getCallingConv());
180 CI->replaceAllUsesWith(NewCI);
181 CI->eraseFromParent();
182}
183
184bool SPIRVLegalizeImplicitBindingImpl::replaceImplicitBindingCalls(Module &M) {
185 // Collect all implicit binding calls.
187 bool Changed = false;
188 for (Function &F : M) {
189 if (!F.isDeclaration())
190 continue;
191
192 uint32_t OrderIdIdx;
193 if (F.getIntrinsicID() == Intrinsic::spv_resource_handlefromimplicitbinding)
194 OrderIdIdx = 0;
195 else if (F.getIntrinsicID() ==
196 Intrinsic::spv_resource_counterhandlefromimplicitbinding)
197 OrderIdIdx = 1;
198 else
199 continue;
200
201 for (User *U : F.users()) {
202 if (CallInst *CI = dyn_cast<CallInst>(U)) {
203 ConstantInt *OrderId = cast<ConstantInt>(CI->getArgOperand(OrderIdIdx));
204 IBCalls.emplace_back(OrderId->getZExtValue(), CI);
205 }
206 }
207 }
208
209 if (IBCalls.empty())
210 return false;
211
212 // Sort the collected calls by their order ID.
213 llvm::sort(IBCalls, llvm::less_first());
214
215 // Assign bindings based on the order ID. Same order ID gets the same binding.
216 // Also make sure that calls with the same order ID have the same descriptor
217 // set.
218 uint32_t LastOrderId = -1;
219 uint32_t LastBinding = -1;
220 uint32_t LastDescSet = -1;
221 for (auto &[OrderId, CI] : IBCalls) {
222 uint32_t Binding;
223 uint32_t DescSet = getDescSet(CI);
224 if (OrderId == LastOrderId) {
225 if (DescSet != LastDescSet)
226 report_fatal_error("Implicit binding calls with the same order ID must "
227 "have the same descriptor set");
228 Binding = LastBinding;
229 } else {
230 Binding = getAndReserveFirstUnusedBinding(DescSet);
231 }
232
233 // Replace the implicit binding call with an explicit binding call.
234 if (CI->getIntrinsicID() ==
235 Intrinsic::spv_resource_handlefromimplicitbinding)
236 replaceWithHandleFromBinding(M, CI, DescSet, Binding,
237 CI->getArgOperand(2), CI->getArgOperand(3),
238 CI->getArgOperand(4));
239 else
240 replaceWithCounterHandleFromBinding(M, CI, DescSet, Binding);
241 Changed = true;
242
243 LastOrderId = OrderId;
244 LastBinding = Binding;
245 LastDescSet = DescSet;
246 }
247 return Changed;
248}
249
250bool SPIRVLegalizeImplicitBindingImpl::runOnModule(Module &M) {
251 collectBindingInfo(M);
252
253 bool Changed = false;
254 if (MayHaveImplicitBindings)
255 Changed |= replaceImplicitBindingCalls(M);
256
257 return Changed;
258}
259} // namespace
260
261PreservedAnalyses
263 return SPIRVLegalizeImplicitBindingImpl().runOnModule(M)
266}
267
268char SPIRVLegalizeImplicitBindingLegacy::ID = 0;
269
270INITIALIZE_PASS(SPIRVLegalizeImplicitBindingLegacy,
271 "legalize-spirv-implicit-binding",
272 "Legalize SPIR-V implicit bindings", false, false)
273
275 return new SPIRVLegalizeImplicitBindingLegacy();
276}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements the BitVector class.
DXIL Resource Implicit Binding
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
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...
This class represents a function call, abstracting a target machine's calling convention.
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
Definition Constants.h:168
void setCallingConv(CallingConv::ID CC)
Definition Function.h:277
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)
reference emplace_back(ArgTypes &&... Args)
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
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
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
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()