Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
SSA.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
53#ifndef SSA_H
54#define SSA_H
55
56#include <limits>
57#include <sstream>
58#include <string>
59#include <utility>
60
61#include <Compiler_Dataflow.H>
62#include <ah-arena.H>
63#include <ah-errors.H>
64#include <tpl_dynArray.H>
65
66namespace Aleph
67{
68 using Compiler_SSA_Value_Id = size_t;
69 using Compiler_SSA_Block_Id = size_t;
70
72 inline constexpr size_t
74 {
75 return std::numeric_limits<size_t>::max();
76 }
77
89
91 inline const char *
93 {
94 switch (kind)
95 {
97 return "Constant";
99 return "LoadGlobal";
101 return "StoreGlobal";
103 return "Unary";
105 return "Binary";
107 return "Call";
109 return "FunctionRef";
110 }
111
112 return "Unknown";
113 }
114
124
134
149
159
178
188
212
220
223 {
225 {
226 void * ptr = nullptr;
227 void (*destroy)(void *) noexcept = nullptr;
228 };
229
232
233 public:
235 explicit Compiler_SSA_Context(const size_t arena_size = AhArenaAllocator::DEFAULT_SIZE)
236 : arena(arena_size)
237 {}
238
241
247
249 template <typename T, typename... Args> T *
251 {
252 T * ptr = allocate<T>(arena, std::forward<Args>(args)...);
253 ah_runtime_error_unless(ptr != nullptr)
254 << "Compiler_SSA_Context: arena allocation failed";
255
256 owned.append({
257 ptr,
258 [](void * raw) noexcept
259 {
260 static_cast<T *>(raw)->~T();
261 }
262 });
263 return ptr;
264 }
265
268 {
269 for (size_t i = owned.size(); i > 0; --i)
270 {
271 auto &[ptr, destroy] = owned.access(i - 1);
272 if (destroy != nullptr and ptr != nullptr)
273 destroy(ptr);
274 }
275 owned.clear();
276 arena.reset();
277 }
278 };
279
287
288 namespace Compiler_SSA_Detail
289 {
295
302
304 {
305 enum class Kind
306 {
307 Invalid,
308 Parameter,
309 Phi,
311 };
312
315 size_t order = 0;
316 };
317
318 inline std::string
320 {
321 return "B" + std::to_string(id);
322 }
323
324 inline std::string
326 {
327 return "v" + std::to_string(id);
328 }
329
330 inline std::string
332 {
333 return "s" + std::to_string(id);
334 }
335
336 inline std::string
338 {
339 return "g" + std::to_string(id);
340 }
341
342 inline std::string
344 {
345 return "f" + std::to_string(id);
346 }
347
348 inline const char *
350 {
351 return compiler_operator_name(kind);
352 }
353
354 inline void
355 append_indented_text(std::ostream & out,
356 const std::string & text,
357 const size_t indent)
358 {
359 const std::string padding(indent, ' ');
360 size_t begin = 0;
361 while (begin < text.size())
362 {
363 const auto end = text.find('\n', begin);
364 if (end == std::string::npos)
365 {
366 out << padding << text.substr(begin) << '\n';
367 return;
368 }
369
370 if (end > begin)
371 out << padding << text.substr(begin, end - begin) << '\n';
372 else
373 out << padding << '\n';
374 begin = end + 1;
375 }
376 }
377
378 inline bool
380 const Compiler_SSA_Block_Id id) noexcept
381 {
382 for (size_t i = 0; i < ids.size(); ++i)
383 if (ids.access(i) == id)
384 return true;
385 return false;
386 }
387
388 inline DynArray<bool>
390 {
392 for (size_t i = 0; i < function.blocks.size(); ++i)
393 reachable.append(false);
394
395 if (function.blocks.is_empty() or function.entry_block >= function.blocks.size())
396 return reachable;
397
399 worklist.append(function.entry_block);
400 while (not worklist.is_empty())
401 {
402 const auto block_id = worklist.pop();
403 if (block_id >= function.blocks.size() or reachable.access(block_id))
404 continue;
405
406 reachable.access(block_id) = true;
407 const auto & block = function.blocks.access(block_id);
408 for (size_t i = 0; i < block.terminator.successors.size(); ++i)
409 {
410 const auto succ = block.terminator.successors.access(i);
411 if (succ < function.blocks.size() and not reachable.access(succ))
412 worklist.append(succ);
413 }
414 }
415
416 return reachable;
417 }
418
419 inline Normalized_IR_Function
421 {
423 normalized.function.id = source.id;
424 normalized.function.name = source.name;
425 normalized.function.span = source.span;
426 normalized.function.type_id = source.type_id;
427 normalized.function.source_function = source.source_function;
428 normalized.function.local_slots = source.local_slots;
429 normalized.function.next_value_id = source.next_value_id;
430
431 const auto reachable = compute_reachable_blocks(source);
433 for (size_t i = 0; i < source.blocks.size(); ++i)
435
436 for (size_t i = 0; i < source.blocks.size(); ++i)
437 if (reachable.access(i))
438 {
439 remap.access(i) = normalized.function.blocks.size();
440 auto block = source.blocks.access(i);
441 block.id = normalized.function.blocks.size();
443 normalized.source_block_ids.append(i);
444 normalized.function.blocks.append(std::move(block));
445 }
446
447 for (size_t i = 0; i < normalized.function.blocks.size(); ++i)
448 {
449 auto & block = normalized.function.blocks.access(i);
451 const auto source_id = normalized.source_block_ids.access(i);
452 const auto & source_block = source.blocks.access(source_id);
453 for (size_t j = 0; j < source_block.terminator.successors.size(); ++j)
454 {
455 const auto succ = source_block.terminator.successors.access(j);
456 if (succ < remap.size() and remap.access(succ) != compiler_ir_invalid_id())
457 successors.append(remap.access(succ));
458 }
459 block.terminator.successors = successors;
460 }
461
462 normalized.function.entry_block =
463 source.entry_block < remap.size() ? remap.access(source.entry_block)
465 normalized.function.exit_block =
466 source.exit_block < remap.size() ? remap.access(source.exit_block)
468
469 for (size_t i = 0; i < normalized.function.blocks.size(); ++i)
470 normalized.function.blocks.access(i).predecessors.clear();
471 for (size_t i = 0; i < normalized.function.blocks.size(); ++i)
472 for (size_t j = 0; j < normalized.function.blocks.access(i).terminator.successors.size(); ++j)
473 {
474 const auto succ = normalized.function.blocks.access(i).terminator.successors.access(j);
475 if (succ < normalized.function.blocks.size()
476 and not contains_block_id(normalized.function.blocks.access(succ).predecessors, i))
477 normalized.function.blocks.access(succ).predecessors.append(i);
478 }
479
480 return normalized;
481 }
482
485 {
487 const auto block_count = function.blocks.size();
488
489 for (size_t i = 0; i < block_count; ++i)
490 {
491 info.reachable_blocks.append(false);
492 info.immediate_dominators.append(compiler_ssa_invalid_id());
493 info.dominators.append({});
494 info.dominance_frontiers.append({});
495 info.dominator_tree_children.append({});
496 }
497
498 if (block_count == 0 or function.entry_block >= block_count)
499 return info;
500
501 info.reachable_blocks = compute_reachable_blocks(function);
502 for (size_t i = 0; i < block_count; ++i)
503 info.dominators.access(i).resize(block_count, false);
504
505 for (size_t i = 0; i < block_count; ++i)
506 {
507 if (not info.reachable_blocks.access(i))
508 continue;
509
510 if (i == function.entry_block)
511 info.dominators.access(i).set(i, true);
512 else
513 for (size_t j = 0; j < block_count; ++j)
514 if (info.reachable_blocks.access(j))
515 info.dominators.access(i).set(j, true);
516 }
517
518 bool changed = true;
519 while (changed)
520 {
521 changed = false;
522 for (size_t block_id = 0; block_id < block_count; ++block_id)
523 {
524 if (not info.reachable_blocks.access(block_id)
525 or block_id == function.entry_block)
526 continue;
527
529 bool have_pred = false;
530 new_dom.resize(block_count, false);
531 const auto & block = function.blocks.access(block_id);
532 for (size_t i = 0; i < block.predecessors.size(); ++i)
533 {
534 const auto pred = block.predecessors.access(i);
535 if (pred >= block_count or not info.reachable_blocks.access(pred))
536 continue;
537
538 if (not have_pred)
539 {
540 new_dom = info.dominators.access(pred);
541 have_pred = true;
542 }
543 else
545 new_dom,
546 info.dominators.access(pred));
547 }
548
549 if (not have_pred)
550 new_dom.resize(block_count, false);
551 new_dom.set(block_id, true);
552
554 info.dominators.access(block_id)))
555 {
556 info.dominators.access(block_id) = new_dom;
557 changed = true;
558 }
559 }
560 }
561
562 for (size_t block_id = 0; block_id < block_count; ++block_id)
563 {
564 if (not info.reachable_blocks.access(block_id)
565 or block_id == function.entry_block)
566 continue;
567
569 for (size_t candidate = 0; candidate < block_count; ++candidate)
570 {
571 if (candidate == block_id
572 or not info.reachable_blocks.access(candidate)
573 or not info.dominators.access(block_id).test(candidate))
574 continue;
575
576 bool dominated_by_other = false;
577 for (size_t other = 0; other < block_count; ++other)
578 {
579 if (other == block_id or other == candidate
580 or not info.reachable_blocks.access(other)
581 or not info.dominators.access(block_id).test(other))
582 continue;
583
584 if (info.dominators.access(other).test(candidate))
585 {
586 dominated_by_other = true;
587 break;
588 }
589 }
590
592 {
593 idom = candidate;
594 break;
595 }
596 }
597
598 info.immediate_dominators.access(block_id) = idom;
600 info.dominator_tree_children.access(idom).append(block_id);
601 }
602
603 for (size_t block_id = 0; block_id < block_count; ++block_id)
604 {
605 if (not info.reachable_blocks.access(block_id))
606 continue;
607
608 const auto & block = function.blocks.access(block_id);
609 if (block.predecessors.size() < 2)
610 continue;
611
612 for (size_t i = 0; i < block.predecessors.size(); ++i)
613 {
614 auto runner = block.predecessors.access(i);
615 while (runner != compiler_ssa_invalid_id()
616 and runner != info.immediate_dominators.access(block_id))
617 {
618 if (not contains_block_id(info.dominance_frontiers.access(runner), block_id))
619 info.dominance_frontiers.access(runner).append(block_id);
620 runner = info.immediate_dominators.access(runner);
621 }
622 }
623 }
624
625 return info;
626 }
627
628 inline size_t
630 {
631 ++function.next_value_id;
632 return function.next_value_id;
633 }
634
635 inline Compiler_SSA_Block &
637 const Compiler_SSA_Block_Id id)
638 {
639 return function.blocks.access(id);
640 }
641
642 inline const Compiler_SSA_Block &
643 block(const Compiler_SSA_Function & function,
644 const Compiler_SSA_Block_Id id)
645 {
646 return function.blocks.access(id);
647 }
648
651 const Compiler_IR_Local_Slot_Id slot_id) noexcept
652 {
653 if (slot_id >= stacks.size() or stacks.access(slot_id).is_empty())
654 return 0;
655 return stacks.access(slot_id).access(stacks.access(slot_id).size() - 1);
656 }
657
658 inline size_t
660 const Compiler_SSA_Block_Id predecessor)
661 {
662 for (size_t i = 0; i < block.predecessors.size(); ++i)
663 if (block.predecessors.access(i) == predecessor)
664 return i;
665
667 }
668
669 inline void
672 const Compiler_SSA_Dominance_Info & dominance,
674 Compiler_SSA_Block_Id block_id,
677 {
679 for (size_t i = 0; i < slot_stacks.size(); ++i)
680 saved_stack_sizes.append(slot_stacks.access(i).size());
681
682 auto & out_block = output.blocks.access(block_id);
683 const auto & in_block = normalized.blocks.access(block_id);
684
685 for (size_t i = 0; i < out_block.phis.size(); ++i)
686 {
687 auto & phi = out_block.phis.access(i);
688 phi.result_id = next_value(output);
689 if (phi.slot_id < slot_stacks.size())
690 slot_stacks.access(phi.slot_id).append(phi.result_id);
691 }
692
693 for (size_t i = 0; i < in_block.instructions.size(); ++i)
694 {
695 const auto & inst = in_block.instructions.access(i);
696 switch (inst.kind)
697 {
699 {
702 lowered.result_id = next_value(output);
703 lowered.type_id = inst.type_id;
704 lowered.span = inst.span;
705 lowered.text = inst.text;
706 lowered.bool_value = inst.bool_value;
707 renamed_values.access(inst.result_id) = lowered.result_id;
708 out_block.instructions.append(std::move(lowered));
709 break;
710 }
711
713 if (inst.local_slot_id != compiler_ir_invalid_id())
714 {
715 renamed_values.access(inst.result_id) =
716 current_slot_value(slot_stacks, inst.local_slot_id);
717 }
718 else
719 {
722 lowered.result_id = next_value(output);
723 lowered.type_id = inst.type_id;
724 lowered.span = inst.span;
725 lowered.global_slot_id = inst.global_slot_id;
726 renamed_values.access(inst.result_id) = lowered.result_id;
727 out_block.instructions.append(std::move(lowered));
728 }
729 break;
730
732 if (inst.local_slot_id != compiler_ir_invalid_id())
733 {
734 const auto incoming =
735 inst.operands.is_empty() ? 0 : renamed_values.access(inst.operands.access(0));
736 if (inst.local_slot_id < slot_stacks.size())
737 slot_stacks.access(inst.local_slot_id).append(incoming);
738 }
739 else
740 {
743 lowered.type_id = inst.type_id;
744 lowered.span = inst.span;
745 lowered.global_slot_id = inst.global_slot_id;
746 if (not inst.operands.is_empty())
747 lowered.operands.append(renamed_values.access(inst.operands.access(0)));
748 out_block.instructions.append(std::move(lowered));
749 }
750 break;
751
756 {
758 lowered.kind =
763 lowered.result_id = inst.result_id != 0 ? next_value(output) : 0;
764 lowered.type_id = inst.type_id;
765 lowered.span = inst.span;
766 lowered.op = inst.op;
767 lowered.function_id = inst.function_id;
768 lowered.text = inst.text;
769 lowered.bool_value = inst.bool_value;
770 for (size_t operand = 0; operand < inst.operands.size(); ++operand)
771 lowered.operands.append(renamed_values.access(inst.operands.access(operand)));
772 if (inst.result_id != 0)
773 renamed_values.access(inst.result_id) = lowered.result_id;
774 out_block.instructions.append(std::move(lowered));
775 break;
776 }
777 }
778 }
779
780 out_block.terminator.kind = in_block.terminator.kind;
781 out_block.terminator.span = in_block.terminator.span;
782 if (in_block.terminator.kind == Compiler_IR_Terminator_Kind::Branch)
783 out_block.terminator.condition_value =
784 renamed_values.access(in_block.terminator.condition_value);
785 else if (in_block.terminator.kind == Compiler_IR_Terminator_Kind::Return)
786 out_block.terminator.return_value =
787 renamed_values.access(in_block.terminator.return_value);
788 out_block.terminator.successors = in_block.terminator.successors;
789
790 for (size_t i = 0; i < out_block.terminator.successors.size(); ++i)
791 {
792 const auto succ_id = out_block.terminator.successors.access(i);
793 auto & succ = output.blocks.access(succ_id);
794 const auto pred_index = predecessor_index(succ, block_id);
796 continue;
797
798 for (size_t phi_index = 0; phi_index < succ.phis.size(); ++phi_index)
799 {
800 auto & phi = succ.phis.access(phi_index);
801 phi.operands.access(pred_index) =
803 }
804 }
805
806 for (size_t i = 0; i < dominance.dominator_tree_children.access(block_id).size(); ++i)
809 dominance,
810 phi_specs,
811 dominance.dominator_tree_children.access(block_id).access(i),
814
815 for (size_t slot = 0; slot < slot_stacks.size(); ++slot)
816 while (slot_stacks.access(slot).size() > saved_stack_sizes.access(slot))
817 (void) slot_stacks.access(slot).pop();
818 }
819 }
820
823 {
825
828 const bool top_level) const
829 {
831 auto * function = ctx->make<Compiler_SSA_Function>();
832 function->id = source.id;
833 function->top_level = top_level;
834 function->name = source.name;
835 function->span = source.span;
836 function->type_id = source.type_id;
837 function->source_function = &source;
838 function->local_slots = source.local_slots;
839 function->entry_block = normalized.function.entry_block;
840 function->exit_block = normalized.function.exit_block;
841 function->dominance =
843
844 for (size_t i = 0; i < normalized.function.blocks.size(); ++i)
845 {
846 Compiler_SSA_Block block;
847 block.id = i;
848 block.source_block_id = normalized.source_block_ids.access(i);
849 block.label = normalized.function.blocks.access(i).label;
850 block.span = normalized.function.blocks.access(i).span;
851 block.predecessors = normalized.function.blocks.access(i).predecessors;
852 function->blocks.append(std::move(block));
853 }
854
858 for (size_t i = 0; i < normalized.function.blocks.size(); ++i)
859 {
860 phi_specs.append({});
861 has_phi.append({});
862 has_phi.access(i).resize(normalized.function.local_slots.size(), false);
863 is_def_block.append({});
864 is_def_block.access(i).resize(normalized.function.local_slots.size(), false);
865 }
866
868 for (size_t i = 0; i < normalized.function.local_slots.size(); ++i)
870
871 for (size_t slot_id = 0; slot_id < normalized.function.local_slots.size(); ++slot_id)
872 if (normalized.function.local_slots.access(slot_id).kind == Compiler_IR_Slot_Kind::Parameter)
873 {
874 definition_blocks.access(slot_id).append(normalized.function.entry_block);
875 is_def_block.access(normalized.function.entry_block).set(slot_id, true);
876 }
877
878 for (size_t block_id = 0; block_id < normalized.function.blocks.size(); ++block_id)
879 {
880 const auto & block = normalized.function.blocks.access(block_id);
881 for (size_t inst_index = 0; inst_index < block.instructions.size(); ++inst_index)
882 {
883 const auto & inst = block.instructions.access(inst_index);
885 and inst.local_slot_id != compiler_ir_invalid_id()
886 and inst.local_slot_id < normalized.function.local_slots.size()
887 and not is_def_block.access(block_id).test(inst.local_slot_id))
888 {
889 definition_blocks.access(inst.local_slot_id).append(block_id);
890 is_def_block.access(block_id).set(inst.local_slot_id, true);
891 }
892 }
893 }
894
895 for (size_t slot_id = 0; slot_id < normalized.function.local_slots.size(); ++slot_id)
896 {
898 while (not worklist.is_empty())
899 {
900 const auto def_block = worklist.pop();
901 if (def_block >= function->dominance.dominance_frontiers.size())
902 continue;
903
904 for (size_t i = 0;
905 i < function->dominance.dominance_frontiers.access(def_block).size();
906 ++i)
907 {
908 const auto frontier =
909 function->dominance.dominance_frontiers.access(def_block).access(i);
910 if (has_phi.access(frontier).test(slot_id))
911 continue;
912
913 phi_specs.access(frontier).append(
914 {slot_id,
915 normalized.function.local_slots.access(slot_id).type_id,
916 normalized.function.local_slots.access(slot_id).span});
917 has_phi.access(frontier).set(slot_id, true);
918 if (not is_def_block.access(frontier).test(slot_id))
919 worklist.append(frontier);
920 }
921 }
922 }
923
924 for (size_t block_id = 0; block_id < function->blocks.size(); ++block_id)
925 for (size_t i = 0; i < phi_specs.access(block_id).size(); ++i)
926 {
928 phi.slot_id = phi_specs.access(block_id).access(i).slot_id;
929 phi.type_id = phi_specs.access(block_id).access(i).type_id;
930 phi.span = phi_specs.access(block_id).access(i).span;
931 for (size_t operand = 0;
932 operand < function->blocks.access(block_id).predecessors.size();
933 ++operand)
934 phi.operands.append(0);
935 function->blocks.access(block_id).phis.append(std::move(phi));
936 }
937
939 for (size_t i = 0; i < normalized.function.local_slots.size(); ++i)
941
942 for (size_t slot_id = 0; slot_id < normalized.function.local_slots.size(); ++slot_id)
943 if (normalized.function.local_slots.access(slot_id).kind == Compiler_IR_Slot_Kind::Parameter)
944 {
947 parameter.slot_id = slot_id;
948 parameter.type_id = normalized.function.local_slots.access(slot_id).type_id;
949 parameter.name = normalized.function.local_slots.access(slot_id).name;
950 parameter.span = normalized.function.local_slots.access(slot_id).span;
951 function->parameters.append(parameter);
952 slot_stacks.access(slot_id).append(parameter.value_id);
953 }
954
956 for (size_t i = 0; i <= normalized.function.next_value_id; ++i)
958
959 if (function->entry_block != compiler_ssa_invalid_id())
961 normalized.function,
962 function->dominance,
963 phi_specs,
964 function->entry_block,
967
968 return function;
969 }
970
971 public:
974 : ctx(&context)
975 {}
976
979 lower_module(const Compiler_IR_Module * module) const
980 {
981 ah_runtime_error_unless(module != nullptr)
982 << "Compiler_SSA_Lowering::lower_module(): null IR module";
983
984 auto * result = ctx->make<Compiler_SSA_Module>();
985 result->global_slots = module->global_slots;
986 for (size_t i = 0; i < module->functions.size(); ++i)
987 result->functions.append(lower_function_internal(*module->functions.access(i), false));
988 if (module->top_level != nullptr)
989 result->top_level = lower_function_internal(*module->top_level, true);
990 return result;
991 }
992 };
993
997 const Compiler_SSA_Module * module = nullptr)
998 {
1000
1001 auto fail = [&report](const std::string & message)
1002 {
1003 report.valid = false;
1004 report.errors.append(message);
1005 };
1006
1007 if (function.blocks.is_empty())
1008 {
1009 fail("SSA function '" + function.name + "' has no blocks");
1010 return report;
1011 }
1012
1013 if (function.entry_block >= function.blocks.size())
1014 fail("SSA function '" + function.name + "' has invalid entry block");
1015 if (function.exit_block >= function.blocks.size())
1016 fail("SSA function '" + function.name + "' has invalid exit block");
1017
1018 if (function.dominance.reachable_blocks.size() != function.blocks.size()
1019 or function.dominance.dominators.size() != function.blocks.size()
1020 or function.dominance.immediate_dominators.size() != function.blocks.size()
1021 or function.dominance.dominance_frontiers.size() != function.blocks.size()
1022 or function.dominance.dominator_tree_children.size() != function.blocks.size())
1023 fail("SSA function '" + function.name
1024 + "' has mismatched dominance metadata sizes");
1025
1026 if (not report.valid)
1027 return report;
1028
1030 for (size_t i = 0; i <= function.next_value_id; ++i)
1031 definitions.append({});
1032
1033 for (size_t i = 0; i < function.parameters.size(); ++i)
1034 {
1035 const auto & parameter = function.parameters.access(i);
1036 if (parameter.value_id == 0 or parameter.value_id >= definitions.size())
1037 fail("SSA parameter in function '" + function.name + "' has invalid value id");
1038 else if (definitions.access(parameter.value_id).kind
1039 != Compiler_SSA_Detail::Definition_Location::Kind::Invalid)
1040 fail("SSA function '" + function.name + "' defines value "
1041 + Compiler_SSA_Detail::value_name(parameter.value_id) + " multiple times");
1042 else
1043 definitions.access(parameter.value_id) =
1044 {Compiler_SSA_Detail::Definition_Location::Kind::Parameter,
1045 function.entry_block,
1046 i};
1047 }
1048
1049 for (size_t block_id = 0; block_id < function.blocks.size(); ++block_id)
1050 {
1051 const auto & block = function.blocks.access(block_id);
1052 if (block.id != block_id)
1053 fail("SSA function '" + function.name
1054 + "' has mismatched block id at "
1056 if (block.terminator.kind == Compiler_IR_Terminator_Kind::None)
1057 fail("SSA block " + Compiler_SSA_Detail::block_name(block_id)
1058 + " in function '" + function.name + "' is unterminated");
1059
1060 if (block_id == function.entry_block
1062 fail("SSA entry block " + Compiler_SSA_Detail::block_name(block_id)
1063 + " must not have an immediate dominator");
1064
1065 for (size_t phi_index = 0; phi_index < block.phis.size(); ++phi_index)
1066 {
1067 const auto & phi = block.phis.access(phi_index);
1068 if (phi.result_id == 0 or phi.result_id >= definitions.size())
1069 fail("Phi in " + Compiler_SSA_Detail::block_name(block_id)
1070 + " of function '" + function.name + "' has invalid result id");
1071 else if (definitions.access(phi.result_id).kind
1072 != Compiler_SSA_Detail::Definition_Location::Kind::Invalid)
1073 fail("SSA function '" + function.name + "' defines value "
1074 + Compiler_SSA_Detail::value_name(phi.result_id) + " multiple times");
1075 else
1076 definitions.access(phi.result_id) =
1077 {Compiler_SSA_Detail::Definition_Location::Kind::Phi,
1078 block_id,
1079 phi_index};
1080
1081 if (phi.slot_id >= function.local_slots.size())
1082 fail("Phi in " + Compiler_SSA_Detail::block_name(block_id)
1083 + " references an invalid local slot");
1084 if (phi.operands.size() != block.predecessors.size())
1085 fail("Phi in " + Compiler_SSA_Detail::block_name(block_id)
1086 + " must have one operand per predecessor");
1087 }
1088
1089 for (size_t inst_index = 0; inst_index < block.instructions.size(); ++inst_index)
1090 {
1091 const auto & inst = block.instructions.access(inst_index);
1092 if ((inst.kind == Compiler_SSA_Instruction_Kind::Constant
1093 or inst.kind == Compiler_SSA_Instruction_Kind::Load_Global
1094 or inst.kind == Compiler_SSA_Instruction_Kind::Unary
1095 or inst.kind == Compiler_SSA_Instruction_Kind::Binary
1096 or inst.kind == Compiler_SSA_Instruction_Kind::Call
1097 or inst.kind == Compiler_SSA_Instruction_Kind::Function_Ref)
1098 and inst.result_id == 0)
1099 fail("SSA instruction in " + Compiler_SSA_Detail::block_name(block_id)
1100 + " of function '" + function.name + "' must define a result");
1101
1102 if (inst.result_id != 0)
1103 {
1104 if (inst.result_id >= definitions.size())
1105 fail("SSA instruction in " + Compiler_SSA_Detail::block_name(block_id)
1106 + " of function '" + function.name + "' has invalid result id");
1107 else if (definitions.access(inst.result_id).kind
1108 != Compiler_SSA_Detail::Definition_Location::Kind::Invalid)
1109 fail("SSA function '" + function.name + "' defines value "
1110 + Compiler_SSA_Detail::value_name(inst.result_id) + " multiple times");
1111 else
1112 definitions.access(inst.result_id) =
1113 {Compiler_SSA_Detail::Definition_Location::Kind::Instruction,
1114 block_id,
1115 inst_index};
1116 }
1117
1118 if ((inst.kind == Compiler_SSA_Instruction_Kind::Load_Global
1119 or inst.kind == Compiler_SSA_Instruction_Kind::Store_Global)
1120 and (module == nullptr or inst.global_slot_id >= module->global_slots.size()))
1121 fail("SSA instruction references invalid global slot in function '"
1122 + function.name + "'");
1123 if (inst.kind == Compiler_SSA_Instruction_Kind::Function_Ref
1124 and (module == nullptr or inst.function_id >= module->functions.size()))
1125 fail("SSA instruction references invalid function id in function '"
1126 + function.name + "'");
1127 }
1128 }
1129
1130 auto dominates = [&function](const Compiler_SSA_Block_Id a,
1131 const Compiler_SSA_Block_Id b) noexcept
1132 {
1133 return a < function.dominance.dominators.size()
1134 and b < function.dominance.dominators.size()
1135 and function.dominance.dominators.access(b).test(a);
1136 };
1137
1138 auto ensure_use = [&](const Compiler_SSA_Value_Id value_id,
1140 const size_t user_order,
1141 const bool phi_edge_use,
1143 const std::string & context)
1144 {
1145 if (value_id == 0 or value_id >= definitions.size())
1146 {
1147 fail(context + " references invalid SSA value");
1148 return;
1149 }
1150
1151 const auto def = definitions.access(value_id);
1152 if (def.kind == Compiler_SSA_Detail::Definition_Location::Kind::Invalid)
1153 {
1154 fail(context + " references undefined SSA value "
1156 return;
1157 }
1158
1159 if (phi_edge_use)
1160 {
1161 if (def.kind == Compiler_SSA_Detail::Definition_Location::Kind::Parameter)
1162 return;
1163 if (def.block_id == predecessor_block)
1164 return;
1165 if (not dominates(def.block_id, predecessor_block))
1166 fail(context + " references non-dominating SSA value "
1168 return;
1169 }
1170
1171 if (def.kind == Compiler_SSA_Detail::Definition_Location::Kind::Parameter)
1172 return;
1173
1174 if (def.block_id == user_block)
1175 {
1176 if (def.kind == Compiler_SSA_Detail::Definition_Location::Kind::Phi)
1177 return;
1178 if (def.order < user_order)
1179 return;
1180 fail(context + " references SSA value defined later in the same block");
1181 return;
1182 }
1183
1184 if (not dominates(def.block_id, user_block))
1185 fail(context + " references non-dominating SSA value "
1187 };
1188
1189 for (size_t block_id = 0; block_id < function.blocks.size(); ++block_id)
1190 {
1191 const auto & block = function.blocks.access(block_id);
1192 for (size_t phi_index = 0; phi_index < block.phis.size(); ++phi_index)
1193 for (size_t operand_index = 0; operand_index < block.phis.access(phi_index).operands.size(); ++operand_index)
1194 ensure_use(block.phis.access(phi_index).operands.access(operand_index),
1195 block_id,
1196 0,
1197 true,
1199 "Phi in " + Compiler_SSA_Detail::block_name(block_id));
1200
1201 for (size_t inst_index = 0; inst_index < block.instructions.size(); ++inst_index)
1202 for (size_t operand_index = 0; operand_index < block.instructions.access(inst_index).operands.size(); ++operand_index)
1203 ensure_use(block.instructions.access(inst_index).operands.access(operand_index),
1204 block_id,
1205 inst_index,
1206 false,
1208 "Instruction in " + Compiler_SSA_Detail::block_name(block_id));
1209
1210 if (block.terminator.kind == Compiler_IR_Terminator_Kind::Branch)
1212 block_id,
1213 block.instructions.size(),
1214 false,
1216 "Branch terminator in " + Compiler_SSA_Detail::block_name(block_id));
1217 else if (block.terminator.kind == Compiler_IR_Terminator_Kind::Return)
1219 block_id,
1220 block.instructions.size(),
1221 false,
1223 "Return terminator in " + Compiler_SSA_Detail::block_name(block_id));
1224 }
1225
1226 return report;
1227 }
1228
1232 {
1234
1235 auto merge = [&report](const Compiler_SSA_Validation_Report & child)
1236 {
1237 if (not child.valid)
1238 report.valid = false;
1239 for (size_t i = 0; i < child.errors.size(); ++i)
1240 report.errors.append(child.errors.access(i));
1241 for (size_t i = 0; i < child.warnings.size(); ++i)
1242 report.warnings.append(child.warnings.access(i));
1243 };
1244
1245 for (size_t i = 0; i < module.functions.size(); ++i)
1246 merge(validate_ssa_function(*module.functions.access(i), &module));
1247 if (module.top_level != nullptr)
1248 merge(validate_ssa_function(*module.top_level, &module));
1249 return report;
1250 }
1251
1253 inline std::string
1255 const Compiler_SSA_Module * module = nullptr,
1256 const Compiler_Type_Context * types = nullptr)
1257 {
1258 (void) module;
1259 std::ostringstream out;
1260 if (function == nullptr)
1261 {
1262 out << "<null-ssa-function>\n";
1263 return out.str();
1264 }
1265
1266 out << "SSAFunction(" << function->name << ")";
1267 if (types != nullptr and function->type_id != 0)
1268 out << ": " << types->to_string(function->type_id);
1269 out << '\n';
1270 out << " Entry: " << Compiler_SSA_Detail::block_name(function->entry_block) << '\n';
1271 out << " Exit: " << Compiler_SSA_Detail::block_name(function->exit_block) << '\n';
1272
1273 if (not function->parameters.is_empty())
1274 {
1275 out << " Parameters:\n";
1276 for (size_t i = 0; i < function->parameters.size(); ++i)
1277 {
1278 const auto & parameter = function->parameters.access(i);
1281 << " (" << parameter.name << ")";
1282 if (types != nullptr and parameter.type_id != 0)
1283 out << " : " << types->to_string(parameter.type_id);
1284 out << '\n';
1285 }
1286 }
1287
1288 for (size_t block_id = 0; block_id < function->blocks.size(); ++block_id)
1289 {
1290 const auto & block = function->blocks.access(block_id);
1291 out << " Block " << Compiler_SSA_Detail::block_name(block_id)
1293 << ", " << block.label << "]\n";
1294
1295 if (block.phis.is_empty())
1296 out << " Phis: <none>\n";
1297 else
1298 {
1299 out << " Phis:\n";
1300 for (size_t phi_index = 0; phi_index < block.phis.size(); ++phi_index)
1301 {
1302 const auto & phi = block.phis.access(phi_index);
1303 out << " " << Compiler_SSA_Detail::value_name(phi.result_id)
1304 << " = Phi " << Compiler_SSA_Detail::local_slot_name(phi.slot_id)
1305 << " [";
1306 for (size_t operand_index = 0; operand_index < phi.operands.size(); ++operand_index)
1307 {
1308 if (operand_index > 0)
1309 out << ", ";
1311 << ": "
1313 }
1314 out << "]";
1315 if (types != nullptr and phi.type_id != 0)
1316 out << " : " << types->to_string(phi.type_id);
1317 out << '\n';
1318 }
1319 }
1320
1321 if (block.instructions.is_empty())
1322 out << " Instructions: <none>\n";
1323 else
1324 {
1325 out << " Instructions:\n";
1326 for (size_t inst_index = 0; inst_index < block.instructions.size(); ++inst_index)
1327 {
1328 const auto & inst = block.instructions.access(inst_index);
1329 out << " ";
1330 if (inst.result_id != 0)
1331 out << Compiler_SSA_Detail::value_name(inst.result_id) << " = ";
1333 if (inst.kind == Compiler_SSA_Instruction_Kind::Load_Global
1334 or inst.kind == Compiler_SSA_Instruction_Kind::Store_Global)
1335 out << ' ' << Compiler_SSA_Detail::global_slot_name(inst.global_slot_id);
1336 else if (inst.kind == Compiler_SSA_Instruction_Kind::Function_Ref)
1337 out << ' ' << Compiler_SSA_Detail::function_name(inst.function_id);
1338 else if (inst.kind == Compiler_SSA_Instruction_Kind::Unary
1339 or inst.kind == Compiler_SSA_Instruction_Kind::Binary)
1340 out << '(' << Compiler_SSA_Detail::token_name(inst.op) << ')';
1341 else if (inst.kind == Compiler_SSA_Instruction_Kind::Constant)
1342 out << '(' << inst.text << ')';
1343
1344 if (not inst.operands.is_empty())
1345 {
1346 out << " [";
1347 for (size_t operand = 0; operand < inst.operands.size(); ++operand)
1348 {
1349 if (operand > 0)
1350 out << ", ";
1351 out << Compiler_SSA_Detail::value_name(inst.operands.access(operand));
1352 }
1353 out << ']';
1354 }
1355
1356 if (types != nullptr and inst.type_id != 0)
1357 out << " : " << types->to_string(inst.type_id);
1358 out << '\n';
1359 }
1360 }
1361
1362 out << " Terminator: "
1364 if (block.terminator.kind == Compiler_IR_Terminator_Kind::Branch)
1366 else if (block.terminator.kind == Compiler_IR_Terminator_Kind::Return)
1368 out << '\n';
1369
1370 out << " Successors:";
1371 if (block.terminator.successors.is_empty())
1372 out << " <none>\n";
1373 else
1374 {
1375 for (size_t i = 0; i < block.terminator.successors.size(); ++i)
1376 {
1377 out << (i == 0 ? " " : ", ");
1379 }
1380 out << '\n';
1381 }
1382
1383 out << " IDom: ";
1384 const auto idom = function->dominance.immediate_dominators.access(block_id);
1386 out << "<none>\n";
1387 else
1389
1390 out << " Frontier:";
1391 if (function->dominance.dominance_frontiers.access(block_id).is_empty())
1392 out << " <none>\n";
1393 else
1394 {
1395 for (size_t i = 0; i < function->dominance.dominance_frontiers.access(block_id).size(); ++i)
1396 {
1397 out << (i == 0 ? " " : ", ");
1399 function->dominance.dominance_frontiers.access(block_id).access(i));
1400 }
1401 out << '\n';
1402 }
1403 }
1404
1405 return out.str();
1406 }
1407
1409 inline std::string
1411 const Compiler_Type_Context * types = nullptr)
1412 {
1413 std::ostringstream out;
1414 if (module == nullptr)
1415 {
1416 out << "<null-ssa-module>\n";
1417 return out.str();
1418 }
1419
1420 out << "SSAModule\n";
1421 if (not module->global_slots.is_empty())
1422 {
1423 out << " Globals:\n";
1424 for (size_t i = 0; i < module->global_slots.size(); ++i)
1425 {
1426 const auto & slot = module->global_slots.access(i);
1428 << " " << slot.name;
1429 if (types != nullptr and slot.type_id != 0)
1430 out << ": " << types->to_string(slot.type_id);
1431 out << '\n';
1432 }
1433 }
1434
1435 for (size_t i = 0; i < module->functions.size(); ++i)
1438 module->functions.access(i),
1439 module,
1440 types),
1441 2);
1442 if (module->top_level != nullptr)
1445 module->top_level,
1446 module,
1447 types),
1448 2);
1449 return out.str();
1450 }
1451}
1452
1453#endif
Reusable dataflow analyses and dead-code elimination over Compiler_IR_Model.H.
Memory arena for fast bulk allocations.
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
Definition ah-errors.H:255
size_t size_t int32_t * out
Definition ca-c-api.h:120
Arena allocator for fast bump-pointer allocation.
Definition ah-arena.H:122
void reset() noexcept
Reset arena, making all memory available again.
Definition ah-arena.H:256
static constexpr size_t DEFAULT_SIZE
Default arena size (1 MB).
Definition ah-arena.H:133
Arena-backed ownership context for SSA functions and modules.
Definition SSA.H:223
DynArray< Owned_Object > owned
Definition SSA.H:231
Compiler_SSA_Context & operator=(const Compiler_SSA_Context &)=delete
T * make(Args &&... args)
Allocates and constructs one SSA object in the arena.
Definition SSA.H:250
AhArenaAllocator arena
Definition SSA.H:230
Compiler_SSA_Context(const Compiler_SSA_Context &)=delete
~Compiler_SSA_Context() noexcept
Destroys all managed objects and rewinds the arena.
Definition SSA.H:243
Compiler_SSA_Context(const size_t arena_size=AhArenaAllocator::DEFAULT_SIZE)
Constructs an SSA context.
Definition SSA.H:235
void reset() noexcept
Resets the arena and destroys all tracked objects.
Definition SSA.H:267
Lowers non-SSA IR to SSA form for all reachable blocks.
Definition SSA.H:823
Compiler_SSA_Context * ctx
Definition SSA.H:824
Compiler_SSA_Lowering(Compiler_SSA_Context &context) noexcept
Builds an SSA lowerer over ctx.
Definition SSA.H:973
Compiler_SSA_Module * lower_module(const Compiler_IR_Module *module) const
Lowers one IR module to SSA form.
Definition SSA.H:979
Compiler_SSA_Function * lower_function_internal(const Compiler_IR_Function &source, const bool top_level) const
Definition SSA.H:827
Context owning all compiler type nodes.
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.
bool is_empty() const noexcept
Return true if the array is empty.
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
Freq_Node * pred
Predecessor node in level-order traversal.
bool bit_set_equals(const Compiler_Dataflow_Bit_Set &lhs, const Compiler_Dataflow_Bit_Set &rhs) noexcept
Compiler_Dataflow_Bit_Set bit_set_intersection(const Compiler_Dataflow_Bit_Set &lhs, const Compiler_Dataflow_Bit_Set &rhs)
std::string block_name(const Compiler_IR_Block_Id id)
std::string function_name(const Compiler_IR_Function_Id id)
Definition SSA.H:343
std::string block_name(const Compiler_SSA_Block_Id id)
Definition SSA.H:319
const char * token_name(const Compiler_Operator_Kind kind) noexcept
Definition SSA.H:349
size_t predecessor_index(const Compiler_SSA_Block &block, const Compiler_SSA_Block_Id predecessor)
Definition SSA.H:659
void append_indented_text(std::ostream &out, const std::string &text, const size_t indent)
Definition SSA.H:355
Compiler_SSA_Value_Id current_slot_value(const DynArray< DynArray< Compiler_SSA_Value_Id > > &stacks, const Compiler_IR_Local_Slot_Id slot_id) noexcept
Definition SSA.H:650
Compiler_SSA_Block & block(Compiler_SSA_Function &function, const Compiler_SSA_Block_Id id)
Definition SSA.H:636
size_t next_value(Compiler_SSA_Function &function) noexcept
Definition SSA.H:629
std::string value_name(const Compiler_SSA_Value_Id id)
Definition SSA.H:325
Normalized_IR_Function normalize_ir_function(const Compiler_IR_Function &source)
Definition SSA.H:420
void rename_recursive(Compiler_SSA_Function &output, const Compiler_IR_Function &normalized, const Compiler_SSA_Dominance_Info &dominance, const DynArray< DynArray< Phi_Spec > > &phi_specs, Compiler_SSA_Block_Id block_id, DynArray< DynArray< Compiler_SSA_Value_Id > > &slot_stacks, DynArray< Compiler_SSA_Value_Id > &renamed_values)
Definition SSA.H:670
std::string local_slot_name(const Compiler_IR_Local_Slot_Id id)
Definition SSA.H:331
std::string global_slot_name(const Compiler_IR_Global_Slot_Id id)
Definition SSA.H:337
bool contains_block_id(const DynArray< Compiler_SSA_Block_Id > &ids, const Compiler_SSA_Block_Id id) noexcept
Definition SSA.H:379
Compiler_SSA_Dominance_Info compute_dominance_info(const Compiler_IR_Function &function)
Definition SSA.H:484
DynArray< bool > compute_reachable_blocks(const Compiler_IR_Function &function)
Definition SSA.H:389
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
const char * compiler_ir_terminator_kind_name(const Compiler_IR_Terminator_Kind kind) noexcept
Stable debug name for one terminator kind.
Compiler_SSA_Validation_Report validate_ssa_module(const Compiler_SSA_Module &module)
Validates all functions in one SSA module.
Definition SSA.H:1231
@ Store_Global
globals[slot] <- src
@ Binary
dst <- op(lhs, rhs)
@ Load_Global
dst <- globals[slot]
@ Call
dst <- callee(args...)
size_t Compiler_IR_Function_Id
@ 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
size_t size(Node *root) noexcept
const char * compiler_operator_name(const Compiler_Operator_Kind kind) noexcept
Returns a stable debug name for one operator kind.
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
size_t Compiler_SSA_Block_Id
Definition SSA.H:69
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
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
constexpr size_t compiler_ir_invalid_id() noexcept
Returns the sentinel invalid IR id.
size_t Compiler_Type_Id
const char * compiler_ssa_instruction_kind_name(const Compiler_SSA_Instruction_Kind kind) noexcept
Returns a stable debug name for an SSA instruction kind.
Definition SSA.H:92
Compiler_SSA_Instruction_Kind
Instruction kinds supported by the SSA form.
Definition SSA.H:80
size_t Compiler_IR_Block_Id
Itor3 merge(Itor1 source1Beg, Itor1 source1End, Itor2 source2Beg, Itor2 source2End, Itor3 destBeg)
Merge two sorted ranges.
Definition ahAlgo.H:1410
size_t Compiler_IR_Global_Slot_Id
size_t Compiler_SSA_Value_Id
Definition SSA.H:68
Compiler_IR_Terminator_Kind
Terminator kinds supported by the MVP IR.
std::string compiler_dump_ssa_module(const Compiler_SSA_Module *module, const Compiler_Type_Context *types=nullptr)
Dumps all SSA functions in one module deterministically.
Definition SSA.H:1410
Compiler_Operator_Kind
Stable operator kinds shared by reusable compiler layers.
size_t Compiler_IR_Local_Slot_Id
Small reusable bit-set for slot-domain dataflow analyses.
void resize(const size_t count, const bool value=false)
Clears all existing bits and resizes the set to count elements.
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_Type_Id type_id
Full function type, when known.
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.
const Compiler_HIR_Function * source_function
Originating HIR function, or nullptr for top-level.
Source_Span span
Source span of the function/body.
Lowered IR module with shared global slots and functions.
DynArray< Compiler_IR_Function * > functions
Lowered non-top-level functions.
Compiler_IR_Function * top_level
Optional top-level body lowered as a function.
One SSA basic block containing phis, instructions, and a terminator.
Definition SSA.H:162
Source_Span span
Aggregate source span.
Definition SSA.H:166
DynArray< Compiler_SSA_Block_Id > predecessors
Predecessor ids in deterministic order.
Definition SSA.H:170
Compiler_SSA_Block_Id id
Dense SSA block id.
Definition SSA.H:163
Compiler_IR_Block_Id source_block_id
Original IR block id before SSA normalization.
Definition SSA.H:164
DynArray< Compiler_SSA_Instruction > instructions
Linear SSA instruction list.
Definition SSA.H:168
bool is_terminated() const noexcept
Returns whether the block already has a terminator.
Definition SSA.H:173
std::string label
Deterministic debug label.
Definition SSA.H:165
Compiler_SSA_Terminator terminator
Explicit block terminator.
Definition SSA.H:169
DynArray< Compiler_SSA_Phi > phis
Phi nodes evaluated before instructions.
Definition SSA.H:167
void(* destroy)(void *) noexcept
Definition SSA.H:227
DynArray< Compiler_IR_Block_Id > source_block_ids
Definition SSA.H:293
Compiler_IR_Local_Slot_Id slot_id
Definition SSA.H:298
Dominance metadata computed for one SSA function.
Definition SSA.H:181
DynArray< bool > reachable_blocks
Reachability from entry for each SSA block.
Definition SSA.H:182
DynArray< Compiler_Dataflow_Bit_Set > dominators
Dominator sets by block.
Definition SSA.H:183
DynArray< DynArray< Compiler_SSA_Block_Id > > dominance_frontiers
Dominance frontier for each block.
Definition SSA.H:185
DynArray< Compiler_SSA_Block_Id > immediate_dominators
Immediate dominator, or invalid for entry/unreachable.
Definition SSA.H:184
DynArray< DynArray< Compiler_SSA_Block_Id > > dominator_tree_children
Dominator-tree children by block.
Definition SSA.H:186
SSA form for one function or top-level body lowered from IR.
Definition SSA.H:191
Compiler_SSA_Block_Id entry_block
Canonical entry block.
Definition SSA.H:202
std::string name
Debug/source name.
Definition SSA.H:194
bool is_top_level() const noexcept
Returns whether this SSA function represents the top-level body.
Definition SSA.H:207
DynArray< Compiler_SSA_Block > blocks
Reachable SSA blocks in deterministic order.
Definition SSA.H:200
Compiler_SSA_Dominance_Info dominance
Dominance metadata over blocks.
Definition SSA.H:201
DynArray< Compiler_IR_Slot > local_slots
Original local/parameter slots for debug and phi ownership.
Definition SSA.H:198
Compiler_SSA_Block_Id exit_block
Canonical exit block.
Definition SSA.H:203
bool top_level
Whether this function represents the lowered top-level body.
Definition SSA.H:193
Compiler_SSA_Value_Id next_value_id
Next SSA value id to allocate.
Definition SSA.H:204
Source_Span span
Source span of the function/body.
Definition SSA.H:195
Compiler_IR_Function_Id id
Stable function id within the source module.
Definition SSA.H:192
const Compiler_IR_Function * source_function
Original non-SSA IR function.
Definition SSA.H:197
DynArray< Compiler_SSA_Parameter > parameters
Entry parameter SSA definitions.
Definition SSA.H:199
Compiler_Type_Id type_id
Full function type, when known.
Definition SSA.H:196
One SSA instruction producing an optional explicit result value.
Definition SSA.H:137
Source_Span span
Source region associated with the instruction.
Definition SSA.H:141
Compiler_Type_Id type_id
Result or stored type.
Definition SSA.H:140
bool bool_value
Decoded boolean payload for bool constants.
Definition SSA.H:147
Compiler_SSA_Value_Id result_id
Produced value id, or 0 for void instructions.
Definition SSA.H:139
Compiler_SSA_Instruction_Kind kind
Instruction category.
Definition SSA.H:138
Compiler_IR_Global_Slot_Id global_slot_id
Referenced global slot, when relevant.
Definition SSA.H:143
std::string text
Constant spelling or debug payload.
Definition SSA.H:146
Compiler_IR_Function_Id function_id
Referenced callee function, when relevant.
Definition SSA.H:144
Compiler_Operator_Kind op
Unary/binary operator when relevant.
Definition SSA.H:142
DynArray< Compiler_SSA_Value_Id > operands
Input SSA values in deterministic order.
Definition SSA.H:145
SSA module sharing global slots with the source IR module.
Definition SSA.H:215
Compiler_SSA_Function * top_level
Optional lowered top-level body.
Definition SSA.H:218
DynArray< Compiler_IR_Slot > global_slots
Shared global slot layout from the source IR module.
Definition SSA.H:216
DynArray< Compiler_SSA_Function * > functions
Lowered non-top-level functions.
Definition SSA.H:217
SSA parameter definition that seeds renaming for one IR parameter slot.
Definition SSA.H:117
Compiler_SSA_Value_Id value_id
SSA value defined by the parameter.
Definition SSA.H:118
std::string name
Debug/source name.
Definition SSA.H:121
Compiler_IR_Local_Slot_Id slot_id
Original IR slot represented by this parameter.
Definition SSA.H:119
Compiler_Type_Id type_id
Parameter type.
Definition SSA.H:120
Source_Span span
Parameter declaration span.
Definition SSA.H:122
Phi node merging values for one original IR local slot.
Definition SSA.H:127
Compiler_IR_Local_Slot_Id slot_id
Original local/parameter slot represented by the phi.
Definition SSA.H:129
Compiler_Type_Id type_id
Merged type.
Definition SSA.H:130
Source_Span span
Source location associated with the slot declaration.
Definition SSA.H:131
Compiler_SSA_Value_Id result_id
SSA value produced by the phi.
Definition SSA.H:128
DynArray< Compiler_SSA_Value_Id > operands
Incoming values aligned with predecessor order.
Definition SSA.H:132
Explicit terminator for one SSA basic block.
Definition SSA.H:152
Source_Span span
Source region associated with the terminator.
Definition SSA.H:154
Compiler_SSA_Value_Id return_value
Return value, when relevant.
Definition SSA.H:156
DynArray< Compiler_SSA_Block_Id > successors
Successors in deterministic order.
Definition SSA.H:157
Compiler_IR_Terminator_Kind kind
Terminator category.
Definition SSA.H:153
Compiler_SSA_Value_Id condition_value
Branch condition, when relevant.
Definition SSA.H:155
Validation report for one SSA function or module.
Definition SSA.H:282
DynArray< std::string > warnings
Non-fatal observations.
Definition SSA.H:285
DynArray< std::string > errors
Hard validation failures.
Definition SSA.H:284
bool valid
Whether all hard validation checks passed.
Definition SSA.H:283
Half-open byte range inside a source file.
Definition ah-source.H:100
Lazy and scalable dynamic array implementation.
ofstream output
Definition writeHeap.C:215