1 //===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 /// Defines the implementation for the fixed point number interface.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/ADT/APFixedPoint.h"
15 #include "llvm/ADT/APFloat.h"
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
;
32 uint32_t FixedPointSemantics::toOpaqueInt() const {
33 return llvm::bit_cast
<uint32_t>(*this);
36 FixedPointSemantics
FixedPointSemantics::getFromOpaqueInt(uint32_t I
) {
37 FixedPointSemantics
F(0, 0, false, false, false);
38 std::memcpy(&F
, &I
, sizeof(F
));
42 APFixedPoint
APFixedPoint::convert(const FixedPointSemantics
&DstSema
,
43 bool *Overflow
) const {
45 int RelativeUpscale
= getLsbWeight() - DstSema
.getLsbWeight();
49 if (RelativeUpscale
> 0)
50 NewVal
= NewVal
.extend(NewVal
.getBitWidth() + RelativeUpscale
);
51 NewVal
= NewVal
.relativeShl(RelativeUpscale
);
53 auto Mask
= APInt::getBitsSetFrom(
55 std::min(DstSema
.getIntegralBits() - DstSema
.getLsbWeight(),
56 NewVal
.getBitWidth()));
57 APInt
Masked(NewVal
& Mask
);
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
;
68 // If the dst semantics are unsigned, but our value is signed and negative, we
70 if (!DstSema
.isSigned() && NewVal
.isSigned() && NewVal
.isNegative()) {
71 // Found negative overflow for unsigned result
72 if (DstSema
.isSaturated())
78 NewVal
= NewVal
.extOrTrunc(DstSema
.getWidth());
79 NewVal
.setIsSigned(DstSema
.isSigned());
80 return APFixedPoint(NewVal
, DstSema
);
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();
89 int CommonLsb
= std::min(getLsbWeight(), Other
.getLsbWeight());
90 int CommonMsb
= std::max(getMsbWeight(), Other
.getMsbWeight());
91 unsigned CommonWidth
= CommonMsb
- CommonLsb
+ 1;
93 ThisVal
= ThisVal
.extOrTrunc(CommonWidth
);
94 OtherVal
= OtherVal
.extOrTrunc(CommonWidth
);
96 ThisVal
= ThisVal
.shl(getLsbWeight() - CommonLsb
);
97 OtherVal
= OtherVal
.shl(Other
.getLsbWeight() - CommonLsb
);
99 if (ThisSigned
&& OtherSigned
) {
100 if (ThisVal
.sgt(OtherVal
))
102 else if (ThisVal
.slt(OtherVal
))
104 } else if (!ThisSigned
&& !OtherSigned
) {
105 if (ThisVal
.ugt(OtherVal
))
107 else if (ThisVal
.ult(OtherVal
))
109 } else if (ThisSigned
&& !OtherSigned
) {
110 if (ThisVal
.isSignBitSet())
112 else if (ThisVal
.ugt(OtherVal
))
114 else if (ThisVal
.ult(OtherVal
))
117 // !ThisSigned && OtherSigned
118 if (OtherVal
.isSignBitSet())
120 else if (ThisVal
.ugt(OtherVal
))
122 else if (ThisVal
.ult(OtherVal
))
129 APFixedPoint
APFixedPoint::getMax(const FixedPointSemantics
&Sema
) {
130 bool IsUnsigned
= !Sema
.isSigned();
131 auto Val
= APSInt::getMaxValue(Sema
.getWidth(), IsUnsigned
);
132 if (IsUnsigned
&& Sema
.hasUnsignedPadding())
134 return APFixedPoint(Val
, Sema
);
137 APFixedPoint
APFixedPoint::getMin(const FixedPointSemantics
&Sema
) {
138 auto Val
= APSInt::getMinValue(Sema
.getWidth(), !Sema
.isSigned());
139 return APFixedPoint(Val
, Sema
);
142 APFixedPoint
APFixedPoint::getEpsilon(const FixedPointSemantics
&Sema
) {
143 APSInt
Val(Sema
.getWidth(), !Sema
.isSigned());
144 Val
.setBit(/*BitPosition=*/0);
145 return APFixedPoint(Val
, Sema
);
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.
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.
158 APSInt MaxInt
= APFixedPoint::getMax(*this).getValue();
159 APFloat
F(FloatSema
);
160 APFloat::opStatus Status
= F
.convertFromAPInt(MaxInt
, MaxInt
.isSigned(),
161 APFloat::rmNearestTiesToAway
);
162 if ((Status
& APFloat::opOverflow
) || !isSigned())
163 return !(Status
& APFloat::opOverflow
);
165 APSInt MinInt
= APFixedPoint::getMin(*this).getValue();
166 Status
= F
.convertFromAPInt(MinInt
, MinInt
.isSigned(),
167 APFloat::rmNearestTiesToAway
);
168 return !(Status
& APFloat::opOverflow
);
171 FixedPointSemantics
FixedPointSemantics::getCommonSemantics(
172 const FixedPointSemantics
&Other
) const {
173 int CommonLsb
= std::min(getLsbWeight(), Other
.getLsbWeight());
174 int CommonMSb
= std::max(getMsbWeight() - hasSignOrPaddingBit(),
175 Other
.getMsbWeight() - Other
.hasSignOrPaddingBit());
176 unsigned CommonWidth
= CommonMSb
- CommonLsb
+ 1;
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
;
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
)
193 return FixedPointSemantics(CommonWidth
, Lsb
{CommonLsb
}, ResultIsSigned
,
194 ResultIsSaturated
, ResultHasUnsignedPadding
);
197 APFixedPoint
APFixedPoint::add(const APFixedPoint
&Other
,
198 bool *Overflow
) const {
199 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
200 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
201 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
202 APSInt ThisVal
= ConvertedThis
.getValue();
203 APSInt OtherVal
= ConvertedOther
.getValue();
204 bool Overflowed
= false;
207 if (CommonFXSema
.isSaturated()) {
208 Result
= CommonFXSema
.isSigned() ? ThisVal
.sadd_sat(OtherVal
)
209 : ThisVal
.uadd_sat(OtherVal
);
211 Result
= ThisVal
.isSigned() ? ThisVal
.sadd_ov(OtherVal
, Overflowed
)
212 : ThisVal
.uadd_ov(OtherVal
, Overflowed
);
216 *Overflow
= Overflowed
;
218 return APFixedPoint(Result
, CommonFXSema
);
221 APFixedPoint
APFixedPoint::sub(const APFixedPoint
&Other
,
222 bool *Overflow
) const {
223 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
224 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
225 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
226 APSInt ThisVal
= ConvertedThis
.getValue();
227 APSInt OtherVal
= ConvertedOther
.getValue();
228 bool Overflowed
= false;
231 if (CommonFXSema
.isSaturated()) {
232 Result
= CommonFXSema
.isSigned() ? ThisVal
.ssub_sat(OtherVal
)
233 : ThisVal
.usub_sat(OtherVal
);
235 Result
= ThisVal
.isSigned() ? ThisVal
.ssub_ov(OtherVal
, Overflowed
)
236 : ThisVal
.usub_ov(OtherVal
, Overflowed
);
240 *Overflow
= Overflowed
;
242 return APFixedPoint(Result
, CommonFXSema
);
245 APFixedPoint
APFixedPoint::mul(const APFixedPoint
&Other
,
246 bool *Overflow
) const {
247 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
248 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
249 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
250 APSInt ThisVal
= ConvertedThis
.getValue();
251 APSInt OtherVal
= ConvertedOther
.getValue();
252 bool Overflowed
= false;
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(Wide
);
258 OtherVal
= OtherVal
.sext(Wide
);
260 ThisVal
= ThisVal
.zext(Wide
);
261 OtherVal
= OtherVal
.zext(Wide
);
264 // Perform the full multiplication and downscale to get the same scale.
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
272 if (CommonFXSema
.isSigned())
273 Result
= ThisVal
.smul_ov(OtherVal
, Overflowed
)
274 .relativeAShl(CommonFXSema
.getLsbWeight());
276 Result
= ThisVal
.umul_ov(OtherVal
, Overflowed
)
277 .relativeLShl(CommonFXSema
.getLsbWeight());
278 assert(!Overflowed
&& "Full multiplication cannot overflow!");
279 Result
.setIsSigned(CommonFXSema
.isSigned());
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(CommonFXSema
).getValue()
285 APSInt Min
= APFixedPoint::getMin(CommonFXSema
).getValue()
287 if (CommonFXSema
.isSaturated()) {
290 else if (Result
> Max
)
293 Overflowed
= Result
< Min
|| Result
> Max
;
296 *Overflow
= Overflowed
;
298 return APFixedPoint(Result
.sextOrTrunc(CommonFXSema
.getWidth()),
302 APFixedPoint
APFixedPoint::div(const APFixedPoint
&Other
,
303 bool *Overflow
) const {
304 auto CommonFXSema
= Sema
.getCommonSemantics(Other
.getSemantics());
305 APFixedPoint ConvertedThis
= convert(CommonFXSema
);
306 APFixedPoint ConvertedOther
= Other
.convert(CommonFXSema
);
307 APSInt ThisVal
= ConvertedThis
.getValue();
308 APSInt OtherVal
= ConvertedOther
.getValue();
309 bool Overflowed
= false;
311 // Widen the LHS and RHS so we can perform a full division.
312 // Also make sure that there will be enough space for the shift below to not
315 CommonFXSema
.getWidth() * 2 + std::max(-CommonFXSema
.getMsbWeight(), 0);
316 if (CommonFXSema
.isSigned()) {
317 ThisVal
= ThisVal
.sext(Wide
);
318 OtherVal
= OtherVal
.sext(Wide
);
320 ThisVal
= ThisVal
.zext(Wide
);
321 OtherVal
= OtherVal
.zext(Wide
);
324 // Upscale to compensate for the loss of precision from division, and
325 // perform the full division.
326 if (CommonFXSema
.getLsbWeight() < 0)
327 ThisVal
= ThisVal
.shl(-CommonFXSema
.getLsbWeight());
328 else if (CommonFXSema
.getLsbWeight() > 0)
329 OtherVal
= OtherVal
.shl(CommonFXSema
.getLsbWeight());
331 if (CommonFXSema
.isSigned()) {
333 APInt::sdivrem(ThisVal
, OtherVal
, Result
, Rem
);
334 // If the quotient is negative and the remainder is nonzero, round
335 // towards negative infinity by subtracting epsilon from the result.
336 if (ThisVal
.isNegative() != OtherVal
.isNegative() && !Rem
.isZero())
339 Result
= ThisVal
.udiv(OtherVal
);
340 Result
.setIsSigned(CommonFXSema
.isSigned());
342 // If our result lies outside of the representative range of the common
343 // semantic, we either have overflow or saturation.
344 APSInt Max
= APFixedPoint::getMax(CommonFXSema
).getValue()
346 APSInt Min
= APFixedPoint::getMin(CommonFXSema
).getValue()
348 if (CommonFXSema
.isSaturated()) {
351 else if (Result
> Max
)
354 Overflowed
= Result
< Min
|| Result
> Max
;
357 *Overflow
= Overflowed
;
359 return APFixedPoint(Result
.sextOrTrunc(CommonFXSema
.getWidth()),
363 APFixedPoint
APFixedPoint::shl(unsigned Amt
, bool *Overflow
) const {
364 APSInt ThisVal
= Val
;
365 bool Overflowed
= false;
368 unsigned Wide
= Sema
.getWidth() * 2;
370 ThisVal
= ThisVal
.sext(Wide
);
372 ThisVal
= ThisVal
.zext(Wide
);
374 // Clamp the shift amount at the original width, and perform the shift.
375 Amt
= std::min(Amt
, ThisVal
.getBitWidth());
376 APSInt Result
= ThisVal
<< Amt
;
377 Result
.setIsSigned(Sema
.isSigned());
379 // If our result lies outside of the representative range of the
380 // semantic, we either have overflow or saturation.
381 APSInt Max
= APFixedPoint::getMax(Sema
).getValue().extOrTrunc(Wide
);
382 APSInt Min
= APFixedPoint::getMin(Sema
).getValue().extOrTrunc(Wide
);
383 if (Sema
.isSaturated()) {
386 else if (Result
> Max
)
389 Overflowed
= Result
< Min
|| Result
> Max
;
392 *Overflow
= Overflowed
;
394 return APFixedPoint(Result
.sextOrTrunc(Sema
.getWidth()), Sema
);
397 void APFixedPoint::toString(SmallVectorImpl
<char> &Str
) const {
398 APSInt Val
= getValue();
399 int Lsb
= getLsbWeight();
400 int OrigWidth
= getWidth();
403 APSInt IntPart
= Val
;
404 IntPart
= IntPart
.extend(IntPart
.getBitWidth() + Lsb
);
406 IntPart
.toString(Str
, /*Radix=*/10);
412 if (Val
.isSigned() && Val
.isNegative()) {
414 Val
.setIsUnsigned(true);
418 int Scale
= -getLsbWeight();
419 APSInt IntPart
= (OrigWidth
> Scale
) ? (Val
>> Scale
) : APSInt::get(0);
421 // Add 4 digits to hold the value after multiplying 10 (the radix)
422 unsigned Width
= std::max(OrigWidth
, Scale
) + 4;
423 APInt FractPart
= Val
.zextOrTrunc(Scale
).zext(Width
);
424 APInt FractPartMask
= APInt::getAllOnes(Scale
).zext(Width
);
425 APInt RadixInt
= APInt(Width
, 10);
427 IntPart
.toString(Str
, /*Radix=*/10);
430 (FractPart
* RadixInt
)
432 .toString(Str
, /*Radix=*/10, Val
.isSigned());
433 FractPart
= (FractPart
* RadixInt
) & FractPartMask
;
434 } while (FractPart
!= 0);
437 void APFixedPoint::print(raw_ostream
&OS
) const {
438 OS
<< "APFixedPoint(" << toString() << ", {";
442 LLVM_DUMP_METHOD
void APFixedPoint::dump() const { print(llvm::errs()); }
444 APFixedPoint
APFixedPoint::negate(bool *Overflow
) const {
445 if (!isSaturated()) {
448 (!isSigned() && Val
!= 0) || (isSigned() && Val
.isMinSignedValue());
449 return APFixedPoint(-Val
, Sema
);
452 // We never overflow for saturation
457 return Val
.isMinSignedValue() ? getMax(Sema
) : APFixedPoint(-Val
, Sema
);
459 return APFixedPoint(Sema
);
462 APSInt
APFixedPoint::convertToInt(unsigned DstWidth
, bool DstSign
,
463 bool *Overflow
) const {
464 APSInt Result
= getIntPart();
465 unsigned SrcWidth
= getWidth();
467 APSInt DstMin
= APSInt::getMinValue(DstWidth
, !DstSign
);
468 APSInt DstMax
= APSInt::getMaxValue(DstWidth
, !DstSign
);
470 if (SrcWidth
< DstWidth
) {
471 Result
= Result
.extend(DstWidth
);
472 } else if (SrcWidth
> DstWidth
) {
473 DstMin
= DstMin
.extend(SrcWidth
);
474 DstMax
= DstMax
.extend(SrcWidth
);
478 if (Result
.isSigned() && !DstSign
) {
479 *Overflow
= Result
.isNegative() || Result
.ugt(DstMax
);
480 } else if (Result
.isUnsigned() && DstSign
) {
481 *Overflow
= Result
.ugt(DstMax
);
483 *Overflow
= Result
< DstMin
|| Result
> DstMax
;
487 Result
.setIsSigned(DstSign
);
488 return Result
.extOrTrunc(DstWidth
);
491 const fltSemantics
*APFixedPoint::promoteFloatSemantics(const fltSemantics
*S
) {
492 if (S
== &APFloat::BFloat())
493 return &APFloat::IEEEdouble();
494 else if (S
== &APFloat::IEEEhalf())
495 return &APFloat::IEEEsingle();
496 else if (S
== &APFloat::IEEEsingle())
497 return &APFloat::IEEEdouble();
498 else if (S
== &APFloat::IEEEdouble())
499 return &APFloat::IEEEquad();
500 llvm_unreachable("Could not promote float type!");
503 APFloat
APFixedPoint::convertToFloat(const fltSemantics
&FloatSema
) const {
504 // For some operations, rounding mode has an effect on the result, while
505 // other operations are lossless and should never result in rounding.
506 // To signify which these operations are, we define two rounding modes here.
507 APFloat::roundingMode RM
= APFloat::rmNearestTiesToEven
;
508 APFloat::roundingMode LosslessRM
= APFloat::rmTowardZero
;
510 // Make sure that we are operating in a type that works with this fixed-point
512 const fltSemantics
*OpSema
= &FloatSema
;
513 while (!Sema
.fitsInFloatSemantics(*OpSema
))
514 OpSema
= promoteFloatSemantics(OpSema
);
516 // Convert the fixed point value bits as an integer. If the floating point
517 // value does not have the required precision, we will round according to the
519 APFloat
Flt(*OpSema
);
520 APFloat::opStatus S
= Flt
.convertFromAPInt(Val
, Sema
.isSigned(), RM
);
522 // If we cared about checking for precision loss, we could look at this
526 // Scale down the integer value in the float to match the correct scaling
528 APFloat
ScaleFactor(std::pow(2, Sema
.getLsbWeight()));
530 ScaleFactor
.convert(*OpSema
, LosslessRM
, &Ignored
);
531 Flt
.multiply(ScaleFactor
, LosslessRM
);
533 if (OpSema
!= &FloatSema
)
534 Flt
.convert(FloatSema
, RM
, &Ignored
);
539 APFixedPoint
APFixedPoint::getFromIntValue(const APSInt
&Value
,
540 const FixedPointSemantics
&DstFXSema
,
542 FixedPointSemantics IntFXSema
= FixedPointSemantics::GetIntegerSemantics(
543 Value
.getBitWidth(), Value
.isSigned());
544 return APFixedPoint(Value
, IntFXSema
).convert(DstFXSema
, Overflow
);
548 APFixedPoint::getFromFloatValue(const APFloat
&Value
,
549 const FixedPointSemantics
&DstFXSema
,
551 // For some operations, rounding mode has an effect on the result, while
552 // other operations are lossless and should never result in rounding.
553 // To signify which these operations are, we define two rounding modes here,
554 // even though they are the same mode.
555 APFloat::roundingMode RM
= APFloat::rmTowardZero
;
556 APFloat::roundingMode LosslessRM
= APFloat::rmTowardZero
;
558 const fltSemantics
&FloatSema
= Value
.getSemantics();
561 // Handle NaN immediately.
564 return APFixedPoint(DstFXSema
);
567 // Make sure that we are operating in a type that works with this fixed-point
569 const fltSemantics
*OpSema
= &FloatSema
;
570 while (!DstFXSema
.fitsInFloatSemantics(*OpSema
))
571 OpSema
= promoteFloatSemantics(OpSema
);
576 if (&FloatSema
!= OpSema
)
577 Val
.convert(*OpSema
, LosslessRM
, &Ignored
);
579 // Scale up the float so that the 'fractional' part of the mantissa ends up in
580 // the integer range instead. Rounding mode is irrelevant here.
581 // It is fine if this overflows to infinity even for saturating types,
582 // since we will use floating point comparisons to check for saturation.
583 APFloat
ScaleFactor(std::pow(2, -DstFXSema
.getLsbWeight()));
584 ScaleFactor
.convert(*OpSema
, LosslessRM
, &Ignored
);
585 Val
.multiply(ScaleFactor
, LosslessRM
);
587 // Convert to the integral representation of the value. This rounding mode
589 APSInt
Res(DstFXSema
.getWidth(), !DstFXSema
.isSigned());
590 Val
.convertToInteger(Res
, RM
, &Ignored
);
592 // Round the integral value and scale back. This makes the
593 // overflow calculations below work properly. If we do not round here,
594 // we risk checking for overflow with a value that is outside the
595 // representable range of the fixed-point semantic even though no overflow
596 // would occur had we rounded first.
597 ScaleFactor
= APFloat(std::pow(2, DstFXSema
.getLsbWeight()));
598 ScaleFactor
.convert(*OpSema
, LosslessRM
, &Ignored
);
599 Val
.roundToIntegral(RM
);
600 Val
.multiply(ScaleFactor
, LosslessRM
);
602 // Check for overflow/saturation by checking if the floating point value
603 // is outside the range representable by the fixed-point value.
604 APFloat FloatMax
= getMax(DstFXSema
).convertToFloat(*OpSema
);
605 APFloat FloatMin
= getMin(DstFXSema
).convertToFloat(*OpSema
);
606 bool Overflowed
= false;
607 if (DstFXSema
.isSaturated()) {
609 Res
= getMax(DstFXSema
).getValue();
610 else if (Val
< FloatMin
)
611 Res
= getMin(DstFXSema
).getValue();
613 Overflowed
= Val
> FloatMax
|| Val
< FloatMin
;
616 *Overflow
= Overflowed
;
618 return APFixedPoint(Res
, DstFXSema
);