This commit is contained in:
blueloveTH 2023-04-10 12:58:32 +08:00
parent 2d8db01cf1
commit 684ebe8e79
9 changed files with 94 additions and 63 deletions

View File

@ -18,42 +18,37 @@ inline PyObject* VM::_run_top_frame(){
try{ try{
if(need_raise){ need_raise = false; _raise(); } 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[] = { static void* OP_LABELS[] = {
#define OPCODE(name) &&CASE_OP_##name, #define OPCODE(name) &&CASE_OP_##name,
#include "opcodes.h" #include "opcodes.h"
#undef OPCODE #undef OPCODE
}; };
#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto *OP_LABELS[byte.op];} #define DISPATCH() { 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 TARGET(op) CASE_OP_##op:
goto *OP_LABELS[byte.op];
#else #else
#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto __NEXT_STEP;} #define TARGET(op) case OP_##op:
#define PREDICTED_DISPATCH(x) {heap._auto_collect(); byte = frame->next_bytecode(); if(byte.op == OP_##x) goto CASE_OP_##x; goto __NEXT_STEP;} #define DISPATCH() { byte = frame->next_bytecode(); goto __NEXT_STEP;}
#endif
#define TARGET(op) case OP_##op: \
CASE_OP_##op:
{
Bytecode byte = frame->next_bytecode();
#if !USE_COMPUTED_GOTO
__NEXT_STEP:; __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 #if DEBUG_CEVAL_STEP
std::cout << frame->stack_info() << " " << OP_NAMES[byte.op] << std::endl; std::cout << frame->stack_info() << " " << OP_NAMES[byte.op] << std::endl;
#endif #endif
switch (byte.op) switch (byte.op)
{ {
#endif
TARGET(NO_OP) DISPATCH(); TARGET(NO_OP) DISPATCH();
/*****************************************/ /*****************************************/
TARGET(POP_TOP) frame->pop(); DISPATCH(); TARGET(POP_TOP) frame->pop(); DISPATCH();
@ -65,7 +60,10 @@ __NEXT_STEP:;
frame->pop(); frame->pop();
} DISPATCH(); } 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_NONE) frame->push(None); DISPATCH();
TARGET(LOAD_TRUE) frame->push(True); DISPATCH(); TARGET(LOAD_TRUE) frame->push(True); DISPATCH();
TARGET(LOAD_FALSE) frame->push(False); DISPATCH(); TARGET(LOAD_FALSE) frame->push(False); DISPATCH();
@ -79,6 +77,7 @@ __NEXT_STEP:;
TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH(); TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH();
/*****************************************/ /*****************************************/
TARGET(LOAD_NAME) { TARGET(LOAD_NAME) {
heap._auto_collect();
StrName name = frame->co->names[byte.arg]; StrName name = frame->co->names[byte.arg];
PyObject* val; PyObject* val;
val = frame->f_locals().try_get(name); val = frame->f_locals().try_get(name);
@ -92,6 +91,7 @@ __NEXT_STEP:;
vm->NameError(name); vm->NameError(name);
} DISPATCH(); } DISPATCH();
TARGET(LOAD_GLOBAL) { TARGET(LOAD_GLOBAL) {
heap._auto_collect();
StrName name = frame->co->names[byte.arg]; StrName name = frame->co->names[byte.arg];
PyObject* val = frame->f_globals().try_get(name); PyObject* val = frame->f_globals().try_get(name);
if(val != nullptr) { frame->push(val); DISPATCH(); } if(val != nullptr) { frame->push(val); DISPATCH(); }
@ -239,22 +239,22 @@ __NEXT_STEP:;
DISPATCH() DISPATCH()
TARGET(COMPARE_LT) TARGET(COMPARE_LT)
INT_BINARY_OP(<, __lt__) INT_BINARY_OP(<, __lt__)
PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) DISPATCH()
TARGET(COMPARE_LE) TARGET(COMPARE_LE)
INT_BINARY_OP(<=, __le__) INT_BINARY_OP(<=, __le__)
PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) DISPATCH()
TARGET(COMPARE_EQ) TARGET(COMPARE_EQ)
INT_BINARY_OP(==, __eq__) INT_BINARY_OP(==, __eq__)
PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) DISPATCH()
TARGET(COMPARE_NE) TARGET(COMPARE_NE)
INT_BINARY_OP(!=, __ne__) INT_BINARY_OP(!=, __ne__)
PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) DISPATCH()
TARGET(COMPARE_GT) TARGET(COMPARE_GT)
INT_BINARY_OP(>, __gt__) INT_BINARY_OP(>, __gt__)
PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) DISPATCH()
TARGET(COMPARE_GE) TARGET(COMPARE_GE)
INT_BINARY_OP(>=, __ge__) INT_BINARY_OP(>=, __ge__)
PREDICTED_DISPATCH(POP_JUMP_IF_FALSE) DISPATCH()
TARGET(BITWISE_LSHIFT) TARGET(BITWISE_LSHIFT)
INT_BINARY_OP(<<, __lshift__) INT_BINARY_OP(<<, __lshift__)
DISPATCH() DISPATCH()
@ -469,9 +469,6 @@ __NEXT_STEP:;
// TARGET(WITH_ENTER) call(frame->pop_value(this), __enter__, no_arg()); DISPATCH(); // 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(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) { TARGET(ASSERT) {
PyObject* obj = frame->top(); PyObject* obj = frame->top();
Str msg; Str msg;
@ -497,10 +494,17 @@ __NEXT_STEP:;
_error(type, msg); _error(type, msg);
} DISPATCH(); } DISPATCH();
TARGET(RE_RAISE) _raise(); 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")); default: throw std::runtime_error(fmt(OP_NAMES[byte.op], " is not implemented"));
#else
default: __builtin_unreachable();
#endif
} }
UNREACHABLE(); #endif
} }
#undef DISPATCH #undef DISPATCH
#undef TARGET #undef TARGET
/**********************************************************************/ /**********************************************************************/

View File

@ -39,8 +39,12 @@ enum CodeBlockType {
struct CodeBlock { struct CodeBlock {
CodeBlockType type; CodeBlockType type;
int parent; // parent index in blocks 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 start; // start index of this block in codes, inclusive
int end; // end index of this block in codes, exclusive 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 { struct CodeObject {
@ -58,7 +62,7 @@ struct CodeObject {
List consts; List consts;
std::vector<StrName> names; std::vector<StrName> names;
std::set<Str> global_names; std::set<Str> global_names;
std::vector<CodeBlock> blocks = { CodeBlock{NO_BLOCK, -1} }; std::vector<CodeBlock> blocks = { CodeBlock(NO_BLOCK, -1, 0, 0) };
std::map<StrName, int> labels; std::map<StrName, int> labels;
std::vector<FuncDecl_> func_decls; std::vector<FuncDecl_> func_decls;

View File

@ -44,9 +44,15 @@
#define DEBUG_GC_STATS 0 #define DEBUG_GC_STATS 0
#if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__) #if (defined(__ANDROID__) && __ANDROID_API__ <= 22) || defined(__EMSCRIPTEN__)
#define PK_ENABLE_FILEIO 0 #define PK_ENABLE_FILEIO 0
#else #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 #endif
#if defined(__EMSCRIPTEN__) || defined(__arm__) || defined(__i386__) #if defined(__EMSCRIPTEN__) || defined(__arm__) || defined(__i386__)

View File

@ -607,9 +607,7 @@ __SUBSCR_END:
void compile_try_except() { void compile_try_except() {
ctx()->enter_block(TRY_EXCEPT); ctx()->enter_block(TRY_EXCEPT);
ctx()->emit(OP_TRY_BLOCK_ENTER, BC_NOARG, prev().line);
compile_block_body(); compile_block_body();
ctx()->emit(OP_TRY_BLOCK_EXIT, BC_NOARG, BC_KEEPLINE);
std::vector<int> patches = { std::vector<int> patches = {
ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE) ctx()->emit(OP_JUMP_ABSOLUTE, BC_NOARG, BC_KEEPLINE)
}; };

View File

@ -37,19 +37,22 @@ struct CodeEmitContext{
int curr_block_i = 0; int curr_block_i = 0;
bool is_compiling_class = false; bool is_compiling_class = false;
int for_loop_depth = 0;
bool is_curr_block_loop() const { bool is_curr_block_loop() const {
return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP; return co->blocks[curr_block_i].type == FOR_LOOP || co->blocks[curr_block_i].type == WHILE_LOOP;
} }
void enter_block(CodeBlockType type){ void enter_block(CodeBlockType type){
co->blocks.push_back(CodeBlock{ if(type == FOR_LOOP) for_loop_depth++;
type, curr_block_i, (int)co->codes.size() co->blocks.push_back(CodeBlock(
}); type, curr_block_i, for_loop_depth, (int)co->codes.size()
));
curr_block_i = co->blocks.size()-1; curr_block_i = co->blocks.size()-1;
} }
void exit_block(){ void exit_block(){
if(co->blocks[curr_block_i].type == FOR_LOOP) for_loop_depth--;
co->blocks[curr_block_i].end = co->codes.size(); co->blocks[curr_block_i].end = co->codes.size();
curr_block_i = co->blocks[curr_block_i].parent; curr_block_i = co->blocks[curr_block_i].parent;
if(curr_block_i < 0) UNREACHABLE(); if(curr_block_i < 0) UNREACHABLE();

View File

@ -17,7 +17,6 @@ struct Frame {
PyObject* _module; PyObject* _module;
NameDict_ _locals; NameDict_ _locals;
NameDict_ _closure; NameDict_ _closure;
std::vector<std::pair<int, ValueStack>> s_try_block;
NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); } NameDict& f_locals() noexcept { return _locals!=nullptr ? *_locals : _module->attr(); }
NameDict& f_globals() noexcept { return _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_abs(int i){ _next_ip = i; }
void jump_rel(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(){ bool jump_to_exception_handler(){
if(s_try_block.empty()) return false; // try to find a parent try block
PyObject* obj = popx(); int block = co->codes[_ip].block;
auto& p = s_try_block.back(); while(block >= 0){
_data = std::move(p.second); if(co->blocks[block].type == TRY_EXCEPT) break;
_data.push_back(obj); block = co->blocks[block].parent;
_next_ip = co->blocks[p.first].end; }
on_try_block_exit(); 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; return true;
} }
int _exit_block(int i){ int _exit_block(int i){
if(co->blocks[i].type == FOR_LOOP) pop(); 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; return co->blocks[i].parent;
} }
@ -153,15 +151,12 @@ struct Frame {
} }
void _gc_mark() const { void _gc_mark() const {
// this frame has been moved // do return if this frame has been moved
if(_data._data == nullptr) return; if(_data._data == nullptr) return;
for(PyObject* obj : _data) OBJ_MARK(obj); for(PyObject* obj : _data) OBJ_MARK(obj);
OBJ_MARK(_module); OBJ_MARK(_module);
if(_locals != nullptr) _locals->_gc_mark(); if(_locals != nullptr) _locals->_gc_mark();
if(_closure != nullptr) _closure->_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(); co->_gc_mark();
} }
}; };

View File

@ -102,8 +102,6 @@ OPCODE(STORE_CLASS_ATTR)
// OPCODE(WITH_ENTER) // OPCODE(WITH_ENTER)
// OPCODE(WITH_EXIT) // OPCODE(WITH_EXIT)
/**************************/ /**************************/
OPCODE(TRY_BLOCK_ENTER)
OPCODE(TRY_BLOCK_EXIT)
OPCODE(ASSERT) OPCODE(ASSERT)
OPCODE(EXCEPTION_MATCH) OPCODE(EXCEPTION_MATCH)
OPCODE(RAISE) OPCODE(RAISE)

View File

@ -105,6 +105,11 @@ struct pod_vector{
_size--; _size--;
} }
void resize(int size){
if(size > _capacity) reserve(size);
_size = size;
}
~pod_vector() { ~pod_vector() {
if(_data!=nullptr) pool128.dealloc(_data); if(_data!=nullptr) pool128.dealloc(_data);
} }

View File

@ -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: class A:
def __getitem__(self, i): def __getitem__(self, i):
raise KeyError(i) raise KeyError(i)