Path: blob/main/contrib/llvm-project/llvm/lib/Support/APFixedPoint.cpp
35232 views
//===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8/// \file9/// Defines the implementation for the fixed point number interface.10//11//===----------------------------------------------------------------------===//1213#include "llvm/ADT/APFixedPoint.h"14#include "llvm/ADT/APFloat.h"1516#include <cmath>1718namespace llvm {1920void FixedPointSemantics::print(llvm::raw_ostream &OS) const {21OS << "width=" << getWidth() << ", ";22if (isValidLegacySema())23OS << "scale=" << getScale() << ", ";24OS << "msb=" << getMsbWeight() << ", ";25OS << "lsb=" << getLsbWeight() << ", ";26OS << "IsSigned=" << IsSigned << ", ";27OS << "HasUnsignedPadding=" << HasUnsignedPadding << ", ";28OS << "IsSaturated=" << IsSaturated;29}3031APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,32bool *Overflow) const {33APSInt NewVal = Val;34int RelativeUpscale = getLsbWeight() - DstSema.getLsbWeight();35if (Overflow)36*Overflow = false;3738if (RelativeUpscale > 0)39NewVal = NewVal.extend(NewVal.getBitWidth() + RelativeUpscale);40NewVal = NewVal.relativeShl(RelativeUpscale);4142auto Mask = APInt::getBitsSetFrom(43NewVal.getBitWidth(),44std::min(DstSema.getIntegralBits() - DstSema.getLsbWeight(),45NewVal.getBitWidth()));46APInt Masked(NewVal & Mask);4748// Change in the bits above the sign49if (!(Masked == Mask || Masked == 0)) {50// Found overflow in the bits above the sign51if (DstSema.isSaturated())52NewVal = NewVal.isNegative() ? Mask : ~Mask;53else if (Overflow)54*Overflow = true;55}5657// If the dst semantics are unsigned, but our value is signed and negative, we58// clamp to zero.59if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) {60// Found negative overflow for unsigned result61if (DstSema.isSaturated())62NewVal = 0;63else if (Overflow)64*Overflow = true;65}6667NewVal = NewVal.extOrTrunc(DstSema.getWidth());68NewVal.setIsSigned(DstSema.isSigned());69return APFixedPoint(NewVal, DstSema);70}7172int APFixedPoint::compare(const APFixedPoint &Other) const {73APSInt ThisVal = getValue();74APSInt OtherVal = Other.getValue();75bool ThisSigned = Val.isSigned();76bool OtherSigned = OtherVal.isSigned();7778int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight());79int CommonMsb = std::max(getMsbWeight(), Other.getMsbWeight());80unsigned CommonWidth = CommonMsb - CommonLsb + 1;8182ThisVal = ThisVal.extOrTrunc(CommonWidth);83OtherVal = OtherVal.extOrTrunc(CommonWidth);8485ThisVal = ThisVal.shl(getLsbWeight() - CommonLsb);86OtherVal = OtherVal.shl(Other.getLsbWeight() - CommonLsb);8788if (ThisSigned && OtherSigned) {89if (ThisVal.sgt(OtherVal))90return 1;91else if (ThisVal.slt(OtherVal))92return -1;93} else if (!ThisSigned && !OtherSigned) {94if (ThisVal.ugt(OtherVal))95return 1;96else if (ThisVal.ult(OtherVal))97return -1;98} else if (ThisSigned && !OtherSigned) {99if (ThisVal.isSignBitSet())100return -1;101else if (ThisVal.ugt(OtherVal))102return 1;103else if (ThisVal.ult(OtherVal))104return -1;105} else {106// !ThisSigned && OtherSigned107if (OtherVal.isSignBitSet())108return 1;109else if (ThisVal.ugt(OtherVal))110return 1;111else if (ThisVal.ult(OtherVal))112return -1;113}114115return 0;116}117118APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) {119bool IsUnsigned = !Sema.isSigned();120auto Val = APSInt::getMaxValue(Sema.getWidth(), IsUnsigned);121if (IsUnsigned && Sema.hasUnsignedPadding())122Val = Val.lshr(1);123return APFixedPoint(Val, Sema);124}125126APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {127auto Val = APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned());128return APFixedPoint(Val, Sema);129}130131APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) {132APSInt Val(Sema.getWidth(), !Sema.isSigned());133Val.setBit(/*BitPosition=*/0);134return APFixedPoint(Val, Sema);135}136137bool FixedPointSemantics::fitsInFloatSemantics(138const fltSemantics &FloatSema) const {139// A fixed point semantic fits in a floating point semantic if the maximum140// and minimum values as integers of the fixed point semantic can fit in the141// floating point semantic.142143// If these values do not fit, then a floating point rescaling of the true144// maximum/minimum value will not fit either, so the floating point semantic145// cannot be used to perform such a rescaling.146147APSInt MaxInt = APFixedPoint::getMax(*this).getValue();148APFloat F(FloatSema);149APFloat::opStatus Status = F.convertFromAPInt(MaxInt, MaxInt.isSigned(),150APFloat::rmNearestTiesToAway);151if ((Status & APFloat::opOverflow) || !isSigned())152return !(Status & APFloat::opOverflow);153154APSInt MinInt = APFixedPoint::getMin(*this).getValue();155Status = F.convertFromAPInt(MinInt, MinInt.isSigned(),156APFloat::rmNearestTiesToAway);157return !(Status & APFloat::opOverflow);158}159160FixedPointSemantics FixedPointSemantics::getCommonSemantics(161const FixedPointSemantics &Other) const {162int CommonLsb = std::min(getLsbWeight(), Other.getLsbWeight());163int CommonMSb = std::max(getMsbWeight() - hasSignOrPaddingBit(),164Other.getMsbWeight() - Other.hasSignOrPaddingBit());165unsigned CommonWidth = CommonMSb - CommonLsb + 1;166167bool ResultIsSigned = isSigned() || Other.isSigned();168bool ResultIsSaturated = isSaturated() || Other.isSaturated();169bool ResultHasUnsignedPadding = false;170if (!ResultIsSigned) {171// Both are unsigned.172ResultHasUnsignedPadding = hasUnsignedPadding() &&173Other.hasUnsignedPadding() && !ResultIsSaturated;174}175176// If the result is signed, add an extra bit for the sign. Otherwise, if it is177// unsigned and has unsigned padding, we only need to add the extra padding178// bit back if we are not saturating.179if (ResultIsSigned || ResultHasUnsignedPadding)180CommonWidth++;181182return FixedPointSemantics(CommonWidth, Lsb{CommonLsb}, ResultIsSigned,183ResultIsSaturated, ResultHasUnsignedPadding);184}185186APFixedPoint APFixedPoint::add(const APFixedPoint &Other,187bool *Overflow) const {188auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());189APFixedPoint ConvertedThis = convert(CommonFXSema);190APFixedPoint ConvertedOther = Other.convert(CommonFXSema);191APSInt ThisVal = ConvertedThis.getValue();192APSInt OtherVal = ConvertedOther.getValue();193bool Overflowed = false;194195APSInt Result;196if (CommonFXSema.isSaturated()) {197Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal)198: ThisVal.uadd_sat(OtherVal);199} else {200Result = ThisVal.isSigned() ? ThisVal.sadd_ov(OtherVal, Overflowed)201: ThisVal.uadd_ov(OtherVal, Overflowed);202}203204if (Overflow)205*Overflow = Overflowed;206207return APFixedPoint(Result, CommonFXSema);208}209210APFixedPoint APFixedPoint::sub(const APFixedPoint &Other,211bool *Overflow) const {212auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());213APFixedPoint ConvertedThis = convert(CommonFXSema);214APFixedPoint ConvertedOther = Other.convert(CommonFXSema);215APSInt ThisVal = ConvertedThis.getValue();216APSInt OtherVal = ConvertedOther.getValue();217bool Overflowed = false;218219APSInt Result;220if (CommonFXSema.isSaturated()) {221Result = CommonFXSema.isSigned() ? ThisVal.ssub_sat(OtherVal)222: ThisVal.usub_sat(OtherVal);223} else {224Result = ThisVal.isSigned() ? ThisVal.ssub_ov(OtherVal, Overflowed)225: ThisVal.usub_ov(OtherVal, Overflowed);226}227228if (Overflow)229*Overflow = Overflowed;230231return APFixedPoint(Result, CommonFXSema);232}233234APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,235bool *Overflow) const {236auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());237APFixedPoint ConvertedThis = convert(CommonFXSema);238APFixedPoint ConvertedOther = Other.convert(CommonFXSema);239APSInt ThisVal = ConvertedThis.getValue();240APSInt OtherVal = ConvertedOther.getValue();241bool Overflowed = false;242243// Widen the LHS and RHS so we can perform a full multiplication.244unsigned Wide = CommonFXSema.getWidth() * 2;245if (CommonFXSema.isSigned()) {246ThisVal = ThisVal.sext(Wide);247OtherVal = OtherVal.sext(Wide);248} else {249ThisVal = ThisVal.zext(Wide);250OtherVal = OtherVal.zext(Wide);251}252253// Perform the full multiplication and downscale to get the same scale.254//255// Note that the right shifts here perform an implicit downwards rounding.256// This rounding could discard bits that would technically place the result257// outside the representable range. We interpret the spec as allowing us to258// perform the rounding step first, avoiding the overflow case that would259// arise.260APSInt Result;261if (CommonFXSema.isSigned())262Result = ThisVal.smul_ov(OtherVal, Overflowed)263.relativeAShl(CommonFXSema.getLsbWeight());264else265Result = ThisVal.umul_ov(OtherVal, Overflowed)266.relativeLShl(CommonFXSema.getLsbWeight());267assert(!Overflowed && "Full multiplication cannot overflow!");268Result.setIsSigned(CommonFXSema.isSigned());269270// If our result lies outside of the representative range of the common271// semantic, we either have overflow or saturation.272APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()273.extOrTrunc(Wide);274APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()275.extOrTrunc(Wide);276if (CommonFXSema.isSaturated()) {277if (Result < Min)278Result = Min;279else if (Result > Max)280Result = Max;281} else282Overflowed = Result < Min || Result > Max;283284if (Overflow)285*Overflow = Overflowed;286287return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),288CommonFXSema);289}290291APFixedPoint APFixedPoint::div(const APFixedPoint &Other,292bool *Overflow) const {293auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());294APFixedPoint ConvertedThis = convert(CommonFXSema);295APFixedPoint ConvertedOther = Other.convert(CommonFXSema);296APSInt ThisVal = ConvertedThis.getValue();297APSInt OtherVal = ConvertedOther.getValue();298bool Overflowed = false;299300// Widen the LHS and RHS so we can perform a full division.301// Also make sure that there will be enough space for the shift below to not302// overflow303unsigned Wide =304CommonFXSema.getWidth() * 2 + std::max(-CommonFXSema.getMsbWeight(), 0);305if (CommonFXSema.isSigned()) {306ThisVal = ThisVal.sext(Wide);307OtherVal = OtherVal.sext(Wide);308} else {309ThisVal = ThisVal.zext(Wide);310OtherVal = OtherVal.zext(Wide);311}312313// Upscale to compensate for the loss of precision from division, and314// perform the full division.315if (CommonFXSema.getLsbWeight() < 0)316ThisVal = ThisVal.shl(-CommonFXSema.getLsbWeight());317else if (CommonFXSema.getLsbWeight() > 0)318OtherVal = OtherVal.shl(CommonFXSema.getLsbWeight());319APSInt Result;320if (CommonFXSema.isSigned()) {321APInt Rem;322APInt::sdivrem(ThisVal, OtherVal, Result, Rem);323// If the quotient is negative and the remainder is nonzero, round324// towards negative infinity by subtracting epsilon from the result.325if (ThisVal.isNegative() != OtherVal.isNegative() && !Rem.isZero())326Result = Result - 1;327} else328Result = ThisVal.udiv(OtherVal);329Result.setIsSigned(CommonFXSema.isSigned());330331// If our result lies outside of the representative range of the common332// semantic, we either have overflow or saturation.333APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()334.extOrTrunc(Wide);335APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()336.extOrTrunc(Wide);337if (CommonFXSema.isSaturated()) {338if (Result < Min)339Result = Min;340else if (Result > Max)341Result = Max;342} else343Overflowed = Result < Min || Result > Max;344345if (Overflow)346*Overflow = Overflowed;347348return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),349CommonFXSema);350}351352APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {353APSInt ThisVal = Val;354bool Overflowed = false;355356// Widen the LHS.357unsigned Wide = Sema.getWidth() * 2;358if (Sema.isSigned())359ThisVal = ThisVal.sext(Wide);360else361ThisVal = ThisVal.zext(Wide);362363// Clamp the shift amount at the original width, and perform the shift.364Amt = std::min(Amt, ThisVal.getBitWidth());365APSInt Result = ThisVal << Amt;366Result.setIsSigned(Sema.isSigned());367368// If our result lies outside of the representative range of the369// semantic, we either have overflow or saturation.370APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(Wide);371APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(Wide);372if (Sema.isSaturated()) {373if (Result < Min)374Result = Min;375else if (Result > Max)376Result = Max;377} else378Overflowed = Result < Min || Result > Max;379380if (Overflow)381*Overflow = Overflowed;382383return APFixedPoint(Result.sextOrTrunc(Sema.getWidth()), Sema);384}385386void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {387APSInt Val = getValue();388int Lsb = getLsbWeight();389int OrigWidth = getWidth();390391if (Lsb >= 0) {392APSInt IntPart = Val;393IntPart = IntPart.extend(IntPart.getBitWidth() + Lsb);394IntPart <<= Lsb;395IntPart.toString(Str, /*Radix=*/10);396Str.push_back('.');397Str.push_back('0');398return;399}400401if (Val.isSigned() && Val.isNegative()) {402Val = -Val;403Val.setIsUnsigned(true);404Str.push_back('-');405}406407int Scale = -getLsbWeight();408APSInt IntPart = (OrigWidth > Scale) ? (Val >> Scale) : APSInt::get(0);409410// Add 4 digits to hold the value after multiplying 10 (the radix)411unsigned Width = std::max(OrigWidth, Scale) + 4;412APInt FractPart = Val.zextOrTrunc(Scale).zext(Width);413APInt FractPartMask = APInt::getAllOnes(Scale).zext(Width);414APInt RadixInt = APInt(Width, 10);415416IntPart.toString(Str, /*Radix=*/10);417Str.push_back('.');418do {419(FractPart * RadixInt)420.lshr(Scale)421.toString(Str, /*Radix=*/10, Val.isSigned());422FractPart = (FractPart * RadixInt) & FractPartMask;423} while (FractPart != 0);424}425426void APFixedPoint::print(raw_ostream &OS) const {427OS << "APFixedPoint(" << toString() << ", {";428Sema.print(OS);429OS << "})";430}431LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(llvm::errs()); }432433APFixedPoint APFixedPoint::negate(bool *Overflow) const {434if (!isSaturated()) {435if (Overflow)436*Overflow =437(!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue());438return APFixedPoint(-Val, Sema);439}440441// We never overflow for saturation442if (Overflow)443*Overflow = false;444445if (isSigned())446return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema);447else448return APFixedPoint(Sema);449}450451APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign,452bool *Overflow) const {453APSInt Result = getIntPart();454unsigned SrcWidth = getWidth();455456APSInt DstMin = APSInt::getMinValue(DstWidth, !DstSign);457APSInt DstMax = APSInt::getMaxValue(DstWidth, !DstSign);458459if (SrcWidth < DstWidth) {460Result = Result.extend(DstWidth);461} else if (SrcWidth > DstWidth) {462DstMin = DstMin.extend(SrcWidth);463DstMax = DstMax.extend(SrcWidth);464}465466if (Overflow) {467if (Result.isSigned() && !DstSign) {468*Overflow = Result.isNegative() || Result.ugt(DstMax);469} else if (Result.isUnsigned() && DstSign) {470*Overflow = Result.ugt(DstMax);471} else {472*Overflow = Result < DstMin || Result > DstMax;473}474}475476Result.setIsSigned(DstSign);477return Result.extOrTrunc(DstWidth);478}479480const fltSemantics *APFixedPoint::promoteFloatSemantics(const fltSemantics *S) {481if (S == &APFloat::BFloat())482return &APFloat::IEEEdouble();483else if (S == &APFloat::IEEEhalf())484return &APFloat::IEEEsingle();485else if (S == &APFloat::IEEEsingle())486return &APFloat::IEEEdouble();487else if (S == &APFloat::IEEEdouble())488return &APFloat::IEEEquad();489llvm_unreachable("Could not promote float type!");490}491492APFloat APFixedPoint::convertToFloat(const fltSemantics &FloatSema) const {493// For some operations, rounding mode has an effect on the result, while494// other operations are lossless and should never result in rounding.495// To signify which these operations are, we define two rounding modes here.496APFloat::roundingMode RM = APFloat::rmNearestTiesToEven;497APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;498499// Make sure that we are operating in a type that works with this fixed-point500// semantic.501const fltSemantics *OpSema = &FloatSema;502while (!Sema.fitsInFloatSemantics(*OpSema))503OpSema = promoteFloatSemantics(OpSema);504505// Convert the fixed point value bits as an integer. If the floating point506// value does not have the required precision, we will round according to the507// given mode.508APFloat Flt(*OpSema);509APFloat::opStatus S = Flt.convertFromAPInt(Val, Sema.isSigned(), RM);510511// If we cared about checking for precision loss, we could look at this512// status.513(void)S;514515// Scale down the integer value in the float to match the correct scaling516// factor.517APFloat ScaleFactor(std::pow(2, Sema.getLsbWeight()));518bool Ignored;519ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);520Flt.multiply(ScaleFactor, LosslessRM);521522if (OpSema != &FloatSema)523Flt.convert(FloatSema, RM, &Ignored);524525return Flt;526}527528APFixedPoint APFixedPoint::getFromIntValue(const APSInt &Value,529const FixedPointSemantics &DstFXSema,530bool *Overflow) {531FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics(532Value.getBitWidth(), Value.isSigned());533return APFixedPoint(Value, IntFXSema).convert(DstFXSema, Overflow);534}535536APFixedPoint537APFixedPoint::getFromFloatValue(const APFloat &Value,538const FixedPointSemantics &DstFXSema,539bool *Overflow) {540// For some operations, rounding mode has an effect on the result, while541// other operations are lossless and should never result in rounding.542// To signify which these operations are, we define two rounding modes here,543// even though they are the same mode.544APFloat::roundingMode RM = APFloat::rmTowardZero;545APFloat::roundingMode LosslessRM = APFloat::rmTowardZero;546547const fltSemantics &FloatSema = Value.getSemantics();548549if (Value.isNaN()) {550// Handle NaN immediately.551if (Overflow)552*Overflow = true;553return APFixedPoint(DstFXSema);554}555556// Make sure that we are operating in a type that works with this fixed-point557// semantic.558const fltSemantics *OpSema = &FloatSema;559while (!DstFXSema.fitsInFloatSemantics(*OpSema))560OpSema = promoteFloatSemantics(OpSema);561562APFloat Val = Value;563564bool Ignored;565if (&FloatSema != OpSema)566Val.convert(*OpSema, LosslessRM, &Ignored);567568// Scale up the float so that the 'fractional' part of the mantissa ends up in569// the integer range instead. Rounding mode is irrelevant here.570// It is fine if this overflows to infinity even for saturating types,571// since we will use floating point comparisons to check for saturation.572APFloat ScaleFactor(std::pow(2, -DstFXSema.getLsbWeight()));573ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);574Val.multiply(ScaleFactor, LosslessRM);575576// Convert to the integral representation of the value. This rounding mode577// is significant.578APSInt Res(DstFXSema.getWidth(), !DstFXSema.isSigned());579Val.convertToInteger(Res, RM, &Ignored);580581// Round the integral value and scale back. This makes the582// overflow calculations below work properly. If we do not round here,583// we risk checking for overflow with a value that is outside the584// representable range of the fixed-point semantic even though no overflow585// would occur had we rounded first.586ScaleFactor = APFloat(std::pow(2, DstFXSema.getLsbWeight()));587ScaleFactor.convert(*OpSema, LosslessRM, &Ignored);588Val.roundToIntegral(RM);589Val.multiply(ScaleFactor, LosslessRM);590591// Check for overflow/saturation by checking if the floating point value592// is outside the range representable by the fixed-point value.593APFloat FloatMax = getMax(DstFXSema).convertToFloat(*OpSema);594APFloat FloatMin = getMin(DstFXSema).convertToFloat(*OpSema);595bool Overflowed = false;596if (DstFXSema.isSaturated()) {597if (Val > FloatMax)598Res = getMax(DstFXSema).getValue();599else if (Val < FloatMin)600Res = getMin(DstFXSema).getValue();601} else602Overflowed = Val > FloatMax || Val < FloatMin;603604if (Overflow)605*Overflow = Overflowed;606607return APFixedPoint(Res, DstFXSema);608}609610} // namespace llvm611612613