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 <limits>
10 #include <type_traits>
11 
12 #include "base/numerics/safe_conversions.h"
13 
14 namespace base {
15 namespace internal {
16 
17 template <typename T, typename U>
18 struct CheckedMulFastAsmOp {
19   static const bool is_supported =
20       FastIntegerArithmeticPromotion<T, U>::is_contained;
21 
22   // The following is much more efficient 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   __attribute__((always_inline)) static 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     *result = static_cast<V>(presult);
40     return IsValueInRangeForNumericType<V>(presult);
41   }
42 };
43 
44 template <typename T, typename U>
45 struct ClampedAddFastAsmOp {
46   static const bool is_supported =
47       BigEnoughPromotion<T, U>::is_contained &&
48       IsTypeInRangeForNumericType<
49           int32_t,
50           typename BigEnoughPromotion<T, U>::type>::value;
51 
52   template <typename V>
DoClampedAddFastAsmOp53   __attribute__((always_inline)) static V Do(T x, U y) {
54     // This will get promoted to an int, so let the compiler do whatever is
55     // clever and rely on the saturated cast to bounds check.
56     if (IsIntegerArithmeticSafe<int, T, U>::value)
57       return saturated_cast<V>(x + y);
58 
59     int32_t result;
60     int32_t x_i32 = x;
61     int32_t y_i32 = y;
62 
63     asm("qadd %[result], %[first], %[second]"
64         : [result] "=r"(result)
65         : [first] "r"(x_i32), [second] "r"(y_i32));
66     return saturated_cast<V>(result);
67   }
68 };
69 
70 template <typename T, typename U>
71 struct ClampedSubFastAsmOp {
72   static const bool is_supported =
73       BigEnoughPromotion<T, U>::is_contained &&
74       IsTypeInRangeForNumericType<
75           int32_t,
76           typename BigEnoughPromotion<T, U>::type>::value;
77 
78   template <typename V>
DoClampedSubFastAsmOp79   __attribute__((always_inline)) static V Do(T x, U y) {
80     // This will get promoted to an int, so let the compiler do whatever is
81     // clever and rely on the saturated cast to bounds check.
82     if (IsIntegerArithmeticSafe<int, T, U>::value)
83       return saturated_cast<V>(x - y);
84 
85     int32_t result;
86     int32_t x_i32 = x;
87     int32_t y_i32 = y;
88 
89     asm("qsub %[result], %[first], %[second]"
90         : [result] "=r"(result)
91         : [first] "r"(x_i32), [second] "r"(y_i32));
92     return saturated_cast<V>(result);
93   }
94 };
95 
96 template <typename T, typename U>
97 struct ClampedMulFastAsmOp {
98   static const bool is_supported = CheckedMulFastAsmOp<T, U>::is_supported;
99 
100   template <typename V>
DoClampedMulFastAsmOp101   __attribute__((always_inline)) static V Do(T x, U y) {
102     // Use the CheckedMulFastAsmOp for full-width 32-bit values, because
103     // it's fewer instructions than promoting and then saturating.
104     if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
105         !IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
106       V result;
107       if (CheckedMulFastAsmOp<T, U>::Do(x, y, &result))
108         return result;
109       return CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
110     }
111 
112     assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
113     using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
114     return saturated_cast<V>(static_cast<Promotion>(x) *
115                              static_cast<Promotion>(y));
116   }
117 };
118 
119 }  // namespace internal
120 }  // namespace base
121 
122 #endif  // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
123