1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef BERBERIS_INTRINSICS_COMMON_INTRINSICS_FLOAT_H_
18 #define BERBERIS_INTRINSICS_COMMON_INTRINSICS_FLOAT_H_
19 
20 // We couldn't safely pass arguments using "raw" float and double on X86 because of peculiarities
21 // of psABI (sometimes floating point registers are used by guest programs to pass integer value and
22 // certain integers when converted to fp80 type and back become corrupted).
23 //
24 // To make sure that we wouldn't use it to return value by accident we would wrap them and use class
25 // which makes such mistakes unlikely on x86.
26 //
27 // It's safe to pass "raw" values as float and double on modern ABI (RISC-V UABI, x86-64 psABI, etc).
28 //
29 // NOTE: That type must be layout-compatible with underlying type thus it must ONLY have one field
30 // value_ inside.
31 //
32 // NOTE: It's perfectly safe to do bit_cast<uint32_t>(Float32) or bit_cast<Float64>(uint64_t).
33 // Yet it's NOT safe to do bit_cast<float>(Float32) or bit_cast<Float64>(double). This is because
34 // bit_cast itself is just a regular function and is affected by that psABI issue as well.
35 //
36 // If you need to convert between float/double and Float32/Float64 then you have to use memcpy
37 // and couldn't use any helper function which would receive or return raw float or double value.
38 
39 #include <stdint.h>
40 
41 #include <limits>
42 
43 #include "berberis/base/bit_util.h"
44 
45 namespace berberis {
46 
47 namespace intrinsics {
48 
49 enum class FPInfo { kNaN, kInfinite, kNormal, kSubnormal, kZero };
50 
51 template <typename BaseType>
52 class WrappedFloatType {
53  public:
54   constexpr WrappedFloatType() = default;
WrappedFloatType(BaseType value)55   explicit constexpr WrappedFloatType(BaseType value) : value_(value) {}
56   constexpr WrappedFloatType(const WrappedFloatType& other) = default;
57   constexpr WrappedFloatType(WrappedFloatType&& other) noexcept = default;
58   WrappedFloatType& operator=(const WrappedFloatType& other) = default;
59   WrappedFloatType& operator=(WrappedFloatType&& other) noexcept = default;
60   ~WrappedFloatType() = default;
61   template <typename IntType,
62             typename = std::enable_if_t<std::is_integral_v<IntType> &&
63                                         sizeof(BaseType) == sizeof(IntType)>>
64   [[nodiscard]] constexpr operator Raw<IntType>() const {
65     // Can't use bit_cast here because of IA32 ABI!
66     Raw<IntType> result;
67     memcpy(&result, &value_, sizeof(BaseType));
68     return result;
69   }
int16_t()70   explicit constexpr operator int16_t() const { return value_; }
uint16_t()71   explicit constexpr operator uint16_t() const { return value_; }
int32_t()72   explicit constexpr operator int32_t() const { return value_; }
uint32_t()73   explicit constexpr operator uint32_t() const { return value_; }
int64_t()74   explicit constexpr operator int64_t() const { return value_; }
uint64_t()75   explicit constexpr operator uint64_t() const { return value_; }
76   explicit constexpr operator WrappedFloatType<_Float16>() const {
77     return WrappedFloatType<_Float16>(value_);
78   }
79   explicit constexpr operator WrappedFloatType<float>() const {
80     return WrappedFloatType<float>(value_);
81   }
82   explicit constexpr operator WrappedFloatType<double>() const {
83     return WrappedFloatType<double>(value_);
84   }
85 #if defined(__i386__) || defined(__x86_64__)
86   explicit constexpr operator long double() const { return value_; }
87 #endif
88   // Note: we don't provide unary operator-.  That's done on purpose: with floats -x and 0.-x
89   // produce different results which could be surprising.  Use fneg instead of unary operator-.
90   friend WrappedFloatType operator+(const WrappedFloatType& v1, const WrappedFloatType& v2);
91   friend WrappedFloatType& operator+=(WrappedFloatType& v1, const WrappedFloatType& v2);
92   friend WrappedFloatType operator-(const WrappedFloatType& v1, const WrappedFloatType& v2);
93   friend WrappedFloatType& operator-=(WrappedFloatType& v1, const WrappedFloatType& v2);
94   friend WrappedFloatType operator*(const WrappedFloatType& v1, const WrappedFloatType& v2);
95   friend WrappedFloatType& operator*=(WrappedFloatType& v1, const WrappedFloatType& v2);
96   friend WrappedFloatType operator/(const WrappedFloatType& v1, const WrappedFloatType& v2);
97   friend WrappedFloatType& operator/=(WrappedFloatType& v1, const WrappedFloatType& v2);
98   friend bool operator==(const WrappedFloatType& v1, const WrappedFloatType& v2);
99   friend bool operator!=(const WrappedFloatType& v1, const WrappedFloatType& v2);
100   friend bool operator<(const WrappedFloatType& v1, const WrappedFloatType& v2);
101   friend bool operator<=(const WrappedFloatType& v1, const WrappedFloatType& v2);
102   friend bool operator>(const WrappedFloatType& v1, const WrappedFloatType& v2);
103   friend bool operator>=(const WrappedFloatType& v1, const WrappedFloatType& v2);
104   friend inline WrappedFloatType CopySignBit(const WrappedFloatType& v1,
105                                              const WrappedFloatType& v2);
106   friend inline WrappedFloatType Absolute(const WrappedFloatType& v);
107   friend inline WrappedFloatType Negative(const WrappedFloatType& v);
108   friend inline FPInfo FPClassify(const WrappedFloatType& v);
109   friend inline WrappedFloatType FPRound(const WrappedFloatType& value, uint32_t round_control);
110   friend inline int IsNan(const WrappedFloatType& v);
111   friend inline int SignBit(const WrappedFloatType& v);
112   friend inline WrappedFloatType Sqrt(const WrappedFloatType& v);
113   friend inline WrappedFloatType MulAdd(const WrappedFloatType& v1,
114                                         const WrappedFloatType& v2,
115                                         const WrappedFloatType& v3);
116   friend inline WrappedFloatType Max(WrappedFloatType op1, WrappedFloatType op2);
117   friend inline WrappedFloatType Min(WrappedFloatType op1, WrappedFloatType op2);
118 
119  private:
120   static_assert(!std::numeric_limits<BaseType>::is_exact,
121                 "WrappedFloatType should only be used with float types!");
122   BaseType value_;
123 };
124 
125 // Note: Float8 is uninhabited and variables of such type couldn't be created. Nonetheless it's
126 // useful for implementation of certain instructions. For example vfwcvt.f.x.v RISC-V instruction
127 // converts from Int8 (single-width int) to Float16 (double-width float) if titular element size if
128 // Float8. Because such instruction never actually accesses elements of Float8 type it's perfectly
129 // valid and allowed (if CPU supports Float16).
130 class Float8PhonyType;  // This class doesn't exist but we may use it in template arguments.
131 using Float8 = WrappedFloatType<Float8PhonyType>;  // Ditto.
132 using Float16 = WrappedFloatType<_Float16>;
133 using Float32 = WrappedFloatType<float>;
134 using Float64 = WrappedFloatType<double>;
135 
136 // It's NOT safe to use ANY functions which return raw float or double.  That's because IA32 ABI
137 // uses x87 stack to pass arguments (even with -mfpmath=sse) which clobbers NaN values.
138 //
139 // Builtins do NOT use the official calling conventions but are instead embedded in the function -
140 // even if all optimizations are disabled. Therefore, it's safe to use builtins here if on x86 host
141 // this file is compiled with SSE enforced for FP calculations, which is always the case for us.
142 // Clang uses SSE whenever possible by default. For GCC we need to specify -msse2 and -mfpmath=sse.
143 
CopySignBit(const Float32 & v1,const Float32 & v2)144 inline Float32 CopySignBit(const Float32& v1, const Float32& v2) {
145   return Float32(__builtin_copysignf(v1.value_, v2.value_));
146 }
147 
CopySignBit(const Float64 & v1,const Float64 & v2)148 inline Float64 CopySignBit(const Float64& v1, const Float64& v2) {
149   return Float64(__builtin_copysign(v1.value_, v2.value_));
150 }
151 
Absolute(const Float32 & v)152 inline Float32 Absolute(const Float32& v) {
153   return Float32(__builtin_fabsf(v.value_));
154 }
155 
Absolute(const Float64 & v)156 inline Float64 Absolute(const Float64& v) {
157   return Float64(__builtin_fabs(v.value_));
158 }
159 
FPClassify(const Float32 & v)160 inline FPInfo FPClassify(const Float32& v) {
161   return static_cast<FPInfo>(__builtin_fpclassify(static_cast<int>(FPInfo::kNaN),
162                                                   static_cast<int>(FPInfo::kInfinite),
163                                                   static_cast<int>(FPInfo::kNormal),
164                                                   static_cast<int>(FPInfo::kSubnormal),
165                                                   static_cast<int>(FPInfo::kZero),
166                                                   v.value_));
167 }
168 
FPClassify(const Float64 & v)169 inline FPInfo FPClassify(const Float64& v) {
170   return static_cast<FPInfo>(__builtin_fpclassify(static_cast<int>(FPInfo::kNaN),
171                                                   static_cast<int>(FPInfo::kInfinite),
172                                                   static_cast<int>(FPInfo::kNormal),
173                                                   static_cast<int>(FPInfo::kSubnormal),
174                                                   static_cast<int>(FPInfo::kZero),
175                                                   v.value_));
176 }
177 
IsNan(const Float32 & v)178 inline int IsNan(const Float32& v) {
179   return __builtin_isnan(v.value_);
180 }
181 
IsNan(const Float64 & v)182 inline int IsNan(const Float64& v) {
183   return __builtin_isnan(v.value_);
184 }
185 
SignBit(const Float32 & v)186 inline int SignBit(const Float32& v) {
187   return __builtin_signbitf(v.value_);
188 }
189 
SignBit(const Float64 & v)190 inline int SignBit(const Float64& v) {
191   return __builtin_signbit(v.value_);
192 }
193 
Sqrt(const Float32 & v)194 inline Float32 Sqrt(const Float32& v) {
195   return Float32(__builtin_sqrtf(v.value_));
196 }
197 
Sqrt(const Float64 & v)198 inline Float64 Sqrt(const Float64& v) {
199   return Float64(__builtin_sqrt(v.value_));
200 }
201 
202 // x*y + z
MulAdd(const Float32 & v1,const Float32 & v2,const Float32 & v3)203 inline Float32 MulAdd(const Float32& v1, const Float32& v2, const Float32& v3) {
204   return Float32(fmaf(v1.value_, v2.value_, v3.value_));
205 }
206 
MulAdd(const Float64 & v1,const Float64 & v2,const Float64 & v3)207 inline Float64 MulAdd(const Float64& v1, const Float64& v2, const Float64& v3) {
208   return Float64(fma(v1.value_, v2.value_, v3.value_));
209 }
210 
211 }  // namespace intrinsics
212 
213 }  // namespace berberis
214 
215 namespace std {
216 
217 template <typename BaseType>
218 class numeric_limits<berberis::intrinsics::WrappedFloatType<BaseType>> {
219  public:
220   static constexpr bool is_specialized = true;
221   static constexpr bool is_signed = true;
222   static constexpr bool is_integer = false;
223   static constexpr bool is_exact = false;
224   static constexpr bool has_infinity = true;
225   static constexpr bool has_quiet_NaN = std::numeric_limits<BaseType>::has_quiet_NaN;
226   static constexpr bool has_signaling_NaN = std::numeric_limits<BaseType>::has_signaling_NaN;
227   static constexpr std::float_denorm_style has_denorm = std::numeric_limits<BaseType>::has_denorm;
228   static constexpr bool has_denorm_loss = std::numeric_limits<BaseType>::has_denorm_loss;
229   static constexpr std::float_round_style round_style = std::numeric_limits<BaseType>::round_style;
230   static constexpr bool is_iec559 = std::numeric_limits<BaseType>::is_iec559;
231   static constexpr bool is_bounded = true;
232   static constexpr bool is_modulo = false;
233   static constexpr int digits = std::numeric_limits<BaseType>::digits;
234   static constexpr int digits10 = std::numeric_limits<BaseType>::digits10;
235   static constexpr int max_digits10 = std::numeric_limits<BaseType>::max_digits10;
236   static constexpr int radix = std::numeric_limits<BaseType>::radix;
237   static constexpr int min_exponent = std::numeric_limits<BaseType>::min_exponent;
238   static constexpr int min_exponent10 = std::numeric_limits<BaseType>::min_exponent10;
239   static constexpr int max_exponent = std::numeric_limits<BaseType>::max_exponent;
240   static constexpr int max_exponent10 = std::numeric_limits<BaseType>::max_exponent10;
241   static constexpr bool traps = std::numeric_limits<BaseType>::traps;
242   static constexpr bool tinyness_before = std::numeric_limits<BaseType>::tinyness_before;
min()243   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> min() {
244     return berberis::intrinsics::WrappedFloatType<BaseType>(std::numeric_limits<BaseType>::min());
245   }
lowest()246   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> lowest() {
247     return berberis::intrinsics::WrappedFloatType<BaseType>(
248         std::numeric_limits<BaseType>::lowest());
249   }
max()250   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> max() {
251     return berberis::intrinsics::WrappedFloatType<BaseType>(std::numeric_limits<BaseType>::max());
252   }
epsilon()253   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> epsilon() {
254     return berberis::intrinsics::WrappedFloatType<BaseType>(
255         std::numeric_limits<BaseType>::epsilon());
256   }
round_error()257   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> round_error() {
258     return berberis::intrinsics::WrappedFloatType<BaseType>(
259         std::numeric_limits<BaseType>::round_error());
260   }
infinity()261   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> infinity() {
262     return berberis::intrinsics::WrappedFloatType<BaseType>(
263         std::numeric_limits<BaseType>::infinity());
264   }
quiet_NaN()265   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> quiet_NaN() {
266     return berberis::intrinsics::WrappedFloatType<BaseType>(
267         std::numeric_limits<BaseType>::quiet_NaN());
268   }
signaling_NaN()269   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> signaling_NaN() {
270     return berberis::intrinsics::WrappedFloatType<BaseType>(
271         std::numeric_limits<BaseType>::signaling_NaN());
272   }
denorm_min()273   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> denorm_min() {
274     return berberis::intrinsics::WrappedFloatType<BaseType>(
275         std::numeric_limits<BaseType>::denorm_min());
276   }
277 };
278 
279 }  // namespace std
280 
281 #endif  // BERBERIS_INTRINSICS_COMMON_INTRINSICS_FLOAT_H_
282