xref: /aosp_15_r20/external/cronet/base/numerics/safe_math_arm_impl.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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