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 | |