toctave

t(iny)octave
Log | Files | Refs | README

commit 581de5ac65753b00541086820ebd15f3eaf2d7e3
parent 92f433cb3835b8b0b70ecc682b69f1b3d231ac8d
Author: Mohammad-Reza Nabipoor <mnabipoor@gnu.org>
Date:   Sat, 28 May 2022 00:49:16 +0430

Add Jittery VM + support code

Diffstat:
M.gitignore | 1+
MMakefile | 36++++++++++++++++++++++++++++++++----
Aast.cpp | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mast.hpp | 105++++++++++---------------------------------------------------------------------
Acgen.cpp | 276+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcgen.hpp | 167+++++++++++++++++++++++++++++++++++++------------------------------------------
Mcgen.test.cpp | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Ajitter.patch | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoctave.jitter | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 870 insertions(+), 186 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1 +1,2 @@ *.test +vm/ diff --git a/Makefile b/Makefile @@ -1,15 +1,43 @@ +JITTER_DISPATCH = switch +JITTER_PREFIX ?= $(HOME)/usr + CXXFLAGS += -Wall -Wextra -std=c++17 -g3 -fsanitize=address -CPPFLAGS += -I. +CPPFLAGS += -I . -I $(JITTER_PREFIX)/include/ +LDFLAGS += -lm + +#--- + +J = ${JITTER_PREFIX}/bin/jitter +JC = ${JITTER_PREFIX}/bin/jitter-config --dispatch=$(JITTER_DISPATCH) + +JCPPFLAGS += -I vm/ $(shell $(JC) --cppflags) +JCFLAGS += $(shell $(JC) --cflags) +JLDFLAGS += $(shell $(JC) --ldflags --ldadd) BINS = ast.test cgen.test +VM = vm/toctave-vm1.c vm/toctave-vm2.c vm/toctave-vm.h +VM_OBJS = vm/toctave-vm1.o vm/toctave-vm2.o all: $(BINS) -ast.test: ast.test.cpp -cgen.test: cgen.test.cpp +ast.test: ast.test.cpp ast.cpp ast.hpp + $(CXX) -o $@ $(CPPFLAGS) $(CXXFLAGS) $< ast.cpp + +cgen.test: cgen.hpp +cgen.test: cgen.test.cpp cgen.cpp ast.cpp ast.hpp $(VM_OBJS) + $(CXX) -o $@ $(CPPFLAGS) $(CXXFLAGS) $< cgen.cpp ast.cpp $(VM_OBJS) \ + $(JCPPFLAGS) $(JLDFLAGS) + +$(VM) &: toctave.jitter + $(J) -o vm/ $< + +vm/toctave-vm1.o: vm/toctave-vm1.c + $(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS) $(JCPPFLAGS) $(JLDFLAGS) +vm/toctave-vm2.o: vm/toctave-vm2.c + $(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS) $(JCPPFLAGS) $(JCFLAGS) $(JLDFLAGS) .PHONY: clean clean: - rm -f $(BINS) + rm -f $(BINS) $(VM) diff --git a/ast.cpp b/ast.cpp @@ -0,0 +1,124 @@ + +#include "ast.hpp" + +namespace ast { + +std::ostream& +operator<<(std::ostream& os, const Num& n) +{ + os << "Num{ " << n.num << " }"; + return os; +} + +std::ostream& +operator<<(std::ostream& os, const Op& o) +{ + os << "Op{ " << o.op << " }"; + return os; +} + +std::ostream& +operator<<(std::ostream& os, const Id& i) +{ + os << "Id{ " << i.id << " }"; + return os; +} + +std::ostream& +operator<<(std::ostream& os, const Str& s) +{ + os << "Str{ \"" << s.str << "\" }"; + return os; +} + +std::ostream& +operator<<(std::ostream& os, const As& a) +{ + os << "As{ " << a.id << " }"; + return os; +} + +std::ostream& +operator<<(std::ostream& os, const Call& c) +{ + os << "Call{ " << c.id << " }"; + return os; +} + +std::ostream& +operator<<(std::ostream& os, const Node& n) +{ + if (std::holds_alternative<Num>(n)) + os << std::get<Num>(n); + else if (std::holds_alternative<Op>(n)) + os << std::get<Op>(n); + else if (std::holds_alternative<Id>(n)) + os << std::get<Id>(n); + else if (std::holds_alternative<Str>(n)) + os << std::get<Str>(n); + else if (std::holds_alternative<As>(n)) + os << std::get<As>(n); + else if (std::holds_alternative<Call>(n)) + os << std::get<Call>(n); + return os; +} + +Nodes +mknum(double n) +{ + Nodes f; + + f.insert(f.end(), Num{ n }); + return f; +} + +Nodes +mkop(Op op, Nodes lhs, Nodes rhs) +{ + Nodes f; + auto it{ stlab::trailing_of(f.insert(f.end(), std::move(op))) }; + + f.splice(it, lhs); + f.splice(it, rhs); + return f; +} + +Nodes +mkid(Id id) +{ + Nodes f; + + f.insert(f.end(), std::move(id)); + return f; +} + +Nodes +mkstr(Str str) +{ + Nodes f; + + f.insert(f.end(), std::move(str)); + return f; +} + +Nodes +mkass(Id id, Nodes expr) +{ + Nodes f; + auto it{ stlab::trailing_of(f.insert(f.end(), ast::As{ std::move(id) })) }; + + f.splice(it, expr); + return f; +} + +Nodes +mkcall(Id id, Nodes args) +{ + Nodes f; + auto it{ stlab::trailing_of(f.insert(f.end(), ast::Call{ std::move(id) })) }; + + f.splice(it, args); + return f; +} + +} // namespace ast diff --git a/ast.hpp b/ast.hpp @@ -43,124 +43,45 @@ struct Call using Node = std::variant<Num, Op, Id, Str, As, Call>; std::ostream& -operator<<(std::ostream& os, const Num& n) -{ - os << "Num{ " << n.num << " }"; - return os; -} +operator<<(std::ostream& os, const Num& n); std::ostream& -operator<<(std::ostream& os, const Op& o) -{ - os << "Op{ " << o.op << " }"; - return os; -} +operator<<(std::ostream& os, const Op& o); std::ostream& -operator<<(std::ostream& os, const Id& i) -{ - os << "Id{ " << i.id << " }"; - return os; -} +operator<<(std::ostream& os, const Id& i); std::ostream& -operator<<(std::ostream& os, const Str& s) -{ - os << "Str{ \"" << s.str << "\" }"; - return os; -} +operator<<(std::ostream& os, const Str& s); std::ostream& -operator<<(std::ostream& os, const As& a) -{ - os << "As{ " << a.id << " }"; - return os; -} +operator<<(std::ostream& os, const As& a); std::ostream& -operator<<(std::ostream& os, const Call& c) -{ - os << "Call{ " << c.id << " }"; - return os; -} +operator<<(std::ostream& os, const Call& c); std::ostream& -operator<<(std::ostream& os, const Node& n) -{ - if (std::holds_alternative<Num>(n)) - os << std::get<Num>(n); - else if (std::holds_alternative<Op>(n)) - os << std::get<Op>(n); - else if (std::holds_alternative<Id>(n)) - os << std::get<Id>(n); - else if (std::holds_alternative<Str>(n)) - os << std::get<Str>(n); - else if (std::holds_alternative<As>(n)) - os << std::get<As>(n); - else if (std::holds_alternative<Call>(n)) - os << std::get<Call>(n); - return os; -} +operator<<(std::ostream& os, const Node& n); using Nodes = stlab::forest<Node>; using NodesIter = Nodes::iterator; Nodes -mknum(double n) -{ - Nodes f; - - f.insert(f.end(), Num{ n }); - return f; -} +mknum(double n); Nodes -mkop(Op op, Nodes lhs, Nodes rhs) -{ - Nodes f; - auto it{ stlab::trailing_of(f.insert(f.end(), std::move(op))) }; - - f.splice(it, lhs); - f.splice(it, rhs); - return f; -} +mkop(Op op, Nodes lhs, Nodes rhs); Nodes -mkid(Id id) -{ - Nodes f; - - f.insert(f.end(), std::move(id)); - return f; -} +mkid(Id id); Nodes -mkstr(Str str) -{ - Nodes f; - - f.insert(f.end(), std::move(str)); - return f; -} +mkstr(Str str); Nodes -mkass(Id id, Nodes expr) -{ - Nodes f; - auto it{ stlab::trailing_of(f.insert(f.end(), ast::As{ std::move(id) })) }; - - f.splice(it, expr); - return f; -} +mkass(Id id, Nodes expr); Nodes -mkcall(Id id, Nodes args) -{ - Nodes f; - auto it{ stlab::trailing_of(f.insert(f.end(), ast::Call{ std::move(id) })) }; - - f.splice(it, args); - return f; -} +mkcall(Id id, Nodes args); } // namespace ast diff --git a/cgen.cpp b/cgen.cpp @@ -0,0 +1,276 @@ + +#include "cgen.hpp" + +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <sstream> +#include <vector> + +namespace { + +template<typename... Ts> +struct overloaded : Ts... +{ + using Ts::operator()...; +}; +template<typename... Ts> +overloaded(Ts...) -> overloaded<Ts...>; + +class JPrinter +{ +private: + std::unique_ptr<void, void (*)(void*)> pcontext; + +public: + JPrinter() + : pcontext(jitter_print_context_make_memory(), [](void* p) { + auto ctx{ reinterpret_cast<jitter_print_context>(p) }; + + jitter_print_context_destroy(ctx); + }) + {} + + jitter_print_context ctx() + { + return reinterpret_cast<jitter_print_context>(pcontext.get()); + } + + std::string buffer() + { + auto str{ jitter_print_context_get_memory(ctx(), /*length_p*/ nullptr) }; + std::string s{ str }; + + free(str); + return s; + } +}; + +} // anonymous namespace + +namespace cgen { + +std::string +sexpr(ast::NodesIter first, ast::NodesIter last) +{ + std::ostringstream oss; + + while (first != last) { + std::visit(overloaded{ + [&](const ast::Num& n) { oss << n.num; }, + [&](const ast::Op& op) { + auto f{ ++stlab::leading_of(first) }; + auto l{ stlab::trailing_of(first) }; + auto count{ 0u }; + + oss << "(" << op.op; + while (f != l) { + auto n{ ++stlab::trailing_of(f) }; + oss << " " << sexpr(f, n); + f = n; + ++count; + } + assert(count == 2); + oss << ")"; + }, + [&](const ast::Id& id) { oss << id.id; }, + [&](const ast::Str& str) { oss << '"' << str.str << '"'; }, + [&](const ast::As& as) { + auto f{ ++stlab::leading_of(first) }; + auto l{ stlab::trailing_of(first) }; + + assert(stlab::has_children(first)); + + oss << "(setq " << as.id.id << " "; + oss << sexpr(f, ++stlab::trailing_of(f)) << ")"; + }, + [&](const ast::Call& call) { + auto f{ ++stlab::leading_of(first) }; + auto l{ stlab::trailing_of(first) }; + + assert(stlab::has_children(first)); + + oss << "(" << call.id.id; + while (f != l) { + auto n{ ++stlab::trailing_of(f) }; + oss << " " << sexpr(f, n); + f = n; + } + oss << ")"; + }, + }, + *first); + + first = ++stlab::trailing_of(first); + } + + return oss.str(); +} + +//--- Jittery VM + +JExecRoutine::JExecRoutine(struct toctave_mutable_routine* r) + : er(toctave_make_executable_routine(r), + [](struct toctave_executable_routine* p) { + toctave_destroy_executable_routine(p); + }) + , state(toctave_state_make(), + [](struct toctave_state* s) { toctave_state_destroy(s); }) +{} + +void +JExecRoutine::exec() +{ + toctave_execute_executable_routine(er.get(), state.get()); +} + +bool +JExecRoutine::exec_continue() +{ + auto point{ TOCTAVE_STATE_RUNTIME_FIELD(state.get(), point) }; + + if (point == nullptr) + return false; + toctave_branch_to_program_point(point, state.get()); + return true; +} + +double +JExecRoutine::result() +{ + return TOCTAVE_STATE_RUNTIME_FIELD(state.get(), result); +} + +//--- + +JRoutine::JRoutine() + : r(toctave_make_mutable_routine(), [](struct toctave_mutable_routine* p) { + toctave_destroy_mutable_routine(p); + }) +{} + +JExecRoutine +JRoutine::mkexec() +{ + return JExecRoutine(r.get()); +} + +JRoutine::label_t +JRoutine::mklabel() +{ + return toctave_fresh_label(r.get()); +} + +JRoutine::label_t +JRoutine::label(JRoutine::label_t l) +{ + toctave_mutable_routine_append_label(r.get(), l); + return l; +} + +JRoutine::label_t +JRoutine::label() +{ + return label(mklabel()); +} + +void +JRoutine::push(double d) +{ + static_assert(sizeof(void*) == sizeof(double), + "We're using double as the type of stack elements; to make " + "the implementation easier, we expect a 64-bit platform"); + uintptr_t num; + + memcpy(&num, &d, sizeof(d)); // copy the bits of double into the uint + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), push); + toctave_mutable_routine_append_unsigned_literal_parameter(r.get(), num); +} + +void +JRoutine::pop() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), pop); +} + +void +JRoutine::drop() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), drop); +} + +void +JRoutine::pow() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), pow); +} + +void +JRoutine::mul() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), mul); +} + +void +JRoutine::div() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), div); +} + +void +JRoutine::add() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), add); +} + +void +JRoutine::sub() +{ + TOCTAVE_MUTABLE_ROUTINE_APPEND_INSTRUCTION(r.get(), sub); +} + +std::string +disasm(const JRoutine& r) +{ + JPrinter jprinter; + + toctave_mutable_routine_print(jprinter.ctx(), r.r.get()); + return jprinter.buffer(); +} + +std::string +disasm(const JExecRoutine& er) +{ + JPrinter jprinter; + + toctave_executable_routine_disassemble( + jprinter.ctx(), er.er.get(), true, "objdump", nullptr); + return jprinter.buffer(); +} + +//--- codegen + +JRoutine +jitter(ast::NodesIter first, ast::NodesIter last) +{ + JRoutine r; + + while (first != last) { + for (const auto& stmt : stlab::child_range(stlab::leading_of(first))) { + std::visit(overloaded{ + [&](const ast::Num&) {}, + [&](const ast::Op&) {}, + [&](const ast::Id&) {}, + [&](const ast::Str&) {}, + [&](const ast::As&) {}, + [&](const ast::Call&) {}, + }, + stmt); + } + first = ++stlab::trailing_of(first); + } + + return r; +} + +} // namespace cgen diff --git a/cgen.hpp b/cgen.hpp @@ -1,103 +1,94 @@ #pragma once -#include <sstream> +#include <memory> #include "ast.hpp" +// Use the VM generated by Jitter +extern "C" +{ +#define restrict +#include "vm/toctave-vm.h" +#undef restrict +} + namespace cgen { -template<typename... Ts> -struct overloaded : Ts... -{ - using Ts::operator()...; -}; -template<typename... Ts> -overloaded(Ts...) -> overloaded<Ts...>; +// Generates S-Expression equivalent of input AST +std::string +sexpr(ast::NodesIter first, ast::NodesIter last); + +//--- Jittery VM -struct jroutine -{}; +class JExecRoutine; +class JRoutine; -jroutine -jitter(const ast::Nodes& ns) +std::string disasm(const JExecRoutine& er); +std::string disasm(const JRoutine& r); + +// Jitter Executable Routine +class JExecRoutine { - jroutine r; - auto first{ ns.begin() }; - auto last{ ns.end() }; - - while (first != last) { - for (const auto& stmt : stlab::child_range(stlab::leading_of(first))) { - std::visit(overloaded{ - [&](const ast::Num&) {}, - [&](const ast::Op&) {}, - [&](const ast::Id&) {}, - [&](const ast::Str&) {}, - [&](const ast::As&) {}, - [&](const ast::Call&) {}, - }, - stmt); - } - first = ++stlab::trailing_of(first); - } - - return r; -} +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::string -sexpr(ast::NodesIter first, ast::NodesIter last) +public: + JExecRoutine() = default; + JExecRoutine(struct toctave_mutable_routine*); + + friend std::string disasm(const JExecRoutine& er); + + void exec(); + bool exec_continue(); + + double result(); +}; + +class JRoutine { - std::ostringstream oss; - - while (first != last) { - std::visit(overloaded{ - [&](const ast::Num& n) { oss << n.num; }, - [&](const ast::Op& op) { - auto f{ ++stlab::leading_of(first) }; - auto l{ stlab::trailing_of(first) }; - auto count{ 0u }; - - oss << "(" << op.op; - while (f != l) { - auto n{ ++stlab::trailing_of(f) }; - oss << " " << sexpr(f, n); - f = n; - ++count; - } - assert(count == 2); - oss << ")"; - }, - [&](const ast::Id& id) { oss << id.id; }, - [&](const ast::Str& str) { oss << '"' << str.str << '"'; }, - [&](const ast::As& as) { - auto f{ ++stlab::leading_of(first) }; - auto l{ stlab::trailing_of(first) }; - - assert(stlab::has_children(first)); - - oss << "(setq " << as.id.id << " "; - oss << sexpr(f, ++stlab::trailing_of(f)) << ")"; - }, - [&](const ast::Call& call) { - auto f{ ++stlab::leading_of(first) }; - auto l{ stlab::trailing_of(first) }; - - assert(stlab::has_children(first)); - - oss << "(" << call.id.id; - while (f != l) { - auto n{ ++stlab::trailing_of(f) }; - oss << " " << sexpr(f, n); - f = n; - } - oss << ")"; - }, - }, - *first); - - first = ++stlab::trailing_of(first); - } - - return oss.str(); -} +private: + std::unique_ptr<struct toctave_mutable_routine, + void (*)(struct toctave_mutable_routine*)> + r; + +public: + using label_t = toctave_label; + + JRoutine(); + + friend std::string disasm(const JRoutine& r); + JExecRoutine mkexec(); + + //--- label manipulation + + label_t mklabel(); + label_t label(label_t l); + label_t label(); + + //--- instructions + + // stack manipulation + void push(double d); + void pop(); + void drop(); + + // arithmetic + void pow(); + void mul(); + void div(); + void add(); + void sub(); + + // branching + // ... +}; + +// Generates code for the VM generated by Jitter +JRoutine +jitter(ast::NodesIter first, ast::NodesIter last); } // namespace cgen diff --git a/cgen.test.cpp b/cgen.test.cpp @@ -7,7 +7,7 @@ namespace { void -cgen_1() +cgen_sexpr() { auto i{ 0 }; auto p = [&](const auto& s) { @@ -23,6 +23,104 @@ cgen_1() p(s); assert(s == "(+ 1 2)"); } + + { + ast::Nodes ass1{ ast::mkass( + ast::Id{ "x" }, + ast::mkop(ast::Op{ '+' }, + ast::mkop(ast::Op{ '/' }, ast::mknum(1), ast::mknum(2)), + ast::mkop(ast::Op{ '*' }, ast::mknum(3), ast::mknum(4)))) }; + auto s{ cgen::sexpr(ass1.begin(), ass1.end()) }; + + p(s); + assert(s == "(setq x (+ (/ 1 2) (* 3 4)))"); + } + + { + ast::Nodes eval1{ ast::mkcall(ast::Id{ "eval" }, + ast::mkstr(ast::Str{ "y = 1 + x" })) }; + auto s{ cgen::sexpr(eval1.begin(), eval1.end()) }; + + p(s); + assert(s == "(eval \"y = 1 + x\")"); + } +} + +void +cgen_jroutine() +{ + auto i{ 0 }; + auto p = [&](const auto& s) { + std::cout << "# Disasm " << i << " {\n" << s << "# Disasm " << i << " }\n"; + ++i; + }; + + { + cgen::JRoutine r; + + r.push(1.0); + r.push(2.0); + r.add(); + r.pop(); /* Moves the result on top of the stack onto `result` */ + + auto d{ cgen::disasm(r) }; + + p(d); + assert(d == R"( push 1.000000 + push 2.000000 + add + pop +)"); + + auto er{ r.mkexec() }; + + er.exec(); + assert(er.result() == 3.0); + } + + { + cgen::JRoutine r; + + r.push(3.0); + r.push(4.0); + r.add(); + r.push(5.0); + r.mul(); + r.pop(); + + auto d{ cgen::disasm(r) }; + + p(d); + assert(d == R"( push 3.000000 + push 4.000000 + add + push 5.000000 + mul + pop +)"); + + auto er{ r.mkexec() }; + + er.exec(); + assert(er.result() == 35.0); + } +} + +void +cgen_jitter() +{ + auto i{ 0 }; + auto p = [&](const auto& s) { + std::cout << "# " << i << " {\n" << s << "\n# " << i << " }\n"; + ++i; + }; + + { + ast::Nodes binop1{ ast::mkop( + ast::Op{ '+' }, ast::mknum(1), ast::mknum(2)) }; + auto j{ cgen::jitter(binop1.begin(), binop1.end()) }; + } + { ast::Nodes ass1{ ast::mkass( ast::Id{ "x" }, @@ -34,6 +132,7 @@ cgen_1() p(s); assert(s == "(setq x (+ (/ 1 2) (* 3 4)))"); } + { ast::Nodes eval1{ ast::mkcall(ast::Id{ "eval" }, ast::mkstr(ast::Str{ "y = 1 + x" })) }; @@ -49,6 +148,17 @@ cgen_1() int main() { - cgen_1(); + // To manage VM initialization/deintialization process + // (this is like a ScopeGuard) + std::unique_ptr<void, void (*)(void*)> vm_init( + [] { + toctave_initialize(); + return reinterpret_cast<void*>(1); /* dummy */ + }(), + [](void*) { toctave_finalize(); }); + + cgen_sexpr(); + cgen_jroutine(); + cgen_jitter(); return 0; } diff --git a/jitter.patch b/jitter.patch @@ -0,0 +1,97 @@ +diff --git a/jitter/jitter-missing.h b/jitter/jitter-missing.h +index 80cefdd..e84dc79 100644 +--- a/jitter/jitter-missing.h ++++ b/jitter/jitter-missing.h +@@ -124,12 +124,12 @@ + + /* Do nothing. */ + void +-flockfile () ++flockfile (FILE *) + __attribute__ ((nonnull (1))); + + /* Do nothing. */ + void +-funlockfile () ++funlockfile (FILE *) + __attribute__ ((nonnull (1))); + + #endif // #ifndef JITTER_MISSING_H_ +diff --git a/templates/vm.h b/templates/vm.h +index 6fcfbbb..c66d43b 100644 +--- a/templates/vm.h ++++ b/templates/vm.h +@@ -1125,6 +1125,36 @@ vmprefix_program_point; + + + ++/* VM exit status. ++ * ************************************************************************** */ ++ ++/* A value of this type is returned by a VM after execution, and the last ++ returned value is also held in the VM state in order to make consistency ++ checks. */ ++enum vmprefix_exit_status ++ { ++ /* This state has never been used for execution. This is the initial value ++ within the state, and is never returned after execution. */ ++ vmprefix_exit_status_never_executed = 0, ++ ++ /* The state is being used in execution right now; this is never returned by ++ the executor. It is an error (checked for) to execute code with a VM ++ state containing this exit status, which shows that there has been a ++ problem -- likely VM code was exited via longjmp, skipping the proper ++ cleanup. */ ++ vmprefix_exit_status_being_executed = 1, ++ ++ /* Some VM code has been executed. It is now possible to execute more code ++ (including the same code again) in the same state. */ ++ vmprefix_exit_status_exited = 2, ++ ++ /* Code execution has been interrupted for debugging, but can be resumed. */ ++ vmprefix_exit_status_debug = 3, ++ }; ++ ++ ++ ++ + /* Executing code from an executable routine. + * ************************************************************************** */ + +@@ -1185,36 +1215,6 @@ vmprefix_execute_routine (jitter_routine r, + __attribute__ ((nonnull (1, 2))); + + +- +- +-/* VM exit status. +- * ************************************************************************** */ +- +-/* A value of this type is returned by a VM after execution, and the last +- returned value is also held in the VM state in order to make consistency +- checks. */ +-enum vmprefix_exit_status +- { +- /* This state has never been used for execution. This is the initial value +- within the state, and is never returned after execution. */ +- vmprefix_exit_status_never_executed = 0, +- +- /* The state is being used in execution right now; this is never returned by +- the executor. It is an error (checked for) to execute code with a VM +- state containing this exit status, which shows that there has been a +- problem -- likely VM code was exited via longjmp, skipping the proper +- cleanup. */ +- vmprefix_exit_status_being_executed = 1, +- +- /* Some VM code has been executed. It is now possible to execute more code +- (including the same code again) in the same state. */ +- vmprefix_exit_status_exited = 2, +- +- /* Code execution has been interrupted for debugging, but can be resumed. */ +- vmprefix_exit_status_debug = 3, +- }; +- +- + + + /* Low-level debugging features relying on assembly: data locations. diff --git a/toctave.jitter b/toctave.jitter @@ -0,0 +1,136 @@ + +vm + set prefix "toctave" +end + +stack s + long-name "mainstack" + c-element-type "double" + c-initial-value "0.0" + element-no 4096 + guard-underflow + guard-overflow + tos-optimized +end + +wrapped-functions + pow + memcpy + toctave_result +end + +state-struct-runtime-c + code + double result; + + jitter_program_point point; + int status; + end +end + +state-initialization-c + code + jitter_state_runtime->result = 0.0; // maybe NaN is better + jitter_state_runtime->point = NULL; + jitter_state_runtime->status = -1; + end +end + +early-header-c + code +#include <math.h> +#include <stdlib.h> + end +end + +printer-c + code + +// Jitter uses this function to print literal values in instructions (like +// push instruction) +static void +double_literal_printer(jitter_print_context ctx, uintptr_t n) +{ + double d; + + memcpy(&d, &n, sizeof(n)); + jitter_print_double(ctx, d); +} + + end +end + +# Stack manipulation + +instruction push (?n double_literal_printer) + code + uintptr_t n = JITTER_ARGN0; + double d; + + memcpy(&d, &n, sizeof(n)); + TOCTAVE_PUSH_MAINSTACK(d); + end +end + +instruction pop () + code + TOCTAVE_STATE_RUNTIME_FIELD (result) = TOCTAVE_TOP_MAINSTACK(); + TOCTAVE_DROP_MAINSTACK(); + end +end + +instruction drop () + code + TOCTAVE_DROP_MAINSTACK(); + end +end + +# Arithmetic + +# Stack: (a b -- a^b) +instruction pow () + code + TOCTAVE_TOP_MAINSTACK() = + pow(/* a */ TOCTAVE_UNDER_TOP_MAINSTACK(), + /* b */ TOCTAVE_TOP_MAINSTACK()); /* Stack: (a a^b) */ + TOCTAVE_NIP_MAINSTACK(); /* Stack: (a^b) */ + end +end + +# Stack: (a b -- a*b) +instruction mul () + code + TOCTAVE_TOP_MAINSTACK() = + /* a */ TOCTAVE_UNDER_TOP_MAINSTACK() * + /* b */ TOCTAVE_TOP_MAINSTACK(); /* Stack: (a a*b) */ + TOCTAVE_NIP_MAINSTACK(); /* Stack: (a*b) */ + end +end + +# Stack: (a b -- a/b) +instruction div () + non-relocatable + code + TOCTAVE_TOP_MAINSTACK() = + /* a */ TOCTAVE_UNDER_TOP_MAINSTACK() / + /* b */ TOCTAVE_TOP_MAINSTACK(); /* Stack: (a a*b) */ + end +end + +# Stack: (a b -- a+b) +instruction add () + code + TOCTAVE_TOP_MAINSTACK() = + /* a */ TOCTAVE_UNDER_TOP_MAINSTACK() + + /* b */ TOCTAVE_TOP_MAINSTACK(); /* Stack: (a a*b) */ + end +end + +# Stack: (a b -- a-b) +instruction sub () + code + TOCTAVE_TOP_MAINSTACK() = + /* a */ TOCTAVE_UNDER_TOP_MAINSTACK() - + /* b */ TOCTAVE_TOP_MAINSTACK(); /* Stack: (a a*b) */ + end +end