Index: llvm/include/llvm/ADT/APFloat.h =================================================================== --- llvm/include/llvm/ADT/APFloat.h +++ llvm/include/llvm/ADT/APFloat.h @@ -27,6 +27,7 @@ class APSInt; class StringRef; class APFloat; +class raw_ostream; template class SmallVectorImpl; @@ -479,6 +480,8 @@ /// @} + cmpResult compareAbsoluteValue(const IEEEFloat &) const; + private: /// \name Simple Queries /// @{ @@ -527,7 +530,6 @@ bool convertFromStringSpecials(StringRef str); opStatus normalize(roundingMode, lostFraction); opStatus addOrSubtract(const IEEEFloat &, roundingMode, bool subtract); - cmpResult compareAbsoluteValue(const IEEEFloat &) const; opStatus handleOverflow(roundingMode); bool roundAwayFromZero(roundingMode, lostFraction, unsigned int) const; opStatus convertToSignExtendedInteger(integerPart *, unsigned int, bool, @@ -600,6 +602,12 @@ const fltSemantics *Semantics; std::unique_ptr Floats; + opStatus addImpl(const APFloat &a, const APFloat &aa, const APFloat &c, + const APFloat &cc, roundingMode RM); + + opStatus addWithSpecial(const DoubleAPFloat &LHS, const DoubleAPFloat &RHS, + DoubleAPFloat &Out, roundingMode RM); + public: DoubleAPFloat(const fltSemantics &S); DoubleAPFloat(const fltSemantics &S, uninitializedTag); @@ -623,6 +631,19 @@ APFloat &getFirst() { return Floats[0]; } const APFloat &getFirst() const { return Floats[0]; } + APFloat &getSecond() { return Floats[1]; } + const APFloat &getSecond() const { return Floats[1]; } + + opStatus add(const DoubleAPFloat &RHS, roundingMode RM); + opStatus subtract(const DoubleAPFloat &RHS, roundingMode RM); + void changeSign(); + cmpResult compareAbsoluteValue(const DoubleAPFloat &RHS) const; + + fltCategory getCategory() const; + bool isNegative() const; + + void makeInf(bool Neg); + void makeNaN(bool SNaN, bool Neg, const APInt *fill); }; } // End detail namespace @@ -747,7 +768,15 @@ void makeZero(bool Neg) { getIEEE().makeZero(Neg); } - void makeInf(bool Neg) { getIEEE().makeInf(Neg); } + void makeInf(bool Neg) { + if (usesLayout(*U.semantics)) { + return U.IEEE.makeInf(Neg); + } else if (usesLayout(*U.semantics)) { + return U.Double.makeInf(Neg); + } else { + llvm_unreachable("Unexpected semantics"); + } + } void makeNaN(bool SNaN, bool Neg, const APInt *fill) { getIEEE().makeNaN(SNaN, Neg, fill); @@ -772,6 +801,17 @@ explicit APFloat(DoubleAPFloat F, const fltSemantics &S) : U(std::move(F), S) {} + cmpResult compareAbsoluteValue(const APFloat &RHS) const { + assert(&getSemantics() == &RHS.getSemantics()); + if (usesLayout(getSemantics())) { + return U.IEEE.compareAbsoluteValue(RHS.U.IEEE); + } else if (usesLayout(getSemantics())) { + return U.Double.compareAbsoluteValue(RHS.U.Double); + } else { + llvm_unreachable("Unexpected semantics"); + } + } + public: APFloat(const fltSemantics &Semantics) : U(Semantics) {} APFloat(const fltSemantics &Semantics, StringRef S); @@ -885,10 +925,22 @@ void Profile(FoldingSetNodeID &NID) const { getIEEE().Profile(NID); } opStatus add(const APFloat &RHS, roundingMode RM) { - return getIEEE().add(RHS.getIEEE(), RM); + if (usesLayout(getSemantics())) { + return U.IEEE.add(RHS.U.IEEE, RM); + } else if (usesLayout(getSemantics())) { + return U.Double.add(RHS.U.Double, RM); + } else { + llvm_unreachable("Unexpected semantics"); + } } opStatus subtract(const APFloat &RHS, roundingMode RM) { - return getIEEE().subtract(RHS.getIEEE(), RM); + if (usesLayout(getSemantics())) { + return U.IEEE.subtract(RHS.U.IEEE, RM); + } else if (usesLayout(getSemantics())) { + return U.Double.subtract(RHS.U.Double, RM); + } else { + llvm_unreachable("Unexpected semantics"); + } } opStatus multiply(const APFloat &RHS, roundingMode RM) { return getIEEE().multiply(RHS.getIEEE(), RM); @@ -1011,14 +1063,24 @@ return getIEEE().toString(Str, FormatPrecision, FormatMaxPadding); } + void Print(raw_ostream &) const; + bool getExactInverse(APFloat *inv) const { return getIEEE().getExactInverse(inv ? &inv->getIEEE() : nullptr); } + // This is for internal test only. + // TODO: Remove it after the PPCDoubleDouble transition. + const APFloat &getSecondFloat() const { + assert(&getSemantics() == &PPCDoubleDouble); + return U.Double.getSecond(); + } + friend hash_code hash_value(const APFloat &Arg); friend int ilogb(const APFloat &Arg) { return ilogb(Arg.getIEEE()); } friend APFloat scalbn(APFloat X, int Exp, roundingMode RM); friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM); + friend IEEEFloat; friend DoubleAPFloat; }; Index: llvm/lib/Support/APFloat.cpp =================================================================== --- llvm/lib/Support/APFloat.cpp +++ llvm/lib/Support/APFloat.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -3858,7 +3859,8 @@ Floats(new APFloat[2]{std::move(First), std::move(Second)}) { assert(Semantics == &PPCDoubleDouble); // TODO Check for First == &IEEEdouble once the transition is done. - assert(&Floats[0].getSemantics() == &PPCDoubleDoubleImpl); + assert(&Floats[0].getSemantics() == &PPCDoubleDoubleImpl || + &Floats[0].getSemantics() == &IEEEdouble); assert(&Floats[1].getSemantics() == &IEEEdouble); } @@ -3887,6 +3889,196 @@ return *this; } +// TODO: correctly propagate overflow and underflow status. +APFloat::opStatus DoubleAPFloat::addImpl(const APFloat &a, const APFloat &aa, + const APFloat &c, const APFloat &cc, + roundingMode RM) { + int Status = opOK; + APFloat z = a; + Status |= z.add(c, RM); + if (!z.isFinite()) { + if (!z.isInfinity()) { + *this = DoubleAPFloat(PPCDoubleDouble, std::move(z), APFloat(IEEEdouble)); + return (opStatus)Status; + } + Status = opOK; + z = cc; + Status |= z.add(aa, RM); + Status |= z.add(c, RM); + Status |= z.add(a, RM); + if (!z.isFinite()) { + *this = DoubleAPFloat(PPCDoubleDouble, std::move(z), APFloat(IEEEdouble)); + return (opStatus)Status; + } + Floats[0] = z; + APFloat zz = aa; + zz.add(cc, RM); + if (a.compareAbsoluteValue(c) == APFloat::cmpGreaterThan) { + Floats[1] = a; + Floats[1].subtract(z, RM); + Floats[1].add(c, RM); + Floats[1].add(zz, RM); + } else { + Floats[1] = c; + Floats[1].subtract(z, RM); + Floats[1].add(a, RM); + Floats[1].add(zz, RM); + } + } else { + APFloat q = a; + q.subtract(z, RM); + auto zz = q; + zz.add(c, RM); + q.add(z, RM); + { + auto tmp = a; + tmp.subtract(q, RM); + zz.add(tmp, RM); + } + zz.add(aa, RM); + zz.add(cc, RM); + if (zz.isZero() && !zz.isNegative()) { + Floats[0] = std::move(z); + Floats[1].makeZero(false); + return opOK; + } + Floats[0] = z; + Status |= Floats[0].add(zz, RM); + if (!Floats[0].isFinite()) { + Floats[1].makeZero(false); + return (opStatus)Status; + } + Floats[1] = std::move(z); + Floats[1].subtract(Floats[0], RM); + Floats[1].add(zz, RM); + } + return opOK; +} + +APFloat::opStatus DoubleAPFloat::addWithSpecial(const DoubleAPFloat &LHS, + const DoubleAPFloat &RHS, + DoubleAPFloat &Out, + roundingMode RM) { + if (LHS.getCategory() == fcNaN) { + Out = LHS; + return opOK; + } + if (RHS.getCategory() == fcNaN) { + Out = RHS; + return opOK; + } + if (LHS.getCategory() == fcZero) { + Out = RHS; + return opOK; + } + if (RHS.getCategory() == fcZero) { + Out = LHS; + return opOK; + } + if (LHS.getCategory() == fcInfinity && RHS.getCategory() == fcInfinity && + LHS.isNegative() != RHS.isNegative()) { + Out.makeNaN(false, Out.isNegative(), nullptr); + return opInvalidOp; + } + if (LHS.getCategory() == fcInfinity) { + Out = LHS; + return opOK; + } + if (RHS.getCategory() == fcInfinity) { + Out = RHS; + return opOK; + } + assert(LHS.getCategory() == fcNormal && RHS.getCategory() == fcNormal); + + // These conversions will go away once PPCDoubleDoubleImpl goes away. + // (PPCDoubleDoubleImpl, IEEEDouble) -> (IEEEDouble, IEEEDouble) + bool losesInfo; + APFloat A(LHS.Floats[0]), AA(LHS.Floats[1]), C(RHS.Floats[0]), + CC(RHS.Floats[1]); + (void)A.convert(IEEEdouble, rmTowardNegative, &losesInfo); + assert(&AA.getSemantics() == &IEEEdouble); + (void)C.convert(IEEEdouble, rmTowardNegative, &losesInfo); + assert(&CC.getSemantics() == &IEEEdouble); + (void)Out.Floats[0].convert(IEEEdouble, rmTowardNegative, &losesInfo); + assert(&Out.Floats[1].getSemantics() == &IEEEdouble); + + auto Ret = Out.addImpl(A, AA, C, CC, RM); + + // (IEEEDouble, IEEEDouble) -> (PPCDoubleDoubleImpl, IEEEDouble) + (void)Out.Floats[0].convert(PPCDoubleDoubleImpl, rmTowardNegative, + &losesInfo); + assert(!losesInfo); + (void)Out.Floats[1].convert(PPCDoubleDoubleImpl, rmTowardNegative, + &losesInfo); + assert(!losesInfo); + Out.Floats[0].add(Out.Floats[1], RM); + (void)Out.Floats[1].convert(IEEEdouble, rmTowardNegative, &losesInfo); + assert(!losesInfo); + return Ret; +} + +APFloat::opStatus DoubleAPFloat::add(const DoubleAPFloat &RHS, + roundingMode RM) { + return addWithSpecial(*this, RHS, *this, RM); +} + +APFloat::opStatus DoubleAPFloat::subtract(const DoubleAPFloat &RHS, + roundingMode RM) { + changeSign(); + auto Ret = add(RHS, RM); + changeSign(); + return Ret; +} + +void DoubleAPFloat::changeSign() { + Floats[0].changeSign(); + Floats[1].changeSign(); +} + +APFloat::cmpResult +DoubleAPFloat::compareAbsoluteValue(const DoubleAPFloat &RHS) const { + auto Result = Floats[0].compareAbsoluteValue(RHS.Floats[0]); + if (Result != cmpEqual) { + return Result; + } + Result = Floats[1].compareAbsoluteValue(RHS.Floats[1]); + if (Result == cmpLessThan || Result == cmpGreaterThan) { + auto Against = Floats[0].isNegative() ^ Floats[1].isNegative(); + auto RHSAgainst = RHS.Floats[0].isNegative() ^ RHS.Floats[1].isNegative(); + if (Against && !RHSAgainst) { + return cmpLessThan; + } + if (!Against && RHSAgainst) { + return cmpGreaterThan; + } + if (!Against && !RHSAgainst) { + return Result; + } + if (Against && RHSAgainst) { + return (cmpResult)(cmpLessThan + cmpGreaterThan - Result); + } + } else { + return Result; + } + llvm_unreachable(""); +} + +APFloat::fltCategory DoubleAPFloat::getCategory() const { + return Floats[0].getCategory(); +} + +bool DoubleAPFloat::isNegative() const { return Floats[0].isNegative(); } + +void DoubleAPFloat::makeInf(bool Neg) { + Floats[0].makeInf(Neg); + Floats[1].makeZero(false); +} + +void DoubleAPFloat::makeNaN(bool SNaN, bool Neg, const APInt *fill) { + Floats[0].makeNaN(SNaN, Neg, fill); + Floats[1].makeZero(false); +} + } // End detail namespace APFloat::Storage::Storage(IEEEFloat F, const fltSemantics &Semantics) { @@ -3959,4 +4151,10 @@ } } +void APFloat::Print(raw_ostream &OS) const { + SmallVector Buffer; + toString(Buffer); + OS << Buffer; +} + } // End llvm namespace Index: llvm/unittests/ADT/APFloatTest.cpp =================================================================== --- llvm/unittests/ADT/APFloatTest.cpp +++ llvm/unittests/ADT/APFloatTest.cpp @@ -1512,22 +1512,6 @@ EXPECT_EQ(0x0360000000000000ull, test.bitcastToAPInt().getRawData()[0]); EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); - test = APFloat(APFloat::PPCDoubleDouble, "1.0"); - test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-105"), APFloat::rmNearestTiesToEven); - EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); - EXPECT_EQ(0x3960000000000000ull, test.bitcastToAPInt().getRawData()[1]); - - test = APFloat(APFloat::PPCDoubleDouble, "1.0"); - test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-106"), APFloat::rmNearestTiesToEven); - EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); -#if 0 // XFAIL - // This is what we would expect with a true double-double implementation - EXPECT_EQ(0x3950000000000000ull, test.bitcastToAPInt().getRawData()[1]); -#else - // This is what we get with our 106-bit mantissa approximation - EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); -#endif - // PR30869 { auto Result = APFloat(APFloat::PPCDoubleDouble, "1.0") + @@ -3186,4 +3170,78 @@ EXPECT_EQ(52, Exp); EXPECT_TRUE(APFloat(APFloat::IEEEdouble, "0x1.c60f120d9f87cp-1").bitwiseIsEqual(Frac)); } + +TEST(APFloatTest, PPCDoubleDoubleAdd) { + auto test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-105"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x3960000000000000ull, test.bitcastToAPInt().getRawData()[1]); + EXPECT_EQ(0x3960000000000000ull, + test.getSecondFloat().bitcastToAPInt().getRawData()[0]); + + test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-106"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); + EXPECT_EQ(0x3950000000000000ull, + test.getSecondFloat().bitcastToAPInt().getRawData()[0]); + + test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-106"), + APFloat::rmNearestTiesToEven); + test.add(APFloat(APFloat::PPCDoubleDouble, "0x1p-106"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x3960000000000000ull, test.bitcastToAPInt().getRawData()[1]); + EXPECT_EQ(0x3960000000000000ull, + test.getSecondFloat().bitcastToAPInt().getRawData()[0]); + + test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + // add epsilon + test.add(APFloat(APFloat::PPCDoubleDouble, + "4.94065645841246544176568792868221e-324"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); + EXPECT_EQ(0x0000000000000001ull, + test.getSecondFloat().bitcastToAPInt().getRawData()[0]); + + test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + test.add(APFloat(APFloat::PPCDoubleDouble, "-1.0"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(APFloat::fcZero, test.getCategory()); + + test = APFloat(APFloat::PPCDoubleDouble, + "1.79769313486231580793728971405301E+308"); + EXPECT_EQ(APFloat::fcNormal, test.getCategory()); + test.add(APFloat(APFloat::PPCDoubleDouble, "1.0E+308"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(APFloat::fcInfinity, test.getCategory()); + + test = APFloat::getNaN(APFloat::PPCDoubleDouble); + EXPECT_EQ(APFloat::fcNaN, test.getCategory()); + test.add(APFloat(APFloat::PPCDoubleDouble, "1.0"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(APFloat::fcNaN, test.getCategory()); +} + +TEST(APFloatTest, PPCDoubleDoubleSubtract) { + auto test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + test.subtract(APFloat(APFloat::PPCDoubleDouble, "-0x1p-105"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x3960000000000000ull, test.bitcastToAPInt().getRawData()[1]); + EXPECT_EQ(0x3960000000000000ull, + test.getSecondFloat().bitcastToAPInt().getRawData()[0]); + + test = APFloat(APFloat::PPCDoubleDouble, "1.0"); + test.subtract(APFloat(APFloat::PPCDoubleDouble, "-0x1p-106"), + APFloat::rmNearestTiesToEven); + EXPECT_EQ(0x3ff0000000000000ull, test.bitcastToAPInt().getRawData()[0]); + EXPECT_EQ(0x0000000000000000ull, test.bitcastToAPInt().getRawData()[1]); + EXPECT_EQ(0x3950000000000000ull, + test.getSecondFloat().bitcastToAPInt().getRawData()[0]); +} }