github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/IR/AttributeDetail.h (about) 1 //===- AttributeDetail.h - MLIR Affine Map details Class --------*- C++ -*-===// 2 // 3 // Copyright 2019 The MLIR Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // ============================================================================= 17 // 18 // This holds implementation details of Attribute. 19 // 20 //===----------------------------------------------------------------------===// 21 22 #ifndef ATTRIBUTEDETAIL_H_ 23 #define ATTRIBUTEDETAIL_H_ 24 25 #include "mlir/IR/AffineMap.h" 26 #include "mlir/IR/Attributes.h" 27 #include "mlir/IR/Identifier.h" 28 #include "mlir/IR/IntegerSet.h" 29 #include "mlir/IR/MLIRContext.h" 30 #include "mlir/IR/StandardTypes.h" 31 #include "mlir/Support/StorageUniquer.h" 32 #include "llvm/ADT/APFloat.h" 33 #include "llvm/ADT/PointerIntPair.h" 34 #include "llvm/Support/TrailingObjects.h" 35 36 namespace mlir { 37 namespace detail { 38 // An attribute representing a reference to an affine map. 39 struct AffineMapAttributeStorage : public AttributeStorage { 40 using KeyTy = AffineMap; 41 42 AffineMapAttributeStorage(AffineMap value) 43 : AttributeStorage(IndexType::get(value.getContext())), value(value) {} 44 45 /// Key equality function. 46 bool operator==(const KeyTy &key) const { return key == value; } 47 48 /// Construct a new storage instance. 49 static AffineMapAttributeStorage * 50 construct(AttributeStorageAllocator &allocator, KeyTy key) { 51 return new (allocator.allocate<AffineMapAttributeStorage>()) 52 AffineMapAttributeStorage(key); 53 } 54 55 AffineMap value; 56 }; 57 58 /// An attribute representing an array of other attributes. 59 struct ArrayAttributeStorage : public AttributeStorage { 60 using KeyTy = ArrayRef<Attribute>; 61 62 ArrayAttributeStorage(ArrayRef<Attribute> value) : value(value) {} 63 64 /// Key equality function. 65 bool operator==(const KeyTy &key) const { return key == value; } 66 67 /// Construct a new storage instance. 68 static ArrayAttributeStorage *construct(AttributeStorageAllocator &allocator, 69 const KeyTy &key) { 70 return new (allocator.allocate<ArrayAttributeStorage>()) 71 ArrayAttributeStorage(allocator.copyInto(key)); 72 } 73 74 ArrayRef<Attribute> value; 75 }; 76 77 /// An attribute representing a boolean value. 78 struct BoolAttributeStorage : public AttributeStorage { 79 using KeyTy = std::pair<MLIRContext *, bool>; 80 81 BoolAttributeStorage(Type type, bool value) 82 : AttributeStorage(type), value(value) {} 83 84 /// We only check equality for and hash with the boolean key parameter. 85 bool operator==(const KeyTy &key) const { return key.second == value; } 86 static unsigned hashKey(const KeyTy &key) { 87 return llvm::hash_value(key.second); 88 } 89 90 static BoolAttributeStorage *construct(AttributeStorageAllocator &allocator, 91 const KeyTy &key) { 92 return new (allocator.allocate<BoolAttributeStorage>()) 93 BoolAttributeStorage(IntegerType::get(1, key.first), key.second); 94 } 95 96 bool value; 97 }; 98 99 /// An attribute representing a dictionary of sorted named attributes. 100 struct DictionaryAttributeStorage final 101 : public AttributeStorage, 102 private llvm::TrailingObjects<DictionaryAttributeStorage, 103 NamedAttribute> { 104 using KeyTy = ArrayRef<NamedAttribute>; 105 106 /// Given a list of NamedAttribute's, canonicalize the list (sorting 107 /// by name) and return the unique'd result. 108 static DictionaryAttributeStorage *get(ArrayRef<NamedAttribute> attrs); 109 110 /// Key equality function. 111 bool operator==(const KeyTy &key) const { return key == getElements(); } 112 113 /// Construct a new storage instance. 114 static DictionaryAttributeStorage * 115 construct(AttributeStorageAllocator &allocator, const KeyTy &key) { 116 auto size = DictionaryAttributeStorage::totalSizeToAlloc<NamedAttribute>( 117 key.size()); 118 auto rawMem = allocator.allocate(size, alignof(NamedAttribute)); 119 120 // Initialize the storage and trailing attribute list. 121 auto result = ::new (rawMem) DictionaryAttributeStorage(key.size()); 122 std::uninitialized_copy(key.begin(), key.end(), 123 result->getTrailingObjects<NamedAttribute>()); 124 return result; 125 } 126 127 /// Return the elements of this dictionary attribute. 128 ArrayRef<NamedAttribute> getElements() const { 129 return {getTrailingObjects<NamedAttribute>(), numElements}; 130 } 131 132 private: 133 friend class llvm::TrailingObjects<DictionaryAttributeStorage, 134 NamedAttribute>; 135 136 // This is used by the llvm::TrailingObjects base class. 137 size_t numTrailingObjects(OverloadToken<NamedAttribute>) const { 138 return numElements; 139 } 140 DictionaryAttributeStorage(unsigned numElements) : numElements(numElements) {} 141 142 /// This is the number of attributes. 143 const unsigned numElements; 144 }; 145 146 /// An attribute representing a floating point value. 147 struct FloatAttributeStorage final 148 : public AttributeStorage, 149 public llvm::TrailingObjects<FloatAttributeStorage, uint64_t> { 150 using KeyTy = std::pair<Type, APFloat>; 151 152 FloatAttributeStorage(const llvm::fltSemantics &semantics, Type type, 153 size_t numObjects) 154 : AttributeStorage(type), semantics(semantics), numObjects(numObjects) {} 155 156 /// Key equality and hash functions. 157 bool operator==(const KeyTy &key) const { 158 return key.first == getType() && key.second.bitwiseIsEqual(getValue()); 159 } 160 static unsigned hashKey(const KeyTy &key) { 161 return llvm::hash_combine(key.first, llvm::hash_value(key.second)); 162 } 163 164 /// Construct a key with a type and double. 165 static KeyTy getKey(Type type, double value) { 166 // Treat BF16 as double because it is not supported in LLVM's APFloat. 167 // TODO(b/121118307): add BF16 support to APFloat? 168 if (type.isBF16() || type.isF64()) 169 return KeyTy(type, APFloat(value)); 170 171 // This handles, e.g., F16 because there is no APFloat constructor for it. 172 bool unused; 173 APFloat val(value); 174 val.convert(type.cast<FloatType>().getFloatSemantics(), 175 APFloat::rmNearestTiesToEven, &unused); 176 return KeyTy(type, val); 177 } 178 179 /// Construct a new storage instance. 180 static FloatAttributeStorage *construct(AttributeStorageAllocator &allocator, 181 const KeyTy &key) { 182 const auto &apint = key.second.bitcastToAPInt(); 183 184 // Here one word's bitwidth equals to that of uint64_t. 185 auto elements = ArrayRef<uint64_t>(apint.getRawData(), apint.getNumWords()); 186 187 auto byteSize = 188 FloatAttributeStorage::totalSizeToAlloc<uint64_t>(elements.size()); 189 auto rawMem = allocator.allocate(byteSize, alignof(FloatAttributeStorage)); 190 auto result = ::new (rawMem) FloatAttributeStorage( 191 key.second.getSemantics(), key.first, elements.size()); 192 std::uninitialized_copy(elements.begin(), elements.end(), 193 result->getTrailingObjects<uint64_t>()); 194 return result; 195 } 196 197 /// Returns an APFloat representing the stored value. 198 APFloat getValue() const { 199 auto val = APInt(APFloat::getSizeInBits(semantics), 200 {getTrailingObjects<uint64_t>(), numObjects}); 201 return APFloat(semantics, val); 202 } 203 204 const llvm::fltSemantics &semantics; 205 size_t numObjects; 206 }; 207 208 /// An attribute representing a integral value. 209 struct IntegerAttributeStorage final 210 : public AttributeStorage, 211 public llvm::TrailingObjects<IntegerAttributeStorage, uint64_t> { 212 using KeyTy = std::pair<Type, APInt>; 213 214 IntegerAttributeStorage(Type type, size_t numObjects) 215 : AttributeStorage(type), numObjects(numObjects) { 216 assert((type.isIndex() || type.isa<IntegerType>()) && "invalid type"); 217 } 218 219 /// Key equality and hash functions. 220 bool operator==(const KeyTy &key) const { 221 return key == KeyTy(getType(), getValue()); 222 } 223 static unsigned hashKey(const KeyTy &key) { 224 return llvm::hash_combine(key.first, llvm::hash_value(key.second)); 225 } 226 227 /// Construct a new storage instance. 228 static IntegerAttributeStorage * 229 construct(AttributeStorageAllocator &allocator, const KeyTy &key) { 230 Type type; 231 APInt value; 232 std::tie(type, value) = key; 233 234 auto elements = ArrayRef<uint64_t>(value.getRawData(), value.getNumWords()); 235 auto size = 236 IntegerAttributeStorage::totalSizeToAlloc<uint64_t>(elements.size()); 237 auto rawMem = allocator.allocate(size, alignof(IntegerAttributeStorage)); 238 auto result = ::new (rawMem) IntegerAttributeStorage(type, elements.size()); 239 std::uninitialized_copy(elements.begin(), elements.end(), 240 result->getTrailingObjects<uint64_t>()); 241 return result; 242 } 243 244 /// Returns an APInt representing the stored value. 245 APInt getValue() const { 246 if (getType().isIndex()) 247 return APInt(64, {getTrailingObjects<uint64_t>(), numObjects}); 248 return APInt(getType().getIntOrFloatBitWidth(), 249 {getTrailingObjects<uint64_t>(), numObjects}); 250 } 251 252 size_t numObjects; 253 }; 254 255 // An attribute representing a reference to an integer set. 256 struct IntegerSetAttributeStorage : public AttributeStorage { 257 using KeyTy = IntegerSet; 258 259 IntegerSetAttributeStorage(IntegerSet value) : value(value) {} 260 261 /// Key equality function. 262 bool operator==(const KeyTy &key) const { return key == value; } 263 264 /// Construct a new storage instance. 265 static IntegerSetAttributeStorage * 266 construct(AttributeStorageAllocator &allocator, KeyTy key) { 267 return new (allocator.allocate<IntegerSetAttributeStorage>()) 268 IntegerSetAttributeStorage(key); 269 } 270 271 IntegerSet value; 272 }; 273 274 /// Opaque Attribute Storage and Uniquing. 275 struct OpaqueAttributeStorage : public AttributeStorage { 276 OpaqueAttributeStorage(Identifier dialectNamespace, StringRef attrData, 277 Type type) 278 : AttributeStorage(type), dialectNamespace(dialectNamespace), 279 attrData(attrData) {} 280 281 /// The hash key used for uniquing. 282 using KeyTy = std::tuple<Identifier, StringRef, Type>; 283 bool operator==(const KeyTy &key) const { 284 return key == KeyTy(dialectNamespace, attrData, getType()); 285 } 286 287 static OpaqueAttributeStorage *construct(AttributeStorageAllocator &allocator, 288 const KeyTy &key) { 289 return new (allocator.allocate<OpaqueAttributeStorage>()) 290 OpaqueAttributeStorage(std::get<0>(key), 291 allocator.copyInto(std::get<1>(key)), 292 std::get<2>(key)); 293 } 294 295 // The dialect namespace. 296 Identifier dialectNamespace; 297 298 // The parser attribute data for this opaque attribute. 299 StringRef attrData; 300 }; 301 302 /// An attribute representing a string value. 303 struct StringAttributeStorage : public AttributeStorage { 304 using KeyTy = std::pair<StringRef, Type>; 305 306 StringAttributeStorage(StringRef value, Type type) 307 : AttributeStorage(type), value(value) {} 308 309 /// Key equality function. 310 bool operator==(const KeyTy &key) const { 311 return key == KeyTy(value, getType()); 312 } 313 314 /// Construct a new storage instance. 315 static StringAttributeStorage *construct(AttributeStorageAllocator &allocator, 316 const KeyTy &key) { 317 return new (allocator.allocate<StringAttributeStorage>()) 318 StringAttributeStorage(allocator.copyInto(key.first), key.second); 319 } 320 321 StringRef value; 322 }; 323 324 /// An attribute representing a reference to a type. 325 struct TypeAttributeStorage : public AttributeStorage { 326 using KeyTy = Type; 327 328 TypeAttributeStorage(Type value) : value(value) {} 329 330 /// Key equality function. 331 bool operator==(const KeyTy &key) const { return key == value; } 332 333 /// Construct a new storage instance. 334 static TypeAttributeStorage *construct(AttributeStorageAllocator &allocator, 335 KeyTy key) { 336 return new (allocator.allocate<TypeAttributeStorage>()) 337 TypeAttributeStorage(key); 338 } 339 340 Type value; 341 }; 342 343 //===----------------------------------------------------------------------===// 344 // Elements Attributes 345 //===----------------------------------------------------------------------===// 346 347 /// An attribute representing a reference to a dense vector or tensor object. 348 struct DenseElementsAttributeStorage : public AttributeStorage { 349 struct KeyTy { 350 KeyTy(ShapedType type, ArrayRef<char> data, llvm::hash_code hashCode, 351 bool isSplat = false) 352 : type(type), data(data), hashCode(hashCode), isSplat(isSplat) {} 353 354 /// The type of the dense elements. 355 ShapedType type; 356 357 /// The raw buffer for the data storage. 358 ArrayRef<char> data; 359 360 /// The computed hash code for the storage data. 361 llvm::hash_code hashCode; 362 363 /// A boolean that indicates if this data is a splat or not. 364 bool isSplat; 365 }; 366 367 DenseElementsAttributeStorage(ShapedType ty, ArrayRef<char> data, 368 bool isSplat = false) 369 : AttributeStorage(ty), data(data), isSplat(isSplat) {} 370 371 /// Compare this storage instance with the provided key. 372 bool operator==(const KeyTy &key) const { 373 if (key.type != getType()) 374 return false; 375 376 // For boolean splats we need to explicitly check that the first bit is the 377 // same. Boolean values are packed at the bit level, and even though a splat 378 // is detected the rest of the bits in the first byte may differ from the 379 // splat value. 380 if (key.type.getElementTypeBitWidth() == 1) { 381 if (key.isSplat != isSplat) 382 return false; 383 if (isSplat) 384 return (key.data.front() & 1) == data.front(); 385 } 386 387 // Otherwise, we can default to just checking the data. 388 return key.data == data; 389 } 390 391 /// Construct a key from a shaped type, raw data buffer, and a flag that 392 /// signals if the data is already known to be a splat. Callers to this 393 /// function are expected to tag preknown splat values when possible, e.g. one 394 /// element shapes. 395 static KeyTy getKey(ShapedType ty, ArrayRef<char> data, bool isKnownSplat) { 396 // Handle an empty storage instance. 397 if (data.empty()) 398 return KeyTy(ty, data, 0); 399 400 // If the data is already known to be a splat, the key hash value is 401 // directly the data buffer. 402 if (isKnownSplat) 403 return KeyTy(ty, data, llvm::hash_value(data), isKnownSplat); 404 405 // Otherwise, we need to check if the data corresponds to a splat or not. 406 407 // Handle the simple case of only one element. 408 size_t numElements = ty.getNumElements(); 409 assert(numElements != 1 && "splat of 1 element should already be detected"); 410 411 // Handle boolean values directly as they are packed to 1-bit. 412 size_t elementWidth = ty.getElementTypeBitWidth(); 413 if (elementWidth == 1) 414 return getKeyForBoolData(ty, data, numElements); 415 416 // FIXME(b/121118307): using 64 bits for BF16 because it is currently stored 417 // with double semantics. 418 if (ty.getElementType().isBF16()) 419 elementWidth = 64; 420 421 // Non 1-bit dense elements are padded to 8-bits. 422 size_t storageSize = llvm::divideCeil(elementWidth, CHAR_BIT); 423 assert(((data.size() / storageSize) == numElements) && 424 "data does not hold expected number of elements"); 425 426 // Create the initial hash value with just the first element. 427 auto firstElt = data.take_front(storageSize); 428 auto hashVal = llvm::hash_value(firstElt); 429 430 // Check to see if this storage represents a splat. If it doesn't then 431 // combine the hash for the data starting with the first non splat element. 432 for (size_t i = storageSize, e = data.size(); i != e; i += storageSize) 433 if (memcmp(data.data(), &data[i], storageSize)) 434 return KeyTy(ty, data, llvm::hash_combine(hashVal, data.drop_front(i))); 435 436 // Otherwise, this is a splat so just return the hash of the first element. 437 return KeyTy(ty, firstElt, hashVal, /*isSplat=*/true); 438 } 439 440 /// Construct a key with a set of boolean data. 441 static KeyTy getKeyForBoolData(ShapedType ty, ArrayRef<char> data, 442 size_t numElements) { 443 ArrayRef<char> splatData = data; 444 bool splatValue = splatData.front() & 1; 445 446 // Helper functor to generate a KeyTy for a boolean splat value. 447 auto generateSplatKey = [=] { 448 return KeyTy(ty, data.take_front(1), 449 llvm::hash_value(ArrayRef<char>(splatValue ? 1 : 0)), 450 /*isSplat=*/true); 451 }; 452 453 // Handle the case where the potential splat value is 1 and the number of 454 // elements is non 8-bit aligned. 455 size_t numOddElements = numElements % CHAR_BIT; 456 if (splatValue && numOddElements != 0) { 457 // Check that all bits are set in the last value. 458 char lastElt = splatData.back(); 459 if (lastElt != llvm::maskTrailingOnes<unsigned char>(numOddElements)) 460 return KeyTy(ty, data, llvm::hash_value(data)); 461 462 // If this is the only element, the data is known to be a splat. 463 if (splatData.size() == 1) 464 return generateSplatKey(); 465 splatData = splatData.drop_back(); 466 } 467 468 // Check that the data buffer corresponds to a splat of the proper mask. 469 char mask = splatValue ? ~0 : 0; 470 return llvm::all_of(splatData, [mask](char c) { return c == mask; }) 471 ? generateSplatKey() 472 : KeyTy(ty, data, llvm::hash_value(data)); 473 } 474 475 /// Hash the key for the storage. 476 static llvm::hash_code hashKey(const KeyTy &key) { 477 return llvm::hash_combine(key.type, key.hashCode); 478 } 479 480 /// Construct a new storage instance. 481 static DenseElementsAttributeStorage * 482 construct(AttributeStorageAllocator &allocator, KeyTy key) { 483 // If the data buffer is non-empty, we copy it into the allocator with a 484 // 64-bit alignment. 485 ArrayRef<char> copy, data = key.data; 486 if (!data.empty()) { 487 char *rawData = reinterpret_cast<char *>( 488 allocator.allocate(data.size(), alignof(uint64_t))); 489 std::memcpy(rawData, data.data(), data.size()); 490 491 // If this is a boolean splat, make sure only the first bit is used. 492 if (key.isSplat && key.type.getElementTypeBitWidth() == 1) 493 rawData[0] &= 1; 494 copy = ArrayRef<char>(rawData, data.size()); 495 } 496 497 return new (allocator.allocate<DenseElementsAttributeStorage>()) 498 DenseElementsAttributeStorage(key.type, copy, key.isSplat); 499 } 500 501 ArrayRef<char> data; 502 bool isSplat; 503 }; 504 505 /// An attribute representing a reference to a tensor constant with opaque 506 /// content. 507 struct OpaqueElementsAttributeStorage : public AttributeStorage { 508 using KeyTy = std::tuple<Type, Dialect *, StringRef>; 509 510 OpaqueElementsAttributeStorage(Type type, Dialect *dialect, StringRef bytes) 511 : AttributeStorage(type), dialect(dialect), bytes(bytes) {} 512 513 /// Key equality and hash functions. 514 bool operator==(const KeyTy &key) const { 515 return key == std::make_tuple(getType(), dialect, bytes); 516 } 517 static unsigned hashKey(const KeyTy &key) { 518 return llvm::hash_combine(std::get<0>(key), std::get<1>(key), 519 std::get<2>(key)); 520 } 521 522 /// Construct a new storage instance. 523 static OpaqueElementsAttributeStorage * 524 construct(AttributeStorageAllocator &allocator, KeyTy key) { 525 // TODO(b/131468830): Provide a way to avoid copying content of large opaque 526 // tensors This will likely require a new reference attribute kind. 527 return new (allocator.allocate<OpaqueElementsAttributeStorage>()) 528 OpaqueElementsAttributeStorage(std::get<0>(key), std::get<1>(key), 529 allocator.copyInto(std::get<2>(key))); 530 } 531 532 Dialect *dialect; 533 StringRef bytes; 534 }; 535 536 /// An attribute representing a reference to a sparse vector or tensor object. 537 struct SparseElementsAttributeStorage : public AttributeStorage { 538 using KeyTy = std::tuple<Type, DenseIntElementsAttr, DenseElementsAttr>; 539 540 SparseElementsAttributeStorage(Type type, DenseIntElementsAttr indices, 541 DenseElementsAttr values) 542 : AttributeStorage(type), indices(indices), values(values) {} 543 544 /// Key equality and hash functions. 545 bool operator==(const KeyTy &key) const { 546 return key == std::make_tuple(getType(), indices, values); 547 } 548 static unsigned hashKey(const KeyTy &key) { 549 return llvm::hash_combine(std::get<0>(key), std::get<1>(key), 550 std::get<2>(key)); 551 } 552 553 /// Construct a new storage instance. 554 static SparseElementsAttributeStorage * 555 construct(AttributeStorageAllocator &allocator, KeyTy key) { 556 return new (allocator.allocate<SparseElementsAttributeStorage>()) 557 SparseElementsAttributeStorage(std::get<0>(key), std::get<1>(key), 558 std::get<2>(key)); 559 } 560 561 DenseIntElementsAttr indices; 562 DenseElementsAttr values; 563 }; 564 } // namespace detail 565 } // namespace mlir 566 567 #endif // ATTRIBUTEDETAIL_H_