diff options
| author | Jeff Epler <jepler@gmail.com> | 2025-07-23 16:14:22 -0500 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-10-01 09:23:05 +1000 |
| commit | a80913292153a14424b29bdb9ca8847e8d35cf73 (patch) | |
| tree | 9d073ab5c9c1773d30daa89d3062c3ee1bbed380 /py/runtime.c | |
| parent | 3dd8073c290c077f17ffdee17a019763ad82604d (diff) | |
py: Add MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC.
Most MCUs apart from Cortex-M0 with Thumb 1 have an instruction
for computing the "high part" of a multiplication (e.g., the upper
32 bits of a 32x32 multiply).
When they do, gcc uses this to implement a small and fast
overflow check using the __builtin_mul_overflow intrinsic, which
is preferable to the guard division method previously used in smallint.c.
However, in contrast to the previous mp_small_int_mul_overflow
routine, which checks that the result fits not only within mp_int_t
but is SMALL_INT_FITS(), __builtin_mul_overflow only checks for
overflow of the C type. As a result, a slight change in the code
flow is needed for MP_BINARY_OP_MULTIPLY.
Other sites using mp_small_int_mul_overflow already had the
result value flow through to a SMALL_INT_FITS check so they didn't
need any additional changes.
Do similarly for the _ll and _ull multiply overflows checks.
Signed-off-by: Jeff Epler <jepler@gmail.com>
Diffstat (limited to 'py/runtime.c')
| -rw-r--r-- | py/runtime.c | 27 |
1 files changed, 6 insertions, 21 deletions
diff --git a/py/runtime.c b/py/runtime.c index 61aeb83f8..58d5732be 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -430,7 +430,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs // Operations that can overflow: // + result always fits in mp_int_t, then handled by SMALL_INT check // - result always fits in mp_int_t, then handled by SMALL_INT check - // * checked explicitly + // * checked explicitly for fit in mp_int_t, then handled by SMALL_INT check // / if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check // % if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check // << checked explicitly @@ -489,31 +489,16 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs break; case MP_BINARY_OP_MULTIPLY: case MP_BINARY_OP_INPLACE_MULTIPLY: { - - // If long long type exists and is larger than mp_int_t, then - // we can use the following code to perform overflow-checked multiplication. - // Otherwise (eg in x64 case) we must use mp_small_int_mul_overflow. - #if 0 - // compute result using long long precision - long long res = (long long)lhs_val * (long long)rhs_val; - if (res > MP_SMALL_INT_MAX || res < MP_SMALL_INT_MIN) { - // result overflowed SMALL_INT, so return higher precision integer - return mp_obj_new_int_from_ll(res); - } else { - // use standard precision - lhs_val = (mp_int_t)res; - } - #endif - mp_int_t int_res; - if (mp_small_int_mul_overflow(lhs_val, rhs_val, &int_res)) { + if (mp_mul_mp_int_t_overflow(lhs_val, rhs_val, &int_res)) { // use higher precision lhs = mp_obj_new_int_from_ll(lhs_val); goto generic_binary_op; } else { // use standard precision - return MP_OBJ_NEW_SMALL_INT(int_res); + lhs_val = int_res; } + break; } case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: @@ -553,7 +538,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs mp_int_t ans = 1; while (rhs_val > 0) { if (rhs_val & 1) { - if (mp_small_int_mul_overflow(ans, lhs_val, &ans)) { + if (mp_mul_mp_int_t_overflow(ans, lhs_val, &ans)) { goto power_overflow; } } @@ -562,7 +547,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs } rhs_val /= 2; mp_int_t int_res; - if (mp_small_int_mul_overflow(lhs_val, lhs_val, &int_res)) { + if (mp_mul_mp_int_t_overflow(lhs_val, lhs_val, &int_res)) { goto power_overflow; } lhs_val = int_res; |
