| //===- 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 |