Commit c5029bcb authored by Damien George's avatar Damien George
Browse files

py: Add MP_BINARY_OP_DIVMOD to simplify and consolidate divmod builtin.

parent 6f495200
...@@ -117,6 +117,6 @@ ...@@ -117,6 +117,6 @@
#define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16) #define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16)
#define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16) #define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16)
#define MP_BC_UNARY_OP_MULTI (0xd0) // + op(6) #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__ #endif // __MICROPY_INCLUDED_PY_BC0_H__
...@@ -230,44 +230,7 @@ STATIC mp_obj_t mp_builtin_dir(mp_uint_t n_args, const mp_obj_t *args) { ...@@ -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); 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) { STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) {
// TODO handle big int return mp_binary_op(MP_BINARY_OP_DIVMOD, o1_in, o2_in);
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)));
}
}
} }
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod); MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod);
......
...@@ -575,7 +575,6 @@ typedef struct _mp_obj_float_t { ...@@ -575,7 +575,6 @@ typedef struct _mp_obj_float_t {
} mp_obj_float_t; } mp_obj_float_t;
mp_float_t mp_obj_float_get(mp_obj_t self_in); 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 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 // complex
void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag); void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);
......
...@@ -126,6 +126,41 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in) { ...@@ -126,6 +126,41 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in) {
return self->value; 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_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) 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) { 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 ...@@ -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; break;
case MP_BINARY_OP_POWER: 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_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_LESS: return MP_BOOL(lhs_val < rhs_val);
case MP_BINARY_OP_MORE: 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); 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 ...@@ -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); 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 #endif // MICROPY_PY_BUILTINS_FLOAT
...@@ -400,6 +400,7 @@ const qstr mp_binary_op_method_name[] = { ...@@ -400,6 +400,7 @@ const qstr mp_binary_op_method_name[] = {
/* /*
MP_BINARY_OP_MODULO, MP_BINARY_OP_MODULO,
MP_BINARY_OP_POWER, MP_BINARY_OP_POWER,
MP_BINARY_OP_DIVMOD,
MP_BINARY_OP_INPLACE_OR, MP_BINARY_OP_INPLACE_OR,
MP_BINARY_OP_INPLACE_XOR, MP_BINARY_OP_INPLACE_XOR,
MP_BINARY_OP_INPLACE_AND, MP_BINARY_OP_INPLACE_AND,
......
...@@ -441,6 +441,17 @@ mp_obj_t mp_binary_op(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs) { ...@@ -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)); lhs = mp_obj_new_int_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs));
goto generic_binary_op; 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_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_MORE: return MP_BOOL(lhs_val > rhs_val); break;
case MP_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break; case MP_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break;
......
...@@ -74,29 +74,30 @@ typedef enum { ...@@ -74,29 +74,30 @@ typedef enum {
MP_BINARY_OP_MODULO, MP_BINARY_OP_MODULO,
MP_BINARY_OP_POWER, 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_OR,
MP_BINARY_OP_INPLACE_XOR, MP_BINARY_OP_INPLACE_XOR,
MP_BINARY_OP_INPLACE_AND,
MP_BINARY_OP_INPLACE_AND,
MP_BINARY_OP_INPLACE_LSHIFT, MP_BINARY_OP_INPLACE_LSHIFT,
MP_BINARY_OP_INPLACE_RSHIFT, MP_BINARY_OP_INPLACE_RSHIFT,
MP_BINARY_OP_INPLACE_ADD, MP_BINARY_OP_INPLACE_ADD,
MP_BINARY_OP_INPLACE_SUBTRACT, 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_FLOOR_DIVIDE,
MP_BINARY_OP_INPLACE_TRUE_DIVIDE, MP_BINARY_OP_INPLACE_TRUE_DIVIDE,
MP_BINARY_OP_INPLACE_MODULO, MP_BINARY_OP_INPLACE_MODULO,
MP_BINARY_OP_INPLACE_POWER, MP_BINARY_OP_INPLACE_POWER,
// these should return a bool // these should return a bool
MP_BINARY_OP_LESS, MP_BINARY_OP_LESS,
MP_BINARY_OP_MORE, MP_BINARY_OP_MORE,
MP_BINARY_OP_EQUAL, MP_BINARY_OP_EQUAL,
MP_BINARY_OP_LESS_EQUAL, MP_BINARY_OP_LESS_EQUAL,
MP_BINARY_OP_MORE_EQUAL, MP_BINARY_OP_MORE_EQUAL,
MP_BINARY_OP_NOT_EQUAL,
MP_BINARY_OP_NOT_EQUAL,
MP_BINARY_OP_IN, MP_BINARY_OP_IN,
MP_BINARY_OP_IS, MP_BINARY_OP_IS,
MP_BINARY_OP_EXCEPTION_MATCH, MP_BINARY_OP_EXCEPTION_MATCH,
......
...@@ -1199,7 +1199,7 @@ yield: ...@@ -1199,7 +1199,7 @@ yield:
} else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 6) { } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 6) {
SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP())); SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP()));
DISPATCH(); 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 rhs = POP();
mp_obj_t lhs = TOP(); mp_obj_t lhs = TOP();
SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs)); SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs));
......
...@@ -112,7 +112,7 @@ static void* entry_table[256] = { ...@@ -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_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_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_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__ #if __clang__
......
...@@ -93,21 +93,21 @@ arg names: ...@@ -93,21 +93,21 @@ arg names:
69 LOAD_DEREF 14 69 LOAD_DEREF 14
71 DUP_TOP 71 DUP_TOP
72 ROT_THREE 72 ROT_THREE
73 BINARY_OP 26 __eq__ 73 BINARY_OP 27 __eq__
74 JUMP_IF_FALSE_OR_POP 82 74 JUMP_IF_FALSE_OR_POP 82
77 LOAD_FAST 1 77 LOAD_FAST 1
78 BINARY_OP 26 __eq__ 78 BINARY_OP 27 __eq__
79 JUMP 84 79 JUMP 84
82 ROT_TWO 82 ROT_TWO
83 POP_TOP 83 POP_TOP
84 STORE_FAST 10 84 STORE_FAST 10
85 LOAD_FAST 0 85 LOAD_FAST 0
86 LOAD_DEREF 14 86 LOAD_DEREF 14
88 BINARY_OP 26 __eq__ 88 BINARY_OP 27 __eq__
89 JUMP_IF_FALSE_OR_POP 96 89 JUMP_IF_FALSE_OR_POP 96
92 LOAD_DEREF 14 92 LOAD_DEREF 14
94 LOAD_FAST 1 94 LOAD_FAST 1
95 BINARY_OP 26 __eq__ 95 BINARY_OP 27 __eq__
96 UNARY_OP 0 96 UNARY_OP 0
97 NOT 97 NOT
98 STORE_FAST 10 98 STORE_FAST 10
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment