mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
[ConstantRangeTest] Make exhaustive testing more principled (NFC)
The current infrastructure for exhaustive ConstantRange testing is somewhat confusing in what exactly it tests and currently cannot even be used for operations that produce smallest-size results, rather than signed/unsigned envelopes. This patch makes the testing more principled by collecting the exact set of results of an operation into a bit set and then comparing it against the range approximation by: * Checking conservative correctness: All elements in the set must be in the range. * Checking optimality under a given preference function: None of the (slack-free) ranges that can be constructed from the set are preferred over the computed range. Implemented preference functions are: * PreferSmallest: Smallest range regardless of signed/unsigned wrapping behavior. Probably what we would call "optimal" without further qualification. * PreferSmallestUnsigned/Signed: Smallest range that has no unsigned/signed wrapping. We use this if our calculation is precise only up to signed/unsigned envelope. * PreferSmallestNonFullUnsigned/Signed: Smallest range that has no unsigned/signed wrapping -- but preferring a smaller wrapping range over a (non-wrapping) full range. We use this if we have a fully precise calculation but apply a sign preference to the result (union/intersection). Even with a sign preference, returning a wrapping range is still "strictly better" than returning a full one. This also addresses PR49273 by replacing the fragile manual range construction logic in testBinarySetOperationExhaustive() with generic code that isn't specialized to the particular form of ranges that set operations can produces. Differential Revision: https://reviews.llvm.org/D88356
This commit is contained in:
parent
81606a10c8
commit
9bdd9a8130
@ -7,6 +7,7 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "llvm/ADT/BitVector.h"
|
#include "llvm/ADT/BitVector.h"
|
||||||
|
#include "llvm/ADT/SmallBitVector.h"
|
||||||
#include "llvm/IR/ConstantRange.h"
|
#include "llvm/IR/ConstantRange.h"
|
||||||
#include "llvm/IR/Instructions.h"
|
#include "llvm/IR/Instructions.h"
|
||||||
#include "llvm/IR/Operator.h"
|
#include "llvm/IR/Operator.h"
|
||||||
@ -59,6 +60,137 @@ static void ForeachNumInConstantRange(const ConstantRange &CR, Fn TestFn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using PreferFn = llvm::function_ref<bool(const ConstantRange &,
|
||||||
|
const ConstantRange &)>;
|
||||||
|
|
||||||
|
bool PreferSmallest(const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
|
return CR1.isSizeStrictlySmallerThan(CR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreferSmallestUnsigned(const ConstantRange &CR1,
|
||||||
|
const ConstantRange &CR2) {
|
||||||
|
if (CR1.isWrappedSet() != CR2.isWrappedSet())
|
||||||
|
return CR1.isWrappedSet() < CR2.isWrappedSet();
|
||||||
|
return PreferSmallest(CR1, CR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreferSmallestSigned(const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
|
if (CR1.isSignWrappedSet() != CR2.isSignWrappedSet())
|
||||||
|
return CR1.isSignWrappedSet() < CR2.isSignWrappedSet();
|
||||||
|
return PreferSmallest(CR1, CR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreferSmallestNonFullUnsigned(const ConstantRange &CR1,
|
||||||
|
const ConstantRange &CR2) {
|
||||||
|
if (CR1.isFullSet() != CR2.isFullSet())
|
||||||
|
return CR1.isFullSet() < CR2.isFullSet();
|
||||||
|
return PreferSmallestUnsigned(CR1, CR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreferSmallestNonFullSigned(const ConstantRange &CR1,
|
||||||
|
const ConstantRange &CR2) {
|
||||||
|
if (CR1.isFullSet() != CR2.isFullSet())
|
||||||
|
return CR1.isFullSet() < CR2.isFullSet();
|
||||||
|
return PreferSmallestSigned(CR1, CR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether constant range CR is an optimal approximation of the set
|
||||||
|
// Elems under the given PreferenceFn. The preference function should return
|
||||||
|
// true if the first range argument is strictly preferred to the second one.
|
||||||
|
static void TestRange(const ConstantRange &CR, const SmallBitVector &Elems,
|
||||||
|
PreferFn PreferenceFn) {
|
||||||
|
unsigned BitWidth = CR.getBitWidth();
|
||||||
|
|
||||||
|
// Check conservative correctness.
|
||||||
|
for (unsigned Elem : Elems.set_bits()) {
|
||||||
|
EXPECT_TRUE(CR.contains(APInt(BitWidth, Elem)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have at least two elements for the code below.
|
||||||
|
if (Elems.none()) {
|
||||||
|
EXPECT_TRUE(CR.isEmptySet());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Elems.count() == 1) {
|
||||||
|
EXPECT_TRUE(CR.isSingleElement());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look at all pairs of adjacent elements and the slack-free ranges
|
||||||
|
// [Elem, PrevElem] they imply. Check that none of the ranges are strictly
|
||||||
|
// preferred over the computed range (they may have equal preference).
|
||||||
|
int FirstElem = Elems.find_first();
|
||||||
|
int PrevElem = FirstElem, Elem;
|
||||||
|
do {
|
||||||
|
Elem = Elems.find_next(PrevElem);
|
||||||
|
if (Elem < 0)
|
||||||
|
Elem = FirstElem; // Wrap around to first element.
|
||||||
|
|
||||||
|
ConstantRange PossibleCR =
|
||||||
|
ConstantRange::getNonEmpty(APInt(BitWidth, Elem),
|
||||||
|
APInt(BitWidth, PrevElem) + 1);
|
||||||
|
// There should be no range that is preferred over CR.
|
||||||
|
EXPECT_FALSE(PreferenceFn(PossibleCR, CR))
|
||||||
|
<< "CR = " << CR << ", BetterCR = " << PossibleCR;
|
||||||
|
|
||||||
|
PrevElem = Elem;
|
||||||
|
} while (Elem != FirstElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
using UnaryRangeFn = llvm::function_ref<ConstantRange(const ConstantRange &)>;
|
||||||
|
using UnaryIntFn = llvm::function_ref<Optional<APInt>(const APInt &)>;
|
||||||
|
|
||||||
|
static void TestUnaryOpExhaustive(UnaryRangeFn RangeFn, UnaryIntFn IntFn,
|
||||||
|
PreferFn PreferenceFn = PreferSmallest) {
|
||||||
|
unsigned Bits = 4;
|
||||||
|
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
|
||||||
|
SmallBitVector Elems(1 << Bits);
|
||||||
|
ForeachNumInConstantRange(CR, [&](const APInt &N) {
|
||||||
|
if (Optional<APInt> ResultN = IntFn(N))
|
||||||
|
Elems.set(ResultN->getZExtValue());
|
||||||
|
});
|
||||||
|
TestRange(RangeFn(CR), Elems, PreferenceFn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
using BinaryRangeFn = llvm::function_ref<ConstantRange(const ConstantRange &,
|
||||||
|
const ConstantRange &)>;
|
||||||
|
using BinaryIntFn = llvm::function_ref<Optional<APInt>(const APInt &,
|
||||||
|
const APInt &)>;
|
||||||
|
|
||||||
|
static void TestBinaryOpExhaustive(BinaryRangeFn RangeFn, BinaryIntFn IntFn,
|
||||||
|
PreferFn PreferenceFn = PreferSmallest) {
|
||||||
|
unsigned Bits = 4;
|
||||||
|
EnumerateTwoConstantRanges(
|
||||||
|
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
|
SmallBitVector Elems(1 << Bits);
|
||||||
|
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
|
||||||
|
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
|
||||||
|
if (Optional<APInt> ResultN = IntFn(N1, N2))
|
||||||
|
Elems.set(ResultN->getZExtValue());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
TestRange(RangeFn(CR1, CR2), Elems, PreferenceFn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TestBinaryOpExhaustiveCorrectnessOnly(BinaryRangeFn RangeFn,
|
||||||
|
BinaryIntFn IntFn) {
|
||||||
|
unsigned Bits = 4;
|
||||||
|
EnumerateTwoConstantRanges(
|
||||||
|
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
|
ConstantRange ResultCR = RangeFn(CR1, CR2);
|
||||||
|
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
|
||||||
|
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
|
||||||
|
if (Optional<APInt> ResultN = IntFn(N1, N2)) {
|
||||||
|
EXPECT_TRUE(ResultCR.contains(*ResultN));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
struct OpRangeGathererBase {
|
struct OpRangeGathererBase {
|
||||||
void account(const APInt &N);
|
void account(const APInt &N);
|
||||||
ConstantRange getRange();
|
ConstantRange getRange();
|
||||||
@ -107,85 +239,6 @@ struct SignedOpRangeGatherer : public OpRangeGathererBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Fn1, typename Fn2>
|
|
||||||
static void TestUnsignedUnaryOpExhaustive(Fn1 RangeFn, Fn2 IntFn,
|
|
||||||
bool SkipSignedIntMin = false) {
|
|
||||||
unsigned Bits = 4;
|
|
||||||
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
|
|
||||||
UnsignedOpRangeGatherer R(CR.getBitWidth());
|
|
||||||
ForeachNumInConstantRange(CR, [&](const APInt &N) {
|
|
||||||
if (SkipSignedIntMin && N.isMinSignedValue())
|
|
||||||
return;
|
|
||||||
R.account(IntFn(N));
|
|
||||||
});
|
|
||||||
|
|
||||||
ConstantRange ExactCR = R.getRange();
|
|
||||||
ConstantRange ActualCR = RangeFn(CR);
|
|
||||||
|
|
||||||
EXPECT_EQ(ExactCR, ActualCR);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Fn1, typename Fn2>
|
|
||||||
static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn,
|
|
||||||
bool SkipZeroRHS = false,
|
|
||||||
bool CorrectnessOnly = false) {
|
|
||||||
unsigned Bits = 4;
|
|
||||||
EnumerateTwoConstantRanges(
|
|
||||||
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
||||||
UnsignedOpRangeGatherer R(CR1.getBitWidth());
|
|
||||||
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
|
|
||||||
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
|
|
||||||
if (SkipZeroRHS && N2 == 0)
|
|
||||||
return;
|
|
||||||
R.account(IntFn(N1, N2));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ConstantRange CR = RangeFn(CR1, CR2);
|
|
||||||
|
|
||||||
ConstantRange Exact = R.getRange();
|
|
||||||
|
|
||||||
if (!CorrectnessOnly) {
|
|
||||||
EXPECT_EQ(Exact, CR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_TRUE(CR.contains(Exact));
|
|
||||||
if (Exact.isEmptySet()) {
|
|
||||||
EXPECT_TRUE(CR.isEmptySet());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Fn1, typename Fn2>
|
|
||||||
static void TestSignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn,
|
|
||||||
bool SkipZeroRHS = false,
|
|
||||||
bool CorrectnessOnly = false) {
|
|
||||||
unsigned Bits = 4;
|
|
||||||
EnumerateTwoConstantRanges(
|
|
||||||
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
||||||
SignedOpRangeGatherer R(CR1.getBitWidth());
|
|
||||||
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
|
|
||||||
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
|
|
||||||
if (SkipZeroRHS && N2 == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
R.account(IntFn(N1, N2));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ConstantRange CR = RangeFn(CR1, CR2);
|
|
||||||
|
|
||||||
ConstantRange Exact = R.getRange();
|
|
||||||
if (CorrectnessOnly) {
|
|
||||||
EXPECT_TRUE(CR.contains(Exact));
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(Exact, CR);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstantRange ConstantRangeTest::Full(16, true);
|
ConstantRange ConstantRangeTest::Full(16, true);
|
||||||
ConstantRange ConstantRangeTest::Empty(16, false);
|
ConstantRange ConstantRangeTest::Empty(16, false);
|
||||||
ConstantRange ConstantRangeTest::One(APInt(16, 0xa));
|
ConstantRange ConstantRangeTest::One(APInt(16, 0xa));
|
||||||
@ -483,126 +536,20 @@ void testBinarySetOperationExhaustive(Fn1 OpFn, Fn2 InResultFn) {
|
|||||||
unsigned Bits = 4;
|
unsigned Bits = 4;
|
||||||
EnumerateTwoConstantRanges(Bits,
|
EnumerateTwoConstantRanges(Bits,
|
||||||
[=](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[=](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
// Collect up to three contiguous unsigned ranges. The HaveInterrupt
|
SmallBitVector Elems(1 << Bits);
|
||||||
// variables are used determine when we have to switch to the next
|
|
||||||
// range because the previous one ended.
|
|
||||||
APInt Lower1(Bits, 0), Upper1(Bits, 0);
|
|
||||||
APInt Lower2(Bits, 0), Upper2(Bits, 0);
|
|
||||||
APInt Lower3(Bits, 0), Upper3(Bits, 0);
|
|
||||||
bool HaveRange1 = false, HaveInterrupt1 = false;
|
|
||||||
bool HaveRange2 = false, HaveInterrupt2 = false;
|
|
||||||
bool HaveRange3 = false, HaveInterrupt3 = false;
|
|
||||||
|
|
||||||
APInt Num(Bits, 0);
|
APInt Num(Bits, 0);
|
||||||
for (unsigned I = 0, Limit = 1 << Bits; I < Limit; ++I, ++Num) {
|
for (unsigned I = 0, Limit = 1 << Bits; I < Limit; ++I, ++Num)
|
||||||
if (!InResultFn(CR1, CR2, Num)) {
|
if (InResultFn(CR1, CR2, Num))
|
||||||
if (HaveRange3)
|
Elems.set(Num.getZExtValue());
|
||||||
HaveInterrupt3 = true;
|
|
||||||
else if (HaveRange2)
|
|
||||||
HaveInterrupt2 = true;
|
|
||||||
else if (HaveRange1)
|
|
||||||
HaveInterrupt1 = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HaveRange3) {
|
|
||||||
Upper3 = Num;
|
|
||||||
} else if (HaveInterrupt2) {
|
|
||||||
HaveRange3 = true;
|
|
||||||
Lower3 = Upper3 = Num;
|
|
||||||
} else if (HaveRange2) {
|
|
||||||
Upper2 = Num;
|
|
||||||
} else if (HaveInterrupt1) {
|
|
||||||
HaveRange2 = true;
|
|
||||||
Lower2 = Upper2 = Num;
|
|
||||||
} else if (HaveRange1) {
|
|
||||||
Upper1 = Num;
|
|
||||||
} else {
|
|
||||||
HaveRange1 = true;
|
|
||||||
Lower1 = Upper1 = Num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)HaveInterrupt3;
|
|
||||||
assert(!HaveInterrupt3 && "Should have at most three ranges");
|
|
||||||
|
|
||||||
ConstantRange SmallestCR = OpFn(CR1, CR2, ConstantRange::Smallest);
|
ConstantRange SmallestCR = OpFn(CR1, CR2, ConstantRange::Smallest);
|
||||||
|
TestRange(SmallestCR, Elems, PreferSmallest);
|
||||||
|
|
||||||
ConstantRange UnsignedCR = OpFn(CR1, CR2, ConstantRange::Unsigned);
|
ConstantRange UnsignedCR = OpFn(CR1, CR2, ConstantRange::Unsigned);
|
||||||
|
TestRange(UnsignedCR, Elems, PreferSmallestNonFullUnsigned);
|
||||||
|
|
||||||
ConstantRange SignedCR = OpFn(CR1, CR2, ConstantRange::Signed);
|
ConstantRange SignedCR = OpFn(CR1, CR2, ConstantRange::Signed);
|
||||||
|
TestRange(SignedCR, Elems, PreferSmallestNonFullSigned);
|
||||||
if (!HaveRange1) {
|
|
||||||
EXPECT_TRUE(SmallestCR.isEmptySet());
|
|
||||||
EXPECT_TRUE(UnsignedCR.isEmptySet());
|
|
||||||
EXPECT_TRUE(SignedCR.isEmptySet());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HaveRange2) {
|
|
||||||
if (Lower1 == Upper1 + 1) {
|
|
||||||
EXPECT_TRUE(SmallestCR.isFullSet());
|
|
||||||
EXPECT_TRUE(UnsignedCR.isFullSet());
|
|
||||||
EXPECT_TRUE(SignedCR.isFullSet());
|
|
||||||
} else {
|
|
||||||
ConstantRange Expected(Lower1, Upper1 + 1);
|
|
||||||
EXPECT_EQ(Expected, SmallestCR);
|
|
||||||
EXPECT_EQ(Expected, UnsignedCR);
|
|
||||||
EXPECT_EQ(Expected, SignedCR);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstantRange Variant1(Bits, /*full*/ true);
|
|
||||||
ConstantRange Variant2(Bits, /*full*/ true);
|
|
||||||
if (!HaveRange3) {
|
|
||||||
// Compute the two possible ways to cover two disjoint ranges.
|
|
||||||
if (Lower1 != Upper2 + 1)
|
|
||||||
Variant1 = ConstantRange(Lower1, Upper2 + 1);
|
|
||||||
if (Lower2 != Upper1 + 1)
|
|
||||||
Variant2 = ConstantRange(Lower2, Upper1 + 1);
|
|
||||||
} else {
|
|
||||||
// If we have three ranges, the first and last one have to be adjacent
|
|
||||||
// to the unsigned domain. It's better to think of this as having two
|
|
||||||
// holes, and we can construct one range using each hole.
|
|
||||||
assert(Lower1.isNullValue() && Upper3.isMaxValue());
|
|
||||||
Variant1 = ConstantRange(Lower2, Upper1 + 1);
|
|
||||||
Variant2 = ConstantRange(Lower3, Upper2 + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smallest: Smaller set, then any set.
|
|
||||||
if (Variant1.isSizeStrictlySmallerThan(Variant2))
|
|
||||||
EXPECT_EQ(Variant1, SmallestCR);
|
|
||||||
else if (Variant2.isSizeStrictlySmallerThan(Variant1))
|
|
||||||
EXPECT_EQ(Variant2, SmallestCR);
|
|
||||||
else
|
|
||||||
EXPECT_TRUE(Variant1 == SmallestCR || Variant2 == SmallestCR);
|
|
||||||
|
|
||||||
// Unsigned: Non-wrapped set, then smaller set, then any set.
|
|
||||||
bool Variant1Full = Variant1.isFullSet() || Variant1.isWrappedSet();
|
|
||||||
bool Variant2Full = Variant2.isFullSet() || Variant2.isWrappedSet();
|
|
||||||
if (!Variant1Full && Variant2Full)
|
|
||||||
EXPECT_EQ(Variant1, UnsignedCR);
|
|
||||||
else if (Variant1Full && !Variant2Full)
|
|
||||||
EXPECT_EQ(Variant2, UnsignedCR);
|
|
||||||
else if (Variant1.isSizeStrictlySmallerThan(Variant2))
|
|
||||||
EXPECT_EQ(Variant1, UnsignedCR);
|
|
||||||
else if (Variant2.isSizeStrictlySmallerThan(Variant1))
|
|
||||||
EXPECT_EQ(Variant2, UnsignedCR);
|
|
||||||
else
|
|
||||||
EXPECT_TRUE(Variant1 == UnsignedCR || Variant2 == UnsignedCR);
|
|
||||||
|
|
||||||
// Signed: Signed non-wrapped set, then smaller set, then any set.
|
|
||||||
Variant1Full = Variant1.isFullSet() || Variant1.isSignWrappedSet();
|
|
||||||
Variant2Full = Variant2.isFullSet() || Variant2.isSignWrappedSet();
|
|
||||||
if (!Variant1Full && Variant2Full)
|
|
||||||
EXPECT_EQ(Variant1, SignedCR);
|
|
||||||
else if (Variant1Full && !Variant2Full)
|
|
||||||
EXPECT_EQ(Variant2, SignedCR);
|
|
||||||
else if (Variant1.isSizeStrictlySmallerThan(Variant2))
|
|
||||||
EXPECT_EQ(Variant1, SignedCR);
|
|
||||||
else if (Variant2.isSizeStrictlySmallerThan(Variant1))
|
|
||||||
EXPECT_EQ(Variant2, SignedCR);
|
|
||||||
else
|
|
||||||
EXPECT_TRUE(Variant1 == SignedCR || Variant2 == SignedCR);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,6 +694,14 @@ TEST_F(ConstantRangeTest, Add) {
|
|||||||
ConstantRange(APInt(16, 0xaae), APInt(16, 0xe)));
|
ConstantRange(APInt(16, 0xaae), APInt(16, 0xe)));
|
||||||
EXPECT_EQ(One.add(APInt(16, 4)),
|
EXPECT_EQ(One.add(APInt(16, 4)),
|
||||||
ConstantRange(APInt(16, 0xe)));
|
ConstantRange(APInt(16, 0xe)));
|
||||||
|
|
||||||
|
TestBinaryOpExhaustive(
|
||||||
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
|
return CR1.add(CR2);
|
||||||
|
},
|
||||||
|
[](const APInt &N1, const APInt &N2) {
|
||||||
|
return N1 + N2;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Fn1, typename Fn2>
|
template <typename Fn1, typename Fn2>
|
||||||
@ -1016,6 +971,14 @@ TEST_F(ConstantRangeTest, Sub) {
|
|||||||
ConstantRange(APInt(16, 0xaa6), APInt(16, 0x6)));
|
ConstantRange(APInt(16, 0xaa6), APInt(16, 0x6)));
|
||||||
EXPECT_EQ(One.sub(APInt(16, 4)),
|
EXPECT_EQ(One.sub(APInt(16, 4)),
|
||||||
ConstantRange(APInt(16, 0x6)));
|
ConstantRange(APInt(16, 0x6)));
|
||||||
|
|
||||||
|
TestBinaryOpExhaustive(
|
||||||
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
|
return CR1.sub(CR2);
|
||||||
|
},
|
||||||
|
[](const APInt &N1, const APInt &N2) {
|
||||||
|
return N1 - N2;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, SubWithNoWrap) {
|
TEST_F(ConstantRangeTest, SubWithNoWrap) {
|
||||||
@ -1283,14 +1246,15 @@ TEST_F(ConstantRangeTest, URem) {
|
|||||||
.urem(ConstantRange(APInt(16, 10))),
|
.urem(ConstantRange(APInt(16, 10))),
|
||||||
ConstantRange(APInt(16, 0), APInt(16, 10)));
|
ConstantRange(APInt(16, 0), APInt(16, 10)));
|
||||||
|
|
||||||
TestUnsignedBinOpExhaustive(
|
TestBinaryOpExhaustiveCorrectnessOnly(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.urem(CR2);
|
return CR1.urem(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) {
|
[](const APInt &N1, const APInt &N2) -> Optional<APInt> {
|
||||||
|
if (N2.isNullValue())
|
||||||
|
return None;
|
||||||
return N1.urem(N2);
|
return N1.urem(N2);
|
||||||
},
|
});
|
||||||
/* SkipZeroRHS */ true, /* CorrectnessOnly */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, SRem) {
|
TEST_F(ConstantRangeTest, SRem) {
|
||||||
@ -1356,14 +1320,15 @@ TEST_F(ConstantRangeTest, SRem) {
|
|||||||
.srem(ConstantRange(APInt(16, 10))),
|
.srem(ConstantRange(APInt(16, 10))),
|
||||||
ConstantRange(APInt(16, 0), APInt(16, 10)));
|
ConstantRange(APInt(16, 0), APInt(16, 10)));
|
||||||
|
|
||||||
TestSignedBinOpExhaustive(
|
TestBinaryOpExhaustiveCorrectnessOnly(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.srem(CR2);
|
return CR1.srem(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) {
|
[](const APInt &N1, const APInt &N2) -> Optional<APInt> {
|
||||||
|
if (N2.isNullValue())
|
||||||
|
return None;
|
||||||
return N1.srem(N2);
|
return N1.srem(N2);
|
||||||
},
|
});
|
||||||
/* SkipZeroRHS */ true, /* CorrectnessOnly */ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, Shl) {
|
TEST_F(ConstantRangeTest, Shl) {
|
||||||
@ -2278,88 +2243,97 @@ TEST_F(ConstantRangeTest, Negative) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, UAddSat) {
|
TEST_F(ConstantRangeTest, UAddSat) {
|
||||||
TestUnsignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.uadd_sat(CR2);
|
return CR1.uadd_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) {
|
[](const APInt &N1, const APInt &N2) {
|
||||||
return N1.uadd_sat(N2);
|
return N1.uadd_sat(N2);
|
||||||
});
|
},
|
||||||
|
PreferSmallestUnsigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, USubSat) {
|
TEST_F(ConstantRangeTest, USubSat) {
|
||||||
TestUnsignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.usub_sat(CR2);
|
return CR1.usub_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) {
|
[](const APInt &N1, const APInt &N2) {
|
||||||
return N1.usub_sat(N2);
|
return N1.usub_sat(N2);
|
||||||
});
|
},
|
||||||
|
PreferSmallestUnsigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, UMulSat) {
|
TEST_F(ConstantRangeTest, UMulSat) {
|
||||||
TestUnsignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.umul_sat(CR2);
|
return CR1.umul_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) { return N1.umul_sat(N2); });
|
[](const APInt &N1, const APInt &N2) { return N1.umul_sat(N2); },
|
||||||
|
PreferSmallestUnsigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, UShlSat) {
|
TEST_F(ConstantRangeTest, UShlSat) {
|
||||||
TestUnsignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.ushl_sat(CR2);
|
return CR1.ushl_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) { return N1.ushl_sat(N2); });
|
[](const APInt &N1, const APInt &N2) { return N1.ushl_sat(N2); },
|
||||||
|
PreferSmallestUnsigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, SAddSat) {
|
TEST_F(ConstantRangeTest, SAddSat) {
|
||||||
TestSignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.sadd_sat(CR2);
|
return CR1.sadd_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) {
|
[](const APInt &N1, const APInt &N2) {
|
||||||
return N1.sadd_sat(N2);
|
return N1.sadd_sat(N2);
|
||||||
});
|
},
|
||||||
|
PreferSmallestSigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, SSubSat) {
|
TEST_F(ConstantRangeTest, SSubSat) {
|
||||||
TestSignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.ssub_sat(CR2);
|
return CR1.ssub_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) {
|
[](const APInt &N1, const APInt &N2) {
|
||||||
return N1.ssub_sat(N2);
|
return N1.ssub_sat(N2);
|
||||||
});
|
},
|
||||||
|
PreferSmallestSigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, SMulSat) {
|
TEST_F(ConstantRangeTest, SMulSat) {
|
||||||
TestSignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.smul_sat(CR2);
|
return CR1.smul_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) { return N1.smul_sat(N2); });
|
[](const APInt &N1, const APInt &N2) { return N1.smul_sat(N2); },
|
||||||
|
PreferSmallestSigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, SShlSat) {
|
TEST_F(ConstantRangeTest, SShlSat) {
|
||||||
TestSignedBinOpExhaustive(
|
TestBinaryOpExhaustive(
|
||||||
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
||||||
return CR1.sshl_sat(CR2);
|
return CR1.sshl_sat(CR2);
|
||||||
},
|
},
|
||||||
[](const APInt &N1, const APInt &N2) { return N1.sshl_sat(N2); });
|
[](const APInt &N1, const APInt &N2) { return N1.sshl_sat(N2); },
|
||||||
|
PreferSmallestSigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, Abs) {
|
TEST_F(ConstantRangeTest, Abs) {
|
||||||
// We're working with unsigned integers here, because it makes the signed
|
TestUnaryOpExhaustive(
|
||||||
// min case non-wrapping.
|
|
||||||
TestUnsignedUnaryOpExhaustive(
|
|
||||||
[](const ConstantRange &CR) { return CR.abs(); },
|
[](const ConstantRange &CR) { return CR.abs(); },
|
||||||
[](const APInt &N) { return N.abs(); });
|
[](const APInt &N) { return N.abs(); });
|
||||||
|
|
||||||
TestUnsignedUnaryOpExhaustive(
|
TestUnaryOpExhaustive(
|
||||||
[](const ConstantRange &CR) { return CR.abs(/*IntMinIsPoison=*/true); },
|
[](const ConstantRange &CR) { return CR.abs(/*IntMinIsPoison=*/true); },
|
||||||
[](const APInt &N) { return N.abs(); },
|
[](const APInt &N) -> Optional<APInt> {
|
||||||
/*SkipSignedIntMin=*/true);
|
if (N.isMinSignedValue())
|
||||||
|
return None;
|
||||||
|
return N.abs();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, castOps) {
|
TEST_F(ConstantRangeTest, castOps) {
|
||||||
@ -2401,21 +2375,26 @@ TEST_F(ConstantRangeTest, binaryXor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ConstantRangeTest, binaryNot) {
|
TEST_F(ConstantRangeTest, binaryNot) {
|
||||||
TestUnsignedUnaryOpExhaustive(
|
// TODO: Improve binaryNot() implementation to remove the need for
|
||||||
|
// PreferSmallestUnsigned.
|
||||||
|
TestUnaryOpExhaustive(
|
||||||
[](const ConstantRange &CR) { return CR.binaryNot(); },
|
[](const ConstantRange &CR) { return CR.binaryNot(); },
|
||||||
[](const APInt &N) { return ~N; });
|
[](const APInt &N) { return ~N; },
|
||||||
TestUnsignedUnaryOpExhaustive(
|
PreferSmallestUnsigned);
|
||||||
|
TestUnaryOpExhaustive(
|
||||||
[](const ConstantRange &CR) {
|
[](const ConstantRange &CR) {
|
||||||
return CR.binaryXor(
|
return CR.binaryXor(
|
||||||
ConstantRange(APInt::getAllOnesValue(CR.getBitWidth())));
|
ConstantRange(APInt::getAllOnesValue(CR.getBitWidth())));
|
||||||
},
|
},
|
||||||
[](const APInt &N) { return ~N; });
|
[](const APInt &N) { return ~N; },
|
||||||
TestUnsignedUnaryOpExhaustive(
|
PreferSmallestUnsigned);
|
||||||
|
TestUnaryOpExhaustive(
|
||||||
[](const ConstantRange &CR) {
|
[](const ConstantRange &CR) {
|
||||||
return ConstantRange(APInt::getAllOnesValue(CR.getBitWidth()))
|
return ConstantRange(APInt::getAllOnesValue(CR.getBitWidth()))
|
||||||
.binaryXor(CR);
|
.binaryXor(CR);
|
||||||
},
|
},
|
||||||
[](const APInt &N) { return ~N; });
|
[](const APInt &N) { return ~N; },
|
||||||
|
PreferSmallestUnsigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user