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
19namespace llvm {
20
21void 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
32uint32_t FixedPointSemantics::toOpaqueInt() const {
33 return llvm::bit_cast<uint32_t>(from: *this);
34}
35
36FixedPointSemantics 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
42APFixedPoint 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
83int 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
129APFixedPoint 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
137APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
138 auto Val = APSInt::getMinValue(numBits: Sema.getWidth(), Unsigned: !Sema.isSigned());
139 return APFixedPoint(Val, Sema);
140}
141
142APFixedPoint APFixedPoint::getEpsilon(const FixedPointSemantics &Sema) {
143 APSInt Val(Sema.getWidth(), !Sema.isSigned());
144 Val.setBit(/*BitPosition=*/0);
145 return APFixedPoint(Val, Sema);
146}
147
148bool 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
171FixedPointSemantics 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
197APFixedPoint 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
221APFixedPoint 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
245APFixedPoint 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
303APFixedPoint 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
366APFixedPoint 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
401void 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
441void 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)
448LLVM_DUMP_METHOD void APFixedPoint::dump() const { print(llvm::errs()); }
449#endif
450
451APFixedPoint 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
469APSInt 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
498const 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
510APFloat 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
546APFixedPoint 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
554APFixedPoint
555APFixedPoint::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