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