1 //===-- SPIRVDuplicatesTracker.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_SPIRVDUPLICATESTRACKER_H
15 #define LLVM_LIB_TARGET_SPIRV_SPIRVDUPLICATESTRACKER_H
16 
17 #include "MCTargetDesc/SPIRVBaseInfo.h"
18 #include "MCTargetDesc/SPIRVMCTargetDesc.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/MapVector.h"
21 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
22 #include "llvm/CodeGen/MachineModuleInfo.h"
23 
24 #include <type_traits>
25 
26 namespace llvm {
27 namespace SPIRV {
28 // NOTE: using MapVector instead of DenseMap because it helps getting
29 // everything ordered in a stable manner for a price of extra (NumKeys)*PtrSize
30 // memory and expensive removals which do not happen anyway.
31 class DTSortableEntry : public MapVector<const MachineFunction *, Register> {
32   SmallVector<DTSortableEntry *, 2> Deps;
33 
34   struct FlagsTy {
35     unsigned IsFunc : 1;
36     unsigned IsGV : 1;
37     // NOTE: bit-field default init is a C++20 feature.
38     FlagsTy() : IsFunc(0), IsGV(0) {}
39   };
40   FlagsTy Flags;
41 
42 public:
43   // Common hoisting utility doesn't support function, because their hoisting
44   // require hoisting of params as well.
45   bool getIsFunc() const { return Flags.IsFunc; }
46   bool getIsGV() const { return Flags.IsGV; }
47   void setIsFunc(bool V) { Flags.IsFunc = V; }
48   void setIsGV(bool V) { Flags.IsGV = V; }
49 
50   const SmallVector<DTSortableEntry *, 2> &getDeps() const { return Deps; }
51   void addDep(DTSortableEntry *E) { Deps.push_back(E); }
52 };
53 
54 struct SpecialTypeDescriptor {
55   enum SpecialTypeKind {
56     STK_Empty = 0,
57     STK_Image,
58     STK_SampledImage,
59     STK_Sampler,
60     STK_Pipe,
61     STK_DeviceEvent,
62     STK_Pointer,
63     STK_Last = -1
64   };
65   SpecialTypeKind Kind;
66 
67   unsigned Hash;
68 
69   SpecialTypeDescriptor() = delete;
70   SpecialTypeDescriptor(SpecialTypeKind K) : Kind(K) { Hash = Kind; }
71 
72   unsigned getHash() const { return Hash; }
73 
74   virtual ~SpecialTypeDescriptor() {}
75 };
76 
77 struct ImageTypeDescriptor : public SpecialTypeDescriptor {
78   union ImageAttrs {
79     struct BitFlags {
80       unsigned Dim : 3;
81       unsigned Depth : 2;
82       unsigned Arrayed : 1;
83       unsigned MS : 1;
84       unsigned Sampled : 2;
85       unsigned ImageFormat : 6;
86       unsigned AQ : 2;
87     } Flags;
88     unsigned Val;
89   };
90 
91   ImageTypeDescriptor(const Type *SampledTy, unsigned Dim, unsigned Depth,
92                       unsigned Arrayed, unsigned MS, unsigned Sampled,
93                       unsigned ImageFormat, unsigned AQ = 0)
94       : SpecialTypeDescriptor(SpecialTypeKind::STK_Image) {
95     ImageAttrs Attrs;
96     Attrs.Val = 0;
97     Attrs.Flags.Dim = Dim;
98     Attrs.Flags.Depth = Depth;
99     Attrs.Flags.Arrayed = Arrayed;
100     Attrs.Flags.MS = MS;
101     Attrs.Flags.Sampled = Sampled;
102     Attrs.Flags.ImageFormat = ImageFormat;
103     Attrs.Flags.AQ = AQ;
104     Hash = (DenseMapInfo<Type *>().getHashValue(SampledTy) & 0xffff) ^
105            ((Attrs.Val << 8) | Kind);
106   }
107 
108   static bool classof(const SpecialTypeDescriptor *TD) {
109     return TD->Kind == SpecialTypeKind::STK_Image;
110   }
111 };
112 
113 struct SampledImageTypeDescriptor : public SpecialTypeDescriptor {
114   SampledImageTypeDescriptor(const Type *SampledTy, const MachineInstr *ImageTy)
115       : SpecialTypeDescriptor(SpecialTypeKind::STK_SampledImage) {
116     assert(ImageTy->getOpcode() == SPIRV::OpTypeImage);
117     ImageTypeDescriptor TD(
118         SampledTy, ImageTy->getOperand(2).getImm(),
119         ImageTy->getOperand(3).getImm(), ImageTy->getOperand(4).getImm(),
120         ImageTy->getOperand(5).getImm(), ImageTy->getOperand(6).getImm(),
121         ImageTy->getOperand(7).getImm(), ImageTy->getOperand(8).getImm());
122     Hash = TD.getHash() ^ Kind;
123   }
124 
125   static bool classof(const SpecialTypeDescriptor *TD) {
126     return TD->Kind == SpecialTypeKind::STK_SampledImage;
127   }
128 };
129 
130 struct SamplerTypeDescriptor : public SpecialTypeDescriptor {
131   SamplerTypeDescriptor()
132       : SpecialTypeDescriptor(SpecialTypeKind::STK_Sampler) {
133     Hash = Kind;
134   }
135 
136   static bool classof(const SpecialTypeDescriptor *TD) {
137     return TD->Kind == SpecialTypeKind::STK_Sampler;
138   }
139 };
140 
141 struct PipeTypeDescriptor : public SpecialTypeDescriptor {
142 
143   PipeTypeDescriptor(uint8_t AQ)
144       : SpecialTypeDescriptor(SpecialTypeKind::STK_Pipe) {
145     Hash = (AQ << 8) | Kind;
146   }
147 
148   static bool classof(const SpecialTypeDescriptor *TD) {
149     return TD->Kind == SpecialTypeKind::STK_Pipe;
150   }
151 };
152 
153 struct DeviceEventTypeDescriptor : public SpecialTypeDescriptor {
154 
155   DeviceEventTypeDescriptor()
156       : SpecialTypeDescriptor(SpecialTypeKind::STK_DeviceEvent) {
157     Hash = Kind;
158   }
159 
160   static bool classof(const SpecialTypeDescriptor *TD) {
161     return TD->Kind == SpecialTypeKind::STK_DeviceEvent;
162   }
163 };
164 
165 struct PointerTypeDescriptor : public SpecialTypeDescriptor {
166   const Type *ElementType;
167   unsigned AddressSpace;
168 
169   PointerTypeDescriptor() = delete;
170   PointerTypeDescriptor(const Type *ElementType, unsigned AddressSpace)
171       : SpecialTypeDescriptor(SpecialTypeKind::STK_Pointer),
172         ElementType(ElementType), AddressSpace(AddressSpace) {
173     Hash = (DenseMapInfo<Type *>().getHashValue(ElementType) & 0xffff) ^
174            ((AddressSpace << 8) | Kind);
175   }
176 
177   static bool classof(const SpecialTypeDescriptor *TD) {
178     return TD->Kind == SpecialTypeKind::STK_Pointer;
179   }
180 };
181 } // namespace SPIRV
182 
183 template <> struct DenseMapInfo<SPIRV::SpecialTypeDescriptor> {
184   static inline SPIRV::SpecialTypeDescriptor getEmptyKey() {
185     return SPIRV::SpecialTypeDescriptor(
186         SPIRV::SpecialTypeDescriptor::STK_Empty);
187   }
188   static inline SPIRV::SpecialTypeDescriptor getTombstoneKey() {
189     return SPIRV::SpecialTypeDescriptor(SPIRV::SpecialTypeDescriptor::STK_Last);
190   }
191   static unsigned getHashValue(SPIRV::SpecialTypeDescriptor Val) {
192     return Val.getHash();
193   }
194   static bool isEqual(SPIRV::SpecialTypeDescriptor LHS,
195                       SPIRV::SpecialTypeDescriptor RHS) {
196     return getHashValue(LHS) == getHashValue(RHS);
197   }
198 };
199 
200 template <typename KeyTy> class SPIRVDuplicatesTrackerBase {
201 public:
202   // NOTE: using MapVector instead of DenseMap helps getting everything ordered
203   // in a stable manner for a price of extra (NumKeys)*PtrSize memory and
204   // expensive removals which don't happen anyway.
205   using StorageTy = MapVector<KeyTy, SPIRV::DTSortableEntry>;
206 
207 private:
208   StorageTy Storage;
209 
210 public:
211   void add(KeyTy V, const MachineFunction *MF, Register R) {
212     if (find(V, MF).isValid())
213       return;
214 
215     Storage[V][MF] = R;
216     if (std::is_same<Function,
217                      typename std::remove_const<
218                          typename std::remove_pointer<KeyTy>::type>::type>() ||
219         std::is_same<Argument,
220                      typename std::remove_const<
221                          typename std::remove_pointer<KeyTy>::type>::type>())
222       Storage[V].setIsFunc(true);
223     if (std::is_same<GlobalVariable,
224                      typename std::remove_const<
225                          typename std::remove_pointer<KeyTy>::type>::type>())
226       Storage[V].setIsGV(true);
227   }
228 
229   Register find(KeyTy V, const MachineFunction *MF) const {
230     auto iter = Storage.find(V);
231     if (iter != Storage.end()) {
232       auto Map = iter->second;
233       auto iter2 = Map.find(MF);
234       if (iter2 != Map.end())
235         return iter2->second;
236     }
237     return Register();
238   }
239 
240   const StorageTy &getAllUses() const { return Storage; }
241 
242 private:
243   StorageTy &getAllUses() { return Storage; }
244 
245   // The friend class needs to have access to the internal storage
246   // to be able to build dependency graph, can't declare only one
247   // function a 'friend' due to the incomplete declaration at this point
248   // and mutual dependency problems.
249   friend class SPIRVGeneralDuplicatesTracker;
250 };
251 
252 template <typename T>
253 class SPIRVDuplicatesTracker : public SPIRVDuplicatesTrackerBase<const T *> {};
254 
255 template <>
256 class SPIRVDuplicatesTracker<SPIRV::SpecialTypeDescriptor>
257     : public SPIRVDuplicatesTrackerBase<SPIRV::SpecialTypeDescriptor> {};
258 
259 class SPIRVGeneralDuplicatesTracker {
260   SPIRVDuplicatesTracker<Type> TT;
261   SPIRVDuplicatesTracker<Constant> CT;
262   SPIRVDuplicatesTracker<GlobalVariable> GT;
263   SPIRVDuplicatesTracker<Function> FT;
264   SPIRVDuplicatesTracker<Argument> AT;
265   SPIRVDuplicatesTracker<SPIRV::SpecialTypeDescriptor> ST;
266 
267   // NOTE: using MOs instead of regs to get rid of MF dependency to be able
268   // to use flat data structure.
269   // NOTE: replacing DenseMap with MapVector doesn't affect overall correctness
270   // but makes LITs more stable, should prefer DenseMap still due to
271   // significant perf difference.
272   using SPIRVReg2EntryTy =
273       MapVector<MachineOperand *, SPIRV::DTSortableEntry *>;
274 
275   template <typename T>
276   void prebuildReg2Entry(SPIRVDuplicatesTracker<T> &DT,
277                          SPIRVReg2EntryTy &Reg2Entry);
278 
279 public:
280   void buildDepsGraph(std::vector<SPIRV::DTSortableEntry *> &Graph,
281                       MachineModuleInfo *MMI);
282 
283   void add(const Type *Ty, const MachineFunction *MF, Register R) {
284     TT.add(Ty, MF, R);
285   }
286 
287   void add(const Type *PointerElementType, unsigned AddressSpace,
288            const MachineFunction *MF, Register R) {
289     ST.add(SPIRV::PointerTypeDescriptor(PointerElementType, AddressSpace), MF,
290            R);
291   }
292 
293   void add(const Constant *C, const MachineFunction *MF, Register R) {
294     CT.add(C, MF, R);
295   }
296 
297   void add(const GlobalVariable *GV, const MachineFunction *MF, Register R) {
298     GT.add(GV, MF, R);
299   }
300 
301   void add(const Function *F, const MachineFunction *MF, Register R) {
302     FT.add(F, MF, R);
303   }
304 
305   void add(const Argument *Arg, const MachineFunction *MF, Register R) {
306     AT.add(Arg, MF, R);
307   }
308 
309   void add(const SPIRV::SpecialTypeDescriptor &TD, const MachineFunction *MF,
310            Register R) {
311     ST.add(TD, MF, R);
312   }
313 
314   Register find(const Type *Ty, const MachineFunction *MF) {
315     return TT.find(const_cast<Type *>(Ty), MF);
316   }
317 
318   Register find(const Type *PointerElementType, unsigned AddressSpace,
319                 const MachineFunction *MF) {
320     return ST.find(
321         SPIRV::PointerTypeDescriptor(PointerElementType, AddressSpace), MF);
322   }
323 
324   Register find(const Constant *C, const MachineFunction *MF) {
325     return CT.find(const_cast<Constant *>(C), MF);
326   }
327 
328   Register find(const GlobalVariable *GV, const MachineFunction *MF) {
329     return GT.find(const_cast<GlobalVariable *>(GV), MF);
330   }
331 
332   Register find(const Function *F, const MachineFunction *MF) {
333     return FT.find(const_cast<Function *>(F), MF);
334   }
335 
336   Register find(const Argument *Arg, const MachineFunction *MF) {
337     return AT.find(const_cast<Argument *>(Arg), MF);
338   }
339 
340   Register find(const SPIRV::SpecialTypeDescriptor &TD,
341                 const MachineFunction *MF) {
342     return ST.find(TD, MF);
343   }
344 
345   const SPIRVDuplicatesTracker<Type> *getTypes() { return &TT; }
346 };
347 } // namespace llvm
348 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVDUPLICATESTRACKER_H
349