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<BaseType> &&
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<float>() const {
77     return WrappedFloatType<float>(value_);
78   }
79   explicit constexpr operator WrappedFloatType<double>() const {
80     return WrappedFloatType<double>(value_);
81   }
82 #if defined(__i386__) || defined(__x86_64__)
83   explicit constexpr operator long double() const { return value_; }
84 #endif
85   // Note: we don't provide unary operator-.  That's done on purpose: with floats -x and 0.-x
86   // produce different results which could be surprising.  Use fneg instead of unary operator-.
87   friend WrappedFloatType operator+(const WrappedFloatType& v1, const WrappedFloatType& v2);
88   friend WrappedFloatType& operator+=(WrappedFloatType& v1, const WrappedFloatType& v2);
89   friend WrappedFloatType operator-(const WrappedFloatType& v1, const WrappedFloatType& v2);
90   friend WrappedFloatType& operator-=(WrappedFloatType& v1, const WrappedFloatType& v2);
91   friend WrappedFloatType operator*(const WrappedFloatType& v1, const WrappedFloatType& v2);
92   friend WrappedFloatType& operator*=(WrappedFloatType& v1, const WrappedFloatType& v2);
93   friend WrappedFloatType operator/(const WrappedFloatType& v1, const WrappedFloatType& v2);
94   friend WrappedFloatType& operator/=(WrappedFloatType& v1, const WrappedFloatType& v2);
95   friend bool operator==(const WrappedFloatType& v1, const WrappedFloatType& v2);
96   friend bool operator!=(const WrappedFloatType& v1, const WrappedFloatType& v2);
97   friend bool operator<(const 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 inline WrappedFloatType CopySignBit(const WrappedFloatType& v1,
102                                              const WrappedFloatType& v2);
103   friend inline WrappedFloatType Absolute(const WrappedFloatType& v);
104   friend inline WrappedFloatType Negative(const WrappedFloatType& v);
105   friend inline FPInfo FPClassify(const WrappedFloatType& v);
106   friend inline WrappedFloatType FPRound(const WrappedFloatType& value, uint32_t round_control);
107   friend inline int IsNan(const WrappedFloatType& v);
108   friend inline int SignBit(const WrappedFloatType& v);
109   friend inline WrappedFloatType Sqrt(const WrappedFloatType& v);
110   friend inline WrappedFloatType MulAdd(const WrappedFloatType& v1,
111                                         const WrappedFloatType& v2,
112                                         const WrappedFloatType& v3);
113   friend inline WrappedFloatType Max(WrappedFloatType op1, WrappedFloatType op2);
114   friend inline WrappedFloatType Min(WrappedFloatType op1, WrappedFloatType op2);
115 
116  private:
117   static_assert(!std::numeric_limits<BaseType>::is_exact,
118                 "WrappedFloatType should only be used with float types!");
119   BaseType value_;
120 };
121 
122 // Note: Float8 is uninhabited and variables of such type couldn't be created. Nonetheless it's
123 // useful for implementation of certain instructions. For example vfwcvt.f.x.v RISC-V instruction
124 // converts from Int8 (single-width int) to Float16 (double-width float) if titular element size if
125 // Float8. Because such instruction never actually accesses elements of Float8 type it's perfectly
126 // valid and allowed (if CPU supports Float16).
127 class Float8PhonyType;  // This class doesn't exist but we may use it in template arguments.
128 using Float8 = WrappedFloatType<Float8PhonyType>;  // Ditto.
129 using Float16 = WrappedFloatType<_Float16>;
130 using Float32 = WrappedFloatType<float>;
131 using Float64 = WrappedFloatType<double>;
132 
133 }  // namespace intrinsics
134 
135 }  // namespace berberis
136 
137 namespace std {
138 
139 template <typename BaseType>
140 class numeric_limits<berberis::intrinsics::WrappedFloatType<BaseType>> {
141  public:
142   static constexpr bool is_specialized = true;
143   static constexpr bool is_signed = true;
144   static constexpr bool is_integer = false;
145   static constexpr bool is_exact = false;
146   static constexpr bool has_infinity = true;
147   static constexpr bool has_quiet_NaN = std::numeric_limits<BaseType>::has_quiet_NaN;
148   static constexpr bool has_signaling_NaN = std::numeric_limits<BaseType>::has_signaling_NaN;
149   static constexpr std::float_denorm_style has_denorm = std::numeric_limits<BaseType>::has_denorm;
150   static constexpr bool has_denorm_loss = std::numeric_limits<BaseType>::has_denorm_loss;
151   static constexpr std::float_round_style round_style = std::numeric_limits<BaseType>::round_style;
152   static constexpr bool is_iec559 = std::numeric_limits<BaseType>::is_iec559;
153   static constexpr bool is_bounded = true;
154   static constexpr bool is_modulo = false;
155   static constexpr int digits = std::numeric_limits<BaseType>::digits;
156   static constexpr int digits10 = std::numeric_limits<BaseType>::digits10;
157   static constexpr int max_digits10 = std::numeric_limits<BaseType>::max_digits10;
158   static constexpr int radix = std::numeric_limits<BaseType>::radix;
159   static constexpr int min_exponent = std::numeric_limits<BaseType>::min_exponent;
160   static constexpr int min_exponent10 = std::numeric_limits<BaseType>::min_exponent10;
161   static constexpr int max_exponent = std::numeric_limits<BaseType>::max_exponent;
162   static constexpr int max_exponent10 = std::numeric_limits<BaseType>::max_exponent10;
163   static constexpr bool traps = std::numeric_limits<BaseType>::traps;
164   static constexpr bool tinyness_before = std::numeric_limits<BaseType>::tinyness_before;
min()165   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> min() {
166     return berberis::intrinsics::WrappedFloatType<BaseType>(std::numeric_limits<BaseType>::min());
167   }
lowest()168   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> lowest() {
169     return berberis::intrinsics::WrappedFloatType<BaseType>(
170         std::numeric_limits<BaseType>::lowest());
171   }
max()172   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> max() {
173     return berberis::intrinsics::WrappedFloatType<BaseType>(std::numeric_limits<BaseType>::max());
174   }
epsilon()175   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> epsilon() {
176     return berberis::intrinsics::WrappedFloatType<BaseType>(
177         std::numeric_limits<BaseType>::epsilon());
178   }
round_error()179   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> round_error() {
180     return berberis::intrinsics::WrappedFloatType<BaseType>(
181         std::numeric_limits<BaseType>::round_error());
182   }
infinity()183   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> infinity() {
184     return berberis::intrinsics::WrappedFloatType<BaseType>(
185         std::numeric_limits<BaseType>::infinity());
186   }
quiet_NaN()187   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> quiet_NaN() {
188     return berberis::intrinsics::WrappedFloatType<BaseType>(
189         std::numeric_limits<BaseType>::quiet_NaN());
190   }
signaling_NaN()191   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> signaling_NaN() {
192     return berberis::intrinsics::WrappedFloatType<BaseType>(
193         std::numeric_limits<BaseType>::signaling_NaN());
194   }
denorm_min()195   static constexpr berberis::intrinsics::WrappedFloatType<BaseType> denorm_min() {
196     return berberis::intrinsics::WrappedFloatType<BaseType>(
197         std::numeric_limits<BaseType>::denorm_min());
198   }
199 };
200 
201 }  // namespace std
202 
203 #endif  // BERBERIS_INTRINSICS_COMMON_INTRINSICS_FLOAT_H_
204