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:
M | cgen.cpp | | | 40 | +++++++++++++++++++++++++++------------- |
M | cgen.hpp | | | 22 | +++++++++++++++------- |
M | cgen.test.cpp | | | 95 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
M | toctave.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