Skip to content

Commit 9dce823

Browse files
committed
py/modbuiltins: Implement abs() by dispatching to MP_UNARY_OP_ABS.
This allows user classes to implement __abs__ special method, and saves code size (104 bytes for x86_64), even though during refactor, an issue was fixed and few optimizations were made: * abs() of minimum (negative) small int value is calculated properly. * objint_longlong and objint_mpz avoid allocating new object is the argument is already non-negative.
1 parent 72491b3 commit 9dce823

10 files changed

Lines changed: 45 additions & 77 deletions

File tree

py/modbuiltins.c

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,7 @@ STATIC mp_obj_t mp_builtin___build_class__(size_t n_args, const mp_obj_t *args)
9191
MP_DEFINE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj, 2, mp_builtin___build_class__);
9292

9393
STATIC mp_obj_t mp_builtin_abs(mp_obj_t o_in) {
94-
#if MICROPY_PY_BUILTINS_FLOAT
95-
if (mp_obj_is_float(o_in)) {
96-
mp_float_t value = mp_obj_float_get(o_in);
97-
// TODO check for NaN etc
98-
if (value < 0) {
99-
return mp_obj_new_float(-value);
100-
} else {
101-
return o_in;
102-
}
103-
#if MICROPY_PY_BUILTINS_COMPLEX
104-
} else if (MP_OBJ_IS_TYPE(o_in, &mp_type_complex)) {
105-
mp_float_t real, imag;
106-
mp_obj_complex_get(o_in, &real, &imag);
107-
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(real*real + imag*imag));
108-
#endif
109-
}
110-
#endif
111-
112-
// this will raise a TypeError if the argument is not integral
113-
return mp_obj_int_abs(o_in);
94+
return mp_unary_op(MP_UNARY_OP_ABS, o_in);
11495
}
11596
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_abs_obj, mp_builtin_abs);
11697

py/objcomplex.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ STATIC mp_obj_t complex_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
124124
case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT(mp_float_hash(o->real) ^ mp_float_hash(o->imag));
125125
case MP_UNARY_OP_POSITIVE: return o_in;
126126
case MP_UNARY_OP_NEGATIVE: return mp_obj_new_complex(-o->real, -o->imag);
127+
case MP_UNARY_OP_ABS: {
128+
mp_float_t real, imag;
129+
mp_obj_complex_get(o_in, &real, &imag);
130+
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(real*real + imag*imag));
131+
}
127132
default: return MP_OBJ_NULL; // op not supported
128133
}
129134
}

py/objfloat.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ STATIC mp_obj_t float_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
162162
case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT(mp_float_hash(val));
163163
case MP_UNARY_OP_POSITIVE: return o_in;
164164
case MP_UNARY_OP_NEGATIVE: return mp_obj_new_float(-val);
165+
case MP_UNARY_OP_ABS: {
166+
mp_float_t value = mp_obj_float_get(o_in);
167+
// TODO check for NaN etc
168+
if (value < 0) {
169+
return mp_obj_new_float(-value);
170+
} else {
171+
return o_in;
172+
}
173+
}
165174
default: return MP_OBJ_NULL; // op not supported
166175
}
167176
}

py/objint.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -314,16 +314,6 @@ int mp_obj_int_sign(mp_obj_t self_in) {
314314
}
315315
}
316316

317-
// This must handle int and bool types, and must raise a
318-
// TypeError if the argument is not integral
319-
mp_obj_t mp_obj_int_abs(mp_obj_t self_in) {
320-
mp_int_t val = mp_obj_get_int(self_in);
321-
if (val < 0) {
322-
val = -val;
323-
}
324-
return MP_OBJ_NEW_SMALL_INT(val);
325-
}
326-
327317
// This is called for operations on SMALL_INT that are not handled by mp_unary_op
328318
mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
329319
return MP_OBJ_NULL; // op not supported

py/objint.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ mp_int_t mp_obj_int_hash(mp_obj_t self_in);
5757
mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf);
5858
void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf);
5959
int mp_obj_int_sign(mp_obj_t self_in);
60-
mp_obj_t mp_obj_int_abs(mp_obj_t self_in);
6160
mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in);
6261
mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);
6362
mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in);

py/objint_longlong.c

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -94,30 +94,6 @@ int mp_obj_int_sign(mp_obj_t self_in) {
9494
}
9595
}
9696

97-
// This must handle int and bool types, and must raise a
98-
// TypeError if the argument is not integral
99-
mp_obj_t mp_obj_int_abs(mp_obj_t self_in) {
100-
if (MP_OBJ_IS_TYPE(self_in, &mp_type_int)) {
101-
mp_obj_int_t *self = self_in;
102-
self = mp_obj_new_int_from_ll(self->val);
103-
if (self->val < 0) {
104-
// TODO could overflow long long
105-
self->val = -self->val;
106-
}
107-
return self;
108-
} else {
109-
mp_int_t val = mp_obj_get_int(self_in);
110-
if (val == MP_SMALL_INT_MIN) {
111-
return mp_obj_new_int_from_ll(-val);
112-
} else {
113-
if (val < 0) {
114-
val = -val;
115-
}
116-
return MP_OBJ_NEW_SMALL_INT(val);
117-
}
118-
}
119-
}
120-
12197
mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
12298
mp_obj_int_t *o = o_in;
12399
switch (op) {
@@ -130,6 +106,16 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
130106
case MP_UNARY_OP_POSITIVE: return o_in;
131107
case MP_UNARY_OP_NEGATIVE: return mp_obj_new_int_from_ll(-o->val);
132108
case MP_UNARY_OP_INVERT: return mp_obj_new_int_from_ll(~o->val);
109+
case MP_UNARY_OP_ABS: {
110+
mp_obj_int_t *self = MP_OBJ_TO_PTR(o_in);
111+
if (self->val >= 0) {
112+
return o_in;
113+
}
114+
self = mp_obj_new_int_from_ll(self->val);
115+
// TODO could overflow long long
116+
self->val = -self->val;
117+
return MP_OBJ_FROM_PTR(self);
118+
}
133119
default: return MP_OBJ_NULL; // op not supported
134120
}
135121
}

py/objint_mpz.c

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -141,27 +141,6 @@ int mp_obj_int_sign(mp_obj_t self_in) {
141141
}
142142
}
143143

144-
// This must handle int and bool types, and must raise a
145-
// TypeError if the argument is not integral
146-
mp_obj_t mp_obj_int_abs(mp_obj_t self_in) {
147-
if (MP_OBJ_IS_TYPE(self_in, &mp_type_int)) {
148-
mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in);
149-
mp_obj_int_t *self2 = mp_obj_int_new_mpz();
150-
mpz_abs_inpl(&self2->mpz, &self->mpz);
151-
return MP_OBJ_FROM_PTR(self2);
152-
} else {
153-
mp_int_t val = mp_obj_get_int(self_in);
154-
if (val == MP_SMALL_INT_MIN) {
155-
return mp_obj_new_int_from_ll(-val);
156-
} else {
157-
if (val < 0) {
158-
val = -val;
159-
}
160-
return MP_OBJ_NEW_SMALL_INT(val);
161-
}
162-
}
163-
}
164-
165144
mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
166145
mp_obj_int_t *o = MP_OBJ_TO_PTR(o_in);
167146
switch (op) {
@@ -170,6 +149,15 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
170149
case MP_UNARY_OP_POSITIVE: return o_in;
171150
case MP_UNARY_OP_NEGATIVE: { mp_obj_int_t *o2 = mp_obj_int_new_mpz(); mpz_neg_inpl(&o2->mpz, &o->mpz); return MP_OBJ_FROM_PTR(o2); }
172151
case MP_UNARY_OP_INVERT: { mp_obj_int_t *o2 = mp_obj_int_new_mpz(); mpz_not_inpl(&o2->mpz, &o->mpz); return MP_OBJ_FROM_PTR(o2); }
152+
case MP_UNARY_OP_ABS: {
153+
mp_obj_int_t *self = MP_OBJ_TO_PTR(o_in);
154+
if (self->mpz.neg == 0) {
155+
return o_in;
156+
}
157+
mp_obj_int_t *self2 = mp_obj_int_new_mpz();
158+
mpz_abs_inpl(&self2->mpz, &self->mpz);
159+
return MP_OBJ_FROM_PTR(self2);
160+
}
173161
default: return MP_OBJ_NULL; // op not supported
174162
}
175163
}

py/runtime.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) {
231231
} else {
232232
return MP_OBJ_NEW_SMALL_INT(-val);
233233
}
234+
case MP_UNARY_OP_ABS:
235+
if (val >= 0) {
236+
return arg;
237+
} else if (val == MP_SMALL_INT_MIN) {
238+
// check for overflow
239+
return mp_obj_new_int(-val);
240+
} else {
241+
return MP_OBJ_NEW_SMALL_INT(-val);
242+
}
234243
default:
235244
assert(op == MP_UNARY_OP_INVERT);
236245
return MP_OBJ_NEW_SMALL_INT(~val);

py/runtime0.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ typedef enum {
5050
MP_UNARY_OP_NEGATIVE,
5151
MP_UNARY_OP_INVERT,
5252
MP_UNARY_OP_NOT,
53+
MP_UNARY_OP_ABS, // __abs__
5354
MP_UNARY_OP_SIZEOF, // for sys.getsizeof()
5455
} mp_unary_op_t;
5556

tests/unix/extra_coverage.py.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ ementation
3838
0
3939
0
4040
# runtime utils
41-
TypeError: can't convert str to int
41+
TypeError: unsupported type for : 'str'
4242
TypeError: unsupported types for : 'str', 'str'
4343
Warning: test
4444
# format float

0 commit comments

Comments
 (0)