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