diff --git a/include/pocketpy/frame.h b/include/pocketpy/frame.h
index b413bbec..a06cdd64 100644
--- a/include/pocketpy/frame.h
+++ b/include/pocketpy/frame.h
@@ -125,14 +125,43 @@ struct Frame {
}
};
-using CallstackContainer = small_vector_no_copy_and_move;
+struct LinkedFrame{
+ LinkedFrame* f_back;
+ Frame frame;
+ template
+ LinkedFrame(LinkedFrame* f_back, Args&&... args) : f_back(f_back), frame(std::forward(args)...) {}
+};
-struct FrameId{
- CallstackContainer* data;
- int index;
- FrameId(CallstackContainer* data, int index) : data(data), index(index) {}
- Frame* operator->() const { return &data->operator[](index); }
- Frame* get() const { return &data->operator[](index); }
+struct CallStack{
+ static_assert(sizeof(LinkedFrame) <= 64 && std::is_trivially_destructible_v);
+
+ LinkedFrame* _tail;
+ int _size;
+ CallStack(): _tail(nullptr), _size(0) {}
+
+ int size() const { return _size; }
+ bool empty() const { return _size == 0; }
+ void clear(){ while(!empty()) pop(); }
+
+ template
+ void emplace(Args&&... args){
+ _tail = new(pool64_alloc()) LinkedFrame(_tail, std::forward(args)...);
+ ++_size;
+ }
+
+ void pop(){
+ LinkedFrame* p = _tail;
+ _tail = p->f_back;
+ pool64_dealloc(p);
+ --_size;
+ }
+
+ Frame& top() const { return _tail->frame; }
+
+ template
+ void apply(Func&& f){
+ for(LinkedFrame* p = _tail; p != nullptr; p = p->f_back) f(p->frame);
+ }
};
}; // namespace pkpy
\ No newline at end of file
diff --git a/include/pocketpy/profiler.h b/include/pocketpy/profiler.h
index e1f77c94..4ed36695 100644
--- a/include/pocketpy/profiler.h
+++ b/include/pocketpy/profiler.h
@@ -14,7 +14,7 @@ struct _LineRecord{
};
struct _FrameRecord{
- FrameId frame;
+ LinkedFrame* frame;
clock_t prev_time;
_LineRecord* prev_record;
};
@@ -26,8 +26,8 @@ struct LineProfiler{
std::set functions;
void begin();
- void _step(FrameId frame);
- void _step_end(FrameId frame, int line);
+ void _step(LinkedFrame*);
+ void _step_end(LinkedFrame*, int);
void end();
Str stats();
};
diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h
index 2c1e413a..283c3f30 100644
--- a/include/pocketpy/vm.h
+++ b/include/pocketpy/vm.h
@@ -94,7 +94,7 @@ class VM {
public:
ManagedHeap heap;
ValueStack s_data;
- stack_no_copy callstack;
+ CallStack callstack;
std::vector _all_types;
NameDict _modules; // loaded modules
@@ -151,7 +151,7 @@ public:
VM(bool enable_os=true);
- FrameId top_frame();
+ Frame* top_frame();
void _pop_frame();
PyObject* py_str(PyObject* obj);
diff --git a/src/ceval.cpp b/src/ceval.cpp
index d331ee91..93ac37e1 100644
--- a/src/ceval.cpp
+++ b/src/ceval.cpp
@@ -44,19 +44,12 @@ bool VM::py_ge(PyObject* _0, PyObject* _1){
#undef BINARY_F_COMPARE
-// static i64 _py_sint(PyObject* obj) noexcept {
-// return (i64)(PK_BITS(obj) >> 2);
-// }
-
PyObject* VM::_run_top_frame(){
- FrameId frame = top_frame();
- const int base_id = frame.index;
+ Frame* frame = top_frame();
+ const Frame* base_frame = frame;
bool need_raise = false;
while(true){
-#if PK_DEBUG_EXTRA_CHECK
- if(frame.index < base_id) PK_FATAL_ERROR();
-#endif
try{
if(need_raise){ need_raise = false; _raise(); }
/**********************************************************************/
@@ -67,8 +60,8 @@ PyObject* VM::_run_top_frame(){
{
#define CEVAL_STEP_CALLBACK() \
- if(_ceval_on_step) _ceval_on_step(this, frame.get(), byte); \
- if(_profiler) _profiler->_step(frame);
+ if(_ceval_on_step) _ceval_on_step(this, frame, byte); \
+ if(_profiler) _profiler->_step(callstack._tail);
#define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
__NEXT_FRAME:
@@ -598,7 +591,7 @@ __NEXT_STEP:;
TARGET(RETURN_VALUE){
PyObject* _0 = byte.arg == BC_NOARG ? POPX() : None;
_pop_frame();
- if(frame.index == base_id){ // [ frameBase<- ]
+ if(frame == base_frame){ // [ frameBase<- ]
return _0;
}else{
frame = top_frame();
@@ -826,16 +819,12 @@ __NEXT_STEP:;
}catch(UnhandledException){
PyObject* e_obj = POPX();
Exception& _e = PK_OBJ_GET(Exception, e_obj);
+ bool is_base_frame_to_be_popped = frame == base_frame;
_pop_frame();
- if(callstack.empty()){
-#if PK_DEBUG_FULL_EXCEPTION
- std::cerr << _e.summary() << std::endl;
-#endif
- throw _e;
- }
+ if(callstack.empty()) throw _e; // propagate to the top level
frame = top_frame();
PUSH(e_obj);
- if(frame.index < base_id) throw ToBeRaisedException();
+ if(is_base_frame_to_be_popped) throw ToBeRaisedException();
need_raise = true;
}catch(ToBeRaisedException){
need_raise = true;
diff --git a/src/iter.cpp b/src/iter.cpp
index 0cd6908e..e62108f1 100644
--- a/src/iter.cpp
+++ b/src/iter.cpp
@@ -48,7 +48,7 @@ namespace pkpy{
// restore the context
for(PyObject* obj: s_backup) vm->s_data.push(obj);
s_backup.clear();
- vm->callstack.push(std::move(frame));
+ vm->callstack.emplace(std::move(frame));
PyObject* ret;
try{
diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp
index ce4df652..53e61949 100644
--- a/src/pocketpy.cpp
+++ b/src/pocketpy.cpp
@@ -77,7 +77,7 @@ void init_builtins(VM* _vm) {
class_arg = args[0];
self_arg = args[1];
}else if(args.size() == 0){
- FrameId frame = vm->top_frame();
+ Frame* frame = vm->top_frame();
if(frame->_callable != nullptr){
class_arg = PK_OBJ_GET(Function, frame->_callable)._class;
if(frame->_locals.size() > 0) self_arg = frame->_locals[0];
@@ -184,7 +184,7 @@ void init_builtins(VM* _vm) {
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EVAL_MODE, true);
PyObject* globals = args[1];
if(globals == vm->None){
- FrameId frame = vm->top_frame();
+ Frame* frame = vm->top_frame();
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
}
vm->check_non_tagged_type(globals, vm->tp_mappingproxy);
@@ -196,7 +196,7 @@ void init_builtins(VM* _vm) {
CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EXEC_MODE, true);
PyObject* globals = args[1];
if(globals == vm->None){
- FrameId frame = vm->top_frame();
+ Frame* frame = vm->top_frame();
vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
return vm->None;
}
diff --git a/src/profiler.cpp b/src/profiler.cpp
index c5c8399f..d609bc28 100644
--- a/src/profiler.cpp
+++ b/src/profiler.cpp
@@ -18,16 +18,17 @@ void LineProfiler::begin(){
frames.clear();
}
-void LineProfiler::_step(FrameId frame){
+void LineProfiler::_step(LinkedFrame* linked_frame){
+ Frame* frame = &linked_frame->frame;
auto line_info = frame->co->lines[frame->_ip];
if(line_info.is_virtual) return;
std::string_view filename = frame->co->src->filename.sv();
int line = line_info.lineno;
if(frames.empty()){
- frames.push({frame, clock(), nullptr});
+ frames.push({linked_frame, clock(), nullptr});
}else{
- _step_end(frame, line);
+ _step_end(linked_frame, line);
}
auto& file_records = records[filename];
@@ -43,13 +44,19 @@ void LineProfiler::_step(FrameId frame){
frames.top().prev_record = &file_records.at(line);
}
-void LineProfiler::_step_end(FrameId frame, int line){
+void LineProfiler::_step_end(LinkedFrame* linked_frame, int line){
clock_t now = clock();
_FrameRecord& top_frame_record = frames.top();
_LineRecord* prev_record = top_frame_record.prev_record;
- int id_delta = frame.index - top_frame_record.frame.index;
- PK_ASSERT(id_delta >= -1 && id_delta <= 1);
+ int id_delta;
+ if(linked_frame == top_frame_record.frame){
+ id_delta = 0;
+ }else if(linked_frame->f_back == top_frame_record.frame){
+ id_delta = 1;
+ }else{
+ id_delta = -1;
+ }
// current line is about to change
if(prev_record->line != line){
@@ -60,7 +67,7 @@ void LineProfiler::_step_end(FrameId frame, int line){
}
if(id_delta == 1){
- frames.push({frame, now, nullptr});
+ frames.push({linked_frame, now, nullptr});
}else{
if(id_delta == -1) frames.pop();
}
diff --git a/src/vm.cpp b/src/vm.cpp
index e4f38144..e0612beb 100644
--- a/src/vm.cpp
+++ b/src/vm.cpp
@@ -119,16 +119,15 @@ namespace pkpy{
PK_UNREACHABLE();
}
- FrameId VM::top_frame(){
+ Frame* VM::top_frame(){
#if PK_DEBUG_EXTRA_CHECK
if(callstack.empty()) PK_FATAL_ERROR();
#endif
- return FrameId(&callstack.container(), callstack.size()-1);
+ return &callstack.top();
}
void VM::_pop_frame(){
- Frame* frame = &callstack.top();
- s_data.reset(frame->_sp_base);
+ s_data.reset(callstack.top()._sp_base);
callstack.pop();
}
@@ -652,7 +651,7 @@ void VM::_log_s_data(const char* title) {
if(f._sp_base == nullptr) PK_FATAL_ERROR();
sp_bases[f._sp_base] += 1;
}
- FrameId frame = top_frame();
+ Frame* frame = top_frame();
int line = frame->co->lines[frame->_ip];
ss << frame->co->name << ":" << line << " [";
for(PyObject** p=s_data.begin(); p!=s_data.end(); p++){
@@ -1238,7 +1237,7 @@ void VM::_error(PyObject* e_obj){
}
void VM::_raise(bool re_raise){
- Frame* frame = top_frame().get();
+ Frame* frame = top_frame();
Exception& e = PK_OBJ_GET(Exception, s_data.top());
if(!re_raise){
e._ip_on_error = frame->_ip;
@@ -1259,7 +1258,7 @@ void VM::_raise(bool re_raise){
void ManagedHeap::mark() {
for(PyObject* obj: _no_gc) PK_OBJ_MARK(obj);
- for(auto& frame : vm->callstack.container()) frame._gc_mark();
+ vm->callstack.apply([](Frame& frame){ frame._gc_mark(); });
for(PyObject* obj: vm->s_data) PK_OBJ_MARK(obj);
for(auto [_, co]: vm->_cached_codes) co->_gc_mark();
if(vm->_last_exception) PK_OBJ_MARK(vm->_last_exception);