Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Bytecode_Interpreter.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
54#ifndef BYTECODE_INTERPRETER_H
55#define BYTECODE_INTERPRETER_H
56
57#include <sstream>
58#include <string>
59#include <utility>
60
61#include <Bytecode.H>
62#include <Interpreter_Runtime.H>
63
64namespace Aleph
65{
74 {
75 Invalid,
76 Value,
78 };
79
87
89 inline const char *
91 {
92 switch (kind)
93 {
95 return "Invalid";
97 return "Value";
99 return "FunctionRef";
100 }
101
102 return "Unknown";
103 }
104
106 inline const char *
108 {
109 switch (kind)
110 {
112 return "Returned";
114 return "Halted";
116 return "RuntimeError";
117 }
118
119 return "Unknown";
120 }
121
130 {
134
138 {
139 return {};
140 }
141
145 {
148 result.runtime_value = value;
149 return result;
150 }
151
155 {
158 result.runtime_value = std::move(value);
159 return result;
160 }
161
168
171 make_bool(const bool value)
172 {
174 }
175
178 make_integer(const long long value)
179 {
181 }
182
185 make_string(std::string text)
186 {
187 return make_value(Interpreter_Value::make_string(std::move(text)));
188 }
189
192 make_character(const char ch)
193 {
195 }
196
203 make_host_function(std::string name,
204 const Interpreter_Host_Function callback,
205 void * user_data = nullptr)
206 {
208 callback,
209 user_data));
210 }
211
215 {
218 result.function_id = id;
219 return result;
220 }
221
223 [[nodiscard]] bool
228
230 [[nodiscard]] bool
235
237 [[nodiscard]] bool
242 };
243
246 {
247 std::string code;
248 std::string message;
249 std::string function_name;
252
254 [[nodiscard]] bool
256 {
257 return not code.empty() or not message.empty();
258 }
259 };
260
263 {
268
270 [[nodiscard]] bool
275
287
299
312 };
313
319 inline bool
321 const Compiler_Bytecode_Value & rhs)
322 {
323 if (lhs.kind != rhs.kind)
324 return false;
325
326 switch (lhs.kind)
327 {
329 return true;
333 return lhs.function_id == rhs.function_id;
334 }
335
336 return false;
337 }
338
346 inline std::string
348 const Compiler_Bytecode_Module * module = nullptr)
349 {
350 switch (value.kind)
351 {
353 return "Invalid";
354
356 return interpreter_value_to_string(value.runtime_value);
357
359 {
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;
364 out << ')';
365 return out.str();
366 }
367 }
368
369 return "Unknown";
370 }
371
378 inline std::string
380 const DynArray<Compiler_Bytecode_Value> & globals)
381 {
382 std::ostringstream out;
383 if (module == nullptr)
384 {
385 out << "<null-bytecode-globals>\n";
386 return out.str();
387 }
388
389 out << "BytecodeGlobals\n";
390 if (module->global_slots.is_empty())
391 {
392 out << " <none>\n";
393 return out.str();
394 }
395
396 for (size_t i = 0; i < module->global_slots.size(); ++i)
397 {
398 const auto & slot = module->global_slots.access(i);
399 out << " g" << slot.id << ' ' << slot.name << " = ";
400 if (slot.id < globals.size())
401 out << compiler_bytecode_value_to_string(globals.access(slot.id), module);
402 else
403 out << "<missing>";
404 out << '\n';
405 }
406 return out.str();
407 }
408
409 namespace Compiler_Bytecode_Interpreter_Detail
410 {
411 inline void
413 const size_t count,
415 {
416 values.clear();
417 for (size_t i = 0; i < count; ++i)
418 values.append(fill);
419 }
420
421 inline size_t
423 {
424 size_t count = 0;
425 for (size_t i = 0; i < function.local_slots.size(); ++i)
426 if (function.local_slots.access(i).kind == Compiler_IR_Slot_Kind::Parameter)
427 ++count;
428 return count;
429 }
430
431 inline bool
434 std::string & error)
435 {
436 switch (constant.kind)
437 {
440 return true;
441
444 return true;
445
448 return true;
449
451 {
452 std::string decoded = constant.text;
453 if (decoded.size() >= 2 and decoded.front() == '"' and decoded.back() == '"')
454 decoded = decoded.substr(1, decoded.size() - 2);
456 return true;
457 }
458
461 return true;
462
464 error = "unsupported raw constant payload '" + constant.text + "'";
465 return false;
466 }
467
468 error = "unknown constant kind";
469 return false;
470 }
471 }
472
484 {
495
499
500 [[nodiscard]] const Frame &
502 {
503 return call_stack.get_last();
504 }
505
506 Frame &
508 {
509 return call_stack.get_last();
510 }
511
513 make_error(const std::string & code,
514 const std::string & message,
515 const size_t executed_instructions,
516 const Frame * frame = nullptr,
517 const Compiler_Bytecode_Instruction * inst = nullptr) const
518 {
520 error.code = code;
521 error.message = message;
522 if (frame != nullptr and frame->function != nullptr)
523 {
524 error.function_name = frame->function->name;
525 error.pc = frame->pc;
526 }
527 if (inst != nullptr)
528 error.span = inst->span;
530 executed_instructions);
531 }
532
535 const size_t executed_instructions = 0) const
536 {
537 const std::string message = report.errors.is_empty()
538 ? "invalid bytecode module"
539 : report.errors.access(0);
540 return make_error("BCV001", message, executed_instructions);
541 }
542
543 [[nodiscard]] bool
545 {
547 if (report.valid)
548 return true;
549
551 return false;
552 }
553
554 [[nodiscard]] bool
556 const DynArray<Compiler_Bytecode_Value> & arguments,
557 const bool returns_to_caller,
558 const Compiler_Bytecode_PC caller_resume_pc,
559 const Compiler_Bytecode_Register_Id caller_destination,
560 const size_t executed_instructions,
562 {
563 const auto expected_arity
565 if (arguments.size() != expected_arity)
566 {
567 error = make_error("BCV004",
568 "function '" + function.name + "' expects "
569 + std::to_string(expected_arity) + " arguments but received "
570 + std::to_string(arguments.size()),
571 executed_instructions);
572 return false;
573 }
574
575 Frame frame;
576 frame.function = &function;
577 frame.pc = function.entry_pc;
578 frame.caller_resume_pc = caller_resume_pc;
579 frame.caller_destination = caller_destination;
580 frame.returns_to_caller = returns_to_caller;
581
583 function.register_count);
585 function.local_slots.size());
586
587 size_t arg_index = 0;
588 for (size_t i = 0; i < function.local_slots.size(); ++i)
589 {
590 const auto & slot = function.local_slots.access(i);
592 {
593 if (slot.id >= frame.locals.size())
594 {
595 error = make_error("BCV006",
596 "parameter slot id " + std::to_string(slot.id)
597 + " is out of bounds in function '" + function.name + "'",
598 executed_instructions);
599 return false;
600 }
601 frame.locals.access(slot.id) = arguments.access(arg_index++);
602 }
603 }
604
605 call_stack.append(std::move(frame));
606 return true;
607 }
608
609 [[nodiscard]] bool
610 read_register(const Frame & frame,
613 const size_t executed_instructions,
616 {
617 if (id >= frame.registers.size())
618 {
619 error = make_error("BCV006",
620 "invalid register r" + std::to_string(id),
621 executed_instructions,
622 &frame,
623 &inst);
624 return false;
625 }
626
627 value = frame.registers.access(id);
628 if (value.is_invalid())
629 {
630 error = make_error("BCV007",
631 "read from uninitialized register r" + std::to_string(id),
632 executed_instructions,
633 &frame,
634 &inst);
635 return false;
636 }
637
638 return true;
639 }
640
641 [[nodiscard]] bool
646 const size_t executed_instructions,
648 {
649 if (id >= frame.registers.size())
650 {
651 error = make_error("BCV006",
652 "invalid register r" + std::to_string(id),
653 executed_instructions,
654 &frame,
655 &inst);
656 return false;
657 }
658
659 frame.registers.access(id) = value;
660 return true;
661 }
662
663 [[nodiscard]] bool
664 read_local(const Frame & frame,
667 const size_t executed_instructions,
670 {
671 if (id >= frame.locals.size())
672 {
673 error = make_error("BCV006",
674 "invalid local slot s" + std::to_string(id),
675 executed_instructions,
676 &frame,
677 &inst);
678 return false;
679 }
680
681 value = frame.locals.access(id);
682 if (value.is_invalid())
683 {
684 error = make_error("BCV008",
685 "read from uninitialized local slot s" + std::to_string(id),
686 executed_instructions,
687 &frame,
688 &inst);
689 return false;
690 }
691
692 return true;
693 }
694
695 [[nodiscard]] bool
700 const size_t executed_instructions,
702 {
703 if (id >= frame.locals.size())
704 {
705 error = make_error("BCV006",
706 "invalid local slot s" + std::to_string(id),
707 executed_instructions,
708 &frame,
709 &inst);
710 return false;
711 }
712
713 frame.locals.access(id) = value;
714 return true;
715 }
716
717 [[nodiscard]] bool
718 read_global(const Frame & frame,
721 const size_t executed_instructions,
724 {
725 if (id >= globals.size())
726 {
727 error = make_error("BCV006",
728 "invalid global slot g" + std::to_string(id),
729 executed_instructions,
730 &frame,
731 &inst);
732 return false;
733 }
734
735 value = globals.access(id);
736 if (value.is_invalid())
737 {
738 error = make_error("BCV009",
739 "read from uninitialized global slot g" + std::to_string(id),
740 executed_instructions,
741 &frame,
742 &inst);
743 return false;
744 }
745
746 return true;
747 }
748
749 [[nodiscard]] bool
754 const size_t executed_instructions,
756 {
757 if (id >= globals.size())
758 {
759 error = make_error("BCV006",
760 "invalid global slot g" + std::to_string(id),
761 executed_instructions,
762 &frame,
763 &inst);
764 return false;
765 }
766
767 globals.access(id) = value;
768 return true;
769 }
770
771 [[nodiscard]] bool
773 const Frame & frame,
775 const size_t executed_instructions,
776 const std::string & context,
777 long long & result,
779 {
780 if (not value.is_runtime_value()
781 or value.runtime_value.kind != Interpreter_Value_Kind::Integer)
782 {
783 error = make_error("BCV011",
784 context + " expects Integer but received "
786 executed_instructions,
787 &frame,
788 &inst);
789 return false;
790 }
791
792 result = value.runtime_value.integer_value;
793 return true;
794 }
795
796 [[nodiscard]] bool
798 const Frame & frame,
800 const size_t executed_instructions,
801 const std::string & context,
802 bool & result,
804 {
805 if (not value.is_runtime_value()
806 or value.runtime_value.kind != Interpreter_Value_Kind::Bool)
807 {
808 error = make_error("BCV012",
809 context + " expects Bool but received "
811 executed_instructions,
812 &frame,
813 &inst);
814 return false;
815 }
816
817 result = value.runtime_value.bool_value;
818 return true;
819 }
820
821 [[nodiscard]] bool
824 const size_t executed_instructions,
828 {
829 if (inst.operands.is_empty())
830 {
831 error = make_error("BCV017",
832 "call instruction has no callee operand",
833 executed_instructions,
834 &frame,
835 &inst);
836 return false;
837 }
838
839 if (not read_register(frame,
840 inst.operands.access(0),
841 inst,
842 executed_instructions,
843 callee,
844 error))
845 return false;
846
847 arguments.clear();
848 for (size_t i = 1; i < inst.operands.size(); ++i)
849 {
851 if (not read_register(frame,
852 inst.operands.access(i),
853 inst,
854 executed_instructions,
855 argument,
856 error))
857 return false;
858 arguments.append(argument);
859 }
860
861 return true;
862 }
863
864 [[nodiscard]] bool
867 const size_t executed_instructions,
870 {
871 const auto finished = call_stack.pop();
872 if (not finished.returns_to_caller)
873 {
876 : Compiler_Bytecode_Execution_Result::halted(value, executed_instructions);
877 return true;
878 }
879
880 auto & caller = current_frame();
882 finished.caller_destination,
883 value,
884 caller.function->code.access(caller.pc),
885 executed_instructions,
886 error))
887 return true;
888
889 caller.pc = finished.caller_resume_pc;
890 return false;
891 }
892
893 [[nodiscard]] bool
896 const Compiler_Bytecode_Value & callee,
897 const DynArray<Compiler_Bytecode_Value> & arguments,
898 const size_t executed_instructions,
900 {
902 for (size_t i = 0; i < arguments.size(); ++i)
903 {
904 if (not arguments.access(i).is_runtime_value())
905 {
906 error = make_error("BCV019",
907 "host function arguments cannot contain bytecode function references",
908 executed_instructions,
909 &frame,
910 &inst);
911 return false;
912 }
913 runtime_arguments.append(arguments.access(i).runtime_value);
914 }
915
918 const bool ok = callee.runtime_value.host_function(callee.runtime_value.host_user_data,
921 host_error);
922 if (not ok)
923 {
924 error = make_error(host_error.code.empty() ? "BCV021" : host_error.code,
925 host_error.message.empty()
926 ? "host function call failed"
927 : host_error.message,
928 executed_instructions,
929 &frame,
930 &inst);
931 if (host_error.has_error() and not host_error.code.empty())
932 error.error.span = host_error.span;
933 return false;
934 }
935
936 if (not write_register(frame,
937 inst.destination,
939 inst,
940 executed_instructions,
941 error))
942 return false;
943
944 ++frame.pc;
945 return true;
946 }
947
950 {
951 size_t executed_instructions = 0;
952
953 while (not call_stack.is_empty())
954 {
955 if (max_steps != 0 and executed_instructions >= max_steps)
956 return make_error("BCV020",
957 "bytecode step limit exceeded",
958 executed_instructions,
959 &current_frame());
960
961 auto & frame = current_frame();
962 if (frame.pc >= frame.function->code.size())
963 return make_error("BCV005",
964 "program counter " + std::to_string(frame.pc)
965 + " is out of bounds in function '" + frame.function->name + "'",
966 executed_instructions,
967 &frame);
968
969 const auto & inst = frame.function->code.access(frame.pc);
970 ++executed_instructions;
971
972 switch (inst.opcode)
973 {
975 {
976 if (inst.constant_id >= frame.function->constants.size())
977 return make_error("BCV006",
978 "invalid constant id k"
979 + std::to_string(inst.constant_id),
980 executed_instructions,
981 &frame,
982 &inst);
983
985 std::string conversion_error;
987 frame.function->constants.access(inst.constant_id),
988 value,
990 return make_error("BCV010",
992 executed_instructions,
993 &frame,
994 &inst);
995
997 if (not write_register(frame,
998 inst.destination,
999 value,
1000 inst,
1001 executed_instructions,
1002 error))
1003 return error;
1004
1005 ++frame.pc;
1006 break;
1007 }
1008
1010 {
1013 if (not read_local(frame,
1014 inst.local_slot_id,
1015 inst,
1016 executed_instructions,
1017 value,
1018 error))
1019 return error;
1020 if (not write_register(frame,
1021 inst.destination,
1022 value,
1023 inst,
1024 executed_instructions,
1025 error))
1026 return error;
1027
1028 ++frame.pc;
1029 break;
1030 }
1031
1033 {
1034 if (inst.operands.is_empty())
1035 return make_error("BCV006",
1036 "StoreLocal requires one source operand",
1037 executed_instructions,
1038 &frame,
1039 &inst);
1040
1043 if (not read_register(frame,
1044 inst.operands.access(0),
1045 inst,
1046 executed_instructions,
1047 value,
1048 error))
1049 return error;
1050 if (not write_local(frame,
1051 inst.local_slot_id,
1052 value,
1053 inst,
1054 executed_instructions,
1055 error))
1056 return error;
1057
1058 ++frame.pc;
1059 break;
1060 }
1061
1063 {
1066 if (not read_global(frame,
1067 inst.global_slot_id,
1068 inst,
1069 executed_instructions,
1070 value,
1071 error))
1072 return error;
1073 if (not write_register(frame,
1074 inst.destination,
1075 value,
1076 inst,
1077 executed_instructions,
1078 error))
1079 return error;
1080
1081 ++frame.pc;
1082 break;
1083 }
1084
1086 {
1087 if (inst.operands.is_empty())
1088 return make_error("BCV006",
1089 "StoreGlobal requires one source operand",
1090 executed_instructions,
1091 &frame,
1092 &inst);
1093
1096 if (not read_register(frame,
1097 inst.operands.access(0),
1098 inst,
1099 executed_instructions,
1100 value,
1101 error))
1102 return error;
1103 if (not write_global(frame,
1104 inst.global_slot_id,
1105 value,
1106 inst,
1107 executed_instructions,
1108 error))
1109 return error;
1110
1111 ++frame.pc;
1112 break;
1113 }
1114
1116 {
1118 if (not write_register(frame,
1119 inst.destination,
1121 inst,
1122 executed_instructions,
1123 error))
1124 return error;
1125
1126 ++frame.pc;
1127 break;
1128 }
1129
1131 {
1132 if (inst.operands.is_empty())
1133 return make_error("BCV006",
1134 "Unary requires one source operand",
1135 executed_instructions,
1136 &frame,
1137 &inst);
1138
1141 if (not read_register(frame,
1142 inst.operands.access(0),
1143 inst,
1144 executed_instructions,
1145 operand,
1146 error))
1147 return error;
1148
1150 switch (inst.op)
1151 {
1153 {
1154 long long integer_value = 0;
1155 if (not require_integer(operand,
1156 frame,
1157 inst,
1158 executed_instructions,
1159 "unary '-'",
1160 integer_value,
1161 error))
1162 return error;
1163 result = Compiler_Bytecode_Value::make_integer(-integer_value);
1164 break;
1165 }
1166
1168 {
1169 bool bool_value = false;
1170 if (not require_bool(operand,
1171 frame,
1172 inst,
1173 executed_instructions,
1174 "unary '!'",
1175 bool_value,
1176 error))
1177 return error;
1178 result = Compiler_Bytecode_Value::make_bool(not bool_value);
1179 break;
1180 }
1181
1183 {
1184 long long integer_value = 0;
1185 if (not require_integer(operand,
1186 frame,
1187 inst,
1188 executed_instructions,
1189 "unary '~'",
1190 integer_value,
1191 error))
1192 return error;
1193 result = Compiler_Bytecode_Value::make_integer(~integer_value);
1194 break;
1195 }
1196
1197 default:
1198 return make_error("BCV015",
1199 "unsupported unary operator '"
1201 + "'",
1202 executed_instructions,
1203 &frame,
1204 &inst);
1205 }
1206
1207 if (not write_register(frame,
1208 inst.destination,
1209 result,
1210 inst,
1211 executed_instructions,
1212 error))
1213 return error;
1214
1215 ++frame.pc;
1216 break;
1217 }
1218
1220 {
1221 if (inst.operands.size() < 2)
1222 return make_error("BCV006",
1223 "Binary requires two source operands",
1224 executed_instructions,
1225 &frame,
1226 &inst);
1227
1231 if (not read_register(frame,
1232 inst.operands.access(0),
1233 inst,
1234 executed_instructions,
1235 lhs_value,
1236 error))
1237 return error;
1238 if (not read_register(frame,
1239 inst.operands.access(1),
1240 inst,
1241 executed_instructions,
1242 rhs_value,
1243 error))
1244 return error;
1245
1247 switch (inst.op)
1248 {
1257 {
1258 long long lhs = 0;
1259 long long rhs = 0;
1261 frame,
1262 inst,
1263 executed_instructions,
1264 "binary operator",
1265 lhs,
1266 error))
1267 return error;
1269 frame,
1270 inst,
1271 executed_instructions,
1272 "binary operator",
1273 rhs,
1274 error))
1275 return error;
1276
1277 switch (inst.op)
1278 {
1280 result = Compiler_Bytecode_Value::make_integer(lhs + rhs);
1281 break;
1283 result = Compiler_Bytecode_Value::make_integer(lhs - rhs);
1284 break;
1286 result = Compiler_Bytecode_Value::make_integer(lhs * rhs);
1287 break;
1289 if (rhs == 0)
1290 return make_error("BCV013",
1291 "division by zero",
1292 executed_instructions,
1293 &frame,
1294 &inst);
1295 result = Compiler_Bytecode_Value::make_integer(lhs / rhs);
1296 break;
1298 if (rhs == 0)
1299 return make_error("BCV014",
1300 "modulo by zero",
1301 executed_instructions,
1302 &frame,
1303 &inst);
1304 result = Compiler_Bytecode_Value::make_integer(lhs % rhs);
1305 break;
1307 result = Compiler_Bytecode_Value::make_integer(lhs & rhs);
1308 break;
1310 result = Compiler_Bytecode_Value::make_integer(lhs | rhs);
1311 break;
1313 result = Compiler_Bytecode_Value::make_integer(lhs ^ rhs);
1314 break;
1315 default:
1316 break;
1317 }
1318 break;
1319 }
1320
1325 {
1326 long long lhs = 0;
1327 long long rhs = 0;
1329 frame,
1330 inst,
1331 executed_instructions,
1332 "comparison",
1333 lhs,
1334 error))
1335 return error;
1337 frame,
1338 inst,
1339 executed_instructions,
1340 "comparison",
1341 rhs,
1342 error))
1343 return error;
1344
1345 bool comparison = false;
1346 switch (inst.op)
1347 {
1349 comparison = lhs < rhs;
1350 break;
1352 comparison = lhs <= rhs;
1353 break;
1355 comparison = lhs > rhs;
1356 break;
1358 comparison = lhs >= rhs;
1359 break;
1360 default:
1361 break;
1362 }
1364 break;
1365 }
1366
1369 {
1370 bool lhs = false;
1371 bool rhs = false;
1373 frame,
1374 inst,
1375 executed_instructions,
1376 "logical operator",
1377 lhs,
1378 error))
1379 return error;
1381 frame,
1382 inst,
1383 executed_instructions,
1384 "logical operator",
1385 rhs,
1386 error))
1387 return error;
1390 ? lhs and rhs
1391 : lhs or rhs);
1392 break;
1393 }
1394
1398 break;
1399
1403 break;
1404
1405 default:
1406 return make_error("BCV016",
1407 "unsupported binary operator '"
1409 + "'",
1410 executed_instructions,
1411 &frame,
1412 &inst);
1413 }
1414
1415 if (not write_register(frame,
1416 inst.destination,
1417 result,
1418 inst,
1419 executed_instructions,
1420 error))
1421 return error;
1422
1423 ++frame.pc;
1424 break;
1425 }
1426
1428 {
1433 inst,
1434 executed_instructions,
1435 callee,
1436 arguments,
1437 error))
1438 return error;
1439
1440 if (callee.is_function_ref())
1441 {
1442 if (callee.function_id >= bytecode_module->functions.size())
1443 return make_error("BCV003",
1444 "invalid function id f"
1445 + std::to_string(callee.function_id),
1446 executed_instructions,
1447 &frame,
1448 &inst);
1449
1450 if (not push_frame(*bytecode_module->functions.access(callee.function_id),
1451 arguments,
1452 true,
1453 frame.pc + 1,
1454 inst.destination,
1455 executed_instructions,
1456 error))
1457 return error;
1458 break;
1459 }
1460
1461 if (not callee.is_runtime_value())
1462 return make_error("BCV017",
1463 "callee is not callable: "
1465 executed_instructions,
1466 &frame,
1467 &inst);
1468
1470 {
1471 if (not execute_host_call(frame,
1472 inst,
1473 callee,
1474 arguments,
1475 executed_instructions,
1476 error))
1477 return error;
1478 break;
1479 }
1480
1482 return make_error("BCV018",
1483 "HIR function values are not callable from bytecode VM",
1484 executed_instructions,
1485 &frame,
1486 &inst);
1487
1488 return make_error("BCV017",
1489 "callee is not callable: "
1491 executed_instructions,
1492 &frame,
1493 &inst);
1494 }
1495
1497 frame.pc = inst.target_pc;
1498 break;
1499
1501 {
1502 if (inst.operands.is_empty())
1503 return make_error("BCV006",
1504 "Branch requires one condition operand",
1505 executed_instructions,
1506 &frame,
1507 &inst);
1508
1509 Compiler_Bytecode_Value condition;
1511 if (not read_register(frame,
1512 inst.operands.access(0),
1513 inst,
1514 executed_instructions,
1515 condition,
1516 error))
1517 return error;
1518
1519 bool branch = false;
1520 if (not require_bool(condition,
1521 frame,
1522 inst,
1523 executed_instructions,
1524 "branch condition",
1525 branch,
1526 error))
1527 return error;
1528
1529 frame.pc = branch ? inst.target_pc : inst.false_target_pc;
1530 break;
1531 }
1532
1534 {
1537 if (not inst.operands.is_empty()
1538 and not read_register(frame,
1539 inst.operands.access(0),
1540 inst,
1541 executed_instructions,
1542 value,
1543 error))
1544 return error;
1545
1549 executed_instructions,
1550 completed,
1551 error))
1552 return error.ok() ? completed : error;
1553 break;
1554 }
1555
1557 {
1562 executed_instructions,
1563 completed,
1564 error))
1565 return error.ok() ? completed : error;
1566 break;
1567 }
1568
1570 return make_error("BCV022",
1571 "executed trap/unreachable instruction",
1572 executed_instructions,
1573 &frame,
1574 &inst);
1575 }
1576 }
1577
1579 }
1580
1583 const DynArray<Compiler_Bytecode_Value> & arguments,
1584 const size_t max_steps)
1585 {
1586 call_stack.clear();
1587
1590 return error;
1591
1592 if (not push_frame(function,
1593 arguments,
1594 false,
1596 0,
1597 0,
1598 error))
1599 return error;
1600
1601 return execute_loop(max_steps);
1602 }
1603
1604 public:
1612 {
1613 reset();
1614 }
1615
1617 void
1619 {
1620 call_stack.clear();
1622 bytecode_module != nullptr
1624 : 0);
1625 }
1626
1630 {
1631 return *bytecode_module;
1632 }
1633
1637 {
1638 return globals;
1639 }
1640
1643 global_slot(const Compiler_IR_Global_Slot_Id slot_id) const noexcept
1644 {
1645 if (slot_id >= globals.size())
1646 return nullptr;
1647 return &globals.access(slot_id);
1648 }
1649
1651 [[nodiscard]] size_t
1653 {
1654 return call_stack.size();
1655 }
1656
1663 bool
1666 {
1667 if (slot_id >= globals.size())
1668 return false;
1669 globals.access(slot_id) = value;
1670 return true;
1671 }
1672
1679 run_top_level(const size_t max_steps = 0)
1680 {
1681 if (bytecode_module == nullptr)
1682 return make_error("BCV001", "null bytecode module", 0);
1683
1684 if (bytecode_module->top_level == nullptr)
1685 return make_error("BCV002", "bytecode module has no top-level body", 0);
1686
1688 return execute_entry(*bytecode_module->top_level, arguments, max_steps);
1689 }
1690
1700 const DynArray<Compiler_Bytecode_Value> & arguments,
1701 const size_t max_steps = 0)
1702 {
1703 if (bytecode_module == nullptr)
1704 return make_error("BCV001", "null bytecode module", 0);
1705
1706 if (function_id >= bytecode_module->functions.size())
1707 return make_error("BCV003",
1708 "invalid function id f" + std::to_string(function_id),
1709 0);
1710
1711 return execute_entry(*bytecode_module->functions.access(function_id),
1712 arguments,
1713 max_steps);
1714 }
1715
1717 [[nodiscard]] std::string
1722 };
1723}
1724
1725#endif
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
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
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
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().
Definition Blossom.H:466
const char * token_name(const Compiler_Operator_Kind kind) noexcept
Definition Bytecode.H:358
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.
Definition ah-arena.H:89
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.
Definition Bytecode.H:84
@ 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.
Definition ah-zip.H:136
@ 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.
Definition ahDefs.C:95
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.
Definition ahAlgo.H:707
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
Definition Bytecode.H:76
Compiler_Bytecode_Validation_Report validate_bytecode_module(const Compiler_Bytecode_Module &module)
Validates all bytecode functions in one module.
Definition Bytecode.H:900
void error(const char *file, int line, const char *format,...)
Print an error message with file and line info.
Definition ahDefs.C:100
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
Definition Bytecode.H:80
size_t Compiler_Bytecode_Function_Id
Definition Bytecode.H:78
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.
Definition ahAlgo.H:127
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.
Definition Bytecode.H:183
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.
Definition Bytecode.H:222
std::string name
Debug/source name.
Definition Bytecode.H:224
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_IR_Slot > local_slots
Local/parameter slot metadata preserved for the VM.
Definition Bytecode.H:229
One lowered bytecode instruction.
Definition Bytecode.H:194
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
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
DynArray< Compiler_Bytecode_Value > registers
Structural validation report for one bytecode function or module.
Definition Bytecode.H:313
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.
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.
Definition ah-source.H:100