diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 8b8f032b..1fcf2cb0 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -233,12 +233,12 @@ struct PyVar final{ bool operator!=(std::nullptr_t) const { return (bool)type; } PyObject* get() const { - PK_DEBUG_ASSERT(!is_sso) + PK_DEBUG_ASSERT(is_ptr) return reinterpret_cast(_1); } PyObject* operator->() const { - PK_DEBUG_ASSERT(!is_sso) + PK_DEBUG_ASSERT(is_ptr) return reinterpret_cast(_1); } diff --git a/include/pocketpy/iter.h b/include/pocketpy/iter.h index c75e6ed0..8d80a88f 100644 --- a/include/pocketpy/iter.h +++ b/include/pocketpy/iter.h @@ -51,9 +51,9 @@ struct Generator{ for(PyVar obj: buffer) s_backup.push_back(obj); } - void _gc_mark(VM* vm) const{ + void _gc_mark(VM* vm) { frame._gc_mark(vm); - for(PyVar obj: s_backup) PK_OBJ_MARK(obj); + vm->__stack_gc_mark(s_backup.begin(), s_backup.end()); } PyVar next(VM* vm); diff --git a/include/pocketpy/obj.h b/include/pocketpy/obj.h index 54383c68..c9414acc 100644 --- a/include/pocketpy/obj.h +++ b/include/pocketpy/obj.h @@ -50,6 +50,14 @@ struct Range { i64 step = 1; }; +struct StackMemory{ + int count; + StackMemory(int count) : count(count) {} +}; + +template<> +inline bool constexpr is_sso_v = true; + struct StarWrapper{ int level; // either 1 or 2 PyVar obj; @@ -155,8 +163,9 @@ static_assert(sizeof(PyObject) <= PyObject::FIXED_SIZE); template inline constexpr int py_sizeof = PyObject::FIXED_SIZE + sizeof(T); -const int kTpIntIndex = 3; -const int kTpFloatIndex = 4; +inline const int kTpIntIndex = 3; +inline const int kTpFloatIndex = 4; +inline const int kTpStackMemoryIndex = 27; inline bool is_tagged(PyVar p) noexcept { return !p.is_ptr; } inline bool is_float(PyVar p) noexcept { return p.type.index == kTpFloatIndex; } @@ -187,14 +196,14 @@ obj_get_t PyVar::obj_get(){ if constexpr(is_sso_v){ return as(); }else{ - PK_DEBUG_ASSERT(!is_sso) + PK_DEBUG_ASSERT(is_ptr) void* v = ((PyObject*)_1)->_value_ptr(); return *reinterpret_cast(v); } } #define PK_OBJ_GET(T, obj) (obj).obj_get() -#define PK_OBJ_MARK(obj) if((obj).is_ptr) vm->__obj_gc_mark(obj.get()); +#define PK_OBJ_MARK(obj) if((obj).is_ptr) vm->__obj_gc_mark((obj).get()); #define VAR(x) py_var(vm, x) #define CAST(T, x) py_cast(vm, x) diff --git a/include/pocketpy/opcodes.h b/include/pocketpy/opcodes.h index e9f74c3d..d820a164 100644 --- a/include/pocketpy/opcodes.h +++ b/include/pocketpy/opcodes.h @@ -117,6 +117,7 @@ OPCODE(UNARY_STAR) OPCODE(UNARY_INVERT) /**************************/ OPCODE(GET_ITER) +OPCODE(GET_ITER_NEW) OPCODE(FOR_ITER) OPCODE(FOR_ITER_STORE_FAST) OPCODE(FOR_ITER_STORE_GLOBAL) diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index e681fb7c..d3931e90 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -82,7 +82,8 @@ struct PyTypeInfo{ i64 (*m__hash__)(VM* vm, PyVar) = nullptr; i64 (*m__len__)(VM* vm, PyVar) = nullptr; PyVar (*m__iter__)(VM* vm, PyVar) = nullptr; - unsigned (*m__next__)(VM* vm, PyVar) = nullptr; + void (*op__iter__)(VM* vm, PyVar) = nullptr; + unsigned (*op__next__)(VM* vm, PyVar) = nullptr; PyVar (*m__neg__)(VM* vm, PyVar) = nullptr; PyVar (*m__invert__)(VM* vm, PyVar) = nullptr; @@ -207,11 +208,12 @@ public: static constexpr Type tp_super=Type(15), tp_exception=Type(16), tp_bytes=Type(17), tp_mappingproxy=Type(18); static constexpr Type tp_dict=Type(19), tp_property=Type(20), tp_star_wrapper=Type(21); static constexpr Type tp_staticmethod=Type(22), tp_classmethod=Type(23); - static constexpr Type tp_none=Type(24), tp_not_implemented=Type(25), tp_ellipsis=Type(26); + static constexpr Type tp_none_type=Type(24), tp_not_implemented=Type(25), tp_ellipsis=Type(26); + static constexpr Type tp_stack_memory=Type(kTpStackMemoryIndex); static constexpr PyVar True{const_sso_var(), tp_bool, 1}; static constexpr PyVar False{const_sso_var(), tp_bool, 0}; - static constexpr PyVar None{const_sso_var(), tp_none, 0}; + static constexpr PyVar None{const_sso_var(), tp_none_type, 0}; static constexpr PyVar NotImplemented{const_sso_var(), tp_not_implemented, 0}; static constexpr PyVar Ellipsis{const_sso_var(), tp_ellipsis, 0}; @@ -440,6 +442,14 @@ public: if constexpr(is_sso_v) return PyVar(type, T(std::forward(args)...)); else return heap.gcnew(type, std::forward(args)...); } + + template + void new_stack_object(Type type, Args&&... args){ + static_assert(std::is_same_v>); + PyObject* p = new(__stack_alloc(py_sizeof)) PyObject(type); + p->placement_new(std::forward(args)...); + vm->s_data.emplace(p->type, p); + } #endif template @@ -488,6 +498,8 @@ public: PyVar __minmax_reduce(bool (VM::*op)(PyVar, PyVar), PyVar args, PyVar key); bool __py_bool_non_trivial(PyVar); void __obj_gc_mark(PyObject*); + void __stack_gc_mark(PyVar* begin, PyVar* end); + void* __stack_alloc(int size); }; @@ -514,6 +526,7 @@ template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_star_wrapper; } template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_staticmethod; } template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_classmethod; } +template<> constexpr Type _find_type_in_const_cxx_typeid_map(){ return VM::tp_stack_memory; } template PyVar py_var(VM* vm, __T&& value){ diff --git a/src/array2d.cpp b/src/array2d.cpp index c04b8529..cff9919a 100644 --- a/src/array2d.cpp +++ b/src/array2d.cpp @@ -366,8 +366,10 @@ struct Array2dIter{ PK_ALWAYS_PASS_BY_POINTER(Array2dIter) PyVar ref; + Array2d* a; int i; - Array2dIter(PyVar ref) : ref(ref), i(0) {} + + Array2dIter(PyVar ref, Array2d* a): ref(ref), a(a), i(0){} void _gc_mark(VM* vm) const{ PK_OBJ_MARK(ref); } @@ -375,12 +377,11 @@ struct Array2dIter{ vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM* vm, PyVar _0) { return _0; }); vm->bind__next__(PK_OBJ_GET(Type, type), [](VM* vm, PyVar _0) -> unsigned{ Array2dIter& self = PK_OBJ_GET(Array2dIter, _0); - Array2d& a = PK_OBJ_GET(Array2d, self.ref); - if(self.i == a.numel) return 0; - std::div_t res = std::div(self.i, a.n_cols); + if(self.i == self.a->numel) return 0; + std::div_t res = std::div(self.i, self.a->n_cols); vm->s_data.emplace(VM::tp_int, res.rem); vm->s_data.emplace(VM::tp_int, res.quot); - vm->s_data.push(a.data[self.i++]); + vm->s_data.push(self.a->data[self.i++]); return 3; }); } @@ -392,9 +393,13 @@ void add_module_array2d(VM* vm){ vm->register_user_class(mod, "array2d", VM::tp_object, true); vm->register_user_class(mod, "_array2d_iter"); - vm->bind__iter__(vm->_tp_user(), [](VM* vm, PyVar _0){ - return vm->new_user_object(_0); + Type array2d_iter_t = vm->_tp_user(); + vm->bind__iter__(array2d_iter_t, [](VM* vm, PyVar _0){ + return vm->new_user_object(_0, &_0.obj_get()); }); + vm->_all_types[array2d_iter_t].op__iter__ = [](VM* vm, PyVar _0){ + vm->new_stack_object(vm->_tp_user(), _0, &_0.obj_get()); + }; } diff --git a/src/ceval.cpp b/src/ceval.cpp index 7ca27248..3467a542 100644 --- a/src/ceval.cpp +++ b/src/ceval.cpp @@ -806,6 +806,17 @@ __NEXT_STEP: case OP_GET_ITER: TOP() = py_iter(TOP()); DISPATCH() + case OP_GET_ITER_NEW: { + // This opcode always creates a temporary iterator object + const PyTypeInfo* _ti = _tp_info(TOP()); + if(_ti->op__iter__){ + PyVar _0 = POPX(); + _ti->op__iter__(this, _0); + }else{ + TOP() = py_iter(TOP()); + } + DISPATCH() + } case OP_FOR_ITER:{ PyVar _0 = py_next(TOP()); if(_0 == StopIteration){ @@ -849,8 +860,8 @@ __NEXT_STEP: case OP_FOR_ITER_UNPACK:{ PyVar _0 = TOP(); const PyTypeInfo* _ti = _tp_info(_0); - if(_ti->m__next__){ - unsigned n = _ti->m__next__(this, _0); + if(_ti->op__next__){ + unsigned n = _ti->op__next__(this, _0); if(n == 0){ // StopIteration int target = frame->prepare_loop_break(&s_data); diff --git a/src/compiler.cpp b/src/compiler.cpp index 282d7735..19dcf4c0 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -685,7 +685,7 @@ __EAT_DOTS_END: Expr_ vars = EXPR_VARS(); consume(TK("in")); EXPR_TUPLE(); ctx()->emit_expr(); - ctx()->emit_(OP_GET_ITER, BC_NOARG, BC_KEEPLINE); + ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, BC_KEEPLINE); CodeBlock* block = ctx()->enter_block(CodeBlockType::FOR_LOOP); int for_codei = ctx()->emit_(OP_FOR_ITER, ctx()->curr_iblock, BC_KEEPLINE); bool ok = vars->emit_store(ctx()); @@ -852,7 +852,7 @@ __EAT_DOTS_END: if (contexts.size() <= 1) SyntaxError("'yield from' outside function"); EXPR_TUPLE(); ctx()->emit_expr(); - ctx()->emit_(OP_GET_ITER, BC_NOARG, kw_line); + ctx()->emit_(OP_GET_ITER_NEW, BC_NOARG, kw_line); ctx()->enter_block(CodeBlockType::FOR_LOOP); ctx()->emit_(OP_FOR_ITER_YIELD_VALUE, BC_NOARG, kw_line); ctx()->emit_(OP_LOOP_CONTINUE, ctx()->get_loop(), kw_line); diff --git a/src/frame.cpp b/src/frame.cpp index 7a461ba1..2144c687 100644 --- a/src/frame.cpp +++ b/src/frame.cpp @@ -42,7 +42,16 @@ namespace pkpy{ int Frame::_exit_block(ValueStack* _s, int i){ auto type = co->blocks[i].type; - if(type==CodeBlockType::FOR_LOOP || type==CodeBlockType::CONTEXT_MANAGER) _s->pop(); + if(type==CodeBlockType::FOR_LOOP){ + _s->pop(); // pop the iterator + // pop possible stack memory slots + if(_s->top().type == kTpStackMemoryIndex){ + int count = _s->top().as().count; + _s->_sp -= (count + 2); + } + }else if(type==CodeBlockType::CONTEXT_MANAGER){ + _s->pop(); + } return co->blocks[i].parent; } diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index 31ad678f..600ced52 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -997,6 +997,11 @@ void __init_builtins(VM* _vm) { List& self = _CAST(List&, _0); return vm->new_user_object(_0.get(), self.begin(), self.end()); }); + _vm->_all_types[VM::tp_list].op__iter__ = [](VM* vm, PyVar _0){ + List& self = _CAST(List&, _0); + vm->new_stack_object(vm->_tp_user(), _0.get(), self.begin(), self.end()); + }; + _vm->bind__getitem__(VM::tp_list, PyArrayGetItem); _vm->bind__setitem__(VM::tp_list, [](VM* vm, PyVar _0, PyVar _1, PyVar _2){ List& self = _CAST(List&, _0); diff --git a/src/vm.cpp b/src/vm.cpp index a4471515..d4e7c79e 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -267,8 +267,8 @@ namespace pkpy{ } PyVar VM::_py_next(const PyTypeInfo* ti, PyVar obj){ - if(ti->m__next__){ - unsigned n = ti->m__next__(this, obj); + if(ti->op__next__){ + unsigned n = ti->op__next__(this, obj); return __pack_next_retval(n); } return call_method(obj, __next__); @@ -448,6 +448,29 @@ void VM::__obj_gc_mark(PyObject* obj){ } } +void VM::__stack_gc_mark(PyVar* begin, PyVar* end){ + for(PyVar* it=begin; it!=end; it++){ + if(it->is_ptr){ + __obj_gc_mark(it->get()); + }else{ + if(it->type == tp_stack_memory){ + // [sm:3, _0, _1, _2, sm:-3] + int count = it->as().count; + if(count > 0) it += count; + } + } + } +} + +void* VM::__stack_alloc(int size){ + int count = size / sizeof(PyVar) + 1; + s_data.emplace(tp_stack_memory, StackMemory(count)); + void* out = s_data._sp; + s_data._sp += count; + s_data.emplace(tp_stack_memory, StackMemory(-count)); + return out; +} + List VM::py_list(PyVar it){ auto _lock = heap.gc_scope_lock(); it = py_iter(it); @@ -804,28 +827,37 @@ void VM::__log_s_data(const char* title) { for(PyVar* p=s_data.begin(); p!=s_data.end(); p++){ ss << std::string(sp_bases[p], '|'); if(sp_bases[p] > 0) ss << " "; - PyVar obj = *p; - if(obj == nullptr) ss << "(nil)"; - else if(obj == PY_NULL) ss << "NULL"; - else if(is_int(obj)) ss << CAST(i64, obj); - else if(is_float(obj)) ss << CAST(f64, obj); - else if(is_type(obj, tp_str)) ss << CAST(Str, obj).escape(); - else if(obj == None) ss << "None"; - else if(obj == True) ss << "True"; - else if(obj == False) ss << "False"; - else if(is_type(obj, tp_function)){ - auto& f = CAST(Function&, obj); - ss << f.decl->code->name << "(...)"; - } else if(is_type(obj, tp_type)){ - Type t = PK_OBJ_GET(Type, obj); - ss << ""; - } else if(is_type(obj, tp_list)){ - auto& t = CAST(List&, obj); - ss << "list(size=" << t.size() << ")"; - } else if(is_type(obj, tp_tuple)){ - auto& t = CAST(Tuple&, obj); - ss << "tuple(size=" << t.size() << ")"; - } else ss << "(" << _type_name(this, obj.type) << ")"; + if(*p == PY_NULL) ss << "NULL"; + else{ + switch(p->type){ + case tp_none_type: ss << "None"; break; + case tp_int: ss << _CAST(i64, *p); break; + case tp_float: ss << _CAST(f64, *p); break; + case tp_bool: ss << ((*p == True) ? "True" : "False"); break; + case tp_str: ss << _CAST(Str, *p).escape(); break; + case tp_function: + ss << p->obj_get().decl->code->name << "()"; + break; + case tp_type: + ss << "obj_get()).escape() + ">"; + break; + case tp_list: + ss << "list(size=" << p->obj_get().size() << ")"; + break; + case tp_tuple: + ss << "tuple(size=" << p->obj_get().size() << ")"; + break; + case tp_stack_memory: { + int count = p->obj_get().count; + ss << "M[" << count << "]"; + if(count > 0) p += count; + break; + } + default: + ss << "(" << _type_name(this, p->type) << ")"; + break; + } + } ss << ", "; } std::string output = ss.str().str(); @@ -874,9 +906,10 @@ void VM::__init_builtin_types(){ validate(tp_staticmethod, new_type_object(nullptr, "staticmethod", tp_object, false)); validate(tp_classmethod, new_type_object(nullptr, "classmethod", tp_object, false)); - validate(tp_none, new_type_object(nullptr, "NoneType", tp_object, false)); + validate(tp_none_type, new_type_object(nullptr, "NoneType", tp_object, false)); validate(tp_not_implemented, new_type_object(nullptr, "NotImplementedType", tp_object, false)); validate(tp_ellipsis, new_type_object(nullptr, "ellipsis", tp_object, false)); + validate(tp_stack_memory, new_type_object(nullptr, "stack_memory", tp_object, false)); // SyntaxError and IndentationError must be created here PyVar SyntaxError = new_type_object(nullptr, "SyntaxError", tp_exception, true); @@ -1468,7 +1501,7 @@ PyVar VM::__pack_next_retval(unsigned n){ } void VM::bind__next__(Type type, unsigned (*f)(VM*, PyVar)){ - _all_types[type].m__next__ = f; + _all_types[type].op__next__ = f; bind_func(type, __next__, 1, [](VM* vm, ArgsView args){ int n = lambda_get_userdata(args.begin())(vm, args[0]); return vm->__pack_next_retval(n); @@ -1831,11 +1864,11 @@ void Frame::_gc_mark(VM* vm) const { void ManagedHeap::mark() { for(PyObject* obj: _no_gc) vm->__obj_gc_mark(obj); vm->callstack.apply([this](Frame& frame){ frame._gc_mark(vm); }); - for(PyVar obj: vm->s_data) PK_OBJ_MARK(obj); for(auto [_, co]: vm->__cached_codes) co->_gc_mark(vm); if(vm->__last_exception) PK_OBJ_MARK(vm->__last_exception); if(vm->__curr_class) PK_OBJ_MARK(vm->__curr_class); if(vm->__c.error != nullptr) PK_OBJ_MARK(vm->__c.error); + vm->__stack_gc_mark(vm->s_data.begin(), vm->s_data.end()); if(_gc_marker_ex) _gc_marker_ex(vm); }