MLIR  18.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() &&
135  other.getIdentifier().equals(getIdentifier());
136 
137  return !other.isIdentified() && other.isPacked() == isPacked() &&
138  other.getTypeList() == getTypeList();
139  }
140 
141  /// Copies dynamically-sized components of the key into the given allocator.
143  if (isIdentified())
144  return Key(allocator.copyInto(name), opaque);
145  return Key(allocator.copyInto(types), packed);
146  }
147 
148  private:
149  ArrayRef<Type> types;
150  StringRef name;
151  bool identified;
152  bool packed;
153  bool opaque;
154  };
155  using KeyTy = Key;
156 
157  /// Returns the string identifier of an identified struct.
158  StringRef getIdentifier() const {
159  assert(isIdentified() && "requested identifier on a non-identified struct");
160  return StringRef(static_cast<const char *>(keyPtr), keySize());
161  }
162 
163  /// Returns the list of types (partially) identifying a literal struct.
165  // If this triggers, use getIdentifiedStructBody() instead.
166  assert(!isIdentified() && "requested typelist on an identified struct");
167  return ArrayRef<Type>(static_cast<const Type *>(keyPtr), keySize());
168  }
169 
170  /// Returns the list of types contained in an identified struct.
172  // If this triggers, use getTypeList() instead.
173  assert(isIdentified() &&
174  "requested struct body on a non-identified struct");
175  return ArrayRef<Type>(identifiedBodyArray, identifiedBodySize());
176  }
177 
178  /// Checks whether the struct is identified.
179  bool isIdentified() const {
180  return llvm::Bitfield::get<KeyFlagIdentified>(keySizeAndFlags);
181  }
182 
183  /// Checks whether the struct is packed (both literal and identified structs).
184  bool isPacked() const {
185  return isIdentified() ? llvm::Bitfield::get<MutableFlagPacked>(
186  identifiedBodySizeAndFlags)
187  : llvm::Bitfield::get<KeyFlagPacked>(keySizeAndFlags);
188  }
189 
190  /// Checks whether a struct is marked as intentionally opaque (an
191  /// uninitialized struct is also considered opaque by the user, call
192  /// isInitialized to check that).
193  bool isOpaque() const {
194  return llvm::Bitfield::get<MutableFlagOpaque>(identifiedBodySizeAndFlags);
195  }
196 
197  /// Checks whether an identified struct has been explicitly initialized either
198  /// by setting its body or by marking it as intentionally opaque.
199  bool isInitialized() const {
200  return llvm::Bitfield::get<MutableFlagInitialized>(
201  identifiedBodySizeAndFlags);
202  }
203 
204  /// Constructs the storage from the given key. This sets up the uniquing key
205  /// components and optionally the mutable component if they construction key
206  /// has the relevant information. In the latter case, the struct is considered
207  /// as initialized and can no longer be mutated.
209  if (!key.isIdentified()) {
210  ArrayRef<Type> types = key.getTypeList();
211  keyPtr = static_cast<const void *>(types.data());
212  setKeySize(types.size());
213  llvm::Bitfield::set<KeyFlagPacked>(keySizeAndFlags, key.isPacked());
214  return;
215  }
216 
217  StringRef name = key.getIdentifier();
218  keyPtr = static_cast<const void *>(name.data());
219  setKeySize(name.size());
220  llvm::Bitfield::set<KeyFlagIdentified>(keySizeAndFlags, true);
221 
222  // If the struct is being constructed directly as opaque, mark it as
223  // initialized.
224  llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags,
225  key.isOpaque());
226  llvm::Bitfield::set<MutableFlagOpaque>(identifiedBodySizeAndFlags,
227  key.isOpaque());
228  }
229 
230  /// Hook into the type uniquing infrastructure.
231  bool operator==(const KeyTy &other) const { return getAsKey() == other; };
232  static llvm::hash_code hashKey(const KeyTy &key) { return key.hashValue(); }
234  const KeyTy &key) {
235  return new (allocator.allocate<LLVMStructTypeStorage>())
237  }
238 
239  /// Sets the body of an identified struct. If the struct is already
240  /// initialized, succeeds only if the body is equal to the current body. Fails
241  /// if the struct is marked as intentionally opaque. The struct will be marked
242  /// as initialized as a result of this operation and can no longer be changed.
244  bool packed) {
245  if (!isIdentified())
246  return failure();
247  if (isInitialized())
248  return success(!isOpaque() && body == getIdentifiedStructBody() &&
249  packed == isPacked());
250 
251  llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags,
252  true);
253  llvm::Bitfield::set<MutableFlagPacked>(identifiedBodySizeAndFlags, packed);
254 
255  ArrayRef<Type> typesInAllocator = allocator.copyInto(body);
256  identifiedBodyArray = typesInAllocator.data();
257  setIdentifiedBodySize(typesInAllocator.size());
258 
259  return success();
260  }
261 
262  /// Returns the key for the current storage.
263  Key getAsKey() const {
264  if (isIdentified())
266  return Key(getTypeList(), isPacked());
267  }
268 
269 private:
270  /// Returns the number of elements in the key.
271  unsigned keySize() const {
272  return llvm::Bitfield::get<KeySize>(keySizeAndFlags);
273  }
274 
275  /// Sets the number of elements in the key.
276  void setKeySize(unsigned value) {
277  llvm::Bitfield::set<KeySize>(keySizeAndFlags, value);
278  }
279 
280  /// Returns the number of types contained in an identified struct.
281  unsigned identifiedBodySize() const {
282  return llvm::Bitfield::get<MutableSize>(identifiedBodySizeAndFlags);
283  }
284  /// Sets the number of types contained in an identified struct.
285  void setIdentifiedBodySize(unsigned value) {
286  llvm::Bitfield::set<MutableSize>(identifiedBodySizeAndFlags, value);
287  }
288 
289  /// Bitfield elements for `keyAndSizeFlags`:
290  /// - bit 0: identified key flag;
291  /// - bit 1: packed key flag;
292  /// - bits 2..bitwidth(unsigned): size of the key.
293  using KeyFlagIdentified =
294  llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>;
295  using KeyFlagPacked = llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>;
296  using KeySize =
297  llvm::Bitfield::Element<unsigned, /*Offset=*/2,
298  std::numeric_limits<unsigned>::digits - 2>;
299 
300  /// Bitfield elements for `identifiedBodySizeAndFlags`:
301  /// - bit 0: opaque flag;
302  /// - bit 1: packed mutable flag;
303  /// - bit 2: initialized flag;
304  /// - bits 3..bitwidth(unsigned): size of the identified body.
305  using MutableFlagOpaque =
306  llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>;
307  using MutableFlagPacked =
308  llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>;
309  using MutableFlagInitialized =
310  llvm::Bitfield::Element<bool, /*Offset=*/2, /*Size=*/1>;
311  using MutableSize =
312  llvm::Bitfield::Element<unsigned, /*Offset=*/3,
313  std::numeric_limits<unsigned>::digits - 3>;
314 
315  /// Pointer to the first element of the uniquing key.
316  // Note: cannot use PointerUnion because bump-ptr allocator does not guarantee
317  // address alignment.
318  const void *keyPtr = nullptr;
319 
320  /// Pointer to the first type contained in an identified struct.
321  const Type *identifiedBodyArray = nullptr;
322 
323  /// Size of the uniquing key combined with identified/literal and
324  /// packedness bits. Must only be used through the Key* bitfields.
325  unsigned keySizeAndFlags = 0;
326 
327  /// Number of the types contained in an identified struct combined with
328  /// mutable flags. Must only be used through the Mutable* bitfields.
329  unsigned identifiedBodySizeAndFlags = 0;
330 };
331 } // end namespace detail
332 } // end namespace LLVM
333 
334 /// Allow walking and replacing the subelements of a LLVMStructTypeStorage key.
335 template <>
336 struct AttrTypeSubElementHandler<LLVM::detail::LLVMStructTypeStorage::Key> {
339  if (param.isIdentified())
340  walker.walkRange(param.getIdentifiedStructBody());
341  else
342  walker.walkRange(param.getTypeList());
343  }
346  AttrSubElementReplacements &attrRepls,
347  TypeSubElementReplacements &typeRepls) {
348  // TODO: It's not clear how we support replacing sub-elements of mutable
349  // types.
350  if (param.isIdentified())
351  return failure();
352 
354  typeRepls.take_front(param.getTypeList().size()), param.isPacked());
355  }
356 };
357 
358 namespace LLVM {
359 namespace detail {
360 //===----------------------------------------------------------------------===//
361 // LLVMTypeAndSizeStorage.
362 //===----------------------------------------------------------------------===//
363 
364 /// Common storage used for LLVM dialect types that need an element type and a
365 /// number: arrays, fixed and scalable vectors. The actual semantics of the
366 /// type is defined by its kind.
368  using KeyTy = std::tuple<Type, unsigned>;
369 
371  : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {}
372 
374  const KeyTy &key) {
375  return new (allocator.allocate<LLVMTypeAndSizeStorage>())
377  }
378 
379  bool operator==(const KeyTy &key) const {
380  return std::make_tuple(elementType, numElements) == key;
381  }
382 
384  unsigned numElements;
385 };
386 
387 } // namespace detail
388 } // namespace LLVM
389 } // namespace mlir
390 
391 #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.
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
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:142
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.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
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:337
static FailureOr< LLVM::detail::LLVMStructTypeStorage::Key > replace(const LLVM::detail::LLVMStructTypeStorage::Key &param, AttrSubElementReplacements &attrRepls, TypeSubElementReplacements &typeRepls)
Definition: TypeDetail.h:345
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:164
StringRef getIdentifier() const
Returns the string identifier of an identified struct.
Definition: TypeDetail.h:158
ArrayRef< Type > getIdentifiedStructBody() const
Returns the list of types contained in an identified struct.
Definition: TypeDetail.h:171
LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef< Type > body, bool packed)
Sets the body of an identified struct.
Definition: TypeDetail.h:243
static LLVMStructTypeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)
Definition: TypeDetail.h:233
static llvm::hash_code hashKey(const KeyTy &key)
Definition: TypeDetail.h:232
Key getAsKey() const
Returns the key for the current storage.
Definition: TypeDetail.h:263
bool operator==(const KeyTy &other) const
Hook into the type uniquing infrastructure.
Definition: TypeDetail.h:231
LLVMStructTypeStorage(const KeyTy &key)
Constructs the storage from the given key.
Definition: TypeDetail.h:208
bool isIdentified() const
Checks whether the struct is identified.
Definition: TypeDetail.h:179
bool isInitialized() const
Checks whether an identified struct has been explicitly initialized either by setting its body or by ...
Definition: TypeDetail.h:199
bool isOpaque() const
Checks whether a struct is marked as intentionally opaque (an uninitialized struct is also considered...
Definition: TypeDetail.h:193
bool isPacked() const
Checks whether the struct is packed (both literal and identified structs).
Definition: TypeDetail.h:184
Common storage used for LLVM dialect types that need an element type and a number: arrays,...
Definition: TypeDetail.h:367
std::tuple< Type, unsigned > KeyTy
Definition: TypeDetail.h:368
static LLVMTypeAndSizeStorage * construct(TypeStorageAllocator &allocator, const KeyTy &key)
Definition: TypeDetail.h:373
bool operator==(const KeyTy &key) const
Definition: TypeDetail.h:379
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26