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