Skip to content

Commit 1b87d10

Browse files
committed
Merge branch 'vm-alloca' of github.com:pfalcon/micropython into pfalcon-vm-alloca
Conflicts: py/vm.c Fixed stack underflow check. Use UINT_FMT/INT_FMT where necessary. Specify maximum VM-stack byte size by multiple of machine word size, so that on 64 bit machines it has same functionality as 32 bit.
2 parents 6c13d79 + b4ebad3 commit 1b87d10

File tree

3 files changed

+84
-85
lines changed

3 files changed

+84
-85
lines changed

py/bc.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,21 @@ typedef struct _mp_exc_stack {
3636
byte opcode;
3737
} mp_exc_stack_t;
3838

39+
typedef struct _mp_code_state {
40+
const byte *code_info;
41+
const byte *ip;
42+
mp_obj_t *sp;
43+
// bit 0 is saved currently_in_except_block value
44+
mp_exc_stack_t *exc_sp;
45+
uint n_state;
46+
// Variable-length
47+
mp_obj_t state[0];
48+
// Variable-length, never accessed by name, only as (void*)(state + n_state)
49+
//mp_exc_stack_t exc_state[0];
50+
} mp_code_state;
51+
3952
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);
40-
mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_in_out, mp_obj_t *fastn, mp_obj_t **sp_in_out, mp_exc_stack_t *exc_stack, mp_exc_stack_t **exc_sp_in_out, volatile mp_obj_t inject_exc);
53+
mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_obj_t inject_exc);
4154
void mp_bytecode_print(const byte *code, int len);
4255
void mp_bytecode_print2(const byte *code, int len);
4356

py/objgenerator.c

+20-31
Original file line numberDiff line numberDiff line change
@@ -82,42 +82,31 @@ mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun) {
8282
typedef struct _mp_obj_gen_instance_t {
8383
mp_obj_base_t base;
8484
mp_obj_dict_t *globals;
85-
const byte *code_info;
86-
const byte *ip;
87-
mp_obj_t *sp;
88-
// bit 0 is saved currently_in_except_block value
89-
mp_exc_stack_t *exc_sp;
90-
uint n_state;
91-
// Variable-length
92-
mp_obj_t state[0];
93-
// Variable-length, never accessed by name, only as (void*)(state + n_state)
94-
//mp_exc_stack_t exc_state[0];
85+
mp_code_state code_state;
9586
} mp_obj_gen_instance_t;
9687

9788
void gen_instance_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
9889
mp_obj_gen_instance_t *self = self_in;
99-
print(env, "<generator object '%s' at %p>", mp_obj_code_get_name(self->code_info), self_in);
90+
print(env, "<generator object '%s' at %p>", mp_obj_code_get_name(self->code_state.code_info), self_in);
10091
}
10192

10293
mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) {
10394
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_gen_instance));
10495
mp_obj_gen_instance_t *self = self_in;
105-
if (self->ip == 0) {
96+
if (self->code_state.ip == 0) {
10697
*ret_val = MP_OBJ_STOP_ITERATION;
10798
return MP_VM_RETURN_NORMAL;
10899
}
109-
if (self->sp == self->state - 1) {
100+
if (self->code_state.sp == self->code_state.state - 1) {
110101
if (send_value != mp_const_none) {
111102
nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "can't send non-None value to a just-started generator"));
112103
}
113104
} else {
114-
*self->sp = send_value;
105+
*self->code_state.sp = send_value;
115106
}
116107
mp_obj_dict_t *old_globals = mp_globals_get();
117108
mp_globals_set(self->globals);
118-
mp_vm_return_kind_t ret_kind = mp_execute_bytecode2(self->code_info, &self->ip,
119-
&self->state[self->n_state - 1], &self->sp, (mp_exc_stack_t*)(self->state + self->n_state),
120-
&self->exc_sp, throw_value);
109+
mp_vm_return_kind_t ret_kind = mp_execute_bytecode2(&self->code_state, throw_value);
121110
mp_globals_set(old_globals);
122111

123112
switch (ret_kind) {
@@ -127,17 +116,17 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
127116
// again and again, leading to side effects.
128117
// TODO: check how return with value behaves under such conditions
129118
// in CPython.
130-
self->ip = 0;
131-
*ret_val = *self->sp;
119+
self->code_state.ip = 0;
120+
*ret_val = *self->code_state.sp;
132121
break;
133122

134123
case MP_VM_RETURN_YIELD:
135-
*ret_val = *self->sp;
124+
*ret_val = *self->code_state.sp;
136125
break;
137126

138127
case MP_VM_RETURN_EXCEPTION:
139-
self->ip = 0;
140-
*ret_val = self->state[self->n_state - 1];
128+
self->code_state.ip = 0;
129+
*ret_val = self->code_state.state[self->code_state.n_state - 1];
141130
break;
142131

143132
default:
@@ -269,32 +258,32 @@ mp_obj_t mp_obj_new_gen_instance(mp_obj_dict_t *globals, const byte *bytecode,
269258
mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte, n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t));
270259
o->base.type = &mp_type_gen_instance;
271260
o->globals = globals;
272-
o->code_info = code_info;
273-
o->sp = &o->state[0] - 1; // sp points to top of stack, which starts off 1 below the state
274-
o->exc_sp = (mp_exc_stack_t*)(o->state + n_state) - 1;
275-
o->n_state = n_state;
261+
o->code_state.code_info = code_info;
262+
o->code_state.sp = &o->code_state.state[0] - 1; // sp points to top of stack, which starts off 1 below the state
263+
o->code_state.exc_sp = (mp_exc_stack_t*)(o->code_state.state + n_state) - 1;
264+
o->code_state.n_state = n_state;
276265

277266
// copy args to end of state array, in reverse (that's how mp_execute_bytecode2 needs it)
278267
for (uint i = 0; i < n_args; i++) {
279-
o->state[n_state - 1 - i] = args[i];
268+
o->code_state.state[n_state - 1 - i] = args[i];
280269
}
281270
for (uint i = 0; i < n_args2; i++) {
282-
o->state[n_state - 1 - n_args - i] = args2[i];
271+
o->code_state.state[n_state - 1 - n_args - i] = args2[i];
283272
}
284273

285274
// set rest of state to MP_OBJ_NULL
286275
for (uint i = 0; i < n_state - n_args - n_args2; i++) {
287-
o->state[i] = MP_OBJ_NULL;
276+
o->code_state.state[i] = MP_OBJ_NULL;
288277
}
289278

290279
// bytecode prelude: initialise closed over variables
291280
for (uint n_local = *bytecode++; n_local > 0; n_local--) {
292281
uint local_num = *bytecode++;
293-
o->state[n_state - 1 - local_num] = mp_obj_new_cell(o->state[n_state - 1 - local_num]);
282+
o->code_state.state[n_state - 1 - local_num] = mp_obj_new_cell(o->code_state.state[n_state - 1 - local_num]);
294283
}
295284

296285
// set ip to start of actual byte code
297-
o->ip = bytecode;
286+
o->code_state.ip = bytecode;
298287

299288
return o;
300289
}

py/vm.c

+50-53
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <stdio.h>
2929
#include <string.h>
3030
#include <assert.h>
31+
#include <alloca.h>
3132

3233
#include "mpconfig.h"
3334
#include "nlr.h"
@@ -40,11 +41,10 @@
4041
#include "bc.h"
4142
#include "objgenerator.h"
4243

43-
// With these macros you can tune the maximum number of state slots
44+
// With these macros you can tune the maximum number of function state bytes
4445
// that will be allocated on the stack. Any function that needs more
4546
// than this will use the heap.
46-
#define VM_MAX_STATE_ON_STACK (10)
47-
#define VM_MAX_EXC_STATE_ON_STACK (4)
47+
#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t))
4848

4949
#define DETECT_VM_STACK_OVERFLOW (0)
5050
#if 0
@@ -117,68 +117,68 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
117117
ip += 4;
118118

119119
// allocate state for locals and stack
120-
mp_obj_t temp_state[VM_MAX_STATE_ON_STACK];
121-
mp_obj_t *state = &temp_state[0];
122120
#if DETECT_VM_STACK_OVERFLOW
123121
n_state += 1;
124122
#endif
125-
if (n_state > VM_MAX_STATE_ON_STACK) {
126-
state = m_new(mp_obj_t, n_state);
127-
}
128-
mp_obj_t *sp = &state[0] - 1;
129123

130-
// allocate state for exceptions
131-
mp_exc_stack_t exc_state[VM_MAX_EXC_STATE_ON_STACK];
132-
mp_exc_stack_t *exc_stack = &exc_state[0];
133-
if (n_exc_stack > VM_MAX_EXC_STATE_ON_STACK) {
134-
exc_stack = m_new(mp_exc_stack_t, n_exc_stack);
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);
135130
}
136-
mp_exc_stack_t *exc_sp = &exc_stack[0] - 1;
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_state->state + n_state) - 1;
135+
code_state->n_state = n_state;
137136

138137
// init args
139138
for (uint i = 0; i < n_args; i++) {
140-
state[n_state - 1 - i] = args[i];
139+
code_state->state[n_state - 1 - i] = args[i];
141140
}
142141
for (uint i = 0; i < n_args2; i++) {
143-
state[n_state - 1 - n_args - i] = args2[i];
142+
code_state->state[n_state - 1 - n_args - i] = args2[i];
144143
}
145144

146145
// set rest of state to MP_OBJ_NULL
147146
for (uint i = 0; i < n_state - n_args - n_args2; i++) {
148-
state[i] = MP_OBJ_NULL;
147+
code_state->state[i] = MP_OBJ_NULL;
149148
}
150149

151150
// bytecode prelude: initialise closed over variables
152151
for (uint n_local = *ip++; n_local > 0; n_local--) {
153152
uint local_num = *ip++;
154-
state[n_state - 1 - local_num] = mp_obj_new_cell(state[n_state - 1 - local_num]);
153+
code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
155154
}
156155

156+
code_state->ip = ip;
157+
157158
// execute the byte code
158-
mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode2(code, &ip, &state[n_state - 1], &sp, exc_stack, &exc_sp, MP_OBJ_NULL);
159+
mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode2(code_state, MP_OBJ_NULL);
159160

160161
#if DETECT_VM_STACK_OVERFLOW
161162
if (vm_return_kind == MP_VM_RETURN_NORMAL) {
162-
if (sp < state) {
163-
printf("VM stack underflow: " INT_FMT "\n", sp - state);
163+
if (code_state->sp < code_state->state) {
164+
printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state);
164165
assert(0);
165166
}
166167
}
167-
168168
// We can't check the case when an exception is returned in state[n_state - 1]
169169
// and there are no arguments, because in this case our detection slot may have
170170
// been overwritten by the returned exception (which is allowed).
171171
if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_args == 0 && n_args2 == 0)) {
172172
// Just check to see that we have at least 1 null object left in the state.
173173
bool overflow = true;
174174
for (uint i = 0; i < n_state - n_args - n_args2; i++) {
175-
if (state[i] == MP_OBJ_NULL) {
175+
if (code_state->state[i] == MP_OBJ_NULL) {
176176
overflow = false;
177177
break;
178178
}
179179
}
180180
if (overflow) {
181-
printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", state, n_state);
181+
printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state);
182182
assert(0);
183183
}
184184
}
@@ -188,13 +188,13 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
188188
switch (vm_return_kind) {
189189
case MP_VM_RETURN_NORMAL:
190190
// return value is in *sp
191-
*ret = *sp;
191+
*ret = *code_state->sp;
192192
ret_kind = MP_VM_RETURN_NORMAL;
193193
break;
194194

195195
case MP_VM_RETURN_EXCEPTION:
196196
// return value is in state[n_state - 1]
197-
*ret = state[n_state - 1];
197+
*ret = code_state->state[n_state - 1];
198198
ret_kind = MP_VM_RETURN_EXCEPTION;
199199
break;
200200

@@ -203,18 +203,13 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
203203
assert(0);
204204
*ret = mp_const_none;
205205
ret_kind = MP_VM_RETURN_NORMAL;
206+
break;
206207
}
207208

208209
// free the state if it was allocated on the heap
209-
if (n_state > VM_MAX_STATE_ON_STACK) {
210-
m_free(state, n_state);
210+
if (state_size > VM_MAX_STATE_ON_STACK) {
211+
m_del_var(mp_code_state, byte, state_size, code_state);
211212
}
212-
213-
// free the exception state if it was allocated on the heap
214-
if (n_exc_stack > VM_MAX_EXC_STATE_ON_STACK) {
215-
m_free(exc_stack, n_exc_stack);
216-
}
217-
218213
return ret_kind;
219214
}
220215

@@ -224,10 +219,7 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
224219
// MP_VM_RETURN_NORMAL, sp valid, return value in *sp
225220
// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
226221
// MP_VM_RETURN_EXCEPTION, exception in fastn[0]
227-
mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_in_out,
228-
mp_obj_t *fastn, mp_obj_t **sp_in_out,
229-
mp_exc_stack_t *exc_stack, mp_exc_stack_t **exc_sp_in_out,
230-
volatile mp_obj_t inject_exc) {
222+
mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_obj_t inject_exc) {
231223
#if MICROPY_OPT_COMPUTED_GOTO
232224
#include "vmentrytable.h"
233225
#define DISPATCH() do { \
@@ -249,20 +241,24 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
249241
// loop and the exception handler, leading to very obscure bugs.
250242
#define RAISE(o) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0)
251243

244+
// Pointers which are constant for particular invocation of mp_execute_bytecode2()
245+
mp_obj_t *const fastn = &code_state->state[code_state->n_state - 1];
246+
mp_exc_stack_t *const exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state);
247+
252248
// variables that are visible to the exception handler (declared volatile)
253-
volatile bool currently_in_except_block = MP_TAGPTR_TAG(*exc_sp_in_out); // 0 or 1, to detect nested exceptions
254-
mp_exc_stack_t *volatile exc_sp = MP_TAGPTR_PTR(*exc_sp_in_out); // stack grows up, exc_sp points to top of stack
255-
const byte *volatile save_ip = *ip_in_out; // this is so we can access ip in the exception handler without making ip volatile (which means the compiler can't keep it in a register in the main loop)
256-
mp_obj_t *volatile save_sp = *sp_in_out; // this is so we can access sp in the exception handler when needed
249+
volatile bool currently_in_except_block = MP_TAGPTR_TAG(code_state->exc_sp); // 0 or 1, to detect nested exceptions
250+
mp_exc_stack_t *volatile exc_sp = MP_TAGPTR_PTR(code_state->exc_sp); // stack grows up, exc_sp points to top of stack
251+
const byte *volatile save_ip = code_state->ip; // this is so we can access ip in the exception handler without making ip volatile (which means the compiler can't keep it in a register in the main loop)
252+
mp_obj_t *volatile save_sp = code_state->sp; // this is so we can access sp in the exception handler when needed
257253

258254
// outer exception handling loop
259255
for (;;) {
260256
nlr_buf_t nlr;
261257
outer_dispatch_loop:
262258
if (nlr_push(&nlr) == 0) {
263259
// local variables that are not visible to the exception handler
264-
const byte *ip = *ip_in_out;
265-
mp_obj_t *sp = *sp_in_out;
260+
const byte *ip = code_state->ip;
261+
mp_obj_t *sp = code_state->sp;
266262
machine_uint_t unum;
267263
mp_obj_t obj_shared;
268264

@@ -905,7 +901,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
905901
exc_sp--;
906902
}
907903
nlr_pop();
908-
*sp_in_out = sp;
904+
code_state->sp = sp;
909905
assert(exc_sp == exc_stack - 1);
910906
return MP_VM_RETURN_NORMAL;
911907

@@ -936,9 +932,9 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
936932
ENTRY(MP_BC_YIELD_VALUE):
937933
yield:
938934
nlr_pop();
939-
*ip_in_out = ip;
940-
*sp_in_out = sp;
941-
*exc_sp_in_out = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
935+
code_state->ip = ip;
936+
code_state->sp = sp;
937+
code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
942938
return MP_VM_RETURN_YIELD;
943939

944940
ENTRY(MP_BC_YIELD_FROM): {
@@ -1032,8 +1028,8 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
10321028
const byte *ip = save_ip + 1;
10331029
machine_uint_t unum;
10341030
DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
1035-
*ip_in_out = ip + unum; // jump to after for-block
1036-
*sp_in_out = save_sp - 1; // pop the exhausted iterator
1031+
code_state->ip = ip + unum; // jump to after for-block
1032+
code_state->sp = save_sp - 1; // pop the exhausted iterator
10371033
goto outer_dispatch_loop; // continue with dispatch loop
10381034
}
10391035

@@ -1042,6 +1038,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
10421038
// But consider how to handle nested exceptions.
10431039
// TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj and MemoryError_obj)
10441040
if (mp_obj_is_exception_instance(nlr.ret_val) && nlr.ret_val != &mp_const_GeneratorExit_obj && nlr.ret_val != &mp_const_MemoryError_obj) {
1041+
const byte *code_info = code_state->code_info;
10451042
machine_uint_t code_info_size = code_info[0] | (code_info[1] << 8) | (code_info[2] << 16) | (code_info[3] << 24);
10461043
qstr source_file = code_info[4] | (code_info[5] << 8) | (code_info[6] << 16) | (code_info[7] << 24);
10471044
qstr block_name = code_info[8] | (code_info[9] << 8) | (code_info[10] << 16) | (code_info[11] << 24);
@@ -1072,15 +1069,15 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
10721069
currently_in_except_block = 1;
10731070

10741071
// catch exception and pass to byte code
1075-
*ip_in_out = exc_sp->handler;
1072+
code_state->ip = exc_sp->handler;
10761073
mp_obj_t *sp = MP_TAGPTR_PTR(exc_sp->val_sp);
10771074
// save this exception in the stack so it can be used in a reraise, if needed
10781075
exc_sp->prev_exc = nlr.ret_val;
10791076
// push(traceback, exc-val, exc-type)
10801077
PUSH(mp_const_none);
10811078
PUSH(nlr.ret_val);
10821079
PUSH(mp_obj_get_type(nlr.ret_val));
1083-
*sp_in_out = sp;
1080+
code_state->sp = sp;
10841081

10851082
} else {
10861083
// propagate exception to higher level

0 commit comments

Comments
 (0)