mirror of
https://github.com/micropython/micropython.git
synced 2026-01-05 03:30:14 +01:00
py: Use variable length encoded uints in more places in bytecode.
Code-info size, block name, source name, n_state and n_exc_stack now use variable length encoded uints. This saves 7-9 bytes per bytecode function for most functions.
This commit is contained in:
186
py/objfun.c
186
py/objfun.c
@@ -143,8 +143,8 @@ mp_obj_t mp_make_function_var_between(int n_args_min, int n_args_max, mp_fun_var
|
||||
/* byte code functions */
|
||||
|
||||
const char *mp_obj_code_get_name(const byte *code_info) {
|
||||
qstr block_name = code_info[8] | (code_info[9] << 8) | (code_info[10] << 16) | (code_info[11] << 24);
|
||||
return qstr_str(block_name);
|
||||
mp_decode_uint(&code_info); // skip code_info_size entry
|
||||
return qstr_str(mp_decode_uint(&code_info));
|
||||
}
|
||||
|
||||
const char *mp_obj_fun_get_name(mp_const_obj_t fun_in) {
|
||||
@@ -172,21 +172,6 @@ STATIC void dump_args(const mp_obj_t *a, int sz) {
|
||||
#define dump_args(...) (void)0
|
||||
#endif
|
||||
|
||||
STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, mp_uint_t expected, mp_uint_t given) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
// Generic message, to be reused for other argument issues
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
|
||||
"argument num/types mismatch"));
|
||||
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
|
||||
"function takes %d positional arguments but %d were given", expected, given));
|
||||
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
|
||||
"%s() takes %d positional arguments but %d were given",
|
||||
mp_obj_fun_get_name(f), expected, given));
|
||||
#endif
|
||||
}
|
||||
|
||||
// With this macro you can tune the maximum number of function state bytes
|
||||
// that will be allocated on the stack. Any function that needs more
|
||||
// than this will use the heap.
|
||||
@@ -195,159 +180,6 @@ STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, mp_uint_t expecte
|
||||
// Set this to enable a simple stack overflow check.
|
||||
#define VM_DETECT_STACK_OVERFLOW (0)
|
||||
|
||||
// code_state should have ->ip filled in (pointing past code info block),
|
||||
// as well as ->n_state.
|
||||
void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
|
||||
// This function is pretty complicated. It's main aim is to be efficient in speed and RAM
|
||||
// usage for the common case of positional only args.
|
||||
mp_obj_fun_bc_t *self = self_in;
|
||||
mp_uint_t n_state = code_state->n_state;
|
||||
const byte *ip = code_state->ip;
|
||||
|
||||
code_state->code_info = self->bytecode;
|
||||
code_state->sp = &code_state->state[0] - 1;
|
||||
code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
|
||||
|
||||
// zero out the local stack to begin with
|
||||
memset(code_state->state, 0, n_state * sizeof(*code_state->state));
|
||||
|
||||
const mp_obj_t *kwargs = args + n_args;
|
||||
|
||||
// var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
|
||||
mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - self->n_pos_args - self->n_kwonly_args];
|
||||
|
||||
// check positional arguments
|
||||
|
||||
if (n_args > self->n_pos_args) {
|
||||
// given more than enough arguments
|
||||
if (!self->takes_var_args) {
|
||||
fun_pos_args_mismatch(self, self->n_pos_args, n_args);
|
||||
}
|
||||
// put extra arguments in varargs tuple
|
||||
*var_pos_kw_args-- = mp_obj_new_tuple(n_args - self->n_pos_args, args + self->n_pos_args);
|
||||
n_args = self->n_pos_args;
|
||||
} else {
|
||||
if (self->takes_var_args) {
|
||||
DEBUG_printf("passing empty tuple as *args\n");
|
||||
*var_pos_kw_args-- = mp_const_empty_tuple;
|
||||
}
|
||||
// Apply processing and check below only if we don't have kwargs,
|
||||
// otherwise, kw handling code below has own extensive checks.
|
||||
if (n_kw == 0 && !self->has_def_kw_args) {
|
||||
if (n_args >= self->n_pos_args - self->n_def_args) {
|
||||
// given enough arguments, but may need to use some default arguments
|
||||
for (mp_uint_t i = n_args; i < self->n_pos_args; i++) {
|
||||
code_state->state[n_state - 1 - i] = self->extra_args[i - (self->n_pos_args - self->n_def_args)];
|
||||
}
|
||||
} else {
|
||||
fun_pos_args_mismatch(self, self->n_pos_args - self->n_def_args, n_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy positional args into state
|
||||
for (mp_uint_t i = 0; i < n_args; i++) {
|
||||
code_state->state[n_state - 1 - i] = args[i];
|
||||
}
|
||||
|
||||
// check keyword arguments
|
||||
|
||||
if (n_kw != 0 || self->has_def_kw_args) {
|
||||
DEBUG_printf("Initial args: ");
|
||||
dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
|
||||
|
||||
mp_obj_t dict = MP_OBJ_NULL;
|
||||
if (self->takes_kw_args) {
|
||||
dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
|
||||
*var_pos_kw_args = dict;
|
||||
}
|
||||
|
||||
for (mp_uint_t i = 0; i < n_kw; i++) {
|
||||
qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]);
|
||||
for (mp_uint_t j = 0; j < self->n_pos_args + self->n_kwonly_args; j++) {
|
||||
if (arg_name == self->args[j]) {
|
||||
if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
|
||||
"function got multiple values for argument '%s'", qstr_str(arg_name)));
|
||||
}
|
||||
code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
|
||||
goto continue2;
|
||||
}
|
||||
}
|
||||
// Didn't find name match with positional args
|
||||
if (!self->takes_kw_args) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "function does not take keyword arguments"));
|
||||
}
|
||||
mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
|
||||
continue2:;
|
||||
}
|
||||
|
||||
DEBUG_printf("Args with kws flattened: ");
|
||||
dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
|
||||
|
||||
// fill in defaults for positional args
|
||||
mp_obj_t *d = &code_state->state[n_state - self->n_pos_args];
|
||||
mp_obj_t *s = &self->extra_args[self->n_def_args - 1];
|
||||
for (int i = self->n_def_args; i > 0; i--, d++, s--) {
|
||||
if (*d == MP_OBJ_NULL) {
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_printf("Args after filling default positional: ");
|
||||
dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
|
||||
|
||||
// Check that all mandatory positional args are specified
|
||||
while (d < &code_state->state[n_state]) {
|
||||
if (*d++ == MP_OBJ_NULL) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
|
||||
"function missing required positional argument #%d", &code_state->state[n_state] - d));
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all mandatory keyword args are specified
|
||||
// Fill in default kw args if we have them
|
||||
for (mp_uint_t i = 0; i < self->n_kwonly_args; i++) {
|
||||
if (code_state->state[n_state - 1 - self->n_pos_args - i] == MP_OBJ_NULL) {
|
||||
mp_map_elem_t *elem = NULL;
|
||||
if (self->has_def_kw_args) {
|
||||
elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, MP_OBJ_NEW_QSTR(self->args[self->n_pos_args + i]), MP_MAP_LOOKUP);
|
||||
}
|
||||
if (elem != NULL) {
|
||||
code_state->state[n_state - 1 - self->n_pos_args - i] = elem->value;
|
||||
} else {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
|
||||
"function missing required keyword argument '%s'", qstr_str(self->args[self->n_pos_args + i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// no keyword arguments given
|
||||
if (self->n_kwonly_args != 0) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
|
||||
"function missing keyword-only argument"));
|
||||
}
|
||||
if (self->takes_kw_args) {
|
||||
*var_pos_kw_args = mp_obj_new_dict(0);
|
||||
}
|
||||
}
|
||||
|
||||
// bytecode prelude: initialise closed over variables
|
||||
for (mp_uint_t n_local = *ip++; n_local > 0; n_local--) {
|
||||
mp_uint_t local_num = *ip++;
|
||||
code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
|
||||
}
|
||||
|
||||
// now that we skipped over the prelude, set the ip for the VM
|
||||
code_state->ip = ip;
|
||||
|
||||
DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", self->n_pos_args, self->n_kwonly_args);
|
||||
dump_args(code_state->state + n_state - self->n_pos_args - self->n_kwonly_args, self->n_pos_args + self->n_kwonly_args);
|
||||
dump_args(code_state->state, n_state);
|
||||
}
|
||||
|
||||
|
||||
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
|
||||
MP_STACK_CHECK();
|
||||
|
||||
@@ -359,16 +191,14 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw,
|
||||
mp_obj_fun_bc_t *self = self_in;
|
||||
DEBUG_printf("Func n_def_args: %d\n", self->n_def_args);
|
||||
|
||||
const byte *ip = self->bytecode;
|
||||
|
||||
// get code info size, and skip line number table
|
||||
mp_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
|
||||
ip += code_info_size;
|
||||
// skip code-info block
|
||||
const byte *code_info = self->bytecode;
|
||||
mp_uint_t code_info_size = mp_decode_uint(&code_info);
|
||||
const byte *ip = self->bytecode + code_info_size;
|
||||
|
||||
// bytecode prelude: state size and exception stack size; 16 bit uints
|
||||
mp_uint_t n_state = ip[0] | (ip[1] << 8);
|
||||
mp_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
|
||||
ip += 4;
|
||||
mp_uint_t n_state = mp_decode_uint(&ip);
|
||||
mp_uint_t n_exc_stack = mp_decode_uint(&ip);
|
||||
|
||||
#if VM_DETECT_STACK_OVERFLOW
|
||||
n_state += 1;
|
||||
|
||||
Reference in New Issue
Block a user