54#ifndef BYTECODE_INTERPRETER_H
55#define BYTECODE_INTERPRETER_H
116 return "RuntimeError";
360 std::ostringstream
out;
361 out <<
"BytecodeFunction(f" <<
value.function_id;
362 if (module !=
nullptr and value.function_id < module->functions.size())
363 out <<
", " <<
module->functions.access(value.function_id)->name;
382 std::ostringstream
out;
383 if (module ==
nullptr)
385 out <<
"<null-bytecode-globals>\n";
389 out <<
"BytecodeGlobals\n";
396 for (
size_t i = 0; i <
module->global_slots.size(); ++i)
398 const auto &
slot =
module->global_slots.access(i);
399 out <<
" g" <<
slot.id <<
' ' <<
slot.name <<
" = ";
409 namespace Compiler_Bytecode_Interpreter_Detail
417 for (
size_t i = 0; i <
count; ++i)
425 for (
size_t i = 0; i < function.local_slots.size(); ++i)
464 error =
"unsupported raw constant payload '" +
constant.text +
"'";
468 error =
"unknown constant kind";
515 const size_t executed_instructions,
516 const Frame * frame =
nullptr,
522 if (frame !=
nullptr and frame->function !=
nullptr)
524 error.function_name = frame->function->name;
525 error.pc = frame->pc;
530 executed_instructions);
535 const size_t executed_instructions = 0)
const
538 ?
"invalid bytecode module"
539 :
report.errors.access(0);
557 const bool returns_to_caller,
560 const size_t executed_instructions,
568 "function '" + function.
name +
"' expects "
570 + std::to_string(arguments.
size()),
571 executed_instructions);
588 for (
size_t i = 0; i < function.
local_slots.size(); ++i)
596 "parameter slot id " + std::to_string(
slot.id)
597 +
" is out of bounds in function '" + function.
name +
"'",
598 executed_instructions);
613 const size_t executed_instructions,
620 "invalid register r" + std::to_string(
id),
621 executed_instructions,
628 if (
value.is_invalid())
631 "read from uninitialized register r" + std::to_string(
id),
632 executed_instructions,
646 const size_t executed_instructions,
652 "invalid register r" + std::to_string(
id),
653 executed_instructions,
667 const size_t executed_instructions,
671 if (
id >= frame.
locals.size())
674 "invalid local slot s" + std::to_string(
id),
675 executed_instructions,
682 if (
value.is_invalid())
685 "read from uninitialized local slot s" + std::to_string(
id),
686 executed_instructions,
700 const size_t executed_instructions,
703 if (
id >= frame.
locals.size())
706 "invalid local slot s" + std::to_string(
id),
707 executed_instructions,
721 const size_t executed_instructions,
728 "invalid global slot g" + std::to_string(
id),
729 executed_instructions,
736 if (
value.is_invalid())
739 "read from uninitialized global slot g" + std::to_string(
id),
740 executed_instructions,
754 const size_t executed_instructions,
760 "invalid global slot g" + std::to_string(
id),
761 executed_instructions,
775 const size_t executed_instructions,
776 const std::string & context,
784 context +
" expects Integer but received "
786 executed_instructions,
792 result =
value.runtime_value.integer_value;
800 const size_t executed_instructions,
801 const std::string & context,
809 context +
" expects Bool but received "
811 executed_instructions,
817 result =
value.runtime_value.bool_value;
824 const size_t executed_instructions,
829 if (
inst.operands.is_empty())
832 "call instruction has no callee operand",
833 executed_instructions,
840 inst.operands.access(0),
842 executed_instructions,
848 for (
size_t i = 1; i <
inst.operands.size(); ++i)
852 inst.operands.access(i),
854 executed_instructions,
867 const size_t executed_instructions,
885 executed_instructions,
898 const size_t executed_instructions,
902 for (
size_t i = 0; i < arguments.
size(); ++i)
904 if (
not arguments.
access(i).is_runtime_value())
907 "host function arguments cannot contain bytecode function references",
908 executed_instructions,
926 ?
"host function call failed"
928 executed_instructions,
940 executed_instructions,
951 size_t executed_instructions = 0;
957 "bytecode step limit exceeded",
958 executed_instructions,
962 if (frame.pc >= frame.function->code.size())
964 "program counter " + std::to_string(frame.pc)
965 +
" is out of bounds in function '" + frame.function->name +
"'",
966 executed_instructions,
969 const auto &
inst = frame.function->code.access(frame.pc);
970 ++executed_instructions;
976 if (
inst.constant_id >= frame.function->constants.size())
978 "invalid constant id k"
979 + std::to_string(
inst.constant_id),
980 executed_instructions,
987 frame.function->constants.access(
inst.constant_id),
992 executed_instructions,
1001 executed_instructions,
1016 executed_instructions,
1024 executed_instructions,
1034 if (
inst.operands.is_empty())
1036 "StoreLocal requires one source operand",
1037 executed_instructions,
1044 inst.operands.access(0),
1046 executed_instructions,
1054 executed_instructions,
1067 inst.global_slot_id,
1069 executed_instructions,
1077 executed_instructions,
1087 if (
inst.operands.is_empty())
1089 "StoreGlobal requires one source operand",
1090 executed_instructions,
1097 inst.operands.access(0),
1099 executed_instructions,
1104 inst.global_slot_id,
1107 executed_instructions,
1122 executed_instructions,
1132 if (
inst.operands.is_empty())
1134 "Unary requires one source operand",
1135 executed_instructions,
1142 inst.operands.access(0),
1144 executed_instructions,
1154 long long integer_value = 0;
1158 executed_instructions,
1169 bool bool_value =
false;
1173 executed_instructions,
1184 long long integer_value = 0;
1188 executed_instructions,
1199 "unsupported unary operator '"
1202 executed_instructions,
1211 executed_instructions,
1221 if (
inst.operands.size() < 2)
1223 "Binary requires two source operands",
1224 executed_instructions,
1232 inst.operands.access(0),
1234 executed_instructions,
1239 inst.operands.access(1),
1241 executed_instructions,
1263 executed_instructions,
1271 executed_instructions,
1292 executed_instructions,
1301 executed_instructions,
1331 executed_instructions,
1339 executed_instructions,
1375 executed_instructions,
1383 executed_instructions,
1407 "unsupported binary operator '"
1410 executed_instructions,
1419 executed_instructions,
1434 executed_instructions,
1444 "invalid function id f"
1446 executed_instructions,
1455 executed_instructions,
1463 "callee is not callable: "
1465 executed_instructions,
1475 executed_instructions,
1483 "HIR function values are not callable from bytecode VM",
1484 executed_instructions,
1489 "callee is not callable: "
1491 executed_instructions,
1497 frame.pc =
inst.target_pc;
1502 if (
inst.operands.is_empty())
1504 "Branch requires one condition operand",
1505 executed_instructions,
1512 inst.operands.access(0),
1514 executed_instructions,
1523 executed_instructions,
1537 if (
not inst.operands.is_empty()
1539 inst.operands.access(0),
1541 executed_instructions,
1549 executed_instructions,
1562 executed_instructions,
1571 "executed trap/unreachable instruction",
1572 executed_instructions,
1645 if (slot_id >=
globals.size())
1647 return &
globals.access(slot_id);
1667 if (slot_id >=
globals.size())
1682 return make_error(
"BCV001",
"null bytecode module", 0);
1685 return make_error(
"BCV002",
"bytecode module has no top-level body", 0);
1704 return make_error(
"BCV001",
"null bytecode module", 0);
1708 "invalid function id f" + std::to_string(function_id),
Reusable bytecode format and lowering from Compiler_IR_Model.H.
Reusable runtime and structured evaluator for HIR-based interpreters.
size_t size_t int32_t value
size_t size_t int32_t * out
Small portable VM for executing one Compiler_Bytecode_Module.
bool execute_host_call(Frame &frame, const Compiler_Bytecode_Instruction &inst, const Compiler_Bytecode_Value &callee, const DynArray< Compiler_Bytecode_Value > &arguments, const size_t executed_instructions, Compiler_Bytecode_Execution_Result &error)
bool require_bool(const Compiler_Bytecode_Value &value, const Frame &frame, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, const std::string &context, bool &result, Compiler_Bytecode_Execution_Result &error) const
std::string dump_globals() const
Returns a deterministic dump of the current global state.
Compiler_Bytecode_Execution_Result make_error(const std::string &code, const std::string &message, const size_t executed_instructions, const Frame *frame=nullptr, const Compiler_Bytecode_Instruction *inst=nullptr) const
bool require_integer(const Compiler_Bytecode_Value &value, const Frame &frame, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, const std::string &context, long long &result, Compiler_Bytecode_Execution_Result &error) const
bool read_global(const Frame &frame, const Compiler_IR_Global_Slot_Id id, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Value &value, Compiler_Bytecode_Execution_Result &error) const
bool read_callee_and_arguments(const Frame &frame, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Value &callee, DynArray< Compiler_Bytecode_Value > &arguments, Compiler_Bytecode_Execution_Result &error) const
DynArray< Frame > call_stack
bool write_local(Frame &frame, const Compiler_IR_Local_Slot_Id id, const Compiler_Bytecode_Value &value, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Execution_Result &error) const
bool read_local(const Frame &frame, const Compiler_IR_Local_Slot_Id id, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Value &value, Compiler_Bytecode_Execution_Result &error) const
const Compiler_Bytecode_Module & module() const noexcept
Returns the immutable bytecode module executed by this VM.
Compiler_Bytecode_Execution_Result execute_loop(const size_t max_steps)
bool module_is_valid(Compiler_Bytecode_Execution_Result &error) const
Compiler_Bytecode_Execution_Result call_function(const Compiler_Bytecode_Function_Id function_id, const DynArray< Compiler_Bytecode_Value > &arguments, const size_t max_steps=0)
Executes one bytecode function directly.
const Compiler_Bytecode_Module * bytecode_module
const DynArray< Compiler_Bytecode_Value > & global_values() const noexcept
Returns the current global slot values.
bool set_global(const Compiler_IR_Global_Slot_Id slot_id, const Compiler_Bytecode_Value &value)
Writes one global slot before or after execution.
size_t call_stack_depth() const noexcept
Returns the current dynamic call-stack depth.
void reset()
Resets globals to Invalid and clears any pending call stack.
Compiler_Bytecode_Execution_Result execute_entry(const Compiler_Bytecode_Function &function, const DynArray< Compiler_Bytecode_Value > &arguments, const size_t max_steps)
Compiler_Bytecode_Execution_Result run_top_level(const size_t max_steps=0)
Executes the module top-level body.
bool read_register(const Frame &frame, const Compiler_Bytecode_Register_Id id, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Value &value, Compiler_Bytecode_Execution_Result &error) const
bool write_global(Frame &frame, const Compiler_IR_Global_Slot_Id id, const Compiler_Bytecode_Value &value, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Execution_Result &error)
const Compiler_Bytecode_Value * global_slot(const Compiler_IR_Global_Slot_Id slot_id) const noexcept
Returns one global slot value if the slot id is valid.
Compiler_Bytecode_VM(const Compiler_Bytecode_Module &module)
Builds a VM over one immutable bytecode module.
const Frame & current_frame() const
Compiler_Bytecode_Execution_Result make_validation_error(const Compiler_Bytecode_Validation_Report &report, const size_t executed_instructions=0) const
bool write_register(Frame &frame, const Compiler_Bytecode_Register_Id id, const Compiler_Bytecode_Value &value, const Compiler_Bytecode_Instruction &inst, const size_t executed_instructions, Compiler_Bytecode_Execution_Result &error) const
DynArray< Compiler_Bytecode_Value > globals
bool push_frame(const Compiler_Bytecode_Function &function, const DynArray< Compiler_Bytecode_Value > &arguments, const bool returns_to_caller, const Compiler_Bytecode_PC caller_resume_pc, const Compiler_Bytecode_Register_Id caller_destination, const size_t executed_instructions, Compiler_Bytecode_Execution_Result &error)
bool complete_current_frame(const Compiler_Bytecode_Value &value, const Compiler_Bytecode_Completion_Kind kind, const size_t executed_instructions, Compiler_Bytecode_Execution_Result &completed, Compiler_Bytecode_Execution_Result &error)
void clear() noexcept
Empties the container.
size_t size() const noexcept
Return the current dimension of array.
T & access(const size_t i) const noexcept
Fast access without checking allocation and bound_min_clock checking.
T & append()
Allocate a new entry to the end of array.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
const char * token_name(const Compiler_Operator_Kind kind) noexcept
size_t count_parameters(const Compiler_Bytecode_Function &function) noexcept
bool constant_to_value(const Compiler_Bytecode_Constant &constant, Compiler_Bytecode_Value &value, std::string &error)
void reset_values(DynArray< Compiler_Bytecode_Value > &values, const size_t count, const Compiler_Bytecode_Value &fill=Compiler_Bytecode_Value::make_invalid())
Main namespace for Aleph-w library functions.
Compiler_Bytecode_Completion_Kind
Final status produced by one VM execution request.
@ Halted
Execution reached Halt and therefore produced Unit.
@ Runtime_Error
Execution failed with a structured runtime error.
@ Returned
Execution finished with an explicit Return.
std::string compiler_dump_bytecode_globals(const Compiler_Bytecode_Module *module, const DynArray< Compiler_Bytecode_Value > &globals)
Dumps the current global state associated with one bytecode module.
constexpr size_t compiler_bytecode_invalid_id() noexcept
Returns the sentinel invalid bytecode id.
@ Load_Local
dst <- locals[slot]
@ Halt
Canonical exit instruction.
@ Load_Function
dst <- function_ref[id]
@ Store_Global
globals[slot] <- src
@ Binary
dst <- op(lhs, rhs)
@ Load_Global
dst <- globals[slot]
@ Trap
Unreachable code path marker.
@ Return
Return one register value.
@ Load_Constant
dst <- constant_pool[id]
@ Call
dst <- callee(args...)
@ Store_Local
locals[slot] <- src
@ Branch
pc <- condition ? true_target : false_target
bool completed() const noexcept
Return true if all underlying iterators are finished.
@ String
String literal payload.
@ Character
Character literal payload.
@ Integer
Signed integer literal.
@ Raw_Text
Fallback textual payload when the literal cannot be classified better.
Compiler_Bytecode_Value_Kind
Runtime categories that can live inside bytecode registers/slots.
@ Invalid
Uninitialized register/slot or explicit invalid placeholder.
@ Value
Regular runtime payload stored as Interpreter_Value.
@ Function_Ref
Reference to one bytecode function in the current module.
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
bool interpreter_values_equal(const Interpreter_Value &lhs, const Interpreter_Value &rhs)
Returns whether two runtime values compare equal.
void fill(Itor beg, const Itor &end, const T &value)
Fill a range with a value.
and
Check uniqueness with explicit hash + equality functors.
bool compiler_bytecode_values_equal(const Compiler_Bytecode_Value &lhs, const Compiler_Bytecode_Value &rhs)
Returns whether two bytecode values compare equal.
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
size_t Compiler_Bytecode_Register_Id
Compiler_Bytecode_Validation_Report validate_bytecode_module(const Compiler_Bytecode_Module &module)
Validates all bytecode functions in one module.
void error(const char *file, int line, const char *format,...)
Print an error message with file and line info.
size_t Compiler_IR_Global_Slot_Id
const char * compiler_bytecode_value_kind_name(const Compiler_Bytecode_Value_Kind kind) noexcept
Stable debug name for one bytecode runtime value kind.
size_t Compiler_Bytecode_PC
size_t Compiler_Bytecode_Function_Id
bool(*)(void *user_data, const DynArray< Interpreter_Value > &arguments, Interpreter_Value &result, Interpreter_Runtime_Error &error) Interpreter_Host_Function
Host callback signature used by runtime built-ins or embeddings.
std::string interpreter_value_to_string(const Interpreter_Value &value)
Formats one runtime value deterministically for dumps and tests.
std::string compiler_bytecode_value_to_string(const Compiler_Bytecode_Value &value, const Compiler_Bytecode_Module *module=nullptr)
Renders one bytecode value deterministically.
size_t Compiler_IR_Local_Slot_Id
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
const char * compiler_bytecode_completion_kind_name(const Compiler_Bytecode_Completion_Kind kind) noexcept
Stable debug name for one execution completion kind.
One constant stored in a function-level constant pool.
Result of one bytecode execution request.
static Compiler_Bytecode_Execution_Result runtime_error(const Compiler_Bytecode_Runtime_Error &error, const size_t executed_instructions)
Builds a failed runtime result.
static Compiler_Bytecode_Execution_Result returned(const Compiler_Bytecode_Value &value, const size_t executed_instructions)
Builds a successful return result.
Compiler_Bytecode_Runtime_Error error
Structured failure when kind == Runtime_Error.
size_t executed_instructions
Number of bytecode instructions observed by the VM.
bool ok() const noexcept
Returns whether execution completed without runtime errors.
Compiler_Bytecode_Completion_Kind kind
Final completion category.
static Compiler_Bytecode_Execution_Result halted(const Compiler_Bytecode_Value &value, const size_t executed_instructions)
Builds a successful halt result.
Compiler_Bytecode_Value value
Produced return value or final Unit.
Bytecode for one lowered function or top-level body.
std::string name
Debug/source name.
Compiler_Bytecode_PC entry_pc
Entry PC used to start execution.
size_t register_count
Number of virtual registers required by the function.
DynArray< Compiler_IR_Slot > local_slots
Local/parameter slot metadata preserved for the VM.
One lowered bytecode instruction.
Bytecode module sharing global slot layout with the source IR module.
DynArray< Compiler_IR_Slot > global_slots
Global slots preserved from the source IR module.
Compiler_Bytecode_Function * top_level
Optional lowered top-level body.
DynArray< Compiler_Bytecode_Function * > functions
Lowered non-top-level functions.
Structured runtime error produced by the bytecode VM.
bool has_error() const noexcept
Returns whether this error object is populated.
std::string message
Human-readable runtime message.
Compiler_Bytecode_PC pc
Program counter active at the failure point, when known.
Source_Span span
Source span associated with the current instruction, when known.
std::string function_name
Function active at the failure point, when known.
std::string code
Stable runtime code such as BCV013.
DynArray< Compiler_Bytecode_Value > locals
const Compiler_Bytecode_Function * function
Compiler_Bytecode_Register_Id caller_destination
Compiler_Bytecode_PC caller_resume_pc
DynArray< Compiler_Bytecode_Value > registers
Structural validation report for one bytecode function or module.
One value stored in bytecode registers, locals, or globals.
static Compiler_Bytecode_Value make_value(const Interpreter_Value &value)
Wraps one regular runtime value.
static Compiler_Bytecode_Value make_character(const char ch)
Builds a character runtime value.
Compiler_Bytecode_Value_Kind kind
Active payload category.
Compiler_Bytecode_Function_Id function_id
Referenced bytecode function when kind == Function_Ref.
static Compiler_Bytecode_Value make_host_function(std::string name, const Interpreter_Host_Function callback, void *user_data=nullptr)
Builds a callable value backed by host code.
bool is_invalid() const noexcept
Returns whether this value is invalid.
bool is_function_ref() const noexcept
Returns whether this value is a bytecode function reference.
static Compiler_Bytecode_Value make_function_ref(const Compiler_Bytecode_Function_Id id)
Builds a reference to one bytecode function in the current module.
static Compiler_Bytecode_Value make_unit()
Builds the Unit singleton.
static Compiler_Bytecode_Value make_invalid()
Builds an invalid placeholder value.
Interpreter_Value runtime_value
Wrapped scalar/tuple/host-function value.
static Compiler_Bytecode_Value make_bool(const bool value)
Builds a boolean runtime value.
static Compiler_Bytecode_Value make_integer(const long long value)
Builds an integer runtime value.
static Compiler_Bytecode_Value make_value(Interpreter_Value &&value)
Wraps one regular runtime value by move.
bool is_runtime_value() const noexcept
Returns whether this value wraps an Interpreter_Value.
static Compiler_Bytecode_Value make_string(std::string text)
Builds a string runtime value.
Structured runtime error.
void * host_user_data
Embedding-specific payload for host functions.
Interpreter_Host_Function host_function
Payload for host function values.
static Interpreter_Value make_bool(const bool b)
Builds a boolean runtime value.
static Interpreter_Value make_character(const char ch)
Builds a character runtime value.
static Interpreter_Value make_invalid()
Builds an invalid value placeholder.
Interpreter_Value_Kind kind
Active value kind.
static Interpreter_Value make_host_function(std::string name, const Interpreter_Host_Function callback, void *user_data=nullptr)
Builds a callable value backed by host code.
static Interpreter_Value make_integer(const long long i)
Builds an integer runtime value.
static Interpreter_Value make_unit()
Builds the Unit value.
static Interpreter_Value make_string(std::string text)
Builds a string runtime value.
Half-open byte range inside a source file.