Commit 55b74d1f authored by Damien George's avatar Damien George
Browse files

py: Combine duplicated code that converts members from a lookup.

Despite initial guess, this code factoring does not hamper performance.
In fact it seems to improve speed by a little: running pystone(1.2) on
pyboard (which gives a very stable result) this patch takes pystones
from 1729.51 up to 1742.16.  Also, pystones on x64 increase by around
the same proportion (but it's much noisier).

Taking a look at the generated machine code, stack usage with this patch
is unchanged, and call is tail-optimised with all arguments in
registers.  Code size decreases by about 50 bytes on Thumb2 archs.
parent 59f68313
...@@ -51,7 +51,6 @@ STATIC mp_obj_t static_class_method_make_new(mp_obj_t self_in, mp_uint_t n_args, ...@@ -51,7 +51,6 @@ STATIC mp_obj_t static_class_method_make_new(mp_obj_t self_in, mp_uint_t n_args,
#define is_instance_type(type) ((type)->make_new == instance_make_new) #define is_instance_type(type) ((type)->make_new == instance_make_new)
#define is_native_type(type) ((type)->make_new != instance_make_new) #define is_native_type(type) ((type)->make_new != instance_make_new)
mp_obj_t instance_make_new(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args); mp_obj_t instance_make_new(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
STATIC void instance_convert_return_attr(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest);
STATIC mp_obj_t mp_obj_new_instance(mp_obj_t class, uint subobjs) { STATIC mp_obj_t mp_obj_new_instance(mp_obj_t class, uint subobjs) {
mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs); mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs);
...@@ -129,19 +128,18 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_ ...@@ -129,19 +128,18 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_
mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict); mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict);
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(lookup->attr), MP_MAP_LOOKUP); mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(lookup->attr), MP_MAP_LOOKUP);
if (elem != NULL) { if (elem != NULL) {
lookup->dest[0] = elem->value;
if (lookup->is_type) { if (lookup->is_type) {
// If we look up a class method, we need to return original type for which we // If we look up a class method, we need to return original type for which we
// do a lookup, not a (base) type in which we found the class method. // do a lookup, not a (base) type in which we found the class method.
const mp_obj_type_t *org_type = (const mp_obj_type_t*)lookup->obj; const mp_obj_type_t *org_type = (const mp_obj_type_t*)lookup->obj;
instance_convert_return_attr(NULL, org_type, elem->value, lookup->dest); mp_convert_member_lookup(NULL, org_type, elem->value, lookup->dest);
} else { } else {
mp_obj_instance_t *obj = lookup->obj; mp_obj_instance_t *obj = lookup->obj;
if (obj != MP_OBJ_NULL && is_native_type(type) && type != &mp_type_object /* object is not a real type */) { if (obj != MP_OBJ_NULL && is_native_type(type) && type != &mp_type_object /* object is not a real type */) {
// If we're dealing with native base class, then it applies to native sub-object // If we're dealing with native base class, then it applies to native sub-object
obj = obj->subobj[0]; obj = obj->subobj[0];
} }
instance_convert_return_attr(obj, type, elem->value, lookup->dest); mp_convert_member_lookup(obj, type, elem->value, lookup->dest);
} }
#if DEBUG_PRINT #if DEBUG_PRINT
printf("mp_obj_class_lookup: Returning: "); printf("mp_obj_class_lookup: Returning: ");
...@@ -411,32 +409,6 @@ const qstr mp_binary_op_method_name[] = { ...@@ -411,32 +409,6 @@ const qstr mp_binary_op_method_name[] = {
[MP_BINARY_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size [MP_BINARY_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size
}; };
// Given a member that was extracted from an instance, convert it correctly
// and put the result in the dest[] array for a possible method call.
// Conversion means dealing with static/class methods, callables, and values.
// see http://docs.python.org/3/howto/descriptor.html
STATIC void instance_convert_return_attr(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest) {
assert(dest[1] == NULL);
if (MP_OBJ_IS_TYPE(member, &mp_type_staticmethod)) {
// return just the function
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
} else if (MP_OBJ_IS_TYPE(member, &mp_type_classmethod)) {
// return a bound method, with self being the type of this object
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
dest[1] = (mp_obj_t)type;
} else if (MP_OBJ_IS_TYPE(member, &mp_type_type)) {
// Don't try to bind types (even though they're callable)
dest[0] = member;
} else if (mp_obj_is_callable(member)) {
// return a bound method, with self being this object
dest[0] = member;
dest[1] = self;
} else {
// class member is a value, so just return that value
dest[0] = member;
}
}
STATIC mp_obj_t instance_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { STATIC mp_obj_t instance_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
// Note: For ducktyping, CPython does not look in the instance members or use // Note: For ducktyping, CPython does not look in the instance members or use
// __getattr__ or __getattribute__. It only looks in the class dictionary. // __getattr__ or __getattribute__. It only looks in the class dictionary.
...@@ -493,13 +465,13 @@ void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { ...@@ -493,13 +465,13 @@ void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
if (MP_OBJ_IS_TYPE(member, &mp_type_property)) { if (MP_OBJ_IS_TYPE(member, &mp_type_property)) {
// object member is a property // object member is a property
// delegate the store to the property // delegate the store to the property
// TODO should this be part of instance_convert_return_attr? // TODO should this be part of mp_convert_member_lookup?
const mp_obj_t *proxy = mp_obj_property_get(member); const mp_obj_t *proxy = mp_obj_property_get(member);
if (proxy[0] == mp_const_none) { if (proxy[0] == mp_const_none) {
// TODO // TODO
} else { } else {
dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self_in); dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self_in);
// TODO should we convert the returned value using instance_convert_return_attr? // TODO should we convert the returned value using mp_convert_member_lookup?
} }
} }
#endif #endif
...@@ -591,7 +563,7 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value ...@@ -591,7 +563,7 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value
return mp_obj_subscr(self->subobj[0], index, value); return mp_obj_subscr(self->subobj[0], index, value);
} else if (member[0] != MP_OBJ_NULL) { } else if (member[0] != MP_OBJ_NULL) {
mp_obj_t args[3] = {self_in, index, value}; mp_obj_t args[3] = {self_in, index, value};
// TODO probably need to call instance_convert_return_attr, and use mp_call_method_n_kw // TODO probably need to call mp_convert_member_lookup, and use mp_call_method_n_kw
mp_obj_t ret = mp_call_function_n_kw(member[0], meth_args, 0, args); mp_obj_t ret = mp_call_function_n_kw(member[0], meth_args, 0, args);
if (value == MP_OBJ_SENTINEL) { if (value == MP_OBJ_SENTINEL) {
return ret; return ret;
......
...@@ -854,6 +854,31 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) { ...@@ -854,6 +854,31 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) {
} }
} }
// Given a member that was extracted from an instance, convert it correctly
// and put the result in the dest[] array for a possible method call.
// Conversion means dealing with static/class methods, callables, and values.
// see http://docs.python.org/3/howto/descriptor.html
void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest) {
if (MP_OBJ_IS_TYPE(member, &mp_type_staticmethod)) {
// return just the function
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
} else if (MP_OBJ_IS_TYPE(member, &mp_type_classmethod)) {
// return a bound method, with self being the type of this object
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
dest[1] = (mp_obj_t)type;
} else if (MP_OBJ_IS_TYPE(member, &mp_type_type)) {
// Don't try to bind types (even though they're callable)
dest[0] = member;
} else if (mp_obj_is_callable(member)) {
// return a bound method, with self being this object
dest[0] = member;
dest[1] = self;
} else {
// class member is a value, so just return that value
dest[0] = member;
}
}
// no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL // no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL
// normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL // normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL
// method attribute found, returns: dest[0] == <method>, dest[1] == <self> // method attribute found, returns: dest[0] == <method>, dest[1] == <self>
...@@ -888,26 +913,7 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) { ...@@ -888,26 +913,7 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict); mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict);
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
if (elem != NULL) { if (elem != NULL) {
// check if the methods are functions, static or class methods mp_convert_member_lookup(obj, type, elem->value, dest);
// see http://docs.python.org/3/howto/descriptor.html
if (MP_OBJ_IS_TYPE(elem->value, &mp_type_staticmethod)) {
// return just the function
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
} else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_classmethod)) {
// return a bound method, with self being the type of this object
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
dest[1] = type;
} else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_type)) {
// Don't try to bind types
dest[0] = elem->value;
} else if (mp_obj_is_callable(elem->value)) {
// return a bound method, with self being this object
dest[0] = elem->value;
dest[1] = obj;
} else {
// class member is a value, so just return that value
dest[0] = elem->value;
}
} }
} }
} }
......
...@@ -101,6 +101,7 @@ void mp_unpack_sequence(mp_obj_t seq, mp_uint_t num, mp_obj_t *items); ...@@ -101,6 +101,7 @@ void mp_unpack_sequence(mp_obj_t seq, mp_uint_t num, mp_obj_t *items);
void mp_unpack_ex(mp_obj_t seq, mp_uint_t num, mp_obj_t *items); void mp_unpack_ex(mp_obj_t seq, mp_uint_t num, mp_obj_t *items);
mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value); mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value);
mp_obj_t mp_load_attr(mp_obj_t base, qstr attr); mp_obj_t mp_load_attr(mp_obj_t base, qstr attr);
void mp_convert_member_lookup(mp_obj_t obj, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest);
void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest); void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest); void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest);
void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val); void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
......
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