py: Add MP_BINARY_OP_DIVMOD to simplify and consolidate divmod builtin.
diff --git a/py/bc0.h b/py/bc0.h
index 2dfdc70..0a2d909 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 ec40056..16b0e32 100644
--- a/py/modbuiltins.c
+++ b/py/modbuiltins.c
@@ -230,44 +230,7 @@
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 7fa2dca..7275886 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -575,7 +575,6 @@
} 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 ffb3c71..0af4199 100644
--- a/py/objfloat.c
+++ b/py/objfloat.c
@@ -126,6 +126,41 @@
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 @@
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 @@
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 f593271..06a067d 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -400,6 +400,7 @@
/*
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 886146f..501ca43 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -441,6 +441,17 @@
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 65c7df0..cdcb6e3 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -74,29 +74,30 @@
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 5ae4180..d6ff153 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1199,7 +1199,7 @@
} 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 7c8f81b..413914b 100644
--- a/py/vmentrytable.h
+++ b/py/vmentrytable.h
@@ -112,7 +112,7 @@
[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 6231d3f..fc3d85f 100644
--- a/tests/cmdline/cmd_showbc.py.exp
+++ b/tests/cmdline/cmd_showbc.py.exp
@@ -93,21 +93,21 @@
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