Skip to content

Commit 571e6f2

Browse files
committed
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.
1 parent 4ebdb1f commit 571e6f2

4 files changed

Lines changed: 108 additions & 55 deletions

File tree

drivers/cc3000/src/ccspi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ STATIC tSpiInformation sSpiInformation;
9999
STATIC char spi_buffer[CC3000_RX_BUFFER_SIZE];
100100
unsigned char wlan_tx_buffer[CC3000_TX_BUFFER_SIZE];
101101

102-
STATIC const mp_obj_fun_builtin_t irq_callback_obj;
102+
STATIC const mp_obj_fun_builtin_fixed_t irq_callback_obj;
103103

104104
// set the pins to use to communicate with the CC3000
105105
// the arguments must be of type pin_obj_t* and SPI_HandleTypeDef*

py/obj.h

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -270,35 +270,35 @@ static inline bool mp_obj_is_integer(mp_const_obj_t o) { return MP_OBJ_IS_INT(o)
270270
// These macros are used to declare and define constant function objects
271271
// You can put "static" in front of the definitions to make them local
272272

273-
#define MP_DECLARE_CONST_FUN_OBJ_0(obj_name) extern const mp_obj_fun_builtin_t obj_name
274-
#define MP_DECLARE_CONST_FUN_OBJ_1(obj_name) extern const mp_obj_fun_builtin_t obj_name
275-
#define MP_DECLARE_CONST_FUN_OBJ_2(obj_name) extern const mp_obj_fun_builtin_t obj_name
276-
#define MP_DECLARE_CONST_FUN_OBJ_3(obj_name) extern const mp_obj_fun_builtin_t obj_name
277-
#define MP_DECLARE_CONST_FUN_OBJ_VAR(obj_name) extern const mp_obj_fun_builtin_t obj_name
278-
#define MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name) extern const mp_obj_fun_builtin_t obj_name
279-
#define MP_DECLARE_CONST_FUN_OBJ_KW(obj_name) extern const mp_obj_fun_builtin_t obj_name
273+
#define MP_DECLARE_CONST_FUN_OBJ_0(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
274+
#define MP_DECLARE_CONST_FUN_OBJ_1(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
275+
#define MP_DECLARE_CONST_FUN_OBJ_2(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
276+
#define MP_DECLARE_CONST_FUN_OBJ_3(obj_name) extern const mp_obj_fun_builtin_fixed_t obj_name
277+
#define MP_DECLARE_CONST_FUN_OBJ_VAR(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
278+
#define MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
279+
#define MP_DECLARE_CONST_FUN_OBJ_KW(obj_name) extern const mp_obj_fun_builtin_var_t obj_name
280280

281281
#define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \
282-
const mp_obj_fun_builtin_t obj_name = \
283-
{{&mp_type_fun_builtin}, false, 0, 0, .fun._0 = fun_name}
282+
const mp_obj_fun_builtin_fixed_t obj_name = \
283+
{{&mp_type_fun_builtin_0}, .fun._0 = fun_name}
284284
#define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) \
285-
const mp_obj_fun_builtin_t obj_name = \
286-
{{&mp_type_fun_builtin}, false, 1, 1, .fun._1 = fun_name}
285+
const mp_obj_fun_builtin_fixed_t obj_name = \
286+
{{&mp_type_fun_builtin_1}, .fun._1 = fun_name}
287287
#define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) \
288-
const mp_obj_fun_builtin_t obj_name = \
289-
{{&mp_type_fun_builtin}, false, 2, 2, .fun._2 = fun_name}
288+
const mp_obj_fun_builtin_fixed_t obj_name = \
289+
{{&mp_type_fun_builtin_2}, .fun._2 = fun_name}
290290
#define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) \
291-
const mp_obj_fun_builtin_t obj_name = \
292-
{{&mp_type_fun_builtin}, false, 3, 3, .fun._3 = fun_name}
291+
const mp_obj_fun_builtin_fixed_t obj_name = \
292+
{{&mp_type_fun_builtin_3}, .fun._3 = fun_name}
293293
#define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) \
294-
const mp_obj_fun_builtin_t obj_name = \
295-
{{&mp_type_fun_builtin}, false, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.var = fun_name}
294+
const mp_obj_fun_builtin_var_t obj_name = \
295+
{{&mp_type_fun_builtin_var}, false, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.var = fun_name}
296296
#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) \
297-
const mp_obj_fun_builtin_t obj_name = \
298-
{{&mp_type_fun_builtin}, false, n_args_min, n_args_max, .fun.var = fun_name}
297+
const mp_obj_fun_builtin_var_t obj_name = \
298+
{{&mp_type_fun_builtin_var}, false, n_args_min, n_args_max, .fun.var = fun_name}
299299
#define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \
300-
const mp_obj_fun_builtin_t obj_name = \
301-
{{&mp_type_fun_builtin}, true, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.kw = fun_name}
300+
const mp_obj_fun_builtin_var_t obj_name = \
301+
{{&mp_type_fun_builtin_var}, true, n_args_min, MP_OBJ_FUN_ARGS_MAX, .fun.kw = fun_name}
302302

303303
// These macros are used to define constant map/dict objects
304304
// 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;
536536
extern const mp_obj_type_t mp_type_array;
537537
extern const mp_obj_type_t mp_type_super;
538538
extern const mp_obj_type_t mp_type_gen_instance;
539-
extern const mp_obj_type_t mp_type_fun_builtin;
539+
extern const mp_obj_type_t mp_type_fun_builtin_0;
540+
extern const mp_obj_type_t mp_type_fun_builtin_1;
541+
extern const mp_obj_type_t mp_type_fun_builtin_2;
542+
extern const mp_obj_type_t mp_type_fun_builtin_3;
543+
extern const mp_obj_type_t mp_type_fun_builtin_var;
540544
extern const mp_obj_type_t mp_type_fun_bc;
541545
extern const mp_obj_type_t mp_type_module;
542546
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);
746750
void mp_obj_slice_get(mp_obj_t self_in, mp_obj_t *start, mp_obj_t *stop, mp_obj_t *step);
747751

748752
// functions
749-
#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below
750-
typedef struct _mp_obj_fun_builtin_t { // use this to make const objects that go in ROM
753+
754+
typedef struct _mp_obj_fun_builtin_fixed_t {
751755
mp_obj_base_t base;
752-
bool is_kw : 1;
753-
mp_uint_t n_args_min : 15; // inclusive
754-
mp_uint_t n_args_max : 16; // inclusive
755756
union {
756757
mp_fun_0_t _0;
757758
mp_fun_1_t _1;
758759
mp_fun_2_t _2;
759760
mp_fun_3_t _3;
761+
} fun;
762+
} mp_obj_fun_builtin_fixed_t;
763+
764+
#define MP_OBJ_FUN_ARGS_MAX (0xffff) // to set maximum value in n_args_max below
765+
typedef struct _mp_obj_fun_builtin_var_t {
766+
mp_obj_base_t base;
767+
bool is_kw : 1;
768+
mp_uint_t n_args_min : 15; // inclusive
769+
mp_uint_t n_args_max : 16; // inclusive
770+
union {
760771
mp_fun_var_t var;
761772
mp_fun_kw_t kw;
762773
} fun;
763-
} mp_obj_fun_builtin_t;
774+
} mp_obj_fun_builtin_var_t;
764775

765776
qstr mp_obj_fun_get_name(mp_const_obj_t fun);
766777
qstr mp_obj_code_get_name(const byte *code_info);

py/objfun.c

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,66 @@
5050
/******************************************************************************/
5151
/* builtin functions */
5252

53-
// mp_obj_fun_builtin_t defined in obj.h
53+
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) {
54+
(void)args;
55+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_0));
56+
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
57+
mp_arg_check_num(n_args, n_kw, 0, 0, false);
58+
return self->fun._0();
59+
}
5460

55-
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) {
56-
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin));
57-
mp_obj_fun_builtin_t *self = MP_OBJ_TO_PTR(self_in);
61+
const mp_obj_type_t mp_type_fun_builtin_0 = {
62+
{ &mp_type_type },
63+
.name = MP_QSTR_function,
64+
.call = fun_builtin_0_call,
65+
.unary_op = mp_generic_unary_op,
66+
};
67+
68+
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) {
69+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_1));
70+
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
71+
mp_arg_check_num(n_args, n_kw, 1, 1, false);
72+
return self->fun._1(args[0]);
73+
}
74+
75+
const mp_obj_type_t mp_type_fun_builtin_1 = {
76+
{ &mp_type_type },
77+
.name = MP_QSTR_function,
78+
.call = fun_builtin_1_call,
79+
.unary_op = mp_generic_unary_op,
80+
};
81+
82+
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) {
83+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_2));
84+
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
85+
mp_arg_check_num(n_args, n_kw, 2, 2, false);
86+
return self->fun._2(args[0], args[1]);
87+
}
88+
89+
const mp_obj_type_t mp_type_fun_builtin_2 = {
90+
{ &mp_type_type },
91+
.name = MP_QSTR_function,
92+
.call = fun_builtin_2_call,
93+
.unary_op = mp_generic_unary_op,
94+
};
95+
96+
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) {
97+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_3));
98+
mp_obj_fun_builtin_fixed_t *self = MP_OBJ_TO_PTR(self_in);
99+
mp_arg_check_num(n_args, n_kw, 3, 3, false);
100+
return self->fun._3(args[0], args[1], args[2]);
101+
}
102+
103+
const mp_obj_type_t mp_type_fun_builtin_3 = {
104+
{ &mp_type_type },
105+
.name = MP_QSTR_function,
106+
.call = fun_builtin_3_call,
107+
.unary_op = mp_generic_unary_op,
108+
};
109+
110+
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) {
111+
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_builtin_var));
112+
mp_obj_fun_builtin_var_t *self = MP_OBJ_TO_PTR(self_in);
58113

59114
// check number of arguments
60115
mp_arg_check_num(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw);
@@ -68,36 +123,17 @@ STATIC mp_obj_t fun_builtin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c
68123

69124
return self->fun.kw(n_args, args, &kw_args);
70125

71-
} else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) {
72-
// function requires a fixed number of arguments
73-
74-
// dispatch function call
75-
switch (self->n_args_min) {
76-
case 0:
77-
return self->fun._0();
78-
79-
case 1:
80-
return self->fun._1(args[0]);
81-
82-
case 2:
83-
return self->fun._2(args[0], args[1]);
84-
85-
case 3:
86-
default:
87-
return self->fun._3(args[0], args[1], args[2]);
88-
}
89-
90126
} else {
91127
// function takes a variable number of arguments, but no keywords
92128

93129
return self->fun.var(n_args, args);
94130
}
95131
}
96132

97-
const mp_obj_type_t mp_type_fun_builtin = {
133+
const mp_obj_type_t mp_type_fun_builtin_var = {
98134
{ &mp_type_type },
99135
.name = MP_QSTR_function,
100-
.call = fun_builtin_call,
136+
.call = fun_builtin_var_call,
101137
.unary_op = mp_generic_unary_op,
102138
};
103139

py/runtime.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,13 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t
972972
|| ((mp_obj_base_t*)MP_OBJ_TO_PTR(member))->type->name == MP_QSTR_generator))) {
973973
// only functions, closures and generators objects can be bound to self
974974
#if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
975-
if (self == MP_OBJ_NULL && mp_obj_get_type(member) == &mp_type_fun_builtin) {
975+
const mp_obj_type_t *m_type = ((mp_obj_base_t*)MP_OBJ_TO_PTR(member))->type;
976+
if (self == MP_OBJ_NULL
977+
&& (m_type == &mp_type_fun_builtin_0
978+
|| m_type == &mp_type_fun_builtin_1
979+
|| m_type == &mp_type_fun_builtin_2
980+
|| m_type == &mp_type_fun_builtin_3
981+
|| m_type == &mp_type_fun_builtin_var)) {
976982
// we extracted a builtin method without a first argument, so we must
977983
// wrap this function in a type checker
978984
dest[0] = mp_obj_new_checked_fun(type, member);

0 commit comments

Comments
 (0)