Switch to immutable stack frames from array-based stack

This commit is contained in:
Konstantin Nazarov 2024-09-13 00:54:32 +01:00
parent 213ce07f34
commit f16850d26c
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
7 changed files with 154 additions and 111 deletions

View file

@ -257,15 +257,13 @@ Result<PodObject*> Arena::gc_stack(PodStackFrame* obj) {
auto nobj = TRY(alloc<PodStackFrame>(sizeof(OffPtr<PodObject>) * obj->size));
nobj->header.tag = Tag::StackFrame;
nobj->parent = TRY(gc_pod(obj->parent.get()));
nobj->fun = TRY(gc_pod(obj->fun.get()));
nobj->parent_pc = obj->parent_pc;
nobj->size = obj->size;
nobj->top = obj->top;
for (uint64_t i = 0; i < obj->top; i++) {
for (uint64_t i = 0; i < obj->size; i++) {
PodObject* val = obj->data[i].get();
nobj->data[i] = TRY(gc_pod(val));
}
for (uint64_t i = obj->top; i < obj->size; i++) {
nobj->data[i] = 0;
}
return nobj;
}

View file

@ -302,18 +302,15 @@ Result<short> Module::cmp(const Module& rhs) const {
return res;
}
Result<StackFrame> StackFrame::create(uint64_t size) {
auto nil = Value(TRY(Nil::create()));
auto pod = TRY(arena_alloc<PodStackFrame>(sizeof(OffPtr<PodObject>) * size));
Result<StackFrame> StackFrame::create(const Value& parent, const Function& fun,
uint64_t parent_pc) {
auto pod = TRY(arena_alloc<PodStackFrame>());
pod->header.tag = Tag::StackFrame;
pod->parent = nil.pod();
pod->size = size;
pod->top = 0;
for (uint64_t i = 0; i < size; i++) {
pod->data[i] = 0;
}
pod->parent = parent.pod();
pod->fun = fun.pod();
pod->parent_pc = parent_pc;
pod->size = 0;
return StackFrame(TRY(MkGcRoot(pod)));
}
@ -323,32 +320,67 @@ Result<Value> StackFrame::copy_value() const {
}
Result<Value> StackFrame::get(uint64_t idx) const {
if (idx >= _value->top) return ERROR(KeyError);
if (idx >= _value->size) return ERROR(KeyError);
return Value::create(_value->data[idx].get());
}
Result<void> StackFrame::set(uint64_t idx, const Value& val) {
if (idx >= _value->size) return ERROR(KeyError);
Result<StackFrame> StackFrame::set(uint64_t idx, const Value& val) {
uint64_t size = std::max(_value->size, idx + 1);
auto pod = TRY(arena_alloc<PodStackFrame>(sizeof(OffPtr<PodObject>) * size));
pod->header.tag = Tag::StackFrame;
if (idx >= _value->top) {
_value->top = idx + 1;
pod->parent = _value->parent;
pod->fun = _value->fun;
pod->parent_pc = _value->parent_pc;
pod->size = size;
for (uint64_t i = 0; i < _value->size; i++) {
pod->data[i] = _value->data[i];
}
if (size > _value->size) {
auto nil = Value(TRY(Nil::create()));
for (uint64_t i = _value->size; i < size; i++) {
pod->data[i] = nil.pod();
}
}
pod->data[idx] = val.pod();
_value->data[idx] = val.pod();
return Result<void>();
return StackFrame(TRY(MkGcRoot(pod)));
}
Result<void> StackFrame::settop(uint64_t idx) {
uint64_t top = _value->top;
for (uint64_t i = std::min(top, idx); i < std::max(top, idx); i++) {
_value->data[i] = 0;
}
_value->top = idx;
Result<StackFrame> StackFrame::settop(uint64_t idx) {
auto pod = TRY(arena_alloc<PodStackFrame>(sizeof(OffPtr<PodObject>) * idx));
pod->header.tag = Tag::StackFrame;
return Result<void>();
pod->parent = _value->parent;
pod->fun = _value->fun;
pod->parent_pc = _value->parent_pc;
pod->size = idx;
auto min_idx = std::min(_value->size, idx);
for (uint64_t i = 0; i < min_idx; i++) {
pod->data[i] = _value->data[i];
}
if (min_idx < idx) {
auto nil = Value(TRY(Nil::create()));
for (uint64_t i = min_idx; i < idx; i++) {
pod->data[i] = nil.pod();
}
}
return StackFrame(TRY(MkGcRoot(pod)));
}
Result<Value> StackFrame::parent() const {
return Value::create(_value->parent.get());
}
Result<Function> StackFrame::fun() const {
return Function::create((PodFunction*)_value->fun.get());
}
uint64_t StackFrame::parent_pc() const { return _value->parent_pc; }
Result<Value> Error::copy_value() const {
return Value(Error(TRY(_value.copy())));
}

View file

@ -1092,6 +1092,11 @@ class StackFrame : public Object {
StackFrame(StackFrame&& rhs) : _value(std::move(rhs._value)) {}
StackFrame(GcRoot<PodStackFrame>&& val) : _value(std::move(val)) {}
StackFrame& operator=(StackFrame&& rhs) {
_value = std::move(rhs._value);
return *this;
}
virtual Tag tag() const final { return Tag::StackFrame; }
virtual PodObject* pod() const final { return _value.get(); }
virtual Result<short> cmp(const Object& rhs) const final {
@ -1109,14 +1114,18 @@ class StackFrame : public Object {
using Object::get;
Result<Value> get(uint64_t idx) const;
Result<void> set(uint64_t idx, const Value& val);
Result<void> settop(uint64_t idx);
uint64_t gettop() { return _value->top; }
Result<StackFrame> set(uint64_t idx, const Value& val);
Result<StackFrame> settop(uint64_t idx);
Result<Value> parent() const;
Result<Function> fun() const;
uint64_t parent_pc() const;
Result<uint64_t> size() const { return _value->size; }
static Result<StackFrame> create(uint64_t size);
static Result<StackFrame> create(const Value& parent, const Function& fun,
uint64_t parent_pc);
Result<Array> slice(uint64_t start, uint64_t end) {
if (start > end || end > gettop()) return ERROR(IndexOutOfRange);
if (start > end || end > _value->size) return ERROR(IndexOutOfRange);
uint64_t res_size = end - start;
auto pod = TRY(arena_alloc<PodArray>(res_size * sizeof(PodObject*)));
pod->header.tag = Tag::Array;

View file

@ -197,8 +197,9 @@ class PodStackFrame final : public PodObject {
PodStackFrame() : PodObject(Tag::StackFrame) {};
OffPtr<PodObject> parent;
OffPtr<PodObject> fun;
uint64_t parent_pc;
uint64_t size;
uint64_t top;
OffPtr<PodObject> data[];
};

View file

@ -21,9 +21,10 @@ Result<Value> run_string(const String& fname, const String& src) {
auto compiled = TRY(compile(fname, parsed));
Module& mod = *compiled.to<Module>();
auto vm = TRY(VM::create());
Dict globals = TRY(Dict::create());
auto vm = TRY(VM::create(mod, globals));
auto res = TRY(vm.run(mod));
auto res = TRY(vm.run());
return res;
}
@ -64,8 +65,8 @@ Result<void> run_repl() {
auto compiled = maybe_compiled.release_value();
Module& mod = *compiled.to<Module>();
auto vm = TRY(VM::create());
auto maybe_res = vm.run(mod, globals);
auto vm = TRY(VM::create(mod, globals));
auto maybe_res = vm.run();
if (maybe_res.has_value()) {
auto res = maybe_res.release_value();

View file

@ -3,11 +3,10 @@
#include "common.hpp"
#include "stdlib.hpp"
Result<Value> VM::getreg(uint64_t idx) { return _stack.get(_base + idx); }
Result<Value> VM::getreg(uint64_t idx) { return _stack.get(idx); }
Result<void> VM::setreg(uint64_t idx, const Value& value) {
TRY(_stack.set(_base + idx, value));
return Result<void>();
Result<StackFrame> VM::setreg(uint64_t idx, const Value& value) {
return _stack.set(idx, value);
}
Result<Value> VM::getconst(uint64_t idx) { return _constants.get(idx); }
@ -20,7 +19,7 @@ Result<Value> VM::get(bool is_const, uint64_t idx) {
Result<void> VM::vm_mov(Opcode& oc) {
uint64_t acc = (uint64_t)oc.arg1().arg;
Value val = TRY(get(oc.arg2().is_const, (uint64_t)oc.arg2().arg));
setreg(acc, val);
_stack = TRY(setreg(acc, val));
_pc++;
return Result<void>();
}
@ -30,7 +29,7 @@ Result<void> VM::vm_add(Opcode& oc) {
Value val1 = TRY(get(oc.arg2().is_const, (uint64_t)oc.arg2().arg));
Value val2 = TRY(get(oc.arg3().is_const, (uint64_t)oc.arg3().arg));
Value res = TRY(val1.add(val2));
setreg(acc, res);
_stack = TRY(setreg(acc, res));
_pc++;
return Result<void>();
}
@ -40,7 +39,7 @@ Result<void> VM::vm_mul(Opcode& oc) {
Value val1 = TRY(get(oc.arg2().is_const, (uint64_t)oc.arg2().arg));
Value val2 = TRY(get(oc.arg3().is_const, (uint64_t)oc.arg3().arg));
Value res = TRY(val1.mul(val2));
setreg(acc, res);
_stack = TRY(setreg(acc, res));
_pc++;
return Result<void>();
}
@ -50,7 +49,7 @@ Result<void> VM::vm_sub(Opcode& oc) {
Value val1 = TRY(get(oc.arg2().is_const, (uint64_t)oc.arg2().arg));
Value val2 = TRY(get(oc.arg3().is_const, (uint64_t)oc.arg3().arg));
Value res = TRY(val1.sub(val2));
setreg(acc, res);
_stack = TRY(setreg(acc, res));
_pc++;
return Result<void>();
}
@ -60,7 +59,7 @@ Result<void> VM::vm_div(Opcode& oc) {
Value val1 = TRY(get(oc.arg2().is_const, (uint64_t)oc.arg2().arg));
Value val2 = TRY(get(oc.arg3().is_const, (uint64_t)oc.arg3().arg));
Value res = TRY(val1.div(val2));
setreg(acc, res);
_stack = TRY(setreg(acc, res));
_pc++;
return Result<void>();
}
@ -109,21 +108,21 @@ Result<void> VM::vm_call_lisp(Opcode& oc, Function& fun) {
return ERROR(ArgumentCountMismatch);
}
uint64_t old_base = _base;
auto trunc_stack = TRY(_stack.settop(reg_start));
auto new_stack =
TRY(StackFrame::create(TRY(trunc_stack.copy_value()), fun, _pc));
Value fun_val = TRY(_fun.copy());
Value oldbase_val = TRY(Int64::create(old_base));
Value pc_val = TRY(Int64::create(_pc));
_callstack.set(_callstack.gettop(), fun_val);
_callstack.set(_callstack.gettop(), oldbase_val);
_callstack.set(_callstack.gettop(), pc_val);
for (uint64_t i = 0; i < reg_end - reg_start; i++) {
new_stack = TRY(new_stack.set(i, TRY(_stack.get(reg_start + i))));
}
_stack = std::move(new_stack);
_code = TRY(fun.code());
_constants = TRY(fun.constants());
_closure = TRY(fun.closure());
_fun = std::move(fun);
_pc = 0;
_base = _base + reg_start;
return Result<void>();
}
@ -135,7 +134,7 @@ Result<void> VM::vm_call_stdlib(Opcode& oc, StdlibFunction& fun) {
auto params = TRY(_stack.slice(reg_start + 1, reg_end));
auto res = TRY(call_stdlib_function(fun.fun_id(), params));
setreg(oc.arg1().arg, res);
_stack = TRY(setreg(oc.arg1().arg, res));
_pc++;
return Result<void>();
@ -164,47 +163,41 @@ Result<void> VM::vm_selfcall(Opcode& oc) {
return ERROR(ArgumentCountMismatch);
}
uint64_t old_base = _base;
auto trunc_stack = TRY(_stack.settop(reg_start));
auto new_stack =
TRY(StackFrame::create(TRY(trunc_stack.copy_value()), _fun, _pc));
Value fun_val = TRY(_fun.copy());
Value oldbase_val = TRY(Int64::create(old_base));
Value pc_val = TRY(Int64::create(_pc));
_callstack.set(_callstack.gettop(), fun_val);
_callstack.set(_callstack.gettop(), oldbase_val);
_callstack.set(_callstack.gettop(), pc_val);
for (uint64_t i = 0; i < reg_end - reg_start; i++) {
new_stack = TRY(new_stack.set(i, TRY(_stack.get(reg_start + i))));
}
_stack = std::move(new_stack);
_pc = 0;
_base = _base + reg_start;
return Result<void>();
}
Result<void> VM::vm_ret(Opcode& oc) {
if (_callstack.gettop() == 0) {
auto parent = TRY(_stack.parent());
if (parent.is<Nil>()) {
_res = TRY(getreg((uint64_t)oc.arg1().arg));
return ERROR(EndOfProgram);
}
Value fun_val = TRY(_callstack.get(_callstack.gettop() - 3));
Value oldbase_val = TRY(_callstack.get(_callstack.gettop() - 2));
Value pc_val = TRY(_callstack.get(_callstack.gettop() - 1));
auto res = TRY(getreg((uint64_t)oc.arg1().arg));
StackFrame& parent_stack = *parent.to<StackFrame>();
uint64_t parent_size = TRY(parent_stack.size());
parent_stack = TRY(parent_stack.set(parent_size, res));
if (!oldbase_val.is<Int64>() || !pc_val.is<Int64>() ||
!fun_val.is<Function>())
return ERROR(TypeMismatch);
Function& fun = *fun_val.to<Function>();
uint64_t oldbase = oldbase_val.to<Int64>()->value();
uint64_t pc = pc_val.to<Int64>()->value();
auto fun = TRY(parent_stack.fun());
uint64_t pc = _stack.parent_pc();
_stack = std::move(parent_stack);
_code = TRY(fun.code());
_constants = TRY(fun.constants());
_closure = TRY(fun.closure());
_fun = std::move(fun);
_pc = pc + 1;
_base = oldbase;
TRY(_callstack.settop(_callstack.gettop() - 3));
return Result<void>();
}
@ -215,8 +208,8 @@ Result<void> VM::vm_jump(Opcode& oc) {
}
Result<void> VM::vm_make_closure(Opcode& oc) {
uint64_t begin = (uint64_t)_base + oc.arg1().arg + 1;
uint64_t end = (uint64_t)_base + oc.arg2().arg;
uint64_t begin = oc.arg1().arg + 1;
uint64_t end = oc.arg2().arg;
Value fun_proto = TRY(get(oc.arg1().is_const, (uint64_t)oc.arg1().arg));
if (!fun_proto.is<Function>()) return ERROR(TypeMismatch);
@ -225,7 +218,7 @@ Result<void> VM::vm_make_closure(Opcode& oc) {
Value fun = TRY(Function::create(*fun_proto.to<Function>(), closure));
setreg(oc.arg1().arg, fun);
_stack = TRY(setreg(oc.arg1().arg, fun));
_pc++;
@ -235,7 +228,7 @@ Result<void> VM::vm_make_closure(Opcode& oc) {
Result<void> VM::vm_closure_load(Opcode& oc) {
uint64_t acc = (uint64_t)oc.arg1().arg;
Value val = TRY(_closure.get((uint64_t)oc.arg2().arg));
setreg(acc, val);
_stack = TRY(setreg(acc, val));
_pc++;
return Result<void>();
}
@ -244,7 +237,7 @@ Result<void> VM::vm_global_load(Opcode& oc) {
uint64_t acc = (uint64_t)oc.arg1().arg;
Value sym = TRY(get(oc.arg2().is_const, (uint64_t)oc.arg2().arg));
Value val = TRY(_globals.get(sym));
setreg(acc, val);
_stack = TRY(setreg(acc, val));
_pc++;
return Result<void>();
}
@ -321,17 +314,7 @@ Result<void> VM::step() {
return Result<void>();
}
// Result<Value> Int64::copy() const { return Value(Int64(TRY(_value.copy())));
// }
Result<Value> VM::run(const Module& mod, const Dict& globals) {
_fun = TRY(mod.fun());
_pc = 0;
_base = 0;
_code = TRY(_fun.code());
_constants = TRY(_fun.constants());
_closure = TRY(_fun.closure());
_globals = TRY(globals.copy());
Result<Value> VM::run() {
while (true) {
auto rc = step();
if (!rc.has_error()) continue;
@ -343,7 +326,3 @@ Result<Value> VM::run(const Module& mod, const Dict& globals) {
}
return ERROR(NotImplemented);
}
Result<Value> VM::run(const Module& mod) {
return run(mod, TRY(Dict::create()));
}

View file

@ -5,20 +5,45 @@
class VM {
public:
VM() {}
VM(StackFrame&& stack, StackFrame&& callstack)
: _stack(std::move(stack)), _callstack(std::move(callstack)) {}
VM(StackFrame&& stack, Function&& fun, Array&& code, Array&& constants,
Array&& closure, Dict&& globals, uint64_t pc, Value&& res)
: _stack(std::move(stack)),
_fun(std::move(fun)),
_code(std::move(code)),
_constants(std::move(constants)),
_closure(std::move(closure)),
_globals(std::move(globals)),
_pc(pc),
_res(std::move(res)) {}
VM(VM&& vm)
: _stack(std::move(vm._stack)), _callstack(std::move(vm._callstack)) {}
: _stack(std::move(vm._stack)),
_fun(std::move(vm._fun)),
_code(std::move(vm._code)),
_constants(std::move(vm._constants)),
_closure(std::move(vm._closure)),
_globals(std::move(vm._globals)),
_pc(vm._pc),
_res(std::move(vm._res)) {}
VM(const VM&) = delete;
static Result<VM> create() {
auto stack = TRY(StackFrame::create(16 * 1024));
auto callstack = TRY(StackFrame::create(16 * 1024));
return VM(std::move(stack), std::move(callstack));
static Result<VM> create(const Module& mod, const Dict& globals) {
auto fun = TRY(mod.fun());
auto nil = Value(TRY(Nil::create()));
auto stack = TRY(StackFrame::create(nil, fun, 0));
uint64_t pc = 0;
auto code = TRY(fun.code());
auto constants = TRY(fun.constants());
auto closure = TRY(fun.closure());
auto globals_copy = TRY(globals.copy());
return VM(std::move(stack), std::move(fun), std::move(code),
std::move(constants), std::move(closure), std::move(globals_copy),
pc, std::move(nil));
}
Result<Value> run(const Module& mod);
Result<Value> run(const Module& mod, const Dict& globals);
Result<Value> run();
Result<void> step();
Result<void> vm_mov(Opcode& oc);
@ -46,19 +71,17 @@ class VM {
Result<Value> get(bool is_const, uint64_t idx);
Result<Value> getconst(uint64_t idx);
Result<Value> getreg(uint64_t idx);
Result<void> setreg(uint64_t idx, const Value& value);
Result<StackFrame> setreg(uint64_t idx, const Value& value);
Result<Dict> globals() { return _globals.copy(); }
private:
StackFrame _stack;
StackFrame _callstack;
Function _fun;
Array _code;
Array _constants;
Array _closure;
Dict _globals;
uint64_t _pc;
uint64_t _base;
Value _res;
};