| 1 | //===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | /// \file |
| 10 | /// Defines the implementation for the fixed point number interface. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "llvm/ADT/APFixedPoint.h" |
| 15 | #include "llvm/ADT/APFloat.h" |
| 16 | |
| 17 | #include <cmath> |
| 18 | |
| 19 | namespace llvm { |
| 20 | |
| 21 | void FixedPointSemantics::print(llvm::raw_ostream &OS) const { |
| 22 | OS << "width=" << getWidth() << ", " ; |
| 23 | if (isValidLegacySema()) |
| 24 | OS << "scale=" << getScale() << ", " ; |
| 25 | OS << "msb=" << getMsbWeight() << ", " ; |
| 26 | OS << "lsb=" << getLsbWeight() << ", " ; |
| 27 | OS << "IsSigned=" << IsSigned << ", " ; |
| 28 | OS << "HasUnsignedPadding=" << HasUnsignedPadding << ", " ; |
| 29 | OS << "IsSaturated=" << IsSaturated; |
| 30 | } |
| 31 | |
| 32 | uint32_t FixedPointSemantics::toOpaqueInt() const { |
| 33 | return llvm::bit_cast<uint32_t>(from: *this); |
| 34 | } |
| 35 | |
| 36 | FixedPointSemantics FixedPointSemantics::getFromOpaqueInt(uint32_t I) { |
| 37 | FixedPointSemantics F(0, 0, false, false, false); |
| 38 | std::memcpy(dest: &F, src: &I, n: sizeof(F)); |
| 39 | return F; |
| 40 | } |
| 41 | |
| 42 | APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema, |
| 43 | bool *Overflow) const { |
| 44 | APSInt NewVal = Val; |
| 45 | int RelativeUpscale = getLsbWeight() - DstSema.getLsbWeight(); |
| 46 | if (Overflow) |
| 47 | *Overflow = false; |
| 48 | |
| 49 | if (RelativeUpscale > 0) |
| 50 | NewVal = NewVal.extend(width: NewVal.getBitWidth() + RelativeUpscale); |
| 51 | NewVal = NewVal.relativeShl(Amt: RelativeUpscale); |
| 52 | |
| 53 | auto Mask = APInt::getBitsSetFrom( |
| 54 | numBits: NewVal.getBitWidth(), |
| 55 | loBit: std::min(a: DstSema.getIntegralBits() - DstSema.getLsbWeight(), |
| 56 | b: NewVal.getBitWidth())); |
| 57 | APInt Masked(NewVal & Mask); |
| 58 | |
| 59 | // Change in the bits above the sign |
| 60 | if (!(Masked == Mask || Masked == 0)) { |
| 61 | // Found overflow in the bits above the sign |
| 62 | if (DstSema.isSaturated()) |
| 63 | NewVal = NewVal.isNegative() ? Mask : ~Mask; |
| 64 | else if (Overflow) |
| 65 | *Overflow = true; |
| 66 | } |
| 67 | |
| 68 | // If the dst semantics are unsigned, but our value is signed and negative, we |
| 69 | // clamp to zero. |
| 70 | if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) { |
| 71 | // Found negative overflow for unsigned result |
| 72 | if (DstSema.isSaturated()) |
| 73 | NewVal = 0; |
| 74 | else if (Overflow) |
| 75 | *Overflow = true; |
| 76 | } |
| 77 | |
| 78 | NewVal = NewVal.extOrTrunc(width: DstSema.getWidth()); |
| 79 | NewVal.setIsSigned(DstSema.isSigned()); |
| 80 | return APFixedPoint(NewVal, DstSema); |
| 81 | } |
| 82 | |
| 83 | int APFixedPoint::compare(const APFixedPoint &Other) const { |
| 84 | APSInt ThisVal = getValue(); |
| 85 | APSInt OtherVal = Other.getValue(); |
| 86 | bool ThisSigned = Val.isSigned(); |
| 87 | bool OtherSigned = OtherVal.isSigned(); |
| 88 | |
| 89 | int CommonLsb = std::min(a: getLsbWeight(), b: Other.getLsbWeight()); |
| 90 | int CommonMsb = std::max(a: getMsbWeight(), b: Other.getMsbWeight()); |
| 91 | unsigned CommonWidth = CommonMsb - CommonLsb + 1; |
| 92 | |
| 93 | ThisVal = ThisVal.extOrTrunc(width: CommonWidth); |
| 94 | OtherVal = OtherVal.extOrTrunc(width: CommonWidth); |
| 95 | |
| 96 | ThisVal = ThisVal.shl(shiftAmt: getLsbWeight() - CommonLsb); |
| 97 | OtherVal = OtherVal.shl(shiftAmt: Other.getLsbWeight() - CommonLsb); |
| 98 | |
| 99 | if (ThisSigned && OtherSigned) { |
| 100 | if (ThisVal.sgt(RHS: OtherVal)) |
| 101 | return 1; |
| 102 | else if (ThisVal.slt(RHS: OtherVal)) |
| 103 | return -1; |
| 104 | } else if (!ThisSigned && !OtherSigned) { |
| 105 | if (ThisVal.ugt(RHS: OtherVal)) |
| 106 | return 1; |
| 107 | else if (ThisVal.ult(RHS: OtherVal)) |
| 108 | return -1; |
| 109 | } else if (ThisSigned && !OtherSigned) { |
| 110 | if (ThisVal.isSignBitSet()) |
| 111 | return -1; |
| 112 | else if (ThisVal.ugt(RHS: OtherVal)) |
| 113 | return 1; |
| 114 | else if (ThisVal.ult(RHS: OtherVal)) |
| 115 | return -1; |
| 116 | } else { |
| 117 | // !ThisSigned && OtherSigned |
| 118 | if (OtherVal.isSignBitSet()) |
| 119 | return 1; |
| 120 | else if (ThisVal.ugt(RHS: OtherVal)) |
| 121 | return 1; |
| 122 | else if (ThisVal.ult(RHS: OtherVal)) |
| 123 | return -1; |
| 124 | } |
| 125 | |
| 126 | return 0; |
| 127 | } |
| 128 | |
| 129 | APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) { |
| 130 | bool IsUnsigned = !Sema.isSigned(); |
| 131 | auto Val = APSInt::getMaxValue(numBits: Sema.getWidth(), Unsigned: IsUnsigned); |
| 132 | if (IsUnsigned && Sema.hasUnsignedPadding()) |
| 133 | Val = Val.lshr(shiftAmt: 1); |
| 134 | return APFixedPoint(Val, Sema); |
| 135 | } |
| 136 | |
| 137 | APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) { |
| 138 | auto Val = APSInt::getMinValue(numBits: Sema.getWidth(), Unsigned: !Sema.isSigned()); |
| 139 | return APFixedPoint(Val, Sema); |
| 140 | } |
| 141 | |
| 142 | APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) { |
| 143 | APSInt Val(Sema.getWidth(), !Sema.isSigned()); |
| 144 | Val.setBit(/*BitPosition=*/0); |
| 145 | return APFixedPoint(Val, Sema); |
| 146 | } |
| 147 | |
| 148 | bool FixedPointSemantics::fitsInFloatSemantics( |
| 149 | const fltSemantics &FloatSema) const { |
| 150 | // A fixed point semantic fits in a floating point semantic if the maximum |
| 151 | // and minimum values as integers of the fixed point semantic can fit in the |
| 152 | // floating point semantic. |
| 153 | |
| 154 | // If these values do not fit, then a floating point rescaling of the true |
| 155 | // maximum/minimum value will not fit either, so the floating point semantic |
| 156 | // cannot be used to perform such a rescaling. |
| 157 | |
| 158 | APSInt MaxInt = APFixedPoint::getMax(Sema: *this).getValue(); |
| 159 | APFloat F(FloatSema); |
| 160 | APFloat::opStatus Status = F.convertFromAPInt(Input: MaxInt, IsSigned: MaxInt.isSigned(), |
| 161 | RM: APFloat::rmNearestTiesToAway); |
| 162 | if ((Status & APFloat::opOverflow) || !isSigned()) |
| 163 | return !(Status & APFloat::opOverflow); |
| 164 | |
| 165 | APSInt MinInt = APFixedPoint::getMin(Sema: *this).getValue(); |
| 166 | Status = F.convertFromAPInt(Input: MinInt, IsSigned: MinInt.isSigned(), |
| 167 | RM: APFloat::rmNearestTiesToAway); |
| 168 | return !(Status & APFloat::opOverflow); |
| 169 | } |
| 170 | |
| 171 | FixedPointSemantics FixedPointSemantics::getCommonSemantics( |
| 172 | const FixedPointSemantics &Other) const { |
| 173 | int CommonLsb = std::min(a: getLsbWeight(), b: Other.getLsbWeight()); |
| 174 | int CommonMSb = std::max(a: getMsbWeight() - hasSignOrPaddingBit(), |
| 175 | b: Other.getMsbWeight() - Other.hasSignOrPaddingBit()); |
| 176 | unsigned CommonWidth = CommonMSb - CommonLsb + 1; |
| 177 | |
| 178 | bool ResultIsSigned = isSigned() || Other.isSigned(); |
| 179 | bool ResultIsSaturated = isSaturated() || Other.isSaturated(); |
| 180 | bool ResultHasUnsignedPadding = false; |
| 181 | if (!ResultIsSigned) { |
| 182 | // Both are unsigned. |
| 183 | ResultHasUnsignedPadding = hasUnsignedPadding() && |
| 184 | Other.hasUnsignedPadding() && !ResultIsSaturated; |
| 185 | } |
| 186 | |
| 187 | // If the result is signed, add an extra bit for the sign. Otherwise, if it is |
| 188 | // unsigned and has unsigned padding, we only need to add the extra padding |
| 189 | // bit back if we are not saturating. |
| 190 | if (ResultIsSigned || ResultHasUnsignedPadding) |
| 191 | CommonWidth++; |
| 192 | |
| 193 | return FixedPointSemantics(CommonWidth, Lsb{.LsbWeight: CommonLsb}, ResultIsSigned, |
| 194 | ResultIsSaturated, ResultHasUnsignedPadding); |
| 195 | } |
| 196 | |
| 197 | APFixedPoint APFixedPoint::add(const APFixedPoint &Other, |
| 198 | bool *Overflow) const { |
| 199 | auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics()); |
| 200 | APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema); |
| 201 | APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema); |
| 202 | APSInt ThisVal = ConvertedThis.getValue(); |
| 203 | APSInt OtherVal = ConvertedOther.getValue(); |
| 204 | bool Overflowed = false; |
| 205 | |
| 206 | APSInt Result; |
| 207 | if (CommonFXSema.isSaturated()) { |
| 208 | Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(RHS: OtherVal) |
| 209 | : ThisVal.uadd_sat(RHS: OtherVal); |
| 210 | } else { |
| 211 | Result = ThisVal.isSigned() ? ThisVal.sadd_ov(RHS: OtherVal, Overflow&: Overflowed) |
| 212 | : ThisVal.uadd_ov(RHS: OtherVal, Overflow&: Overflowed); |
| 213 | } |
| 214 | |
| 215 | if (Overflow) |
| 216 | *Overflow = Overflowed; |
| 217 | |
| 218 | return APFixedPoint(Result, CommonFXSema); |
| 219 | } |
| 220 | |
| 221 | APFixedPoint APFixedPoint::sub(const APFixedPoint &Other, |
| 222 | bool *Overflow) const { |
| 223 | auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics()); |
| 224 | APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema); |
| 225 | APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema); |
| 226 | APSInt ThisVal = ConvertedThis.getValue(); |
| 227 | APSInt OtherVal = ConvertedOther.getValue(); |
| 228 | bool Overflowed = false; |
| 229 | |
| 230 | APSInt Result; |
| 231 | if (CommonFXSema.isSaturated()) { |
| 232 | Result = CommonFXSema.isSigned() ? ThisVal.ssub_sat(RHS: OtherVal) |
| 233 | : ThisVal.usub_sat(RHS: OtherVal); |
| 234 | } else { |
| 235 | Result = ThisVal.isSigned() ? ThisVal.ssub_ov(RHS: OtherVal, Overflow&: Overflowed) |
| 236 | : ThisVal.usub_ov(RHS: OtherVal, Overflow&: Overflowed); |
| 237 | } |
| 238 | |
| 239 | if (Overflow) |
| 240 | *Overflow = Overflowed; |
| 241 | |
| 242 | return APFixedPoint(Result, CommonFXSema); |
| 243 | } |
| 244 | |
| 245 | APFixedPoint APFixedPoint::mul(const APFixedPoint &Other, |
| 246 | bool *Overflow) const { |
| 247 | auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics()); |
| 248 | APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema); |
| 249 | APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema); |
| 250 | APSInt ThisVal = ConvertedThis.getValue(); |
| 251 | APSInt OtherVal = ConvertedOther.getValue(); |
| 252 | bool Overflowed = false; |
| 253 | |
| 254 | // Widen the LHS and RHS so we can perform a full multiplication. |
| 255 | unsigned Wide = CommonFXSema.getWidth() * 2; |
| 256 | if (CommonFXSema.isSigned()) { |
| 257 | ThisVal = ThisVal.sext(width: Wide); |
| 258 | OtherVal = OtherVal.sext(width: Wide); |
| 259 | } else { |
| 260 | ThisVal = ThisVal.zext(width: Wide); |
| 261 | OtherVal = OtherVal.zext(width: Wide); |
| 262 | } |
| 263 | |
| 264 | // Perform the full multiplication and downscale to get the same scale. |
| 265 | // |
| 266 | // Note that the right shifts here perform an implicit downwards rounding. |
| 267 | // This rounding could discard bits that would technically place the result |
| 268 | // outside the representable range. We interpret the spec as allowing us to |
| 269 | // perform the rounding step first, avoiding the overflow case that would |
| 270 | // arise. |
| 271 | APSInt Result; |
| 272 | if (CommonFXSema.isSigned()) |
| 273 | Result = ThisVal.smul_ov(RHS: OtherVal, Overflow&: Overflowed) |
| 274 | .relativeAShl(RelativeShift: CommonFXSema.getLsbWeight()); |
| 275 | else |
| 276 | Result = ThisVal.umul_ov(RHS: OtherVal, Overflow&: Overflowed) |
| 277 | .relativeLShl(RelativeShift: CommonFXSema.getLsbWeight()); |
| 278 | assert(!Overflowed && "Full multiplication cannot overflow!" ); |
| 279 | Result.setIsSigned(CommonFXSema.isSigned()); |
| 280 | |
| 281 | // If our result lies outside of the representative range of the common |
| 282 | // semantic, we either have overflow or saturation. |
| 283 | APSInt Max = APFixedPoint::getMax(Sema: CommonFXSema).getValue() |
| 284 | .extOrTrunc(width: Wide); |
| 285 | APSInt Min = APFixedPoint::getMin(Sema: CommonFXSema).getValue() |
| 286 | .extOrTrunc(width: Wide); |
| 287 | if (CommonFXSema.isSaturated()) { |
| 288 | if (Result < Min) |
| 289 | Result = Min; |
| 290 | else if (Result > Max) |
| 291 | Result = Max; |
| 292 | } else { |
| 293 | Overflowed = Result < Min || Result > Max; |
| 294 | } |
| 295 | |
| 296 | if (Overflow) |
| 297 | *Overflow = Overflowed; |
| 298 | |
| 299 | return APFixedPoint(Result.sextOrTrunc(width: CommonFXSema.getWidth()), |
| 300 | CommonFXSema); |
| 301 | } |
| 302 | |
| 303 | APFixedPoint APFixedPoint::div(const APFixedPoint &Other, |
| 304 | bool *Overflow) const { |
| 305 | auto CommonFXSema = Sema.getCommonSemantics(Other: Other.getSemantics()); |
| 306 | APFixedPoint ConvertedThis = convert(DstSema: CommonFXSema); |
| 307 | APFixedPoint ConvertedOther = Other.convert(DstSema: CommonFXSema); |
| 308 | APSInt ThisVal = ConvertedThis.getValue(); |
| 309 | APSInt OtherVal = ConvertedOther.getValue(); |
| 310 | bool Overflowed = false; |
| 311 | |
| 312 | // Widen the LHS and RHS so we can perform a full division. |
| 313 | // Also make sure that there will be enough space for the shift below to not |
| 314 | // overflow |
| 315 | unsigned Wide = |
| 316 | CommonFXSema.getWidth() * 2 + std::max(a: -CommonFXSema.getMsbWeight(), b: 0); |
| 317 | if (CommonFXSema.isSigned()) { |
| 318 | ThisVal = ThisVal.sext(width: Wide); |
| 319 | OtherVal = OtherVal.sext(width: Wide); |
| 320 | } else { |
| 321 | ThisVal = ThisVal.zext(width: Wide); |
| 322 | OtherVal = OtherVal.zext(width: Wide); |
| 323 | } |
| 324 | |
| 325 | // Upscale to compensate for the loss of precision from division, and |
| 326 | // perform the full division. |
| 327 | if (CommonFXSema.getLsbWeight() < 0) |
| 328 | ThisVal = ThisVal.shl(shiftAmt: -CommonFXSema.getLsbWeight()); |
| 329 | else if (CommonFXSema.getLsbWeight() > 0) |
| 330 | OtherVal = OtherVal.shl(shiftAmt: CommonFXSema.getLsbWeight()); |
| 331 | APSInt Result; |
| 332 | if (CommonFXSema.isSigned()) { |
| 333 | APInt Rem; |
| 334 | APInt::sdivrem(LHS: ThisVal, RHS: OtherVal, Quotient&: Result, Remainder&: Rem); |
| 335 | // If the quotient is negative and the remainder is nonzero, round |
| 336 | // towards negative infinity by subtracting epsilon from the result. |
| 337 | if (ThisVal.isNegative() != OtherVal.isNegative() && !Rem.isZero()) |
| 338 | Result = Result - 1; |
| 339 | } else { |
| 340 | Result = ThisVal.udiv(RHS: OtherVal); |
| 341 | } |
| 342 | Result.setIsSigned(CommonFXSema.isSigned()); |
| 343 | |
| 344 | // If our result lies outside of the representative range of the common |
| 345 | // semantic, we either have overflow or saturation. |
| 346 | APSInt Max = APFixedPoint::getMax(Sema: CommonFXSema).getValue() |
| 347 | .extOrTrunc(width: Wide); |
| 348 | APSInt Min = APFixedPoint::getMin(Sema: CommonFXSema).getValue() |
| 349 | .extOrTrunc(width: Wide); |
| 350 | if (CommonFXSema.isSaturated()) { |
| 351 | if (Result < Min) |
| 352 | Result = Min; |
| 353 | else if (Result > Max) |
| 354 | Result = Max; |
| 355 | } else { |
| 356 | Overflowed = Result < Min || Result > Max; |
| 357 | } |
| 358 | |
| 359 | if (Overflow) |
| 360 | *Overflow = Overflowed; |
| 361 | |
| 362 | return APFixedPoint(Result.sextOrTrunc(width: CommonFXSema.getWidth()), |
| 363 | CommonFXSema); |
| 364 | } |
| 365 | |
| 366 | APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const { |
| 367 | APSInt ThisVal = Val; |
| 368 | bool Overflowed = false; |
| 369 | |
| 370 | // Widen the LHS. |
| 371 | unsigned Wide = Sema.getWidth() * 2; |
| 372 | if (Sema.isSigned()) |
| 373 | ThisVal = ThisVal.sext(width: Wide); |
| 374 | else |
| 375 | ThisVal = ThisVal.zext(width: Wide); |
| 376 | |
| 377 | // Clamp the shift amount at the original width, and perform the shift. |
| 378 | Amt = std::min(a: Amt, b: ThisVal.getBitWidth()); |
| 379 | APSInt Result = ThisVal << Amt; |
| 380 | Result.setIsSigned(Sema.isSigned()); |
| 381 | |
| 382 | // If our result lies outside of the representative range of the |
| 383 | // semantic, we either have overflow or saturation. |
| 384 | APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(width: Wide); |
| 385 | APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(width: Wide); |
| 386 | if (Sema.isSaturated()) { |
| 387 | if (Result < Min) |
| 388 | Result = Min; |
| 389 | else if (Result > Max) |
| 390 | Result = Max; |
| 391 | } else { |
| 392 | Overflowed = Result < Min || Result > Max; |
| 393 | } |
| 394 | |
| 395 | if (Overflow) |
| 396 | *Overflow = Overflowed; |
| 397 | |
| 398 | return APFixedPoint(Result.sextOrTrunc(width: Sema.getWidth()), Sema); |
| 399 | } |
| 400 | |
| 401 | void APFixedPoint::toString(SmallVectorImpl<char> &Str) const { |
| 402 | APSInt Val = getValue(); |
| 403 | int Lsb = getLsbWeight(); |
| 404 | int OrigWidth = getWidth(); |
| 405 | |
| 406 | if (Lsb >= 0) { |
| 407 | APSInt IntPart = Val; |
| 408 | IntPart = IntPart.extend(width: IntPart.getBitWidth() + Lsb); |
| 409 | IntPart <<= Lsb; |
| 410 | IntPart.toString(Str, /*Radix=*/10); |
| 411 | Str.push_back(Elt: '.'); |
| 412 | Str.push_back(Elt: '0'); |
| 413 | return; |
| 414 | } |
| 415 | |
| 416 | if (Val.isSigned() && Val.isNegative()) { |
| 417 | Val = -Val; |
| 418 | Val.setIsUnsigned(true); |
| 419 | Str.push_back(Elt: '-'); |
| 420 | } |
| 421 | |
| 422 | int Scale = -getLsbWeight(); |
| 423 | APSInt IntPart = (OrigWidth > Scale) ? (Val >> Scale) : APSInt::get(X: 0); |
| 424 | |
| 425 | // Add 4 digits to hold the value after multiplying 10 (the radix) |
| 426 | unsigned Width = std::max(a: OrigWidth, b: Scale) + 4; |
| 427 | APInt FractPart = Val.zextOrTrunc(width: Scale).zext(width: Width); |
| 428 | APInt FractPartMask = APInt::getAllOnes(numBits: Scale).zext(width: Width); |
| 429 | APInt RadixInt = APInt(Width, 10); |
| 430 | |
| 431 | IntPart.toString(Str, /*Radix=*/10); |
| 432 | Str.push_back(Elt: '.'); |
| 433 | do { |
| 434 | (FractPart * RadixInt) |
| 435 | .lshr(shiftAmt: Scale) |
| 436 | .toString(Str, /*Radix=*/10, Signed: Val.isSigned()); |
| 437 | FractPart = (FractPart * RadixInt) & FractPartMask; |
| 438 | } while (FractPart != 0); |
| 439 | } |
| 440 | |
| 441 | void APFixedPoint::print(raw_ostream &OS) const { |
| 442 | OS << "APFixedPoint(" << toString() << ", {" ; |
| 443 | Sema.print(OS); |
| 444 | OS << "})" ; |
| 445 | } |
| 446 | |
| 447 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| 448 | LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(llvm::errs()); } |
| 449 | #endif |
| 450 | |
| 451 | APFixedPoint APFixedPoint::negate(bool *Overflow) const { |
| 452 | if (!isSaturated()) { |
| 453 | if (Overflow) |
| 454 | *Overflow = |
| 455 | (!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue()); |
| 456 | return APFixedPoint(-Val, Sema); |
| 457 | } |
| 458 | |
| 459 | // We never overflow for saturation |
| 460 | if (Overflow) |
| 461 | *Overflow = false; |
| 462 | |
| 463 | if (isSigned()) |
| 464 | return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema); |
| 465 | else |
| 466 | return APFixedPoint(Sema); |
| 467 | } |
| 468 | |
| 469 | APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign, |
| 470 | bool *Overflow) const { |
| 471 | APSInt Result = getIntPart(); |
| 472 | unsigned SrcWidth = getWidth(); |
| 473 | |
| 474 | APSInt DstMin = APSInt::getMinValue(numBits: DstWidth, Unsigned: !DstSign); |
| 475 | APSInt DstMax = APSInt::getMaxValue(numBits: DstWidth, Unsigned: !DstSign); |
| 476 | |
| 477 | if (SrcWidth < DstWidth) { |
| 478 | Result = Result.extend(width: DstWidth); |
| 479 | } else if (SrcWidth > DstWidth) { |
| 480 | DstMin = DstMin.extend(width: SrcWidth); |
| 481 | DstMax = DstMax.extend(width: SrcWidth); |
| 482 | } |
| 483 | |
| 484 | if (Overflow) { |
| 485 | if (Result.isSigned() && !DstSign) { |
| 486 | *Overflow = Result.isNegative() || Result.ugt(RHS: DstMax); |
| 487 | } else if (Result.isUnsigned() && DstSign) { |
| 488 | *Overflow = Result.ugt(RHS: DstMax); |
| 489 | } else { |
| 490 | *Overflow = Result < DstMin || Result > DstMax; |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | Result.setIsSigned(DstSign); |
| 495 | return Result.extOrTrunc(width: DstWidth); |
| 496 | } |
| 497 | |
| 498 | const fltSemantics *APFixedPoint::promoteFloatSemantics(const fltSemantics *S) { |
| 499 | if (S == &APFloat::BFloat()) |
| 500 | return &APFloat::IEEEdouble(); |
| 501 | else if (S == &APFloat::IEEEhalf()) |
| 502 | return &APFloat::IEEEsingle(); |
| 503 | else if (S == &APFloat::IEEEsingle()) |
| 504 | return &APFloat::IEEEdouble(); |
| 505 | else if (S == &APFloat::IEEEdouble()) |
| 506 | return &APFloat::IEEEquad(); |
| 507 | llvm_unreachable("Could not promote float type!" ); |
| 508 | } |
| 509 | |
| 510 | APFloat APFixedPoint::convertToFloat(const fltSemantics &FloatSema) const { |
| 511 | // For some operations, rounding mode has an effect on the result, while |
| 512 | // other operations are lossless and should never result in rounding. |
| 513 | // To signify which these operations are, we define two rounding modes here. |
| 514 | APFloat::roundingMode RM = APFloat::rmNearestTiesToEven; |
| 515 | APFloat::roundingMode LosslessRM = APFloat::rmTowardZero; |
| 516 | |
| 517 | // Make sure that we are operating in a type that works with this fixed-point |
| 518 | // semantic. |
| 519 | const fltSemantics *OpSema = &FloatSema; |
| 520 | while (!Sema.fitsInFloatSemantics(FloatSema: *OpSema)) |
| 521 | OpSema = promoteFloatSemantics(S: OpSema); |
| 522 | |
| 523 | // Convert the fixed point value bits as an integer. If the floating point |
| 524 | // value does not have the required precision, we will round according to the |
| 525 | // given mode. |
| 526 | APFloat Flt(*OpSema); |
| 527 | APFloat::opStatus S = Flt.convertFromAPInt(Input: Val, IsSigned: Sema.isSigned(), RM); |
| 528 | |
| 529 | // If we cared about checking for precision loss, we could look at this |
| 530 | // status. |
| 531 | (void)S; |
| 532 | |
| 533 | // Scale down the integer value in the float to match the correct scaling |
| 534 | // factor. |
| 535 | APFloat ScaleFactor(std::pow(x: 2, y: Sema.getLsbWeight())); |
| 536 | bool Ignored; |
| 537 | ScaleFactor.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored); |
| 538 | Flt.multiply(RHS: ScaleFactor, RM: LosslessRM); |
| 539 | |
| 540 | if (OpSema != &FloatSema) |
| 541 | Flt.convert(ToSemantics: FloatSema, RM, losesInfo: &Ignored); |
| 542 | |
| 543 | return Flt; |
| 544 | } |
| 545 | |
| 546 | APFixedPoint APFixedPoint::getFromIntValue(const APSInt &Value, |
| 547 | const FixedPointSemantics &DstFXSema, |
| 548 | bool *Overflow) { |
| 549 | FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics( |
| 550 | Width: Value.getBitWidth(), IsSigned: Value.isSigned()); |
| 551 | return APFixedPoint(Value, IntFXSema).convert(DstSema: DstFXSema, Overflow); |
| 552 | } |
| 553 | |
| 554 | APFixedPoint |
| 555 | APFixedPoint::getFromFloatValue(const APFloat &Value, |
| 556 | const FixedPointSemantics &DstFXSema, |
| 557 | bool *Overflow) { |
| 558 | // For some operations, rounding mode has an effect on the result, while |
| 559 | // other operations are lossless and should never result in rounding. |
| 560 | // To signify which these operations are, we define two rounding modes here, |
| 561 | // even though they are the same mode. |
| 562 | APFloat::roundingMode RM = APFloat::rmTowardZero; |
| 563 | APFloat::roundingMode LosslessRM = APFloat::rmTowardZero; |
| 564 | |
| 565 | const fltSemantics &FloatSema = Value.getSemantics(); |
| 566 | |
| 567 | if (Value.isNaN()) { |
| 568 | // Handle NaN immediately. |
| 569 | if (Overflow) |
| 570 | *Overflow = true; |
| 571 | return APFixedPoint(DstFXSema); |
| 572 | } |
| 573 | |
| 574 | // Make sure that we are operating in a type that works with this fixed-point |
| 575 | // semantic. |
| 576 | const fltSemantics *OpSema = &FloatSema; |
| 577 | while (!DstFXSema.fitsInFloatSemantics(FloatSema: *OpSema)) |
| 578 | OpSema = promoteFloatSemantics(S: OpSema); |
| 579 | |
| 580 | APFloat Val = Value; |
| 581 | |
| 582 | bool Ignored; |
| 583 | if (&FloatSema != OpSema) |
| 584 | Val.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored); |
| 585 | |
| 586 | // Scale up the float so that the 'fractional' part of the mantissa ends up in |
| 587 | // the integer range instead. Rounding mode is irrelevant here. |
| 588 | // It is fine if this overflows to infinity even for saturating types, |
| 589 | // since we will use floating point comparisons to check for saturation. |
| 590 | APFloat ScaleFactor(std::pow(x: 2, y: -DstFXSema.getLsbWeight())); |
| 591 | ScaleFactor.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored); |
| 592 | Val.multiply(RHS: ScaleFactor, RM: LosslessRM); |
| 593 | |
| 594 | // Convert to the integral representation of the value. This rounding mode |
| 595 | // is significant. |
| 596 | APSInt Res(DstFXSema.getWidth(), !DstFXSema.isSigned()); |
| 597 | Val.convertToInteger(Result&: Res, RM, IsExact: &Ignored); |
| 598 | |
| 599 | // Round the integral value and scale back. This makes the |
| 600 | // overflow calculations below work properly. If we do not round here, |
| 601 | // we risk checking for overflow with a value that is outside the |
| 602 | // representable range of the fixed-point semantic even though no overflow |
| 603 | // would occur had we rounded first. |
| 604 | ScaleFactor = APFloat(std::pow(x: 2, y: DstFXSema.getLsbWeight())); |
| 605 | ScaleFactor.convert(ToSemantics: *OpSema, RM: LosslessRM, losesInfo: &Ignored); |
| 606 | Val.roundToIntegral(RM); |
| 607 | Val.multiply(RHS: ScaleFactor, RM: LosslessRM); |
| 608 | |
| 609 | // Check for overflow/saturation by checking if the floating point value |
| 610 | // is outside the range representable by the fixed-point value. |
| 611 | APFloat FloatMax = getMax(Sema: DstFXSema).convertToFloat(FloatSema: *OpSema); |
| 612 | APFloat FloatMin = getMin(Sema: DstFXSema).convertToFloat(FloatSema: *OpSema); |
| 613 | bool Overflowed = false; |
| 614 | if (DstFXSema.isSaturated()) { |
| 615 | if (Val > FloatMax) |
| 616 | Res = getMax(Sema: DstFXSema).getValue(); |
| 617 | else if (Val < FloatMin) |
| 618 | Res = getMin(Sema: DstFXSema).getValue(); |
| 619 | } else { |
| 620 | Overflowed = Val > FloatMax || Val < FloatMin; |
| 621 | } |
| 622 | |
| 623 | if (Overflow) |
| 624 | *Overflow = Overflowed; |
| 625 | |
| 626 | return APFixedPoint(Res, DstFXSema); |
| 627 | } |
| 628 | |
| 629 | } // namespace llvm |
| 630 | |