diff --git a/src/ceval.h b/src/ceval.h index 6260e9ff..ca6a594a 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -18,42 +18,37 @@ inline PyObject* VM::_run_top_frame(){ try{ if(need_raise){ need_raise = false; _raise(); } /**********************************************************************/ -#define USE_COMPUTED_GOTO 0 +/* NOTE: + * Be aware of accidental gc! + * DO NOT leave any strong reference of PyObject* in the C stack + * For example, frame->popx() returns a strong reference which may be dangerous + * `Args` containing strong references is safe if it is passed to `call` or `fast_call` + */ +{ + Bytecode byte = frame->next_bytecode(); -#if USE_COMPUTED_GOTO +#if PK_ENABLE_COMPUTED_GOTO static void* OP_LABELS[] = { #define OPCODE(name) &&CASE_OP_##name, #include "opcodes.h" #undef OPCODE }; -#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto *OP_LABELS[byte.op];} -#define PREDICTED_DISPATCH(x) {heap._auto_collect(); byte = frame->next_bytecode(); if(byte.op == OP_##x) goto CASE_OP_##x; goto *OP_LABELS[byte.op];} +#define DISPATCH() { byte = frame->next_bytecode(); goto *OP_LABELS[byte.op];} +#define TARGET(op) CASE_OP_##op: +goto *OP_LABELS[byte.op]; + #else -#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto __NEXT_STEP;} -#define PREDICTED_DISPATCH(x) {heap._auto_collect(); byte = frame->next_bytecode(); if(byte.op == OP_##x) goto CASE_OP_##x; goto __NEXT_STEP;} -#endif +#define TARGET(op) case OP_##op: +#define DISPATCH() { byte = frame->next_bytecode(); goto __NEXT_STEP;} -#define TARGET(op) case OP_##op: \ - CASE_OP_##op: - -{ - Bytecode byte = frame->next_bytecode(); -#if !USE_COMPUTED_GOTO __NEXT_STEP:; -#endif - /* NOTE: - * Be aware of accidental gc! - * DO NOT leave any strong reference of PyObject* in the C stack - * For example, frame->popx() returns a strong reference which may be dangerous - * `Args` containing strong references is safe if it is passed to `call` or `fast_call` - */ #if DEBUG_CEVAL_STEP std::cout << frame->stack_info() << " " << OP_NAMES[byte.op] << std::endl; #endif - switch (byte.op) { +#endif TARGET(NO_OP) DISPATCH(); /*****************************************/ TARGET(POP_TOP) frame->pop(); DISPATCH(); @@ -65,7 +60,10 @@ __NEXT_STEP:; frame->pop(); } DISPATCH(); /*****************************************/ - TARGET(LOAD_CONST) frame->push(frame->co->consts[byte.arg]); DISPATCH(); + TARGET(LOAD_CONST) + heap._auto_collect(); + frame->push(frame->co->consts[byte.arg]); + DISPATCH(); TARGET(LOAD_NONE) frame->push(None); DISPATCH(); TARGET(LOAD_TRUE) frame->push(True); DISPATCH(); TARGET(LOAD_FALSE) frame->push(False); DISPATCH(); @@ -79,6 +77,7 @@ __NEXT_STEP:; TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH(); /*****************************************/ TARGET(LOAD_NAME) { + heap._auto_collect(); StrName name = frame->co->names[byte.arg]; PyObject* val; val = frame->f_locals().try_get(name); @@ -92,6 +91,7 @@ __NEXT_STEP:; vm->NameError(name); } DISPATCH(); TARGET(LOAD_GLOBAL) { + heap._auto_collect(); StrName name = frame->co->names[byte.arg]; PyObject* val = frame->f_globals().try_get(name); if(val != nullptr) { frame->push(val); DISPATCH(); } @@ -239,22 +239,22 @@ __NEXT_STEP:; DISPATCH() TARGET(COMPARE_LT) INT_BINARY_OP(<, __lt__) - PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) + DISPATCH() TARGET(COMPARE_LE) INT_BINARY_OP(<=, __le__) - PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) + DISPATCH() TARGET(COMPARE_EQ) INT_BINARY_OP(==, __eq__) - PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) + DISPATCH() TARGET(COMPARE_NE) INT_BINARY_OP(!=, __ne__) - PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) + DISPATCH() TARGET(COMPARE_GT) INT_BINARY_OP(>, __gt__) - PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) + DISPATCH() TARGET(COMPARE_GE) INT_BINARY_OP(>=, __ge__) - PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) + DISPATCH() TARGET(BITWISE_LSHIFT) INT_BINARY_OP(<<, __lshift__) DISPATCH() @@ -469,9 +469,6 @@ __NEXT_STEP:; // TARGET(WITH_ENTER) call(frame->pop_value(this), __enter__, no_arg()); DISPATCH(); // TARGET(WITH_EXIT) call(frame->pop_value(this), __exit__, no_arg()); DISPATCH(); /*****************************************/ - TARGET(TRY_BLOCK_ENTER) frame->on_try_block_enter(); DISPATCH(); - TARGET(TRY_BLOCK_EXIT) frame->on_try_block_exit(); DISPATCH(); - /*****************************************/ TARGET(ASSERT) { PyObject* obj = frame->top(); Str msg; @@ -497,10 +494,17 @@ __NEXT_STEP:; _error(type, msg); } DISPATCH(); TARGET(RE_RAISE) _raise(); DISPATCH(); +#if !PK_ENABLE_COMPUTED_GOTO +#if DEBUG_EXTRA_CHECK default: throw std::runtime_error(fmt(OP_NAMES[byte.op], " is not implemented")); +#else + default: __builtin_unreachable(); +#endif } - UNREACHABLE(); +#endif } + + #undef DISPATCH #undef TARGET /**********************************************************************/ diff --git a/src/codeobject.h b/src/codeobject.h index dc7e8f22..54c7807d 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -39,8 +39,12 @@ enum CodeBlockType { struct CodeBlock { CodeBlockType type; int parent; // parent index in blocks + int for_loop_depth; // this is used for exception handling int start; // start index of this block in codes, inclusive int end; // end index of this block in codes, exclusive + + CodeBlock(CodeBlockType type, int parent, int for_loop_depth, int start): + type(type), parent(parent), for_loop_depth(for_loop_depth), start(start), end(-1) {} }; struct CodeObject { @@ -58,7 +62,7 @@ struct CodeObject { List consts; std::vector names; std::set global_names; - std::vector blocks = { CodeBlock{NO_BLOCK, -1} }; + std::vector blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) }; std::map labels; std::vector func_decls; diff --git a/src/common.h b/src/common.h index 5e9661f6..370954b9 100644 --- a/src/common.h +++ b/src/common.h @@ -44,9 +44,15 @@ #define DEBUG_GC_STATS 0 #if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__) -#define PK_ENABLE_FILEIO 0 +#define PK_ENABLE_FILEIO 0 #else -#define PK_ENABLE_FILEIO 0 // TODO: refactor this +#define PK_ENABLE_FILEIO 0 // TODO: refactor this +#endif + +#if _MSC_VER +#define PK_ENABLE_COMPUTED_GOTO 0 +#else +#define PK_ENABLE_COMPUTED_GOTO 1 #endif #if defined(__EMSCRIPTEN__) || defined(__arm__) || defined(__i386__) diff --git a/src/compiler.h b/src/compiler.h index 3ad6fed2..7e9ef870 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -607,9 +607,7 @@ __SUBSCR_END: void compile_try_except() { ctx()->enter_block(TRY_EXCEPT); - ctx()->emit(OP_TRY_BLOCK_ENTER, BC_NOARG, prev().line); compile_block_body(); - ctx()->emit(OP_TRY_BLOCK_EXIT, BC_NOARG, BC_KEEPLINE); std::vector patches = { ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE) }; diff --git a/src/expr.h b/src/expr.h index 3a0f71ac..d2506da6 100644 --- a/src/expr.h +++ b/src/expr.h @@ -37,19 +37,22 @@ struct CodeEmitContext{ int curr_block_i = 0; bool is_compiling_class = false; + int for_loop_depth = 0; bool is_curr_block_loop() const { return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP; } void enter_block(CodeBlockType type){ - co->blocks.push_back(CodeBlock{ - type, curr_block_i, (int)co->codes.size() - }); + if(type == FOR_LOOP) for_loop_depth++; + co->blocks.push_back(CodeBlock( + type, curr_block_i, for_loop_depth, (int)co->codes.size() + )); curr_block_i = co->blocks.size()-1; } void exit_block(){ + if(co->blocks[curr_block_i].type == FOR_LOOP) for_loop_depth--; co->blocks[curr_block_i].end = co->codes.size(); curr_block_i = co->blocks[curr_block_i].parent; if(curr_block_i < 0) UNREACHABLE(); diff --git a/src/frame.h b/src/frame.h index ed18bf8e..0599ce2d 100644 --- a/src/frame.h +++ b/src/frame.h @@ -17,7 +17,6 @@ struct Frame { PyObject* _module; NameDict_ _locals; NameDict_ _closure; - std::vector> s_try_block; NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); } NameDict& f_globals() noexcept { return _module->attr(); } @@ -104,28 +103,27 @@ struct Frame { void jump_abs(int i){ _next_ip = i; } void jump_rel(int i){ _next_ip += i; } - void on_try_block_enter(){ - s_try_block.emplace_back(co->codes[_ip].block, _data); - } - - void on_try_block_exit(){ - s_try_block.pop_back(); - } - bool jump_to_exception_handler(){ - if(s_try_block.empty()) return false; - PyObject* obj = popx(); - auto& p = s_try_block.back(); - _data = std::move(p.second); - _data.push_back(obj); - _next_ip = co->blocks[p.first].end; - on_try_block_exit(); + // try to find a parent try block + int block = co->codes[_ip].block; + while(block >= 0){ + if(co->blocks[block].type == TRY_EXCEPT) break; + block = co->blocks[block].parent; + } + if(block < 0) return false; + PyObject* obj = popx(); // pop exception object + // get the stack size of the try block (depth of for loops) + int stack_size = co->blocks[block].for_loop_depth; + // std::cout << "stack_size: " << stack_size << std::endl; + if(_data.size() < stack_size) throw std::runtime_error("invalid stack size"); + _data.resize(stack_size); // rollback the stack + _data.push_back(obj); // push exception object + _next_ip = co->blocks[block].end; return true; } int _exit_block(int i){ if(co->blocks[i].type == FOR_LOOP) pop(); - else if(co->blocks[i].type == TRY_EXCEPT) on_try_block_exit(); return co->blocks[i].parent; } @@ -153,15 +151,12 @@ struct Frame { } void _gc_mark() const { - // this frame has been moved + // do return if this frame has been moved if(_data._data == nullptr) return; for(PyObject* obj : _data) OBJ_MARK(obj); OBJ_MARK(_module); if(_locals != nullptr) _locals->_gc_mark(); if(_closure != nullptr) _closure->_gc_mark(); - for(auto& p : s_try_block){ - for(PyObject* obj : p.second) OBJ_MARK(obj); - } co->_gc_mark(); } }; diff --git a/src/opcodes.h b/src/opcodes.h index 49e6a2a1..eb5c8aab 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -102,8 +102,6 @@ OPCODE(STORE_CLASS_ATTR) // OPCODE(WITH_ENTER) // OPCODE(WITH_EXIT) /**************************/ -OPCODE(TRY_BLOCK_ENTER) -OPCODE(TRY_BLOCK_EXIT) OPCODE(ASSERT) OPCODE(EXCEPTION_MATCH) OPCODE(RAISE) diff --git a/src/vector.h b/src/vector.h index f10f52fd..efffc63f 100644 --- a/src/vector.h +++ b/src/vector.h @@ -105,6 +105,11 @@ struct pod_vector{ _size--; } + void resize(int size){ + if(size > _capacity) reserve(size); + _size = size; + } + ~pod_vector() { if(_data!=nullptr) pool128.dealloc(_data); } diff --git a/tests/41_exception.py b/tests/41_exception.py index 996f6b1e..c7c4e878 100644 --- a/tests/41_exception.py +++ b/tests/41_exception.py @@ -1,3 +1,21 @@ +try: + for i in range(5): + raise KeyError(i) + exit(1) +except KeyError: + pass + +x = 0 +for i in range(5): + try: + for j in range(5): + while True: + raise KeyError(i) + x += 3 + except KeyError: + x += i +assert x == 10 + class A: def __getitem__(self, i): raise KeyError(i)