Commit 713ea180 authored by Damien George's avatar Damien George
Browse files

py: Add constant table to bytecode.

Contains just argument names at the moment but makes it easy to add
arbitrary constants.
parent 3a3db4dc
......@@ -97,6 +97,9 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
// ip comes in as an offset into bytecode, so turn it into a true pointer
code_state->ip = self->bytecode + (mp_uint_t)code_state->ip;
// store pointer to constant table
code_state->const_table = self->const_table;
#if MICROPY_STACKLESS
code_state->prev = NULL;
#endif
......@@ -107,9 +110,6 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
mp_uint_t n_kwonly_args = *code_state->ip++;
mp_uint_t n_def_pos_args = *code_state->ip++;
// align ip
code_state->ip = MP_ALIGN(code_state->ip, sizeof(mp_uint_t));
code_state->sp = &code_state->state[0] - 1;
code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
......@@ -168,7 +168,7 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
}
// get pointer to arg_names array
const mp_obj_t *arg_names = (const mp_obj_t*)code_state->ip;
const mp_obj_t *arg_names = (const mp_obj_t*)code_state->const_table;
for (mp_uint_t i = 0; i < n_kw; i++) {
mp_obj_t wanted_arg_name = kwargs[2 * i];
......@@ -243,7 +243,6 @@ continue2:;
// get the ip and skip argument names
const byte *ip = code_state->ip;
ip += (n_pos_args + n_kwonly_args) * sizeof(mp_uint_t);
// store pointer to code_info and jump over it
{
......
......@@ -38,24 +38,26 @@
// n_kwonly_args : byte number of keyword-only arguments this function takes
// n_def_pos_args : byte number of default positional arguments
//
// <word alignment padding>
//
// argname0 : obj (qstr)
// ... : obj (qstr)
// argnameN : obj (qstr) N = num_pos_args + num_kwonly_args
//
// code_info_size : var uint | code_info_size counts bytes in this chunk
// simple_name : var qstr |
// source_file : var qstr |
// <line number info> |
// <word alignment padding> |
// <word alignment padding> | only needed if bytecode contains pointers
//
// num_cells : byte number of locals that are cells
// local_num0 : byte
// ... : byte
// local_numN : byte N = num_cells
// local_num0 : byte |
// ... : byte |
// local_numN : byte | N = num_cells
// 255 : byte | end of list sentinel
// <bytecode> |
//
// <bytecode>
//
// constant table layout:
//
// argname0 : obj (qstr)
// ... : obj (qstr)
// argnameN : obj (qstr) N = num_pos_args + num_kwonly_args
// const0 : obj
// constN : obj
// Exception stack entry
typedef struct _mp_exc_stack {
......@@ -70,6 +72,7 @@ typedef struct _mp_exc_stack {
typedef struct _mp_code_state {
const byte *code_info;
const byte *ip;
const mp_uint_t *const_table;
mp_obj_t *sp;
// bit 0 is saved currently_in_except_block value
mp_exc_stack_t *exc_sp;
......@@ -89,7 +92,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);
mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
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, const byte *code, mp_uint_t len, const mp_uint_t *const_table);
void mp_bytecode_print2(const byte *code, mp_uint_t len);
const byte *mp_bytecode_print_str(const byte *ip);
#define mp_bytecode_print_inst(code) mp_bytecode_print2(code, 1)
......
......@@ -56,6 +56,7 @@ struct _emit_t {
mp_uint_t bytecode_offset;
mp_uint_t bytecode_size;
byte *code_base; // stores both byte code and code info
mp_uint_t *const_table;
// Accessed as mp_uint_t, so must be aligned as such
byte dummy_data[DUMMY_DATA_SIZE];
};
......@@ -123,13 +124,6 @@ STATIC void emit_write_code_info_qstr(emit_t *emit, qstr qst) {
emit_write_uint(emit, emit_get_cur_to_write_code_info, qst);
}
STATIC void emit_write_code_info_prealigned_ptr(emit_t* emit, void *ptr) {
mp_uint_t *c = (mp_uint_t*)emit_get_cur_to_write_code_info(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;
}
#if MICROPY_ENABLE_SOURCE_LINE
STATIC void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_skip, mp_uint_t lines_to_skip) {
assert(bytes_to_skip > 0 || lines_to_skip > 0);
......@@ -301,37 +295,6 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
emit_write_code_info_byte(emit, emit->scope->num_kwonly_args);
emit_write_code_info_byte(emit, emit->scope->num_def_pos_args);
// Align code-info so that following pointers are aligned on a machine word.
emit_align_code_info_to_machine_word(emit);
// Write 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 a given argument position (indexed by i) we need to find the
// corresponding id_info which is a parameter, as it has the correct
// qstr name to use as the argument name. Note that it's not a simple
// 1-1 mapping (ie i!=j in general) because of possible closed-over
// variables. In the case that the argument i has no corresponding
// parameter we use "*" as its name (since no argument can ever be named
// "*"). We could use a blank qstr but "*" is better for debugging.
// Note: there is some wasted RAM here for the case of storing a qstr
// for each closed-over variable, and maybe there is a better way to do
// it, but that would require changes to mp_setup_code_state.
for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) {
qstr qst = MP_QSTR__star_;
for (int j = 0; j < scope->id_info_len; ++j) {
id_info_t *id = &scope->id_info[j];
if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) {
qst = id->qst;
break;
}
}
emit_write_code_info_prealigned_ptr(emit, MP_OBJ_NEW_QSTR(qst));
}
}
// Write size of the rest of the code info. We don't know how big this
// variable uint will be on the MP_PASS_CODE_SIZE pass so we reserve 2 bytes
// for it and hope that is enough! TODO assert this or something.
......@@ -354,6 +317,35 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
}
}
emit_write_bytecode_byte(emit, 255); // end of list sentinel
if (pass == MP_PASS_EMIT) {
// Write 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 a given argument position (indexed by i) we need to find the
// corresponding id_info which is a parameter, as it has the correct
// qstr name to use as the argument name. Note that it's not a simple
// 1-1 mapping (ie i!=j in general) because of possible closed-over
// variables. In the case that the argument i has no corresponding
// parameter we use "*" as its name (since no argument can ever be named
// "*"). We could use a blank qstr but "*" is better for debugging.
// Note: there is some wasted RAM here for the case of storing a qstr
// for each closed-over variable, and maybe there is a better way to do
// it, but that would require changes to mp_setup_code_state.
for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) {
qstr qst = MP_QSTR__star_;
for (int j = 0; j < scope->id_info_len; ++j) {
id_info_t *id = &scope->id_info[j];
if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) {
qst = id->qst;
break;
}
}
emit->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst);
}
}
}
void mp_emit_bc_end_pass(emit_t *emit) {
......@@ -377,9 +369,12 @@ void mp_emit_bc_end_pass(emit_t *emit) {
emit->bytecode_size = emit->bytecode_offset;
emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size);
emit->const_table = m_new0(mp_uint_t, emit->scope->num_pos_args + emit->scope->num_kwonly_args);
} else if (emit->pass == MP_PASS_EMIT) {
mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base,
emit->code_info_size + emit->bytecode_size, emit->scope->scope_flags);
emit->code_info_size + emit->bytecode_size,
emit->const_table, emit->scope->scope_flags);
}
}
......
......@@ -51,9 +51,11 @@ struct _mp_raw_code_t {
union {
struct {
const byte *code;
const mp_uint_t *const_table;
} u_byte;
struct {
void *fun_data;
const mp_uint_t *const_table;
mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc
} u_native;
} data;
......@@ -65,28 +67,30 @@ 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 scope_flags) {
void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len, const mp_uint_t *const_table, mp_uint_t scope_flags) {
rc->kind = MP_CODE_BYTECODE;
rc->scope_flags = scope_flags;
rc->data.u_byte.code = code;
rc->data.u_byte.const_table = const_table;
#ifdef DEBUG_PRINT
DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags);
#endif
#if MICROPY_DEBUG_PRINTERS
if (mp_verbose_flag >= 2) {
mp_bytecode_print(rc, code, len);
mp_bytecode_print(rc, code, len, const_table);
}
#endif
}
#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB
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_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) {
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, const mp_uint_t *const_table, mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) {
assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
rc->kind = kind;
rc->scope_flags = scope_flags;
rc->n_pos_args = n_pos_args;
rc->data.u_native.fun_data = fun_data;
rc->data.u_native.const_table = const_table;
rc->data.u_native.type_sig = type_sig;
#ifdef DEBUG_PRINT
......@@ -125,11 +129,11 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
switch (rc->kind) {
case MP_CODE_BYTECODE:
no_other_choice:
fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.code);
fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.code, rc->data.u_byte.const_table);
break;
#if MICROPY_EMIT_NATIVE
case MP_CODE_NATIVE_PY:
fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data);
fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data, rc->data.u_native.const_table);
break;
case MP_CODE_NATIVE_VIPER:
fun = mp_obj_new_fun_viper(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig);
......
......@@ -43,8 +43,8 @@ typedef struct _mp_raw_code_t 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 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_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig);
void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t len, const mp_uint_t *const_table, 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, const mp_uint_t *const_table, mp_uint_t n_pos_args, mp_uint_t scope_flags, 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);
mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args);
......
......@@ -90,7 +90,7 @@ STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) {
if (emit->pass == MP_PASS_EMIT) {
void *f = asm_thumb_get_code(emit->as);
mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0, 0);
mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), NULL, emit->scope->num_pos_args, 0, 0);
}
}
......
......@@ -567,6 +567,7 @@ struct _emit_t {
vtype_kind_t saved_stack_vtype;
int prelude_offset;
int const_table_offset;
int n_state;
int stack_start;
int stack_size;
......@@ -828,7 +829,24 @@ STATIC void emit_native_end_pass(emit_t *emit) {
ASM_DATA(emit->as, 1, emit->scope->num_pos_args);
ASM_DATA(emit->as, 1, emit->scope->num_kwonly_args);
ASM_DATA(emit->as, 1, emit->scope->num_def_pos_args);
// write code info (just contains block name and source file)
ASM_DATA(emit->as, 1, 5);
ASM_DATA(emit->as, 2, emit->scope->simple_name);
ASM_DATA(emit->as, 2, emit->scope->source_file);
// bytecode prelude: initialise closed over variables
for (int i = 0; i < emit->scope->id_info_len; i++) {
id_info_t *id = &emit->scope->id_info[i];
if (id->kind == ID_INFO_KIND_CELL) {
assert(id->local_num < 255);
ASM_DATA(emit->as, 1, id->local_num); // write the local which should be converted to a cell
}
}
ASM_DATA(emit->as, 1, 255); // end of list sentinel
ASM_ALIGN(emit->as, ASM_WORD_SIZE);
emit->const_table_offset = ASM_GET_CODE_POS(emit->as);
// write argument names as qstr objects
// see comment in corresponding part of emitbc.c about the logic here
......@@ -844,18 +862,6 @@ STATIC void emit_native_end_pass(emit_t *emit) {
ASM_DATA(emit->as, ASM_WORD_SIZE, (mp_uint_t)MP_OBJ_NEW_QSTR(qst));
}
// write dummy code info (for mp_setup_code_state to parse)
ASM_DATA(emit->as, 1, 1);
// bytecode prelude: initialise closed over variables
for (int i = 0; i < emit->scope->id_info_len; i++) {
id_info_t *id = &emit->scope->id_info[i];
if (id->kind == ID_INFO_KIND_CELL) {
assert(id->local_num < 255);
ASM_DATA(emit->as, 1, id->local_num); // write the local which should be converted to a cell
}
}
ASM_DATA(emit->as, 1, 255); // end of list sentinel
}
ASM_END_PASS(emit->as);
......@@ -878,7 +884,8 @@ STATIC void emit_native_end_pass(emit_t *emit) {
mp_emit_glue_assign_native(emit->scope->raw_code,
emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY,
f, f_len, emit->scope->num_pos_args, emit->scope->scope_flags, type_sig);
f, f_len, (mp_uint_t*)((byte*)f + emit->const_table_offset),
emit->scope->num_pos_args, emit->scope->scope_flags, type_sig);
}
}
......
......@@ -537,8 +537,8 @@ 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_obj_t def_args, mp_obj_t def_kw_args, const byte *code);
mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data);
mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table);
mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table);
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);
mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
......
......@@ -126,11 +126,9 @@ qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) {
mp_decode_uint(&bc); // skip n_state
mp_decode_uint(&bc); // skip n_exc_stack
bc++; // skip scope_params
mp_uint_t n_pos_args = *bc++;
mp_uint_t n_kwonly_args = *bc++;
bc++; // skip n_pos_args
bc++; // skip n_kwonly_args
bc++; // skip n_def_pos_args
bc = MP_ALIGN(bc, sizeof(mp_uint_t)); // align
bc += (n_pos_args + n_kwonly_args) * sizeof(mp_uint_t); // skip arg names
return mp_obj_code_get_name(bc);
}
......@@ -320,7 +318,7 @@ const mp_obj_type_t mp_type_fun_bc = {
#endif
};
mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code, const mp_uint_t *const_table) {
mp_uint_t n_def_args = 0;
mp_uint_t n_extra_args = 0;
mp_obj_tuple_t *def_args = def_args_in;
......@@ -336,6 +334,7 @@ mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args_in, mp_obj_t def_kw_args, const byt
o->base.type = &mp_type_fun_bc;
o->globals = mp_globals_get();
o->bytecode = code;
o->const_table = const_table;
if (def_args != MP_OBJ_NULL) {
memcpy(o->extra_args, def_args->items, n_def_args * sizeof(mp_obj_t));
}
......@@ -364,8 +363,8 @@ STATIC const mp_obj_type_t mp_type_fun_native = {
.unary_op = mp_generic_unary_op,
};
mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data) {
mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte*)fun_data);
mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table) {
mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(def_args_in, def_kw_args, (const byte*)fun_data, const_table);
o->base.type = &mp_type_fun_native;
return o;
}
......
......@@ -32,6 +32,7 @@ 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
const byte *bytecode; // bytecode for the function
const mp_uint_t *const_table; // constant table
// 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)
......
......@@ -68,7 +68,6 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw
o->globals = self_fun->globals;
o->code_state.n_state = n_state;
o->code_state.code_info = 0; // offset to code-info
o->code_state.ip = (byte*)(ip - self_fun->bytecode); // offset to prelude
mp_setup_code_state(&o->code_state, self_fun, n_args, n_kw, args);
return o;
......
......@@ -54,7 +54,7 @@
const byte *mp_showbc_code_start;
void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len) {
void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) {
mp_showbc_code_start = ip;
// get bytecode parameters
......@@ -65,12 +65,6 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len) {
mp_uint_t n_kwonly_args = *ip++;
/*mp_uint_t n_def_pos_args =*/ ip++;
ip = MP_ALIGN(ip, sizeof(mp_uint_t));
// get and skip arg names
const mp_obj_t *arg_names = (const mp_obj_t*)ip;
ip += (n_pos_args + n_kwonly_args) * sizeof(mp_uint_t);
const byte *code_info = ip;
mp_uint_t code_info_size = mp_decode_uint(&code_info);
ip += code_info_size;
......@@ -93,7 +87,7 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len) {
// bytecode prelude: arg names (as qstr objects)
printf("arg names:");
for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) {
printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(arg_names[i])));
printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i])));
}
printf("\n");
......
Markdown is supported
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