83 inline constexpr size_t
86 return std::numeric_limits<size_t>::max();
157 return "StoreGlobal";
159 return "LoadFunction";
280 template <
typename T,
typename...
Args>
T *
285 <<
"Compiler_Bytecode_Context: arena allocation failed";
289 [](
void * raw)
noexcept
291 static_cast<T *
>(raw)->~
T();
300 for (
size_t i =
owned.size(); i > 0; --i)
302 auto &[ptr, destroy] =
owned.access(i - 1);
303 if (destroy !=
nullptr and ptr !=
nullptr)
319 namespace Compiler_Bytecode_Detail
324 return "r" + std::to_string(
id);
330 return "k" + std::to_string(
id);
336 return "B" + std::to_string(
id);
342 return "s" + std::to_string(
id);
348 return "g" + std::to_string(
id);
354 return "f" + std::to_string(
id);
365 const std::string & text,
368 const std::string
padding(indent,
' ');
370 while (begin < text.size())
372 const auto end = text.find(
'\n', begin);
373 if (end == std::string::npos)
380 out <<
padding << text.substr(begin, end - begin) <<
'\n';
389 long long &
value)
noexcept
394 char * end =
nullptr;
395 value = std::strtoll(text.c_str(), &end, 10);
396 return end !=
nullptr and *end ==
'\0';
403 result.reserve(raw.size());
404 for (
size_t i = 0; i < raw.size(); ++i)
406 if (raw[i] !=
'\\' or i + 1 >= raw.size())
414 case 'n': result +=
'\n';
break;
415 case 't': result +=
'\t';
break;
416 case 'r': result +=
'\r';
break;
417 case '\\': result +=
'\\';
break;
418 case '\'': result +=
'\'';
break;
419 case '"': result +=
'"';
break;
420 case '0': result +=
'\0';
break;
421 case 'a': result +=
'\a';
break;
422 case 'b': result +=
'\b';
break;
423 case 'f': result +=
'\f';
break;
424 case 'v': result +=
'\v';
break;
427 if (i + 2 < raw.size()
and std::isxdigit(
static_cast<unsigned char>(raw[i+1]))
428 and std::isxdigit(
static_cast<unsigned char>(raw[i+2])))
430 const char hex[3] = {raw[i+1], raw[i+2],
'\0'};
431 result +=
static_cast<char>(std::strtol(hex,
nullptr, 16));
458 case 'n':
return '\n';
459 case 't':
return '\t';
460 case 'r':
return '\r';
461 case '\\':
return '\\';
462 case '\'':
return '\'';
463 case '"':
return '"';
464 case '0':
return '\0';
465 case 'a':
return '\a';
466 case 'b':
return '\b';
467 case 'f':
return '\f';
468 case 'v':
return '\v';
469 default:
return raw[1];
481 if (
inst.text ==
"unit")
487 if (
inst.text ==
"true" or inst.text ==
"false")
494 long long integer_value = 0;
498 constant.integer_value = integer_value;
505 const auto inner =
inst.text.substr(1,
inst.text.size() - 2);
513 const auto inner =
inst.text.substr(1,
inst.text.size() - 2);
526 return lhs.kind == rhs.kind
527 and lhs.type_id == rhs.type_id
528 and lhs.text == rhs.text
529 and lhs.integer_value == rhs.integer_value
530 and lhs.bool_value == rhs.bool_value
531 and lhs.char_value == rhs.char_value;
538 for (
size_t i = 0; i < pool.
size(); ++i)
543 return pool.
size() - 1;
550 for (
size_t i = 0; i < blocks.size(); ++i)
551 if (blocks.access(i).begin_pc == pc)
564 return std::string(
"Boolean(") + (
constant.bool_value ?
"true" :
"false") +
")";
566 return "Integer(" + std::to_string(
constant.integer_value) +
")";
568 return "String(" +
constant.text +
")";
570 return "Character(" +
constant.text +
")";
572 return "RawText(" +
constant.text +
")";
586 const bool top_level)
const
589 function->
id = source.
id;
590 function->name = source.
name;
591 function->top_level = top_level;
592 function->span = source.
span;
593 function->type_id = source.
type_id;
594 function->source_function = &source;
599 for (
size_t i = 0; i < source.
blocks.size(); ++i)
602 for (
size_t block_id = 0; block_id < source.
blocks.size(); ++block_id)
604 const auto & block = source.
blocks.access(block_id);
607 info.source_block_id = block.
id;
609 info.begin_pc = function->code.size();
680 function->code.append(std::move(
lowered));
684 terminator.
pc = function->code.size();
725 function->code.append(std::move(terminator));
726 info.end_pc = function->code.size();
727 function->blocks.append(std::move(
info));
730 for (
size_t i = 0; i < function->code.size(); ++i)
732 auto &
inst = function->code.access(i);
768 <<
"Compiler_Bytecode_Lowering::lower_module(): null IR module";
772 for (
size_t i = 0; i <
module->functions.size(); ++i)
793 if (function.
code.is_empty())
795 fail(
"Bytecode function '" + function.
name +
"' has no instructions");
799 if (function.
blocks.is_empty())
800 fail(
"Bytecode function '" + function.
name +
"' has no block metadata");
802 fail(
"Bytecode function '" + function.
name +
"' has invalid entry pc");
804 fail(
"Bytecode function '" + function.
name +
"' has invalid exit pc");
809 for (
size_t block_id = 0; block_id < function.
blocks.size(); ++block_id)
811 const auto & block = function.
blocks.access(block_id);
812 if (block.
id != block_id)
813 fail(
"Bytecode function '" + function.
name +
"' has mismatched block id at "
815 if (block.begin_pc >= block.end_pc
or block.end_pc > function.
code.size())
817 +
" in function '" + function.
name +
"' has invalid pc range");
821 fail(
"Bytecode function '" + function.
name +
"' entry pc does not match any block start");
823 fail(
"Bytecode function '" + function.
name +
"' exit pc does not match any block start");
825 for (
size_t pc = 0; pc < function.
code.size(); ++pc)
827 const auto &
inst = function.
code.access(pc);
829 fail(
"Bytecode function '" + function.
name +
"' has mismatched pc at "
830 + std::to_string(pc));
833 const std::string & what)
835 if (
reg >= function.register_count)
836 fail(
"Bytecode instruction at pc "
837 + std::to_string(pc) +
" in function '" + function.name
838 +
"' references invalid register for " + what);
849 fail(
"Bytecode producer instruction at pc " + std::to_string(pc)
850 +
" in function '" + function.name +
"' must define a destination register");
852 if (
inst.destination != 0)
860 and inst.constant_id >= function.constants.size())
861 fail(
"Bytecode instruction at pc " + std::to_string(pc)
862 +
" in function '" + function.name +
"' references invalid constant id");
866 and inst.local_slot_id >= function.local_slots.size())
867 fail(
"Bytecode instruction at pc " + std::to_string(pc)
868 +
" in function '" + function.name +
"' references invalid local slot");
872 and (module ==
nullptr or inst.global_slot_id >= module->global_slots.size()))
873 fail(
"Bytecode instruction at pc " + std::to_string(pc)
874 +
" in function '" + function.name +
"' references invalid global slot");
877 and (module ==
nullptr or inst.function_id >= module->functions.size()))
878 fail(
"Bytecode instruction at pc " + std::to_string(pc)
879 +
" in function '" + function.name +
"' references invalid function id");
883 and (
inst.target_pc >= function.code.size()
885 fail(
"Bytecode control-flow instruction at pc " + std::to_string(pc)
886 +
" in function '" + function.name +
"' has invalid target pc");
889 and (
inst.false_target_pc >= function.code.size()
891 fail(
"Bytecode branch at pc " + std::to_string(pc)
892 +
" in function '" + function.name +
"' has invalid false target pc");
908 for (
size_t i = 0; i < child.errors.size(); ++i)
909 report.errors.append(child.errors.access(i));
910 for (
size_t i = 0; i < child.warnings.size(); ++i)
911 report.warnings.append(child.warnings.access(i));
914 for (
size_t i = 0; i <
module.functions.size(); ++i)
928 std::ostringstream
out;
929 if (function ==
nullptr)
931 out <<
"<null-bytecode-function>\n";
935 out <<
"BytecodeFunction(" << function->
name <<
")";
936 if (types !=
nullptr and function->
type_id != 0)
937 out <<
": " << types->to_string(function->
type_id);
940 out <<
" ExitPC: " << function->
exit_pc <<
'\n';
946 for (
size_t i = 0; i < function->
local_slots.size(); ++i)
952 if (types !=
nullptr and slot.type_id != 0)
953 out <<
": " << types->to_string(
slot.type_id);
959 out <<
" Constants: <none>\n";
962 out <<
" Constants:\n";
963 for (
size_t i = 0; i < function->
constants.size(); ++i)
968 if (types !=
nullptr and function->
constants.access(i).type_id != 0)
969 out <<
" : " << types->to_string(function->
constants.access(i).type_id);
977 for (
size_t i = 0; i < function->
blocks.size(); ++i)
979 const auto & block = function->
blocks.access(i);
982 <<
", " << block.
label <<
"] "
983 <<
"pc=" << block.begin_pc <<
".." << block.end_pc <<
'\n';
988 for (
size_t i = 0; i < function->
code.size(); ++i)
990 const auto &
inst = function->
code.access(i);
1008 if (
not inst.operands.is_empty())
1019 if (
not inst.operands.is_empty())
1031 if (
not inst.operands.is_empty())
1038 if (
inst.operands.size() > 0)
1040 if (
inst.operands.size() > 1)
1046 for (
size_t operand = 0; operand <
inst.operands.size(); ++operand)
1047 out << (operand == 0 ?
", " :
", ")
1056 if (
not inst.operands.is_empty())
1058 out <<
", " <<
inst.target_pc <<
", " <<
inst.false_target_pc;
1062 if (
not inst.operands.is_empty())
1071 if (types !=
nullptr and inst.type_id != 0)
1072 out <<
" : " << types->to_string(
inst.type_id);
1084 std::ostringstream
out;
1085 if (module ==
nullptr)
1087 out <<
"<null-bytecode-module>\n";
1091 out <<
"BytecodeModule\n";
1094 out <<
" Globals:\n";
1095 for (
size_t i = 0; i <
module->global_slots.size(); ++i)
1097 const auto &
slot =
module->global_slots.access(i);
1099 <<
" " <<
slot.name;
1100 if (types !=
nullptr and slot.type_id != 0)
1101 out <<
": " << types->to_string(
slot.type_id);
1106 for (
size_t i = 0; i <
module->functions.size(); ++i)
Reusable explicit-value IR model, validation, and deterministic dumps.
Memory arena for fast bulk allocations.
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
size_t size_t int32_t value
size_t size_t int32_t * out
Arena allocator for fast bump-pointer allocation.
void reset() noexcept
Reset arena, making all memory available again.
static constexpr size_t DEFAULT_SIZE
Default arena size (1 MB).
Arena-backed ownership context for bytecode functions and modules.
T * make(Args &&... args)
Allocates and constructs one bytecode object in the arena.
Compiler_Bytecode_Context & operator=(const Compiler_Bytecode_Context &)=delete
DynArray< Owned_Object > owned
void reset() noexcept
Resets the arena and destroys all tracked objects.
Compiler_Bytecode_Context(const size_t arena_size=AhArenaAllocator::DEFAULT_SIZE)
Constructs a bytecode context.
~Compiler_Bytecode_Context() noexcept
Destroys all tracked objects and rewinds the arena.
Compiler_Bytecode_Context(const Compiler_Bytecode_Context &)=delete
Lowers explicit IR into register-based bytecode.
Compiler_Bytecode_Function * lower_function(const Compiler_IR_Function &source, const bool top_level) const
Compiler_Bytecode_Context * ctx
Compiler_Bytecode_Lowering(Compiler_Bytecode_Context &context) noexcept
Builds a bytecode lowerer over context.
Compiler_Bytecode_Module * lower_module(const Compiler_IR_Module *module) const
Lowers one IR module to bytecode.
Context owning all compiler type nodes.
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.
bool is_empty() const noexcept
Return true if the array is empty.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
std::string block_name(const Compiler_Bytecode_Block_Id id)
bool parse_integer(const std::string &text, long long &value) noexcept
const char * token_name(const Compiler_Operator_Kind kind) noexcept
Compiler_Bytecode_Constant_Id intern_constant(DynArray< Compiler_Bytecode_Constant > &pool, const Compiler_Bytecode_Constant &constant)
Compiler_Bytecode_Constant classify_constant(const Compiler_IR_Instruction &inst)
char decode_escape_char(const std::string &raw)
std::string constant_to_string(const Compiler_Bytecode_Constant &constant)
void append_indented_text(std::ostream &out, const std::string &text, const size_t indent)
std::string constant_name(const Compiler_Bytecode_Constant_Id id)
std::string function_name(const Compiler_Bytecode_Function_Id id)
bool same_constant(const Compiler_Bytecode_Constant &lhs, const Compiler_Bytecode_Constant &rhs) noexcept
bool contains_pc(const DynArray< Compiler_Bytecode_Block_Info > &blocks, const Compiler_Bytecode_PC pc) noexcept
std::string local_slot_name(const Compiler_IR_Local_Slot_Id id)
std::string register_name(const Compiler_Bytecode_Register_Id id)
std::string global_slot_name(const Compiler_IR_Global_Slot_Id id)
std::string decode_escape_string(const std::string &raw)
std::string block_name(const Compiler_IR_Block_Id id)
Main namespace for Aleph-w library functions.
constexpr size_t compiler_bytecode_invalid_id() noexcept
Returns the sentinel invalid bytecode id.
Compiler_Bytecode_Opcode
VM opcodes supported by the bytecode MVP.
@ 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
Compiler_Bytecode_Constant_Kind
Constant kinds supported by the bytecode constant pool.
@ String
String literal payload.
@ Character
Character literal payload.
@ Integer
Signed integer literal.
@ Raw_Text
Fallback textual payload when the literal cannot be classified better.
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
const char * compiler_bytecode_opcode_name(const Compiler_Bytecode_Opcode opcode) noexcept
Stable debug name for one bytecode opcode.
size_t Compiler_Bytecode_Block_Id
const char * compiler_operator_name(const Compiler_Operator_Kind kind) noexcept
Returns a stable debug name for one operator kind.
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
std::string compiler_dump_bytecode_module(const Compiler_Bytecode_Module *module, const Compiler_Type_Context *types=nullptr)
Dumps all lowered bytecode in one module deterministically.
size_t Compiler_Bytecode_Register_Id
constexpr size_t compiler_ir_invalid_id() noexcept
Returns the sentinel invalid IR id.
Compiler_Bytecode_Validation_Report validate_bytecode_function(const Compiler_Bytecode_Function &function, const Compiler_Bytecode_Module *module=nullptr)
Validates one lowered bytecode function structurally.
size_t Compiler_Bytecode_Constant_Id
std::string compiler_dump_bytecode_function(const Compiler_Bytecode_Function *function, const Compiler_Bytecode_Module *module=nullptr, const Compiler_Type_Context *types=nullptr)
Dumps one lowered bytecode function deterministically.
size_t Compiler_IR_Block_Id
Compiler_Bytecode_Validation_Report validate_bytecode_module(const Compiler_Bytecode_Module &module)
Validates all bytecode functions in one module.
const char * compiler_ir_slot_kind_name(const Compiler_IR_Slot_Kind kind) noexcept
Stable debug name for one slot kind.
Itor3 merge(Itor1 source1Beg, Itor1 source1End, Itor2 source2Beg, Itor2 source2End, Itor3 destBeg)
Merge two sorted ranges.
size_t Compiler_IR_Global_Slot_Id
size_t Compiler_Bytecode_PC
const char * compiler_bytecode_constant_kind_name(const Compiler_Bytecode_Constant_Kind kind) noexcept
Stable debug name for one constant kind.
size_t Compiler_Bytecode_Function_Id
Compiler_Operator_Kind
Stable operator kinds shared by reusable compiler layers.
size_t Compiler_IR_Local_Slot_Id
Debug metadata that maps original IR basic blocks to bytecode PCs.
Compiler_Bytecode_PC end_pc
One-past-last PC for the block.
Compiler_IR_Block_Id source_block_id
Original IR block id.
Compiler_Bytecode_PC begin_pc
First PC emitted for the block.
std::string label
Original IR block label.
Compiler_Bytecode_Block_Id id
Dense bytecode block id.
One constant stored in a function-level constant pool.
bool bool_value
Boolean payload when kind == Boolean.
Compiler_Type_Id type_id
Original IR type, when known.
std::string text
Canonical textual payload for dumps/debugging.
char char_value
Character payload when kind == Character.
Compiler_Bytecode_Constant_Kind kind
Logical constant category.
long long integer_value
Integer payload when kind == Integer.
void(* destroy)(void *) noexcept
Bytecode for one lowered function or top-level body.
Compiler_Bytecode_PC exit_pc
PC of the canonical exit instruction.
DynArray< Compiler_Bytecode_Instruction > code
Linear bytecode stream.
Compiler_Type_Id type_id
Full function type, when known.
std::string name
Debug/source name.
Compiler_Bytecode_Function_Id id
Stable function id within the module.
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_Bytecode_Constant > constants
Per-function constant pool.
const Compiler_IR_Function * source_function
Original non-bytecode IR function.
DynArray< Compiler_Bytecode_Block_Info > blocks
Debug mapping from blocks to PCs.
bool is_top_level() const noexcept
Returns whether this bytecode function represents the top-level body.
DynArray< Compiler_IR_Slot > local_slots
Local/parameter slot metadata preserved for the VM.
bool top_level
Whether this function represents the top-level body.
Source_Span span
Source span of the function/body.
One lowered bytecode instruction.
Compiler_Bytecode_PC target_pc
Jump target or branch true target.
Compiler_Type_Id type_id
Result or stored type, when known.
Compiler_Bytecode_PC false_target_pc
Branch false target.
Compiler_IR_Global_Slot_Id global_slot_id
Referenced global slot, when relevant.
Compiler_IR_Local_Slot_Id local_slot_id
Referenced local slot, when relevant.
Compiler_Bytecode_Opcode opcode
Operation category.
Compiler_Bytecode_Register_Id destination
Destination register, or 0 for void instructions.
Compiler_Bytecode_Function_Id function_id
Referenced function id, when relevant.
DynArray< Compiler_Bytecode_Register_Id > operands
Source registers in deterministic order.
Compiler_Operator_Kind op
Unary/binary operator, when relevant.
Compiler_Bytecode_Constant_Id constant_id
Referenced constant pool entry, when relevant.
Source_Span span
Source region associated with the instruction.
Compiler_Bytecode_PC pc
Program counter within the function code array.
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.
Structural validation report for one bytecode function or module.
bool valid
Whether all hard validation checks passed.
DynArray< std::string > errors
Hard validation failures.
DynArray< std::string > warnings
Non-fatal findings.
Lowered IR for one function or top-level body.
Compiler_IR_Value_Id next_value_id
Next value id to allocate while lowering.
DynArray< Compiler_IR_Block > blocks
Basic blocks in deterministic id order.
std::string name
Debug or source-level name.
Compiler_Type_Id type_id
Full function type, when known.
Compiler_IR_Block_Id exit_block
Canonical exit block.
Compiler_IR_Block_Id entry_block
Canonical entry block.
DynArray< Compiler_IR_Slot > local_slots
Parameter/local slots in stable order.
Compiler_IR_Function_Id id
Stable function id within the module.
Source_Span span
Source span of the function/body.
One instruction producing an optional explicit result value.
Lowered IR module with shared global slots and functions.
DynArray< Compiler_IR_Function * > functions
Lowered non-top-level functions.
Compiler_IR_Function * top_level
Optional top-level body lowered as a function.
Compiler_SSA_Block_Id id
Dense SSA block id.
Compiler_IR_Block_Id source_block_id
Original IR block id before SSA normalization.
DynArray< Compiler_SSA_Instruction > instructions
Linear SSA instruction list.
std::string label
Deterministic debug label.
Compiler_SSA_Terminator terminator
Explicit block terminator.
Source_Span span
Source region associated with the terminator.
Compiler_SSA_Value_Id return_value
Return value, when relevant.
DynArray< Compiler_SSA_Block_Id > successors
Successors in deterministic order.
Compiler_IR_Terminator_Kind kind
Terminator category.
Compiler_SSA_Value_Id condition_value
Branch condition, when relevant.
Half-open byte range inside a source file.
Lazy and scalable dynamic array implementation.