1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 04:02:41 +01:00

[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
This commit is contained in:
Guillaume Chatelet 2021-07-21 12:48:09 +00:00
parent 912cf6c6cb
commit a29bc1a45f
8 changed files with 405 additions and 211 deletions

View File

@ -15,163 +15,223 @@
#ifndef LLVM_ADT_SEQUENCE_H
#define LLVM_ADT_SEQUENCE_H
#include <cstddef> //std::ptrdiff_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 {
// 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)));
}
// 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;
}
// 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));
}
// Equality
bool operator==(const CheckedInt &O) const { return Value == O.Value; }
bool operator!=(const CheckedInt &O) const { return Value != O.Value; }
CheckedInt operator+(intmax_t Offset) const {
CheckedInt Result;
if (AddOverflow(Value, Offset, Result.Value))
assertOutOfBounds();
return Result;
}
intmax_t operator-(CheckedInt Other) const {
intmax_t Result;
if (SubOverflow(Value, Other.Value, Result))
assertOutOfBounds();
return Result;
}
// 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);
}
// 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>());
}
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 difference_type = std::ptrdiff_t;
using difference_type = intmax_t;
using pointer = T *;
using reference = T &;
private:
struct Forward {
static void increment(T &V) { ++V; }
static void decrement(T &V) { --V; }
static void offset(T &V, difference_type Offset) { V += Offset; }
static T add(const T &V, difference_type Offset) { return V + Offset; }
static difference_type difference(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, difference_type Offset) { V -= Offset; }
static T add(const T &V, difference_type Offset) { return V - Offset; }
static difference_type difference(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;
}
// Comparison
bool operator<(const iota_range_iterator &Other) const {
return Op::difference(Value, Other.Value) < 0;
}
bool operator<=(const iota_range_iterator &Other) const {
return Op::difference(Value, Other.Value) <= 0;
}
bool operator>(const iota_range_iterator &Other) const {
return Op::difference(Value, Other.Value) > 0;
}
bool operator>=(const iota_range_iterator &Other) const {
return Op::difference(Value, Other.Value) >= 0;
}
// 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
T operator*() const { return Value; }
T operator[](difference_type Offset) const { return Op::add(Value, Offset); }
value_type operator*() const { return SI.to<T>(); }
// Indexing
value_type operator[](intmax_t Offset) const { return *(*this + Offset); }
// Arithmetic
iota_range_iterator operator+(difference_type Offset) const {
return {Op::add(Value, Offset)};
}
iota_range_iterator operator-(difference_type Offset) const {
return {Op::add(Value, -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; }
// Iterator difference
difference_type operator-(const iota_range_iterator &Other) const {
return Op::difference(Value, Other.Value);
}
// Pre Increment/Decrement
void operator++() { offset(1); }
void operator--() { offset(-1); }
// Pre/Post Increment
iota_range_iterator &operator++() {
Op::increment(Value);
return *this;
// Post Increment/Decrement
SafeIntIterator operator++(int) {
const auto Copy = *this;
++*this;
return Copy;
}
iota_range_iterator operator++(int) {
iota_range_iterator Tmp = *this;
Op::increment(Value);
return Tmp;
}
// Pre/Post Decrement
iota_range_iterator &operator--() {
Op::decrement(Value);
return *this;
}
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+=(difference_type Offset) {
Op::offset(Value, Offset);
return *this;
}
iota_range_iterator &operator-=(difference_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 difference_type = std::ptrdiff_t;
using difference_type = intmax_t;
using size_type = std::size_t;
value_type Begin;
value_type 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;
}
explicit iota_range(ValueT Begin, ValueT End) : Begin(Begin), End(End) {}
size_t size() const { return PastEndValue - BeginValue; }
bool empty() const { return BeginValue == PastEndValue; }
size_t size() const { return End - Begin; }
bool empty() const { return Begin == End; }
auto begin() const { return const_iterator(BeginValue); }
auto end() const { return const_iterator(PastEndValue); }
auto begin() const { return const_iterator(Begin); }
auto end() const { return const_iterator(End); }
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

View File

@ -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);
}
/// @}
};

View File

@ -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;

View File

@ -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;

View File

@ -84,7 +84,8 @@ public:
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),

View File

@ -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(SequenceTest, Backward) {
int X = 9;
for (int I : reverse(seq(0, 10))) {
EXPECT_EQ(X, I);
--X;
}
EXPECT_EQ(-1, X);
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, 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) {

View File

@ -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());

View File

@ -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) {