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"
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 ;
261257outer_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 ):
937933yield :
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