From c5029bcbf37fd1a3fb145d9fa9e4a5094478f17b Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 13 Jun 2015 22:00:10 +0100 Subject: [PATCH] py: Add MP_BINARY_OP_DIVMOD to simplify and consolidate divmod builtin. --- py/bc0.h | 2 +- py/modbuiltins.c | 39 +--------------- py/obj.h | 1 - py/objfloat.c | 81 +++++++++++++++++++-------------- py/objtype.c | 1 + py/runtime.c | 11 +++++ py/runtime0.h | 9 ++-- py/vm.c | 2 +- py/vmentrytable.h | 2 +- tests/cmdline/cmd_showbc.py.exp | 8 ++-- 10 files changed, 71 insertions(+), 85 deletions(-) diff --git a/py/bc0.h b/py/bc0.h index 2dfdc70119..0a2d9092f9 100644 --- a/py/bc0.h +++ b/py/bc0.h @@ -117,6 +117,6 @@ #define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16) #define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16) #define MP_BC_UNARY_OP_MULTI (0xd0) // + op(6) -#define MP_BC_BINARY_OP_MULTI (0xd6) // + op(35) +#define MP_BC_BINARY_OP_MULTI (0xd6) // + op(36) #endif // __MICROPY_INCLUDED_PY_BC0_H__ diff --git a/py/modbuiltins.c b/py/modbuiltins.c index ec40056e82..16b0e320ca 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -230,44 +230,7 @@ STATIC mp_obj_t mp_builtin_dir(mp_uint_t n_args, const mp_obj_t *args) { MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj, 0, 1, mp_builtin_dir); STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) { - // TODO handle big int - if (MP_OBJ_IS_SMALL_INT(o1_in) && MP_OBJ_IS_SMALL_INT(o2_in)) { - mp_int_t i1 = MP_OBJ_SMALL_INT_VALUE(o1_in); - mp_int_t i2 = MP_OBJ_SMALL_INT_VALUE(o2_in); - if (i2 == 0) { - #if MICROPY_PY_BUILTINS_FLOAT - zero_division_error: - #endif - nlr_raise(mp_obj_new_exception_msg(&mp_type_ZeroDivisionError, "division by zero")); - } - mp_obj_t args[2]; - args[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(i1, i2)); - args[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(i1, i2)); - return mp_obj_new_tuple(2, args); - #if MICROPY_PY_BUILTINS_FLOAT - } else if (MP_OBJ_IS_TYPE(o1_in, &mp_type_float) || MP_OBJ_IS_TYPE(o2_in, &mp_type_float)) { - mp_float_t f1 = mp_obj_get_float(o1_in); - mp_float_t f2 = mp_obj_get_float(o2_in); - if (f2 == 0.0) { - goto zero_division_error; - } - mp_obj_float_divmod(&f1, &f2); - mp_obj_t tuple[2] = { - mp_obj_new_float(f1), - mp_obj_new_float(f2), - }; - return mp_obj_new_tuple(2, tuple); - #endif - } else { - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, - "unsupported operand type(s) for divmod()")); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, - "unsupported operand type(s) for divmod(): '%s' and '%s'", - mp_obj_get_type_str(o1_in), mp_obj_get_type_str(o2_in))); - } - } + return mp_binary_op(MP_BINARY_OP_DIVMOD, o1_in, o2_in); } MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod); diff --git a/py/obj.h b/py/obj.h index 7fa2dca098..7275886c76 100644 --- a/py/obj.h +++ b/py/obj.h @@ -575,7 +575,6 @@ typedef struct _mp_obj_float_t { } mp_obj_float_t; mp_float_t mp_obj_float_get(mp_obj_t self_in); mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs); // can return MP_OBJ_NULL if op not supported -void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y); // complex void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); diff --git a/py/objfloat.c b/py/objfloat.c index ffb3c71964..0af41990ff 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -126,6 +126,41 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in) { return self->value; } +STATIC void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) { + // logic here follows that of CPython + // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations + // x == (x//y)*y + (x%y) + // divmod(x, y) == (x//y, x%y) + mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y); + mp_float_t div = (*x - mod) / *y; + + // Python specs require that mod has same sign as second operand + if (mod == 0.0) { + mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y); + } else { + if ((mod < 0.0) != (*y < 0.0)) { + mod += *y; + div -= 1.0; + } + } + + mp_float_t floordiv; + if (div == 0.0) { + // if division is zero, take the correct sign of zero + floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y); + } else { + // Python specs require that x == (x//y)*y + (x%y) + floordiv = MICROPY_FLOAT_C_FUN(floor)(div); + if (div - floordiv > 0.5) { + floordiv += 1.0; + } + } + + // return results + *x = floordiv; + *y = mod; +} + mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_in) { mp_float_t rhs_val = mp_obj_get_float(rhs_in); // can be any type, this function will convert to float (if possible) switch (op) { @@ -170,6 +205,17 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i break; case MP_BINARY_OP_POWER: case MP_BINARY_OP_INPLACE_POWER: lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break; + case MP_BINARY_OP_DIVMOD: { + if (rhs_val == 0) { + goto zero_division_error; + } + mp_obj_float_divmod(&lhs_val, &rhs_val); + mp_obj_t tuple[2] = { + mp_obj_new_float(lhs_val), + mp_obj_new_float(rhs_val), + }; + return mp_obj_new_tuple(2, tuple); + } case MP_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val); case MP_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val); case MP_BINARY_OP_EQUAL: return MP_BOOL(lhs_val == rhs_val); @@ -182,39 +228,4 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i return mp_obj_new_float(lhs_val); } -void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) { - // logic here follows that of CPython - // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations - // x == (x//y)*y + (x%y) - // divmod(x, y) == (x//y, x%y) - mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y); - mp_float_t div = (*x - mod) / *y; - - // Python specs require that mod has same sign as second operand - if (mod == 0.0) { - mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y); - } else { - if ((mod < 0.0) != (*y < 0.0)) { - mod += *y; - div -= 1.0; - } - } - - mp_float_t floordiv; - if (div == 0.0) { - // if division is zero, take the correct sign of zero - floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y); - } else { - // Python specs require that x == (x//y)*y + (x%y) - floordiv = MICROPY_FLOAT_C_FUN(floor)(div); - if (div - floordiv > 0.5) { - floordiv += 1.0; - } - } - - // return results - *x = floordiv; - *y = mod; -} - #endif // MICROPY_PY_BUILTINS_FLOAT diff --git a/py/objtype.c b/py/objtype.c index f593271fb5..06a067dcdc 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -400,6 +400,7 @@ const qstr mp_binary_op_method_name[] = { /* MP_BINARY_OP_MODULO, MP_BINARY_OP_POWER, + MP_BINARY_OP_DIVMOD, MP_BINARY_OP_INPLACE_OR, MP_BINARY_OP_INPLACE_XOR, MP_BINARY_OP_INPLACE_AND, diff --git a/py/runtime.c b/py/runtime.c index 886146fa87..501ca435a0 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -441,6 +441,17 @@ mp_obj_t mp_binary_op(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs) { lhs = mp_obj_new_int_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs)); goto generic_binary_op; + case MP_BINARY_OP_DIVMOD: { + if (rhs_val == 0) { + goto zero_division; + } + // to reduce stack usage we don't pass a temp array of the 2 items + mp_obj_tuple_t *tuple = mp_obj_new_tuple(2, NULL); + tuple->items[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(lhs_val, rhs_val)); + tuple->items[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(lhs_val, rhs_val)); + return tuple; + } + case MP_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val); break; case MP_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val); break; case MP_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break; diff --git a/py/runtime0.h b/py/runtime0.h index 65c7df0ae3..cdcb6e3dca 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -74,29 +74,30 @@ typedef enum { MP_BINARY_OP_MODULO, MP_BINARY_OP_POWER, + MP_BINARY_OP_DIVMOD, // not emitted by the compiler but supported by the runtime MP_BINARY_OP_INPLACE_OR, MP_BINARY_OP_INPLACE_XOR, - MP_BINARY_OP_INPLACE_AND, + MP_BINARY_OP_INPLACE_AND, MP_BINARY_OP_INPLACE_LSHIFT, MP_BINARY_OP_INPLACE_RSHIFT, MP_BINARY_OP_INPLACE_ADD, MP_BINARY_OP_INPLACE_SUBTRACT, - MP_BINARY_OP_INPLACE_MULTIPLY, + MP_BINARY_OP_INPLACE_MULTIPLY, MP_BINARY_OP_INPLACE_FLOOR_DIVIDE, MP_BINARY_OP_INPLACE_TRUE_DIVIDE, MP_BINARY_OP_INPLACE_MODULO, MP_BINARY_OP_INPLACE_POWER, + // these should return a bool MP_BINARY_OP_LESS, - MP_BINARY_OP_MORE, MP_BINARY_OP_EQUAL, MP_BINARY_OP_LESS_EQUAL, MP_BINARY_OP_MORE_EQUAL, - MP_BINARY_OP_NOT_EQUAL, + MP_BINARY_OP_NOT_EQUAL, MP_BINARY_OP_IN, MP_BINARY_OP_IS, MP_BINARY_OP_EXCEPTION_MATCH, diff --git a/py/vm.c b/py/vm.c index 5ae4180689..d6ff15356e 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1199,7 +1199,7 @@ yield: } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 6) { SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP())); DISPATCH(); - } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 35) { + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 36) { mp_obj_t rhs = POP(); mp_obj_t lhs = TOP(); SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs)); diff --git a/py/vmentrytable.h b/py/vmentrytable.h index 7c8f81bf30..413914b6ef 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -112,7 +112,7 @@ static void* entry_table[256] = { [MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + 15] = &&entry_MP_BC_LOAD_FAST_MULTI, [MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + 15] = &&entry_MP_BC_STORE_FAST_MULTI, [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + 5] = &&entry_MP_BC_UNARY_OP_MULTI, - [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 34] = &&entry_MP_BC_BINARY_OP_MULTI, + [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 35] = &&entry_MP_BC_BINARY_OP_MULTI, }; #if __clang__ diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 6231d3fe96..fc3d85f9da 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -93,21 +93,21 @@ arg names: 69 LOAD_DEREF 14 71 DUP_TOP 72 ROT_THREE -73 BINARY_OP 26 __eq__ +73 BINARY_OP 27 __eq__ 74 JUMP_IF_FALSE_OR_POP 82 77 LOAD_FAST 1 -78 BINARY_OP 26 __eq__ +78 BINARY_OP 27 __eq__ 79 JUMP 84 82 ROT_TWO 83 POP_TOP 84 STORE_FAST 10 85 LOAD_FAST 0 86 LOAD_DEREF 14 -88 BINARY_OP 26 __eq__ +88 BINARY_OP 27 __eq__ 89 JUMP_IF_FALSE_OR_POP 96 92 LOAD_DEREF 14 94 LOAD_FAST 1 -95 BINARY_OP 26 __eq__ +95 BINARY_OP 27 __eq__ 96 UNARY_OP 0 97 NOT 98 STORE_FAST 10