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