mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
9bdd9a8130
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
2401 lines
93 KiB
C++
2401 lines
93 KiB
C++
//===- 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/ADT/BitVector.h"
|
|
#include "llvm/ADT/SmallBitVector.h"
|
|
#include "llvm/IR/ConstantRange.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());
|
|
}
|
|
}
|
|
|
|
using PreferFn = llvm::function_ref<bool(const ConstantRange &,
|
|
const ConstantRange &)>;
|
|
|
|
bool PreferSmallest(const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.isSizeStrictlySmallerThan(CR2);
|
|
}
|
|
|
|
bool PreferSmallestUnsigned(const ConstantRange &CR1,
|
|
const ConstantRange &CR2) {
|
|
if (CR1.isWrappedSet() != CR2.isWrappedSet())
|
|
return CR1.isWrappedSet() < CR2.isWrappedSet();
|
|
return PreferSmallest(CR1, CR2);
|
|
}
|
|
|
|
bool PreferSmallestSigned(const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
if (CR1.isSignWrappedSet() != CR2.isSignWrappedSet())
|
|
return CR1.isSignWrappedSet() < CR2.isSignWrappedSet();
|
|
return PreferSmallest(CR1, CR2);
|
|
}
|
|
|
|
bool PreferSmallestNonFullUnsigned(const ConstantRange &CR1,
|
|
const ConstantRange &CR2) {
|
|
if (CR1.isFullSet() != CR2.isFullSet())
|
|
return CR1.isFullSet() < CR2.isFullSet();
|
|
return PreferSmallestUnsigned(CR1, CR2);
|
|
}
|
|
|
|
bool PreferSmallestNonFullSigned(const ConstantRange &CR1,
|
|
const ConstantRange &CR2) {
|
|
if (CR1.isFullSet() != CR2.isFullSet())
|
|
return CR1.isFullSet() < CR2.isFullSet();
|
|
return PreferSmallestSigned(CR1, CR2);
|
|
}
|
|
|
|
// Check whether constant range CR is an optimal approximation of the set
|
|
// Elems under the given PreferenceFn. The preference function should return
|
|
// true if the first range argument is strictly preferred to the second one.
|
|
static void TestRange(const ConstantRange &CR, const SmallBitVector &Elems,
|
|
PreferFn PreferenceFn) {
|
|
unsigned BitWidth = CR.getBitWidth();
|
|
|
|
// Check conservative correctness.
|
|
for (unsigned Elem : Elems.set_bits()) {
|
|
EXPECT_TRUE(CR.contains(APInt(BitWidth, Elem)));
|
|
}
|
|
|
|
// Make sure we have at least two elements for the code below.
|
|
if (Elems.none()) {
|
|
EXPECT_TRUE(CR.isEmptySet());
|
|
return;
|
|
}
|
|
|
|
if (Elems.count() == 1) {
|
|
EXPECT_TRUE(CR.isSingleElement());
|
|
return;
|
|
}
|
|
|
|
// Look at all pairs of adjacent elements and the slack-free ranges
|
|
// [Elem, PrevElem] they imply. Check that none of the ranges are strictly
|
|
// preferred over the computed range (they may have equal preference).
|
|
int FirstElem = Elems.find_first();
|
|
int PrevElem = FirstElem, Elem;
|
|
do {
|
|
Elem = Elems.find_next(PrevElem);
|
|
if (Elem < 0)
|
|
Elem = FirstElem; // Wrap around to first element.
|
|
|
|
ConstantRange PossibleCR =
|
|
ConstantRange::getNonEmpty(APInt(BitWidth, Elem),
|
|
APInt(BitWidth, PrevElem) + 1);
|
|
// There should be no range that is preferred over CR.
|
|
EXPECT_FALSE(PreferenceFn(PossibleCR, CR))
|
|
<< "CR = " << CR << ", BetterCR = " << PossibleCR;
|
|
|
|
PrevElem = Elem;
|
|
} while (Elem != FirstElem);
|
|
}
|
|
|
|
using UnaryRangeFn = llvm::function_ref<ConstantRange(const ConstantRange &)>;
|
|
using UnaryIntFn = llvm::function_ref<Optional<APInt>(const APInt &)>;
|
|
|
|
static void TestUnaryOpExhaustive(UnaryRangeFn RangeFn, UnaryIntFn IntFn,
|
|
PreferFn PreferenceFn = PreferSmallest) {
|
|
unsigned Bits = 4;
|
|
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
|
|
SmallBitVector Elems(1 << Bits);
|
|
ForeachNumInConstantRange(CR, [&](const APInt &N) {
|
|
if (Optional<APInt> ResultN = IntFn(N))
|
|
Elems.set(ResultN->getZExtValue());
|
|
});
|
|
TestRange(RangeFn(CR), Elems, PreferenceFn);
|
|
});
|
|
}
|
|
|
|
using BinaryRangeFn = llvm::function_ref<ConstantRange(const ConstantRange &,
|
|
const ConstantRange &)>;
|
|
using BinaryIntFn = llvm::function_ref<Optional<APInt>(const APInt &,
|
|
const APInt &)>;
|
|
|
|
static void TestBinaryOpExhaustive(BinaryRangeFn RangeFn, BinaryIntFn IntFn,
|
|
PreferFn PreferenceFn = PreferSmallest) {
|
|
unsigned Bits = 4;
|
|
EnumerateTwoConstantRanges(
|
|
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
SmallBitVector Elems(1 << Bits);
|
|
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
|
|
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
|
|
if (Optional<APInt> ResultN = IntFn(N1, N2))
|
|
Elems.set(ResultN->getZExtValue());
|
|
});
|
|
});
|
|
TestRange(RangeFn(CR1, CR2), Elems, PreferenceFn);
|
|
});
|
|
}
|
|
|
|
static void TestBinaryOpExhaustiveCorrectnessOnly(BinaryRangeFn RangeFn,
|
|
BinaryIntFn IntFn) {
|
|
unsigned Bits = 4;
|
|
EnumerateTwoConstantRanges(
|
|
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
ConstantRange ResultCR = RangeFn(CR1, CR2);
|
|
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
|
|
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
|
|
if (Optional<APInt> ResultN = IntFn(N1, N2)) {
|
|
EXPECT_TRUE(ResultCR.contains(*ResultN));
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
struct OpRangeGathererBase {
|
|
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) {
|
|
SmallBitVector Elems(1 << Bits);
|
|
APInt Num(Bits, 0);
|
|
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);
|
|
|
|
ConstantRange UnsignedCR = OpFn(CR1, CR2, ConstantRange::Unsigned);
|
|
TestRange(UnsignedCR, Elems, PreferSmallestNonFullUnsigned);
|
|
|
|
ConstantRange SignedCR = OpFn(CR1, CR2, ConstantRange::Signed);
|
|
TestRange(SignedCR, Elems, PreferSmallestNonFullSigned);
|
|
});
|
|
}
|
|
|
|
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)));
|
|
|
|
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)));
|
|
|
|
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);
|
|
// TODO: ConstantRange is currently over-conservative here.
|
|
EXPECT_EQ(Wrap.umax(Wrap), Full);
|
|
EXPECT_EQ(Wrap.umax(One), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
|
|
EXPECT_EQ(One.umax(One), One);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
// TODO: ConstantRange is currently over-conservative here.
|
|
EXPECT_EQ(Wrap.umin(Wrap), Full);
|
|
EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
|
|
EXPECT_EQ(One.umin(One), One);
|
|
}
|
|
|
|
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);
|
|
// TODO: ConstantRange is currently over-conservative here.
|
|
EXPECT_EQ(Wrap.smin(Wrap), Full);
|
|
EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
|
|
APInt(16, 0xb)));
|
|
EXPECT_EQ(One.smin(One), One);
|
|
}
|
|
|
|
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)));
|
|
|
|
TestBinaryOpExhaustiveCorrectnessOnly(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.urem(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) -> Optional<APInt> {
|
|
if (N2.isNullValue())
|
|
return None;
|
|
return N1.urem(N2);
|
|
});
|
|
}
|
|
|
|
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)));
|
|
|
|
TestBinaryOpExhaustiveCorrectnessOnly(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.srem(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) -> Optional<APInt> {
|
|
if (N2.isNullValue())
|
|
return None;
|
|
return N1.srem(N2);
|
|
});
|
|
}
|
|
|
|
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)));
|
|
}
|
|
|
|
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) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.uadd_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) {
|
|
return N1.uadd_sat(N2);
|
|
},
|
|
PreferSmallestUnsigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, USubSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.usub_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) {
|
|
return N1.usub_sat(N2);
|
|
},
|
|
PreferSmallestUnsigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, UMulSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.umul_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) { return N1.umul_sat(N2); },
|
|
PreferSmallestUnsigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, UShlSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.ushl_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) { return N1.ushl_sat(N2); },
|
|
PreferSmallestUnsigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, SAddSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.sadd_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) {
|
|
return N1.sadd_sat(N2);
|
|
},
|
|
PreferSmallestSigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, SSubSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.ssub_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) {
|
|
return N1.ssub_sat(N2);
|
|
},
|
|
PreferSmallestSigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, SMulSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.smul_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) { return N1.smul_sat(N2); },
|
|
PreferSmallestSigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, SShlSat) {
|
|
TestBinaryOpExhaustive(
|
|
[](const ConstantRange &CR1, const ConstantRange &CR2) {
|
|
return CR1.sshl_sat(CR2);
|
|
},
|
|
[](const APInt &N1, const APInt &N2) { return N1.sshl_sat(N2); },
|
|
PreferSmallestSigned);
|
|
}
|
|
|
|
TEST_F(ConstantRangeTest, Abs) {
|
|
TestUnaryOpExhaustive(
|
|
[](const ConstantRange &CR) { return CR.abs(); },
|
|
[](const APInt &N) { return N.abs(); });
|
|
|
|
TestUnaryOpExhaustive(
|
|
[](const ConstantRange &CR) { return CR.abs(/*IntMinIsPoison=*/true); },
|
|
[](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) {
|
|
// TODO: Improve binaryNot() implementation to remove the need for
|
|
// PreferSmallestUnsigned.
|
|
TestUnaryOpExhaustive(
|
|
[](const ConstantRange &CR) { return CR.binaryNot(); },
|
|
[](const APInt &N) { return ~N; },
|
|
PreferSmallestUnsigned);
|
|
TestUnaryOpExhaustive(
|
|
[](const ConstantRange &CR) {
|
|
return CR.binaryXor(
|
|
ConstantRange(APInt::getAllOnesValue(CR.getBitWidth())));
|
|
},
|
|
[](const APInt &N) { return ~N; },
|
|
PreferSmallestUnsigned);
|
|
TestUnaryOpExhaustive(
|
|
[](const ConstantRange &CR) {
|
|
return ConstantRange(APInt::getAllOnesValue(CR.getBitWidth()))
|
|
.binaryXor(CR);
|
|
},
|
|
[](const APInt &N) { return ~N; },
|
|
PreferSmallestUnsigned);
|
|
}
|
|
|
|
} // anonymous namespace
|