Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Bytecode.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
59#ifndef BYTECODE_H
60#define BYTECODE_H
61
62#include <cctype>
63#include <cstdlib>
64#include <limits>
65#include <sstream>
66#include <string>
67#include <utility>
68
69#include <Compiler_IR_Model.H>
70#include <ah-arena.H>
71#include <ah-errors.H>
72#include <tpl_dynArray.H>
73
74namespace Aleph
75{
80 using Compiler_Bytecode_PC = size_t;
81
83 inline constexpr size_t
85 {
86 return std::numeric_limits<size_t>::max();
87 }
88
91 {
92 Unit,
93 Boolean,
94 Integer,
95 String,
96 Character,
98 };
99
102 {
104 Load_Local,
109 Unary,
110 Binary,
111 Call,
112 Jump,
113 Branch,
114 Return,
115 Halt,
116 Trap
117 };
118
120 inline const char *
122 {
123 switch (kind)
124 {
126 return "Unit";
128 return "Boolean";
130 return "Integer";
132 return "String";
134 return "Character";
136 return "RawText";
137 }
138
139 return "Unknown";
140 }
141
143 inline const char *
145 {
146 switch (opcode)
147 {
149 return "LoadConst";
151 return "LoadLocal";
153 return "StoreLocal";
155 return "LoadGlobal";
157 return "StoreGlobal";
159 return "LoadFunction";
161 return "Unary";
163 return "Binary";
165 return "Call";
167 return "Jump";
169 return "Branch";
171 return "Return";
173 return "Halt";
175 return "Trap";
176 }
177
178 return "Unknown";
179 }
180
191
209
219
243
251
254 {
256 {
257 void * ptr = nullptr;
258 void (*destroy)(void *) noexcept = nullptr;
259 };
260
263
264 public:
267 : arena(arena_size)
268 {}
269
272
278
280 template <typename T, typename... Args> T *
282 {
283 T * ptr = allocate<T>(arena, std::forward<Args>(args)...);
284 ah_runtime_error_unless(ptr != nullptr)
285 << "Compiler_Bytecode_Context: arena allocation failed";
286
287 owned.append({
288 ptr,
289 [](void * raw) noexcept
290 {
291 static_cast<T *>(raw)->~T();
292 }
293 });
294 return ptr;
295 }
296
299 {
300 for (size_t i = owned.size(); i > 0; --i)
301 {
302 auto &[ptr, destroy] = owned.access(i - 1);
303 if (destroy != nullptr and ptr != nullptr)
304 destroy(ptr);
305 }
306 owned.clear();
307 arena.reset();
308 }
309 };
310
318
319 namespace Compiler_Bytecode_Detail
320 {
321 inline std::string
323 {
324 return "r" + std::to_string(id);
325 }
326
327 inline std::string
329 {
330 return "k" + std::to_string(id);
331 }
332
333 inline std::string
335 {
336 return "B" + std::to_string(id);
337 }
338
339 inline std::string
341 {
342 return "s" + std::to_string(id);
343 }
344
345 inline std::string
347 {
348 return "g" + std::to_string(id);
349 }
350
351 inline std::string
353 {
354 return "f" + std::to_string(id);
355 }
356
357 inline const char *
359 {
360 return compiler_operator_name(kind);
361 }
362
363 inline void
364 append_indented_text(std::ostream & out,
365 const std::string & text,
366 const size_t indent)
367 {
368 const std::string padding(indent, ' ');
369 size_t begin = 0;
370 while (begin < text.size())
371 {
372 const auto end = text.find('\n', begin);
373 if (end == std::string::npos)
374 {
375 out << padding << text.substr(begin) << '\n';
376 return;
377 }
378
379 if (end > begin)
380 out << padding << text.substr(begin, end - begin) << '\n';
381 else
382 out << padding << '\n';
383 begin = end + 1;
384 }
385 }
386
387 inline bool
388 parse_integer(const std::string & text,
389 long long & value) noexcept
390 {
391 if (text.empty())
392 return false;
393
394 char * end = nullptr;
395 value = std::strtoll(text.c_str(), &end, 10);
396 return end != nullptr and *end == '\0';
397 }
398
399 inline std::string
400 decode_escape_string(const std::string & raw)
401 {
402 std::string result;
403 result.reserve(raw.size());
404 for (size_t i = 0; i < raw.size(); ++i)
405 {
406 if (raw[i] != '\\' or i + 1 >= raw.size())
407 {
408 result += raw[i];
409 continue;
410 }
411 ++i;
412 switch (raw[i])
413 {
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;
425 case 'x':
426 {
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])))
429 {
430 const char hex[3] = {raw[i+1], raw[i+2], '\0'};
431 result += static_cast<char>(std::strtol(hex, nullptr, 16));
432 i += 2;
433 }
434 else
435 result += raw[i];
436 break;
437 }
438 default:
439 result += '\\';
440 result += raw[i];
441 break;
442 }
443 }
444 return result;
445 }
446
447 inline char
448 decode_escape_char(const std::string & raw)
449 {
450 if (raw.empty())
451 return '\0';
452 if (raw[0] != '\\')
453 return raw[0];
454 if (raw.size() < 2)
455 return '\\';
456 switch (raw[1])
457 {
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];
470 }
471 }
472
475 {
477 constant.type_id = inst.type_id;
478 constant.text = inst.text;
479 constant.bool_value = inst.bool_value;
480
481 if (inst.text == "unit")
482 {
484 return constant;
485 }
486
487 if (inst.text == "true" or inst.text == "false")
488 {
490 constant.bool_value = inst.text == "true";
491 return constant;
492 }
493
494 long long integer_value = 0;
495 if (parse_integer(inst.text, integer_value))
496 {
498 constant.integer_value = integer_value;
499 return constant;
500 }
501
502 if (inst.text.size() >= 2 and inst.text.front() == '"' and inst.text.back() == '"')
503 {
505 const auto inner = inst.text.substr(1, inst.text.size() - 2);
506 constant.text = decode_escape_string(inner);
507 return constant;
508 }
509
510 if (inst.text.size() >= 3 and inst.text.front() == '\'' and inst.text.back() == '\'')
511 {
513 const auto inner = inst.text.substr(1, inst.text.size() - 2);
514 constant.char_value = decode_escape_char(inner);
515 return constant;
516 }
517
519 return constant;
520 }
521
522 inline bool
524 const Compiler_Bytecode_Constant & rhs) noexcept
525 {
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;
532 }
533
537 {
538 for (size_t i = 0; i < pool.size(); ++i)
539 if (same_constant(pool.access(i), constant))
540 return i;
541
542 pool.append(constant);
543 return pool.size() - 1;
544 }
545
546 inline bool
548 const Compiler_Bytecode_PC pc) noexcept
549 {
550 for (size_t i = 0; i < blocks.size(); ++i)
551 if (blocks.access(i).begin_pc == pc)
552 return true;
553 return false;
554 }
555
556 inline std::string
558 {
559 switch (constant.kind)
560 {
562 return "Unit";
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 + ")";
573 }
574
575 return "Unknown";
576 }
577 }
578
581 {
583
586 const bool top_level) const
587 {
588 auto * function = ctx->make<Compiler_Bytecode_Function>();
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;
595 function->local_slots = source.local_slots;
596 function->register_count = source.next_value_id + 1;
597
599 for (size_t i = 0; i < source.blocks.size(); ++i)
601
602 for (size_t block_id = 0; block_id < source.blocks.size(); ++block_id)
603 {
604 const auto & block = source.blocks.access(block_id);
606 info.id = block_id;
607 info.source_block_id = block.id;
608 info.label = block.label;
609 info.begin_pc = function->code.size();
610 block_pcs.access(block_id) = info.begin_pc;
611
612 for (size_t inst_index = 0; inst_index < block.instructions.size(); ++inst_index)
613 {
614 const auto & inst = block.instructions.access(inst_index);
616 lowered.pc = function->code.size();
617 lowered.destination = inst.result_id;
618 lowered.type_id = inst.type_id;
619 lowered.span = inst.span;
620 lowered.op = inst.op;
621
622 switch (inst.kind)
623 {
626 lowered.constant_id =
629 break;
630
632 if (inst.local_slot_id != compiler_ir_invalid_id())
633 {
635 lowered.local_slot_id = inst.local_slot_id;
636 }
637 else
638 {
640 lowered.global_slot_id = inst.global_slot_id;
641 }
642 break;
643
645 if (inst.local_slot_id != compiler_ir_invalid_id())
646 {
648 lowered.local_slot_id = inst.local_slot_id;
649 }
650 else
651 {
653 lowered.global_slot_id = inst.global_slot_id;
654 }
655 lowered.destination = 0;
656 lowered.operands = inst.operands;
657 break;
658
661 lowered.operands = inst.operands;
662 break;
663
666 lowered.operands = inst.operands;
667 break;
668
671 lowered.operands = inst.operands;
672 break;
673
676 lowered.function_id = inst.function_id;
677 break;
678 }
679
680 function->code.append(std::move(lowered));
681 }
682
684 terminator.pc = function->code.size();
685 terminator.span = block.terminator.span;
686
687 switch (block.terminator.kind)
688 {
691 terminator.target_pc = block.terminator.successors.is_empty()
693 : block.terminator.successors.access(0);
694 break;
695
698 terminator.operands.append(block.terminator.condition_value);
699 terminator.target_pc = block.terminator.successors.size() > 0
700 ? block.terminator.successors.access(0)
702 terminator.false_target_pc = block.terminator.successors.size() > 1
703 ? block.terminator.successors.access(1)
705 break;
706
709 terminator.operands.append(block.terminator.return_value);
710 break;
711
714 break;
715
718 break;
719
722 break;
723 }
724
725 function->code.append(std::move(terminator));
726 info.end_pc = function->code.size();
727 function->blocks.append(std::move(info));
728 }
729
730 for (size_t i = 0; i < function->code.size(); ++i)
731 {
732 auto & inst = function->code.access(i);
734 inst.target_pc = inst.target_pc < block_pcs.size()
735 ? block_pcs.access(inst.target_pc)
737 else if (inst.opcode == Compiler_Bytecode_Opcode::Branch)
738 {
739 inst.target_pc = inst.target_pc < block_pcs.size()
740 ? block_pcs.access(inst.target_pc)
742 inst.false_target_pc = inst.false_target_pc < block_pcs.size()
743 ? block_pcs.access(inst.false_target_pc)
745 }
746 }
747
748 function->entry_pc = source.entry_block < block_pcs.size()
749 ? block_pcs.access(source.entry_block)
751 function->exit_pc = source.exit_block < block_pcs.size()
752 ? block_pcs.access(source.exit_block)
754 return function;
755 }
756
757 public:
760 : ctx(&context)
761 {}
762
765 lower_module(const Compiler_IR_Module * module) const
766 {
767 ah_runtime_error_unless(module != nullptr)
768 << "Compiler_Bytecode_Lowering::lower_module(): null IR module";
769
770 auto * result = ctx->make<Compiler_Bytecode_Module>();
771 result->global_slots = module->global_slots;
772 for (size_t i = 0; i < module->functions.size(); ++i)
773 result->functions.append(lower_function(*module->functions.access(i), false));
774 if (module->top_level != nullptr)
775 result->top_level = lower_function(*module->top_level, true);
776 return result;
777 }
778 };
779
783 const Compiler_Bytecode_Module * module = nullptr)
784 {
786
787 auto fail = [&report](const std::string & message)
788 {
789 report.valid = false;
790 report.errors.append(message);
791 };
792
793 if (function.code.is_empty())
794 {
795 fail("Bytecode function '" + function.name + "' has no instructions");
796 return report;
797 }
798
799 if (function.blocks.is_empty())
800 fail("Bytecode function '" + function.name + "' has no block metadata");
801 if (function.entry_pc >= function.code.size())
802 fail("Bytecode function '" + function.name + "' has invalid entry pc");
803 if (function.exit_pc >= function.code.size())
804 fail("Bytecode function '" + function.name + "' has invalid exit pc");
805
806 if (not report.valid)
807 return report;
808
809 for (size_t block_id = 0; block_id < function.blocks.size(); ++block_id)
810 {
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())
816 fail("Bytecode block " + Compiler_Bytecode_Detail::block_name(block_id)
817 + " in function '" + function.name + "' has invalid pc range");
818 }
819
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");
824
825 for (size_t pc = 0; pc < function.code.size(); ++pc)
826 {
827 const auto & inst = function.code.access(pc);
828 if (inst.pc != pc)
829 fail("Bytecode function '" + function.name + "' has mismatched pc at "
830 + std::to_string(pc));
831
832 const auto check_register = [&fail, &function, pc](const Compiler_Bytecode_Register_Id reg,
833 const std::string & what)
834 {
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);
839 };
840
848 and inst.destination == 0)
849 fail("Bytecode producer instruction at pc " + std::to_string(pc)
850 + " in function '" + function.name + "' must define a destination register");
851
852 if (inst.destination != 0)
853 check_register(inst.destination, "destination");
854
855 for (size_t operand_index = 0; operand_index < inst.operands.size(); ++operand_index)
856 check_register(inst.operands.access(operand_index),
857 "operand #" + std::to_string(operand_index));
858
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");
863
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");
869
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");
875
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");
880
883 and (inst.target_pc >= function.code.size()
884 or not Compiler_Bytecode_Detail::contains_pc(function.blocks, inst.target_pc)))
885 fail("Bytecode control-flow instruction at pc " + std::to_string(pc)
886 + " in function '" + function.name + "' has invalid target pc");
887
889 and (inst.false_target_pc >= function.code.size()
890 or not Compiler_Bytecode_Detail::contains_pc(function.blocks, inst.false_target_pc)))
891 fail("Bytecode branch at pc " + std::to_string(pc)
892 + " in function '" + function.name + "' has invalid false target pc");
893 }
894
895 return report;
896 }
897
901 {
903
904 auto merge = [&report](const Compiler_Bytecode_Validation_Report & child)
905 {
906 if (not child.valid)
907 report.valid = false;
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));
912 };
913
914 for (size_t i = 0; i < module.functions.size(); ++i)
915 merge(validate_bytecode_function(*module.functions.access(i), &module));
916 if (module.top_level != nullptr)
917 merge(validate_bytecode_function(*module.top_level, &module));
918 return report;
919 }
920
922 inline std::string
924 const Compiler_Bytecode_Module * module = nullptr,
925 const Compiler_Type_Context * types = nullptr)
926 {
927 (void) module;
928 std::ostringstream out;
929 if (function == nullptr)
930 {
931 out << "<null-bytecode-function>\n";
932 return out.str();
933 }
934
935 out << "BytecodeFunction(" << function->name << ")";
936 if (types != nullptr and function->type_id != 0)
937 out << ": " << types->to_string(function->type_id);
938 out << '\n';
939 out << " EntryPC: " << function->entry_pc << '\n';
940 out << " ExitPC: " << function->exit_pc << '\n';
941 out << " Registers: " << function->register_count << '\n';
942
943 if (not function->local_slots.is_empty())
944 {
945 out << " Slots:\n";
946 for (size_t i = 0; i < function->local_slots.size(); ++i)
947 {
948 const auto & slot = function->local_slots.access(i);
950 << " [" << compiler_ir_slot_kind_name(slot.kind) << "] "
951 << slot.name;
952 if (types != nullptr and slot.type_id != 0)
953 out << ": " << types->to_string(slot.type_id);
954 out << '\n';
955 }
956 }
957
958 if (function->constants.is_empty())
959 out << " Constants: <none>\n";
960 else
961 {
962 out << " Constants:\n";
963 for (size_t i = 0; i < function->constants.size(); ++i)
964 {
967 function->constants.access(i));
968 if (types != nullptr and function->constants.access(i).type_id != 0)
969 out << " : " << types->to_string(function->constants.access(i).type_id);
970 out << '\n';
971 }
972 }
973
974 if (not function->blocks.is_empty())
975 {
976 out << " Blocks:\n";
977 for (size_t i = 0; i < function->blocks.size(); ++i)
978 {
979 const auto & block = function->blocks.access(i);
982 << ", " << block.label << "] "
983 << "pc=" << block.begin_pc << ".." << block.end_pc << '\n';
984 }
985 }
986
987 out << " Code:\n";
988 for (size_t i = 0; i < function->code.size(); ++i)
989 {
990 const auto & inst = function->code.access(i);
991 out << " " << inst.pc << ": "
993
994 switch (inst.opcode)
995 {
998 << ", " << Compiler_Bytecode_Detail::constant_name(inst.constant_id);
999 break;
1000
1002 out << " " << Compiler_Bytecode_Detail::register_name(inst.destination)
1003 << ", " << Compiler_Bytecode_Detail::local_slot_name(inst.local_slot_id);
1004 break;
1005
1007 out << " " << Compiler_Bytecode_Detail::local_slot_name(inst.local_slot_id);
1008 if (not inst.operands.is_empty())
1009 out << ", " << Compiler_Bytecode_Detail::register_name(inst.operands.access(0));
1010 break;
1011
1013 out << " " << Compiler_Bytecode_Detail::register_name(inst.destination)
1014 << ", " << Compiler_Bytecode_Detail::global_slot_name(inst.global_slot_id);
1015 break;
1016
1018 out << " " << Compiler_Bytecode_Detail::global_slot_name(inst.global_slot_id);
1019 if (not inst.operands.is_empty())
1020 out << ", " << Compiler_Bytecode_Detail::register_name(inst.operands.access(0));
1021 break;
1022
1024 out << " " << Compiler_Bytecode_Detail::register_name(inst.destination)
1025 << ", " << Compiler_Bytecode_Detail::function_name(inst.function_id);
1026 break;
1027
1029 out << " " << Compiler_Bytecode_Detail::register_name(inst.destination)
1031 if (not inst.operands.is_empty())
1032 out << ", " << Compiler_Bytecode_Detail::register_name(inst.operands.access(0));
1033 break;
1034
1036 out << " " << Compiler_Bytecode_Detail::register_name(inst.destination)
1038 if (inst.operands.size() > 0)
1039 out << ", " << Compiler_Bytecode_Detail::register_name(inst.operands.access(0));
1040 if (inst.operands.size() > 1)
1041 out << ", " << Compiler_Bytecode_Detail::register_name(inst.operands.access(1));
1042 break;
1043
1045 out << " " << Compiler_Bytecode_Detail::register_name(inst.destination);
1046 for (size_t operand = 0; operand < inst.operands.size(); ++operand)
1047 out << (operand == 0 ? ", " : ", ")
1048 << Compiler_Bytecode_Detail::register_name(inst.operands.access(operand));
1049 break;
1050
1052 out << " " << inst.target_pc;
1053 break;
1054
1056 if (not inst.operands.is_empty())
1057 out << " " << Compiler_Bytecode_Detail::register_name(inst.operands.access(0));
1058 out << ", " << inst.target_pc << ", " << inst.false_target_pc;
1059 break;
1060
1062 if (not inst.operands.is_empty())
1063 out << " " << Compiler_Bytecode_Detail::register_name(inst.operands.access(0));
1064 break;
1065
1068 break;
1069 }
1070
1071 if (types != nullptr and inst.type_id != 0)
1072 out << " : " << types->to_string(inst.type_id);
1073 out << '\n';
1074 }
1075
1076 return out.str();
1077 }
1078
1080 inline std::string
1082 const Compiler_Type_Context * types = nullptr)
1083 {
1084 std::ostringstream out;
1085 if (module == nullptr)
1086 {
1087 out << "<null-bytecode-module>\n";
1088 return out.str();
1089 }
1090
1091 out << "BytecodeModule\n";
1092 if (not module->global_slots.is_empty())
1093 {
1094 out << " Globals:\n";
1095 for (size_t i = 0; i < module->global_slots.size(); ++i)
1096 {
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);
1102 out << '\n';
1103 }
1104 }
1105
1106 for (size_t i = 0; i < module->functions.size(); ++i)
1109 module->functions.access(i),
1110 module,
1111 types),
1112 2);
1113 if (module->top_level != nullptr)
1116 module->top_level,
1117 module,
1118 types),
1119 2);
1120 return out.str();
1121 }
1122}
1123
1124#endif
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.
Definition ah-errors.H:255
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Arena allocator for fast bump-pointer allocation.
Definition ah-arena.H:122
void reset() noexcept
Reset arena, making all memory available again.
Definition ah-arena.H:256
static constexpr size_t DEFAULT_SIZE
Default arena size (1 MB).
Definition ah-arena.H:133
Arena-backed ownership context for bytecode functions and modules.
Definition Bytecode.H:254
T * make(Args &&... args)
Allocates and constructs one bytecode object in the arena.
Definition Bytecode.H:281
Compiler_Bytecode_Context & operator=(const Compiler_Bytecode_Context &)=delete
DynArray< Owned_Object > owned
Definition Bytecode.H:262
void reset() noexcept
Resets the arena and destroys all tracked objects.
Definition Bytecode.H:298
Compiler_Bytecode_Context(const size_t arena_size=AhArenaAllocator::DEFAULT_SIZE)
Constructs a bytecode context.
Definition Bytecode.H:266
~Compiler_Bytecode_Context() noexcept
Destroys all tracked objects and rewinds the arena.
Definition Bytecode.H:274
Compiler_Bytecode_Context(const Compiler_Bytecode_Context &)=delete
Lowers explicit IR into register-based bytecode.
Definition Bytecode.H:581
Compiler_Bytecode_Function * lower_function(const Compiler_IR_Function &source, const bool top_level) const
Definition Bytecode.H:585
Compiler_Bytecode_Context * ctx
Definition Bytecode.H:582
Compiler_Bytecode_Lowering(Compiler_Bytecode_Context &context) noexcept
Builds a bytecode lowerer over context.
Definition Bytecode.H:759
Compiler_Bytecode_Module * lower_module(const Compiler_IR_Module *module) const
Lowers one IR module to bytecode.
Definition Bytecode.H:765
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().
Definition Blossom.H:466
std::string block_name(const Compiler_Bytecode_Block_Id id)
Definition Bytecode.H:334
bool parse_integer(const std::string &text, long long &value) noexcept
Definition Bytecode.H:388
const char * token_name(const Compiler_Operator_Kind kind) noexcept
Definition Bytecode.H:358
Compiler_Bytecode_Constant_Id intern_constant(DynArray< Compiler_Bytecode_Constant > &pool, const Compiler_Bytecode_Constant &constant)
Definition Bytecode.H:535
Compiler_Bytecode_Constant classify_constant(const Compiler_IR_Instruction &inst)
Definition Bytecode.H:474
char decode_escape_char(const std::string &raw)
Definition Bytecode.H:448
std::string constant_to_string(const Compiler_Bytecode_Constant &constant)
Definition Bytecode.H:557
void append_indented_text(std::ostream &out, const std::string &text, const size_t indent)
Definition Bytecode.H:364
std::string constant_name(const Compiler_Bytecode_Constant_Id id)
Definition Bytecode.H:328
std::string function_name(const Compiler_Bytecode_Function_Id id)
Definition Bytecode.H:352
bool same_constant(const Compiler_Bytecode_Constant &lhs, const Compiler_Bytecode_Constant &rhs) noexcept
Definition Bytecode.H:523
bool contains_pc(const DynArray< Compiler_Bytecode_Block_Info > &blocks, const Compiler_Bytecode_PC pc) noexcept
Definition Bytecode.H:547
std::string local_slot_name(const Compiler_IR_Local_Slot_Id id)
Definition Bytecode.H:340
std::string register_name(const Compiler_Bytecode_Register_Id id)
Definition Bytecode.H:322
std::string global_slot_name(const Compiler_IR_Global_Slot_Id id)
Definition Bytecode.H:346
std::string decode_escape_string(const std::string &raw)
Definition Bytecode.H:400
std::string block_name(const Compiler_IR_Block_Id id)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
constexpr size_t compiler_bytecode_invalid_id() noexcept
Returns the sentinel invalid bytecode id.
Definition Bytecode.H:84
Compiler_Bytecode_Opcode
VM opcodes supported by the bytecode MVP.
Definition Bytecode.H:102
@ 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.
Definition Bytecode.H:91
@ 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.
Definition ahDefs.C:95
const char * compiler_bytecode_opcode_name(const Compiler_Bytecode_Opcode opcode) noexcept
Stable debug name for one bytecode opcode.
Definition Bytecode.H:144
size_t Compiler_Bytecode_Block_Id
Definition Bytecode.H:79
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
Definition ah-zip.H:105
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.
Definition Bytecode.H:1081
size_t Compiler_Bytecode_Register_Id
Definition Bytecode.H:76
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.
Definition Bytecode.H:782
size_t Compiler_Bytecode_Constant_Id
Definition Bytecode.H:77
size_t Compiler_Type_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.
Definition Bytecode.H:923
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.
Definition Bytecode.H:900
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.
Definition ahAlgo.H:1410
size_t Compiler_IR_Global_Slot_Id
size_t Compiler_Bytecode_PC
Definition Bytecode.H:80
const char * compiler_bytecode_constant_kind_name(const Compiler_Bytecode_Constant_Kind kind) noexcept
Stable debug name for one constant kind.
Definition Bytecode.H:121
Field< int > Integer
Definition ahField.H:136
size_t Compiler_Bytecode_Function_Id
Definition Bytecode.H:78
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.
Definition Bytecode.H:212
Compiler_Bytecode_PC end_pc
One-past-last PC for the block.
Definition Bytecode.H:217
Compiler_IR_Block_Id source_block_id
Original IR block id.
Definition Bytecode.H:214
Compiler_Bytecode_PC begin_pc
First PC emitted for the block.
Definition Bytecode.H:216
std::string label
Original IR block label.
Definition Bytecode.H:215
Compiler_Bytecode_Block_Id id
Dense bytecode block id.
Definition Bytecode.H:213
One constant stored in a function-level constant pool.
Definition Bytecode.H:183
bool bool_value
Boolean payload when kind == Boolean.
Definition Bytecode.H:188
Compiler_Type_Id type_id
Original IR type, when known.
Definition Bytecode.H:185
std::string text
Canonical textual payload for dumps/debugging.
Definition Bytecode.H:186
char char_value
Character payload when kind == Character.
Definition Bytecode.H:189
Compiler_Bytecode_Constant_Kind kind
Logical constant category.
Definition Bytecode.H:184
long long integer_value
Integer payload when kind == Integer.
Definition Bytecode.H:187
Bytecode for one lowered function or top-level body.
Definition Bytecode.H:222
Compiler_Bytecode_PC exit_pc
PC of the canonical exit instruction.
Definition Bytecode.H:234
DynArray< Compiler_Bytecode_Instruction > code
Linear bytecode stream.
Definition Bytecode.H:232
Compiler_Type_Id type_id
Full function type, when known.
Definition Bytecode.H:227
std::string name
Debug/source name.
Definition Bytecode.H:224
Compiler_Bytecode_Function_Id id
Stable function id within the module.
Definition Bytecode.H:223
Compiler_Bytecode_PC entry_pc
Entry PC used to start execution.
Definition Bytecode.H:233
size_t register_count
Number of virtual registers required by the function.
Definition Bytecode.H:235
DynArray< Compiler_Bytecode_Constant > constants
Per-function constant pool.
Definition Bytecode.H:230
const Compiler_IR_Function * source_function
Original non-bytecode IR function.
Definition Bytecode.H:228
DynArray< Compiler_Bytecode_Block_Info > blocks
Debug mapping from blocks to PCs.
Definition Bytecode.H:231
bool is_top_level() const noexcept
Returns whether this bytecode function represents the top-level body.
Definition Bytecode.H:238
DynArray< Compiler_IR_Slot > local_slots
Local/parameter slot metadata preserved for the VM.
Definition Bytecode.H:229
bool top_level
Whether this function represents the top-level body.
Definition Bytecode.H:225
Source_Span span
Source span of the function/body.
Definition Bytecode.H:226
One lowered bytecode instruction.
Definition Bytecode.H:194
Compiler_Bytecode_PC target_pc
Jump target or branch true target.
Definition Bytecode.H:206
Compiler_Type_Id type_id
Result or stored type, when known.
Definition Bytecode.H:198
Compiler_Bytecode_PC false_target_pc
Branch false target.
Definition Bytecode.H:207
Compiler_IR_Global_Slot_Id global_slot_id
Referenced global slot, when relevant.
Definition Bytecode.H:202
Compiler_IR_Local_Slot_Id local_slot_id
Referenced local slot, when relevant.
Definition Bytecode.H:201
Compiler_Bytecode_Opcode opcode
Operation category.
Definition Bytecode.H:196
Compiler_Bytecode_Register_Id destination
Destination register, or 0 for void instructions.
Definition Bytecode.H:197
Compiler_Bytecode_Function_Id function_id
Referenced function id, when relevant.
Definition Bytecode.H:204
DynArray< Compiler_Bytecode_Register_Id > operands
Source registers in deterministic order.
Definition Bytecode.H:205
Compiler_Operator_Kind op
Unary/binary operator, when relevant.
Definition Bytecode.H:200
Compiler_Bytecode_Constant_Id constant_id
Referenced constant pool entry, when relevant.
Definition Bytecode.H:203
Source_Span span
Source region associated with the instruction.
Definition Bytecode.H:199
Compiler_Bytecode_PC pc
Program counter within the function code array.
Definition Bytecode.H:195
Bytecode module sharing global slot layout with the source IR module.
Definition Bytecode.H:246
DynArray< Compiler_IR_Slot > global_slots
Global slots preserved from the source IR module.
Definition Bytecode.H:247
Compiler_Bytecode_Function * top_level
Optional lowered top-level body.
Definition Bytecode.H:249
DynArray< Compiler_Bytecode_Function * > functions
Lowered non-top-level functions.
Definition Bytecode.H:248
Structural validation report for one bytecode function or module.
Definition Bytecode.H:313
bool valid
Whether all hard validation checks passed.
Definition Bytecode.H:314
DynArray< std::string > errors
Hard validation failures.
Definition Bytecode.H:315
DynArray< std::string > warnings
Non-fatal findings.
Definition Bytecode.H:316
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.
Definition SSA.H:163
Compiler_IR_Block_Id source_block_id
Original IR block id before SSA normalization.
Definition SSA.H:164
DynArray< Compiler_SSA_Instruction > instructions
Linear SSA instruction list.
Definition SSA.H:168
std::string label
Deterministic debug label.
Definition SSA.H:165
Compiler_SSA_Terminator terminator
Explicit block terminator.
Definition SSA.H:169
Source_Span span
Source region associated with the terminator.
Definition SSA.H:154
Compiler_SSA_Value_Id return_value
Return value, when relevant.
Definition SSA.H:156
DynArray< Compiler_SSA_Block_Id > successors
Successors in deterministic order.
Definition SSA.H:157
Compiler_IR_Terminator_Kind kind
Terminator category.
Definition SSA.H:153
Compiler_SSA_Value_Id condition_value
Branch condition, when relevant.
Definition SSA.H:155
Half-open byte range inside a source file.
Definition ah-source.H:100
Lazy and scalable dynamic array implementation.