Skip to content

Commit c5029bc

Browse files
committed
py: Add MP_BINARY_OP_DIVMOD to simplify and consolidate divmod builtin.
1 parent 6f49520 commit c5029bc

File tree

10 files changed

+71
-85
lines changed

10 files changed

+71
-85
lines changed

py/bc0.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,6 @@
117117
#define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16)
118118
#define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16)
119119
#define MP_BC_UNARY_OP_MULTI (0xd0) // + op(6)
120-
#define MP_BC_BINARY_OP_MULTI (0xd6) // + op(35)
120+
#define MP_BC_BINARY_OP_MULTI (0xd6) // + op(36)
121121

122122
#endif // __MICROPY_INCLUDED_PY_BC0_H__

py/modbuiltins.c

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -230,44 +230,7 @@ STATIC mp_obj_t mp_builtin_dir(mp_uint_t n_args, const mp_obj_t *args) {
230230
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj, 0, 1, mp_builtin_dir);
231231

232232
STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) {
233-
// TODO handle big int
234-
if (MP_OBJ_IS_SMALL_INT(o1_in) && MP_OBJ_IS_SMALL_INT(o2_in)) {
235-
mp_int_t i1 = MP_OBJ_SMALL_INT_VALUE(o1_in);
236-
mp_int_t i2 = MP_OBJ_SMALL_INT_VALUE(o2_in);
237-
if (i2 == 0) {
238-
#if MICROPY_PY_BUILTINS_FLOAT
239-
zero_division_error:
240-
#endif
241-
nlr_raise(mp_obj_new_exception_msg(&mp_type_ZeroDivisionError, "division by zero"));
242-
}
243-
mp_obj_t args[2];
244-
args[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(i1, i2));
245-
args[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(i1, i2));
246-
return mp_obj_new_tuple(2, args);
247-
#if MICROPY_PY_BUILTINS_FLOAT
248-
} else if (MP_OBJ_IS_TYPE(o1_in, &mp_type_float) || MP_OBJ_IS_TYPE(o2_in, &mp_type_float)) {
249-
mp_float_t f1 = mp_obj_get_float(o1_in);
250-
mp_float_t f2 = mp_obj_get_float(o2_in);
251-
if (f2 == 0.0) {
252-
goto zero_division_error;
253-
}
254-
mp_obj_float_divmod(&f1, &f2);
255-
mp_obj_t tuple[2] = {
256-
mp_obj_new_float(f1),
257-
mp_obj_new_float(f2),
258-
};
259-
return mp_obj_new_tuple(2, tuple);
260-
#endif
261-
} else {
262-
if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
263-
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
264-
"unsupported operand type(s) for divmod()"));
265-
} else {
266-
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
267-
"unsupported operand type(s) for divmod(): '%s' and '%s'",
268-
mp_obj_get_type_str(o1_in), mp_obj_get_type_str(o2_in)));
269-
}
270-
}
233+
return mp_binary_op(MP_BINARY_OP_DIVMOD, o1_in, o2_in);
271234
}
272235
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod);
273236

py/obj.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,6 @@ typedef struct _mp_obj_float_t {
575575
} mp_obj_float_t;
576576
mp_float_t mp_obj_float_get(mp_obj_t self_in);
577577
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
578-
void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y);
579578

580579
// complex
581580
void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);

py/objfloat.c

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,41 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in) {
126126
return self->value;
127127
}
128128

129+
STATIC void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) {
130+
// logic here follows that of CPython
131+
// https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
132+
// x == (x//y)*y + (x%y)
133+
// divmod(x, y) == (x//y, x%y)
134+
mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y);
135+
mp_float_t div = (*x - mod) / *y;
136+
137+
// Python specs require that mod has same sign as second operand
138+
if (mod == 0.0) {
139+
mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y);
140+
} else {
141+
if ((mod < 0.0) != (*y < 0.0)) {
142+
mod += *y;
143+
div -= 1.0;
144+
}
145+
}
146+
147+
mp_float_t floordiv;
148+
if (div == 0.0) {
149+
// if division is zero, take the correct sign of zero
150+
floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y);
151+
} else {
152+
// Python specs require that x == (x//y)*y + (x%y)
153+
floordiv = MICROPY_FLOAT_C_FUN(floor)(div);
154+
if (div - floordiv > 0.5) {
155+
floordiv += 1.0;
156+
}
157+
}
158+
159+
// return results
160+
*x = floordiv;
161+
*y = mod;
162+
}
163+
129164
mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_in) {
130165
mp_float_t rhs_val = mp_obj_get_float(rhs_in); // can be any type, this function will convert to float (if possible)
131166
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
170205
break;
171206
case MP_BINARY_OP_POWER:
172207
case MP_BINARY_OP_INPLACE_POWER: lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break;
208+
case MP_BINARY_OP_DIVMOD: {
209+
if (rhs_val == 0) {
210+
goto zero_division_error;
211+
}
212+
mp_obj_float_divmod(&lhs_val, &rhs_val);
213+
mp_obj_t tuple[2] = {
214+
mp_obj_new_float(lhs_val),
215+
mp_obj_new_float(rhs_val),
216+
};
217+
return mp_obj_new_tuple(2, tuple);
218+
}
173219
case MP_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val);
174220
case MP_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val);
175221
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
182228
return mp_obj_new_float(lhs_val);
183229
}
184230

185-
void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) {
186-
// logic here follows that of CPython
187-
// https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
188-
// x == (x//y)*y + (x%y)
189-
// divmod(x, y) == (x//y, x%y)
190-
mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y);
191-
mp_float_t div = (*x - mod) / *y;
192-
193-
// Python specs require that mod has same sign as second operand
194-
if (mod == 0.0) {
195-
mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y);
196-
} else {
197-
if ((mod < 0.0) != (*y < 0.0)) {
198-
mod += *y;
199-
div -= 1.0;
200-
}
201-
}
202-
203-
mp_float_t floordiv;
204-
if (div == 0.0) {
205-
// if division is zero, take the correct sign of zero
206-
floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y);
207-
} else {
208-
// Python specs require that x == (x//y)*y + (x%y)
209-
floordiv = MICROPY_FLOAT_C_FUN(floor)(div);
210-
if (div - floordiv > 0.5) {
211-
floordiv += 1.0;
212-
}
213-
}
214-
215-
// return results
216-
*x = floordiv;
217-
*y = mod;
218-
}
219-
220231
#endif // MICROPY_PY_BUILTINS_FLOAT

py/objtype.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ const qstr mp_binary_op_method_name[] = {
400400
/*
401401
MP_BINARY_OP_MODULO,
402402
MP_BINARY_OP_POWER,
403+
MP_BINARY_OP_DIVMOD,
403404
MP_BINARY_OP_INPLACE_OR,
404405
MP_BINARY_OP_INPLACE_XOR,
405406
MP_BINARY_OP_INPLACE_AND,

py/runtime.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,17 @@ mp_obj_t mp_binary_op(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs) {
441441
lhs = mp_obj_new_int_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs));
442442
goto generic_binary_op;
443443

444+
case MP_BINARY_OP_DIVMOD: {
445+
if (rhs_val == 0) {
446+
goto zero_division;
447+
}
448+
// to reduce stack usage we don't pass a temp array of the 2 items
449+
mp_obj_tuple_t *tuple = mp_obj_new_tuple(2, NULL);
450+
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(lhs_val, rhs_val));
451+
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(lhs_val, rhs_val));
452+
return tuple;
453+
}
454+
444455
case MP_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val); break;
445456
case MP_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val); break;
446457
case MP_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break;

py/runtime0.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,30 @@ typedef enum {
7474

7575
MP_BINARY_OP_MODULO,
7676
MP_BINARY_OP_POWER,
77+
MP_BINARY_OP_DIVMOD, // not emitted by the compiler but supported by the runtime
7778
MP_BINARY_OP_INPLACE_OR,
7879
MP_BINARY_OP_INPLACE_XOR,
79-
MP_BINARY_OP_INPLACE_AND,
8080

81+
MP_BINARY_OP_INPLACE_AND,
8182
MP_BINARY_OP_INPLACE_LSHIFT,
8283
MP_BINARY_OP_INPLACE_RSHIFT,
8384
MP_BINARY_OP_INPLACE_ADD,
8485
MP_BINARY_OP_INPLACE_SUBTRACT,
85-
MP_BINARY_OP_INPLACE_MULTIPLY,
8686

87+
MP_BINARY_OP_INPLACE_MULTIPLY,
8788
MP_BINARY_OP_INPLACE_FLOOR_DIVIDE,
8889
MP_BINARY_OP_INPLACE_TRUE_DIVIDE,
8990
MP_BINARY_OP_INPLACE_MODULO,
9091
MP_BINARY_OP_INPLACE_POWER,
92+
9193
// these should return a bool
9294
MP_BINARY_OP_LESS,
93-
9495
MP_BINARY_OP_MORE,
9596
MP_BINARY_OP_EQUAL,
9697
MP_BINARY_OP_LESS_EQUAL,
9798
MP_BINARY_OP_MORE_EQUAL,
98-
MP_BINARY_OP_NOT_EQUAL,
9999

100+
MP_BINARY_OP_NOT_EQUAL,
100101
MP_BINARY_OP_IN,
101102
MP_BINARY_OP_IS,
102103
MP_BINARY_OP_EXCEPTION_MATCH,

py/vm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,7 @@ unwind_jump:;
11991199
} else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 6) {
12001200
SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP()));
12011201
DISPATCH();
1202-
} else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 35) {
1202+
} else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 36) {
12031203
mp_obj_t rhs = POP();
12041204
mp_obj_t lhs = TOP();
12051205
SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs));

py/vmentrytable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ static void* entry_table[256] = {
112112
[MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + 15] = &&entry_MP_BC_LOAD_FAST_MULTI,
113113
[MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + 15] = &&entry_MP_BC_STORE_FAST_MULTI,
114114
[MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + 5] = &&entry_MP_BC_UNARY_OP_MULTI,
115-
[MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 34] = &&entry_MP_BC_BINARY_OP_MULTI,
115+
[MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 35] = &&entry_MP_BC_BINARY_OP_MULTI,
116116
};
117117

118118
#if __clang__

tests/cmdline/cmd_showbc.py.exp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,21 @@ arg names:
9393
69 LOAD_DEREF 14
9494
71 DUP_TOP
9595
72 ROT_THREE
96-
73 BINARY_OP 26 __eq__
96+
73 BINARY_OP 27 __eq__
9797
74 JUMP_IF_FALSE_OR_POP 82
9898
77 LOAD_FAST 1
99-
78 BINARY_OP 26 __eq__
99+
78 BINARY_OP 27 __eq__
100100
79 JUMP 84
101101
82 ROT_TWO
102102
83 POP_TOP
103103
84 STORE_FAST 10
104104
85 LOAD_FAST 0
105105
86 LOAD_DEREF 14
106-
88 BINARY_OP 26 __eq__
106+
88 BINARY_OP 27 __eq__
107107
89 JUMP_IF_FALSE_OR_POP 96
108108
92 LOAD_DEREF 14
109109
94 LOAD_FAST 1
110-
95 BINARY_OP 26 __eq__
110+
95 BINARY_OP 27 __eq__
111111
96 UNARY_OP 0
112112
97 NOT
113113
98 STORE_FAST 10

0 commit comments

Comments
 (0)