Skip to content

Commit 1762990

Browse files
committed
py/bc: Provide separate code-state setup funcs for bytecode and native.
mpy-cross will now generate native code based on the size of mp_code_state_native_t, and the runtime will use this struct to calculate the offset of the .state field. This makes native code generation and execution (which rely on this struct) independent to the settings MICROPY_STACKLESS and MICROPY_PY_SYS_SETTRACE, both of which change the size of the mp_code_state_t struct. Fixes issue adafruit#5059. Signed-off-by: Damien George <[email protected]>
1 parent 8e1db99 commit 1762990

File tree

7 files changed

+98
-46
lines changed

7 files changed

+98
-46
lines changed

py/bc.c

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,15 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) {
122122
// contain the following valid entries:
123123
// - code_state->fun_bc should contain a pointer to the function object
124124
// - code_state->ip should contain a pointer to the beginning of the prelude
125+
// - code_state->sp should be: &code_state->state[0] - 1
125126
// - code_state->n_state should be the number of objects in the local state
126-
void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
127+
STATIC void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
127128
// This function is pretty complicated. It's main aim is to be efficient in speed and RAM
128129
// usage for the common case of positional only args.
129130

130131
// get the function object that we want to set up (could be bytecode or native code)
131132
mp_obj_fun_bc_t *self = code_state->fun_bc;
132133

133-
#if MICROPY_STACKLESS
134-
code_state->prev = NULL;
135-
#endif
136-
137-
#if MICROPY_PY_SYS_SETTRACE
138-
code_state->prev_state = NULL;
139-
code_state->frame = NULL;
140-
#endif
141-
142134
// Get cached n_state (rather than decode it again)
143135
size_t n_state = code_state->n_state;
144136

@@ -149,16 +141,16 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
149141
(void)n_state_unused;
150142
(void)n_exc_stack_unused;
151143

152-
code_state->sp = &code_state->state[0] - 1;
144+
mp_obj_t *code_state_state = code_state->sp + 1;
153145
code_state->exc_sp_idx = 0;
154146

155147
// zero out the local stack to begin with
156-
memset(code_state->state, 0, n_state * sizeof(*code_state->state));
148+
memset(code_state_state, 0, n_state * sizeof(*code_state->state));
157149

158150
const mp_obj_t *kwargs = args + n_args;
159151

160152
// var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
161-
mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args];
153+
mp_obj_t *var_pos_kw_args = &code_state_state[n_state - 1 - n_pos_args - n_kwonly_args];
162154

163155
// check positional arguments
164156

@@ -181,7 +173,7 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
181173
if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) {
182174
// given enough arguments, but may need to use some default arguments
183175
for (size_t i = n_args; i < n_pos_args; i++) {
184-
code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)];
176+
code_state_state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)];
185177
}
186178
} else {
187179
fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args);
@@ -191,14 +183,14 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
191183

192184
// copy positional args into state
193185
for (size_t i = 0; i < n_args; i++) {
194-
code_state->state[n_state - 1 - i] = args[i];
186+
code_state_state[n_state - 1 - i] = args[i];
195187
}
196188

197189
// check keyword arguments
198190

199191
if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
200192
DEBUG_printf("Initial args: ");
201-
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
193+
dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
202194

203195
mp_obj_t dict = MP_OBJ_NULL;
204196
if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
@@ -220,11 +212,11 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
220212
arg_qstr = self->context->constants.qstr_table[arg_qstr];
221213
#endif
222214
if (wanted_arg_name == MP_OBJ_NEW_QSTR(arg_qstr)) {
223-
if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
215+
if (code_state_state[n_state - 1 - j] != MP_OBJ_NULL) {
224216
mp_raise_msg_varg(&mp_type_TypeError,
225217
MP_ERROR_TEXT("function got multiple values for argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name));
226218
}
227-
code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
219+
code_state_state[n_state - 1 - j] = kwargs[2 * i + 1];
228220
goto continue2;
229221
}
230222
}
@@ -242,10 +234,10 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
242234
}
243235

244236
DEBUG_printf("Args with kws flattened: ");
245-
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
237+
dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
246238

247239
// fill in defaults for positional args
248-
mp_obj_t *d = &code_state->state[n_state - n_pos_args];
240+
mp_obj_t *d = &code_state_state[n_state - n_pos_args];
249241
mp_obj_t *s = &self->extra_args[n_def_pos_args - 1];
250242
for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) {
251243
if (*d == MP_OBJ_NULL) {
@@ -254,13 +246,13 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
254246
}
255247

256248
DEBUG_printf("Args after filling default positional: ");
257-
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
249+
dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
258250

259251
// Check that all mandatory positional args are specified
260-
while (d < &code_state->state[n_state]) {
252+
while (d < &code_state_state[n_state]) {
261253
if (*d++ == MP_OBJ_NULL) {
262254
mp_raise_msg_varg(&mp_type_TypeError,
263-
MP_ERROR_TEXT("function missing required positional argument #%d"), &code_state->state[n_state] - d);
255+
MP_ERROR_TEXT("function missing required positional argument #%d"), &code_state_state[n_state] - d);
264256
}
265257
}
266258

@@ -275,13 +267,13 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
275267
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
276268
arg_qstr = self->context->constants.qstr_table[arg_qstr];
277269
#endif
278-
if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) {
270+
if (code_state_state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) {
279271
mp_map_elem_t *elem = NULL;
280272
if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
281273
elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, MP_OBJ_NEW_QSTR(arg_qstr), MP_MAP_LOOKUP);
282274
}
283275
if (elem != NULL) {
284-
code_state->state[n_state - 1 - n_pos_args - i] = elem->value;
276+
code_state_state[n_state - 1 - n_pos_args - i] = elem->value;
285277
} else {
286278
mp_raise_msg_varg(&mp_type_TypeError,
287279
MP_ERROR_TEXT("function missing required keyword argument '%q'"), arg_qstr);
@@ -305,18 +297,47 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw
305297
// bytecode prelude: initialise closed over variables
306298
for (; n_cell; --n_cell) {
307299
size_t local_num = *ip++;
308-
code_state->state[n_state - 1 - local_num] =
309-
mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
300+
code_state_state[n_state - 1 - local_num] =
301+
mp_obj_new_cell(code_state_state[n_state - 1 - local_num]);
310302
}
311303

312304
// now that we skipped over the prelude, set the ip for the VM
313305
code_state->ip = ip;
314306

315307
DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args);
316-
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
317-
dump_args(code_state->state, n_state);
308+
dump_args(code_state_state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
309+
dump_args(code_state_state, n_state);
318310
}
319311

312+
// On entry code_state should be allocated somewhere (stack/heap) and
313+
// contain the following valid entries:
314+
// - code_state->fun_bc should contain a pointer to the function object
315+
// - code_state->n_state should be the number of objects in the local state
316+
void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
317+
code_state->ip = code_state->fun_bc->bytecode;
318+
code_state->sp = &code_state->state[0] - 1;
319+
#if MICROPY_STACKLESS
320+
code_state->prev = NULL;
321+
#endif
322+
#if MICROPY_PY_SYS_SETTRACE
323+
code_state->prev_state = NULL;
324+
code_state->frame = NULL;
325+
#endif
326+
mp_setup_code_state_helper(code_state, n_args, n_kw, args);
327+
}
328+
329+
#if MICROPY_EMIT_NATIVE
330+
// On entry code_state should be allocated somewhere (stack/heap) and
331+
// contain the following valid entries:
332+
// - code_state->fun_bc should contain a pointer to the function object
333+
// - code_state->ip should contain a pointer to the beginning of the prelude
334+
// - code_state->n_state should be the number of objects in the local state
335+
void mp_setup_code_state_native(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
336+
code_state->sp = &code_state->state[0] - 1;
337+
mp_setup_code_state_helper((mp_code_state_t *)code_state, n_args, n_kw, args);
338+
}
339+
#endif
340+
320341
#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
321342

322343
// The following table encodes the number of bytes that a specific opcode

py/bc.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,17 @@ typedef struct _mp_code_state_t {
254254
// mp_exc_stack_t exc_state[0];
255255
} mp_code_state_t;
256256

257+
// State for an executing native function (based on mp_code_state_t).
258+
typedef struct _mp_code_state_native_t {
259+
struct _mp_obj_fun_bc_t *fun_bc;
260+
const byte *ip;
261+
mp_obj_t *sp;
262+
uint16_t n_state;
263+
uint16_t exc_sp_idx;
264+
mp_obj_dict_t *old_globals;
265+
mp_obj_t state[0];
266+
} mp_code_state_native_t;
267+
257268
// Allocator may return NULL, in which case data is not stored (can be used to compute size).
258269
typedef uint8_t *(*mp_encode_uint_allocator_t)(void *env, size_t nbytes);
259270

@@ -269,6 +280,7 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state,
269280
mp_obj_t inject_exc);
270281
mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args);
271282
void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
283+
void mp_setup_code_state_native(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
272284
void mp_bytecode_print(const mp_print_t *print, const struct _mp_raw_code_t *rc, const mp_module_constants_t *cm);
273285
void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm);
274286
const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm);

py/emitnative.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@
6363

6464
// C stack layout for native functions:
6565
// 0: nlr_buf_t [optional]
66-
// emit->code_state_start: mp_code_state_t
66+
// emit->code_state_start: mp_code_state_native_t
6767
// emit->stack_start: Python object stack | emit->n_state
6868
// locals (reversed, L0 at end) |
6969
//
7070
// C stack layout for native generator functions:
7171
// 0=emit->stack_start: nlr_buf_t
7272
//
7373
// Then REG_GENERATOR_STATE points to:
74-
// 0=emit->code_state_start: mp_code_state_t
74+
// 0=emit->code_state_start: mp_code_state_native_t
7575
// emit->stack_start: Python object stack | emit->n_state
7676
// locals (reversed, L0 at end) |
7777
//
@@ -88,12 +88,12 @@
8888
#else
8989
#define SIZEOF_NLR_BUF (sizeof(nlr_buf_t) / sizeof(uintptr_t))
9090
#endif
91-
#define SIZEOF_CODE_STATE (sizeof(mp_code_state_t) / sizeof(uintptr_t))
92-
#define OFFSETOF_CODE_STATE_STATE (offsetof(mp_code_state_t, state) / sizeof(uintptr_t))
93-
#define OFFSETOF_CODE_STATE_FUN_BC (offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t))
94-
#define OFFSETOF_CODE_STATE_IP (offsetof(mp_code_state_t, ip) / sizeof(uintptr_t))
95-
#define OFFSETOF_CODE_STATE_SP (offsetof(mp_code_state_t, sp) / sizeof(uintptr_t))
96-
#define OFFSETOF_CODE_STATE_N_STATE (offsetof(mp_code_state_t, n_state) / sizeof(uintptr_t))
91+
#define SIZEOF_CODE_STATE (sizeof(mp_code_state_native_t) / sizeof(uintptr_t))
92+
#define OFFSETOF_CODE_STATE_STATE (offsetof(mp_code_state_native_t, state) / sizeof(uintptr_t))
93+
#define OFFSETOF_CODE_STATE_FUN_BC (offsetof(mp_code_state_native_t, fun_bc) / sizeof(uintptr_t))
94+
#define OFFSETOF_CODE_STATE_IP (offsetof(mp_code_state_native_t, ip) / sizeof(uintptr_t))
95+
#define OFFSETOF_CODE_STATE_SP (offsetof(mp_code_state_native_t, sp) / sizeof(uintptr_t))
96+
#define OFFSETOF_CODE_STATE_N_STATE (offsetof(mp_code_state_native_t, n_state) / sizeof(uintptr_t))
9797
#define OFFSETOF_OBJ_FUN_BC_CONTEXT (offsetof(mp_obj_fun_bc_t, context) / sizeof(uintptr_t))
9898
#define OFFSETOF_OBJ_FUN_BC_CHILD_TABLE (offsetof(mp_obj_fun_bc_t, child_table) / sizeof(uintptr_t))
9999
#define OFFSETOF_OBJ_FUN_BC_BYTECODE (offsetof(mp_obj_fun_bc_t, bytecode) / sizeof(uintptr_t))
@@ -412,7 +412,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
412412

413413
// generate code for entry to function
414414

415-
// Work out start of code state (mp_code_state_t or reduced version for viper)
415+
// Work out start of code state (mp_code_state_native_t or reduced version for viper)
416416
emit->code_state_start = 0;
417417
if (NEED_GLOBAL_EXC_HANDLER(emit)) {
418418
emit->code_state_start = SIZEOF_NLR_BUF;

py/nativeglue.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ const mp_fun_table_t mp_fun_table = {
302302
mp_delete_global,
303303
mp_obj_new_closure,
304304
mp_arg_check_num_sig,
305-
mp_setup_code_state,
305+
mp_setup_code_state_native,
306306
mp_small_int_floor_divide,
307307
mp_small_int_modulo,
308308
mp_native_yield_from,

py/nativeglue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ typedef struct _mp_fun_table_t {
131131
void (*delete_global)(qstr qst);
132132
mp_obj_t (*new_closure)(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed);
133133
void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig);
134-
void (*setup_code_state)(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
134+
void (*setup_code_state_native)(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
135135
mp_int_t (*small_int_floor_divide)(mp_int_t num, mp_int_t denom);
136136
mp_int_t (*small_int_modulo)(mp_int_t dividend, mp_int_t divisor);
137137
bool (*yield_from)(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value);

py/objfun.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) {
209209

210210
#define INIT_CODESTATE(code_state, _fun_bc, _n_state, n_args, n_kw, args) \
211211
code_state->fun_bc = _fun_bc; \
212-
code_state->ip = _fun_bc->bytecode; \
213212
code_state->n_state = _n_state; \
214213
mp_setup_code_state(code_state, n_args, n_kw, args); \
215214
code_state->old_globals = mp_globals_get();

py/objgenerator.c

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons
6565

6666
o->pend_exc = mp_const_none;
6767
o->code_state.fun_bc = self_fun;
68-
o->code_state.ip = self_fun->bytecode;
6968
o->code_state.n_state = n_state;
7069
mp_setup_code_state(&o->code_state, n_args, n_kw, args);
7170
return MP_OBJ_FROM_PTR(o);
@@ -87,6 +86,13 @@ const mp_obj_type_t mp_type_gen_wrap = {
8786

8887
#if MICROPY_EMIT_NATIVE
8988

89+
// Based on mp_obj_gen_instance_t.
90+
typedef struct _mp_obj_gen_instance_native_t {
91+
mp_obj_base_t base;
92+
mp_obj_t pend_exc;
93+
mp_code_state_native_t code_state;
94+
} mp_obj_gen_instance_native_t;
95+
9096
STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
9197
// The state for a native generating function is held in the same struct as a bytecode function
9298
mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in);
@@ -106,14 +112,15 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k
106112
MP_BC_PRELUDE_SIG_DECODE(ip);
107113

108114
// Allocate the generator object, with room for local stack (exception stack not needed).
109-
mp_obj_gen_instance_t *o = mp_obj_malloc_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t), &mp_type_gen_instance);
115+
mp_obj_gen_instance_native_t *o = mp_obj_malloc_var(mp_obj_gen_instance_native_t, byte, n_state * sizeof(mp_obj_t), &mp_type_gen_instance);
110116

111117
// Parse the input arguments and set up the code state
112118
o->pend_exc = mp_const_none;
113119
o->code_state.fun_bc = self_fun;
114120
o->code_state.ip = prelude_ptr;
115121
o->code_state.n_state = n_state;
116-
mp_setup_code_state(&o->code_state, n_args, n_kw, args);
122+
o->code_state.sp = &o->code_state.state[0] - 1;
123+
mp_setup_code_state_native(&o->code_state, n_args, n_kw, args);
117124

118125
// Indicate we are a native function, which doesn't use this variable
119126
o->code_state.exc_sp_idx = MP_CODE_STATE_EXC_SP_IDX_SENTINEL;
@@ -171,7 +178,13 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
171178
#endif
172179

173180
// If the generator is started, allow sending a value.
174-
if (self->code_state.sp == self->code_state.state - 1) {
181+
void *state_start = self->code_state.state - 1;
182+
#if MICROPY_EMIT_NATIVE
183+
if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) {
184+
state_start = ((mp_obj_gen_instance_native_t *)self)->code_state.state - 1;
185+
}
186+
#endif
187+
if (self->code_state.sp == state_start) {
175188
if (send_value != mp_const_none) {
176189
mp_raise_TypeError(MP_ERROR_TEXT("can't send non-None value to a just-started generator"));
177190
}
@@ -226,7 +239,14 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
226239

227240
case MP_VM_RETURN_EXCEPTION: {
228241
self->code_state.ip = 0;
229-
*ret_val = self->code_state.state[0];
242+
#if MICROPY_EMIT_NATIVE
243+
if (self->code_state.exc_sp_idx == MP_CODE_STATE_EXC_SP_IDX_SENTINEL) {
244+
*ret_val = ((mp_obj_gen_instance_native_t *)self)->code_state.state[0];
245+
} else
246+
#endif
247+
{
248+
*ret_val = self->code_state.state[0];
249+
}
230250
// PEP479: if StopIteration is raised inside a generator it is replaced with RuntimeError
231251
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(*ret_val)), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
232252
*ret_val = mp_obj_new_exception_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("generator raised StopIteration"));

0 commit comments

Comments
 (0)