28
28
#include <stdio.h>
29
29
#include <string.h>
30
30
#include <assert.h>
31
+ #include <alloca.h>
31
32
32
33
#include "mpconfig.h"
33
34
#include "nlr.h"
40
41
#include "bc.h"
41
42
#include "objgenerator.h"
42
43
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
44
45
// that will be allocated on the stack. Any function that needs more
45
46
// 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))
48
48
49
49
#define DETECT_VM_STACK_OVERFLOW (0)
50
50
#if 0
@@ -117,68 +117,68 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
117
117
ip += 4 ;
118
118
119
119
// 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 ];
122
120
#if DETECT_VM_STACK_OVERFLOW
123
121
n_state += 1 ;
124
122
#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 ;
129
123
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 );
135
130
}
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 ;
137
136
138
137
// init args
139
138
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 ];
141
140
}
142
141
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 ];
144
143
}
145
144
146
145
// set rest of state to MP_OBJ_NULL
147
146
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 ;
149
148
}
150
149
151
150
// bytecode prelude: initialise closed over variables
152
151
for (uint n_local = * ip ++ ; n_local > 0 ; n_local -- ) {
153
152
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 ]);
155
154
}
156
155
156
+ code_state -> ip = ip ;
157
+
157
158
// 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 );
159
160
160
161
#if DETECT_VM_STACK_OVERFLOW
161
162
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 );
164
165
assert (0 );
165
166
}
166
167
}
167
-
168
168
// We can't check the case when an exception is returned in state[n_state - 1]
169
169
// and there are no arguments, because in this case our detection slot may have
170
170
// been overwritten by the returned exception (which is allowed).
171
171
if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_args == 0 && n_args2 == 0 )) {
172
172
// Just check to see that we have at least 1 null object left in the state.
173
173
bool overflow = true;
174
174
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 ) {
176
176
overflow = false;
177
177
break ;
178
178
}
179
179
}
180
180
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 );
182
182
assert (0 );
183
183
}
184
184
}
@@ -188,13 +188,13 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
188
188
switch (vm_return_kind ) {
189
189
case MP_VM_RETURN_NORMAL :
190
190
// return value is in *sp
191
- * ret = * sp ;
191
+ * ret = * code_state -> sp ;
192
192
ret_kind = MP_VM_RETURN_NORMAL ;
193
193
break ;
194
194
195
195
case MP_VM_RETURN_EXCEPTION :
196
196
// return value is in state[n_state - 1]
197
- * ret = state [n_state - 1 ];
197
+ * ret = code_state -> state [n_state - 1 ];
198
198
ret_kind = MP_VM_RETURN_EXCEPTION ;
199
199
break ;
200
200
@@ -203,18 +203,13 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
203
203
assert (0 );
204
204
* ret = mp_const_none ;
205
205
ret_kind = MP_VM_RETURN_NORMAL ;
206
+ break ;
206
207
}
207
208
208
209
// 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 );
211
212
}
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
-
218
213
return ret_kind ;
219
214
}
220
215
@@ -224,10 +219,7 @@ mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args,
224
219
// MP_VM_RETURN_NORMAL, sp valid, return value in *sp
225
220
// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
226
221
// 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 ) {
231
223
#if MICROPY_OPT_COMPUTED_GOTO
232
224
#include "vmentrytable.h"
233
225
#define DISPATCH () do { \
@@ -249,20 +241,24 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
249
241
// loop and the exception handler, leading to very obscure bugs.
250
242
#define RAISE (o ) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0)
251
243
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
+
252
248
// 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
257
253
258
254
// outer exception handling loop
259
255
for (;;) {
260
256
nlr_buf_t nlr ;
261
257
outer_dispatch_loop :
262
258
if (nlr_push (& nlr ) == 0 ) {
263
259
// 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 ;
266
262
machine_uint_t unum ;
267
263
mp_obj_t obj_shared ;
268
264
@@ -905,7 +901,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
905
901
exc_sp -- ;
906
902
}
907
903
nlr_pop ();
908
- * sp_in_out = sp ;
904
+ code_state -> sp = sp ;
909
905
assert (exc_sp == exc_stack - 1 );
910
906
return MP_VM_RETURN_NORMAL ;
911
907
@@ -936,9 +932,9 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
936
932
ENTRY (MP_BC_YIELD_VALUE ):
937
933
yield :
938
934
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 );
942
938
return MP_VM_RETURN_YIELD ;
943
939
944
940
ENTRY (MP_BC_YIELD_FROM ): {
@@ -1032,8 +1028,8 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
1032
1028
const byte * ip = save_ip + 1 ;
1033
1029
machine_uint_t unum ;
1034
1030
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
1037
1033
goto outer_dispatch_loop ; // continue with dispatch loop
1038
1034
}
1039
1035
@@ -1042,6 +1038,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(const byte *code_info, const byte **ip_
1042
1038
// But consider how to handle nested exceptions.
1043
1039
// TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj and MemoryError_obj)
1044
1040
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 ;
1045
1042
machine_uint_t code_info_size = code_info [0 ] | (code_info [1 ] << 8 ) | (code_info [2 ] << 16 ) | (code_info [3 ] << 24 );
1046
1043
qstr source_file = code_info [4 ] | (code_info [5 ] << 8 ) | (code_info [6 ] << 16 ) | (code_info [7 ] << 24 );
1047
1044
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_
1072
1069
currently_in_except_block = 1 ;
1073
1070
1074
1071
// catch exception and pass to byte code
1075
- * ip_in_out = exc_sp -> handler ;
1072
+ code_state -> ip = exc_sp -> handler ;
1076
1073
mp_obj_t * sp = MP_TAGPTR_PTR (exc_sp -> val_sp );
1077
1074
// save this exception in the stack so it can be used in a reraise, if needed
1078
1075
exc_sp -> prev_exc = nlr .ret_val ;
1079
1076
// push(traceback, exc-val, exc-type)
1080
1077
PUSH (mp_const_none );
1081
1078
PUSH (nlr .ret_val );
1082
1079
PUSH (mp_obj_get_type (nlr .ret_val ));
1083
- * sp_in_out = sp ;
1080
+ code_state -> sp = sp ;
1084
1081
1085
1082
} else {
1086
1083
// propagate exception to higher level
0 commit comments