From 8f3f08503a7c2d5e2506fbaadd5b1ff2266af6f2 Mon Sep 17 00:00:00 2001 From: BLUELOVETH Date: Sun, 9 Apr 2023 17:00:58 +0000 Subject: [PATCH] up --- benchmarks/primes.py | 2 - src/ceval.h | 203 +++++++++++++++++++++++-------------------- src/gc.h | 2 + src/opcodes.h | 5 +- 4 files changed, 114 insertions(+), 98 deletions(-) diff --git a/benchmarks/primes.py b/benchmarks/primes.py index 4ebf49a1..4debcd5f 100644 --- a/benchmarks/primes.py +++ b/benchmarks/primes.py @@ -1,5 +1,3 @@ -# BUG!! There is a memory leak in this code - UPPER_BOUND = 5000000 PREFIX = 32338 diff --git a/src/ceval.h b/src/ceval.h index 51268cf2..fd7a3197 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -18,51 +18,65 @@ inline PyObject* VM::_run_top_frame(){ try{ if(need_raise){ need_raise = false; _raise(); } /**********************************************************************/ -#define DISPATCH() goto __NEXT_STEP +static void* OP_LABELS[] = { + #define OPCODE(name) &&CASE_OP_##name, + #include "opcodes.h" + #undef OPCODE +}; + +#define USE_COMPUTED_GOTO 1 + +#if USE_COMPUTED_GOTO +#define DISPATCH() {heap._auto_collect(); byte = frame->next_bytecode(); goto *OP_LABELS[byte.op];} +#else +#define DISPATCH() {heap._auto_collect(); 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:; +#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_NO_AUTO_GC - heap._auto_collect(); -#endif - - Bytecode byte = frame->next_bytecode(); #if DEBUG_CEVAL_STEP std::cout << frame->stack_info() << " " << OP_NAMES[byte.op] << std::endl; #endif switch (byte.op) { - case OP_NO_OP: DISPATCH(); + TARGET(NO_OP) DISPATCH(); /*****************************************/ - case OP_POP_TOP: frame->pop(); DISPATCH(); - case OP_DUP_TOP: frame->push(frame->top()); DISPATCH(); - case OP_ROT_TWO: std::swap(frame->top(), frame->top_1()); DISPATCH(); - case OP_PRINT_EXPR: { + TARGET(POP_TOP) frame->pop(); DISPATCH(); + TARGET(DUP_TOP) frame->push(frame->top()); DISPATCH(); + TARGET(ROT_TWO) std::swap(frame->top(), frame->top_1()); DISPATCH(); + TARGET(PRINT_EXPR) { PyObject* obj = frame->top(); // use top() to avoid accidental gc if(obj != None) *_stdout << CAST(Str&, asRepr(obj)) << '\n'; frame->pop(); } DISPATCH(); /*****************************************/ - case OP_LOAD_CONST: frame->push(frame->co->consts[byte.arg]); DISPATCH(); - case OP_LOAD_NONE: frame->push(None); DISPATCH(); - case OP_LOAD_TRUE: frame->push(True); DISPATCH(); - case OP_LOAD_FALSE: frame->push(False); DISPATCH(); - case OP_LOAD_ELLIPSIS: frame->push(Ellipsis); DISPATCH(); - case OP_LOAD_BUILTIN_EVAL: frame->push(builtins->attr(m_eval)); DISPATCH(); - case OP_LOAD_FUNCTION: { + TARGET(LOAD_CONST) 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(); + TARGET(LOAD_ELLIPSIS) frame->push(Ellipsis); DISPATCH(); + TARGET(LOAD_BUILTIN_EVAL) frame->push(builtins->attr(m_eval)); DISPATCH(); + TARGET(LOAD_FUNCTION) { FuncDecl_ decl = frame->co->func_decls[byte.arg]; PyObject* obj = VAR(Function({decl, frame->_module, frame->_locals})); frame->push(obj); } DISPATCH(); - case OP_LOAD_NULL: frame->push(_py_null); DISPATCH(); + TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH(); /*****************************************/ - case OP_LOAD_NAME: { + TARGET(LOAD_NAME) { StrName name = frame->co->names[byte.arg]; PyObject* val; val = frame->f_locals().try_get(name); @@ -75,7 +89,7 @@ __NEXT_STEP:; if(val != nullptr) { frame->push(val); DISPATCH(); } vm->NameError(name); } DISPATCH(); - case OP_LOAD_GLOBAL: { + TARGET(LOAD_GLOBAL) { StrName name = frame->co->names[byte.arg]; PyObject* val = frame->f_globals().try_get(name); if(val != nullptr) { frame->push(val); DISPATCH(); } @@ -83,47 +97,47 @@ __NEXT_STEP:; if(val != nullptr) { frame->push(val); DISPATCH(); } vm->NameError(name); } DISPATCH(); - case OP_LOAD_ATTR: { + TARGET(LOAD_ATTR) { PyObject* a = frame->top(); StrName name = frame->co->names[byte.arg]; frame->top() = getattr(a, name); } DISPATCH(); - case OP_LOAD_METHOD: { + TARGET(LOAD_METHOD) { PyObject* a = frame->top(); StrName name = frame->co->names[byte.arg]; PyObject* self; frame->top() = get_unbound_method(a, name, &self, true, true); frame->push(self); } DISPATCH(); - case OP_LOAD_SUBSCR: { + TARGET(LOAD_SUBSCR) { Args args(2); args[1] = frame->popx(); // b args[0] = frame->top(); // a frame->top() = fast_call(__getitem__, std::move(args)); } DISPATCH(); - case OP_STORE_LOCAL: { + TARGET(STORE_LOCAL) { StrName name = frame->co->names[byte.arg]; frame->f_locals().set(name, frame->popx()); } DISPATCH(); - case OP_STORE_GLOBAL: { + TARGET(STORE_GLOBAL) { StrName name = frame->co->names[byte.arg]; frame->f_globals().set(name, frame->popx()); } DISPATCH(); - case OP_STORE_ATTR: { + TARGET(STORE_ATTR) { StrName name = frame->co->names[byte.arg]; PyObject* a = frame->top(); PyObject* val = frame->top_1(); setattr(a, name, val); frame->pop_n(2); } DISPATCH(); - case OP_STORE_SUBSCR: { + TARGET(STORE_SUBSCR) { Args args(3); args[1] = frame->popx(); // b args[0] = frame->popx(); // a args[2] = frame->popx(); // val fast_call(__setitem__, std::move(args)); } DISPATCH(); - case OP_DELETE_LOCAL: { + TARGET(DELETE_LOCAL) { StrName name = frame->co->names[byte.arg]; if(frame->f_locals().contains(name)){ frame->f_locals().erase(name); @@ -131,7 +145,7 @@ __NEXT_STEP:; NameError(name); } } DISPATCH(); - case OP_DELETE_GLOBAL: { + TARGET(DELETE_GLOBAL) { StrName name = frame->co->names[byte.arg]; if(frame->f_globals().contains(name)){ frame->f_globals().erase(name); @@ -139,33 +153,33 @@ __NEXT_STEP:; NameError(name); } } DISPATCH(); - case OP_DELETE_ATTR: { + TARGET(DELETE_ATTR) { PyObject* a = frame->popx(); StrName name = frame->co->names[byte.arg]; if(!a->is_attr_valid()) TypeError("cannot delete attribute"); if(!a->attr().contains(name)) AttributeError(a, name); a->attr().erase(name); } DISPATCH(); - case OP_DELETE_SUBSCR: { + TARGET(DELETE_SUBSCR) { PyObject* b = frame->popx(); PyObject* a = frame->popx(); fast_call(__delitem__, Args{a, b}); } DISPATCH(); /*****************************************/ - case OP_BUILD_LIST: + TARGET(BUILD_LIST) frame->push(VAR(frame->popx_n_reversed(byte.arg).to_list())); DISPATCH(); - case OP_BUILD_DICT: { + TARGET(BUILD_DICT) { PyObject* t = VAR(frame->popx_n_reversed(byte.arg)); PyObject* obj = call(builtins->attr(m_dict), Args{t}); frame->push(obj); } DISPATCH(); - case OP_BUILD_SET: { + TARGET(BUILD_SET) { PyObject* t = VAR(frame->popx_n_reversed(byte.arg)); PyObject* obj = call(builtins->attr(m_set), Args{t}); frame->push(obj); } DISPATCH(); - case OP_BUILD_SLICE: { + TARGET(BUILD_SLICE) { PyObject* step = frame->popx(); PyObject* stop = frame->popx(); PyObject* start = frame->popx(); @@ -175,18 +189,18 @@ __NEXT_STEP:; if(step != None) s.step = CAST(int, step); frame->push(VAR(s)); } DISPATCH(); - case OP_BUILD_TUPLE: { + TARGET(BUILD_TUPLE) { Tuple items = frame->popx_n_reversed(byte.arg); frame->push(VAR(std::move(items))); } DISPATCH(); - case OP_BUILD_STRING: { + TARGET(BUILD_STRING) { std::stringstream ss; // asStr() may run extra bytecode for(int i=byte.arg-1; i>=0; i--) ss << CAST(Str&, asStr(frame->top_n(i))); frame->pop_n(byte.arg); frame->push(VAR(ss.str())); } DISPATCH(); /*****************************************/ - case OP_BINARY_OP: { + TARGET(BINARY_OP) { Args args(2); args[1] = frame->popx(); // lhs args[0] = frame->top(); // rhs @@ -207,47 +221,47 @@ __NEXT_STEP:; } \ DISPATCH(); - case OP_BINARY_ADD: + TARGET(BINARY_ADD) INT_BINARY_OP(+, __add__) - case OP_BINARY_SUB: + TARGET(BINARY_SUB) INT_BINARY_OP(-, __sub__) - case OP_BINARY_MUL: + TARGET(BINARY_MUL) INT_BINARY_OP(*, __mul__) - case OP_BINARY_FLOORDIV: + TARGET(BINARY_FLOORDIV) INT_BINARY_OP(/, __floordiv__) - case OP_BINARY_MOD: + TARGET(BINARY_MOD) INT_BINARY_OP(%, __mod__) - case OP_COMPARE_LT: + TARGET(COMPARE_LT) INT_BINARY_OP(<, __lt__) - case OP_COMPARE_LE: + TARGET(COMPARE_LE) INT_BINARY_OP(<=, __le__) - case OP_COMPARE_EQ: + TARGET(COMPARE_EQ) INT_BINARY_OP(==, __eq__) - case OP_COMPARE_NE: + TARGET(COMPARE_NE) INT_BINARY_OP(!=, __ne__) - case OP_COMPARE_GT: + TARGET(COMPARE_GT) INT_BINARY_OP(>, __gt__) - case OP_COMPARE_GE: + TARGET(COMPARE_GE) INT_BINARY_OP(>=, __ge__) - case OP_BITWISE_LSHIFT: + TARGET(BITWISE_LSHIFT) INT_BINARY_OP(<<, __lshift__) - case OP_BITWISE_RSHIFT: + TARGET(BITWISE_RSHIFT) INT_BINARY_OP(>>, __rshift__) - case OP_BITWISE_AND: + TARGET(BITWISE_AND) INT_BINARY_OP(&, __and__) - case OP_BITWISE_OR: + TARGET(BITWISE_OR) INT_BINARY_OP(|, __or__) - case OP_BITWISE_XOR: + TARGET(BITWISE_XOR) INT_BINARY_OP(^, __xor__) #undef INT_BINARY_OP - case OP_IS_OP: { + TARGET(IS_OP) { PyObject* rhs = frame->popx(); PyObject* lhs = frame->top(); bool ret_c = lhs == rhs; if(byte.arg == 1) ret_c = !ret_c; frame->top() = VAR(ret_c); } DISPATCH(); - case OP_CONTAINS_OP: { + TARGET(CONTAINS_OP) { Args args(2); args[0] = frame->popx(); args[1] = frame->top(); @@ -257,35 +271,35 @@ __NEXT_STEP:; frame->top() = VAR(ret_c); } DISPATCH(); /*****************************************/ - case OP_JUMP_ABSOLUTE: frame->jump_abs(byte.arg); DISPATCH(); - case OP_POP_JUMP_IF_FALSE: + TARGET(JUMP_ABSOLUTE) frame->jump_abs(byte.arg); DISPATCH(); + TARGET(POP_JUMP_IF_FALSE) if(!asBool(frame->popx())) frame->jump_abs(byte.arg); DISPATCH(); - case OP_JUMP_IF_TRUE_OR_POP: + TARGET(JUMP_IF_TRUE_OR_POP) if(asBool(frame->top()) == true) frame->jump_abs(byte.arg); else frame->pop(); DISPATCH(); - case OP_JUMP_IF_FALSE_OR_POP: + TARGET(JUMP_IF_FALSE_OR_POP) if(asBool(frame->top()) == false) frame->jump_abs(byte.arg); else frame->pop(); DISPATCH(); - case OP_LOOP_CONTINUE: { + TARGET(LOOP_CONTINUE) { int target = frame->co->blocks[byte.block].start; frame->jump_abs(target); } DISPATCH(); - case OP_LOOP_BREAK: { + TARGET(LOOP_BREAK) { int target = frame->co->blocks[byte.block].end; frame->jump_abs_break(target); } DISPATCH(); - case OP_GOTO: { + TARGET(GOTO) { StrName label = frame->co->names[byte.arg]; auto it = frame->co->labels.find(label); if(it == frame->co->labels.end()) _error("KeyError", fmt("label ", label.escape(), " not found")); frame->jump_abs_break(it->second); } DISPATCH(); /*****************************************/ - // TODO: examine this later - case OP_CALL: case OP_CALL_UNPACK: { + TARGET(CALL) + TARGET(CALL_UNPACK) { int ARGC = byte.arg; bool method_call = frame->top_n(ARGC) != _py_null; @@ -297,9 +311,10 @@ __NEXT_STEP:; PyObject* callable = frame->popx(); PyObject* ret = call(callable, std::move(args), no_arg(), true); if(ret == _py_op_call) { __ret=ret; goto __PY_OP_CALL; } - frame->push(std::move(ret)); + frame->push(ret); } DISPATCH(); - case OP_CALL_KWARGS: case OP_CALL_KWARGS_UNPACK: { + TARGET(CALL_KWARGS) + TARGET(CALL_KWARGS_UNPACK) { int ARGC = byte.arg & 0xFFFF; int KWARGC = (byte.arg >> 16) & 0xFFFF; Args kwargs = frame->popx_n_reversed(KWARGC*2); @@ -313,40 +328,40 @@ __NEXT_STEP:; PyObject* callable = frame->popx(); PyObject* ret = call(callable, std::move(args), kwargs, true); if(ret == _py_op_call) { __ret=ret; goto __PY_OP_CALL; } - frame->push(std::move(ret)); + frame->push(ret); } DISPATCH(); - case OP_RETURN_VALUE: { __ret=frame->popx(); goto __PY_SIMPLE_RETURN; } - case OP_YIELD_VALUE: return _py_op_yield; + TARGET(RETURN_VALUE) { __ret=frame->popx(); goto __PY_SIMPLE_RETURN; } + TARGET(YIELD_VALUE) return _py_op_yield; /*****************************************/ - case OP_LIST_APPEND: { + TARGET(LIST_APPEND) { PyObject* obj = frame->popx(); List& list = CAST(List&, frame->top_1()); list.push_back(obj); } DISPATCH(); - case OP_DICT_ADD: { + TARGET(DICT_ADD) { PyObject* kv = frame->popx(); Tuple& t = CAST(Tuple& ,kv); fast_call(__setitem__, Args{frame->top_1(), t[0], t[1]}); } DISPATCH(); - case OP_SET_ADD: { + TARGET(SET_ADD) { PyObject* obj = frame->popx(); fast_call(m_add, Args{frame->top_1(), obj}); } DISPATCH(); /*****************************************/ - case OP_UNARY_NEGATIVE: + TARGET(UNARY_NEGATIVE) frame->top() = num_negated(frame->top()); DISPATCH(); - case OP_UNARY_NOT: + TARGET(UNARY_NOT) frame->top() = VAR(!asBool(frame->top())); DISPATCH(); - case OP_UNARY_STAR: + TARGET(UNARY_STAR) frame->top() = VAR(StarWrapper(frame->top())); DISPATCH(); /*****************************************/ - case OP_GET_ITER: + TARGET(GET_ITER) frame->top() = asIter(frame->top()); DISPATCH(); - case OP_FOR_ITER: { + TARGET(FOR_ITER) { BaseIter* it = PyIter_AS_C(frame->top()); PyObject* obj = it->next(); if(obj != nullptr){ @@ -357,7 +372,7 @@ __NEXT_STEP:; } } DISPATCH(); /*****************************************/ - case OP_IMPORT_NAME: { + TARGET(IMPORT_NAME) { StrName name = frame->co->names[byte.arg]; PyObject* ext_mod = _modules.try_get(name); if(ext_mod == nullptr){ @@ -380,7 +395,7 @@ __NEXT_STEP:; frame->push(ext_mod); } } DISPATCH(); - case OP_IMPORT_STAR: { + TARGET(IMPORT_STAR) { PyObject* obj = frame->popx(); for(auto& [name, value]: obj->attr().items()){ std::string_view s = name.sv(); @@ -389,7 +404,8 @@ __NEXT_STEP:; } }; DISPATCH(); /*****************************************/ - case OP_UNPACK_SEQUENCE: case OP_UNPACK_EX: { + TARGET(UNPACK_SEQUENCE) + TARGET(UNPACK_EX) { // asIter or iter->next may run bytecode, accidential gc may happen auto _lock = heap.gc_scope_lock(); // lock the gc via RAII!! PyObject* obj = asIter(frame->popx()); @@ -413,7 +429,7 @@ __NEXT_STEP:; } }; DISPATCH(); /*****************************************/ - case OP_BEGIN_CLASS: { + TARGET(BEGIN_CLASS) { StrName name = frame->co->names[byte.arg]; PyObject* super_cls = frame->popx(); if(super_cls == None) super_cls = _t(tp_object); @@ -421,11 +437,11 @@ __NEXT_STEP:; PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, super_cls)); frame->push(cls); } DISPATCH(); - case OP_END_CLASS: { + TARGET(END_CLASS) { PyObject* cls = frame->popx(); cls->attr()._try_perfect_rehash(); }; DISPATCH(); - case OP_STORE_CLASS_ATTR: { + TARGET(STORE_CLASS_ATTR) { StrName name = frame->co->names[byte.arg]; PyObject* obj = frame->popx(); PyObject* cls = frame->top(); @@ -433,13 +449,13 @@ __NEXT_STEP:; } DISPATCH(); /*****************************************/ // // TODO: using "goto" inside with block may cause __exit__ not called - // case OP_WITH_ENTER: call(frame->pop_value(this), __enter__, no_arg()); DISPATCH(); - // case OP_WITH_EXIT: call(frame->pop_value(this), __exit__, 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(); /*****************************************/ - case OP_TRY_BLOCK_ENTER: frame->on_try_block_enter(); DISPATCH(); - case OP_TRY_BLOCK_EXIT: frame->on_try_block_exit(); DISPATCH(); + TARGET(TRY_BLOCK_ENTER) frame->on_try_block_enter(); DISPATCH(); + TARGET(TRY_BLOCK_EXIT) frame->on_try_block_exit(); DISPATCH(); /*****************************************/ - case OP_ASSERT: { + TARGET(ASSERT) { PyObject* obj = frame->top(); Str msg; if(is_type(obj, tp_tuple)){ @@ -452,23 +468,24 @@ __NEXT_STEP:; frame->pop(); if(!ok) _error("AssertionError", msg); } DISPATCH(); - case OP_EXCEPTION_MATCH: { + TARGET(EXCEPTION_MATCH) { const auto& e = CAST(Exception&, frame->top()); StrName name = frame->co->names[byte.arg]; frame->push(VAR(e.match_type(name))); } DISPATCH(); - case OP_RAISE: { + TARGET(RAISE) { PyObject* obj = frame->popx(); Str msg = obj == None ? "" : CAST(Str, asStr(obj)); StrName type = frame->co->names[byte.arg]; _error(type, msg); } DISPATCH(); - case OP_RE_RAISE: _raise(); DISPATCH(); + TARGET(RE_RAISE) _raise(); DISPATCH(); default: throw std::runtime_error(fmt(OP_NAMES[byte.op], " is not implemented")); } UNREACHABLE(); } #undef DISPATCH +#undef TARGET /**********************************************************************/ __PY_SIMPLE_RETURN: if(frame.index == base_id){ // [ frameBase<- ] diff --git a/src/gc.h b/src/gc.h index 9e620197..a35f021a 100644 --- a/src/gc.h +++ b/src/gc.h @@ -90,12 +90,14 @@ struct ManagedHeap{ } void _auto_collect(){ +#if !DEBUG_NO_AUTO_GC if(_gc_lock_counter > 0) return; if(gc_counter < gc_threshold) return; gc_counter = 0; collect(); gc_threshold = gen.size() * 2; if(gc_threshold < kMinGCThreshold) gc_threshold = kMinGCThreshold; +#endif } int collect(){ diff --git a/src/opcodes.h b/src/opcodes.h index 700c10af..49e6a2a1 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -47,7 +47,6 @@ OPCODE(BINARY_MUL) OPCODE(BINARY_FLOORDIV) OPCODE(BINARY_MOD) -OPCODE(COMPARE_OP) OPCODE(COMPARE_LT) OPCODE(COMPARE_LE) OPCODE(COMPARE_EQ) @@ -100,8 +99,8 @@ OPCODE(BEGIN_CLASS) OPCODE(END_CLASS) OPCODE(STORE_CLASS_ATTR) /**************************/ -OPCODE(WITH_ENTER) -OPCODE(WITH_EXIT) +// OPCODE(WITH_ENTER) +// OPCODE(WITH_EXIT) /**************************/ OPCODE(TRY_BLOCK_ENTER) OPCODE(TRY_BLOCK_EXIT)