From decc0c75a782a129d8b135edef3a441f8a95519d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 28 Jul 2025 21:04:37 -0500 Subject: [PATCH] core: Introduce mp_obj_new_long_ll. Since #9531 a few ways remained to produce long ints that were not reduced to small ints. Explicitly introduce "mp_obj_new_long_from_ll" and make the "new_int" functions always produce small ints when possible. At sites where a long int is really intended (or will eventually be reduced to a small int if the result fits), the called function is changed to be the new _long_ function. Signed-off-by: Jeff Epler --- ports/unix/coverage.c | 14 +++++++------- py/obj.h | 5 +++-- py/objint.c | 8 +++++++- py/objint_longlong.c | 10 +++++++--- py/objint_mpz.c | 31 +++++++++++++++++++++++-------- py/runtime.c | 10 +++++----- 6 files changed, 52 insertions(+), 26 deletions(-) diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index b7c3d2c25e..c70cf2993c 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -527,9 +527,9 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz)); // convert a large integer value (stored in a mpz) to mp_uint_t and to ll; - mp_obj_t obj_bigint = mp_obj_new_int_from_uint((mp_uint_t)0xdeadbeef); + mp_obj_t obj_bigint = mp_obj_new_int_from_ull((unsigned long long)0xdeadbeef); mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); - obj_bigint = mp_obj_new_int_from_ll(0xc0ffee777c0ffeell); + obj_bigint = mp_obj_new_long_from_ll(0xc0ffee777c0ffeell); long long value_ll = mp_obj_get_ll(obj_bigint); mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); @@ -541,7 +541,7 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); // convert a smaller integer value to mp_uint_t and to ll - obj_bigint = mp_obj_new_int_from_uint(0xc0ffee); + obj_bigint = mp_obj_new_int_from_ull(0xc0ffeeull); mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); value_ll = mp_obj_get_ll(obj_bigint); mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); @@ -568,7 +568,7 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(MP_OBJ_NEW_SMALL_INT(1))); // mp_obj_int_get_uint_checked with non-negative big-int - mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(mp_obj_new_int_from_ll(2))); + mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(mp_obj_new_long_from_ll(2))); // mp_obj_int_get_uint_checked with negative small-int (should raise exception) nlr_buf_t nlr; @@ -581,7 +581,7 @@ static mp_obj_t extra_coverage(void) { // mp_obj_int_get_uint_checked with negative big-int (should raise exception) if (nlr_push(&nlr) == 0) { - mp_obj_int_get_uint_checked(mp_obj_new_int_from_ll(-2)); + mp_obj_int_get_uint_checked(mp_obj_new_long_from_ll(-2)); nlr_pop(); } else { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); @@ -853,12 +853,12 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_bool(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_bool(mp_const_none)); // mp_obj_is_integer accepts ints and booleans - mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_integer(mp_obj_new_int_from_ll(1))); + mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_integer(mp_obj_new_long_from_ll(1))); mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(mp_const_true), mp_obj_is_integer(mp_const_false)); mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_integer(mp_obj_new_str_from_cstr("1")), mp_obj_is_integer(mp_const_none)); // mp_obj_is_int accepts small int and object ints - mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_int(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_int(mp_obj_new_int_from_ll(1))); + mp_printf(&mp_plat_print, "%d %d\n", mp_obj_is_int(MP_OBJ_NEW_SMALL_INT(1)), mp_obj_is_int(mp_obj_new_long_from_ll(1))); } // Legacy stackctrl.h API, this has been replaced by cstack.h diff --git a/py/obj.h b/py/obj.h index d756d237ab..2d0706d813 100644 --- a/py/obj.h +++ b/py/obj.h @@ -994,8 +994,9 @@ mp_obj_t mp_obj_new_cell(mp_obj_t obj); mp_obj_t mp_obj_new_int(mp_int_t value); mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value); mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base); -mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception) -mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception) +mp_obj_t mp_obj_new_int_from_ll(long long val); // returns a small int if value fits +mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // returns a small int if value fits +mp_obj_t mp_obj_new_long_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception) mp_obj_t mp_obj_new_str(const char *data, size_t len); // will check utf-8 (raises UnicodeError) mp_obj_t mp_obj_new_str_from_cstr(const char *str); // // accepts null-terminated string, will check utf-8 (raises UnicodeError) mp_obj_t mp_obj_new_str_via_qstr(const char *data, size_t len); // input data must be valid utf-8 diff --git a/py/objint.c b/py/objint.c index 87d8a27852..05b344eeac 100644 --- a/py/objint.c +++ b/py/objint.c @@ -326,7 +326,13 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i // This is called only with strings whose value doesn't fit in SMALL_INT mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { - mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("long int not supported in this build")); + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); + return mp_const_none; +} + +// This is called when an integer larger than a SMALL_INT is needed (although val might still fit in a SMALL_INT) +mp_obj_t mp_obj_new_long_from_ll(long long val) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); return mp_const_none; } diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 1a6242b979..074fc1c1c9 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -298,14 +298,18 @@ mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { return mp_obj_new_int_from_ll(value); } +mp_obj_t mp_obj_new_long_from_ll(long long val) { + mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); + o->val = val; + return MP_OBJ_FROM_PTR(o); +} + mp_obj_t mp_obj_new_int_from_ll(long long val) { if ((long long)(mp_int_t)val == val && MP_SMALL_INT_FITS(val)) { return MP_OBJ_NEW_SMALL_INT(val); } - mp_obj_int_t *o = mp_obj_malloc(mp_obj_int_t, &mp_type_int); - o->val = val; - return MP_OBJ_FROM_PTR(o); + return mp_obj_new_long_from_ll(val); } mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { diff --git a/py/objint_mpz.c b/py/objint_mpz.c index ea4e409a25..ccf857c19b 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -382,23 +382,38 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { } #endif +// This routine *always* returns an mpz integer. Call it only if the value is *known* +// to exceed the small integer range, because integers that fit are required to always +// be returned as small integers. +static mp_obj_t mp_obj_new_mpz_from_ll(long long val, bool is_signed) { + mp_obj_int_t *o = mp_obj_int_new_mpz(); + mpz_set_from_ll(&o->mpz, val, is_signed); + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_obj_new_long_from_ll(long long val) { + return mp_obj_new_mpz_from_ll(val, true); +} + mp_obj_t mp_obj_new_int(mp_int_t value) { if (MP_SMALL_INT_FITS(value)) { return MP_OBJ_NEW_SMALL_INT(value); } - return mp_obj_new_int_from_ll(value); + return mp_obj_new_mpz_from_ll(value, true); } mp_obj_t mp_obj_new_int_from_ll(long long val) { - mp_obj_int_t *o = mp_obj_int_new_mpz(); - mpz_set_from_ll(&o->mpz, val, true); - return MP_OBJ_FROM_PTR(o); + if (val == (mp_int_t)val && MP_SMALL_INT_FITS((mp_int_t)val)) { + return MP_OBJ_NEW_SMALL_INT(val); + } + return mp_obj_new_mpz_from_ll(val, true); } mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { - mp_obj_int_t *o = mp_obj_int_new_mpz(); - mpz_set_from_ll(&o->mpz, val, false); - return MP_OBJ_FROM_PTR(o); + if ((val & ~(unsigned long long)MP_SMALL_INT_POSITIVE_MASK) == 0) { + return mp_obj_new_int(val); + } + return mp_obj_new_mpz_from_ll(val, false); } mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { @@ -407,7 +422,7 @@ mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value) { if ((value & ~MP_SMALL_INT_POSITIVE_MASK) == 0) { return MP_OBJ_NEW_SMALL_INT(value); } - return mp_obj_new_int_from_ull(value); + return mp_obj_new_mpz_from_ll(value, false); } mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { diff --git a/py/runtime.c b/py/runtime.c index 0ab0626ef9..25d7dc551b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -456,7 +456,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs || lhs_val > (MP_SMALL_INT_MAX >> rhs_val) || lhs_val < (MP_SMALL_INT_MIN >> rhs_val)) { // left-shift will overflow, so use higher precision integer - lhs = mp_obj_new_int_from_ll(lhs_val); + lhs = mp_obj_new_long_from_ll(lhs_val); goto generic_binary_op; } else { // use standard precision @@ -498,7 +498,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs 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); + return mp_obj_new_long_from_ll(res); } else { // use standard precision lhs_val = (mp_int_t)res; @@ -508,7 +508,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs mp_int_t int_res; if (mp_small_int_mul_overflow(lhs_val, rhs_val, &int_res)) { // use higher precision - lhs = mp_obj_new_int_from_ll(lhs_val); + lhs = mp_obj_new_long_from_ll(lhs_val); goto generic_binary_op; } else { // use standard precision @@ -573,7 +573,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs power_overflow: // use higher precision - lhs = mp_obj_new_int_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs)); + lhs = mp_obj_new_long_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs)); goto generic_binary_op; case MP_BINARY_OP_DIVMOD: { @@ -603,7 +603,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs if (MP_SMALL_INT_FITS(lhs_val)) { return MP_OBJ_NEW_SMALL_INT(lhs_val); } else { - return mp_obj_new_int_from_ll(lhs_val); + return mp_obj_new_long_from_ll(lhs_val); } #if MICROPY_PY_BUILTINS_FLOAT } else if (mp_obj_is_float(rhs)) {