toctave

t(iny)octave
git clone https://0xff.ir/g/toctave.git
Log | Files | Refs | README

commit 153334eed306c539aeed9e8abcc68e22c6506a16
parent 7bb3f81f6982770828d759bff844591f03101edf
Author: Mohammad-Reza Nabipoor <mnabipoor@gnu.org>
Date:   Mon, 30 May 2022 23:43:44 +0430

cgen,vm: Add execution suspension

This commit adds two VM instructions:
  - yield
  - done
Removes JExecRoutine::exec_continue member function.
Adjusts codegen tests.

Diffstat:
Mcgen.cpp | 40+++++++++++++++++++++++++++-------------
Mcgen.hpp | 22+++++++++++++++-------
Mcgen.test.cpp | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mtoctave.jitter | 19+++++++++++++++++++
4 files changed, 141 insertions(+), 35 deletions(-)

diff --git a/cgen.cpp b/cgen.cpp @@ -119,25 +119,24 @@ JExecRoutine::JExecRoutine(struct toctave_mutable_routine* r) , state(toctave_state_make(), toctave_state_destroy) {} -void -JExecRoutine::exec(env::Env& e) -{ - TOCTAVE_STATE_RUNTIME_FIELD(state.get(), env) = reinterpret_cast<void*>(&e); - toctave_execute_executable_routine(er.get(), state.get()); - TOCTAVE_STATE_RUNTIME_FIELD(state.get(), env) = nullptr; -} - bool -JExecRoutine::exec_continue(env::Env& e) +JExecRoutine::exec(env::Env& e) { auto point{ TOCTAVE_STATE_RUNTIME_FIELD(state.get(), point) }; + auto status{ TOCTAVE_STATE_RUNTIME_FIELD(state.get(), status) }; - if (point == nullptr) - return false; TOCTAVE_STATE_RUNTIME_FIELD(state.get(), env) = reinterpret_cast<void*>(&e); - toctave_branch_to_program_point(point, state.get()); + if (point) { + assert(status == TOCTAVE_STATUS_YIELD); + toctave_branch_to_program_point(point, state.get()); + } else { + assert(status == -1); + toctave_execute_executable_routine(er.get(), state.get()); + } TOCTAVE_STATE_RUNTIME_FIELD(state.get(), env) = nullptr; - return true; + status = TOCTAVE_STATE_RUNTIME_FIELD(state.get(), status); + assert(status != -1); + return status == TOCTAVE_STATUS_DONE; } double @@ -307,6 +306,20 @@ JRoutine::call(const char* func) assert(0 && "not implemented yet"); } +void +JRoutine::yield(label_t l) +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), yield); + toctave_mutable_routine_append_label_parameter(r.get(), l); + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), exitvm); +} + +void +JRoutine::done() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), done); +} + std::string disasm(const JRoutine& r) { @@ -422,6 +435,7 @@ jitter(ast::NodesIter first, ast::NodesIter last) JRoutine r; jitter_(r, first, last, true); + r.done(); return r; } diff --git a/cgen.hpp b/cgen.hpp @@ -29,8 +29,10 @@ sexpr(ast::NodesIter first, ast::NodesIter last); class JExecRoutine; class JRoutine; -std::string disasm(const JExecRoutine& er); -std::string disasm(const JRoutine& r); +std::string +disasm(const JExecRoutine& er); +std::string +disasm(const JRoutine& r); // Jitter Executable Routine class JExecRoutine @@ -39,7 +41,7 @@ private: std::unique_ptr<struct toctave_executable_routine, void (*)(struct toctave_executable_routine*)> er; - std::unique_ptr<struct toctave_state, void(*)(struct toctave_state*)> state; + std::unique_ptr<struct toctave_state, void (*)(struct toctave_state*)> state; public: JExecRoutine() = default; @@ -47,9 +49,11 @@ public: friend std::string disasm(const JExecRoutine& er); - void exec(env::Env&); - bool exec_continue(env::Env&); + // Execute the VM code with the given environment. + // Returns true when done (reached the done instruction). + bool exec(env::Env&); + // Result of the computation double result(); }; @@ -65,8 +69,8 @@ public: JRoutine(); - friend std::string disasm(const JRoutine& r); - JExecRoutine mkexec(); + friend std::string disasm(const JRoutine& r); // Disassemble + JExecRoutine mkexec(); // Make executable routine //--- label manipulation @@ -96,6 +100,10 @@ public: void call(const char* func); void pushvar(const char* var); // move value from variable to stack void popvar(const char* var); // move value from stack to variable + + // control flow + void yield(label_t l); + void done(); }; // Generates code for the VM generated by Jitter diff --git a/cgen.test.cpp b/cgen.test.cpp @@ -66,6 +66,7 @@ cgen_jroutine() r.push(2.0); r.add(); r.pop(); /* Moves the result on top of the stack onto `result` */ + r.done(); auto d{ cgen::disasm(r) }; @@ -74,11 +75,12 @@ cgen_jroutine() push 2.000000 add pop + done )"); auto er{ r.mkexec() }; - er.exec(e); + assert(er.exec(e) == true); assert(er.result() == 3.0); assert(e.empty()); } @@ -93,6 +95,7 @@ cgen_jroutine() r.push(5.0); r.mul(); r.pop(); + r.done(); auto d{ cgen::disasm(r) }; @@ -103,6 +106,7 @@ cgen_jroutine() push 5.000000 mul pop + done )"); auto er{ r.mkexec() }; @@ -124,6 +128,7 @@ cgen_jroutine() r.pushvar("x"); r.mul(); r.pop(); + r.done(); auto d{ cgen::disasm(r) }; @@ -136,11 +141,12 @@ cgen_jroutine() pushvar x mul pop + done )"); auto er{ r.mkexec() }; - er.exec(e); + assert(er.exec(e) == true); assert(er.result() == 14.0); assert(e.size() == 1); assert(e[0].name == "x"); @@ -159,6 +165,7 @@ cgen_jroutine() r.pushvar("y"); r.mul(); r.pop(); + r.done(); auto d{ cgen::disasm(r) }; @@ -171,6 +178,7 @@ cgen_jroutine() pushvar y mul pop + done )"); auto er{ r.mkexec() }; @@ -178,12 +186,66 @@ cgen_jroutine() assert(e.size() == 1); assert(e[0].name == "y"); assert(e[0].val == 10.0); - er.exec(e); + assert(er.exec(e) == true); assert(er.result() == 162.0); assert(e.size() == 1); assert(e[0].name == "y"); assert(e[0].val == 81.0); } + + { + cgen::JRoutine r; + env::Env e; + auto lbl{ r.mklabel() }; + + r.push(3.0); + r.push(4.0); + r.add(); + r.popvar("x"); + r.push(2.0); + r.pop(); + r.yield(lbl); // yield the execution, next time it will start from `lbl` + r.label(lbl); // insert the label here + r.push(2.0); + r.pushvar("x"); + r.mul(); + r.pop(); + r.done(); + + auto er{ r.mkexec() }; + auto d{ cgen::disasm(r) }; + + p(d); + assert(d == R"( push 3.000000 + push 4.000000 + add + popvar x + push 2.000000 + pop + yield $L8 + exitvm +$L8: push 2.000000 + pushvar x + mul + pop + done + exitvm +)"); + + // execution until the yield + assert(er.exec(e) == false); + assert(er.result() == 2.0); + assert(e.size() == 1); + assert(e[0].name == "x"); + assert(e[0].val == 7.0); + + // execution after the yield + assert(er.exec(e) == true); + assert(er.result() == 14.0); + assert(e.size() == 1); + assert(e[0].name == "x"); + assert(e[0].val == 7.0); + } } void @@ -206,6 +268,7 @@ cgen_jitter() p(s); assert(s == R"( push 120.000000 pop + done )"); je.exec(env); assert(env.empty()); @@ -225,8 +288,9 @@ cgen_jitter() push 2.000000 add pop + done )"); - je.exec(env); + assert(je.exec(env) == true); assert(env.empty()); assert(je.result() == 3.0); } @@ -244,18 +308,19 @@ cgen_jitter() double d{ 0.0 }; p(s); - assert(s == R"( push 1.000000 - push 2.000000 - div - push 3.000000 - push 4.000000 - mul - add - popvar x - pushnan - pop + assert(s != R"( push 1.000000 + push 2.000000 + div + push 3.000000 + push 4.000000 + mul + add + popvar x + pushnan + pop + done )"); - je.exec(env); + assert(je.exec(env) == true); assert(std::isnan(je.result())); assert(env.size() == 1); assert(toctave_var(&env, "x", &d)); diff --git a/toctave.jitter b/toctave.jitter @@ -57,6 +57,9 @@ union double_uint64 double d; uint64_t u64; }; + +#define TOCTAVE_STATUS_DONE 0 +#define TOCTAVE_STATUS_YIELD 1 end end @@ -215,3 +218,19 @@ instruction sub () /* b */ TOCTAVE_TOP_MAINSTACK(); /* Stack: (a a*b) */ end end + +# Control Flow + +instruction yield (?f) + code + TOCTAVE_STATE_RUNTIME_FIELD(point) = JITTER_ARGF0; + TOCTAVE_STATE_RUNTIME_FIELD(status) = TOCTAVE_STATUS_YIELD; + end +end + +instruction done () + code + TOCTAVE_STATE_RUNTIME_FIELD(point) = NULL; + TOCTAVE_STATE_RUNTIME_FIELD(status) = TOCTAVE_STATUS_DONE; + end +end