• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- TypeDetail.h - Details of MLIR LLVM dialect types --------*- 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 // This file contains implementation details, such as storage structures, of
10 // MLIR LLVM dialect types.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef DIALECT_LLVMIR_IR_TYPEDETAIL_H
15 #define DIALECT_LLVMIR_IR_TYPEDETAIL_H
16 
17 #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
18 #include "mlir/IR/TypeSupport.h"
19 #include "mlir/IR/Types.h"
20 
21 #include "llvm/ADT/Bitfields.h"
22 #include "llvm/ADT/PointerIntPair.h"
23 
24 namespace mlir {
25 namespace LLVM {
26 namespace detail {
27 
28 //===----------------------------------------------------------------------===//
29 // LLVMStructTypeStorage.
30 //===----------------------------------------------------------------------===//
31 
32 /// Type storage for LLVM structure types.
33 ///
34 /// Structures are uniqued using:
35 /// - a bit indicating whether a struct is literal or identified;
36 /// - for identified structs, in addition to the bit:
37 ///   - a string identifier;
38 /// - for literal structs, in addition to the bit:
39 ///   - a list of contained types;
40 ///   - a bit indicating whether the literal struct is packed.
41 ///
42 /// Identified structures only have a mutable component consisting of:
43 ///   - a list of contained types;
44 ///   - a bit indicating whether the identified struct is packed;
45 ///   - a bit indicating whether the identified struct is intentionally opaque;
46 ///   - a bit indicating whether the identified struct has been initialized.
47 /// Uninitialized structs are considered opaque by the user, and can be mutated.
48 /// Initialized and still opaque structs cannot be mutated.
49 ///
50 /// The struct storage consists of:
51 ///   - immutable part:
52 ///     - a pointer to the first element of the key (character for identified
53 ///       structs, type for literal structs);
54 ///     - the number of elements in the key packed together with bits indicating
55 ///       whether a type is literal or identified, and the packedness bit for
56 ///       literal structs only;
57 ///   - mutable part:
58 ///     - a pointer to the first contained type for identified structs only;
59 ///     - the number of contained types packed together with bits of the mutable
60 ///       component, for identified structs only.
61 struct LLVMStructTypeStorage : public TypeStorage {
62 public:
63   /// Construction/uniquing key class for LLVM dialect structure storage. Note
64   /// that this is a transient helper data structure that is NOT stored.
65   /// Therefore, it intentionally avoids bit manipulation and type erasure in
66   /// pointers to make manipulation more straightforward. Not all elements of
67   /// the key participate in uniquing, but all elements participate in
68   /// construction.
69   class Key {
70   public:
71     /// Constructs a key for an identified struct.
KeyLLVMStructTypeStorage72     Key(StringRef name, bool opaque)
73         : name(name), identified(true), packed(false), opaque(opaque) {}
74     /// Constructs a key for a literal struct.
KeyLLVMStructTypeStorage75     Key(ArrayRef<LLVMType> types, bool packed)
76         : types(types), identified(false), packed(packed), opaque(false) {}
77 
78     /// Checks a specific property of the struct.
isIdentifiedLLVMStructTypeStorage79     bool isIdentified() const { return identified; }
isPackedLLVMStructTypeStorage80     bool isPacked() const {
81       assert(!isIdentified() &&
82              "'packed' bit is not part of the key for identified structs");
83       return packed;
84     }
isOpaqueLLVMStructTypeStorage85     bool isOpaque() const {
86       assert(isIdentified() &&
87              "'opaque' bit is meaningless on literal structs");
88       return opaque;
89     }
90 
91     /// Returns the identifier of a key for identified structs.
getIdentifierLLVMStructTypeStorage92     StringRef getIdentifier() const {
93       assert(isIdentified() &&
94              "non-identified struct key canont have an identifier");
95       return name;
96     }
97 
98     /// Returns the list of type contained in the key of a literal struct.
getTypeListLLVMStructTypeStorage99     ArrayRef<LLVMType> getTypeList() const {
100       assert(!isIdentified() &&
101              "identified struct key cannot have a type list");
102       return types;
103     }
104 
105     /// Returns the hash value of the key. This combines various flags into a
106     /// single value: the identified flag sets the first bit, and the packedness
107     /// flag sets the second bit. Opacity bit is only used for construction and
108     /// does not participate in uniquing.
hashValueLLVMStructTypeStorage109     llvm::hash_code hashValue() const {
110       constexpr static unsigned kIdentifiedHashFlag = 1;
111       constexpr static unsigned kPackedHashFlag = 2;
112 
113       unsigned flags = 0;
114       if (isIdentified()) {
115         flags |= kIdentifiedHashFlag;
116         return llvm::hash_combine(flags, getIdentifier());
117       }
118       if (isPacked())
119         flags |= kPackedHashFlag;
120       return llvm::hash_combine(flags, getTypeList());
121     }
122 
123     /// Compares two keys.
124     bool operator==(const Key &other) const {
125       if (isIdentified())
126         return other.isIdentified() &&
127                other.getIdentifier().equals(getIdentifier());
128 
129       return !other.isIdentified() && other.isPacked() == isPacked() &&
130              other.getTypeList() == getTypeList();
131     }
132 
133     /// Copies dynamically-sized components of the key into the given allocator.
copyIntoAllocatorLLVMStructTypeStorage134     Key copyIntoAllocator(TypeStorageAllocator &allocator) const {
135       if (isIdentified())
136         return Key(allocator.copyInto(name), opaque);
137       return Key(allocator.copyInto(types), packed);
138     }
139 
140   private:
141     ArrayRef<LLVMType> types;
142     StringRef name;
143     bool identified;
144     bool packed;
145     bool opaque;
146   };
147   using KeyTy = Key;
148 
149   /// Returns the string identifier of an identified struct.
getIdentifierLLVMStructTypeStorage150   StringRef getIdentifier() const {
151     assert(isIdentified() && "requested identifier on a non-identified struct");
152     return StringRef(static_cast<const char *>(keyPtr), keySize());
153   }
154 
155   /// Returns the list of types (partially) identifying a literal struct.
getTypeListLLVMStructTypeStorage156   ArrayRef<LLVMType> getTypeList() const {
157     // If this triggers, use getIdentifiedStructBody() instead.
158     assert(!isIdentified() && "requested typelist on an identified struct");
159     return ArrayRef<LLVMType>(static_cast<const LLVMType *>(keyPtr), keySize());
160   }
161 
162   /// Returns the list of types contained in an identified struct.
getIdentifiedStructBodyLLVMStructTypeStorage163   ArrayRef<LLVMType> getIdentifiedStructBody() const {
164     // If this triggers, use getTypeList() instead.
165     assert(isIdentified() &&
166            "requested struct body on a non-identified struct");
167     return ArrayRef<LLVMType>(identifiedBodyArray, identifiedBodySize());
168   }
169 
170   /// Checks whether the struct is identified.
isIdentifiedLLVMStructTypeStorage171   bool isIdentified() const {
172     return llvm::Bitfield::get<KeyFlagIdentified>(keySizeAndFlags);
173   }
174 
175   /// Checks whether the struct is packed (both literal and identified structs).
isPackedLLVMStructTypeStorage176   bool isPacked() const {
177     return isIdentified() ? llvm::Bitfield::get<MutableFlagPacked>(
178                                 identifiedBodySizeAndFlags)
179                           : llvm::Bitfield::get<KeyFlagPacked>(keySizeAndFlags);
180   }
181 
182   /// Checks whether a struct is marked as intentionally opaque (an
183   /// uninitialized struct is also considered opaque by the user, call
184   /// isInitialized to check that).
isOpaqueLLVMStructTypeStorage185   bool isOpaque() const {
186     return llvm::Bitfield::get<MutableFlagOpaque>(identifiedBodySizeAndFlags);
187   }
188 
189   /// Checks whether an identified struct has been explicitly initialized either
190   /// by setting its body or by marking it as intentionally opaque.
isInitializedLLVMStructTypeStorage191   bool isInitialized() const {
192     return llvm::Bitfield::get<MutableFlagInitialized>(
193         identifiedBodySizeAndFlags);
194   }
195 
196   /// Constructs the storage from the given key. This sets up the uniquing key
197   /// components and optionally the mutable component if they construction key
198   /// has the relevant information. In the latter case, the struct is considered
199   /// as initialized and can no longer be mutated.
LLVMStructTypeStorageLLVMStructTypeStorage200   LLVMStructTypeStorage(const KeyTy &key) {
201     if (!key.isIdentified()) {
202       ArrayRef<LLVMType> types = key.getTypeList();
203       keyPtr = static_cast<const void *>(types.data());
204       setKeySize(types.size());
205       llvm::Bitfield::set<KeyFlagPacked>(keySizeAndFlags, key.isPacked());
206       return;
207     }
208 
209     StringRef name = key.getIdentifier();
210     keyPtr = static_cast<const void *>(name.data());
211     setKeySize(name.size());
212     llvm::Bitfield::set<KeyFlagIdentified>(keySizeAndFlags, true);
213 
214     // If the struct is being constructed directly as opaque, mark it as
215     // initialized.
216     llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags,
217                                                 key.isOpaque());
218     llvm::Bitfield::set<MutableFlagOpaque>(identifiedBodySizeAndFlags,
219                                            key.isOpaque());
220   }
221 
222   /// Hook into the type unquing infrastructure.
223   bool operator==(const KeyTy &other) const { return getKey() == other; };
hashKeyLLVMStructTypeStorage224   static llvm::hash_code hashKey(const KeyTy &key) { return key.hashValue(); }
constructLLVMStructTypeStorage225   static LLVMStructTypeStorage *construct(TypeStorageAllocator &allocator,
226                                           const KeyTy &key) {
227     return new (allocator.allocate<LLVMStructTypeStorage>())
228         LLVMStructTypeStorage(key.copyIntoAllocator(allocator));
229   }
230 
231   /// Sets the body of an identified struct. If the struct is already
232   /// initialized, succeeds only if the body is equal to the current body. Fails
233   /// if the struct is marked as intentionally opaque. The struct will be marked
234   /// as initialized as a result of this operation and can no longer be changed.
mutateLLVMStructTypeStorage235   LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef<LLVMType> body,
236                        bool packed) {
237     if (!isIdentified())
238       return failure();
239     if (isInitialized())
240       return success(!isOpaque() && body == getIdentifiedStructBody() &&
241                      packed == isPacked());
242 
243     llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags,
244                                                 true);
245     llvm::Bitfield::set<MutableFlagPacked>(identifiedBodySizeAndFlags, packed);
246 
247     ArrayRef<LLVMType> typesInAllocator = allocator.copyInto(body);
248     identifiedBodyArray = typesInAllocator.data();
249     setIdentifiedBodySize(typesInAllocator.size());
250 
251     return success();
252   }
253 
254 private:
255   /// Returns the number of elements in the key.
keySizeLLVMStructTypeStorage256   unsigned keySize() const {
257     return llvm::Bitfield::get<KeySize>(keySizeAndFlags);
258   }
259 
260   /// Sets the number of elements in the key.
setKeySizeLLVMStructTypeStorage261   void setKeySize(unsigned value) {
262     llvm::Bitfield::set<KeySize>(keySizeAndFlags, value);
263   }
264 
265   /// Returns the number of types contained in an identified struct.
identifiedBodySizeLLVMStructTypeStorage266   unsigned identifiedBodySize() const {
267     return llvm::Bitfield::get<MutableSize>(identifiedBodySizeAndFlags);
268   }
269   /// Sets the number of types contained in an identified struct.
setIdentifiedBodySizeLLVMStructTypeStorage270   void setIdentifiedBodySize(unsigned value) {
271     llvm::Bitfield::set<MutableSize>(identifiedBodySizeAndFlags, value);
272   }
273 
274   /// Returns the key for the current storage.
getKeyLLVMStructTypeStorage275   Key getKey() const {
276     if (isIdentified())
277       return Key(getIdentifier(), isOpaque());
278     return Key(getTypeList(), isPacked());
279   }
280 
281   /// Bitfield elements for `keyAndSizeFlags`:
282   ///   - bit 0: identified key flag;
283   ///   - bit 1: packed key flag;
284   ///   - bits 2..bitwidth(unsigned): size of the key.
285   using KeyFlagIdentified =
286       llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>;
287   using KeyFlagPacked = llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>;
288   using KeySize =
289       llvm::Bitfield::Element<unsigned, /*Offset=*/2,
290                               std::numeric_limits<unsigned>::digits - 2>;
291 
292   /// Bitfield elements for `identifiedBodySizeAndFlags`:
293   ///   - bit 0: opaque flag;
294   ///   - bit 1: packed mutable flag;
295   ///   - bit 2: initialized flag;
296   ///   - bits 3..bitwidth(unsigned): size of the identified body.
297   using MutableFlagOpaque =
298       llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>;
299   using MutableFlagPacked =
300       llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>;
301   using MutableFlagInitialized =
302       llvm::Bitfield::Element<bool, /*Offset=*/2, /*Size=*/1>;
303   using MutableSize =
304       llvm::Bitfield::Element<unsigned, /*Offset=*/3,
305                               std::numeric_limits<unsigned>::digits - 3>;
306 
307   /// Pointer to the first element of the uniquing key.
308   // Note: cannot use PointerUnion because bump-ptr allocator does not guarantee
309   // address alignment.
310   const void *keyPtr = nullptr;
311 
312   /// Pointer to the first type contained in an identified struct.
313   const LLVMType *identifiedBodyArray = nullptr;
314 
315   /// Size of the uniquing key combined with identified/literal and
316   /// packedness bits. Must only be used through the Key* bitfields.
317   unsigned keySizeAndFlags = 0;
318 
319   /// Number of the types contained in an identified struct combined with
320   /// mutable flags. Must only be used through the Mutable* bitfields.
321   unsigned identifiedBodySizeAndFlags = 0;
322 };
323 
324 //===----------------------------------------------------------------------===//
325 // LLVMFunctionTypeStorage.
326 //===----------------------------------------------------------------------===//
327 
328 /// Type storage for LLVM dialect function types. These are uniqued using the
329 /// list of types they contain and the vararg bit.
330 struct LLVMFunctionTypeStorage : public TypeStorage {
331   using KeyTy = std::tuple<LLVMType, ArrayRef<LLVMType>, bool>;
332 
333   /// Construct a storage from the given components. The list is expected to be
334   /// allocated in the context.
LLVMFunctionTypeStorageLLVMFunctionTypeStorage335   LLVMFunctionTypeStorage(LLVMType result, ArrayRef<LLVMType> arguments,
336                           bool variadic)
337       : argumentTypes(arguments) {
338     returnTypeAndVariadic.setPointerAndInt(result, variadic);
339   }
340 
341   /// Hook into the type uniquing infrastructure.
constructLLVMFunctionTypeStorage342   static LLVMFunctionTypeStorage *construct(TypeStorageAllocator &allocator,
343                                             const KeyTy &key) {
344     return new (allocator.allocate<LLVMFunctionTypeStorage>())
345         LLVMFunctionTypeStorage(std::get<0>(key),
346                                 allocator.copyInto(std::get<1>(key)),
347                                 std::get<2>(key));
348   }
349 
hashKeyLLVMFunctionTypeStorage350   static unsigned hashKey(const KeyTy &key) {
351     // LLVM doesn't like hashing bools in tuples.
352     return llvm::hash_combine(std::get<0>(key), std::get<1>(key),
353                               static_cast<int>(std::get<2>(key)));
354   }
355 
356   bool operator==(const KeyTy &key) const {
357     return std::make_tuple(getReturnType(), getArgumentTypes(), isVariadic()) ==
358            key;
359   }
360 
361   /// Returns the list of function argument types.
getArgumentTypesLLVMFunctionTypeStorage362   ArrayRef<LLVMType> getArgumentTypes() const { return argumentTypes; }
363 
364   /// Checks whether the function type is variadic.
isVariadicLLVMFunctionTypeStorage365   bool isVariadic() const { return returnTypeAndVariadic.getInt(); }
366 
367   /// Returns the function result type.
getReturnTypeLLVMFunctionTypeStorage368   LLVMType getReturnType() const { return returnTypeAndVariadic.getPointer(); }
369 
370 private:
371   /// Function result type packed with the variadic bit.
372   llvm::PointerIntPair<LLVMType, 1, bool> returnTypeAndVariadic;
373   /// Argument types.
374   ArrayRef<LLVMType> argumentTypes;
375 };
376 
377 //===----------------------------------------------------------------------===//
378 // LLVMIntegerTypeStorage.
379 //===----------------------------------------------------------------------===//
380 
381 /// Storage type for LLVM dialect integer types. These are uniqued by bitwidth.
382 struct LLVMIntegerTypeStorage : public TypeStorage {
383   using KeyTy = unsigned;
384 
LLVMIntegerTypeStorageLLVMIntegerTypeStorage385   LLVMIntegerTypeStorage(unsigned width) : bitwidth(width) {}
386 
constructLLVMIntegerTypeStorage387   static LLVMIntegerTypeStorage *construct(TypeStorageAllocator &allocator,
388                                            const KeyTy &key) {
389     return new (allocator.allocate<LLVMIntegerTypeStorage>())
390         LLVMIntegerTypeStorage(key);
391   }
392 
393   bool operator==(const KeyTy &key) const { return key == bitwidth; }
394 
395   unsigned bitwidth;
396 };
397 
398 //===----------------------------------------------------------------------===//
399 // LLVMPointerTypeStorage.
400 //===----------------------------------------------------------------------===//
401 
402 /// Storage type for LLVM dialect pointer types. These are uniqued by a pair of
403 /// element type and address space.
404 struct LLVMPointerTypeStorage : public TypeStorage {
405   using KeyTy = std::tuple<LLVMType, unsigned>;
406 
LLVMPointerTypeStorageLLVMPointerTypeStorage407   LLVMPointerTypeStorage(const KeyTy &key)
408       : pointeeType(std::get<0>(key)), addressSpace(std::get<1>(key)) {}
409 
constructLLVMPointerTypeStorage410   static LLVMPointerTypeStorage *construct(TypeStorageAllocator &allocator,
411                                            const KeyTy &key) {
412     return new (allocator.allocate<LLVMPointerTypeStorage>())
413         LLVMPointerTypeStorage(key);
414   }
415 
416   bool operator==(const KeyTy &key) const {
417     return std::make_tuple(pointeeType, addressSpace) == key;
418   }
419 
420   LLVMType pointeeType;
421   unsigned addressSpace;
422 };
423 
424 //===----------------------------------------------------------------------===//
425 // LLVMTypeAndSizeStorage.
426 //===----------------------------------------------------------------------===//
427 
428 /// Common storage used for LLVM dialect types that need an element type and a
429 /// number: arrays, fixed and scalable vectors. The actual semantics of the
430 /// type is defined by its kind.
431 struct LLVMTypeAndSizeStorage : public TypeStorage {
432   using KeyTy = std::tuple<LLVMType, unsigned>;
433 
LLVMTypeAndSizeStorageLLVMTypeAndSizeStorage434   LLVMTypeAndSizeStorage(const KeyTy &key)
435       : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {}
436 
constructLLVMTypeAndSizeStorage437   static LLVMTypeAndSizeStorage *construct(TypeStorageAllocator &allocator,
438                                            const KeyTy &key) {
439     return new (allocator.allocate<LLVMTypeAndSizeStorage>())
440         LLVMTypeAndSizeStorage(key);
441   }
442 
443   bool operator==(const KeyTy &key) const {
444     return std::make_tuple(elementType, numElements) == key;
445   }
446 
447   LLVMType elementType;
448   unsigned numElements;
449 };
450 
451 } // end namespace detail
452 } // end namespace LLVM
453 } // end namespace mlir
454 
455 #endif // DIALECT_LLVMIR_IR_TYPEDETAIL_H
456