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/CHANGELOG.md b/CHANGELOG.md index 8a80fe82..dd45973f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # 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 +- 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 + +### 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 - `list:permutations` is deprecated in favor of `list:combinations` diff --git a/include/Ark/Compiler/Instructions.hpp b/include/Ark/Compiler/Instructions.hpp index 58329c33..b7dd9365 100644 --- a/include/Ark/Compiler/Instructions.hpp +++ b/include/Ark/Compiler/Instructions.hpp @@ -116,63 +116,63 @@ 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, - - // @args symbol id (function name) - // @role Push the current page address as a value on the stack - GET_CURRENT_PAGE_ADDR = 0x26, + POP_SCOPE = 0x26, // @role Pop a List from the stack and a function, and call the function with the given list as arguments APPLY = 0x27, @@ -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 }; @@ -481,6 +485,7 @@ namespace Ark::internal "DEL", "MAKE_CLOSURE", "GET_FIELD", + "GET_FIELD_AS_CLOSURE", "PLUGIN", "LIST", "APPEND", @@ -497,7 +502,6 @@ namespace Ark::internal "CREATE_SCOPE", "RESET_SCOPE_JUMP", "POP_SCOPE", - "GET_CURRENT_PAGE_ADDR", "APPLY", // operators "BREAKPOINT", @@ -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/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 a0204a0a..a17fb070 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); @@ -275,13 +275,16 @@ 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 * * @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 @@ -361,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); @@ -404,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 bf2d5651..ce95da89 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 @@ -175,11 +174,21 @@ inline Value* VM::pop(internal::ExecutionContext& context) return &m_undefined_value; } -inline Value* VM::peekAndResolveAsPtr(internal::ExecutionContext& context) +inline Value* VM::peek(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]; + return tmp; + } + return &m_undefined_value; +} + +inline Value* VM::peekAndResolveAsPtr(internal::ExecutionContext& context, const std::size_t offset) +{ + if (context.sp > offset) + { + Value* tmp = &context.stack[context.sp - 1 - offset]; if (tmp->valueType() == ValueType::Reference) return tmp->reference(); return tmp; @@ -233,15 +242,8 @@ 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) { - /* - 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]] @@ -252,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 = popAndResolveAsPtr(context); + 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; @@ -264,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); @@ -274,14 +281,16 @@ 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; } // 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()); @@ -320,6 +329,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; @@ -342,10 +355,10 @@ 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) +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; @@ -358,13 +371,13 @@ inline void VM::callBuiltin(internal::ExecutionContext& context, const Value& bu // because we pull `argc` from the CALL instruction generated by the compiler, // we are guaranteed to have `argc` values pushed on the stack ; thus we can // skip the `if (context.sp > 0)` check - Value* val = &context.stack[context.sp - 1 - j]; + Value* val = &context.stack[context.sp - argc + j]; if (val->valueType() == ValueType::Reference) val = val->reference(); args.emplace_back(*val); } // +2 to skip PP/IP that were pushed by PUSH_RETURN_ADDRESS - context.sp -= static_cast(argc + (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/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/BytecodeReader.cpp b/src/arkreactor/Compiler/BytecodeReader.cpp index ce1c294e..9f6fe733 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 }, @@ -518,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 }, @@ -554,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 }, @@ -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]); 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/IntermediateRepresentation/IROptimizer.cpp b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp index a94fc0ef..5ce10c14 100644 --- a/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp +++ b/src/arkreactor/Compiler/IntermediateRepresentation/IROptimizer.cpp @@ -30,32 +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 }, + Rule { { STORE, PUSH_RETURN_ADDRESS, LOAD_FAST_BY_INDEX, CALL_BUILTIN }, [](const Entities entities, const std::size_t start_idx) { - return Builtins::builtins[entities[3].primaryArg()].second.isFunction() && start_idx == 0 && entities[6].inst() == RET; + 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, BUILTIN, CALL }, + 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 Builtins::builtins[entities[5].primaryArg()].second.isFunction() && start_idx == 0 && entities[8].inst() == RET; + 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, BUILTIN, CALL }, + 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 Builtins::builtins[entities[7].primaryArg()].second.isFunction() && start_idx == 0 && entities[10].inst() == RET; + return start_idx == 0 && entities[9].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 c3703619..03504f6e 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,15 @@ namespace Ark::internal { using namespace literals; + enum class CallType + { + Classic, + SelfNotRecursive, + Symbol, + SymbolByIndex, + Builtin + }; + ASTLowerer::ASTLowerer(const unsigned debug) : Pass("ASTLowerer", debug) {} @@ -522,7 +532,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) { @@ -539,7 +549,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 CALL_CURRENT_PAGE, which would have returned the wrong page. if (x.isAnonymousFunction()) m_opened_vars.emplace("#anonymous"); // push body of the function @@ -660,23 +670,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()); @@ -831,7 +833,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 proc_page = createNewCodePage(/* temp= */ true); + 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 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())) @@ -841,27 +849,52 @@ 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 { // closure chains have been handled (eg: closure.field.field.function) compileExpression(node, proc_page, false, false); // storing proc - } - if (m_temp_pages.back().empty()) - buildAndThrowError(fmt::format("Can not call {}", x.constList()[0].repr()), x); + 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) + { + const Instruction inst = page(proc_page).back().inst(); + const uint16_t arg = page(proc_page).back().primaryArg(); - 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); + 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 == 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; + call_arg = arg; + page(proc_page).clear(); + } + } + } - 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); 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) @@ -869,8 +902,33 @@ 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: + 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::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); + 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/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 41879023..27765f2d 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,8 +82,7 @@ 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) + if (push_with_env) return Value(Closure(closure->refClosure().scopePtr(), field->pageAddr())); else return *field; @@ -474,6 +473,7 @@ namespace Ark &&TARGET_DEL, &&TARGET_MAKE_CLOSURE, &&TARGET_GET_FIELD, + &&TARGET_GET_FIELD_AS_CLOSURE, &&TARGET_PLUGIN, &&TARGET_LIST, &&TARGET_APPEND, @@ -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, @@ -551,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, @@ -666,29 +666,47 @@ namespace Ark TARGET(RET) { { - Value ip_or_val = *popAndResolveAsPtr(context); - // no return value on the stack - if (ip_or_val.valueType() == ValueType::InstPtr) [[unlikely]] + Value ts = *popAndResolveAsPtr(context); + Value ts1 = *popAndResolveAsPtr(context); + + if (ts1.valueType() == ValueType::InstPtr) { - context.ip = ip_or_val.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); - push(Builtins::nil, context); + if (ts.valueType() == ValueType::Garbage) + push(Builtins::nil, context); + else + 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(ip_or_val), 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(); @@ -785,6 +803,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); @@ -1089,13 +1114,6 @@ namespace Ark DISPATCH(); } - TARGET(GET_CURRENT_PAGE_ADDR) - { - context.last_symbol = arg; - push(Value(static_cast(context.pp)), context); - DISPATCH(); - } - TARGET(APPLY) { { @@ -1119,9 +1137,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())); } @@ -1701,7 +1719,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(); @@ -1711,7 +1734,14 @@ 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, + // 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) GOTO_HALT(); DISPATCH(); @@ -1813,7 +1843,16 @@ 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(); + } + + TARGET(CALL_SYMBOL_BY_INDEX) + { + UNPACK_ARGS(); + call(context, secondary_arg, /* function_ptr= */ loadSymbolFromIndex(primary_arg, context)); if (!m_running) GOTO_HALT(); DISPATCH(); @@ -2126,44 +2165,44 @@ 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); - 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 - 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); } @@ -2299,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/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/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,,,,, diff --git a/tests/unittests/Suites/BytecodeReaderSuite.cpp b/tests/unittests/Suites/BytecodeReaderSuite.cpp index 158cab7f..4b446fbf 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 @@ -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/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; } 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..3cc59f03 100644 --- a/tests/unittests/resources/CompilerSuite/ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/ir/ackermann.expected @@ -2,15 +2,13 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L5 - PUSH_RETURN_ADDRESS L6 LOAD_CONST 3 + PUSH_RETURN_ADDRESS L6 LOAD_CONST 4 - LOAD_FAST_BY_INDEX 0 - CALL 2 -.L6: LOAD_CONST 5 - BUILTIN 9 - CALL 2 + CALL_SYMBOL_BY_INDEX 0, 2 +.L6: + CALL_BUILTIN 9, 2 .L5: POP 0 HALT 0 @@ -18,37 +16,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..94309078 100644 --- a/tests/unittests/resources/CompilerSuite/ir/args_attr.expected +++ b/tests/unittests/resources/CompilerSuite/ir/args_attr.expected @@ -8,23 +8,21 @@ 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 - CALL 2 + LOAD_FAST_BY_INDEX 0 + CALL_SYMBOL_BY_INDEX 1, 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..8999a7f5 100644 --- a/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected +++ b/tests/unittests/resources/CompilerSuite/ir/breakpoints.expected @@ -7,38 +7,34 @@ page_0 LOAD_CONST 4 LOAD_CONST 5 LOAD_CONST 6 - LOAD_FAST_BY_INDEX 0 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 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_CONST 4 - LOAD_CONST 5 BUILTIN 0 BREAKPOINT 1 + LOAD_CONST 5 LOAD_CONST 6 - LOAD_FAST_BY_INDEX 0 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L7: POP 0 PUSH_RETURN_ADDRESS L8 LOAD_CONST 8 - BUILTIN 9 - CALL 1 + CALL_BUILTIN 9, 1 .L8: POP 0 .L5: @@ -47,8 +43,7 @@ page_0 PUSH_RETURN_ADDRESS L9 BUILTIN 1 BREAKPOINT 1 - LOAD_FAST_BY_INDEX 0 - CALL 0 + CALL_SYMBOL_BY_INDEX 0, 0 .L9: POP 0 HALT 0 @@ -57,43 +52,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..1446ff72 100644 --- a/tests/unittests/resources/CompilerSuite/ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/ir/closures.expected @@ -5,91 +5,78 @@ page_0 LOAD_CONST 3 LOAD_CONST 4 LOAD_CONST 5 - LOAD_FAST_BY_INDEX 0 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L0: STORE 6 PUSH_RETURN_ADDRESS L1 LOAD_CONST 6 LOAD_CONST 7 LOAD_CONST 8 - LOAD_FAST_BY_INDEX 1 - CALL 3 + CALL_SYMBOL_BY_INDEX 1, 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 - 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: - 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 + CALL_SYMBOL_BY_INDEX 0, 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 + CALL_SYMBOL_BY_INDEX 0, 0 .L13: - LOAD_CONST 18 - BUILTIN 9 - CALL 2 + CALL_BUILTIN 9, 2 .L12: POP 0 HALT 0 @@ -101,9 +88,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..61151b24 100644 --- a/tests/unittests/resources/CompilerSuite/ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/ir/factorial.expected @@ -2,14 +2,12 @@ page_0 LOAD_CONST 0 STORE 0 PUSH_RETURN_ADDRESS L2 - PUSH_RETURN_ADDRESS L3 LOAD_CONST 3 - LOAD_FAST_BY_INDEX 0 - CALL 1 -.L3: + PUSH_RETURN_ADDRESS L3 LOAD_CONST 4 - BUILTIN 9 - CALL 2 + CALL_SYMBOL_BY_INDEX 0, 1 +.L3: + 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..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 - BUILTIN 2 LOAD_CONST 2 - LOAD_FAST_BY_INDEX 0 - CALL 2 + BUILTIN 2 + CALL_SYMBOL_BY_INDEX 0, 2 .L3: POP 0 HALT 0 @@ -13,18 +12,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..e1798fff 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/ackermann.expected @@ -1,12 +1,11 @@ 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 - CALL 2 + LOAD_CONST_LOAD_CONST 4, 5 + CALL_SYMBOL_BY_INDEX 0, 2 .L6: - LOAD_CONST 5 CALL_BUILTIN 9, 2 .L5: POP 0 @@ -15,24 +14,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..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 @@ -2,15 +2,13 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L1 LOAD_CONST 1 - LOAD_FAST_BY_INDEX 0 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L1: POP 0 LOAD_CONST_STORE 2, 3 PUSH_RETURN_ADDRESS L3 LOAD_CONST 3 - LOAD_FAST_BY_INDEX 0 - 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 338714eb..41d9dbe6 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/closures.expected @@ -3,20 +3,18 @@ page_0 PUSH_RETURN_ADDRESS L0 LOAD_CONST_LOAD_CONST 3, 4 LOAD_CONST 5 - LOAD_FAST_BY_INDEX 0 - CALL 3 + CALL_SYMBOL_BY_INDEX 0, 3 .L0: STORE 6 PUSH_RETURN_ADDRESS L1 LOAD_CONST_LOAD_CONST 6, 7 LOAD_CONST 8 - LOAD_FAST_BY_INDEX 1 - CALL 3 + CALL_SYMBOL_BY_INDEX 1, 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,28 +24,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 - CALL 1 + CALL_SYMBOL_BY_INDEX 0, 1 .L7: STORE 10 LOAD_CONST_STORE 18, 11 @@ -59,9 +57,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 +91,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..c75344f7 100644 --- a/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected +++ b/tests/unittests/resources/CompilerSuite/optimized_ir/factorial.expected @@ -1,12 +1,11 @@ page_0 LOAD_CONST_STORE 0, 0 PUSH_RETURN_ADDRESS L2 - PUSH_RETURN_ADDRESS L3 LOAD_CONST 3 - LOAD_FAST_BY_INDEX 0 - CALL 1 -.L3: + PUSH_RETURN_ADDRESS L3 LOAD_CONST 4 + CALL_SYMBOL_BY_INDEX 0, 1 +.L3: CALL_BUILTIN 9, 2 .L2: POP 0 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 4dc361ae..7638509e 100644 --- a/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected +++ b/tests/unittests/resources/DebuggerSuite/stack_and_locals.expected @@ -28,9 +28,9 @@ In file tests/unittests/resources/DebuggerSuite/stack_and_locals.ark:3 5 | (f "correct" "wrong") dbg[pp:1,ip:4]:000> stack -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 @@ -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 -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 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/tools/ark_frequent_instructions.py b/tools/ark_frequent_instructions.py index ff15947b..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", @@ -52,9 +53,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 +74,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 +96,7 @@ ] # remove the page name (page_) ir.append(insts[1:]) - os.remove(f"{file}.ir") + os.remove(path) def window(iterable, size): @@ -136,12 +148,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])