8000 py: Merge mp_execute_bytecode into fun_bc_call. · micropython/micropython@aabd83e · GitHub
[go: up one dir, main page]

Skip to content

Commit aabd83e

Browse files
committed
py: Merge mp_execute_bytecode into fun_bc_call.
This reduces stack usage by 16 words (64 bytes) for stmhal/ port. See issue #640.
1 parent 82ed3d6 commit aabd83e

File tree

4 files changed

+120
-123
lines changed

4 files changed

+120
-123
lines changed

py/bc.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ typedef struct _mp_code_state {
4949
//mp_exc_stack_t exc_state[0];
5050
} mp_code_state;
5151

52-
mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args, uint n_args, const mp_obj_t *args2, uint n_args2, mp_obj_t *ret);
53-
mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_obj_t inject_exc);
52+
mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc);
5453
void mp_bytecode_print(const void *descr, const byte *code, int len);
5554
void mp_bytecode_print2(const byte *code, int len);
5655

py/objfun.c

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,14 @@ bool mp_obj_fun_prepare_simple_args(mp_obj_t self_in, uint n_args, uint n_kw, co
237237
fun_pos_args_mismatch(self, self->n_pos_args, n_args);
238238
}
239239

240+
// With this macro you can tune the maximum number of function state bytes
241+
// that will be allocated on the stack. Any function that needs more
242+
// than this will use the heap.
243+
#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t))
244+
245+
// Set this to enable a simple stack overflow check.
246+
#define VM_DETECT_STACK_OVERFLOW (0)
247+
240248
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
241249
// This function is pretty complicated. It's main aim is to be efficient in speed and RAM
242250
// usage for the common case of positional only args.
@@ -379,7 +387,113 @@ continue2:;
379387
DEBUG_printf("Calling: args=%p, n_args=%d, extra_args=%p, n_extra_args=%d\n", args, n_args, extra_args, n_extra_args);
380388
dump_args(args, n_args);
381389
dump_args(extra_args, n_extra_args);
382-
mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(self->bytecode, args, n_args, extra_args, n_extra_args, &result);
390+
391+
// At this point the args have all been processed and we are ready to
392+
// execute the bytecode. But we must first build the execution context.
393+
394+
const byte *ip = self->bytecode;
395+
396+
// get code info size, and skip line number table
397+
machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
398+
ip += code_info_size;
399+
400+
// bytecode prelude: state size and exception stack size; 16 bit uints
401+
machine_uint_t n_state = ip[0] | (ip[1] << 8);
402+
machine_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
403+
ip += 4;
404+
405+
// allocate state for locals and stack
406+
#if VM_DETECT_STACK_OVERFLOW
407+
n_state += 1;
408+
#endif
409+
410+
int state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
411+
mp_code_state *code_state;
412+
if (state_size > VM_MAX_STATE_ON_STACK) {
413+
code_state = m_new_obj_var(mp_code_state, byte, state_size);
414+
} else {
415+
code_state = alloca(sizeof(mp_code_state) + state_size);
416+
}
417+
418+
code_state->code_info = self->bytecode;
419+
code_state->sp = &code_state->state[0] - 1;
420+
code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
421+
code_state->n_state = n_state;
422+
423+
// init args
424+
for (uint i = 0; i < n_args; i++) {
425+
code_state->state[n_state - 1 - i] = args[i];
426+
}
427+
for (uint i = 0; i < n_extra_args; i++) {
428+
code_state->state[n_state - 1 - n_args - i] = extra_args[i];
429+
}
430+
431+
// set rest of state to MP_OBJ_NULL
432+
for (uint i = 0; i < n_state - n_args - n_extra_args; i++) {
433+
code_state->state[i] = MP_OBJ_NULL;
434+
}
435+
436+
// bytecode prelude: initialise closed over variables
437+
for (uint n_local = *ip++; n_local > 0; n_local--) {
438+
uint local_num = *ip++;
439+
code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
440+
}
441+
442+
code_state->ip = ip;
443+
444+
// execute the byte code
445+
mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL);
446+
447+
#if VM_DETECT_STACK_OVERFLOW
448+
if (vm_return_kind == MP_VM_RETURN_NORMAL) {
449+
if (code_state->sp < code_state->state) {
450+
printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state);
451+
assert(0);
452+
}
453+
}
454+
// We can't check the case when an exception is returned in state[n_state - 1]
455+
// and there are no arguments, because in this case our detection slot may have
456+
// been overwritten by the returned exception (which is allowed).
457+
if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_args == 0 && n_extra_args == 0)) {
458+
// Just check to see that we have at least 1 null object left in the state.
459+
bool overflow = true;
460+
for (uint i = 0; i < n_state - n_args - n_extra_args; i++) {
461+
if (code_state->state[i] == MP_OBJ_NULL) {
462+
overflow = false;
463+
break;
464+
}
465+
}
466+
if (overflow) {
467+
printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state);
468+
assert(0);
469+
}
470+
}
471+
#endif
472+
473+
switch (vm_return_kind) {
474+
case MP_VM_RETURN_NORMAL:
475+
// return value is in *sp
476+
result = *code_state->sp;
477+
break;
478+
479+
case MP_VM_RETURN_EXCEPTION:
480+
// return value is in state[n_state - 1]
481+
result = code_state->state[n_state - 1];
482+
break;
483+
484+
case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
485+
default:
486+
assert(0);
487+
result = mp_const_none;
488+
vm_return_kind = MP_VM_RETURN_NORMAL;
489+
break;
490+
}
491+
492+
// free the state if it was allocated on the heap
493+
if (state_size > VM_MAX_STATE_ON_STACK) {
494+
m_del_var(mp_code_state, byte, state_size, code_state);
495+
}
496+
383497
mp_globals_set(old_globals);
384498

385499
if (vm_return_kind == MP_VM_RETURN_NORMAL) {

py/objgenerator.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
106106
}
107107
mp_obj_dict_t *old_globals = mp_globals_get();
108108
mp_globals_set(self->globals);
109-
mp_vm_return_kind_t ret_kind = mp_execute_bytecode2(&self->code_state, throw_value);
109+
mp_vm_return_kind_t ret_kind = mp_execute_bytecode(&self->code_state, throw_value);
110110
mp_globals_set(old_globals);
111111

112112
switch (ret_kind) {
@@ -263,7 +263,7 @@ mp_obj_t mp_obj_new_gen_instance(mp_obj_dict_t *globals, const byte *bytecode,
263263
o->code_state.exc_sp = (mp_exc_stack_t*)(o->code_state.state + n_state) - 1;
264264
o->code_state.n_state = n_state;
265265

266-
// copy args to end of state array, in reverse (that's how mp_execute_bytecode2 needs it)
266+
// copy args to end of state array, in reverse (that's how mp_execute_bytecode needs it)
267267
for (uint i = 0; i < n_args; i++) {
268268
o->code_state.state[n_state - 1 - i] = args[i];
269269
}

py/vm.c

Lines changed: 2 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@
4141
#include "bc.h"
4242
#include "objgenerator.h"
4343

44-
// With these macros you can tune the maximum number of function state bytes
45-
// that will be allocated on the stack. Any function that needs more
46-
// than this will use the heap.
47-
#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t))
48-
49-
#define DETECT_VM_STACK_OVERFLOW (0)
5044
#if 0
5145
#define TRACE(ip) mp_bytecode_print2(ip, 1);
5246
#else
@@ -103,123 +97,13 @@ typedef enum {
10397
currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \
10498
exc_sp--; /* pop back to previous exception handler */
10599

106-
mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args, uint n_args,
107-
const mp_obj_t *args2, uint n_args2, mp_obj_t *ret) {
108-
const byte *ip = code;
109-
110-
// get code info size, and skip line number table
111-
machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
112-
ip += code_info_size;
113-
114-
// bytecode prelude: state size and exception stack size; 16 bit uints
115-
machine_uint_t n_state = ip[0] | (ip[1] << 8);
116-
machine_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
117-
ip += 4;
118-
119-
// allocate state for locals and stack
120-
#if DETECT_VM_STACK_OVERFLOW
121-
n_state += 1;
122-
#endif
123-
124-
int state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
125-
mp_code_state *code_state;
126-
if (state_size > VM_MAX_STATE_ON_STACK) {
127-
code_state = m_new_obj_var(mp_code_state, byte, state_size);
128-
} else {
129-
code_state = alloca(sizeof(mp_code_state) + state_size);
130-
}
131-
132-
code_state->code_info = code;
133-
code_state->sp = &code_state->state[0] - 1;
134-
code_state->exc_sp = (mp_exc_stack_t*)(code 2851 _state->state + n_state) - 1;
135-
code_state->n_state = n_state;
136-
137-
// init args
138-
for (uint i = 0; i < n_args; i++) {
139-
code_state->state[n_state - 1 - i] = args[i];
140-
}
141-
for (uint i = 0; i < n_args2; i++) {
142-
code_state->state[n_state - 1 - n_args - i] = args2[i];
143-
}
144-
145-
// set rest of state to MP_OBJ_NULL
146-
for (uint i = 0; i < n_state - n_args - n_args2; i++) {
147-
code_state->state[i] = MP_OBJ_NULL;
148-
}
149-
150-
// bytecode prelude: initialise closed over variables
151-
for (uint n_local = *ip++; n_local > 0; n_local--) {
152-
uint local_num = *ip++;
153-
code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
154-
}
155-
156-
code_state->ip = ip;
157-
158-
// execute the byte code
159-
mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode2(code_state, MP_OBJ_NULL);
160-
161-
#if DETECT_VM_STACK_OVERFLOW
162-
if (vm_return_kind == MP_VM_RETURN_NORMAL) {
163-
if (code_state->sp < code_state->state) {
164-
printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state);
165-
assert(0);
166-
}
167-
}
168-
// We can't check the case when an exception is returned in state[n_state - 1]
169-
// and there are no arguments, because in this case our detection slot may have
170-
// been overwritten by the returned exception (which is allowed).
171-
if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_args == 0 && n_args2 == 0)) {
172-
// Just check to see that we have at least 1 null object left in the state.
173-
bool overflow = true;
174-
for (uint i = 0; i < n_state - n_args - n_args2; i++) {
175-
if (code_state->state[i] == MP_OBJ_NULL) {
176-
overflow = false;
177-
break;
178-
}
179-
}
180-
if (overflow) {
181-
printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state);
182-
assert(0);
183-
}
184-
}
185-
#endif
186-
187-
mp_vm_return_kind_t ret_kind;
188-
switch (vm_return_kind) {
189-
case MP_VM_RETURN_NORMAL:
190-
// return value is in *sp
191-
*ret = *code_state->sp;
192-
ret_kind = MP_VM_RETURN_NORMAL;
193-
break;
194-
195-
case MP_VM_RETURN_EXCEPTION:
196-
// return value is in state[n_state - 1]
197-
*ret = code_state->state[n_state - 1];
198-
ret_kind = MP_VM_RETURN_EXCEPTION;
199-
break;
200-
201-
case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
202-
default:
203-
assert(0);
204-
*ret = mp_const_none;
205-
ret_kind = MP_VM_RETURN_NORMAL;
206-
break;
207-
}
208-
209-
// free the state if it was allocated on the heap
210-
if (state_size > VM_MAX_STATE_ON_STACK) {
211-
m_del_var(mp_code_state, byte, state_size, code_state);
212-
}
213-
return ret_kind;
214-
}
215-
216100
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
217101
// sp points to bottom of stack which grows up
218102
// returns:
219103
// MP_VM_RETURN_NORMAL, sp valid, return value in *sp
220104
// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
221105
// MP_VM_RETURN_EXCEPTION, exception in fastn[0]
222-
mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_obj_t inject_exc) {
106+
mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc) {
223107
#if MICROPY_OPT_COMPUTED_GOTO
224108
#include "vmentrytable.h"
225109
#define DISPATCH() do { \
@@ -241,7 +125,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_
241125
// loop and the exception handler, leading to very obscure bugs.
242126
#define RAISE(o) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0)
243127

244-
// Pointers which are constant for particular invocation of mp_execute_bytecode2()
128+
// Pointers which are constant for particular invocation of mp_execute_bytecode()
245129
mp_obj_t *const fastn = &code_state->state[code_state->n_state - 1];
246130
mp_exc_stack_t *const exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state);
247131

0 commit comments

Comments
 (0)
0