526#if ARK_USE_COMPUTED_GOTOS
527# define TARGET(op) TARGET_##op:
528# define DISPATCH_GOTO() \
529 _Pragma("GCC diagnostic push") \
530 _Pragma("GCC diagnostic ignored \"-Wpedantic\"") goto* opcode_targets[inst];
531 _Pragma(
"GCC diagnostic pop")
532# define GOTO_HALT() goto dispatch_end
534# define TARGET(op) case op:
535# define DISPATCH_GOTO() goto dispatch_opcode
536# define GOTO_HALT() break
542 inst = m_state.inst(context.pp, context.ip); \
543 padding = m_state.inst(context.pp, context.ip + 1); \
544 arg = static_cast<uint16_t>((m_state.inst(context.pp, context.ip + 2) << 8) + \
545 m_state.inst(context.pp, context.ip + 3)); \
547 context.inst_exec_counter = (context.inst_exec_counter + 1) % VMOverflowBufferSize; \
548 if (context.inst_exec_counter < 2 && context.sp >= VMStackSize) \
550 if (context.pp != 0) \
551 throw Error("Stack overflow. You could consider rewriting your function to make use of tail-call optimization."); \
553 throw Error("Stack overflow. Are you trying to call a function with too many arguments?"); \
559#define UNPACK_ARGS() \
562 secondary_arg = static_cast<uint16_t>((padding << 4) | (arg & 0xf000) >> 12); \
563 primary_arg = arg & 0x0fff; \
566#if ARK_USE_COMPUTED_GOTOS
567# pragma GCC diagnostic push
568# pragma GCC diagnostic ignored "-Wpedantic"
569 constexpr std::array opcode_targets = {
572 &&TARGET_LOAD_SYMBOL,
573 &&TARGET_LOAD_SYMBOL_BY_INDEX,
575 &&TARGET_POP_JUMP_IF_TRUE,
579 &&TARGET_POP_JUMP_IF_FALSE,
583 &&TARGET_PUSH_RETURN_ADDRESS,
586 &&TARGET_RENAME_NEXT_CAPTURE,
589 &&TARGET_MAKE_CLOSURE,
595 &&TARGET_APPEND_IN_PLACE,
596 &&TARGET_CONCAT_IN_PLACE,
598 &&TARGET_POP_LIST_IN_PLACE,
599 &&TARGET_SET_AT_INDEX,
600 &&TARGET_SET_AT_2_INDEX,
602 &&TARGET_SHORTCIRCUIT_AND,
603 &&TARGET_SHORTCIRCUIT_OR,
604 &&TARGET_CREATE_SCOPE,
605 &&TARGET_RESET_SCOPE_JUMP,
607 &&TARGET_GET_CURRENT_PAGE_ADDR,
632 &&TARGET_LOAD_CONST_LOAD_CONST,
633 &&TARGET_LOAD_CONST_STORE,
634 &&TARGET_LOAD_CONST_SET_VAL,
636 &&TARGET_STORE_FROM_INDEX,
637 &&TARGET_SET_VAL_FROM,
638 &&TARGET_SET_VAL_FROM_INDEX,
640 &&TARGET_INCREMENT_BY_INDEX,
641 &&TARGET_INCREMENT_STORE,
643 &&TARGET_DECREMENT_BY_INDEX,
644 &&TARGET_DECREMENT_STORE,
646 &&TARGET_STORE_TAIL_BY_INDEX,
648 &&TARGET_STORE_HEAD_BY_INDEX,
650 &&TARGET_SET_VAL_TAIL,
651 &&TARGET_SET_VAL_TAIL_BY_INDEX,
652 &&TARGET_SET_VAL_HEAD,
653 &&TARGET_SET_VAL_HEAD_BY_INDEX,
654 &&TARGET_CALL_BUILTIN,
655 &&TARGET_CALL_BUILTIN_WITHOUT_RETURN_ADDRESS,
656 &&TARGET_LT_CONST_JUMP_IF_FALSE,
657 &&TARGET_LT_CONST_JUMP_IF_TRUE,
658 &&TARGET_LT_SYM_JUMP_IF_FALSE,
659 &&TARGET_GT_CONST_JUMP_IF_TRUE,
660 &&TARGET_GT_CONST_JUMP_IF_FALSE,
661 &&TARGET_GT_SYM_JUMP_IF_FALSE,
662 &&TARGET_EQ_CONST_JUMP_IF_TRUE,
663 &&TARGET_EQ_SYM_INDEX_JUMP_IF_TRUE,
664 &&TARGET_NEQ_CONST_JUMP_IF_TRUE,
665 &&TARGET_NEQ_SYM_JUMP_IF_FALSE,
666 &&TARGET_CALL_SYMBOL,
667 &&TARGET_CALL_CURRENT_PAGE,
668 &&TARGET_GET_FIELD_FROM_SYMBOL,
669 &&TARGET_GET_FIELD_FROM_SYMBOL_INDEX,
671 &&TARGET_AT_SYM_INDEX_SYM_INDEX,
672 &&TARGET_AT_SYM_INDEX_CONST,
673 &&TARGET_CHECK_TYPE_OF,
674 &&TARGET_CHECK_TYPE_OF_BY_INDEX,
675 &&TARGET_APPEND_IN_PLACE_SYM,
676 &&TARGET_APPEND_IN_PLACE_SYM_INDEX,
678 &&TARGET_LT_LEN_SYM_JUMP_IF_FALSE,
680 &&TARGET_MUL_BY_INDEX,
681 &&TARGET_MUL_SET_VAL,
685 static_assert(opcode_targets.size() ==
static_cast<std::size_t
>(Instruction::InstructionsCount) &&
"Some instructions are not implemented in the VM");
686# pragma GCC diagnostic pop
694 uint16_t primary_arg = 0;
695 uint16_t secondary_arg = 0;
702#if !ARK_USE_COMPUTED_GOTOS
707#pragma region "Instructions"
749 store(arg, tmp, context);
796 push(std::move(ip_or_val), context);
799 if (context.
fc <= untilFrameCount)
865 var->usertypeRef().del();
897 push(std::move(l), context);
908 std::vector<Value> args = { *list };
909 for (uint16_t i = 0; i < arg; ++i)
917 const auto size =
static_cast<uint16_t
>(list->
constList().size());
920 obj.
list().reserve(size + arg);
922 for (uint16_t i = 0; i < arg; ++i)
924 push(std::move(obj), context);
935 for (uint16_t i = 0; i < arg; ++i)
945 std::ranges::copy(next->
list(), std::back_inserter(obj.list()));
947 push(std::move(obj), context);
963 for (uint16_t i = 0; i < arg; ++i)
973 std::ranges::copy(next->
list(), std::back_inserter(list->
list()));
990 long idx =
static_cast<long>(number.
number());
991 idx = idx < 0 ? static_cast<long>(list.
list().size()) + idx : idx;
992 if (std::cmp_greater_equal(idx, list.
list().size()) || idx < 0)
995 fmt::format(
"pop index ({}) out of range (list size: {})", idx, list.
list().size()));
997 list.
list().erase(list.
list().begin() + idx);
1015 long idx =
static_cast<long>(number.
number());
1016 idx = idx < 0 ? static_cast<long>(list->
list().size()) + idx : idx;
1017 if (std::cmp_greater_equal(idx, list->
list().size()) || idx < 0)
1020 fmt::format(
"pop! index ({}) out of range (list size: {})", idx, list->
list().size()));
1022 list->
list().erase(list->
list().begin() + idx);
1045 { *list, number, new_value });
1048 long idx =
static_cast<long>(number.
number());
1049 idx = idx < 0 ? static_cast<long>(size) + idx : idx;
1050 if (std::cmp_greater_equal(idx, size) || idx < 0)
1053 fmt::format(
"@= index ({}) out of range (indexable size: {})", idx, size));
1056 list->
list()[
static_cast<std::size_t
>(idx)] = new_value;
1058 list->
stringRef()[
static_cast<std::size_t
>(idx)] = new_value.
string()[0];
1079 { *list, x, y, new_value });
1081 long idx_y =
static_cast<long>(x.
number());
1082 idx_y = idx_y < 0 ? static_cast<long>(list->
list().size()) + idx_y : idx_y;
1083 if (std::cmp_greater_equal(idx_y, list->
list().size()) || idx_y < 0)
1086 fmt::format(
"@@= index (y: {}) out of range (list size: {})", idx_y, list->
list().size()));
1088 if (!list->
list()[
static_cast<std::size_t
>(idx_y)].isIndexable() ||
1102 { *list, x, y, new_value });
1104 const bool is_list = list->
list()[
static_cast<std::size_t
>(idx_y)].valueType() ==
ValueType::List;
1105 const std::size_t size =
1107 ? list->
list()[
static_cast<std::size_t
>(idx_y)].list().size()
1108 : list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef().size();
1110 long idx_x =
static_cast<long>(y.
number());
1111 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
1112 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
1115 fmt::format(
"@@= index (x: {}) out of range (inner indexable size: {})", idx_x, size));
1118 list->
list()[
static_cast<std::size_t
>(idx_y)].list()[
static_cast<std::size_t
>(idx_x)] = new_value;
1120 list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef()[
static_cast<std::size_t
>(idx_x)] = new_value.
string()[0];
1157 context.
locals.back().reset();
1164 context.
locals.pop_back();
1177#pragma region "Operators"
1233 throwVMError(ErrorKind::DivisionByZero, fmt::format(
"Can not compute expression (/ {} {})", a->toString(*
this), b->
toString(*
this)));
1405 long idx_y =
static_cast<long>(y->
number());
1406 idx_y = idx_y < 0 ? static_cast<long>(list.
list().size()) + idx_y : idx_y;
1407 if (std::cmp_greater_equal(idx_y, list.
list().size()) || idx_y < 0)
1410 fmt::format(
"@@ index ({}) out of range (list size: {})", idx_y, list.
list().size()));
1412 const bool is_list = list.
list()[
static_cast<std::size_t
>(idx_y)].valueType() ==
ValueType::List;
1413 const std::size_t size =
1415 ? list.
list()[
static_cast<std::size_t
>(idx_y)].list().size()
1416 : list.
list()[
static_cast<std::size_t
>(idx_y)].stringRef().size();
1418 long idx_x =
static_cast<long>(x->
number());
1419 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
1420 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
1423 fmt::format(
"@@ index (x: {}) out of range (inner indexable size: {})", idx_x, size));
1426 push(list.
list()[
static_cast<std::size_t
>(idx_y)].list()[
static_cast<std::size_t
>(idx_x)], context);
1428 push(
Value(std::string(1, list.
list()[
static_cast<std::size_t
>(idx_y)].stringRef()[
static_cast<std::size_t
>(idx_x)])), context);
1461 { *closure, *field });
1470 auto id =
static_cast<std::uint16_t
>(std::distance(
m_state.
m_symbols.begin(), it));
1485#pragma region "Super Instructions"
1553 { *var,
Value(secondary_arg) });
1574 { *var,
Value(secondary_arg) });
1592 setVal(primary_arg, &val, context);
1598 { *var,
Value(secondary_arg) });
1619 { *var,
Value(secondary_arg) });
1640 { *var,
Value(secondary_arg) });
1658 setVal(primary_arg, &val, context);
1664 { *var,
Value(secondary_arg) });
1675 store(secondary_arg, &tail, context);
1686 store(secondary_arg, &tail, context);
1697 store(secondary_arg, &head, context);
1708 store(secondary_arg, &head, context);
1718 store(secondary_arg, &l, context);
1729 setVal(secondary_arg, &tail, context);
1740 setVal(secondary_arg, &tail, context);
1751 setVal(secondary_arg, &head, context);
1762 setVal(secondary_arg, &head, context);
1792 jump(secondary_arg, context);
1801 jump(secondary_arg, context);
1809 if (!(*sym < *
loadSymbol(primary_arg, context)))
1810 jump(secondary_arg, context);
1820 jump(secondary_arg, context);
1830 jump(secondary_arg, context);
1840 jump(secondary_arg, context);
1849 jump(secondary_arg, context);
1858 jump(secondary_arg, context);
1867 jump(secondary_arg, context);
1875 if (*sym == *
loadSymbol(primary_arg, context))
1876 jump(secondary_arg, context);
1893 call(context, secondary_arg,
nullptr,
static_cast<PageAddr_t>(context.
pp));
1985 len =
Value(
static_cast<int>(a->
string().size()));
1992 store(secondary_arg, &len, context);
2007 size =
Value(
static_cast<int>(sym->
string().size()));
2016 jump(secondary_arg, context);
2026 const int other =
static_cast<int>(secondary_arg) - 2048;
2038 { *var,
Value(other) });
2048 const int other =
static_cast<int>(secondary_arg) - 2048;
2060 { *var,
Value(other) });
2070 const int other =
static_cast<int>(secondary_arg) - 2048;
2079 setVal(primary_arg, &val, context);
2085 { *var,
Value(other) });
2092 const auto op1 =
static_cast<Instruction>(padding),
2093 op2 =
static_cast<Instruction>((arg & 0xff00) >> 8),
2095 const std::size_t arg_count = (op1 !=
NOP) + (op2 !=
NOP) + (op3 !=
NOP);
2112 { *b,
Value(temp) });
2117 else if (arg_count == 3)
2124 { *a,
Value(temp) });
2132 "FUSED_MATH got {} arguments, expected 2 or 3. Arguments: {:x}{:x}{:x}. There is a bug in the codegen!",
2133 arg_count,
static_cast<uint8_t
>(op1),
static_cast<uint8_t
>(op2),
static_cast<uint8_t
>(op3)));
2138#if ARK_USE_COMPUTED_GOTOS
2146 catch (
const Error& e)
2148 if (fail_with_exception)
2150 std::stringstream stream;
2159 catch (
const std::exception& e)
2161 if (fail_with_exception)
2163 std::stringstream stream;
2172 if (fail_with_exception)
2175#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2178 fmt::println(
"Unknown error");
2306 const std::size_t saved_ip = context.
ip;
2307 const std::size_t saved_pp = context.
pp;
2308 const uint16_t saved_sp = context.
sp;
2309 constexpr std::size_t max_consecutive_traces = 7;
2319 .filename = filename,
2320 .start =
FilePos { .line = maybe_location->line, .column = 0 },
2321 .end = std::nullopt },
2325 fmt::println(os,
"");
2333 std::string previous_trace;
2334 std::size_t displayed_traces = 0;
2335 std::size_t consecutive_similar_traces = 0;
2337 while (context.
fc != 0 && context.
pp != 0)
2340 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2347 if (func_name + loc_as_text != previous_trace)
2351 "[{:4}] In function `{}'{}",
2352 fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2353 fmt::styled(func_name, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()),
2355 previous_trace = func_name + loc_as_text;
2357 consecutive_similar_traces = 0;
2359 else if (consecutive_similar_traces == 0)
2361 fmt::println(os,
" ...");
2362 ++consecutive_similar_traces;
2375 if (displayed_traces > max_consecutive_traces)
2377 fmt::println(os,
" ...");
2382 if (context.
pp == 0)
2385 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2386 fmt::println(os,
"[{:4}] In global scope{}", fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), loc_as_text);
2390 fmt::println(os,
"\nCurrent scope variables values:");
2391 for (std::size_t i = 0, size = old_scope.
size(); i < size; ++i)
2396 fmt::styled(
m_state.
m_symbols[old_scope.
atPos(i).first], colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2397 old_scope.
atPos(i).second.toString(*
this));
2403 "At IP: {}, PP: {}, SP: {}",
2405 fmt::styled(saved_ip / 4, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2406 fmt::styled(saved_pp, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()),
2407 fmt::styled(saved_sp, colorize ? fmt::fg(fmt::color::yellow) : fmt::text_style()));