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 PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ 6 #define PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ 7 8 #include <cassert> 9 #include <type_traits> 10 11 #include "partition_alloc/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 } 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>(x + y); 60 } 61 62 int32_t result; 63 int32_t x_i32 = checked_cast<int32_t>(x); 64 int32_t y_i32 = checked_cast<int32_t>(y); 65 66 asm("qadd %[result], %[first], %[second]" 67 : [result] "=r"(result) 68 : [first] "r"(x_i32), [second] "r"(y_i32)); 69 return saturated_cast<V>(result); 70 } 71 }; 72 73 template <typename T, typename U> 74 struct ClampedSubFastAsmOp { 75 static const bool is_supported = 76 kEnableAsmCode && BigEnoughPromotion<T, U>::is_contained && 77 IsTypeInRangeForNumericType< 78 int32_t, 79 typename BigEnoughPromotion<T, U>::type>::value; 80 81 template <typename V> DoClampedSubFastAsmOp82 __attribute__((always_inline)) static V Do(T x, U y) { 83 // This will get promoted to an int, so let the compiler do whatever is 84 // clever and rely on the saturated cast to bounds check. 85 if (IsIntegerArithmeticSafe<int, T, U>::value) { 86 return saturated_cast<V>(x - y); 87 } 88 89 int32_t result; 90 int32_t x_i32 = checked_cast<int32_t>(x); 91 int32_t y_i32 = checked_cast<int32_t>(y); 92 93 asm("qsub %[result], %[first], %[second]" 94 : [result] "=r"(result) 95 : [first] "r"(x_i32), [second] "r"(y_i32)); 96 return saturated_cast<V>(result); 97 } 98 }; 99 100 template <typename T, typename U> 101 struct ClampedMulFastAsmOp { 102 static const bool is_supported = 103 kEnableAsmCode && CheckedMulFastAsmOp<T, U>::is_supported; 104 105 template <typename V> DoClampedMulFastAsmOp106 __attribute__((always_inline)) static V Do(T x, U y) { 107 // Use the CheckedMulFastAsmOp for full-width 32-bit values, because 108 // it's fewer instructions than promoting and then saturating. 109 if (!IsIntegerArithmeticSafe<int32_t, T, U>::value && 110 !IsIntegerArithmeticSafe<uint32_t, T, U>::value) { 111 V result; 112 return CheckedMulFastAsmOp<T, U>::Do(x, y, &result) 113 ? result 114 : CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y)); 115 } 116 117 assert((FastIntegerArithmeticPromotion<T, U>::is_contained)); 118 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; 119 return saturated_cast<V>(static_cast<Promotion>(x) * 120 static_cast<Promotion>(y)); 121 } 122 }; 123 124 } // namespace partition_alloc::internal::base::internal 125 126 #endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_ 127