Browse Source

py: Specialise builtin funcs to use separate type for fixed arg count.

Builtin functions with a fixed number of arguments (0, 1, 2 or 3) are
quite common.  Before this patch the wrapper for such a function cost
3 machine words.  After this patch it only takes 2, which can reduce the
code size by quite a bit (and pays off even more, the more functions are
added).  It also makes function dispatch slightly more efficient in CPU
usage, and furthermore reduces stack usage for these cases.  On x86 and
Thumb archs the dispatch functions are now tail-call optimised by the
compiler.

The bare-arm port has its code size increase by 76 bytes, but stmhal drops
by 904 bytes.  Stack usage by these builtin functions is decreased by 48
bytes on Thumb2 archs.
pull/2529/head
Damien George 8 years ago
parent
commit
571e6f26db
  1. 2
      drivers/cc3000/src/ccspi.c
  2. 67
      py/obj.h
  3. 86
      py/objfun.c
  4. 8
      py/runtime.c

2
drivers/cc3000/src/ccspi.c

@ -99,7 +99,7 @@ STATIC tSpiInformation sSpiInformation;
STATIC char spi_buffer[CC3000_RX_BUFFER_SIZE];
unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE];
STATIC const mp_obj_fun_builtin_t irq_callback_obj;
STATIC const mp_obj_fun_builtin_fixed_t irq_callback_obj;
// set the pins to use to communicate with the CC3000
// the arguments must be of type pin_obj_t* and SPI_HandleTypeDef*

67
py/obj.h

@ -270,35 +270,35 @@ static inline bool mp_obj_is_integer(mp_const_obj_t o) { return MP_OBJ_IS_INT(o)
// These macros are used to declare and define constant function objects
// You can put "static" in front of the definitions to make them local
#define MP_DECLARE_CONST_FUN_OBJ_0(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_1(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_2(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_3(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_VAR(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_KW(obj_name) extern const mp_obj_fun_builtin_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_0(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_1(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_2(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_3(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_VAR(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
#define MP_DECLARE_CONST_FUN_OBJ_KW(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, false, 0, 0, .fun._0 = fun_name}
const mp_obj_fun_builtin_fixed_t obj_name = \
{{&mp_type_fun_builtin_0}, .fun._0 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, false, 1, 1, .fun._1 = fun_name}
const mp_obj_fun_builtin_fixed_t obj_name = \
{{&mp_type_fun_builtin_1}, .fun._1 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, false, 2, 2, .fun._2 = fun_name}
const mp_obj_fun_builtin_fixed_t obj_name = \
{{&mp_type_fun_builtin_2}, .fun._2 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, false, 3, 3, .fun._3 = fun_name}
const mp_obj_fun_builtin_fixed_t obj_name = \
{{&mp_type_fun_builtin_3}, .fun._3 = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, false, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.var = fun_name}
const mp_obj_fun_builtin_var_t obj_name = \
{{&mp_type_fun_builtin_var}, false, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.var = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, false, n_args_min, n_args_max, .fun.var = fun_name}
const mp_obj_fun_builtin_var_t obj_name = \
{{&mp_type_fun_builtin_var}, false, n_args_min, n_args_max, .fun.var = fun_name}
#define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \
const mp_obj_fun_builtin_t obj_name = \
{{&mp_type_fun_builtin}, true, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.kw = fun_name}
const mp_obj_fun_builtin_var_t obj_name = \
{{&mp_type_fun_builtin_var}, true, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.kw = fun_name}
// These macros are used to define constant map/dict objects
// You can put "static" in front of the definition to make it local
@ -536,7 +536,11 @@ extern const mp_obj_type_t mp_type_zip;
extern const mp_obj_type_t mp_type_array;
extern const mp_obj_type_t mp_type_super;
extern const mp_obj_type_t mp_type_gen_instance;
extern const mp_obj_type_t mp_type_fun_builtin;
extern const mp_obj_type_t mp_type_fun_builtin_0;
extern const mp_obj_type_t mp_type_fun_builtin_1;
extern const mp_obj_type_t mp_type_fun_builtin_2;
extern const mp_obj_type_t mp_type_fun_builtin_3;
extern const mp_obj_type_t mp_type_fun_builtin_var;
extern const mp_obj_type_t mp_type_fun_bc;
extern const mp_obj_type_t mp_type_module;
extern const mp_obj_type_t mp_type_staticmethod;
@ -746,21 +750,28 @@ void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item);
void mp_obj_slice_get(mp_obj_t self_in, mp_obj_t *start, mp_obj_t *stop, mp_obj_t *step);
// functions
#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below
typedef struct _mp_obj_fun_builtin_t { // use this to make const objects that go in ROM
typedef struct _mp_obj_fun_builtin_fixed_t {
mp_obj_base_t base;
bool is_kw : 1;
mp_uint_t n_args_min : 15; // inclusive
mp_uint_t n_args_max : 16; // inclusive
union {
mp_fun_0_t _0;
mp_fun_1_t _1;
mp_fun_2_t _2;
mp_fun_3_t _3;
} fun;
} mp_obj_fun_builtin_fixed_t;
#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below
typedef struct _mp_obj_fun_builtin_var_t {
mp_obj_base_t base;
bool is_kw : 1;
mp_uint_t n_args_min : 15; // inclusive
mp_uint_t n_args_max : 16; // inclusive
union {
mp_fun_var_t var;
mp_fun_kw_t kw;
} fun;
} mp_obj_fun_builtin_t;
} mp_obj_fun_builtin_var_t;
qstr mp_obj_fun_get_name(mp_const_obj_t fun);
qstr mp_obj_code_get_name(const byte *code_info);

86
py/objfun.c

@ -50,11 +50,66 @@
/******************************************************************************/
/* builtin functions */
// mp_obj_fun_builtin_t defined in obj.h
STATIC mp_obj_t fun_builtin_0_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void)args;
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_0));
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_check_num(n_args, n_kw, 0, 0, false);
return self->fun._0();
}
STATIC mp_obj_t fun_builtin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin));
mp_obj_fun_builtin_t *self = MP_OBJ_TO_PTR(self_in);
const mp_obj_type_t mp_type_fun_builtin_0 = {
{ &mp_type_type },
.name = MP_QSTR_function,
.call = fun_builtin_0_call,
.unary_op = mp_generic_unary_op,
};
STATIC mp_obj_t fun_builtin_1_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_1));
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_check_num(n_args, n_kw, 1, 1, false);
return self->fun._1(args[0]);
}
const mp_obj_type_t mp_type_fun_builtin_1 = {
{ &mp_type_type },
.name = MP_QSTR_function,
.call = fun_builtin_1_call,
.unary_op = mp_generic_unary_op,
};
STATIC mp_obj_t fun_builtin_2_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_2));
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_check_num(n_args, n_kw, 2, 2, false);
return self->fun._2(args[0], args[1]);
}
const mp_obj_type_t mp_type_fun_builtin_2 = {
{ &mp_type_type },
.name = MP_QSTR_function,
.call = fun_builtin_2_call,
.unary_op = mp_generic_unary_op,
};
STATIC mp_obj_t fun_builtin_3_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_3));
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_check_num(n_args, n_kw, 3, 3, false);
return self->fun._3(args[0], args[1], args[2]);
}
const mp_obj_type_t mp_type_fun_builtin_3 = {
{ &mp_type_type },
.name = MP_QSTR_function,
.call = fun_builtin_3_call,
.unary_op = mp_generic_unary_op,
};
STATIC mp_obj_t fun_builtin_var_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_var));
mp_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in);
// check number of arguments
mp_arg_check_num(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw);
@ -68,25 +123,6 @@ STATIC mp_obj_t fun_builtin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c
return self->fun.kw(n_args, args, &kw_args);
} else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
// function requires a fixed number of arguments
// dispatch function call
switch (self->n_args_min) {
case 0:
return self->fun._0();
case 1:
return self->fun._1(args[0]);
case 2:
return self->fun._2(args[0], args[1]);
case 3:
default:
return self->fun._3(args[0], args[1], args[2]);
}
} else {
// function takes a variable number of arguments, but no keywords
@ -94,10 +130,10 @@ STATIC mp_obj_t fun_builtin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c
}
}
const mp_obj_type_t mp_type_fun_builtin = {
const mp_obj_type_t mp_type_fun_builtin_var = {
{ &mp_type_type },
.name = MP_QSTR_function,
.call = fun_builtin_call,
.call = fun_builtin_var_call,
.unary_op = mp_generic_unary_op,
};

8
py/runtime.c

@ -972,7 +972,13 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t
|| ((mp_obj_base_t*)MP_OBJ_TO_PTR(member))->type->name == MP_QSTR_generator))) {
// only functions, closures and generators objects can be bound to self
#if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
if (self == MP_OBJ_NULL && mp_obj_get_type(member) == &mp_type_fun_builtin) {
const mp_obj_type_t *m_type = ((mp_obj_base_t*)MP_OBJ_TO_PTR(member))->type;
if (self == MP_OBJ_NULL
&& (m_type == &mp_type_fun_builtin_0
|| m_type == &mp_type_fun_builtin_1
|| m_type == &mp_type_fun_builtin_2
|| m_type == &mp_type_fun_builtin_3
|| m_type == &mp_type_fun_builtin_var)) {
// we extracted a builtin method without a first argument, so we must
// wrap this function in a type checker
dest[0] = mp_obj_new_checked_fun(type, member);

Loading…
Cancel
Save