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 ¶m, 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 ¶m, 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