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