diff --git a/tests/unittests/CompilerSuite.cpp b/tests/unittests/CompilerSuite.cpp index d177bbbb..e25d1623 100644 --- a/tests/unittests/CompilerSuite.cpp +++ b/tests/unittests/CompilerSuite.cpp @@ -1,7 +1,10 @@ #include +#include #include +#include "TestsHelper.hpp" + using namespace boost; ut::suite<"Compiler"> compiler_suite = [] { @@ -34,4 +37,26 @@ ut::suite<"Compiler"> compiler_suite = [] { expect(that % secondary_arg == ((padding << 4) | (arg & 0xf000) >> 12)); }; }; + + "IR generation and optimization"_test = [] { + constexpr uint16_t features = Ark::DefaultFeatures | Ark::FeatureTestFailOnException; + + iter_test_files( + "CompilerSuite/ir", + [](TestData&& data) { + Ark::Welder welder(0, { std::filesystem::path(ARK_TESTS_ROOT "/lib/") }, features); + + should("compile without error ir/" + data.stem) = [&] { + expect(mut(welder).computeASTFromFile(data.path)); + expect(mut(welder).generateBytecode()); + }; + + should("output expected IR for " + data.stem) = [&] { + std::string ir = welder.textualIR(); + + ltrim(rtrim(ir)); + expect(that % ir == data.expected); + }; + }); + }; }; diff --git a/tests/unittests/TestsHelper.cpp b/tests/unittests/TestsHelper.cpp index f1f0ff53..f31a58a6 100644 --- a/tests/unittests/TestsHelper.cpp +++ b/tests/unittests/TestsHelper.cpp @@ -31,7 +31,7 @@ std::string get_resource_path(const std::string& folder) return (ARK_TESTS_ROOT "tests/unittests/resources/") + folder; } -std::string sanitize_error(const Ark::CodeError& e, bool remove_in_file_line) +std::string sanitize_error(const Ark::CodeError& e, const bool remove_in_file_line) { std::stringstream stream; Ark::Diagnostics::generate(e, stream, /* colorize= */ false); diff --git a/tests/unittests/resources/CompilerSuite/ir/ackermann_iroptimized.ark b/tests/unittests/resources/CompilerSuite/ir/ackermann_iroptimized.ark new file mode 100644 index 00000000..f652e592 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/ackermann_iroptimized.ark @@ -0,0 +1,20 @@ +# the Ackermann Peter function (see https://en.wikipedia.org/wiki/Ackermann_function) +# One of the simplest and earliest-discovered examples of a total computable function, +# that is not primitive. All primitive recursive functions are total and computable, +# but the Ackermann function illustrates that not all total computable functions +# are primitive recursive. +# Due to its definitions in terms of extremely deep recursion, it can be used as a +# benchmark of a compiler's ability to optimize recursion, which is the reason why +# we are using this function to benchmark the language. +(let ackermann (fun (m n) { + (if (> m 0) + # then + (if (= 0 n) + # then + (ackermann (- m 1) 1) + # else + (ackermann (- m 1) (ackermann m (- n 1)))) + # else + (+ 1 n)) })) + +(print "Ackermann-Péter function, m=3, n=6: " (ackermann 3 6)) diff --git a/tests/unittests/resources/CompilerSuite/ir/ackermann_iroptimized.expected b/tests/unittests/resources/CompilerSuite/ir/ackermann_iroptimized.expected new file mode 100644 index 00000000..6a510d57 --- /dev/null +++ b/tests/unittests/resources/CompilerSuite/ir/ackermann_iroptimized.expected @@ -0,0 +1,38 @@ +page_0 + LOAD_CONST_STORE 0, 0 + LOAD_CONST_LOAD_CONST 3, 4 + LOAD_CONST 5 + LOAD_SYMBOL 0 + CALL 2 + CALL_BUILTIN 9, 2 + HALT 0 + +page_1 + STORE 1 + STORE 2 + LOAD_SYMBOL 1 + LOAD_CONST 1 + GT 0 + GOTO_IF_TRUE L0 + INCREMENT 2 + GOTO L1 +.L0: + LOAD_CONST 1 + LOAD_SYMBOL 2 + EQ 0 + GOTO_IF_TRUE L2 + LOAD_SYMBOL 1 + DECREMENT 2 + LOAD_SYMBOL 0 + CALL 2 + DECREMENT 1 + JUMP 0 + GOTO L3 +.L2: + LOAD_CONST 2 + DECREMENT 1 + JUMP 0 +.L3: +.L1: + RET 0 + HALT 0