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_Last = -1
70};
71
72union ImageAttrs {
73 struct BitFlags {
74 unsigned Dim : 3;
75 unsigned Depth : 2;
76 unsigned Arrayed : 1;
77 unsigned MS : 1;
78 unsigned Sampled : 2;
79 unsigned ImageFormat : 6;
80 unsigned AQ : 2;
81 } Flags;
82 unsigned Val;
83
84 ImageAttrs(unsigned Dim, unsigned Depth, unsigned Arrayed, unsigned MS,
85 unsigned Sampled, unsigned ImageFormat, unsigned AQ = 0) {
86 Val = 0;
87 Flags.Dim = Dim;
88 Flags.Depth = Depth;
89 Flags.Arrayed = Arrayed;
90 Flags.MS = MS;
91 Flags.Sampled = Sampled;
92 Flags.ImageFormat = ImageFormat;
93 Flags.AQ = AQ;
94 }
95};
96
97inline IRHandle irhandle_image(const Type *SampledTy, unsigned Dim,
98 unsigned Depth, unsigned Arrayed, unsigned MS,
99 unsigned Sampled, unsigned ImageFormat,
100 unsigned AQ = 0) {
101 return std::make_tuple(
102 args&: SampledTy,
103 args: ImageAttrs(Dim, Depth, Arrayed, MS, Sampled, ImageFormat, AQ).Val,
104 args: SpecialTypeKind::STK_Image);
105}
106
107inline IRHandle irhandle_sampled_image(const Type *SampledTy,
108 const MachineInstr *ImageTy) {
109 assert(ImageTy->getOpcode() == SPIRV::OpTypeImage);
110 unsigned AC = AccessQualifier::AccessQualifier::None;
111 if (ImageTy->getNumOperands() > 8)
112 AC = ImageTy->getOperand(i: 8).getImm();
113 return std::make_tuple(
114 args&: SampledTy,
115 args: ImageAttrs(
116 ImageTy->getOperand(i: 2).getImm(), ImageTy->getOperand(i: 3).getImm(),
117 ImageTy->getOperand(i: 4).getImm(), ImageTy->getOperand(i: 5).getImm(),
118 ImageTy->getOperand(i: 6).getImm(), ImageTy->getOperand(i: 7).getImm(), AC)
119 .Val,
120 args: SpecialTypeKind::STK_SampledImage);
121}
122
123inline IRHandle irhandle_sampler() {
124 return std::make_tuple(args: nullptr, args: 0U, args: SpecialTypeKind::STK_Sampler);
125}
126
127inline IRHandle irhandle_pipe(uint8_t AQ) {
128 return std::make_tuple(args: nullptr, args&: AQ, args: SpecialTypeKind::STK_Pipe);
129}
130
131inline IRHandle irhandle_event() {
132 return std::make_tuple(args: nullptr, args: 0U, args: SpecialTypeKind::STK_DeviceEvent);
133}
134
135inline IRHandle irhandle_pointee(const Type *ElementType,
136 unsigned AddressSpace) {
137 return std::make_tuple(args: unifyPtrType(Ty: ElementType), args&: AddressSpace,
138 args: SpecialTypeKind::STK_ElementPointer);
139}
140
141inline IRHandle irhandle_ptr(const void *Ptr, unsigned Arg,
142 enum SpecialTypeKind STK) {
143 return std::make_tuple(args&: Ptr, args&: Arg, args&: STK);
144}
145
146inline IRHandle irhandle_vkbuffer(const Type *ElementType,
147 StorageClass::StorageClass SC,
148 bool IsWriteable) {
149 return std::make_tuple(args&: ElementType, args: (SC << 1) | IsWriteable,
150 args: SpecialTypeKind::STK_VkBuffer);
151}
152
153inline IRHandle irhandle_padding() {
154 return std::make_tuple(args: nullptr, args: 0, args: SpecialTypeKind::STK_Padding);
155}
156
157inline IRHandle irhandle_explict_layout_type(const Type *Ty) {
158 const Type *WrpTy = unifyPtrType(Ty);
159 return irhandle_ptr(Ptr: WrpTy, Arg: Ty->getTypeID(), STK: STK_ExplictLayoutType);
160}
161
162inline IRHandle handle(const Type *Ty) {
163 const Type *WrpTy = unifyPtrType(Ty);
164 return irhandle_ptr(Ptr: WrpTy, Arg: Ty->getTypeID(), STK: STK_Type);
165}
166
167inline IRHandle handle(const Value *V) {
168 return irhandle_ptr(Ptr: V, Arg: V->getValueID(), STK: STK_Value);
169}
170
171inline IRHandle handle(const MachineInstr *KeyMI) {
172 return irhandle_ptr(Ptr: KeyMI, Arg: SPIRV::to_hash(MI: KeyMI), STK: STK_MachineInstr);
173}
174
175inline bool type_has_layout_decoration(const Type *T) {
176 return (isa<StructType>(Val: T) || isa<ArrayType>(Val: T));
177}
178
179} // namespace SPIRV
180
181// Bi-directional mappings between LLVM entities and (v-reg, machine function)
182// pairs support management of unique SPIR-V definitions per machine function
183// per an LLVM/GlobalISel entity (e.g., Type, Constant, Machine Instruction).
184class SPIRVIRMapping {
185 DenseMap<SPIRV::IRHandleMF, SPIRV::MIHandle> Vregs;
186 DenseMap<const MachineInstr *, SPIRV::IRHandleMF> Defs;
187
188public:
189 bool add(SPIRV::IRHandle Handle, const MachineInstr *MI) {
190 if (auto DefIt = Defs.find(Val: MI); DefIt != Defs.end()) {
191 auto [ExistHandle, ExistMF] = DefIt->second;
192 if (Handle == ExistHandle && MI->getMF() == ExistMF)
193 return false; // already exists
194 // invalidate the record
195 Vregs.erase(Val: DefIt->second);
196 Defs.erase(I: DefIt);
197 }
198 SPIRV::IRHandleMF HandleMF = SPIRV::getIRHandleMF(Handle, MF: MI->getMF());
199 SPIRV::MIHandle MIKey = SPIRV::getMIKey(MI);
200 auto It1 = Vregs.try_emplace(Key: HandleMF, Args&: MIKey);
201 if (!It1.second) {
202 // there is an expired record that we need to invalidate
203 Defs.erase(Val: std::get<0>(t&: It1.first->second));
204 // update the record
205 It1.first->second = MIKey;
206 }
207 [[maybe_unused]] auto It2 = Defs.try_emplace(Key: MI, Args&: HandleMF);
208 assert(It2.second);
209 return true;
210 }
211 bool erase(const MachineInstr *MI) {
212 bool Res = false;
213 if (auto It = Defs.find(Val: MI); It != Defs.end()) {
214 Res = Vregs.erase(Val: It->second);
215 Defs.erase(I: It);
216 }
217 return Res;
218 }
219 const MachineInstr *findMI(SPIRV::IRHandle Handle,
220 const MachineFunction *MF) {
221 SPIRV::IRHandleMF HandleMF = SPIRV::getIRHandleMF(Handle, MF);
222 auto It = Vregs.find(Val: HandleMF);
223 if (It == Vregs.end())
224 return nullptr;
225 auto [MI, Reg, Hash] = It->second;
226 const MachineInstr *Def = MF->getRegInfo().getVRegDef(Reg);
227 if (!Def || Def != MI || SPIRV::to_hash(MI) != Hash) {
228 // there is an expired record that we need to invalidate
229 erase(MI);
230 return nullptr;
231 }
232 assert(Defs.contains(MI) && Defs.find(MI)->second == HandleMF);
233 return MI;
234 }
235 Register find(SPIRV::IRHandle Handle, const MachineFunction *MF) {
236 const MachineInstr *MI = findMI(Handle, MF);
237 return MI ? MI->getOperand(i: 0).getReg() : Register();
238 }
239
240 // helpers
241 bool add(const Type *PointeeTy, unsigned AddressSpace,
242 const MachineInstr *MI) {
243 return add(Handle: SPIRV::irhandle_pointee(ElementType: PointeeTy, AddressSpace), MI);
244 }
245 Register find(const Type *PointeeTy, unsigned AddressSpace,
246 const MachineFunction *MF) {
247 return find(Handle: SPIRV::irhandle_pointee(ElementType: PointeeTy, AddressSpace), MF);
248 }
249 const MachineInstr *findMI(const Type *PointeeTy, unsigned AddressSpace,
250 const MachineFunction *MF) {
251 return findMI(Handle: SPIRV::irhandle_pointee(ElementType: PointeeTy, AddressSpace), MF);
252 }
253
254 bool add(const Value *V, const MachineInstr *MI) {
255 return add(Handle: SPIRV::handle(V), MI);
256 }
257
258 bool add(const Type *T, bool RequiresExplicitLayout, const MachineInstr *MI) {
259 if (RequiresExplicitLayout && SPIRV::type_has_layout_decoration(T)) {
260 return add(Handle: SPIRV::irhandle_explict_layout_type(Ty: T), MI);
261 }
262 return add(Handle: SPIRV::handle(Ty: T), MI);
263 }
264
265 bool add(const MachineInstr *Obj, const MachineInstr *MI) {
266 return add(Handle: SPIRV::handle(KeyMI: Obj), MI);
267 }
268
269 Register find(const Value *V, const MachineFunction *MF) {
270 return find(Handle: SPIRV::handle(V), MF);
271 }
272
273 Register find(const Type *T, bool RequiresExplicitLayout,
274 const MachineFunction *MF) {
275 if (RequiresExplicitLayout && SPIRV::type_has_layout_decoration(T))
276 return find(Handle: SPIRV::irhandle_explict_layout_type(Ty: T), MF);
277 return find(Handle: SPIRV::handle(Ty: T), MF);
278 }
279
280 Register find(const MachineInstr *MI, const MachineFunction *MF) {
281 return find(Handle: SPIRV::handle(KeyMI: MI), MF);
282 }
283
284 const MachineInstr *findMI(const Value *Obj, const MachineFunction *MF) {
285 return findMI(Handle: SPIRV::handle(V: Obj), MF);
286 }
287
288 const MachineInstr *findMI(const Type *T, bool RequiresExplicitLayout,
289 const MachineFunction *MF) {
290 if (RequiresExplicitLayout && SPIRV::type_has_layout_decoration(T))
291 return findMI(Handle: SPIRV::irhandle_explict_layout_type(Ty: T), MF);
292 return findMI(Handle: SPIRV::handle(Ty: T), MF);
293 }
294
295 const MachineInstr *findMI(const MachineInstr *Obj,
296 const MachineFunction *MF) {
297 return findMI(Handle: SPIRV::handle(KeyMI: Obj), MF);
298 }
299};
300} // namespace llvm
301#endif // LLVM_LIB_TARGET_SPIRV_SPIRVIRMAPPING_H
302