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<Type> 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 cannot have an identifier");
95       return name;
96     }
97 
98     /// Returns the list of type contained in the key of a literal struct.
getTypeListLLVMStructTypeStorage99     ArrayRef<Type> 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<Type> 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<Type> getTypeList() const {
157     // If this triggers, use getIdentifiedStructBody() instead.
158     assert(!isIdentified() && "requested typelist on an identified struct");
159     return ArrayRef<Type>(static_cast<const Type *>(keyPtr), keySize());
160   }
161 
162   /// Returns the list of types contained in an identified struct.
getIdentifiedStructBodyLLVMStructTypeStorage163   ArrayRef<Type> getIdentifiedStructBody() const {
164     // If this triggers, use getTypeList() instead.
165     assert(isIdentified() &&
166            "requested struct body on a non-identified struct");
167     return ArrayRef<Type>(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<Type> 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 uniquing 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<Type> 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<Type> 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 Type *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<Type, ArrayRef<Type>, bool>;
332 
333   /// Construct a storage from the given components. The list is expected to be
334   /// allocated in the context.
LLVMFunctionTypeStorageLLVMFunctionTypeStorage335   LLVMFunctionTypeStorage(Type result, ArrayRef<Type> arguments, bool variadic)
336       : resultType(result), isVariadicFlag(variadic),
337         numArguments(arguments.size()), argumentTypes(arguments.data()) {}
338 
339   /// Hook into the type uniquing infrastructure.
constructLLVMFunctionTypeStorage340   static LLVMFunctionTypeStorage *construct(TypeStorageAllocator &allocator,
341                                             const KeyTy &key) {
342     return new (allocator.allocate<LLVMFunctionTypeStorage>())
343         LLVMFunctionTypeStorage(std::get<0>(key),
344                                 allocator.copyInto(std::get<1>(key)),
345                                 std::get<2>(key));
346   }
347 
hashKeyLLVMFunctionTypeStorage348   static unsigned hashKey(const KeyTy &key) {
349     // LLVM doesn't like hashing bools in tuples.
350     return llvm::hash_combine(std::get<0>(key), std::get<1>(key),
351                               static_cast<int>(std::get<2>(key)));
352   }
353 
354   bool operator==(const KeyTy &key) const {
355     return std::make_tuple(getReturnType(), getArgumentTypes(), isVariadic()) ==
356            key;
357   }
358 
359   /// Returns the list of function argument types.
getArgumentTypesLLVMFunctionTypeStorage360   ArrayRef<Type> getArgumentTypes() const {
361     return ArrayRef<Type>(argumentTypes, numArguments);
362   }
363 
364   /// Checks whether the function type is variadic.
isVariadicLLVMFunctionTypeStorage365   bool isVariadic() const { return isVariadicFlag; }
366 
367   /// Returns the function result type.
getReturnTypeLLVMFunctionTypeStorage368   const Type &getReturnType() const { return resultType; }
369 
370 private:
371   /// The result type of the function.
372   Type resultType;
373   /// Flag indicating if the function is variadic.
374   bool isVariadicFlag;
375   /// The argument types of the function.
376   unsigned numArguments;
377   const Type *argumentTypes;
378 };
379 
380 //===----------------------------------------------------------------------===//
381 // LLVMPointerTypeStorage.
382 //===----------------------------------------------------------------------===//
383 
384 /// Storage type for LLVM dialect pointer types. These are uniqued by a pair of
385 /// element type and address space. The element type may be null indicating that
386 /// the pointer is opaque.
387 struct LLVMPointerTypeStorage : public TypeStorage {
388   using KeyTy = std::tuple<Type, unsigned>;
389 
LLVMPointerTypeStorageLLVMPointerTypeStorage390   LLVMPointerTypeStorage(const KeyTy &key)
391       : pointeeType(std::get<0>(key)), addressSpace(std::get<1>(key)) {}
392 
constructLLVMPointerTypeStorage393   static LLVMPointerTypeStorage *construct(TypeStorageAllocator &allocator,
394                                            const KeyTy &key) {
395     return new (allocator.allocate<LLVMPointerTypeStorage>())
396         LLVMPointerTypeStorage(key);
397   }
398 
399   bool operator==(const KeyTy &key) const {
400     return std::make_tuple(pointeeType, addressSpace) == key;
401   }
402 
403   Type pointeeType;
404   unsigned addressSpace;
405 };
406 
407 //===----------------------------------------------------------------------===//
408 // LLVMTypeAndSizeStorage.
409 //===----------------------------------------------------------------------===//
410 
411 /// Common storage used for LLVM dialect types that need an element type and a
412 /// number: arrays, fixed and scalable vectors. The actual semantics of the
413 /// type is defined by its kind.
414 struct LLVMTypeAndSizeStorage : public TypeStorage {
415   using KeyTy = std::tuple<Type, unsigned>;
416 
LLVMTypeAndSizeStorageLLVMTypeAndSizeStorage417   LLVMTypeAndSizeStorage(const KeyTy &key)
418       : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {}
419 
constructLLVMTypeAndSizeStorage420   static LLVMTypeAndSizeStorage *construct(TypeStorageAllocator &allocator,
421                                            const KeyTy &key) {
422     return new (allocator.allocate<LLVMTypeAndSizeStorage>())
423         LLVMTypeAndSizeStorage(key);
424   }
425 
426   bool operator==(const KeyTy &key) const {
427     return std::make_tuple(elementType, numElements) == key;
428   }
429 
430   Type elementType;
431   unsigned numElements;
432 };
433 
434 } // namespace detail
435 } // namespace LLVM
436 } // namespace mlir
437 
438 #endif // DIALECT_LLVMIR_IR_TYPEDETAIL_H
439