MLIR  20.0.0git
TypeDetail.h
Go to the documentation of this file.
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 
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.
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)
73  : types(types), name(name), identified(true), packed(false),
74  opaque(opaque) {}
75  /// Constructs a key for a literal struct.
76  Key(ArrayRef<Type> types, bool packed)
77  : types(types), identified(false), packed(packed), opaque(false) {}
78 
79  /// Checks a specific property of the struct.
80  bool isIdentified() const { return identified; }
81  bool isPacked() const {
82  assert(!isIdentified() &&
83  "'packed' bit is not part of the key for identified structs");
84  return packed;
85  }
86  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.
93  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.
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.
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.
117  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.
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.
157  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.
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.
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.
178  bool isIdentified() const {
179  return llvm::Bitfield::get<KeyFlagIdentified>(keySizeAndFlags);
180  }
181 
182  /// Checks whether the struct is packed (both literal and identified structs).
183  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).
192  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.
198  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.
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; };
231  static llvm::hash_code hashKey(const KeyTy &key) { return key.hashValue(); }
233  const KeyTy &key) {
234  return new (allocator.allocate<LLVMStructTypeStorage>())
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.
242  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.
262  Key getAsKey() const {
263  if (isIdentified())
265  return Key(getTypeList(), isPacked());
266  }
267 
268 private:
269  /// Returns the number of elements in the key.
270  unsigned keySize() const {
271  return llvm::Bitfield::get<KeySize>(keySizeAndFlags);
272  }
273 
274  /// Sets the number of elements in the key.
275  void setKeySize(unsigned value) {
276  llvm::Bitfield::set<KeySize>(keySizeAndFlags, value);
277  }
278 
279  /// Returns the number of types contained in an identified struct.
280  unsigned identifiedBodySize() const {
281  return llvm::Bitfield::get<MutableSize>(identifiedBodySizeAndFlags);
282  }
283  /// Sets the number of types contained in an identified struct.
284  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> {
338  if (param.isIdentified())
339  walker.walkRange(param.getIdentifiedStructBody());
340  else
341  walker.walkRange(param.getTypeList());
342  }
343  static FailureOr<LLVM::detail::LLVMStructTypeStorage::Key>
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 
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.
367  using KeyTy = std::tuple<Type, unsigned>;
368 
370  : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {}
371 
373  const KeyTy &key) {
374  return new (allocator.allocate<LLVMTypeAndSizeStorage>())
376  }
377 
378  bool operator==(const KeyTy &key) const {
379  return std::make_tuple(elementType, numElements) == key;
380  }
381 
383  unsigned numElements;
384 };
385 
386 } // namespace detail
387 } // namespace LLVM
388 } // namespace mlir
389 
390 #endif // DIALECT_LLVMIR_IR_TYPEDETAIL_H
void walkRange(RangeT &&elements)
Walk a range of attributes or types.
This class is used by AttrTypeSubElementHandler instances to process sub element replacements.
ArrayRef< T > take_front(unsigned n)
Take the first N replacements as an ArrayRef, dropping them from this replacement list.
Construction/uniquing key class for LLVM dialect structure storage.
Definition: TypeDetail.h:69
Key(StringRef name, bool opaque, ArrayRef< Type > types=std::nullopt)
Constructs a key for an identified struct.
Definition: TypeDetail.h:72
bool isIdentified() const
Checks a specific property of the struct.
Definition: TypeDetail.h:80
llvm::hash_code hashValue() const
Returns the hash value of the key.
Definition: TypeDetail.h:117
StringRef getIdentifier() const
Returns the identifier of a key for identified structs.
Definition: TypeDetail.h:93
bool operator==(const Key &other) const
Compares two keys.
Definition: TypeDetail.h:132
ArrayRef< Type > getIdentifiedStructBody() const
Returns the list of type contained in an identified struct.
Definition: TypeDetail.h:107
Key copyIntoAllocator(TypeStorageAllocator &allocator) const
Copies dynamically-sized components of the key into the given allocator.
Definition: TypeDetail.h:141
ArrayRef< Type > getTypeList() const
Returns the list of type contained in the key of a literal struct.
Definition: TypeDetail.h:100
Key(ArrayRef< Type > types, bool packed)
Constructs a key for a literal struct.
Definition: TypeDetail.h:76
This is a utility allocator used to allocate memory for instances of derived types.
ArrayRef< T > copyInto(ArrayRef< T > elements)
Copy the specified array of elements into memory managed by our bump pointer allocator.
T * allocate()
Allocate an instance of the provided type.
Base storage class appearing in a Type.
Definition: TypeSupport.h:166
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
@ Type
An inlay hint that for a type annotation.
Include the generated interface declarations.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
static void walk(const LLVM::detail::LLVMStructTypeStorage::Key &param, AttrTypeImmediateSubElementWalker &walker)
Definition: TypeDetail.h:336
static FailureOr< LLVM::detail::LLVMStructTypeStorage::Key > replace(const LLVM::detail::LLVMStructTypeStorage::Key &param, AttrSubElementReplacements &attrRepls, TypeSubElementReplacements &typeRepls)
Definition: TypeDetail.h:344
This class provides support for interacting with the SubElementInterfaces for different types of para...
Type storage for LLVM structure types.
Definition: TypeDetail.h:61
ArrayRef< Type > getTypeList() const
Returns the list of types (partially) identifying a literal struct.
Definition: TypeDetail.h:163
StringRef getIdentifier() const
Returns the string identifier of an identified struct.
Definition: TypeDetail.h:157
ArrayRef< Type > getIdentifiedStructBody() const
Returns the list of types contained in an identified struct.
Definition: TypeDetail.h:170
LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef< Type > body, bool packed)
Sets the body of an identified struct.
Definition: TypeDetail.h:242
static LLVMStructTypeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)
Definition: TypeDetail.h:232
static llvm::hash_code hashKey(const KeyTy &key)
Definition: TypeDetail.h:231
Key getAsKey() const
Returns the key for the current storage.
Definition: TypeDetail.h:262
bool operator==(const KeyTy &other) const
Hook into the type uniquing infrastructure.
Definition: TypeDetail.h:230
LLVMStructTypeStorage(const KeyTy &key)
Constructs the storage from the given key.
Definition: TypeDetail.h:207
bool isIdentified() const
Checks whether the struct is identified.
Definition: TypeDetail.h:178
bool isInitialized() const
Checks whether an identified struct has been explicitly initialized either by setting its body or by ...
Definition: TypeDetail.h:198
bool isOpaque() const
Checks whether a struct is marked as intentionally opaque (an uninitialized struct is also considered...
Definition: TypeDetail.h:192
bool isPacked() const
Checks whether the struct is packed (both literal and identified structs).
Definition: TypeDetail.h:183
Common storage used for LLVM dialect types that need an element type and a number: arrays,...
Definition: TypeDetail.h:366
std::tuple< Type, unsigned > KeyTy
Definition: TypeDetail.h:367
static LLVMTypeAndSizeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)
Definition: TypeDetail.h:372
bool operator==(const KeyTy &key) const
Definition: TypeDetail.h:378