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