1343173Sdim//===- FixedPoint.cpp - Fixed point constant handling -----------*- C++ -*-===// 2343173Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6343173Sdim// 7343173Sdim//===----------------------------------------------------------------------===// 8343173Sdim// 9343173Sdim/// \file 10343173Sdim/// Defines the implementation for the fixed point number interface. 11343173Sdim// 12343173Sdim//===----------------------------------------------------------------------===// 13343173Sdim 14343173Sdim#include "clang/Basic/FixedPoint.h" 15343173Sdim 16343173Sdimnamespace clang { 17343173Sdim 18353358SdimAPFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema, 19353358Sdim bool *Overflow) const { 20343173Sdim llvm::APSInt NewVal = Val; 21343173Sdim unsigned DstWidth = DstSema.getWidth(); 22343173Sdim unsigned DstScale = DstSema.getScale(); 23343173Sdim bool Upscaling = DstScale > getScale(); 24353358Sdim if (Overflow) 25353358Sdim *Overflow = false; 26343173Sdim 27343173Sdim if (Upscaling) { 28343173Sdim NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale()); 29343173Sdim NewVal <<= (DstScale - getScale()); 30343173Sdim } else { 31343173Sdim NewVal >>= (getScale() - DstScale); 32343173Sdim } 33343173Sdim 34353358Sdim auto Mask = llvm::APInt::getBitsSetFrom( 35353358Sdim NewVal.getBitWidth(), 36353358Sdim std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth())); 37353358Sdim llvm::APInt Masked(NewVal & Mask); 38343173Sdim 39353358Sdim // Change in the bits above the sign 40353358Sdim if (!(Masked == Mask || Masked == 0)) { 41353358Sdim // Found overflow in the bits above the sign 42353358Sdim if (DstSema.isSaturated()) 43343173Sdim NewVal = NewVal.isNegative() ? Mask : ~Mask; 44353358Sdim else if (Overflow) 45353358Sdim *Overflow = true; 46353358Sdim } 47343173Sdim 48353358Sdim // If the dst semantics are unsigned, but our value is signed and negative, we 49353358Sdim // clamp to zero. 50353358Sdim if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) { 51353358Sdim // Found negative overflow for unsigned result 52353358Sdim if (DstSema.isSaturated()) 53343173Sdim NewVal = 0; 54353358Sdim else if (Overflow) 55353358Sdim *Overflow = true; 56343173Sdim } 57343173Sdim 58343173Sdim NewVal = NewVal.extOrTrunc(DstWidth); 59343173Sdim NewVal.setIsSigned(DstSema.isSigned()); 60343173Sdim return APFixedPoint(NewVal, DstSema); 61343173Sdim} 62343173Sdim 63343173Sdimint APFixedPoint::compare(const APFixedPoint &Other) const { 64343173Sdim llvm::APSInt ThisVal = getValue(); 65343173Sdim llvm::APSInt OtherVal = Other.getValue(); 66343173Sdim bool ThisSigned = Val.isSigned(); 67343173Sdim bool OtherSigned = OtherVal.isSigned(); 68343173Sdim unsigned OtherScale = Other.getScale(); 69343173Sdim unsigned OtherWidth = OtherVal.getBitWidth(); 70343173Sdim 71343173Sdim unsigned CommonWidth = std::max(Val.getBitWidth(), OtherWidth); 72343173Sdim 73343173Sdim // Prevent overflow in the event the widths are the same but the scales differ 74343173Sdim CommonWidth += getScale() >= OtherScale ? getScale() - OtherScale 75343173Sdim : OtherScale - getScale(); 76343173Sdim 77343173Sdim ThisVal = ThisVal.extOrTrunc(CommonWidth); 78343173Sdim OtherVal = OtherVal.extOrTrunc(CommonWidth); 79343173Sdim 80343173Sdim unsigned CommonScale = std::max(getScale(), OtherScale); 81343173Sdim ThisVal = ThisVal.shl(CommonScale - getScale()); 82343173Sdim OtherVal = OtherVal.shl(CommonScale - OtherScale); 83343173Sdim 84343173Sdim if (ThisSigned && OtherSigned) { 85343173Sdim if (ThisVal.sgt(OtherVal)) 86343173Sdim return 1; 87343173Sdim else if (ThisVal.slt(OtherVal)) 88343173Sdim return -1; 89343173Sdim } else if (!ThisSigned && !OtherSigned) { 90343173Sdim if (ThisVal.ugt(OtherVal)) 91343173Sdim return 1; 92343173Sdim else if (ThisVal.ult(OtherVal)) 93343173Sdim return -1; 94343173Sdim } else if (ThisSigned && !OtherSigned) { 95343173Sdim if (ThisVal.isSignBitSet()) 96343173Sdim return -1; 97343173Sdim else if (ThisVal.ugt(OtherVal)) 98343173Sdim return 1; 99343173Sdim else if (ThisVal.ult(OtherVal)) 100343173Sdim return -1; 101343173Sdim } else { 102343173Sdim // !ThisSigned && OtherSigned 103343173Sdim if (OtherVal.isSignBitSet()) 104343173Sdim return 1; 105343173Sdim else if (ThisVal.ugt(OtherVal)) 106343173Sdim return 1; 107343173Sdim else if (ThisVal.ult(OtherVal)) 108343173Sdim return -1; 109343173Sdim } 110343173Sdim 111343173Sdim return 0; 112343173Sdim} 113343173Sdim 114343173SdimAPFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) { 115343173Sdim bool IsUnsigned = !Sema.isSigned(); 116343173Sdim auto Val = llvm::APSInt::getMaxValue(Sema.getWidth(), IsUnsigned); 117343173Sdim if (IsUnsigned && Sema.hasUnsignedPadding()) 118343173Sdim Val = Val.lshr(1); 119343173Sdim return APFixedPoint(Val, Sema); 120343173Sdim} 121343173Sdim 122343173SdimAPFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) { 123343173Sdim auto Val = llvm::APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned()); 124343173Sdim return APFixedPoint(Val, Sema); 125343173Sdim} 126343173Sdim 127353358SdimFixedPointSemantics FixedPointSemantics::getCommonSemantics( 128353358Sdim const FixedPointSemantics &Other) const { 129353358Sdim unsigned CommonScale = std::max(getScale(), Other.getScale()); 130353358Sdim unsigned CommonWidth = 131353358Sdim std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale; 132353358Sdim 133353358Sdim bool ResultIsSigned = isSigned() || Other.isSigned(); 134353358Sdim bool ResultIsSaturated = isSaturated() || Other.isSaturated(); 135353358Sdim bool ResultHasUnsignedPadding = false; 136353358Sdim if (!ResultIsSigned) { 137353358Sdim // Both are unsigned. 138353358Sdim ResultHasUnsignedPadding = hasUnsignedPadding() && 139353358Sdim Other.hasUnsignedPadding() && !ResultIsSaturated; 140353358Sdim } 141353358Sdim 142353358Sdim // If the result is signed, add an extra bit for the sign. Otherwise, if it is 143353358Sdim // unsigned and has unsigned padding, we only need to add the extra padding 144353358Sdim // bit back if we are not saturating. 145353358Sdim if (ResultIsSigned || ResultHasUnsignedPadding) 146353358Sdim CommonWidth++; 147353358Sdim 148353358Sdim return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned, 149353358Sdim ResultIsSaturated, ResultHasUnsignedPadding); 150353358Sdim} 151353358Sdim 152353358SdimAPFixedPoint APFixedPoint::add(const APFixedPoint &Other, 153353358Sdim bool *Overflow) const { 154353358Sdim auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics()); 155353358Sdim APFixedPoint ConvertedThis = convert(CommonFXSema); 156353358Sdim APFixedPoint ConvertedOther = Other.convert(CommonFXSema); 157353358Sdim llvm::APSInt ThisVal = ConvertedThis.getValue(); 158353358Sdim llvm::APSInt OtherVal = ConvertedOther.getValue(); 159353358Sdim bool Overflowed = false; 160353358Sdim 161353358Sdim llvm::APSInt Result; 162353358Sdim if (CommonFXSema.isSaturated()) { 163353358Sdim Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal) 164353358Sdim : ThisVal.uadd_sat(OtherVal); 165353358Sdim } else { 166353358Sdim Result = ThisVal.isSigned() ? ThisVal.sadd_ov(OtherVal, Overflowed) 167353358Sdim : ThisVal.uadd_ov(OtherVal, Overflowed); 168353358Sdim } 169353358Sdim 170353358Sdim if (Overflow) 171353358Sdim *Overflow = Overflowed; 172353358Sdim 173353358Sdim return APFixedPoint(Result, CommonFXSema); 174353358Sdim} 175353358Sdim 176353358Sdimvoid APFixedPoint::toString(llvm::SmallVectorImpl<char> &Str) const { 177353358Sdim llvm::APSInt Val = getValue(); 178353358Sdim unsigned Scale = getScale(); 179353358Sdim 180353358Sdim if (Val.isSigned() && Val.isNegative() && Val != -Val) { 181353358Sdim Val = -Val; 182353358Sdim Str.push_back('-'); 183353358Sdim } 184353358Sdim 185353358Sdim llvm::APSInt IntPart = Val >> Scale; 186353358Sdim 187353358Sdim // Add 4 digits to hold the value after multiplying 10 (the radix) 188353358Sdim unsigned Width = Val.getBitWidth() + 4; 189353358Sdim llvm::APInt FractPart = Val.zextOrTrunc(Scale).zext(Width); 190353358Sdim llvm::APInt FractPartMask = llvm::APInt::getAllOnesValue(Scale).zext(Width); 191353358Sdim llvm::APInt RadixInt = llvm::APInt(Width, 10); 192353358Sdim 193353358Sdim IntPart.toString(Str, /*Radix=*/10); 194353358Sdim Str.push_back('.'); 195353358Sdim do { 196353358Sdim (FractPart * RadixInt) 197353358Sdim .lshr(Scale) 198353358Sdim .toString(Str, /*Radix=*/10, Val.isSigned()); 199353358Sdim FractPart = (FractPart * RadixInt) & FractPartMask; 200353358Sdim } while (FractPart != 0); 201353358Sdim} 202353358Sdim 203353358SdimAPFixedPoint APFixedPoint::negate(bool *Overflow) const { 204353358Sdim if (!isSaturated()) { 205353358Sdim if (Overflow) 206353358Sdim *Overflow = 207353358Sdim (!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue()); 208353358Sdim return APFixedPoint(-Val, Sema); 209353358Sdim } 210353358Sdim 211353358Sdim // We never overflow for saturation 212353358Sdim if (Overflow) 213353358Sdim *Overflow = false; 214353358Sdim 215353358Sdim if (isSigned()) 216353358Sdim return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema); 217353358Sdim else 218353358Sdim return APFixedPoint(Sema); 219353358Sdim} 220353358Sdim 221353358Sdimllvm::APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign, 222353358Sdim bool *Overflow) const { 223353358Sdim llvm::APSInt Result = getIntPart(); 224353358Sdim unsigned SrcWidth = getWidth(); 225353358Sdim 226353358Sdim llvm::APSInt DstMin = llvm::APSInt::getMinValue(DstWidth, !DstSign); 227353358Sdim llvm::APSInt DstMax = llvm::APSInt::getMaxValue(DstWidth, !DstSign); 228353358Sdim 229353358Sdim if (SrcWidth < DstWidth) { 230353358Sdim Result = Result.extend(DstWidth); 231353358Sdim } else if (SrcWidth > DstWidth) { 232353358Sdim DstMin = DstMin.extend(SrcWidth); 233353358Sdim DstMax = DstMax.extend(SrcWidth); 234353358Sdim } 235353358Sdim 236353358Sdim if (Overflow) { 237353358Sdim if (Result.isSigned() && !DstSign) { 238353358Sdim *Overflow = Result.isNegative() || Result.ugt(DstMax); 239353358Sdim } else if (Result.isUnsigned() && DstSign) { 240353358Sdim *Overflow = Result.ugt(DstMax); 241353358Sdim } else { 242353358Sdim *Overflow = Result < DstMin || Result > DstMax; 243353358Sdim } 244353358Sdim } 245353358Sdim 246353358Sdim Result.setIsSigned(DstSign); 247353358Sdim return Result.extOrTrunc(DstWidth); 248353358Sdim} 249353358Sdim 250353358SdimAPFixedPoint APFixedPoint::getFromIntValue(const llvm::APSInt &Value, 251353358Sdim const FixedPointSemantics &DstFXSema, 252353358Sdim bool *Overflow) { 253353358Sdim FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics( 254353358Sdim Value.getBitWidth(), Value.isSigned()); 255353358Sdim return APFixedPoint(Value, IntFXSema).convert(DstFXSema, Overflow); 256353358Sdim} 257353358Sdim 258343173Sdim} // namespace clang 259