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/SmallString.h" |

21 | #include "llvm/Support/raw_ostream.h" |

22 | |

23 | namespace llvm { |

24 | |

25 | class APFloat; |

26 | struct fltSemantics; |

27 | |

28 | /// The fixed point semantics work similarly to fltSemantics. The width |

29 | /// specifies the whole bit width of the underlying scaled integer (with padding |

30 | /// if any). The scale represents the number of fractional bits in this type. |

31 | /// When HasUnsignedPadding is true and this type is unsigned, the first bit |

32 | /// in the value this represents is treated as padding. |

33 | class FixedPointSemantics { |

34 | public: |

35 | FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned, |

36 | bool IsSaturated, bool HasUnsignedPadding) |

37 | : Width(Width), Scale(Scale), IsSigned(IsSigned), |

38 | IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) { |

39 | assert(Width >= Scale && "Not enough room for the scale"); |

40 | assert(!(IsSigned && HasUnsignedPadding) && |

41 | "Cannot have unsigned padding on a signed type."); |

42 | } |

43 | |

44 | unsigned getWidth() const { return Width; } |

45 | unsigned getScale() const { return Scale; } |

46 | bool isSigned() const { return IsSigned; } |

47 | bool isSaturated() const { return IsSaturated; } |

48 | bool hasUnsignedPadding() const { return HasUnsignedPadding; } |

49 | |

50 | void setSaturated(bool Saturated) { IsSaturated = Saturated; } |

51 | |

52 | /// Return the number of integral bits represented by these semantics. These |

53 | /// are separate from the fractional bits and do not include the sign or |

54 | /// padding bit. |

55 | unsigned getIntegralBits() const { |

56 | if (IsSigned || (!IsSigned && HasUnsignedPadding)) |

57 | return Width - Scale - 1; |

58 | else |

59 | return Width - Scale; |

60 | } |

61 | |

62 | /// Return the FixedPointSemantics that allows for calculating the full |

63 | /// precision semantic that can precisely represent the precision and ranges |

64 | /// of both input values. This does not compute the resulting semantics for a |

65 | /// given binary operation. |

66 | FixedPointSemantics |

67 | getCommonSemantics(const FixedPointSemantics &Other) const; |

68 | |

69 | /// Returns true if this fixed-point semantic with its value bits interpreted |

70 | /// as an integer can fit in the given floating point semantic without |

71 | /// overflowing to infinity. |

72 | /// For example, a signed 8-bit fixed-point semantic has a maximum and |

73 | /// minimum integer representation of 127 and -128, respectively. If both of |

74 | /// these values can be represented (possibly inexactly) in the floating |

75 | /// point semantic without overflowing, this returns true. |

76 | bool fitsInFloatSemantics(const fltSemantics &FloatSema) const; |

77 | |

78 | /// Return the FixedPointSemantics for an integer type. |

79 | static FixedPointSemantics GetIntegerSemantics(unsigned Width, |

80 | bool IsSigned) { |

81 | return FixedPointSemantics(Width, /*Scale=*/0, IsSigned, |

82 | /*IsSaturated=*/false, |

83 | /*HasUnsignedPadding=*/false); |

84 | } |

85 | |

86 | private: |

87 | unsigned Width : 16; |

88 | unsigned Scale : 13; |

89 | unsigned IsSigned : 1; |

90 | unsigned IsSaturated : 1; |

91 | unsigned HasUnsignedPadding : 1; |

92 | }; |

93 | |

94 | /// The APFixedPoint class works similarly to APInt/APSInt in that it is a |

95 | /// functional replacement for a scaled integer. It is meant to replicate the |

96 | /// fixed point types proposed in ISO/IEC JTC1 SC22 WG14 N1169. The class carries |

97 | /// info about the fixed point type's width, sign, scale, and saturation, and |

98 | /// provides different operations that would normally be performed on fixed point |

99 | /// types. |

100 | class APFixedPoint { |

101 | public: |

102 | APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema) |

103 | : Val(Val, !Sema.isSigned()), Sema(Sema) { |

104 | assert(Val.getBitWidth() == Sema.getWidth() && |

105 | "The value should have a bit width that matches the Sema width"); |

106 | } |

107 | |

108 | APFixedPoint(uint64_t Val, const FixedPointSemantics &Sema) |

109 | : APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned()), Sema) {} |

110 | |

111 | // Zero initialization. |

112 | APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {} |

113 | |

114 | APSInt getValue() const { return APSInt(Val, !Sema.isSigned()); } |

115 | inline unsigned getWidth() const { return Sema.getWidth(); } |

116 | inline unsigned getScale() const { return Sema.getScale(); } |

117 | inline bool isSaturated() const { return Sema.isSaturated(); } |

118 | inline bool isSigned() const { return Sema.isSigned(); } |

119 | inline bool hasPadding() const { return Sema.hasUnsignedPadding(); } |

120 | FixedPointSemantics getSemantics() const { return Sema; } |

121 | |

122 | bool getBoolValue() const { return Val.getBoolValue(); } |

123 | |

124 | // Convert this number to match the semantics provided. If the overflow |

125 | // parameter is provided, set this value to true or false to indicate if this |

126 | // operation results in an overflow. |

127 | APFixedPoint convert(const FixedPointSemantics &DstSema, |

128 | bool *Overflow = nullptr) const; |

129 | |

130 | // Perform binary operations on a fixed point type. The resulting fixed point |

131 | // value will be in the common, full precision semantics that can represent |

132 | // the precision and ranges of both input values. See convert() for an |

133 | // explanation of the Overflow parameter. |

134 | APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const; |

135 | APFixedPoint sub(const APFixedPoint &Other, bool *Overflow = nullptr) const; |

136 | APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const; |

137 | APFixedPoint div(const APFixedPoint &Other, bool *Overflow = nullptr) const; |

138 | |

139 | // Perform shift operations on a fixed point type. Unlike the other binary |

140 | // operations, the resulting fixed point value will be in the original |

141 | // semantic. |

142 | APFixedPoint shl(unsigned Amt, bool *Overflow = nullptr) const; |

143 | APFixedPoint shr(unsigned Amt, bool *Overflow = nullptr) const { |

144 | // Right shift cannot overflow. |

145 | if (Overflow) |

146 | *Overflow = false; |

147 | return APFixedPoint(Val >> Amt, Sema); |

148 | } |

149 | |

150 | /// Perform a unary negation (-X) on this fixed point type, taking into |

151 | /// account saturation if applicable. |

152 | APFixedPoint negate(bool *Overflow = nullptr) const; |

153 | |

154 | /// Return the integral part of this fixed point number, rounded towards |

155 | /// zero. (-2.5k -> -2) |

156 | APSInt getIntPart() const { |

157 | if (Val < 0 && Val != -Val) // Cover the case when we have the min val |

158 | return -(-Val >> getScale()); |

159 | else |

160 | return Val >> getScale(); |

161 | } |

162 | |

163 | /// Return the integral part of this fixed point number, rounded towards |

164 | /// zero. The value is stored into an APSInt with the provided width and sign. |

165 | /// If the overflow parameter is provided, and the integral value is not able |

166 | /// to be fully stored in the provided width and sign, the overflow parameter |

167 | /// is set to true. |

168 | APSInt convertToInt(unsigned DstWidth, bool DstSign, |

169 | bool *Overflow = nullptr) const; |

170 | |

171 | /// Convert this fixed point number to a floating point value with the |

172 | /// provided semantics. |

173 | APFloat convertToFloat(const fltSemantics &FloatSema) const; |

174 | |

175 | void toString(SmallVectorImpl<char> &Str) const; |

176 | std::string toString() const { |

177 | SmallString<40> S; |

178 | toString(S); |

179 | return std::string(S.str()); |

180 | } |

181 | |

182 | // If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1. |

183 | int compare(const APFixedPoint &Other) const; |

184 | bool operator==(const APFixedPoint &Other) const { |

185 | return compare(Other) == 0; |

186 | } |

187 | bool operator!=(const APFixedPoint &Other) const { |

188 | return compare(Other) != 0; |

189 | } |

190 | bool operator>(const APFixedPoint &Other) const { return compare(Other) > 0; } |

191 | bool operator<(const APFixedPoint &Other) const { return compare(Other) < 0; } |

192 | bool operator>=(const APFixedPoint &Other) const { |

193 | return compare(Other) >= 0; |

194 | } |

195 | bool operator<=(const APFixedPoint &Other) const { |

196 | return compare(Other) <= 0; |

197 | } |

198 | |

199 | static APFixedPoint getMax(const FixedPointSemantics &Sema); |

200 | static APFixedPoint getMin(const FixedPointSemantics &Sema); |

201 | |

202 | /// Given a floating point semantic, return the next floating point semantic |

203 | /// with a larger exponent and larger or equal mantissa. |

204 | static const fltSemantics *promoteFloatSemantics(const fltSemantics *S); |

205 | |

206 | /// Create an APFixedPoint with a value equal to that of the provided integer, |

207 | /// and in the same semantics as the provided target semantics. If the value |

208 | /// is not able to fit in the specified fixed point semantics, and the |

209 | /// overflow parameter is provided, it is set to true. |

210 | static APFixedPoint getFromIntValue(const APSInt &Value, |

211 | const FixedPointSemantics &DstFXSema, |

212 | bool *Overflow = nullptr); |

213 | |

214 | /// Create an APFixedPoint with a value equal to that of the provided |

215 | /// floating point value, in the provided target semantics. If the value is |

216 | /// not able to fit in the specified fixed point semantics and the overflow |

217 | /// parameter is specified, it is set to true. |

218 | /// For NaN, the Overflow flag is always set. For +inf and -inf, if the |

219 | /// semantic is saturating, the value saturates. Otherwise, the Overflow flag |

220 | /// is set. |

221 | static APFixedPoint getFromFloatValue(const APFloat &Value, |

222 | const FixedPointSemantics &DstFXSema, |

223 | bool *Overflow = nullptr); |

224 | |

225 | private: |

226 | APSInt Val; |

227 | FixedPointSemantics Sema; |

228 | }; |

229 | |

230 | inline raw_ostream &operator<<(raw_ostream &OS, const APFixedPoint &FX) { |

231 | OS << FX.toString(); |

232 | return OS; |

233 | } |

234 | |

235 | } // namespace llvm |

236 | |

237 | #endif |

238 |