From cb4bcdf84bb6d9c1a3bf17d016f6dffb41628c63 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Sun, 15 Mar 2026 13:33:03 +0100 Subject: [PATCH 01/13] feat(bytecode reader): print the instruction number in hex and display the argument of push_return_address as hex --- src/arkreactor/Compiler/BytecodeReader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/arkreactor/Compiler/BytecodeReader.cpp b/src/arkreactor/Compiler/BytecodeReader.cpp index ce1c294e..d42d60f4 100644 --- a/src/arkreactor/Compiler/BytecodeReader.cpp +++ b/src/arkreactor/Compiler/BytecodeReader.cpp @@ -459,6 +459,7 @@ namespace Ark Constant, Builtin, Raw, ///< eg: Stack index, jump address, number + RawHex, ConstConst, ConstSym, SymConst, @@ -500,6 +501,7 @@ namespace Ark { SET_VAL, ArgKind::Symbol }, { POP_JUMP_IF_FALSE, ArgKind::Raw }, { JUMP, ArgKind::Raw }, + { PUSH_RETURN_ADDRESS, ArgKind::RawHex }, { CALL, ArgKind::Raw }, { CAPTURE, ArgKind::Symbol }, { RENAME_NEXT_CAPTURE, ArgKind::Symbol }, @@ -604,6 +606,9 @@ namespace Ark case ArgKind::Raw: fmt::print(raw_color, " ({})\n", idx); break; + case ArgKind::RawHex: + fmt::print(raw_color, " ({:#x})\n", idx); + break; case ArgKind::ConstConst: fmt::print(" {}, {}\n", fmt::styled(value_str(arg->primary()), const_color), fmt::styled(value_str(arg->secondary()), const_color)); break; @@ -696,7 +701,7 @@ namespace Ark else fmt::print(" "); // instruction number - fmt::print(fmt::fg(fmt::color::cyan), "{:>4}", j / 4); + fmt::print(fmt::fg(fmt::color::cyan), "{:>4x}", j / 4); // padding inst arg arg fmt::print(" {:02x} {:02x} {:02x} {:02x} ", inst, padding, page[j + 2], page[j + 3]); From d5bcd09b83f51f29b6b2300fee932b5b045dbe32 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Sun, 15 Mar 2026 20:50:45 +0100 Subject: [PATCH 02/13] fix: compile and register the arguments of a function call before the function Functions are compiled last to be near their CALL instruction, so we need to compile the arguments first to have them registered by the AST lowerer, and have the correct IDs --- src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index c3703619..26e97350 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -831,6 +831,12 @@ namespace Ark::internal if (!nodeProducesOutput(node)) buildAndThrowError(fmt::format("Can not call `{}', as it doesn't return a value", node.repr()), node); + const auto label_return = IR::Entity::Label(m_current_label++); + page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); + page(p).back().setSourceLocation(x.filename(), x.position().start.line); + + pushFunctionCallArguments(x, p, /* is_tail_call= */ false); + const auto proc_page = createNewCodePage(/* temp= */ true); // compile the function resolution to a separate page @@ -852,11 +858,6 @@ namespace Ark::internal if (m_temp_pages.back().empty()) buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); - const auto label_return = IR::Entity::Label(m_current_label++); - page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); - page(p).back().setSourceLocation(x.filename(), x.position().start.line); - - 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); From ffd1352db538027f96be9acf71c100d2eb5945e2 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 16 Mar 2026 14:14:34 +0100 Subject: [PATCH 03/13] feat!: load the function before the arguments for function calls --- include/Ark/VM/VM.hpp | 3 +- include/Ark/VM/VM.inl | 17 +++--- .../IROptimizer.cpp | 52 +++++++++---------- .../Compiler/Lowerer/ASTLowerer.cpp | 6 +-- src/arkreactor/VM/VM.cpp | 1 + 5 files changed, 38 insertions(+), 41 deletions(-) diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index a0204a0a..a5bf6ef6 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -279,9 +279,10 @@ namespace Ark * @brief Return a pointer to the top of the stack without consuming it, and resolve it if possible * * @param context + * @param offset * @return Value* */ - inline Value* peekAndResolveAsPtr(internal::ExecutionContext& context); + inline Value* peekAndResolveAsPtr(internal::ExecutionContext& context, std::size_t offset = 0); /** * @brief Push a value on the stack diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index bf2d5651..ef1859a0 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -175,11 +175,11 @@ inline Value* VM::pop(internal::ExecutionContext& context) return &m_undefined_value; } -inline Value* VM::peekAndResolveAsPtr(internal::ExecutionContext& context) +inline Value* VM::peekAndResolveAsPtr(internal::ExecutionContext& context, const std::size_t offset) { - if (context.sp > 0) + if (context.sp > offset) { - Value* tmp = &context.stack[context.sp - 1]; + Value* tmp = &context.stack[context.sp - 1 - offset]; if (tmp->valueType() == ValueType::Reference) return tmp->reference(); return tmp; @@ -235,13 +235,6 @@ inline void VM::returnFromFuncCall(internal::ExecutionContext& context) inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, Value* function_ptr, internal::PageAddr_t or_address) { - /* - Argument: number of arguments when calling the function - Job: 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 - */ using namespace internal; if (std::cmp_greater_equal(context.sp + 2, VMStackSize)) [[unlikely]] @@ -254,7 +247,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V ValueType call_type; Value* maybe_value_ptr = nullptr; if (function_ptr == nullptr && or_address == 0) - maybe_value_ptr = popAndResolveAsPtr(context); + maybe_value_ptr = peekAndResolveAsPtr(context, argc); else if (or_address != 0) call_type = ValueType::PageAddr; else @@ -365,6 +358,8 @@ inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& bu } // +2 to skip PP/IP that were pushed by PUSH_RETURN_ADDRESS context.sp -= static_cast(argc + (remove_return_address ? 2_u16 : 0_u16)); + // todo: merge this with the context.sp modification above? + pop(context); // remove the builtin from the stack // call proc push(builtin.proc()(args, this), context); } diff --git a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp index a94fc0ef..0dc2bbb0 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp @@ -30,32 +30,32 @@ namespace Ark::internal Rule { { LOAD_FAST_BY_INDEX, STORE }, STORE_FROM_INDEX }, Rule { { LOAD_FAST, SET_VAL }, SET_VAL_FROM }, Rule { { LOAD_FAST_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX }, - Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, - [](const Entities entities, const std::size_t start_idx) { - return Builtins::builtins[entities[3].primaryArg()].second.isFunction() && start_idx == 0 && entities[6].inst() == RET; - }, - [](const Entities e) { - return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1); - } }, - Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, - [](const Entities entities, const std::size_t start_idx) { - return Builtins::builtins[entities[5].primaryArg()].second.isFunction() && start_idx == 0 && entities[8].inst() == RET; - }, - [](const Entities e) { - return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2); - } }, - Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, - [](const Entities entities, const std::size_t start_idx) { - return Builtins::builtins[entities[7].primaryArg()].second.isFunction() && start_idx == 0 && entities[10].inst() == RET; - }, - [](const Entities e) { - return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3); - } }, - Rule { { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities entities, const std::size_t) { - return Builtins::builtins[entities[0].primaryArg()].second.isFunction(); - } }, - Rule { { LOAD_FAST, CALL }, CALL_SYMBOL }, - Rule { { GET_CURRENT_PAGE_ADDR, CALL }, CALL_CURRENT_PAGE }, + // Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, + // [](const Entities entities, const std::size_t start_idx) { + // return Builtins::builtins[entities[3].primaryArg()].second.isFunction() && start_idx == 0 && entities[6].inst() == RET; + // }, + // [](const Entities e) { + // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1); + // } }, + // Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, + // [](const Entities entities, const std::size_t start_idx) { + // return Builtins::builtins[entities[5].primaryArg()].second.isFunction() && start_idx == 0 && entities[8].inst() == RET; + // }, + // [](const Entities e) { + // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2); + // } }, + // Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, + // [](const Entities entities, const std::size_t start_idx) { + // return Builtins::builtins[entities[7].primaryArg()].second.isFunction() && start_idx == 0 && entities[10].inst() == RET; + // }, + // [](const Entities e) { + // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3); + // } }, + // Rule { { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities entities, const std::size_t) { + // return Builtins::builtins[entities[0].primaryArg()].second.isFunction(); + // } }, + // Rule { { LOAD_FAST, CALL }, CALL_SYMBOL }, + // Rule { { GET_CURRENT_PAGE_ADDR, CALL }, CALL_CURRENT_PAGE }, Rule { { LOAD_FAST, GET_FIELD }, GET_FIELD_FROM_SYMBOL }, Rule { { LOAD_FAST_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX }, Rule { { LIST, STORE }, STORE_LIST }, diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 26e97350..5a64646e 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -835,8 +835,6 @@ namespace Ark::internal page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); page(p).back().setSourceLocation(x.filename(), x.position().start.line); - pushFunctionCallArguments(x, p, /* is_tail_call= */ false); - const auto proc_page = createNewCodePage(/* temp= */ true); // compile the function resolution to a separate page @@ -855,7 +853,7 @@ namespace Ark::internal compileExpression(node, proc_page, false, false); // storing proc } - if (m_temp_pages.back().empty()) + if (page(proc_page).empty()) buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); // push proc from temp page @@ -863,6 +861,8 @@ namespace Ark::internal page(p).push_back(inst); m_temp_pages.pop_back(); + pushFunctionCallArguments(x, p, /* is_tail_call= */ false); + // number of arguments std::size_t args_count = 0; for (auto it = x.constList().begin() + start_index, it_end = x.constList().end(); it != it_end; ++it) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 41879023..59330d20 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -667,6 +667,7 @@ namespace Ark { { Value ip_or_val = *popAndResolveAsPtr(context); + pop(context); // to remove the function from the stack // no return value on the stack if (ip_or_val.valueType() == ValueType::InstPtr) [[unlikely]] { From 00416ca3d820808740a2ff3cd558b5a24d1d4170 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 16 Mar 2026 15:26:05 +0100 Subject: [PATCH 04/13] feat!: invert arguments' evaluation order (to be left to right) in function calls --- CHANGELOG.md | 15 +++++++++++++++ include/Ark/VM/VM.inl | 2 +- src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp | 16 ++++------------ src/arkreactor/VM/VM.cpp | 16 +++++++++------- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a80fe82..63f002eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [Unreleased changes] - 2026-MM-DD +### Breaking changes +- in function calls, the function to call is now always evaluated first +- in function calls, the arguments are now evaluated from left to right + +### Deprecations + +### Added +- the bytecode reader can print the argument of a `PUSH_RETURN_ADDRESS` instruction as a hex number + +### Changed +- instruction counter in the bytecode reader are displayed in hex, and count each instruction instead of each byte + +### Removed + ## [4.4.0] - 2026-03-13 ### Deprecations - `list:permutations` is deprecated in favor of `list:combinations` diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index ef1859a0..8fb0f443 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -351,7 +351,7 @@ 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 - 1 - j]; + Value* val = &context.stack[context.sp - argc + j]; if (val->valueType() == ValueType::Reference) val = val->reference(); args.emplace_back(*val); diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 5a64646e..6c21acab 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -522,7 +522,7 @@ namespace Ark::internal page(p).emplace_back(is_closure ? MAKE_CLOSURE : LOAD_CONST, addValue(function_body_page.index, x)); // pushing arguments from the stack into variables in the new scope - for (const auto& node : x.constList()[1].constList()) + for (const auto& node : x.constList()[1].constList() | std::ranges::views::reverse) { if (node.nodeType() == NodeType::Symbol || node.nodeType() == NodeType::MutArg) { @@ -660,23 +660,15 @@ namespace Ark::internal { 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 (Node& value : std::ranges::drop_view(call.list(), 1) | std::views::reverse) + for (Node& value : std::ranges::drop_view(call.list(), 1)) { - // FIXME: in (foo a b (breakpoint (< c 0)) c), we will push c before the breakpoint if (nodeProducesOutput(value) || isBreakpoint(value)) { // we have to disallow usage of references in tail calls, because if we shuffle arguments around while using refs, they will end up with the same value if (value.nodeType() == NodeType::Symbol && is_tail_call) - compileSymbol(value, p, false, /* can_use_ref= */ false); + compileSymbol(value, p, /* is_result_unused= */ false, /* can_use_ref= */ false); else - compileExpression(value, p, false, false); + compileExpression(value, p, /* is_result_unused= */ false, /* is_terminal= */ false); } else makeError(is_tail_call ? ErrorKind::InvalidNodeInTailCallNoReturnValue : ErrorKind::InvalidNodeNoReturnValue, value, node.repr()); diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 59330d20..9460294f 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -82,11 +82,13 @@ namespace Ark if (Value* field = closure->refClosure().refScope()[id]; field != nullptr) { - // check for CALL instruction (the instruction because context.ip is already on the next instruction word) - if (m_state.inst(context.pp, context.ip) == CALL) - return Value(Closure(closure->refClosure().scopePtr(), field->pageAddr())); - else - return *field; + // todo: this was needed for (closure.field args...), when we emitted the args first, then closure.field, then a CALL + // so that we could call closure.field. + // todo: introduce a CALL_CLOSURE or something to solve this problem + // if (m_state.inst(context.pp, context.ip) == CALL) + // return Value(Closure(closure->refClosure().scopePtr(), field->pageAddr())); + // else + return *field; } else { @@ -1120,9 +1122,9 @@ namespace Ark { func, args_list }); } - for (const Value& a : args_list.constList() | std::ranges::views::reverse) - push(a, context); push(func, context); + for (const Value& a : args_list.constList()) + push(a, context); call(context, static_cast(args_list.constList().size())); } From 85e4fab3e9bad5a119c292ce905f03d12ed5ba44 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 16 Mar 2026 17:08:08 +0100 Subject: [PATCH 05/13] feat!: handle the new argument order when embedding arkscript, and add a GET_FIELD_AS_CLOSURE instruction to call closure fields --- include/Ark/Compiler/Instructions.hpp | 193 +++++++++--------- .../IntermediateRepresentation/Entity.hpp | 2 + include/Ark/VM/VM.hpp | 2 +- include/Ark/VM/VM.inl | 26 ++- .../IntermediateRepresentation/Entity.cpp | 5 + .../Compiler/Lowerer/ASTLowerer.cpp | 3 + src/arkreactor/VM/VM.cpp | 31 +-- 7 files changed, 140 insertions(+), 122 deletions(-) diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 58329c33..c509687b 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -116,344 +116,348 @@ namespace Ark::internal // @role Read the field named following the given symbol id (cf symbols table) of a `Closure` stored in TS. Pop TS and push the value of field read on the stack GET_FIELD = 0x15, + // @args symbol id + // @role Read the field named following the given symbol id (cf symbols table) of a `Closure` stored in TS. Pop TS and push the value of field read on the stack, wrapping it in its closure environment + GET_FIELD_AS_CLOSURE = 0x16, + // @args constant id // @role Load a plugin dynamically, plugin name is stored as a string in the constants table - PLUGIN = 0x16, + PLUGIN = 0x17, // @args number of elements // @role Create a list from the N elements pushed on the stack. Follows the function calling convention - LIST = 0x17, + LIST = 0x18, // @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 = 0x18, + APPEND = 0x19, // @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 = 0x19, + CONCAT = 0x1a, // @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 = 0x1a, + APPEND_IN_PLACE = 0x1b, // @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 = 0x1b, + CONCAT_IN_PLACE = 0x1c, // @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 = 0x1c, + POP_LIST = 0x1d, // @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 = 0x1d, + POP_LIST_IN_PLACE = 0x1e, // @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 = 0x1e, + SET_AT_INDEX = 0x1f, // @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 = 0x1f, + SET_AT_2_INDEX = 0x20, // @role Remove the top of the stack - POP = 0x20, + POP = 0x21, // @role Pop the top of the stack, if it's false, jump to an address - SHORTCIRCUIT_AND = 0x21, + SHORTCIRCUIT_AND = 0x22, // @role Pop the top of the stack, if it's true, jump to an address - SHORTCIRCUIT_OR = 0x22, + SHORTCIRCUIT_OR = 0x23, // @role Create a new local scope - CREATE_SCOPE = 0x23, + CREATE_SCOPE = 0x24, // @role Reset the current scope so that it is empty, and jump to a given location - RESET_SCOPE_JUMP = 0x24, + RESET_SCOPE_JUMP = 0x25, // @role Destroy the last local scope - POP_SCOPE = 0x25, + POP_SCOPE = 0x26, // @args symbol id (function name) // @role Push the current page address as a value on the stack - GET_CURRENT_PAGE_ADDR = 0x26, + GET_CURRENT_PAGE_ADDR = 0x27, // @role Pop a List from the stack and a function, and call the function with the given list as arguments - APPLY = 0x27, + APPLY = 0x28, - FIRST_OPERATOR = 0x28, + FIRST_OPERATOR = 0x29, // @role Pop the top of the stack, if it's true, trigger the debugger - BREAKPOINT = 0x28, + BREAKPOINT = 0x29, // @role Push `TS1 + TS` - ADD = 0x29, + ADD = 0x2a, // @role Push `TS1 - TS` - SUB = 0x2a, + SUB = 0x2b, // @role Push `TS1 * TS` - MUL = 0x2b, + MUL = 0x2c, // @role Push `TS1 / TS` - DIV = 0x2c, + DIV = 0x2d, // @role Push `TS1 > TS` - GT = 0x2d, + GT = 0x2e, // @role Push `TS1 < TS` - LT = 0x2e, + LT = 0x2f, // @role Push `TS1 <= TS` - LE = 0x2f, + LE = 0x30, // @role Push `TS1 >= TS` - GE = 0x30, + GE = 0x31, // @role Push `TS1 != TS` - NEQ = 0x31, + NEQ = 0x32, // @role Push `TS1 == TS` - EQ = 0x32, + EQ = 0x33, // @role Push `len(TS)`, TS must be a list - LEN = 0x33, + LEN = 0x34, // @role Push `empty?(TS)`, TS must be a list or string - IS_EMPTY = 0x34, + IS_EMPTY = 0x35, // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string - TAIL = 0x35, + TAIL = 0x36, // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string - HEAD = 0x36, + HEAD = 0x37, // @role Push true if TS is nil, false otherwise - IS_NIL = 0x37, + IS_NIL = 0x38, // @role Convert TS to number (must be a string) - TO_NUM = 0x38, + TO_NUM = 0x39, // @role Convert TS to string - TO_STR = 0x39, + TO_STR = 0x3a, // @role Push the value at index TS (must be a number) in TS1, which must be a list or string - AT = 0x3a, + AT = 0x3b, // @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 = 0x3b, + AT_AT = 0x3c, // @role Push `TS1 % TS` - MOD = 0x3c, + MOD = 0x3d, // @role Push the type of TS as a string - TYPE = 0x3d, + TYPE = 0x3e, // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String - HAS_FIELD = 0x3e, + HAS_FIELD = 0x3f, // @role Push `!TS` - NOT = 0x3f, + NOT = 0x40, // @args constant id, constant id // @role Load two consts (`primary` then `secondary`) on the stack in one instruction - LOAD_CONST_LOAD_CONST = 0x40, + LOAD_CONST_LOAD_CONST = 0x41, // @args constant id, symbol id // @role Load const `primary` into the symbol `secondary` (create a variable) - LOAD_CONST_STORE = 0x41, + LOAD_CONST_STORE = 0x42, // @args constant id, symbol id // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id) - LOAD_CONST_SET_VAL = 0x42, + LOAD_CONST_SET_VAL = 0x43, // @args symbol id, symbol id // @role Store the value of the symbol `primary` into a new variable `secondary` - STORE_FROM = 0x43, + STORE_FROM = 0x44, // @args symbol index, symbol id // @role Store the value of the symbol `primary` into a new variable `secondary` - STORE_FROM_INDEX = 0x44, + STORE_FROM_INDEX = 0x45, // @args symbol id, symbol id // @role Store the value of the symbol `primary` into an existing variable `secondary` - SET_VAL_FROM = 0x45, + SET_VAL_FROM = 0x46, // @args symbol index, symbol id // @role Store the value of the symbol `primary` into an existing variable `secondary` - SET_VAL_FROM_INDEX = 0x46, + SET_VAL_FROM_INDEX = 0x47, // @args symbol id, count // @role Increment the variable `primary` by `count` and push its value on the stack - INCREMENT = 0x47, + INCREMENT = 0x48, // @args symbol index, count // @role Increment the variable `primary` by `count` and push its value on the stack - INCREMENT_BY_INDEX = 0x48, + INCREMENT_BY_INDEX = 0x49, // @args symbol id, count // @role Increment the variable `primary` by `count` and store its value in the given symbol id - INCREMENT_STORE = 0x49, + INCREMENT_STORE = 0x4a, // @args symbol id, count // @role Decrement the variable `primary` by `count` and push its value on the stack - DECREMENT = 0x4a, + DECREMENT = 0x4b, // @args symbol index, count // @role Decrement the variable `primary` by `count` and push its value on the stack - DECREMENT_BY_INDEX = 0x4b, + DECREMENT_BY_INDEX = 0x4c, // @args symbol id, count // @role Decrement the variable `primary` by `count` and store its value in the given symbol id - DECREMENT_STORE = 0x4c, + DECREMENT_STORE = 0x4d, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary` - STORE_TAIL = 0x4d, + STORE_TAIL = 0x4e, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary` - STORE_TAIL_BY_INDEX = 0x4e, + STORE_TAIL_BY_INDEX = 0x4f, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary` - STORE_HEAD = 0x4f, + STORE_HEAD = 0x50, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary` - STORE_HEAD_BY_INDEX = 0x50, + STORE_HEAD_BY_INDEX = 0x51, // @args number, symbol id // @role Create a list of `number` elements, and store it in a new variable `secondary` - STORE_LIST = 0x51, + STORE_LIST = 0x52, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary` - SET_VAL_TAIL = 0x52, + SET_VAL_TAIL = 0x53, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary` - SET_VAL_TAIL_BY_INDEX = 0x53, + SET_VAL_TAIL_BY_INDEX = 0x54, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary` - SET_VAL_HEAD = 0x54, + SET_VAL_HEAD = 0x55, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary` - SET_VAL_HEAD_BY_INDEX = 0x55, + SET_VAL_HEAD_BY_INDEX = 0x56, // @args builtin id, argument count // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack - CALL_BUILTIN = 0x56, + CALL_BUILTIN = 0x57, // @args builtin id, argument count // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack, as well as the return address removal - CALL_BUILTIN_WITHOUT_RETURN_ADDRESS = 0x57, + CALL_BUILTIN_WITHOUT_RETURN_ADDRESS = 0x58, // @args constant id, absolute address to jump to // @role Compare `TS < constant`, if the comparison fails, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_FALSE = 0x58, + LT_CONST_JUMP_IF_FALSE = 0x59, // @args constant id, absolute address to jump to // @role Compare `TS < constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_TRUE = 0x59, + LT_CONST_JUMP_IF_TRUE = 0x5a, // @args symbol id, absolute address to jump to // @role Compare `TS < symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - LT_SYM_JUMP_IF_FALSE = 0x5a, + LT_SYM_JUMP_IF_FALSE = 0x5b, // @args constant id, absolute address to jump to // @role Compare `TS > constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_TRUE = 0x5b, + GT_CONST_JUMP_IF_TRUE = 0x5c, // @args constant id, absolute address to jump to // @role Compare `TS > constant`, if the comparison fails, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_FALSE = 0x5c, + GT_CONST_JUMP_IF_FALSE = 0x5d, // @args symbol id, absolute address to jump to // @role Compare `TS > symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - GT_SYM_JUMP_IF_FALSE = 0x5d, + GT_SYM_JUMP_IF_FALSE = 0x5e, // @args constant id, absolute address to jump to // @role Compare `TS == constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_CONST_JUMP_IF_TRUE = 0x5e, + EQ_CONST_JUMP_IF_TRUE = 0x5f, // @args symbol index, absolute address to jump to // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_SYM_INDEX_JUMP_IF_TRUE = 0x5f, + EQ_SYM_INDEX_JUMP_IF_TRUE = 0x60, // @args constant id, absolute address to jump to // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - NEQ_CONST_JUMP_IF_TRUE = 0x60, + NEQ_CONST_JUMP_IF_TRUE = 0x61, // @args symbol id, absolute address to jump to // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - NEQ_SYM_JUMP_IF_FALSE = 0x61, + NEQ_SYM_JUMP_IF_FALSE = 0x62, // @args symbol id, argument count // @role Call a symbol by its id in `primary`, with `secondary` arguments - CALL_SYMBOL = 0x62, + CALL_SYMBOL = 0x63, // @args symbol id (function name), argument count // @role Call the current page with `secondary` arguments - CALL_CURRENT_PAGE = 0x63, + CALL_CURRENT_PAGE = 0x64, // @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 = 0x64, + GET_FIELD_FROM_SYMBOL = 0x65, // @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 = 0x65, + GET_FIELD_FROM_SYMBOL_INDEX = 0x66, // @args symbol id, symbol id2 // @role Push symbol[symbol2] - AT_SYM_SYM = 0x66, + AT_SYM_SYM = 0x67, // @args symbol index, symbol index2 // @role Push symbol[symbol2] - AT_SYM_INDEX_SYM_INDEX = 0x67, + AT_SYM_INDEX_SYM_INDEX = 0x68, // @args symbol index, constant id // @role Push symbol[constant] - AT_SYM_INDEX_CONST = 0x68, + AT_SYM_INDEX_CONST = 0x69, // @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 = 0x69, + CHECK_TYPE_OF = 0x6a, // @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 = 0x6a, + CHECK_TYPE_OF_BY_INDEX = 0x6b, // @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 = 0x6b, + APPEND_IN_PLACE_SYM = 0x6c, // @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 = 0x6c, + APPEND_IN_PLACE_SYM_INDEX = 0x6d, // @args symbol index, symbol id // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id) - STORE_LEN = 0x6d, + STORE_LEN = 0x6e, // @args symbol id, absolute address to jump to // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false - LT_LEN_SYM_JUMP_IF_FALSE = 0x6e, + LT_LEN_SYM_JUMP_IF_FALSE = 0x6f, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY = 0x6f, + MUL_BY = 0x70, // @args symbol index, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY_INDEX = 0x70, + MUL_BY_INDEX = 0x71, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id - MUL_SET_VAL = 0x71, + MUL_SET_VAL = 0x72, // @args op1, op2, op3 // @role Pop 3 or 4 values from the stack, and apply the ops sequentially (only ADD, SUB, MUL, and DIV are supported). Push the result to the stack. Only op3 may be NOP. - FUSED_MATH = 0x72, + FUSED_MATH = 0x73, InstructionsCount }; @@ -481,6 +485,7 @@ namespace Ark::internal "DEL", "MAKE_CLOSURE", "GET_FIELD", + "GET_FIELD_AS_CLOSURE", "PLUGIN", "LIST", "APPEND", diff --git a/include/Ark/Compiler/IntermediateRepresentation/Entity.hpp b/include/Ark/Compiler/IntermediateRepresentation/Entity.hpp index d49d957d..869e8fab 100644 --- a/include/Ark/Compiler/IntermediateRepresentation/Entity.hpp +++ b/include/Ark/Compiler/IntermediateRepresentation/Entity.hpp @@ -48,6 +48,8 @@ namespace Ark::internal::IR Entity(Instruction inst, uint8_t inst2, uint8_t inst3, uint8_t inst4); + void replaceInstruction(Instruction replacement); + static Entity Label(label_t value); static Entity Goto(const Entity& label, Instruction inst = Instruction::JUMP); diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index a5bf6ef6..c6d5bed5 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -257,7 +257,7 @@ namespace Ark inline void jump(uint16_t address, internal::ExecutionContext& context); - Value getField(Value* closure, uint16_t id, const internal::ExecutionContext& context); + Value getField(Value* closure, uint16_t id, const internal::ExecutionContext& context, bool push_with_env = false); Value createList(std::size_t count, internal::ExecutionContext& context); diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index 8fb0f443..726069e0 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -20,14 +20,6 @@ Value VM::call(const std::string& name, Args&&... args) push(Value(static_cast(0)), context); push(Value(ValueType::InstPtr, static_cast(0)), context); - // convert and push arguments - if (sizeof...(args) > 0) - { - std::vector fnargs { { Value(std::forward(args))... } }; - 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, MaxValue16Bits)) { @@ -41,6 +33,13 @@ Value VM::call(const std::string& name, Args&&... args) push(Value(var), context); context.last_symbol = id; } + // convert and push arguments + if (sizeof...(args) > 0) + { + std::vector fnargs { { Value(std::forward(args))... } }; + for (auto&& arg : fnargs) + push(arg, context); + } const std::size_t frames_count = context.fc; // call it @@ -69,10 +68,10 @@ inline Value VM::resolve(internal::ExecutionContext* context, const std::vector< 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 the function and all its args in the order they were given to us + // the function must go first! + for (auto&& val : n) push(val, *context); - push(n[0], *context); const std::size_t frames_count = context->fc; // call it @@ -356,10 +355,9 @@ inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& bu val = val->reference(); args.emplace_back(*val); } + // argc+1 to remove the arguments and the builtin // +2 to skip PP/IP that were pushed by PUSH_RETURN_ADDRESS - context.sp -= static_cast(argc + (remove_return_address ? 2_u16 : 0_u16)); - // todo: merge this with the context.sp modification above? - pop(context); // remove the builtin from the stack + context.sp -= static_cast(argc + 1_u16 + (remove_return_address ? 2_u16 : 0_u16)); // call proc push(builtin.proc()(args, this), context); } diff --git a/src/arkreactor/Compiler/IntermediateRepresentation/Entity.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/Entity.cpp index 178877d7..972de2e5 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/Entity.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/Entity.cpp @@ -22,6 +22,11 @@ namespace Ark::internal::IR m_inst(inst), m_primary_arg(inst2), m_secondary_arg(inst3), m_tertiary_arg(inst4) {} + void Entity::replaceInstruction(const Instruction replacement) + { + m_inst = replacement; + } + Entity Entity::Label(const label_t value) { auto entity = Entity(Kind::Label); diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 6c21acab..3a589d95 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -847,6 +847,9 @@ namespace Ark::internal if (page(proc_page).empty()) buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); + else if (page(proc_page).back().inst() == GET_FIELD) + // the last GET_FIELD instruction should push the closure environment with it + page(proc_page).back().replaceInstruction(GET_FIELD_AS_CLOSURE); // push proc from temp page for (const auto& inst : m_temp_pages.back()) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 9460294f..af072ed2 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -59,7 +59,7 @@ namespace Ark } } - Value VM::getField(Value* closure, const uint16_t id, const ExecutionContext& context) + Value VM::getField(Value* closure, const uint16_t id, const ExecutionContext& context, const bool push_with_env) { if (closure->valueType() != ValueType::Closure) { @@ -82,13 +82,10 @@ namespace Ark if (Value* field = closure->refClosure().refScope()[id]; field != nullptr) { - // todo: this was needed for (closure.field args...), when we emitted the args first, then closure.field, then a CALL - // so that we could call closure.field. - // todo: introduce a CALL_CLOSURE or something to solve this problem - // if (m_state.inst(context.pp, context.ip) == CALL) - // return Value(Closure(closure->refClosure().scopePtr(), field->pageAddr())); - // else - return *field; + if (push_with_env) + return Value(Closure(closure->refClosure().scopePtr(), field->pageAddr())); + else + return *field; } else { @@ -476,6 +473,7 @@ namespace Ark &&TARGET_DEL, &&TARGET_MAKE_CLOSURE, &&TARGET_GET_FIELD, + &&TARGET_GET_FIELD_AS_CLOSURE, &&TARGET_PLUGIN, &&TARGET_LIST, &&TARGET_APPEND, @@ -668,12 +666,12 @@ namespace Ark TARGET(RET) { { - Value ip_or_val = *popAndResolveAsPtr(context); - pop(context); // to remove the function from the stack + Value ret_or_func = *popAndResolveAsPtr(context); + Value ip_or_func = *popAndResolveAsPtr(context); // to remove the function from the stack // no return value on the stack - if (ip_or_val.valueType() == ValueType::InstPtr) [[unlikely]] + if (ip_or_func.valueType() == ValueType::InstPtr) [[unlikely]] { - context.ip = ip_or_val.pageAddr(); + context.ip = ip_or_func.pageAddr(); // we always push PP then IP, thus the next value // MUST be the page pointer context.pp = pop(context)->pageAddr(); @@ -690,7 +688,7 @@ namespace Ark context.pp = pop(context)->pageAddr(); returnFromFuncCall(context); - push(std::move(ip_or_val), context); + push(std::move(ret_or_func), context); } if (context.fc <= untilFrameCount) @@ -788,6 +786,13 @@ namespace Ark DISPATCH(); } + TARGET(GET_FIELD_AS_CLOSURE) + { + Value* var = popAndResolveAsPtr(context); + push(getField(var, arg, context, /* push_with_env= */ true), context); + DISPATCH(); + } + TARGET(PLUGIN) { loadPlugin(arg, context); From 2cca3c777cee5361399d47ff2ad867d0b2584cc3 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 16 Mar 2026 22:42:07 +0100 Subject: [PATCH 06/13] fix: clean up the stack after a function call with the new calling convention --- include/Ark/VM/VM.hpp | 2 + include/Ark/VM/VM.inl | 25 +++++++- include/Ark/VM/Value/Value.hpp | 4 +- .../Compiler/Lowerer/ASTLowerer.cpp | 61 +++++++++++++++++-- src/arkreactor/VM/VM.cpp | 29 +++++---- 5 files changed, 98 insertions(+), 23 deletions(-) diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index c6d5bed5..22074a17 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -275,6 +275,8 @@ namespace Ark */ inline Value* pop(internal::ExecutionContext& context); + inline Value* peek(internal::ExecutionContext& context, std::size_t offset = 0); + /** * @brief Return a pointer to the top of the stack without consuming it, and resolve it if possible * diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index 726069e0..09fc7cb2 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -174,6 +174,16 @@ inline Value* VM::pop(internal::ExecutionContext& context) return &m_undefined_value; } +inline Value* VM::peek(internal::ExecutionContext& context, const std::size_t offset) +{ + if (context.sp > offset) + { + Value* tmp = &context.stack[context.sp - 1 - offset]; + return tmp; + } + return &m_undefined_value; +} + inline Value* VM::peekAndResolveAsPtr(internal::ExecutionContext& context, const std::size_t offset) { if (context.sp > offset) @@ -232,7 +242,7 @@ inline void VM::returnFromFuncCall(internal::ExecutionContext& context) context.locals.pop_back(); } -inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, Value* function_ptr, internal::PageAddr_t or_address) +inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, Value* function_ptr, const internal::PageAddr_t or_address) { using namespace internal; @@ -244,11 +254,16 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V m_state.m_symbols[context.last_symbol])); ValueType call_type; + PageAddr_t page_addr = 0; Value* maybe_value_ptr = nullptr; + if (function_ptr == nullptr && or_address == 0) maybe_value_ptr = peekAndResolveAsPtr(context, argc); else if (or_address != 0) + { call_type = ValueType::PageAddr; + page_addr = or_address; + } else maybe_value_ptr = function_ptr; @@ -256,7 +271,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V { call_type = maybe_value_ptr->valueType(); if (call_type == ValueType::PageAddr) - or_address = maybe_value_ptr->pageAddr(); + page_addr = maybe_value_ptr->pageAddr(); } context.stacked_closure_scopes.emplace_back(nullptr); @@ -273,7 +288,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V // is it a user defined function? case ValueType::PageAddr: { - const PageAddr_t new_page_pointer = or_address; + const PageAddr_t new_page_pointer = page_addr; // create dedicated scope context.locals.emplace_back(context.scopes_storage.data(), context.locals.back().storageEnd()); @@ -312,6 +327,10 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V } } + if (function_ptr == nullptr && or_address == 0) + // so that RET knows it can collect it + peek(context, argc)->m_type = ValueType::Garbage; + // checking function arity std::size_t index = 0, needed_argc = 0; diff --git a/include/Ark/VM/Value/Value.hpp b/include/Ark/VM/Value/Value.hpp index 27fba55c..281e3462 100644 --- a/include/Ark/VM/Value/Value.hpp +++ b/include/Ark/VM/Value/Value.hpp @@ -46,7 +46,8 @@ namespace Ark Reference = 12, InstPtr = 13, - Any = 14 ///< Used only for typechecking + Garbage = 14, ///< Used to signal a value was used and can/should be collected and removed from the stack + Any = 15 ///< Used only for typechecking }; constexpr std::array types_to_str = { @@ -64,6 +65,7 @@ namespace Ark "Undefined", "Reference", "InstPtr", + "Garbage", "Any" }; } diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 3a589d95..8c2d0439 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -14,6 +15,14 @@ namespace Ark::internal { using namespace literals; + enum class CallType + { + Classic, + SelfNotRecursive, + Symbol, + Builtin + }; + ASTLowerer::ASTLowerer(const unsigned debug) : Pass("ASTLowerer", debug) {} @@ -539,7 +548,7 @@ namespace Ark::internal // Register an opened variable as "#anonymous", which won't match any valid names inside ASTLowerer::handleCalls. // This way we can continue to safely apply optimisations on // (let name (fun (e) (map lst (fun (e) (name e))))) - // Otherwise, `name` would have been optimized to a GET_CURRENT_PAGE_ADDRESS, which would have returned the wrong page. + // Otherwise, `name` would have been optimized to a GET_CURRENT_PAGE_ADDR, which would have returned the wrong page. if (x.isAnonymousFunction()) m_opened_vars.emplace("#anonymous"); // push body of the function @@ -823,11 +832,13 @@ namespace Ark::internal if (!nodeProducesOutput(node)) buildAndThrowError(fmt::format("Can not call `{}', as it doesn't return a value", node.repr()), node); - const auto label_return = IR::Entity::Label(m_current_label++); + const IR::Entity label_return = IR::Entity::Label(m_current_label++); page(p).emplace_back(IR::Entity::Goto(label_return, PUSH_RETURN_ADDRESS)); page(p).back().setSourceLocation(x.filename(), x.position().start.line); - const auto proc_page = createNewCodePage(/* temp= */ true); + const Page proc_page = createNewCodePage(/* temp= */ true); + CallType call_type = CallType::Classic; + std::optional call_arg = std::nullopt; // compile the function resolution to a separate page if (node.nodeType() == NodeType::Symbol && isFunctionCallingItself(node.string())) @@ -837,7 +848,7 @@ namespace Ark::internal // function page, which will be quicker than a local variable resolution. // We set its argument to the symbol id of the function we are calling, // so that the VM knows the name of the last called function. - page(proc_page).emplace_back(GET_CURRENT_PAGE_ADDR, addSymbol(node)); + call_type = CallType::SelfNotRecursive; } else { @@ -845,11 +856,30 @@ namespace Ark::internal compileExpression(node, proc_page, false, false); // storing proc } - if (page(proc_page).empty()) + if (page(proc_page).empty() && call_type == CallType::Classic) buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); else if (page(proc_page).back().inst() == GET_FIELD) // the last GET_FIELD instruction should push the closure environment with it page(proc_page).back().replaceInstruction(GET_FIELD_AS_CLOSURE); + else if (page(proc_page).size() == 1) + { + [[maybe_unused]] const Instruction inst = page(proc_page).back().inst(); + + // todo: bug in the VM: we pop when we shouldn't, because the function wasn't pushed since it was optimised + // maybe using a 'garbage' value type can help to flag values we can delete (if any)? + // if (inst == LOAD_FAST) + // { + // call_type = CallType::Symbol; + // // we don't want to push any instruction, as we'll use an optimised instruction instead of CALL + // page(proc_page).clear(); + // } + // else if (inst == BUILTIN) + // { + // call_type = CallType::Builtin; + // call_arg = page(proc_page).back().primaryArg(); + // page(proc_page).clear(); + // } + } // push proc from temp page for (const auto& inst : m_temp_pages.back()) @@ -865,8 +895,27 @@ namespace Ark::internal if (it->nodeType() != NodeType::Capture && !isBreakpoint(*it)) args_count++; } + // call the procedure - page(p).emplace_back(CALL, args_count); + switch (call_type) + { + case CallType::Classic: + page(p).emplace_back(CALL, args_count); + break; + + case CallType::SelfNotRecursive: + page(p).emplace_back(CALL_CURRENT_PAGE, addSymbol(node), args_count); + break; + + case CallType::Symbol: + page(p).emplace_back(CALL_SYMBOL, addSymbol(node), args_count); + break; + + case CallType::Builtin: + assert(call_arg.has_value() && "Expected a value for call_arg with CallType::Builtin"); + page(p).emplace_back(CALL_BUILTIN, call_arg.value(), args_count); + break; + } page(p).back().setSourceLocation(node.filename(), node.position().start.line); // patch the PUSH_RETURN_ADDRESS instruction with the return location (IP=CALL instruction IP) diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index af072ed2..021bba79 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -668,7 +668,7 @@ namespace Ark { Value ret_or_func = *popAndResolveAsPtr(context); Value ip_or_func = *popAndResolveAsPtr(context); // to remove the function from the stack - // no return value on the stack + if (ip_or_func.valueType() == ValueType::InstPtr) [[unlikely]] { context.ip = ip_or_func.pageAddr(); @@ -677,7 +677,10 @@ namespace Ark context.pp = pop(context)->pageAddr(); returnFromFuncCall(context); - push(Builtins::nil, context); + if (ret_or_func.valueType() == ValueType::Garbage) + push(Builtins::nil, context); + else + push(std::move(ret_or_func), context); } // value on the stack else [[likely]] @@ -2138,40 +2141,40 @@ namespace Ark { std::vector arg_names; arg_names.reserve(expected_arg_count + 1); - if (expected_arg_count > 0) - arg_names.emplace_back(""); // for formatting, so that we have a space between the function and the args std::size_t index = 0; while (m_state.inst(context.pp, index) == STORE || m_state.inst(context.pp, index) == STORE_REF) { const auto id = static_cast((m_state.inst(context.pp, index + 2) << 8) + m_state.inst(context.pp, index + 3)); - arg_names.push_back(m_state.m_symbols[id]); + arg_names.insert(arg_names.begin(), m_state.m_symbols[id]); index += 4; } - // we only the blank space for formatting and no arg names, probably because of a CALL_BUILTIN_WITHOUT_RETURN_ADDRESS - if (arg_names.size() == 1 && index == 0) + // we have no arg names, probably because of a CALL_BUILTIN_WITHOUT_RETURN_ADDRESS + if (arg_names.empty() && index == 0) { - assert(m_state.inst(context.pp, 0) == CALL_BUILTIN_WITHOUT_RETURN_ADDRESS && "expected a CALL_BUILTIN_WITHOUT_RETURN_ADDRESS instruction or STORE instructions"); for (std::size_t i = 0; i < expected_arg_count; ++i) arg_names.emplace_back(1, static_cast('a' + i)); } + if (expected_arg_count > 0) + arg_names.insert(arg_names.begin(), ""); // for formatting, so that we have a space between the function and the args std::vector arg_vals; arg_vals.reserve(passed_arg_count + 1); - if (passed_arg_count > 0) - arg_vals.emplace_back(""); // for formatting, so that we have a space between the function and the args for (std::size_t i = 0; i < passed_arg_count && i + 1 <= context.sp; ++i) // -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)); + arg_vals.push_back(context.stack[context.sp - passed_arg_count + i].toString(*this)); + if (passed_arg_count > 0) + arg_vals.insert(arg_vals.begin(), ""); // for formatting, so that we have a space between the function and the args // 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) { - context.ip = context.stack[context.sp - 1 - passed_arg_count].pageAddr(); - context.pp = context.stack[context.sp - 2 - passed_arg_count].pageAddr(); + // -2/-3 instead of -1/-2 to skip over the function pushed on the stack + context.ip = context.stack[context.sp - 2 - passed_arg_count].pageAddr(); + context.pp = context.stack[context.sp - 3 - passed_arg_count].pageAddr(); context.sp -= 2; returnFromFuncCall(context); } From 701615d1183b321b518406c04ae19f005e6e34e7 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Mon, 16 Mar 2026 22:50:47 +0100 Subject: [PATCH 07/13] fix: apply optimisations for CALL instructions only when we have something to compile for a function --- .../Compiler/Lowerer/ASTLowerer.cpp | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 8c2d0439..0df38c55 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -854,31 +854,31 @@ namespace Ark::internal { // closure chains have been handled (eg: closure.field.field.function) compileExpression(node, proc_page, false, false); // storing proc - } - if (page(proc_page).empty() && call_type == CallType::Classic) - buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); - else if (page(proc_page).back().inst() == GET_FIELD) - // the last GET_FIELD instruction should push the closure environment with it - page(proc_page).back().replaceInstruction(GET_FIELD_AS_CLOSURE); - else if (page(proc_page).size() == 1) - { - [[maybe_unused]] const Instruction inst = page(proc_page).back().inst(); - - // todo: bug in the VM: we pop when we shouldn't, because the function wasn't pushed since it was optimised - // maybe using a 'garbage' value type can help to flag values we can delete (if any)? - // if (inst == LOAD_FAST) - // { - // call_type = CallType::Symbol; - // // we don't want to push any instruction, as we'll use an optimised instruction instead of CALL - // page(proc_page).clear(); - // } - // else if (inst == BUILTIN) - // { - // call_type = CallType::Builtin; - // call_arg = page(proc_page).back().primaryArg(); - // page(proc_page).clear(); - // } + if (page(proc_page).empty()) + buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); + else if (page(proc_page).back().inst() == GET_FIELD) + // the last GET_FIELD instruction should push the closure environment with it + page(proc_page).back().replaceInstruction(GET_FIELD_AS_CLOSURE); + else if (page(proc_page).size() == 1) + { + [[maybe_unused]] const Instruction inst = page(proc_page).back().inst(); + + // todo: bug in the VM: we pop when we shouldn't, because the function wasn't pushed since it was optimised + // maybe using a 'garbage' value type can help to flag values we can delete (if any)? + // if (inst == LOAD_FAST) + // { + // call_type = CallType::Symbol; + // // we don't want to push any instruction, as we'll use an optimised instruction instead of CALL + // page(proc_page).clear(); + // } + // else if (inst == BUILTIN) + // { + // call_type = CallType::Builtin; + // call_arg = page(proc_page).back().primaryArg(); + // page(proc_page).clear(); + // } + } } // push proc from temp page From 6b0c5b388fb9d523656d5016cbfbae7e4d8bc544 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 17 Mar 2026 15:12:24 +0100 Subject: [PATCH 08/13] feat: reintroduce CALL_BUILTIN and CALL_SYMBOL --- include/Ark/VM/VM.hpp | 3 +- include/Ark/VM/VM.inl | 9 ++-- .../IROptimizer.cpp | 5 -- .../Compiler/Lowerer/ASTLowerer.cpp | 35 ++++++------- src/arkreactor/VM/VM.cpp | 50 ++++++++++++++----- src/arkreactor/VM/Value/Value.cpp | 3 ++ .../unittests/Suites/BytecodeReaderSuite.cpp | 2 +- tests/unittests/Suites/TypeCheckerSuite.cpp | 2 + 8 files changed, 68 insertions(+), 41 deletions(-) diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index 22074a17..472c88e1 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -407,8 +407,9 @@ namespace Ark * @param builtin the builtin to call * @param argc number of arguments already sent * @param remove_return_address remove the return address pushed by the compiler + * @param remove_builtin remove the builtin that was pushed to the stack for the call */ - inline void callBuiltin(internal::ExecutionContext& context, const Value& builtin, uint16_t argc, bool remove_return_address = true); + inline void callBuiltin(internal::ExecutionContext& context, const Value& builtin, uint16_t argc, bool remove_return_address = true, bool remove_builtin = true); }; #include "VM.inl" diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index 09fc7cb2..116f5fd0 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -281,7 +281,9 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V // is it a builtin function name? case ValueType::CProc: { - callBuiltin(context, *maybe_value_ptr, argc); + // We need to remove the builtin from the stack if we came from a standard CALL instruction. + // We know we came from a CALL instruction is function_ptr is null, since only CALL_SYMBOL supplies it. + callBuiltin(context, *maybe_value_ptr, argc, /* remove_return_address= */ true, /* remove_builtin= */ function_ptr == nullptr); return; } @@ -356,7 +358,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V throwArityError(argc, needed_argc, context); } -inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& builtin, const uint16_t argc, const bool remove_return_address) +inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& builtin, const uint16_t argc, const bool remove_return_address, const bool remove_builtin) { using namespace Ark::literals; @@ -374,9 +376,8 @@ inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& bu val = val->reference(); args.emplace_back(*val); } - // argc+1 to remove the arguments and the builtin // +2 to skip PP/IP that were pushed by PUSH_RETURN_ADDRESS - context.sp -= static_cast(argc + 1_u16 + (remove_return_address ? 2_u16 : 0_u16)); + context.sp -= static_cast(argc + (remove_return_address ? 2_u16 : 0_u16) + (remove_builtin ? 1_u16 : 0_u16)); // call proc push(builtin.proc()(args, this), context); } diff --git a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp index 0dc2bbb0..3d4c9d84 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp @@ -51,11 +51,6 @@ namespace Ark::internal // [](const Entities e) { // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3); // } }, - // Rule { { BUILTIN, CALL }, CALL_BUILTIN, [](const Entities entities, const std::size_t) { - // return Builtins::builtins[entities[0].primaryArg()].second.isFunction(); - // } }, - // Rule { { LOAD_FAST, CALL }, CALL_SYMBOL }, - // Rule { { GET_CURRENT_PAGE_ADDR, CALL }, CALL_CURRENT_PAGE }, Rule { { LOAD_FAST, GET_FIELD }, GET_FIELD_FROM_SYMBOL }, Rule { { LOAD_FAST_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX }, Rule { { LIST, STORE }, STORE_LIST }, diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 0df38c55..aca5f112 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -862,22 +862,22 @@ namespace Ark::internal page(proc_page).back().replaceInstruction(GET_FIELD_AS_CLOSURE); else if (page(proc_page).size() == 1) { - [[maybe_unused]] const Instruction inst = page(proc_page).back().inst(); - - // todo: bug in the VM: we pop when we shouldn't, because the function wasn't pushed since it was optimised - // maybe using a 'garbage' value type can help to flag values we can delete (if any)? - // if (inst == LOAD_FAST) - // { - // call_type = CallType::Symbol; - // // we don't want to push any instruction, as we'll use an optimised instruction instead of CALL - // page(proc_page).clear(); - // } - // else if (inst == BUILTIN) - // { - // call_type = CallType::Builtin; - // call_arg = page(proc_page).back().primaryArg(); - // page(proc_page).clear(); - // } + const Instruction inst = page(proc_page).back().inst(); + const uint16_t arg = page(proc_page).back().primaryArg(); + + if (inst == LOAD_FAST) + { + call_type = CallType::Symbol; + call_arg = arg; + // we don't want to push any instruction, as we'll use an optimised instruction instead of CALL + page(proc_page).clear(); + } + else if (inst == BUILTIN && Builtins::builtins[arg].second.isFunction()) + { + call_type = CallType::Builtin; + call_arg = arg; + page(proc_page).clear(); + } } } @@ -908,7 +908,8 @@ namespace Ark::internal break; case CallType::Symbol: - page(p).emplace_back(CALL_SYMBOL, addSymbol(node), args_count); + assert(call_arg.has_value() && "Expected a value for call_arg with CallType::Symbol"); + page(p).emplace_back(CALL_SYMBOL, call_arg.value(), args_count); break; case CallType::Builtin: diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 021bba79..e22660d1 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -666,33 +666,47 @@ namespace Ark TARGET(RET) { { - Value ret_or_func = *popAndResolveAsPtr(context); - Value ip_or_func = *popAndResolveAsPtr(context); // to remove the function from the stack + Value ts = *popAndResolveAsPtr(context); + Value ts1 = *popAndResolveAsPtr(context); - if (ip_or_func.valueType() == ValueType::InstPtr) [[unlikely]] + if (ts1.valueType() == ValueType::InstPtr) { - context.ip = ip_or_func.pageAddr(); + context.ip = ts1.pageAddr(); // we always push PP then IP, thus the next value // MUST be the page pointer context.pp = pop(context)->pageAddr(); returnFromFuncCall(context); - if (ret_or_func.valueType() == ValueType::Garbage) + if (ts.valueType() == ValueType::Garbage) push(Builtins::nil, context); else - push(std::move(ret_or_func), context); + push(std::move(ts), context); } - // value on the stack - else [[likely]] + else if (ts1.valueType() == ValueType::Garbage) { - const Value* ip = popAndResolveAsPtr(context); + const Value* ip = pop(context); assert(ip->valueType() == ValueType::InstPtr && "Expected instruction pointer on the stack (is the stack trashed?)"); context.ip = ip->pageAddr(); context.pp = pop(context)->pageAddr(); returnFromFuncCall(context); - push(std::move(ret_or_func), context); + push(std::move(ts), context); } + else if (ts.valueType() == ValueType::InstPtr) + { + context.ip = ts.pageAddr(); + context.pp = ts1.pageAddr(); + returnFromFuncCall(context); + push(Builtins::nil, context); + } + else + throw Error( + fmt::format( + "Unhandled case when returning from function call. TS=({}){}, TS1=({}){}", + std::to_string(ts.valueType()), + ts.toString(*this), + std::to_string(ts1.valueType()), + ts1.toString(*this))); if (context.fc <= untilFrameCount) GOTO_HALT(); @@ -1712,7 +1726,12 @@ namespace Ark { UNPACK_ARGS(); // no stack size check because we do not push IP/PP since we are just calling a builtin - callBuiltin(context, Builtins::builtins[primary_arg].second, secondary_arg); + callBuiltin( + context, + Builtins::builtins[primary_arg].second, + secondary_arg, + /* remove_return_address= */ true, + /* remove_builtin= */ false); if (!m_running) GOTO_HALT(); DISPATCH(); @@ -1722,7 +1741,12 @@ namespace Ark { UNPACK_ARGS(); // no stack size check because we do not push IP/PP since we are just calling a builtin - callBuiltin(context, Builtins::builtins[primary_arg].second, secondary_arg, /* remove_return_address= */ false); + callBuiltin( + context, + Builtins::builtins[primary_arg].second, + secondary_arg, + /* remove_return_address= */ false, + /* remove_builtin= */ false); if (!m_running) GOTO_HALT(); DISPATCH(); @@ -1824,7 +1848,7 @@ namespace Ark TARGET(CALL_SYMBOL) { UNPACK_ARGS(); - call(context, secondary_arg, loadSymbol(primary_arg, context)); + call(context, secondary_arg, /* function_ptr= */ loadSymbol(primary_arg, context)); if (!m_running) GOTO_HALT(); DISPATCH(); diff --git a/src/arkreactor/VM/Value/Value.cpp b/src/arkreactor/VM/Value/Value.cpp index dc064c6f..e57fb60b 100644 --- a/src/arkreactor/VM/Value/Value.cpp +++ b/src/arkreactor/VM/Value/Value.cpp @@ -136,6 +136,9 @@ namespace Ark case ValueType::InstPtr: return fmt::format("Instruction@{}", pageAddr()); + case ValueType::Garbage: + return fmt::format("Garbage (expected)"); + default: return "~\\._./~"; } diff --git a/tests/unittests/Suites/BytecodeReaderSuite.cpp b/tests/unittests/Suites/BytecodeReaderSuite.cpp index 158cab7f..c5ec1154 100644 --- a/tests/unittests/Suites/BytecodeReaderSuite.cpp +++ b/tests/unittests/Suites/BytecodeReaderSuite.cpp @@ -71,7 +71,7 @@ ut::suite<"BytecodeReader"> bcr_suite = [] { using namespace std::literals::string_literals; const auto expected_symbols = std::vector { - "ackermann", "m", "n" + "ackermann", "n", "m" }; expect(that % symbols_block.symbols == expected_symbols); // 'ark\0' + version (2 bytes per number) + timestamp + sha -> first byte of the sym table diff --git a/tests/unittests/Suites/TypeCheckerSuite.cpp b/tests/unittests/Suites/TypeCheckerSuite.cpp index d1296a28..14d23465 100644 --- a/tests/unittests/Suites/TypeCheckerSuite.cpp +++ b/tests/unittests/Suites/TypeCheckerSuite.cpp @@ -152,6 +152,8 @@ Input parse_input(const std::string& path) [[fallthrough]]; case Ark::ValueType::InstPtr: [[fallthrough]]; + case Ark::ValueType::Garbage: + [[fallthrough]]; case Ark::ValueType::Any: break; } From 5793674dd26f28ba405b0fac9a627d6792a0ab71 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 17 Mar 2026 16:19:55 +0100 Subject: [PATCH 09/13] feat: reintroduce CALL_BUILTIN_WITHOUT_RETURN_ADDRESS in the IROptimizer --- .../IROptimizer.cpp | 42 ++--- src/arkreactor/VM/Debugger.cpp | 5 +- src/arkreactor/VM/VM.cpp | 2 + .../CompilerSuite/ir/99bottles.expected | 16 +- .../CompilerSuite/ir/ackermann.expected | 28 ++- .../CompilerSuite/ir/args_attr.expected | 17 +- .../CompilerSuite/ir/breakpoints.expected | 39 ++--- .../CompilerSuite/ir/closures.expected | 47 +++-- .../CompilerSuite/ir/factorial.expected | 7 +- .../CompilerSuite/ir/operators.expected | 105 ++++++------ .../ir/operators_as_builtins.expected | 45 +++-- .../CompilerSuite/ir/plugin.expected | 10 +- .../CompilerSuite/ir/renamed_capture.expected | 3 +- .../CompilerSuite/ir/tail_call.expected | 13 +- .../optimized_ir/99bottles.expected | 4 +- .../optimized_ir/ackermann.expected | 18 +- .../call_builtin_not_optimized.expected | 4 +- .../optimized_ir/closures.expected | 25 +-- .../optimized_ir/factorial.expected | 4 +- .../optimized_ir/fused_math_ops.expected | 160 +++++++++--------- .../DebuggerSuite/stack_and_locals.expected | 18 +- .../runtime/recursion_depth.expected | 4 +- 22 files changed, 298 insertions(+), 318 deletions(-) diff --git a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp index 3d4c9d84..5ce10c14 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp @@ -30,27 +30,27 @@ namespace Ark::internal Rule { { LOAD_FAST_BY_INDEX, STORE }, STORE_FROM_INDEX }, Rule { { LOAD_FAST, SET_VAL }, SET_VAL_FROM }, Rule { { LOAD_FAST_BY_INDEX, SET_VAL }, SET_VAL_FROM_INDEX }, - // Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, - // [](const Entities entities, const std::size_t start_idx) { - // return Builtins::builtins[entities[3].primaryArg()].second.isFunction() && start_idx == 0 && entities[6].inst() == RET; - // }, - // [](const Entities e) { - // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1); - // } }, - // Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, - // [](const Entities entities, const std::size_t start_idx) { - // return Builtins::builtins[entities[5].primaryArg()].second.isFunction() && start_idx == 0 && entities[8].inst() == RET; - // }, - // [](const Entities e) { - // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2); - // } }, - // Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, BUILTIN, CALL }, - // [](const Entities entities, const std::size_t start_idx) { - // return Builtins::builtins[entities[7].primaryArg()].second.isFunction() && start_idx == 0 && entities[10].inst() == RET; - // }, - // [](const Entities e) { - // return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3); - // } }, + Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, CALL_BUILTIN }, + [](const Entities entities, const std::size_t start_idx) { + return start_idx == 0 && entities[5].inst() == RET; + }, + [](const Entities e) { + return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[3].primaryArg(), 1); + } }, + Rule { { STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, CALL_BUILTIN }, + [](const Entities entities, const std::size_t start_idx) { + return start_idx == 0 && entities[7].inst() == RET; + }, + [](const Entities e) { + return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[5].primaryArg(), 2); + } }, + Rule { { STORE, STORE, STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, LOAD_FAST_BY_INDEX, CALL_BUILTIN }, + [](const Entities entities, const std::size_t start_idx) { + return start_idx == 0 && entities[9].inst() == RET; + }, + [](const Entities e) { + return IR::Entity(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, e[7].primaryArg(), 3); + } }, Rule { { LOAD_FAST, GET_FIELD }, GET_FIELD_FROM_SYMBOL }, Rule { { LOAD_FAST_BY_INDEX, GET_FIELD }, GET_FIELD_FROM_SYMBOL_INDEX }, Rule { { LIST, STORE }, STORE_LIST }, diff --git a/src/arkreactor/VM/Debugger.cpp b/src/arkreactor/VM/Debugger.cpp index 08ae3d9d..5b119417 100644 --- a/src/arkreactor/VM/Debugger.cpp +++ b/src/arkreactor/VM/Debugger.cpp @@ -98,7 +98,10 @@ namespace Ark::internal last_ip = context.ip - 4; const Value* maybe_value = vm.peekAndResolveAsPtr(context); - if (maybe_value != nullptr && maybe_value->valueType() != ValueType::Undefined && maybe_value->valueType() != ValueType::InstPtr) + if (maybe_value != nullptr && + maybe_value->valueType() != ValueType::Undefined && + maybe_value->valueType() != ValueType::InstPtr && + maybe_value->valueType() != ValueType::Garbage) fmt::println( m_os, "{}", diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index e22660d1..79b9ab08 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -1745,6 +1745,8 @@ namespace Ark context, Builtins::builtins[primary_arg].second, secondary_arg, + // we didn't have a PUSH_RETURN_ADDRESS instruction before, + // so do not attempt to remove (pp,ip) from the stack: they're not there! /* remove_return_address= */ false, /* remove_builtin= */ false); if (!m_running) diff --git a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected index e5132d22..feea5ee2 100644 --- a/tests/unittests/resources/CompilerSuite/ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/ir/99bottles.expected @@ -34,14 +34,12 @@ page_0 POP_JUMP_IF_FALSE L5 PUSH_RETURN_ADDRESS L6 PUSH_RETURN_ADDRESS L7 + LOAD_CONST 3 LOAD_FAST 4 LOAD_FAST 4 - LOAD_CONST 3 - BUILTIN 27 - CALL 3 + CALL_BUILTIN 27, 3 .L7: - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L6: POP 0 LOAD_FAST 4 @@ -50,13 +48,11 @@ page_0 SET_VAL 4 PUSH_RETURN_ADDRESS L8 PUSH_RETURN_ADDRESS L9 - LOAD_FAST 4 LOAD_CONST 4 - BUILTIN 27 - CALL 2 + LOAD_FAST 4 + CALL_BUILTIN 27, 2 .L9: - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L8: POP 0 RESET_SCOPE_JUMP L4 diff --git a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected index 3f276fd9..50ef808a 100644 --- a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected @@ -2,15 +2,14 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L5 - PUSH_RETURN_ADDRESS L6 LOAD_CONST 3 - LOAD_CONST 4 + PUSH_RETURN_ADDRESS L6 LOAD_FAST_BY_INDEX 0 - CALL 2 -.L6: + LOAD_CONST 4 LOAD_CONST 5 - BUILTIN 9 CALL 2 +.L6: + CALL_BUILTIN 9, 2 .L5: POP 0 HALT 0 @@ -18,37 +17,36 @@ page_0 page_1 STORE 1 STORE 2 - LOAD_FAST_BY_INDEX 1 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 1 GT 0 POP_JUMP_IF_TRUE L0 LOAD_CONST 2 - LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 1 ADD 0 JUMP L1 .L0: LOAD_CONST 1 - LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 1 EQ 0 POP_JUMP_IF_TRUE L2 - PUSH_RETURN_ADDRESS L3 LOAD_FAST_BY_INDEX 0 LOAD_CONST 2 SUB 0 - LOAD_FAST_BY_INDEX 1 - GET_CURRENT_PAGE_ADDR 0 - CALL 2 -.L3: + PUSH_RETURN_ADDRESS L3 + LOAD_FAST_BY_INDEX 0 LOAD_FAST_BY_INDEX 1 LOAD_CONST 2 SUB 0 + CALL_CURRENT_PAGE 0, 2 +.L3: TAIL_CALL_SELF 0 JUMP L4 .L2: - LOAD_CONST 2 - LOAD_FAST_BY_INDEX 1 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 2 SUB 0 + LOAD_CONST 2 TAIL_CALL_SELF 0 .L4: .L1: diff --git a/tests/unittests/resources/CompilerSuite/ir/args_attr.expected b/tests/unittests/resources/CompilerSuite/ir/args_attr.expected index 9480a291..c4d3ce84 100644 --- a/tests/unittests/resources/CompilerSuite/ir/args_attr.expected +++ b/tests/unittests/resources/CompilerSuite/ir/args_attr.expected @@ -8,23 +8,22 @@ page_0 STORE 3 PUSH_RETURN_ADDRESS L0 PUSH_RETURN_ADDRESS L1 - LOAD_FAST_BY_INDEX 0 - LOAD_CONST 3 LOAD_FAST_BY_INDEX 1 + LOAD_CONST 3 + LOAD_FAST_BY_INDEX 0 CALL 2 .L1: - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L0: POP 0 HALT 0 page_1 - STORE 1 - STORE_REF 2 - LOAD_FAST_BY_INDEX 0 - LEN 0 - SET_VAL 1 + STORE_REF 1 + STORE 2 LOAD_FAST_BY_INDEX 1 + LEN 0 + SET_VAL 2 + LOAD_FAST_BY_INDEX 0 RET 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected index 1dfa6ed3..75e241f1 100644 --- a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected +++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected @@ -4,50 +4,48 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L3 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 LOAD_CONST 5 LOAD_CONST 6 - LOAD_FAST_BY_INDEX 0 CALL 3 .L3: POP 0 BUILTIN 1 BREAKPOINT 1 LOAD_CONST 5 - LOAD_CONST 6 + LOAD_CONST 4 GT 0 POP_JUMP_IF_TRUE L4 JUMP L5 .L4: PUSH_RETURN_ADDRESS L6 LOAD_CONST 7 - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L6: POP 0 PUSH_RETURN_ADDRESS L7 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 - LOAD_CONST 5 BUILTIN 0 BREAKPOINT 1 + LOAD_CONST 5 LOAD_CONST 6 - LOAD_FAST_BY_INDEX 0 CALL 3 .L7: POP 0 PUSH_RETURN_ADDRESS L8 LOAD_CONST 8 - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L8: POP 0 .L5: LOAD_CONST 9 STORE 5 PUSH_RETURN_ADDRESS L9 + LOAD_FAST_BY_INDEX 0 BUILTIN 1 BREAKPOINT 1 - LOAD_FAST_BY_INDEX 0 CALL 0 .L9: POP 0 @@ -57,43 +55,40 @@ page_1 STORE 1 STORE 2 STORE 3 - LOAD_FAST_BY_INDEX 2 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 1 GT 0 BREAKPOINT 1 PUSH_RETURN_ADDRESS L0 PUSH_RETURN_ADDRESS L1 + LOAD_CONST 2 LOAD_FAST_BY_INDEX 0 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 2 - LOAD_CONST 2 - BUILTIN 27 - CALL 4 + CALL_BUILTIN 27, 4 .L1: - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L0: POP 0 - LOAD_FAST_BY_INDEX 2 + LOAD_FAST_BY_INDEX 0 LOAD_FAST_BY_INDEX 1 MUL 0 - LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 2 LOAD_CONST 3 LT 0 BREAKPOINT 1 - LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 2 MUL 0 LOAD_CONST 1 MUL 0 STORE 4 PUSH_RETURN_ADDRESS L2 - BUILTIN 0 + BUILTIN 1 BREAKPOINT 1 LOAD_FAST_BY_INDEX 0 - BUILTIN 1 + BUILTIN 0 BREAKPOINT 1 - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L2: POP 0 LOAD_FAST_BY_INDEX 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/closures.expected b/tests/unittests/resources/CompilerSuite/ir/closures.expected index 956b92e5..58794f36 100644 --- a/tests/unittests/resources/CompilerSuite/ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/ir/closures.expected @@ -2,94 +2,87 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L0 + LOAD_FAST_BY_INDEX 0 LOAD_CONST 3 LOAD_CONST 4 LOAD_CONST 5 - LOAD_FAST_BY_INDEX 0 CALL 3 .L0: STORE 6 PUSH_RETURN_ADDRESS L1 + LOAD_FAST_BY_INDEX 1 LOAD_CONST 6 LOAD_CONST 7 LOAD_CONST 8 - LOAD_FAST_BY_INDEX 1 CALL 3 .L1: STORE 7 PUSH_RETURN_ADDRESS L2 + LOAD_CONST 9 LOAD_FAST_BY_INDEX 1 GET_FIELD 2 - LOAD_CONST 9 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L2: POP 0 PUSH_RETURN_ADDRESS L3 LOAD_CONST 10 - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L3: POP 0 PUSH_RETURN_ADDRESS L4 - LOAD_CONST 11 LOAD_FAST_BY_INDEX 1 - GET_FIELD 4 + GET_FIELD_AS_CLOSURE 4 + LOAD_CONST 11 CALL 1 .L4: POP 0 PUSH_RETURN_ADDRESS L5 + LOAD_CONST 12 LOAD_FAST_BY_INDEX 1 GET_FIELD 2 - LOAD_CONST 12 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L5: POP 0 PUSH_RETURN_ADDRESS L6 + LOAD_CONST 13 LOAD_FAST_BY_INDEX 0 GET_FIELD 2 - LOAD_CONST 13 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L6: POP 0 LOAD_CONST 14 STORE 8 PUSH_RETURN_ADDRESS L7 - LOAD_CONST 17 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 17 CALL 1 .L7: STORE 10 PUSH_RETURN_ADDRESS L8 + LOAD_CONST 18 PUSH_RETURN_ADDRESS L9 LOAD_FAST_BY_INDEX 0 CALL 0 .L9: - LOAD_CONST 18 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L8: POP 0 PUSH_RETURN_ADDRESS L10 + LOAD_CONST 18 PUSH_RETURN_ADDRESS L11 LOAD_FAST_BY_INDEX 0 CALL 0 .L11: - LOAD_CONST 18 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L10: POP 0 PUSH_RETURN_ADDRESS L12 + LOAD_CONST 18 PUSH_RETURN_ADDRESS L13 LOAD_FAST_BY_INDEX 0 CALL 0 .L13: - LOAD_CONST 18 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L12: POP 0 HALT 0 @@ -101,9 +94,9 @@ page_1 LOAD_CONST 1 STORE 4 CAPTURE 4 - CAPTURE 1 - CAPTURE 2 CAPTURE 3 + CAPTURE 2 + CAPTURE 1 MAKE_CLOSURE 2 RET 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/factorial.expected b/tests/unittests/resources/CompilerSuite/ir/factorial.expected index 6beab98b..631af8e9 100644 --- a/tests/unittests/resources/CompilerSuite/ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/ir/factorial.expected @@ -2,14 +2,13 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L2 - PUSH_RETURN_ADDRESS L3 LOAD_CONST 3 + PUSH_RETURN_ADDRESS L3 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 4 CALL 1 .L3: - LOAD_CONST 4 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L2: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/operators.expected b/tests/unittests/resources/CompilerSuite/ir/operators.expected index 9d3505c0..4854cff5 100644 --- a/tests/unittests/resources/CompilerSuite/ir/operators.expected +++ b/tests/unittests/resources/CompilerSuite/ir/operators.expected @@ -1,73 +1,72 @@ page_0 PUSH_RETURN_ADDRESS L0 LOAD_CONST 0 - TYPE 0 LOAD_CONST 1 - LIST 1 - LOAD_CONST 2 - LIST 1 - LOAD_CONST 3 - LIST 1 - LIST 3 - LOAD_CONST 4 - LOAD_CONST 4 - AT_AT 0 + ADD 0 + LOAD_CONST 0 + LOAD_CONST 1 + SUB 0 + LOAD_CONST 0 + LOAD_CONST 1 + MUL 0 + LOAD_CONST 0 + LOAD_CONST 1 + DIV 0 + LOAD_CONST 0 + LOAD_CONST 1 + MOD 0 LOAD_CONST 2 + TO_NUM 0 LOAD_CONST 3 - LIST 2 + TO_STR 0 + LOAD_CONST 0 + LOAD_CONST 1 + LT 0 + LOAD_CONST 0 + LOAD_CONST 1 + LE 0 + LOAD_CONST 0 + LOAD_CONST 1 + GT 0 + LOAD_CONST 0 + LOAD_CONST 1 + GE 0 + LOAD_CONST 0 + LOAD_CONST 1 + EQ 0 + LOAD_CONST 0 + LOAD_CONST 1 + NEQ 0 LOAD_CONST 3 - AT 0 + NOT 0 LIST 0 - HEAD 0 + LEN 0 LIST 0 - TAIL 0 + IS_EMPTY 0 LIST 0 IS_NIL 0 LIST 0 - IS_EMPTY 0 + TAIL 0 LIST 0 - LEN 0 + HEAD 0 + LOAD_CONST 1 LOAD_CONST 0 - NOT 0 - LOAD_CONST 3 - LOAD_CONST 2 - NEQ 0 - LOAD_CONST 3 - LOAD_CONST 2 - EQ 0 - LOAD_CONST 3 - LOAD_CONST 2 - GE 0 - LOAD_CONST 3 - LOAD_CONST 2 - GT 0 - LOAD_CONST 3 - LOAD_CONST 2 - LE 0 - LOAD_CONST 3 - LOAD_CONST 2 - LT 0 + LIST 2 LOAD_CONST 0 - TO_STR 0 + AT 0 + LOAD_CONST 4 + LIST 1 + LOAD_CONST 1 + LIST 1 + LOAD_CONST 0 + LIST 1 + LIST 3 LOAD_CONST 5 - TO_NUM 0 - LOAD_CONST 3 - LOAD_CONST 2 - MOD 0 - LOAD_CONST 3 - LOAD_CONST 2 - DIV 0 - LOAD_CONST 3 - LOAD_CONST 2 - MUL 0 - LOAD_CONST 3 - LOAD_CONST 2 - SUB 0 + LOAD_CONST 5 + AT_AT 0 LOAD_CONST 3 - LOAD_CONST 2 - ADD 0 - BUILTIN 9 - CALL 22 + TYPE 0 + CALL_BUILTIN 9, 22 .L0: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected index 0662b415..a53fb6ae 100644 --- a/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected +++ b/tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected @@ -1,29 +1,28 @@ page_0 PUSH_RETURN_ADDRESS L0 - BUILTIN 90 - BUILTIN 89 - BUILTIN 88 - BUILTIN 87 - BUILTIN 86 - BUILTIN 85 - BUILTIN 84 - BUILTIN 83 - BUILTIN 82 - BUILTIN 81 - BUILTIN 80 - BUILTIN 79 - BUILTIN 78 - BUILTIN 77 - BUILTIN 76 - BUILTIN 75 - BUILTIN 74 - BUILTIN 73 - BUILTIN 72 - BUILTIN 71 - BUILTIN 70 BUILTIN 69 - BUILTIN 9 - CALL 22 + BUILTIN 70 + BUILTIN 71 + BUILTIN 72 + BUILTIN 73 + BUILTIN 74 + BUILTIN 75 + BUILTIN 76 + BUILTIN 77 + BUILTIN 78 + BUILTIN 79 + BUILTIN 80 + BUILTIN 81 + BUILTIN 82 + BUILTIN 83 + BUILTIN 84 + BUILTIN 85 + BUILTIN 86 + BUILTIN 87 + BUILTIN 88 + BUILTIN 89 + BUILTIN 90 + CALL_BUILTIN 9, 22 .L0: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/plugin.expected b/tests/unittests/resources/CompilerSuite/ir/plugin.expected index a1b7d256..77c9c212 100644 --- a/tests/unittests/resources/CompilerSuite/ir/plugin.expected +++ b/tests/unittests/resources/CompilerSuite/ir/plugin.expected @@ -2,15 +2,13 @@ page_0 PLUGIN 0 PUSH_RETURN_ADDRESS L0 LOAD_CONST 1 - LOAD_CONST 2 PUSH_RETURN_ADDRESS L1 - LOAD_CONST 3 - LOAD_FAST 0 - CALL 1 + LOAD_CONST 2 + CALL_SYMBOL 0, 1 .L1: EQ 0 - BUILTIN 26 - CALL 2 + LOAD_CONST 3 + CALL_BUILTIN 26, 2 .L0: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/renamed_capture.expected b/tests/unittests/resources/CompilerSuite/ir/renamed_capture.expected index 9768b32f..f85e13b2 100644 --- a/tests/unittests/resources/CompilerSuite/ir/renamed_capture.expected +++ b/tests/unittests/resources/CompilerSuite/ir/renamed_capture.expected @@ -7,8 +7,7 @@ page_0 STORE 1 PUSH_RETURN_ADDRESS L0 LOAD_FAST_BY_INDEX 0 - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L0: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/tail_call.expected b/tests/unittests/resources/CompilerSuite/ir/tail_call.expected index ee62ce3f..bd017d27 100644 --- a/tests/unittests/resources/CompilerSuite/ir/tail_call.expected +++ b/tests/unittests/resources/CompilerSuite/ir/tail_call.expected @@ -2,9 +2,9 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L3 - BUILTIN 2 - LOAD_CONST 2 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 2 + BUILTIN 2 CALL 2 .L3: POP 0 @@ -13,18 +13,17 @@ page_0 page_1 STORE 1 STORE 2 - LOAD_FAST_BY_INDEX 0 + LOAD_FAST_BY_INDEX 1 IS_NIL 0 POP_JUMP_IF_TRUE L0 PUSH_RETURN_ADDRESS L1 - LOAD_FAST_BY_INDEX 0 - BUILTIN 9 - CALL 1 + LOAD_FAST_BY_INDEX 1 + CALL_BUILTIN 9, 1 .L1: JUMP L2 .L0: - LOAD_SYMBOL 1 LOAD_CONST 1 + LOAD_SYMBOL 2 TAIL_CALL_SELF 0 .L2: RET 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected index 4386e411..2131676d 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected @@ -34,9 +34,9 @@ page_0 GT_CONST_JUMP_IF_FALSE L8, 3 PUSH_RETURN_ADDRESS L9 PUSH_RETURN_ADDRESS L10 + LOAD_CONST 6 LOAD_FAST 13 LOAD_FAST 13 - LOAD_CONST 6 CALL_BUILTIN 27, 3 .L10: CALL_BUILTIN 9, 1 @@ -45,8 +45,8 @@ page_0 DECREMENT_STORE 13, 1 PUSH_RETURN_ADDRESS L11 PUSH_RETURN_ADDRESS L12 - LOAD_FAST 13 LOAD_CONST 7 + LOAD_FAST 13 CALL_BUILTIN 27, 2 .L12: CALL_BUILTIN 9, 1 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected index 3b282808..b8c57442 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected @@ -1,12 +1,12 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L5 + LOAD_CONST 3 PUSH_RETURN_ADDRESS L6 - LOAD_CONST_LOAD_CONST 3, 4 LOAD_FAST_BY_INDEX 0 + LOAD_CONST_LOAD_CONST 4, 5 CALL 2 .L6: - LOAD_CONST 5 CALL_BUILTIN 9, 2 .L5: POP 0 @@ -15,24 +15,24 @@ page_0 page_1 STORE 1 STORE 2 - LOAD_FAST_BY_INDEX 1 + LOAD_FAST_BY_INDEX 0 GT_CONST_JUMP_IF_TRUE L0, 1 - INCREMENT_BY_INDEX 0, 1 + INCREMENT_BY_INDEX 1, 1 JUMP L1 .L0: LOAD_CONST 1 - EQ_SYM_INDEX_JUMP_IF_TRUE L2, 0 - PUSH_RETURN_ADDRESS L3 + EQ_SYM_INDEX_JUMP_IF_TRUE L2, 1 DECREMENT_BY_INDEX 0, 1 - LOAD_FAST_BY_INDEX 1 + PUSH_RETURN_ADDRESS L3 + LOAD_FAST_BY_INDEX 0 + DECREMENT_BY_INDEX 1, 1 CALL_CURRENT_PAGE 0, 2 .L3: - DECREMENT_BY_INDEX 1, 1 TAIL_CALL_SELF 0 JUMP L4 .L2: + DECREMENT_BY_INDEX 0, 1 LOAD_CONST 2 - DECREMENT_BY_INDEX 1, 1 TAIL_CALL_SELF 0 .L4: .L1: diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected index 61340c32..b802a198 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected @@ -1,15 +1,15 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L1 - LOAD_CONST 1 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 1 CALL 1 .L1: POP 0 LOAD_CONST_STORE 2, 3 PUSH_RETURN_ADDRESS L3 - LOAD_CONST 3 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 3 CALL 1 .L3: POP 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected index 338714eb..15daae10 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected @@ -1,22 +1,22 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L0 + LOAD_FAST_BY_INDEX 0 LOAD_CONST_LOAD_CONST 3, 4 LOAD_CONST 5 - LOAD_FAST_BY_INDEX 0 CALL 3 .L0: STORE 6 PUSH_RETURN_ADDRESS L1 + LOAD_FAST_BY_INDEX 1 LOAD_CONST_LOAD_CONST 6, 7 LOAD_CONST 8 - LOAD_FAST_BY_INDEX 1 CALL 3 .L1: STORE 7 PUSH_RETURN_ADDRESS L2 - GET_FIELD_FROM_SYMBOL_INDEX 1, 2 LOAD_CONST 9 + GET_FIELD_FROM_SYMBOL_INDEX 1, 2 CALL_BUILTIN 9, 2 .L2: POP 0 @@ -26,27 +26,28 @@ page_0 .L3: POP 0 PUSH_RETURN_ADDRESS L4 + LOAD_FAST_BY_INDEX 1 + GET_FIELD_AS_CLOSURE 4 LOAD_CONST 11 - GET_FIELD_FROM_SYMBOL_INDEX 1, 4 CALL 1 .L4: POP 0 PUSH_RETURN_ADDRESS L5 - GET_FIELD_FROM_SYMBOL_INDEX 1, 2 LOAD_CONST 12 + GET_FIELD_FROM_SYMBOL_INDEX 1, 2 CALL_BUILTIN 9, 2 .L5: POP 0 PUSH_RETURN_ADDRESS L6 - GET_FIELD_FROM_SYMBOL_INDEX 0, 2 LOAD_CONST 13 + GET_FIELD_FROM_SYMBOL_INDEX 0, 2 CALL_BUILTIN 9, 2 .L6: POP 0 LOAD_CONST_STORE 14, 8 PUSH_RETURN_ADDRESS L7 - LOAD_CONST 17 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 17 CALL 1 .L7: STORE 10 @@ -59,9 +60,9 @@ page_1 STORE 3 LOAD_CONST_STORE 1, 4 CAPTURE 4 - CAPTURE 1 - CAPTURE 2 CAPTURE 3 + CAPTURE 2 + CAPTURE 1 MAKE_CLOSURE 2 RET 0 HALT 0 @@ -93,26 +94,26 @@ page_5 page_6 PUSH_RETURN_ADDRESS L8 + LOAD_CONST 19 PUSH_RETURN_ADDRESS L9 CALL_SYMBOL 10, 0 .L9: - LOAD_CONST 19 CALL_BUILTIN 9, 2 .L8: POP 0 PUSH_RETURN_ADDRESS L10 + LOAD_CONST 19 PUSH_RETURN_ADDRESS L11 CALL_SYMBOL 10, 0 .L11: - LOAD_CONST 19 CALL_BUILTIN 9, 2 .L10: POP 0 PUSH_RETURN_ADDRESS L12 + LOAD_CONST 19 PUSH_RETURN_ADDRESS L13 CALL_SYMBOL 10, 0 .L13: - LOAD_CONST 19 CALL_BUILTIN 9, 2 .L12: POP 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected index 5961d1ee..05f8bcba 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected @@ -1,12 +1,12 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L2 - PUSH_RETURN_ADDRESS L3 LOAD_CONST 3 + PUSH_RETURN_ADDRESS L3 LOAD_FAST_BY_INDEX 0 + LOAD_CONST 4 CALL 1 .L3: - LOAD_CONST 4 CALL_BUILTIN 9, 2 .L2: POP 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected index 26bcca48..0f8f12e6 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected @@ -8,386 +8,386 @@ page_0 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 44 + FUSED_MATH 45, 45, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 44 + FUSED_MATH 45, 43, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 44 + FUSED_MATH 45, 44, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 41, 44 + FUSED_MATH 45, 42, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 43 + FUSED_MATH 45, 45, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 43 + FUSED_MATH 45, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 43 + FUSED_MATH 45, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 41, 43 + FUSED_MATH 45, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 42 + FUSED_MATH 45, 45, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 42 + FUSED_MATH 45, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 42 + FUSED_MATH 45, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 41, 42 + FUSED_MATH 45, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 41 + FUSED_MATH 45, 45, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 41 + FUSED_MATH 45, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 41 + FUSED_MATH 45, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 41, 41 + FUSED_MATH 45, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 44 + FUSED_MATH 44, 45, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 44 + FUSED_MATH 44, 43, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 44 + FUSED_MATH 44, 44, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 41, 44 + FUSED_MATH 44, 42, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 43 + FUSED_MATH 44, 45, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 43 + FUSED_MATH 44, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 43 + FUSED_MATH 44, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 41, 43 + FUSED_MATH 44, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 42 + FUSED_MATH 44, 45, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 42 + FUSED_MATH 44, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 42 + FUSED_MATH 44, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 41, 42 + FUSED_MATH 44, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 41 + FUSED_MATH 44, 45, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 41 + FUSED_MATH 44, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 41 + FUSED_MATH 44, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 41, 41 + FUSED_MATH 44, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 44 + FUSED_MATH 43, 45, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 44 + FUSED_MATH 43, 43, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 44 + FUSED_MATH 43, 44, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 44 + FUSED_MATH 43, 42, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 43 + FUSED_MATH 43, 45, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 43 + FUSED_MATH 43, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 43 + FUSED_MATH 43, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 43 + FUSED_MATH 43, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 42 + FUSED_MATH 43, 45, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 42 + FUSED_MATH 43, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 42 + FUSED_MATH 43, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 42 + FUSED_MATH 43, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 41 + FUSED_MATH 43, 45, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 41 + FUSED_MATH 43, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 41 + FUSED_MATH 43, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 41, 41 + FUSED_MATH 43, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 44, 44 + FUSED_MATH 42, 45, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 44 + FUSED_MATH 42, 43, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 43, 44 + FUSED_MATH 42, 44, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 44 + FUSED_MATH 42, 42, 45 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 44, 43 + FUSED_MATH 42, 45, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 43 + FUSED_MATH 42, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 43, 43 + FUSED_MATH 42, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 43 + FUSED_MATH 42, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 44, 42 + FUSED_MATH 42, 45, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 42 + FUSED_MATH 42, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 43, 42 + FUSED_MATH 42, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 42 + FUSED_MATH 42, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 44, 41 + FUSED_MATH 42, 45, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 42, 41 + FUSED_MATH 42, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 43, 41 + FUSED_MATH 42, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 41, 41, 41 + FUSED_MATH 42, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 44, 0 + FUSED_MATH 45, 45, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 44, 0 + FUSED_MATH 43, 45, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 44, 0 + FUSED_MATH 44, 45, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 44, 0 + FUSED_MATH 42, 45, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 43, 0 + FUSED_MATH 45, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 43, 0 + FUSED_MATH 43, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 43, 0 + FUSED_MATH 44, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 43, 0 + FUSED_MATH 42, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 42, 0 + FUSED_MATH 45, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 42, 0 + FUSED_MATH 43, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 42, 0 + FUSED_MATH 44, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 42, 0 + FUSED_MATH 42, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 41, 0 + FUSED_MATH 45, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 41, 0 + FUSED_MATH 43, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 41, 0 + FUSED_MATH 44, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 41, 41, 0 + FUSED_MATH 42, 42, 0 LIST 80 CALL_BUILTIN 9, 1 .L0: diff --git a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected index 4dc361ae..a19ba2fc 100644 --- a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected +++ b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected @@ -28,10 +28,10 @@ In file tests/unittests/resources/DebuggerSuite/stack_and_locals.ark:3 5 | (f "correct" "wrong") dbg[pp:1,ip:4]:000> stack +4 -> Garbage (expected) 3 -> Instruction@28 2 -> Function@0 1 -> Instruction@32 -0 -> Function@0 dbg[pp:1,ip:4]:000> locals 1 scope size: 3 @@ -42,21 +42,21 @@ dbg[pp:1,ip:4]:000> locals 2 scope size: 3 index | id | name | type | value 2 | 3 | x | Number | 1 - 1 | 2 | b | Number | 2 + 1 | 2 | a | Number | 1 dbg[pp:1,ip:4]:000> locals 3 scope size: 3 index | id | name | type | value 2 | 3 | x | Number | 1 - 1 | 2 | b | Number | 2 - 0 | 1 | a | Number | 1 + 1 | 2 | a | Number | 1 + 0 | 1 | b | Number | 2 dbg[pp:1,ip:4]:000> locals scope size: 3 index | id | name | type | value 2 | 3 | x | Number | 1 - 1 | 2 | b | Number | 2 - 0 | 1 | a | Number | 1 + 1 | 2 | a | Number | 1 + 0 | 1 | b | Number | 2 dbg[pp:1,ip:4]:000> c dbg: continue @@ -70,17 +70,17 @@ In file tests/unittests/resources/DebuggerSuite/stack_and_locals.ark:3 5 | (f "correct" "wrong") dbg[pp:1,ip:4]:000> stack +4 -> Garbage (expected) 3 -> Instruction@28 2 -> Function@0 1 -> Instruction@32 -0 -> Function@0 dbg[pp:1,ip:4]:000> locals scope size: 3 index | id | name | type | value 2 | 3 | x | String | "correct" - 1 | 2 | b | String | "wrong" - 0 | 1 | a | String | "correct" + 1 | 2 | a | String | "correct" + 0 | 1 | b | String | "wrong" dbg[pp:1,ip:4]:000> c dbg: continue diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected index 2f78d0b8..88cb5f65 100644 --- a/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected @@ -8,9 +8,9 @@ In file tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3 4 | tmp 5 | })) -[2047] In function `foo' (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3) +[2046] In function `foo' (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3) ... [ 0] In global scope (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:7) Current scope variables values: -a = 2045 +a = 2044 From be9c9c03bcff489bef1ffe9f25e65c47c71ed3b8 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 17 Mar 2026 16:25:28 +0100 Subject: [PATCH 10/13] chore: update stored benchmarks results after changing the arguments evaluation order --- tests/benchmarks/results/014-b5039d40.csv | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/benchmarks/results/014-b5039d40.csv diff --git a/tests/benchmarks/results/014-b5039d40.csv b/tests/benchmarks/results/014-b5039d40.csv new file mode 100644 index 00000000..714787c6 --- /dev/null +++ b/tests/benchmarks/results/014-b5039d40.csv @@ -0,0 +1,11 @@ +name,iterations,real_time,cpu_time,time_unit,bytes_per_second,items_per_second,label,error_occurred,error_message +"quicksort",7420,0.0914816,0.091408,ms,,,,, +"ackermann/iterations:50",50,30.7419,30.7046,ms,,,,, +"fibonacci/iterations:100",100,3.18342,3.18024,ms,,,,, +"builtins",1185,0.584659,0.584104,ms,,,,, +"binary_trees",1,871.301,870.207,ms,,,,, +"for_sum",8,82.8231,82.7417,ms,,,,, +"create_closure/iterations:500",500,0.7656,0.764976,ms,,,,, +"create_list/iterations:500",500,1.69178,1.69003,ms,,,,, +"create_list_with_ref/iterations:500",500,1.52249,1.52134,ms,,,,, +"n_queens/iterations:50",50,12.2839,12.2734,ms,,,,, From 931f6dad8da32ed423c08bb785e1a8872816c6bc Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 17 Mar 2026 17:00:10 +0100 Subject: [PATCH 11/13] chore: remove GET_CURRENT_PAGE_ADDR instruction --- CHANGELOG.md | 1 + include/Ark/Compiler/Instructions.hpp | 159 +++++++++-------- src/arkreactor/Compiler/BytecodeReader.cpp | 1 - .../Compiler/Lowerer/ASTLowerer.cpp | 2 +- src/arkreactor/VM/VM.cpp | 8 - .../optimized_ir/fused_math_ops.expected | 160 +++++++++--------- 6 files changed, 159 insertions(+), 172 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f002eb..a6f757bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - instruction counter in the bytecode reader are displayed in hex, and count each instruction instead of each byte ### Removed +- removed a nearly never emitted `GET_CURRENT_PAGE_ADDR` instruction, since it's now always optimised with `CALL` into a `CALL_CURRENT_PAGE` instruction ## [4.4.0] - 2026-03-13 ### Deprecations diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index c509687b..4b808944 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -174,290 +174,286 @@ namespace Ark::internal // @role Destroy the last local scope POP_SCOPE = 0x26, - // @args symbol id (function name) - // @role Push the current page address as a value on the stack - GET_CURRENT_PAGE_ADDR = 0x27, - // @role Pop a List from the stack and a function, and call the function with the given list as arguments - APPLY = 0x28, + APPLY = 0x27, - FIRST_OPERATOR = 0x29, + FIRST_OPERATOR = 0x28, // @role Pop the top of the stack, if it's true, trigger the debugger - BREAKPOINT = 0x29, + BREAKPOINT = 0x28, // @role Push `TS1 + TS` - ADD = 0x2a, + ADD = 0x29, // @role Push `TS1 - TS` - SUB = 0x2b, + SUB = 0x2a, // @role Push `TS1 * TS` - MUL = 0x2c, + MUL = 0x2b, // @role Push `TS1 / TS` - DIV = 0x2d, + DIV = 0x2c, // @role Push `TS1 > TS` - GT = 0x2e, + GT = 0x2d, // @role Push `TS1 < TS` - LT = 0x2f, + LT = 0x2e, // @role Push `TS1 <= TS` - LE = 0x30, + LE = 0x2f, // @role Push `TS1 >= TS` - GE = 0x31, + GE = 0x30, // @role Push `TS1 != TS` - NEQ = 0x32, + NEQ = 0x31, // @role Push `TS1 == TS` - EQ = 0x33, + EQ = 0x32, // @role Push `len(TS)`, TS must be a list - LEN = 0x34, + LEN = 0x33, // @role Push `empty?(TS)`, TS must be a list or string - IS_EMPTY = 0x35, + IS_EMPTY = 0x34, // @role Push `tail(TS)`, all the elements of TS except the first one. TS must be a list or string - TAIL = 0x36, + TAIL = 0x35, // @role Push `head(TS)`, the first element of TS or nil if empty. TS must be a list or string - HEAD = 0x37, + HEAD = 0x36, // @role Push true if TS is nil, false otherwise - IS_NIL = 0x38, + IS_NIL = 0x37, // @role Convert TS to number (must be a string) - TO_NUM = 0x39, + TO_NUM = 0x38, // @role Convert TS to string - TO_STR = 0x3a, + TO_STR = 0x39, // @role Push the value at index TS (must be a number) in TS1, which must be a list or string - AT = 0x3b, + AT = 0x3a, // @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 = 0x3c, + AT_AT = 0x3b, // @role Push `TS1 % TS` - MOD = 0x3d, + MOD = 0x3c, // @role Push the type of TS as a string - TYPE = 0x3e, + TYPE = 0x3d, // @role Check if TS1 is a closure field of TS. TS must be a Closure, TS1 a String - HAS_FIELD = 0x3f, + HAS_FIELD = 0x3e, // @role Push `!TS` - NOT = 0x40, + NOT = 0x3f, // @args constant id, constant id // @role Load two consts (`primary` then `secondary`) on the stack in one instruction - LOAD_CONST_LOAD_CONST = 0x41, + LOAD_CONST_LOAD_CONST = 0x40, // @args constant id, symbol id // @role Load const `primary` into the symbol `secondary` (create a variable) - LOAD_CONST_STORE = 0x42, + LOAD_CONST_STORE = 0x41, // @args constant id, symbol id // @role Load const `primary` into the symbol `secondary` (search for the variable with the given symbol id) - LOAD_CONST_SET_VAL = 0x43, + LOAD_CONST_SET_VAL = 0x42, // @args symbol id, symbol id // @role Store the value of the symbol `primary` into a new variable `secondary` - STORE_FROM = 0x44, + STORE_FROM = 0x43, // @args symbol index, symbol id // @role Store the value of the symbol `primary` into a new variable `secondary` - STORE_FROM_INDEX = 0x45, + STORE_FROM_INDEX = 0x44, // @args symbol id, symbol id // @role Store the value of the symbol `primary` into an existing variable `secondary` - SET_VAL_FROM = 0x46, + SET_VAL_FROM = 0x45, // @args symbol index, symbol id // @role Store the value of the symbol `primary` into an existing variable `secondary` - SET_VAL_FROM_INDEX = 0x47, + SET_VAL_FROM_INDEX = 0x46, // @args symbol id, count // @role Increment the variable `primary` by `count` and push its value on the stack - INCREMENT = 0x48, + INCREMENT = 0x47, // @args symbol index, count // @role Increment the variable `primary` by `count` and push its value on the stack - INCREMENT_BY_INDEX = 0x49, + INCREMENT_BY_INDEX = 0x48, // @args symbol id, count // @role Increment the variable `primary` by `count` and store its value in the given symbol id - INCREMENT_STORE = 0x4a, + INCREMENT_STORE = 0x49, // @args symbol id, count // @role Decrement the variable `primary` by `count` and push its value on the stack - DECREMENT = 0x4b, + DECREMENT = 0x4a, // @args symbol index, count // @role Decrement the variable `primary` by `count` and push its value on the stack - DECREMENT_BY_INDEX = 0x4c, + DECREMENT_BY_INDEX = 0x4b, // @args symbol id, count // @role Decrement the variable `primary` by `count` and store its value in the given symbol id - DECREMENT_STORE = 0x4d, + DECREMENT_STORE = 0x4c, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary` - STORE_TAIL = 0x4e, + STORE_TAIL = 0x4d, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its tail, store it in a new variable `secondary` - STORE_TAIL_BY_INDEX = 0x4f, + STORE_TAIL_BY_INDEX = 0x4e, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary` - STORE_HEAD = 0x50, + STORE_HEAD = 0x4f, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its head, store it in a new variable `secondary` - STORE_HEAD_BY_INDEX = 0x51, + STORE_HEAD_BY_INDEX = 0x50, // @args number, symbol id // @role Create a list of `number` elements, and store it in a new variable `secondary` - STORE_LIST = 0x52, + STORE_LIST = 0x51, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary` - SET_VAL_TAIL = 0x53, + SET_VAL_TAIL = 0x52, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its tail, store it in an existing variable `secondary` - SET_VAL_TAIL_BY_INDEX = 0x54, + SET_VAL_TAIL_BY_INDEX = 0x53, // @args symbol id, symbol id // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary` - SET_VAL_HEAD = 0x55, + SET_VAL_HEAD = 0x54, // @args symbol index, symbol id // @role Load the symbol `primary`, compute its head, store it in an existing variable `secondary` - SET_VAL_HEAD_BY_INDEX = 0x56, + SET_VAL_HEAD_BY_INDEX = 0x55, // @args builtin id, argument count // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack - CALL_BUILTIN = 0x57, + CALL_BUILTIN = 0x56, // @args builtin id, argument count // @role Call a builtin by its id in `primary`, with `secondary` arguments. Bypass the stack size check because we do not push IP/PP since builtins calls do not alter the stack, as well as the return address removal - CALL_BUILTIN_WITHOUT_RETURN_ADDRESS = 0x58, + CALL_BUILTIN_WITHOUT_RETURN_ADDRESS = 0x57, // @args constant id, absolute address to jump to // @role Compare `TS < constant`, if the comparison fails, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_FALSE = 0x59, + LT_CONST_JUMP_IF_FALSE = 0x58, // @args constant id, absolute address to jump to // @role Compare `TS < constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - LT_CONST_JUMP_IF_TRUE = 0x5a, + LT_CONST_JUMP_IF_TRUE = 0x59, // @args symbol id, absolute address to jump to // @role Compare `TS < symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - LT_SYM_JUMP_IF_FALSE = 0x5b, + LT_SYM_JUMP_IF_FALSE = 0x5a, // @args constant id, absolute address to jump to // @role Compare `TS > constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_TRUE = 0x5c, + GT_CONST_JUMP_IF_TRUE = 0x5b, // @args constant id, absolute address to jump to // @role Compare `TS > constant`, if the comparison fails, jump to the given address. Otherwise, does nothing - GT_CONST_JUMP_IF_FALSE = 0x5d, + GT_CONST_JUMP_IF_FALSE = 0x5c, // @args symbol id, absolute address to jump to // @role Compare `TS > symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - GT_SYM_JUMP_IF_FALSE = 0x5e, + GT_SYM_JUMP_IF_FALSE = 0x5d, // @args constant id, absolute address to jump to // @role Compare `TS == constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_CONST_JUMP_IF_TRUE = 0x5f, + EQ_CONST_JUMP_IF_TRUE = 0x5e, // @args symbol index, absolute address to jump to // @role Compare `TS == symbol`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - EQ_SYM_INDEX_JUMP_IF_TRUE = 0x60, + EQ_SYM_INDEX_JUMP_IF_TRUE = 0x5f, // @args constant id, absolute address to jump to // @role Compare `TS != constant`, if the comparison succeeds, jump to the given address. Otherwise, does nothing - NEQ_CONST_JUMP_IF_TRUE = 0x61, + NEQ_CONST_JUMP_IF_TRUE = 0x60, // @args symbol id, absolute address to jump to // @role Compare `TS != symbol`, if the comparison fails, jump to the given address. Otherwise, does nothing - NEQ_SYM_JUMP_IF_FALSE = 0x62, + NEQ_SYM_JUMP_IF_FALSE = 0x61, // @args symbol id, argument count // @role Call a symbol by its id in `primary`, with `secondary` arguments - CALL_SYMBOL = 0x63, + CALL_SYMBOL = 0x62, // @args symbol id (function name), argument count // @role Call the current page with `secondary` arguments - CALL_CURRENT_PAGE = 0x64, + CALL_CURRENT_PAGE = 0x63, // @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 = 0x65, + GET_FIELD_FROM_SYMBOL = 0x64, // @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 = 0x66, + GET_FIELD_FROM_SYMBOL_INDEX = 0x65, // @args symbol id, symbol id2 // @role Push symbol[symbol2] - AT_SYM_SYM = 0x67, + AT_SYM_SYM = 0x66, // @args symbol index, symbol index2 // @role Push symbol[symbol2] - AT_SYM_INDEX_SYM_INDEX = 0x68, + AT_SYM_INDEX_SYM_INDEX = 0x67, // @args symbol index, constant id // @role Push symbol[constant] - AT_SYM_INDEX_CONST = 0x69, + AT_SYM_INDEX_CONST = 0x68, // @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 = 0x6a, + CHECK_TYPE_OF = 0x69, // @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 = 0x6b, + CHECK_TYPE_OF_BY_INDEX = 0x6a, // @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 = 0x6c, + APPEND_IN_PLACE_SYM = 0x6b, // @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 = 0x6d, + APPEND_IN_PLACE_SYM_INDEX = 0x6c, // @args symbol index, symbol id // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id) - STORE_LEN = 0x6e, + STORE_LEN = 0x6d, // @args symbol id, absolute address to jump to // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false - LT_LEN_SYM_JUMP_IF_FALSE = 0x6f, + LT_LEN_SYM_JUMP_IF_FALSE = 0x6e, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY = 0x70, + MUL_BY = 0x6f, // @args symbol index, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY_INDEX = 0x71, + MUL_BY_INDEX = 0x70, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id - MUL_SET_VAL = 0x72, + MUL_SET_VAL = 0x71, // @args op1, op2, op3 // @role Pop 3 or 4 values from the stack, and apply the ops sequentially (only ADD, SUB, MUL, and DIV are supported). Push the result to the stack. Only op3 may be NOP. - FUSED_MATH = 0x73, + FUSED_MATH = 0x72, InstructionsCount }; @@ -502,7 +498,6 @@ namespace Ark::internal "CREATE_SCOPE", "RESET_SCOPE_JUMP", "POP_SCOPE", - "GET_CURRENT_PAGE_ADDR", "APPLY", // operators "BREAKPOINT", diff --git a/src/arkreactor/Compiler/BytecodeReader.cpp b/src/arkreactor/Compiler/BytecodeReader.cpp index d42d60f4..0e581b3b 100644 --- a/src/arkreactor/Compiler/BytecodeReader.cpp +++ b/src/arkreactor/Compiler/BytecodeReader.cpp @@ -520,7 +520,6 @@ namespace Ark { SET_AT_INDEX, ArgKind::Raw }, { SET_AT_2_INDEX, ArgKind::Raw }, { RESET_SCOPE_JUMP, ArgKind::Raw }, - { GET_CURRENT_PAGE_ADDR, ArgKind::Symbol }, { LOAD_CONST_LOAD_CONST, ArgKind::ConstConst }, { LOAD_CONST_STORE, ArgKind::ConstSym }, { LOAD_CONST_SET_VAL, ArgKind::ConstSym }, diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index aca5f112..431b1366 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -548,7 +548,7 @@ namespace Ark::internal // Register an opened variable as "#anonymous", which won't match any valid names inside ASTLowerer::handleCalls. // This way we can continue to safely apply optimisations on // (let name (fun (e) (map lst (fun (e) (name e))))) - // Otherwise, `name` would have been optimized to a GET_CURRENT_PAGE_ADDR, which would have returned the wrong page. + // Otherwise, `name` would have been optimized to a CALL_CURRENT_PAGE, which would have returned the wrong page. if (x.isAnonymousFunction()) m_opened_vars.emplace("#anonymous"); // push body of the function diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 79b9ab08..008b05a1 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -490,7 +490,6 @@ namespace Ark &&TARGET_CREATE_SCOPE, &&TARGET_RESET_SCOPE_JUMP, &&TARGET_POP_SCOPE, - &&TARGET_GET_CURRENT_PAGE_ADDR, &&TARGET_APPLY, &&TARGET_BREAKPOINT, &&TARGET_ADD, @@ -1114,13 +1113,6 @@ namespace Ark DISPATCH(); } - TARGET(GET_CURRENT_PAGE_ADDR) - { - context.last_symbol = arg; - push(Value(static_cast(context.pp)), context); - DISPATCH(); - } - TARGET(APPLY) { { diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected index 0f8f12e6..26bcca48 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/fused_math_ops.expected @@ -8,386 +8,386 @@ page_0 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 45, 45 + FUSED_MATH 44, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 43, 45 + FUSED_MATH 44, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 44, 45 + FUSED_MATH 44, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 42, 45 + FUSED_MATH 44, 41, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 45, 44 + FUSED_MATH 44, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 43, 44 + FUSED_MATH 44, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 44, 44 + FUSED_MATH 44, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 42, 44 + FUSED_MATH 44, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 45, 43 + FUSED_MATH 44, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 43, 43 + FUSED_MATH 44, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 44, 43 + FUSED_MATH 44, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 42, 43 + FUSED_MATH 44, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 45, 42 + FUSED_MATH 44, 44, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 43, 42 + FUSED_MATH 44, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 44, 42 + FUSED_MATH 44, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 45, 42, 42 + FUSED_MATH 44, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 45, 45 + FUSED_MATH 43, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 45 + FUSED_MATH 43, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 45 + FUSED_MATH 43, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 45 + FUSED_MATH 43, 41, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 45, 44 + FUSED_MATH 43, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 44 + FUSED_MATH 43, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 44 + FUSED_MATH 43, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 44 + FUSED_MATH 43, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 45, 43 + FUSED_MATH 43, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 43 + FUSED_MATH 43, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 43 + FUSED_MATH 43, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 43 + FUSED_MATH 43, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 45, 42 + FUSED_MATH 43, 44, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 43, 42 + FUSED_MATH 43, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 44, 42 + FUSED_MATH 43, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 44, 42, 42 + FUSED_MATH 43, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 45, 45 + FUSED_MATH 42, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 45 + FUSED_MATH 42, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 45 + FUSED_MATH 42, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 45 + FUSED_MATH 42, 41, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 45, 44 + FUSED_MATH 42, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 44 + FUSED_MATH 42, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 44 + FUSED_MATH 42, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 44 + FUSED_MATH 42, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 45, 43 + FUSED_MATH 42, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 43 + FUSED_MATH 42, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 43 + FUSED_MATH 42, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 43 + FUSED_MATH 42, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 45, 42 + FUSED_MATH 42, 44, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 43, 42 + FUSED_MATH 42, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 44, 42 + FUSED_MATH 42, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 43, 42, 42 + FUSED_MATH 42, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 45, 45 + FUSED_MATH 41, 44, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 45 + FUSED_MATH 41, 42, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 45 + FUSED_MATH 41, 43, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 45 + FUSED_MATH 41, 41, 44 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 45, 44 + FUSED_MATH 41, 44, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 44 + FUSED_MATH 41, 42, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 44 + FUSED_MATH 41, 43, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 44 + FUSED_MATH 41, 41, 43 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 45, 43 + FUSED_MATH 41, 44, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 43 + FUSED_MATH 41, 42, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 43 + FUSED_MATH 41, 43, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 43 + FUSED_MATH 41, 41, 42 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 45, 42 + FUSED_MATH 41, 44, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 43, 42 + FUSED_MATH 41, 42, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 44, 42 + FUSED_MATH 41, 43, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 LOAD_FAST_BY_INDEX 0 - FUSED_MATH 42, 42, 42 + FUSED_MATH 41, 41, 41 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 45, 45, 0 + FUSED_MATH 44, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 45, 0 + FUSED_MATH 42, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 45, 0 + FUSED_MATH 43, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 45, 0 + FUSED_MATH 41, 44, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 45, 44, 0 + FUSED_MATH 44, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 44, 0 + FUSED_MATH 42, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 44, 0 + FUSED_MATH 43, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 44, 0 + FUSED_MATH 41, 43, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 45, 43, 0 + FUSED_MATH 44, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 43, 0 + FUSED_MATH 42, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 43, 0 + FUSED_MATH 43, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 43, 0 + FUSED_MATH 41, 42, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 45, 42, 0 + FUSED_MATH 44, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 43, 42, 0 + FUSED_MATH 42, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 44, 42, 0 + FUSED_MATH 43, 41, 0 LOAD_FAST_BY_INDEX 3 LOAD_FAST_BY_INDEX 2 LOAD_FAST_BY_INDEX 1 - FUSED_MATH 42, 42, 0 + FUSED_MATH 41, 41, 0 LIST 80 CALL_BUILTIN 9, 1 .L0: From 9add421d9d769922ebcd18ae64c10c50ff769af1 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 17 Mar 2026 18:19:20 +0100 Subject: [PATCH 12/13] feat(ci): add a job to report the super instructions usage --- .github/workflows/ci.yml | 68 ++++++++++++++++++++++++++++++ tools/ark_frequent_instructions.py | 54 ++++++++++++++++++------ 2 files changed, 108 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c7a00f1..801531b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -247,6 +247,74 @@ jobs: exit 1 fi + check-super-insts-usage: + runs-on: ubuntu-24.04 + name: Check Super Instructions usage + needs: [ check ] + + steps: + - uses: actions/checkout@v5 + with: + submodules: recursive + + - name: Setup compilers, dependencies, project and build + uses: ./.github/workflows/setup-compilers + with: + os_name: ubuntu-24.04 + compiler: clang + compiler_version: 16 + sanitizers: "On" + with_deps: false + + - uses: actions/setup-python@v6 + if: ${{ ! startsWith(matrix.config.name, 'Windows') }} + with: + python-version: '3.13' + + - run: | + KO=0 + python3 tools/ark_frequent_instructions.py super_insts_usage > output.txt || KO=1 + echo "SUPER_INSTS_REPORT_KO=$KO" >> $GITHUB_ENV + echo "SUPER_INSTS_REPORT<> $GITHUB_ENV + cat output.txt >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - uses: 8BitJonny/gh-get-current-pr@4.0.0 + id: PR + with: + sha: ${{ github.event.pull_request.head.sha }} + # Only return if PR is still open + filterOutClosed: true + + - run: | + echo "PR found: ${prFound}" + echo "PR ${prNumber:-ERROR} ${prTitle:-NOTITLE}" + env: + # Direct access to common PR properties + prNumber: ${{ steps.PR.outputs.number }} + prTitle: ${{ steps.PR.outputs.pr_title }} + prFound: ${{ steps.PR.outputs.pr_found }} + + - name: Find Comment + uses: peter-evans/find-comment@v4 + if: steps.PR.outputs.pr_found == 'true' + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: Super Instructions report + + - name: Create or update comment + uses: peter-evans/create-or-update-comment@v5.0.0 + if: steps.PR.outputs.pr_found == 'true' + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + ## Super Instructions report + ${{ env.SUPER_INSTS_REPORT }} + edit-mode: replace + build: runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.name }} diff --git a/tools/ark_frequent_instructions.py b/tools/ark_frequent_instructions.py index ff15947b..6ce88637 100755 --- a/tools/ark_frequent_instructions.py +++ b/tools/ark_frequent_instructions.py @@ -52,9 +52,15 @@ "APPEND_IN_PLACE_SYM", "APPEND_IN_PLACE_SYM_INDEX", "STORE_LEN", - "LT_LEN_SYM_JUMP_IF_FALSE" + "LT_LEN_SYM_JUMP_IF_FALSE", + "MUL_BY", + "MUL_BY_INDEX", + "MUL_SET_VAL", + "FUSED_MATH" ] +compute_super_insts_usage = sys.argv[1] == "super_insts_usage" + executable = None for p in ["./arkscript", "cmake-build-debug/arkscript", "build/arkscript", "build/arkscript.exe"]: if os.path.exists(p): @@ -67,14 +73,19 @@ ir = [] rosetta = glob.glob("tests/unittests/resources/RosettaSuite/*.ark") -for file in [ - "tests/unittests/resources/LangSuite/unittests.ark", - "lib/std/tests/all.ark" - ] + rosetta: - os.system(f"{executable} -c {file} -fno-optimizer -fdump-ir --lib './lib/;./tests/unittests/'") - - if os.path.exists(f"{file}.ir"): - with open(f"{file}.ir") as f: +examples = glob.glob("examples/*.ark") +for file in rosetta + examples + [ + "tests/unittests/resources/LangSuite/unittests.ark", + "lib/std/tests/all.ark" +]: + os.system(f"{executable} -c {file} -fdump-ir --lib './lib/;./tests/unittests/'") + + d = os.path.dirname(file) + f = os.path.basename(file) + path = f"{d}/__arkscript__/{f}.ir" + + if os.path.exists(path): + with open(path) as f: pages = f.read().split("\n\n") for page in pages: # only keep the instruction names @@ -84,7 +95,7 @@ ] # remove the page name (page_) ir.append(insts[1:]) - os.remove(f"{file}.ir") + os.remove(path) def window(iterable, size): @@ -136,12 +147,27 @@ def skip_inst_for_frequency(inst): def print_most_freqs(data, max_percent=10): most = sorted(data.items(), key=lambda e: e[1], reverse=True) interesting = most[:(len(most) * max_percent) // 100] - print("\n".join(f"{insts} -> {count}" for (insts, count) in interesting)) - - -print("Super instructions present:") + if compute_super_insts_usage: + print("| Super Instruction | Uses in compiled code |") + print("| ----------------- | --------------------- |") + print("\n".join(f"| {insts} | {count} |" for (insts, count) in interesting)) + else: + print("\n".join(f"{insts} -> {count}" for (insts, count) in interesting)) + + if compute_super_insts_usage: + threshold = 10 + for (inst, count) in most: + if count <= threshold: + sys.exit(1) + + +if not compute_super_insts_usage: + print("Super instructions present:") print_most_freqs(super_insts_freqs, max_percent=100) +if compute_super_insts_usage: + sys.exit(0) + for i in (2, 3, 4): print(f"\nPairs of {i}:") print_most_freqs(frequent[i]) From 7a48276d496a11063b4596316fc334f3e8e24ad1 Mon Sep 17 00:00:00 2001 From: Lexy Plt Date: Tue, 17 Mar 2026 19:05:02 +0100 Subject: [PATCH 13/13] feat(vm): add a new super instruction CALL_SYMBOL_BY_INDEX --- CHANGELOG.md | 1 + include/Ark/Compiler/Instructions.hpp | 37 +++++++++++-------- include/Ark/VM/VM.hpp | 2 +- include/Ark/VM/VM.inl | 2 +- src/arkreactor/Compiler/BytecodeReader.cpp | 1 + .../Compiler/Lowerer/ASTLowerer.cpp | 12 ++++++ src/arkreactor/VM/VM.cpp | 18 +++++++-- .../unittests/Suites/BytecodeReaderSuite.cpp | 2 +- .../CompilerSuite/ir/ackermann.expected | 3 +- .../CompilerSuite/ir/args_attr.expected | 3 +- .../CompilerSuite/ir/breakpoints.expected | 9 ++--- .../CompilerSuite/ir/closures.expected | 18 +++------ .../CompilerSuite/ir/factorial.expected | 3 +- .../CompilerSuite/ir/tail_call.expected | 3 +- .../optimized_ir/ackermann.expected | 3 +- .../call_builtin_not_optimized.expected | 6 +-- .../optimized_ir/closures.expected | 9 ++--- .../optimized_ir/factorial.expected | 3 +- .../CompilerSuite/optimized_ir/lists.expected | 3 +- .../CompilerSuite/optimized_ir/mul.expected | 3 +- .../CompilerSuite/optimized_ir/type.expected | 3 +- .../DebuggerSuite/stack_and_locals.expected | 12 +++--- .../runtime/recursion_depth.expected | 4 +- tools/ark_frequent_instructions.py | 1 + 24 files changed, 84 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6f757bf..dd45973f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Added - the bytecode reader can print the argument of a `PUSH_RETURN_ADDRESS` instruction as a hex number +- new super instruction `CALL_SYMBOL_BY_INDEX` to optimise a `LOAD_FAST_BY_INDEX` followed by a `CALL` ### Changed - instruction counter in the bytecode reader are displayed in hex, and count each instruction instead of each byte diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 4b808944..b7dd9365 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -391,69 +391,73 @@ namespace Ark::internal // @role Call a symbol by its id in `primary`, with `secondary` arguments CALL_SYMBOL = 0x62, + // @args symbol index, argument count + // @role Call a symbol by its index in the locals in `primary`, with `secondary` arguments + CALL_SYMBOL_BY_INDEX = 0x63, + // @args symbol id (function name), argument count // @role Call the current page with `secondary` arguments - CALL_CURRENT_PAGE = 0x63, + CALL_CURRENT_PAGE = 0x64, // @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 = 0x64, + GET_FIELD_FROM_SYMBOL = 0x65, // @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 = 0x65, + GET_FIELD_FROM_SYMBOL_INDEX = 0x66, // @args symbol id, symbol id2 // @role Push symbol[symbol2] - AT_SYM_SYM = 0x66, + AT_SYM_SYM = 0x67, // @args symbol index, symbol index2 // @role Push symbol[symbol2] - AT_SYM_INDEX_SYM_INDEX = 0x67, + AT_SYM_INDEX_SYM_INDEX = 0x68, // @args symbol index, constant id // @role Push symbol[constant] - AT_SYM_INDEX_CONST = 0x68, + AT_SYM_INDEX_CONST = 0x69, // @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 = 0x69, + CHECK_TYPE_OF = 0x6a, // @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 = 0x6a, + CHECK_TYPE_OF_BY_INDEX = 0x6b, // @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 = 0x6b, + APPEND_IN_PLACE_SYM = 0x6c, // @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 = 0x6c, + APPEND_IN_PLACE_SYM_INDEX = 0x6d, // @args symbol index, symbol id // @role Compute the length of the list or string at symbol index, and store it in a variable (symbol id) - STORE_LEN = 0x6d, + STORE_LEN = 0x6e, // @args symbol id, absolute address to jump to // @role Compute the length of a symbol (list or string), and pop TS to compare it, then jump if false - LT_LEN_SYM_JUMP_IF_FALSE = 0x6e, + LT_LEN_SYM_JUMP_IF_FALSE = 0x6f, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY = 0x6f, + MUL_BY = 0x70, // @args symbol index, offset number // @role Multiply the symbol by (offset symbol - 2048), then push it to the stack - MUL_BY_INDEX = 0x70, + MUL_BY_INDEX = 0x71, // @args symbol id, offset number // @role Multiply the symbol by (offset symbol - 2048), then store the result using the given symbol id - MUL_SET_VAL = 0x71, + MUL_SET_VAL = 0x72, // @args op1, op2, op3 // @role Pop 3 or 4 values from the stack, and apply the ops sequentially (only ADD, SUB, MUL, and DIV are supported). Push the result to the stack. Only op3 may be NOP. - FUSED_MATH = 0x72, + FUSED_MATH = 0x73, InstructionsCount }; @@ -560,6 +564,7 @@ namespace Ark::internal "NEQ_CONST_JUMP_IF_TRUE", "NEQ_SYM_JUMP_IF_FALSE", "CALL_SYMBOL", + "CALL_SYMBOL_BY_INDEX", "CALL_CURRENT_PAGE", "GET_FIELD_FROM_SYMBOL", "GET_FIELD_FROM_SYMBOL_INDEX", diff --git a/include/Ark/VM/VM.hpp b/include/Ark/VM/VM.hpp index 472c88e1..a17fb070 100644 --- a/include/Ark/VM/VM.hpp +++ b/include/Ark/VM/VM.hpp @@ -364,7 +364,7 @@ namespace Ark */ uint16_t findNearestVariableIdWithValue(const Value& value, internal::ExecutionContext& context) const noexcept; - [[noreturn]] void throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, internal::ExecutionContext& context); + [[noreturn]] void throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, internal::ExecutionContext& context, bool skip_function = true); void initDebugger(internal::ExecutionContext& context); diff --git a/include/Ark/VM/VM.inl b/include/Ark/VM/VM.inl index 116f5fd0..ce95da89 100644 --- a/include/Ark/VM/VM.inl +++ b/include/Ark/VM/VM.inl @@ -355,7 +355,7 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc, V } if (std::cmp_not_equal(needed_argc, argc)) [[unlikely]] - throwArityError(argc, needed_argc, context); + throwArityError(argc, needed_argc, context, /* skip_function= */ function_ptr == nullptr); } inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& builtin, const uint16_t argc, const bool remove_return_address, const bool remove_builtin) diff --git a/src/arkreactor/Compiler/BytecodeReader.cpp b/src/arkreactor/Compiler/BytecodeReader.cpp index 0e581b3b..9f6fe733 100644 --- a/src/arkreactor/Compiler/BytecodeReader.cpp +++ b/src/arkreactor/Compiler/BytecodeReader.cpp @@ -555,6 +555,7 @@ namespace Ark { NEQ_CONST_JUMP_IF_TRUE, ArgKind::ConstRaw }, { NEQ_SYM_JUMP_IF_FALSE, ArgKind::SymRaw }, { CALL_SYMBOL, ArgKind::SymRaw }, + { CALL_SYMBOL_BY_INDEX, ArgKind::RawRaw }, { CALL_CURRENT_PAGE, ArgKind::SymRaw }, { GET_FIELD_FROM_SYMBOL, ArgKind::SymSym }, { GET_FIELD_FROM_SYMBOL_INDEX, ArgKind::RawSym }, diff --git a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp index 431b1366..03504f6e 100644 --- a/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp +++ b/src/arkreactor/Compiler/Lowerer/ASTLowerer.cpp @@ -20,6 +20,7 @@ namespace Ark::internal Classic, SelfNotRecursive, Symbol, + SymbolByIndex, Builtin }; @@ -872,6 +873,12 @@ namespace Ark::internal // we don't want to push any instruction, as we'll use an optimised instruction instead of CALL page(proc_page).clear(); } + else if (inst == LOAD_FAST_BY_INDEX) + { + call_type = CallType::SymbolByIndex; + call_arg = arg; + page(proc_page).clear(); + } else if (inst == BUILTIN && Builtins::builtins[arg].second.isFunction()) { call_type = CallType::Builtin; @@ -912,6 +919,11 @@ namespace Ark::internal page(p).emplace_back(CALL_SYMBOL, call_arg.value(), args_count); break; + case CallType::SymbolByIndex: + assert(call_arg.has_value() && "Expected a value for call_arg with CallType::SymbolByIndex"); + page(p).emplace_back(CALL_SYMBOL_BY_INDEX, call_arg.value(), args_count); + break; + case CallType::Builtin: assert(call_arg.has_value() && "Expected a value for call_arg with CallType::Builtin"); page(p).emplace_back(CALL_BUILTIN, call_arg.value(), args_count); diff --git a/src/arkreactor/VM/VM.cpp b/src/arkreactor/VM/VM.cpp index 008b05a1..27765f2d 100644 --- a/src/arkreactor/VM/VM.cpp +++ b/src/arkreactor/VM/VM.cpp @@ -550,6 +550,7 @@ namespace Ark &&TARGET_NEQ_CONST_JUMP_IF_TRUE, &&TARGET_NEQ_SYM_JUMP_IF_FALSE, &&TARGET_CALL_SYMBOL, + &&TARGET_CALL_SYMBOL_BY_INDEX, &&TARGET_CALL_CURRENT_PAGE, &&TARGET_GET_FIELD_FROM_SYMBOL, &&TARGET_GET_FIELD_FROM_SYMBOL_INDEX, @@ -1848,6 +1849,15 @@ namespace Ark DISPATCH(); } + TARGET(CALL_SYMBOL_BY_INDEX) + { + UNPACK_ARGS(); + call(context, secondary_arg, /* function_ptr= */ loadSymbolFromIndex(primary_arg, context)); + if (!m_running) + GOTO_HALT(); + DISPATCH(); + } + TARGET(CALL_CURRENT_PAGE) { UNPACK_ARGS(); @@ -2155,7 +2165,7 @@ namespace Ark return MaxValue16Bits; } - void VM::throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, ExecutionContext& context) + void VM::throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, ExecutionContext& context, const bool skip_function) { std::vector arg_names; arg_names.reserve(expected_arg_count + 1); @@ -2191,8 +2201,8 @@ namespace Ark if (context.sp >= 2 + passed_arg_count) { // -2/-3 instead of -1/-2 to skip over the function pushed on the stack - context.ip = context.stack[context.sp - 2 - passed_arg_count].pageAddr(); - context.pp = context.stack[context.sp - 3 - passed_arg_count].pageAddr(); + context.ip = context.stack[context.sp - 1 - (skip_function ? 1 : 0) - passed_arg_count].pageAddr(); + context.pp = context.stack[context.sp - 2 - (skip_function ? 1 : 0) - passed_arg_count].pageAddr(); context.sp -= 2; returnFromFuncCall(context); } @@ -2328,7 +2338,7 @@ namespace Ark std::size_t displayed_traces = 0; std::size_t consecutive_similar_traces = 0; - while (context.fc != 0 && context.pp != 0) + while (context.fc != 0 && context.pp != 0 && context.sp > 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) : ""; diff --git a/tests/unittests/Suites/BytecodeReaderSuite.cpp b/tests/unittests/Suites/BytecodeReaderSuite.cpp index c5ec1154..4b446fbf 100644 --- a/tests/unittests/Suites/BytecodeReaderSuite.cpp +++ b/tests/unittests/Suites/BytecodeReaderSuite.cpp @@ -121,7 +121,7 @@ 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); - expect(that % pages[0].size() == 10 * 4ull); + expect(that % pages[0].size() == 9 * 4ull); expect(that % pages[1].size() == 20 * 4ull); }; }; diff --git a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected index 50ef808a..3cc59f03 100644 --- a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected @@ -4,10 +4,9 @@ page_0 PUSH_RETURN_ADDRESS L5 LOAD_CONST 3 PUSH_RETURN_ADDRESS L6 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 LOAD_CONST 5 - CALL 2 + CALL_SYMBOL_BY_INDEX 0, 2 .L6: CALL_BUILTIN 9, 2 .L5: diff --git a/tests/unittests/resources/CompilerSuite/ir/args_attr.expected b/tests/unittests/resources/CompilerSuite/ir/args_attr.expected index c4d3ce84..94309078 100644 --- a/tests/unittests/resources/CompilerSuite/ir/args_attr.expected +++ b/tests/unittests/resources/CompilerSuite/ir/args_attr.expected @@ -8,10 +8,9 @@ page_0 STORE 3 PUSH_RETURN_ADDRESS L0 PUSH_RETURN_ADDRESS L1 - LOAD_FAST_BY_INDEX 1 LOAD_CONST 3 LOAD_FAST_BY_INDEX 0 - CALL 2 + CALL_SYMBOL_BY_INDEX 1, 2 .L1: CALL_BUILTIN 9, 1 .L0: diff --git a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected index 75e241f1..8999a7f5 100644 --- a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected +++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected @@ -4,11 +4,10 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L3 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 LOAD_CONST 5 LOAD_CONST 6 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L3: POP 0 BUILTIN 1 @@ -25,13 +24,12 @@ page_0 .L6: POP 0 PUSH_RETURN_ADDRESS L7 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 BUILTIN 0 BREAKPOINT 1 LOAD_CONST 5 LOAD_CONST 6 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L7: POP 0 PUSH_RETURN_ADDRESS L8 @@ -43,10 +41,9 @@ page_0 LOAD_CONST 9 STORE 5 PUSH_RETURN_ADDRESS L9 - LOAD_FAST_BY_INDEX 0 BUILTIN 1 BREAKPOINT 1 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L9: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/ir/closures.expected b/tests/unittests/resources/CompilerSuite/ir/closures.expected index 58794f36..1446ff72 100644 --- a/tests/unittests/resources/CompilerSuite/ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/ir/closures.expected @@ -2,19 +2,17 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L0 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 3 LOAD_CONST 4 LOAD_CONST 5 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L0: STORE 6 PUSH_RETURN_ADDRESS L1 - LOAD_FAST_BY_INDEX 1 LOAD_CONST 6 LOAD_CONST 7 LOAD_CONST 8 - CALL 3 + CALL_SYMBOL_BY_INDEX 1, 3 .L1: STORE 7 PUSH_RETURN_ADDRESS L2 @@ -53,16 +51,14 @@ page_0 LOAD_CONST 14 STORE 8 PUSH_RETURN_ADDRESS L7 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 17 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L7: STORE 10 PUSH_RETURN_ADDRESS L8 LOAD_CONST 18 PUSH_RETURN_ADDRESS L9 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L9: CALL_BUILTIN 9, 2 .L8: @@ -70,8 +66,7 @@ page_0 PUSH_RETURN_ADDRESS L10 LOAD_CONST 18 PUSH_RETURN_ADDRESS L11 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L11: CALL_BUILTIN 9, 2 .L10: @@ -79,8 +74,7 @@ page_0 PUSH_RETURN_ADDRESS L12 LOAD_CONST 18 PUSH_RETURN_ADDRESS L13 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L13: CALL_BUILTIN 9, 2 .L12: diff --git a/tests/unittests/resources/CompilerSuite/ir/factorial.expected b/tests/unittests/resources/CompilerSuite/ir/factorial.expected index 631af8e9..61151b24 100644 --- a/tests/unittests/resources/CompilerSuite/ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/ir/factorial.expected @@ -4,9 +4,8 @@ page_0 PUSH_RETURN_ADDRESS L2 LOAD_CONST 3 PUSH_RETURN_ADDRESS L3 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L3: CALL_BUILTIN 9, 2 .L2: diff --git a/tests/unittests/resources/CompilerSuite/ir/tail_call.expected b/tests/unittests/resources/CompilerSuite/ir/tail_call.expected index bd017d27..222f464b 100644 --- a/tests/unittests/resources/CompilerSuite/ir/tail_call.expected +++ b/tests/unittests/resources/CompilerSuite/ir/tail_call.expected @@ -2,10 +2,9 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L3 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 2 BUILTIN 2 - CALL 2 + CALL_SYMBOL_BY_INDEX 0, 2 .L3: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected index b8c57442..e1798fff 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected @@ -3,9 +3,8 @@ page_0 PUSH_RETURN_ADDRESS L5 LOAD_CONST 3 PUSH_RETURN_ADDRESS L6 - LOAD_FAST_BY_INDEX 0 LOAD_CONST_LOAD_CONST 4, 5 - CALL 2 + CALL_SYMBOL_BY_INDEX 0, 2 .L6: CALL_BUILTIN 9, 2 .L5: diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected index b802a198..dc510a52 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/call_builtin_not_optimized.expected @@ -1,16 +1,14 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L1 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 1 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L1: POP 0 LOAD_CONST_STORE 2, 3 PUSH_RETURN_ADDRESS L3 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 3 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L3: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected index 15daae10..41d9dbe6 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected @@ -1,17 +1,15 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L0 - LOAD_FAST_BY_INDEX 0 LOAD_CONST_LOAD_CONST 3, 4 LOAD_CONST 5 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L0: STORE 6 PUSH_RETURN_ADDRESS L1 - LOAD_FAST_BY_INDEX 1 LOAD_CONST_LOAD_CONST 6, 7 LOAD_CONST 8 - CALL 3 + CALL_SYMBOL_BY_INDEX 1, 3 .L1: STORE 7 PUSH_RETURN_ADDRESS L2 @@ -46,9 +44,8 @@ page_0 POP 0 LOAD_CONST_STORE 14, 8 PUSH_RETURN_ADDRESS L7 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 17 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L7: STORE 10 LOAD_CONST_STORE 18, 11 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected index 05f8bcba..c75344f7 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected @@ -3,9 +3,8 @@ page_0 PUSH_RETURN_ADDRESS L2 LOAD_CONST 3 PUSH_RETURN_ADDRESS L3 - LOAD_FAST_BY_INDEX 0 LOAD_CONST 4 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L3: CALL_BUILTIN 9, 2 .L2: diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected index 53dcdc19..c0211361 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/lists.expected @@ -6,8 +6,7 @@ page_0 LOAD_CONST_STORE 3, 2 LOAD_CONST_STORE 4, 3 PUSH_RETURN_ADDRESS L2 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L2: POP 0 STORE_HEAD_BY_INDEX 3, 7 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/mul.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/mul.expected index c29fe005..a8ba10dd 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/mul.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/mul.expected @@ -6,8 +6,7 @@ page_0 SET_VAL 0 LOAD_CONST_STORE 2, 1 PUSH_RETURN_ADDRESS L0 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L0: POP 0 HALT 0 diff --git a/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected b/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected index 90aafdf6..6094a62a 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/type.expected @@ -22,8 +22,7 @@ page_0 .L4: LOAD_CONST_STORE 3, 1 PUSH_RETURN_ADDRESS L12 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L12: POP 0 HALT 0 diff --git a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected index a19ba2fc..7638509e 100644 --- a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected +++ b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected @@ -28,10 +28,10 @@ In file tests/unittests/resources/DebuggerSuite/stack_and_locals.ark:3 5 | (f "correct" "wrong") dbg[pp:1,ip:4]:000> stack -4 -> Garbage (expected) -3 -> Instruction@28 +3 -> Instruction@24 2 -> Function@0 -1 -> Instruction@32 +1 -> Instruction@28 +0 -> Function@0 dbg[pp:1,ip:4]:000> locals 1 scope size: 3 @@ -70,10 +70,10 @@ In file tests/unittests/resources/DebuggerSuite/stack_and_locals.ark:3 5 | (f "correct" "wrong") dbg[pp:1,ip:4]:000> stack -4 -> Garbage (expected) -3 -> Instruction@28 +3 -> Instruction@24 2 -> Function@0 -1 -> Instruction@32 +1 -> Instruction@28 +0 -> Function@0 dbg[pp:1,ip:4]:000> locals scope size: 3 diff --git a/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected b/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected index 88cb5f65..2f78d0b8 100644 --- a/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected +++ b/tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.expected @@ -8,9 +8,9 @@ In file tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:3 4 | tmp 5 | })) -[2046] 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) ... [ 0] In global scope (tests/unittests/resources/DiagnosticsSuite/runtime/recursion_depth.ark:7) Current scope variables values: -a = 2044 +a = 2045 diff --git a/tools/ark_frequent_instructions.py b/tools/ark_frequent_instructions.py index 6ce88637..452ac05e 100755 --- a/tools/ark_frequent_instructions.py +++ b/tools/ark_frequent_instructions.py @@ -41,6 +41,7 @@ "NEQ_CONST_JUMP_IF_TRUE", "NEQ_SYM_JUMP_IF_FALSE", "CALL_SYMBOL", + "CALL_SYMBOL_BY_INDEX", "CALL_CURRENT_PAGE", "GET_FIELD_FROM_SYMBOL", "GET_FIELD_FROM_SYMBOL_INDEX",