• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_BASE_OVERFLOWING_MATH_H_
6 #define V8_BASE_OVERFLOWING_MATH_H_
7 
8 #include <stdint.h>
9 
10 #include <cmath>
11 #include <type_traits>
12 
13 #include "src/base/macros.h"
14 
15 namespace v8 {
16 namespace base {
17 
18 // Helpers for performing overflowing arithmetic operations without relying
19 // on C++ undefined behavior.
20 #define ASSERT_SIGNED_INTEGER_TYPE(Type)                                      \
21   static_assert(std::is_integral<Type>::value && std::is_signed<Type>::value, \
22                 "use this for signed integer types");
23 #define OP_WITH_WRAPAROUND(Name, OP)                                      \
24   template <typename signed_type>                                         \
25   inline signed_type Name##WithWraparound(signed_type a, signed_type b) { \
26     ASSERT_SIGNED_INTEGER_TYPE(signed_type);                              \
27     using unsigned_type = typename std::make_unsigned<signed_type>::type; \
28     unsigned_type a_unsigned = static_cast<unsigned_type>(a);             \
29     unsigned_type b_unsigned = static_cast<unsigned_type>(b);             \
30     unsigned_type result = a_unsigned OP b_unsigned;                      \
31     return static_cast<signed_type>(result);                              \
32   }
33 
34 OP_WITH_WRAPAROUND(Add, +)
35 OP_WITH_WRAPAROUND(Sub, -)
36 OP_WITH_WRAPAROUND(Mul, *)
37 
38 // 16-bit integers are special due to C++'s implicit conversion rules.
39 // See https://bugs.llvm.org/show_bug.cgi?id=25580.
40 template <>
MulWithWraparound(int16_t a,int16_t b)41 inline int16_t MulWithWraparound(int16_t a, int16_t b) {
42   uint32_t a_unsigned = static_cast<uint32_t>(a);
43   uint32_t b_unsigned = static_cast<uint32_t>(b);
44   uint32_t result = a_unsigned * b_unsigned;
45   return static_cast<int16_t>(static_cast<uint16_t>(result));
46 }
47 
48 #undef OP_WITH_WRAPAROUND
49 
50 template <typename signed_type>
NegateWithWraparound(signed_type a)51 inline signed_type NegateWithWraparound(signed_type a) {
52   ASSERT_SIGNED_INTEGER_TYPE(signed_type);
53   if (a == std::numeric_limits<signed_type>::min()) return a;
54   return -a;
55 }
56 
57 template <typename signed_type>
ShlWithWraparound(signed_type a,signed_type b)58 inline signed_type ShlWithWraparound(signed_type a, signed_type b) {
59   ASSERT_SIGNED_INTEGER_TYPE(signed_type);
60   using unsigned_type = typename std::make_unsigned<signed_type>::type;
61   const unsigned_type kMask = (sizeof(a) * 8) - 1;
62   return static_cast<signed_type>(static_cast<unsigned_type>(a) << (b & kMask));
63 }
64 
65 #undef ASSERT_SIGNED_INTEGER_TYPE
66 
67 // Returns the quotient x/y, avoiding C++ undefined behavior if y == 0.
68 template <typename T>
Divide(T x,T y)69 inline T Divide(T x, T y) {
70   if (y != 0) return x / y;
71   if (x == 0 || x != x) return std::numeric_limits<T>::quiet_NaN();
72   if ((x >= 0) == (std::signbit(y) == 0)) {
73     return std::numeric_limits<T>::infinity();
74   }
75   return -std::numeric_limits<T>::infinity();
76 }
77 
Recip(float a)78 inline float Recip(float a) { return Divide(1.0f, a); }
79 
RecipSqrt(float a)80 inline float RecipSqrt(float a) {
81   if (a != 0) return 1.0f / std::sqrt(a);
82   if (std::signbit(a) == 0) return std::numeric_limits<float>::infinity();
83   return -std::numeric_limits<float>::infinity();
84 }
85 
86 }  // namespace base
87 }  // namespace v8
88 
89 #endif  // V8_BASE_OVERFLOWING_MATH_H_
90