Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
bytecode_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
38#include <Bytecode.H>
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 = "add_one";
60 function.entry_block = 0;
61 function.exit_block = 1;
62 function.next_value_id = 4;
63
65 param.id = 0;
66 param.kind = Compiler_IR_Slot_Kind::Parameter;
67 param.name = "x";
68 function.local_slots.append(param);
69
70 Compiler_IR_Slot local;
71 local.id = 1;
72 local.kind = Compiler_IR_Slot_Kind::Local;
73 local.name = "tmp";
74 function.local_slots.append(local);
75
77 entry.id = 0;
78 entry.label = "entry";
79
81 constant.kind = Compiler_IR_Instruction_Kind::Constant;
82 constant.result_id = 1;
83 constant.text = "1";
84 entry.instructions.append(constant);
85
87 store.kind = Compiler_IR_Instruction_Kind::Store;
88 store.local_slot_id = 1;
89 store.operands.append(1);
90 entry.instructions.append(store);
91
93 load_x.kind = Compiler_IR_Instruction_Kind::Load;
94 load_x.result_id = 2;
95 load_x.local_slot_id = 0;
96 entry.instructions.append(load_x);
97
99 load_tmp.kind = Compiler_IR_Instruction_Kind::Load;
100 load_tmp.result_id = 3;
101 load_tmp.local_slot_id = 1;
102 entry.instructions.append(load_tmp);
103
105 add.kind = Compiler_IR_Instruction_Kind::Binary;
106 add.result_id = 4;
107 add.op = Compiler_Operator_Kind::Plus;
108 add.operands.append(2);
109 add.operands.append(3);
110 entry.instructions.append(add);
111
112 entry.terminator.kind = Compiler_IR_Terminator_Kind::Return;
113 entry.terminator.return_value = 4;
115
116 auto exit = make_exit_block();
117 exit.predecessors.append(0);
118
119 function.blocks.append(entry);
120 function.blocks.append(exit);
121 return function;
122 }
123
126 {
127 Compiler_IR_Function function;
128 function.id = 0;
129 function.name = "choose";
130 function.entry_block = 0;
131 function.exit_block = 1;
132 function.next_value_id = 5;
133
134 Compiler_IR_Slot flag;
135 flag.id = 0;
136 flag.kind = Compiler_IR_Slot_Kind::Parameter;
137 flag.name = "flag";
138 function.local_slots.append(flag);
139
141 value.id = 1;
142 value.kind = Compiler_IR_Slot_Kind::Local;
143 value.name = "value";
144 function.local_slots.append(value);
145
146 Compiler_IR_Block entry;
147 entry.id = 0;
148 entry.label = "entry";
149
151 zero.kind = Compiler_IR_Instruction_Kind::Constant;
152 zero.result_id = 1;
153 zero.text = "0";
154 entry.instructions.append(zero);
155
157 init_store.kind = Compiler_IR_Instruction_Kind::Store;
158 init_store.local_slot_id = 1;
159 init_store.operands.append(1);
160 entry.instructions.append(init_store);
161
163 load_flag.kind = Compiler_IR_Instruction_Kind::Load;
164 load_flag.result_id = 2;
165 load_flag.local_slot_id = 0;
166 entry.instructions.append(load_flag);
167
168 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
169 entry.terminator.condition_value = 2;
172
173 auto exit = make_exit_block();
174 exit.predecessors.append(3);
175
177 then_block.id = 2;
178 then_block.label = "if.then.0";
179
181 one.kind = Compiler_IR_Instruction_Kind::Constant;
182 one.result_id = 3;
183 one.text = "1";
184 then_block.instructions.append(one);
185
187 then_store.kind = Compiler_IR_Instruction_Kind::Store;
188 then_store.local_slot_id = 1;
189 then_store.operands.append(3);
190 then_block.instructions.append(then_store);
191
192 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
193 then_block.terminator.successors.append(3);
194 then_block.predecessors.append(0);
195
197 join_block.id = 3;
198 join_block.label = "if.end.0";
199
201 load_value.kind = Compiler_IR_Instruction_Kind::Load;
202 load_value.result_id = 5;
203 load_value.local_slot_id = 1;
204 join_block.instructions.append(load_value);
205
206 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
207 join_block.terminator.return_value = 5;
208 join_block.terminator.successors.append(1);
209 join_block.predecessors.append(2);
210 join_block.predecessors.append(4);
211
213 else_block.id = 4;
214 else_block.label = "if.else.0";
215
217 two.kind = Compiler_IR_Instruction_Kind::Constant;
218 two.result_id = 4;
219 two.text = "2";
220 else_block.instructions.append(two);
221
223 else_store.kind = Compiler_IR_Instruction_Kind::Store;
224 else_store.local_slot_id = 1;
225 else_store.operands.append(4);
226 else_block.instructions.append(else_store);
227
228 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
229 else_block.terminator.successors.append(3);
230 else_block.predecessors.append(0);
231
232 function.blocks.append(entry);
233 function.blocks.append(exit);
234 function.blocks.append(then_block);
235 function.blocks.append(join_block);
236 function.blocks.append(else_block);
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}
302
304{
305 Compiler_Bytecode_Context ctx(1 << 16);
307
308 Compiler_IR_Module module;
309 auto source = make_linear_ir();
310 module.functions.append(&source);
311
312 const auto * bytecode = lowering.lower_module(&module);
313 ASSERT_EQ(bytecode->functions.size(), 1u);
314 const auto & function = *bytecode->functions.access(0);
315
316 const auto report = validate_bytecode_function(function, bytecode);
317 ASSERT_TRUE(report.valid);
318 EXPECT_EQ(function.register_count, 5u);
319 ASSERT_EQ(function.constants.size(), 1u);
320 EXPECT_EQ(function.constants.access(0).kind,
321 Compiler_Bytecode_Constant_Kind::Integer);
322 EXPECT_EQ(function.constants.access(0).integer_value, 1);
323
324 ASSERT_GE(function.code.size(), 6u);
325 EXPECT_EQ(function.code.access(0).opcode,
326 Compiler_Bytecode_Opcode::Load_Constant);
327 EXPECT_EQ(function.code.access(1).opcode,
328 Compiler_Bytecode_Opcode::Store_Local);
329 EXPECT_EQ(function.code.access(2).opcode,
330 Compiler_Bytecode_Opcode::Load_Local);
331 EXPECT_EQ(function.code.access(4).opcode,
332 Compiler_Bytecode_Opcode::Binary);
333 EXPECT_EQ(function.code.access(5).opcode,
334 Compiler_Bytecode_Opcode::Return);
335}
336
338{
339 Compiler_Bytecode_Context ctx(1 << 16);
341
342 Compiler_IR_Module module;
343 auto source = make_branch_ir();
344 module.functions.append(&source);
345
346 const auto * bytecode = lowering.lower_module(&module);
347 const auto & function = *bytecode->functions.access(0);
348
349 const auto report = validate_bytecode_function(function, bytecode);
350 ASSERT_TRUE(report.valid);
351 ASSERT_EQ(function.blocks.size(), 5u);
352
353 const auto & entry_branch = function.code.access(3);
354 EXPECT_EQ(entry_branch.opcode, Compiler_Bytecode_Opcode::Branch);
355 EXPECT_EQ(entry_branch.target_pc, function.blocks.access(2).begin_pc);
356 EXPECT_EQ(entry_branch.false_target_pc, function.blocks.access(4).begin_pc);
357
358 const auto & then_jump = function.code.access(function.blocks.access(2).end_pc - 1);
359 EXPECT_EQ(then_jump.opcode, Compiler_Bytecode_Opcode::Jump);
360 EXPECT_EQ(then_jump.target_pc, function.blocks.access(3).begin_pc);
361}
362
364{
365 Compiler_Bytecode_Context ctx(1 << 16);
367
368 auto module = make_module_with_top_level_call();
369 const auto * bytecode = lowering.lower_module(&module);
370
372 ASSERT_TRUE(report.valid);
373 ASSERT_EQ(bytecode->functions.size(), 1u);
374 ASSERT_NE(bytecode->top_level, nullptr);
375 ASSERT_EQ(bytecode->global_slots.size(), 1u);
376
377 const auto & top = *bytecode->top_level;
378 ASSERT_GE(top.code.size(), 5u);
379 EXPECT_EQ(top.code.access(0).opcode, Compiler_Bytecode_Opcode::Load_Function);
380 EXPECT_EQ(top.code.access(1).opcode, Compiler_Bytecode_Opcode::Load_Constant);
381 EXPECT_EQ(top.code.access(2).opcode, Compiler_Bytecode_Opcode::Call);
382 EXPECT_EQ(top.code.access(3).opcode, Compiler_Bytecode_Opcode::Store_Global);
383 EXPECT_EQ(top.code.access(4).opcode, Compiler_Bytecode_Opcode::Jump);
384
386 EXPECT_NE(dump.find("BytecodeModule"), std::string::npos);
387 EXPECT_NE(dump.find("LoadFunction"), std::string::npos);
388 EXPECT_NE(dump.find("StoreGlobal"), std::string::npos);
389
390 delete module.functions.access(0);
391 delete module.top_level;
392}
Reusable bytecode format and lowering from Compiler_IR_Model.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
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
void exit(const char *file, int line, const char *format,...)
Print a message and exit the program.
Definition ahDefs.C:125
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
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
Compiler_Bytecode_Validation_Report validate_bytecode_module(const Compiler_Bytecode_Module &module)
Validates all bytecode functions in one module.
Definition Bytecode.H:900
double add(double a, double b)
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