| 1 | //===- APFixedPoint.h - 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 fixed point number interface. | 
| 11 | /// This is a class for abstracting various operations performed on fixed point | 
| 12 | /// types. | 
| 13 | /// | 
| 14 | //===----------------------------------------------------------------------===// | 
| 15 |  | 
| 16 | #ifndef LLVM_ADT_APFIXEDPOINT_H | 
| 17 | #define LLVM_ADT_APFIXEDPOINT_H | 
| 18 |  | 
| 19 | #include "llvm/ADT/APSInt.h" | 
| 20 | #include "llvm/ADT/DenseMapInfo.h" | 
| 21 | #include "llvm/ADT/Hashing.h" | 
| 22 | #include "llvm/ADT/SmallString.h" | 
| 23 | #include "llvm/Support/raw_ostream.h" | 
| 24 |  | 
| 25 | namespace llvm { | 
| 26 |  | 
| 27 | class APFloat; | 
| 28 | struct fltSemantics; | 
| 29 |  | 
| 30 | /// The fixed point semantics work similarly to fltSemantics. The width | 
| 31 | /// specifies the whole bit width of the underlying scaled integer (with padding | 
| 32 | /// if any). The scale represents the number of fractional bits in this type. | 
| 33 | /// When HasUnsignedPadding is true and this type is unsigned, the first bit | 
| 34 | /// in the value this represents is treated as padding. | 
| 35 | class FixedPointSemantics { | 
| 36 | public: | 
| 37 |   static constexpr unsigned WidthBitWidth = 16; | 
| 38 |   static constexpr unsigned LsbWeightBitWidth = 13; | 
| 39 |   /// Used to differentiate between constructors with Width and Lsb from the | 
| 40 |   /// default Width and scale | 
| 41 |   struct Lsb { | 
| 42 |     int LsbWeight; | 
| 43 |   }; | 
| 44 |   FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned, | 
| 45 |                       bool IsSaturated, bool HasUnsignedPadding) | 
| 46 |       : FixedPointSemantics(Width, Lsb{.LsbWeight: -static_cast<int>(Scale)}, IsSigned, | 
| 47 |                             IsSaturated, HasUnsignedPadding) {} | 
| 48 |   FixedPointSemantics(unsigned Width, Lsb Weight, bool IsSigned, | 
| 49 |                       bool IsSaturated, bool HasUnsignedPadding) | 
| 50 |       : Width(Width), LsbWeight(Weight.LsbWeight), IsSigned(IsSigned), | 
| 51 |         IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) { | 
| 52 |     assert(isUInt<WidthBitWidth>(Width) && isInt<LsbWeightBitWidth>(Weight.LsbWeight)); | 
| 53 |     assert(!(IsSigned && HasUnsignedPadding) && | 
| 54 |            "Cannot have unsigned padding on a signed type." ); | 
| 55 |   } | 
| 56 |  | 
| 57 |   /// Check if the Semantic follow the requirements of an older more limited | 
| 58 |   /// version of this class | 
| 59 |   bool isValidLegacySema() const { | 
| 60 |     return LsbWeight <= 0 && static_cast<int>(Width) >= -LsbWeight; | 
| 61 |   } | 
| 62 |   unsigned getWidth() const { return Width; } | 
| 63 |   unsigned getScale() const { assert(isValidLegacySema()); return -LsbWeight; } | 
| 64 |   int getLsbWeight() const { return LsbWeight; } | 
| 65 |   int getMsbWeight() const { | 
| 66 |     return LsbWeight + Width - 1 /*Both lsb and msb are both part of width*/; | 
| 67 |   } | 
| 68 |   bool isSigned() const { return IsSigned; } | 
| 69 |   bool isSaturated() const { return IsSaturated; } | 
| 70 |   bool hasUnsignedPadding() const { return HasUnsignedPadding; } | 
| 71 |  | 
| 72 |   void setSaturated(bool Saturated) { IsSaturated = Saturated; } | 
| 73 |  | 
| 74 |   /// return true if the first bit doesn't have a strictly positive weight | 
| 75 |   bool hasSignOrPaddingBit() const { return IsSigned || HasUnsignedPadding; } | 
| 76 |  | 
| 77 |   /// Return the number of integral bits represented by these semantics. These | 
| 78 |   /// are separate from the fractional bits and do not include the sign or | 
| 79 |   /// padding bit. | 
| 80 |   unsigned getIntegralBits() const { | 
| 81 |     return std::max(a: getMsbWeight() + 1 - hasSignOrPaddingBit(), b: 0); | 
| 82 |   } | 
| 83 |  | 
| 84 |   /// Return the FixedPointSemantics that allows for calculating the full | 
| 85 |   /// precision semantic that can precisely represent the precision and ranges | 
| 86 |   /// of both input values. This does not compute the resulting semantics for a | 
| 87 |   /// given binary operation. | 
| 88 |   FixedPointSemantics | 
| 89 |   getCommonSemantics(const FixedPointSemantics &Other) const; | 
| 90 |  | 
| 91 |   /// Print semantics for debug purposes | 
| 92 |   void print(llvm::raw_ostream& OS) const; | 
| 93 |  | 
| 94 |   /// Returns true if this fixed-point semantic with its value bits interpreted | 
| 95 |   /// as an integer can fit in the given floating point semantic without | 
| 96 |   /// overflowing to infinity. | 
| 97 |   /// For example, a signed 8-bit fixed-point semantic has a maximum and | 
| 98 |   /// minimum integer representation of 127 and -128, respectively. If both of | 
| 99 |   /// these values can be represented (possibly inexactly) in the floating | 
| 100 |   /// point semantic without overflowing, this returns true. | 
| 101 |   bool fitsInFloatSemantics(const fltSemantics &FloatSema) const; | 
| 102 |  | 
| 103 |   /// Return the FixedPointSemantics for an integer type. | 
| 104 |   static FixedPointSemantics GetIntegerSemantics(unsigned Width, | 
| 105 |                                                  bool IsSigned) { | 
| 106 |     return FixedPointSemantics(Width, /*Scale=*/0, IsSigned, | 
| 107 |                                /*IsSaturated=*/false, | 
| 108 |                                /*HasUnsignedPadding=*/false); | 
| 109 |   } | 
| 110 |  | 
| 111 |   bool operator==(FixedPointSemantics Other) const { | 
| 112 |     return Width == Other.Width && LsbWeight == Other.LsbWeight && | 
| 113 |            IsSigned == Other.IsSigned && IsSaturated == Other.IsSaturated && | 
| 114 |            HasUnsignedPadding == Other.HasUnsignedPadding; | 
| 115 |   } | 
| 116 |   bool operator!=(FixedPointSemantics Other) const { return !(*this == Other); } | 
| 117 |  | 
| 118 | private: | 
| 119 |   unsigned Width          : WidthBitWidth; | 
| 120 |   signed int LsbWeight    : LsbWeightBitWidth; | 
| 121 |   unsigned IsSigned       : 1; | 
| 122 |   unsigned IsSaturated    : 1; | 
| 123 |   unsigned HasUnsignedPadding : 1; | 
| 124 | }; | 
| 125 |  | 
| 126 | static_assert(sizeof(FixedPointSemantics) == 4, "" ); | 
| 127 |  | 
| 128 | inline hash_code hash_value(const FixedPointSemantics &Val) { | 
| 129 |   return hash_value(value: bit_cast<uint32_t>(from: Val)); | 
| 130 | } | 
| 131 |  | 
| 132 | template <> struct DenseMapInfo<FixedPointSemantics> { | 
| 133 |   static inline FixedPointSemantics getEmptyKey() { | 
| 134 |     return FixedPointSemantics(0, 0, false, false, false); | 
| 135 |   } | 
| 136 |  | 
| 137 |   static inline FixedPointSemantics getTombstoneKey() { | 
| 138 |     return FixedPointSemantics(0, 1, false, false, false); | 
| 139 |   } | 
| 140 |  | 
| 141 |   static unsigned getHashValue(const FixedPointSemantics &Val) { | 
| 142 |     return hash_value(Val); | 
| 143 |   } | 
| 144 |  | 
| 145 |   static bool isEqual(const char &LHS, const char &RHS) { return LHS == RHS; } | 
| 146 | }; | 
| 147 |  | 
| 148 | /// The APFixedPoint class works similarly to APInt/APSInt in that it is a | 
| 149 | /// functional replacement for a scaled integer. It supports a wide range of | 
| 150 | /// semantics including the one used by fixed point types proposed in ISO/IEC | 
| 151 | /// JTC1 SC22 WG14 N1169. The class carries the value and semantics of | 
| 152 | /// a fixed point, and provides different operations that would normally be | 
| 153 | /// performed on fixed point types. | 
| 154 | class APFixedPoint { | 
| 155 | public: | 
| 156 |   APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema) | 
| 157 |       : Val(Val, !Sema.isSigned()), Sema(Sema) { | 
| 158 |     assert(Val.getBitWidth() == Sema.getWidth() && | 
| 159 |            "The value should have a bit width that matches the Sema width" ); | 
| 160 |   } | 
| 161 |  | 
| 162 |   APFixedPoint(uint64_t Val, const FixedPointSemantics &Sema) | 
| 163 |       : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned()), Sema) {} | 
| 164 |  | 
| 165 |   // Zero initialization. | 
| 166 |   APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {} | 
| 167 |  | 
| 168 |   APSInt getValue() const { return APSInt(Val, !Sema.isSigned()); } | 
| 169 |   inline unsigned getWidth() const { return Sema.getWidth(); } | 
| 170 |   inline unsigned getScale() const { return Sema.getScale(); } | 
| 171 |   int getLsbWeight() const { return Sema.getLsbWeight(); } | 
| 172 |   int getMsbWeight() const { return Sema.getMsbWeight(); } | 
| 173 |   inline bool isSaturated() const { return Sema.isSaturated(); } | 
| 174 |   inline bool isSigned() const { return Sema.isSigned(); } | 
| 175 |   inline bool hasPadding() const { return Sema.hasUnsignedPadding(); } | 
| 176 |   FixedPointSemantics getSemantics() const { return Sema; } | 
| 177 |  | 
| 178 |   bool getBoolValue() const { return Val.getBoolValue(); } | 
| 179 |  | 
| 180 |   // Convert this number to match the semantics provided. If the overflow | 
| 181 |   // parameter is provided, set this value to true or false to indicate if this | 
| 182 |   // operation results in an overflow. | 
| 183 |   APFixedPoint convert(const FixedPointSemantics &DstSema, | 
| 184 |                        bool *Overflow = nullptr) const; | 
| 185 |  | 
| 186 |   // Perform binary operations on a fixed point type. The resulting fixed point | 
| 187 |   // value will be in the common, full precision semantics that can represent | 
| 188 |   // the precision and ranges of both input values. See convert() for an | 
| 189 |   // explanation of the Overflow parameter. | 
| 190 |   APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const; | 
| 191 |   APFixedPoint sub(const APFixedPoint &Other, bool *Overflow = nullptr) const; | 
| 192 |   APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const; | 
| 193 |   APFixedPoint div(const APFixedPoint &Other, bool *Overflow = nullptr) const; | 
| 194 |  | 
| 195 |   // Perform shift operations on a fixed point type. Unlike the other binary | 
| 196 |   // operations, the resulting fixed point value will be in the original | 
| 197 |   // semantic. | 
| 198 |   APFixedPoint shl(unsigned Amt, bool *Overflow = nullptr) const; | 
| 199 |   APFixedPoint shr(unsigned Amt, bool *Overflow = nullptr) const { | 
| 200 |     // Right shift cannot overflow. | 
| 201 |     if (Overflow) | 
| 202 |       *Overflow = false; | 
| 203 |     return APFixedPoint(Val >> Amt, Sema); | 
| 204 |   } | 
| 205 |  | 
| 206 |   /// Perform a unary negation (-X) on this fixed point type, taking into | 
| 207 |   /// account saturation if applicable. | 
| 208 |   APFixedPoint negate(bool *Overflow = nullptr) const; | 
| 209 |  | 
| 210 |   /// Return the integral part of this fixed point number, rounded towards | 
| 211 |   /// zero. (-2.5k -> -2) | 
| 212 |   APSInt getIntPart() const { | 
| 213 |     if (getMsbWeight() < 0) | 
| 214 |       return APSInt(APInt::getZero(numBits: getWidth()), Val.isUnsigned()); | 
| 215 |     APSInt ExtVal = | 
| 216 |         (getLsbWeight() > 0) ? Val.extend(width: getWidth() + getLsbWeight()) : Val; | 
| 217 |     if (Val < 0 && Val != -Val) // Cover the case when we have the min val | 
| 218 |       return -((-ExtVal).relativeShl(Amt: getLsbWeight())); | 
| 219 |     return ExtVal.relativeShl(Amt: getLsbWeight()); | 
| 220 |   } | 
| 221 |  | 
| 222 |   /// Return the integral part of this fixed point number, rounded towards | 
| 223 |   /// zero. The value is stored into an APSInt with the provided width and sign. | 
| 224 |   /// If the overflow parameter is provided, and the integral value is not able | 
| 225 |   /// to be fully stored in the provided width and sign, the overflow parameter | 
| 226 |   /// is set to true. | 
| 227 |   APSInt convertToInt(unsigned DstWidth, bool DstSign, | 
| 228 |                       bool *Overflow = nullptr) const; | 
| 229 |  | 
| 230 |   /// Convert this fixed point number to a floating point value with the | 
| 231 |   /// provided semantics. | 
| 232 |   APFloat convertToFloat(const fltSemantics &FloatSema) const; | 
| 233 |  | 
| 234 |   void toString(SmallVectorImpl<char> &Str) const; | 
| 235 |   std::string toString() const { | 
| 236 |     SmallString<40> S; | 
| 237 |     toString(Str&: S); | 
| 238 |     return std::string(S.str()); | 
| 239 |   } | 
| 240 |  | 
| 241 |   void print(raw_ostream &) const; | 
| 242 |   void dump() const; | 
| 243 |  | 
| 244 |   // If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1. | 
| 245 |   int compare(const APFixedPoint &Other) const; | 
| 246 |   bool operator==(const APFixedPoint &Other) const { | 
| 247 |     return compare(Other) == 0; | 
| 248 |   } | 
| 249 |   bool operator!=(const APFixedPoint &Other) const { | 
| 250 |     return compare(Other) != 0; | 
| 251 |   } | 
| 252 |   bool operator>(const APFixedPoint &Other) const { return compare(Other) > 0; } | 
| 253 |   bool operator<(const APFixedPoint &Other) const { return compare(Other) < 0; } | 
| 254 |   bool operator>=(const APFixedPoint &Other) const { | 
| 255 |     return compare(Other) >= 0; | 
| 256 |   } | 
| 257 |   bool operator<=(const APFixedPoint &Other) const { | 
| 258 |     return compare(Other) <= 0; | 
| 259 |   } | 
| 260 |  | 
| 261 |   static APFixedPoint getMax(const FixedPointSemantics &Sema); | 
| 262 |   static APFixedPoint getMin(const FixedPointSemantics &Sema); | 
| 263 |  | 
| 264 |   /// Given a floating point semantic, return the next floating point semantic | 
| 265 |   /// with a larger exponent and larger or equal mantissa. | 
| 266 |   static const fltSemantics *promoteFloatSemantics(const fltSemantics *S); | 
| 267 |  | 
| 268 |   /// Create an APFixedPoint with a value equal to that of the provided integer, | 
| 269 |   /// and in the same semantics as the provided target semantics. If the value | 
| 270 |   /// is not able to fit in the specified fixed point semantics, and the | 
| 271 |   /// overflow parameter is provided, it is set to true. | 
| 272 |   static APFixedPoint getFromIntValue(const APSInt &Value, | 
| 273 |                                       const FixedPointSemantics &DstFXSema, | 
| 274 |                                       bool *Overflow = nullptr); | 
| 275 |  | 
| 276 |   /// Create an APFixedPoint with a value equal to that of the provided | 
| 277 |   /// floating point value, in the provided target semantics. If the value is | 
| 278 |   /// not able to fit in the specified fixed point semantics and the overflow | 
| 279 |   /// parameter is specified, it is set to true. | 
| 280 |   /// For NaN, the Overflow flag is always set. For +inf and -inf, if the | 
| 281 |   /// semantic is saturating, the value saturates. Otherwise, the Overflow flag | 
| 282 |   /// is set. | 
| 283 |   static APFixedPoint getFromFloatValue(const APFloat &Value, | 
| 284 |                                         const FixedPointSemantics &DstFXSema, | 
| 285 |                                         bool *Overflow = nullptr); | 
| 286 |  | 
| 287 | private: | 
| 288 |   APSInt Val; | 
| 289 |   FixedPointSemantics Sema; | 
| 290 | }; | 
| 291 |  | 
| 292 | inline raw_ostream &operator<<(raw_ostream &OS, const APFixedPoint &FX) { | 
| 293 |   OS << FX.toString(); | 
| 294 |   return OS; | 
| 295 | } | 
| 296 |  | 
| 297 | inline hash_code hash_value(const APFixedPoint &Val) { | 
| 298 |   return hash_combine(args: Val.getSemantics(), args: Val.getValue()); | 
| 299 | } | 
| 300 |  | 
| 301 | template <> struct DenseMapInfo<APFixedPoint> { | 
| 302 |   static inline APFixedPoint getEmptyKey() { | 
| 303 |     return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getEmptyKey()); | 
| 304 |   } | 
| 305 |  | 
| 306 |   static inline APFixedPoint getTombstoneKey() { | 
| 307 |     return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getTombstoneKey()); | 
| 308 |   } | 
| 309 |  | 
| 310 |   static unsigned getHashValue(const APFixedPoint &Val) { | 
| 311 |     return hash_value(Val); | 
| 312 |   } | 
| 313 |  | 
| 314 |   static bool isEqual(const APFixedPoint &LHS, const APFixedPoint &RHS) { | 
| 315 |     return LHS.getSemantics() == RHS.getSemantics() && | 
| 316 |            LHS.getValue() == RHS.getValue(); | 
| 317 |   } | 
| 318 | }; | 
| 319 |  | 
| 320 | } // namespace llvm | 
| 321 |  | 
| 322 | #endif | 
| 323 |  |