//===- BuiltinAttributes.h - MLIR Builtin Attribute Classes -----*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_IR_BUILTINATTRIBUTES_H
#define MLIR_IR_BUILTINATTRIBUTES_H

#include "mlir/IR/BuiltinAttributeInterfaces.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/Sequence.h"
#include <complex>
#include <optional>

namespace mlir {
class AffineMap;
class AsmResourceBlob;
class BoolAttr;
class BuiltinDialect;
class DenseIntElementsAttr;
template <typename T>
struct DialectResourceBlobHandle;
class FlatSymbolRefAttr;
class FunctionType;
class IntegerSet;
class IntegerType;
class Location;
class Operation;
class RankedTensorType;

namespace detail {
struct DenseIntOrFPElementsAttrStorage;
struct DenseStringElementsAttrStorage;
struct StringAttrStorage;
} // namespace detail

//===----------------------------------------------------------------------===//
// Elements Attributes
//===----------------------------------------------------------------------===//

namespace detail {
/// Pair of raw pointer and a boolean flag of whether the pointer holds a splat,
using DenseIterPtrAndSplat = std::pair<const char *, bool>;

/// Impl iterator for indexed DenseElementsAttr iterators that records a data
/// pointer and data index that is adjusted for the case of a splat attribute.
template <typename ConcreteT, typename T, typename PointerT = T *,
          typename ReferenceT = T &>
class DenseElementIndexedIteratorImpl
    : public llvm::indexed_accessor_iterator<ConcreteT, DenseIterPtrAndSplat, T,
                                             PointerT, ReferenceT> {
protected:
  DenseElementIndexedIteratorImpl(const char *data, bool isSplat,
                                  size_t dataIndex)
      : llvm::indexed_accessor_iterator<ConcreteT, DenseIterPtrAndSplat, T,
                                        PointerT, ReferenceT>({data, isSplat},
                                                              dataIndex) {}

  /// Return the current index for this iterator, adjusted for the case of a
  /// splat.
  ptrdiff_t getDataIndex() const {
    bool isSplat = this->base.second;
    return isSplat ? 0 : this->index;
  }

  /// Return the data base pointer.
  const char *getData() const { return this->base.first; }
};

/// Type trait detector that checks if a given type T is a complex type.
template <typename T>
struct is_complex_t : public std::false_type {};
template <typename T>
struct is_complex_t<std::complex<T>> : public std::true_type {};
} // namespace detail

/// An attribute that represents a reference to a dense vector or tensor
/// object.
class DenseElementsAttr : public Attribute {
public:
  using Attribute::Attribute;

  /// Allow implicit conversion to ElementsAttr.
  operator ElementsAttr() const { return cast_if_present<ElementsAttr>(*this); }
  /// Allow implicit conversion to TypedAttr.
  operator TypedAttr() const { return ElementsAttr(*this); }

  /// Type trait used to check if the given type T is a potentially valid C++
  /// floating point type that can be used to access the underlying element
  /// types of a DenseElementsAttr.
  template <typename T>
  struct is_valid_cpp_fp_type {
    /// The type is a valid floating point type if it is a builtin floating
    /// point type, or is a potentially user defined floating point type. The
    /// latter allows for supporting users that have custom types defined for
    /// bfloat16/half/etc.
    static constexpr bool value = llvm::is_one_of<T, float, double>::value ||
                                  (std::numeric_limits<T>::is_specialized &&
                                   !std::numeric_limits<T>::is_integer);
  };

  /// Method for support type inquiry through isa, cast and dyn_cast.
  static bool classof(Attribute attr);

  /// Constructs a dense elements attribute from an array of element values.
  /// Each element attribute value is expected to be an element of 'type'.
  /// 'type' must be a vector or tensor with static shape. If the element of
  /// `type` is non-integer/index/float it is assumed to be a string type.
  static DenseElementsAttr get(ShapedType type, ArrayRef<Attribute> values);

  /// Constructs a dense integer elements attribute from an array of integer
  /// or floating-point values. Each value is expected to be the same bitwidth
  /// of the element type of 'type'. 'type' must be a vector or tensor with
  /// static shape.
  template <typename T,
            typename = std::enable_if_t<std::numeric_limits<T>::is_integer ||
                                        is_valid_cpp_fp_type<T>::value>>
  static DenseElementsAttr get(const ShapedType &type, ArrayRef<T> values) {
    const char *data = reinterpret_cast<const char *>(values.data());
    return getRawIntOrFloat(
        type, ArrayRef<char>(data, values.size() * sizeof(T)), sizeof(T),
        std::numeric_limits<T>::is_integer, std::numeric_limits<T>::is_signed);
  }

  /// Constructs a dense integer elements attribute from a single element.
  template <typename T,
            typename = std::enable_if_t<std::numeric_limits<T>::is_integer ||
                                        is_valid_cpp_fp_type<T>::value ||
                                        detail::is_complex_t<T>::value>>
  static DenseElementsAttr get(const ShapedType &type, T value) {
    return get(type, llvm::ArrayRef(value));
  }

  /// Constructs a dense complex elements attribute from an array of complex
  /// values. Each value is expected to be the same bitwidth of the element type
  /// of 'type'. 'type' must be a vector or tensor with static shape.
  template <
      typename T, typename ElementT = typename T::value_type,
      typename = std::enable_if_t<detail::is_complex_t<T>::value &&
                                  (std::numeric_limits<ElementT>::is_integer ||
                                   is_valid_cpp_fp_type<ElementT>::value)>>
  static DenseElementsAttr get(const ShapedType &type, ArrayRef<T> values) {
    const char *data = reinterpret_cast<const char *>(values.data());
    return getRawComplex(type, ArrayRef<char>(data, values.size() * sizeof(T)),
                         sizeof(T), std::numeric_limits<ElementT>::is_integer,
                         std::numeric_limits<ElementT>::is_signed);
  }

  /// Overload of the above 'get' method that is specialized for boolean values.
  static DenseElementsAttr get(ShapedType type, ArrayRef<bool> values);

  /// Overload of the above 'get' method that is specialized for StringRef
  /// values.
  static DenseElementsAttr get(ShapedType type, ArrayRef<StringRef> values);

  /// Constructs a dense integer elements attribute from an array of APInt
  /// values. Each APInt value is expected to have the same bitwidth as the
  /// element type of 'type'. 'type' must be a vector or tensor with static
  /// shape.
  static DenseElementsAttr get(ShapedType type, ArrayRef<APInt> values);

  /// Constructs a dense complex elements attribute from an array of APInt
  /// values. Each APInt value is expected to have the same bitwidth as the
  /// element type of 'type'. 'type' must be a vector or tensor with static
  /// shape.
  static DenseElementsAttr get(ShapedType type,
                               ArrayRef<std::complex<APInt>> values);

  /// Constructs a dense float elements attribute from an array of APFloat
  /// values. Each APFloat value is expected to have the same bitwidth as the
  /// element type of 'type'. 'type' must be a vector or tensor with static
  /// shape.
  static DenseElementsAttr get(ShapedType type, ArrayRef<APFloat> values);

  /// Constructs a dense complex elements attribute from an array of APFloat
  /// values. Each APFloat value is expected to have the same bitwidth as the
  /// element type of 'type'. 'type' must be a vector or tensor with static
  /// shape.
  static DenseElementsAttr get(ShapedType type,
                               ArrayRef<std::complex<APFloat>> values);

  /// Construct a dense elements attribute for an initializer_list of values.
  /// Each value is expected to be the same bitwidth of the element type of
  /// 'type'. 'type' must be a vector or tensor with static shape.
  template <typename T>
  static DenseElementsAttr get(const ShapedType &type,
                               const std::initializer_list<T> &list) {
    return get(type, ArrayRef<T>(list));
  }

  /// Construct a dense elements attribute from a raw buffer representing the
  /// data for this attribute. Users are encouraged to use one of the
  /// constructors above, which provide more safeties. However, this
  /// constructor is useful for tools which may want to interop and can
  /// follow the precise definition.
  ///
  /// The format of the raw buffer is a densely packed array of values that
  /// can be bitcast to the storage format of the element type specified.
  /// Types that are not byte aligned will be:
  ///   - For bitwidth > 1: Rounded up to the next byte.
  ///   - For bitwidth = 1: Packed into 8bit bytes with bits corresponding to
  ///     the linear order of the shape type from MSB to LSB, padded to on the
  ///     right.
  static DenseElementsAttr getFromRawBuffer(ShapedType type,
                                            ArrayRef<char> rawBuffer);

  /// Returns true if the given buffer is a valid raw buffer for the given type.
  /// `detectedSplat` is set if the buffer is valid and represents a splat
  /// buffer. The definition may be expanded over time, but currently, a
  /// splat buffer is detected if:
  ///   - For >1bit: The buffer consists of a single element.
  ///   - For 1bit: The buffer consists of a single byte with value 0 or 255.
  ///
  /// User code should be prepared for additional, conformant patterns to be
  /// identified as splats in the future.
  static bool isValidRawBuffer(ShapedType type, ArrayRef<char> rawBuffer,
                               bool &detectedSplat);

  //===--------------------------------------------------------------------===//
  // Iterators
  //===--------------------------------------------------------------------===//

  /// The iterator range over the given iterator type T.
  template <typename IteratorT>
  using iterator_range_impl = detail::ElementsAttrRange<IteratorT>;

  /// The iterator for the given element type T.
  template <typename T, typename AttrT = DenseElementsAttr>
  using iterator = decltype(std::declval<AttrT>().template value_begin<T>());
  /// The iterator range over the given element T.
  template <typename T, typename AttrT = DenseElementsAttr>
  using iterator_range =
      decltype(std::declval<AttrT>().template getValues<T>());

  /// A utility iterator that allows walking over the internal Attribute values
  /// of a DenseElementsAttr.
  class AttributeElementIterator
      : public llvm::indexed_accessor_iterator<AttributeElementIterator,
                                               const void *, Attribute,
                                               Attribute, Attribute> {
  public:
    /// Accesses the Attribute value at this iterator position.
    Attribute operator*() const;

  private:
    friend DenseElementsAttr;

    /// Constructs a new iterator.
    AttributeElementIterator(DenseElementsAttr attr, size_t index);
  };

  /// Iterator for walking raw element values of the specified type 'T', which
  /// may be any c++ data type matching the stored representation: int32_t,
  /// float, etc.
  template <typename T>
  class ElementIterator
      : public detail::DenseElementIndexedIteratorImpl<ElementIterator<T>,
                                                       const T> {
  public:
    /// Accesses the raw value at this iterator position.
    const T &operator*() const {
      return reinterpret_cast<const T *>(this->getData())[this->getDataIndex()];
    }

  private:
    friend DenseElementsAttr;

    /// Constructs a new iterator.
    ElementIterator(const char *data, bool isSplat, size_t dataIndex)
        : detail::DenseElementIndexedIteratorImpl<ElementIterator<T>, const T>(
              data, isSplat, dataIndex) {}
  };

  /// A utility iterator that allows walking over the internal bool values.
  class BoolElementIterator
      : public detail::DenseElementIndexedIteratorImpl<BoolElementIterator,
                                                       bool, bool, bool> {
  public:
    /// Accesses the bool value at this iterator position.
    bool operator*() const;

  private:
    friend DenseElementsAttr;

    /// Constructs a new iterator.
    BoolElementIterator(DenseElementsAttr attr, size_t dataIndex);
  };

  /// A utility iterator that allows walking over the internal raw APInt values.
  class IntElementIterator
      : public detail::DenseElementIndexedIteratorImpl<IntElementIterator,
                                                       APInt, APInt, APInt> {
  public:
    /// Accesses the raw APInt value at this iterator position.
    APInt operator*() const;

  private:
    friend DenseElementsAttr;

    /// Constructs a new iterator.
    IntElementIterator(DenseElementsAttr attr, size_t dataIndex);

    /// The bitwidth of the element type.
    size_t bitWidth;
  };

  /// A utility iterator that allows walking over the internal raw complex APInt
  /// values.
  class ComplexIntElementIterator
      : public detail::DenseElementIndexedIteratorImpl<
            ComplexIntElementIterator, std::complex<APInt>, std::complex<APInt>,
            std::complex<APInt>> {
  public:
    /// Accesses the raw std::complex<APInt> value at this iterator position.
    std::complex<APInt> operator*() const;

  private:
    friend DenseElementsAttr;

    /// Constructs a new iterator.
    ComplexIntElementIterator(DenseElementsAttr attr, size_t dataIndex);

    /// The bitwidth of the element type.
    size_t bitWidth;
  };

  /// Iterator for walking over APFloat values.
  class FloatElementIterator final
      : public llvm::mapped_iterator_base<FloatElementIterator,
                                          IntElementIterator, APFloat> {
  public:
    /// Map the element to the iterator result type.
    APFloat mapElement(const APInt &value) const {
      return APFloat(*smt, value);
    }

  private:
    friend DenseElementsAttr;

    /// Initializes the float element iterator to the specified iterator.
    FloatElementIterator(const llvm::fltSemantics &smt, IntElementIterator it)
        : BaseT(it), smt(&smt) {}

    /// The float semantics to use when constructing the APFloat.
    const llvm::fltSemantics *smt;
  };

  /// Iterator for walking over complex APFloat values.
  class ComplexFloatElementIterator final
      : public llvm::mapped_iterator_base<ComplexFloatElementIterator,
                                          ComplexIntElementIterator,
                                          std::complex<APFloat>> {
  public:
    /// Map the element to the iterator result type.
    std::complex<APFloat> mapElement(const std::complex<APInt> &value) const {
      return {APFloat(*smt, value.real()), APFloat(*smt, value.imag())};
    }

  private:
    friend DenseElementsAttr;

    /// Initializes the float element iterator to the specified iterator.
    ComplexFloatElementIterator(const llvm::fltSemantics &smt,
                                ComplexIntElementIterator it)
        : BaseT(it), smt(&smt) {}

    /// The float semantics to use when constructing the APFloat.
    const llvm::fltSemantics *smt;
  };

  //===--------------------------------------------------------------------===//
  // Value Querying
  //===--------------------------------------------------------------------===//

  /// Returns true if this attribute corresponds to a splat, i.e. if all element
  /// values are the same.
  bool isSplat() const;

  /// Return the splat value for this attribute. This asserts that the attribute
  /// corresponds to a splat.
  template <typename T>
  std::enable_if_t<!std::is_base_of<Attribute, T>::value ||
                       std::is_same<Attribute, T>::value,
                   T>
  getSplatValue() const {
    assert(isSplat() && "expected the attribute to be a splat");
    return *value_begin<T>();
  }
  /// Return the splat value for derived attribute element types.
  template <typename T>
  std::enable_if_t<std::is_base_of<Attribute, T>::value &&
                       !std::is_same<Attribute, T>::value,
                   T>
  getSplatValue() const {
    return llvm::cast<T>(getSplatValue<Attribute>());
  }

  /// Try to get an iterator of the given type to the start of the held element
  /// values. Return failure if the type cannot be iterated.
  template <typename T>
  auto try_value_begin() const {
    auto range = tryGetValues<T>();
    using iterator = decltype(range->begin());
    return failed(range) ? FailureOr<iterator>(failure()) : range->begin();
  }

  /// Try to get an iterator of the given type to the end of the held element
  /// values. Return failure if the type cannot be iterated.
  template <typename T>
  auto try_value_end() const {
    auto range = tryGetValues<T>();
    using iterator = decltype(range->begin());
    return failed(range) ? FailureOr<iterator>(failure()) : range->end();
  }

  /// Return the held element values as a range of the given type.
  template <typename T>
  auto getValues() const {
    auto range = tryGetValues<T>();
    assert(succeeded(range) && "element type cannot be iterated");
    return std::move(*range);
  }

  /// Get an iterator of the given type to the start of the held element values.
  template <typename T>
  auto value_begin() const {
    return getValues<T>().begin();
  }

  /// Get an iterator of the given type to the end of the held element values.
  template <typename T>
  auto value_end() const {
    return getValues<T>().end();
  }

  /// Try to get the held element values as a range of integer or floating-point
  /// values.
  template <typename T>
  using IntFloatValueTemplateCheckT =
      std::enable_if_t<(!std::is_same<T, bool>::value &&
                        std::numeric_limits<T>::is_integer) ||
                       is_valid_cpp_fp_type<T>::value>;
  template <typename T, typename = IntFloatValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<ElementIterator<T>>> tryGetValues() const {
    if (!isValidIntOrFloat(sizeof(T), std::numeric_limits<T>::is_integer,
                           std::numeric_limits<T>::is_signed))
      return failure();
    const char *rawData = getRawData().data();
    bool splat = isSplat();
    return iterator_range_impl<ElementIterator<T>>(
        getType(), ElementIterator<T>(rawData, splat, 0),
        ElementIterator<T>(rawData, splat, getNumElements()));
  }

  /// Try to get the held element values as a range of std::complex.
  template <typename T, typename ElementT>
  using ComplexValueTemplateCheckT =
      std::enable_if_t<detail::is_complex_t<T>::value &&
                       (std::numeric_limits<ElementT>::is_integer ||
                        is_valid_cpp_fp_type<ElementT>::value)>;
  template <typename T, typename ElementT = typename T::value_type,
            typename = ComplexValueTemplateCheckT<T, ElementT>>
  FailureOr<iterator_range_impl<ElementIterator<T>>> tryGetValues() const {
    if (!isValidComplex(sizeof(T), std::numeric_limits<ElementT>::is_integer,
                        std::numeric_limits<ElementT>::is_signed))
      return failure();
    const char *rawData = getRawData().data();
    bool splat = isSplat();
    return iterator_range_impl<ElementIterator<T>>(
        getType(), ElementIterator<T>(rawData, splat, 0),
        ElementIterator<T>(rawData, splat, getNumElements()));
  }

  /// Try to get the held element values as a range of StringRef.
  template <typename T>
  using StringRefValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, StringRef>::value>;
  template <typename T, typename = StringRefValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<ElementIterator<StringRef>>>
  tryGetValues() const {
    auto stringRefs = getRawStringData();
    const char *ptr = reinterpret_cast<const char *>(stringRefs.data());
    bool splat = isSplat();
    return iterator_range_impl<ElementIterator<StringRef>>(
        getType(), ElementIterator<StringRef>(ptr, splat, 0),
        ElementIterator<StringRef>(ptr, splat, getNumElements()));
  }

  /// Try to get the held element values as a range of Attributes.
  template <typename T>
  using AttributeValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, Attribute>::value>;
  template <typename T, typename = AttributeValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<AttributeElementIterator>>
  tryGetValues() const {
    return iterator_range_impl<AttributeElementIterator>(
        getType(), AttributeElementIterator(*this, 0),
        AttributeElementIterator(*this, getNumElements()));
  }

  /// Try to get the held element values a range of T, where T is a derived
  /// attribute type.
  template <typename T>
  using DerivedAttrValueTemplateCheckT =
      std::enable_if_t<std::is_base_of<Attribute, T>::value &&
                       !std::is_same<Attribute, T>::value>;
  template <typename T>
  struct DerivedAttributeElementIterator
      : public llvm::mapped_iterator_base<DerivedAttributeElementIterator<T>,
                                          AttributeElementIterator, T> {
    using llvm::mapped_iterator_base<DerivedAttributeElementIterator<T>,
                                     AttributeElementIterator,
                                     T>::mapped_iterator_base;

    /// Map the element to the iterator result type.
    T mapElement(Attribute attr) const { return llvm::cast<T>(attr); }
  };
  template <typename T, typename = DerivedAttrValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<DerivedAttributeElementIterator<T>>>
  tryGetValues() const {
    using DerivedIterT = DerivedAttributeElementIterator<T>;
    return iterator_range_impl<DerivedIterT>(
        getType(), DerivedIterT(value_begin<Attribute>()),
        DerivedIterT(value_end<Attribute>()));
  }

  /// Try to get the held element values as a range of bool. The element type of
  /// this attribute must be of integer type of bitwidth 1.
  template <typename T>
  using BoolValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, bool>::value>;
  template <typename T, typename = BoolValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<BoolElementIterator>> tryGetValues() const {
    if (!isValidBool())
      return failure();
    return iterator_range_impl<BoolElementIterator>(
        getType(), BoolElementIterator(*this, 0),
        BoolElementIterator(*this, getNumElements()));
  }

  /// Try to get the held element values as a range of APInts. The element type
  /// of this attribute must be of integer type.
  template <typename T>
  using APIntValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, APInt>::value>;
  template <typename T, typename = APIntValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<IntElementIterator>> tryGetValues() const {
    if (!getElementType().isIntOrIndex())
      return failure();
    return iterator_range_impl<IntElementIterator>(getType(), raw_int_begin(),
                                                   raw_int_end());
  }

  /// Try to get the held element values as a range of complex APInts. The
  /// element type of this attribute must be a complex of integer type.
  template <typename T>
  using ComplexAPIntValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, std::complex<APInt>>::value>;
  template <typename T, typename = ComplexAPIntValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<ComplexIntElementIterator>>
  tryGetValues() const {
    return tryGetComplexIntValues();
  }

  /// Try to get the held element values as a range of APFloat. The element type
  /// of this attribute must be of float type.
  template <typename T>
  using APFloatValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, APFloat>::value>;
  template <typename T, typename = APFloatValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<FloatElementIterator>> tryGetValues() const {
    return tryGetFloatValues();
  }

  /// Try to get the held element values as a range of complex APFloat. The
  /// element type of this attribute must be a complex of float type.
  template <typename T>
  using ComplexAPFloatValueTemplateCheckT =
      std::enable_if_t<std::is_same<T, std::complex<APFloat>>::value>;
  template <typename T, typename = ComplexAPFloatValueTemplateCheckT<T>>
  FailureOr<iterator_range_impl<ComplexFloatElementIterator>>
  tryGetValues() const {
    return tryGetComplexFloatValues();
  }

  /// Return the raw storage data held by this attribute. Users should generally
  /// not use this directly, as the internal storage format is not always in the
  /// form the user might expect.
  ArrayRef<char> getRawData() const;

  /// Return the raw StringRef data held by this attribute.
  ArrayRef<StringRef> getRawStringData() const;

  /// Return the type of this ElementsAttr, guaranteed to be a vector or tensor
  /// with static shape.
  ShapedType getType() const;

  /// Return the element type of this DenseElementsAttr.
  Type getElementType() const;

  /// Returns the number of elements held by this attribute.
  int64_t getNumElements() const;

  /// Returns the number of elements held by this attribute.
  int64_t size() const { return getNumElements(); }

  /// Returns if the number of elements held by this attribute is 0.
  bool empty() const { return size() == 0; }

  //===--------------------------------------------------------------------===//
  // Mutation Utilities
  //===--------------------------------------------------------------------===//

  /// Return a new DenseElementsAttr that has the same data as the current
  /// attribute, but has been reshaped to 'newType'. The new type must have the
  /// same total number of elements as well as element type.
  DenseElementsAttr reshape(ShapedType newType);

  /// Return a new DenseElementsAttr that has the same data as the current
  /// attribute, but with a different shape for a splat type. The new type must
  /// have the same element type.
  DenseElementsAttr resizeSplat(ShapedType newType);

  /// Return a new DenseElementsAttr that has the same data as the current
  /// attribute, but has bitcast elements to 'newElType'. The new type must have
  /// the same bitwidth as the current element type.
  DenseElementsAttr bitcast(Type newElType);

  /// Generates a new DenseElementsAttr by mapping each int value to a new
  /// underlying APInt. The new values can represent either an integer or float.
  /// This underlying type must be an DenseIntElementsAttr.
  DenseElementsAttr mapValues(Type newElementType,
                              function_ref<APInt(const APInt &)> mapping) const;

  /// Generates a new DenseElementsAttr by mapping each float value to a new
  /// underlying APInt. the new values can represent either an integer or float.
  /// This underlying type must be an DenseFPElementsAttr.
  DenseElementsAttr
  mapValues(Type newElementType,
            function_ref<APInt(const APFloat &)> mapping) const;

protected:
  /// Iterators to various elements that require out-of-line definition. These
  /// are hidden from the user to encourage consistent use of the
  /// getValues/value_begin/value_end API.
  IntElementIterator raw_int_begin() const {
    return IntElementIterator(*this, 0);
  }
  IntElementIterator raw_int_end() const {
    return IntElementIterator(*this, getNumElements());
  }
  FailureOr<iterator_range_impl<ComplexIntElementIterator>>
  tryGetComplexIntValues() const;
  FailureOr<iterator_range_impl<FloatElementIterator>>
  tryGetFloatValues() const;
  FailureOr<iterator_range_impl<ComplexFloatElementIterator>>
  tryGetComplexFloatValues() const;

  /// Overload of the raw 'get' method that asserts that the given type is of
  /// complex type. This method is used to verify type invariants that the
  /// templatized 'get' method cannot.
  static DenseElementsAttr getRawComplex(ShapedType type, ArrayRef<char> data,
                                         int64_t dataEltSize, bool isInt,
                                         bool isSigned);

  /// Overload of the raw 'get' method that asserts that the given type is of
  /// integer or floating-point type. This method is used to verify type
  /// invariants that the templatized 'get' method cannot.
  static DenseElementsAttr getRawIntOrFloat(ShapedType type,
                                            ArrayRef<char> data,
                                            int64_t dataEltSize, bool isInt,
                                            bool isSigned);

  /// Check the information for a C++ data type, check if this type is valid for
  /// the current attribute. This method is used to verify specific type
  /// invariants that the templatized 'getValues' method cannot.
  bool isValidBool() const { return getElementType().isInteger(1); }
  bool isValidIntOrFloat(int64_t dataEltSize, bool isInt, bool isSigned) const;
  bool isValidComplex(int64_t dataEltSize, bool isInt, bool isSigned) const;
};

/// An attribute that represents a reference to a splat vector or tensor
/// constant, meaning all of the elements have the same value.
class SplatElementsAttr : public DenseElementsAttr {
public:
  using DenseElementsAttr::DenseElementsAttr;

  /// Method for support type inquiry through isa, cast and dyn_cast.
  static bool classof(Attribute attr) {
    auto denseAttr = llvm::dyn_cast<DenseElementsAttr>(attr);
    return denseAttr && denseAttr.isSplat();
  }
};

//===----------------------------------------------------------------------===//
// DenseResourceElementsAttr
//===----------------------------------------------------------------------===//

using DenseResourceElementsHandle = DialectResourceBlobHandle<BuiltinDialect>;

} // namespace mlir

//===----------------------------------------------------------------------===//
// Tablegen Attribute Declarations
//===----------------------------------------------------------------------===//

#define GET_ATTRDEF_CLASSES
#include "mlir/IR/BuiltinAttributes.h.inc"

//===----------------------------------------------------------------------===//
// C++ Attribute Declarations
//===----------------------------------------------------------------------===//

namespace mlir {
//===----------------------------------------------------------------------===//
// DenseArrayAttr
//===----------------------------------------------------------------------===//

namespace detail {
/// Base class for DenseArrayAttr that is instantiated and specialized for each
/// supported element type below.
template <typename T>
class DenseArrayAttrImpl : public DenseArrayAttr {
public:
  using DenseArrayAttr::DenseArrayAttr;

  /// Implicit conversion to ArrayRef<T>.
  operator ArrayRef<T>() const;
  ArrayRef<T> asArrayRef() const { return ArrayRef<T>{*this}; }

  /// Random access to elements.
  T operator[](std::size_t index) const { return asArrayRef()[index]; }

  /// Builder from ArrayRef<T>.
  static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef<T> content);

  /// Print the short form `[42, 100, -1]` without any type prefix.
  void print(AsmPrinter &printer) const;
  void print(raw_ostream &os) const;
  /// Print the short form `42, 100, -1` without any braces or type prefix.
  void printWithoutBraces(raw_ostream &os) const;

  /// Parse the short form `[42, 100, -1]` without any type prefix.
  static Attribute parse(AsmParser &parser, Type type);

  /// Parse the short form `42, 100, -1` without any type prefix or braces.
  static Attribute parseWithoutBraces(AsmParser &parser, Type type);

  /// Support for isa<>/cast<>.
  static bool classof(Attribute attr);
};

extern template class DenseArrayAttrImpl<bool>;
extern template class DenseArrayAttrImpl<int8_t>;
extern template class DenseArrayAttrImpl<int16_t>;
extern template class DenseArrayAttrImpl<int32_t>;
extern template class DenseArrayAttrImpl<int64_t>;
extern template class DenseArrayAttrImpl<float>;
extern template class DenseArrayAttrImpl<double>;
} // namespace detail

// Public name for all the supported DenseArrayAttr
using DenseBoolArrayAttr = detail::DenseArrayAttrImpl<bool>;
using DenseI8ArrayAttr = detail::DenseArrayAttrImpl<int8_t>;
using DenseI16ArrayAttr = detail::DenseArrayAttrImpl<int16_t>;
using DenseI32ArrayAttr = detail::DenseArrayAttrImpl<int32_t>;
using DenseI64ArrayAttr = detail::DenseArrayAttrImpl<int64_t>;
using DenseF32ArrayAttr = detail::DenseArrayAttrImpl<float>;
using DenseF64ArrayAttr = detail::DenseArrayAttrImpl<double>;

//===----------------------------------------------------------------------===//
// DenseResourceElementsAttr
//===----------------------------------------------------------------------===//

namespace detail {
/// Base class for DenseResourceElementsAttr that is instantiated and
/// specialized for each supported element type below.
template <typename T>
class DenseResourceElementsAttrBase : public DenseResourceElementsAttr {
public:
  using DenseResourceElementsAttr::DenseResourceElementsAttr;

  /// A builder that inserts a new resource using the provided blob. The handle
  /// of the inserted blob is used when building the attribute. The provided
  /// `blobName` is used as a hint for the key of the new handle for the `blob`
  /// resource, but may be changed if necessary to ensure uniqueness during
  /// insertion.
  static DenseResourceElementsAttrBase<T>
  get(ShapedType type, StringRef blobName, AsmResourceBlob blob);

  /// Return the data of this attribute as an ArrayRef<T> if it is present,
  /// returns std::nullopt otherwise.
  std::optional<ArrayRef<T>> tryGetAsArrayRef() const;

  /// Support for isa<>/cast<>.
  static bool classof(Attribute attr);
};

extern template class DenseResourceElementsAttrBase<bool>;
extern template class DenseResourceElementsAttrBase<int8_t>;
extern template class DenseResourceElementsAttrBase<int16_t>;
extern template class DenseResourceElementsAttrBase<int32_t>;
extern template class DenseResourceElementsAttrBase<int64_t>;
extern template class DenseResourceElementsAttrBase<uint8_t>;
extern template class DenseResourceElementsAttrBase<uint16_t>;
extern template class DenseResourceElementsAttrBase<uint32_t>;
extern template class DenseResourceElementsAttrBase<uint64_t>;
extern template class DenseResourceElementsAttrBase<float>;
extern template class DenseResourceElementsAttrBase<double>;
} // namespace detail

// Public names for all the supported DenseResourceElementsAttr.

using DenseBoolResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<bool>;
using DenseI8ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<int8_t>;
using DenseI16ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<int16_t>;
using DenseI32ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<int32_t>;
using DenseI64ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<int64_t>;
using DenseUI8ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<uint8_t>;
using DenseUI16ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<uint16_t>;
using DenseUI32ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<uint32_t>;
using DenseUI64ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<uint64_t>;
using DenseF32ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<float>;
using DenseF64ResourceElementsAttr =
    detail::DenseResourceElementsAttrBase<double>;

//===----------------------------------------------------------------------===//
// BoolAttr
//===----------------------------------------------------------------------===//

/// Special case of IntegerAttr to represent boolean integers, i.e., signless i1
/// integers.
class BoolAttr : public Attribute {
public:
  using Attribute::Attribute;
  using ValueType = bool;

  static BoolAttr get(MLIRContext *context, bool value);

  /// Enable conversion to IntegerAttr and its interfaces. This uses conversion
  /// vs. inheritance to avoid bringing in all of IntegerAttrs methods.
  operator IntegerAttr() const { return IntegerAttr(impl); }
  operator TypedAttr() const { return IntegerAttr(impl); }

  /// Return the boolean value of this attribute.
  bool getValue() const;

  /// Methods for support type inquiry through isa, cast, and dyn_cast.
  static bool classof(Attribute attr);
};

//===----------------------------------------------------------------------===//
// FlatSymbolRefAttr
//===----------------------------------------------------------------------===//

/// A symbol reference with a reference path containing a single element. This
/// is used to refer to an operation within the current symbol table.
class FlatSymbolRefAttr : public SymbolRefAttr {
public:
  using SymbolRefAttr::SymbolRefAttr;
  using ValueType = StringRef;

  /// Construct a symbol reference for the given value name.
  static FlatSymbolRefAttr get(StringAttr value) {
    return SymbolRefAttr::get(value);
  }
  static FlatSymbolRefAttr get(MLIRContext *ctx, StringRef value) {
    return SymbolRefAttr::get(ctx, value);
  }

  /// Convenience getter for building a SymbolRefAttr based on an operation
  /// that implements the SymbolTrait.
  static FlatSymbolRefAttr get(Operation *symbol) {
    return SymbolRefAttr::get(symbol);
  }

  /// Returns the name of the held symbol reference as a StringAttr.
  StringAttr getAttr() const { return getRootReference(); }

  /// Returns the name of the held symbol reference.
  StringRef getValue() const { return getAttr().getValue(); }

  /// Methods for support type inquiry through isa, cast, and dyn_cast.
  static bool classof(Attribute attr) {
    SymbolRefAttr refAttr = llvm::dyn_cast<SymbolRefAttr>(attr);
    return refAttr && refAttr.getNestedReferences().empty();
  }

private:
  using SymbolRefAttr::get;
  using SymbolRefAttr::getNestedReferences;
};

//===----------------------------------------------------------------------===//
// DenseFPElementsAttr
//===----------------------------------------------------------------------===//

/// An attribute that represents a reference to a dense float vector or tensor
/// object. Each element is stored as a double.
class DenseFPElementsAttr : public DenseIntOrFPElementsAttr {
public:
  using iterator = DenseElementsAttr::FloatElementIterator;

  using DenseIntOrFPElementsAttr::DenseIntOrFPElementsAttr;

  /// Get an instance of a DenseFPElementsAttr with the given arguments. This
  /// simply wraps the DenseElementsAttr::get calls.
  template <typename Arg>
  static DenseFPElementsAttr get(const ShapedType &type, Arg &&arg) {
    return llvm::cast<DenseFPElementsAttr>(
        DenseElementsAttr::get(type, llvm::ArrayRef(arg)));
  }
  template <typename T>
  static DenseFPElementsAttr get(const ShapedType &type,
                                 const std::initializer_list<T> &list) {
    return llvm::cast<DenseFPElementsAttr>(DenseElementsAttr::get(type, list));
  }

  /// Generates a new DenseElementsAttr by mapping each value attribute, and
  /// constructing the DenseElementsAttr given the new element type.
  DenseElementsAttr
  mapValues(Type newElementType,
            function_ref<APInt(const APFloat &)> mapping) const;

  /// Iterator access to the float element values.
  iterator begin() const { return tryGetFloatValues()->begin(); }
  iterator end() const { return tryGetFloatValues()->end(); }

  /// Method for supporting type inquiry through isa, cast and dyn_cast.
  static bool classof(Attribute attr);
};

//===----------------------------------------------------------------------===//
// DenseIntElementsAttr
//===----------------------------------------------------------------------===//

/// An attribute that represents a reference to a dense integer vector or tensor
/// object.
class DenseIntElementsAttr : public DenseIntOrFPElementsAttr {
public:
  /// DenseIntElementsAttr iterates on APInt, so we can use the raw element
  /// iterator directly.
  using iterator = DenseElementsAttr::IntElementIterator;

  using DenseIntOrFPElementsAttr::DenseIntOrFPElementsAttr;

  /// Get an instance of a DenseIntElementsAttr with the given arguments. This
  /// simply wraps the DenseElementsAttr::get calls.
  template <typename Arg>
  static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg) {
    return llvm::cast<DenseIntElementsAttr>(
        DenseElementsAttr::get(type, llvm::ArrayRef(arg)));
  }
  template <typename T>
  static DenseIntElementsAttr get(const ShapedType &type,
                                  const std::initializer_list<T> &list) {
    return llvm::cast<DenseIntElementsAttr>(DenseElementsAttr::get(type, list));
  }

  /// Generates a new DenseElementsAttr by mapping each value attribute, and
  /// constructing the DenseElementsAttr given the new element type.
  DenseElementsAttr mapValues(Type newElementType,
                              function_ref<APInt(const APInt &)> mapping) const;

  /// Iterator access to the integer element values.
  iterator begin() const { return raw_int_begin(); }
  iterator end() const { return raw_int_end(); }

  /// Method for supporting type inquiry through isa, cast and dyn_cast.
  static bool classof(Attribute attr);
};

//===----------------------------------------------------------------------===//
// SparseElementsAttr
//===----------------------------------------------------------------------===//

template <typename T>
auto SparseElementsAttr::try_value_begin_impl(OverloadToken<T>) const
    -> FailureOr<iterator<T>> {
  auto zeroValue = getZeroValue<T>();
  auto valueIt = getValues().try_value_begin<T>();
  if (failed(valueIt))
    return failure();
  const SmallVector<ptrdiff_t> flatSparseIndices(getFlattenedSparseIndices());
  std::function<T(ptrdiff_t)> mapFn =
      [flatSparseIndices{flatSparseIndices}, valueIt{std::move(*valueIt)},
       zeroValue{std::move(zeroValue)}](ptrdiff_t index) {
        // Try to map the current index to one of the sparse indices.
        for (unsigned i = 0, e = flatSparseIndices.size(); i != e; ++i)
          if (flatSparseIndices[i] == index)
            return *std::next(valueIt, i);
        // Otherwise, return the zero value.
        return zeroValue;
      };
  return iterator<T>(llvm::seq<ptrdiff_t>(0, getNumElements()).begin(), mapFn);
}

//===----------------------------------------------------------------------===//
// DistinctAttr
//===----------------------------------------------------------------------===//

namespace detail {
struct DistinctAttrStorage;
class DistinctAttributeUniquer;
} // namespace detail

/// An attribute that associates a referenced attribute with a unique
/// identifier. Every call to the create function allocates a new distinct
/// attribute instance. The address of the attribute instance serves as a
/// temporary identifier. Similar to the names of SSA values, the final
/// identifiers are generated during pretty printing. This delayed numbering
/// ensures the printed identifiers are deterministic even if multiple distinct
/// attribute instances are created in-parallel.
///
/// Examples:
///
/// #distinct = distinct[0]<42.0 : f32>
/// #distinct1 = distinct[1]<42.0 : f32>
/// #distinct2 = distinct[2]<array<i32: 10, 42>>
///
/// NOTE: The distinct attribute cannot be defined using ODS since it uses a
/// custom distinct attribute uniquer that cannot be set from ODS.
class DistinctAttr
    : public detail::StorageUserBase<DistinctAttr, Attribute,
                                     detail::DistinctAttrStorage,
                                     detail::DistinctAttributeUniquer> {
public:
  using Base::Base;

  /// Returns the referenced attribute.
  Attribute getReferencedAttr() const;

  /// Creates a distinct attribute that associates a referenced attribute with a
  /// unique identifier.
  static DistinctAttr create(Attribute referencedAttr);

  static constexpr StringLiteral name = "builtin.distinct";
};

//===----------------------------------------------------------------------===//
// StringAttr
//===----------------------------------------------------------------------===//

/// Define comparisons for StringAttr against nullptr and itself to avoid the
/// StringRef overloads from being chosen when not desirable.
inline bool operator==(StringAttr lhs, std::nullptr_t) { return !lhs; }
inline bool operator!=(StringAttr lhs, std::nullptr_t) {
  return static_cast<bool>(lhs);
}
inline bool operator==(StringAttr lhs, StringAttr rhs) {
  return (Attribute)lhs == (Attribute)rhs;
}
inline bool operator!=(StringAttr lhs, StringAttr rhs) { return !(lhs == rhs); }

/// Allow direct comparison with StringRef.
inline bool operator==(StringAttr lhs, StringRef rhs) {
  return lhs.getValue() == rhs;
}
inline bool operator!=(StringAttr lhs, StringRef rhs) { return !(lhs == rhs); }
inline bool operator==(StringRef lhs, StringAttr rhs) {
  return rhs.getValue() == lhs;
}
inline bool operator!=(StringRef lhs, StringAttr rhs) { return !(lhs == rhs); }

} // namespace mlir

//===----------------------------------------------------------------------===//
// Attribute Utilities
//===----------------------------------------------------------------------===//

namespace mlir {

/// Given a list of strides (in which ShapedType::kDynamic
/// represents a dynamic value), return the single result AffineMap which
/// represents the linearized strided layout map. Dimensions correspond to the
/// offset followed by the strides in order. Symbols are inserted for each
/// dynamic dimension in order. A stride is always positive.
///
/// Examples:
/// =========
///
///   1. For offset: 0 strides: ?, ?, 1 return
///         (i, j, k)[M, N]->(M * i + N * j + k)
///
///   2. For offset: 3 strides: 32, ?, 16 return
///         (i, j, k)[M]->(3 + 32 * i + M * j + 16 * k)
///
///   3. For offset: ? strides: ?, ?, ? return
///         (i, j, k)[off, M, N, P]->(off + M * i + N * j + P * k)
AffineMap makeStridedLinearLayoutMap(ArrayRef<int64_t> strides, int64_t offset,
                                     MLIRContext *context);

} // namespace mlir

namespace llvm {

template <>
struct DenseMapInfo<mlir::StringAttr> : public DenseMapInfo<mlir::Attribute> {
  static mlir::StringAttr getEmptyKey() {
    const void *pointer = llvm::DenseMapInfo<const void *>::getEmptyKey();
    return mlir::StringAttr::getFromOpaquePointer(pointer);
  }
  static mlir::StringAttr getTombstoneKey() {
    const void *pointer = llvm::DenseMapInfo<const void *>::getTombstoneKey();
    return mlir::StringAttr::getFromOpaquePointer(pointer);
  }
};
template <>
struct PointerLikeTypeTraits<mlir::StringAttr>
    : public PointerLikeTypeTraits<mlir::Attribute> {
  static inline mlir::StringAttr getFromVoidPointer(void *p) {
    return mlir::StringAttr::getFromOpaquePointer(p);
  }
};

template <>
struct PointerLikeTypeTraits<mlir::IntegerAttr>
    : public PointerLikeTypeTraits<mlir::Attribute> {
  static inline mlir::IntegerAttr getFromVoidPointer(void *p) {
    return mlir::IntegerAttr::getFromOpaquePointer(p);
  }
};

template <>
struct PointerLikeTypeTraits<mlir::SymbolRefAttr>
    : public PointerLikeTypeTraits<mlir::Attribute> {
  static inline mlir::SymbolRefAttr getFromVoidPointer(void *ptr) {
    return mlir::SymbolRefAttr::getFromOpaquePointer(ptr);
  }
};

} // namespace llvm

#endif // MLIR_IR_BUILTINATTRIBUTES_H
