xref: /llvm-project/mlir/lib/Dialect/LLVMIR/IR/TypeDetail.h (revision dec8055a1e71fe25d4b85416ede742e8fdfaf3f0)
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.
72     Key(StringRef name, bool opaque, ArrayRef<Type> types = std::nullopt)
typesLLVMStructTypeStorage73         : types(types), name(name), identified(true), packed(false),
74           opaque(opaque) {}
75     /// Constructs a key for a literal struct.
KeyLLVMStructTypeStorage76     Key(ArrayRef<Type> types, bool packed)
77         : types(types), identified(false), packed(packed), opaque(false) {}
78 
79     /// Checks a specific property of the struct.
isIdentifiedLLVMStructTypeStorage80     bool isIdentified() const { return identified; }
isPackedLLVMStructTypeStorage81     bool isPacked() const {
82       assert(!isIdentified() &&
83              "'packed' bit is not part of the key for identified structs");
84       return packed;
85     }
isOpaqueLLVMStructTypeStorage86     bool isOpaque() const {
87       assert(isIdentified() &&
88              "'opaque' bit is meaningless on literal structs");
89       return opaque;
90     }
91 
92     /// Returns the identifier of a key for identified structs.
getIdentifierLLVMStructTypeStorage93     StringRef getIdentifier() const {
94       assert(isIdentified() &&
95              "non-identified struct key cannot have an identifier");
96       return name;
97     }
98 
99     /// Returns the list of type contained in the key of a literal struct.
getTypeListLLVMStructTypeStorage100     ArrayRef<Type> getTypeList() const {
101       assert(!isIdentified() &&
102              "identified struct key cannot have a type list");
103       return types;
104     }
105 
106     /// Returns the list of type contained in an identified struct.
getIdentifiedStructBodyLLVMStructTypeStorage107     ArrayRef<Type> getIdentifiedStructBody() const {
108       assert(isIdentified() &&
109              "requested struct body on a non-identified struct");
110       return types;
111     }
112 
113     /// Returns the hash value of the key. This combines various flags into a
114     /// single value: the identified flag sets the first bit, and the packedness
115     /// flag sets the second bit. Opacity bit is only used for construction and
116     /// does not participate in uniquing.
hashValueLLVMStructTypeStorage117     llvm::hash_code hashValue() const {
118       constexpr static unsigned kIdentifiedHashFlag = 1;
119       constexpr static unsigned kPackedHashFlag = 2;
120 
121       unsigned flags = 0;
122       if (isIdentified()) {
123         flags |= kIdentifiedHashFlag;
124         return llvm::hash_combine(flags, getIdentifier());
125       }
126       if (isPacked())
127         flags |= kPackedHashFlag;
128       return llvm::hash_combine(flags, getTypeList());
129     }
130 
131     /// Compares two keys.
132     bool operator==(const Key &other) const {
133       if (isIdentified())
134         return other.isIdentified() && other.getIdentifier() == getIdentifier();
135 
136       return !other.isIdentified() && other.isPacked() == isPacked() &&
137              other.getTypeList() == getTypeList();
138     }
139 
140     /// Copies dynamically-sized components of the key into the given allocator.
copyIntoAllocatorLLVMStructTypeStorage141     Key copyIntoAllocator(TypeStorageAllocator &allocator) const {
142       if (isIdentified())
143         return Key(allocator.copyInto(name), opaque);
144       return Key(allocator.copyInto(types), packed);
145     }
146 
147   private:
148     ArrayRef<Type> types;
149     StringRef name;
150     bool identified;
151     bool packed;
152     bool opaque;
153   };
154   using KeyTy = Key;
155 
156   /// Returns the string identifier of an identified struct.
getIdentifierLLVMStructTypeStorage157   StringRef getIdentifier() const {
158     assert(isIdentified() && "requested identifier on a non-identified struct");
159     return StringRef(static_cast<const char *>(keyPtr), keySize());
160   }
161 
162   /// Returns the list of types (partially) identifying a literal struct.
getTypeListLLVMStructTypeStorage163   ArrayRef<Type> getTypeList() const {
164     // If this triggers, use getIdentifiedStructBody() instead.
165     assert(!isIdentified() && "requested typelist on an identified struct");
166     return ArrayRef<Type>(static_cast<const Type *>(keyPtr), keySize());
167   }
168 
169   /// Returns the list of types contained in an identified struct.
getIdentifiedStructBodyLLVMStructTypeStorage170   ArrayRef<Type> getIdentifiedStructBody() const {
171     // If this triggers, use getTypeList() instead.
172     assert(isIdentified() &&
173            "requested struct body on a non-identified struct");
174     return ArrayRef<Type>(identifiedBodyArray, identifiedBodySize());
175   }
176 
177   /// Checks whether the struct is identified.
isIdentifiedLLVMStructTypeStorage178   bool isIdentified() const {
179     return llvm::Bitfield::get<KeyFlagIdentified>(keySizeAndFlags);
180   }
181 
182   /// Checks whether the struct is packed (both literal and identified structs).
isPackedLLVMStructTypeStorage183   bool isPacked() const {
184     return isIdentified() ? llvm::Bitfield::get<MutableFlagPacked>(
185                                 identifiedBodySizeAndFlags)
186                           : llvm::Bitfield::get<KeyFlagPacked>(keySizeAndFlags);
187   }
188 
189   /// Checks whether a struct is marked as intentionally opaque (an
190   /// uninitialized struct is also considered opaque by the user, call
191   /// isInitialized to check that).
isOpaqueLLVMStructTypeStorage192   bool isOpaque() const {
193     return llvm::Bitfield::get<MutableFlagOpaque>(identifiedBodySizeAndFlags);
194   }
195 
196   /// Checks whether an identified struct has been explicitly initialized either
197   /// by setting its body or by marking it as intentionally opaque.
isInitializedLLVMStructTypeStorage198   bool isInitialized() const {
199     return llvm::Bitfield::get<MutableFlagInitialized>(
200         identifiedBodySizeAndFlags);
201   }
202 
203   /// Constructs the storage from the given key. This sets up the uniquing key
204   /// components and optionally the mutable component if they construction key
205   /// has the relevant information. In the latter case, the struct is considered
206   /// as initialized and can no longer be mutated.
LLVMStructTypeStorageLLVMStructTypeStorage207   LLVMStructTypeStorage(const KeyTy &key) {
208     if (!key.isIdentified()) {
209       ArrayRef<Type> types = key.getTypeList();
210       keyPtr = static_cast<const void *>(types.data());
211       setKeySize(types.size());
212       llvm::Bitfield::set<KeyFlagPacked>(keySizeAndFlags, key.isPacked());
213       return;
214     }
215 
216     StringRef name = key.getIdentifier();
217     keyPtr = static_cast<const void *>(name.data());
218     setKeySize(name.size());
219     llvm::Bitfield::set<KeyFlagIdentified>(keySizeAndFlags, true);
220 
221     // If the struct is being constructed directly as opaque, mark it as
222     // initialized.
223     llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags,
224                                                 key.isOpaque());
225     llvm::Bitfield::set<MutableFlagOpaque>(identifiedBodySizeAndFlags,
226                                            key.isOpaque());
227   }
228 
229   /// Hook into the type uniquing infrastructure.
230   bool operator==(const KeyTy &other) const { return getAsKey() == other; };
hashKeyLLVMStructTypeStorage231   static llvm::hash_code hashKey(const KeyTy &key) { return key.hashValue(); }
constructLLVMStructTypeStorage232   static LLVMStructTypeStorage *construct(TypeStorageAllocator &allocator,
233                                           const KeyTy &key) {
234     return new (allocator.allocate<LLVMStructTypeStorage>())
235         LLVMStructTypeStorage(key.copyIntoAllocator(allocator));
236   }
237 
238   /// Sets the body of an identified struct. If the struct is already
239   /// initialized, succeeds only if the body is equal to the current body. Fails
240   /// if the struct is marked as intentionally opaque. The struct will be marked
241   /// as initialized as a result of this operation and can no longer be changed.
mutateLLVMStructTypeStorage242   LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef<Type> body,
243                        bool packed) {
244     if (!isIdentified())
245       return failure();
246     if (isInitialized())
247       return success(!isOpaque() && body == getIdentifiedStructBody() &&
248                      packed == isPacked());
249 
250     llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags,
251                                                 true);
252     llvm::Bitfield::set<MutableFlagPacked>(identifiedBodySizeAndFlags, packed);
253 
254     ArrayRef<Type> typesInAllocator = allocator.copyInto(body);
255     identifiedBodyArray = typesInAllocator.data();
256     setIdentifiedBodySize(typesInAllocator.size());
257 
258     return success();
259   }
260 
261   /// Returns the key for the current storage.
getAsKeyLLVMStructTypeStorage262   Key getAsKey() const {
263     if (isIdentified())
264       return Key(getIdentifier(), isOpaque(), getIdentifiedStructBody());
265     return Key(getTypeList(), isPacked());
266   }
267 
268 private:
269   /// Returns the number of elements in the key.
keySizeLLVMStructTypeStorage270   unsigned keySize() const {
271     return llvm::Bitfield::get<KeySize>(keySizeAndFlags);
272   }
273 
274   /// Sets the number of elements in the key.
setKeySizeLLVMStructTypeStorage275   void setKeySize(unsigned value) {
276     llvm::Bitfield::set<KeySize>(keySizeAndFlags, value);
277   }
278 
279   /// Returns the number of types contained in an identified struct.
identifiedBodySizeLLVMStructTypeStorage280   unsigned identifiedBodySize() const {
281     return llvm::Bitfield::get<MutableSize>(identifiedBodySizeAndFlags);
282   }
283   /// Sets the number of types contained in an identified struct.
setIdentifiedBodySizeLLVMStructTypeStorage284   void setIdentifiedBodySize(unsigned value) {
285     llvm::Bitfield::set<MutableSize>(identifiedBodySizeAndFlags, value);
286   }
287 
288   /// Bitfield elements for `keyAndSizeFlags`:
289   ///   - bit 0: identified key flag;
290   ///   - bit 1: packed key flag;
291   ///   - bits 2..bitwidth(unsigned): size of the key.
292   using KeyFlagIdentified =
293       llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>;
294   using KeyFlagPacked = llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>;
295   using KeySize =
296       llvm::Bitfield::Element<unsigned, /*Offset=*/2,
297                               std::numeric_limits<unsigned>::digits - 2>;
298 
299   /// Bitfield elements for `identifiedBodySizeAndFlags`:
300   ///   - bit 0: opaque flag;
301   ///   - bit 1: packed mutable flag;
302   ///   - bit 2: initialized flag;
303   ///   - bits 3..bitwidth(unsigned): size of the identified body.
304   using MutableFlagOpaque =
305       llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>;
306   using MutableFlagPacked =
307       llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>;
308   using MutableFlagInitialized =
309       llvm::Bitfield::Element<bool, /*Offset=*/2, /*Size=*/1>;
310   using MutableSize =
311       llvm::Bitfield::Element<unsigned, /*Offset=*/3,
312                               std::numeric_limits<unsigned>::digits - 3>;
313 
314   /// Pointer to the first element of the uniquing key.
315   // Note: cannot use PointerUnion because bump-ptr allocator does not guarantee
316   // address alignment.
317   const void *keyPtr = nullptr;
318 
319   /// Pointer to the first type contained in an identified struct.
320   const Type *identifiedBodyArray = nullptr;
321 
322   /// Size of the uniquing key combined with identified/literal and
323   /// packedness bits. Must only be used through the Key* bitfields.
324   unsigned keySizeAndFlags = 0;
325 
326   /// Number of the types contained in an identified struct combined with
327   /// mutable flags. Must only be used through the Mutable* bitfields.
328   unsigned identifiedBodySizeAndFlags = 0;
329 };
330 } // end namespace detail
331 } // end namespace LLVM
332 
333 /// Allow walking and replacing the subelements of a LLVMStructTypeStorage key.
334 template <>
335 struct AttrTypeSubElementHandler<LLVM::detail::LLVMStructTypeStorage::Key> {
336   static void walk(const LLVM::detail::LLVMStructTypeStorage::Key &param,
337                    AttrTypeImmediateSubElementWalker &walker) {
338     if (param.isIdentified())
339       walker.walkRange(param.getIdentifiedStructBody());
340     else
341       walker.walkRange(param.getTypeList());
342   }
343   static FailureOr<LLVM::detail::LLVMStructTypeStorage::Key>
344   replace(const LLVM::detail::LLVMStructTypeStorage::Key &param,
345           AttrSubElementReplacements &attrRepls,
346           TypeSubElementReplacements &typeRepls) {
347     // TODO: It's not clear how we support replacing sub-elements of mutable
348     // types.
349     if (param.isIdentified())
350       return failure();
351 
352     return LLVM::detail::LLVMStructTypeStorage::Key(
353         typeRepls.take_front(param.getTypeList().size()), param.isPacked());
354   }
355 };
356 
357 namespace LLVM {
358 namespace detail {
359 //===----------------------------------------------------------------------===//
360 // LLVMTypeAndSizeStorage.
361 //===----------------------------------------------------------------------===//
362 
363 /// Common storage used for LLVM dialect types that need an element type and a
364 /// number: arrays, fixed and scalable vectors. The actual semantics of the
365 /// type is defined by its kind.
366 struct LLVMTypeAndSizeStorage : public TypeStorage {
367   using KeyTy = std::tuple<Type, unsigned>;
368 
369   LLVMTypeAndSizeStorage(const KeyTy &key)
370       : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {}
371 
372   static LLVMTypeAndSizeStorage *construct(TypeStorageAllocator &allocator,
373                                            const KeyTy &key) {
374     return new (allocator.allocate<LLVMTypeAndSizeStorage>())
375         LLVMTypeAndSizeStorage(key);
376   }
377 
378   bool operator==(const KeyTy &key) const {
379     return std::make_tuple(elementType, numElements) == key;
380   }
381 
382   Type elementType;
383   unsigned numElements;
384 };
385 
386 } // namespace detail
387 } // namespace LLVM
388 } // namespace mlir
389 
390 #endif // DIALECT_LLVMIR_IR_TYPEDETAIL_H
391