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() && 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.
141 Key copyIntoAllocator(TypeStorageAllocator &allocator) const {
142 if (isIdentified())
143 return Key(allocator.copyInto(str: name), opaque);
144 return Key(allocator.copyInto(elements: 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.
163 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.
170 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.
178 bool isIdentified() const {
179 return llvm::Bitfield::get<KeyFlagIdentified>(Packed: 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 Packed: identifiedBodySizeAndFlags)
186 : llvm::Bitfield::get<KeyFlagPacked>(Packed: 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>(Packed: 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 Packed: 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.
207 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>(Packed&: keySizeAndFlags, Value: 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>(Packed&: keySizeAndFlags, Value: true);
220
221 // If the struct is being constructed directly as opaque, mark it as
222 // initialized.
223 llvm::Bitfield::set<MutableFlagInitialized>(Packed&: identifiedBodySizeAndFlags,
224 Value: key.isOpaque());
225 llvm::Bitfield::set<MutableFlagOpaque>(Packed&: identifiedBodySizeAndFlags,
226 Value: 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(); }
232 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.
242 LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef<Type> body,
243 bool packed) {
244 if (!isIdentified())
245 return failure();
246 if (isInitialized())
247 return success(IsSuccess: !isOpaque() && body == getIdentifiedStructBody() &&
248 packed == isPacked());
249
250 llvm::Bitfield::set<MutableFlagInitialized>(Packed&: identifiedBodySizeAndFlags,
251 Value: true);
252 llvm::Bitfield::set<MutableFlagPacked>(Packed&: identifiedBodySizeAndFlags, Value: packed);
253
254 ArrayRef<Type> typesInAllocator = allocator.copyInto(elements: 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())
264 return Key(getIdentifier(), isOpaque(), getIdentifiedStructBody());
265 return Key(getTypeList(), isPacked());
266 }
267
268private:
269 /// Returns the number of elements in the key.
270 unsigned keySize() const {
271 return llvm::Bitfield::get<KeySize>(Packed: keySizeAndFlags);
272 }
273
274 /// Sets the number of elements in the key.
275 void setKeySize(unsigned value) {
276 llvm::Bitfield::set<KeySize>(Packed&: keySizeAndFlags, Value: value);
277 }
278
279 /// Returns the number of types contained in an identified struct.
280 unsigned identifiedBodySize() const {
281 return llvm::Bitfield::get<MutableSize>(Packed: identifiedBodySizeAndFlags);
282 }
283 /// Sets the number of types contained in an identified struct.
284 void setIdentifiedBodySize(unsigned value) {
285 llvm::Bitfield::set<MutableSize>(Packed&: identifiedBodySizeAndFlags, Value: 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.
334template <>
335struct 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(elements: param.getIdentifiedStructBody());
340 else
341 walker.walkRange(elements: 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(n: param.getTypeList().size()), param.isPacked());
354 }
355};
356
357namespace LLVM {
358namespace 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.
366struct LLVMTypeAndSizeStorage : public TypeStorage {
367 using KeyTy = std::tuple<Type, unsigned>;
368
369 LLVMTypeAndSizeStorage(const KeyTy &key)
370 : elementType(std::get<0>(t: key)), numElements(std::get<1>(t: 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(args: elementType, args: 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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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