summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2014-10-25 15:07:02 +0100
committerDamien George <damien.p.george@gmail.com>2014-10-25 20:23:13 +0100
commit1084b0f9c21b093618da4494508dec9ca8467e35 (patch)
treee21a6e19240346b6a449b432cbf961d7839fba4b /py
parentfcff4663dd5bd33eed931c7731fd133f49551b4b (diff)
downloadmicropython-1084b0f9c21b093618da4494508dec9ca8467e35.tar.gz
micropython-1084b0f9c21b093618da4494508dec9ca8467e35.zip
py: Store bytecode arg names in bytecode (were in own array).
This saves a lot of RAM for 2 reasons: 1. For functions that don't have default values, var args or var kw args (which is a large number of functions in the general case), the mp_obj_fun_bc_t type now fits in 1 GC block (previously needed 2 because of the extra pointer to point to the arg_names array). So this saves 16 bytes per function (32 bytes on 64-bit machines). 2. Combining separate memory regions generally saves RAM because the unused bytes at the end of the GC block are saved for 1 of the blocks (since that block doesn't exist on its own anymore). So generally this saves 8 bytes per function. Tested by importing lots of modules: - 64-bit Linux gave about an 8% RAM saving for 86k of used RAM. - pyboard gave about a 6% RAM saving for 31k of used RAM.
Diffstat (limited to 'py')
-rw-r--r--py/bc.c20
-rw-r--r--py/bc.h2
-rw-r--r--py/emitbc.c24
-rw-r--r--py/emitglue.c12
-rw-r--r--py/emitglue.h3
-rw-r--r--py/obj.h2
-rw-r--r--py/objfun.c8
-rw-r--r--py/objfun.h7
-rw-r--r--py/objgenerator.c3
-rw-r--r--py/showbc.c10
10 files changed, 58 insertions, 33 deletions
diff --git a/py/bc.c b/py/bc.c
index 91ef9c070f..b5515bcd79 100644
--- a/py/bc.c
+++ b/py/bc.c
@@ -93,7 +93,6 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
// 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;
@@ -153,13 +152,21 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
*var_pos_kw_args = dict;
}
+ // get pointer to arg_names array at start of bytecode prelude
+ const mp_obj_t *arg_names;
+ {
+ const byte *code_info = code_state->code_info;
+ mp_uint_t code_info_size = mp_decode_uint(&code_info);
+ arg_names = (const mp_obj_t*)(code_state->code_info + code_info_size);
+ }
+
for (mp_uint_t i = 0; i < n_kw; i++) {
- qstr arg_name = MP_OBJ_QSTR_VALUE(kwargs[2 * i]);
+ mp_obj_t wanted_arg_name = 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 (wanted_arg_name == arg_names[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)));
+ "function got multiple values for argument '%s'", qstr_str(MP_OBJ_QSTR_VALUE(wanted_arg_name))));
}
code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
goto continue2;
@@ -202,13 +209,13 @@ continue2:;
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);
+ elem = mp_map_lookup(&((mp_obj_dict_t*)self->extra_args[self->n_def_args])->map, arg_names[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])));
+ "function missing required keyword argument '%s'", qstr_str(MP_OBJ_QSTR_VALUE(arg_names[self->n_pos_args + i]))));
}
}
}
@@ -225,6 +232,7 @@ continue2:;
}
// bytecode prelude: initialise closed over variables
+ const byte *ip = code_state->ip;
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]);
diff --git a/py/bc.h b/py/bc.h
index 6cda15b046..058eaba401 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -53,7 +53,7 @@ mp_uint_t mp_decode_uint(const byte **ptr);
mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc);
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);
-void mp_bytecode_print(const void *descr, const byte *code, mp_uint_t len);
+void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *code, mp_uint_t len);
void mp_bytecode_print2(const byte *code, mp_uint_t len);
// Helper macros to access pointer with least significant bit holding a flag
diff --git a/py/emitbc.c b/py/emitbc.c
index 05dc79ef4f..cee6d3396c 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -218,6 +218,13 @@ STATIC void emit_write_bytecode_byte_uint(emit_t* emit, byte b, mp_uint_t val) {
emit_write_uint(emit, emit_get_cur_to_write_bytecode, val);
}
+STATIC void emit_write_bytecode_prealigned_ptr(emit_t* emit, void *ptr) {
+ mp_uint_t *c = (mp_uint_t*)emit_get_cur_to_write_bytecode(emit, sizeof(mp_uint_t));
+ // Verify thar c is already uint-aligned
+ assert(c == MP_ALIGN(c, sizeof(mp_uint_t)));
+ *c = (mp_uint_t)ptr;
+}
+
// aligns the pointer so it is friendly to GC
STATIC void emit_write_bytecode_byte_ptr(emit_t* emit, byte b, void *ptr) {
emit_write_bytecode_byte(emit, b);
@@ -294,7 +301,16 @@ STATIC void emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
emit_write_code_info_qstr(emit, scope->simple_name);
emit_write_code_info_qstr(emit, scope->source_file);
- // bytecode prelude: local state size and exception stack size; 16 bit uints for now
+ // bytecode prelude: argument names (needed to resolve positional args passed as keywords)
+ // we store them as full word-sized objects for efficient access in mp_setup_code_state
+ // this is the start of the prelude and is guaranteed to be aligned on a word boundary
+ {
+ for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) {
+ emit_write_bytecode_prealigned_ptr(emit, MP_OBJ_NEW_QSTR(scope->id_info[i].qst));
+ }
+ }
+
+ // bytecode prelude: local state size and exception stack size
{
mp_uint_t n_state = scope->num_locals + scope->stack_size;
if (n_state == 0) {
@@ -358,13 +374,9 @@ STATIC void emit_bc_end_pass(emit_t *emit) {
emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size);
} else if (emit->pass == MP_PASS_EMIT) {
- qstr *arg_names = m_new(qstr, emit->scope->num_pos_args + emit->scope->num_kwonly_args);
- for (int i = 0; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) {
- arg_names[i] = emit->scope->id_info[i].qst;
- }
mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base,
emit->code_info_size + emit->bytecode_size,
- emit->scope->num_pos_args, emit->scope->num_kwonly_args, arg_names,
+ emit->scope->num_pos_args, emit->scope->num_kwonly_args,
emit->scope->scope_flags);
}
}
diff --git a/py/emitglue.c b/py/emitglue.c
index dd31de925b..41a6720e70 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -55,26 +55,20 @@ mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
return rc;
}
-void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, qstr *arg_names, mp_uint_t scope_flags) {
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags) {
rc->kind = MP_CODE_BYTECODE;
rc->scope_flags = scope_flags;
rc->n_pos_args = n_pos_args;
rc->n_kwonly_args = n_kwonly_args;
- rc->arg_names = arg_names;
rc->u_byte.code = code;
rc->u_byte.len = len;
#ifdef DEBUG_PRINT
DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " n_kwonly_args=" UINT_FMT " flags=%x\n", code, len, n_pos_args, n_kwonly_args, (uint)scope_flags);
- DEBUG_printf(" arg names:");
- for (int i = 0; i < n_pos_args + n_kwonly_args; i++) {
- DEBUG_printf(" %s", qstr_str(arg_names[i]));
- }
- DEBUG_printf("\n");
#endif
#if MICROPY_DEBUG_PRINTERS
if (mp_verbose_flag > 0) {
- mp_bytecode_print(rc, code, len);
+ mp_bytecode_print(rc, n_pos_args + n_kwonly_args, code, len);
}
#endif
}
@@ -121,7 +115,7 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
mp_obj_t fun;
switch (rc->kind) {
case MP_CODE_BYTECODE:
- fun = mp_obj_new_fun_bc(rc->scope_flags, rc->arg_names, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->u_byte.code);
+ fun = mp_obj_new_fun_bc(rc->scope_flags, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->u_byte.code);
break;
#if MICROPY_EMIT_NATIVE
case MP_CODE_NATIVE_PY:
diff --git a/py/emitglue.h b/py/emitglue.h
index 91d5285ad5..fd55af26a6 100644
--- a/py/emitglue.h
+++ b/py/emitglue.h
@@ -40,7 +40,6 @@ typedef struct _mp_raw_code_t {
mp_uint_t scope_flags : 7;
mp_uint_t n_pos_args : 11;
mp_uint_t n_kwonly_args : 11;
- qstr *arg_names;
union {
struct {
byte *code;
@@ -55,7 +54,7 @@ typedef struct _mp_raw_code_t {
mp_raw_code_t *mp_emit_glue_new_raw_code(void);
-void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, qstr *arg_names, mp_uint_t scope_flags);
+void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags);
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_args, mp_uint_t type_sig);
mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
diff --git a/py/obj.h b/py/obj.h
index 0ae9dc0303..4d4c73a001 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -383,7 +383,7 @@ mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg);
mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, mp_uint_t n_args, const mp_obj_t *args);
mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg);
mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!)
-mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, qstr *args, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code);
+mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code);
mp_obj_t mp_obj_new_fun_native(mp_uint_t n_args, void *fun_data);
mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig);
mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data);
diff --git a/py/objfun.c b/py/objfun.c
index e64d088bfa..3adf05e3b0 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -173,7 +173,10 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw,
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
+ // bytecode prelude: skip arg names
+ ip += (self->n_pos_args + self->n_kwonly_args) * sizeof(mp_obj_t);
+
+ // bytecode prelude: state size and exception stack size
mp_uint_t n_state = mp_decode_uint(&ip);
mp_uint_t n_exc_stack = mp_decode_uint(&ip);
@@ -268,7 +271,7 @@ const mp_obj_type_t mp_type_fun_bc = {
.binary_op = mp_obj_fun_binary_op,
};
-mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, qstr *args, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
+mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
mp_uint_t n_def_args = 0;
mp_uint_t n_extra_args = 0;
mp_obj_tuple_t *def_args = def_args_in;
@@ -283,7 +286,6 @@ mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, qstr *args, mp_uint_t n_pos_ar
mp_obj_fun_bc_t *o = m_new_obj_var(mp_obj_fun_bc_t, mp_obj_t, n_extra_args);
o->base.type = &mp_type_fun_bc;
o->globals = mp_globals_get();
- o->args = args;
o->n_pos_args = n_pos_args;
o->n_kwonly_args = n_kwonly_args;
o->n_def_args = n_def_args;
diff --git a/py/objfun.h b/py/objfun.h
index cc8035ef37..e23fe8d529 100644
--- a/py/objfun.h
+++ b/py/objfun.h
@@ -27,14 +27,13 @@
typedef struct _mp_obj_fun_bc_t {
mp_obj_base_t base;
mp_obj_dict_t *globals; // the context within which this function was defined
- mp_uint_t n_pos_args : 16; // number of arguments this function takes
- mp_uint_t n_kwonly_args : 16; // number of arguments this function takes
- mp_uint_t n_def_args : 16; // number of default arguments
+ mp_uint_t n_pos_args : 8; // number of arguments this function takes
+ mp_uint_t n_kwonly_args : 8; // number of keyword-only arguments this function takes
+ mp_uint_t n_def_args : 8; // number of default arguments
mp_uint_t has_def_kw_args : 1; // set if this function has default keyword args
mp_uint_t takes_var_args : 1; // set if this function takes variable args
mp_uint_t takes_kw_args : 1; // set if this function takes keyword args
const byte *bytecode; // bytecode for the function
- qstr *args; // argument names (needed to resolve positional args passed as keywords)
// the following extra_args array is allocated space to take (in order):
// - values of positional default args (if any)
// - a single slot for default kw args dict (if it has them)
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 9d16d2bee0..ce26cbce26 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -62,6 +62,9 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw
mp_uint_t code_info_size = mp_decode_uint(&code_info);
const byte *ip = self_fun->bytecode + code_info_size;
+ // bytecode prelude: skip arg names
+ ip += (self_fun->n_pos_args + self_fun->n_kwonly_args) * sizeof(mp_obj_t);
+
// bytecode prelude: get state size and exception stack size
mp_uint_t n_state = mp_decode_uint(&ip);
mp_uint_t n_exc_stack = mp_decode_uint(&ip);
diff --git a/py/showbc.c b/py/showbc.c
index 28fed14c9e..13d257d304 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -57,7 +57,7 @@
ip += sizeof(mp_uint_t); \
} while (0)
-void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len) {
+void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *ip, mp_uint_t len) {
const byte *ip_start = ip;
// get code info size
@@ -80,6 +80,14 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len) {
}
printf("\n");
+ // bytecode prelude: arg names (as qstr objects)
+ printf("arg names:");
+ for (int i = 0; i < n_total_args; i++) {
+ printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(*(mp_obj_t*)ip)));
+ ip += sizeof(mp_obj_t);
+ }
+ printf("\n");
+
// bytecode prelude: state size and exception stack size; 16 bit uints
{
uint n_state = mp_decode_uint(&ip);