Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
compiler_dataflow_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 <climits>
37
38#include <gtest/gtest.h>
39
40#include <Compiler_Dataflow.H>
41
42using namespace Aleph;
43
44namespace
45{
48 {
50 block.id = 1;
51 block.label = "exit";
52 block.terminator.kind = Compiler_IR_Terminator_Kind::Exit;
53 return block;
54 }
55
58 {
59 Compiler_IR_Function function;
60 function.name = "manual";
61 function.entry_block = 0;
62 function.exit_block = 1;
63 function.next_value_id = 2;
64
66 slot.id = 0;
67 slot.kind = Compiler_IR_Slot_Kind::Local;
68 slot.name = "x";
69 function.local_slots.append(slot);
70
72 entry.id = 0;
73 entry.label = "entry";
74
76 load.kind = Compiler_IR_Instruction_Kind::Load;
77 load.result_id = 1;
78 load.local_slot_id = 0;
79 entry.instructions.append(load);
80
82 constant.kind = Compiler_IR_Instruction_Kind::Constant;
83 constant.result_id = 2;
84 constant.text = "1";
85 entry.instructions.append(constant);
86
88 store.kind = Compiler_IR_Instruction_Kind::Store;
89 store.local_slot_id = 0;
90 store.operands.append(2);
91 entry.instructions.append(store);
92
93 entry.terminator.kind = Compiler_IR_Terminator_Kind::Return;
94 entry.terminator.return_value = 1;
96
97 auto exit = make_exit_block();
98 exit.predecessors.append(0);
99
100 function.blocks.append(entry);
101 function.blocks.append(exit);
102 return function;
103 }
104
107 {
108 Compiler_IR_Function function;
109 function.name = "choose";
110 function.entry_block = 0;
111 function.exit_block = 1;
112 function.next_value_id = 5;
113
114 Compiler_IR_Slot flag;
115 flag.id = 0;
116 flag.kind = Compiler_IR_Slot_Kind::Parameter;
117 flag.name = "flag";
118 function.local_slots.append(flag);
119
121 value.id = 1;
122 value.kind = Compiler_IR_Slot_Kind::Local;
123 value.name = "value";
124 function.local_slots.append(value);
125
126 Compiler_IR_Block entry;
127 entry.id = 0;
128 entry.label = "entry";
129
131 c0.kind = Compiler_IR_Instruction_Kind::Constant;
132 c0.result_id = 1;
133 c0.text = "0";
134 entry.instructions.append(c0);
135
137 store0.kind = Compiler_IR_Instruction_Kind::Store;
138 store0.local_slot_id = 1;
139 store0.operands.append(1);
140 entry.instructions.append(store0);
141
143 load_flag.kind = Compiler_IR_Instruction_Kind::Load;
144 load_flag.result_id = 2;
145 load_flag.local_slot_id = 0;
146 entry.instructions.append(load_flag);
147
148 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
149 entry.terminator.condition_value = 2;
152
153 auto exit = make_exit_block();
154 exit.predecessors.append(3);
155
157 then_block.id = 2;
158 then_block.label = "if.then.0";
159
161 c1.kind = Compiler_IR_Instruction_Kind::Constant;
162 c1.result_id = 3;
163 c1.text = "1";
164 then_block.instructions.append(c1);
165
167 store1.kind = Compiler_IR_Instruction_Kind::Store;
168 store1.local_slot_id = 1;
169 store1.operands.append(3);
170 then_block.instructions.append(store1);
171
172 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
173 then_block.terminator.successors.append(3);
174 then_block.predecessors.append(0);
175
177 join_block.id = 3;
178 join_block.label = "if.end.0";
179
181 load_value.kind = Compiler_IR_Instruction_Kind::Load;
182 load_value.result_id = 5;
183 load_value.local_slot_id = 1;
184 join_block.instructions.append(load_value);
185
186 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
187 join_block.terminator.return_value = 5;
188 join_block.terminator.successors.append(1);
189 join_block.predecessors.append(2);
190 join_block.predecessors.append(4);
191
193 else_block.id = 4;
194 else_block.label = "if.else.0";
195
197 c2.kind = Compiler_IR_Instruction_Kind::Constant;
198 c2.result_id = 4;
199 c2.text = "2";
200 else_block.instructions.append(c2);
201
203 store2.kind = Compiler_IR_Instruction_Kind::Store;
204 store2.local_slot_id = 1;
205 store2.operands.append(4);
206 else_block.instructions.append(store2);
207
208 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
209 else_block.terminator.successors.append(3);
210 else_block.predecessors.append(0);
211
212 function.blocks.append(entry);
213 function.blocks.append(exit);
214 function.blocks.append(then_block);
215 function.blocks.append(join_block);
216 function.blocks.append(else_block);
217 return function;
218 }
219
222 {
223 Compiler_IR_Function function;
224 function.name = "simplify";
225 function.entry_block = 0;
226 function.exit_block = 1;
227 function.next_value_id = 5;
228
230 slot.id = 0;
231 slot.kind = Compiler_IR_Slot_Kind::Local;
232 slot.name = "x";
233 function.local_slots.append(slot);
234
235 Compiler_IR_Block entry;
236 entry.id = 0;
237 entry.label = "entry";
238
240 c1.kind = Compiler_IR_Instruction_Kind::Constant;
241 c1.result_id = 1;
242 c1.text = "1";
243 entry.instructions.append(c1);
244
246 store1.kind = Compiler_IR_Instruction_Kind::Store;
247 store1.local_slot_id = 0;
248 store1.operands.append(1);
249 entry.instructions.append(store1);
250
252 cond.kind = Compiler_IR_Instruction_Kind::Constant;
253 cond.result_id = 2;
254 cond.text = "true";
255 cond.bool_value = true;
256 entry.instructions.append(cond);
257
258 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
259 entry.terminator.condition_value = 2;
262
263 auto exit = make_exit_block();
264 exit.predecessors.append(3);
265
267 then_block.id = 2;
268 then_block.label = "if.then.0";
269
271 c2.kind = Compiler_IR_Instruction_Kind::Constant;
272 c2.result_id = 3;
273 c2.text = "2";
274 then_block.instructions.append(c2);
275
277 store2.kind = Compiler_IR_Instruction_Kind::Store;
278 store2.local_slot_id = 0;
279 store2.operands.append(3);
280 then_block.instructions.append(store2);
281
282 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
283 then_block.terminator.successors.append(3);
284 then_block.predecessors.append(0);
285
287 join_block.id = 3;
288 join_block.label = "if.end.0";
289
291 load_value.kind = Compiler_IR_Instruction_Kind::Load;
292 load_value.result_id = 5;
293 load_value.local_slot_id = 0;
294 join_block.instructions.append(load_value);
295
296 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
297 join_block.terminator.return_value = 5;
298 join_block.terminator.successors.append(1);
299 join_block.predecessors.append(2);
300 join_block.predecessors.append(4);
301
303 else_block.id = 4;
304 else_block.label = "if.else.0";
305
307 c3.kind = Compiler_IR_Instruction_Kind::Constant;
308 c3.result_id = 4;
309 c3.text = "3";
310 else_block.instructions.append(c3);
311
313 store3.kind = Compiler_IR_Instruction_Kind::Store;
314 store3.local_slot_id = 0;
315 store3.operands.append(4);
316 else_block.instructions.append(store3);
317
318 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
319 else_block.terminator.successors.append(3);
320 else_block.predecessors.append(0);
321
322 function.blocks.append(entry);
323 function.blocks.append(exit);
324 function.blocks.append(then_block);
325 function.blocks.append(join_block);
326 function.blocks.append(else_block);
327 return function;
328 }
329
332 {
333 Compiler_IR_Function function;
334 function.name = "stale_preds";
335 function.entry_block = 0;
336 function.exit_block = 1;
337 function.next_value_id = 4;
338
340 slot.id = 0;
341 slot.kind = Compiler_IR_Slot_Kind::Local;
342 slot.name = "x";
343 function.local_slots.append(slot);
344
345 Compiler_IR_Block entry;
346 entry.id = 0;
347 entry.label = "entry";
348
350 cond.kind = Compiler_IR_Instruction_Kind::Constant;
351 cond.result_id = 1;
352 cond.text = "true";
353 cond.bool_value = true;
354 entry.instructions.append(cond);
355
356 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
357 entry.terminator.condition_value = 1;
360
361 auto exit = make_exit_block();
362 exit.predecessors.append(4);
363
365 then_block.id = 2;
366 then_block.label = "then";
367 then_block.predecessors.append(99);
368
370 c1.kind = Compiler_IR_Instruction_Kind::Constant;
371 c1.result_id = 2;
372 c1.text = "1";
373 then_block.instructions.append(c1);
374
376 s1.kind = Compiler_IR_Instruction_Kind::Store;
377 s1.local_slot_id = 0;
378 s1.operands.append(2);
379 then_block.instructions.append(s1);
380
381 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
382 then_block.terminator.successors.append(4);
383
385 else_block.id = 3;
386 else_block.label = "else";
387 else_block.predecessors.clear();
388
390 c2.kind = Compiler_IR_Instruction_Kind::Constant;
391 c2.result_id = 3;
392 c2.text = "2";
393 else_block.instructions.append(c2);
394
396 s2.kind = Compiler_IR_Instruction_Kind::Store;
397 s2.local_slot_id = 0;
398 s2.operands.append(3);
399 else_block.instructions.append(s2);
400
401 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
402 else_block.terminator.successors.append(4);
403
405 join_block.id = 4;
406 join_block.label = "join";
407 join_block.predecessors.clear();
408
410 load_x.kind = Compiler_IR_Instruction_Kind::Load;
411 load_x.result_id = 4;
412 load_x.local_slot_id = 0;
413 join_block.instructions.append(load_x);
414
415 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
416 join_block.terminator.return_value = 4;
417 join_block.terminator.successors.append(1);
418
419 function.blocks.append(entry);
420 function.blocks.append(exit);
421 function.blocks.append(then_block);
422 function.blocks.append(else_block);
423 function.blocks.append(join_block);
424 return function;
425 }
426
429 {
430 Compiler_IR_Function function;
431 function.name = "spin";
432 function.entry_block = 0;
433 function.exit_block = 1;
434
435 Compiler_IR_Block entry;
436 entry.id = 0;
437 entry.label = "entry";
438 entry.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
440
441 auto exit = make_exit_block();
442
444 loop.id = 2;
445 loop.label = "loop";
446 loop.predecessors.append(0);
447 loop.predecessors.append(2);
448 loop.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
450
451 function.blocks.append(entry);
452 function.blocks.append(exit);
453 function.blocks.append(loop);
454 return function;
455 }
456}
457
458// ---------------------------------------------------------------------------
459// Helpers that build minimal IR functions for integer-constant tests.
460// ---------------------------------------------------------------------------
461namespace
462{
463 // Build a branch-on-constant function where the branch condition is a
464 // known boolean; after DCE the dead arm should be removed and live_out
465 // for slot 0 should only reflect the taken successor.
466 //
467 // entry: store 0 -> x; cond = <flag>; branch cond -> then[2] / else[4]
468 // then[2]: store 99 -> x; jump join[3]
469 // else[4]: store -1 -> x; jump join[3] <-- dead when flag==true
470 // join[3]: load x; return x
471 // exit[1]
474 {
475 Compiler_IR_Function function;
476 function.name = "branch_fold";
477 function.entry_block = 0;
478 function.exit_block = 1;
479 function.next_value_id = 7;
480
482 slot.id = 0;
483 slot.kind = Compiler_IR_Slot_Kind::Local;
484 slot.name = "x";
485 function.local_slots.append(slot);
486
487 Compiler_IR_Block entry;
488 entry.id = 0;
489 entry.label = "entry";
490
492 c0.kind = Compiler_IR_Instruction_Kind::Constant;
493 c0.result_id = 1;
494 c0.text = "0";
495 entry.instructions.append(c0);
496
498 s0.kind = Compiler_IR_Instruction_Kind::Store;
499 s0.local_slot_id = 0;
500 s0.operands.append(1);
501 entry.instructions.append(s0);
502
504 cond.kind = Compiler_IR_Instruction_Kind::Constant;
505 cond.result_id = 2;
506 cond.text = flag_value ? "true" : "false";
507 cond.bool_value = flag_value;
508 entry.instructions.append(cond);
509
510 entry.terminator.kind = Compiler_IR_Terminator_Kind::Branch;
511 entry.terminator.condition_value = 2;
512 entry.terminator.successors.append(2); // then
513 entry.terminator.successors.append(4); // else
514
516 exit_blk.id = 1;
517 exit_blk.label = "exit";
518 exit_blk.terminator.kind = Compiler_IR_Terminator_Kind::Exit;
519 exit_blk.predecessors.append(3);
520
522 then_block.id = 2;
523 then_block.label = "if.then.0";
524 then_block.predecessors.append(0);
525
527 c99.kind = Compiler_IR_Instruction_Kind::Constant;
528 c99.result_id = 3;
529 c99.text = "99";
530 then_block.instructions.append(c99);
531
533 s99.kind = Compiler_IR_Instruction_Kind::Store;
534 s99.local_slot_id = 0;
535 s99.operands.append(3);
536 then_block.instructions.append(s99);
537
538 then_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
539 then_block.terminator.successors.append(3);
540
542 join_block.id = 3;
543 join_block.label = "if.end.0";
544 join_block.predecessors.append(2);
545 join_block.predecessors.append(4);
546
548 load_x.kind = Compiler_IR_Instruction_Kind::Load;
549 load_x.result_id = 6;
550 load_x.local_slot_id = 0;
551 join_block.instructions.append(load_x);
552
553 join_block.terminator.kind = Compiler_IR_Terminator_Kind::Return;
554 join_block.terminator.return_value = 6;
555 join_block.terminator.successors.append(1);
556
558 else_block.id = 4;
559 else_block.label = "if.else.0";
560 else_block.predecessors.append(0);
561
563 cm1.kind = Compiler_IR_Instruction_Kind::Constant;
564 cm1.result_id = 5;
565 cm1.text = "-1";
566 else_block.instructions.append(cm1);
567
569 sm1.kind = Compiler_IR_Instruction_Kind::Store;
570 sm1.local_slot_id = 0;
571 sm1.operands.append(5);
572 else_block.instructions.append(sm1);
573
574 else_block.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
575 else_block.terminator.successors.append(3);
576
577 function.blocks.append(entry);
578 function.blocks.append(exit_blk);
579 function.blocks.append(then_block);
580 function.blocks.append(join_block);
581 function.blocks.append(else_block);
582 return function;
583 }
584} // namespace
585
586// ---------------------------------------------------------------------------
587// parse_integer_constant boundary tests
588// ---------------------------------------------------------------------------
589
590// parse_integer_constant is a free function in Compiler_Dataflow_Detail.
591// Expose it through a thin wrapper to avoid repeating the namespace.
592namespace
593{
594 bool test_parse_int(const std::string &text, long long &value)
595 {
597 }
598
600 test_unary(Compiler_Operator_Kind op, long long v)
601 {
603 operand.kind = Compiler_Dataflow_Constant_Kind::Integer;
604 operand.integer_value = v;
606 }
607
609 test_binary(Compiler_Operator_Kind op, long long lv, long long rv)
610 {
612 lhs.kind = rhs.kind = Compiler_Dataflow_Constant_Kind::Integer;
613 lhs.integer_value = lv;
614 rhs.integer_value = rv;
616 }
617} // namespace
618
625
632
634{
635 long long value = 0;
636 EXPECT_FALSE(test_parse_int("9223372036854775808", value)); // LLONG_MAX + 1
637}
638
644
645// ---------------------------------------------------------------------------
646// evaluate_unary: -LLONG_MIN must NOT fold (overflow)
647// ---------------------------------------------------------------------------
648
650{
651 const auto result = test_unary(Compiler_Operator_Kind::Minus, LLONG_MIN);
652 EXPECT_EQ(result.kind, Compiler_Dataflow_Constant_Kind::Unknown);
653}
654
656{
657 const auto result = test_unary(Compiler_Operator_Kind::Minus, 42);
658 EXPECT_EQ(result.kind, Compiler_Dataflow_Constant_Kind::Integer);
659 EXPECT_EQ(result.integer_value, -42);
660}
661
662// ---------------------------------------------------------------------------
663// evaluate_binary: LLONG_MIN / -1 and LLONG_MIN % -1 must NOT fold
664// ---------------------------------------------------------------------------
665
667{
668 const auto result = test_binary(Compiler_Operator_Kind::Slash, LLONG_MIN, -1);
669 EXPECT_EQ(result.kind, Compiler_Dataflow_Constant_Kind::Unknown);
670}
671
673{
674 const auto result = test_binary(Compiler_Operator_Kind::Percent, LLONG_MIN, -1);
675 EXPECT_EQ(result.kind, Compiler_Dataflow_Constant_Kind::Unknown);
676}
677
679{
680 const auto result = test_binary(Compiler_Operator_Kind::Plus, LLONG_MAX, 1);
681 EXPECT_EQ(result.kind, Compiler_Dataflow_Constant_Kind::Unknown);
682}
683
685{
686 const auto result = test_binary(Compiler_Operator_Kind::Star, LLONG_MIN, 1);
687 EXPECT_EQ(result.kind, Compiler_Dataflow_Constant_Kind::Integer);
688 EXPECT_EQ(result.integer_value, LLONG_MIN);
689}
690
692{
693 // LLONG_MAX * 2 overflows: portable fallback must return Unknown.
694 auto r = test_binary(Compiler_Operator_Kind::Star, LLONG_MAX, 2);
695 EXPECT_EQ(r.kind, Compiler_Dataflow_Constant_Kind::Unknown);
696
697 // LLONG_MIN * 2 overflows (negative side).
698 r = test_binary(Compiler_Operator_Kind::Star, LLONG_MIN, 2);
699 EXPECT_EQ(r.kind, Compiler_Dataflow_Constant_Kind::Unknown);
700
701 // LLONG_MAX * -2 overflows.
702 r = test_binary(Compiler_Operator_Kind::Star, LLONG_MAX, -2);
703 EXPECT_EQ(r.kind, Compiler_Dataflow_Constant_Kind::Unknown);
704}
705
706// ---------------------------------------------------------------------------
707// Branch-fold live_out liveness preservation
708// ---------------------------------------------------------------------------
709
711{
712 // With flag=true the "then" arm (stores 99) is taken; the "else" arm is dead.
713 // After DCE: slot 0 must still be live going into the join block (so the
714 // load of x at join is not erroneously eliminated as a dead store).
715 const auto function = make_branch_fold_liveout_ir(true);
716 const auto ir_report = validate_ir_function(function);
717 ASSERT_TRUE(ir_report.valid);
718
719 const auto optimized = eliminate_dead_code(function);
721 ASSERT_TRUE(report.valid);
722
723 EXPECT_EQ(optimized.folded_branches, 1u);
724 EXPECT_GT(optimized.removed_blocks, 0u);
725
726 // The join block must survive and its load of x must be kept.
727 bool found_join = false;
728 for (size_t i = 0; i < optimized.function.blocks.size(); ++i)
729 {
730 const auto &blk = optimized.function.blocks.access(i);
731 if (blk.label == "if.end.0")
732 {
733 found_join = true;
734 bool has_load = false;
735 for (size_t j = 0; j < blk.instructions.size(); ++j)
736 if (blk.instructions.access(j).kind == Compiler_IR_Instruction_Kind::Load)
737 has_load = true;
738 EXPECT_TRUE(has_load) << "load of x must survive in join block";
739 }
740 }
741 EXPECT_TRUE(found_join) << "join block must survive branch fold";
742
743 // The else arm must be gone.
744 for (size_t i = 0; i < optimized.function.blocks.size(); ++i)
745 EXPECT_NE(optimized.function.blocks.access(i).label, "if.else.0")
746 << "dead else block must be removed";
747}
748
750{
751 // With flag=false the "else" arm is taken; "then" arm is dead.
752 const auto function = make_branch_fold_liveout_ir(false);
753 const auto ir_report = validate_ir_function(function);
754 ASSERT_TRUE(ir_report.valid);
755
756 const auto optimized = eliminate_dead_code(function);
758
759 EXPECT_EQ(optimized.folded_branches, 1u);
760
761 for (size_t i = 0; i < optimized.function.blocks.size(); ++i)
762 EXPECT_NE(optimized.function.blocks.access(i).label, "if.then.0")
763 << "dead then block must be removed";
764}
765
767{
768 const auto function = make_manual_uninitialized_ir();
769 const auto ir_report = validate_ir_function(function);
770 ASSERT_TRUE(ir_report.valid);
771
772 const auto analysis = analyze_dataflow_function(function);
773 const auto report = validate_dataflow_analysis(function, analysis);
774 ASSERT_TRUE(report.valid);
775 ASSERT_EQ(analysis.uninitialized_reads.size(), 1u);
776 EXPECT_EQ(analysis.uninitialized_reads.access(0).block_id, 0u);
777 EXPECT_EQ(analysis.uninitialized_reads.access(0).instruction_index, 0u);
778 EXPECT_EQ(analysis.uninitialized_reads.access(0).slot_id, 0u);
779
780 const auto dump = compiler_dump_dataflow_analysis(&function, analysis);
781 EXPECT_NE(dump.find("UninitializedReads:"), std::string::npos);
782 EXPECT_NE(dump.find("B0/I0 -> s0"), std::string::npos);
783}
784
786{
787 const auto function = make_branching_ir();
788 const auto ir_report = validate_ir_function(function);
789 ASSERT_TRUE(ir_report.valid);
790
791 const auto analysis = analyze_dataflow_function(function);
792 const auto report = validate_dataflow_analysis(function, analysis);
793 ASSERT_TRUE(report.valid);
794
795 ASSERT_EQ(function.blocks.size(), 5u);
796 EXPECT_TRUE(analysis.reachable_blocks.access(0));
797 EXPECT_TRUE(analysis.reachable_blocks.access(1));
798 EXPECT_TRUE(analysis.reachable_blocks.access(2));
799 EXPECT_TRUE(analysis.reachable_blocks.access(3));
800 EXPECT_TRUE(analysis.reachable_blocks.access(4));
801 EXPECT_EQ(analysis.foldable_branch_count, 0u);
802 EXPECT_TRUE(analysis.uninitialized_reads.is_empty());
803
804 ASSERT_EQ(function.local_slots.size(), 2u);
805 EXPECT_TRUE(analysis.assigned_in_slots.access(0).test(0));
806 EXPECT_FALSE(analysis.assigned_in_slots.access(0).test(1));
807 EXPECT_EQ(analysis.constant_out_slots.access(0).access(1).kind,
808 Compiler_Dataflow_Constant_Kind::Integer);
809 EXPECT_EQ(analysis.constant_out_slots.access(0).access(1).integer_value, 0);
810 EXPECT_TRUE(analysis.live_out_slots.access(2).test(1));
811 EXPECT_TRUE(analysis.live_out_slots.access(4).test(1));
812}
813
815{
816 const auto function = make_stale_predecessor_ir();
817 const auto analysis = analyze_dataflow_function(function);
818 const auto report = validate_dataflow_analysis(function, analysis);
819 ASSERT_TRUE(report.valid);
820
821 ASSERT_EQ(analysis.assigned_in_slots.size(), function.blocks.size());
822 EXPECT_TRUE(analysis.assigned_in_slots.access(4).test(0));
823 EXPECT_TRUE(analysis.assigned_out_slots.access(4).test(0));
824}
825
827{
828 const auto function = make_constant_branch_ir();
829 const auto ir_report = validate_ir_function(function);
830 ASSERT_TRUE(ir_report.valid);
831
832 const auto optimized = eliminate_dead_code(function);
834 ASSERT_TRUE(report.valid);
835 EXPECT_EQ(optimized.folded_branches, 1u);
836 EXPECT_GT(optimized.removed_blocks, 0u);
837 EXPECT_GT(optimized.removed_instructions, 0u);
838 ASSERT_EQ(optimized.function.blocks.size(), 4u);
839 EXPECT_EQ(optimized.function.blocks.access(0).terminator.kind,
840 Compiler_IR_Terminator_Kind::Jump);
841 EXPECT_EQ(optimized.function.blocks.access(0).terminator.successors.size(), 1u);
842 EXPECT_EQ(optimized.function.blocks.access(2).label, "if.then.0");
843 EXPECT_EQ(optimized.function.blocks.access(3).label, "if.end.0");
844 for (size_t i = 0; i < optimized.function.blocks.size(); ++i)
845 EXPECT_NE(optimized.function.blocks.access(i).label, "if.else.0");
846}
847
849{
850 const auto function = make_infinite_loop_ir();
851 const auto ir_report = validate_ir_function(function);
852 ASSERT_TRUE(ir_report.valid);
853
854 const auto optimized = eliminate_dead_code(function);
856 ASSERT_TRUE(report.valid);
857
858 EXPECT_NE(optimized.function.exit_block, compiler_ir_invalid_id());
859 ASSERT_LT(optimized.function.exit_block, optimized.function.blocks.size());
860 EXPECT_EQ(optimized.function.blocks.access(optimized.function.exit_block).terminator.kind,
861 Compiler_IR_Terminator_Kind::Exit);
862}
Reusable dataflow analyses and dead-code elimination over Compiler_IR_Model.H.
size_t size_t int32_t value
Definition ca-c-api.h:116
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_Dataflow_Constant evaluate_binary(const Compiler_Operator_Kind op, const Compiler_Dataflow_Constant &lhs, const Compiler_Dataflow_Constant &rhs)
Compiler_Dataflow_Constant evaluate_unary(const Compiler_Operator_Kind op, const Compiler_Dataflow_Constant &operand)
bool parse_integer_constant(const std::string &text, long long &value) noexcept
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_Dataflow_Validation_Report validate_dead_code_elimination(const Compiler_Dead_Code_Elimination_Result &result, const Compiler_IR_Module *module=nullptr)
Validates the result of dead-code elimination.
Compiler_Dataflow_Function_Analysis analyze_dataflow_function(const Compiler_IR_Function &function)
Computes reachability, liveness, definite assignment, and constant propagation for one IR function.
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.
Compiler_Dead_Code_Elimination_Result eliminate_dead_code(const Compiler_IR_Function &function)
Eliminates unreachable blocks, dead pure instructions, dead local stores, and folds constant-conditio...
Compiler_Dataflow_Validation_Report validate_dataflow_analysis(const Compiler_IR_Function &function, const Compiler_Dataflow_Function_Analysis &analysis)
Validates structural invariants of a dataflow result against its source function.
Compiler_IR_Validation_Report validate_ir_function(const Compiler_IR_Function &function, const Compiler_IR_Module *module=nullptr)
Validates one lowered IR function structurally.
std::string compiler_dump_dataflow_analysis(const Compiler_IR_Function *function, const Compiler_Dataflow_Function_Analysis &analysis, const Compiler_Type_Context *types=nullptr)
Produces a deterministic, human-readable dump of a dataflow result.
Compiler_Operator_Kind
Stable operator kinds shared by reusable compiler layers.
One propagated constant value in the local-slot lattice.
Compiler_Dataflow_Constant_Kind kind
Lattice kind.
long long integer_value
Integer payload when kind == Integer.
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.
bool bool_value
Decoded boolean payload for bool constants.
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.
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
gsl_rng * r