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

2492 lines
96 KiB
C++
Raw Normal View History

//===- ConstantRangeTest.cpp - ConstantRange tests ------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/ConstantRange.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/Sequence.h"
[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
2020-09-23 20:43:59 +02:00
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Operator.h"
#include "llvm/Support/KnownBits.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
class ConstantRangeTest : public ::testing::Test {
protected:
static ConstantRange Full;
static ConstantRange Empty;
static ConstantRange One;
static ConstantRange Some;
static ConstantRange Wrap;
};
template<typename Fn>
static void EnumerateConstantRanges(unsigned Bits, Fn TestFn) {
unsigned Max = 1 << Bits;
for (unsigned Lo = 0; Lo < Max; Lo++) {
for (unsigned Hi = 0; Hi < Max; Hi++) {
// Enforce ConstantRange invariant.
if (Lo == Hi && Lo != 0 && Lo != Max - 1)
continue;
ConstantRange CR(APInt(Bits, Lo), APInt(Bits, Hi));
TestFn(CR);
}
}
}
template<typename Fn>
static void EnumerateTwoConstantRanges(unsigned Bits, Fn TestFn) {
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR1) {
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR2) {
TestFn(CR1, CR2);
});
});
}
template<typename Fn>
static void ForeachNumInConstantRange(const ConstantRange &CR, Fn TestFn) {
if (!CR.isEmptySet()) {
APInt N = CR.getLower();
do TestFn(N);
while (++N != CR.getUpper());
}
}
[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
2020-09-23 20:43:59 +02:00
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);
}
[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
2020-09-23 20:43:59 +02:00
// 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, ArrayRef<ConstantRange> Inputs) {
[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
2020-09-23 20:43:59 +02:00
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 one element for the code below.
[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
2020-09-23 20:43:59 +02:00
if (Elems.none()) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
auto NotPreferred = [&](const ConstantRange &PossibleCR) {
if (!PreferenceFn(PossibleCR, CR))
return testing::AssertionSuccess();
testing::AssertionResult Result = testing::AssertionFailure();
Result << "Inputs = ";
for (const ConstantRange &Input : Inputs)
Result << Input << ", ";
Result << "CR = " << CR << ", BetterCR = " << PossibleCR;
return Result;
};
[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
2020-09-23 20:43:59 +02:00
// 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);
// We get a full range any time PrevElem and Elem are adjacent. Avoid
// repeated checks by skipping here, and explicitly checking below instead.
if (!PossibleCR.isFullSet()) {
EXPECT_TRUE(NotPreferred(PossibleCR));
}
[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
2020-09-23 20:43:59 +02:00
PrevElem = Elem;
} while (Elem != FirstElem);
EXPECT_TRUE(NotPreferred(ConstantRange::getFull(BitWidth)));
[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
2020-09-23 20:43:59 +02:00
}
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, {CR});
[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
2020-09-23 20:43:59 +02:00
});
}
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, {CR1, CR2});
[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
2020-09-23 20:43:59 +02:00
});
}
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 {
void account(const APInt &N);
ConstantRange getRange();
};
struct UnsignedOpRangeGatherer : public OpRangeGathererBase {
APInt Min;
APInt Max;
UnsignedOpRangeGatherer(unsigned Bits)
: Min(APInt::getMaxValue(Bits)), Max(APInt::getMinValue(Bits)) {}
void account(const APInt &N) {
if (N.ult(Min))
Min = N;
if (N.ugt(Max))
Max = N;
}
ConstantRange getRange() {
if (Min.ugt(Max))
return ConstantRange::getEmpty(Min.getBitWidth());
return ConstantRange::getNonEmpty(Min, Max + 1);
}
};
struct SignedOpRangeGatherer : public OpRangeGathererBase {
APInt Min;
APInt Max;
SignedOpRangeGatherer(unsigned Bits)
: Min(APInt::getSignedMaxValue(Bits)),
Max(APInt::getSignedMinValue(Bits)) {}
void account(const APInt &N) {
if (N.slt(Min))
Min = N;
if (N.sgt(Max))
Max = N;
}
ConstantRange getRange() {
if (Min.sgt(Max))
return ConstantRange::getEmpty(Min.getBitWidth());
return ConstantRange::getNonEmpty(Min, Max + 1);
}
};
ConstantRange ConstantRangeTest::Full(16, true);
ConstantRange ConstantRangeTest::Empty(16, false);
ConstantRange ConstantRangeTest::One(APInt(16, 0xa));
ConstantRange ConstantRangeTest::Some(APInt(16, 0xa), APInt(16, 0xaaa));
ConstantRange ConstantRangeTest::Wrap(APInt(16, 0xaaa), APInt(16, 0xa));
TEST_F(ConstantRangeTest, Basics) {
EXPECT_TRUE(Full.isFullSet());
EXPECT_FALSE(Full.isEmptySet());
EXPECT_TRUE(Full.inverse().isEmptySet());
EXPECT_FALSE(Full.isWrappedSet());
EXPECT_TRUE(Full.contains(APInt(16, 0x0)));
EXPECT_TRUE(Full.contains(APInt(16, 0x9)));
EXPECT_TRUE(Full.contains(APInt(16, 0xa)));
EXPECT_TRUE(Full.contains(APInt(16, 0xaa9)));
EXPECT_TRUE(Full.contains(APInt(16, 0xaaa)));
EXPECT_FALSE(Empty.isFullSet());
EXPECT_TRUE(Empty.isEmptySet());
EXPECT_TRUE(Empty.inverse().isFullSet());
EXPECT_FALSE(Empty.isWrappedSet());
EXPECT_FALSE(Empty.contains(APInt(16, 0x0)));
EXPECT_FALSE(Empty.contains(APInt(16, 0x9)));
EXPECT_FALSE(Empty.contains(APInt(16, 0xa)));
EXPECT_FALSE(Empty.contains(APInt(16, 0xaa9)));
EXPECT_FALSE(Empty.contains(APInt(16, 0xaaa)));
EXPECT_FALSE(One.isFullSet());
EXPECT_FALSE(One.isEmptySet());
EXPECT_FALSE(One.isWrappedSet());
EXPECT_FALSE(One.contains(APInt(16, 0x0)));
EXPECT_FALSE(One.contains(APInt(16, 0x9)));
EXPECT_TRUE(One.contains(APInt(16, 0xa)));
EXPECT_FALSE(One.contains(APInt(16, 0xaa9)));
EXPECT_FALSE(One.contains(APInt(16, 0xaaa)));
EXPECT_FALSE(One.inverse().contains(APInt(16, 0xa)));
EXPECT_FALSE(Some.isFullSet());
EXPECT_FALSE(Some.isEmptySet());
EXPECT_FALSE(Some.isWrappedSet());
EXPECT_FALSE(Some.contains(APInt(16, 0x0)));
EXPECT_FALSE(Some.contains(APInt(16, 0x9)));
EXPECT_TRUE(Some.contains(APInt(16, 0xa)));
EXPECT_TRUE(Some.contains(APInt(16, 0xaa9)));
EXPECT_FALSE(Some.contains(APInt(16, 0xaaa)));
EXPECT_FALSE(Wrap.isFullSet());
EXPECT_FALSE(Wrap.isEmptySet());
EXPECT_TRUE(Wrap.isWrappedSet());
EXPECT_TRUE(Wrap.contains(APInt(16, 0x0)));
EXPECT_TRUE(Wrap.contains(APInt(16, 0x9)));
EXPECT_FALSE(Wrap.contains(APInt(16, 0xa)));
EXPECT_FALSE(Wrap.contains(APInt(16, 0xaa9)));
EXPECT_TRUE(Wrap.contains(APInt(16, 0xaaa)));
}
TEST_F(ConstantRangeTest, Equality) {
EXPECT_EQ(Full, Full);
EXPECT_EQ(Empty, Empty);
EXPECT_EQ(One, One);
EXPECT_EQ(Some, Some);
EXPECT_EQ(Wrap, Wrap);
EXPECT_NE(Full, Empty);
EXPECT_NE(Full, One);
EXPECT_NE(Full, Some);
EXPECT_NE(Full, Wrap);
EXPECT_NE(Empty, One);
EXPECT_NE(Empty, Some);
EXPECT_NE(Empty, Wrap);
EXPECT_NE(One, Some);
EXPECT_NE(One, Wrap);
EXPECT_NE(Some, Wrap);
}
TEST_F(ConstantRangeTest, SingleElement) {
EXPECT_EQ(Full.getSingleElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(Empty.getSingleElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(Full.getSingleMissingElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(Empty.getSingleMissingElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(*One.getSingleElement(), APInt(16, 0xa));
EXPECT_EQ(Some.getSingleElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(Wrap.getSingleElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(One.getSingleMissingElement(), static_cast<APInt *>(nullptr));
EXPECT_EQ(Some.getSingleMissingElement(), static_cast<APInt *>(nullptr));
ConstantRange OneInverse = One.inverse();
EXPECT_EQ(*OneInverse.getSingleMissingElement(), *One.getSingleElement());
EXPECT_FALSE(Full.isSingleElement());
EXPECT_FALSE(Empty.isSingleElement());
EXPECT_TRUE(One.isSingleElement());
EXPECT_FALSE(Some.isSingleElement());
EXPECT_FALSE(Wrap.isSingleElement());
}
TEST_F(ConstantRangeTest, GetMinsAndMaxes) {
EXPECT_EQ(Full.getUnsignedMax(), APInt(16, UINT16_MAX));
EXPECT_EQ(One.getUnsignedMax(), APInt(16, 0xa));
EXPECT_EQ(Some.getUnsignedMax(), APInt(16, 0xaa9));
EXPECT_EQ(Wrap.getUnsignedMax(), APInt(16, UINT16_MAX));
EXPECT_EQ(Full.getUnsignedMin(), APInt(16, 0));
EXPECT_EQ(One.getUnsignedMin(), APInt(16, 0xa));
EXPECT_EQ(Some.getUnsignedMin(), APInt(16, 0xa));
EXPECT_EQ(Wrap.getUnsignedMin(), APInt(16, 0));
EXPECT_EQ(Full.getSignedMax(), APInt(16, INT16_MAX));
EXPECT_EQ(One.getSignedMax(), APInt(16, 0xa));
EXPECT_EQ(Some.getSignedMax(), APInt(16, 0xaa9));
EXPECT_EQ(Wrap.getSignedMax(), APInt(16, INT16_MAX));
EXPECT_EQ(Full.getSignedMin(), APInt(16, (uint64_t)INT16_MIN));
EXPECT_EQ(One.getSignedMin(), APInt(16, 0xa));
EXPECT_EQ(Some.getSignedMin(), APInt(16, 0xa));
EXPECT_EQ(Wrap.getSignedMin(), APInt(16, (uint64_t)INT16_MIN));
// Found by Klee
EXPECT_EQ(ConstantRange(APInt(4, 7), APInt(4, 0)).getSignedMax(),
APInt(4, 7));
}
TEST_F(ConstantRangeTest, SignWrapped) {
EXPECT_FALSE(Full.isSignWrappedSet());
EXPECT_FALSE(Empty.isSignWrappedSet());
EXPECT_FALSE(One.isSignWrappedSet());
EXPECT_FALSE(Some.isSignWrappedSet());
EXPECT_TRUE(Wrap.isSignWrappedSet());
EXPECT_FALSE(ConstantRange(APInt(8, 127), APInt(8, 128)).isSignWrappedSet());
EXPECT_TRUE(ConstantRange(APInt(8, 127), APInt(8, 129)).isSignWrappedSet());
EXPECT_FALSE(ConstantRange(APInt(8, 128), APInt(8, 129)).isSignWrappedSet());
EXPECT_TRUE(ConstantRange(APInt(8, 10), APInt(8, 9)).isSignWrappedSet());
EXPECT_TRUE(ConstantRange(APInt(8, 10), APInt(8, 250)).isSignWrappedSet());
EXPECT_FALSE(ConstantRange(APInt(8, 250), APInt(8, 10)).isSignWrappedSet());
EXPECT_FALSE(ConstantRange(APInt(8, 250), APInt(8, 251)).isSignWrappedSet());
}
TEST_F(ConstantRangeTest, UpperWrapped) {
// The behavior here is the same as for isWrappedSet() / isSignWrappedSet().
EXPECT_FALSE(Full.isUpperWrapped());
EXPECT_FALSE(Empty.isUpperWrapped());
EXPECT_FALSE(One.isUpperWrapped());
EXPECT_FALSE(Some.isUpperWrapped());
EXPECT_TRUE(Wrap.isUpperWrapped());
EXPECT_FALSE(Full.isUpperSignWrapped());
EXPECT_FALSE(Empty.isUpperSignWrapped());
EXPECT_FALSE(One.isUpperSignWrapped());
EXPECT_FALSE(Some.isUpperSignWrapped());
EXPECT_TRUE(Wrap.isUpperSignWrapped());
// The behavior differs if Upper is the Min/SignedMin value.
ConstantRange CR1(APInt(8, 42), APInt::getMinValue(8));
EXPECT_FALSE(CR1.isWrappedSet());
EXPECT_TRUE(CR1.isUpperWrapped());
ConstantRange CR2(APInt(8, 42), APInt::getSignedMinValue(8));
EXPECT_FALSE(CR2.isSignWrappedSet());
EXPECT_TRUE(CR2.isUpperSignWrapped());
}
TEST_F(ConstantRangeTest, Trunc) {
ConstantRange TFull = Full.truncate(10);
ConstantRange TEmpty = Empty.truncate(10);
ConstantRange TOne = One.truncate(10);
ConstantRange TSome = Some.truncate(10);
ConstantRange TWrap = Wrap.truncate(10);
EXPECT_TRUE(TFull.isFullSet());
EXPECT_TRUE(TEmpty.isEmptySet());
EXPECT_EQ(TOne, ConstantRange(One.getLower().trunc(10),
One.getUpper().trunc(10)));
EXPECT_TRUE(TSome.isFullSet());
EXPECT_TRUE(TWrap.isFullSet());
// trunc([2, 5), 3->2) = [2, 1)
ConstantRange TwoFive(APInt(3, 2), APInt(3, 5));
EXPECT_EQ(TwoFive.truncate(2), ConstantRange(APInt(2, 2), APInt(2, 1)));
// trunc([2, 6), 3->2) = full
ConstantRange TwoSix(APInt(3, 2), APInt(3, 6));
EXPECT_TRUE(TwoSix.truncate(2).isFullSet());
// trunc([5, 7), 3->2) = [1, 3)
ConstantRange FiveSeven(APInt(3, 5), APInt(3, 7));
EXPECT_EQ(FiveSeven.truncate(2), ConstantRange(APInt(2, 1), APInt(2, 3)));
// trunc([7, 1), 3->2) = [3, 1)
ConstantRange SevenOne(APInt(3, 7), APInt(3, 1));
EXPECT_EQ(SevenOne.truncate(2), ConstantRange(APInt(2, 3), APInt(2, 1)));
}
TEST_F(ConstantRangeTest, ZExt) {
ConstantRange ZFull = Full.zeroExtend(20);
ConstantRange ZEmpty = Empty.zeroExtend(20);
ConstantRange ZOne = One.zeroExtend(20);
ConstantRange ZSome = Some.zeroExtend(20);
ConstantRange ZWrap = Wrap.zeroExtend(20);
EXPECT_EQ(ZFull, ConstantRange(APInt(20, 0), APInt(20, 0x10000)));
EXPECT_TRUE(ZEmpty.isEmptySet());
EXPECT_EQ(ZOne, ConstantRange(One.getLower().zext(20),
One.getUpper().zext(20)));
EXPECT_EQ(ZSome, ConstantRange(Some.getLower().zext(20),
Some.getUpper().zext(20)));
EXPECT_EQ(ZWrap, ConstantRange(APInt(20, 0), APInt(20, 0x10000)));
// zext([5, 0), 3->7) = [5, 8)
ConstantRange FiveZero(APInt(3, 5), APInt(3, 0));
EXPECT_EQ(FiveZero.zeroExtend(7), ConstantRange(APInt(7, 5), APInt(7, 8)));
}
TEST_F(ConstantRangeTest, SExt) {
ConstantRange SFull = Full.signExtend(20);
ConstantRange SEmpty = Empty.signExtend(20);
ConstantRange SOne = One.signExtend(20);
ConstantRange SSome = Some.signExtend(20);
ConstantRange SWrap = Wrap.signExtend(20);
EXPECT_EQ(SFull, ConstantRange(APInt(20, (uint64_t)INT16_MIN, true),
APInt(20, INT16_MAX + 1, true)));
EXPECT_TRUE(SEmpty.isEmptySet());
EXPECT_EQ(SOne, ConstantRange(One.getLower().sext(20),
One.getUpper().sext(20)));
EXPECT_EQ(SSome, ConstantRange(Some.getLower().sext(20),
Some.getUpper().sext(20)));
EXPECT_EQ(SWrap, ConstantRange(APInt(20, (uint64_t)INT16_MIN, true),
APInt(20, INT16_MAX + 1, true)));
EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, 140)).signExtend(16),
ConstantRange(APInt(16, -128), APInt(16, 128)));
EXPECT_EQ(ConstantRange(APInt(16, 0x0200), APInt(16, 0x8000)).signExtend(19),
ConstantRange(APInt(19, 0x0200), APInt(19, 0x8000)));
}
TEST_F(ConstantRangeTest, IntersectWith) {
EXPECT_EQ(Empty.intersectWith(Full), Empty);
EXPECT_EQ(Empty.intersectWith(Empty), Empty);
EXPECT_EQ(Empty.intersectWith(One), Empty);
EXPECT_EQ(Empty.intersectWith(Some), Empty);
EXPECT_EQ(Empty.intersectWith(Wrap), Empty);
EXPECT_EQ(Full.intersectWith(Full), Full);
EXPECT_EQ(Some.intersectWith(Some), Some);
EXPECT_EQ(Some.intersectWith(One), One);
EXPECT_EQ(Full.intersectWith(One), One);
EXPECT_EQ(Full.intersectWith(Some), Some);
EXPECT_EQ(Some.intersectWith(Wrap), Empty);
EXPECT_EQ(One.intersectWith(Wrap), Empty);
EXPECT_EQ(One.intersectWith(Wrap), Wrap.intersectWith(One));
// Klee generated testcase from PR4545.
// The intersection of i16 [4, 2) and [6, 5) is disjoint, looking like
// 01..4.6789ABCDEF where the dots represent values not in the intersection.
ConstantRange LHS(APInt(16, 4), APInt(16, 2));
ConstantRange RHS(APInt(16, 6), APInt(16, 5));
EXPECT_TRUE(LHS.intersectWith(RHS) == LHS);
// previous bug: intersection of [min, 3) and [2, max) should be 2
LHS = ConstantRange(APInt(32, -2147483646), APInt(32, 3));
RHS = ConstantRange(APInt(32, 2), APInt(32, 2147483646));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 2)));
// [2, 0) /\ [4, 3) = [2, 0)
LHS = ConstantRange(APInt(32, 2), APInt(32, 0));
RHS = ConstantRange(APInt(32, 4), APInt(32, 3));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 2), APInt(32, 0)));
// [2, 0) /\ [4, 2) = [4, 0)
LHS = ConstantRange(APInt(32, 2), APInt(32, 0));
RHS = ConstantRange(APInt(32, 4), APInt(32, 2));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 4), APInt(32, 0)));
// [4, 2) /\ [5, 1) = [5, 1)
LHS = ConstantRange(APInt(32, 4), APInt(32, 2));
RHS = ConstantRange(APInt(32, 5), APInt(32, 1));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 5), APInt(32, 1)));
// [2, 0) /\ [7, 4) = [7, 4)
LHS = ConstantRange(APInt(32, 2), APInt(32, 0));
RHS = ConstantRange(APInt(32, 7), APInt(32, 4));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 7), APInt(32, 4)));
// [4, 2) /\ [1, 0) = [1, 0)
LHS = ConstantRange(APInt(32, 4), APInt(32, 2));
RHS = ConstantRange(APInt(32, 1), APInt(32, 0));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 4), APInt(32, 2)));
// [15, 0) /\ [7, 6) = [15, 0)
LHS = ConstantRange(APInt(32, 15), APInt(32, 0));
RHS = ConstantRange(APInt(32, 7), APInt(32, 6));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 15), APInt(32, 0)));
}
template<typename Fn1, typename Fn2>
void testBinarySetOperationExhaustive(Fn1 OpFn, Fn2 InResultFn) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits,
[=](const ConstantRange &CR1, const ConstantRange &CR2) {
[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
2020-09-23 20:43:59 +02:00
SmallBitVector Elems(1 << Bits);
APInt Num(Bits, 0);
[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
2020-09-23 20:43:59 +02:00
for (unsigned I = 0, Limit = 1 << Bits; I < Limit; ++I, ++Num)
if (InResultFn(CR1, CR2, Num))
Elems.set(Num.getZExtValue());
ConstantRange SmallestCR = OpFn(CR1, CR2, ConstantRange::Smallest);
TestRange(SmallestCR, Elems, PreferSmallest, {CR1, CR2});
[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
2020-09-23 20:43:59 +02:00
ConstantRange UnsignedCR = OpFn(CR1, CR2, ConstantRange::Unsigned);
TestRange(UnsignedCR, Elems, PreferSmallestNonFullUnsigned, {CR1, CR2});
[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
2020-09-23 20:43:59 +02:00
ConstantRange SignedCR = OpFn(CR1, CR2, ConstantRange::Signed);
TestRange(SignedCR, Elems, PreferSmallestNonFullSigned, {CR1, CR2});
});
}
TEST_F(ConstantRangeTest, IntersectWithExhaustive) {
testBinarySetOperationExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2,
ConstantRange::PreferredRangeType Type) {
return CR1.intersectWith(CR2, Type);
},
[](const ConstantRange &CR1, const ConstantRange &CR2, const APInt &N) {
return CR1.contains(N) && CR2.contains(N);
});
}
TEST_F(ConstantRangeTest, UnionWithExhaustive) {
testBinarySetOperationExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2,
ConstantRange::PreferredRangeType Type) {
return CR1.unionWith(CR2, Type);
},
[](const ConstantRange &CR1, const ConstantRange &CR2, const APInt &N) {
return CR1.contains(N) || CR2.contains(N);
});
}
TEST_F(ConstantRangeTest, UnionWith) {
EXPECT_EQ(Wrap.unionWith(One),
ConstantRange(APInt(16, 0xaaa), APInt(16, 0xb)));
EXPECT_EQ(One.unionWith(Wrap), Wrap.unionWith(One));
EXPECT_EQ(Empty.unionWith(Empty), Empty);
EXPECT_EQ(Full.unionWith(Full), Full);
EXPECT_EQ(Some.unionWith(Wrap), Full);
// PR4545
EXPECT_EQ(ConstantRange(APInt(16, 14), APInt(16, 1)).unionWith(
ConstantRange(APInt(16, 0), APInt(16, 8))),
ConstantRange(APInt(16, 14), APInt(16, 8)));
EXPECT_EQ(ConstantRange(APInt(16, 6), APInt(16, 4)).unionWith(
ConstantRange(APInt(16, 4), APInt(16, 0))),
ConstantRange::getFull(16));
EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 0)).unionWith(
ConstantRange(APInt(16, 2), APInt(16, 1))),
ConstantRange::getFull(16));
}
TEST_F(ConstantRangeTest, SetDifference) {
EXPECT_EQ(Full.difference(Empty), Full);
EXPECT_EQ(Full.difference(Full), Empty);
EXPECT_EQ(Empty.difference(Empty), Empty);
EXPECT_EQ(Empty.difference(Full), Empty);
ConstantRange A(APInt(16, 3), APInt(16, 7));
ConstantRange B(APInt(16, 5), APInt(16, 9));
ConstantRange C(APInt(16, 3), APInt(16, 5));
ConstantRange D(APInt(16, 7), APInt(16, 9));
ConstantRange E(APInt(16, 5), APInt(16, 4));
ConstantRange F(APInt(16, 7), APInt(16, 3));
EXPECT_EQ(A.difference(B), C);
EXPECT_EQ(B.difference(A), D);
EXPECT_EQ(E.difference(A), F);
}
TEST_F(ConstantRangeTest, getActiveBits) {
unsigned Bits = 4;
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
unsigned Exact = 0;
ForeachNumInConstantRange(CR, [&](const APInt &N) {
Exact = std::max(Exact, N.getActiveBits());
});
unsigned ResultCR = CR.getActiveBits();
EXPECT_EQ(Exact, ResultCR);
});
}
TEST_F(ConstantRangeTest, losslessUnsignedTruncationZeroext) {
unsigned Bits = 4;
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
unsigned MinBitWidth = CR.getActiveBits();
if (MinBitWidth == 0) {
EXPECT_TRUE(CR.isEmptySet() || (CR.isSingleElement() &&
CR.getSingleElement()->isNullValue()));
return;
}
if (MinBitWidth == Bits)
return;
EXPECT_EQ(CR, CR.truncate(MinBitWidth).zeroExtend(Bits));
});
}
TEST_F(ConstantRangeTest, getMinSignedBits) {
unsigned Bits = 4;
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
unsigned Exact = 0;
ForeachNumInConstantRange(CR, [&](const APInt &N) {
Exact = std::max(Exact, N.getMinSignedBits());
});
unsigned ResultCR = CR.getMinSignedBits();
EXPECT_EQ(Exact, ResultCR);
});
}
TEST_F(ConstantRangeTest, losslessSignedTruncationSignext) {
unsigned Bits = 4;
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
unsigned MinBitWidth = CR.getMinSignedBits();
if (MinBitWidth == 0) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
if (MinBitWidth == Bits)
return;
EXPECT_EQ(CR, CR.truncate(MinBitWidth).signExtend(Bits));
});
}
TEST_F(ConstantRangeTest, SubtractAPInt) {
EXPECT_EQ(Full.subtract(APInt(16, 4)), Full);
EXPECT_EQ(Empty.subtract(APInt(16, 4)), Empty);
EXPECT_EQ(Some.subtract(APInt(16, 4)),
ConstantRange(APInt(16, 0x6), APInt(16, 0xaa6)));
EXPECT_EQ(Wrap.subtract(APInt(16, 4)),
ConstantRange(APInt(16, 0xaa6), APInt(16, 0x6)));
EXPECT_EQ(One.subtract(APInt(16, 4)),
ConstantRange(APInt(16, 0x6)));
}
TEST_F(ConstantRangeTest, Add) {
EXPECT_EQ(Full.add(APInt(16, 4)), Full);
EXPECT_EQ(Full.add(Full), Full);
EXPECT_EQ(Full.add(Empty), Empty);
EXPECT_EQ(Full.add(One), Full);
EXPECT_EQ(Full.add(Some), Full);
EXPECT_EQ(Full.add(Wrap), Full);
EXPECT_EQ(Empty.add(Empty), Empty);
EXPECT_EQ(Empty.add(One), Empty);
EXPECT_EQ(Empty.add(Some), Empty);
EXPECT_EQ(Empty.add(Wrap), Empty);
EXPECT_EQ(Empty.add(APInt(16, 4)), Empty);
EXPECT_EQ(Some.add(APInt(16, 4)),
ConstantRange(APInt(16, 0xe), APInt(16, 0xaae)));
EXPECT_EQ(Wrap.add(APInt(16, 4)),
ConstantRange(APInt(16, 0xaae), APInt(16, 0xe)));
EXPECT_EQ(One.add(APInt(16, 4)),
ConstantRange(APInt(16, 0xe)));
[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
2020-09-23 20:43:59 +02:00
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>
static void TestAddWithNoSignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
ConstantRange CR = RangeFn(CR1, CR2);
SignedOpRangeGatherer R(CR.getBitWidth());
bool AllOverflow = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
bool IsOverflow = false;
APInt N = IntFn(IsOverflow, N1, N2);
if (!IsOverflow) {
AllOverflow = false;
R.account(N);
EXPECT_TRUE(CR.contains(N));
}
});
});
EXPECT_EQ(CR.isEmptySet(), AllOverflow);
if (CR1.isSignWrappedSet() || CR2.isSignWrappedSet())
return;
ConstantRange Exact = R.getRange();
EXPECT_EQ(Exact, CR);
});
}
template <typename Fn1, typename Fn2>
static void TestAddWithNoUnsignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
ConstantRange CR = RangeFn(CR1, CR2);
UnsignedOpRangeGatherer R(CR.getBitWidth());
bool AllOverflow = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
bool IsOverflow = false;
APInt N = IntFn(IsOverflow, N1, N2);
if (!IsOverflow) {
AllOverflow = false;
R.account(N);
EXPECT_TRUE(CR.contains(N));
}
});
});
EXPECT_EQ(CR.isEmptySet(), AllOverflow);
if (CR1.isWrappedSet() || CR2.isWrappedSet())
return;
ConstantRange Exact = R.getRange();
EXPECT_EQ(Exact, CR);
});
}
template <typename Fn1, typename Fn2, typename Fn3>
static void TestAddWithNoSignedUnsignedWrapExhaustive(Fn1 RangeFn,
Fn2 IntFnSigned,
Fn3 IntFnUnsigned) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
ConstantRange CR = RangeFn(CR1, CR2);
UnsignedOpRangeGatherer UR(CR.getBitWidth());
SignedOpRangeGatherer SR(CR.getBitWidth());
bool AllOverflow = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
bool IsOverflow = false, IsSignedOverflow = false;
APInt N = IntFnSigned(IsSignedOverflow, N1, N2);
(void) IntFnUnsigned(IsOverflow, N1, N2);
if (!IsSignedOverflow && !IsOverflow) {
AllOverflow = false;
UR.account(N);
SR.account(N);
EXPECT_TRUE(CR.contains(N));
}
});
});
EXPECT_EQ(CR.isEmptySet(), AllOverflow);
if (CR1.isWrappedSet() || CR2.isWrappedSet() ||
CR1.isSignWrappedSet() || CR2.isSignWrappedSet())
return;
ConstantRange ExactUnsignedCR = UR.getRange();
ConstantRange ExactSignedCR = SR.getRange();
if (ExactUnsignedCR.isEmptySet() || ExactSignedCR.isEmptySet()) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
ConstantRange Exact = ExactSignedCR.intersectWith(ExactUnsignedCR);
EXPECT_EQ(Exact, CR);
});
}
TEST_F(ConstantRangeTest, AddWithNoWrap) {
typedef OverflowingBinaryOperator OBO;
EXPECT_EQ(Empty.addWithNoWrap(Some, OBO::NoSignedWrap), Empty);
EXPECT_EQ(Some.addWithNoWrap(Empty, OBO::NoSignedWrap), Empty);
EXPECT_EQ(Full.addWithNoWrap(Full, OBO::NoSignedWrap), Full);
EXPECT_NE(Full.addWithNoWrap(Some, OBO::NoSignedWrap), Full);
EXPECT_NE(Some.addWithNoWrap(Full, OBO::NoSignedWrap), Full);
EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, 1), APInt(16, 2)),
OBO::NoSignedWrap),
ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN)));
EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 2))
.addWithNoWrap(Full, OBO::NoSignedWrap),
ConstantRange(APInt(16, INT16_MIN + 1), APInt(16, INT16_MIN)));
EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, -1), APInt(16, 0)),
OBO::NoSignedWrap),
ConstantRange(APInt(16, INT16_MIN), APInt(16, INT16_MAX)));
EXPECT_EQ(ConstantRange(APInt(8, 100), APInt(8, 120))
.addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, 123)),
OBO::NoSignedWrap),
ConstantRange(8, false));
EXPECT_EQ(ConstantRange(APInt(8, -120), APInt(8, -100))
.addWithNoWrap(ConstantRange(APInt(8, -110), APInt(8, -100)),
OBO::NoSignedWrap),
ConstantRange(8, false));
EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101))
.addWithNoWrap(ConstantRange(APInt(8, -128), APInt(8, 28)),
OBO::NoSignedWrap),
ConstantRange(8, true));
EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101))
.addWithNoWrap(ConstantRange(APInt(8, -120), APInt(8, 29)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, -120), APInt(8, -128)));
EXPECT_EQ(ConstantRange(APInt(8, -50), APInt(8, 50))
.addWithNoWrap(ConstantRange(APInt(8, 10), APInt(8, 20)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, -40), APInt(8, 69)));
EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20))
.addWithNoWrap(ConstantRange(APInt(8, -50), APInt(8, 50)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, -40), APInt(8, 69)));
EXPECT_EQ(ConstantRange(APInt(8, 120), APInt(8, -10))
.addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, 125), APInt(8, 9)));
EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20))
.addWithNoWrap(ConstantRange(APInt(8, 120), APInt(8, -10)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, 125), APInt(8, 9)));
TestAddWithNoSignedWrapExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.addWithNoWrap(CR2, OBO::NoSignedWrap);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.sadd_ov(N2, IsOverflow);
});
EXPECT_EQ(Empty.addWithNoWrap(Some, OBO::NoUnsignedWrap), Empty);
EXPECT_EQ(Some.addWithNoWrap(Empty, OBO::NoUnsignedWrap), Empty);
EXPECT_EQ(Full.addWithNoWrap(Full, OBO::NoUnsignedWrap), Full);
EXPECT_NE(Full.addWithNoWrap(Some, OBO::NoUnsignedWrap), Full);
EXPECT_NE(Some.addWithNoWrap(Full, OBO::NoUnsignedWrap), Full);
EXPECT_EQ(Full.addWithNoWrap(ConstantRange(APInt(16, 1), APInt(16, 2)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(16, 1), APInt(16, 0)));
EXPECT_EQ(ConstantRange(APInt(16, 1), APInt(16, 2))
.addWithNoWrap(Full, OBO::NoUnsignedWrap),
ConstantRange(APInt(16, 1), APInt(16, 0)));
EXPECT_EQ(ConstantRange(APInt(8, 200), APInt(8, 220))
.addWithNoWrap(ConstantRange(APInt(8, 100), APInt(8, 123)),
OBO::NoUnsignedWrap),
ConstantRange(8, false));
EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101))
.addWithNoWrap(ConstantRange(APInt(8, 0), APInt(8, 156)),
OBO::NoUnsignedWrap),
ConstantRange(8, true));
EXPECT_EQ(ConstantRange(APInt(8, 0), APInt(8, 101))
.addWithNoWrap(ConstantRange(APInt(8, 10), APInt(8, 29)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 10), APInt(8, 129)));
EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, 10))
.addWithNoWrap(ConstantRange(APInt(8, 50), APInt(8, 200)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 50), APInt(8, 0)));
EXPECT_EQ(ConstantRange(APInt(8, 10), APInt(8, 20))
.addWithNoWrap(ConstantRange(APInt(8, 50), APInt(8, 200)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 60), APInt(8, -37)));
EXPECT_EQ(ConstantRange(APInt(8, 20), APInt(8, -30))
.addWithNoWrap(ConstantRange(APInt(8, 5), APInt(8, 20)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 25), APInt(8, -11)));
EXPECT_EQ(ConstantRange(APInt(8, 5), APInt(8, 20))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, -30)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 25), APInt(8, -11)));
TestAddWithNoUnsignedWrapExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.addWithNoWrap(CR2, OBO::NoUnsignedWrap);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.uadd_ov(N2, IsOverflow);
});
EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, 70), APInt(8, -128)));
EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 70), APInt(8, 169)));
EXPECT_EQ(ConstantRange(APInt(8, 50), APInt(8, 100))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 70)),
OBO::NoUnsignedWrap | OBO::NoSignedWrap),
ConstantRange(APInt(8, 70), APInt(8, -128)));
EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)),
OBO::NoSignedWrap),
ConstantRange(APInt(8, -80), APInt(8, -21)));
EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)),
OBO::NoUnsignedWrap),
ConstantRange(APInt(8, 176), APInt(8, 235)));
EXPECT_EQ(ConstantRange(APInt(8, -100), APInt(8, -50))
.addWithNoWrap(ConstantRange(APInt(8, 20), APInt(8, 30)),
OBO::NoUnsignedWrap | OBO::NoSignedWrap),
ConstantRange(APInt(8, 176), APInt(8, 235)));
TestAddWithNoSignedUnsignedWrapExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.addWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.sadd_ov(N2, IsOverflow);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.uadd_ov(N2, IsOverflow);
});
}
TEST_F(ConstantRangeTest, Sub) {
EXPECT_EQ(Full.sub(APInt(16, 4)), Full);
EXPECT_EQ(Full.sub(Full), Full);
EXPECT_EQ(Full.sub(Empty), Empty);
EXPECT_EQ(Full.sub(One), Full);
EXPECT_EQ(Full.sub(Some), Full);
EXPECT_EQ(Full.sub(Wrap), Full);
EXPECT_EQ(Empty.sub(Empty), Empty);
EXPECT_EQ(Empty.sub(One), Empty);
EXPECT_EQ(Empty.sub(Some), Empty);
EXPECT_EQ(Empty.sub(Wrap), Empty);
EXPECT_EQ(Empty.sub(APInt(16, 4)), Empty);
EXPECT_EQ(Some.sub(APInt(16, 4)),
ConstantRange(APInt(16, 0x6), APInt(16, 0xaa6)));
EXPECT_EQ(Some.sub(Some),
ConstantRange(APInt(16, 0xf561), APInt(16, 0xaa0)));
EXPECT_EQ(Wrap.sub(APInt(16, 4)),
ConstantRange(APInt(16, 0xaa6), APInt(16, 0x6)));
EXPECT_EQ(One.sub(APInt(16, 4)),
ConstantRange(APInt(16, 0x6)));
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.sub(CR2);
},
[](const APInt &N1, const APInt &N2) {
return N1 - N2;
});
}
TEST_F(ConstantRangeTest, SubWithNoWrap) {
typedef OverflowingBinaryOperator OBO;
TestAddWithNoSignedWrapExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.subWithNoWrap(CR2, OBO::NoSignedWrap);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.ssub_ov(N2, IsOverflow);
});
TestAddWithNoUnsignedWrapExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.subWithNoWrap(CR2, OBO::NoUnsignedWrap);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.usub_ov(N2, IsOverflow);
});
TestAddWithNoSignedUnsignedWrapExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.subWithNoWrap(CR2, OBO::NoUnsignedWrap | OBO::NoSignedWrap);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.ssub_ov(N2, IsOverflow);
},
[](bool &IsOverflow, const APInt &N1, const APInt &N2) {
return N1.usub_ov(N2, IsOverflow);
});
}
TEST_F(ConstantRangeTest, Multiply) {
EXPECT_EQ(Full.multiply(Full), Full);
EXPECT_EQ(Full.multiply(Empty), Empty);
EXPECT_EQ(Full.multiply(One), Full);
EXPECT_EQ(Full.multiply(Some), Full);
EXPECT_EQ(Full.multiply(Wrap), Full);
EXPECT_EQ(Empty.multiply(Empty), Empty);
EXPECT_EQ(Empty.multiply(One), Empty);
EXPECT_EQ(Empty.multiply(Some), Empty);
EXPECT_EQ(Empty.multiply(Wrap), Empty);
EXPECT_EQ(One.multiply(One), ConstantRange(APInt(16, 0xa*0xa),
APInt(16, 0xa*0xa + 1)));
EXPECT_EQ(One.multiply(Some), ConstantRange(APInt(16, 0xa*0xa),
APInt(16, 0xa*0xaa9 + 1)));
EXPECT_EQ(One.multiply(Wrap), Full);
EXPECT_EQ(Some.multiply(Some), Full);
EXPECT_EQ(Some.multiply(Wrap), Full);
EXPECT_EQ(Wrap.multiply(Wrap), Full);
ConstantRange Zero(APInt(16, 0));
EXPECT_EQ(Zero.multiply(Full), Zero);
EXPECT_EQ(Zero.multiply(Some), Zero);
EXPECT_EQ(Zero.multiply(Wrap), Zero);
EXPECT_EQ(Full.multiply(Zero), Zero);
EXPECT_EQ(Some.multiply(Zero), Zero);
EXPECT_EQ(Wrap.multiply(Zero), Zero);
// http://llvm.org/PR4545
EXPECT_EQ(ConstantRange(APInt(4, 1), APInt(4, 6)).multiply(
ConstantRange(APInt(4, 6), APInt(4, 2))),
ConstantRange(4, /*isFullSet=*/true));
EXPECT_EQ(ConstantRange(APInt(8, 254), APInt(8, 0)).multiply(
ConstantRange(APInt(8, 252), APInt(8, 4))),
ConstantRange(APInt(8, 250), APInt(8, 9)));
EXPECT_EQ(ConstantRange(APInt(8, 254), APInt(8, 255)).multiply(
ConstantRange(APInt(8, 2), APInt(8, 4))),
ConstantRange(APInt(8, 250), APInt(8, 253)));
// TODO: This should be return [-2, 0]
EXPECT_EQ(ConstantRange(APInt(8, -2)).multiply(
ConstantRange(APInt(8, 0), APInt(8, 2))),
ConstantRange(APInt(8, -2), APInt(8, 1)));
}
TEST_F(ConstantRangeTest, UMax) {
EXPECT_EQ(Full.umax(Full), Full);
EXPECT_EQ(Full.umax(Empty), Empty);
EXPECT_EQ(Full.umax(Some), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
EXPECT_EQ(Full.umax(Wrap), Full);
EXPECT_EQ(Full.umax(Some), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
EXPECT_EQ(Empty.umax(Empty), Empty);
EXPECT_EQ(Empty.umax(Some), Empty);
EXPECT_EQ(Empty.umax(Wrap), Empty);
EXPECT_EQ(Empty.umax(One), Empty);
EXPECT_EQ(Some.umax(Some), Some);
EXPECT_EQ(Some.umax(Wrap), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
EXPECT_EQ(Some.umax(One), Some);
EXPECT_EQ(Wrap.umax(Wrap), Wrap);
EXPECT_EQ(Wrap.umax(One), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
EXPECT_EQ(One.umax(One), One);
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.umax(CR2);
},
[](const APInt &N1, const APInt &N2) {
return APIntOps::umax(N1, N2);
},
PreferSmallestNonFullUnsigned);
}
TEST_F(ConstantRangeTest, SMax) {
EXPECT_EQ(Full.smax(Full), Full);
EXPECT_EQ(Full.smax(Empty), Empty);
EXPECT_EQ(Full.smax(Some), ConstantRange(APInt(16, 0xa),
APInt::getSignedMinValue(16)));
EXPECT_EQ(Full.smax(Wrap), Full);
EXPECT_EQ(Full.smax(One), ConstantRange(APInt(16, 0xa),
APInt::getSignedMinValue(16)));
EXPECT_EQ(Empty.smax(Empty), Empty);
EXPECT_EQ(Empty.smax(Some), Empty);
EXPECT_EQ(Empty.smax(Wrap), Empty);
EXPECT_EQ(Empty.smax(One), Empty);
EXPECT_EQ(Some.smax(Some), Some);
EXPECT_EQ(Some.smax(Wrap), ConstantRange(APInt(16, 0xa),
APInt(16, (uint64_t)INT16_MIN)));
EXPECT_EQ(Some.smax(One), Some);
EXPECT_EQ(Wrap.smax(One), ConstantRange(APInt(16, 0xa),
APInt(16, (uint64_t)INT16_MIN)));
EXPECT_EQ(One.smax(One), One);
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.smax(CR2);
},
[](const APInt &N1, const APInt &N2) {
return APIntOps::smax(N1, N2);
},
PreferSmallestNonFullSigned);
}
TEST_F(ConstantRangeTest, UMin) {
EXPECT_EQ(Full.umin(Full), Full);
EXPECT_EQ(Full.umin(Empty), Empty);
EXPECT_EQ(Full.umin(Some), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
EXPECT_EQ(Full.umin(Wrap), Full);
EXPECT_EQ(Empty.umin(Empty), Empty);
EXPECT_EQ(Empty.umin(Some), Empty);
EXPECT_EQ(Empty.umin(Wrap), Empty);
EXPECT_EQ(Empty.umin(One), Empty);
EXPECT_EQ(Some.umin(Some), Some);
EXPECT_EQ(Some.umin(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
EXPECT_EQ(Some.umin(One), One);
EXPECT_EQ(Wrap.umin(Wrap), Wrap);
EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
EXPECT_EQ(One.umin(One), One);
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.umin(CR2);
},
[](const APInt &N1, const APInt &N2) {
return APIntOps::umin(N1, N2);
},
PreferSmallestNonFullUnsigned);
}
TEST_F(ConstantRangeTest, SMin) {
EXPECT_EQ(Full.smin(Full), Full);
EXPECT_EQ(Full.smin(Empty), Empty);
EXPECT_EQ(Full.smin(Some), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
APInt(16, 0xaaa)));
EXPECT_EQ(Full.smin(Wrap), Full);
EXPECT_EQ(Empty.smin(Empty), Empty);
EXPECT_EQ(Empty.smin(Some), Empty);
EXPECT_EQ(Empty.smin(Wrap), Empty);
EXPECT_EQ(Empty.smin(One), Empty);
EXPECT_EQ(Some.smin(Some), Some);
EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
APInt(16, 0xaaa)));
EXPECT_EQ(Some.smin(One), One);
EXPECT_EQ(Wrap.smin(Wrap), Wrap);
EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
APInt(16, 0xb)));
EXPECT_EQ(One.smin(One), One);
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.smin(CR2);
},
[](const APInt &N1, const APInt &N2) {
return APIntOps::smin(N1, N2);
},
PreferSmallestNonFullSigned);
}
TEST_F(ConstantRangeTest, UDiv) {
EXPECT_EQ(Full.udiv(Full), Full);
EXPECT_EQ(Full.udiv(Empty), Empty);
EXPECT_EQ(Full.udiv(One), ConstantRange(APInt(16, 0),
APInt(16, 0xffff / 0xa + 1)));
EXPECT_EQ(Full.udiv(Some), ConstantRange(APInt(16, 0),
APInt(16, 0xffff / 0xa + 1)));
EXPECT_EQ(Full.udiv(Wrap), Full);
EXPECT_EQ(Empty.udiv(Empty), Empty);
EXPECT_EQ(Empty.udiv(One), Empty);
EXPECT_EQ(Empty.udiv(Some), Empty);
EXPECT_EQ(Empty.udiv(Wrap), Empty);
EXPECT_EQ(One.udiv(One), ConstantRange(APInt(16, 1)));
EXPECT_EQ(One.udiv(Some), ConstantRange(APInt(16, 0), APInt(16, 2)));
EXPECT_EQ(One.udiv(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
EXPECT_EQ(Some.udiv(Some), ConstantRange(APInt(16, 0), APInt(16, 0x111)));
EXPECT_EQ(Some.udiv(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
EXPECT_EQ(Wrap.udiv(Wrap), Full);
ConstantRange Zero(APInt(16, 0));
EXPECT_EQ(Zero.udiv(One), Zero);
EXPECT_EQ(Zero.udiv(Full), Zero);
EXPECT_EQ(ConstantRange(APInt(16, 0), APInt(16, 99)).udiv(Full),
ConstantRange(APInt(16, 0), APInt(16, 99)));
EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 99)).udiv(Full),
ConstantRange(APInt(16, 0), APInt(16, 99)));
}
TEST_F(ConstantRangeTest, SDiv) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
// Collect possible results in a bit vector. We store the signed value plus
// a bias to make it unsigned.
int Bias = 1 << (Bits - 1);
BitVector Results(1 << Bits);
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
// Division by zero is UB.
if (N2 == 0)
return;
// SignedMin / -1 is UB.
if (N1.isMinSignedValue() && N2.isAllOnesValue())
return;
APInt N = N1.sdiv(N2);
Results.set(N.getSExtValue() + Bias);
});
});
ConstantRange CR = CR1.sdiv(CR2);
if (Results.none()) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
// If there is a non-full signed envelope, that should be the result.
APInt SMin(Bits, Results.find_first() - Bias);
APInt SMax(Bits, Results.find_last() - Bias);
ConstantRange Envelope = ConstantRange::getNonEmpty(SMin, SMax + 1);
if (!Envelope.isFullSet()) {
EXPECT_EQ(Envelope, CR);
return;
}
// If the signed envelope is a full set, try to find a smaller sign wrapped
// set that is separated in negative and positive components (or one which
// can also additionally contain zero).
int LastNeg = Results.find_last_in(0, Bias) - Bias;
int LastPos = Results.find_next(Bias) - Bias;
if (Results[Bias]) {
if (LastNeg == -1)
++LastNeg;
else if (LastPos == 1)
--LastPos;
}
APInt WMax(Bits, LastNeg);
APInt WMin(Bits, LastPos);
ConstantRange Wrapped = ConstantRange::getNonEmpty(WMin, WMax + 1);
EXPECT_EQ(Wrapped, CR);
});
}
TEST_F(ConstantRangeTest, URem) {
EXPECT_EQ(Full.urem(Empty), Empty);
EXPECT_EQ(Empty.urem(Full), Empty);
// urem by zero is poison.
EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0))), Empty);
// urem by full range doesn't contain MaxValue.
EXPECT_EQ(Full.urem(Full), ConstantRange(APInt(16, 0), APInt(16, 0xffff)));
// urem is upper bounded by maximum RHS minus one.
EXPECT_EQ(Full.urem(ConstantRange(APInt(16, 0), APInt(16, 123))),
ConstantRange(APInt(16, 0), APInt(16, 122)));
// urem is upper bounded by maximum LHS.
EXPECT_EQ(ConstantRange(APInt(16, 0), APInt(16, 123)).urem(Full),
ConstantRange(APInt(16, 0), APInt(16, 123)));
// If the LHS is always lower than the RHS, the result is the LHS.
EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20))
.urem(ConstantRange(APInt(16, 20), APInt(16, 30))),
ConstantRange(APInt(16, 10), APInt(16, 20)));
// It has to be strictly lower, otherwise the top value may wrap to zero.
EXPECT_EQ(ConstantRange(APInt(16, 10), APInt(16, 20))
.urem(ConstantRange(APInt(16, 19), APInt(16, 30))),
ConstantRange(APInt(16, 0), APInt(16, 20)));
// [12, 14] % 10 is [2, 4], but we conservatively compute [0, 9].
EXPECT_EQ(ConstantRange(APInt(16, 12), APInt(16, 15))
.urem(ConstantRange(APInt(16, 10))),
ConstantRange(APInt(16, 0), APInt(16, 10)));
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustiveCorrectnessOnly(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.urem(CR2);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N1, const APInt &N2) -> Optional<APInt> {
if (N2.isNullValue())
return None;
return N1.urem(N2);
[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
2020-09-23 20:43:59 +02:00
});
}
TEST_F(ConstantRangeTest, SRem) {
EXPECT_EQ(Full.srem(Empty), Empty);
EXPECT_EQ(Empty.srem(Full), Empty);
// srem by zero is UB.
EXPECT_EQ(Full.srem(ConstantRange(APInt(16, 0))), Empty);
// srem by full range doesn't contain SignedMinValue.
EXPECT_EQ(Full.srem(Full), ConstantRange(APInt::getSignedMinValue(16) + 1,
APInt::getSignedMinValue(16)));
ConstantRange PosMod(APInt(16, 10), APInt(16, 21)); // [10, 20]
ConstantRange NegMod(APInt(16, -20), APInt(16, -9)); // [-20, -10]
ConstantRange IntMinMod(APInt::getSignedMinValue(16));
ConstantRange Expected(16, true);
// srem is bounded by abs(RHS) minus one.
ConstantRange PosLargeLHS(APInt(16, 0), APInt(16, 41));
Expected = ConstantRange(APInt(16, 0), APInt(16, 20));
EXPECT_EQ(PosLargeLHS.srem(PosMod), Expected);
EXPECT_EQ(PosLargeLHS.srem(NegMod), Expected);
ConstantRange NegLargeLHS(APInt(16, -40), APInt(16, 1));
Expected = ConstantRange(APInt(16, -19), APInt(16, 1));
EXPECT_EQ(NegLargeLHS.srem(PosMod), Expected);
EXPECT_EQ(NegLargeLHS.srem(NegMod), Expected);
ConstantRange PosNegLargeLHS(APInt(16, -32), APInt(16, 38));
Expected = ConstantRange(APInt(16, -19), APInt(16, 20));
EXPECT_EQ(PosNegLargeLHS.srem(PosMod), Expected);
EXPECT_EQ(PosNegLargeLHS.srem(NegMod), Expected);
// srem is bounded by LHS.
ConstantRange PosLHS(APInt(16, 0), APInt(16, 16));
EXPECT_EQ(PosLHS.srem(PosMod), PosLHS);
EXPECT_EQ(PosLHS.srem(NegMod), PosLHS);
EXPECT_EQ(PosLHS.srem(IntMinMod), PosLHS);
ConstantRange NegLHS(APInt(16, -15), APInt(16, 1));
EXPECT_EQ(NegLHS.srem(PosMod), NegLHS);
EXPECT_EQ(NegLHS.srem(NegMod), NegLHS);
EXPECT_EQ(NegLHS.srem(IntMinMod), NegLHS);
ConstantRange PosNegLHS(APInt(16, -12), APInt(16, 18));
EXPECT_EQ(PosNegLHS.srem(PosMod), PosNegLHS);
EXPECT_EQ(PosNegLHS.srem(NegMod), PosNegLHS);
EXPECT_EQ(PosNegLHS.srem(IntMinMod), PosNegLHS);
// srem is LHS if it is smaller than RHS.
ConstantRange PosSmallLHS(APInt(16, 3), APInt(16, 8));
EXPECT_EQ(PosSmallLHS.srem(PosMod), PosSmallLHS);
EXPECT_EQ(PosSmallLHS.srem(NegMod), PosSmallLHS);
EXPECT_EQ(PosSmallLHS.srem(IntMinMod), PosSmallLHS);
ConstantRange NegSmallLHS(APInt(16, -7), APInt(16, -2));
EXPECT_EQ(NegSmallLHS.srem(PosMod), NegSmallLHS);
EXPECT_EQ(NegSmallLHS.srem(NegMod), NegSmallLHS);
EXPECT_EQ(NegSmallLHS.srem(IntMinMod), NegSmallLHS);
ConstantRange PosNegSmallLHS(APInt(16, -3), APInt(16, 8));
EXPECT_EQ(PosNegSmallLHS.srem(PosMod), PosNegSmallLHS);
EXPECT_EQ(PosNegSmallLHS.srem(NegMod), PosNegSmallLHS);
EXPECT_EQ(PosNegSmallLHS.srem(IntMinMod), PosNegSmallLHS);
// Example of a suboptimal result:
// [12, 14] srem 10 is [2, 4], but we conservatively compute [0, 9].
EXPECT_EQ(ConstantRange(APInt(16, 12), APInt(16, 15))
.srem(ConstantRange(APInt(16, 10))),
ConstantRange(APInt(16, 0), APInt(16, 10)));
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustiveCorrectnessOnly(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.srem(CR2);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N1, const APInt &N2) -> Optional<APInt> {
if (N2.isNullValue())
return None;
return N1.srem(N2);
[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
2020-09-23 20:43:59 +02:00
});
}
TEST_F(ConstantRangeTest, Shl) {
ConstantRange Some2(APInt(16, 0xfff), APInt(16, 0x8000));
ConstantRange WrapNullMax(APInt(16, 0x1), APInt(16, 0x0));
EXPECT_EQ(Full.shl(Full), Full);
EXPECT_EQ(Full.shl(Empty), Empty);
EXPECT_EQ(Full.shl(One), Full); // TODO: [0, (-1 << 0xa) + 1)
EXPECT_EQ(Full.shl(Some), Full); // TODO: [0, (-1 << 0xa) + 1)
EXPECT_EQ(Full.shl(Wrap), Full);
EXPECT_EQ(Empty.shl(Empty), Empty);
EXPECT_EQ(Empty.shl(One), Empty);
EXPECT_EQ(Empty.shl(Some), Empty);
EXPECT_EQ(Empty.shl(Wrap), Empty);
EXPECT_EQ(One.shl(One), ConstantRange(APInt(16, 0xa << 0xa),
APInt(16, (0xa << 0xa) + 1)));
EXPECT_EQ(One.shl(Some), Full); // TODO: [0xa << 0xa, 0)
EXPECT_EQ(One.shl(Wrap), Full); // TODO: [0xa, 0xa << 14 + 1)
EXPECT_EQ(Some.shl(Some), Full); // TODO: [0xa << 0xa, 0xfc01)
EXPECT_EQ(Some.shl(Wrap), Full); // TODO: [0xa, 0x7ff << 0x5 + 1)
EXPECT_EQ(Wrap.shl(Wrap), Full);
EXPECT_EQ(
Some2.shl(ConstantRange(APInt(16, 0x1))),
ConstantRange(APInt(16, 0xfff << 0x1), APInt(16, 0x7fff << 0x1) + 1));
EXPECT_EQ(One.shl(WrapNullMax), Full);
}
TEST_F(ConstantRangeTest, Lshr) {
EXPECT_EQ(Full.lshr(Full), Full);
EXPECT_EQ(Full.lshr(Empty), Empty);
EXPECT_EQ(Full.lshr(One), ConstantRange(APInt(16, 0),
APInt(16, (0xffff >> 0xa) + 1)));
EXPECT_EQ(Full.lshr(Some), ConstantRange(APInt(16, 0),
APInt(16, (0xffff >> 0xa) + 1)));
EXPECT_EQ(Full.lshr(Wrap), Full);
EXPECT_EQ(Empty.lshr(Empty), Empty);
EXPECT_EQ(Empty.lshr(One), Empty);
EXPECT_EQ(Empty.lshr(Some), Empty);
EXPECT_EQ(Empty.lshr(Wrap), Empty);
EXPECT_EQ(One.lshr(One), ConstantRange(APInt(16, 0)));
EXPECT_EQ(One.lshr(Some), ConstantRange(APInt(16, 0)));
EXPECT_EQ(One.lshr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
EXPECT_EQ(Some.lshr(Some), ConstantRange(APInt(16, 0),
APInt(16, (0xaaa >> 0xa) + 1)));
EXPECT_EQ(Some.lshr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
EXPECT_EQ(Wrap.lshr(Wrap), Full);
}
TEST_F(ConstantRangeTest, Ashr) {
EXPECT_EQ(Full.ashr(Full), Full);
EXPECT_EQ(Full.ashr(Empty), Empty);
EXPECT_EQ(Full.ashr(One), ConstantRange(APInt(16, 0xffe0),
APInt(16, (0x7fff >> 0xa) + 1 )));
ConstantRange Small(APInt(16, 0xa), APInt(16, 0xb));
EXPECT_EQ(Full.ashr(Small), ConstantRange(APInt(16, 0xffe0),
APInt(16, (0x7fff >> 0xa) + 1 )));
EXPECT_EQ(Full.ashr(Some), ConstantRange(APInt(16, 0xffe0),
APInt(16, (0x7fff >> 0xa) + 1 )));
EXPECT_EQ(Full.ashr(Wrap), Full);
EXPECT_EQ(Empty.ashr(Empty), Empty);
EXPECT_EQ(Empty.ashr(One), Empty);
EXPECT_EQ(Empty.ashr(Some), Empty);
EXPECT_EQ(Empty.ashr(Wrap), Empty);
EXPECT_EQ(One.ashr(One), ConstantRange(APInt(16, 0)));
EXPECT_EQ(One.ashr(Some), ConstantRange(APInt(16, 0)));
EXPECT_EQ(One.ashr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
EXPECT_EQ(Some.ashr(Some), ConstantRange(APInt(16, 0),
APInt(16, (0xaaa >> 0xa) + 1)));
EXPECT_EQ(Some.ashr(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
EXPECT_EQ(Wrap.ashr(Wrap), Full);
ConstantRange Neg(APInt(16, 0xf3f0, true), APInt(16, 0xf7f8, true));
EXPECT_EQ(Neg.ashr(Small), ConstantRange(APInt(16, 0xfffc, true),
APInt(16, 0xfffe, true)));
}
TEST(ConstantRange, MakeAllowedICmpRegion) {
// PR8250
ConstantRange SMax = ConstantRange(APInt::getSignedMaxValue(32));
EXPECT_TRUE(ConstantRange::makeAllowedICmpRegion(ICmpInst::ICMP_SGT, SMax)
.isEmptySet());
}
TEST(ConstantRange, MakeSatisfyingICmpRegion) {
ConstantRange LowHalf(APInt(8, 0), APInt(8, 128));
ConstantRange HighHalf(APInt(8, 128), APInt(8, 0));
ConstantRange EmptySet(8, /* isFullSet = */ false);
EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_NE, LowHalf),
HighHalf);
EXPECT_EQ(
ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_NE, HighHalf),
LowHalf);
EXPECT_TRUE(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_EQ,
HighHalf).isEmptySet());
ConstantRange UnsignedSample(APInt(8, 5), APInt(8, 200));
EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_ULT,
UnsignedSample),
ConstantRange(APInt(8, 0), APInt(8, 5)));
EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_ULE,
UnsignedSample),
ConstantRange(APInt(8, 0), APInt(8, 6)));
EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_UGT,
UnsignedSample),
ConstantRange(APInt(8, 200), APInt(8, 0)));
EXPECT_EQ(ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_UGE,
UnsignedSample),
ConstantRange(APInt(8, 199), APInt(8, 0)));
ConstantRange SignedSample(APInt(8, -5), APInt(8, 5));
EXPECT_EQ(
ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLT, SignedSample),
ConstantRange(APInt(8, -128), APInt(8, -5)));
EXPECT_EQ(
ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SLE, SignedSample),
ConstantRange(APInt(8, -128), APInt(8, -4)));
EXPECT_EQ(
ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGT, SignedSample),
ConstantRange(APInt(8, 5), APInt(8, -128)));
EXPECT_EQ(
ConstantRange::makeSatisfyingICmpRegion(ICmpInst::ICMP_SGE, SignedSample),
ConstantRange(APInt(8, 4), APInt(8, -128)));
}
static bool icmp(CmpInst::Predicate Pred, const APInt &LHS, const APInt &RHS) {
switch (Pred) {
case CmpInst::Predicate::ICMP_EQ:
return LHS.eq(RHS);
case CmpInst::Predicate::ICMP_NE:
return LHS.ne(RHS);
case CmpInst::Predicate::ICMP_UGT:
return LHS.ugt(RHS);
case CmpInst::Predicate::ICMP_UGE:
return LHS.uge(RHS);
case CmpInst::Predicate::ICMP_ULT:
return LHS.ult(RHS);
case CmpInst::Predicate::ICMP_ULE:
return LHS.ule(RHS);
case CmpInst::Predicate::ICMP_SGT:
return LHS.sgt(RHS);
case CmpInst::Predicate::ICMP_SGE:
return LHS.sge(RHS);
case CmpInst::Predicate::ICMP_SLT:
return LHS.slt(RHS);
case CmpInst::Predicate::ICMP_SLE:
return LHS.sle(RHS);
default:
llvm_unreachable("Not an ICmp predicate!");
}
}
void ICmpTestImpl(CmpInst::Predicate Pred) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
bool Exhaustive = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(
CR2, [&](const APInt &N2) { Exhaustive &= icmp(Pred, N1, N2); });
});
EXPECT_EQ(CR1.icmp(Pred, CR2), Exhaustive);
});
}
TEST(ConstantRange, ICmp) {
for (auto Pred : seq_inclusive(CmpInst::Predicate::FIRST_ICMP_PREDICATE,
CmpInst::Predicate::LAST_ICMP_PREDICATE))
ICmpTestImpl(Pred);
}
TEST(ConstantRange, MakeGuaranteedNoWrapRegion) {
const int IntMin4Bits = 8;
const int IntMax4Bits = 7;
typedef OverflowingBinaryOperator OBO;
for (int Const : {0, -1, -2, 1, 2, IntMin4Bits, IntMax4Bits}) {
APInt C(4, Const, true /* = isSigned */);
auto NUWRegion = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, C, OBO::NoUnsignedWrap);
EXPECT_FALSE(NUWRegion.isEmptySet());
auto NSWRegion = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, C, OBO::NoSignedWrap);
EXPECT_FALSE(NSWRegion.isEmptySet());
for (APInt I = NUWRegion.getLower(), E = NUWRegion.getUpper(); I != E;
++I) {
bool Overflow = false;
(void)I.uadd_ov(C, Overflow);
EXPECT_FALSE(Overflow);
}
for (APInt I = NSWRegion.getLower(), E = NSWRegion.getUpper(); I != E;
++I) {
bool Overflow = false;
(void)I.sadd_ov(C, Overflow);
EXPECT_FALSE(Overflow);
}
}
for (int Const : {0, -1, -2, 1, 2, IntMin4Bits, IntMax4Bits}) {
APInt C(4, Const, true /* = isSigned */);
auto NUWRegion = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, C, OBO::NoUnsignedWrap);
EXPECT_FALSE(NUWRegion.isEmptySet());
auto NSWRegion = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, C, OBO::NoSignedWrap);
EXPECT_FALSE(NSWRegion.isEmptySet());
for (APInt I = NUWRegion.getLower(), E = NUWRegion.getUpper(); I != E;
++I) {
bool Overflow = false;
(void)I.usub_ov(C, Overflow);
EXPECT_FALSE(Overflow);
}
for (APInt I = NSWRegion.getLower(), E = NSWRegion.getUpper(); I != E;
++I) {
bool Overflow = false;
(void)I.ssub_ov(C, Overflow);
EXPECT_FALSE(Overflow);
}
}
auto NSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, ConstantRange(32, /* isFullSet = */ true),
OBO::NoSignedWrap);
EXPECT_TRUE(NSWForAllValues.isSingleElement() &&
NSWForAllValues.getSingleElement()->isMinValue());
NSWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, ConstantRange(32, /* isFullSet = */ true),
OBO::NoSignedWrap);
EXPECT_TRUE(NSWForAllValues.isSingleElement() &&
NSWForAllValues.getSingleElement()->isMaxValue());
auto NUWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, ConstantRange(32, /* isFullSet = */ true),
OBO::NoUnsignedWrap);
EXPECT_TRUE(NUWForAllValues.isSingleElement() &&
NUWForAllValues.getSingleElement()->isMinValue());
NUWForAllValues = ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, ConstantRange(32, /* isFullSet = */ true),
OBO::NoUnsignedWrap);
EXPECT_TRUE(NUWForAllValues.isSingleElement() &&
NUWForAllValues.getSingleElement()->isMaxValue());
EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, APInt(32, 0), OBO::NoUnsignedWrap).isFullSet());
EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, APInt(32, 0), OBO::NoSignedWrap).isFullSet());
EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, APInt(32, 0), OBO::NoUnsignedWrap).isFullSet());
EXPECT_TRUE(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, APInt(32, 0), OBO::NoSignedWrap).isFullSet());
ConstantRange OneToFive(APInt(32, 1), APInt(32, 6));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, OneToFive, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32),
APInt::getSignedMaxValue(32) - 4));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, OneToFive, OBO::NoUnsignedWrap),
ConstantRange(APInt::getMinValue(32), APInt::getMinValue(32) - 5));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, OneToFive, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32) + 5,
APInt::getSignedMinValue(32)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, OneToFive, OBO::NoUnsignedWrap),
ConstantRange(APInt::getMinValue(32) + 5, APInt::getMinValue(32)));
ConstantRange MinusFiveToMinusTwo(APInt(32, -5), APInt(32, -1));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, MinusFiveToMinusTwo, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32) + 5,
APInt::getSignedMinValue(32)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, MinusFiveToMinusTwo, OBO::NoUnsignedWrap),
ConstantRange(APInt(32, 0), APInt(32, 2)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, MinusFiveToMinusTwo, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32),
APInt::getSignedMaxValue(32) - 4));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, MinusFiveToMinusTwo, OBO::NoUnsignedWrap),
ConstantRange(APInt::getMaxValue(32) - 1,
APInt::getMinValue(32)));
ConstantRange MinusOneToOne(APInt(32, -1), APInt(32, 2));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, MinusOneToOne, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32) + 1,
APInt::getSignedMinValue(32) - 1));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, MinusOneToOne, OBO::NoUnsignedWrap),
ConstantRange(APInt(32, 0), APInt(32, 1)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, MinusOneToOne, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32) + 1,
APInt::getSignedMinValue(32) - 1));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, MinusOneToOne, OBO::NoUnsignedWrap),
ConstantRange(APInt::getMaxValue(32),
APInt::getMinValue(32)));
ConstantRange One(APInt(32, 1), APInt(32, 2));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, One, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32),
APInt::getSignedMaxValue(32)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Add, One, OBO::NoUnsignedWrap),
ConstantRange(APInt::getMinValue(32), APInt::getMaxValue(32)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, One, OBO::NoSignedWrap),
ConstantRange(APInt::getSignedMinValue(32) + 1,
APInt::getSignedMinValue(32)));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Sub, One, OBO::NoUnsignedWrap),
ConstantRange(APInt::getMinValue(32) + 1, APInt::getMinValue(32)));
ConstantRange OneLessThanBitWidth(APInt(32, 0), APInt(32, 31) + 1);
ConstantRange UpToBitWidth(APInt(32, 0), APInt(32, 32) + 1);
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, UpToBitWidth, OBO::NoUnsignedWrap),
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, OneLessThanBitWidth, OBO::NoUnsignedWrap));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap),
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, OneLessThanBitWidth, OBO::NoSignedWrap));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, UpToBitWidth, OBO::NoUnsignedWrap),
ConstantRange(APInt(32, 0), APInt(32, 1) + 1));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, UpToBitWidth, OBO::NoSignedWrap),
ConstantRange(APInt(32, -1), APInt(32, 0) + 1));
EXPECT_EQ(
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, ConstantRange::getFull(32), OBO::NoUnsignedWrap),
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, OneLessThanBitWidth, OBO::NoUnsignedWrap));
EXPECT_EQ(
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, ConstantRange::getFull(32), OBO::NoSignedWrap),
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, OneLessThanBitWidth, OBO::NoSignedWrap));
ConstantRange IllegalShAmt(APInt(32, 32), APInt(32, 0) + 1);
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, IllegalShAmt, OBO::NoUnsignedWrap),
ConstantRange::getFull(32));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, IllegalShAmt, OBO::NoSignedWrap),
ConstantRange::getFull(32));
EXPECT_EQ(
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1),
OBO::NoUnsignedWrap),
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1),
OBO::NoUnsignedWrap));
EXPECT_EQ(
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, ConstantRange(APInt(32, -32), APInt(32, 16) + 1),
OBO::NoSignedWrap),
ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl, ConstantRange(APInt(32, 0), APInt(32, 16) + 1),
OBO::NoSignedWrap));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl,
ConstantRange(APInt(32, -32), APInt(32, 16) + 1),
OBO::NoUnsignedWrap),
ConstantRange(APInt(32, 0), APInt(32, 65535) + 1));
EXPECT_EQ(ConstantRange::makeGuaranteedNoWrapRegion(
Instruction::Shl,
ConstantRange(APInt(32, -32), APInt(32, 16) + 1),
OBO::NoSignedWrap),
ConstantRange(APInt(32, -32768), APInt(32, 32767) + 1));
}
template<typename Fn>
void TestNoWrapRegionExhaustive(Instruction::BinaryOps BinOp,
unsigned NoWrapKind, Fn OverflowFn) {
unsigned Bits = 5;
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
if (CR.isEmptySet())
return;
if (Instruction::isShift(BinOp) && CR.getUnsignedMax().uge(Bits))
return;
ConstantRange NoWrap =
ConstantRange::makeGuaranteedNoWrapRegion(BinOp, CR, NoWrapKind);
ConstantRange Full = ConstantRange::getFull(Bits);
ForeachNumInConstantRange(Full, [&](const APInt &N1) {
bool NoOverflow = true;
bool Overflow = true;
ForeachNumInConstantRange(CR, [&](const APInt &N2) {
if (OverflowFn(N1, N2))
NoOverflow = false;
else
Overflow = false;
});
EXPECT_EQ(NoOverflow, NoWrap.contains(N1));
// The no-wrap range is exact for single-element ranges.
if (CR.isSingleElement()) {
EXPECT_EQ(Overflow, !NoWrap.contains(N1));
}
});
});
}
// Show that makeGuaranteedNoWrapRegion() is maximal, and for single-element
// ranges also exact.
TEST(ConstantRange, NoWrapRegionExhaustive) {
TestNoWrapRegionExhaustive(
Instruction::Add, OverflowingBinaryOperator::NoUnsignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.uadd_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(
Instruction::Add, OverflowingBinaryOperator::NoSignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.sadd_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(
Instruction::Sub, OverflowingBinaryOperator::NoUnsignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.usub_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(
Instruction::Sub, OverflowingBinaryOperator::NoSignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.ssub_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(
Instruction::Mul, OverflowingBinaryOperator::NoUnsignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.umul_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(
Instruction::Mul, OverflowingBinaryOperator::NoSignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.smul_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(Instruction::Shl,
OverflowingBinaryOperator::NoUnsignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void)N1.ushl_ov(N2, Overflow);
return Overflow;
});
TestNoWrapRegionExhaustive(Instruction::Shl,
OverflowingBinaryOperator::NoSignedWrap,
[](const APInt &N1, const APInt &N2) {
bool Overflow;
(void)N1.sshl_ov(N2, Overflow);
return Overflow;
});
}
TEST(ConstantRange, GetEquivalentICmp) {
APInt RHS;
CmpInst::Predicate Pred;
EXPECT_TRUE(ConstantRange(APInt::getMinValue(32), APInt(32, 100))
.getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_ULT);
EXPECT_EQ(RHS, APInt(32, 100));
EXPECT_TRUE(ConstantRange(APInt::getSignedMinValue(32), APInt(32, 100))
.getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_SLT);
EXPECT_EQ(RHS, APInt(32, 100));
EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getMinValue(32))
.getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_UGE);
EXPECT_EQ(RHS, APInt(32, 100));
EXPECT_TRUE(ConstantRange(APInt(32, 100), APInt::getSignedMinValue(32))
.getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_SGE);
EXPECT_EQ(RHS, APInt(32, 100));
EXPECT_TRUE(
ConstantRange(32, /*isFullSet=*/true).getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_UGE);
EXPECT_EQ(RHS, APInt(32, 0));
EXPECT_TRUE(
ConstantRange(32, /*isFullSet=*/false).getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_ULT);
EXPECT_EQ(RHS, APInt(32, 0));
EXPECT_FALSE(ConstantRange(APInt(32, 100), APInt(32, 200))
.getEquivalentICmp(Pred, RHS));
EXPECT_FALSE(ConstantRange(APInt::getSignedMinValue(32) - APInt(32, 100),
APInt::getSignedMinValue(32) + APInt(32, 100))
.getEquivalentICmp(Pred, RHS));
EXPECT_FALSE(ConstantRange(APInt::getMinValue(32) - APInt(32, 100),
APInt::getMinValue(32) + APInt(32, 100))
.getEquivalentICmp(Pred, RHS));
EXPECT_TRUE(ConstantRange(APInt(32, 100)).getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_EQ);
EXPECT_EQ(RHS, APInt(32, 100));
EXPECT_TRUE(
ConstantRange(APInt(32, 100)).inverse().getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_NE);
EXPECT_EQ(RHS, APInt(32, 100));
EXPECT_TRUE(
ConstantRange(APInt(512, 100)).inverse().getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_NE);
EXPECT_EQ(RHS, APInt(512, 100));
// NB! It would be correct for the following four calls to getEquivalentICmp
// to return ordered predicates like CmpInst::ICMP_ULT or CmpInst::ICMP_UGT.
// However, that's not the case today.
EXPECT_TRUE(ConstantRange(APInt(32, 0)).getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_EQ);
EXPECT_EQ(RHS, APInt(32, 0));
EXPECT_TRUE(
ConstantRange(APInt(32, 0)).inverse().getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_NE);
EXPECT_EQ(RHS, APInt(32, 0));
EXPECT_TRUE(ConstantRange(APInt(32, -1)).getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_EQ);
EXPECT_EQ(RHS, APInt(32, -1));
EXPECT_TRUE(
ConstantRange(APInt(32, -1)).inverse().getEquivalentICmp(Pred, RHS));
EXPECT_EQ(Pred, CmpInst::ICMP_NE);
EXPECT_EQ(RHS, APInt(32, -1));
}
#define EXPECT_MAY_OVERFLOW(op) \
EXPECT_EQ(ConstantRange::OverflowResult::MayOverflow, (op))
#define EXPECT_ALWAYS_OVERFLOWS_LOW(op) \
EXPECT_EQ(ConstantRange::OverflowResult::AlwaysOverflowsLow, (op))
#define EXPECT_ALWAYS_OVERFLOWS_HIGH(op) \
EXPECT_EQ(ConstantRange::OverflowResult::AlwaysOverflowsHigh, (op))
#define EXPECT_NEVER_OVERFLOWS(op) \
EXPECT_EQ(ConstantRange::OverflowResult::NeverOverflows, (op))
TEST_F(ConstantRangeTest, UnsignedAddOverflow) {
// Ill-defined - may overflow is a conservative result.
EXPECT_MAY_OVERFLOW(Some.unsignedAddMayOverflow(Empty));
EXPECT_MAY_OVERFLOW(Empty.unsignedAddMayOverflow(Some));
// Never overflow despite one full/wrap set.
ConstantRange Zero(APInt::getNullValue(16));
EXPECT_NEVER_OVERFLOWS(Full.unsignedAddMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Wrap.unsignedAddMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Zero.unsignedAddMayOverflow(Full));
EXPECT_NEVER_OVERFLOWS(Zero.unsignedAddMayOverflow(Wrap));
// But usually full/wrap always may overflow.
EXPECT_MAY_OVERFLOW(Full.unsignedAddMayOverflow(One));
EXPECT_MAY_OVERFLOW(Wrap.unsignedAddMayOverflow(One));
EXPECT_MAY_OVERFLOW(One.unsignedAddMayOverflow(Full));
EXPECT_MAY_OVERFLOW(One.unsignedAddMayOverflow(Wrap));
ConstantRange A(APInt(16, 0xfd00), APInt(16, 0xfe00));
ConstantRange B1(APInt(16, 0x0100), APInt(16, 0x0201));
ConstantRange B2(APInt(16, 0x0100), APInt(16, 0x0202));
EXPECT_NEVER_OVERFLOWS(A.unsignedAddMayOverflow(B1));
EXPECT_MAY_OVERFLOW(A.unsignedAddMayOverflow(B2));
EXPECT_NEVER_OVERFLOWS(B1.unsignedAddMayOverflow(A));
EXPECT_MAY_OVERFLOW(B2.unsignedAddMayOverflow(A));
ConstantRange C1(APInt(16, 0x0299), APInt(16, 0x0400));
ConstantRange C2(APInt(16, 0x0300), APInt(16, 0x0400));
EXPECT_MAY_OVERFLOW(A.unsignedAddMayOverflow(C1));
EXPECT_ALWAYS_OVERFLOWS_HIGH(A.unsignedAddMayOverflow(C2));
EXPECT_MAY_OVERFLOW(C1.unsignedAddMayOverflow(A));
EXPECT_ALWAYS_OVERFLOWS_HIGH(C2.unsignedAddMayOverflow(A));
}
TEST_F(ConstantRangeTest, UnsignedSubOverflow) {
// Ill-defined - may overflow is a conservative result.
EXPECT_MAY_OVERFLOW(Some.unsignedSubMayOverflow(Empty));
EXPECT_MAY_OVERFLOW(Empty.unsignedSubMayOverflow(Some));
// Never overflow despite one full/wrap set.
ConstantRange Zero(APInt::getNullValue(16));
ConstantRange Max(APInt::getAllOnesValue(16));
EXPECT_NEVER_OVERFLOWS(Full.unsignedSubMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Wrap.unsignedSubMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Max.unsignedSubMayOverflow(Full));
EXPECT_NEVER_OVERFLOWS(Max.unsignedSubMayOverflow(Wrap));
// But usually full/wrap always may overflow.
EXPECT_MAY_OVERFLOW(Full.unsignedSubMayOverflow(One));
EXPECT_MAY_OVERFLOW(Wrap.unsignedSubMayOverflow(One));
EXPECT_MAY_OVERFLOW(One.unsignedSubMayOverflow(Full));
EXPECT_MAY_OVERFLOW(One.unsignedSubMayOverflow(Wrap));
ConstantRange A(APInt(16, 0x0000), APInt(16, 0x0100));
ConstantRange B(APInt(16, 0x0100), APInt(16, 0x0200));
EXPECT_NEVER_OVERFLOWS(B.unsignedSubMayOverflow(A));
EXPECT_ALWAYS_OVERFLOWS_LOW(A.unsignedSubMayOverflow(B));
ConstantRange A1(APInt(16, 0x0000), APInt(16, 0x0101));
ConstantRange B1(APInt(16, 0x0100), APInt(16, 0x0201));
EXPECT_NEVER_OVERFLOWS(B1.unsignedSubMayOverflow(A1));
EXPECT_MAY_OVERFLOW(A1.unsignedSubMayOverflow(B1));
ConstantRange A2(APInt(16, 0x0000), APInt(16, 0x0102));
ConstantRange B2(APInt(16, 0x0100), APInt(16, 0x0202));
EXPECT_MAY_OVERFLOW(B2.unsignedSubMayOverflow(A2));
EXPECT_MAY_OVERFLOW(A2.unsignedSubMayOverflow(B2));
}
TEST_F(ConstantRangeTest, SignedAddOverflow) {
// Ill-defined - may overflow is a conservative result.
EXPECT_MAY_OVERFLOW(Some.signedAddMayOverflow(Empty));
EXPECT_MAY_OVERFLOW(Empty.signedAddMayOverflow(Some));
// Never overflow despite one full/wrap set.
ConstantRange Zero(APInt::getNullValue(16));
EXPECT_NEVER_OVERFLOWS(Full.signedAddMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Wrap.signedAddMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Zero.signedAddMayOverflow(Full));
EXPECT_NEVER_OVERFLOWS(Zero.signedAddMayOverflow(Wrap));
// But usually full/wrap always may overflow.
EXPECT_MAY_OVERFLOW(Full.signedAddMayOverflow(One));
EXPECT_MAY_OVERFLOW(Wrap.signedAddMayOverflow(One));
EXPECT_MAY_OVERFLOW(One.signedAddMayOverflow(Full));
EXPECT_MAY_OVERFLOW(One.signedAddMayOverflow(Wrap));
ConstantRange A(APInt(16, 0x7d00), APInt(16, 0x7e00));
ConstantRange B1(APInt(16, 0x0100), APInt(16, 0x0201));
ConstantRange B2(APInt(16, 0x0100), APInt(16, 0x0202));
EXPECT_NEVER_OVERFLOWS(A.signedAddMayOverflow(B1));
EXPECT_MAY_OVERFLOW(A.signedAddMayOverflow(B2));
ConstantRange B3(APInt(16, 0x8000), APInt(16, 0x0201));
ConstantRange B4(APInt(16, 0x8000), APInt(16, 0x0202));
EXPECT_NEVER_OVERFLOWS(A.signedAddMayOverflow(B3));
EXPECT_MAY_OVERFLOW(A.signedAddMayOverflow(B4));
ConstantRange B5(APInt(16, 0x0299), APInt(16, 0x0400));
ConstantRange B6(APInt(16, 0x0300), APInt(16, 0x0400));
EXPECT_MAY_OVERFLOW(A.signedAddMayOverflow(B5));
EXPECT_ALWAYS_OVERFLOWS_HIGH(A.signedAddMayOverflow(B6));
ConstantRange C(APInt(16, 0x8200), APInt(16, 0x8300));
ConstantRange D1(APInt(16, 0xfe00), APInt(16, 0xff00));
ConstantRange D2(APInt(16, 0xfd99), APInt(16, 0xff00));
EXPECT_NEVER_OVERFLOWS(C.signedAddMayOverflow(D1));
EXPECT_MAY_OVERFLOW(C.signedAddMayOverflow(D2));
ConstantRange D3(APInt(16, 0xfe00), APInt(16, 0x8000));
ConstantRange D4(APInt(16, 0xfd99), APInt(16, 0x8000));
EXPECT_NEVER_OVERFLOWS(C.signedAddMayOverflow(D3));
EXPECT_MAY_OVERFLOW(C.signedAddMayOverflow(D4));
ConstantRange D5(APInt(16, 0xfc00), APInt(16, 0xfd02));
ConstantRange D6(APInt(16, 0xfc00), APInt(16, 0xfd01));
EXPECT_MAY_OVERFLOW(C.signedAddMayOverflow(D5));
EXPECT_ALWAYS_OVERFLOWS_LOW(C.signedAddMayOverflow(D6));
ConstantRange E(APInt(16, 0xff00), APInt(16, 0x0100));
EXPECT_NEVER_OVERFLOWS(E.signedAddMayOverflow(E));
ConstantRange F(APInt(16, 0xf000), APInt(16, 0x7000));
EXPECT_MAY_OVERFLOW(F.signedAddMayOverflow(F));
}
TEST_F(ConstantRangeTest, SignedSubOverflow) {
// Ill-defined - may overflow is a conservative result.
EXPECT_MAY_OVERFLOW(Some.signedSubMayOverflow(Empty));
EXPECT_MAY_OVERFLOW(Empty.signedSubMayOverflow(Some));
// Never overflow despite one full/wrap set.
ConstantRange Zero(APInt::getNullValue(16));
EXPECT_NEVER_OVERFLOWS(Full.signedSubMayOverflow(Zero));
EXPECT_NEVER_OVERFLOWS(Wrap.signedSubMayOverflow(Zero));
// But usually full/wrap always may overflow.
EXPECT_MAY_OVERFLOW(Full.signedSubMayOverflow(One));
EXPECT_MAY_OVERFLOW(Wrap.signedSubMayOverflow(One));
EXPECT_MAY_OVERFLOW(One.signedSubMayOverflow(Full));
EXPECT_MAY_OVERFLOW(One.signedSubMayOverflow(Wrap));
ConstantRange A(APInt(16, 0x7d00), APInt(16, 0x7e00));
ConstantRange B1(APInt(16, 0xfe00), APInt(16, 0xff00));
ConstantRange B2(APInt(16, 0xfd99), APInt(16, 0xff00));
EXPECT_NEVER_OVERFLOWS(A.signedSubMayOverflow(B1));
EXPECT_MAY_OVERFLOW(A.signedSubMayOverflow(B2));
ConstantRange B3(APInt(16, 0xfc00), APInt(16, 0xfd02));
ConstantRange B4(APInt(16, 0xfc00), APInt(16, 0xfd01));
EXPECT_MAY_OVERFLOW(A.signedSubMayOverflow(B3));
EXPECT_ALWAYS_OVERFLOWS_HIGH(A.signedSubMayOverflow(B4));
ConstantRange C(APInt(16, 0x8200), APInt(16, 0x8300));
ConstantRange D1(APInt(16, 0x0100), APInt(16, 0x0201));
ConstantRange D2(APInt(16, 0x0100), APInt(16, 0x0202));
EXPECT_NEVER_OVERFLOWS(C.signedSubMayOverflow(D1));
EXPECT_MAY_OVERFLOW(C.signedSubMayOverflow(D2));
ConstantRange D3(APInt(16, 0x0299), APInt(16, 0x0400));
ConstantRange D4(APInt(16, 0x0300), APInt(16, 0x0400));
EXPECT_MAY_OVERFLOW(C.signedSubMayOverflow(D3));
EXPECT_ALWAYS_OVERFLOWS_LOW(C.signedSubMayOverflow(D4));
ConstantRange E(APInt(16, 0xff00), APInt(16, 0x0100));
EXPECT_NEVER_OVERFLOWS(E.signedSubMayOverflow(E));
ConstantRange F(APInt(16, 0xf000), APInt(16, 0x7001));
EXPECT_MAY_OVERFLOW(F.signedSubMayOverflow(F));
}
template<typename Fn1, typename Fn2>
static void TestOverflowExhaustive(Fn1 OverflowFn, Fn2 MayOverflowFn) {
// Constant range overflow checks are tested exhaustively on 4-bit numbers.
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits, [=](const ConstantRange &CR1,
const ConstantRange &CR2) {
// Loop over all N1 in CR1 and N2 in CR2 and check whether any of the
// operations have overflow / have no overflow.
bool RangeHasOverflowLow = false;
bool RangeHasOverflowHigh = false;
bool RangeHasNoOverflow = false;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
bool IsOverflowHigh;
if (!OverflowFn(IsOverflowHigh, N1, N2)) {
RangeHasNoOverflow = true;
return;
}
if (IsOverflowHigh)
RangeHasOverflowHigh = true;
else
RangeHasOverflowLow = true;
});
});
ConstantRange::OverflowResult OR = MayOverflowFn(CR1, CR2);
switch (OR) {
case ConstantRange::OverflowResult::AlwaysOverflowsLow:
EXPECT_TRUE(RangeHasOverflowLow);
EXPECT_FALSE(RangeHasOverflowHigh);
EXPECT_FALSE(RangeHasNoOverflow);
break;
case ConstantRange::OverflowResult::AlwaysOverflowsHigh:
EXPECT_TRUE(RangeHasOverflowHigh);
EXPECT_FALSE(RangeHasOverflowLow);
EXPECT_FALSE(RangeHasNoOverflow);
break;
case ConstantRange::OverflowResult::NeverOverflows:
EXPECT_FALSE(RangeHasOverflowLow);
EXPECT_FALSE(RangeHasOverflowHigh);
EXPECT_TRUE(RangeHasNoOverflow);
break;
case ConstantRange::OverflowResult::MayOverflow:
// We return MayOverflow for empty sets as a conservative result,
// but of course neither the RangeHasOverflow nor the
// RangeHasNoOverflow flags will be set.
if (CR1.isEmptySet() || CR2.isEmptySet())
break;
EXPECT_TRUE(RangeHasOverflowLow || RangeHasOverflowHigh);
EXPECT_TRUE(RangeHasNoOverflow);
break;
}
});
}
TEST_F(ConstantRangeTest, UnsignedAddOverflowExhaustive) {
TestOverflowExhaustive(
[](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.uadd_ov(N2, Overflow);
IsOverflowHigh = true;
return Overflow;
},
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.unsignedAddMayOverflow(CR2);
});
}
TEST_F(ConstantRangeTest, UnsignedSubOverflowExhaustive) {
TestOverflowExhaustive(
[](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.usub_ov(N2, Overflow);
IsOverflowHigh = false;
return Overflow;
},
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.unsignedSubMayOverflow(CR2);
});
}
TEST_F(ConstantRangeTest, UnsignedMulOverflowExhaustive) {
TestOverflowExhaustive(
[](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.umul_ov(N2, Overflow);
IsOverflowHigh = true;
return Overflow;
},
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.unsignedMulMayOverflow(CR2);
});
}
TEST_F(ConstantRangeTest, SignedAddOverflowExhaustive) {
TestOverflowExhaustive(
[](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.sadd_ov(N2, Overflow);
IsOverflowHigh = N1.isNonNegative();
return Overflow;
},
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.signedAddMayOverflow(CR2);
});
}
TEST_F(ConstantRangeTest, SignedSubOverflowExhaustive) {
TestOverflowExhaustive(
[](bool &IsOverflowHigh, const APInt &N1, const APInt &N2) {
bool Overflow;
(void) N1.ssub_ov(N2, Overflow);
IsOverflowHigh = N1.isNonNegative();
return Overflow;
},
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.signedSubMayOverflow(CR2);
});
}
TEST_F(ConstantRangeTest, FromKnownBits) {
KnownBits Unknown(16);
EXPECT_EQ(Full, ConstantRange::fromKnownBits(Unknown, /*signed*/false));
EXPECT_EQ(Full, ConstantRange::fromKnownBits(Unknown, /*signed*/true));
// .10..01. -> unsigned 01000010 (66) to 11011011 (219)
// -> signed 11000010 (194) to 01011011 (91)
KnownBits Known(8);
Known.Zero = 36;
Known.One = 66;
ConstantRange Unsigned(APInt(8, 66), APInt(8, 219 + 1));
ConstantRange Signed(APInt(8, 194), APInt(8, 91 + 1));
EXPECT_EQ(Unsigned, ConstantRange::fromKnownBits(Known, /*signed*/false));
EXPECT_EQ(Signed, ConstantRange::fromKnownBits(Known, /*signed*/true));
// 1.10.10. -> 10100100 (164) to 11101101 (237)
Known.Zero = 18;
Known.One = 164;
ConstantRange CR1(APInt(8, 164), APInt(8, 237 + 1));
EXPECT_EQ(CR1, ConstantRange::fromKnownBits(Known, /*signed*/false));
EXPECT_EQ(CR1, ConstantRange::fromKnownBits(Known, /*signed*/true));
// 01.0.1.0 -> 01000100 (68) to 01101110 (110)
Known.Zero = 145;
Known.One = 68;
ConstantRange CR2(APInt(8, 68), APInt(8, 110 + 1));
EXPECT_EQ(CR2, ConstantRange::fromKnownBits(Known, /*signed*/false));
EXPECT_EQ(CR2, ConstantRange::fromKnownBits(Known, /*signed*/true));
}
TEST_F(ConstantRangeTest, FromKnownBitsExhaustive) {
unsigned Bits = 4;
unsigned Max = 1 << Bits;
KnownBits Known(Bits);
for (unsigned Zero = 0; Zero < Max; ++Zero) {
for (unsigned One = 0; One < Max; ++One) {
Known.Zero = Zero;
Known.One = One;
if (Known.hasConflict() || Known.isUnknown())
continue;
UnsignedOpRangeGatherer UR(Bits);
SignedOpRangeGatherer SR(Bits);
for (unsigned N = 0; N < Max; ++N) {
APInt Num(Bits, N);
if ((Num & Known.Zero) != 0 || (~Num & Known.One) != 0)
continue;
UR.account(Num);
SR.account(Num);
}
ConstantRange UnsignedCR = UR.getRange();
ConstantRange SignedCR = SR.getRange();
EXPECT_EQ(UnsignedCR, ConstantRange::fromKnownBits(Known, false));
EXPECT_EQ(SignedCR, ConstantRange::fromKnownBits(Known, true));
}
}
}
TEST_F(ConstantRangeTest, Negative) {
// All elements in an empty set (of which there are none) are both negative
// and non-negative. Empty & full sets checked explicitly for clarity, but
// they are also covered by the exhaustive test below.
EXPECT_TRUE(Empty.isAllNegative());
EXPECT_TRUE(Empty.isAllNonNegative());
EXPECT_FALSE(Full.isAllNegative());
EXPECT_FALSE(Full.isAllNonNegative());
unsigned Bits = 4;
EnumerateConstantRanges(Bits, [](const ConstantRange &CR) {
bool AllNegative = true;
bool AllNonNegative = true;
ForeachNumInConstantRange(CR, [&](const APInt &N) {
if (!N.isNegative())
AllNegative = false;
if (!N.isNonNegative())
AllNonNegative = false;
});
assert((CR.isEmptySet() || !AllNegative || !AllNonNegative) &&
"Only empty set can be both all negative and all non-negative");
EXPECT_EQ(AllNegative, CR.isAllNegative());
EXPECT_EQ(AllNonNegative, CR.isAllNonNegative());
});
}
TEST_F(ConstantRangeTest, UAddSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.uadd_sat(CR2);
},
[](const APInt &N1, const APInt &N2) {
return N1.uadd_sat(N2);
[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
2020-09-23 20:43:59 +02:00
},
PreferSmallestUnsigned);
}
TEST_F(ConstantRangeTest, USubSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.usub_sat(CR2);
},
[](const APInt &N1, const APInt &N2) {
return N1.usub_sat(N2);
[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
2020-09-23 20:43:59 +02:00
},
PreferSmallestUnsigned);
}
TEST_F(ConstantRangeTest, UMulSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.umul_sat(CR2);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N1, const APInt &N2) { return N1.umul_sat(N2); },
PreferSmallestUnsigned);
}
TEST_F(ConstantRangeTest, UShlSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.ushl_sat(CR2);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N1, const APInt &N2) { return N1.ushl_sat(N2); },
PreferSmallestUnsigned);
}
TEST_F(ConstantRangeTest, SAddSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.sadd_sat(CR2);
},
[](const APInt &N1, const APInt &N2) {
return N1.sadd_sat(N2);
[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
2020-09-23 20:43:59 +02:00
},
PreferSmallestSigned);
}
TEST_F(ConstantRangeTest, SSubSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.ssub_sat(CR2);
},
[](const APInt &N1, const APInt &N2) {
return N1.ssub_sat(N2);
[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
2020-09-23 20:43:59 +02:00
},
PreferSmallestSigned);
}
TEST_F(ConstantRangeTest, SMulSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.smul_sat(CR2);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N1, const APInt &N2) { return N1.smul_sat(N2); },
PreferSmallestSigned);
}
TEST_F(ConstantRangeTest, SShlSat) {
[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
2020-09-23 20:43:59 +02:00
TestBinaryOpExhaustive(
[](const ConstantRange &CR1, const ConstantRange &CR2) {
return CR1.sshl_sat(CR2);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N1, const APInt &N2) { return N1.sshl_sat(N2); },
PreferSmallestSigned);
}
TEST_F(ConstantRangeTest, Abs) {
[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
2020-09-23 20:43:59 +02:00
TestUnaryOpExhaustive(
[](const ConstantRange &CR) { return CR.abs(); },
[](const APInt &N) { return N.abs(); });
[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
2020-09-23 20:43:59 +02:00
TestUnaryOpExhaustive(
[](const ConstantRange &CR) { return CR.abs(/*IntMinIsPoison=*/true); },
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N) -> Optional<APInt> {
if (N.isMinSignedValue())
return None;
return N.abs();
});
}
TEST_F(ConstantRangeTest, castOps) {
ConstantRange A(APInt(16, 66), APInt(16, 128));
ConstantRange FpToI8 = A.castOp(Instruction::FPToSI, 8);
EXPECT_EQ(8u, FpToI8.getBitWidth());
EXPECT_TRUE(FpToI8.isFullSet());
ConstantRange FpToI16 = A.castOp(Instruction::FPToSI, 16);
EXPECT_EQ(16u, FpToI16.getBitWidth());
EXPECT_EQ(A, FpToI16);
ConstantRange FPExtToDouble = A.castOp(Instruction::FPExt, 64);
EXPECT_EQ(64u, FPExtToDouble.getBitWidth());
EXPECT_TRUE(FPExtToDouble.isFullSet());
ConstantRange PtrToInt = A.castOp(Instruction::PtrToInt, 64);
EXPECT_EQ(64u, PtrToInt.getBitWidth());
EXPECT_TRUE(PtrToInt.isFullSet());
ConstantRange IntToPtr = A.castOp(Instruction::IntToPtr, 64);
EXPECT_EQ(64u, IntToPtr.getBitWidth());
EXPECT_TRUE(IntToPtr.isFullSet());
}
TEST_F(ConstantRangeTest, binaryXor) {
// Single element ranges.
ConstantRange R16(APInt(8, 16));
ConstantRange R20(APInt(8, 20));
EXPECT_EQ(*R16.binaryXor(R16).getSingleElement(), APInt(8, 0));
EXPECT_EQ(*R16.binaryXor(R20).getSingleElement(), APInt(8, 16 ^ 20));
// Ranges with more than a single element. Handled conservatively for now.
ConstantRange R16_35(APInt(8, 16), APInt(8, 35));
ConstantRange R0_99(APInt(8, 0), APInt(8, 99));
EXPECT_TRUE(R16_35.binaryXor(R16_35).isFullSet());
EXPECT_TRUE(R16_35.binaryXor(R0_99).isFullSet());
EXPECT_TRUE(R0_99.binaryXor(R16_35).isFullSet());
}
TEST_F(ConstantRangeTest, binaryNot) {
[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
2020-09-23 20:43:59 +02:00
TestUnaryOpExhaustive(
[](const ConstantRange &CR) { return CR.binaryNot(); },
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N) { return ~N; },
PreferSmallest);
[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
2020-09-23 20:43:59 +02:00
TestUnaryOpExhaustive(
[](const ConstantRange &CR) {
return CR.binaryXor(
ConstantRange(APInt::getAllOnesValue(CR.getBitWidth())));
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N) { return ~N; },
PreferSmallest);
[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
2020-09-23 20:43:59 +02:00
TestUnaryOpExhaustive(
[](const ConstantRange &CR) {
return ConstantRange(APInt::getAllOnesValue(CR.getBitWidth()))
.binaryXor(CR);
},
[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
2020-09-23 20:43:59 +02:00
[](const APInt &N) { return ~N; },
PreferSmallest);
}
} // anonymous namespace