llvm-project/llvm/unittests/IR/ConstantFPRangeTest.cpp
Yingwei Zheng a3a253d3c7
[ConstantFPRange] Implement ConstantFPRange::makeExactFCmpRegion (#111490)
Note: The current implementation doesn't return optimal result for `fcmp
one/une x, +/-inf` since we don't handle this case in
https://github.com/llvm/llvm-project/pull/110891. Maybe we can make it
optimal after seeing some real-world cases.
2024-10-08 15:55:10 +08:00

771 lines
30 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/IR/ConstantFPRange.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Operator.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
class ConstantFPRangeTest : public ::testing::Test {
protected:
static const fltSemantics &Sem;
static ConstantFPRange Full;
static ConstantFPRange Empty;
static ConstantFPRange Finite;
static ConstantFPRange One;
static ConstantFPRange PosZero;
static ConstantFPRange NegZero;
static ConstantFPRange Zero;
static ConstantFPRange PosInf;
static ConstantFPRange NegInf;
static ConstantFPRange Denormal;
static ConstantFPRange NaN;
static ConstantFPRange SNaN;
static ConstantFPRange QNaN;
static ConstantFPRange Some;
static ConstantFPRange SomePos;
static ConstantFPRange SomeNeg;
};
const fltSemantics &ConstantFPRangeTest::Sem = APFloat::IEEEdouble();
ConstantFPRange ConstantFPRangeTest::Full =
ConstantFPRange::getFull(APFloat::IEEEdouble());
ConstantFPRange ConstantFPRangeTest::Empty =
ConstantFPRange::getEmpty(APFloat::IEEEdouble());
ConstantFPRange ConstantFPRangeTest::Finite =
ConstantFPRange::getFinite(APFloat::IEEEdouble());
ConstantFPRange ConstantFPRangeTest::One = ConstantFPRange(APFloat(1.0));
ConstantFPRange ConstantFPRangeTest::PosZero = ConstantFPRange(
APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/false));
ConstantFPRange ConstantFPRangeTest::NegZero =
ConstantFPRange(APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true));
ConstantFPRange ConstantFPRangeTest::Zero = ConstantFPRange::getNonNaN(
APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true),
APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/false));
ConstantFPRange ConstantFPRangeTest::Denormal =
ConstantFPRange(APFloat::getSmallest(APFloat::IEEEdouble()));
ConstantFPRange ConstantFPRangeTest::PosInf =
ConstantFPRange(APFloat::getInf(APFloat::IEEEdouble(), /*Negative=*/false));
ConstantFPRange ConstantFPRangeTest::NegInf =
ConstantFPRange(APFloat::getInf(APFloat::IEEEdouble(), /*Negative=*/true));
ConstantFPRange ConstantFPRangeTest::NaN = ConstantFPRange::getNaNOnly(
APFloat::IEEEdouble(), /*MayBeQNaN=*/true, /*MayBeSNaN=*/true);
ConstantFPRange ConstantFPRangeTest::SNaN =
ConstantFPRange(APFloat::getSNaN(APFloat::IEEEdouble()));
ConstantFPRange ConstantFPRangeTest::QNaN =
ConstantFPRange(APFloat::getQNaN(APFloat::IEEEdouble()));
ConstantFPRange ConstantFPRangeTest::Some =
ConstantFPRange::getNonNaN(APFloat(-3.0), APFloat(3.0));
ConstantFPRange ConstantFPRangeTest::SomePos = ConstantFPRange::getNonNaN(
APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/false), APFloat(3.0));
ConstantFPRange ConstantFPRangeTest::SomeNeg = ConstantFPRange::getNonNaN(
APFloat(-3.0), APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true));
static void strictNext(APFloat &V) {
// Note: nextUp(+/-0) is smallest.
if (V.isNegZero())
V = APFloat::getZero(V.getSemantics(), /*Negative=*/false);
else
V.next(/*nextDown=*/false);
}
template <typename Fn>
static void EnumerateConstantFPRangesImpl(Fn TestFn, bool Exhaustive,
bool MayBeQNaN, bool MayBeSNaN) {
const fltSemantics &Sem = APFloat::Float8E4M3();
APFloat PosInf = APFloat::getInf(Sem, /*Negative=*/false);
APFloat NegInf = APFloat::getInf(Sem, /*Negative=*/true);
TestFn(ConstantFPRange(PosInf, NegInf, MayBeQNaN, MayBeSNaN));
if (!Exhaustive) {
SmallVector<APFloat, 36> Values;
Values.push_back(APFloat::getInf(Sem, /*Negative=*/true));
Values.push_back(APFloat::getLargest(Sem, /*Negative=*/true));
unsigned BitWidth = APFloat::semanticsSizeInBits(Sem);
unsigned Exponents = APFloat::semanticsMaxExponent(Sem) -
APFloat::semanticsMinExponent(Sem) + 3;
unsigned MantissaBits = APFloat::semanticsPrecision(Sem) - 1;
// Add -2^(max exponent), -2^(max exponent-1), ..., -2^(min exponent)
for (unsigned M = Exponents - 2; M != 0; --M)
Values.push_back(
APFloat(Sem, APInt(BitWidth, (M + Exponents) << MantissaBits)));
Values.push_back(APFloat::getSmallest(Sem, /*Negative=*/true));
Values.push_back(APFloat::getZero(Sem, /*Negative=*/true));
size_t E = Values.size();
for (size_t I = 1; I <= E; ++I)
Values.push_back(-Values[E - I]);
for (size_t I = 0; I != Values.size(); ++I)
for (size_t J = I; J != Values.size(); ++J)
TestFn(ConstantFPRange(Values[I], Values[J], MayBeQNaN, MayBeSNaN));
return;
}
auto Next = [&](APFloat &V) {
if (V.isPosInfinity())
return false;
strictNext(V);
return true;
};
APFloat Lower = NegInf;
do {
APFloat Upper = Lower;
do {
TestFn(ConstantFPRange(Lower, Upper, MayBeQNaN, MayBeSNaN));
} while (Next(Upper));
} while (Next(Lower));
}
template <typename Fn>
static void EnumerateConstantFPRanges(Fn TestFn, bool Exhaustive) {
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/false,
/*MayBeSNaN=*/false);
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/false,
/*MayBeSNaN=*/true);
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/true,
/*MayBeSNaN=*/false);
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/true,
/*MayBeSNaN=*/true);
}
template <typename Fn>
static void EnumerateTwoInterestingConstantFPRanges(Fn TestFn,
bool Exhaustive) {
EnumerateConstantFPRanges(
[&](const ConstantFPRange &CR1) {
EnumerateConstantFPRanges(
[&](const ConstantFPRange &CR2) { TestFn(CR1, CR2); }, Exhaustive);
},
Exhaustive);
}
template <typename Fn>
static void EnumerateValuesInConstantFPRange(const ConstantFPRange &CR,
Fn TestFn, bool IgnoreNaNPayload) {
const fltSemantics &Sem = CR.getSemantics();
if (IgnoreNaNPayload) {
if (CR.containsSNaN()) {
TestFn(APFloat::getSNaN(Sem, false));
TestFn(APFloat::getSNaN(Sem, true));
}
if (CR.containsQNaN()) {
TestFn(APFloat::getQNaN(Sem, false));
TestFn(APFloat::getQNaN(Sem, true));
}
if (CR.isNaNOnly())
return;
APFloat Lower = CR.getLower();
const APFloat &Upper = CR.getUpper();
auto Next = [&](APFloat &V) {
if (V.bitwiseIsEqual(Upper))
return false;
strictNext(V);
return true;
};
do
TestFn(Lower);
while (Next(Lower));
} else {
unsigned Bits = APFloat::semanticsSizeInBits(Sem);
assert(Bits < 32 && "Too many bits");
for (unsigned I = 0, E = (1U << Bits) - 1; I != E; ++I) {
APFloat V(Sem, APInt(Bits, I));
if (CR.contains(V))
TestFn(V);
}
}
}
template <typename Fn>
static bool AnyOfValueInConstantFPRange(const ConstantFPRange &CR, Fn TestFn,
bool IgnoreNaNPayload) {
const fltSemantics &Sem = CR.getSemantics();
if (IgnoreNaNPayload) {
if (CR.containsSNaN()) {
if (TestFn(APFloat::getSNaN(Sem, false)))
return true;
if (TestFn(APFloat::getSNaN(Sem, true)))
return true;
}
if (CR.containsQNaN()) {
if (TestFn(APFloat::getQNaN(Sem, false)))
return true;
if (TestFn(APFloat::getQNaN(Sem, true)))
return true;
}
if (CR.isNaNOnly())
return false;
APFloat Lower = CR.getLower();
const APFloat &Upper = CR.getUpper();
auto Next = [&](APFloat &V) {
if (V.bitwiseIsEqual(Upper))
return false;
strictNext(V);
return true;
};
do {
if (TestFn(Lower))
return true;
} while (Next(Lower));
} else {
unsigned Bits = APFloat::semanticsSizeInBits(Sem);
assert(Bits < 32 && "Too many bits");
for (unsigned I = 0, E = (1U << Bits) - 1; I != E; ++I) {
APFloat V(Sem, APInt(Bits, I));
if (CR.contains(V) && TestFn(V))
return true;
}
}
return false;
}
TEST_F(ConstantFPRangeTest, Basics) {
EXPECT_TRUE(Full.isFullSet());
EXPECT_FALSE(Full.isEmptySet());
EXPECT_TRUE(Full.contains(APFloat::getNaN(Sem)));
EXPECT_TRUE(Full.contains(APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_TRUE(Full.contains(APFloat::getInf(Sem, /*Negative=*/true)));
EXPECT_TRUE(Full.contains(APFloat::getZero(Sem, /*Negative=*/false)));
EXPECT_TRUE(Full.contains(APFloat::getZero(Sem, /*Negative=*/true)));
EXPECT_TRUE(Full.contains(APFloat::getSmallest(Sem)));
EXPECT_TRUE(Full.contains(APFloat(2.0)));
EXPECT_TRUE(Full.contains(Full));
EXPECT_TRUE(Full.contains(Empty));
EXPECT_TRUE(Full.contains(Finite));
EXPECT_TRUE(Full.contains(Zero));
EXPECT_TRUE(Full.contains(Some));
EXPECT_FALSE(Empty.isFullSet());
EXPECT_TRUE(Empty.isEmptySet());
EXPECT_FALSE(Empty.contains(APFloat::getNaN(Sem)));
EXPECT_FALSE(Empty.contains(APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_FALSE(Empty.contains(APFloat::getZero(Sem, /*Negative=*/true)));
EXPECT_FALSE(Empty.contains(APFloat(2.0)));
EXPECT_TRUE(Empty.contains(Empty));
EXPECT_FALSE(Finite.isFullSet());
EXPECT_FALSE(Finite.isEmptySet());
EXPECT_FALSE(Finite.contains(APFloat::getNaN(Sem)));
EXPECT_FALSE(Finite.contains(APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_FALSE(Finite.contains(APFloat::getInf(Sem, /*Negative=*/true)));
EXPECT_TRUE(Finite.contains(APFloat::getLargest(Sem, /*Negative=*/false)));
EXPECT_TRUE(Finite.contains(APFloat::getLargest(Sem, /*Negative=*/true)));
EXPECT_TRUE(Finite.contains(Finite));
EXPECT_TRUE(Finite.contains(Some));
EXPECT_TRUE(Finite.contains(Denormal));
EXPECT_TRUE(Finite.contains(Zero));
EXPECT_FALSE(Finite.contains(PosInf));
EXPECT_FALSE(Finite.contains(NaN));
EXPECT_TRUE(One.contains(APFloat(1.0)));
EXPECT_FALSE(One.contains(APFloat(1.1)));
EXPECT_TRUE(PosZero.contains(APFloat::getZero(Sem, /*Negative=*/false)));
EXPECT_FALSE(PosZero.contains(APFloat::getZero(Sem, /*Negative=*/true)));
EXPECT_TRUE(NegZero.contains(APFloat::getZero(Sem, /*Negative=*/true)));
EXPECT_FALSE(NegZero.contains(APFloat::getZero(Sem, /*Negative=*/false)));
EXPECT_TRUE(Zero.contains(PosZero));
EXPECT_TRUE(Zero.contains(NegZero));
EXPECT_TRUE(Denormal.contains(APFloat::getSmallest(Sem)));
EXPECT_FALSE(Denormal.contains(APFloat::getSmallestNormalized(Sem)));
EXPECT_TRUE(PosInf.contains(APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_TRUE(NegInf.contains(APFloat::getInf(Sem, /*Negative=*/true)));
EXPECT_TRUE(NaN.contains(APFloat::getQNaN(Sem)));
EXPECT_TRUE(NaN.contains(APFloat::getSNaN(Sem)));
EXPECT_TRUE(NaN.contains(SNaN));
EXPECT_TRUE(NaN.contains(QNaN));
EXPECT_TRUE(Some.contains(APFloat(3.0)));
EXPECT_TRUE(Some.contains(APFloat(-3.0)));
EXPECT_FALSE(Some.contains(APFloat(4.0)));
APFloat Next1(3.0);
Next1.next(/*nextDown=*/true);
EXPECT_TRUE(Some.contains(Next1));
APFloat Next2(3.0);
Next2.next(/*nextDown=*/false);
EXPECT_FALSE(Some.contains(Next2));
EXPECT_TRUE(Some.contains(Zero));
EXPECT_TRUE(Some.contains(Some));
EXPECT_TRUE(Some.contains(One));
EXPECT_FALSE(Some.contains(NaN));
EXPECT_FALSE(Some.contains(PosInf));
EXPECT_TRUE(SomePos.contains(APFloat(3.0)));
EXPECT_FALSE(SomeNeg.contains(APFloat(3.0)));
EXPECT_TRUE(SomeNeg.contains(APFloat(-3.0)));
EXPECT_FALSE(SomePos.contains(APFloat(-3.0)));
EXPECT_TRUE(Some.contains(SomePos));
EXPECT_TRUE(Some.contains(SomeNeg));
}
TEST_F(ConstantFPRangeTest, Equality) {
EXPECT_EQ(Full, Full);
EXPECT_EQ(Empty, Empty);
EXPECT_EQ(One, One);
EXPECT_EQ(Some, Some);
EXPECT_NE(Full, Empty);
EXPECT_NE(Zero, PosZero);
EXPECT_NE(One, NaN);
EXPECT_NE(Some, One);
EXPECT_NE(SNaN, QNaN);
}
TEST_F(ConstantFPRangeTest, SingleElement) {
EXPECT_EQ(Full.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(Empty.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(Finite.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(Zero.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(NaN.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(SNaN.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(QNaN.getSingleElement(), static_cast<APFloat *>(nullptr));
EXPECT_EQ(*One.getSingleElement(), APFloat(1.0));
EXPECT_EQ(*PosZero.getSingleElement(), APFloat::getZero(Sem));
EXPECT_EQ(*PosInf.getSingleElement(), APFloat::getInf(Sem));
ConstantFPRange PosZeroOrNaN = PosZero.unionWith(NaN);
EXPECT_EQ(*PosZeroOrNaN.getSingleElement(/*ExcludesNaN=*/true),
APFloat::getZero(Sem));
EXPECT_FALSE(Full.isSingleElement());
EXPECT_FALSE(Empty.isSingleElement());
EXPECT_TRUE(One.isSingleElement());
EXPECT_FALSE(Some.isSingleElement());
EXPECT_FALSE(Zero.isSingleElement());
EXPECT_TRUE(PosZeroOrNaN.isSingleElement(/*ExcludesNaN=*/true));
}
TEST_F(ConstantFPRangeTest, ExhaustivelyEnumerate) {
constexpr unsigned NNaNValues = (1 << 8) - 2 * ((1 << 3) - 1);
constexpr unsigned Expected = 4 * ((NNaNValues + 1) * NNaNValues / 2 + 1);
unsigned Count = 0;
EnumerateConstantFPRanges([&](const ConstantFPRange &) { ++Count; },
/*Exhaustive=*/true);
EXPECT_EQ(Expected, Count);
}
TEST_F(ConstantFPRangeTest, Enumerate) {
constexpr unsigned NNaNValues = 2 * ((1 << 4) - 2 + 4);
constexpr unsigned Expected = 4 * ((NNaNValues + 1) * NNaNValues / 2 + 1);
unsigned Count = 0;
EnumerateConstantFPRanges([&](const ConstantFPRange &) { ++Count; },
/*Exhaustive=*/false);
EXPECT_EQ(Expected, Count);
}
TEST_F(ConstantFPRangeTest, 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(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(SomePos), SomePos);
EXPECT_EQ(Some.intersectWith(SomeNeg), SomeNeg);
EXPECT_EQ(NaN.intersectWith(Finite), Empty);
EXPECT_EQ(NaN.intersectWith(SNaN), SNaN);
EXPECT_EQ(NaN.intersectWith(QNaN), QNaN);
EXPECT_EQ(Finite.intersectWith(One), One);
EXPECT_EQ(Some.intersectWith(Zero), Zero);
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(4.0))
.intersectWith(
ConstantFPRange::getNonNaN(APFloat(3.0), APFloat(6.0))),
ConstantFPRange::getNonNaN(APFloat(3.0), APFloat(4.0)));
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))
.intersectWith(
ConstantFPRange::getNonNaN(APFloat(5.0), APFloat(6.0))),
Empty);
}
TEST_F(ConstantFPRangeTest, UnionWith) {
EXPECT_EQ(Empty.unionWith(Full), Full);
EXPECT_EQ(Empty.unionWith(Empty), Empty);
EXPECT_EQ(Empty.unionWith(One), One);
EXPECT_EQ(Empty.unionWith(Some), Some);
EXPECT_EQ(Full.unionWith(Full), Full);
EXPECT_EQ(Some.unionWith(Some), Some);
EXPECT_EQ(Some.unionWith(One), Some);
EXPECT_EQ(Full.unionWith(Some), Full);
EXPECT_EQ(Some.unionWith(SomePos), Some);
EXPECT_EQ(Some.unionWith(SomeNeg), Some);
EXPECT_EQ(Finite.unionWith(One), Finite);
EXPECT_EQ(Some.unionWith(Zero), Some);
EXPECT_EQ(Finite.unionWith(PosInf).unionWith(NegInf).unionWith(NaN), Full);
EXPECT_EQ(PosZero.unionWith(NegZero), Zero);
EXPECT_EQ(NaN.unionWith(SNaN), NaN);
EXPECT_EQ(NaN.unionWith(QNaN), NaN);
EXPECT_EQ(SNaN.unionWith(QNaN), NaN);
EXPECT_EQ(
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(4.0))
.unionWith(ConstantFPRange::getNonNaN(APFloat(3.0), APFloat(6.0))),
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(6.0)));
EXPECT_EQ(
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))
.unionWith(ConstantFPRange::getNonNaN(APFloat(5.0), APFloat(6.0))),
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(6.0)));
}
TEST_F(ConstantFPRangeTest, FPClassify) {
EXPECT_EQ(Empty.classify(), fcNone);
EXPECT_EQ(Full.classify(), fcAllFlags);
EXPECT_EQ(Finite.classify(), fcFinite);
EXPECT_EQ(Zero.classify(), fcZero);
EXPECT_EQ(NaN.classify(), fcNan);
EXPECT_EQ(SNaN.classify(), fcSNan);
EXPECT_EQ(QNaN.classify(), fcQNan);
EXPECT_EQ(One.classify(), fcPosNormal);
EXPECT_EQ(Some.classify(), fcFinite);
EXPECT_EQ(SomePos.classify(), fcPosFinite);
EXPECT_EQ(SomeNeg.classify(), fcNegFinite);
EXPECT_EQ(PosInf.classify(), fcPosInf);
EXPECT_EQ(NegInf.classify(), fcNegInf);
EXPECT_EQ(Finite.getSignBit(), std::nullopt);
EXPECT_EQ(PosZero.getSignBit(), false);
EXPECT_EQ(NegZero.getSignBit(), true);
EXPECT_EQ(SomePos.getSignBit(), false);
EXPECT_EQ(SomeNeg.getSignBit(), true);
#if defined(EXPENSIVE_CHECKS)
EnumerateConstantFPRanges(
[](const ConstantFPRange &CR) {
unsigned Mask = fcNone;
bool HasPos = false, HasNeg = false;
EnumerateValuesInConstantFPRange(
CR,
[&](const APFloat &V) {
Mask |= V.classify();
if (V.isNegative())
HasNeg = true;
else
HasPos = true;
},
/*IgnoreNaNPayload=*/true);
std::optional<bool> SignBit = std::nullopt;
if (HasPos != HasNeg)
SignBit = HasNeg;
EXPECT_EQ(SignBit, CR.getSignBit()) << CR;
EXPECT_EQ(Mask, CR.classify()) << CR;
},
/*Exhaustive=*/true);
#endif
}
TEST_F(ConstantFPRangeTest, Print) {
auto ToString = [](const ConstantFPRange &CR) {
std::string Str;
raw_string_ostream OS(Str);
CR.print(OS);
return Str;
};
EXPECT_EQ(ToString(Full), "full-set");
EXPECT_EQ(ToString(Empty), "empty-set");
EXPECT_EQ(ToString(NaN), "NaN");
EXPECT_EQ(ToString(SNaN), "SNaN");
EXPECT_EQ(ToString(QNaN), "QNaN");
EXPECT_EQ(ToString(One), "[1, 1]");
EXPECT_EQ(ToString(Some.unionWith(SNaN)), "[-3, 3] with SNaN");
}
#ifdef GTEST_HAS_DEATH_TEST
#ifndef NDEBUG
TEST_F(ConstantFPRangeTest, NonCanonicalEmptySet) {
EXPECT_DEATH((void)(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(0.0))),
"Non-canonical form");
}
TEST_F(ConstantFPRangeTest, MismatchedSemantics) {
EXPECT_DEATH((void)(ConstantFPRange::getNonNaN(APFloat(0.0), APFloat(1.0f))),
"Should only use the same semantics");
EXPECT_DEATH((void)(One.contains(APFloat(1.0f))),
"Should only use the same semantics");
ConstantFPRange OneF32 = ConstantFPRange(APFloat(1.0f));
EXPECT_DEATH((void)(One.contains(OneF32)),
"Should only use the same semantics");
EXPECT_DEATH((void)(One.intersectWith(OneF32)),
"Should only use the same semantics");
EXPECT_DEATH((void)(One.unionWith(OneF32)),
"Should only use the same semantics");
}
#endif
#endif
TEST_F(ConstantFPRangeTest, makeAllowedFCmpRegion) {
EXPECT_EQ(ConstantFPRange::makeAllowedFCmpRegion(
FCmpInst::FCMP_OLE,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
APFloat(2.0)));
EXPECT_EQ(
ConstantFPRange::makeAllowedFCmpRegion(
FCmpInst::FCMP_OLT,
ConstantFPRange::getNonNaN(APFloat(1.0),
APFloat::getInf(Sem, /*Negative=*/false))),
ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
APFloat::getLargest(Sem, /*Negative=*/false)));
EXPECT_EQ(
ConstantFPRange::makeAllowedFCmpRegion(
FCmpInst::FCMP_OGT,
ConstantFPRange::getNonNaN(APFloat::getZero(Sem, /*Negative=*/true),
APFloat(2.0))),
ConstantFPRange::getNonNaN(APFloat::getSmallest(Sem, /*Negative=*/false),
APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_EQ(ConstantFPRange::makeAllowedFCmpRegion(
FCmpInst::FCMP_OGE,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
ConstantFPRange::getNonNaN(
APFloat(1.0), APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_EQ(ConstantFPRange::makeAllowedFCmpRegion(
FCmpInst::FCMP_OEQ,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0)));
#if defined(EXPENSIVE_CHECKS)
for (auto Pred : FCmpInst::predicates()) {
EnumerateConstantFPRanges(
[Pred](const ConstantFPRange &CR) {
ConstantFPRange Res =
ConstantFPRange::makeAllowedFCmpRegion(Pred, CR);
ConstantFPRange Optimal =
ConstantFPRange::getEmpty(CR.getSemantics());
EnumerateValuesInConstantFPRange(
ConstantFPRange::getFull(CR.getSemantics()),
[&](const APFloat &V) {
if (AnyOfValueInConstantFPRange(
CR,
[&](const APFloat &U) {
return FCmpInst::compare(V, U, Pred);
},
/*IgnoreNaNPayload=*/true))
Optimal = Optimal.unionWith(ConstantFPRange(V));
},
/*IgnoreNaNPayload=*/true);
EXPECT_TRUE(Res.contains(Optimal))
<< "Wrong result for makeAllowedFCmpRegion(" << Pred << ", " << CR
<< "). Expected " << Optimal << ", but got " << Res;
EXPECT_EQ(Res, Optimal)
<< "Suboptimal result for makeAllowedFCmpRegion(" << Pred << ", "
<< CR << ")";
},
/*Exhaustive=*/false);
}
#endif
}
TEST_F(ConstantFPRangeTest, makeSatisfyingFCmpRegion) {
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
FCmpInst::FCMP_OLE,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
APFloat(1.0)));
EXPECT_EQ(
ConstantFPRange::makeSatisfyingFCmpRegion(
FCmpInst::FCMP_OLT, ConstantFPRange::getNonNaN(
APFloat::getSmallest(Sem, /*Negative=*/false),
APFloat::getInf(Sem, /*Negative=*/false))),
ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
APFloat::getZero(Sem, /*Negative=*/false)));
EXPECT_EQ(
ConstantFPRange::makeSatisfyingFCmpRegion(
FCmpInst::FCMP_OGT, ConstantFPRange::getNonNaN(
APFloat::getZero(Sem, /*Negative=*/true),
APFloat::getZero(Sem, /*Negative=*/false))),
ConstantFPRange::getNonNaN(APFloat::getSmallest(Sem, /*Negative=*/false),
APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
FCmpInst::FCMP_OGE,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
ConstantFPRange::getNonNaN(
APFloat(2.0), APFloat::getInf(Sem, /*Negative=*/false)));
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
FCmpInst::FCMP_OEQ,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
ConstantFPRange::getEmpty(Sem));
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
FCmpInst::FCMP_OEQ,
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(1.0))),
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(1.0)));
#if defined(EXPENSIVE_CHECKS)
for (auto Pred : FCmpInst::predicates()) {
EnumerateConstantFPRanges(
[Pred](const ConstantFPRange &CR) {
ConstantFPRange Res =
ConstantFPRange::makeSatisfyingFCmpRegion(Pred, CR);
// Super set of the optimal set excluding NaNs
ConstantFPRange SuperSet(CR.getSemantics());
bool ContainsSNaN = false;
bool ContainsQNaN = false;
unsigned NonNaNValsInOptimalSet = 0;
EnumerateValuesInConstantFPRange(
ConstantFPRange::getFull(CR.getSemantics()),
[&](const APFloat &V) {
if (AnyOfValueInConstantFPRange(
CR,
[&](const APFloat &U) {
return !FCmpInst::compare(V, U, Pred);
},
/*IgnoreNaNPayload=*/true)) {
EXPECT_FALSE(Res.contains(V))
<< "Wrong result for makeSatisfyingFCmpRegion(" << Pred
<< ", " << CR << "). The result " << Res
<< " should not contain " << V;
} else {
if (V.isNaN()) {
if (V.isSignaling())
ContainsSNaN = true;
else
ContainsQNaN = true;
} else {
SuperSet = SuperSet.unionWith(ConstantFPRange(V));
++NonNaNValsInOptimalSet;
}
}
},
/*IgnoreNaNPayload=*/true);
// Check optimality
// The usefullness of making the result optimal for one/une is
// questionable.
if (Pred == FCmpInst::FCMP_ONE || Pred == FCmpInst::FCMP_UNE)
return;
EXPECT_FALSE(ContainsSNaN && !Res.containsSNaN())
<< "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
<< ", " << CR << "), should contain SNaN, but got " << Res;
EXPECT_FALSE(ContainsQNaN && !Res.containsQNaN())
<< "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
<< ", " << CR << "), should contain QNaN, but got " << Res;
// We only care about the cases where the result is representable by
// ConstantFPRange.
unsigned NonNaNValsInSuperSet = 0;
EnumerateValuesInConstantFPRange(
SuperSet,
[&](const APFloat &V) {
if (!V.isNaN())
++NonNaNValsInSuperSet;
},
/*IgnoreNaNPayload=*/true);
if (NonNaNValsInSuperSet == NonNaNValsInOptimalSet) {
ConstantFPRange Optimal =
ConstantFPRange(SuperSet.getLower(), SuperSet.getUpper(),
ContainsQNaN, ContainsSNaN);
EXPECT_EQ(Res, Optimal)
<< "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
<< ", " << CR << ")";
}
},
/*Exhaustive=*/false);
}
#endif
}
TEST_F(ConstantFPRangeTest, fcmp) {
std::vector<ConstantFPRange> InterestingRanges;
const fltSemantics &Sem = APFloat::Float8E4M3();
auto FpImm = [&](double V) {
bool ignored;
APFloat APF(V);
APF.convert(Sem, APFloat::rmNearestTiesToEven, &ignored);
return APF;
};
InterestingRanges.push_back(ConstantFPRange::getEmpty(Sem));
InterestingRanges.push_back(ConstantFPRange::getFull(Sem));
InterestingRanges.push_back(ConstantFPRange::getFinite(Sem));
InterestingRanges.push_back(ConstantFPRange(FpImm(1.0)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/false)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/true)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/false)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/true)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/false)));
InterestingRanges.push_back(
ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true)));
InterestingRanges.push_back(
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true));
InterestingRanges.push_back(
ConstantFPRange::getNonNaN(FpImm(0.0), FpImm(1.0)));
InterestingRanges.push_back(
ConstantFPRange::getNonNaN(FpImm(2.0), FpImm(3.0)));
InterestingRanges.push_back(
ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(1.0)));
InterestingRanges.push_back(
ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(-0.0)));
InterestingRanges.push_back(ConstantFPRange::getNonNaN(
APFloat::getInf(Sem, /*Negative=*/true), FpImm(-1.0)));
InterestingRanges.push_back(ConstantFPRange::getNonNaN(
FpImm(1.0), APFloat::getInf(Sem, /*Negative=*/false)));
for (auto &LHS : InterestingRanges) {
for (auto &RHS : InterestingRanges) {
for (auto Pred : FCmpInst::predicates()) {
if (LHS.fcmp(Pred, RHS)) {
EnumerateValuesInConstantFPRange(
LHS,
[&](const APFloat &LHSC) {
EnumerateValuesInConstantFPRange(
RHS,
[&](const APFloat &RHSC) {
EXPECT_TRUE(FCmpInst::compare(LHSC, RHSC, Pred))
<< LHS << " " << Pred << " " << RHS
<< " doesn't hold";
},
/*IgnoreNaNPayload=*/true);
},
/*IgnoreNaNPayload=*/true);
}
}
}
}
}
TEST_F(ConstantFPRangeTest, makeExactFCmpRegion) {
for (auto Pred : FCmpInst::predicates()) {
EnumerateValuesInConstantFPRange(
ConstantFPRange::getFull(APFloat::Float8E4M3()),
[Pred](const APFloat &V) {
std::optional<ConstantFPRange> Res =
ConstantFPRange::makeExactFCmpRegion(Pred, V);
ConstantFPRange Allowed =
ConstantFPRange::makeAllowedFCmpRegion(Pred, ConstantFPRange(V));
ConstantFPRange Satisfying =
ConstantFPRange::makeSatisfyingFCmpRegion(Pred,
ConstantFPRange(V));
if (Allowed == Satisfying)
EXPECT_EQ(Res, Allowed) << "Wrong result for makeExactFCmpRegion("
<< Pred << ", " << V << ").";
else
EXPECT_FALSE(Res.has_value())
<< "Wrong result for makeExactFCmpRegion(" << Pred << ", " << V
<< ").";
},
/*IgnoreNaNPayload=*/true);
}
}
} // anonymous namespace