mirror of
https://github.com/micropython/micropython.git
synced 2025-07-21 13:01:10 +02:00
Compare commits
8 Commits
c72a3e528d
...
17fbc5abdc
Author | SHA1 | Date | |
---|---|---|---|
|
17fbc5abdc | ||
|
e9845ab20e | ||
|
516aa02104 | ||
|
d07f103d68 | ||
|
0cf1e7c059 | ||
|
a54b5d9aed | ||
|
6d93b150b8 | ||
|
2d8d64059f |
14
.github/workflows/ports_unix.yml
vendored
14
.github/workflows/ports_unix.yml
vendored
@@ -134,6 +134,20 @@ jobs:
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
longlong:
|
||||
runs-on: ubuntu-22.04 # use 22.04 to get python2, and libffi-dev:i386
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
run: source tools/ci.sh && ci_unix_32bit_setup
|
||||
- name: Build
|
||||
run: source tools/ci.sh && ci_unix_longlong_build
|
||||
- name: Run main test suite
|
||||
run: source tools/ci.sh && ci_unix_longlong_run_tests
|
||||
- name: Print failures
|
||||
if: failure()
|
||||
run: tests/run-tests.py --print-failures
|
||||
|
||||
float:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
37
ports/unix/variants/longlong/mpconfigvariant.h
Normal file
37
ports/unix/variants/longlong/mpconfigvariant.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// This config exists to test that the MICROPY_LONGINT_IMPL_LONGLONG variant
|
||||
// (i.e. minimal form of "big integer" that's backed by 64-bit int only) builds
|
||||
// and passes tests.
|
||||
|
||||
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
|
||||
|
||||
// Set base feature level.
|
||||
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
|
||||
|
||||
// Enable extra Unix features.
|
||||
#include "../mpconfigvariant_common.h"
|
8
ports/unix/variants/longlong/mpconfigvariant.mk
Normal file
8
ports/unix/variants/longlong/mpconfigvariant.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
# build interpreter with "bigints" implemented as "longlong"
|
||||
|
||||
# otherwise, small int is essentially 64-bit
|
||||
MICROPY_FORCE_32BIT := 1
|
||||
|
||||
MICROPY_PY_FFI := 0
|
||||
|
||||
MPY_TOOL_FLAGS = -mlongint-impl longlong
|
105
py/misc.h
105
py/misc.h
@@ -33,10 +33,15 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) (0)
|
||||
#endif
|
||||
|
||||
/** generic ops *************************************************/
|
||||
|
||||
#ifndef MIN
|
||||
@@ -374,26 +379,23 @@ static inline bool mp_check(bool value) {
|
||||
static inline uint32_t mp_popcount(uint32_t x) {
|
||||
return __popcnt(x);
|
||||
}
|
||||
#else
|
||||
#else // _MSC_VER
|
||||
#define mp_clz(x) __builtin_clz(x)
|
||||
#define mp_clzl(x) __builtin_clzl(x)
|
||||
#define mp_clzll(x) __builtin_clzll(x)
|
||||
#define mp_ctz(x) __builtin_ctz(x)
|
||||
#define mp_check(x) (x)
|
||||
#if defined __has_builtin
|
||||
#if __has_builtin(__builtin_popcount)
|
||||
#define mp_popcount(x) __builtin_popcount(x)
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(mp_popcount)
|
||||
#else
|
||||
static inline uint32_t mp_popcount(uint32_t x) {
|
||||
x = x - ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
||||
return (x * 0x01010101) >> 24;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif // __has_builtin(__builtin_popcount)
|
||||
#endif // _MSC_VER
|
||||
|
||||
#define MP_FIT_UNSIGNED(bits, value) (((value) & (~0U << (bits))) == 0)
|
||||
#define MP_FIT_SIGNED(bits, value) \
|
||||
@@ -426,4 +428,93 @@ static inline uint32_t mp_clz_mpi(mp_int_t x) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Overflow-checked operations for long long
|
||||
|
||||
// Integer overflow builtins were added to GCC 5, but __has_builtin only in GCC 10
|
||||
//
|
||||
// Note that the builtins has a defined result when overflow occurs, whereas the custom
|
||||
// functions below don't update the result if an overflow would occur (to avoid UB).
|
||||
#define MP_GCC_HAS_BUILTIN_OVERFLOW (__GNUC__ >= 5)
|
||||
|
||||
#if __has_builtin(__builtin_umulll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW
|
||||
#define mp_mul_ull_overflow __builtin_umulll_overflow
|
||||
#else
|
||||
inline static bool mp_mul_ull_overflow(unsigned long long int x, unsigned long long int y, unsigned long long int *res) {
|
||||
if (y > 0 && x > (ULLONG_MAX / y)) {
|
||||
return true; // overflow
|
||||
}
|
||||
*res = x * y;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_smulll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW
|
||||
#define mp_mul_ll_overflow __builtin_smulll_overflow
|
||||
#else
|
||||
inline static bool mp_mul_ll_overflow(long long int x, long long int y, long long int *res) {
|
||||
bool overflow;
|
||||
|
||||
// Check for multiply overflow; see CERT INT32-C
|
||||
if (x > 0) { // x is positive
|
||||
if (y > 0) { // x and y are positive
|
||||
overflow = (x > (LLONG_MAX / y));
|
||||
} else { // x positive, y nonpositive
|
||||
overflow = (y < (LLONG_MIN / x));
|
||||
} // x positive, y nonpositive
|
||||
} else { // x is nonpositive
|
||||
if (y > 0) { // x is nonpositive, y is positive
|
||||
overflow = (x < (LLONG_MIN / y));
|
||||
} else { // x and y are nonpositive
|
||||
overflow = (x != 0 && y < (LLONG_MAX / x));
|
||||
} // End if x and y are nonpositive
|
||||
} // End if x is nonpositive
|
||||
|
||||
if (!overflow) {
|
||||
*res = x * y;
|
||||
}
|
||||
|
||||
return overflow;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_saddll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW
|
||||
#define mp_add_ll_overflow __builtin_saddll_overflow
|
||||
#else
|
||||
inline static bool mp_add_ll_overflow(long long int lhs, long long int rhs, long long int *res) {
|
||||
bool overflow;
|
||||
|
||||
if (rhs > 0) {
|
||||
overflow = (lhs > LLONG_MAX - rhs);
|
||||
} else {
|
||||
overflow = (lhs < LLONG_MIN - rhs);
|
||||
}
|
||||
|
||||
if (!overflow) {
|
||||
*res = lhs + rhs;
|
||||
}
|
||||
|
||||
return overflow;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_ssubll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW
|
||||
#define mp_sub_ll_overflow __builtin_ssubll_overflow
|
||||
#else
|
||||
inline static bool mp_sub_ll_overflow(long long int lhs, long long int rhs, long long int *res) {
|
||||
bool overflow;
|
||||
|
||||
if (rhs > 0) {
|
||||
overflow = (lhs < LLONG_MIN + rhs);
|
||||
} else {
|
||||
overflow = (lhs > LLONG_MAX + rhs);
|
||||
}
|
||||
|
||||
if (!overflow) {
|
||||
*res = lhs - rhs;
|
||||
}
|
||||
|
||||
return overflow;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MISC_H
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include "py/smallint.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
#include <math.h>
|
||||
@@ -43,6 +44,10 @@
|
||||
const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX};
|
||||
#endif
|
||||
|
||||
static void raise_long_long_overflow(void) {
|
||||
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage"));
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf) {
|
||||
int delta = 1;
|
||||
if (!big_endian) {
|
||||
@@ -120,7 +125,6 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
|
||||
// small int if the value fits without truncation
|
||||
case MP_UNARY_OP_HASH:
|
||||
return MP_OBJ_NEW_SMALL_INT((mp_int_t)o->val);
|
||||
|
||||
case MP_UNARY_OP_POSITIVE:
|
||||
return o_in;
|
||||
case MP_UNARY_OP_NEGATIVE:
|
||||
@@ -147,6 +151,8 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
|
||||
mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
|
||||
long long lhs_val;
|
||||
long long rhs_val;
|
||||
bool overflow = false;
|
||||
long long result;
|
||||
|
||||
if (mp_obj_is_small_int(lhs_in)) {
|
||||
lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs_in);
|
||||
@@ -167,13 +173,16 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
|
||||
switch (op) {
|
||||
case MP_BINARY_OP_ADD:
|
||||
case MP_BINARY_OP_INPLACE_ADD:
|
||||
return mp_obj_new_int_from_ll(lhs_val + rhs_val);
|
||||
overflow = mp_add_ll_overflow(lhs_val, rhs_val, &result);
|
||||
break;
|
||||
case MP_BINARY_OP_SUBTRACT:
|
||||
case MP_BINARY_OP_INPLACE_SUBTRACT:
|
||||
return mp_obj_new_int_from_ll(lhs_val - rhs_val);
|
||||
overflow = mp_sub_ll_overflow(lhs_val, rhs_val, &result);
|
||||
break;
|
||||
case MP_BINARY_OP_MULTIPLY:
|
||||
case MP_BINARY_OP_INPLACE_MULTIPLY:
|
||||
return mp_obj_new_int_from_ll(lhs_val * rhs_val);
|
||||
overflow = mp_mul_ll_overflow(lhs_val, rhs_val, &result);
|
||||
break;
|
||||
case MP_BINARY_OP_FLOOR_DIVIDE:
|
||||
case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE:
|
||||
if (rhs_val == 0) {
|
||||
@@ -199,9 +208,21 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
|
||||
|
||||
case MP_BINARY_OP_LSHIFT:
|
||||
case MP_BINARY_OP_INPLACE_LSHIFT:
|
||||
return mp_obj_new_int_from_ll(lhs_val << (int)rhs_val);
|
||||
if ((int)rhs_val < 0) {
|
||||
// negative shift not allowed
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("negative shift count"));
|
||||
}
|
||||
result = lhs_val << (int)rhs_val;
|
||||
// Left-shifting of negative values is implementation defined in C, but assume compiler
|
||||
// will give us typical 2s complement behaviour unless the value overflows
|
||||
overflow = rhs_val > 0 && ((lhs_val >= 0 && result < lhs_val) || (lhs_val < 0 && result > lhs_val));
|
||||
break;
|
||||
case MP_BINARY_OP_RSHIFT:
|
||||
case MP_BINARY_OP_INPLACE_RSHIFT:
|
||||
if ((int)rhs_val < 0) {
|
||||
// negative shift not allowed
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("negative shift count"));
|
||||
}
|
||||
return mp_obj_new_int_from_ll(lhs_val >> (int)rhs_val);
|
||||
|
||||
case MP_BINARY_OP_POWER:
|
||||
@@ -213,18 +234,18 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("negative power with no float support"));
|
||||
#endif
|
||||
}
|
||||
long long ans = 1;
|
||||
while (rhs_val > 0) {
|
||||
result = 1;
|
||||
while (rhs_val > 0 && !overflow) {
|
||||
if (rhs_val & 1) {
|
||||
ans *= lhs_val;
|
||||
overflow = mp_mul_ll_overflow(result, lhs_val, &result);
|
||||
}
|
||||
if (rhs_val == 1) {
|
||||
if (rhs_val == 1 || overflow) {
|
||||
break;
|
||||
}
|
||||
rhs_val /= 2;
|
||||
lhs_val *= lhs_val;
|
||||
overflow = mp_mul_ll_overflow(lhs_val, lhs_val, &lhs_val);
|
||||
}
|
||||
return mp_obj_new_int_from_ll(ans);
|
||||
break;
|
||||
}
|
||||
|
||||
case MP_BINARY_OP_LESS:
|
||||
@@ -242,6 +263,12 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
raise_long_long_overflow();
|
||||
}
|
||||
|
||||
return mp_obj_new_int_from_ll(result);
|
||||
|
||||
zero_division:
|
||||
mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero"));
|
||||
}
|
||||
@@ -265,22 +292,12 @@ mp_obj_t mp_obj_new_int_from_ll(long long val) {
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) {
|
||||
// TODO raise an exception if the unsigned long long won't fit
|
||||
if (val >> (sizeof(unsigned long long) * 8 - 1) != 0) {
|
||||
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ulonglong too large"));
|
||||
raise_long_long_overflow();
|
||||
}
|
||||
return mp_obj_new_int_from_ll(val);
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) {
|
||||
// TODO this does not honor the given length of the string, but it all cases it should anyway be null terminated
|
||||
// TODO check overflow
|
||||
char *endptr;
|
||||
mp_obj_t result = mp_obj_new_int_from_ll(strtoll(*str, &endptr, base));
|
||||
*str = endptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) {
|
||||
if (mp_obj_is_small_int(self_in)) {
|
||||
return MP_OBJ_SMALL_INT_VALUE(self_in);
|
||||
|
@@ -46,6 +46,27 @@ static MP_NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) {
|
||||
nlr_raise(exc);
|
||||
}
|
||||
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG
|
||||
// For the common small integer parsing case, we parse directly to mp_int_t and
|
||||
// check that the value doesn't overflow a smallint (in which case we fail over
|
||||
// to bigint parsing if supported)
|
||||
typedef mp_int_t parsed_int_t;
|
||||
|
||||
#define PARSED_INT_MUL_OVERFLOW mp_small_int_mul_overflow
|
||||
#define PARSED_INT_FITS MP_SMALL_INT_FITS
|
||||
#else
|
||||
// In the special case where bigint support is long long, we save code size by
|
||||
// parsing directly to long long and then return either a bigint or smallint
|
||||
// from the same result.
|
||||
//
|
||||
// To avoid pulling in (slow) signed 64-bit math routines we do the initial
|
||||
// parsing to an unsigned long long and only convert to signed at the end.
|
||||
typedef unsigned long long parsed_int_t;
|
||||
|
||||
#define PARSED_INT_MUL_OVERFLOW mp_mul_ull_overflow
|
||||
#define PARSED_INT_FITS(I) ((I) <= (unsigned long long)LLONG_MAX)
|
||||
#endif
|
||||
|
||||
mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, mp_lexer_t *lex) {
|
||||
const byte *restrict str = (const byte *)str_;
|
||||
const byte *restrict top = str + len;
|
||||
@@ -76,7 +97,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m
|
||||
str += mp_parse_num_base((const char *)str, top - str, &base);
|
||||
|
||||
// string should be an integer number
|
||||
mp_int_t int_val = 0;
|
||||
parsed_int_t parsed_val = 0;
|
||||
const byte *restrict str_val_start = str;
|
||||
for (; str < top; str++) {
|
||||
// get next digit as a value
|
||||
@@ -98,25 +119,29 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m
|
||||
break;
|
||||
}
|
||||
|
||||
// add next digi and check for overflow
|
||||
if (mp_small_int_mul_overflow(int_val, base)) {
|
||||
// add next digit and check for overflow
|
||||
if (PARSED_INT_MUL_OVERFLOW(parsed_val, base, &parsed_val)) {
|
||||
goto overflow;
|
||||
}
|
||||
int_val = int_val * base + dig;
|
||||
if (!MP_SMALL_INT_FITS(int_val)) {
|
||||
parsed_val += dig;
|
||||
if (!PARSED_INT_FITS(parsed_val)) {
|
||||
goto overflow;
|
||||
}
|
||||
}
|
||||
|
||||
// negate value if needed
|
||||
if (neg) {
|
||||
int_val = -int_val;
|
||||
}
|
||||
|
||||
// create the small int
|
||||
ret_val = MP_OBJ_NEW_SMALL_INT(int_val);
|
||||
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG
|
||||
// The PARSED_INT_FITS check above ensures parsed_val fits in small int representation
|
||||
ret_val = MP_OBJ_NEW_SMALL_INT(neg ? (-parsed_val) : parsed_val);
|
||||
have_ret_val:
|
||||
#else
|
||||
// The PARSED_INT_FITS check above ensures parsed_val won't overflow signed long long
|
||||
long long signed_val = parsed_val;
|
||||
if (neg) {
|
||||
signed_val = -signed_val;
|
||||
}
|
||||
ret_val = mp_obj_new_int_from_ll(signed_val); // Could be large or small int
|
||||
#endif
|
||||
|
||||
// check we parsed something
|
||||
if (str == str_val_start) {
|
||||
goto value_error;
|
||||
@@ -135,6 +160,7 @@ have_ret_val:
|
||||
return ret_val;
|
||||
|
||||
overflow:
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG
|
||||
// reparse using long int
|
||||
{
|
||||
const char *s2 = (const char *)str_val_start;
|
||||
@@ -142,6 +168,9 @@ overflow:
|
||||
str = (const byte *)s2;
|
||||
goto have_ret_val;
|
||||
}
|
||||
#else
|
||||
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage"));
|
||||
#endif
|
||||
|
||||
value_error:
|
||||
{
|
||||
|
13
py/runtime.c
13
py/runtime.c
@@ -505,13 +505,14 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mp_small_int_mul_overflow(lhs_val, rhs_val)) {
|
||||
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);
|
||||
goto generic_binary_op;
|
||||
} else {
|
||||
// use standard precision
|
||||
return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val);
|
||||
return MP_OBJ_NEW_SMALL_INT(int_res);
|
||||
}
|
||||
}
|
||||
case MP_BINARY_OP_FLOOR_DIVIDE:
|
||||
@@ -552,19 +553,19 @@ 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)) {
|
||||
if (mp_small_int_mul_overflow(ans, lhs_val, &ans)) {
|
||||
goto power_overflow;
|
||||
}
|
||||
ans *= lhs_val;
|
||||
}
|
||||
if (rhs_val == 1) {
|
||||
break;
|
||||
}
|
||||
rhs_val /= 2;
|
||||
if (mp_small_int_mul_overflow(lhs_val, lhs_val)) {
|
||||
mp_int_t int_res;
|
||||
if (mp_small_int_mul_overflow(lhs_val, lhs_val, &int_res)) {
|
||||
goto power_overflow;
|
||||
}
|
||||
lhs_val *= lhs_val;
|
||||
lhs_val = int_res;
|
||||
}
|
||||
lhs_val = ans;
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "py/smallint.h"
|
||||
|
||||
bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) {
|
||||
bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y, mp_int_t *res) {
|
||||
// Check for multiply overflow; see CERT INT32-C
|
||||
if (x > 0) { // x is positive
|
||||
if (y > 0) { // x and y are positive
|
||||
@@ -49,6 +49,9 @@ bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) {
|
||||
}
|
||||
} // End if x and y are nonpositive
|
||||
} // End if x is nonpositive
|
||||
|
||||
// Result doesn't overflow
|
||||
*res = x * y;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -68,7 +68,10 @@
|
||||
// The number of bits in a MP_SMALL_INT including the sign bit.
|
||||
#define MP_SMALL_INT_BITS (MP_IMAX_BITS(MP_SMALL_INT_MAX) + 1)
|
||||
|
||||
bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y);
|
||||
// Multiply two small ints.
|
||||
// If returns false, the correct result is stored in 'res'
|
||||
// If returns true, the multiplication would have overflowed. 'res' is unchanged.
|
||||
bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y, mp_int_t *res);
|
||||
mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor);
|
||||
mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom);
|
||||
|
||||
|
137
tests/basics/int_64_basics.py
Normal file
137
tests/basics/int_64_basics.py
Normal file
@@ -0,0 +1,137 @@
|
||||
# test support for 64-bit long integers
|
||||
# (some ports don't support arbitrary precision but do support these)
|
||||
|
||||
# this test is adapted from int_big1.py with numbers kept within 64-bit signed range
|
||||
|
||||
# to test arbitrary precision integers
|
||||
|
||||
x = 1000000000000000000
|
||||
xn = -1000000000000000000
|
||||
y = 2000000000000000000
|
||||
|
||||
# printing
|
||||
print(x)
|
||||
print(y)
|
||||
print('%#X' % (x - x)) # print prefix
|
||||
print('{:#,}'.format(x)) # print with commas
|
||||
|
||||
# construction
|
||||
print(int(x))
|
||||
|
||||
# addition
|
||||
print(x + 1)
|
||||
print(x + y)
|
||||
print(x + xn == 0)
|
||||
print(bool(x + xn))
|
||||
|
||||
# subtraction
|
||||
print(x - 1)
|
||||
print(x - y)
|
||||
print(y - x)
|
||||
print(x - x == 0)
|
||||
print(bool(x - x))
|
||||
|
||||
# multiplication
|
||||
print(x * 2)
|
||||
print(1090511627776 * 1048500)
|
||||
|
||||
# integer division
|
||||
print(x // 2)
|
||||
print(y // x)
|
||||
|
||||
# bit inversion
|
||||
print(~x)
|
||||
print(~(-x))
|
||||
|
||||
# left shift
|
||||
print("left shift positive")
|
||||
x = 0x40000000
|
||||
for i in range(32):
|
||||
x = x << 1
|
||||
print(x)
|
||||
|
||||
# right shift
|
||||
print("right shift positive")
|
||||
x = 0x2000000000000000 # TODO: why can't second-tip bit be set?
|
||||
for i in range(64):
|
||||
x = x >> 1
|
||||
print(x)
|
||||
|
||||
# left shift of a negative number
|
||||
print("left shift negative")
|
||||
for i in range(8):
|
||||
print(-10000000000000000 << i)
|
||||
print(-10000000000000001 << i)
|
||||
print(-10000000000000002 << i)
|
||||
print(-10000000000000003 << i)
|
||||
print(-10000000000000004 << i)
|
||||
|
||||
|
||||
# right shift of a negative number
|
||||
print("right shift negative")
|
||||
for i in range(8):
|
||||
print(-1000000000000000000 >> i)
|
||||
print(-1000000000000000001 >> i)
|
||||
print(-1000000000000000002 >> i)
|
||||
print(-1000000000000000003 >> i)
|
||||
print(-1000000000000000004 >> i)
|
||||
|
||||
# conversion from string
|
||||
print(int("1234567890123456789"))
|
||||
print(int("-1234567890123456789"))
|
||||
print(int("1234567890abcdef", 16))
|
||||
print(int("1234567890ABCDEF", 16))
|
||||
print(int("-1234567890ABCDEF", 16))
|
||||
print(int("ijklmnopqrsz", 36))
|
||||
|
||||
# numbers close to 64-bit limits
|
||||
print(int("-9111222333444555666"))
|
||||
print(int("9111222333444555666"))
|
||||
|
||||
# numbers with preceding 0s
|
||||
print(int("-00000000000000000000009111222333444555666"))
|
||||
print(int("0000000000000000000000009111222333444555666"))
|
||||
|
||||
# invalid characters in string
|
||||
try:
|
||||
print(int("1234567890abcdef"))
|
||||
except ValueError:
|
||||
print('ValueError');
|
||||
try:
|
||||
print(int("123456789\x01"))
|
||||
except ValueError:
|
||||
print('ValueError');
|
||||
|
||||
# test parsing ints just on threshold of small to big
|
||||
# for 32 bit archs
|
||||
x = 1073741823 # small
|
||||
x = -1073741823 # small
|
||||
x = 1073741824 # big
|
||||
x = -1073741824 # big
|
||||
# for 64 bit archs
|
||||
x = 4611686018427387903 # small
|
||||
x = -4611686018427387903 # small
|
||||
x = 4611686018427387904 # big
|
||||
x = -4611686018427387904 # big
|
||||
|
||||
# sys.maxsize is a constant bigint, so test it's compatible with dynamic ones
|
||||
import sys
|
||||
if hasattr(sys, "maxsize"):
|
||||
print(sys.maxsize - 1 + 1 == sys.maxsize)
|
||||
else:
|
||||
print(True) # No maxsize property in this config
|
||||
|
||||
# test extraction of big int value via mp_obj_get_int_maybe
|
||||
x = 1 << 62
|
||||
print('a' * (x + 4 - x))
|
||||
|
||||
# negative shifts are invalid
|
||||
try:
|
||||
print((1 << 48) >> -4)
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
|
||||
try:
|
||||
print((1 << 48) << -6)
|
||||
except ValueError as e:
|
||||
print(e)
|
16
tests/extmod/json_loads_int_64.py
Normal file
16
tests/extmod/json_loads_int_64.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Parse 64-bit integers from JSON payloads.
|
||||
#
|
||||
# This also exercises parsing integers from strings
|
||||
# where the value may not be null terminated (last line)
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
print(json.loads("9111222333444555666"))
|
||||
print(json.loads("-9111222333444555666"))
|
||||
print(json.loads("9111222333444555666"))
|
||||
print(json.loads("-9111222333444555666"))
|
||||
print(json.loads("[\"9111222333444555666777\",9111222333444555666]"))
|
@@ -12,5 +12,8 @@ for i in range(8):
|
||||
print(uctypes.addressof(uctypes.bytearray_at(1 << i, 8)))
|
||||
|
||||
# Test address that is bigger than the greatest small-int but still within the address range.
|
||||
try:
|
||||
large_addr = maxsize + 1
|
||||
print(uctypes.addressof(uctypes.bytearray_at(large_addr, 8)) == large_addr)
|
||||
except OverflowError:
|
||||
print(True) # systems with 64-bit bigints will overflow on the above operation
|
||||
|
@@ -6,6 +6,13 @@ except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
# 'int' needs to be able to represent UINT64 for this test
|
||||
try:
|
||||
int("FF" * 8, 16)
|
||||
except OverflowError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
N = 5
|
||||
|
||||
for endian in ("NATIVE", "LITTLE_ENDIAN", "BIG_ENDIAN"):
|
||||
|
2
tests/feature_check/int_64.py
Normal file
2
tests/feature_check/int_64.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# Check whether 64-bit long integers are supported
|
||||
print(1 << 62)
|
1
tests/feature_check/int_64.py.exp
Normal file
1
tests/feature_check/int_64.py.exp
Normal file
@@ -0,0 +1 @@
|
||||
4611686018427387904
|
@@ -5,6 +5,12 @@
|
||||
# This benchmark stresses big integer arithmetic.
|
||||
# Adapted from code on: http://benchmarksgame.alioth.debian.org/
|
||||
|
||||
try:
|
||||
int("0x10000000000000000", 16)
|
||||
except:
|
||||
print("SKIP") # No support for >64-bit integers
|
||||
raise SystemExit
|
||||
|
||||
|
||||
def compose(a, b):
|
||||
aq, ar, as_, at = a
|
||||
|
@@ -628,6 +628,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
||||
skip_tests = set()
|
||||
skip_native = False
|
||||
skip_int_big = False
|
||||
skip_int_64 = False
|
||||
skip_bytearray = False
|
||||
skip_set_type = False
|
||||
skip_slice = False
|
||||
@@ -658,6 +659,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
||||
if output != b"1000000000000000000000000000000000000000000000\n":
|
||||
skip_int_big = True
|
||||
|
||||
# Check if 'long long' precision integers are supported, even if arbitrary precision is not
|
||||
output = run_feature_check(pyb, args, "int_64.py")
|
||||
if output != b"4611686018427387904\n":
|
||||
skip_int_64 = True
|
||||
|
||||
# Check if bytearray is supported, and skip such tests if it's not
|
||||
output = run_feature_check(pyb, args, "bytearray.py")
|
||||
if output != b"bytearray\n":
|
||||
@@ -885,7 +891,12 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
||||
test_name = os.path.splitext(os.path.basename(test_file))[0]
|
||||
is_native = test_name.startswith("native_") or test_name.startswith("viper_")
|
||||
is_endian = test_name.endswith("_endian")
|
||||
is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig")
|
||||
is_int_big = (
|
||||
test_name.startswith("int_big")
|
||||
or test_name.endswith("_intbig")
|
||||
or test_name.startswith("ffi_int") # these tests contain large integer literals
|
||||
)
|
||||
is_int_64 = test_name.startswith("int_64") or test_name.endswith("_int64")
|
||||
is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray")
|
||||
is_set_type = test_name.startswith(("set_", "frozenset")) or test_name.endswith("_set")
|
||||
is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests
|
||||
@@ -899,6 +910,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
||||
skip_it |= skip_native and is_native
|
||||
skip_it |= skip_endian and is_endian
|
||||
skip_it |= skip_int_big and is_int_big
|
||||
skip_it |= skip_int_64 and is_int_64
|
||||
skip_it |= skip_bytearray and is_bytearray
|
||||
skip_it |= skip_set_type and is_set_type
|
||||
skip_it |= skip_slice and is_slice
|
||||
|
@@ -695,6 +695,14 @@ function ci_unix_nanbox_run_tests {
|
||||
ci_unix_run_tests_full_no_native_helper nanbox PYTHON=python2.7
|
||||
}
|
||||
|
||||
function ci_unix_longlong_build {
|
||||
ci_unix_build_helper VARIANT=longlong
|
||||
}
|
||||
|
||||
function ci_unix_longlong_run_tests {
|
||||
ci_unix_run_tests_full_helper longlong
|
||||
}
|
||||
|
||||
function ci_unix_float_build {
|
||||
ci_unix_build_helper VARIANT=standard CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT"
|
||||
ci_unix_build_ffi_lib_helper gcc
|
||||
|
Reference in New Issue
Block a user