Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ssa_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 <SSA.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.name = "choose";
59 function.entry_block = 0;
60 function.exit_block = 1;
61 function.next_value_id = 5;
62
64 flag.id = 0;
65 flag.kind = Compiler_IR_Slot_Kind::Parameter;
66 flag.name = "flag";
67 function.local_slots.append(flag);
68
70 value.id = 1;
71 value.kind = Compiler_IR_Slot_Kind::Local;
72 value.name = "value";
73 function.local_slots.append(value);
74
76 entry.id = 0;
77 entry.label = "entry";
78
80 zero.kind = Compiler_IR_Instruction_Kind::Constant;
81 zero.result_id = 1;
82 zero.text = "0";
83 entry.instructions.append(zero);
84
86 init_store.kind = Compiler_IR_Instruction_Kind::Store;
87 init_store.local_slot_id = 1;
88 init_store.operands.append(1);
89 entry.instructions.append(init_store);
90
92 load_flag.kind = Compiler_IR_Instruction_Kind::Load;
93 load_flag.result_id = 2;
94 load_flag.local_slot_id = 0;
95 entry.instructions.append(load_flag);
96
97 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
101
102 auto exit = make_exit_block();
103 exit.predecessors.append(3);
104
106 then_block.id = 2;
107 then_block.label = "if.then.0";
108
110 one.kind = Compiler_IR_Instruction_Kind::Constant;
111 one.result_id = 3;
112 one.text = "1";
113 then_block.instructions.append(one);
114
116 then_store.kind = Compiler_IR_Instruction_Kind::Store;
117 then_store.local_slot_id = 1;
118 then_store.operands.append(3);
119 then_block.instructions.append(then_store);
120
121 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
122 then_block.terminator.successors.append(3);
123 then_block.predecessors.append(0);
124
126 join_block.id = 3;
127 join_block.label = "if.end.0";
128
130 load_value.kind = Compiler_IR_Instruction_Kind::Load;
131 load_value.result_id = 5;
132 load_value.local_slot_id = 1;
133 join_block.instructions.append(load_value);
134
135 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
136 join_block.terminator.return_value = 5;
137 join_block.terminator.successors.append(1);
138 join_block.predecessors.append(2);
139 join_block.predecessors.append(4);
140
142 else_block.id = 4;
143 else_block.label = "if.else.0";
144
146 two.kind = Compiler_IR_Instruction_Kind::Constant;
147 two.result_id = 4;
148 two.text = "2";
149 else_block.instructions.append(two);
150
152 else_store.kind = Compiler_IR_Instruction_Kind::Store;
153 else_store.local_slot_id = 1;
154 else_store.operands.append(4);
155 else_block.instructions.append(else_store);
156
157 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
158 else_block.terminator.successors.append(3);
159 else_block.predecessors.append(0);
160
161 function.blocks.append(entry);
162 function.blocks.append(exit);
163 function.blocks.append(then_block);
164 function.blocks.append(join_block);
165 function.blocks.append(else_block);
166 return function;
167 }
168
171 {
172 Compiler_IR_Function function;
173 function.name = "loop";
174 function.entry_block = 0;
175 function.exit_block = 1;
176 function.next_value_id = 6;
177
178 Compiler_IR_Slot flag;
179 flag.id = 0;
180 flag.kind = Compiler_IR_Slot_Kind::Parameter;
181 flag.name = "flag";
182 function.local_slots.append(flag);
183
185 i_slot.id = 1;
186 i_slot.kind = Compiler_IR_Slot_Kind::Local;
187 i_slot.name = "i";
188 function.local_slots.append(i_slot);
189
190 Compiler_IR_Block entry;
191 entry.id = 0;
192 entry.label = "entry";
193
195 zero.kind = Compiler_IR_Instruction_Kind::Constant;
196 zero.result_id = 1;
197 zero.text = "0";
198 entry.instructions.append(zero);
199
201 init_store.kind = Compiler_IR_Instruction_Kind::Store;
202 init_store.local_slot_id = 1;
203 init_store.operands.append(1);
204 entry.instructions.append(init_store);
205
206 entry.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
208
209 auto exit = make_exit_block();
210 exit.predecessors.append(4);
211
212 Compiler_IR_Block header;
213 header.id = 2;
214 header.label = "while.cond.0";
215
217 load_flag.kind = Compiler_IR_Instruction_Kind::Load;
218 load_flag.result_id = 2;
219 load_flag.local_slot_id = 0;
220 header.instructions.append(load_flag);
221
222 header.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
223 header.terminator.condition_value = 2;
224 header.terminator.successors.append(3);
225 header.terminator.successors.append(4);
226 header.predecessors.append(0);
227 header.predecessors.append(3);
228
230 body.id = 3;
231 body.label = "while.body.0";
232
234 load_i.kind = Compiler_IR_Instruction_Kind::Load;
235 load_i.result_id = 3;
236 load_i.local_slot_id = 1;
237 body.instructions.append(load_i);
238
240 one.kind = Compiler_IR_Instruction_Kind::Constant;
241 one.result_id = 4;
242 one.text = "1";
243 body.instructions.append(one);
244
246 add.kind = Compiler_IR_Instruction_Kind::Binary;
247 add.result_id = 5;
248 add.op = Compiler_Operator_Kind::Plus;
249 add.operands.append(3);
250 add.operands.append(4);
251 body.instructions.append(add);
252
254 store_i.kind = Compiler_IR_Instruction_Kind::Store;
255 store_i.local_slot_id = 1;
256 store_i.operands.append(5);
257 body.instructions.append(store_i);
258
259 body.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
261 body.predecessors.append(2);
262
264 end_block.id = 4;
265 end_block.label = "while.end.0";
266
268 load_final.kind = Compiler_IR_Instruction_Kind::Load;
269 load_final.result_id = 6;
270 load_final.local_slot_id = 1;
271 end_block.instructions.append(load_final);
272
273 end_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
274 end_block.terminator.return_value = 6;
275 end_block.terminator.successors.append(1);
276 end_block.predecessors.append(2);
277
278 function.blocks.append(entry);
279 function.blocks.append(exit);
280 function.blocks.append(header);
281 function.blocks.append(body);
282 function.blocks.append(end_block);
283 return function;
284 }
285}
286
288{
289 Compiler_SSA_Context ctx(1 << 16);
291
292 Compiler_IR_Module module;
293 auto source = make_diamond_ir();
294 module.functions.append(&source);
295
296 const auto * ssa = lowering.lower_module(&module);
297 ASSERT_EQ(ssa->functions.size(), 1u);
298 const auto & function = *ssa->functions.access(0);
299
300 const auto report = validate_ssa_function(function, ssa);
301 ASSERT_TRUE(report.valid);
302 ASSERT_EQ(function.blocks.size(), 5u);
303 EXPECT_EQ(function.dominance.immediate_dominators.access(0), compiler_ssa_invalid_id());
304 EXPECT_EQ(function.dominance.immediate_dominators.access(2), 0u);
305 EXPECT_EQ(function.dominance.immediate_dominators.access(4), 0u);
306 EXPECT_EQ(function.dominance.immediate_dominators.access(3), 0u);
307 EXPECT_EQ(function.dominance.immediate_dominators.access(1), 3u);
308 ASSERT_EQ(function.dominance.dominance_frontiers.access(2).size(), 1u);
309 EXPECT_EQ(function.dominance.dominance_frontiers.access(2).access(0), 3u);
310 ASSERT_EQ(function.dominance.dominance_frontiers.access(4).size(), 1u);
311 EXPECT_EQ(function.dominance.dominance_frontiers.access(4).access(0), 3u);
312}
313
315{
316 Compiler_SSA_Context ctx(1 << 16);
318
319 Compiler_IR_Module module;
320 auto source = make_diamond_ir();
321 module.functions.append(&source);
322
323 const auto * ssa = lowering.lower_module(&module);
324 const auto & function = *ssa->functions.access(0);
325
326 const auto report = validate_ssa_function(function, ssa);
327 ASSERT_TRUE(report.valid);
328
329 ASSERT_EQ(function.parameters.size(), 1u);
330 EXPECT_EQ(function.parameters.access(0).slot_id, 0u);
331
332 const auto & entry = function.blocks.access(0);
333 ASSERT_EQ(entry.instructions.size(), 1u);
334 EXPECT_EQ(entry.instructions.access(0).kind,
335 Compiler_SSA_Instruction_Kind::Constant);
336 EXPECT_EQ(entry.terminator.kind, Compiler_IR_Terminator_Kind::Branch);
337 EXPECT_EQ(entry.terminator.condition_value, function.parameters.access(0).value_id);
338
339 const auto & join = function.blocks.access(3);
340 ASSERT_EQ(join.phis.size(), 1u);
341 EXPECT_EQ(join.phis.access(0).slot_id, 1u);
342 ASSERT_EQ(join.phis.access(0).operands.size(), 2u);
343 EXPECT_EQ(join.instructions.size(), 0u);
344 EXPECT_EQ(join.terminator.kind, Compiler_IR_Terminator_Kind::Return);
345 EXPECT_EQ(join.terminator.return_value, join.phis.access(0).result_id);
346
347 const auto dump = compiler_dump_ssa_function(&function, ssa);
348 EXPECT_NE(dump.find("Phi s1"), std::string::npos);
349}
350
352{
353 Compiler_SSA_Context ctx(1 << 16);
355
356 Compiler_IR_Module module;
357 auto source = make_loop_ir();
358 module.functions.append(&source);
359
360 const auto * ssa = lowering.lower_module(&module);
361 const auto & function = *ssa->functions.access(0);
362
363 const auto report = validate_ssa_function(function, ssa);
364 ASSERT_TRUE(report.valid);
365 ASSERT_EQ(function.blocks.size(), 5u);
366
367 const auto & header = function.blocks.access(2);
368 ASSERT_EQ(header.phis.size(), 1u);
369 EXPECT_EQ(header.phis.access(0).slot_id, 1u);
370 ASSERT_EQ(header.phis.access(0).operands.size(), 2u);
371 EXPECT_NE(header.phis.access(0).operands.access(0), 0u);
372 EXPECT_NE(header.phis.access(0).operands.access(1), 0u);
373 EXPECT_EQ(header.terminator.kind, Compiler_IR_Terminator_Kind::Branch);
374 EXPECT_EQ(header.terminator.condition_value, function.parameters.access(0).value_id);
375
376 const auto & body = function.blocks.access(3);
377 ASSERT_EQ(body.instructions.size(), 2u);
378 EXPECT_EQ(body.instructions.access(0).kind,
379 Compiler_SSA_Instruction_Kind::Constant);
380 EXPECT_EQ(body.instructions.access(1).kind,
381 Compiler_SSA_Instruction_Kind::Binary);
382
383 const auto & end = function.blocks.access(4);
384 EXPECT_EQ(end.instructions.size(), 0u);
385 EXPECT_EQ(end.terminator.kind, Compiler_IR_Terminator_Kind::Return);
386 EXPECT_EQ(end.terminator.return_value, header.phis.access(0).result_id);
387}
Static single assignment form over Compiler_IR.H.
size_t size_t int32_t value
Definition ca-c-api.h:116
Arena-backed ownership context for SSA functions and modules.
Definition SSA.H:223
Lowers non-SSA IR to SSA form for all reachable blocks.
Definition SSA.H:823
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
Compiler_SSA_Validation_Report validate_ssa_function(const Compiler_SSA_Function &function, const Compiler_SSA_Module *module=nullptr)
Validates one SSA function structurally and semantically.
Definition SSA.H:996
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_ssa_invalid_id() noexcept
Returns the sentinel invalid SSA id.
Definition SSA.H:73
std::string compiler_dump_ssa_function(const Compiler_SSA_Function *function, const Compiler_SSA_Module *module=nullptr, const Compiler_Type_Context *types=nullptr)
Dumps one SSA function deterministically.
Definition SSA.H:1254
std::ostream & join(const C &c, const std::string &sep, std::ostream &out)
Join elements of an Aleph-style container into a stream.
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_Block_Id > predecessors
Predecessor block ids.
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.
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.
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_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