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