11a995a0aSBevin Hansson //===- APFixedPoint.h - Fixed point constant handling -----------*- C++ -*-===// 21a995a0aSBevin Hansson // 31a995a0aSBevin Hansson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41a995a0aSBevin Hansson // See https://llvm.org/LICENSE.txt for license information. 51a995a0aSBevin Hansson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61a995a0aSBevin Hansson // 71a995a0aSBevin Hansson //===----------------------------------------------------------------------===// 8c95df64cSShivam Gupta /// 91a995a0aSBevin Hansson /// \file 101a995a0aSBevin Hansson /// Defines the fixed point number interface. 111a995a0aSBevin Hansson /// This is a class for abstracting various operations performed on fixed point 121a995a0aSBevin Hansson /// types. 13c95df64cSShivam Gupta /// 141a995a0aSBevin Hansson //===----------------------------------------------------------------------===// 151a995a0aSBevin Hansson 161a995a0aSBevin Hansson #ifndef LLVM_ADT_APFIXEDPOINT_H 171a995a0aSBevin Hansson #define LLVM_ADT_APFIXEDPOINT_H 181a995a0aSBevin Hansson 191a995a0aSBevin Hansson #include "llvm/ADT/APSInt.h" 20a73db7f1SFangrui Song #include "llvm/ADT/Hashing.h" 211a995a0aSBevin Hansson #include "llvm/ADT/SmallString.h" 221a995a0aSBevin Hansson #include "llvm/Support/raw_ostream.h" 231a995a0aSBevin Hansson 241a995a0aSBevin Hansson namespace llvm { 251a995a0aSBevin Hansson 26dd3014f3SBevin Hansson class APFloat; 27dd3014f3SBevin Hansson struct fltSemantics; 28dd3014f3SBevin Hansson 291a995a0aSBevin Hansson /// The fixed point semantics work similarly to fltSemantics. The width 301a995a0aSBevin Hansson /// specifies the whole bit width of the underlying scaled integer (with padding 311a995a0aSBevin Hansson /// if any). The scale represents the number of fractional bits in this type. 321a995a0aSBevin Hansson /// When HasUnsignedPadding is true and this type is unsigned, the first bit 331a995a0aSBevin Hansson /// in the value this represents is treated as padding. 341a995a0aSBevin Hansson class FixedPointSemantics { 351a995a0aSBevin Hansson public: 361654b22aSTyker static constexpr unsigned WidthBitWidth = 16; 371654b22aSTyker static constexpr unsigned LsbWeightBitWidth = 13; 381654b22aSTyker /// Used to differentiate between constructors with Width and Lsb from the 391654b22aSTyker /// default Width and scale 401654b22aSTyker struct Lsb { 411654b22aSTyker int LsbWeight; 421654b22aSTyker }; 431a995a0aSBevin Hansson FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned, 441a995a0aSBevin Hansson bool IsSaturated, bool HasUnsignedPadding) 451654b22aSTyker : FixedPointSemantics(Width, Lsb{-static_cast<int>(Scale)}, IsSigned, 461654b22aSTyker IsSaturated, HasUnsignedPadding) {} 471654b22aSTyker FixedPointSemantics(unsigned Width, Lsb Weight, bool IsSigned, 481654b22aSTyker bool IsSaturated, bool HasUnsignedPadding) 491654b22aSTyker : Width(Width), LsbWeight(Weight.LsbWeight), IsSigned(IsSigned), 501a995a0aSBevin Hansson IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) { 511654b22aSTyker assert(isUInt<WidthBitWidth>(Width) && isInt<LsbWeightBitWidth>(Weight.LsbWeight)); 521a995a0aSBevin Hansson assert(!(IsSigned && HasUnsignedPadding) && 531a995a0aSBevin Hansson "Cannot have unsigned padding on a signed type."); 541a995a0aSBevin Hansson } 551a995a0aSBevin Hansson 561654b22aSTyker /// Check if the Semantic follow the requirements of an older more limited 571654b22aSTyker /// version of this class 581654b22aSTyker bool isValidLegacySema() const { 595922b920SAaron Ballman return LsbWeight <= 0 && static_cast<int>(Width) >= -LsbWeight; 601654b22aSTyker } 611a995a0aSBevin Hansson unsigned getWidth() const { return Width; } 621654b22aSTyker unsigned getScale() const { assert(isValidLegacySema()); return -LsbWeight; } 631654b22aSTyker int getLsbWeight() const { return LsbWeight; } 641654b22aSTyker int getMsbWeight() const { 651654b22aSTyker return LsbWeight + Width - 1 /*Both lsb and msb are both part of width*/; 661654b22aSTyker } 671a995a0aSBevin Hansson bool isSigned() const { return IsSigned; } 681a995a0aSBevin Hansson bool isSaturated() const { return IsSaturated; } 691a995a0aSBevin Hansson bool hasUnsignedPadding() const { return HasUnsignedPadding; } 701a995a0aSBevin Hansson 711a995a0aSBevin Hansson void setSaturated(bool Saturated) { IsSaturated = Saturated; } 721a995a0aSBevin Hansson 731654b22aSTyker /// return true if the first bit doesn't have a strictly positive weight 741654b22aSTyker bool hasSignOrPaddingBit() const { return IsSigned || HasUnsignedPadding; } 751654b22aSTyker 761a995a0aSBevin Hansson /// Return the number of integral bits represented by these semantics. These 771a995a0aSBevin Hansson /// are separate from the fractional bits and do not include the sign or 781a995a0aSBevin Hansson /// padding bit. 791a995a0aSBevin Hansson unsigned getIntegralBits() const { 801654b22aSTyker return std::max(getMsbWeight() + 1 - hasSignOrPaddingBit(), 0); 811a995a0aSBevin Hansson } 821a995a0aSBevin Hansson 831a995a0aSBevin Hansson /// Return the FixedPointSemantics that allows for calculating the full 841a995a0aSBevin Hansson /// precision semantic that can precisely represent the precision and ranges 851a995a0aSBevin Hansson /// of both input values. This does not compute the resulting semantics for a 861a995a0aSBevin Hansson /// given binary operation. 871a995a0aSBevin Hansson FixedPointSemantics 881a995a0aSBevin Hansson getCommonSemantics(const FixedPointSemantics &Other) const; 891a995a0aSBevin Hansson 901654b22aSTyker /// Print semantics for debug purposes 911654b22aSTyker void print(llvm::raw_ostream& OS) const; 921654b22aSTyker 93dd3014f3SBevin Hansson /// Returns true if this fixed-point semantic with its value bits interpreted 94dd3014f3SBevin Hansson /// as an integer can fit in the given floating point semantic without 95dd3014f3SBevin Hansson /// overflowing to infinity. 96dd3014f3SBevin Hansson /// For example, a signed 8-bit fixed-point semantic has a maximum and 97dd3014f3SBevin Hansson /// minimum integer representation of 127 and -128, respectively. If both of 98dd3014f3SBevin Hansson /// these values can be represented (possibly inexactly) in the floating 99dd3014f3SBevin Hansson /// point semantic without overflowing, this returns true. 100dd3014f3SBevin Hansson bool fitsInFloatSemantics(const fltSemantics &FloatSema) const; 101dd3014f3SBevin Hansson 1021a995a0aSBevin Hansson /// Return the FixedPointSemantics for an integer type. 1031a995a0aSBevin Hansson static FixedPointSemantics GetIntegerSemantics(unsigned Width, 1041a995a0aSBevin Hansson bool IsSigned) { 1051a995a0aSBevin Hansson return FixedPointSemantics(Width, /*Scale=*/0, IsSigned, 1061a995a0aSBevin Hansson /*IsSaturated=*/false, 1071a995a0aSBevin Hansson /*HasUnsignedPadding=*/false); 1081a995a0aSBevin Hansson } 1091a995a0aSBevin Hansson 1101654b22aSTyker bool operator==(FixedPointSemantics Other) const { 1111654b22aSTyker return Width == Other.Width && LsbWeight == Other.LsbWeight && 1121654b22aSTyker IsSigned == Other.IsSigned && IsSaturated == Other.IsSaturated && 1131654b22aSTyker HasUnsignedPadding == Other.HasUnsignedPadding; 1141654b22aSTyker } 1151654b22aSTyker bool operator!=(FixedPointSemantics Other) const { return !(*this == Other); } 1161654b22aSTyker 117a579782aSTimm Baeder /// Convert the semantics to a 32-bit unsigned integer. 118a579782aSTimm Baeder /// The result is dependent on the host endianness and not stable across LLVM 119a579782aSTimm Baeder /// versions. See getFromOpaqueInt() to convert it back to a 120a579782aSTimm Baeder /// FixedPointSemantics object. 121a579782aSTimm Baeder uint32_t toOpaqueInt() const; 122a579782aSTimm Baeder /// Create a FixedPointSemantics object from an integer created via 123a579782aSTimm Baeder /// toOpaqueInt(). 124a579782aSTimm Baeder static FixedPointSemantics getFromOpaqueInt(uint32_t); 125a579782aSTimm Baeder 1261a995a0aSBevin Hansson private: 1271654b22aSTyker unsigned Width : WidthBitWidth; 1281654b22aSTyker signed int LsbWeight : LsbWeightBitWidth; 1291a995a0aSBevin Hansson unsigned IsSigned : 1; 1301a995a0aSBevin Hansson unsigned IsSaturated : 1; 1311a995a0aSBevin Hansson unsigned HasUnsignedPadding : 1; 1321a995a0aSBevin Hansson }; 1331a995a0aSBevin Hansson 1341654b22aSTyker static_assert(sizeof(FixedPointSemantics) == 4, ""); 1351654b22aSTyker 1361654b22aSTyker inline hash_code hash_value(const FixedPointSemantics &Val) { 1371654b22aSTyker return hash_value(bit_cast<uint32_t>(Val)); 1381654b22aSTyker } 1391654b22aSTyker 1401654b22aSTyker template <> struct DenseMapInfo<FixedPointSemantics> { 1411654b22aSTyker static inline FixedPointSemantics getEmptyKey() { 1421654b22aSTyker return FixedPointSemantics(0, 0, false, false, false); 1431654b22aSTyker } 1441654b22aSTyker 1451654b22aSTyker static inline FixedPointSemantics getTombstoneKey() { 1461654b22aSTyker return FixedPointSemantics(0, 1, false, false, false); 1471654b22aSTyker } 1481654b22aSTyker 1491654b22aSTyker static unsigned getHashValue(const FixedPointSemantics &Val) { 1501654b22aSTyker return hash_value(Val); 1511654b22aSTyker } 1521654b22aSTyker 1531654b22aSTyker static bool isEqual(const char &LHS, const char &RHS) { return LHS == RHS; } 1541654b22aSTyker }; 1551654b22aSTyker 1561a995a0aSBevin Hansson /// The APFixedPoint class works similarly to APInt/APSInt in that it is a 1571654b22aSTyker /// functional replacement for a scaled integer. It supports a wide range of 1581654b22aSTyker /// semantics including the one used by fixed point types proposed in ISO/IEC 1591654b22aSTyker /// JTC1 SC22 WG14 N1169. The class carries the value and semantics of 1601654b22aSTyker /// a fixed point, and provides different operations that would normally be 1611654b22aSTyker /// performed on fixed point types. 1621a995a0aSBevin Hansson class APFixedPoint { 1631a995a0aSBevin Hansson public: 1641a995a0aSBevin Hansson APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema) 1651a995a0aSBevin Hansson : Val(Val, !Sema.isSigned()), Sema(Sema) { 1661a995a0aSBevin Hansson assert(Val.getBitWidth() == Sema.getWidth() && 1671a995a0aSBevin Hansson "The value should have a bit width that matches the Sema width"); 1681a995a0aSBevin Hansson } 1691a995a0aSBevin Hansson 1701a995a0aSBevin Hansson APFixedPoint(uint64_t Val, const FixedPointSemantics &Sema) 171*255a99c2SNikita Popov : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned(), 172*255a99c2SNikita Popov /*implicitTrunc=*/true), 173*255a99c2SNikita Popov Sema) {} 1741a995a0aSBevin Hansson 1751a995a0aSBevin Hansson // Zero initialization. 1761a995a0aSBevin Hansson APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {} 1771a995a0aSBevin Hansson 1781a995a0aSBevin Hansson APSInt getValue() const { return APSInt(Val, !Sema.isSigned()); } 1791a995a0aSBevin Hansson inline unsigned getWidth() const { return Sema.getWidth(); } 1801a995a0aSBevin Hansson inline unsigned getScale() const { return Sema.getScale(); } 1811654b22aSTyker int getLsbWeight() const { return Sema.getLsbWeight(); } 1821654b22aSTyker int getMsbWeight() const { return Sema.getMsbWeight(); } 1831a995a0aSBevin Hansson inline bool isSaturated() const { return Sema.isSaturated(); } 1841a995a0aSBevin Hansson inline bool isSigned() const { return Sema.isSigned(); } 1851a995a0aSBevin Hansson inline bool hasPadding() const { return Sema.hasUnsignedPadding(); } 1861a995a0aSBevin Hansson FixedPointSemantics getSemantics() const { return Sema; } 1871a995a0aSBevin Hansson 1881a995a0aSBevin Hansson bool getBoolValue() const { return Val.getBoolValue(); } 1891a995a0aSBevin Hansson 1901a995a0aSBevin Hansson // Convert this number to match the semantics provided. If the overflow 1911a995a0aSBevin Hansson // parameter is provided, set this value to true or false to indicate if this 1921a995a0aSBevin Hansson // operation results in an overflow. 1931a995a0aSBevin Hansson APFixedPoint convert(const FixedPointSemantics &DstSema, 1941a995a0aSBevin Hansson bool *Overflow = nullptr) const; 1951a995a0aSBevin Hansson 1961a995a0aSBevin Hansson // Perform binary operations on a fixed point type. The resulting fixed point 1971a995a0aSBevin Hansson // value will be in the common, full precision semantics that can represent 1981a995a0aSBevin Hansson // the precision and ranges of both input values. See convert() for an 1991a995a0aSBevin Hansson // explanation of the Overflow parameter. 2001a995a0aSBevin Hansson APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const; 2011a995a0aSBevin Hansson APFixedPoint sub(const APFixedPoint &Other, bool *Overflow = nullptr) const; 2021a995a0aSBevin Hansson APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const; 2031a995a0aSBevin Hansson APFixedPoint div(const APFixedPoint &Other, bool *Overflow = nullptr) const; 2041a995a0aSBevin Hansson 2051a995a0aSBevin Hansson // Perform shift operations on a fixed point type. Unlike the other binary 2061a995a0aSBevin Hansson // operations, the resulting fixed point value will be in the original 2071a995a0aSBevin Hansson // semantic. 2081a995a0aSBevin Hansson APFixedPoint shl(unsigned Amt, bool *Overflow = nullptr) const; 2091a995a0aSBevin Hansson APFixedPoint shr(unsigned Amt, bool *Overflow = nullptr) const { 2101a995a0aSBevin Hansson // Right shift cannot overflow. 2111a995a0aSBevin Hansson if (Overflow) 2121a995a0aSBevin Hansson *Overflow = false; 2131a995a0aSBevin Hansson return APFixedPoint(Val >> Amt, Sema); 2141a995a0aSBevin Hansson } 2151a995a0aSBevin Hansson 2161a995a0aSBevin Hansson /// Perform a unary negation (-X) on this fixed point type, taking into 2171a995a0aSBevin Hansson /// account saturation if applicable. 2181a995a0aSBevin Hansson APFixedPoint negate(bool *Overflow = nullptr) const; 2191a995a0aSBevin Hansson 2201a995a0aSBevin Hansson /// Return the integral part of this fixed point number, rounded towards 2211a995a0aSBevin Hansson /// zero. (-2.5k -> -2) 2221a995a0aSBevin Hansson APSInt getIntPart() const { 2231654b22aSTyker if (getMsbWeight() < 0) 2241654b22aSTyker return APSInt(APInt::getZero(getWidth()), Val.isUnsigned()); 2251654b22aSTyker APSInt ExtVal = 2261654b22aSTyker (getLsbWeight() > 0) ? Val.extend(getWidth() + getLsbWeight()) : Val; 2271a995a0aSBevin Hansson if (Val < 0 && Val != -Val) // Cover the case when we have the min val 2282c7b7ecaSTyker return -((-ExtVal).relativeShl(getLsbWeight())); 2291654b22aSTyker return ExtVal.relativeShl(getLsbWeight()); 2301a995a0aSBevin Hansson } 2311a995a0aSBevin Hansson 2321a995a0aSBevin Hansson /// Return the integral part of this fixed point number, rounded towards 2331a995a0aSBevin Hansson /// zero. The value is stored into an APSInt with the provided width and sign. 2341a995a0aSBevin Hansson /// If the overflow parameter is provided, and the integral value is not able 2351a995a0aSBevin Hansson /// to be fully stored in the provided width and sign, the overflow parameter 2361a995a0aSBevin Hansson /// is set to true. 2371a995a0aSBevin Hansson APSInt convertToInt(unsigned DstWidth, bool DstSign, 2381a995a0aSBevin Hansson bool *Overflow = nullptr) const; 2391a995a0aSBevin Hansson 240dd3014f3SBevin Hansson /// Convert this fixed point number to a floating point value with the 241dd3014f3SBevin Hansson /// provided semantics. 242dd3014f3SBevin Hansson APFloat convertToFloat(const fltSemantics &FloatSema) const; 243dd3014f3SBevin Hansson 2441a995a0aSBevin Hansson void toString(SmallVectorImpl<char> &Str) const; 2451a995a0aSBevin Hansson std::string toString() const { 2461a995a0aSBevin Hansson SmallString<40> S; 2471a995a0aSBevin Hansson toString(S); 248b7a66d0fSKazu Hirata return std::string(S); 2491a995a0aSBevin Hansson } 2501a995a0aSBevin Hansson 2511654b22aSTyker void print(raw_ostream &) const; 2521654b22aSTyker void dump() const; 2531654b22aSTyker 2541a995a0aSBevin Hansson // If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1. 2551a995a0aSBevin Hansson int compare(const APFixedPoint &Other) const; 2561a995a0aSBevin Hansson bool operator==(const APFixedPoint &Other) const { 2571a995a0aSBevin Hansson return compare(Other) == 0; 2581a995a0aSBevin Hansson } 2591a995a0aSBevin Hansson bool operator!=(const APFixedPoint &Other) const { 2601a995a0aSBevin Hansson return compare(Other) != 0; 2611a995a0aSBevin Hansson } 2621a995a0aSBevin Hansson bool operator>(const APFixedPoint &Other) const { return compare(Other) > 0; } 2631a995a0aSBevin Hansson bool operator<(const APFixedPoint &Other) const { return compare(Other) < 0; } 2641a995a0aSBevin Hansson bool operator>=(const APFixedPoint &Other) const { 2651a995a0aSBevin Hansson return compare(Other) >= 0; 2661a995a0aSBevin Hansson } 2671a995a0aSBevin Hansson bool operator<=(const APFixedPoint &Other) const { 2681a995a0aSBevin Hansson return compare(Other) <= 0; 2691a995a0aSBevin Hansson } 2701a995a0aSBevin Hansson 2711a995a0aSBevin Hansson static APFixedPoint getMax(const FixedPointSemantics &Sema); 2721a995a0aSBevin Hansson static APFixedPoint getMin(const FixedPointSemantics &Sema); 273ad49657aSPiJoules static APFixedPoint getEpsilon(const FixedPointSemantics &Sema); 2741a995a0aSBevin Hansson 275dd3014f3SBevin Hansson /// Given a floating point semantic, return the next floating point semantic 276dd3014f3SBevin Hansson /// with a larger exponent and larger or equal mantissa. 277dd3014f3SBevin Hansson static const fltSemantics *promoteFloatSemantics(const fltSemantics *S); 278dd3014f3SBevin Hansson 2791a995a0aSBevin Hansson /// Create an APFixedPoint with a value equal to that of the provided integer, 2801a995a0aSBevin Hansson /// and in the same semantics as the provided target semantics. If the value 2811a995a0aSBevin Hansson /// is not able to fit in the specified fixed point semantics, and the 2821a995a0aSBevin Hansson /// overflow parameter is provided, it is set to true. 2831a995a0aSBevin Hansson static APFixedPoint getFromIntValue(const APSInt &Value, 2841a995a0aSBevin Hansson const FixedPointSemantics &DstFXSema, 2851a995a0aSBevin Hansson bool *Overflow = nullptr); 2861a995a0aSBevin Hansson 287dd3014f3SBevin Hansson /// Create an APFixedPoint with a value equal to that of the provided 288dd3014f3SBevin Hansson /// floating point value, in the provided target semantics. If the value is 289dd3014f3SBevin Hansson /// not able to fit in the specified fixed point semantics and the overflow 290dd3014f3SBevin Hansson /// parameter is specified, it is set to true. 291dd3014f3SBevin Hansson /// For NaN, the Overflow flag is always set. For +inf and -inf, if the 292dd3014f3SBevin Hansson /// semantic is saturating, the value saturates. Otherwise, the Overflow flag 293dd3014f3SBevin Hansson /// is set. 294dd3014f3SBevin Hansson static APFixedPoint getFromFloatValue(const APFloat &Value, 295dd3014f3SBevin Hansson const FixedPointSemantics &DstFXSema, 296dd3014f3SBevin Hansson bool *Overflow = nullptr); 297dd3014f3SBevin Hansson 2981a995a0aSBevin Hansson private: 2991a995a0aSBevin Hansson APSInt Val; 3001a995a0aSBevin Hansson FixedPointSemantics Sema; 3011a995a0aSBevin Hansson }; 3021a995a0aSBevin Hansson 3031a995a0aSBevin Hansson inline raw_ostream &operator<<(raw_ostream &OS, const APFixedPoint &FX) { 3041a995a0aSBevin Hansson OS << FX.toString(); 3051a995a0aSBevin Hansson return OS; 3061a995a0aSBevin Hansson } 3071a995a0aSBevin Hansson 3081654b22aSTyker inline hash_code hash_value(const APFixedPoint &Val) { 3091654b22aSTyker return hash_combine(Val.getSemantics(), Val.getValue()); 3101654b22aSTyker } 3111654b22aSTyker 3121654b22aSTyker template <> struct DenseMapInfo<APFixedPoint> { 3131654b22aSTyker static inline APFixedPoint getEmptyKey() { 3141654b22aSTyker return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getEmptyKey()); 3151654b22aSTyker } 3161654b22aSTyker 3171654b22aSTyker static inline APFixedPoint getTombstoneKey() { 3181654b22aSTyker return APFixedPoint(DenseMapInfo<FixedPointSemantics>::getTombstoneKey()); 3191654b22aSTyker } 3201654b22aSTyker 3211654b22aSTyker static unsigned getHashValue(const APFixedPoint &Val) { 3221654b22aSTyker return hash_value(Val); 3231654b22aSTyker } 3241654b22aSTyker 3251654b22aSTyker static bool isEqual(const APFixedPoint &LHS, const APFixedPoint &RHS) { 3261654b22aSTyker return LHS.getSemantics() == RHS.getSemantics() && 3271654b22aSTyker LHS.getValue() == RHS.getValue(); 3281654b22aSTyker } 3291654b22aSTyker }; 3301654b22aSTyker 3311a995a0aSBevin Hansson } // namespace llvm 3321a995a0aSBevin Hansson 3331a995a0aSBevin Hansson #endif 334