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
24namespace mlir {
25namespace LLVM {
26namespace 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.
61struct LLVMStructTypeStorage : public TypeStorage {
62public:
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.
100 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.
107 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.
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(args: flags, args: getIdentifier());
125 }
126 if (isPacked())
127 flags |= kPackedHashFlag;
128 return llvm::hash_combine(args: flags, args: 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(RHS: 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.
142 Key copyIntoAllocator(TypeStorageAllocator &allocator) const {
143 if (isIdentified())
144 return Key(allocator.copyInto(str: name), opaque);
145 return Key(allocator.copyInto(elements: 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.
164 ArrayRef<Type> getTypeList() const {
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.
171 ArrayRef<Type> getIdentifiedStructBody() const {
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>(Packed: 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 Packed: identifiedBodySizeAndFlags)
187 : llvm::Bitfield::get<KeyFlagPacked>(Packed: 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>(Packed: 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 Packed: 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.
208 LLVMStructTypeStorage(const KeyTy &key) {
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>(Packed&: keySizeAndFlags, Value: 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>(Packed&: keySizeAndFlags, Value: true);
221
222 // If the struct is being constructed directly as opaque, mark it as
223 // initialized.
224 llvm::Bitfield::set<MutableFlagInitialized>(Packed&: identifiedBodySizeAndFlags,
225 Value: key.isOpaque());
226 llvm::Bitfield::set<MutableFlagOpaque>(Packed&: identifiedBodySizeAndFlags,
227 Value: 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(); }
233 static LLVMStructTypeStorage *construct(TypeStorageAllocator &allocator,
234 const KeyTy &key) {
235 return new (allocator.allocate<LLVMStructTypeStorage>())
236 LLVMStructTypeStorage(key.copyIntoAllocator(allocator));
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.
243 LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef<Type> body,
244 bool packed) {
245 if (!isIdentified())
246 return failure();
247 if (isInitialized())
248 return success(isSuccess: !isOpaque() && body == getIdentifiedStructBody() &&
249 packed == isPacked());
250
251 llvm::Bitfield::set<MutableFlagInitialized>(Packed&: identifiedBodySizeAndFlags,
252 Value: true);
253 llvm::Bitfield::set<MutableFlagPacked>(Packed&: identifiedBodySizeAndFlags, Value: packed);
254
255 ArrayRef<Type> typesInAllocator = allocator.copyInto(elements: 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())
265 return Key(getIdentifier(), isOpaque(), getIdentifiedStructBody());
266 return Key(getTypeList(), isPacked());
267 }
268
269private:
270 /// Returns the number of elements in the key.
271 unsigned keySize() const {
272 return llvm::Bitfield::get<KeySize>(Packed: keySizeAndFlags);
273 }
274
275 /// Sets the number of elements in the key.
276 void setKeySize(unsigned value) {
277 llvm::Bitfield::set<KeySize>(Packed&: keySizeAndFlags, Value: value);
278 }
279
280 /// Returns the number of types contained in an identified struct.
281 unsigned identifiedBodySize() const {
282 return llvm::Bitfield::get<MutableSize>(Packed: identifiedBodySizeAndFlags);
283 }
284 /// Sets the number of types contained in an identified struct.
285 void setIdentifiedBodySize(unsigned value) {
286 llvm::Bitfield::set<MutableSize>(Packed&: identifiedBodySizeAndFlags, Value: 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.
335template <>
336struct AttrTypeSubElementHandler<LLVM::detail::LLVMStructTypeStorage::Key> {
337 static void walk(const LLVM::detail::LLVMStructTypeStorage::Key &param,
338 AttrTypeImmediateSubElementWalker &walker) {
339 if (param.isIdentified())
340 walker.walkRange(elements: param.getIdentifiedStructBody());
341 else
342 walker.walkRange(elements: param.getTypeList());
343 }
344 static FailureOr<LLVM::detail::LLVMStructTypeStorage::Key>
345 replace(const LLVM::detail::LLVMStructTypeStorage::Key &param,
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
353 return LLVM::detail::LLVMStructTypeStorage::Key(
354 typeRepls.take_front(n: param.getTypeList().size()), param.isPacked());
355 }
356};
357
358namespace LLVM {
359namespace 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.
367struct LLVMTypeAndSizeStorage : public TypeStorage {
368 using KeyTy = std::tuple<Type, unsigned>;
369
370 LLVMTypeAndSizeStorage(const KeyTy &key)
371 : elementType(std::get<0>(t: key)), numElements(std::get<1>(t: key)) {}
372
373 static LLVMTypeAndSizeStorage *construct(TypeStorageAllocator &allocator,
374 const KeyTy &key) {
375 return new (allocator.allocate<LLVMTypeAndSizeStorage>())
376 LLVMTypeAndSizeStorage(key);
377 }
378
379 bool operator==(const KeyTy &key) const {
380 return std::make_tuple(args: elementType, args: numElements) == key;
381 }
382
383 Type elementType;
384 unsigned numElements;
385};
386
387} // namespace detail
388} // namespace LLVM
389} // namespace mlir
390
391#endif // DIALECT_LLVMIR_IR_TYPEDETAIL_H
392

source code of mlir/lib/Dialect/LLVMIR/IR/TypeDetail.h