[llvm] 1654b22 - [ADT] Add support for more formats in APFixedPoint

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 6 08:55:51 PDT 2022


Author: Tyker
Date: 2022-10-06T17:55:31+02:00
New Revision: 1654b22ac048010a6e045dbe69b530fe7cd54fcc

URL: https://github.com/llvm/llvm-project/commit/1654b22ac048010a6e045dbe69b530fe7cd54fcc
DIFF: https://github.com/llvm/llvm-project/commit/1654b22ac048010a6e045dbe69b530fe7cd54fcc.diff

LOG: [ADT] Add support for more formats in APFixedPoint

Prior to this patch FixedPointSemantics and APFixedPoint only support semantics where
the Scale larger or equal to zero and the Width is larger or equal to the Scale.
This patch removes both those requirements while staying API compatible.

Added: 
    

Modified: 
    llvm/include/llvm/ADT/APFixedPoint.h
    llvm/include/llvm/ADT/APInt.h
    llvm/include/llvm/ADT/APSInt.h
    llvm/lib/Support/APFixedPoint.cpp
    llvm/unittests/ADT/APFixedPointTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/APFixedPoint.h b/llvm/include/llvm/ADT/APFixedPoint.h
index 92cabdd9f9e4..aa1a7bfed6b5 100644
--- a/llvm/include/llvm/ADT/APFixedPoint.h
+++ b/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 @@ struct fltSemantics;
 /// in the value this represents is treated as padding.
 class FixedPointSemantics {
 public:
+  static constexpr unsigned WidthBitWidth = 16;
+  static constexpr unsigned LsbWeightBitWidth = 13;
+  /// Used to 
diff erentiate 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<int>(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<WidthBitWidth>(Width) && isInt<LsbWeightBitWidth>(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 @@ class FixedPointSemantics {
   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 @@ class FixedPointSemantics {
                                /*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<uint32_t>(Val));
+}
+
+template <> struct DenseMapInfo<FixedPointSemantics> {
+  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 
diff erent 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 
diff erent operations that would normally be
+/// performed on fixed point types.
 class APFixedPoint {
 public:
   APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema)
@@ -114,6 +167,8 @@ class APFixedPoint {
   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 @@ class APFixedPoint {
   /// 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 @@ class APFixedPoint {
     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 @@ inline raw_ostream &operator<<(raw_ostream &OS, const APFixedPoint &FX) {
   return OS;
 }
 
+inline hash_code hash_value(const APFixedPoint &Val) {
+  return hash_combine(Val.getSemantics(), Val.getValue());
+}
+
+template <> struct DenseMapInfo<APFixedPoint> {
+  static inline APFixedPoint getEmptyKey() {
+    return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getEmptyKey());
+  }
+
+  static inline APFixedPoint getTombstoneKey() {
+    return APFixedPoint(DenseMapInfo<FixedPointSemantics>::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

diff  --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 9d371928e636..302fdd395550 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -19,6 +19,7 @@
 #include "llvm/Support/MathExtras.h"
 #include <cassert>
 #include <climits>
+#include <cmath>
 #include <cstring>
 #include <utility>
 
@@ -857,6 +858,28 @@ class [[nodiscard]] APInt {
     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;
 

diff  --git a/llvm/include/llvm/ADT/APSInt.h b/llvm/include/llvm/ADT/APSInt.h
index bf7454c41843..c9c2118716fb 100644
--- a/llvm/include/llvm/ADT/APSInt.h
+++ b/llvm/include/llvm/ADT/APSInt.h
@@ -144,6 +144,10 @@ class [[nodiscard]] APSInt : public APInt {
       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 @@ class [[nodiscard]] APSInt : public APInt {
     static_cast<APInt&>(*this) <<= Amt;
     return *this;
   }
+  APSInt relativeShl(unsigned Amt) const {
+    return IsUnsigned ? APSInt(relativeLShl(Amt), true)
+                      : APSInt(relativeAShl(Amt), false);
+  }
 
   APSInt& operator++() {
     ++(static_cast<APInt&>(*this));

diff  --git a/llvm/lib/Support/APFixedPoint.cpp b/llvm/lib/Support/APFixedPoint.cpp
index 461a389b6c88..3eea01bc9809 100644
--- a/llvm/lib/Support/APFixedPoint.cpp
+++ b/llvm/lib/Support/APFixedPoint.cpp
@@ -18,25 +18,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
@@ -58,7 +65,7 @@ APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,
       *Overflow = true;
   }
 
-  NewVal = NewVal.extOrTrunc(DstWidth);
+  NewVal = NewVal.extOrTrunc(DstSema.getWidth());
   NewVal.setIsSigned(DstSema.isSigned());
   return APFixedPoint(NewVal, DstSema);
 }
@@ -68,21 +75,16 @@ int APFixedPoint::compare(const APFixedPoint &Other) const {
   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 
diff er
-  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))
@@ -152,9 +154,10 @@ bool FixedPointSemantics::fitsInFloatSemantics(
 
 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();
@@ -171,7 +174,7 @@ FixedPointSemantics FixedPointSemantics::getCommonSemantics(
   if (ResultIsSigned || ResultHasUnsignedPadding)
     CommonWidth++;
 
-  return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
+  return FixedPointSemantics(CommonWidth, Lsb{CommonLsb}, ResultIsSigned,
                              ResultIsSaturated, ResultHasUnsignedPadding);
 }
 
@@ -252,10 +255,10 @@ APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,
   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());
 
@@ -290,7 +293,10 @@ APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
   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);
@@ -301,7 +307,10 @@ APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
 
   // 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;
@@ -371,17 +380,30 @@ APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {
 
 void APFixedPoint::toString(SmallVectorImpl<char> &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);
@@ -396,6 +418,13 @@ void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {
   } 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)
@@ -480,7 +509,7 @@ APFloat APFixedPoint::convertToFloat(const fltSemantics &FloatSema) const {
 
   // 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);
@@ -535,7 +564,7 @@ APFixedPoint::getFromFloatValue(const APFloat &Value,
   // 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);
 
@@ -549,7 +578,7 @@ APFixedPoint::getFromFloatValue(const APFloat &Value,
   // 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);

diff  --git a/llvm/unittests/ADT/APFixedPointTest.cpp b/llvm/unittests/ADT/APFixedPointTest.cpp
index f54df99446d8..0b85db531f73 100644
--- a/llvm/unittests/ADT/APFixedPointTest.cpp
+++ b/llvm/unittests/ADT/APFixedPointTest.cpp
@@ -132,6 +132,35 @@ FixedPointSemantics getPadULFractSema() {
                              /*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 @@ TEST(FixedPointTest, getMax) {
   CheckUnpaddedMax(getUSFractSema());
   CheckUnpaddedMax(getUFractSema());
   CheckUnpaddedMax(getULFractSema());
+  CheckUnpaddedMax(getU8Neg10());
+  CheckUnpaddedMax(getS16Neg18());
+  CheckUnpaddedMax(getU8Pos4());
+  CheckUnpaddedMax(getS32Pos2());
 
   CheckPaddedMax(getPadUSAccumSema());
   CheckPaddedMax(getPadUAccumSema());
@@ -182,6 +215,10 @@ TEST(FixedPointTest, getMin) {
   CheckMin(getUSFractSema());
   CheckMin(getUFractSema());
   CheckMin(getULFractSema());
+  CheckMin(getU8Neg10());
+  CheckMin(getS16Neg18());
+  CheckMin(getU8Pos4());
+  CheckMin(getS32Pos2());
 
   CheckMin(getPadUSAccumSema());
   CheckMin(getPadUAccumSema());
@@ -191,30 +228,40 @@ TEST(FixedPointTest, getMin) {
   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 @@ TEST(FixedPoint, getIntPart) {
   CheckIntPart(getUSAccumSema(), 2);
   CheckIntPart(getUAccumSema(), 2);
   CheckIntPart(getULAccumSema(), 2);
+  CheckIntPart(getU8Pos4(), 32);
+  CheckIntPart(getS32Pos2(), 32);
 
   // Zero
   CheckIntPart(getSAccumSema(), 0);
@@ -253,6 +302,11 @@ TEST(FixedPoint, getIntPart) {
   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 @@ TEST(FixedPoint, getIntPart) {
   CheckIntPartMin(getFractSema(), -1);
   CheckIntPartMin(getLFractSema(), -1);
 
+  CheckIntPartMin(getS32Pos2(), -8589934592);
+
   // Max
   CheckIntPartMax(getSAccumSema(), 255);
   CheckIntPartMax(getAccumSema(), 65535);
@@ -270,6 +326,9 @@ TEST(FixedPoint, getIntPart) {
   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 @@ TEST(FixedPoint, compare) {
             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 @@ TEST(FixedPoint, compare) {
   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 @@ void CheckUnsaturatedConversion(FixedPointSemantics Src,
   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 @@ void CheckFloatToFixedConversion(APFloat &Val, const FixedPointSemantics &Sema,
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FloatToFixed) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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 @@ TEST(FixedPoint, FixedToFloat) {
   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


        


More information about the llvm-commits mailing list