blueloveTH 2 лет назад
Родитель
Сommit
84d71762c2
7 измененных файлов с 226 добавлено и 225 удалено
  1. 2 2
      benchmarks/recursive.py
  2. 154 137
      src/ceval.h
  3. 60 80
      src/frame.h
  4. 1 2
      src/iter.h
  5. 5 2
      src/obj.h
  6. 1 0
      src/vector.h
  7. 3 2
      src/vm.h

+ 2 - 2
benchmarks/recursive.py

@@ -1,9 +1,9 @@
 import sys
 
-sys.setrecursionlimit(10000)
+sys.setrecursionlimit(5000)
 
 def f(n):
-    if n == 8000:
+    if n == 4000:
         return -1
     return f(n + 1)
 

+ 154 - 137
src/ceval.h

@@ -13,7 +13,7 @@ inline PyObject* VM::_run_top_frame(){
 
     while(true){
 #if DEBUG_EXTRA_CHECK
-        if(frame->id < base_id) FATAL_ERROR();
+        if(frame.index < base_id) FATAL_ERROR();
 #endif
         try{
             if(need_raise){ need_raise = false; _raise(); }
@@ -21,11 +21,23 @@ inline PyObject* VM::_run_top_frame(){
 /* 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
+ * For example, POPX() returns a strong reference which may be dangerous
  * `Args` containing strong references is safe if it is passed to `call` or `fast_call`
  */
 {
-    #define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
+
+/* Stack manipulation macros */
+// https://github.com/python/cpython/blob/3.9/Python/ceval.c#L1123
+#define TOP()             (frame->_s.top())
+#define SECOND()          (frame->_s.second())
+#define PEEK(n)           (frame->_s.peek(n))
+#define STACK_SHRINK(n)   (frame->_s.shrink(n))
+#define PUSH(v)           (frame->_s.push(v))
+#define POP()             (frame->_s.pop())
+#define POPX()            (frame->_s.popx())
+#define STACK_VIEW(n)     (frame->_s.view(n))
+
+#define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
 __NEXT_FRAME:
     Bytecode byte = frame->next_bytecode();
     // cache
@@ -57,25 +69,25 @@ __NEXT_STEP:;
 #endif
     TARGET(NO_OP) DISPATCH();
     /*****************************************/
-    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(POP_TOP) POP(); DISPATCH();
+    TARGET(DUP_TOP) PUSH(TOP()); DISPATCH();
+    TARGET(ROT_TWO) std::swap(TOP(), SECOND()); DISPATCH();
     TARGET(PRINT_EXPR) {
-        PyObject* obj = frame->top();  // use top() to avoid accidental gc
+        PyObject* obj = TOP();  // use top() to avoid accidental gc
         if(obj != None) *_stdout << CAST(Str&, asRepr(obj)) << '\n';
-        frame->pop();
+        POP();
     } DISPATCH();
     /*****************************************/
     TARGET(LOAD_CONST)
         heap._auto_collect();
-        frame->push(co_consts[byte.arg]);
+        PUSH(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_INTEGER) frame->push(VAR(byte.arg)); DISPATCH();
-    TARGET(LOAD_ELLIPSIS) frame->push(Ellipsis); DISPATCH();
-    TARGET(LOAD_BUILTIN_EVAL) frame->push(builtins->attr(m_eval)); DISPATCH();
+    TARGET(LOAD_NONE) PUSH(None); DISPATCH();
+    TARGET(LOAD_TRUE) PUSH(True); DISPATCH();
+    TARGET(LOAD_FALSE) PUSH(False); DISPATCH();
+    TARGET(LOAD_INTEGER) PUSH(VAR(byte.arg)); DISPATCH();
+    TARGET(LOAD_ELLIPSIS) PUSH(Ellipsis); DISPATCH();
+    TARGET(LOAD_BUILTIN_EVAL) PUSH(builtins->attr(m_eval)); DISPATCH();
     TARGET(LOAD_FUNCTION) {
         FuncDecl_ decl = co->func_decls[byte.arg];
         PyObject* obj;
@@ -84,28 +96,28 @@ __NEXT_STEP:;
         }else{
             obj = VAR(Function({decl, frame->_module}));
         }
-        frame->push(obj);
+        PUSH(obj);
     } DISPATCH();
-    TARGET(LOAD_NULL) frame->push(_py_null); DISPATCH();
+    TARGET(LOAD_NULL) PUSH(_py_null); DISPATCH();
     /*****************************************/
     TARGET(LOAD_FAST) {
         heap._auto_collect();
         PyObject* val = frame->_locals[byte.arg];
         if(val == nullptr) vm->NameError(co->varnames[byte.arg]);
-        frame->push(val);
+        PUSH(val);
     } DISPATCH();
     TARGET(LOAD_NAME) {
         heap._auto_collect();
         StrName name(byte.arg);
         PyObject* val;
         val = frame->_locals.try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         val = frame->_closure.try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         val = frame->f_globals().try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         val = vm->builtins->attr().try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         vm->NameError(name);
     } DISPATCH();
     TARGET(LOAD_NONLOCAL) {
@@ -113,59 +125,59 @@ __NEXT_STEP:;
         StrName name(byte.arg);
         PyObject* val;
         val = frame->_closure.try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         val = frame->f_globals().try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         val = vm->builtins->attr().try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         vm->NameError(name);
     } DISPATCH();
     TARGET(LOAD_GLOBAL) {
         heap._auto_collect();
         StrName name(byte.arg);
         PyObject* val = frame->f_globals().try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         val = vm->builtins->attr().try_get(name);
-        if(val != nullptr) { frame->push(val); DISPATCH(); }
+        if(val != nullptr) { PUSH(val); DISPATCH(); }
         vm->NameError(name);
     } DISPATCH();
     TARGET(LOAD_ATTR) {
-        PyObject* a = frame->top();
+        PyObject* a = TOP();
         StrName name(byte.arg);
-        frame->top() = getattr(a, name);
+        TOP() = getattr(a, name);
     } DISPATCH();
     TARGET(LOAD_METHOD) {
-        PyObject* a = frame->top();
+        PyObject* a = TOP();
         StrName name(byte.arg);
         PyObject* self;
-        frame->top() = get_unbound_method(a, name, &self, true, true);
-        frame->push(self);
+        TOP() = get_unbound_method(a, name, &self, true, true);
+        PUSH(self);
     } DISPATCH();
     TARGET(LOAD_SUBSCR) {
         Args args(2);
-        args[1] = frame->popx();    // b
-        args[0] = frame->top();     // a
-        frame->top() = fast_call(__getitem__, std::move(args));
+        args[1] = POPX();    // b
+        args[0] = TOP();     // a
+        TOP() = fast_call(__getitem__, std::move(args));
     } DISPATCH();
     TARGET(STORE_FAST)
-        frame->_locals[byte.arg] = frame->popx();
+        frame->_locals[byte.arg] = POPX();
         DISPATCH();
     TARGET(STORE_GLOBAL) {
         StrName name(byte.arg);
-        frame->f_globals().set(name, frame->popx());
+        frame->f_globals().set(name, POPX());
     } DISPATCH();
     TARGET(STORE_ATTR) {
         StrName name(byte.arg);
-        PyObject* a = frame->top();
-        PyObject* val = frame->top_1();
+        PyObject* a = TOP();
+        PyObject* val = SECOND();
         setattr(a, name, val);
-        frame->pop_n(2);
+        STACK_SHRINK(2);
     } DISPATCH();
     TARGET(STORE_SUBSCR) {
         Args args(3);
-        args[1] = frame->popx();    // b
-        args[0] = frame->popx();    // a
-        args[2] = frame->popx();    // val
+        args[1] = POPX();    // b
+        args[0] = POPX();    // a
+        args[2] = POPX();    // val
         fast_call(__setitem__, std::move(args));
     } DISPATCH();
     TARGET(DELETE_FAST) {
@@ -182,70 +194,76 @@ __NEXT_STEP:;
         }
     } DISPATCH();
     TARGET(DELETE_ATTR) {
-        PyObject* a = frame->popx();
+        PyObject* a = POPX();
         StrName name(byte.arg);
         if(!a->is_attr_valid()) TypeError("cannot delete attribute");
         if(!a->attr().contains(name)) AttributeError(a, name);
         a->attr().erase(name);
     } DISPATCH();
     TARGET(DELETE_SUBSCR) {
-        PyObject* b = frame->popx();
-        PyObject* a = frame->popx();
+        PyObject* b = POPX();
+        PyObject* a = POPX();
         fast_call(__delitem__, Args{a, b});
     } DISPATCH();
     /*****************************************/
-    TARGET(BUILD_LIST)
-        frame->push(VAR(frame->popx_n_reversed(byte.arg).to_list()));
-        DISPATCH();
+    TARGET(BUILD_LIST) {
+        PyObject* obj = VAR(STACK_VIEW(byte.arg).to_list());
+        STACK_SHRINK(byte.arg);
+        PUSH(obj);
+    } DISPATCH();
     TARGET(BUILD_DICT) {
-        PyObject* t = VAR(frame->popx_n_reversed(byte.arg));
+        PyObject* t = VAR(STACK_VIEW(byte.arg).to_tuple());
+        STACK_SHRINK(byte.arg);
         PyObject* obj = call(builtins->attr(m_dict), Args{t});
-        frame->push(obj);
+        PUSH(obj);
     } DISPATCH();
     TARGET(BUILD_SET) {
-        PyObject* t = VAR(frame->popx_n_reversed(byte.arg));
+        PyObject* t = VAR(STACK_VIEW(byte.arg).to_tuple());
+        STACK_SHRINK(byte.arg);
         PyObject* obj = call(builtins->attr(m_set), Args{t});
-        frame->push(obj);
+        PUSH(obj);
     } DISPATCH();
     TARGET(BUILD_SLICE) {
-        PyObject* step = frame->popx();
-        PyObject* stop = frame->popx();
-        PyObject* start = frame->popx();
+        PyObject* step = POPX();
+        PyObject* stop = POPX();
+        PyObject* start = POPX();
         Slice s;
         if(start != None) s.start = CAST(int, start);
         if(stop != None) s.stop = CAST(int, stop);
         if(step != None) s.step = CAST(int, step);
-        frame->push(VAR(s));
+        PUSH(VAR(s));
     } DISPATCH();
     TARGET(BUILD_TUPLE) {
-        Tuple items = frame->popx_n_reversed(byte.arg);
-        frame->push(VAR(std::move(items)));
+        PyObject* obj = VAR(STACK_VIEW(byte.arg).to_tuple());
+        STACK_SHRINK(byte.arg);
+        PUSH(obj);
     } DISPATCH();
     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()));
+        std::stringstream ss;
+        auto view = STACK_VIEW(byte.arg);
+        for(PyObject* obj : view) ss << CAST(Str&, asStr(obj));
+        STACK_SHRINK(byte.arg);
+        PUSH(VAR(ss.str()));
     } DISPATCH();
     /*****************************************/
     TARGET(BINARY_OP) {
         Args args(2);
-        args[1] = frame->popx();    // lhs
-        args[0] = frame->top();     // rhs
-        frame->top() = fast_call(BINARY_SPECIAL_METHODS[byte.arg], std::move(args));
+        args[1] = POPX();    // lhs
+        args[0] = TOP();     // rhs
+        TOP() = fast_call(BINARY_SPECIAL_METHODS[byte.arg], std::move(args));
     } DISPATCH();
 
 #define INT_BINARY_OP(op, func) \
-        if(is_both_int(frame->top(), frame->top_1())){      \
-            i64 b = _CAST(i64, frame->top());               \
-            i64 a = _CAST(i64, frame->top_1());             \
-            frame->pop();                                   \
-            frame->top() = VAR(a op b);                     \
-        }else{                                              \
-            Args args(2);                                   \
-            args[1] = frame->popx();                        \
-            args[0] = frame->top();                         \
-            frame->top() = fast_call(func, std::move(args));\
+        if(is_both_int(TOP(), SECOND())){               \
+            i64 b = _CAST(i64, TOP());                  \
+            i64 a = _CAST(i64, SECOND());               \
+            POP();                                      \
+            TOP() = VAR(a op b);                        \
+        }else{                                          \
+            Args args(2);                               \
+            args[1] = POPX();                    \
+            args[0] = TOP();                            \
+            TOP() = fast_call(func, std::move(args));   \
         }
 
     TARGET(BINARY_ADD)
@@ -298,33 +316,33 @@ __NEXT_STEP:;
         DISPATCH()
 #undef INT_BINARY_OP
     TARGET(IS_OP) {
-        PyObject* rhs = frame->popx();
-        PyObject* lhs = frame->top();
+        PyObject* rhs = POPX();
+        PyObject* lhs = TOP();
         bool ret_c = lhs == rhs;
         if(byte.arg == 1) ret_c = !ret_c;
-        frame->top() = VAR(ret_c);
+        TOP() = VAR(ret_c);
     } DISPATCH();
     TARGET(CONTAINS_OP) {
         Args args(2);
-        args[0] = frame->popx();
-        args[1] = frame->top();
+        args[0] = POPX();
+        args[1] = TOP();
         PyObject* ret = fast_call(__contains__, std::move(args));
         bool ret_c = CAST(bool, ret);
         if(byte.arg == 1) ret_c = !ret_c;
-        frame->top() = VAR(ret_c);
+        TOP() = VAR(ret_c);
     } DISPATCH();
     /*****************************************/
     TARGET(JUMP_ABSOLUTE) frame->jump_abs(byte.arg); DISPATCH();
     TARGET(POP_JUMP_IF_FALSE)
-        if(!asBool(frame->popx())) frame->jump_abs(byte.arg);
+        if(!asBool(POPX())) frame->jump_abs(byte.arg);
         DISPATCH();
     TARGET(JUMP_IF_TRUE_OR_POP)
-        if(asBool(frame->top()) == true) frame->jump_abs(byte.arg);
-        else frame->pop();
+        if(asBool(TOP()) == true) frame->jump_abs(byte.arg);
+        else POP();
         DISPATCH();
     TARGET(JUMP_IF_FALSE_OR_POP)
-        if(asBool(frame->top()) == false) frame->jump_abs(byte.arg);
-        else frame->pop();
+        if(asBool(TOP()) == false) frame->jump_abs(byte.arg);
+        else POP();
         DISPATCH();
     TARGET(LOOP_CONTINUE) {
         int target = co_blocks[byte.block].start;
@@ -344,92 +362,91 @@ __NEXT_STEP:;
     TARGET(CALL)
     TARGET(CALL_UNPACK) {
         int ARGC = byte.arg;
-        PyObject* callable = frame->top_n(ARGC+1);
-        bool method_call = frame->top_n(ARGC) != _py_null;
+        PyObject* callable = PEEK(ARGC+2);
+        bool method_call = PEEK(ARGC+1) != _py_null;
 
         // fast path
         if(byte.op==OP_CALL && is_type(callable, tp_function)){
-            ArgsView args = frame->top_n_view(ARGC + int(method_call));
-            PyObject* ret = _py_call(callable, args, {});
-            frame->pop_n(ARGC + 2);
+            PyObject* ret = _py_call(callable, STACK_VIEW(ARGC + int(method_call)), {});
+            STACK_SHRINK(ARGC + 2);
             if(ret == nullptr) { DISPATCH_OP_CALL(); }
-            else frame->push(ret);      // a generator
+            else PUSH(ret);      // a generator
             DISPATCH();
         }
-
-        Args args = frame->popx_n_reversed(ARGC + int(method_call));
-        if(!method_call) frame->pop();
-
+        Args args = STACK_VIEW(ARGC + int(method_call)).to_tuple();
         if(byte.op == OP_CALL_UNPACK) unpack_args(args);
-        frame->pop();
         PyObject* ret = call(callable, std::move(args), no_arg(), true);
+        STACK_SHRINK(ARGC + 2);
         if(ret == _py_op_call) { DISPATCH_OP_CALL(); }
-        frame->push(ret);
+        PUSH(ret);
     } DISPATCH();
     TARGET(CALL_KWARGS)
     TARGET(CALL_KWARGS_UNPACK) {
+        // TODO: poor performance, refactor needed
         int ARGC = byte.arg & 0xFFFF;
         int KWARGC = (byte.arg >> 16) & 0xFFFF;
-        Args kwargs = frame->popx_n_reversed(KWARGC*2);
+        Args kwargs = STACK_VIEW(KWARGC*2).to_tuple();
+        STACK_SHRINK(KWARGC*2);
 
-        bool method_call = frame->top_n(ARGC) != _py_null;
+        bool method_call = PEEK(ARGC+1) != _py_null;
         if(method_call) ARGC++;         // add self into args
-        Args args = frame->popx_n_reversed(ARGC);
-        if(!method_call) frame->pop();
+        Args args = STACK_VIEW(ARGC).to_tuple();
+        STACK_SHRINK(ARGC);
+        if(!method_call) POP();
 
         if(byte.op == OP_CALL_KWARGS_UNPACK) unpack_args(args);
-        PyObject* callable = frame->popx();
+        PyObject* callable = POPX();
         PyObject* ret = call(callable, std::move(args), kwargs, true);
         if(ret == _py_op_call) { DISPATCH_OP_CALL(); }
-        frame->push(ret);
+        PUSH(ret);
     } DISPATCH();
     TARGET(RETURN_VALUE) {
-        PyObject* __ret = frame->popx();
+        PyObject* __ret = POPX();
         if(frame.index == base_id){       // [ frameBase<- ]
             callstack.pop();
             return __ret;
         }else{
             callstack.pop();
             frame = top_frame();
-            frame->push(__ret);
+            PUSH(__ret);
             goto __NEXT_FRAME;
         }
     }
     TARGET(YIELD_VALUE) return _py_op_yield;
     /*****************************************/
     TARGET(LIST_APPEND) {
-        PyObject* obj = frame->popx();
-        List& list = CAST(List&, frame->top_1());
+        PyObject* obj = POPX();
+        List& list = CAST(List&, SECOND());
         list.push_back(obj);
     } DISPATCH();
     TARGET(DICT_ADD) {
-        PyObject* kv = frame->popx();
+        PyObject* kv = POPX();
         Tuple& t = CAST(Tuple& ,kv);
-        fast_call(__setitem__, Args{frame->top_1(), t[0], t[1]});
+        fast_call(__setitem__, Args{SECOND(), t[0], t[1]});
     } DISPATCH();
     TARGET(SET_ADD) {
-        PyObject* obj = frame->popx();
-        fast_call(m_add, Args{frame->top_1(), obj});
+        PyObject* obj = POPX();
+        fast_call(m_add, Args{SECOND(), obj});
     } DISPATCH();
     /*****************************************/
     TARGET(UNARY_NEGATIVE)
-        frame->top() = num_negated(frame->top());
+        TOP() = num_negated(TOP());
         DISPATCH();
     TARGET(UNARY_NOT)
-        frame->top() = VAR(!asBool(frame->top()));
+        TOP() = VAR(!asBool(TOP()));
         DISPATCH();
     TARGET(UNARY_STAR)
-        frame->top() = VAR(StarWrapper(frame->top()));
+        TOP() = VAR(StarWrapper(TOP()));
         DISPATCH();
     /*****************************************/
     TARGET(GET_ITER)
-        frame->top() = asIter(frame->top());
+        TOP() = asIter(TOP());
         DISPATCH();
     TARGET(FOR_ITER) {
-        BaseIter* it = PyIter_AS_C(frame->top());
+        BaseIter* it = PyIter_AS_C(TOP());
         PyObject* obj = it->next();
         if(obj != nullptr){
-            frame->push(obj);
+            PUSH(obj);
         }else{
             int target = co_blocks[byte.block].end;
             frame->jump_abs_break(target);
@@ -454,13 +471,13 @@ __NEXT_STEP:;
             PyObject* new_mod = new_module(name);
             _exec(code, new_mod);
             new_mod->attr()._try_perfect_rehash();
-            frame->push(new_mod);
+            PUSH(new_mod);
         }else{
-            frame->push(ext_mod);
+            PUSH(ext_mod);
         }
     } DISPATCH();
     TARGET(IMPORT_STAR) {
-        PyObject* obj = frame->popx();
+        PyObject* obj = POPX();
         for(auto& [name, value]: obj->attr().items()){
             std::string_view s = name.sv();
             if(s.empty() || s[0] == '_') continue;
@@ -472,12 +489,12 @@ __NEXT_STEP:;
     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());
+        PyObject* obj = asIter(POPX());
         BaseIter* iter = PyIter_AS_C(obj);
         for(int i=0; i<byte.arg; i++){
             PyObject* item = iter->next();
             if(item == nullptr) ValueError("not enough values to unpack");
-            frame->push(item);
+            PUSH(item);
         }
         // handle extra items
         if(byte.op == OP_UNPACK_EX){
@@ -487,7 +504,7 @@ __NEXT_STEP:;
                 if(item == nullptr) break;
                 extras.push_back(item);
             }
-            frame->push(VAR(extras));
+            PUSH(VAR(extras));
         }else{
             if(iter->next() != nullptr) ValueError("too many values to unpack");
         }
@@ -495,20 +512,20 @@ __NEXT_STEP:;
     /*****************************************/
     TARGET(BEGIN_CLASS) {
         StrName name(byte.arg);
-        PyObject* super_cls = frame->popx();
+        PyObject* super_cls = POPX();
         if(super_cls == None) super_cls = _t(tp_object);
         check_type(super_cls, tp_type);
         PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, super_cls));
-        frame->push(cls);
+        PUSH(cls);
     } DISPATCH();
     TARGET(END_CLASS) {
-        PyObject* cls = frame->popx();
+        PyObject* cls = POPX();
         cls->attr()._try_perfect_rehash();
     }; DISPATCH();
     TARGET(STORE_CLASS_ATTR) {
         StrName name(byte.arg);
-        PyObject* obj = frame->popx();
-        PyObject* cls = frame->top();
+        PyObject* obj = POPX();
+        PyObject* cls = TOP();
         cls->attr().set(name, obj);
     } DISPATCH();
     /*****************************************/
@@ -517,7 +534,7 @@ __NEXT_STEP:;
     // TARGET(WITH_EXIT) call(frame->pop_value(this), __exit__, no_arg()); DISPATCH();
     /*****************************************/
     TARGET(ASSERT) {
-        PyObject* obj = frame->top();
+        PyObject* obj = TOP();
         Str msg;
         if(is_type(obj, tp_tuple)){
             auto& t = CAST(Tuple&, obj);
@@ -526,16 +543,16 @@ __NEXT_STEP:;
             msg = CAST(Str&, asStr(t[1]));
         }
         bool ok = asBool(obj);
-        frame->pop();
+        POP();
         if(!ok) _error("AssertionError", msg);
     } DISPATCH();
     TARGET(EXCEPTION_MATCH) {
-        const auto& e = CAST(Exception&, frame->top());
+        const auto& e = CAST(Exception&, TOP());
         StrName name(byte.arg);
-        frame->push(VAR(e.match_type(name)));
+        PUSH(VAR(e.match_type(name)));
     } DISPATCH();
     TARGET(RAISE) {
-        PyObject* obj = frame->popx();
+        PyObject* obj = POPX();
         Str msg = obj == None ? "" : CAST(Str, asStr(obj));
         _error(StrName(byte.arg), msg);
     } DISPATCH();
@@ -558,7 +575,7 @@ __NEXT_STEP:;
         }catch(HandledException& e){
             continue;
         }catch(UnhandledException& e){
-            PyObject* obj = frame->popx();
+            PyObject* obj = POPX();
             Exception& _e = CAST(Exception&, obj);
             _e.st_push(frame->snapshot());
             callstack.pop();
@@ -569,7 +586,7 @@ __NEXT_STEP:;
                 throw _e;
             }
             frame = top_frame();
-            frame->push(obj);
+            PUSH(obj);
             if(frame.index < base_id) throw ToBeRaisedException();
             need_raise = true;
         }catch(ToBeRaisedException& e){

+ 60 - 80
src/frame.h

@@ -6,23 +6,26 @@
 
 namespace pkpy{
 
-using ValueStack = pod_vector<PyObject*>;
-
 struct FastLocals{
     NameDictInt_ varnames_inv;
     PyObject** a;
 
-    int size() const{ return varnames_inv->size(); }
+    int size() const{
+        return varnames_inv->size();
+    }
 
     PyObject*& operator[](int i){ return a[i]; }
     PyObject* operator[](int i) const { return a[i]; }
 
+    FastLocals(): varnames_inv(nullptr), a(nullptr) {}
+    FastLocals(std::nullptr_t): varnames_inv(nullptr), a(nullptr) {}
+
     FastLocals(const CodeObject* co): varnames_inv(co->varnames_inv){
-        size_t size = co->varnames.size() * sizeof(void*);
+        size_t size = this->size() * sizeof(void*);
         int* counter = (int*)pool128.alloc(sizeof(int) + size);
         *counter = 1;
         a = (PyObject**)(counter + 1);
-        memset(a, 0, size);
+        memset(a, 0, this->size() * sizeof(void*));
     }
 
     PyObject* try_get(StrName name){
@@ -40,9 +43,6 @@ struct FastLocals{
         return true;
     }
 
-    FastLocals(): varnames_inv(nullptr), a(nullptr) {}
-    FastLocals(std::nullptr_t): varnames_inv(nullptr), a(nullptr) {}
-
     FastLocals(const FastLocals& other){
         varnames_inv = other.varnames_inv;
         a = other.a;
@@ -112,8 +112,47 @@ template<> inline void gc_mark<Function>(Function& t){
     t._closure._gc_mark();
 }
 
+struct ValueStack {
+    PyObject** _begin;
+    PyObject** _sp;
+
+    ValueStack(int n=16): _begin((PyObject**)pool128.alloc(n * sizeof(void*))), _sp(_begin) { }
+
+    PyObject*& top(){ return _sp[-1]; }
+    PyObject* top() const { return _sp[-1]; }
+    PyObject*& second(){ return _sp[-2]; }
+    PyObject* second() const { return _sp[-2]; }
+    PyObject*& peek(int n){ return _sp[-n]; }
+    PyObject* peek(int n) const { return _sp[-n]; }
+    void push(PyObject* v){ *_sp++ = v; }
+    void pop(){ --_sp; }
+    PyObject* popx(){ return *--_sp; }
+    ArgsView view(int n){ return ArgsView(_sp-n, _sp); }
+    void shrink(int n){ _sp -= n; }
+    int size() const { return _sp - _begin; }
+    bool empty() const { return _sp == _begin; }
+    PyObject** begin() const { return _begin; }
+    PyObject** end() const { return _sp; }
+    void resize(int n) { _sp = _begin + n; }
+
+    ValueStack(ValueStack&& other) noexcept{
+        _begin = other._begin;
+        _sp = other._sp;
+        other._begin = nullptr;
+    }
+
+    ValueStack& operator=(ValueStack&& other) noexcept{
+        if(_begin != nullptr) pool128.dealloc(_begin);
+        _begin = other._begin;
+        _sp = other._sp;
+        other._begin = nullptr;
+        return *this;
+    }
+
+    ~ValueStack(){ if(_begin!=nullptr) pool128.dealloc(_begin); }
+};
+
 struct Frame {
-    ValueStack _data;
     int _ip = -1;
     int _next_ip = 0;
     const CodeObject* co;
@@ -121,6 +160,7 @@ struct Frame {
 
     FastLocals _locals;
     FastLocals _closure;
+    ValueStack _s;
 
     NameDict& f_globals() noexcept { return _module->attr(); }
 
@@ -150,60 +190,15 @@ struct Frame {
 
     std::string stack_info(){
         std::stringstream ss;
-        ss << " [";
-        for(int i=0; i<_data.size(); i++){
-            ss << (i64)_data[i];
-            if(i != _data.size()-1) ss << ", ";
+        ss << this << ": [";
+        for(PyObject** t=_s.begin(); t<_s.end(); t++){
+            ss << *t;
+            if(t != _s.end()-1) ss << ", ";
         }
         ss << "]";
         return ss.str();
     }
 
-    void pop(){
-#if DEBUG_EXTRA_CHECK
-        if(_data.empty()) throw std::runtime_error("_data.empty() is true");
-#endif
-        _data.pop_back();
-    }
-
-    PyObject* popx(){
-#if DEBUG_EXTRA_CHECK
-        if(_data.empty()) throw std::runtime_error("_data.empty() is true");
-#endif
-        PyObject* ret = _data.back();
-        _data.pop_back();
-        return ret;
-    }
-
-    PyObject*& top(){
-#if DEBUG_EXTRA_CHECK
-        if(_data.empty()) throw std::runtime_error("_data.empty() is true");
-#endif
-        return _data.back();
-    }
-
-    PyObject*& top_1(){
-#if DEBUG_EXTRA_CHECK
-        if(_data.size() < 2) throw std::runtime_error("_data.size() < 2");
-#endif
-        return _data[_data.size()-2];
-    }
-
-    PyObject*& top_n(int n){
-        n += 1;
-#if DEBUG_EXTRA_CHECK
-        if(_data.size() < n) throw std::runtime_error("_data.size() < n");
-#endif
-        return _data[_data.size()-n];
-    }
-
-    void push(PyObject* obj){
-#if DEBUG_EXTRA_CHECK
-        if(obj == nullptr) throw std::runtime_error("obj == nullptr");
-#endif
-        _data.push_back(obj);
-    }
-
     void jump_abs(int i){ _next_ip = i; }
     void jump_rel(int i){ _next_ip += i; }
 
@@ -215,19 +210,18 @@ struct Frame {
             block = co->blocks[block].parent;
         }
         if(block < 0) return false;
-        PyObject* obj = popx();         // pop exception object
+        PyObject* obj = _s.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
+        if(_s.size() < stack_size) throw std::runtime_error("invalid stack size");
+        _s.resize(stack_size);             // rollback the stack   
+        _s.push(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();
+        if(co->blocks[i].type == FOR_LOOP) _s.pop();
         return co->blocks[i].parent;
     }
 
@@ -244,24 +238,10 @@ struct Frame {
         }
     }
 
-    Args popx_n_reversed(int n){
-        Args v(n);
-        for(int i=n-1; i>=0; i--) v[i] = popx();
-        return v;
-    }
-
-    void pop_n(int n){
-        _data.pop_back_n(n);
-    }
-
-    ArgsView top_n_view(int n){
-        return ArgsView(_data.end()-n, _data.end());
-    }
-
     void _gc_mark() const {
         // do return if this frame has been moved
-        if(_data._data == nullptr) return;
-        for(PyObject* obj : _data) OBJ_MARK(obj);
+        if(!_locals.is_valid()) return;
+        for(PyObject* obj: _s) OBJ_MARK(obj);
         OBJ_MARK(_module);
         _locals._gc_mark();
         _closure._gc_mark();

+ 1 - 2
src/iter.h

@@ -69,8 +69,7 @@ inline PyObject* Generator::next(){
         frame = std::move(vm->callstack.top());
         vm->callstack.pop();
         state = 1;
-        PyObject** _sp = frame._sp;
-        return POPX();
+        return frame._s.popx();
     }else{
         state = 2;
         return nullptr;

+ 5 - 2
src/obj.h

@@ -143,7 +143,7 @@ struct Py_ final: PyObject {
 
 Str obj_type_name(VM* vm, Type type);
 
-#if DEBUG_NO_BUILTIN_MODULES || DEBUG_NO_NAME_GETTER
+#if DEBUG_NO_BUILTIN_MODULES
 #define OBJ_NAME(obj) Str("<?>")
 #else
 #define OBJ_NAME(obj) OBJ_GET(Str, vm->getattr(obj, __name__))
@@ -171,7 +171,10 @@ inline bool is_type(PyObject* obj, Type type) {
     }                                                                       \
     static PyObject* register_class(VM* vm, PyObject* mod) {                \
         PyObject* type = vm->new_type_object(mod, #name, vm->tp_object);    \
-        if(OBJ_NAME(mod) != #mod) FATAL_ERROR();                            \
+        if(OBJ_NAME(mod) != #mod) {                                         \
+            auto msg = fmt("register_class() failed: ", OBJ_NAME(mod), " != ", #mod); \
+            throw std::runtime_error(msg);                                  \
+        }                                                                   \
         T::_register(vm, mod, type);                                        \
         type->attr()._try_perfect_rehash();                                 \
         return type;                                                        \

+ 1 - 0
src/vector.h

@@ -133,6 +133,7 @@ public:
 	T& top(){ return vec.back(); }
 	const T& top() const { return vec.back(); }
 	T popx(){ T t = std::move(vec.back()); vec.pop_back(); return t; }
+    void reserve(int n){ vec.reserve(n); }
 	Container& data() { return vec; }
 };
 

+ 3 - 2
src/vm.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include "codeobject.h"
 #include "common.h"
 #include "frame.h"
 #include "error.h"
@@ -897,8 +898,8 @@ inline void VM::_error(Exception e){
         e.is_re = false;
         throw e;
     }
-    PyObject** _sp = top_frame()->_sp;
-    PUSH(VAR(e));
+    Frame* frame = &callstack.top();
+    frame->_s.push(VAR(e));
     _raise();
 }