blob: 681b2d27fb54713d47c1e5cfbf2b6ddf71acde8c [file] [edit]
//===- KnownFPClassTest.cpp - KnownFPClass 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/Support/KnownFPClass.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/Support/KnownBits.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
static void expectConstant(const char *SemanticsName, const char *ValueName,
const fltSemantics &Semantics, APInt ValueBits,
FPClassTest PositiveClass, bool Negative) {
if (Negative)
ValueBits.setBit(Semantics.sizeInBits - 1);
SCOPED_TRACE(testing::Message()
<< SemanticsName << ' ' << (Negative ? "negative " : "positive ")
<< ValueName);
KnownFPClass Known =
KnownFPClass::bitcast(Semantics, KnownBits::makeConstant(ValueBits));
FPClassTest ExpectedClass =
Negative ? llvm::fneg(PositiveClass) : PositiveClass;
EXPECT_EQ(ExpectedClass, Known.getKnownFPClasses());
EXPECT_EQ(Negative, Known.getSignBit());
}
TEST(KnownFPClassTest, BitcastExhaustiveIEEEHalf) {
const fltSemantics &Semantics = APFloat::IEEEhalf();
for (uint64_t RawBits = 0; RawBits != (1u << 16); ++RawBits) {
APInt ValueBits(16, RawBits);
KnownFPClass Known =
KnownFPClass::bitcast(Semantics, KnownBits::makeConstant(ValueBits));
KnownFPClass Expected(APFloat(Semantics, ValueBits));
ASSERT_EQ(Expected.getKnownFPClasses(), Known.getKnownFPClasses())
<< RawBits;
ASSERT_EQ(Expected.getSignBit(), Known.getSignBit()) << RawBits;
}
}
TEST(KnownFPClassTest, BitcastConflict) {
const fltSemantics &Semantics = APFloat::IEEEsingle();
KnownBits Bits(Semantics.sizeInBits);
Bits.setAllConflict();
ASSERT_TRUE(Bits.hasConflict());
KnownFPClass Known = KnownFPClass::bitcast(Semantics, Bits);
EXPECT_EQ(fcAllFlags, Known.getKnownFPClasses());
EXPECT_EQ(std::nullopt, Known.getSignBit());
}
TEST(KnownFPClassTest, BitcastPartialConflict) {
const fltSemantics &Semantics = APFloat::IEEEsingle();
KnownBits Bits(Semantics.sizeInBits);
Bits.Zero.setAllBits();
Bits.One.setBit(0);
ASSERT_TRUE(Bits.hasConflict());
KnownFPClass Known = KnownFPClass::bitcast(Semantics, Bits);
EXPECT_EQ(fcAllFlags, Known.getKnownFPClasses());
EXPECT_EQ(std::nullopt, Known.getSignBit());
}
TEST(KnownFPClassTest, BitcastConstant) {
struct SemanticsCase {
const char *Name;
const fltSemantics *Semantics;
};
for (const SemanticsCase &TestCase :
{SemanticsCase{"ieee_binary16", &APFloat::IEEEhalf()},
SemanticsCase{"bfloat16", &APFloat::BFloat()},
SemanticsCase{"ieee_binary32", &APFloat::IEEEsingle()},
SemanticsCase{"ieee_binary64", &APFloat::IEEEdouble()},
SemanticsCase{"ieee_binary128", &APFloat::IEEEquad()}}) {
const fltSemantics &Semantics = *TestCase.Semantics;
const unsigned BitWidth = Semantics.sizeInBits;
const unsigned MantissaBits = Semantics.precision - 1;
const APInt ExponentMask =
APInt::getBitsSet(BitWidth, MantissaBits, BitWidth - 1);
const APInt MantissaMask = APInt::getLowBitsSet(BitWidth, MantissaBits);
const APInt QuietBit = APInt::getOneBitSet(BitWidth, MantissaBits - 1);
for (bool Negative : {false, true}) {
expectConstant(TestCase.Name, "0.0", Semantics, APInt::getZero(BitWidth),
fcPosZero, Negative);
expectConstant(TestCase.Name, "min_subnormal", Semantics,
APInt(BitWidth, 1), fcPosSubnormal, Negative);
expectConstant(TestCase.Name, "max_subnormal", Semantics, MantissaMask,
fcPosSubnormal, Negative);
expectConstant(TestCase.Name, "min_normal", Semantics,
APInt::getOneBitSet(BitWidth, MantissaBits), fcPosNormal,
Negative);
expectConstant(TestCase.Name, "1.0", Semantics,
APFloat::getOne(Semantics).bitcastToAPInt(), fcPosNormal,
Negative);
expectConstant(TestCase.Name, "max_normal", Semantics,
APFloat::getLargest(Semantics).bitcastToAPInt(),
fcPosNormal, Negative);
expectConstant(TestCase.Name, "inf", Semantics, ExponentMask, fcPosInf,
Negative);
// An sNaN has a clear quiet bit and a non-zero payload.
expectConstant(TestCase.Name, "snan_mostly_zero", Semantics,
ExponentMask | APInt(BitWidth, 1), fcSNan, Negative);
// A qNaN has a set quiet bit. The remaining payload bits may be zero.
expectConstant(TestCase.Name, "qnan_mostly_zero", Semantics,
ExponentMask | QuietBit, fcQNan, Negative);
expectConstant(TestCase.Name, "snan_mostly_one", Semantics,
ExponentMask | (MantissaMask & ~QuietBit), fcSNan,
Negative);
expectConstant(TestCase.Name, "qnan_mostly_one", Semantics,
ExponentMask | MantissaMask, fcQNan, Negative);
}
}
}
TEST(KnownFPClassTest, BitcastUnsupported) {
auto IsSupported = [](const fltSemantics &Semantics) {
switch (APFloat::SemanticsToEnum(Semantics)) {
case APFloatBase::S_IEEEhalf:
case APFloatBase::S_BFloat:
case APFloatBase::S_IEEEsingle:
case APFloatBase::S_IEEEdouble:
case APFloatBase::S_IEEEquad:
case APFloatBase::S_x87DoubleExtended:
return true;
default:
return false;
}
};
for (unsigned I = 0; I != APFloat::S_MaxSemantics + 1; ++I) {
APFloat::Semantics SemanticsKind = static_cast<APFloat::Semantics>(I);
const fltSemantics &Semantics = APFloat::EnumToSemantics(SemanticsKind);
for (const APInt &ValueBits : {APInt::getZero(Semantics.sizeInBits),
APInt::getAllOnes(Semantics.sizeInBits)}) {
SCOPED_TRACE(testing::Message()
<< "Semantics = " << I << ", bits = " << ValueBits);
KnownFPClass Known =
KnownFPClass::bitcast(Semantics, KnownBits::makeConstant(ValueBits));
if (IsSupported(Semantics)) {
// We should be able to make at least one deduction for "Supported"
// types.
EXPECT_FALSE(Known.isUnknown());
} else {
EXPECT_TRUE(Known.isUnknown());
}
}
}
}
TEST(KnownFPClassTest, ToKnownBitsUnsupported) {
auto IsSupported = [](const fltSemantics &Semantics) {
switch (APFloat::SemanticsToEnum(Semantics)) {
case APFloatBase::S_IEEEhalf:
case APFloatBase::S_BFloat:
case APFloatBase::S_IEEEsingle:
case APFloatBase::S_IEEEdouble:
case APFloatBase::S_IEEEquad:
case APFloatBase::S_x87DoubleExtended:
return true;
default:
return false;
}
};
for (unsigned I = 0; I != APFloat::S_MaxSemantics + 1; ++I) {
APFloat::Semantics SemanticsKind = static_cast<APFloat::Semantics>(I);
const fltSemantics &Semantics = APFloat::EnumToSemantics(SemanticsKind);
for (FPClassTest FPClass : {fcPosZero, fcPosNormal}) {
SCOPED_TRACE(testing::Message()
<< "Semantics = " << I << ", class = " << FPClass);
KnownBits Known = KnownFPClass(FPClass, false).toKnownBits(Semantics);
if (IsSupported(Semantics)) {
// The following expectations are true for all "supported" types.
EXPECT_TRUE(Known.isNonNegative());
if (FPClass == fcPosZero)
EXPECT_TRUE(Known.isZero());
} else {
EXPECT_TRUE(Known.isUnknown());
}
}
}
}
} // end anonymous namespace