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