Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
bytecode_interpreter_test.cc
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
36#include <gtest/gtest.h>
37
39
40using namespace Aleph;
41
42namespace
43{
46 {
48 block.id = 1;
49 block.label = "exit";
50 block.terminator.kind = Compiler_IR_Terminator_Kind::Exit;
51 return block;
52 }
53
56 {
57 Compiler_IR_Function function;
58 function.id = 0;
59 function.name = "choose";
60 function.entry_block = 0;
61 function.exit_block = 1;
62 function.next_value_id = 5;
63
65 flag.id = 0;
66 flag.kind = Compiler_IR_Slot_Kind::Parameter;
67 flag.name = "flag";
68 function.local_slots.append(flag);
69
71 value.id = 1;
72 value.kind = Compiler_IR_Slot_Kind::Local;
73 value.name = "value";
74 function.local_slots.append(value);
75
77 entry.id = 0;
78 entry.label = "entry";
79
81 zero.kind = Compiler_IR_Instruction_Kind::Constant;
82 zero.result_id = 1;
83 zero.text = "0";
84 entry.instructions.append(zero);
85
87 init_store.kind = Compiler_IR_Instruction_Kind::Store;
88 init_store.local_slot_id = 1;
89 init_store.operands.append(1);
90 entry.instructions.append(init_store);
91
93 load_flag.kind = Compiler_IR_Instruction_Kind::Load;
94 load_flag.result_id = 2;
95 load_flag.local_slot_id = 0;
96 entry.instructions.append(load_flag);
97
98 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
102
103 auto exit = make_exit_block();
104 exit.predecessors.append(3);
105
107 then_block.id = 2;
108 then_block.label = "if.then.0";
109
111 one.kind = Compiler_IR_Instruction_Kind::Constant;
112 one.result_id = 3;
113 one.text = "1";
114 then_block.instructions.append(one);
115
117 then_store.kind = Compiler_IR_Instruction_Kind::Store;
118 then_store.local_slot_id = 1;
119 then_store.operands.append(3);
120 then_block.instructions.append(then_store);
121
122 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
123 then_block.terminator.successors.append(3);
124 then_block.predecessors.append(0);
125
127 join_block.id = 3;
128 join_block.label = "if.end.0";
129
131 load_value.kind = Compiler_IR_Instruction_Kind::Load;
132 load_value.result_id = 5;
133 load_value.local_slot_id = 1;
134 join_block.instructions.append(load_value);
135
136 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
137 join_block.terminator.return_value = 5;
138 join_block.terminator.successors.append(1);
139 join_block.predecessors.append(2);
140 join_block.predecessors.append(4);
141
143 else_block.id = 4;
144 else_block.label = "if.else.0";
145
147 two.kind = Compiler_IR_Instruction_Kind::Constant;
148 two.result_id = 4;
149 two.text = "2";
150 else_block.instructions.append(two);
151
153 else_store.kind = Compiler_IR_Instruction_Kind::Store;
154 else_store.local_slot_id = 1;
155 else_store.operands.append(4);
156 else_block.instructions.append(else_store);
157
158 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
159 else_block.terminator.successors.append(3);
160 else_block.predecessors.append(0);
161
162 function.blocks.append(entry);
163 function.blocks.append(exit);
164 function.blocks.append(then_block);
165 function.blocks.append(join_block);
166 function.blocks.append(else_block);
167 return function;
168 }
169
172 {
173 Compiler_IR_Function function;
174 function.id = 0;
175 function.name = "add_one";
176 function.entry_block = 0;
177 function.exit_block = 1;
178 function.next_value_id = 4;
179
181 param.id = 0;
182 param.kind = Compiler_IR_Slot_Kind::Parameter;
183 param.name = "x";
184 function.local_slots.append(param);
185
186 Compiler_IR_Slot local;
187 local.id = 1;
188 local.kind = Compiler_IR_Slot_Kind::Local;
189 local.name = "tmp";
190 function.local_slots.append(local);
191
192 Compiler_IR_Block entry;
193 entry.id = 0;
194 entry.label = "entry";
195
197 constant.kind = Compiler_IR_Instruction_Kind::Constant;
198 constant.result_id = 1;
199 constant.text = "1";
200 entry.instructions.append(constant);
201
203 store.kind = Compiler_IR_Instruction_Kind::Store;
204 store.local_slot_id = 1;
205 store.operands.append(1);
206 entry.instructions.append(store);
207
209 load_x.kind = Compiler_IR_Instruction_Kind::Load;
210 load_x.result_id = 2;
211 load_x.local_slot_id = 0;
212 entry.instructions.append(load_x);
213
215 load_tmp.kind = Compiler_IR_Instruction_Kind::Load;
216 load_tmp.result_id = 3;
217 load_tmp.local_slot_id = 1;
218 entry.instructions.append(load_tmp);
219
221 add.kind = Compiler_IR_Instruction_Kind::Binary;
222 add.result_id = 4;
223 add.op = Compiler_Operator_Kind::Plus;
224 add.operands.append(2);
225 add.operands.append(3);
226 entry.instructions.append(add);
227
228 entry.terminator.kind = Compiler_IR_Terminator_Kind::Return;
229 entry.terminator.return_value = 4;
231
232 auto exit = make_exit_block();
233 exit.predecessors.append(0);
234
235 function.blocks.append(entry);
236 function.blocks.append(exit);
237 return function;
238 }
239
242 {
243 Compiler_IR_Module module;
244
246 global.id = 0;
247 global.kind = Compiler_IR_Slot_Kind::Global;
248 global.name = "answer";
249 module.global_slots.append(global);
250
251 auto * function = new Compiler_IR_Function(make_linear_ir());
252 module.functions.append(function);
253
254 auto * top = new Compiler_IR_Function();
255 top->id = compiler_ir_invalid_id();
256 top->name = "<top-level>";
257 top->entry_block = 0;
258 top->exit_block = 1;
259 top->next_value_id = 3;
260
261 Compiler_IR_Block entry;
262 entry.id = 0;
263 entry.label = "entry";
264
266 function_ref.kind = Compiler_IR_Instruction_Kind::Function_Ref;
267 function_ref.result_id = 1;
268 function_ref.function_id = 0;
269 entry.instructions.append(function_ref);
270
272 constant.kind = Compiler_IR_Instruction_Kind::Constant;
273 constant.result_id = 2;
274 constant.text = "41";
275 entry.instructions.append(constant);
276
278 call.kind = Compiler_IR_Instruction_Kind::Call;
279 call.result_id = 3;
280 call.operands.append(1);
281 call.operands.append(2);
282 entry.instructions.append(call);
283
285 store_global.kind = Compiler_IR_Instruction_Kind::Store;
286 store_global.global_slot_id = 0;
287 store_global.operands.append(3);
288 entry.instructions.append(store_global);
289
290 entry.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
292
293 auto exit = make_exit_block();
294 exit.predecessors.append(0);
295
296 top->blocks.append(entry);
297 top->blocks.append(exit);
298 module.top_level = top;
299 return module;
300 }
301
304 {
305 Compiler_IR_Function function;
306 function.id = 0;
307 function.name = "explode";
308 function.entry_block = 0;
309 function.exit_block = 1;
310 function.next_value_id = 3;
311
312 Compiler_IR_Block entry;
313 entry.id = 0;
314 entry.label = "entry";
315
317 lhs.kind = Compiler_IR_Instruction_Kind::Constant;
318 lhs.result_id = 1;
319 lhs.text = "5";
320 entry.instructions.append(lhs);
321
323 rhs.kind = Compiler_IR_Instruction_Kind::Constant;
324 rhs.result_id = 2;
325 rhs.text = "0";
326 entry.instructions.append(rhs);
327
329 div.kind = Compiler_IR_Instruction_Kind::Binary;
330 div.result_id = 3;
331 div.op = Compiler_Operator_Kind::Slash;
332 div.operands.append(1);
333 div.operands.append(2);
334 entry.instructions.append(div);
335
336 entry.terminator.kind = Compiler_IR_Terminator_Kind::Return;
337 entry.terminator.return_value = 3;
339
340 auto exit = make_exit_block();
341 exit.predecessors.append(0);
342
343 function.blocks.append(entry);
344 function.blocks.append(exit);
345 return function;
346 }
347}
348
350{
351 Compiler_Bytecode_Context ctx(1 << 16);
353
354 Compiler_IR_Module module;
355 auto source = make_branch_ir();
356 module.functions.append(&source);
357
358 const auto * bytecode = lowering.lower_module(&module);
360
363
364 const auto when_true = vm.call_function(0, arguments);
366 EXPECT_EQ(when_true.kind, Compiler_Bytecode_Completion_Kind::Returned);
367 ASSERT_TRUE(when_true.value.is_runtime_value());
368 EXPECT_EQ(when_true.value.runtime_value.kind, Interpreter_Value_Kind::Integer);
369 EXPECT_EQ(when_true.value.runtime_value.integer_value, 1);
370
371 arguments.access(0) = Compiler_Bytecode_Value::make_bool(false);
372 const auto when_false = vm.call_function(0, arguments);
374 EXPECT_EQ(when_false.kind, Compiler_Bytecode_Completion_Kind::Returned);
375 ASSERT_TRUE(when_false.value.is_runtime_value());
376 EXPECT_EQ(when_false.value.runtime_value.kind, Interpreter_Value_Kind::Integer);
377 EXPECT_EQ(when_false.value.runtime_value.integer_value, 2);
378}
379
381{
382 Compiler_Bytecode_Context ctx(1 << 16);
384
385 auto module = make_module_with_top_level_call();
386 const auto * bytecode = lowering.lower_module(&module);
388
389 const auto executed = vm.run_top_level();
390 ASSERT_TRUE(executed.ok());
391 EXPECT_EQ(executed.kind, Compiler_Bytecode_Completion_Kind::Halted);
392 ASSERT_TRUE(executed.value.is_runtime_value());
393 EXPECT_TRUE(executed.value.runtime_value.is_unit());
394
395 const auto * answer = vm.global_slot(0);
396 ASSERT_NE(answer, nullptr);
397 ASSERT_TRUE(answer->is_runtime_value());
398 EXPECT_EQ(answer->runtime_value.kind, Interpreter_Value_Kind::Integer);
399 EXPECT_EQ(answer->runtime_value.integer_value, 42);
400
401 const auto dump = vm.dump_globals();
402 EXPECT_NE(dump.find("BytecodeGlobals"), std::string::npos);
403 EXPECT_NE(dump.find("g0 answer = Int(42)"), std::string::npos);
404
405 delete module.functions.access(0);
406 delete module.top_level;
407}
408
410{
411 Compiler_Bytecode_Context ctx(1 << 16);
413
414 Compiler_IR_Module module;
415 auto source = make_divide_by_zero_ir();
416 module.functions.append(&source);
417
418 const auto * bytecode = lowering.lower_module(&module);
420
422 const auto executed = vm.call_function(0, arguments);
423
424 ASSERT_FALSE(executed.ok());
425 EXPECT_EQ(executed.kind, Compiler_Bytecode_Completion_Kind::Runtime_Error);
426 EXPECT_EQ(executed.error.code, "BCV013");
427 EXPECT_EQ(executed.error.message, "division by zero");
428 EXPECT_EQ(executed.error.function_name, "explode");
429 EXPECT_NE(executed.error.pc, compiler_bytecode_invalid_id());
430}
Simple portable VM for executing the register bytecode in Bytecode.H.
size_t size_t int32_t value
Definition ca-c-api.h:116
Arena-backed ownership context for bytecode functions and modules.
Definition Bytecode.H:254
Lowers explicit IR into register-based bytecode.
Definition Bytecode.H:581
Small portable VM for executing one Compiler_Bytecode_Module.
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.
#define TEST(name)
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
Compiler_SSA_Block & block(Compiler_SSA_Function &function, const Compiler_SSA_Block_Id id)
Definition SSA.H:636
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
void exit(const char *file, int line, const char *format,...)
Print a message and exit the program.
Definition ahDefs.C:125
constexpr size_t compiler_ir_invalid_id() noexcept
Returns the sentinel invalid IR id.
double add(double a, double b)
double div(double a, double b)
static Compiler_Bytecode_Value make_bool(const bool value)
Builds a boolean runtime value.
One basic block of IR instructions.
std::string label
Deterministic debug label.
Compiler_IR_Terminator terminator
Explicit block terminator.
DynArray< Compiler_IR_Instruction > instructions
Linear instruction list.
Compiler_IR_Block_Id id
Stable block id.
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_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.
One instruction producing an optional explicit result value.
std::string text
Constant spelling or debug payload.
Compiler_IR_Instruction_Kind kind
Instruction category.
Compiler_IR_Value_Id result_id
Produced value id, or 0 for void instructions.
DynArray< Compiler_IR_Value_Id > operands
Input values in deterministic order.
Compiler_IR_Local_Slot_Id local_slot_id
Referenced local/parameter slot, when relevant.
Lowered IR module with shared global slots and functions.
One storage slot used by the IR.
std::string name
Debug name of the slot.
size_t id
Stable slot id within its storage space.
Compiler_IR_Slot_Kind kind
Slot storage category.
Compiler_IR_Terminator_Kind kind
Terminator category.
DynArray< Compiler_IR_Block_Id > successors
Successor blocks in deterministic order.
Compiler_IR_Value_Id condition_value
Branch condition value, if any.
Compiler_IR_Value_Id return_value
Return value, if any.
Compiler_SSA_Block_Id id
Dense SSA block id.
Definition SSA.H:163
std::string label
Deterministic debug label.
Definition SSA.H:165
Compiler_SSA_Terminator terminator
Explicit block terminator.
Definition SSA.H:169
Compiler_IR_Terminator_Kind kind
Terminator category.
Definition SSA.H:153