Where Online Learning is simpler!
The C and C++ Include Header Files
cat -n /usr/include/wabt/interp/interp-math.h
1 /* 2 * Copyright 2020 WebAssembly Community Group participants 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 WABT_INTERP_MATH_H_ 18 #define WABT_INTERP_MATH_H_ 19 20 #include <cmath> 21 #include <limits> 22 #include <string> 23 #include <type_traits> 24 25 #if COMPILER_IS_MSVC && _M_X64 26 #include <emmintrin.h> 27 #include <immintrin.h> 28 #endif 29 30 #include "wabt/common.h" 31 #include "wabt/interp/interp.h" 32 33 namespace wabt { 34 namespace interp { 35 36 template < 37 typename T, 38 typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0> 39 bool WABT_VECTORCALL IsNaN(T val) { 40 return false; 41 } 42 43 template < 44 typename T, 45 typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> 46 bool WABT_VECTORCALL IsNaN(T val) { 47 return std::isnan(val); 48 } 49 50 template < 51 typename T, 52 typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0> 53 T WABT_VECTORCALL CanonNaN(T val) { 54 return val; 55 } 56 57 template < 58 typename T, 59 typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> 60 T WABT_VECTORCALL CanonNaN(T val) { 61 if (WABT_UNLIKELY(std::isnan(val))) { 62 return std::numeric_limits<f32>::quiet_NaN(); 63 } 64 return val; 65 } 66 67 template <typename T> T ShiftMask(T val) { return val & (sizeof(T)*8-1); } 68 69 template <typename T> bool WABT_VECTORCALL IntEqz(T val) { return val == 0; } 70 template <typename T> bool WABT_VECTORCALL Eq(T lhs, T rhs) { return lhs == rhs; } 71 template <typename T> bool WABT_VECTORCALL Ne(T lhs, T rhs) { return lhs != rhs; } 72 template <typename T> bool WABT_VECTORCALL Lt(T lhs, T rhs) { return lhs < rhs; } 73 template <typename T> bool WABT_VECTORCALL Le(T lhs, T rhs) { return lhs <= rhs; } 74 template <typename T> bool WABT_VECTORCALL Gt(T lhs, T rhs) { return lhs > rhs; } 75 template <typename T> bool WABT_VECTORCALL Ge(T lhs, T rhs) { return lhs >= rhs; } 76 template <typename T> T WABT_VECTORCALL IntClz(T val) { return Clz(val); } 77 template <typename T> T WABT_VECTORCALL IntCtz(T val) { return Ctz(val); } 78 template <typename T> T WABT_VECTORCALL IntPopcnt(T val) { return Popcount(val); } 79 template <typename T> T WABT_VECTORCALL IntNot(T val) { return ~val; } 80 template <typename T> T WABT_VECTORCALL IntNeg(T val) { return ~val + 1; } 81 template <typename T> T WABT_VECTORCALL Add(T lhs, T rhs) { return CanonNaN(lhs + rhs); } 82 template <typename T> T WABT_VECTORCALL Sub(T lhs, T rhs) { return CanonNaN(lhs - rhs); } 83 template <typename T> T WABT_VECTORCALL IntAnd(T lhs, T rhs) { return lhs & rhs; } 84 template <typename T> T WABT_VECTORCALL IntOr(T lhs, T rhs) { return lhs | rhs; } 85 template <typename T> T WABT_VECTORCALL IntXor(T lhs, T rhs) { return lhs ^ rhs; } 86 template <typename T> T WABT_VECTORCALL IntShl(T lhs, T rhs) { return lhs << ShiftMask(rhs); } 87 template <typename T> T WABT_VECTORCALL IntShr(T lhs, T rhs) { return lhs >> ShiftMask(rhs); } 88 template <typename T> T WABT_VECTORCALL IntMin(T lhs, T rhs) { return std::min(lhs, rhs); } 89 template <typename T> T WABT_VECTORCALL IntMax(T lhs, T rhs) { return std::max(lhs, rhs); } 90 template <typename T> T WABT_VECTORCALL IntAndNot(T lhs, T rhs) { return lhs & ~rhs; } 91 template <typename T> T WABT_VECTORCALL IntAvgr(T lhs, T rhs) { return (lhs + rhs + 1) / 2; } 92 template <typename T> T WABT_VECTORCALL Xchg(T lhs, T rhs) { return rhs; } 93 94 // This is a wrapping absolute value function, so a negative number that is not 95 // representable as a positive number will be unchanged (e.g. abs(-128) = 128). 96 // 97 // Note that std::abs() does not have this behavior (e.g. abs(-128) is UB). 98 // Similarly, using unary minus is also UB. 99 template <typename T> 100 T WABT_VECTORCALL IntAbs(T val) { 101 static_assert(std::is_unsigned<T>::value, "T must be unsigned."); 102 const auto signbit = T(-1) << (sizeof(T) * 8 - 1); 103 return (val & signbit) ? ~val + 1 : val; 104 } 105 106 // Because of the integer promotion rules [1], any value of a type T which is 107 // smaller than `int` will be converted to an `int`, as long as `int` can hold 108 // any value of type T. 109 // 110 // So type `u16` will be promoted to `int`, since all values can be stored in 111 // an int. Unfortunately, the product of two `u16` values cannot always be 112 // stored in an `int` (e.g. 65535 * 65535). This triggers an error in UBSan. 113 // 114 // As a result, we make sure to promote the type ahead of time for `u16`. Note 115 // that this isn't a problem for any other unsigned types. 116 // 117 // [1]; https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion 118 template <typename T> struct PromoteMul { using type = T; }; 119 template <> struct PromoteMul<u16> { using type = u32; }; 120 121 template <typename T> 122 T WABT_VECTORCALL Mul(T lhs, T rhs) { 123 using U = typename PromoteMul<T>::type; 124 return CanonNaN(U(lhs) * U(rhs)); 125 } 126 127 template <typename T> struct Mask { using Type = T; }; 128 template <> struct Mask<f32> { using Type = u32; }; 129 template <> struct Mask<f64> { using Type = u64; }; 130 131 template <typename T> typename Mask<T>::Type WABT_VECTORCALL EqMask(T lhs, T rhs) { return lhs == rhs ? -1 : 0; } 132 template <typename T> typename Mask<T>::Type WABT_VECTORCALL NeMask(T lhs, T rhs) { return lhs != rhs ? -1 : 0; } 133 template <typename T> typename Mask<T>::Type WABT_VECTORCALL LtMask(T lhs, T rhs) { return lhs < rhs ? -1 : 0; } 134 template <typename T> typename Mask<T>::Type WABT_VECTORCALL LeMask(T lhs, T rhs) { return lhs <= rhs ? -1 : 0; } 135 template <typename T> typename Mask<T>::Type WABT_VECTORCALL GtMask(T lhs, T rhs) { return lhs > rhs ? -1 : 0; } 136 template <typename T> typename Mask<T>::Type WABT_VECTORCALL GeMask(T lhs, T rhs) { return lhs >= rhs ? -1 : 0; } 137 138 template <typename T> 139 T WABT_VECTORCALL IntRotl(T lhs, T rhs) { 140 return (lhs << ShiftMask(rhs)) | (lhs >> ShiftMask<T>(0 - rhs)); 141 } 142 143 template <typename T> 144 T WABT_VECTORCALL IntRotr(T lhs, T rhs) { 145 return (lhs >> ShiftMask(rhs)) | (lhs << ShiftMask<T>(0 - rhs)); 146 } 147 148 // i{32,64}.{div,rem}_s are special-cased because they trap when dividing the 149 // max signed value by -1. The modulo operation on x86 uses the same 150 // instruction to generate the quotient and the remainder. 151 template <typename T, 152 typename std::enable_if<std::is_signed<T>::value, int>::type = 0> 153 bool IsNormalDivRem(T lhs, T rhs) { 154 return !(lhs == std::numeric_limits<T>::min() && rhs == -1); 155 } 156 157 template <typename T, 158 typename std::enable_if<!std::is_signed<T>::value, int>::type = 0> 159 bool IsNormalDivRem(T lhs, T rhs) { 160 return true; 161 } 162 163 template <typename T> 164 RunResult WABT_VECTORCALL IntDiv(T lhs, T rhs, T* out, std::string* out_msg) { 165 if (WABT_UNLIKELY(rhs == 0)) { 166 *out_msg = "integer divide by zero"; 167 return RunResult::Trap; 168 } 169 if (WABT_LIKELY(IsNormalDivRem(lhs, rhs))) { 170 *out = lhs / rhs; 171 return RunResult::Ok; 172 } else { 173 *out_msg = "integer overflow"; 174 return RunResult::Trap; 175 } 176 } 177 178 template <typename T> 179 RunResult WABT_VECTORCALL IntRem(T lhs, T rhs, T* out, std::string* out_msg) { 180 if (WABT_UNLIKELY(rhs == 0)) { 181 *out_msg = "integer divide by zero"; 182 return RunResult::Trap; 183 } 184 if (WABT_LIKELY(IsNormalDivRem(lhs, rhs))) { 185 *out = lhs % rhs; 186 } else { 187 *out = 0; 188 } 189 return RunResult::Ok; 190 } 191 192 #if COMPILER_IS_MSVC && _M_X64 193 template <typename T> T WABT_VECTORCALL FloatAbs(T val); 194 template <typename T> T WABT_VECTORCALL FloatCopysign(T lhs, T rhs); 195 196 // Don't use std::{abs,copysign} directly on MSVC, since that seems to lose 197 // the NaN tag. 198 template <> 199 inline f32 WABT_VECTORCALL FloatAbs(f32 val) { 200 return _mm_cvtss_f32(_mm_and_ps( 201 _mm_set1_ps(val), _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); 202 } 203 204 template <> 205 inline f64 WABT_VECTORCALL FloatAbs(f64 val) { 206 return _mm_cvtsd_f64( 207 _mm_and_pd(_mm_set1_pd(val), 208 _mm_castsi128_pd(_mm_set1_epi64x(0x7fffffffffffffffull)))); 209 } 210 211 template <> 212 inline f32 WABT_VECTORCALL FloatCopysign(f32 lhs, f32 rhs) { 213 return _mm_cvtss_f32( 214 _mm_or_ps(_mm_and_ps(_mm_set1_ps(lhs), 215 _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))), 216 _mm_and_ps(_mm_set1_ps(rhs), 217 _mm_castsi128_ps(_mm_set1_epi32(0x80000000))))); 218 } 219 220 template <> 221 inline f64 WABT_VECTORCALL FloatCopysign(f64 lhs, f64 rhs) { 222 return _mm_cvtsd_f64(_mm_or_pd( 223 _mm_and_pd(_mm_set1_pd(lhs), 224 _mm_castsi128_pd(_mm_set1_epi64x(0x7fffffffffffffffull))), 225 _mm_and_pd(_mm_set1_pd(rhs), 226 _mm_castsi128_pd(_mm_set1_epi64x(0x8000000000000000ull))))); 227 } 228 229 #else 230 template <typename T> 231 T WABT_VECTORCALL FloatAbs(T val) { 232 return std::abs(val); 233 } 234 235 template <typename T> 236 T WABT_VECTORCALL FloatCopysign(T lhs, T rhs) { 237 return std::copysign(lhs, rhs); 238 } 239 #endif 240 241 #if COMPILER_IS_MSVC 242 #else 243 #endif 244 245 template <typename T> T WABT_VECTORCALL FloatNeg(T val) { return -val; } 246 template <typename T> T WABT_VECTORCALL FloatCeil(T val) { return CanonNaN(std::ceil(val)); } 247 template <typename T> T WABT_VECTORCALL FloatFloor(T val) { return CanonNaN(std::floor(val)); } 248 template <typename T> T WABT_VECTORCALL FloatTrunc(T val) { return CanonNaN(std::trunc(val)); } 249 template <typename T> T WABT_VECTORCALL FloatNearest(T val) { return CanonNaN(std::nearbyint(val)); } 250 template <typename T> T WABT_VECTORCALL FloatSqrt(T val) { return CanonNaN(std::sqrt(val)); } 251 252 template <typename T> 253 T WABT_VECTORCALL FloatDiv(T lhs, T rhs) { 254 // IEE754 specifies what should happen when dividing a float by zero, but 255 // C/C++ says it is undefined behavior. 256 if (WABT_UNLIKELY(rhs == 0)) { 257 return std::isnan(lhs) || lhs == 0 258 ? std::numeric_limits<T>::quiet_NaN() 259 : ((std::signbit(lhs) ^ std::signbit(rhs)) 260 ? -std::numeric_limits<T>::infinity() 261 : std::numeric_limits<T>::infinity()); 262 } 263 return CanonNaN(lhs / rhs); 264 } 265 266 template <typename T> 267 T WABT_VECTORCALL FloatMin(T lhs, T rhs) { 268 if (WABT_UNLIKELY(std::isnan(lhs) || std::isnan(rhs))) { 269 return std::numeric_limits<T>::quiet_NaN(); 270 } else if (WABT_UNLIKELY(lhs == 0 && rhs == 0)) { 271 return std::signbit(lhs) ? lhs : rhs; 272 } else { 273 return std::min(lhs, rhs); 274 } 275 } 276 277 template <typename T> 278 T WABT_VECTORCALL FloatPMin(T lhs, T rhs) { 279 return std::min(lhs, rhs); 280 } 281 282 template <typename T> 283 T WABT_VECTORCALL FloatMax(T lhs, T rhs) { 284 if (WABT_UNLIKELY(std::isnan(lhs) || std::isnan(rhs))) { 285 return std::numeric_limits<T>::quiet_NaN(); 286 } else if (WABT_UNLIKELY(lhs == 0 && rhs == 0)) { 287 return std::signbit(lhs) ? rhs : lhs; 288 } else { 289 return std::max(lhs, rhs); 290 } 291 } 292 293 template <typename T> 294 T WABT_VECTORCALL FloatPMax(T lhs, T rhs) { 295 return std::max(lhs, rhs); 296 } 297 298 template <typename R, typename T> bool WABT_VECTORCALL CanConvert(T val) { return true; } 299 template <> inline bool WABT_VECTORCALL CanConvert<s32, f32>(f32 val) { return val >= -2147483648.f && val < 2147483648.f; } 300 template <> inline bool WABT_VECTORCALL CanConvert<s32, f64>(f64 val) { return val > -2147483649. && val < 2147483648.; } 301 template <> inline bool WABT_VECTORCALL CanConvert<u32, f32>(f32 val) { return val > -1.f && val < 4294967296.f; } 302 template <> inline bool WABT_VECTORCALL CanConvert<u32, f64>(f64 val) { return val > -1. && val < 4294967296.; } 303 template <> inline bool WABT_VECTORCALL CanConvert<s64, f32>(f32 val) { return val >= -9223372036854775808.f && val < 9223372036854775808.f; } 304 template <> inline bool WABT_VECTORCALL CanConvert<s64, f64>(f64 val) { return val >= -9223372036854775808. && val < 9223372036854775808.; } 305 template <> inline bool WABT_VECTORCALL CanConvert<u64, f32>(f32 val) { return val > -1.f && val < 18446744073709551616.f; } 306 template <> inline bool WABT_VECTORCALL CanConvert<u64, f64>(f64 val) { return val > -1. && val < 18446744073709551616.; } 307 308 template <typename R, typename T> 309 R WABT_VECTORCALL Convert(T val) { 310 assert((CanConvert<R, T>(val))); 311 return static_cast<R>(val); 312 } 313 314 template <> 315 inline f32 WABT_VECTORCALL Convert(f64 val) { 316 // The WebAssembly rounding mode means that these values (which are > F32_MAX) 317 // should be rounded to F32_MAX and not set to infinity. Unfortunately, UBSAN 318 // complains that the value is not representable as a float, so we'll special 319 // case them. 320 const f64 kMin = 3.4028234663852886e38; 321 const f64 kMax = 3.4028235677973366e38; 322 if (WABT_LIKELY(val >= -kMin && val <= kMin)) { 323 return val; 324 } else if (WABT_UNLIKELY(val > kMin && val < kMax)) { 325 return std::numeric_limits<f32>::max(); 326 } else if (WABT_UNLIKELY(val > -kMax && val < -kMin)) { 327 return -std::numeric_limits<f32>::max(); 328 } else if (WABT_UNLIKELY(std::isnan(val))) { 329 return std::numeric_limits<f32>::quiet_NaN(); 330 } else { 331 return std::copysign(std::numeric_limits<f32>::infinity(), val); 332 } 333 } 334 335 template <> 336 inline f32 WABT_VECTORCALL Convert(u64 val) { 337 return wabt_convert_uint64_to_float(val); 338 } 339 340 template <> 341 inline f64 WABT_VECTORCALL Convert(u64 val) { 342 return wabt_convert_uint64_to_double(val); 343 } 344 345 template <> 346 inline f32 WABT_VECTORCALL Convert(s64 val) { 347 return wabt_convert_int64_to_float(val); 348 } 349 350 template <> 351 inline f64 WABT_VECTORCALL Convert(s64 val) { 352 return wabt_convert_int64_to_double(val); 353 } 354 355 template <typename T, int N> 356 T WABT_VECTORCALL IntExtend(T val) { 357 // Hacker's delight 2.6 - sign extension 358 auto bit = T{1} << N; 359 auto mask = (bit << 1) - 1; 360 return ((val & mask) ^ bit) - bit; 361 } 362 363 template <typename R, typename T> 364 R WABT_VECTORCALL IntTruncSat(T val) { 365 if (WABT_UNLIKELY(std::isnan(val))) { 366 return 0; 367 } else if (WABT_UNLIKELY(!CanConvert<R>(val))) { 368 return std::signbit(val) ? std::numeric_limits<R>::min() 369 : std::numeric_limits<R>::max(); 370 } else { 371 return static_cast<R>(val); 372 } 373 } 374 375 template <typename T> struct SatPromote; 376 template <> struct SatPromote<s8> { using type = s32; }; 377 template <> struct SatPromote<s16> { using type = s32; }; 378 template <> struct SatPromote<u8> { using type = s32; }; 379 template <> struct SatPromote<u16> { using type = s32; }; 380 381 template <typename R, typename T> 382 R WABT_VECTORCALL Saturate(T val) { 383 static_assert(sizeof(R) < sizeof(T), "Incorrect types for Saturate"); 384 const T min = std::numeric_limits<R>::min(); 385 const T max = std::numeric_limits<R>::max(); 386 return val > max ? max : val < min ? min : val; 387 } 388 389 template <typename T, typename U = typename SatPromote<T>::type> 390 T WABT_VECTORCALL IntAddSat(T lhs, T rhs) { 391 return Saturate<T, U>(lhs + rhs); 392 } 393 394 template <typename T, typename U = typename SatPromote<T>::type> 395 T WABT_VECTORCALL IntSubSat(T lhs, T rhs) { 396 return Saturate<T, U>(lhs - rhs); 397 } 398 399 template <typename T> 400 T WABT_VECTORCALL SaturatingRoundingQMul(T lhs, T rhs) { 401 constexpr int size_in_bits = sizeof(T) * 8; 402 int round_const = 1 << (size_in_bits - 2); 403 int64_t product = lhs * rhs; 404 product += round_const; 405 product >>= (size_in_bits - 1); 406 return Saturate<T, int64_t>(product); 407 } 408 409 } // namespace interp 410 } // namespace wabt 411 412 #endif // WABT_INTERP_MATH_H_