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{-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(getMsbWeight() + 1 - hasSignOrPaddingBit(), 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(bit_cast<uint32_t>(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(getWidth()), Val.isUnsigned()); 215 APSInt ExtVal = 216 (getLsbWeight() > 0) ? Val.extend(getWidth() + getLsbWeight()) : Val; 217 if (Val < 0 && Val != -Val) // Cover the case when we have the min val 218 return -((-ExtVal).relativeShl(getLsbWeight())); 219 return ExtVal.relativeShl(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(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(Val.getSemantics(), 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