Index: llvm/include/llvm/ADT/APFixedPoint.h =================================================================== --- llvm/include/llvm/ADT/APFixedPoint.h +++ llvm/include/llvm/ADT/APFixedPoint.h @@ -17,6 +17,7 @@ #define LLVM_ADT_APFIXEDPOINT_H #include "llvm/ADT/APSInt.h" +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" @@ -32,31 +33,51 @@ /// in the value this represents is treated as padding. class FixedPointSemantics { public: + static constexpr unsigned WidthBitWidth = 16; + static constexpr unsigned LsbWeightBitWidth = 13; + /// Used to differentiate between constructors with Width and Lsb from the + /// default Width and scale + struct Lsb { + int LsbWeight; + }; FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned, bool IsSaturated, bool HasUnsignedPadding) - : Width(Width), Scale(Scale), IsSigned(IsSigned), + : FixedPointSemantics(Width, Lsb{-static_cast(Scale)}, IsSigned, + IsSaturated, HasUnsignedPadding) {} + FixedPointSemantics(unsigned Width, Lsb Weight, bool IsSigned, + bool IsSaturated, bool HasUnsignedPadding) + : Width(Width), LsbWeight(Weight.LsbWeight), IsSigned(IsSigned), IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) { - assert(Width >= Scale && "Not enough room for the scale"); + assert(isUInt(Width) && isInt(Weight.LsbWeight)); assert(!(IsSigned && HasUnsignedPadding) && "Cannot have unsigned padding on a signed type."); } + /// Check if the Semantic follow the requirements of an older more limited + /// version of this class + bool isValidLegacySema() const { + return LsbWeight <= 0 && Width >= -LsbWeight; + } unsigned getWidth() const { return Width; } - unsigned getScale() const { return Scale; } + unsigned getScale() const { assert(isValidLegacySema()); return -LsbWeight; } + int getLsbWeight() const { return LsbWeight; } + int getMsbWeight() const { + return LsbWeight + Width - 1 /*Both lsb and msb are both part of width*/; + } bool isSigned() const { return IsSigned; } bool isSaturated() const { return IsSaturated; } bool hasUnsignedPadding() const { return HasUnsignedPadding; } void setSaturated(bool Saturated) { IsSaturated = Saturated; } + /// return true if the first bit doesn't have a strictly positive weight + bool hasSignOrPaddingBit() const { return IsSigned || HasUnsignedPadding; } + /// Return the number of integral bits represented by these semantics. These /// are separate from the fractional bits and do not include the sign or /// padding bit. unsigned getIntegralBits() const { - if (IsSigned || (!IsSigned && HasUnsignedPadding)) - return Width - Scale - 1; - else - return Width - Scale; + return std::max(getMsbWeight() + 1 - hasSignOrPaddingBit(), 0); } /// Return the FixedPointSemantics that allows for calculating the full @@ -66,6 +87,9 @@ FixedPointSemantics getCommonSemantics(const FixedPointSemantics &Other) const; + /// Print semantics for debug purposes + void print(llvm::raw_ostream& OS) const; + /// Returns true if this fixed-point semantic with its value bits interpreted /// as an integer can fit in the given floating point semantic without /// overflowing to infinity. @@ -83,20 +107,49 @@ /*HasUnsignedPadding=*/false); } + bool operator==(FixedPointSemantics Other) const { + return Width == Other.Width && LsbWeight == Other.LsbWeight && + IsSigned == Other.IsSigned && IsSaturated == Other.IsSaturated && + HasUnsignedPadding == Other.HasUnsignedPadding; + } + bool operator!=(FixedPointSemantics Other) const { return !(*this == Other); } + private: - unsigned Width : 16; - unsigned Scale : 13; + unsigned Width : WidthBitWidth; + signed int LsbWeight : LsbWeightBitWidth; unsigned IsSigned : 1; unsigned IsSaturated : 1; unsigned HasUnsignedPadding : 1; }; +static_assert(sizeof(FixedPointSemantics) == 4, ""); + +inline hash_code hash_value(const FixedPointSemantics &Val) { + return hash_value(bit_cast(Val)); +} + +template <> struct DenseMapInfo { + static inline FixedPointSemantics getEmptyKey() { + return FixedPointSemantics(0, 0, false, false, false); + } + + static inline FixedPointSemantics getTombstoneKey() { + return FixedPointSemantics(0, 1, false, false, false); + } + + static unsigned getHashValue(const FixedPointSemantics &Val) { + return hash_value(Val); + } + + static bool isEqual(const char &LHS, const char &RHS) { return LHS == RHS; } +}; + /// The APFixedPoint class works similarly to APInt/APSInt in that it is a -/// functional replacement for a scaled integer. It is meant to replicate the -/// fixed point types proposed in ISO/IEC JTC1 SC22 WG14 N1169. The class carries -/// info about the fixed point type's width, sign, scale, and saturation, and -/// provides different operations that would normally be performed on fixed point -/// types. +/// functional replacement for a scaled integer. It supports a wide range of +/// semantics including the one used by fixed point types proposed in ISO/IEC +/// JTC1 SC22 WG14 N1169. The class carries the value and semantics of +/// a fixed point, and provides different operations that would normally be +/// performed on fixed point types. class APFixedPoint { public: APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema) @@ -114,6 +167,8 @@ APSInt getValue() const { return APSInt(Val, !Sema.isSigned()); } inline unsigned getWidth() const { return Sema.getWidth(); } inline unsigned getScale() const { return Sema.getScale(); } + int getLsbWeight() const { return Sema.getLsbWeight(); } + int getMsbWeight() const { return Sema.getMsbWeight(); } inline bool isSaturated() const { return Sema.isSaturated(); } inline bool isSigned() const { return Sema.isSigned(); } inline bool hasPadding() const { return Sema.hasUnsignedPadding(); } @@ -154,10 +209,13 @@ /// Return the integral part of this fixed point number, rounded towards /// zero. (-2.5k -> -2) APSInt getIntPart() const { + if (getMsbWeight() < 0) + return APSInt(APInt::getZero(getWidth()), Val.isUnsigned()); + APSInt ExtVal = + (getLsbWeight() > 0) ? Val.extend(getWidth() + getLsbWeight()) : Val; if (Val < 0 && Val != -Val) // Cover the case when we have the min val - return -(-Val >> getScale()); - else - return Val >> getScale(); + return -(-ExtVal.relativeShl(getLsbWeight())); + return ExtVal.relativeShl(getLsbWeight()); } /// Return the integral part of this fixed point number, rounded towards @@ -179,6 +237,9 @@ return std::string(S.str()); } + void print(raw_ostream &) const; + void dump() const; + // If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1. int compare(const APFixedPoint &Other) const; bool operator==(const APFixedPoint &Other) const { @@ -232,6 +293,29 @@ return OS; } +inline hash_code hash_value(const APFixedPoint &Val) { + return hash_combine(Val.getSemantics(), Val.getValue()); +} + +template <> struct DenseMapInfo { + static inline APFixedPoint getEmptyKey() { + return APFixedPoint(DenseMapInfo::getEmptyKey()); + } + + static inline APFixedPoint getTombstoneKey() { + return APFixedPoint(DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const APFixedPoint &Val) { + return hash_value(Val); + } + + static bool isEqual(const APFixedPoint &LHS, const APFixedPoint &RHS) { + return LHS.getSemantics() == RHS.getSemantics() && + LHS.getValue() == RHS.getValue(); + } +}; + } // namespace llvm #endif Index: llvm/include/llvm/ADT/APInt.h =================================================================== --- llvm/include/llvm/ADT/APInt.h +++ llvm/include/llvm/ADT/APInt.h @@ -857,6 +857,28 @@ return R; } + /// relative logical shift right + APInt relativeLShr(int RelativeShift) const { + int Shift = std::abs(RelativeShift); + return RelativeShift > 0 ? lshr(Shift) : shl(Shift); + } + + /// relative logical shift left + APInt relativeLShl(int RelativeShift) const { + return relativeLShr(-RelativeShift); + } + + /// relative arithmetic shift right + APInt relativeAShr(int RelativeShift) const { + int Shift = std::abs(RelativeShift); + return RelativeShift > 0 ? ashr(Shift) : shl(Shift); + } + + /// relative arithmetic shift left + APInt relativeAShl(int RelativeShift) const { + return relativeAShr(-RelativeShift); + } + /// Rotate left by rotateAmt. APInt rotl(unsigned rotateAmt) const; Index: llvm/include/llvm/ADT/APSInt.h =================================================================== --- llvm/include/llvm/ADT/APSInt.h +++ llvm/include/llvm/ADT/APSInt.h @@ -144,6 +144,10 @@ ashrInPlace(Amt); return *this; } + APSInt relativeShr(unsigned Amt) const { + return IsUnsigned ? APSInt(relativeLShr(Amt), true) + : APSInt(relativeAShr(Amt), false); + } inline bool operator<(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); @@ -198,6 +202,10 @@ static_cast(*this) <<= Amt; return *this; } + APSInt relativeShl(unsigned Amt) const { + return IsUnsigned ? APSInt(relativeLShl(Amt), true) + : APSInt(relativeAShl(Amt), false); + } APSInt& operator++() { ++(static_cast(*this)); Index: llvm/lib/Support/APFixedPoint.cpp =================================================================== --- llvm/lib/Support/APFixedPoint.cpp +++ llvm/lib/Support/APFixedPoint.cpp @@ -16,25 +16,32 @@ namespace llvm { +void FixedPointSemantics::print(llvm::raw_ostream &OS) const { + OS << "width=" << getWidth() << ", "; + if (isValidLegacySema()) + OS << "scale=" << getScale() << ", "; + OS << "msb=" << getMsbWeight() << ", "; + OS << "lsb=" << getLsbWeight() << ", "; + OS << "IsSigned=" << IsSigned << ", "; + OS << "HasUnsignedPadding=" << HasUnsignedPadding << ", "; + OS << "IsSaturated=" << IsSaturated; +} + APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema, bool *Overflow) const { APSInt NewVal = Val; - unsigned DstWidth = DstSema.getWidth(); - unsigned DstScale = DstSema.getScale(); - bool Upscaling = DstScale > getScale(); + int RelativeUpscale = getLsbWeight() - DstSema.getLsbWeight(); if (Overflow) *Overflow = false; - if (Upscaling) { - NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale()); - NewVal <<= (DstScale - getScale()); - } else { - NewVal >>= (getScale() - DstScale); - } + if (RelativeUpscale > 0) + NewVal = NewVal.extend(NewVal.getBitWidth() + RelativeUpscale); + NewVal = NewVal.relativeShl(RelativeUpscale); auto Mask = APInt::getBitsSetFrom( NewVal.getBitWidth(), - std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth())); + std::min(DstSema.getIntegralBits() - DstSema.getLsbWeight(), + NewVal.getBitWidth())); APInt Masked(NewVal & Mask); // Change in the bits above the sign @@ -56,7 +63,7 @@ *Overflow = true; } - NewVal = NewVal.extOrTrunc(DstWidth); + NewVal = NewVal.extOrTrunc(DstSema.getWidth()); NewVal.setIsSigned(DstSema.isSigned()); return APFixedPoint(NewVal, DstSema); } @@ -66,21 +73,16 @@ APSInt OtherVal = Other.getValue(); bool ThisSigned = Val.isSigned(); bool OtherSigned = OtherVal.isSigned(); - unsigned OtherScale = Other.getScale(); - unsigned OtherWidth = OtherVal.getBitWidth(); - - unsigned CommonWidth = std::max(Val.getBitWidth(), OtherWidth); - // Prevent overflow in the event the widths are the same but the scales differ - CommonWidth += getScale() >= OtherScale ? getScale() - OtherScale - : OtherScale - getScale(); + int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight()); + int CommonMsb = std::max(getMsbWeight(), Other.getMsbWeight()); + unsigned CommonWidth = CommonMsb - CommonLsb + 1; ThisVal = ThisVal.extOrTrunc(CommonWidth); OtherVal = OtherVal.extOrTrunc(CommonWidth); - unsigned CommonScale = std::max(getScale(), OtherScale); - ThisVal = ThisVal.shl(CommonScale - getScale()); - OtherVal = OtherVal.shl(CommonScale - OtherScale); + ThisVal = ThisVal.shl(getLsbWeight() - CommonLsb); + OtherVal = OtherVal.shl(Other.getLsbWeight() - CommonLsb); if (ThisSigned && OtherSigned) { if (ThisVal.sgt(OtherVal)) @@ -150,9 +152,10 @@ FixedPointSemantics FixedPointSemantics::getCommonSemantics( const FixedPointSemantics &Other) const { - unsigned CommonScale = std::max(getScale(), Other.getScale()); - unsigned CommonWidth = - std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale; + int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight()); + int CommonMSb = std::max(getMsbWeight() - hasSignOrPaddingBit(), + Other.getMsbWeight() - Other.hasSignOrPaddingBit()); + unsigned CommonWidth = CommonMSb - CommonLsb + 1; bool ResultIsSigned = isSigned() || Other.isSigned(); bool ResultIsSaturated = isSaturated() || Other.isSaturated(); @@ -169,7 +172,7 @@ if (ResultIsSigned || ResultHasUnsignedPadding) CommonWidth++; - return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned, + return FixedPointSemantics(CommonWidth, Lsb{CommonLsb}, ResultIsSigned, ResultIsSaturated, ResultHasUnsignedPadding); } @@ -250,10 +253,10 @@ APSInt Result; if (CommonFXSema.isSigned()) Result = ThisVal.smul_ov(OtherVal, Overflowed) - .ashr(CommonFXSema.getScale()); + .relativeAShl(CommonFXSema.getLsbWeight()); else Result = ThisVal.umul_ov(OtherVal, Overflowed) - .lshr(CommonFXSema.getScale()); + .relativeLShl(CommonFXSema.getLsbWeight()); assert(!Overflowed && "Full multiplication cannot overflow!"); Result.setIsSigned(CommonFXSema.isSigned()); @@ -288,7 +291,10 @@ bool Overflowed = false; // Widen the LHS and RHS so we can perform a full division. - unsigned Wide = CommonFXSema.getWidth() * 2; + // Also make sure that there will be enough space for the shift below to not + // overflow + unsigned Wide = + CommonFXSema.getWidth() * 2 + std::max(-CommonFXSema.getMsbWeight(), 0); if (CommonFXSema.isSigned()) { ThisVal = ThisVal.sext(Wide); OtherVal = OtherVal.sext(Wide); @@ -299,7 +305,10 @@ // Upscale to compensate for the loss of precision from division, and // perform the full division. - ThisVal = ThisVal.shl(CommonFXSema.getScale()); + if (CommonFXSema.getLsbWeight() < 0) + ThisVal = ThisVal.shl(-CommonFXSema.getLsbWeight()); + else if (CommonFXSema.getLsbWeight() > 0) + OtherVal = OtherVal.shl(CommonFXSema.getLsbWeight()); APSInt Result; if (CommonFXSema.isSigned()) { APInt Rem; @@ -369,17 +378,30 @@ void APFixedPoint::toString(SmallVectorImpl &Str) const { APSInt Val = getValue(); - unsigned Scale = getScale(); + int Lsb = getLsbWeight(); + int OrigWidth = getWidth(); + + if (Lsb >= 0) { + APSInt IntPart = Val; + IntPart = IntPart.extend(IntPart.getBitWidth() + Lsb); + IntPart <<= Lsb; + IntPart.toString(Str, /*Radix=*/10); + Str.push_back('.'); + Str.push_back('0'); + return; + } - if (Val.isSigned() && Val.isNegative() && Val != -Val) { + if (Val.isSigned() && Val.isNegative()) { Val = -Val; + Val.setIsUnsigned(true); Str.push_back('-'); } - APSInt IntPart = Val >> Scale; + int Scale = -getLsbWeight(); + APSInt IntPart = (OrigWidth > Scale) ? (Val >> Scale) : APSInt::get(0); // Add 4 digits to hold the value after multiplying 10 (the radix) - unsigned Width = Val.getBitWidth() + 4; + unsigned Width = std::max(OrigWidth, Scale) + 4; APInt FractPart = Val.zextOrTrunc(Scale).zext(Width); APInt FractPartMask = APInt::getAllOnes(Scale).zext(Width); APInt RadixInt = APInt(Width, 10); @@ -394,6 +416,13 @@ } while (FractPart != 0); } +void APFixedPoint::print(raw_ostream &OS) const { + OS << "APFixedPoint(" << toString() << ", {"; + Sema.print(OS); + OS << "})"; +} +LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(llvm::errs()); } + APFixedPoint APFixedPoint::negate(bool *Overflow) const { if (!isSaturated()) { if (Overflow) @@ -478,7 +507,7 @@ // Scale down the integer value in the float to match the correct scaling // factor. - APFloat ScaleFactor(std::pow(2, -(int)Sema.getScale())); + APFloat ScaleFactor(std::pow(2, Sema.getLsbWeight())); bool Ignored; ScaleFactor.convert(*OpSema, LosslessRM, &Ignored); Flt.multiply(ScaleFactor, LosslessRM); @@ -533,7 +562,7 @@ // the integer range instead. Rounding mode is irrelevant here. // It is fine if this overflows to infinity even for saturating types, // since we will use floating point comparisons to check for saturation. - APFloat ScaleFactor(std::pow(2, DstFXSema.getScale())); + APFloat ScaleFactor(std::pow(2, -DstFXSema.getLsbWeight())); ScaleFactor.convert(*OpSema, LosslessRM, &Ignored); Val.multiply(ScaleFactor, LosslessRM); @@ -547,7 +576,7 @@ // we risk checking for overflow with a value that is outside the // representable range of the fixed-point semantic even though no overflow // would occur had we rounded first. - ScaleFactor = APFloat(std::pow(2, -(int)DstFXSema.getScale())); + ScaleFactor = APFloat(std::pow(2, DstFXSema.getLsbWeight())); ScaleFactor.convert(*OpSema, LosslessRM, &Ignored); Val.roundToIntegral(RM); Val.multiply(ScaleFactor, LosslessRM); Index: llvm/unittests/ADT/APFixedPointTest.cpp =================================================================== --- llvm/unittests/ADT/APFixedPointTest.cpp +++ llvm/unittests/ADT/APFixedPointTest.cpp @@ -132,6 +132,35 @@ /*hasUnsignedPadding=*/true); } +FixedPointSemantics getU8Neg10() { + return FixedPointSemantics(/*width=*/8, /*lsb=*/FixedPointSemantics::Lsb{-10}, + /*isSigned=*/false, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getS16Neg18() { + return FixedPointSemantics(/*width=*/16, + /*lsb=*/FixedPointSemantics::Lsb{-18}, + /*isSigned=*/true, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getU8Pos4() { + return FixedPointSemantics(/*width=*/8, /*lsb=*/FixedPointSemantics::Lsb{4}, + /*isSigned=*/false, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getS32Pos2() { + return FixedPointSemantics(/*width=*/32, /*lsb=*/FixedPointSemantics::Lsb{2}, + /*isSigned=*/true, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + void CheckUnpaddedMax(const FixedPointSemantics &Sema) { ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(), APSInt::getMaxValue(Sema.getWidth(), !Sema.isSigned())); @@ -160,6 +189,10 @@ CheckUnpaddedMax(getUSFractSema()); CheckUnpaddedMax(getUFractSema()); CheckUnpaddedMax(getULFractSema()); + CheckUnpaddedMax(getU8Neg10()); + CheckUnpaddedMax(getS16Neg18()); + CheckUnpaddedMax(getU8Pos4()); + CheckUnpaddedMax(getS32Pos2()); CheckPaddedMax(getPadUSAccumSema()); CheckPaddedMax(getPadUAccumSema()); @@ -182,6 +215,10 @@ CheckMin(getUSFractSema()); CheckMin(getUFractSema()); CheckMin(getULFractSema()); + CheckMin(getU8Neg10()); + CheckMin(getS16Neg18()); + CheckMin(getU8Pos4()); + CheckMin(getS32Pos2()); CheckMin(getPadUSAccumSema()); CheckMin(getPadUAccumSema()); @@ -191,30 +228,40 @@ CheckMin(getPadULFractSema()); } +int64_t relativeShr(int64_t Int, int64_t Shift) { + return (Shift > 0) ? Int >> Shift : Int << -Shift; +} + void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) { - unsigned Scale = Sema.getScale(); + int64_t FullFactPart = + (Sema.getLsbWeight() > 0) ? 0 : (1ULL << (-Sema.getLsbWeight() - 1)); // Value with a fraction - APFixedPoint ValWithFract(APInt(Sema.getWidth(), - (IntPart << Scale) + (1ULL << (Scale - 1)), - Sema.isSigned()), - Sema); + APFixedPoint ValWithFract( + APInt(Sema.getWidth(), + relativeShr(IntPart, Sema.getLsbWeight()) + FullFactPart, + Sema.isSigned()), + Sema); ASSERT_EQ(ValWithFract.getIntPart(), IntPart); // Just fraction - APFixedPoint JustFract( - APInt(Sema.getWidth(), (1ULL << (Scale - 1)), Sema.isSigned()), Sema); + APFixedPoint JustFract(APInt(Sema.getWidth(), FullFactPart, Sema.isSigned()), + Sema); ASSERT_EQ(JustFract.getIntPart(), 0); // Whole number - APFixedPoint WholeNum( - APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema); + APFixedPoint WholeNum(APInt(Sema.getWidth(), + relativeShr(IntPart, Sema.getLsbWeight()), + Sema.isSigned()), + Sema); ASSERT_EQ(WholeNum.getIntPart(), IntPart); // Negative if (Sema.isSigned()) { - APFixedPoint Negative( - APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema); + APFixedPoint Negative(APInt(Sema.getWidth(), + relativeShr(IntPart, Sema.getLsbWeight()), + Sema.isSigned()), + Sema); ASSERT_EQ(Negative.getIntPart(), IntPart); } } @@ -237,6 +284,8 @@ CheckIntPart(getUSAccumSema(), 2); CheckIntPart(getUAccumSema(), 2); CheckIntPart(getULAccumSema(), 2); + CheckIntPart(getU8Pos4(), 32); + CheckIntPart(getS32Pos2(), 32); // Zero CheckIntPart(getSAccumSema(), 0); @@ -253,6 +302,11 @@ CheckIntPart(getUFractSema(), 0); CheckIntPart(getULFractSema(), 0); + CheckIntPart(getS16Neg18(), 0); + CheckIntPart(getU8Neg10(), 0); + CheckIntPart(getU8Pos4(), 0); + CheckIntPart(getS32Pos2(), 0); + // Min CheckIntPartMin(getSAccumSema(), -256); CheckIntPartMin(getAccumSema(), -65536); @@ -262,6 +316,8 @@ CheckIntPartMin(getFractSema(), -1); CheckIntPartMin(getLFractSema(), -1); + CheckIntPartMin(getS32Pos2(), -8589934592); + // Max CheckIntPartMax(getSAccumSema(), 255); CheckIntPartMax(getAccumSema(), 65535); @@ -270,6 +326,9 @@ CheckIntPartMax(getUAccumSema(), 65535); CheckIntPartMax(getULAccumSema(), 4294967295); + CheckIntPartMax(getU8Pos4(), 255 << 4); + CheckIntPartMax(getS32Pos2(), 2147483647ull << 2); + CheckIntPartMax(getSFractSema(), 0); CheckIntPartMax(getFractSema(), 0); CheckIntPartMax(getLFractSema(), 0); @@ -312,6 +371,13 @@ APFixedPoint(5368709120, getLAccumSema())); ASSERT_EQ(APFixedPoint(0, getSAccumSema()), APFixedPoint(0, getLAccumSema())); + ASSERT_EQ(APFixedPoint(0, getS16Neg18()), APFixedPoint(0, getU8Neg10())); + ASSERT_EQ(APFixedPoint(256, getS16Neg18()), APFixedPoint(1, getU8Neg10())); + ASSERT_EQ(APFixedPoint(32512, getS16Neg18()), + APFixedPoint(127, getU8Neg10())); + ASSERT_EQ(APFixedPoint(4, getS32Pos2()), APFixedPoint(1, getU8Pos4())); + ASSERT_EQ(APFixedPoint(1020, getS32Pos2()), APFixedPoint(255, getU8Pos4())); + // Across types (0.5) ASSERT_EQ(APFixedPoint(64, getSAccumSema()), APFixedPoint(64, getSFractSema())); @@ -351,6 +417,8 @@ ASSERT_LT(APFixedPoint(0, getUSAccumSema()), APFixedPoint(1, getAccumSema())); ASSERT_LT(APFixedPoint(0, getUSAccumSema()), APFixedPoint(1, getUAccumSema())); + ASSERT_LT(APFixedPoint(65280, getS16Neg18()), + APFixedPoint(255, getU8Neg10())); // Greater than ASSERT_GT(APFixedPoint(0, getAccumSema()), APFixedPoint(-1, getSAccumSema())); @@ -371,10 +439,10 @@ if (IsNegative) ScaledVal = -ScaledVal; - if (Dst.getScale() > Src.getScale()) { - ScaledVal <<= (Dst.getScale() - Src.getScale()); + if (Dst.getLsbWeight() < Src.getLsbWeight()) { + ScaledVal <<= (Src.getLsbWeight() - Dst.getLsbWeight()); } else { - ScaledVal >>= (Src.getScale() - Dst.getScale()); + ScaledVal >>= (Dst.getLsbWeight() - Src.getLsbWeight()); } if (IsNegative) @@ -672,6 +740,26 @@ ASSERT_EQ(Ovf, false); } +TEST(FixedPoint, toString) { + ASSERT_EQ(APFixedPoint::getMax(getS16Neg18()).toString(), + "0.124996185302734375"); + ASSERT_EQ(APFixedPoint::getMin(getS16Neg18()) + .add(APFixedPoint(1, getS16Neg18())) + .toString(), + "-0.124996185302734375"); + ASSERT_EQ(APFixedPoint::getMin(getS16Neg18()).toString(), "-0.125"); + ASSERT_EQ(APFixedPoint::getMax(getU8Neg10()).toString(), "0.2490234375"); + ASSERT_EQ(APFixedPoint::getMin(getU8Neg10()).toString(), "0.0"); + ASSERT_EQ(APFixedPoint::getMax(getS32Pos2()).toString(), "8589934588.0"); + ASSERT_EQ(APFixedPoint::getMin(getS32Pos2()) + .add(APFixedPoint(1, getS32Pos2())) + .toString(), + "-8589934588.0"); + ASSERT_EQ(APFixedPoint::getMin(getS32Pos2()).toString(), "-8589934592.0"); + ASSERT_EQ(APFixedPoint::getMax(getU8Pos4()).toString(), "4080.0"); + ASSERT_EQ(APFixedPoint::getMin(getU8Pos4()).toString(), "0.0"); +} + TEST(FixedPoint, FloatToFixed) { APFloat Val(0.0f); @@ -693,6 +781,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), 3ULL << 14); CheckFloatToFixedConversion(Val, getULFractSema(), 3ULL << 30); + CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); + // Simple negative exact fraction Val = APFloat(-0.75f); CheckFloatToFixedConversion(Val, getSAccumSema(), -3ULL << 5); @@ -711,6 +804,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), MinSat); CheckFloatToFixedConversion(Val, getULFractSema(), MinSat); + CheckFloatToFixedConversion(Val, getU8Neg10(), MinSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), MinSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); + // Highly precise fraction Val = APFloat(0.999999940395355224609375f); CheckFloatToFixedConversion(Val, getSAccumSema(), 0x7FULL); @@ -729,6 +827,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), 0xFFFFULL); CheckFloatToFixedConversion(Val, getULFractSema(), 0xFFFFFFULL << 8); + CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); + // Integral and fraction Val = APFloat(17.99609375f); CheckFloatToFixedConversion(Val, getSAccumSema(), 0x11FFULL >> 1); @@ -747,6 +850,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), MaxSat); CheckFloatToFixedConversion(Val, getULFractSema(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), 1); + CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), 1 << 2); + // Negative integral and fraction Val = APFloat(-17.99609375f); CheckFloatToFixedConversion(Val, getSAccumSema(), -0x11FELL >> 1); @@ -765,6 +873,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), MinSat); CheckFloatToFixedConversion(Val, getULFractSema(), MinSat); + CheckFloatToFixedConversion(Val, getU8Neg10(), MinSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), MinSat); + CheckFloatToFixedConversion(Val, getS16Neg18(), MinSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), -4); + // Very large value Val = APFloat(1.0e38f); CheckFloatToFixedConversion(Val, getSAccumSema(), MaxSat); @@ -783,6 +896,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), MaxSat); CheckFloatToFixedConversion(Val, getULFractSema(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), MaxSat); + CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), MaxSat); + // Very small value Val = APFloat(1.0e-38f); CheckFloatToFixedConversion(Val, getSAccumSema(), 0); @@ -801,6 +919,11 @@ CheckFloatToFixedConversion(Val, getUFractSema(), 0); CheckFloatToFixedConversion(Val, getULFractSema(), 0); + CheckFloatToFixedConversion(Val, getU8Neg10(), 0); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), 0); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); + // Half conversion Val = APFloat(0.99951171875f); bool Ignored; @@ -821,6 +944,23 @@ CheckFloatToFixedConversion(Val, getUSFractSema(), 0xFFULL); CheckFloatToFixedConversion(Val, getUFractSema(), 0xFFEULL << 4); CheckFloatToFixedConversion(Val, getULFractSema(), 0xFFEULL << 20); + + CheckFloatToFixedConversion(Val, getU8Neg10(), MaxSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), MaxSat); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); + + Val = APFloat(0.124996185302734375); + CheckFloatToFixedConversion(Val, getU8Neg10(), 0x7f); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), 0x7fff); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); + + Val = APFloat(-0.124996185302734375); + CheckFloatToFixedConversion(Val, getU8Neg10(), MinSat); + CheckFloatToFixedConversion(Val, getU8Pos4(), 0); + CheckFloatToFixedConversion(Val, getS16Neg18(), -0x7fff); + CheckFloatToFixedConversion(Val, getS32Pos2(), 0); } void CheckFixedToFloatConversion(int64_t Val, const FixedPointSemantics &Sema, @@ -853,6 +993,11 @@ CheckFixedToFloatConversion(Val, getULFractSema(), 0.00000000023283064365386962890625f); + CheckFixedToFloatConversion(Val, getU8Neg10(), 0.0009765625f); + CheckFixedToFloatConversion(Val, getU8Pos4(), 16.0f); + CheckFixedToFloatConversion(Val, getS16Neg18(), 0.000003814697265625f); + CheckFixedToFloatConversion(Val, getS32Pos2(), 4.0f); + Val = 0x7FULL; CheckFixedToFloatConversion(Val, getSAccumSema(), 0.9921875f); CheckFixedToFloatConversion(Val, getFractSema(), 0.003875732421875f); @@ -866,6 +1011,11 @@ CheckFixedToFloatConversion(Val, getULFractSema(), 0.00000002956949174404144287109375f); + CheckFixedToFloatConversion(Val, getU8Neg10(), 0.1240234375f); + CheckFixedToFloatConversion(Val, getU8Pos4(), 2032.0f); + CheckFixedToFloatConversion(Val, getS16Neg18(), 0.000484466552734375f); + CheckFixedToFloatConversion(Val, getS32Pos2(), 508.0f); + Val = -0x1ULL; CheckFixedToFloatConversion(Val, getSAccumSema(), -0.0078125f); CheckFixedToFloatConversion(Val, getFractSema(), -0.000030517578125f); @@ -873,6 +1023,10 @@ CheckFixedToFloatConversion(Val, getLFractSema(), -0.0000000004656612873077392578125f); + CheckFixedToFloatConversion(Val, getU8Neg10(), 0.249023437f); + CheckFixedToFloatConversion(Val, getU8Pos4(), 4080.0f); + CheckFixedToFloatConversion(Val, getS16Neg18(), -0.000003814697265625f); + CheckFixedToFloatConversion(Val, getS32Pos2(), -4.0f); CheckFixedToFloatConversion(-0x80ULL, getSAccumSema(), -1.0f); CheckFixedToFloatConversion(-0x8000ULL, getFractSema(), -1.0f); @@ -892,6 +1046,9 @@ CheckFixedToFloatConversion(Val, getULFractSema(), 0.0000006542541086673736572265625f); + CheckFixedToFloatConversion(Val, getS16Neg18(), 0.01071929931640625f); + CheckFixedToFloatConversion(Val, getS32Pos2(), 11240.0f); + Val = -0xAFAULL; CheckFixedToFloatConversion(Val, getSAccumSema(), -21.953125f); CheckFixedToFloatConversion(Val, getFractSema(), -0.08575439453125f); @@ -899,6 +1056,9 @@ CheckFixedToFloatConversion(Val, getLFractSema(), -0.000001308508217334747314453125f); + CheckFixedToFloatConversion(Val, getS16Neg18(), -0.01071929931640625f); + CheckFixedToFloatConversion(Val, getS32Pos2(), -11240.0f); + Val = 0x40000080ULL; CheckFixedToFloatConversion(Val, getAccumSema(), 32768.00390625f); CheckFixedToFloatConversion(Val, getLFractSema(), @@ -908,6 +1068,8 @@ CheckFixedToFloatConversion(Val, getULFractSema(), 0.2500000298023223876953125f); + CheckFixedToFloatConversion(Val, getS32Pos2(), 4294967808.0f); + Val = 0x40000040ULL; CheckFixedToFloatConversion(Val, getAccumSema(), 32768.0f); CheckFixedToFloatConversion(Val, getLFractSema(), 0.5f); @@ -915,12 +1077,189 @@ CheckFixedToFloatConversion(Val, getUAccumSema(), 16384.0f); CheckFixedToFloatConversion(Val, getULFractSema(), 0.25f); + CheckFixedToFloatConversion(Val, getS32Pos2(), 4294967552.0f); + Val = 0x7FF0ULL; CheckFixedToHalfConversion(Val, getAccumSema(), 0.99951171875f); CheckFixedToHalfConversion(Val, getLFractSema(), 0.000015251338481903076171875f); CheckFixedToHalfConversion(Val, getUAccumSema(), 0.499755859375f); CheckFixedToHalfConversion(Val, getULFractSema(), 0.0000076256692409515380859375f); + + CheckFixedToFloatConversion(Val, getS32Pos2(), 131008.0f); +} + +void CheckAdd(const APFixedPoint &Lhs, const APFixedPoint &Rhs, + const APFixedPoint &Res) { + bool Overflow = false; + APFixedPoint Result = Lhs.add(Rhs, &Overflow); + ASSERT_FALSE(Overflow); + ASSERT_EQ(Result.getSemantics(), Res.getSemantics()); + ASSERT_EQ(Result, Res); +} + +void CheckAddOverflow(const APFixedPoint &Lhs, const APFixedPoint &Rhs) { + bool Overflow = false; + APFixedPoint Result = Lhs.add(Rhs, &Overflow); + ASSERT_TRUE(Overflow); +} + +TEST(FixedPoint, add) { + CheckAdd(APFixedPoint(1, getS32Pos2()), APFixedPoint(1, getS32Pos2()), + APFixedPoint(2, getS32Pos2())); + CheckAdd(APFixedPoint(1, getS16Neg18()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(2, getS16Neg18())); + CheckAdd(APFixedPoint(1, getU8Neg10()), APFixedPoint(1, getU8Neg10()), + APFixedPoint(2, getU8Neg10())); + CheckAdd(APFixedPoint(1, getU8Pos4()), APFixedPoint(1, getU8Pos4()), + APFixedPoint(2, getU8Pos4())); + + CheckAdd(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS32Pos2()), + APFixedPoint(12, getS32Pos2())); + CheckAdd(APFixedPoint(11, getS16Neg18()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(12, getS16Neg18())); + CheckAdd(APFixedPoint(11, getU8Neg10()), APFixedPoint(1, getU8Neg10()), + APFixedPoint(12, getU8Neg10())); + CheckAdd(APFixedPoint(11, getU8Pos4()), APFixedPoint(1, getU8Pos4()), + APFixedPoint(12, getU8Pos4())); + + CheckAdd(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(11534337, + FixedPointSemantics(52, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckAdd( + APFixedPoint(11, getU8Neg10()), APFixedPoint(-9472, getS16Neg18()), + APFixedPoint(-6656, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckAddOverflow( + APFixedPoint::getMax(getU8Neg10()), APFixedPoint::getMax(getS16Neg18())); + CheckAdd(APFixedPoint::getMin(getU8Neg10()), + APFixedPoint::getMin(getS16Neg18()), + APFixedPoint::getMin(getS16Neg18()) + .convert(FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckAddOverflow(APFixedPoint::getMin(getS32Pos2()), + APFixedPoint::getMin(getS16Neg18())); +} + +void CheckMul(const APFixedPoint &Lhs, const APFixedPoint &Rhs, + const APFixedPoint &Res) { + bool Overflow = false; + APFixedPoint Result = Lhs.mul(Rhs, &Overflow); + ASSERT_FALSE(Overflow); + ASSERT_EQ(Result.getSemantics(), Res.getSemantics()); + ASSERT_EQ(Result, Res); +} + +void CheckMulOverflow(const APFixedPoint &Lhs, const APFixedPoint &Rhs) { + bool Overflow = false; + APFixedPoint Result = Lhs.mul(Rhs, &Overflow); + ASSERT_TRUE(Overflow); +} + +TEST(FixedPoint, mul) { + CheckMul(APFixedPoint(1, getS32Pos2()), APFixedPoint(1, getS32Pos2()), + APFixedPoint(4, getS32Pos2())); + CheckMul(APFixedPoint(1, getS16Neg18()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(0, getS16Neg18())); + CheckMul(APFixedPoint(1, getU8Neg10()), APFixedPoint(1, getU8Neg10()), + APFixedPoint(0, getU8Neg10())); + CheckMul(APFixedPoint(1, getU8Pos4()), APFixedPoint(1, getU8Pos4()), + APFixedPoint(16, getU8Pos4())); + + CheckMul(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS32Pos2()), + APFixedPoint(44, getS32Pos2())); + CheckMul(APFixedPoint(11, getS16Neg18()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(0, getS16Neg18())); + CheckMul(APFixedPoint(11, getU8Neg10()), APFixedPoint(1, getU8Neg10()), + APFixedPoint(0, getU8Neg10())); + CheckMul(APFixedPoint(11, getU8Pos4()), APFixedPoint(1, getU8Pos4()), + APFixedPoint(176, getU8Pos4())); + + CheckMul(APFixedPoint(512, getS16Neg18()), APFixedPoint(512, getS16Neg18()), + APFixedPoint(1, getS16Neg18())); + CheckMul(APFixedPoint(32, getU8Neg10()), APFixedPoint(32, getU8Neg10()), + APFixedPoint(1, getU8Neg10())); + + CheckMul(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(44, + FixedPointSemantics(52, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckMul( + APFixedPoint(11, getU8Neg10()), APFixedPoint(-9472, getS16Neg18()), + APFixedPoint(-102, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckMul( + APFixedPoint::getMax(getU8Neg10()), APFixedPoint::getMax(getS16Neg18()), + APFixedPoint(8159, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckMul( + APFixedPoint::getMin(getU8Neg10()), APFixedPoint::getMin(getS16Neg18()), + APFixedPoint(0, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckMul(APFixedPoint::getMin(getS32Pos2()), + APFixedPoint::getMin(getS16Neg18()), + APFixedPoint(281474976710656, + FixedPointSemantics(52, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckMulOverflow(APFixedPoint::getMax(getS32Pos2()), APFixedPoint::getMax(getU8Pos4())); + CheckMulOverflow(APFixedPoint::getMin(getS32Pos2()), APFixedPoint::getMax(getU8Pos4())); +} + +void CheckDiv(const APFixedPoint &Lhs, const APFixedPoint &Rhs, + const APFixedPoint &Expected) { + bool Overflow = false; + APFixedPoint Result = Lhs.div(Rhs, &Overflow); + ASSERT_FALSE(Overflow); + ASSERT_EQ(Result.getSemantics(), Expected.getSemantics()); + ASSERT_EQ(Result, Expected); +} + +void CheckDivOverflow(const APFixedPoint &Lhs, const APFixedPoint &Rhs) { + bool Overflow = false; + APFixedPoint Result = Lhs.div(Rhs, &Overflow); + ASSERT_TRUE(Overflow); +} + +TEST(FixedPoint, div) { + CheckDiv(APFixedPoint(1, getS32Pos2()), APFixedPoint(1, getS32Pos2()), + APFixedPoint(0, getS32Pos2())); + CheckDivOverflow(APFixedPoint(1, getS16Neg18()), APFixedPoint(1, getS16Neg18())); + CheckDivOverflow(APFixedPoint(1, getU8Neg10()), APFixedPoint(1, getU8Neg10())); + CheckDiv(APFixedPoint(1, getU8Pos4()), APFixedPoint(1, getU8Pos4()), + APFixedPoint(0, getU8Pos4())); + + CheckDiv(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS32Pos2()), + APFixedPoint(2, getS32Pos2())); + CheckDiv(APFixedPoint(11, getU8Pos4()), APFixedPoint(1, getU8Pos4()), + APFixedPoint(0, getU8Pos4())); + + CheckDiv(APFixedPoint(11, getS32Pos2()), APFixedPoint(1, getS16Neg18()), + APFixedPoint(3023656976384, + FixedPointSemantics(52, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckDiv(APFixedPoint(11, getU8Neg10()), APFixedPoint(-11264, getS16Neg18()), + APFixedPoint::getMin(FixedPointSemantics( + 17, FixedPointSemantics::Lsb{-18}, true, false, false))); + CheckDiv(APFixedPoint(11, getU8Neg10()), APFixedPoint(11265, getS16Neg18()), + APFixedPoint(0xfffa, + FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckDivOverflow(APFixedPoint(11, getU8Neg10()), + APFixedPoint(11264, getS16Neg18())); + + CheckDivOverflow(APFixedPoint(11, getU8Neg10()), + APFixedPoint(-9472, getS16Neg18())); + CheckDivOverflow(APFixedPoint::getMax(getU8Neg10()), + APFixedPoint::getMax(getS16Neg18())); + CheckDiv( + APFixedPoint::getMin(getU8Neg10()), APFixedPoint::getMin(getS16Neg18()), + APFixedPoint(0, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); + CheckDiv( + APFixedPoint(1, getU8Neg10()), APFixedPoint::getMin(getS16Neg18()), + APFixedPoint(-2048, FixedPointSemantics(17, FixedPointSemantics::Lsb{-18}, + true, false, false))); } } // namespace