1 // Copyright 2017 The Chromium Authors 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 BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ 7 8 #include <cassert> 9 #include <type_traits> 10 11 #include "base/numerics/safe_conversions.h" 12 13 namespace base { 14 namespace internal { 15 16 template <typename T, typename U> 17 struct CheckedMulFastAsmOp { 18 static const bool is_supported = 19 kEnableAsmCode && FastIntegerArithmeticPromotion<T, U>::is_contained; 20 21 // The following is not an assembler routine and is thus constexpr safe, it 22 // just emits much more efficient code than the Clang and GCC builtins for 23 // performing overflow-checked multiplication when a twice wider type is 24 // available. The below compiles down to 2-3 instructions, depending on the 25 // width of the types in use. 26 // As an example, an int32_t multiply compiles to: 27 // smull r0, r1, r0, r1 28 // cmp r1, r1, asr #31 29 // And an int16_t multiply compiles to: 30 // smulbb r1, r1, r0 31 // asr r2, r1, #16 32 // cmp r2, r1, asr #15 33 template <typename V> DoCheckedMulFastAsmOp34 static constexpr bool Do(T x, U y, V* result) { 35 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; 36 Promotion presult; 37 38 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); 39 if (!IsValueInRangeForNumericType<V>(presult)) 40 return false; 41 *result = static_cast<V>(presult); 42 return true; 43 } 44 }; 45 46 template <typename T, typename U> 47 struct ClampedAddFastAsmOp { 48 static const bool is_supported = 49 kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained && 50 IsTypeInRangeForNumericType< 51 int32_t, 52 typename BigEnoughPromotion<T, U>::type>::value; 53 54 template <typename V> DoClampedAddFastAsmOp55 __attribute__((always_inline)) static V Do(T x, U y) { 56 // This will get promoted to an int, so let the compiler do whatever is 57 // clever and rely on the saturated cast to bounds check. 58 if (IsIntegerArithmeticSafe<int, T, U>::value) 59 return saturated_cast<V>(static_cast<int>(x) + static_cast<int>(y)); 60 61 int32_t result; 62 int32_t x_i32 = checked_cast<int32_t>(x); 63 int32_t y_i32 = checked_cast<int32_t>(y); 64 65 asm("qadd %[result], %[first], %[second]" 66 : [result] "=r"(result) 67 : [first] "r"(x_i32), [second] "r"(y_i32)); 68 return saturated_cast<V>(result); 69 } 70 }; 71 72 template <typename T, typename U> 73 struct ClampedSubFastAsmOp { 74 static const bool is_supported = 75 kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained && 76 IsTypeInRangeForNumericType< 77 int32_t, 78 typename BigEnoughPromotion<T, U>::type>::value; 79 80 template <typename V> DoClampedSubFastAsmOp81 __attribute__((always_inline)) static V Do(T x, U y) { 82 // This will get promoted to an int, so let the compiler do whatever is 83 // clever and rely on the saturated cast to bounds check. 84 if (IsIntegerArithmeticSafe<int, T, U>::value) 85 return saturated_cast<V>(static_cast<int>(x) - static_cast<int>(y)); 86 87 int32_t result; 88 int32_t x_i32 = checked_cast<int32_t>(x); 89 int32_t y_i32 = checked_cast<int32_t>(y); 90 91 asm("qsub %[result], %[first], %[second]" 92 : [result] "=r"(result) 93 : [first] "r"(x_i32), [second] "r"(y_i32)); 94 return saturated_cast<V>(result); 95 } 96 }; 97 98 template <typename T, typename U> 99 struct ClampedMulFastAsmOp { 100 static const bool is_supported = 101 kEnableAsmCode && CheckedMulFastAsmOp<T, U>::is_supported; 102 103 template <typename V> DoClampedMulFastAsmOp104 __attribute__((always_inline)) static V Do(T x, U y) { 105 // Use the CheckedMulFastAsmOp for full-width 32-bit values, because 106 // it's fewer instructions than promoting and then saturating. 107 if (!IsIntegerArithmeticSafe<int32_t, T, U>::value && 108 !IsIntegerArithmeticSafe<uint32_t, T, U>::value) { 109 V result; 110 return CheckedMulFastAsmOp<T, U>::Do(x, y, &result) 111 ? result 112 : CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); 113 } 114 115 assert((FastIntegerArithmeticPromotion<T, U>::is_contained)); 116 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; 117 return saturated_cast<V>(static_cast<Promotion>(x) * 118 static_cast<Promotion>(y)); 119 } 120 }; 121 122 } // namespace internal 123 } // namespace base 124 125 #endif // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ 126