1//===------------ SPIRVMapping.h - SPIR-V Duplicates Tracker ----*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// General infrastructure for keeping track of the values that according to
10// the SPIR-V binary layout should be global to the whole module.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVIRMAPPING_H
15#define LLVM_LIB_TARGET_SPIRV_SPIRVIRMAPPING_H
16
17#include "MCTargetDesc/SPIRVBaseInfo.h"
18#include "MCTargetDesc/SPIRVMCTargetDesc.h"
19#include "SPIRVUtils.h"
20#include "llvm/ADT/DenseMap.h"
21#include "llvm/ADT/Hashing.h"
22#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
23#include "llvm/CodeGen/MachineModuleInfo.h"
24
25namespace llvm {
26namespace SPIRV {
27
28inline size_t to_hash(const MachineInstr *MI) {
29 hash_code H = llvm::hash_combine(args: MI->getOpcode(), args: MI->getNumOperands());
30 for (unsigned I = MI->getNumDefs(); I < MI->getNumOperands(); ++I) {
31 const MachineOperand &MO = MI->getOperand(i: I);
32 if (MO.getType() == MachineOperand::MO_CImmediate)
33 H = llvm::hash_combine(args: H, args: MO.getType(), args: MO.getCImm());
34 else if (MO.getType() == MachineOperand::MO_FPImmediate)
35 H = llvm::hash_combine(args: H, args: MO.getType(), args: MO.getFPImm());
36 else
37 H = llvm::hash_combine(args: H, args: MO.getType());
38 }
39 return H;
40}
41
42using MIHandle = std::tuple<const MachineInstr *, Register, size_t>;
43
44inline MIHandle getMIKey(const MachineInstr *MI) {
45 return std::make_tuple(args&: MI, args: MI->getOperand(i: 0).getReg(), args: SPIRV::to_hash(MI));
46}
47
48using IRHandle = std::tuple<const void *, unsigned, unsigned>;
49using IRHandleMF = std::pair<IRHandle, const MachineFunction *>;
50
51inline IRHandleMF getIRHandleMF(IRHandle Handle, const MachineFunction *MF) {
52 return std::make_pair(x&: Handle, y&: MF);
53}
54
55enum SpecialTypeKind {
56 STK_Empty = 0,
57 STK_Image,
58 STK_SampledImage,
59 STK_Sampler,
60 STK_Pipe,
61 STK_DeviceEvent,
62 STK_ElementPointer,
63 STK_Type,
64 STK_Value,
65 STK_MachineInstr,
66 STK_VkBuffer,
67 STK_Padding,
68 STK_ExplictLayoutType,
69 STK_UntypedPointer,
70 STK_Last = -1
71};
72
73union ImageAttrs {
74 struct BitFlags {
75 unsigned Dim : 3;
76 unsigned Depth : 2;
77 unsigned Arrayed : 1;
78 unsigned MS : 1;
79 unsigned Sampled : 2;
80 unsigned ImageFormat : 6;
81 unsigned AQ : 2;
82 } Flags;
83 unsigned Val;
84
85 ImageAttrs(unsigned Dim, unsigned Depth, unsigned Arrayed, unsigned MS,
86 unsigned Sampled, unsigned ImageFormat, unsigned AQ = 0) {
87 Val = 0;
88 Flags.Dim = Dim;
89 Flags.Depth = Depth;
90 Flags.Arrayed = Arrayed;
91 Flags.MS = MS;
92 Flags.Sampled = Sampled;
93 Flags.ImageFormat = ImageFormat;
94 Flags.AQ = AQ;
95 }
96};
97
98inline IRHandle irhandle_image(const Type *SampledTy, unsigned Dim,
99 unsigned Depth, unsigned Arrayed, unsigned MS,
100 unsigned Sampled, unsigned ImageFormat,
101 unsigned AQ = 0) {
102 return std::make_tuple(
103 args&: SampledTy,
104 args: ImageAttrs(Dim, Depth, Arrayed, MS, Sampled, ImageFormat, AQ).Val,
105 args: SpecialTypeKind::STK_Image);
106}
107
108inline IRHandle irhandle_sampled_image(const Type *SampledTy,
109 const MachineInstr *ImageTy) {
110 assert(ImageTy->getOpcode() == SPIRV::OpTypeImage);
111 unsigned AC = AccessQualifier::AccessQualifier::None;
112 if (ImageTy->getNumOperands() > 8)
113 AC = ImageTy->getOperand(i: 8).getImm();
114 return std::make_tuple(
115 args&: SampledTy,
116 args: ImageAttrs(
117 ImageTy->getOperand(i: 2).getImm(), ImageTy->getOperand(i: 3).getImm(),
118 ImageTy->getOperand(i: 4).getImm(), ImageTy->getOperand(i: 5).getImm(),
119 ImageTy->getOperand(i: 6).getImm(), ImageTy->getOperand(i: 7).getImm(), AC)
120 .Val,
121 args: SpecialTypeKind::STK_SampledImage);
122}
123
124inline IRHandle irhandle_sampler() {
125 return std::make_tuple(args: nullptr, args: 0U, args: SpecialTypeKind::STK_Sampler);
126}
127
128inline IRHandle irhandle_pipe(uint8_t AQ) {
129 return std::make_tuple(args: nullptr, args&: AQ, args: SpecialTypeKind::STK_Pipe);
130}
131
132inline IRHandle irhandle_event() {
133 return std::make_tuple(args: nullptr, args: 0U, args: SpecialTypeKind::STK_DeviceEvent);
134}
135
136inline IRHandle irhandle_pointee(const Type *ElementType,
137 unsigned AddressSpace) {
138 return std::make_tuple(args: unifyPtrType(Ty: ElementType), args&: AddressSpace,
139 args: SpecialTypeKind::STK_ElementPointer);
140}
141
142inline IRHandle irhandle_ptr(const void *Ptr, unsigned Arg,
143 enum SpecialTypeKind STK) {
144 return std::make_tuple(args&: Ptr, args&: Arg, args&: STK);
145}
146
147inline IRHandle irhandle_vkbuffer(const Type *ElementType,
148 StorageClass::StorageClass SC,
149 bool IsWriteable) {
150 return std::make_tuple(args&: ElementType, args: (SC << 1) | IsWriteable,
151 args: SpecialTypeKind::STK_VkBuffer);
152}
153
154inline IRHandle irhandle_padding() {
155 return std::make_tuple(args: nullptr, args: 0, args: SpecialTypeKind::STK_Padding);
156}
157
158inline IRHandle irhandle_untyped_pointer(unsigned AddressSpace) {
159 return std::make_tuple(args: nullptr, args&: AddressSpace,
160 args: SpecialTypeKind::STK_UntypedPointer);
161}
162
163inline IRHandle irhandle_explict_layout_type(const Type *Ty) {
164 const Type *WrpTy = unifyPtrType(Ty);
165 return irhandle_ptr(Ptr: WrpTy, Arg: Ty->getTypeID(), STK: STK_ExplictLayoutType);
166}
167
168inline IRHandle handle(const Type *Ty) {
169 const Type *WrpTy = unifyPtrType(Ty);
170 return irhandle_ptr(Ptr: WrpTy, Arg: Ty->getTypeID(), STK: STK_Type);
171}
172
173inline IRHandle handle(const Value *V) {
174 return irhandle_ptr(Ptr: V, Arg: V->getValueID(), STK: STK_Value);
175}
176
177inline IRHandle handle(const MachineInstr *KeyMI) {
178 return irhandle_ptr(Ptr: KeyMI, Arg: SPIRV::to_hash(MI: KeyMI), STK: STK_MachineInstr);
179}
180
181inline bool type_has_layout_decoration(const Type *T) {
182 return (isa<StructType>(Val: T) || isa<ArrayType>(Val: T));
183}
184
185} // namespace SPIRV
186
187// Bi-directional mappings between LLVM entities and (v-reg, machine function)
188// pairs support management of unique SPIR-V definitions per machine function
189// per an LLVM/GlobalISel entity (e.g., Type, Constant, Machine Instruction).
190class SPIRVIRMapping {
191 DenseMap<SPIRV::IRHandleMF, SPIRV::MIHandle> Vregs;
192 DenseMap<const MachineInstr *, SPIRV::IRHandleMF> Defs;
193
194public:
195 bool add(SPIRV::IRHandle Handle, const MachineInstr *MI) {
196 if (auto DefIt = Defs.find(Val: MI); DefIt != Defs.end()) {
197 auto [ExistHandle, ExistMF] = DefIt->second;
198 if (Handle == ExistHandle && MI->getMF() == ExistMF)
199 return false; // already exists
200 // invalidate the record
201 Vregs.erase(Val: DefIt->second);
202 Defs.erase(I: DefIt);
203 }
204 SPIRV::IRHandleMF HandleMF = SPIRV::getIRHandleMF(Handle, MF: MI->getMF());
205 SPIRV::MIHandle MIKey = SPIRV::getMIKey(MI);
206 auto It1 = Vregs.try_emplace(Key: HandleMF, Args&: MIKey);
207 if (!It1.second) {
208 // there is an expired record that we need to invalidate
209 Defs.erase(Val: std::get<0>(t&: It1.first->second));
210 // update the record
211 It1.first->second = MIKey;
212 }
213 [[maybe_unused]] auto It2 = Defs.try_emplace(Key: MI, Args&: HandleMF);
214 assert(It2.second);
215 return true;
216 }
217 bool erase(const MachineInstr *MI) {
218 bool Res = false;
219 if (auto It = Defs.find(Val: MI); It != Defs.end()) {
220 Res = Vregs.erase(Val: It->second);
221 Defs.erase(I: It);
222 }
223 return Res;
224 }
225 const MachineInstr *findMI(SPIRV::IRHandle Handle,
226 const MachineFunction *MF) {
227 SPIRV::IRHandleMF HandleMF = SPIRV::getIRHandleMF(Handle, MF);
228 auto It = Vregs.find(Val: HandleMF);
229 if (It == Vregs.end())
230 return nullptr;
231 auto [MI, Reg, Hash] = It->second;
232 const MachineInstr *Def = MF->getRegInfo().getVRegDef(Reg);
233 if (!Def || Def != MI || SPIRV::to_hash(MI) != Hash) {
234 // there is an expired record that we need to invalidate
235 erase(MI);
236 return nullptr;
237 }
238 assert(Defs.contains(MI) && Defs.find(MI)->second == HandleMF);
239 return MI;
240 }
241 Register find(SPIRV::IRHandle Handle, const MachineFunction *MF) {
242 const MachineInstr *MI = findMI(Handle, MF);
243 return MI ? MI->getOperand(i: 0).getReg() : Register();
244 }
245
246 // helpers
247 bool add(const Type *PointeeTy, unsigned AddressSpace,
248 const MachineInstr *MI) {
249 return add(Handle: SPIRV::irhandle_pointee(ElementType: PointeeTy, AddressSpace), MI);
250 }
251 Register find(const Type *PointeeTy, unsigned AddressSpace,
252 const MachineFunction *MF) {
253 return find(Handle: SPIRV::irhandle_pointee(ElementType: PointeeTy, AddressSpace), MF);
254 }
255 const MachineInstr *findMI(const Type *PointeeTy, unsigned AddressSpace,
256 const MachineFunction *MF) {
257 return findMI(Handle: SPIRV::irhandle_pointee(ElementType: PointeeTy, AddressSpace), MF);
258 }
259
260 bool add(const Value *V, const MachineInstr *MI) {
261 return add(Handle: SPIRV::handle(V), MI);
262 }
263
264 bool add(const Type *T, bool RequiresExplicitLayout, const MachineInstr *MI) {
265 if (RequiresExplicitLayout && SPIRV::type_has_layout_decoration(T)) {
266 return add(Handle: SPIRV::irhandle_explict_layout_type(Ty: T), MI);
267 }
268 return add(Handle: SPIRV::handle(Ty: T), MI);
269 }
270
271 bool add(const MachineInstr *Obj, const MachineInstr *MI) {
272 return add(Handle: SPIRV::handle(KeyMI: Obj), MI);
273 }
274
275 Register find(const Value *V, const MachineFunction *MF) {
276 return find(Handle: SPIRV::handle(V), MF);
277 }
278
279 Register find(const Type *T, bool RequiresExplicitLayout,
280 const MachineFunction *MF) {
281 if (RequiresExplicitLayout && SPIRV::type_has_layout_decoration(T))
282 return find(Handle: SPIRV::irhandle_explict_layout_type(Ty: T), MF);
283 return find(Handle: SPIRV::handle(Ty: T), MF);
284 }
285
286 Register find(const MachineInstr *MI, const MachineFunction *MF) {
287 return find(Handle: SPIRV::handle(KeyMI: MI), MF);
288 }
289
290 const MachineInstr *findMI(const Value *Obj, const MachineFunction *MF) {
291 return findMI(Handle: SPIRV::handle(V: Obj), MF);
292 }
293
294 const MachineInstr *findMI(const Type *T, bool RequiresExplicitLayout,
295 const MachineFunction *MF) {
296 if (RequiresExplicitLayout && SPIRV::type_has_layout_decoration(T))
297 return findMI(Handle: SPIRV::irhandle_explict_layout_type(Ty: T), MF);
298 return findMI(Handle: SPIRV::handle(Ty: T), MF);
299 }
300
301 const MachineInstr *findMI(const MachineInstr *Obj,
302 const MachineFunction *MF) {
303 return findMI(Handle: SPIRV::handle(KeyMI: Obj), MF);
304 }
305};
306} // namespace llvm
307#endif // LLVM_LIB_TARGET_SPIRV_SPIRVIRMAPPING_H
308