diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f8ae0f6..2069f903 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -224,18 +224,19 @@ jobs: export ASAN_OPTIONS=detect_odr_violation=0 ./unittests + - uses: actions/setup-python@v5 + if: ${{ ! startsWith(matrix.config.name, 'Windows') }} + with: + python-version: '3.13' + - name: Modules tests + if: ${{ ! startsWith(matrix.config.name, 'Windows') }} shell: bash run: | export ASAN_OPTIONS=detect_odr_violation=0 chmod u+x ./lib/modules/.github/run-tests (./lib/modules/.github/run-tests src) - - uses: actions/setup-python@v5 - if: ${{ ! startsWith(matrix.config.name, 'Windows') }} - with: - python-version: '3.13' - - name: REPL tests if: ${{ ! startsWith(matrix.config.name, 'Windows') }} shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 08869a97..429c6e68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ - `CHECK_TYPE_OF` and `CHECK_TYPE_OF_BY_INDEX` super instructions, to check the type of variable against a constant in a single instruction - `INCREMENT_STORE` and `DECREMENT_STORE` super instructions, to update a value in place when incrementing/decrementing it by a set amount - `APPEND_IN_PLACE_SYM` and `APPEND_IN_PLACE_SYM_INDEX` super instructions +- `PUSH_RETURN_ADDRESS` instruction now replaces the VM auto push of IP/PP +- remove the stack swapping by pushing arguments in the reverse order by which they are loaded ### Changed - instructions are on 4 bytes: 1 byte for the instruction, 1 byte of padding, 2 bytes for an immediate argument diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 91b2c56d..976858e7 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -74,325 +74,328 @@ namespace Ark::internal // @role Stop the Virtual Machine HALT = 0x0a, + // @role push pp, then ip on the stack, preparing for a call instruction + PUSH_RETURN_ADDRESS = 0x0b, + // @args argument count // @role Call function from its symbol id located on top of the stack. Take the given number of arguments from the top of stack and give them to the function (the first argument taken from the stack will be the last one of the function). The stack of the function is now composed of its arguments, from the first to the last one - CALL = 0x0b, + CALL = 0x0c, // @args symbol id // @role Tell the Virtual Machine to capture the variable from the current environment. Main goal is to be able to handle closures, which need to save the environment in which they were created - CAPTURE = 0x0c, + CAPTURE = 0x0d, // @args builtin id // @role Push the corresponding builtin function object on the stack - BUILTIN = 0x0d, + BUILTIN = 0x0e, // @args symbol id // @role Remove a variable/constant named following the given symbol id (cf symbols table) - DEL = 0x0e, + DEL = 0x0f, // @args constant id // @role Push a Closure with the page address pointed by the constant, along with the saved scope created by CAPTURE instruction(s) - MAKE_CLOSURE = 0x0f, + MAKE_CLOSURE = 0x10, // @args symbol id // @role Read the field named following the given symbol id (cf symbols table) of a #[code Closure] stored in TS. Pop TS and push the value of field read on the stack - GET_FIELD = 0x10, + GET_FIELD = 0x11, // @args constant id // @role Load a plugin dynamically, plugin name is stored as a string in the constants table - PLUGIN = 0x11, + PLUGIN = 0x12, // @args number of elements // @role Create a list from the N elements pushed on the stack. Follows the function calling convention - LIST = 0x12, + LIST = 0x13, // @args number of elements // @role Append N elements to a list (TS). Elements are stored in TS(1)..TS(N). Follows the function calling convention - APPEND = 0x13, + APPEND = 0x14, // @args number of elements // @role Concatenate N lists to a list (TS). Lists to concat to TS are stored in TS(1)..TS(N). Follows the function calling convention - CONCAT = 0x14, + CONCAT = 0x15, // @args number of elements // @role Append N elements to a reference to a list (TS), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention - APPEND_IN_PLACE = 0x15, + APPEND_IN_PLACE = 0x16, // @args number of elements // @role Concatenate N lists to a reference to a list (TS), the list is being mutated in-place, no new object created. Lists to concat to TS are stored in TS(1)..TS(N). Follows the function calling convention - CONCAT_IN_PLACE = 0x16, + CONCAT_IN_PLACE = 0x17, // @role Remove an element from a list (TS), given an index (TS1). Push a new list without the removed element to the stack - POP_LIST = 0x17, + POP_LIST = 0x18, // @role Remove an element from a reference to a list (TS), given an index (TS1). The list is mutated in-place, no new object created - POP_LIST_IN_PLACE = 0x18, + POP_LIST_IN_PLACE = 0x19, // @role Modify a reference to a list or string (TS) by replacing the element at TS1 (must be a number) by the value in TS2. The object is mutated in-place, no new object created - SET_AT_INDEX = 0x19, + SET_AT_INDEX = 0x1a, // @role Modify a reference to a list (TS) by replacing TS[TS2][TS1] by the value in TS3. TS[TS2] can be a string (if it is, TS3 must be a string). The object is mutated in-place, no new object created - SET_AT_2_INDEX = 0x1a, + SET_AT_2_INDEX = 0x1b, // @role Remove the top of the stack - POP = 0x1b, + POP = 0x1c, // @role Pop the top of the stack, if it's false, jump to an address - SHORTCIRCUIT_AND = 0x1c, + SHORTCIRCUIT_AND = 0x1d, // @role Pop the top of the stack, if it's true, jump to an address - SHORTCIRCUIT_OR = 0x1d, + SHORTCIRCUIT_OR = 0x1e, // @role Create a new local scope - CREATE_SCOPE = 0x1e, + CREATE_SCOPE = 0x1f, // @role Reset the current scope so that it is empty, and jump to a given location - RESET_SCOPE_JUMP = 0x1f, + RESET_SCOPE_JUMP = 0x20, // @role Destroy the last local scope - POP_SCOPE = 0x20, + POP_SCOPE = 0x21, - FIRST_OPERATOR = 0x21, + FIRST_OPERATOR = 0x22, // @role Push #[code TS1 + TS] - ADD = 0x21, + ADD = 0x22, // @role Push #[code TS1 - TS] - SUB = 0x22, + SUB = 0x23, // @role Push #[code TS1 * TS] - MUL = 0x23, + MUL = 0x24, // @role Push #[code TS1 / TS] - DIV = 0x24, + DIV = 0x25, // @role Push #[code TS1 > TS] - GT = 0x25, + GT = 0x26, // @role Push #[code TS1 < TS] - LT = 0x26, + LT = 0x27, // @role Push #[code TS1 <= TS] - LE = 0x27, + LE = 0x28, // @role Push #[code TS1 >= TS] - GE = 0x28, + GE = 0x29, // @role Push #[code TS1 != TS] - NEQ = 0x29, + NEQ = 0x2a, // @role Push #[code TS1 == TS] - EQ = 0x2a, + EQ = 0x2b, // @role Push #[code len(TS)], TS must be a list - LEN = 0x2b, + LEN = 0x2c, // @role Push #[code empty?(TS)], TS must be a list or string - EMPTY = 0x2c, + EMPTY = 0x2d, // @role Push #[code tail(TS)], all the elements of TS except the first one. TS must be a list or string - TAIL = 0x2d, + TAIL = 0x2e, // @role Push #[code head(TS)], the first element of TS or nil if empty. TS must be a list or string - HEAD = 0x2e, + HEAD = 0x2f, // @role Push true if TS is nil, false otherwise - ISNIL = 0x2f, + ISNIL = 0x30, // @role Throw an exception if TS1 is false, and display TS (must be a string). Do not push anything on the stack - ASSERT = 0x30, + ASSERT = 0x31, // @role Convert TS to number (must be a string) - TO_NUM = 0x31, + TO_NUM = 0x32, // @role Convert TS to string - TO_STR = 0x32, + TO_STR = 0x33, // @role Push the value at index TS (must be a number) in TS1, which must be a list or string - AT = 0x33, + AT = 0x34, // @role Push the value at index TS (must be a number), inside the list or string at index TS1 (must be a number) in the list at TS2 - AT_AT = 0x34, + AT_AT = 0x35, // @role Push #[code TS1 % TS] - MOD = 0x35, + MOD = 0x36, // @role Push the type of TS as a string - TYPE = 0x36, + TYPE = 0x37, // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String - HASFIELD = 0x37, + HASFIELD = 0x38, // @role Push #[code !TS] - NOT = 0x38, + NOT = 0x39, // @args constant id, constant id // @role Load two consts (#[code primary] then #[code secondary]) on the stack in one instruction - LOAD_CONST_LOAD_CONST = 0x39, + LOAD_CONST_LOAD_CONST = 0x3a, // @args constant id, symbol id // @role Load const #[code primary] into the symbol #[code secondary] (create a variable) - LOAD_CONST_STORE = 0x3a, + LOAD_CONST_STORE = 0x3b, // @args constant id, symbol id // @role Load const #[code primary] into the symbol #[code secondary] (search for the variable with the given symbol id) - LOAD_CONST_SET_VAL = 0x3b, + LOAD_CONST_SET_VAL = 0x3c, // @args symbol id, symbol id // @role Store the value of the symbol #[code primary] into a new variable #[code secondary] - STORE_FROM = 0x3c, + STORE_FROM = 0x3d, // @args symbol index, symbol id // @role Store the value of the symbol #[code primary] into a new variable #[code secondary] - STORE_FROM_INDEX = 0x3d, + STORE_FROM_INDEX = 0x3e, // @args symbol id, symbol id // @role Store the value of the symbol #[code primary] into an existing variable #[code secondary] - SET_VAL_FROM = 0x3e, + SET_VAL_FROM = 0x3f, // @args symbol index, symbol id // @role Store the value of the symbol #[code primary] into an existing variable #[code secondary] - SET_VAL_FROM_INDEX = 0x3f, + SET_VAL_FROM_INDEX = 0x40, // @args symbol id, count // @role Increment the variable #[code primary] by #[code count] and push its value on the stack - INCREMENT = 0x40, + INCREMENT = 0x41, // @args symbol index, count // @role Increment the variable #[code primary] by #[code count] and push its value on the stack - INCREMENT_BY_INDEX = 0x41, + INCREMENT_BY_INDEX = 0x42, // @args symbol id, count // @role Increment the variable #[code primary] by #[code count] and store its value in the given symbol id - INCREMENT_STORE = 0x42, + INCREMENT_STORE = 0x43, // @args symbol id, count // @role Decrement the variable #[code primary] by #[code count] and push its value on the stack - DECREMENT = 0x43, + DECREMENT = 0x44, // @args symbol index, count // @role Decrement the variable #[code primary] by #[code count] and push its value on the stack - DECREMENT_BY_INDEX = 0x44, + DECREMENT_BY_INDEX = 0x45, // @args symbol id, count // @role Decrement the variable #[code primary] by #[code count] and store its value in the given symbol id - DECREMENT_STORE = 0x45, + DECREMENT_STORE = 0x46, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its tail, store it in a new variable #[code secondary] - STORE_TAIL = 0x46, + STORE_TAIL = 0x47, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its tail, store it in a new variable #[code secondary] - STORE_TAIL_BY_INDEX = 0x47, + STORE_TAIL_BY_INDEX = 0x48, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its head, store it in a new variable #[code secondary] - STORE_HEAD = 0x48, + STORE_HEAD = 0x49, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its head, store it in a new variable #[code secondary] - STORE_HEAD_BY_INDEX = 0x49, + STORE_HEAD_BY_INDEX = 0x4a, // @args number, symbol id // @role Create a list of #[code number] elements, and store it in a new variable #[code secondary] - STORE_LIST = 0x4a, + STORE_LIST = 0x4b, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its tail, store it in an existing variable #[code secondary] - SET_VAL_TAIL = 0x4b, + SET_VAL_TAIL = 0x4c, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its tail, store it in an existing variable #[code secondary] - SET_VAL_TAIL_BY_INDEX = 0x4c, + SET_VAL_TAIL_BY_INDEX = 0x4d, // @args symbol id, symbol id // @role Load the symbol #[code primary], compute its head, store it in an existing variable #[code secondary] - SET_VAL_HEAD = 0x4d, + SET_VAL_HEAD = 0x4e, // @args symbol index, symbol id // @role Load the symbol #[code primary], compute its head, store it in an existing variable #[code secondary] - SET_VAL_HEAD_BY_INDEX = 0x4e, + SET_VAL_HEAD_BY_INDEX = 0x4f, // @args builtin id, argument count // @role Call a builtin by its id in #[code primary], with #[code secondary] arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack - CALL_BUILTIN = 0x4f, + CALL_BUILTIN = 0x50, // @args constant id, absolute address to jump to // @role Compare #[code TS < constant], if the comparison fails, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_FALSE = 0x50, + LT_CONST_JUMP_IF_FALSE = 0x51, // @args constant id, absolute address to jump to // @role Compare #[code TS < constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_TRUE = 0x51, + LT_CONST_JUMP_IF_TRUE = 0x52, // @args symbol id, absolute address to jump to // @role Compare #[code TS < symbol], if the comparison fails, jump to the given address. Otherwise, does nothing - LT_SYM_JUMP_IF_FALSE = 0x52, + LT_SYM_JUMP_IF_FALSE = 0x53, // @args constant id, absolute address to jump to // @role Compare #[code TS > constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_TRUE = 0x53, + GT_CONST_JUMP_IF_TRUE = 0x54, // @args constant id, absolute address to jump to // @role Compare #[code TS > constant], if the comparison fails, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_FALSE = 0x54, + GT_CONST_JUMP_IF_FALSE = 0x55, // @args symbol id, absolute address to jump to // @role Compare #[code TS > symbol], if the comparison fails, jump to the given address. Otherwise, does nothing - GT_SYM_JUMP_IF_FALSE = 0x55, + GT_SYM_JUMP_IF_FALSE = 0x56, // @args constant id, absolute address to jump to // @role Compare #[code TS == constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_CONST_JUMP_IF_TRUE = 0x56, + EQ_CONST_JUMP_IF_TRUE = 0x57, // @args symbol index, absolute address to jump to // @role Compare #[code TS == symbol], if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_SYM_INDEX_JUMP_IF_TRUE = 0x57, + EQ_SYM_INDEX_JUMP_IF_TRUE = 0x58, // @args constant id, absolute address to jump to // @role Compare #[code TS != constant], if the comparison succeeds, jump to the given address. Otherwise, does nothing - NEQ_CONST_JUMP_IF_TRUE = 0x58, + NEQ_CONST_JUMP_IF_TRUE = 0x59, // @args symbol id, absolute address to jump to // @role Compare #[code TS != symbol], if the comparison fails, jump to the given address. Otherwise, does nothing - NEQ_SYM_JUMP_IF_FALSE = 0x59, + NEQ_SYM_JUMP_IF_FALSE = 0x5a, // @args symbol id, argument count // @role Call a symbol by its id in #[code primary], with #[code secondary] arguments - CALL_SYMBOL = 0x5a, + CALL_SYMBOL = 0x5b, // @args symbol id, field id in symbols table // @role Push the field of a given symbol (which has to be a closure) on the stack - GET_FIELD_FROM_SYMBOL = 0x5b, + GET_FIELD_FROM_SYMBOL = 0x5c, // @args symbol index, field id in symbols table // @role Push the field of a given symbol (which has to be a closure) on the stack - GET_FIELD_FROM_SYMBOL_INDEX = 0x5c, + GET_FIELD_FROM_SYMBOL_INDEX = 0x5d, // @args symbol id, symbol id2 // @role Push symbol[symbol2] - AT_SYM_SYM = 0x5d, + AT_SYM_SYM = 0x5e, // @args symbol index, symbol index2 // @role Push symbol[symbol2] - AT_SYM_INDEX_SYM_INDEX = 0x5e, + AT_SYM_INDEX_SYM_INDEX = 0x5f, // @args symbol id, constant id // @role Check that the type of symbol is the given constant, push true if so, false otherwise - CHECK_TYPE_OF = 0x5f, + CHECK_TYPE_OF = 0x60, // @args symbol index, constant id // @role Check that the type of symbol is the given constant, push true if so, false otherwise - CHECK_TYPE_OF_BY_INDEX = 0x60, + CHECK_TYPE_OF_BY_INDEX = 0x61, // @args symbol id, number of elements // @role Append N elements to a reference to a list (symbol id), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention - APPEND_IN_PLACE_SYM = 0x61, + APPEND_IN_PLACE_SYM = 0x62, // @args symbol index, number of elements // @role Append N elements to a reference to a list (symbol index), the list is being mutated in-place, no new object created. Elements are stored in TS(1)..TS(N). Follows the function calling convention - APPEND_IN_PLACE_SYM_INDEX = 0x62, + APPEND_IN_PLACE_SYM_INDEX = 0x63, InstructionsCount }; @@ -409,6 +412,7 @@ namespace Ark::internal "JUMP", "RET", "HALT", + "PUSH_RETURN_ADDRESS", "CALL", "CAPTURE", "BUILTIN", diff --git a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp index 89532c74..c919c9aa 100644 --- a/include/Ark/Compiler/Lowerer/ASTLowerer.hpp +++ b/include/Ark/Compiler/Lowerer/ASTLowerer.hpp @@ -186,6 +186,7 @@ namespace Ark::internal void compileLetMutSet(Keyword n, const Node& x, Page p); void compileWhile(const Node& x, Page p); void compilePluginImport(const Node& x, Page p); + void pushFunctionCallArguments(const Node& call, Page p, bool is_tail_call); void handleCalls(const Node& x, Page p, bool is_result_unused, bool is_terminal, const std::string& var_name); /** diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index b49b2f22..5f6d79d8 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -295,17 +296,6 @@ namespace Ark */ inline Value* popAndResolveAsPtr(internal::ExecutionContext& context); - /** - * @brief Move stack values around and invert them - * @details values: 1, 2, 3, _, _ - * wanted: pp, ip, 3, 2, 1 - * positions: 0, 1, 2, 3, 4 - * - * @param argc number of arguments to swap around - * @param context - */ - inline void swapStackForFunCall(uint16_t argc, internal::ExecutionContext& context); - // ================================================ // locals related // ================================================ diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index 5b0936f3..a39fb744 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -16,10 +16,14 @@ Value VM::call(const std::string& name, Args&&... args) const auto it = std::ranges::find(m_state.m_symbols, name); assert(it != m_state.m_symbols.end() && "Unbound variable"); - // convert and push arguments in reverse order + // we need to push pp then ip manually, because the PUSH_RETURN_ADDRESS instruction is not present + push(Value(static_cast(0)), context); + push(Value(ValueType::InstPtr, static_cast(0)), context); + + // convert and push arguments std::vector fnargs { { Value(std::forward(args))... } }; - for (auto it2 = fnargs.rbegin(), it_end = fnargs.rend(); it2 != it_end; ++it2) - push(*it2, context); + for (auto&& arg : fnargs | std::views::reverse) + push(arg, context); // find function object and push it if it's a pageaddr/closure if (const auto dist = std::distance(m_state.m_symbols.begin(), it); std::cmp_less(dist, std::numeric_limits::max())) @@ -58,9 +62,13 @@ inline Value VM::resolve(internal::ExecutionContext* context, const std::vector< const std::size_t ip = context->ip; const std::size_t pp = context->pp; - // convert and push arguments in reverse order - for (auto it = n.begin() + 1, it_end = n.end(); it != it_end; ++it) - push(*it, *context); + // we need to push pp then ip manually, because the PUSH_RETURN_ADDRESS instruction is not present + push(Value(static_cast(pp)), *context); + push(Value(ValueType::InstPtr, static_cast(ip)), *context); + + // convert and push arguments + for (auto&& val : std::ranges::drop_view(n, 1) | std::views::reverse) + push(val, *context); push(n[0], *context); const std::size_t frames_count = context->fc; @@ -195,61 +203,6 @@ inline Value* VM::popAndResolveAsPtr(internal::ExecutionContext& context) return tmp; } -inline void VM::swapStackForFunCall(const uint16_t argc, internal::ExecutionContext& context) -{ - using namespace internal; - - // move values around and invert them - // - // values: 1, 2, 3, _, _ - // wanted: pp, ip, 3, 2, 1 - // positions: 0, 1, 2, 3, 4 - // - // move values first, from position x to y, with - // y = argc - x + 1 - // then place pp and ip - switch (argc) // must be positive - { - case 0: - push(Value(static_cast(context.pp)), context); - push(Value(ValueType::InstPtr, static_cast(context.ip)), context); - break; - - case 1: - context.stack[context.sp + 1] = context.stack[context.sp - 1]; - context.stack[context.sp - 1] = Value(static_cast(context.pp)); - context.stack[context.sp + 0] = Value(ValueType::InstPtr, static_cast(context.ip)); - context.sp += 2; - break; - - default: // 2 or more elements - { - const auto first = static_cast(context.sp - argc); - // move first argument to the very end - context.stack[context.sp + 1] = context.stack[static_cast(first + 0)]; - // move second argument right before the last one - context.stack[context.sp + 0] = context.stack[static_cast(first + 1)]; - // move the rest, if any - uint16_t x = 2; - const uint16_t stop = ((argc % 2 == 0) ? argc : (argc - 1)) / 2; - while (x <= stop) - { - // destination, origin - std::swap( - context.stack[static_cast(context.sp - x + 1)], - context.stack[static_cast(first + x)]); - ++x; - } - context.stack[static_cast(first + 0)] = Value(static_cast(context.pp)); - context.stack[static_cast(first + 1)] = Value(ValueType::InstPtr, static_cast(context.ip)); - context.sp += 2; - break; - } - } - - context.fc++; -} - #pragma endregion inline Value* VM::findNearestVariable(const uint16_t id, internal::ExecutionContext& context) noexcept @@ -280,8 +233,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V */ using namespace internal; - // stack pointer + 2 because we push IP and PP - if (context.sp + 2u >= VMStackSize) [[unlikely]] + if (std::cmp_greater_equal(context.sp + 2, VMStackSize)) [[unlikely]] throwVMError( ErrorKind::VM, fmt::format( @@ -307,12 +259,12 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V // create dedicated scope context.locals.emplace_back(context.scopes_storage.data(), context.locals.back().storageEnd()); - swapStackForFunCall(argc, context); // store "reference" to the function to speed the recursive functions if (context.last_symbol < m_state.m_symbols.size()) [[likely]] context.locals.back().push_back(context.last_symbol, function); + context.fc++; context.pp = new_page_pointer; context.ip = 0; break; @@ -330,8 +282,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V c.refScope().mergeRefInto(context.locals.back()); context.stacked_closure_scopes.back() = c.scopePtr(); - swapStackForFunCall(argc, context); - + context.fc++; context.pp = new_page_pointer; context.ip = 0; break; @@ -373,12 +324,13 @@ inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& bu // because we pull `argc` from the CALL instruction generated by the compiler, // we are guaranteed to have `argc` values pushed on the stack ; thus we can // skip the `if (context.sp > 0)` check - Value* val = &context.stack[context.sp - argc + j]; + Value* val = &context.stack[context.sp - 1 - j]; if (val->valueType() == ValueType::Reference) val = val->reference(); args.emplace_back(*val); } - context.sp -= argc; + // +2 to skip PP/IP that were pushed by PUSH_RETURN_ADDRESS + context.sp -= argc + 2; // call proc push(builtin.proc()(args, this), context); } diff --git a/src/arkreactor/Compiler/AST/Node.cpp b/src/arkreactor/Compiler/AST/Node.cpp index 778a34fb..674ec7d9 100644 --- a/src/arkreactor/Compiler/AST/Node.cpp +++ b/src/arkreactor/Compiler/AST/Node.cpp @@ -3,6 +3,7 @@ #include +#include #include namespace Ark::internal diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 7503dbd4..a5aef72d 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -1,7 +1,9 @@ #include #include +#include #include +#include #include #include #include @@ -490,6 +492,33 @@ namespace Ark::internal page(p).back().setSourceLocation(x.filename(), x.line()); } + void ASTLowerer::pushFunctionCallArguments(const Node& call, const Page p, const bool is_tail_call) + { + const auto node = call.constList()[0]; + + // push the arguments in reverse order because the function loads its arguments in the order they are defined: + // (fun (a b c) ...) -> load 'a', then 'b', then 'c' + // We have to push arguments in this order and load them in reverse, because we are using references internally, + // which can cause problems for recursive functions that swap their arguments around. + // Eg (let foo (fun (a b c) (if (> a 0) (foo (- a 1) c (+ b c)) 1))) (foo 12 0 1) + // On the second self-call, b and c would have the same value, since we set c to (+ b c), and we pushed c as the + // value for argument b, but loaded it as a reference. + for (auto&& value : std::ranges::drop_view(call.constList(), 1) | std::views::reverse) + { + if (nodeProducesOutput(value)) + compileExpression(value, p, false, false); + else + { + std::string message; + if (is_tail_call) + message = fmt::format("Invalid node inside tail call to `{}'", node.repr()); + else + message = fmt::format("Invalid node inside call to `{}'", node.repr()); + buildAndThrowError(message, value); + } + } + } + void ASTLowerer::handleCalls(const Node& x, const Page p, bool is_result_unused, const bool is_terminal, const std::string& var_name) { constexpr std::size_t start_index = 1; @@ -536,14 +565,7 @@ namespace Ark::internal { if (is_terminal && x.constList()[0].nodeType() == NodeType::Symbol && var_name == x.constList()[0].string()) { - // push the arguments in reverse order - for (std::size_t i = x.constList().size() - 1; i >= start_index; --i) - { - if (nodeProducesOutput(x.constList()[i])) - compileExpression(x.constList()[i], p, false, false); - else - buildAndThrowError(fmt::format("Invalid node inside tail call to `{}'", node.repr()), x.constList()[i]); - } + pushFunctionCallArguments(x, p, /* is_tail_call= */ true); // jump to the top of the function page(p).emplace_back(JUMP, 0_u16); @@ -559,14 +581,10 @@ namespace Ark::internal if (m_temp_pages.back().empty()) buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); - // push arguments on current page - for (auto exp = x.constList().begin() + start_index, exp_end = x.constList().end(); exp != exp_end; ++exp) - { - if (nodeProducesOutput(*exp)) - compileExpression(*exp, p, false, false); - else - buildAndThrowError(fmt::format("Invalid node inside call to `{}'", node.repr()), *exp); - } + const auto label_return = IR::Entity::Label(m_current_label++); + page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); + + pushFunctionCallArguments(x, p, /* is_tail_call= */ false); // push proc from temp page for (const auto& inst : m_temp_pages.back()) page(p).push_back(inst); @@ -574,7 +592,7 @@ namespace Ark::internal // number of arguments std::size_t args_count = 0; - for (auto it = x.constList().begin() + 1, it_end = x.constList().end(); it != it_end; ++it) + for (auto it = x.constList().begin() + start_index, it_end = x.constList().end(); it != it_end; ++it) { if (it->nodeType() != NodeType::Capture) args_count++; @@ -582,6 +600,9 @@ namespace Ark::internal // call the procedure page(p).emplace_back(CALL, args_count); page(p).back().setSourceLocation(node.filename(), node.line()); + + // patch the PUSH_RETURN_ADDRESS instruction with the return location (IP=CALL instruction IP) + page(p).emplace_back(label_return); } } else // operator diff --git a/src/arkreactor/Exceptions.cpp b/src/arkreactor/Exceptions.cpp index e3ca0478..9903b546 100644 --- a/src/arkreactor/Exceptions.cpp +++ b/src/arkreactor/Exceptions.cpp @@ -195,7 +195,7 @@ namespace Ark::Diagnostics show_file_location(); LineColorContextCounts line_color_context_counts; - for (auto i = first_line; i < last_line; ++i) + for (auto i = first_line; i < last_line && i < lines.size(); ++i) { if (i >= start_line_skipping_at && i < stop_line_skipping_at) continue; @@ -239,10 +239,10 @@ namespace Ark::Diagnostics "{: <{}}{:~<{}}\n", // padding of spaces " ", - std::max(1_z, curr_col_start), // fixing padding when the error is on the first character + std::max(1_z, std::min(curr_col_start, col_end)), // fixing padding when the error is on the first character // underline the error in red fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()), - col_end - curr_col_start); + curr_col_start < col_end ? col_end - curr_col_start : 1); else if (i == target_line) // maybe_context has a value, i == target_line to avoid having to deal with overflow { const auto padding_size = std::max(1_z, maybe_context->col); diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index eeefc85a..0a3d3d69 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -497,6 +496,7 @@ namespace Ark &&TARGET_JUMP, &&TARGET_RET, &&TARGET_HALT, + &&TARGET_PUSH_RETURN_ADDRESS, &&TARGET_CALL, &&TARGET_CAPTURE, &&TARGET_BUILTIN, @@ -705,6 +705,14 @@ namespace Ark GOTO_HALT(); } + TARGET(PUSH_RETURN_ADDRESS) + { + push(Value(static_cast(context.pp)), context); + // arg * 4 to skip over the call instruction, so that the return address points to AFTER the call + push(Value(ValueType::InstPtr, static_cast(arg * 4)), context); + DISPATCH(); + } + TARGET(CALL) { call(context, arg); @@ -1775,8 +1783,8 @@ namespace Ark TARGET(CHECK_TYPE_OF) { UNPACK_ARGS(); - Value* sym = loadSymbol(primary_arg, context); - Value* cst = loadConstAsPtr(secondary_arg); + const Value* sym = loadSymbol(primary_arg, context); + const Value* cst = loadConstAsPtr(secondary_arg); push( cst->valueType() == ValueType::String && types_to_str[static_cast(sym->valueType())] == cst->string() @@ -1789,8 +1797,8 @@ namespace Ark TARGET(CHECK_TYPE_OF_BY_INDEX) { UNPACK_ARGS(); - Value* sym = loadSymbolFromIndex(primary_arg, context); - Value* cst = loadConstAsPtr(secondary_arg); + const Value* sym = loadSymbolFromIndex(primary_arg, context); + const Value* cst = loadConstAsPtr(secondary_arg); push( cst->valueType() == ValueType::String && types_to_str[static_cast(sym->valueType())] == cst->string() @@ -1897,7 +1905,7 @@ namespace Ark // -1 on the stack because we always point to the next available slot arg_vals.push_back(context.stack[context.sp - i - 1].toString(*this)); - // set ip/pp to the callee location so that the error can pin-point the line + // set ip/pp to the callee location so that the error can pinpoint the line // where the bad call happened if (context.sp >= 2 + passed_arg_count) { @@ -1963,7 +1971,7 @@ namespace Ark std::string VM::debugShowSource() { - auto& context = m_execution_contexts.front(); + const auto& context = m_execution_contexts.front(); auto maybe_source_loc = findSourceLocation(context->ip, context->pp); if (maybe_source_loc) { @@ -1978,6 +1986,7 @@ namespace Ark const std::size_t saved_ip = context.ip; const std::size_t saved_pp = context.pp; const uint16_t saved_sp = context.sp; + const std::size_t max_consecutive_traces = 7; const auto maybe_location = findSourceLocation(context.ip, context.pp); if (maybe_location) @@ -2047,19 +2056,21 @@ namespace Ark context.pp = pop(context)->pageAddr(); returnFromFuncCall(context); } - else - { - fmt::println(os, "[{:4}] In global scope{}", fmt::styled(context.fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), loc_as_text); - break; - } - if (displayed_traces > 7) + if (displayed_traces > max_consecutive_traces) { - fmt::println(os, "..."); + fmt::println(os, " ..."); break; } } + if (context.pp == 0) + { + const auto maybe_call_loc = findSourceLocation(context.ip, context.pp); + const auto loc_as_text = maybe_call_loc ? fmt::format(" ({}:{})", m_state.m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) : ""; + fmt::println(os, "[{:4}] In global scope{}", fmt::styled(context.fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), loc_as_text); + } + // display variables values in the current scope fmt::println(os, "\nCurrent scope variables values:"); for (std::size_t i = 0, size = old_scope.size(); i < size; ++i) diff --git a/src/arkscript/main.cpp b/src/arkscript/main.cpp index 72cf4473..eda36faf 100644 --- a/src/arkscript/main.cpp +++ b/src/arkscript/main.cpp @@ -14,6 +14,12 @@ #include #include +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +# define ARK_ERROR_EXIT_CODE 0 +#else +# define ARK_ERROR_EXIT_CODE -1 +#endif + int main(int argc, char** argv) { using namespace clipp; @@ -247,7 +253,7 @@ int main(int argc, char** argv) state.setDebug(debug); if (!state.doFile(file, passes)) - return -1; + return ARK_ERROR_EXIT_CODE; break; } @@ -259,7 +265,7 @@ int main(int argc, char** argv) state.setArgs(script_args); if (!state.doFile(file, passes)) - return -1; + return ARK_ERROR_EXIT_CODE; Ark::VM vm(state); return vm.run(); @@ -273,7 +279,7 @@ int main(int argc, char** argv) if (!state.doString(eval_expression)) { std::cerr << "Could not evaluate expression\n"; - return -1; + return ARK_ERROR_EXIT_CODE; } Ark::VM vm(state); @@ -317,7 +323,7 @@ int main(int argc, char** argv) catch (const std::exception& e) { std::cerr << e.what() << std::endl; - return -1; + return ARK_ERROR_EXIT_CODE; } break; } diff --git a/tests/benchmarks/main.cpp b/tests/benchmarks/main.cpp index f15035c6..4fca211c 100644 --- a/tests/benchmarks/main.cpp +++ b/tests/benchmarks/main.cpp @@ -29,7 +29,7 @@ std::string get_resource(const std::string& path) ARK_CREATE_RUNTIME_BENCH(quicksort); ARK_CREATE_RUNTIME_BENCH(ackermann)->Iterations(50); ARK_CREATE_RUNTIME_BENCH(fibonacci)->Iterations(100); -ARK_CREATE_RUNTIME_BENCH(man_or_boy); +// ARK_CREATE_RUNTIME_BENCH(man_or_boy); ARK_CREATE_RUNTIME_BENCH(builtins); ARK_CREATE_RUNTIME_BENCH(binary_trees); ARK_CREATE_RUNTIME_BENCH(for_sum); diff --git a/tests/unittests/Suites/BytecodeReaderSuite.cpp b/tests/unittests/Suites/BytecodeReaderSuite.cpp index c46e32fc..e0e5f84f 100644 --- a/tests/unittests/Suites/BytecodeReaderSuite.cpp +++ b/tests/unittests/Suites/BytecodeReaderSuite.cpp @@ -121,10 +121,8 @@ ut::suite<"BytecodeReader"> bcr_suite = [] { should("list all code page") = [inst_locations_block, pages, start_code] { expect(that % start_code == inst_locations_block.end); expect(that % pages.size() == 2ull); - // 7 instructions on 4 bytes - expect(that % pages[0].size() == 7 * 4ull); - // 19 instructions on 4 bytes - expect(that % pages[1].size() == 19 * 4ull); + expect(that % pages[0].size() == 9 * 4ull); + expect(that % pages[1].size() == 20 * 4ull); }; }; }; diff --git a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected index 0eee12a9..6d629619 100644 --- a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected @@ -30,24 +30,32 @@ page_0 LOAD_CONST 0 GT 0 POP_JUMP_IF_FALSE L5 - LOAD_CONST 3 + PUSH_RETURN_ADDRESS L6 + PUSH_RETURN_ADDRESS L7 LOAD_SYMBOL 3 LOAD_SYMBOL 3 + LOAD_CONST 3 BUILTIN 24 CALL 3 +.L7: BUILTIN 9 CALL 1 +.L6: POP 0 LOAD_SYMBOL 3 LOAD_CONST 0 SUB 0 SET_VAL 3 - LOAD_CONST 4 + PUSH_RETURN_ADDRESS L8 + PUSH_RETURN_ADDRESS L9 LOAD_SYMBOL 3 + LOAD_CONST 4 BUILTIN 24 CALL 2 +.L9: BUILTIN 9 CALL 1 +.L8: POP 0 RESET_SCOPE_JUMP L4 .L5: diff --git a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected index df8534e9..cbe8de45 100644 --- a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected @@ -1,13 +1,17 @@ page_0 LOAD_CONST 0 STORE 0 + PUSH_RETURN_ADDRESS L5 + PUSH_RETURN_ADDRESS L6 LOAD_CONST 3 LOAD_CONST 4 - LOAD_CONST 5 LOAD_SYMBOL_BY_INDEX 0 CALL 2 +.L6: + LOAD_CONST 5 BUILTIN 9 CALL 2 +.L5: HALT 0 page_1 @@ -26,24 +30,26 @@ page_1 LOAD_SYMBOL_BY_INDEX 0 EQ 0 POP_JUMP_IF_TRUE L2 - LOAD_SYMBOL_BY_INDEX 1 + PUSH_RETURN_ADDRESS L3 LOAD_SYMBOL_BY_INDEX 0 LOAD_CONST 2 SUB 0 + LOAD_SYMBOL_BY_INDEX 1 LOAD_SYMBOL 0 CALL 2 +.L3: LOAD_SYMBOL_BY_INDEX 1 LOAD_CONST 2 SUB 0 JUMP 0 - JUMP L3 + JUMP L4 .L2: LOAD_CONST 2 LOAD_SYMBOL_BY_INDEX 1 LOAD_CONST 2 SUB 0 JUMP 0 -.L3: +.L4: .L1: RET 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/closures.expected b/tests/unittests/resources/CompilerSuite/ir/closures.expected index 2418a1f5..0db3bce0 100644 --- a/tests/unittests/resources/CompilerSuite/ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/ir/closures.expected @@ -1,68 +1,96 @@ page_0 LOAD_CONST 0 STORE 0 + PUSH_RETURN_ADDRESS L0 LOAD_CONST 3 LOAD_CONST 4 LOAD_CONST 5 LOAD_SYMBOL_BY_INDEX 0 CALL 3 +.L0: STORE 6 + PUSH_RETURN_ADDRESS L1 LOAD_CONST 6 LOAD_CONST 7 LOAD_CONST 8 LOAD_SYMBOL_BY_INDEX 1 CALL 3 +.L1: STORE 7 - LOAD_CONST 9 + PUSH_RETURN_ADDRESS L2 LOAD_SYMBOL_BY_INDEX 1 GET_FIELD 2 + LOAD_CONST 9 BUILTIN 9 CALL 2 +.L2: POP 0 + PUSH_RETURN_ADDRESS L3 LOAD_CONST 10 BUILTIN 9 CALL 1 +.L3: POP 0 + PUSH_RETURN_ADDRESS L4 LOAD_CONST 11 LOAD_SYMBOL_BY_INDEX 1 GET_FIELD 4 CALL 1 +.L4: POP 0 - LOAD_CONST 12 + PUSH_RETURN_ADDRESS L5 LOAD_SYMBOL_BY_INDEX 1 GET_FIELD 2 + LOAD_CONST 12 BUILTIN 9 CALL 2 +.L5: POP 0 - LOAD_CONST 13 + PUSH_RETURN_ADDRESS L6 LOAD_SYMBOL_BY_INDEX 0 GET_FIELD 2 + LOAD_CONST 13 BUILTIN 9 CALL 2 +.L6: POP 0 LOAD_CONST 14 STORE 8 + PUSH_RETURN_ADDRESS L7 LOAD_CONST 17 LOAD_SYMBOL_BY_INDEX 0 CALL 1 +.L7: STORE 10 - LOAD_CONST 18 + PUSH_RETURN_ADDRESS L8 + PUSH_RETURN_ADDRESS L9 LOAD_SYMBOL_BY_INDEX 0 CALL 0 +.L9: + LOAD_CONST 18 BUILTIN 9 CALL 2 +.L8: POP 0 - LOAD_CONST 18 + PUSH_RETURN_ADDRESS L10 + PUSH_RETURN_ADDRESS L11 LOAD_SYMBOL_BY_INDEX 0 CALL 0 +.L11: + LOAD_CONST 18 BUILTIN 9 CALL 2 +.L10: POP 0 - LOAD_CONST 18 + PUSH_RETURN_ADDRESS L12 + PUSH_RETURN_ADDRESS L13 LOAD_SYMBOL_BY_INDEX 0 CALL 0 +.L13: + LOAD_CONST 18 BUILTIN 9 CALL 2 +.L12: HALT 0 page_1 diff --git a/tests/unittests/resources/CompilerSuite/ir/factorial.expected b/tests/unittests/resources/CompilerSuite/ir/factorial.expected index 8427da39..483d829d 100644 --- a/tests/unittests/resources/CompilerSuite/ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/ir/factorial.expected @@ -1,12 +1,16 @@ page_0 LOAD_CONST 0 STORE 0 + PUSH_RETURN_ADDRESS L2 + PUSH_RETURN_ADDRESS L3 LOAD_CONST 3 - LOAD_CONST 4 LOAD_SYMBOL_BY_INDEX 0 CALL 1 +.L3: + LOAD_CONST 4 BUILTIN 9 CALL 2 +.L2: HALT 0 page_1 diff --git a/tests/unittests/resources/CompilerSuite/ir/plugin.expected b/tests/unittests/resources/CompilerSuite/ir/plugin.expected index 7e0662b1..d1d7c6e6 100644 --- a/tests/unittests/resources/CompilerSuite/ir/plugin.expected +++ b/tests/unittests/resources/CompilerSuite/ir/plugin.expected @@ -1,9 +1,11 @@ page_0 PLUGIN 0 LOAD_CONST 1 + PUSH_RETURN_ADDRESS L0 LOAD_CONST 2 LOAD_SYMBOL 0 CALL 1 +.L0: EQ 0 LOAD_CONST 3 ASSERT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected index 72e46ddb..87a548db 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected @@ -27,20 +27,29 @@ page_0 .L4: LOAD_SYMBOL 3 GT_CONST_JUMP_IF_FALSE L5, 0 - LOAD_CONST 3 + PUSH_RETURN_ADDRESS L6 + PUSH_RETURN_ADDRESS L7 LOAD_SYMBOL 3 LOAD_SYMBOL 3 + LOAD_CONST 3 CALL_BUILTIN 24, 3 +.L7: CALL_BUILTIN 9, 1 +.L6: POP 0 DECREMENT 3, 1 SET_VAL 3 - LOAD_CONST 4 + PUSH_RETURN_ADDRESS L8 + PUSH_RETURN_ADDRESS L9 LOAD_SYMBOL 3 + LOAD_CONST 4 CALL_BUILTIN 24, 2 +.L9: CALL_BUILTIN 9, 1 +.L8: POP 0 RESET_SCOPE_JUMP L4 .L5: POP_SCOPE 0 HALT 0 + diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected index 1f61bd7a..9ed66806 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected @@ -1,10 +1,14 @@ page_0 LOAD_CONST_STORE 0, 0 + PUSH_RETURN_ADDRESS L5 + PUSH_RETURN_ADDRESS L6 LOAD_CONST_LOAD_CONST 3, 4 - LOAD_CONST 5 LOAD_SYMBOL_BY_INDEX 0 CALL 2 +.L6: + LOAD_CONST 5 CALL_BUILTIN 9, 2 +.L5: HALT 0 page_1 @@ -17,17 +21,19 @@ page_1 .L0: LOAD_CONST 1 EQ_SYM_INDEX_JUMP_IF_TRUE L2, 0 - LOAD_SYMBOL_BY_INDEX 1 + PUSH_RETURN_ADDRESS L3 DECREMENT_BY_INDEX 0, 1 + LOAD_SYMBOL_BY_INDEX 1 CALL_SYMBOL 0, 2 +.L3: DECREMENT_BY_INDEX 1, 1 JUMP 0 - JUMP L3 + JUMP L4 .L2: LOAD_CONST 2 DECREMENT_BY_INDEX 1, 1 JUMP 0 -.L3: +.L4: .L1: RET 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected index 525a45f5..38e1ed89 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected @@ -1,38 +1,54 @@ page_0 LOAD_CONST_STORE 0, 0 + PUSH_RETURN_ADDRESS L0 LOAD_CONST_LOAD_CONST 3, 4 LOAD_CONST 5 LOAD_SYMBOL_BY_INDEX 0 CALL 3 +.L0: STORE 6 + PUSH_RETURN_ADDRESS L1 LOAD_CONST_LOAD_CONST 6, 7 LOAD_CONST 8 LOAD_SYMBOL_BY_INDEX 1 CALL 3 +.L1: STORE 7 - LOAD_CONST 9 + PUSH_RETURN_ADDRESS L2 GET_FIELD_FROM_SYMBOL_INDEX 1, 2 + LOAD_CONST 9 CALL_BUILTIN 9, 2 +.L2: POP 0 + PUSH_RETURN_ADDRESS L3 LOAD_CONST 10 CALL_BUILTIN 9, 1 +.L3: POP 0 + PUSH_RETURN_ADDRESS L4 LOAD_CONST 11 GET_FIELD_FROM_SYMBOL_INDEX 1, 4 CALL 1 +.L4: POP 0 - LOAD_CONST 12 + PUSH_RETURN_ADDRESS L5 GET_FIELD_FROM_SYMBOL_INDEX 1, 2 + LOAD_CONST 12 CALL_BUILTIN 9, 2 +.L5: POP 0 - LOAD_CONST 13 + PUSH_RETURN_ADDRESS L6 GET_FIELD_FROM_SYMBOL_INDEX 0, 2 + LOAD_CONST 13 CALL_BUILTIN 9, 2 +.L6: POP 0 LOAD_CONST_STORE 14, 8 + PUSH_RETURN_ADDRESS L7 LOAD_CONST 17 LOAD_SYMBOL_BY_INDEX 0 CALL 1 +.L7: STORE 10 LOAD_CONST_STORE 18, 11 HALT 0 @@ -76,19 +92,33 @@ page_5 HALT 0 page_6 - LOAD_CONST 19 + PUSH_RETURN_ADDRESS L8 + PUSH_RETURN_ADDRESS L9 CALL_SYMBOL 10, 0 +.L9: + LOAD_CONST 19 CALL_BUILTIN 9, 2 +.L8: POP 0 - LOAD_CONST 19 + PUSH_RETURN_ADDRESS L10 + PUSH_RETURN_ADDRESS L11 CALL_SYMBOL 10, 0 +.L11: + LOAD_CONST 19 CALL_BUILTIN 9, 2 +.L10: POP 0 - LOAD_CONST 19 + PUSH_RETURN_ADDRESS L12 + PUSH_RETURN_ADDRESS L13 CALL_SYMBOL 10, 0 +.L13: + LOAD_CONST 19 CALL_BUILTIN 9, 2 +.L12: POP 0 + PUSH_RETURN_ADDRESS L14 GET_FIELD_FROM_SYMBOL 10, 9 CALL_BUILTIN 9, 1 +.L14: RET 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected index 4b1ee825..de7675e0 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected @@ -1,9 +1,14 @@ page_0 LOAD_CONST_STORE 0, 0 - LOAD_CONST_LOAD_CONST 3, 4 + PUSH_RETURN_ADDRESS L2 + PUSH_RETURN_ADDRESS L3 + LOAD_CONST 3 LOAD_SYMBOL_BY_INDEX 0 CALL 1 +.L3: + LOAD_CONST 4 CALL_BUILTIN 9, 2 +.L2: HALT 0 page_1 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/jumps.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/jumps.expected index 3ed00cd9..08e7591e 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/jumps.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/jumps.expected @@ -5,94 +5,114 @@ page_0 .L0: LOAD_SYMBOL 2 LT_CONST_JUMP_IF_FALSE L1, 2 + PUSH_RETURN_ADDRESS L2 LOAD_SYMBOL 2 CALL_SYMBOL 1, 1 +.L2: POP 0 INCREMENT_STORE 2, 1 RESET_SCOPE_JUMP L0 .L1: POP_SCOPE 0 LOAD_SYMBOL 3 - LT_CONST_JUMP_IF_TRUE L2, 4 - JUMP L3 -.L2: + LT_CONST_JUMP_IF_TRUE L3, 4 + JUMP L4 +.L3: + PUSH_RETURN_ADDRESS L5 LOAD_SYMBOL 3 CALL_BUILTIN 9, 1 +.L5: POP 0 -.L3: +.L4: LOAD_CONST_STORE 2, 3 CREATE_SCOPE 0 -.L4: +.L6: LOAD_SYMBOL 2 - LT_SYM_JUMP_IF_FALSE L5, 3 + LT_SYM_JUMP_IF_FALSE L7, 3 + PUSH_RETURN_ADDRESS L8 LOAD_SYMBOL 2 CALL_SYMBOL 1, 1 +.L8: POP 0 INCREMENT_STORE 2, 1 - RESET_SCOPE_JUMP L4 -.L5: + RESET_SCOPE_JUMP L6 +.L7: POP_SCOPE 0 LOAD_SYMBOL_BY_INDEX 0 - GT_CONST_JUMP_IF_TRUE L6, 4 - JUMP L7 -.L6: + GT_CONST_JUMP_IF_TRUE L9, 4 + JUMP L10 +.L9: + PUSH_RETURN_ADDRESS L11 LOAD_SYMBOL_BY_INDEX 0 CALL_BUILTIN 9, 1 +.L11: POP 0 -.L7: +.L10: CREATE_SCOPE 0 -.L8: +.L12: LOAD_SYMBOL 2 - GT_CONST_JUMP_IF_FALSE L9, 5 + GT_CONST_JUMP_IF_FALSE L13, 5 + PUSH_RETURN_ADDRESS L14 LOAD_SYMBOL 2 CALL_BUILTIN 9, 1 +.L14: POP 0 - RESET_SCOPE_JUMP L8 -.L9: + RESET_SCOPE_JUMP L12 +.L13: POP_SCOPE 0 CREATE_SCOPE 0 -.L10: +.L15: LOAD_SYMBOL 3 - GT_SYM_JUMP_IF_FALSE L11, 2 + GT_SYM_JUMP_IF_FALSE L16, 2 + PUSH_RETURN_ADDRESS L17 LOAD_SYMBOL 3 CALL_BUILTIN 9, 1 +.L17: POP 0 - RESET_SCOPE_JUMP L10 -.L11: + RESET_SCOPE_JUMP L15 +.L16: POP_SCOPE 0 LOAD_SYMBOL_BY_INDEX 1 - EQ_CONST_JUMP_IF_TRUE L12, 4 - JUMP L13 -.L12: + EQ_CONST_JUMP_IF_TRUE L18, 4 + JUMP L19 +.L18: + PUSH_RETURN_ADDRESS L20 LOAD_CONST 6 CALL_BUILTIN 9, 1 +.L20: POP 0 -.L13: +.L19: LOAD_SYMBOL_BY_INDEX 1 - EQ_SYM_INDEX_JUMP_IF_TRUE L14, 0 - JUMP L15 -.L14: + EQ_SYM_INDEX_JUMP_IF_TRUE L21, 0 + JUMP L22 +.L21: + PUSH_RETURN_ADDRESS L23 LOAD_CONST 7 CALL_BUILTIN 9, 1 +.L23: POP 0 -.L15: +.L22: LOAD_SYMBOL_BY_INDEX 1 - NEQ_CONST_JUMP_IF_TRUE L16, 4 - JUMP L17 -.L16: + NEQ_CONST_JUMP_IF_TRUE L24, 4 + JUMP L25 +.L24: + PUSH_RETURN_ADDRESS L26 LOAD_CONST 8 CALL_BUILTIN 9, 1 +.L26: POP 0 -.L17: +.L25: CREATE_SCOPE 0 -.L18: +.L27: LOAD_SYMBOL 2 - NEQ_SYM_JUMP_IF_FALSE L19, 3 + NEQ_SYM_JUMP_IF_FALSE L28, 3 + PUSH_RETURN_ADDRESS L29 LOAD_SYMBOL 2 CALL_BUILTIN 9, 1 +.L29: POP 0 - RESET_SCOPE_JUMP L18 -.L19: + RESET_SCOPE_JUMP L27 +.L28: POP_SCOPE 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/lists.ark b/tests/unittests/resources/CompilerSuite/optimized_ir/lists.ark index 28a4ce1e..f3a31c6e 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/lists.ark +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/lists.ark @@ -24,6 +24,7 @@ # STORE_HEAD_BY_INDEX (mut head_2 (head source)) +(print head_2) # STORE_TAIL_BY_INDEX (mut tail_2 (tail source)) diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected index 6d75a376..3a6f64e9 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected @@ -4,10 +4,17 @@ page_0 STORE_LIST 4, 0 LOAD_CONST_STORE 3, 1 LOAD_CONST_STORE 4, 2 + PUSH_RETURN_ADDRESS L0 LOAD_SYMBOL_BY_INDEX 0 CALL 0 +.L0: POP 0 STORE_HEAD_BY_INDEX 2, 6 + PUSH_RETURN_ADDRESS L1 + LOAD_SYMBOL_BY_INDEX 0 + CALL_BUILTIN 9, 1 +.L1: + POP 0 STORE_TAIL_BY_INDEX 3, 7 STORE_FROM_INDEX 4, 8 SET_VAL_HEAD_BY_INDEX 5, 8 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected index 316ff98c..0942a68b 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected @@ -4,38 +4,48 @@ page_0 POP_JUMP_IF_TRUE L0 JUMP L1 .L0: + PUSH_RETURN_ADDRESS L2 LOAD_CONST 2 CALL_BUILTIN 9, 1 +.L2: POP 0 .L1: CHECK_TYPE_OF_BY_INDEX 0, 1 - POP_JUMP_IF_TRUE L2 - JUMP L3 -.L2: + POP_JUMP_IF_TRUE L3 + JUMP L4 +.L3: + PUSH_RETURN_ADDRESS L5 LOAD_CONST 2 CALL_BUILTIN 9, 1 +.L5: POP 0 -.L3: +.L4: LOAD_CONST_STORE 3, 1 + PUSH_RETURN_ADDRESS L12 LOAD_SYMBOL_BY_INDEX 0 CALL 0 +.L12: HALT 0 page_1 - CHECK_TYPE_OF 0, 1 - POP_JUMP_IF_TRUE L4 - JUMP L5 -.L4: - LOAD_CONST 2 - CALL_BUILTIN 9, 1 - POP 0 -.L5: CHECK_TYPE_OF 0, 1 POP_JUMP_IF_TRUE L6 JUMP L7 .L6: + PUSH_RETURN_ADDRESS L8 LOAD_CONST 2 CALL_BUILTIN 9, 1 +.L8: + POP 0 .L7: + CHECK_TYPE_OF 0, 1 + POP_JUMP_IF_TRUE L9 + JUMP L10 +.L9: + PUSH_RETURN_ADDRESS L11 + LOAD_CONST 2 + CALL_BUILTIN 9, 1 +.L11: +.L10: RET 0 HALT 0 diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/invalid_node_multilines.ark b/tests/unittests/resources/DiagnosticsSuite/compileTime/invalid_node_multilines.ark new file mode 100644 index 00000000..ca98d78a --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/invalid_node_multilines.ark @@ -0,0 +1,3 @@ +000000000080000000000000 +80000000000000000 +( diff --git a/tests/unittests/resources/DiagnosticsSuite/compileTime/invalid_node_multilines.expected b/tests/unittests/resources/DiagnosticsSuite/compileTime/invalid_node_multilines.expected new file mode 100644 index 00000000..d9074810 --- /dev/null +++ b/tests/unittests/resources/DiagnosticsSuite/compileTime/invalid_node_multilines.expected @@ -0,0 +1,9 @@ +At 0 @ 1:1 + 1 | 000000000080000000000000 + | ^~~~~~~~~~~~~~~~~~~~~~~ + 2 | 80000000000000000 + | ^~~~~~~~~~~~~~~~ + 3 | ( + | ^ + 4 | + invalid syntax, expected node diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected index 1e3a4028..a7c0df76 100644 --- a/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected @@ -8,10 +8,10 @@ In file tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3 4 | tmp 5 | })) -[2048] In function `foo' (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3) +[2047] In function `foo' (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3) ... -[ 1] In global scope (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:7) +[ 0] In global scope (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:7) Current scope variables values: foo = Function@1 -a = 2046 +a = 2045