[Mlir-commits] [mlir] d6da02d - [llvm] Add enum iteration to Sequence

Guillaume Chatelet llvmlistbot at llvm.org
Wed Jul 21 05:49:05 PDT 2021


Author: Guillaume Chatelet
Date: 2021-07-21T12:48:53Z
New Revision: d6da02d952470ac976824da9aefdfb13ea1dde34

URL: https://github.com/llvm/llvm-project/commit/d6da02d952470ac976824da9aefdfb13ea1dde34
DIFF: https://github.com/llvm/llvm-project/commit/d6da02d952470ac976824da9aefdfb13ea1dde34.diff

LOG: [llvm] Add enum iteration to Sequence

This patch allows iterating typed enum via the ADT/Sequence utility.

It also changes the original design to better separate concerns:
 - `StrongInt` only deals with safe `intmax_t` operations,
 - `SafeIntIterator` presents the iterator and reverse iterator
 interface but only deals with safe `StrongInt` internally.
 - `iota_range` only deals with `SafeIntIterator` internally.

 This design ensures that operations are always valid. In particular,
 "Out of bounds" assertions fire when:
  - the `value_type` is not representable as an `intmax_t`
  - iterator operations make internal computation underflow/overflow
  - the internal representation cannot be converted back to `value_type`

Differential Revision: https://reviews.llvm.org/D106279

Added: 
    

Modified: 
    llvm/include/llvm/ADT/Sequence.h
    llvm/include/llvm/Support/MachineValueType.h
    llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
    llvm/tools/llvm-exegesis/lib/X86/Target.cpp
    llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp
    llvm/unittests/ADT/SequenceTest.cpp
    llvm/unittests/CodeGen/ScalableVectorMVTsTest.cpp
    llvm/unittests/IR/ConstantRangeTest.cpp
    mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/Sequence.h b/llvm/include/llvm/ADT/Sequence.h
index d033c01ecc573..3e4bf0932222a 100644
--- a/llvm/include/llvm/ADT/Sequence.h
+++ b/llvm/include/llvm/ADT/Sequence.h
@@ -15,163 +15,223 @@
 #ifndef LLVM_ADT_SEQUENCE_H
 #define LLVM_ADT_SEQUENCE_H
 
-#include <cstddef>  //std::ptr
diff _t
-#include <iterator> //std::random_access_iterator_tag
+#include <cassert>     // assert
+#include <iterator>    // std::random_access_iterator_tag
+#include <limits>      // std::numeric_limits
+#include <type_traits> // std::underlying_type, std::is_enum
+
+#include "llvm/Support/MathExtras.h" // AddOverflow / SubOverflow
 
 namespace llvm {
 
 namespace detail {
 
-template <typename T, bool IsReversed> struct iota_range_iterator {
-  using iterator_category = std::random_access_iterator_tag;
-  using value_type = T;
-  using 
diff erence_type = std::ptr
diff _t;
-  using pointer = T *;
-  using reference = T &;
+// Returns whether a value of type U can be represented with type T.
+template <typename T, typename U> bool canTypeFitValue(const U Value) {
+  const intmax_t BotT = intmax_t(std::numeric_limits<T>::min());
+  const intmax_t BotU = intmax_t(std::numeric_limits<U>::min());
+  const uintmax_t TopT = uintmax_t(std::numeric_limits<T>::max());
+  const uintmax_t TopU = uintmax_t(std::numeric_limits<U>::max());
+  return !((BotT > BotU && Value < static_cast<U>(BotT)) ||
+           (TopT < TopU && Value > static_cast<U>(TopT)));
+}
 
-private:
-  struct Forward {
-    static void increment(T &V) { ++V; }
-    static void decrement(T &V) { --V; }
-    static void offset(T &V, 
diff erence_type Offset) { V += Offset; }
-    static T add(const T &V, 
diff erence_type Offset) { return V + Offset; }
-    static 
diff erence_type 
diff erence(const T &A, const T &B) { return A - B; }
-  };
-
-  struct Reverse {
-    static void increment(T &V) { --V; }
-    static void decrement(T &V) { ++V; }
-    static void offset(T &V, 
diff erence_type Offset) { V -= Offset; }
-    static T add(const T &V, 
diff erence_type Offset) { return V - Offset; }
-    static 
diff erence_type 
diff erence(const T &A, const T &B) { return B - A; }
-  };
-
-  using Op = std::conditional_t<!IsReversed, Forward, Reverse>;
-
-public:
-  // default-constructible
-  iota_range_iterator() = default;
-  // copy-constructible
-  iota_range_iterator(const iota_range_iterator &) = default;
-  // value constructor
-  explicit iota_range_iterator(T Value) : Value(Value) {}
-  // copy-assignable
-  iota_range_iterator &operator=(const iota_range_iterator &) = default;
-  // destructible
-  ~iota_range_iterator() = default;
-
-  // Can be compared for equivalence using the equality/inequality operators,
-  bool operator!=(const iota_range_iterator &RHS) const {
-    return Value != RHS.Value;
-  }
-  bool operator==(const iota_range_iterator &RHS) const {
-    return Value == RHS.Value;
+// An integer type that asserts when:
+// - constructed from a value that doesn't fit into intmax_t,
+// - casted to a type that cannot hold the current value,
+// - its internal representation overflows.
+struct CheckedInt {
+  // Integral constructor, asserts if Value cannot be represented as intmax_t.
+  template <typename Integral, typename std::enable_if_t<
+                                   std::is_integral<Integral>::value, bool> = 0>
+  static CheckedInt from(Integral FromValue) {
+    if (!canTypeFitValue<intmax_t>(FromValue))
+      assertOutOfBounds();
+    CheckedInt Result;
+    Result.Value = static_cast<intmax_t>(FromValue);
+    return Result;
   }
 
-  // Comparison
-  bool operator<(const iota_range_iterator &Other) const {
-    return Op::
diff erence(Value, Other.Value) < 0;
-  }
-  bool operator<=(const iota_range_iterator &Other) const {
-    return Op::
diff erence(Value, Other.Value) <= 0;
-  }
-  bool operator>(const iota_range_iterator &Other) const {
-    return Op::
diff erence(Value, Other.Value) > 0;
-  }
-  bool operator>=(const iota_range_iterator &Other) const {
-    return Op::
diff erence(Value, Other.Value) >= 0;
+  // Enum constructor, asserts if Value cannot be represented as intmax_t.
+  template <typename Enum,
+            typename std::enable_if_t<std::is_enum<Enum>::value, bool> = 0>
+  static CheckedInt from(Enum FromValue) {
+    using type = typename std::underlying_type<Enum>::type;
+    return from<type>(static_cast<type>(FromValue));
   }
 
-  // Dereference
-  T operator*() const { return Value; }
-  T operator[](
diff erence_type Offset) const { return Op::add(Value, Offset); }
+  // Equality
+  bool operator==(const CheckedInt &O) const { return Value == O.Value; }
+  bool operator!=(const CheckedInt &O) const { return Value != O.Value; }
 
-  // Arithmetic
-  iota_range_iterator operator+(
diff erence_type Offset) const {
-    return {Op::add(Value, Offset)};
-  }
-  iota_range_iterator operator-(
diff erence_type Offset) const {
-    return {Op::add(Value, -Offset)};
+  CheckedInt operator+(intmax_t Offset) const {
+    CheckedInt Result;
+    if (AddOverflow(Value, Offset, Result.Value))
+      assertOutOfBounds();
+    return Result;
   }
 
-  // Iterator 
diff erence
-  
diff erence_type operator-(const iota_range_iterator &Other) const {
-    return Op::
diff erence(Value, Other.Value);
+  intmax_t operator-(CheckedInt Other) const {
+    intmax_t Result;
+    if (SubOverflow(Value, Other.Value, Result))
+      assertOutOfBounds();
+    return Result;
   }
 
-  // Pre/Post Increment
-  iota_range_iterator &operator++() {
-    Op::increment(Value);
-    return *this;
+  // Convert to integral, asserts if Value cannot be represented as Integral.
+  template <typename Integral, typename std::enable_if_t<
+                                   std::is_integral<Integral>::value, bool> = 0>
+  Integral to() const {
+    if (!canTypeFitValue<Integral>(Value))
+      assertOutOfBounds();
+    return static_cast<Integral>(Value);
   }
-  iota_range_iterator operator++(int) {
-    iota_range_iterator Tmp = *this;
-    Op::increment(Value);
-    return Tmp;
+
+  // Convert to enum, asserts if Value cannot be represented as Enum's
+  // underlying type.
+  template <typename Enum,
+            typename std::enable_if_t<std::is_enum<Enum>::value, bool> = 0>
+  Enum to() const {
+    using type = typename std::underlying_type<Enum>::type;
+    return Enum(to<type>());
   }
 
-  // Pre/Post Decrement
-  iota_range_iterator &operator--() {
-    Op::decrement(Value);
-    return *this;
+private:
+  static void assertOutOfBounds() { assert(false && "Out of bounds"); }
+
+  intmax_t Value;
+};
+
+template <typename T, bool IsReverse> struct SafeIntIterator {
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = T;
+  using 
diff erence_type = intmax_t;
+  using pointer = T *;
+  using reference = T &;
+
+  // Construct from T.
+  explicit SafeIntIterator(T Value) : SI(CheckedInt::from<T>(Value)) {}
+  // Construct from other direction.
+  SafeIntIterator(const SafeIntIterator<T, !IsReverse> &O) : SI(O.SI) {}
+
+  // Dereference
+  value_type operator*() const { return SI.to<T>(); }
+  // Indexing
+  value_type operator[](intmax_t Offset) const { return *(*this + Offset); }
+
+  // Can be compared for equivalence using the equality/inequality operators.
+  bool operator==(const SafeIntIterator &O) const { return SI == O.SI; }
+  bool operator!=(const SafeIntIterator &O) const { return SI != O.SI; }
+  // Comparison
+  bool operator<(const SafeIntIterator &O) const { return (*this - O) < 0; }
+  bool operator>(const SafeIntIterator &O) const { return (*this - O) > 0; }
+  bool operator<=(const SafeIntIterator &O) const { return (*this - O) <= 0; }
+  bool operator>=(const SafeIntIterator &O) const { return (*this - O) >= 0; }
+
+  // Pre Increment/Decrement
+  void operator++() { offset(1); }
+  void operator--() { offset(-1); }
+
+  // Post Increment/Decrement
+  SafeIntIterator operator++(int) {
+    const auto Copy = *this;
+    ++*this;
+    return Copy;
   }
-  iota_range_iterator operator--(int) {
-    iota_range_iterator Tmp = *this;
-    Op::decrement(Value);
-    return Tmp;
+  SafeIntIterator operator--(int) {
+    const auto Copy = *this;
+    --*this;
+    return Copy;
   }
 
   // Compound assignment operators
-  iota_range_iterator &operator+=(
diff erence_type Offset) {
-    Op::offset(Value, Offset);
-    return *this;
-  }
-  iota_range_iterator &operator-=(
diff erence_type Offset) {
-    Op::offset(Value, -Offset);
-    return *this;
+  void operator+=(intmax_t Offset) { offset(Offset); }
+  void operator-=(intmax_t Offset) { offset(-Offset); }
+
+  // Arithmetic
+  SafeIntIterator operator+(intmax_t Offset) const { return add(Offset); }
+  SafeIntIterator operator-(intmax_t Offset) const { return add(-Offset); }
+
+  // Difference
+  intmax_t operator-(const SafeIntIterator &O) const {
+    return IsReverse ? O.SI - SI : SI - O.SI;
   }
 
 private:
-  T Value;
+  SafeIntIterator(const CheckedInt &SI) : SI(SI) {}
+
+  static intmax_t getOffset(intmax_t Offset) {
+    return IsReverse ? -Offset : Offset;
+  }
+
+  CheckedInt add(intmax_t Offset) const { return SI + getOffset(Offset); }
+
+  void offset(intmax_t Offset) { SI = SI + getOffset(Offset); }
+
+  CheckedInt SI;
+
+  // To allow construction from the other direction.
+  template <typename, bool> friend struct SafeIntIterator;
 };
 
 } // namespace detail
 
-template <typename ValueT> struct iota_range {
-  static_assert(std::is_integral<ValueT>::value,
-                "ValueT must be an integral type");
-
-  using value_type = ValueT;
-  using reference = ValueT &;
-  using const_reference = const ValueT &;
-  using iterator = detail::iota_range_iterator<value_type, false>;
+template <typename T> struct iota_range {
+  using value_type = T;
+  using reference = T &;
+  using const_reference = const T &;
+  using iterator = detail::SafeIntIterator<value_type, false>;
   using const_iterator = iterator;
-  using reverse_iterator = detail::iota_range_iterator<value_type, true>;
+  using reverse_iterator = detail::SafeIntIterator<value_type, true>;
   using const_reverse_iterator = reverse_iterator;
-  using 
diff erence_type = std::ptr
diff _t;
+  using 
diff erence_type = intmax_t;
   using size_type = std::size_t;
 
-  value_type Begin;
-  value_type End;
-
-  explicit iota_range(ValueT Begin, ValueT End) : Begin(Begin), End(End) {}
+  explicit iota_range(T Begin, T End, bool Inclusive)
+      : BeginValue(Begin), PastEndValue(End) {
+    assert(Begin <= End && "Begin must be less or equal to End.");
+    if (Inclusive)
+      ++PastEndValue;
+  }
 
-  size_t size() const { return End - Begin; }
-  bool empty() const { return Begin == End; }
+  size_t size() const { return PastEndValue - BeginValue; }
+  bool empty() const { return BeginValue == PastEndValue; }
 
-  auto begin() const { return const_iterator(Begin); }
-  auto end() const { return const_iterator(End); }
+  auto begin() const { return const_iterator(BeginValue); }
+  auto end() const { return const_iterator(PastEndValue); }
 
-  auto rbegin() const { return const_reverse_iterator(End - 1); }
-  auto rend() const { return const_reverse_iterator(Begin - 1); }
+  auto rbegin() const { return const_reverse_iterator(PastEndValue - 1); }
+  auto rend() const { return const_reverse_iterator(BeginValue - 1); }
 
 private:
-  static_assert(std::is_same<ValueT, std::remove_cv_t<ValueT>>::value,
-                "ValueT must not be const nor volatile");
+  static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
+                "T must be an integral or enum type");
+  static_assert(std::is_same<T, std::remove_cv_t<T>>::value,
+                "T must not be const nor volatile");
+
+  iterator BeginValue;
+  iterator PastEndValue;
 };
 
-template <typename ValueT> auto seq(ValueT Begin, ValueT End) {
-  return iota_range<ValueT>(Begin, End);
+/// Iterate over an integral/enum type from Begin up to - but not including -
+/// End.
+/// Note on enum iteration: `seq` will generate each consecutive value, even if
+/// no enumerator with that value exists.
+/// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for
+/// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse
+/// iteration).
+template <typename T> auto seq(T Begin, T End) {
+  return iota_range<T>(Begin, End, false);
+}
+
+/// Iterate over an integral/enum type from Begin to End inclusive.
+/// Note on enum iteration: `seq_inclusive` will generate each consecutive
+/// value, even if no enumerator with that value exists.
+/// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1]
+/// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse
+/// iteration).
+template <typename T> auto seq_inclusive(T Begin, T End) {
+  return iota_range<T>(Begin, End, true);
 }
 
 } // end namespace llvm

diff  --git a/llvm/include/llvm/Support/MachineValueType.h b/llvm/include/llvm/Support/MachineValueType.h
index 4b8d937bde379..31f2d5a481832 100644
--- a/llvm/include/llvm/Support/MachineValueType.h
+++ b/llvm/include/llvm/Support/MachineValueType.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_SUPPORT_MACHINEVALUETYPE_H
 #define LLVM_SUPPORT_MACHINEVALUETYPE_H
 
+#include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MathExtras.h"
@@ -1398,84 +1399,55 @@ namespace llvm {
     /// returned as Other, otherwise they are invalid.
     static MVT getVT(Type *Ty, bool HandleUnknown = false);
 
-  private:
-    /// A simple iterator over the MVT::SimpleValueType enum.
-    struct mvt_iterator {
-      SimpleValueType VT;
-
-      mvt_iterator(SimpleValueType VT) : VT(VT) {}
-
-      MVT operator*() const { return VT; }
-      bool operator!=(const mvt_iterator &LHS) const { return VT != LHS.VT; }
-
-      mvt_iterator& operator++() {
-        VT = (MVT::SimpleValueType)((int)VT + 1);
-        assert((int)VT <= MVT::MAX_ALLOWED_VALUETYPE &&
-               "MVT iterator overflowed.");
-        return *this;
-      }
-    };
-
-    /// A range of the MVT::SimpleValueType enum.
-    using mvt_range = iterator_range<mvt_iterator>;
-
   public:
     /// SimpleValueType Iteration
     /// @{
-    static mvt_range all_valuetypes() {
-      return mvt_range(MVT::FIRST_VALUETYPE,
-                       (MVT::SimpleValueType)(MVT::LAST_VALUETYPE + 1));
+    static auto all_valuetypes() {
+      return seq_inclusive(MVT::FIRST_VALUETYPE, MVT::LAST_VALUETYPE);
     }
 
-    static mvt_range integer_valuetypes() {
-      return mvt_range(MVT::FIRST_INTEGER_VALUETYPE,
-                       (MVT::SimpleValueType)(MVT::LAST_INTEGER_VALUETYPE + 1));
+    static auto integer_valuetypes() {
+      return seq_inclusive(MVT::FIRST_INTEGER_VALUETYPE,
+                           MVT::LAST_INTEGER_VALUETYPE);
     }
 
-    static mvt_range fp_valuetypes() {
-      return mvt_range(MVT::FIRST_FP_VALUETYPE,
-                       (MVT::SimpleValueType)(MVT::LAST_FP_VALUETYPE + 1));
+    static auto fp_valuetypes() {
+      return seq_inclusive(MVT::FIRST_FP_VALUETYPE, MVT::LAST_FP_VALUETYPE);
     }
 
-    static mvt_range vector_valuetypes() {
-      return mvt_range(MVT::FIRST_VECTOR_VALUETYPE,
-                       (MVT::SimpleValueType)(MVT::LAST_VECTOR_VALUETYPE + 1));
+    static auto vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_VECTOR_VALUETYPE,
+                           MVT::LAST_VECTOR_VALUETYPE);
     }
 
-    static mvt_range fixedlen_vector_valuetypes() {
-      return mvt_range(
-               MVT::FIRST_FIXEDLEN_VECTOR_VALUETYPE,
-               (MVT::SimpleValueType)(MVT::LAST_FIXEDLEN_VECTOR_VALUETYPE + 1));
+    static auto fixedlen_vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_FIXEDLEN_VECTOR_VALUETYPE,
+                           MVT::LAST_FIXEDLEN_VECTOR_VALUETYPE);
     }
 
-    static mvt_range scalable_vector_valuetypes() {
-      return mvt_range(
-               MVT::FIRST_SCALABLE_VECTOR_VALUETYPE,
-               (MVT::SimpleValueType)(MVT::LAST_SCALABLE_VECTOR_VALUETYPE + 1));
+    static auto scalable_vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_SCALABLE_VECTOR_VALUETYPE,
+                           MVT::LAST_SCALABLE_VECTOR_VALUETYPE);
     }
 
-    static mvt_range integer_fixedlen_vector_valuetypes() {
-      return mvt_range(
-       MVT::FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE,
-       (MVT::SimpleValueType)(MVT::LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE + 1));
+    static auto integer_fixedlen_vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE,
+                           MVT::LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE);
     }
 
-    static mvt_range fp_fixedlen_vector_valuetypes() {
-      return mvt_range(
-          MVT::FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE,
-          (MVT::SimpleValueType)(MVT::LAST_FP_FIXEDLEN_VECTOR_VALUETYPE + 1));
+    static auto fp_fixedlen_vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE,
+                           MVT::LAST_FP_FIXEDLEN_VECTOR_VALUETYPE);
     }
 
-    static mvt_range integer_scalable_vector_valuetypes() {
-      return mvt_range(
-       MVT::FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE,
-       (MVT::SimpleValueType)(MVT::LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE + 1));
+    static auto integer_scalable_vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE,
+                           MVT::LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE);
     }
 
-    static mvt_range fp_scalable_vector_valuetypes() {
-      return mvt_range(
-            MVT::FIRST_FP_SCALABLE_VECTOR_VALUETYPE,
-            (MVT::SimpleValueType)(MVT::LAST_FP_SCALABLE_VECTOR_VALUETYPE + 1));
+    static auto fp_scalable_vector_valuetypes() {
+      return seq_inclusive(MVT::FIRST_FP_SCALABLE_VECTOR_VALUETYPE,
+                           MVT::LAST_FP_SCALABLE_VECTOR_VALUETYPE);
     }
     /// @}
   };

diff  --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index f286bc9067b75..91242bbf866f9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -4634,8 +4634,7 @@ SDValue DAGTypeLegalizer::WidenVecOp_EXTEND(SDNode *N) {
   EVT InVT = InOp.getValueType();
   if (InVT.getSizeInBits() != VT.getSizeInBits()) {
     EVT InEltVT = InVT.getVectorElementType();
-    for (int i = MVT::FIRST_VECTOR_VALUETYPE, e = MVT::LAST_VECTOR_VALUETYPE; i < e; ++i) {
-      EVT FixedVT = (MVT::SimpleValueType)i;
+    for (EVT FixedVT : MVT::vector_valuetypes()) {
       EVT FixedEltVT = FixedVT.getVectorElementType();
       if (TLI.isTypeLegal(FixedVT) &&
           FixedVT.getSizeInBits() == VT.getSizeInBits() &&
@@ -5162,14 +5161,11 @@ static EVT FindMemType(SelectionDAG& DAG, const TargetLowering &TLI,
   if (!Scalable && Width == WidenEltWidth)
     return RetVT;
 
-  // See if there is larger legal integer than the element type to load/store.
-  unsigned VT;
   // Don't bother looking for an integer type if the vector is scalable, skip
   // to vector types.
   if (!Scalable) {
-    for (VT = (unsigned)MVT::LAST_INTEGER_VALUETYPE;
-         VT >= (unsigned)MVT::FIRST_INTEGER_VALUETYPE; --VT) {
-      EVT MemVT((MVT::SimpleValueType) VT);
+    // See if there is larger legal integer than the element type to load/store.
+    for (EVT MemVT : reverse(MVT::integer_valuetypes())) {
       unsigned MemVTWidth = MemVT.getSizeInBits();
       if (MemVT.getSizeInBits() <= WidenEltWidth)
         break;
@@ -5190,9 +5186,7 @@ static EVT FindMemType(SelectionDAG& DAG, const TargetLowering &TLI,
 
   // See if there is a larger vector type to load/store that has the same vector
   // element type and is evenly divisible with the WidenVT.
-  for (VT = (unsigned)MVT::LAST_VECTOR_VALUETYPE;
-       VT >= (unsigned)MVT::FIRST_VECTOR_VALUETYPE; --VT) {
-    EVT MemVT = (MVT::SimpleValueType) VT;
+  for (EVT MemVT : reverse(MVT::vector_valuetypes())) {
     // Skip vector MVTs which don't match the scalable property of WidenVT.
     if (Scalable != MemVT.isScalableVector())
       continue;

diff  --git a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
index 40fed0789a819..1be119a508d54 100644
--- a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
+++ b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp
@@ -918,9 +918,9 @@ std::vector<InstructionTemplate> ExegesisX86Target::generateInstructionVariants(
       continue;
     case X86::OperandType::OPERAND_COND_CODE: {
       Exploration = true;
-      auto CondCodes = seq((int)X86::CondCode::COND_O,
-                           1 + (int)X86::CondCode::LAST_VALID_COND);
-      Choices.reserve(std::distance(CondCodes.begin(), CondCodes.end()));
+      auto CondCodes =
+          seq_inclusive(X86::CondCode::COND_O, X86::CondCode::LAST_VALID_COND);
+      Choices.reserve(CondCodes.size());
       for (int CondCode : CondCodes)
         Choices.emplace_back(MCOperand::createImm(CondCode));
       break;

diff  --git a/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp b/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp
index 26b77bfd5aba5..223866ba52c25 100644
--- a/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp
+++ b/llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp
@@ -84,7 +84,8 @@ class AttributeRemapper : public InstVisitor<AttributeRemapper> {
                           AttrPtrVecVecTy &AttributeSetsToPreserve) {
     assert(AttributeSetsToPreserve.empty() && "Should not be sharing vectors.");
     AttributeSetsToPreserve.reserve(AL.getNumAttrSets());
-    for (unsigned SetIdx : seq(AL.index_begin(), AL.index_end())) {
+    for (unsigned SetIdx = AL.index_begin(), SetEndIdx = AL.index_end();
+         SetIdx != SetEndIdx; ++SetIdx) {
       AttrPtrIdxVecVecTy AttributesToPreserve;
       AttributesToPreserve.first = SetIdx;
       visitAttributeSet(AL.getAttributes(AttributesToPreserve.first),

diff  --git a/llvm/unittests/ADT/SequenceTest.cpp b/llvm/unittests/ADT/SequenceTest.cpp
index f10e80ff4125c..4ed54f1589d05 100644
--- a/llvm/unittests/ADT/SequenceTest.cpp
+++ b/llvm/unittests/ADT/SequenceTest.cpp
@@ -7,30 +7,197 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/Sequence.h"
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
-#include <list>
+#include <algorithm>
+#include <numeric>
 
 using namespace llvm;
 
+using testing::ElementsAre;
+
 namespace {
 
-TEST(SequenceTest, Forward) {
-  int X = 0;
-  for (int I : seq(0, 10)) {
-    EXPECT_EQ(X, I);
-    ++X;
-  }
-  EXPECT_EQ(10, X);
+using detail::canTypeFitValue;
+using detail::CheckedInt;
+
+using IntegralTypes = testing::Types<uint8_t,   // 0
+                                     uint16_t,  // 1
+                                     uint32_t,  // 2
+                                     uint64_t,  // 3
+                                     uintmax_t, // 4
+                                     int8_t,    // 5
+                                     int16_t,   // 6
+                                     int32_t,   // 7
+                                     int64_t,   // 8
+                                     intmax_t   // 9
+                                     >;
+
+template <class T> class StrongIntTest : public testing::Test {};
+TYPED_TEST_SUITE(StrongIntTest, IntegralTypes);
+TYPED_TEST(StrongIntTest, Operations) {
+  using T = TypeParam;
+  auto Max = std::numeric_limits<T>::max();
+  auto Min = std::numeric_limits<T>::min();
+
+  // We bail out for types that are not entirely representable within intmax_t.
+  if (!canTypeFitValue<intmax_t>(Max) || !canTypeFitValue<intmax_t>(Min))
+    return;
+
+  // All representable values convert back and forth.
+  EXPECT_EQ(CheckedInt::from(Min).template to<T>(), Min);
+  EXPECT_EQ(CheckedInt::from(Max).template to<T>(), Max);
+
+  // Addition -2, -1, 0, 1, 2.
+  const T Expected = Max / 2;
+  const CheckedInt Actual = CheckedInt::from(Expected);
+  EXPECT_EQ((Actual + -2).template to<T>(), Expected - 2);
+  EXPECT_EQ((Actual + -1).template to<T>(), Expected - 1);
+  EXPECT_EQ((Actual + 0).template to<T>(), Expected);
+  EXPECT_EQ((Actual + 1).template to<T>(), Expected + 1);
+  EXPECT_EQ((Actual + 2).template to<T>(), Expected + 2);
+
+  // EQ/NEQ
+  EXPECT_EQ(Actual, Actual);
+  EXPECT_NE(Actual, Actual + 1);
+
+  // Difference
+  EXPECT_EQ(Actual - Actual, 0);
+  EXPECT_EQ((Actual + 1) - Actual, 1);
+  EXPECT_EQ(Actual - (Actual + 2), -2);
+}
+
+TEST(StrongIntTest, Enums) {
+  enum UntypedEnum { A = 3 };
+  EXPECT_EQ(CheckedInt::from(A).to<UntypedEnum>(), A);
+
+  enum TypedEnum : uint32_t { B = 3 };
+  EXPECT_EQ(CheckedInt::from(B).to<TypedEnum>(), B);
+
+  enum class ScopedEnum : uint16_t { C = 3 };
+  EXPECT_EQ(CheckedInt::from(ScopedEnum::C).to<ScopedEnum>(), ScopedEnum::C);
+}
+
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
+TEST(StrongIntDeathTest, OutOfBounds) {
+  // Values above 'INTMAX_MAX' are not representable.
+  EXPECT_DEATH(CheckedInt::from<uintmax_t>(INTMAX_MAX + 1ULL), "Out of bounds");
+  EXPECT_DEATH(CheckedInt::from<uintmax_t>(UINTMAX_MAX), "Out of bounds");
+  // Casting to narrower type asserts when out of bounds.
+  EXPECT_DEATH(CheckedInt::from(-1).to<uint8_t>(), "Out of bounds");
+  EXPECT_DEATH(CheckedInt::from(256).to<uint8_t>(), "Out of bounds");
+  // Operations leading to intmax_t overflow assert.
+  EXPECT_DEATH(CheckedInt::from(INTMAX_MAX) + 1, "Out of bounds");
+  EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) + -1, "Out of bounds");
+  EXPECT_DEATH(CheckedInt::from(INTMAX_MIN) - CheckedInt::from(INTMAX_MAX),
+               "Out of bounds");
+}
+#endif
+
+TEST(SafeIntIteratorTest, Operations) {
+  detail::SafeIntIterator<int, false> Forward(0);
+  detail::SafeIntIterator<int, true> Reverse(0);
+
+  const auto SetToZero = [&]() {
+    Forward = detail::SafeIntIterator<int, false>(0);
+    Reverse = detail::SafeIntIterator<int, true>(0);
+  };
+
+  // Equality / Comparisons
+  SetToZero();
+  EXPECT_EQ(Forward, Forward);
+  EXPECT_LT(Forward - 1, Forward);
+  EXPECT_LE(Forward, Forward);
+  EXPECT_LE(Forward - 1, Forward);
+  EXPECT_GT(Forward + 1, Forward);
+  EXPECT_GE(Forward, Forward);
+  EXPECT_GE(Forward + 1, Forward);
+
+  EXPECT_EQ(Reverse, Reverse);
+  EXPECT_LT(Reverse - 1, Reverse);
+  EXPECT_LE(Reverse, Reverse);
+  EXPECT_LE(Reverse - 1, Reverse);
+  EXPECT_GT(Reverse + 1, Reverse);
+  EXPECT_GE(Reverse, Reverse);
+  EXPECT_GE(Reverse + 1, Reverse);
+
+  // Dereference
+  SetToZero();
+  EXPECT_EQ(*Forward, 0);
+  EXPECT_EQ(*Reverse, 0);
+
+  // Indexing
+  SetToZero();
+  EXPECT_EQ(Forward[2], 2);
+  EXPECT_EQ(Reverse[2], -2);
+
+  // Pre-increment
+  SetToZero();
+  ++Forward;
+  EXPECT_EQ(*Forward, 1);
+  ++Reverse;
+  EXPECT_EQ(*Reverse, -1);
+
+  // Pre-decrement
+  SetToZero();
+  --Forward;
+  EXPECT_EQ(*Forward, -1);
+  --Reverse;
+  EXPECT_EQ(*Reverse, 1);
+
+  // Post-increment
+  SetToZero();
+  EXPECT_EQ(*(Forward++), 0);
+  EXPECT_EQ(*Forward, 1);
+  EXPECT_EQ(*(Reverse++), 0);
+  EXPECT_EQ(*Reverse, -1);
+
+  // Post-decrement
+  SetToZero();
+  EXPECT_EQ(*(Forward--), 0);
+  EXPECT_EQ(*Forward, -1);
+  EXPECT_EQ(*(Reverse--), 0);
+  EXPECT_EQ(*Reverse, 1);
+
+  // Compound assignment operators
+  SetToZero();
+  Forward += 1;
+  EXPECT_EQ(*Forward, 1);
+  Reverse += 1;
+  EXPECT_EQ(*Reverse, -1);
+  SetToZero();
+  Forward -= 2;
+  EXPECT_EQ(*Forward, -2);
+  Reverse -= 2;
+  EXPECT_EQ(*Reverse, 2);
+
+  // Arithmetic
+  SetToZero();
+  EXPECT_EQ(*(Forward + 3), 3);
+  EXPECT_EQ(*(Reverse + 3), -3);
+  SetToZero();
+  EXPECT_EQ(*(Forward - 4), -4);
+  EXPECT_EQ(*(Reverse - 4), 4);
+
+  // Difference
+  SetToZero();
+  EXPECT_EQ(Forward - Forward, 0);
+  EXPECT_EQ(Reverse - Reverse, 0);
+  EXPECT_EQ((Forward + 1) - Forward, 1);
+  EXPECT_EQ(Forward - (Forward + 1), -1);
+  EXPECT_EQ((Reverse + 1) - Reverse, 1);
+  EXPECT_EQ(Reverse - (Reverse + 1), -1);
 }
 
-TEST(SequenceTest, Backward) {
-  int X = 9;
-  for (int I : reverse(seq(0, 10))) {
-    EXPECT_EQ(X, I);
-    --X;
-  }
-  EXPECT_EQ(-1, X);
+TEST(SequenceTest, Iteration) {
+  EXPECT_THAT(seq(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4));
+  EXPECT_THAT(reverse(seq(-4, 5)), ElementsAre(4, 3, 2, 1, 0, -1, -2, -3, -4));
+
+  EXPECT_THAT(seq_inclusive(-4, 5),
+              ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4, 5));
+  EXPECT_THAT(reverse(seq_inclusive(-4, 5)),
+              ElementsAre(5, 4, 3, 2, 1, 0, -1, -2, -3, -4));
 }
 
 TEST(SequenceTest, Distance) {

diff  --git a/llvm/unittests/CodeGen/ScalableVectorMVTsTest.cpp b/llvm/unittests/CodeGen/ScalableVectorMVTsTest.cpp
index e38d1045586cb..1f6d23859186e 100644
--- a/llvm/unittests/CodeGen/ScalableVectorMVTsTest.cpp
+++ b/llvm/unittests/CodeGen/ScalableVectorMVTsTest.cpp
@@ -18,7 +18,7 @@ using namespace llvm;
 namespace {
 
 TEST(ScalableVectorMVTsTest, IntegerMVTs) {
-  for (auto VecTy : MVT::integer_scalable_vector_valuetypes()) {
+  for (MVT VecTy : MVT::integer_scalable_vector_valuetypes()) {
     ASSERT_TRUE(VecTy.isValid());
     ASSERT_TRUE(VecTy.isInteger());
     ASSERT_TRUE(VecTy.isVector());
@@ -30,7 +30,7 @@ TEST(ScalableVectorMVTsTest, IntegerMVTs) {
 }
 
 TEST(ScalableVectorMVTsTest, FloatMVTs) {
-  for (auto VecTy : MVT::fp_scalable_vector_valuetypes()) {
+  for (MVT VecTy : MVT::fp_scalable_vector_valuetypes()) {
     ASSERT_TRUE(VecTy.isValid());
     ASSERT_TRUE(VecTy.isFloatingPoint());
     ASSERT_TRUE(VecTy.isVector());

diff  --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index f8816e4d43df4..8eca261e65e29 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -1551,9 +1551,9 @@ void ICmpTestImpl(CmpInst::Predicate Pred) {
 }
 
 TEST(ConstantRange, ICmp) {
-  for (auto Pred : seq<unsigned>(CmpInst::Predicate::FIRST_ICMP_PREDICATE,
-                                 1 + CmpInst::Predicate::LAST_ICMP_PREDICATE))
-    ICmpTestImpl((CmpInst::Predicate)Pred);
+  for (auto Pred : seq_inclusive(CmpInst::Predicate::FIRST_ICMP_PREDICATE,
+                                 CmpInst::Predicate::LAST_ICMP_PREDICATE))
+    ICmpTestImpl(Pred);
 }
 
 TEST(ConstantRange, MakeGuaranteedNoWrapRegion) {

diff  --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
index 23927a0fd2ca4..9ef9a05c1284e 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -1173,7 +1173,7 @@ getCollapsedOutputDimFromInputShape(OpBuilder &builder, Location loc,
   unsigned endPos = map.getResults().back().cast<AffineDimExpr>().getPosition();
   AffineExpr expr;
   SmallVector<Value, 2> dynamicDims;
-  for (auto dim : llvm::seq(startPos, endPos + 1)) {
+  for (auto dim : llvm::seq_inclusive(startPos, endPos)) {
     dynamicDims.push_back(builder.createOrFold<tensor::DimOp>(loc, src, dim));
     AffineExpr currExpr = builder.getAffineSymbolExpr(dim - startPos);
     expr = (expr ? expr * currExpr : currExpr);
@@ -1206,7 +1206,7 @@ getExpandedDimToCollapsedDimMap(ArrayRef<AffineMap> reassociation) {
         map.value().getResults().front().cast<AffineDimExpr>().getPosition();
     unsigned endPos =
         map.value().getResults().back().cast<AffineDimExpr>().getPosition();
-    for (auto dim : llvm::seq(startPos, endPos + 1)) {
+    for (auto dim : llvm::seq_inclusive(startPos, endPos)) {
       expandedDimToCollapsedDim[dim] = map.index();
     }
   }


        


More information about the Mlir-commits mailing list