xref: /llvm-project/llvm/lib/Support/APFixedPoint.cpp (revision a579782a775ebc2bfe6203d7178ee524b3559006)
11a995a0aSBevin Hansson //===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//
21a995a0aSBevin Hansson //
31a995a0aSBevin Hansson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41a995a0aSBevin Hansson // See https://llvm.org/LICENSE.txt for license information.
51a995a0aSBevin Hansson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61a995a0aSBevin Hansson //
71a995a0aSBevin Hansson //===----------------------------------------------------------------------===//
81a995a0aSBevin Hansson //
91a995a0aSBevin Hansson /// \file
101a995a0aSBevin Hansson /// Defines the implementation for the fixed point number interface.
111a995a0aSBevin Hansson //
121a995a0aSBevin Hansson //===----------------------------------------------------------------------===//
131a995a0aSBevin Hansson 
141a995a0aSBevin Hansson #include "llvm/ADT/APFixedPoint.h"
15dd3014f3SBevin Hansson #include "llvm/ADT/APFloat.h"
161a995a0aSBevin Hansson 
1716544cbeSserge-sans-paille #include <cmath>
1816544cbeSserge-sans-paille 
191a995a0aSBevin Hansson namespace llvm {
201a995a0aSBevin Hansson 
211654b22aSTyker void FixedPointSemantics::print(llvm::raw_ostream &OS) const {
221654b22aSTyker   OS << "width=" << getWidth() << ", ";
231654b22aSTyker   if (isValidLegacySema())
241654b22aSTyker     OS << "scale=" << getScale() << ", ";
251654b22aSTyker   OS << "msb=" << getMsbWeight() << ", ";
261654b22aSTyker   OS << "lsb=" << getLsbWeight() << ", ";
271654b22aSTyker   OS << "IsSigned=" << IsSigned << ", ";
281654b22aSTyker   OS << "HasUnsignedPadding=" << HasUnsignedPadding << ", ";
291654b22aSTyker   OS << "IsSaturated=" << IsSaturated;
301654b22aSTyker }
311654b22aSTyker 
32*a579782aSTimm Baeder uint32_t FixedPointSemantics::toOpaqueInt() const {
33*a579782aSTimm Baeder   return llvm::bit_cast<uint32_t>(*this);
34*a579782aSTimm Baeder }
35*a579782aSTimm Baeder 
36*a579782aSTimm Baeder FixedPointSemantics FixedPointSemantics::getFromOpaqueInt(uint32_t I) {
37*a579782aSTimm Baeder   FixedPointSemantics F(0, 0, false, false, false);
38*a579782aSTimm Baeder   std::memcpy(&F, &I, sizeof(F));
39*a579782aSTimm Baeder   return F;
40*a579782aSTimm Baeder }
41*a579782aSTimm Baeder 
421a995a0aSBevin Hansson APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,
431a995a0aSBevin Hansson                                    bool *Overflow) const {
441a995a0aSBevin Hansson   APSInt NewVal = Val;
451654b22aSTyker   int RelativeUpscale = getLsbWeight() - DstSema.getLsbWeight();
461a995a0aSBevin Hansson   if (Overflow)
471a995a0aSBevin Hansson     *Overflow = false;
481a995a0aSBevin Hansson 
491654b22aSTyker   if (RelativeUpscale > 0)
501654b22aSTyker     NewVal = NewVal.extend(NewVal.getBitWidth() + RelativeUpscale);
511654b22aSTyker   NewVal = NewVal.relativeShl(RelativeUpscale);
521a995a0aSBevin Hansson 
531a995a0aSBevin Hansson   auto Mask = APInt::getBitsSetFrom(
541a995a0aSBevin Hansson       NewVal.getBitWidth(),
551654b22aSTyker       std::min(DstSema.getIntegralBits() - DstSema.getLsbWeight(),
561654b22aSTyker                NewVal.getBitWidth()));
571a995a0aSBevin Hansson   APInt Masked(NewVal & Mask);
581a995a0aSBevin Hansson 
591a995a0aSBevin Hansson   // Change in the bits above the sign
601a995a0aSBevin Hansson   if (!(Masked == Mask || Masked == 0)) {
611a995a0aSBevin Hansson     // Found overflow in the bits above the sign
621a995a0aSBevin Hansson     if (DstSema.isSaturated())
631a995a0aSBevin Hansson       NewVal = NewVal.isNegative() ? Mask : ~Mask;
641a995a0aSBevin Hansson     else if (Overflow)
651a995a0aSBevin Hansson       *Overflow = true;
661a995a0aSBevin Hansson   }
671a995a0aSBevin Hansson 
681a995a0aSBevin Hansson   // If the dst semantics are unsigned, but our value is signed and negative, we
691a995a0aSBevin Hansson   // clamp to zero.
701a995a0aSBevin Hansson   if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) {
711a995a0aSBevin Hansson     // Found negative overflow for unsigned result
721a995a0aSBevin Hansson     if (DstSema.isSaturated())
731a995a0aSBevin Hansson       NewVal = 0;
741a995a0aSBevin Hansson     else if (Overflow)
751a995a0aSBevin Hansson       *Overflow = true;
761a995a0aSBevin Hansson   }
771a995a0aSBevin Hansson 
781654b22aSTyker   NewVal = NewVal.extOrTrunc(DstSema.getWidth());
791a995a0aSBevin Hansson   NewVal.setIsSigned(DstSema.isSigned());
801a995a0aSBevin Hansson   return APFixedPoint(NewVal, DstSema);
811a995a0aSBevin Hansson }
821a995a0aSBevin Hansson 
831a995a0aSBevin Hansson int APFixedPoint::compare(const APFixedPoint &Other) const {
841a995a0aSBevin Hansson   APSInt ThisVal = getValue();
851a995a0aSBevin Hansson   APSInt OtherVal = Other.getValue();
861a995a0aSBevin Hansson   bool ThisSigned = Val.isSigned();
871a995a0aSBevin Hansson   bool OtherSigned = OtherVal.isSigned();
881a995a0aSBevin Hansson 
891654b22aSTyker   int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight());
901654b22aSTyker   int CommonMsb = std::max(getMsbWeight(), Other.getMsbWeight());
911654b22aSTyker   unsigned CommonWidth = CommonMsb - CommonLsb + 1;
921a995a0aSBevin Hansson 
931a995a0aSBevin Hansson   ThisVal = ThisVal.extOrTrunc(CommonWidth);
941a995a0aSBevin Hansson   OtherVal = OtherVal.extOrTrunc(CommonWidth);
951a995a0aSBevin Hansson 
961654b22aSTyker   ThisVal = ThisVal.shl(getLsbWeight() - CommonLsb);
971654b22aSTyker   OtherVal = OtherVal.shl(Other.getLsbWeight() - CommonLsb);
981a995a0aSBevin Hansson 
991a995a0aSBevin Hansson   if (ThisSigned && OtherSigned) {
1001a995a0aSBevin Hansson     if (ThisVal.sgt(OtherVal))
1011a995a0aSBevin Hansson       return 1;
1021a995a0aSBevin Hansson     else if (ThisVal.slt(OtherVal))
1031a995a0aSBevin Hansson       return -1;
1041a995a0aSBevin Hansson   } else if (!ThisSigned && !OtherSigned) {
1051a995a0aSBevin Hansson     if (ThisVal.ugt(OtherVal))
1061a995a0aSBevin Hansson       return 1;
1071a995a0aSBevin Hansson     else if (ThisVal.ult(OtherVal))
1081a995a0aSBevin Hansson       return -1;
1091a995a0aSBevin Hansson   } else if (ThisSigned && !OtherSigned) {
1101a995a0aSBevin Hansson     if (ThisVal.isSignBitSet())
1111a995a0aSBevin Hansson       return -1;
1121a995a0aSBevin Hansson     else if (ThisVal.ugt(OtherVal))
1131a995a0aSBevin Hansson       return 1;
1141a995a0aSBevin Hansson     else if (ThisVal.ult(OtherVal))
1151a995a0aSBevin Hansson       return -1;
1161a995a0aSBevin Hansson   } else {
1171a995a0aSBevin Hansson     // !ThisSigned && OtherSigned
1181a995a0aSBevin Hansson     if (OtherVal.isSignBitSet())
1191a995a0aSBevin Hansson       return 1;
1201a995a0aSBevin Hansson     else if (ThisVal.ugt(OtherVal))
1211a995a0aSBevin Hansson       return 1;
1221a995a0aSBevin Hansson     else if (ThisVal.ult(OtherVal))
1231a995a0aSBevin Hansson       return -1;
1241a995a0aSBevin Hansson   }
1251a995a0aSBevin Hansson 
1261a995a0aSBevin Hansson   return 0;
1271a995a0aSBevin Hansson }
1281a995a0aSBevin Hansson 
1291a995a0aSBevin Hansson APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) {
1301a995a0aSBevin Hansson   bool IsUnsigned = !Sema.isSigned();
1311a995a0aSBevin Hansson   auto Val = APSInt::getMaxValue(Sema.getWidth(), IsUnsigned);
1321a995a0aSBevin Hansson   if (IsUnsigned && Sema.hasUnsignedPadding())
1331a995a0aSBevin Hansson     Val = Val.lshr(1);
1341a995a0aSBevin Hansson   return APFixedPoint(Val, Sema);
1351a995a0aSBevin Hansson }
1361a995a0aSBevin Hansson 
1371a995a0aSBevin Hansson APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
1381a995a0aSBevin Hansson   auto Val = APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned());
1391a995a0aSBevin Hansson   return APFixedPoint(Val, Sema);
1401a995a0aSBevin Hansson }
1411a995a0aSBevin Hansson 
142ad49657aSPiJoules APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) {
143ad49657aSPiJoules   APSInt Val(Sema.getWidth(), !Sema.isSigned());
144ad49657aSPiJoules   Val.setBit(/*BitPosition=*/0);
145ad49657aSPiJoules   return APFixedPoint(Val, Sema);
146ad49657aSPiJoules }
147ad49657aSPiJoules 
148dd3014f3SBevin Hansson bool FixedPointSemantics::fitsInFloatSemantics(
149dd3014f3SBevin Hansson     const fltSemantics &FloatSema) const {
150dd3014f3SBevin Hansson   // A fixed point semantic fits in a floating point semantic if the maximum
151dd3014f3SBevin Hansson   // and minimum values as integers of the fixed point semantic can fit in the
152dd3014f3SBevin Hansson   // floating point semantic.
153dd3014f3SBevin Hansson 
154dd3014f3SBevin Hansson   // If these values do not fit, then a floating point rescaling of the true
155dd3014f3SBevin Hansson   // maximum/minimum value will not fit either, so the floating point semantic
156dd3014f3SBevin Hansson   // cannot be used to perform such a rescaling.
157dd3014f3SBevin Hansson 
158dd3014f3SBevin Hansson   APSInt MaxInt = APFixedPoint::getMax(*this).getValue();
159dd3014f3SBevin Hansson   APFloat F(FloatSema);
160dd3014f3SBevin Hansson   APFloat::opStatus Status = F.convertFromAPInt(MaxInt, MaxInt.isSigned(),
161dd3014f3SBevin Hansson                                                 APFloat::rmNearestTiesToAway);
162dd3014f3SBevin Hansson   if ((Status & APFloat::opOverflow) || !isSigned())
163dd3014f3SBevin Hansson     return !(Status & APFloat::opOverflow);
164dd3014f3SBevin Hansson 
165dd3014f3SBevin Hansson   APSInt MinInt = APFixedPoint::getMin(*this).getValue();
166dd3014f3SBevin Hansson   Status = F.convertFromAPInt(MinInt, MinInt.isSigned(),
167dd3014f3SBevin Hansson                               APFloat::rmNearestTiesToAway);
168dd3014f3SBevin Hansson   return !(Status & APFloat::opOverflow);
169dd3014f3SBevin Hansson }
170dd3014f3SBevin Hansson 
1711a995a0aSBevin Hansson FixedPointSemantics FixedPointSemantics::getCommonSemantics(
1721a995a0aSBevin Hansson     const FixedPointSemantics &Other) const {
1731654b22aSTyker   int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight());
1741654b22aSTyker   int CommonMSb = std::max(getMsbWeight() - hasSignOrPaddingBit(),
1751654b22aSTyker                            Other.getMsbWeight() - Other.hasSignOrPaddingBit());
1761654b22aSTyker   unsigned CommonWidth = CommonMSb - CommonLsb + 1;
1771a995a0aSBevin Hansson 
1781a995a0aSBevin Hansson   bool ResultIsSigned = isSigned() || Other.isSigned();
1791a995a0aSBevin Hansson   bool ResultIsSaturated = isSaturated() || Other.isSaturated();
1801a995a0aSBevin Hansson   bool ResultHasUnsignedPadding = false;
1811a995a0aSBevin Hansson   if (!ResultIsSigned) {
1821a995a0aSBevin Hansson     // Both are unsigned.
1831a995a0aSBevin Hansson     ResultHasUnsignedPadding = hasUnsignedPadding() &&
1841a995a0aSBevin Hansson                                Other.hasUnsignedPadding() && !ResultIsSaturated;
1851a995a0aSBevin Hansson   }
1861a995a0aSBevin Hansson 
1871a995a0aSBevin Hansson   // If the result is signed, add an extra bit for the sign. Otherwise, if it is
1881a995a0aSBevin Hansson   // unsigned and has unsigned padding, we only need to add the extra padding
1891a995a0aSBevin Hansson   // bit back if we are not saturating.
1901a995a0aSBevin Hansson   if (ResultIsSigned || ResultHasUnsignedPadding)
1911a995a0aSBevin Hansson     CommonWidth++;
1921a995a0aSBevin Hansson 
1931654b22aSTyker   return FixedPointSemantics(CommonWidth, Lsb{CommonLsb}, ResultIsSigned,
1941a995a0aSBevin Hansson                              ResultIsSaturated, ResultHasUnsignedPadding);
1951a995a0aSBevin Hansson }
1961a995a0aSBevin Hansson 
1971a995a0aSBevin Hansson APFixedPoint APFixedPoint::add(const APFixedPoint &Other,
1981a995a0aSBevin Hansson                                bool *Overflow) const {
1991a995a0aSBevin Hansson   auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
2001a995a0aSBevin Hansson   APFixedPoint ConvertedThis = convert(CommonFXSema);
2011a995a0aSBevin Hansson   APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
2021a995a0aSBevin Hansson   APSInt ThisVal = ConvertedThis.getValue();
2031a995a0aSBevin Hansson   APSInt OtherVal = ConvertedOther.getValue();
2041a995a0aSBevin Hansson   bool Overflowed = false;
2051a995a0aSBevin Hansson 
2061a995a0aSBevin Hansson   APSInt Result;
2071a995a0aSBevin Hansson   if (CommonFXSema.isSaturated()) {
2081a995a0aSBevin Hansson     Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal)
2091a995a0aSBevin Hansson                                      : ThisVal.uadd_sat(OtherVal);
2101a995a0aSBevin Hansson   } else {
2111a995a0aSBevin Hansson     Result = ThisVal.isSigned() ? ThisVal.sadd_ov(OtherVal, Overflowed)
2121a995a0aSBevin Hansson                                 : ThisVal.uadd_ov(OtherVal, Overflowed);
2131a995a0aSBevin Hansson   }
2141a995a0aSBevin Hansson 
2151a995a0aSBevin Hansson   if (Overflow)
2161a995a0aSBevin Hansson     *Overflow = Overflowed;
2171a995a0aSBevin Hansson 
2181a995a0aSBevin Hansson   return APFixedPoint(Result, CommonFXSema);
2191a995a0aSBevin Hansson }
2201a995a0aSBevin Hansson 
2211a995a0aSBevin Hansson APFixedPoint APFixedPoint::sub(const APFixedPoint &Other,
2221a995a0aSBevin Hansson                                bool *Overflow) const {
2231a995a0aSBevin Hansson   auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
2241a995a0aSBevin Hansson   APFixedPoint ConvertedThis = convert(CommonFXSema);
2251a995a0aSBevin Hansson   APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
2261a995a0aSBevin Hansson   APSInt ThisVal = ConvertedThis.getValue();
2271a995a0aSBevin Hansson   APSInt OtherVal = ConvertedOther.getValue();
2281a995a0aSBevin Hansson   bool Overflowed = false;
2291a995a0aSBevin Hansson 
2301a995a0aSBevin Hansson   APSInt Result;
2311a995a0aSBevin Hansson   if (CommonFXSema.isSaturated()) {
2321a995a0aSBevin Hansson     Result = CommonFXSema.isSigned() ? ThisVal.ssub_sat(OtherVal)
2331a995a0aSBevin Hansson                                      : ThisVal.usub_sat(OtherVal);
2341a995a0aSBevin Hansson   } else {
2351a995a0aSBevin Hansson     Result = ThisVal.isSigned() ? ThisVal.ssub_ov(OtherVal, Overflowed)
2361a995a0aSBevin Hansson                                 : ThisVal.usub_ov(OtherVal, Overflowed);
2371a995a0aSBevin Hansson   }
2381a995a0aSBevin Hansson 
2391a995a0aSBevin Hansson   if (Overflow)
2401a995a0aSBevin Hansson     *Overflow = Overflowed;
2411a995a0aSBevin Hansson 
2421a995a0aSBevin Hansson   return APFixedPoint(Result, CommonFXSema);
2431a995a0aSBevin Hansson }
2441a995a0aSBevin Hansson 
2451a995a0aSBevin Hansson APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,
2461a995a0aSBevin Hansson                                bool *Overflow) const {
2471a995a0aSBevin Hansson   auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
2481a995a0aSBevin Hansson   APFixedPoint ConvertedThis = convert(CommonFXSema);
2491a995a0aSBevin Hansson   APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
2501a995a0aSBevin Hansson   APSInt ThisVal = ConvertedThis.getValue();
2511a995a0aSBevin Hansson   APSInt OtherVal = ConvertedOther.getValue();
2521a995a0aSBevin Hansson   bool Overflowed = false;
2531a995a0aSBevin Hansson 
2541a995a0aSBevin Hansson   // Widen the LHS and RHS so we can perform a full multiplication.
2551a995a0aSBevin Hansson   unsigned Wide = CommonFXSema.getWidth() * 2;
2561a995a0aSBevin Hansson   if (CommonFXSema.isSigned()) {
2576bec3e93SJay Foad     ThisVal = ThisVal.sext(Wide);
2586bec3e93SJay Foad     OtherVal = OtherVal.sext(Wide);
2591a995a0aSBevin Hansson   } else {
2606bec3e93SJay Foad     ThisVal = ThisVal.zext(Wide);
2616bec3e93SJay Foad     OtherVal = OtherVal.zext(Wide);
2621a995a0aSBevin Hansson   }
2631a995a0aSBevin Hansson 
2641a995a0aSBevin Hansson   // Perform the full multiplication and downscale to get the same scale.
2651a995a0aSBevin Hansson   //
2661a995a0aSBevin Hansson   // Note that the right shifts here perform an implicit downwards rounding.
2671a995a0aSBevin Hansson   // This rounding could discard bits that would technically place the result
2681a995a0aSBevin Hansson   // outside the representable range. We interpret the spec as allowing us to
2691a995a0aSBevin Hansson   // perform the rounding step first, avoiding the overflow case that would
2701a995a0aSBevin Hansson   // arise.
2711a995a0aSBevin Hansson   APSInt Result;
2721a995a0aSBevin Hansson   if (CommonFXSema.isSigned())
2731a995a0aSBevin Hansson     Result = ThisVal.smul_ov(OtherVal, Overflowed)
2741654b22aSTyker                  .relativeAShl(CommonFXSema.getLsbWeight());
2751a995a0aSBevin Hansson   else
2761a995a0aSBevin Hansson     Result = ThisVal.umul_ov(OtherVal, Overflowed)
2771654b22aSTyker                  .relativeLShl(CommonFXSema.getLsbWeight());
2781a995a0aSBevin Hansson   assert(!Overflowed && "Full multiplication cannot overflow!");
2791a995a0aSBevin Hansson   Result.setIsSigned(CommonFXSema.isSigned());
2801a995a0aSBevin Hansson 
2811a995a0aSBevin Hansson   // If our result lies outside of the representative range of the common
2821a995a0aSBevin Hansson   // semantic, we either have overflow or saturation.
2831a995a0aSBevin Hansson   APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
2841a995a0aSBevin Hansson                                                  .extOrTrunc(Wide);
2851a995a0aSBevin Hansson   APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
2861a995a0aSBevin Hansson                                                  .extOrTrunc(Wide);
2871a995a0aSBevin Hansson   if (CommonFXSema.isSaturated()) {
2881a995a0aSBevin Hansson     if (Result < Min)
2891a995a0aSBevin Hansson       Result = Min;
2901a995a0aSBevin Hansson     else if (Result > Max)
2911a995a0aSBevin Hansson       Result = Max;
2921a995a0aSBevin Hansson   } else
2931a995a0aSBevin Hansson     Overflowed = Result < Min || Result > Max;
2941a995a0aSBevin Hansson 
2951a995a0aSBevin Hansson   if (Overflow)
2961a995a0aSBevin Hansson     *Overflow = Overflowed;
2971a995a0aSBevin Hansson 
2981a995a0aSBevin Hansson   return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
2991a995a0aSBevin Hansson                       CommonFXSema);
3001a995a0aSBevin Hansson }
3011a995a0aSBevin Hansson 
3021a995a0aSBevin Hansson APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
3031a995a0aSBevin Hansson                                bool *Overflow) const {
3041a995a0aSBevin Hansson   auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
3051a995a0aSBevin Hansson   APFixedPoint ConvertedThis = convert(CommonFXSema);
3061a995a0aSBevin Hansson   APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
3071a995a0aSBevin Hansson   APSInt ThisVal = ConvertedThis.getValue();
3081a995a0aSBevin Hansson   APSInt OtherVal = ConvertedOther.getValue();
3091a995a0aSBevin Hansson   bool Overflowed = false;
3101a995a0aSBevin Hansson 
3111a995a0aSBevin Hansson   // Widen the LHS and RHS so we can perform a full division.
3121654b22aSTyker   // Also make sure that there will be enough space for the shift below to not
3131654b22aSTyker   // overflow
3141654b22aSTyker   unsigned Wide =
3151654b22aSTyker       CommonFXSema.getWidth() * 2 + std::max(-CommonFXSema.getMsbWeight(), 0);
3161a995a0aSBevin Hansson   if (CommonFXSema.isSigned()) {
3176bec3e93SJay Foad     ThisVal = ThisVal.sext(Wide);
3186bec3e93SJay Foad     OtherVal = OtherVal.sext(Wide);
3191a995a0aSBevin Hansson   } else {
3206bec3e93SJay Foad     ThisVal = ThisVal.zext(Wide);
3216bec3e93SJay Foad     OtherVal = OtherVal.zext(Wide);
3221a995a0aSBevin Hansson   }
3231a995a0aSBevin Hansson 
3241a995a0aSBevin Hansson   // Upscale to compensate for the loss of precision from division, and
3251a995a0aSBevin Hansson   // perform the full division.
3261654b22aSTyker   if (CommonFXSema.getLsbWeight() < 0)
3271654b22aSTyker     ThisVal = ThisVal.shl(-CommonFXSema.getLsbWeight());
3281654b22aSTyker   else if (CommonFXSema.getLsbWeight() > 0)
3291654b22aSTyker     OtherVal = OtherVal.shl(CommonFXSema.getLsbWeight());
3301a995a0aSBevin Hansson   APSInt Result;
3311a995a0aSBevin Hansson   if (CommonFXSema.isSigned()) {
3321a995a0aSBevin Hansson     APInt Rem;
3331a995a0aSBevin Hansson     APInt::sdivrem(ThisVal, OtherVal, Result, Rem);
3341a995a0aSBevin Hansson     // If the quotient is negative and the remainder is nonzero, round
3351a995a0aSBevin Hansson     // towards negative infinity by subtracting epsilon from the result.
336a9bceb2bSJay Foad     if (ThisVal.isNegative() != OtherVal.isNegative() && !Rem.isZero())
3371a995a0aSBevin Hansson       Result = Result - 1;
3381a995a0aSBevin Hansson   } else
3391a995a0aSBevin Hansson     Result = ThisVal.udiv(OtherVal);
3401a995a0aSBevin Hansson   Result.setIsSigned(CommonFXSema.isSigned());
3411a995a0aSBevin Hansson 
3421a995a0aSBevin Hansson   // If our result lies outside of the representative range of the common
3431a995a0aSBevin Hansson   // semantic, we either have overflow or saturation.
3441a995a0aSBevin Hansson   APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
3451a995a0aSBevin Hansson                                                  .extOrTrunc(Wide);
3461a995a0aSBevin Hansson   APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
3471a995a0aSBevin Hansson                                                  .extOrTrunc(Wide);
3481a995a0aSBevin Hansson   if (CommonFXSema.isSaturated()) {
3491a995a0aSBevin Hansson     if (Result < Min)
3501a995a0aSBevin Hansson       Result = Min;
3511a995a0aSBevin Hansson     else if (Result > Max)
3521a995a0aSBevin Hansson       Result = Max;
3531a995a0aSBevin Hansson   } else
3541a995a0aSBevin Hansson     Overflowed = Result < Min || Result > Max;
3551a995a0aSBevin Hansson 
3561a995a0aSBevin Hansson   if (Overflow)
3571a995a0aSBevin Hansson     *Overflow = Overflowed;
3581a995a0aSBevin Hansson 
3591a995a0aSBevin Hansson   return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
3601a995a0aSBevin Hansson                       CommonFXSema);
3611a995a0aSBevin Hansson }
3621a995a0aSBevin Hansson 
3631a995a0aSBevin Hansson APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {
3641a995a0aSBevin Hansson   APSInt ThisVal = Val;
3651a995a0aSBevin Hansson   bool Overflowed = false;
3661a995a0aSBevin Hansson 
3671a995a0aSBevin Hansson   // Widen the LHS.
3681a995a0aSBevin Hansson   unsigned Wide = Sema.getWidth() * 2;
3691a995a0aSBevin Hansson   if (Sema.isSigned())
3706bec3e93SJay Foad     ThisVal = ThisVal.sext(Wide);
3711a995a0aSBevin Hansson   else
3726bec3e93SJay Foad     ThisVal = ThisVal.zext(Wide);
3731a995a0aSBevin Hansson 
3741a995a0aSBevin Hansson   // Clamp the shift amount at the original width, and perform the shift.
3751a995a0aSBevin Hansson   Amt = std::min(Amt, ThisVal.getBitWidth());
3761a995a0aSBevin Hansson   APSInt Result = ThisVal << Amt;
3771a995a0aSBevin Hansson   Result.setIsSigned(Sema.isSigned());
3781a995a0aSBevin Hansson 
3791a995a0aSBevin Hansson   // If our result lies outside of the representative range of the
3801a995a0aSBevin Hansson   // semantic, we either have overflow or saturation.
3811a995a0aSBevin Hansson   APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(Wide);
3821a995a0aSBevin Hansson   APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(Wide);
3831a995a0aSBevin Hansson   if (Sema.isSaturated()) {
3841a995a0aSBevin Hansson     if (Result < Min)
3851a995a0aSBevin Hansson       Result = Min;
3861a995a0aSBevin Hansson     else if (Result > Max)
3871a995a0aSBevin Hansson       Result = Max;
3881a995a0aSBevin Hansson   } else
3891a995a0aSBevin Hansson     Overflowed = Result < Min || Result > Max;
3901a995a0aSBevin Hansson 
3911a995a0aSBevin Hansson   if (Overflow)
3921a995a0aSBevin Hansson     *Overflow = Overflowed;
3931a995a0aSBevin Hansson 
3941a995a0aSBevin Hansson   return APFixedPoint(Result.sextOrTrunc(Sema.getWidth()), Sema);
3951a995a0aSBevin Hansson }
3961a995a0aSBevin Hansson 
3971a995a0aSBevin Hansson void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {
3981a995a0aSBevin Hansson   APSInt Val = getValue();
3991654b22aSTyker   int Lsb = getLsbWeight();
4001654b22aSTyker   int OrigWidth = getWidth();
4011a995a0aSBevin Hansson 
4021654b22aSTyker   if (Lsb >= 0) {
4031654b22aSTyker     APSInt IntPart = Val;
4041654b22aSTyker     IntPart = IntPart.extend(IntPart.getBitWidth() + Lsb);
4051654b22aSTyker     IntPart <<= Lsb;
4061654b22aSTyker     IntPart.toString(Str, /*Radix=*/10);
4071654b22aSTyker     Str.push_back('.');
4081654b22aSTyker     Str.push_back('0');
4091654b22aSTyker     return;
4101654b22aSTyker   }
4111654b22aSTyker 
4121654b22aSTyker   if (Val.isSigned() && Val.isNegative()) {
4131a995a0aSBevin Hansson     Val = -Val;
4141654b22aSTyker     Val.setIsUnsigned(true);
4151a995a0aSBevin Hansson     Str.push_back('-');
4161a995a0aSBevin Hansson   }
4171a995a0aSBevin Hansson 
4181654b22aSTyker   int Scale = -getLsbWeight();
4191654b22aSTyker   APSInt IntPart = (OrigWidth > Scale) ? (Val >> Scale) : APSInt::get(0);
4201a995a0aSBevin Hansson 
4211a995a0aSBevin Hansson   // Add 4 digits to hold the value after multiplying 10 (the radix)
4221654b22aSTyker   unsigned Width = std::max(OrigWidth, Scale) + 4;
4231a995a0aSBevin Hansson   APInt FractPart = Val.zextOrTrunc(Scale).zext(Width);
424735f4671SChris Lattner   APInt FractPartMask = APInt::getAllOnes(Scale).zext(Width);
4251a995a0aSBevin Hansson   APInt RadixInt = APInt(Width, 10);
4261a995a0aSBevin Hansson 
4271a995a0aSBevin Hansson   IntPart.toString(Str, /*Radix=*/10);
4281a995a0aSBevin Hansson   Str.push_back('.');
4291a995a0aSBevin Hansson   do {
4301a995a0aSBevin Hansson     (FractPart * RadixInt)
4311a995a0aSBevin Hansson         .lshr(Scale)
4321a995a0aSBevin Hansson         .toString(Str, /*Radix=*/10, Val.isSigned());
4331a995a0aSBevin Hansson     FractPart = (FractPart * RadixInt) & FractPartMask;
4341a995a0aSBevin Hansson   } while (FractPart != 0);
4351a995a0aSBevin Hansson }
4361a995a0aSBevin Hansson 
4371654b22aSTyker void APFixedPoint::print(raw_ostream &OS) const {
4381654b22aSTyker   OS << "APFixedPoint(" << toString() << ", {";
4391654b22aSTyker   Sema.print(OS);
4401654b22aSTyker   OS << "})";
4411654b22aSTyker }
4421654b22aSTyker LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(llvm::errs()); }
4431654b22aSTyker 
4441a995a0aSBevin Hansson APFixedPoint APFixedPoint::negate(bool *Overflow) const {
4451a995a0aSBevin Hansson   if (!isSaturated()) {
4461a995a0aSBevin Hansson     if (Overflow)
4471a995a0aSBevin Hansson       *Overflow =
4481a995a0aSBevin Hansson           (!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue());
4491a995a0aSBevin Hansson     return APFixedPoint(-Val, Sema);
4501a995a0aSBevin Hansson   }
4511a995a0aSBevin Hansson 
4521a995a0aSBevin Hansson   // We never overflow for saturation
4531a995a0aSBevin Hansson   if (Overflow)
4541a995a0aSBevin Hansson     *Overflow = false;
4551a995a0aSBevin Hansson 
4561a995a0aSBevin Hansson   if (isSigned())
4571a995a0aSBevin Hansson     return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema);
4581a995a0aSBevin Hansson   else
4591a995a0aSBevin Hansson     return APFixedPoint(Sema);
4601a995a0aSBevin Hansson }
4611a995a0aSBevin Hansson 
4621a995a0aSBevin Hansson APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign,
4631a995a0aSBevin Hansson                                   bool *Overflow) const {
4641a995a0aSBevin Hansson   APSInt Result = getIntPart();
4651a995a0aSBevin Hansson   unsigned SrcWidth = getWidth();
4661a995a0aSBevin Hansson 
4671a995a0aSBevin Hansson   APSInt DstMin = APSInt::getMinValue(DstWidth, !DstSign);
4681a995a0aSBevin Hansson   APSInt DstMax = APSInt::getMaxValue(DstWidth, !DstSign);
4691a995a0aSBevin Hansson 
4701a995a0aSBevin Hansson   if (SrcWidth < DstWidth) {
4711a995a0aSBevin Hansson     Result = Result.extend(DstWidth);
4721a995a0aSBevin Hansson   } else if (SrcWidth > DstWidth) {
4731a995a0aSBevin Hansson     DstMin = DstMin.extend(SrcWidth);
4741a995a0aSBevin Hansson     DstMax = DstMax.extend(SrcWidth);
4751a995a0aSBevin Hansson   }
4761a995a0aSBevin Hansson 
4771a995a0aSBevin Hansson   if (Overflow) {
4781a995a0aSBevin Hansson     if (Result.isSigned() && !DstSign) {
4791a995a0aSBevin Hansson       *Overflow = Result.isNegative() || Result.ugt(DstMax);
4801a995a0aSBevin Hansson     } else if (Result.isUnsigned() && DstSign) {
4811a995a0aSBevin Hansson       *Overflow = Result.ugt(DstMax);
4821a995a0aSBevin Hansson     } else {
4831a995a0aSBevin Hansson       *Overflow = Result < DstMin || Result > DstMax;
4841a995a0aSBevin Hansson     }
4851a995a0aSBevin Hansson   }
4861a995a0aSBevin Hansson 
4871a995a0aSBevin Hansson   Result.setIsSigned(DstSign);
4881a995a0aSBevin Hansson   return Result.extOrTrunc(DstWidth);
4891a995a0aSBevin Hansson }
4901a995a0aSBevin Hansson 
491dd3014f3SBevin Hansson const fltSemantics *APFixedPoint::promoteFloatSemantics(const fltSemantics *S) {
492dd3014f3SBevin Hansson   if (S == &APFloat::BFloat())
493dd3014f3SBevin Hansson     return &APFloat::IEEEdouble();
494dd3014f3SBevin Hansson   else if (S == &APFloat::IEEEhalf())
495dd3014f3SBevin Hansson     return &APFloat::IEEEsingle();
496dd3014f3SBevin Hansson   else if (S == &APFloat::IEEEsingle())
497dd3014f3SBevin Hansson     return &APFloat::IEEEdouble();
498dd3014f3SBevin Hansson   else if (S == &APFloat::IEEEdouble())
499dd3014f3SBevin Hansson     return &APFloat::IEEEquad();
500dd3014f3SBevin Hansson   llvm_unreachable("Could not promote float type!");
501dd3014f3SBevin Hansson }
502dd3014f3SBevin Hansson 
503dd3014f3SBevin Hansson APFloat APFixedPoint::convertToFloat(const fltSemantics &FloatSema) const {
504dd3014f3SBevin Hansson   // For some operations, rounding mode has an effect on the result, while
505dd3014f3SBevin Hansson   // other operations are lossless and should never result in rounding.
506dd3014f3SBevin Hansson   // To signify which these operations are, we define two rounding modes here.
507dd3014f3SBevin Hansson   APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;
508dd3014f3SBevin Hansson   APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;
509dd3014f3SBevin Hansson 
510dd3014f3SBevin Hansson   // Make sure that we are operating in a type that works with this fixed-point
511dd3014f3SBevin Hansson   // semantic.
512dd3014f3SBevin Hansson   const fltSemantics *OpSema = &FloatSema;
513dd3014f3SBevin Hansson   while (!Sema.fitsInFloatSemantics(*OpSema))
514dd3014f3SBevin Hansson     OpSema = promoteFloatSemantics(OpSema);
515dd3014f3SBevin Hansson 
516dd3014f3SBevin Hansson   // Convert the fixed point value bits as an integer. If the floating point
517dd3014f3SBevin Hansson   // value does not have the required precision, we will round according to the
518dd3014f3SBevin Hansson   // given mode.
519dd3014f3SBevin Hansson   APFloat Flt(*OpSema);
520dd3014f3SBevin Hansson   APFloat::opStatus S = Flt.convertFromAPInt(Val, Sema.isSigned(), RM);
521dd3014f3SBevin Hansson 
522dd3014f3SBevin Hansson   // If we cared about checking for precision loss, we could look at this
523dd3014f3SBevin Hansson   // status.
524dd3014f3SBevin Hansson   (void)S;
525dd3014f3SBevin Hansson 
526dd3014f3SBevin Hansson   // Scale down the integer value in the float to match the correct scaling
527dd3014f3SBevin Hansson   // factor.
5281654b22aSTyker   APFloat ScaleFactor(std::pow(2, Sema.getLsbWeight()));
529dd3014f3SBevin Hansson   bool Ignored;
530dd3014f3SBevin Hansson   ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
531dd3014f3SBevin Hansson   Flt.multiply(ScaleFactor, LosslessRM);
532dd3014f3SBevin Hansson 
533dd3014f3SBevin Hansson   if (OpSema != &FloatSema)
534dd3014f3SBevin Hansson     Flt.convert(FloatSema, RM, &Ignored);
535dd3014f3SBevin Hansson 
536dd3014f3SBevin Hansson   return Flt;
537dd3014f3SBevin Hansson }
538dd3014f3SBevin Hansson 
5391a995a0aSBevin Hansson APFixedPoint APFixedPoint::getFromIntValue(const APSInt &Value,
5401a995a0aSBevin Hansson                                            const FixedPointSemantics &DstFXSema,
5411a995a0aSBevin Hansson                                            bool *Overflow) {
5421a995a0aSBevin Hansson   FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics(
5431a995a0aSBevin Hansson       Value.getBitWidth(), Value.isSigned());
5441a995a0aSBevin Hansson   return APFixedPoint(Value, IntFXSema).convert(DstFXSema, Overflow);
5451a995a0aSBevin Hansson }
5461a995a0aSBevin Hansson 
547dd3014f3SBevin Hansson APFixedPoint
548dd3014f3SBevin Hansson APFixedPoint::getFromFloatValue(const APFloat &Value,
549dd3014f3SBevin Hansson                                 const FixedPointSemantics &DstFXSema,
550dd3014f3SBevin Hansson                                 bool *Overflow) {
551dd3014f3SBevin Hansson   // For some operations, rounding mode has an effect on the result, while
552dd3014f3SBevin Hansson   // other operations are lossless and should never result in rounding.
553dd3014f3SBevin Hansson   // To signify which these operations are, we define two rounding modes here,
554dd3014f3SBevin Hansson   // even though they are the same mode.
555dd3014f3SBevin Hansson   APFloat::roundingMode RM = APFloat::rmTowardZero;
556dd3014f3SBevin Hansson   APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;
557dd3014f3SBevin Hansson 
558dd3014f3SBevin Hansson   const fltSemantics &FloatSema = Value.getSemantics();
559dd3014f3SBevin Hansson 
560dd3014f3SBevin Hansson   if (Value.isNaN()) {
561dd3014f3SBevin Hansson     // Handle NaN immediately.
562dd3014f3SBevin Hansson     if (Overflow)
563dd3014f3SBevin Hansson       *Overflow = true;
564dd3014f3SBevin Hansson     return APFixedPoint(DstFXSema);
565dd3014f3SBevin Hansson   }
566dd3014f3SBevin Hansson 
567dd3014f3SBevin Hansson   // Make sure that we are operating in a type that works with this fixed-point
568dd3014f3SBevin Hansson   // semantic.
569dd3014f3SBevin Hansson   const fltSemantics *OpSema = &FloatSema;
570dd3014f3SBevin Hansson   while (!DstFXSema.fitsInFloatSemantics(*OpSema))
571dd3014f3SBevin Hansson     OpSema = promoteFloatSemantics(OpSema);
572dd3014f3SBevin Hansson 
573dd3014f3SBevin Hansson   APFloat Val = Value;
574dd3014f3SBevin Hansson 
575dd3014f3SBevin Hansson   bool Ignored;
576dd3014f3SBevin Hansson   if (&FloatSema != OpSema)
577dd3014f3SBevin Hansson     Val.convert(*OpSema, LosslessRM, &Ignored);
578dd3014f3SBevin Hansson 
579dd3014f3SBevin Hansson   // Scale up the float so that the 'fractional' part of the mantissa ends up in
580dd3014f3SBevin Hansson   // the integer range instead. Rounding mode is irrelevant here.
581dd3014f3SBevin Hansson   // It is fine if this overflows to infinity even for saturating types,
582dd3014f3SBevin Hansson   // since we will use floating point comparisons to check for saturation.
5831654b22aSTyker   APFloat ScaleFactor(std::pow(2, -DstFXSema.getLsbWeight()));
584dd3014f3SBevin Hansson   ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
585dd3014f3SBevin Hansson   Val.multiply(ScaleFactor, LosslessRM);
586dd3014f3SBevin Hansson 
587dd3014f3SBevin Hansson   // Convert to the integral representation of the value. This rounding mode
588dd3014f3SBevin Hansson   // is significant.
589dd3014f3SBevin Hansson   APSInt Res(DstFXSema.getWidth(), !DstFXSema.isSigned());
590dd3014f3SBevin Hansson   Val.convertToInteger(Res, RM, &Ignored);
591dd3014f3SBevin Hansson 
592dd3014f3SBevin Hansson   // Round the integral value and scale back. This makes the
593dd3014f3SBevin Hansson   // overflow calculations below work properly. If we do not round here,
594dd3014f3SBevin Hansson   // we risk checking for overflow with a value that is outside the
595dd3014f3SBevin Hansson   // representable range of the fixed-point semantic even though no overflow
596dd3014f3SBevin Hansson   // would occur had we rounded first.
5971654b22aSTyker   ScaleFactor = APFloat(std::pow(2, DstFXSema.getLsbWeight()));
598dd3014f3SBevin Hansson   ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);
599dd3014f3SBevin Hansson   Val.roundToIntegral(RM);
600dd3014f3SBevin Hansson   Val.multiply(ScaleFactor, LosslessRM);
601dd3014f3SBevin Hansson 
602dd3014f3SBevin Hansson   // Check for overflow/saturation by checking if the floating point value
603dd3014f3SBevin Hansson   // is outside the range representable by the fixed-point value.
604dd3014f3SBevin Hansson   APFloat FloatMax = getMax(DstFXSema).convertToFloat(*OpSema);
605dd3014f3SBevin Hansson   APFloat FloatMin = getMin(DstFXSema).convertToFloat(*OpSema);
606dd3014f3SBevin Hansson   bool Overflowed = false;
607dd3014f3SBevin Hansson   if (DstFXSema.isSaturated()) {
608dd3014f3SBevin Hansson     if (Val > FloatMax)
609dd3014f3SBevin Hansson       Res = getMax(DstFXSema).getValue();
610dd3014f3SBevin Hansson     else if (Val < FloatMin)
611dd3014f3SBevin Hansson       Res = getMin(DstFXSema).getValue();
612dd3014f3SBevin Hansson   } else
613dd3014f3SBevin Hansson     Overflowed = Val > FloatMax || Val < FloatMin;
614dd3014f3SBevin Hansson 
615dd3014f3SBevin Hansson   if (Overflow)
616dd3014f3SBevin Hansson     *Overflow = Overflowed;
617dd3014f3SBevin Hansson 
618dd3014f3SBevin Hansson   return APFixedPoint(Res, DstFXSema);
619dd3014f3SBevin Hansson }
620dd3014f3SBevin Hansson 
621dd3014f3SBevin Hansson } // namespace llvm
622