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. KeyLLVMStructTypeStorage72 Key(StringRef name, bool opaque) 73 : name(name), identified(true), packed(false), opaque(opaque) {} 74 /// Constructs a key for a literal struct. KeyLLVMStructTypeStorage75 Key(ArrayRef<LLVMType> types, bool packed) 76 : types(types), identified(false), packed(packed), opaque(false) {} 77 78 /// Checks a specific property of the struct. isIdentifiedLLVMStructTypeStorage79 bool isIdentified() const { return identified; } isPackedLLVMStructTypeStorage80 bool isPacked() const { 81 assert(!isIdentified() && 82 "'packed' bit is not part of the key for identified structs"); 83 return packed; 84 } isOpaqueLLVMStructTypeStorage85 bool isOpaque() const { 86 assert(isIdentified() && 87 "'opaque' bit is meaningless on literal structs"); 88 return opaque; 89 } 90 91 /// Returns the identifier of a key for identified structs. getIdentifierLLVMStructTypeStorage92 StringRef getIdentifier() const { 93 assert(isIdentified() && 94 "non-identified struct key canont have an identifier"); 95 return name; 96 } 97 98 /// Returns the list of type contained in the key of a literal struct. getTypeListLLVMStructTypeStorage99 ArrayRef<LLVMType> getTypeList() const { 100 assert(!isIdentified() && 101 "identified struct key cannot have a type list"); 102 return types; 103 } 104 105 /// Returns the hash value of the key. This combines various flags into a 106 /// single value: the identified flag sets the first bit, and the packedness 107 /// flag sets the second bit. Opacity bit is only used for construction and 108 /// does not participate in uniquing. hashValueLLVMStructTypeStorage109 llvm::hash_code hashValue() const { 110 constexpr static unsigned kIdentifiedHashFlag = 1; 111 constexpr static unsigned kPackedHashFlag = 2; 112 113 unsigned flags = 0; 114 if (isIdentified()) { 115 flags |= kIdentifiedHashFlag; 116 return llvm::hash_combine(flags, getIdentifier()); 117 } 118 if (isPacked()) 119 flags |= kPackedHashFlag; 120 return llvm::hash_combine(flags, getTypeList()); 121 } 122 123 /// Compares two keys. 124 bool operator==(const Key &other) const { 125 if (isIdentified()) 126 return other.isIdentified() && 127 other.getIdentifier().equals(getIdentifier()); 128 129 return !other.isIdentified() && other.isPacked() == isPacked() && 130 other.getTypeList() == getTypeList(); 131 } 132 133 /// Copies dynamically-sized components of the key into the given allocator. copyIntoAllocatorLLVMStructTypeStorage134 Key copyIntoAllocator(TypeStorageAllocator &allocator) const { 135 if (isIdentified()) 136 return Key(allocator.copyInto(name), opaque); 137 return Key(allocator.copyInto(types), packed); 138 } 139 140 private: 141 ArrayRef<LLVMType> types; 142 StringRef name; 143 bool identified; 144 bool packed; 145 bool opaque; 146 }; 147 using KeyTy = Key; 148 149 /// Returns the string identifier of an identified struct. getIdentifierLLVMStructTypeStorage150 StringRef getIdentifier() const { 151 assert(isIdentified() && "requested identifier on a non-identified struct"); 152 return StringRef(static_cast<const char *>(keyPtr), keySize()); 153 } 154 155 /// Returns the list of types (partially) identifying a literal struct. getTypeListLLVMStructTypeStorage156 ArrayRef<LLVMType> getTypeList() const { 157 // If this triggers, use getIdentifiedStructBody() instead. 158 assert(!isIdentified() && "requested typelist on an identified struct"); 159 return ArrayRef<LLVMType>(static_cast<const LLVMType *>(keyPtr), keySize()); 160 } 161 162 /// Returns the list of types contained in an identified struct. getIdentifiedStructBodyLLVMStructTypeStorage163 ArrayRef<LLVMType> getIdentifiedStructBody() const { 164 // If this triggers, use getTypeList() instead. 165 assert(isIdentified() && 166 "requested struct body on a non-identified struct"); 167 return ArrayRef<LLVMType>(identifiedBodyArray, identifiedBodySize()); 168 } 169 170 /// Checks whether the struct is identified. isIdentifiedLLVMStructTypeStorage171 bool isIdentified() const { 172 return llvm::Bitfield::get<KeyFlagIdentified>(keySizeAndFlags); 173 } 174 175 /// Checks whether the struct is packed (both literal and identified structs). isPackedLLVMStructTypeStorage176 bool isPacked() const { 177 return isIdentified() ? llvm::Bitfield::get<MutableFlagPacked>( 178 identifiedBodySizeAndFlags) 179 : llvm::Bitfield::get<KeyFlagPacked>(keySizeAndFlags); 180 } 181 182 /// Checks whether a struct is marked as intentionally opaque (an 183 /// uninitialized struct is also considered opaque by the user, call 184 /// isInitialized to check that). isOpaqueLLVMStructTypeStorage185 bool isOpaque() const { 186 return llvm::Bitfield::get<MutableFlagOpaque>(identifiedBodySizeAndFlags); 187 } 188 189 /// Checks whether an identified struct has been explicitly initialized either 190 /// by setting its body or by marking it as intentionally opaque. isInitializedLLVMStructTypeStorage191 bool isInitialized() const { 192 return llvm::Bitfield::get<MutableFlagInitialized>( 193 identifiedBodySizeAndFlags); 194 } 195 196 /// Constructs the storage from the given key. This sets up the uniquing key 197 /// components and optionally the mutable component if they construction key 198 /// has the relevant information. In the latter case, the struct is considered 199 /// as initialized and can no longer be mutated. LLVMStructTypeStorageLLVMStructTypeStorage200 LLVMStructTypeStorage(const KeyTy &key) { 201 if (!key.isIdentified()) { 202 ArrayRef<LLVMType> types = key.getTypeList(); 203 keyPtr = static_cast<const void *>(types.data()); 204 setKeySize(types.size()); 205 llvm::Bitfield::set<KeyFlagPacked>(keySizeAndFlags, key.isPacked()); 206 return; 207 } 208 209 StringRef name = key.getIdentifier(); 210 keyPtr = static_cast<const void *>(name.data()); 211 setKeySize(name.size()); 212 llvm::Bitfield::set<KeyFlagIdentified>(keySizeAndFlags, true); 213 214 // If the struct is being constructed directly as opaque, mark it as 215 // initialized. 216 llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags, 217 key.isOpaque()); 218 llvm::Bitfield::set<MutableFlagOpaque>(identifiedBodySizeAndFlags, 219 key.isOpaque()); 220 } 221 222 /// Hook into the type unquing infrastructure. 223 bool operator==(const KeyTy &other) const { return getKey() == other; }; hashKeyLLVMStructTypeStorage224 static llvm::hash_code hashKey(const KeyTy &key) { return key.hashValue(); } constructLLVMStructTypeStorage225 static LLVMStructTypeStorage *construct(TypeStorageAllocator &allocator, 226 const KeyTy &key) { 227 return new (allocator.allocate<LLVMStructTypeStorage>()) 228 LLVMStructTypeStorage(key.copyIntoAllocator(allocator)); 229 } 230 231 /// Sets the body of an identified struct. If the struct is already 232 /// initialized, succeeds only if the body is equal to the current body. Fails 233 /// if the struct is marked as intentionally opaque. The struct will be marked 234 /// as initialized as a result of this operation and can no longer be changed. mutateLLVMStructTypeStorage235 LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef<LLVMType> body, 236 bool packed) { 237 if (!isIdentified()) 238 return failure(); 239 if (isInitialized()) 240 return success(!isOpaque() && body == getIdentifiedStructBody() && 241 packed == isPacked()); 242 243 llvm::Bitfield::set<MutableFlagInitialized>(identifiedBodySizeAndFlags, 244 true); 245 llvm::Bitfield::set<MutableFlagPacked>(identifiedBodySizeAndFlags, packed); 246 247 ArrayRef<LLVMType> typesInAllocator = allocator.copyInto(body); 248 identifiedBodyArray = typesInAllocator.data(); 249 setIdentifiedBodySize(typesInAllocator.size()); 250 251 return success(); 252 } 253 254 private: 255 /// Returns the number of elements in the key. keySizeLLVMStructTypeStorage256 unsigned keySize() const { 257 return llvm::Bitfield::get<KeySize>(keySizeAndFlags); 258 } 259 260 /// Sets the number of elements in the key. setKeySizeLLVMStructTypeStorage261 void setKeySize(unsigned value) { 262 llvm::Bitfield::set<KeySize>(keySizeAndFlags, value); 263 } 264 265 /// Returns the number of types contained in an identified struct. identifiedBodySizeLLVMStructTypeStorage266 unsigned identifiedBodySize() const { 267 return llvm::Bitfield::get<MutableSize>(identifiedBodySizeAndFlags); 268 } 269 /// Sets the number of types contained in an identified struct. setIdentifiedBodySizeLLVMStructTypeStorage270 void setIdentifiedBodySize(unsigned value) { 271 llvm::Bitfield::set<MutableSize>(identifiedBodySizeAndFlags, value); 272 } 273 274 /// Returns the key for the current storage. getKeyLLVMStructTypeStorage275 Key getKey() const { 276 if (isIdentified()) 277 return Key(getIdentifier(), isOpaque()); 278 return Key(getTypeList(), isPacked()); 279 } 280 281 /// Bitfield elements for `keyAndSizeFlags`: 282 /// - bit 0: identified key flag; 283 /// - bit 1: packed key flag; 284 /// - bits 2..bitwidth(unsigned): size of the key. 285 using KeyFlagIdentified = 286 llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>; 287 using KeyFlagPacked = llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>; 288 using KeySize = 289 llvm::Bitfield::Element<unsigned, /*Offset=*/2, 290 std::numeric_limits<unsigned>::digits - 2>; 291 292 /// Bitfield elements for `identifiedBodySizeAndFlags`: 293 /// - bit 0: opaque flag; 294 /// - bit 1: packed mutable flag; 295 /// - bit 2: initialized flag; 296 /// - bits 3..bitwidth(unsigned): size of the identified body. 297 using MutableFlagOpaque = 298 llvm::Bitfield::Element<bool, /*Offset=*/0, /*Size=*/1>; 299 using MutableFlagPacked = 300 llvm::Bitfield::Element<bool, /*Offset=*/1, /*Size=*/1>; 301 using MutableFlagInitialized = 302 llvm::Bitfield::Element<bool, /*Offset=*/2, /*Size=*/1>; 303 using MutableSize = 304 llvm::Bitfield::Element<unsigned, /*Offset=*/3, 305 std::numeric_limits<unsigned>::digits - 3>; 306 307 /// Pointer to the first element of the uniquing key. 308 // Note: cannot use PointerUnion because bump-ptr allocator does not guarantee 309 // address alignment. 310 const void *keyPtr = nullptr; 311 312 /// Pointer to the first type contained in an identified struct. 313 const LLVMType *identifiedBodyArray = nullptr; 314 315 /// Size of the uniquing key combined with identified/literal and 316 /// packedness bits. Must only be used through the Key* bitfields. 317 unsigned keySizeAndFlags = 0; 318 319 /// Number of the types contained in an identified struct combined with 320 /// mutable flags. Must only be used through the Mutable* bitfields. 321 unsigned identifiedBodySizeAndFlags = 0; 322 }; 323 324 //===----------------------------------------------------------------------===// 325 // LLVMFunctionTypeStorage. 326 //===----------------------------------------------------------------------===// 327 328 /// Type storage for LLVM dialect function types. These are uniqued using the 329 /// list of types they contain and the vararg bit. 330 struct LLVMFunctionTypeStorage : public TypeStorage { 331 using KeyTy = std::tuple<LLVMType, ArrayRef<LLVMType>, bool>; 332 333 /// Construct a storage from the given components. The list is expected to be 334 /// allocated in the context. LLVMFunctionTypeStorageLLVMFunctionTypeStorage335 LLVMFunctionTypeStorage(LLVMType result, ArrayRef<LLVMType> arguments, 336 bool variadic) 337 : argumentTypes(arguments) { 338 returnTypeAndVariadic.setPointerAndInt(result, variadic); 339 } 340 341 /// Hook into the type uniquing infrastructure. constructLLVMFunctionTypeStorage342 static LLVMFunctionTypeStorage *construct(TypeStorageAllocator &allocator, 343 const KeyTy &key) { 344 return new (allocator.allocate<LLVMFunctionTypeStorage>()) 345 LLVMFunctionTypeStorage(std::get<0>(key), 346 allocator.copyInto(std::get<1>(key)), 347 std::get<2>(key)); 348 } 349 hashKeyLLVMFunctionTypeStorage350 static unsigned hashKey(const KeyTy &key) { 351 // LLVM doesn't like hashing bools in tuples. 352 return llvm::hash_combine(std::get<0>(key), std::get<1>(key), 353 static_cast<int>(std::get<2>(key))); 354 } 355 356 bool operator==(const KeyTy &key) const { 357 return std::make_tuple(getReturnType(), getArgumentTypes(), isVariadic()) == 358 key; 359 } 360 361 /// Returns the list of function argument types. getArgumentTypesLLVMFunctionTypeStorage362 ArrayRef<LLVMType> getArgumentTypes() const { return argumentTypes; } 363 364 /// Checks whether the function type is variadic. isVariadicLLVMFunctionTypeStorage365 bool isVariadic() const { return returnTypeAndVariadic.getInt(); } 366 367 /// Returns the function result type. getReturnTypeLLVMFunctionTypeStorage368 LLVMType getReturnType() const { return returnTypeAndVariadic.getPointer(); } 369 370 private: 371 /// Function result type packed with the variadic bit. 372 llvm::PointerIntPair<LLVMType, 1, bool> returnTypeAndVariadic; 373 /// Argument types. 374 ArrayRef<LLVMType> argumentTypes; 375 }; 376 377 //===----------------------------------------------------------------------===// 378 // LLVMIntegerTypeStorage. 379 //===----------------------------------------------------------------------===// 380 381 /// Storage type for LLVM dialect integer types. These are uniqued by bitwidth. 382 struct LLVMIntegerTypeStorage : public TypeStorage { 383 using KeyTy = unsigned; 384 LLVMIntegerTypeStorageLLVMIntegerTypeStorage385 LLVMIntegerTypeStorage(unsigned width) : bitwidth(width) {} 386 constructLLVMIntegerTypeStorage387 static LLVMIntegerTypeStorage *construct(TypeStorageAllocator &allocator, 388 const KeyTy &key) { 389 return new (allocator.allocate<LLVMIntegerTypeStorage>()) 390 LLVMIntegerTypeStorage(key); 391 } 392 393 bool operator==(const KeyTy &key) const { return key == bitwidth; } 394 395 unsigned bitwidth; 396 }; 397 398 //===----------------------------------------------------------------------===// 399 // LLVMPointerTypeStorage. 400 //===----------------------------------------------------------------------===// 401 402 /// Storage type for LLVM dialect pointer types. These are uniqued by a pair of 403 /// element type and address space. 404 struct LLVMPointerTypeStorage : public TypeStorage { 405 using KeyTy = std::tuple<LLVMType, unsigned>; 406 LLVMPointerTypeStorageLLVMPointerTypeStorage407 LLVMPointerTypeStorage(const KeyTy &key) 408 : pointeeType(std::get<0>(key)), addressSpace(std::get<1>(key)) {} 409 constructLLVMPointerTypeStorage410 static LLVMPointerTypeStorage *construct(TypeStorageAllocator &allocator, 411 const KeyTy &key) { 412 return new (allocator.allocate<LLVMPointerTypeStorage>()) 413 LLVMPointerTypeStorage(key); 414 } 415 416 bool operator==(const KeyTy &key) const { 417 return std::make_tuple(pointeeType, addressSpace) == key; 418 } 419 420 LLVMType pointeeType; 421 unsigned addressSpace; 422 }; 423 424 //===----------------------------------------------------------------------===// 425 // LLVMTypeAndSizeStorage. 426 //===----------------------------------------------------------------------===// 427 428 /// Common storage used for LLVM dialect types that need an element type and a 429 /// number: arrays, fixed and scalable vectors. The actual semantics of the 430 /// type is defined by its kind. 431 struct LLVMTypeAndSizeStorage : public TypeStorage { 432 using KeyTy = std::tuple<LLVMType, unsigned>; 433 LLVMTypeAndSizeStorageLLVMTypeAndSizeStorage434 LLVMTypeAndSizeStorage(const KeyTy &key) 435 : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {} 436 constructLLVMTypeAndSizeStorage437 static LLVMTypeAndSizeStorage *construct(TypeStorageAllocator &allocator, 438 const KeyTy &key) { 439 return new (allocator.allocate<LLVMTypeAndSizeStorage>()) 440 LLVMTypeAndSizeStorage(key); 441 } 442 443 bool operator==(const KeyTy &key) const { 444 return std::make_tuple(elementType, numElements) == key; 445 } 446 447 LLVMType elementType; 448 unsigned numElements; 449 }; 450 451 } // end namespace detail 452 } // end namespace LLVM 453 } // end namespace mlir 454 455 #endif // DIALECT_LLVMIR_IR_TYPEDETAIL_H 456