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_