diff --git a/README.md b/README.md index 72bba3ebb..a9e0bbf9f 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ sudo rm -rf /usr/local/share/meevax | Target Name | Description |:-------------------|:-- -| `all` (default) | Build shared-library `libmeevax.0.4.59.so` and executable `meevax`. +| `all` (default) | Build shared-library `libmeevax.0.4.64.so` and executable `meevax`. | `test` | Test executable `meevax`. -| `package` | Generate debian package `meevax_0.4.59_amd64.deb`. +| `package` | Generate debian package `meevax_0.4.64_amd64.deb`. | `install` | Copy files into `/usr/local` __(1)__. | `install.deb` | `all` + `package` + `sudo apt install .deb` | `safe-install.deb` | `all` + `test` + `package` + `sudo apt install .deb` @@ -120,7 +120,7 @@ __(1)__ Meevax installed by `make install` cannot be uninstalled by the system's ## Usage ``` -Meevax Lisp System, version 0.4.59 +Meevax Lisp System, version 0.4.64 Usage: meevax [OPTION...] [FILE...] diff --git a/VERSION b/VERSION index b7c7a484a..96b9b226d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.59 +0.4.64 diff --git a/include/meevax/kernel/environment.hpp b/include/meevax/kernel/environment.hpp index 8c7ee5d37..70142d36d 100644 --- a/include/meevax/kernel/environment.hpp +++ b/include/meevax/kernel/environment.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -28,9 +29,10 @@ inline namespace kernel { class environment : public virtual pair , public configurator - , public machine - , public reader - , public writer + , public machine + , public optimizer + , public reader + , public writer { using pair::pair; diff --git a/include/meevax/kernel/machine.hpp b/include/meevax/kernel/machine.hpp index a00662782..afddfdad7 100644 --- a/include/meevax/kernel/machine.hpp +++ b/include/meevax/kernel/machine.hpp @@ -471,16 +471,23 @@ inline namespace kernel * ------------------------------------------------------------------- */ [this]() { - auto && [current_expression, current_scope] = unpair(cadr(c)); + auto [current_expression, current_scope] = unpair(cadr(c)); + + let const syntactic_environment = fork(cdr(current_scope)); + + let const c_ = c; for (let const& keyword_ : car(current_scope)) { let & binding = keyword_.as().load(); - binding = make(environment(static_cast(*this)).execute(binding), - fork(cdr(current_scope))); + c = binding; + + binding = make(execute(), syntactic_environment); } + c = c_; + std::swap(c.as(), compile(context(), static_cast(*this), @@ -526,66 +533,40 @@ inline namespace kernel }(); goto decode; - case mnemonic::call: + case mnemonic::tail_call: if (let const& callee = car(s); callee.is()) /* --------------- * - * ( xs . s) e (%call . c) d => () (xs . e') c' (s e c . d) + * ( xs . s) e (%tail-call . c) d => () (xs . e') c' d * * where = (c' . e') * * ------------------------------------------------------------------- */ { - d = cons(cddr(s), e, cdr(c), d); c = callee.as().c(); e = cons(cadr(s), callee.as().e()); s = unit; + goto decode; } - else if (callee.is_also()) /* ------------------------------- - * - * ( xs . s) e (%call . c) d => (x . s) e c d - * - * where x = procedure(xs) - * - * ------------------------------------------------------------------- */ - { - s = cons(callee.as().call(cadr(s)), cddr(s)); - c = cdr(c); - } - else if (callee.is()) /* --------------------------------- - * - * ( xs . s) e (%call . c) d => (xs . s') e' c' d' - * - * where = (s' e' c' . 'd) - * - * ------------------------------------------------------------------- */ - { - s = cons(caadr(s), callee.as().s()); - e = callee.as().e(); - c = callee.as().c(); - d = callee.as().d(); - } - else - { - throw error(make("not applicable"), callee); - } - goto decode; + [[fallthrough]]; // This is inefficient because the type check occurs twice, but currently the performance difference caused by this is too small. - case mnemonic::tail_call: + case mnemonic::call: if (let const& callee = car(s); callee.is()) /* --------------- * - * ( xs . s) e (%tail-call . c) d => () (xs . e') c' d + * ( xs . s) e (%call . c) d => () (xs . e') c' (s e c . d) * * where = (c' . e') * * ------------------------------------------------------------------- */ { + d = cons(cddr(s), e, cdr(c), d); c = callee.as().c(); e = cons(cadr(s), callee.as().e()); s = unit; + goto decode; } else if (callee.is_also()) /* ------------------------------- * - * ( xs . s) e (%tail-call . c) d => (x . s) e c d + * ( xs . s) e (%call . c) d => (x . s) e c d * * where x = procedure(xs) * @@ -593,10 +574,11 @@ inline namespace kernel { s = cons(callee.as().call(cadr(s)), cddr(s)); c = cdr(c); + goto decode; } else if (callee.is()) /* --------------------------------- * - * ( xs . s) e (%tail-call . c) d => (xs . s') e' c' d' + * ( xs . s) e (%call . c) d => (xs . s') e' c' d' * * where = (s' e' c' . 'd) * @@ -606,12 +588,12 @@ inline namespace kernel e = callee.as().e(); c = callee.as().c(); d = callee.as().d(); + goto decode; } else { throw error(make("not applicable"), callee); } - goto decode; case mnemonic::dummy: /* ------------------------------------------------- * @@ -699,8 +681,13 @@ inline namespace kernel return [this]() { assert(cdr(s).template is()); + assert(cdr(c).template is()); + let const x = car(s); - s = unit; + + s = cdr(s); + c = cdr(c); + return x; }(); } @@ -740,6 +727,7 @@ inline namespace kernel return variable.is() ? variable.as().identify_with_offset(scope) : f; } + [[deprecated]] inline auto reset() -> void { s = unit; diff --git a/include/meevax/kernel/optimizer.hpp b/include/meevax/kernel/optimizer.hpp new file mode 100644 index 000000000..f10466acb --- /dev/null +++ b/include/meevax/kernel/optimizer.hpp @@ -0,0 +1,168 @@ +/* + Copyright 2018-2022 Tatsuya Yamasaki. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef INCLUDED_MEEVAX_KERNEL_OPTIMIZER_HPP +#define INCLUDED_MEEVAX_KERNEL_OPTIMIZER_HPP + +#include + +namespace meevax +{ +inline namespace kernel +{ + struct optimizer + { + static inline auto fmerge_constants = true; + + static auto merge_constants(const_reference c) -> lvalue + { + if (not c.is()) + { + return c; + } + else switch (car(c).template as()) + { + case mnemonic::call: + case mnemonic::cons: + case mnemonic::drop: + case mnemonic::dummy: + case mnemonic::join: + case mnemonic::letrec: + case mnemonic::return_: + case mnemonic::stop: + case mnemonic::tail_call: + return [&]() + { + if (let const& continuation = merge_constants(cdr(c)); continuation == cdr(c)) + { + return c; + } + else + { + return cons(car(c), continuation); + } + }(); + + case mnemonic::define: + case mnemonic::define_syntax: + case mnemonic::let_syntax: + case mnemonic::letrec_syntax: + case mnemonic::load_absolute: + case mnemonic::load_relative: + case mnemonic::load_variadic: + case mnemonic::store_absolute: + case mnemonic::store_relative: + case mnemonic::store_variadic: + return [&]() + { + if (let const& continuation = merge_constants(cddr(c)); continuation == cddr(c)) + { + return c; + } + else + { + return cons(car(c), cadr(c), continuation); + } + }(); + + case mnemonic::load_closure: + case mnemonic::load_continuation: + return [&]() + { + if (let const& branch = merge_constants(cadr(c)), + continuation = merge_constants(cddr(c)); + branch == cadr(c) and continuation == cddr(c)) + { + return c; + } + else + { + return cons(car(c), branch, continuation); + } + }(); + + case mnemonic::select: + case mnemonic::tail_select: + return [&]() + { + if (let const& consequent = merge_constants(cadr(c)), + alternate = merge_constants(caddr(c)), + continuation = merge_constants(cdddr(c)); + consequent == cadr(c) and alternate == caddr(c) and continuation == cdddr(c)) + { + return c; + } + else + { + return cons(car(c), consequent, alternate, continuation); + } + }(); + + + case mnemonic::load_constant: /* ----------------------------------------- + * + * (load-constant x + * load-constant y + * cons + * ...) + * + * => (load-constant (x . y) + * ...) + * + * --------------------------------------------------------------------- */ + if (5 <= length(c) and + list_ref(c, 0).is() and + list_ref(c, 0).as() == mnemonic::load_constant and + list_ref(c, 2).is() and + list_ref(c, 2).as() == mnemonic::load_constant and + list_ref(c, 4).is() and + list_ref(c, 4).as() == mnemonic::cons) + { + return merge_constants(cons(list_ref(c, 0), cons(list_ref(c, 3), + list_ref(c, 1)), + merge_constants(list_tail(c, 5)))); + } + else if (let const& continuation = merge_constants(cddr(c)); continuation == cddr(c)) + { + return c; + } + else + { + return cons(car(c), cadr(c), continuation); + } + + default: + assert(false); + return c; + } + } + + static auto optimize(const_reference c) + { + let code = c; + + if (fmerge_constants) + { + code = merge_constants(code); + } + + return code; + } + }; +} // namespace kernel +} // namespace meevax + +#endif // INCLUDED_MEEVAX_KERNEL_OPTIMIZER_HPP diff --git a/src/kernel/environment.cpp b/src/kernel/environment.cpp index e6697b516..60b22e459 100644 --- a/src/kernel/environment.cpp +++ b/src/kernel/environment.cpp @@ -33,6 +33,8 @@ inline namespace kernel auto environment::apply(const_reference f, const_reference xs) -> lvalue { + assert(f.is() or f.is() or f.is()); + auto dump = std::make_tuple(std::exchange(s, list(f, xs)), std::exchange(e, unit), std::exchange(c, list(make(mnemonic::call), @@ -124,10 +126,14 @@ inline namespace kernel } else { - auto dump = std::make_tuple(std::exchange(s, unit), - std::exchange(e, unit), - std::exchange(c, compile(context(), *this, expression, scope())), - std::exchange(d, unit)); + assert(s.is()); + assert(e.is()); + assert(c.is()); + assert(d.is()); + + c = compile(context(), *this, expression, scope()); + + c = optimize(c); if (debug) { @@ -136,10 +142,10 @@ inline namespace kernel let const result = execute(); - s = std::get<0>(dump); - e = std::get<1>(dump); - c = std::get<2>(dump); - d = std::get<3>(dump); + assert(s.is()); + assert(e.is()); + assert(c.is()); + assert(d.is()); return result; } diff --git a/src/kernel/identity.cpp b/src/kernel/identity.cpp index a824472bf..fcde5f6e2 100644 --- a/src/kernel/identity.cpp +++ b/src/kernel/identity.cpp @@ -31,7 +31,7 @@ inline namespace kernel auto identity::symbol() const -> const_reference { - assert(first.is()); + assert(first.is_also()); return first; } diff --git a/src/kernel/syntax.cpp b/src/kernel/syntax.cpp index 2c3e9464b..6b9f7a763 100644 --- a/src/kernel/syntax.cpp +++ b/src/kernel/syntax.cpp @@ -22,7 +22,7 @@ inline namespace kernel { syntax::syntax(std::string const& name, function_type const& compile) : description { name } - , compile { compile } + , compile { compile } {} auto operator <<(std::ostream & os, syntax const& datum) -> std::ostream &