Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Compiler_CFG.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
55#ifndef COMPILER_CFG_H
56#define COMPILER_CFG_H
57
58#include <limits>
59#include <sstream>
60#include <string>
61#include <utility>
62
63#include <Compiler_HIR_Model.H>
64#include <ah-arena.H>
65#include <ah-errors.H>
66#include <tpl_dynArray.H>
67
68namespace Aleph {
69
71using Compiler_CFG_Block_Id = size_t;
72
78{
79 return std::numeric_limits<Compiler_CFG_Block_Id>::max();
80}
81
88{
89 None,
90 Jump,
91 Branch,
92 Return,
93 Exit,
95};
96
103{
104 switch (kind)
105 {
107 return "None";
109 return "Jump";
111 return "Branch";
113 return "Return";
115 return "Exit";
117 return "Unreachable";
118 }
119
120 return "Unknown";
121}
122
135
157
178
185
191{
194 {
195 void *ptr = nullptr;
196 void (*destroy)(void *) noexcept = nullptr;
197 };
198
201
202public:
204 explicit Compiler_CFG_Context(const size_t arena_size = AhArenaAllocator::DEFAULT_SIZE)
205 : arena_(arena_size)
206 {}
207
210
216
218 template <typename T, typename... Args>
220 {
221 T *ptr = allocate<T>(arena_, std::forward<Args>(args)...);
222 ah_runtime_error_unless(ptr != nullptr) << "Compiler_CFG_Context: arena allocation failed";
223
224 owned_.append({ptr,
225 [](void *raw) noexcept
226 {
227 static_cast<T *>(raw)->~T();
228 }});
229 return ptr;
230 }
231
234 {
235 for (size_t i = owned_.size(); i > 0; --i)
236 {
237 auto &[ptr, destroy] = owned_.access(i - 1);
238 if (destroy != nullptr and ptr != nullptr)
239 destroy(ptr);
240 }
241 owned_.clear();
242 arena_.reset();
243 }
244
247 {
248 return arena_.allocated_size();
249 }
250};
251
259
260namespace Compiler_CFG_Detail {
261
263inline std::string block_name(const Compiler_CFG_Block_Id id)
264{
265 return "B" + std::to_string(id);
266}
267
270 const Compiler_CFG_Block_Id id) noexcept
271{
272 for (size_t i = 0; i < ids.size(); ++i)
273 if (ids.access(i) == id)
274 return true;
275 return false;
276}
277
279inline void append_indented_text(std::ostream &out, const std::string &text, const size_t indent)
280{
281 const std::string padding(indent, ' ');
282 size_t begin = 0;
283 while (begin < text.size())
284 {
285 const auto end = text.find('\n', begin);
286 if (end == std::string::npos)
287 {
288 out << padding << text.substr(begin) << '\n';
289 return;
290 }
291
292 if (end > begin)
293 out << padding << text.substr(begin, end - begin) << '\n';
294 else
295 out << padding << '\n';
296 begin = end + 1;
297 }
298}
299
301inline void append_hir_stmt_dump(std::ostream &out,
302 const Compiler_HIR_Stmt *stmt,
303 const Compiler_Type_Context *types,
304 const size_t indent)
305{
306 if (types != nullptr)
307 append_indented_text(out, compiler_dump_hir_stmt(stmt, *types), indent);
308 else
309 {
312 }
313}
314
316inline void append_hir_expr_dump(std::ostream &out,
317 const Compiler_HIR_Expr *expr,
318 const Compiler_Type_Context *types,
319 const size_t indent)
320{
321 if (types != nullptr)
322 append_indented_text(out, compiler_dump_hir_expr(expr, *types), indent);
323 else
324 {
327 }
328}
329
331inline bool is_linear_stmt(const Compiler_HIR_Stmt *stmt) noexcept
332{
333 if (stmt == nullptr)
334 return false;
335
336 return stmt->kind == Compiler_HIR_Stmt_Kind::Eval or stmt->kind == Compiler_HIR_Stmt_Kind::Let
338}
339} // namespace Compiler_CFG_Detail
340
347{
354
356 const Compiler_Type_Context *types_ = nullptr;
357 size_t if_counter_ = 0;
358 size_t while_counter_ = 0;
359 size_t dead_counter_ = 0;
360
363 {
364 return function->blocks.access(id);
365 }
366
368 static void merge_span(Source_Span &dst, const Source_Span &src)
369 {
370 if (not src.is_valid())
371 return;
372 if (not dst.is_valid())
373 {
374 dst = src;
375 return;
376 }
378 }
379
381 Compiler_CFG_Block_Id create_block(Compiler_CFG_Function *function, std::string label) const
382 {
384 block.id = function->blocks.size();
385 block.label = std::move(label);
386 function->blocks.append(std::move(block));
387 return function->blocks.size() - 1;
388 }
389
392 const Compiler_CFG_Block_Id from,
393 const Compiler_CFG_Block_Id to) const
394 {
395 auto &src = block(function, from);
396 auto &dst = block(function, to);
397
398 if (not Compiler_CFG_Detail::contains_block_id(src.terminator.successors, to))
399 src.terminator.successors.append(to);
400 if (not Compiler_CFG_Detail::contains_block_id(dst.predecessors, from))
401 dst.predecessors.append(from);
402 }
403
406 const Compiler_CFG_Block_Id id,
407 const Compiler_HIR_Stmt *stmt) const
408 {
409 auto &blk = block(function, id);
410 blk.statements.append(stmt);
411 merge_span(blk.span, stmt->span);
412 }
413
416 const Compiler_CFG_Block_Id from,
417 const Compiler_CFG_Block_Id to,
418 const Source_Span &span = {}) const
419 {
420 auto &blk = block(function, from);
421 ah_runtime_error_unless(not blk.is_terminated()) << "Compiler_CFG_Lowering: block already terminated";
422 blk.terminator.kind = Compiler_CFG_Terminator_Kind::Jump;
423 blk.terminator.span = span;
424 merge_span(blk.span, span);
425 add_edge(function, from, to);
426 }
427
430 const Compiler_CFG_Block_Id from,
431 const Compiler_HIR_Expr *condition,
434 {
435 auto &blk = block(function, from);
436 ah_runtime_error_unless(not blk.is_terminated()) << "Compiler_CFG_Lowering: block already terminated";
438 blk.terminator.condition = condition;
439 blk.terminator.span = condition != nullptr ? condition->span : Source_Span();
440 merge_span(blk.span, blk.terminator.span);
441 add_edge(function, from, then_block);
442 add_edge(function, from, else_block);
443 }
444
447 const Compiler_CFG_Block_Id from,
449 const Source_Span &span = {}) const
450 {
451 auto &blk = block(function, from);
452 ah_runtime_error_unless(not blk.is_terminated()) << "Compiler_CFG_Lowering: block already terminated";
454 blk.terminator.value = value;
455 blk.terminator.span = span.is_valid() ? span : (value != nullptr ? value->span : Source_Span());
456 merge_span(blk.span, blk.terminator.span);
457 add_edge(function, from, function->exit_block);
458 }
459
462 {
463 auto &blk = block(function, id);
464 blk.terminator.kind = Compiler_CFG_Terminator_Kind::Exit;
465 }
466
469 const Compiler_CFG_Block_Id id,
470 const Source_Span &span = {}) const
471 {
472 auto &blk = block(function, id);
473 ah_runtime_error_unless(not blk.is_terminated()) << "Compiler_CFG_Lowering: block already terminated";
475 blk.terminator.span = span;
476 merge_span(blk.span, span);
477 }
478
481 Compiler_CFG_Block_Id current)
482 {
483 if (current != compiler_cfg_invalid_block_id())
484 return current;
485 return create_block(function, "dead." + std::to_string(dead_counter_++));
486 }
487
490 const Compiler_HIR_Stmt *stmt,
491 Compiler_CFG_Block_Id current,
493 {
494 if (stmt == nullptr)
495 return current;
496
498 {
499 current = ensure_current_block(function, current);
500 append_linear_stmt(function, current, stmt);
501 return current;
502 }
503
504 switch (stmt->kind)
505 {
507 {
508 const auto *node = static_cast<const Compiler_HIR_Block_Stmt *>(stmt);
509 for (size_t i = 0; i < node->statements.size(); ++i)
510 current = lower_stmt(function, node->statements.access(i), current, loops);
511 return current;
512 }
513
515 {
516 const auto *node = static_cast<const Compiler_HIR_Return_Stmt *>(stmt);
517 current = ensure_current_block(function, current);
518 set_return(function, current, node->value, node->span);
520 }
521
523 {
524 const auto *node = static_cast<const Compiler_HIR_If_Stmt *>(stmt);
525 current = ensure_current_block(function, current);
526
527 const auto id = if_counter_++;
528 const auto then_block = create_block(function, "if.then." + std::to_string(id));
529 const auto join_block = create_block(function, "if.end." + std::to_string(id));
530
532 if (node->else_branch != nullptr)
533 else_block = create_block(function, "if.else." + std::to_string(id));
534
535 set_branch(function, current, node->condition, then_block, else_block);
536
537 auto then_end = lower_stmt(function, node->then_branch, then_block, loops);
539 set_jump(function, then_end, join_block);
540
541 if (node->else_branch != nullptr)
542 {
543 auto else_end = lower_stmt(function, node->else_branch, else_block, loops);
545 set_jump(function, else_end, join_block);
546 }
547
548 return join_block;
549 }
550
552 {
553 const auto *node = static_cast<const Compiler_HIR_While_Stmt *>(stmt);
554 current = ensure_current_block(function, current);
555
556 const auto id = while_counter_++;
557 const auto cond_block = create_block(function, "while.cond." + std::to_string(id));
558 const auto body_block = create_block(function, "while.body." + std::to_string(id));
559 const auto end_block = create_block(function, "while.end." + std::to_string(id));
560
561 set_jump(function, current, cond_block, node->keyword_span);
562 set_branch(function, cond_block, node->condition, body_block, end_block);
563
564 loops.append({end_block, cond_block});
565 auto body_end = lower_stmt(function, node->body, body_block, loops);
566 (void) loops.pop();
568 set_jump(function, body_end, cond_block);
569
570 return end_block;
571 }
572
574 {
575 current = ensure_current_block(function, current);
576 if (loops.is_empty())
577 {
578 set_unreachable(function, current, stmt->span);
580 }
581
582 set_jump(function, current, loops.access(loops.size() - 1).break_block, stmt->span);
584 }
585
587 {
588 current = ensure_current_block(function, current);
589 if (loops.is_empty())
590 {
591 set_unreachable(function, current, stmt->span);
593 }
594
595 set_jump(function, current, loops.access(loops.size() - 1).continue_block, stmt->span);
597 }
598
602 return current;
603 }
604
605 return current;
606 }
607
610 const Source_Span &span,
611 const Compiler_Type_Id type_id,
612 const Compiler_HIR_Function *source_function,
613 const DynArray<Compiler_HIR_Stmt *> &statements)
614 {
615 auto *function = cfg_->make<Compiler_CFG_Function>();
616 function->name = name;
617 function->span = span;
618 function->type_id = type_id;
619 function->source_function = source_function;
620 function->entry_block = create_block(function, "entry");
621 function->exit_block = create_block(function, "exit");
622
624 Compiler_CFG_Block_Id current = function->entry_block;
625 for (size_t i = 0; i < statements.size(); ++i)
626 current = lower_stmt(function, statements.access(i), current, loops);
627
628 if (current != compiler_cfg_invalid_block_id() and not block(function, current).is_terminated())
629 set_jump(function, current, function->exit_block);
630
631 set_exit(function, function->exit_block);
632 return function;
633 }
634
635public:
642 const Compiler_Type_Context *type_ctx = nullptr) noexcept
643 : cfg_(&ctx), types_(type_ctx)
644 {}
645
648 {
649 return types_;
650 }
651
658 {
659 ah_runtime_error_unless(function != nullptr) << "Compiler_CFG_Lowering::lower_function(): null HIR function";
660 if (function->body == nullptr)
661 {
663 return lower_statement_sequence(function->name, function->span, function->type_id,
664 function, empty_stmts);
665 }
666 return lower_statement_sequence(function->name,
667 function->span,
668 function->type_id,
669 function,
670 function->body->statements);
671 }
672
679 {
680 ah_runtime_error_unless(module != nullptr) << "Compiler_CFG_Lowering::lower_module(): null HIR module";
681
682 auto *result = cfg_->make<Compiler_CFG_Module>();
683 for (size_t i = 0; i < module->functions.size(); ++i)
684 result->functions.append(lower_function(module->functions.access(i)));
685
686 if (not module->statements.is_empty())
687 result->top_level = lower_statement_sequence("<top-level>",
688 module->span,
689 0,
690 nullptr,
691 module->statements);
692 return result;
693 }
694};
695
705{
707
708 auto fail = [&report](const std::string &msg)
709 {
710 report.valid = false;
711 report.errors.append(msg);
712 };
713
714 if (function.blocks.is_empty())
715 {
716 fail("CFG function '" + function.name + "' has no basic blocks");
717 return report;
718 }
719
720 if (function.entry_block >= function.blocks.size())
721 fail("CFG function '" + function.name + "' has invalid entry block");
722 if (function.exit_block >= function.blocks.size())
723 fail("CFG function '" + function.name + "' has invalid exit block");
724 if (not report.valid)
725 return report;
726
727 for (size_t i = 0; i < function.blocks.size(); ++i)
728 {
729 const auto &block = function.blocks.access(i);
730 if (block.id != i)
731 fail("CFG function '" + function.name + "' has mismatched block id at "
733
735 fail("CFG block " + Compiler_CFG_Detail::block_name(block.id) + " in function '" + function.name
736 + "' is unterminated");
737
738 switch (block.terminator.kind)
739 {
741 if (block.terminator.successors.size() != 1)
742 fail("Jump terminator in " + Compiler_CFG_Detail::block_name(block.id)
743 + " must have exactly one successor");
744 break;
745
747 if (block.terminator.condition == nullptr)
748 fail("Branch terminator in " + Compiler_CFG_Detail::block_name(block.id)
749 + " is missing its condition");
750 if (block.terminator.successors.size() != 2)
751 fail("Branch terminator in " + Compiler_CFG_Detail::block_name(block.id)
752 + " must have exactly two successors");
753 break;
754
756 if (block.terminator.successors.size() != 1)
757 fail("Return terminator in " + Compiler_CFG_Detail::block_name(block.id)
758 + " must point to the canonical exit block");
759 else if (block.terminator.successors.access(0) != function.exit_block)
760 fail("Return terminator in " + Compiler_CFG_Detail::block_name(block.id)
761 + " must point to the canonical exit block");
762 break;
763
765 if (block.id != function.exit_block)
766 fail("Only the canonical exit block may use the Exit terminator in function '" + function.name
767 + "'");
768 if (not block.terminator.successors.is_empty())
769 fail("Exit block " + Compiler_CFG_Detail::block_name(block.id) + " must not have successors");
770 break;
771
774 break;
775 }
776
777 for (size_t j = 0; j < block.terminator.successors.size(); ++j)
778 {
779 const auto succ = block.terminator.successors.access(j);
780 if (succ >= function.blocks.size())
781 {
782 fail("CFG block " + Compiler_CFG_Detail::block_name(block.id)
783 + " references invalid successor " + Compiler_CFG_Detail::block_name(succ));
784 continue;
785 }
786
787 const auto &succ_block = function.blocks.access(succ);
789 fail("CFG edge " + Compiler_CFG_Detail::block_name(block.id) + " -> "
790 + Compiler_CFG_Detail::block_name(succ) + " is missing from predecessor lists");
791 }
792
793 for (size_t j = 0; j < block.predecessors.size(); ++j)
794 {
795 const auto pred = block.predecessors.access(j);
796 if (pred >= function.blocks.size())
797 {
798 fail("CFG block " + Compiler_CFG_Detail::block_name(block.id)
799 + " references invalid predecessor " + Compiler_CFG_Detail::block_name(pred));
800 continue;
801 }
802
803 const auto &pred_block = function.blocks.access(pred);
804 if (not Compiler_CFG_Detail::contains_block_id(pred_block.terminator.successors, block.id))
805 fail("CFG predecessor edge " + Compiler_CFG_Detail::block_name(pred) + " -> "
806 + Compiler_CFG_Detail::block_name(block.id) + " is missing from successor lists");
807 }
808 }
809
810 // Reachability analysis
812 for (size_t i = 0; i < function.blocks.size(); ++i)
813 reachable.append(false);
814
816 worklist.append(function.entry_block);
817 while (not worklist.is_empty())
818 {
819 const auto current = worklist.pop();
820 if (reachable.access(current))
821 continue;
822 reachable.access(current) = true;
823
824 const auto &block = function.blocks.access(current);
825 for (size_t i = 0; i < block.terminator.successors.size(); ++i)
826 {
827 const auto succ = block.terminator.successors.access(i);
828 if (succ < function.blocks.size() and not reachable.access(succ))
829 worklist.append(succ);
830 }
831 }
832
833 for (size_t i = 0; i < function.blocks.size(); ++i)
834 if (not reachable.access(i))
835 report.warnings.append("CFG block " + Compiler_CFG_Detail::block_name(i) + " in function '"
836 + function.name + "' is unreachable from entry");
837
838 return report;
839}
840
847{
849
850 auto merge = [&report](const Compiler_CFG_Validation_Report &child)
851 {
852 if (not child.valid)
853 report.valid = false;
854 for (size_t i = 0; i < child.errors.size(); ++i)
855 report.errors.append(child.errors.access(i));
856 for (size_t i = 0; i < child.warnings.size(); ++i)
857 report.warnings.append(child.warnings.access(i));
858 };
859
860 for (size_t i = 0; i < module.functions.size(); ++i)
861 merge(validate_cfg_function(*module.functions.access(i)));
862 if (module.top_level != nullptr)
864 return report;
865}
866
873inline std::string compiler_dump_cfg_function(const Compiler_CFG_Function *function,
874 const Compiler_Type_Context *types = nullptr)
875{
876 std::ostringstream out;
877 if (function == nullptr)
878 {
879 out << "<null-cfg-function>\n";
880 return out.str();
881 }
882
883 out << "CFGFunction(" << function->name << ")";
884 if (types != nullptr and function->type_id != 0)
885 out << ": " << types->to_string(function->type_id);
886 out << '\n';
887 out << " Entry: " << Compiler_CFG_Detail::block_name(function->entry_block) << '\n';
888 out << " Exit: " << Compiler_CFG_Detail::block_name(function->exit_block) << '\n';
889
890 for (size_t i = 0; i < function->blocks.size(); ++i)
891 {
892 const auto &block = function->blocks.access(i);
893 out << " Block " << Compiler_CFG_Detail::block_name(block.id) << " [" << block.label << "]\n";
894
895 if (block.statements.is_empty())
896 out << " Statements: <none>\n";
897 else
898 {
899 out << " Statements:\n";
900 for (size_t j = 0; j < block.statements.size(); ++j)
901 Compiler_CFG_Detail::append_hir_stmt_dump(out, block.statements.access(j), types, 6);
902 }
903
904 out << " Terminator: " << compiler_cfg_terminator_kind_name(block.terminator.kind) << '\n';
905
907 and block.terminator.condition != nullptr)
908 {
909 out << " Condition:\n";
910 Compiler_CFG_Detail::append_hir_expr_dump(out, block.terminator.condition, types, 8);
911 }
913 and block.terminator.value != nullptr)
914 {
915 out << " Value:\n";
917 }
918
919 out << " Successors:";
920 if (block.terminator.successors.is_empty())
921 out << " <none>\n";
922 else
923 {
924 for (size_t j = 0; j < block.terminator.successors.size(); ++j)
925 {
926 out << (j == 0 ? " " : ", ");
928 }
929 out << '\n';
930 }
931
932 out << " Predecessors:";
933 if (block.predecessors.is_empty())
934 out << " <none>\n";
935 else
936 {
937 for (size_t j = 0; j < block.predecessors.size(); ++j)
938 {
939 out << (j == 0 ? " " : ", ");
941 }
942 out << '\n';
943 }
944 }
945
946 return out.str();
947}
948
955inline std::string compiler_dump_cfg_module(const Compiler_CFG_Module *module,
956 const Compiler_Type_Context *types = nullptr)
957{
958 std::ostringstream out;
959 if (module == nullptr)
960 {
961 out << "<null-cfg-module>\n";
962 return out.str();
963 }
964
965 out << "CFGModule\n";
966 for (size_t i = 0; i < module->functions.size(); ++i)
968 out, compiler_dump_cfg_function(module->functions.access(i), types), 2);
969 if (module->top_level != nullptr)
971 return out.str();
972}
973
974} // namespace Aleph
975
976#endif // COMPILER_CFG_H
Reusable typed high-level IR model independent from any concrete frontend.
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 value
Definition ca-c-api.h:116
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
size_t allocated_size() const noexcept
Get total bytes currently allocated.
Definition ah-arena.H:395
static constexpr size_t DEFAULT_SIZE
Default arena size (1 MB).
Definition ah-arena.H:133
Arena-backed ownership context for CFG objects.
void reset() noexcept
Resets the arena and destroys all tracked objects.
DynArray< Owned_Object > owned_
size_t allocated_size() const noexcept
Returns currently used bytes.
Compiler_CFG_Context(const Compiler_CFG_Context &)=delete
Compiler_CFG_Context & operator=(const Compiler_CFG_Context &)=delete
~Compiler_CFG_Context() noexcept
Cleans up the context and all managed objects.
Compiler_CFG_Context(const size_t arena_size=AhArenaAllocator::DEFAULT_SIZE)
Constructs a CFG context.
T * make(Args &&...args)
Allocates and constructs an object in the arena.
Transformation engine that lowers typed HIR into reusable CFGs.
void add_edge(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id from, const Compiler_CFG_Block_Id to) const
Adds a directed edge from one block to another.
void set_jump(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id from, const Compiler_CFG_Block_Id to, const Source_Span &span={}) const
Sets a Jump terminator on a block.
Compiler_CFG_Function * lower_function(const Compiler_HIR_Function *function)
Lowers one HIR function into a CFG.
void set_return(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id from, const Compiler_HIR_Expr *value, const Source_Span &span={}) const
Sets a Return terminator on a block.
Compiler_CFG_Context * cfg_
CFG owner.
static void merge_span(Source_Span &dst, const Source_Span &src)
Utility to merge a source span into a destination span.
void set_branch(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id from, const Compiler_HIR_Expr *condition, const Compiler_CFG_Block_Id then_block, const Compiler_CFG_Block_Id else_block) const
Sets a Branch terminator on a block.
Compiler_CFG_Module * lower_module(const Compiler_HIR_Module *module)
Lowers one HIR module into function CFGs and optional top-level CFG.
void set_exit(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id id) const
Sets an Exit terminator (only for the canonical exit block).
size_t dead_counter_
ID generator for unreachable blocks.
Compiler_CFG_Block_Id ensure_current_block(Compiler_CFG_Function *function, Compiler_CFG_Block_Id current)
Ensures a valid current block exists, creating a "dead" one if needed.
Compiler_CFG_Lowering(Compiler_CFG_Context &ctx, const Compiler_Type_Context *type_ctx=nullptr) noexcept
Builds a CFG lowerer over the specified context.
Compiler_CFG_Function * lower_statement_sequence(const std::string &name, const Source_Span &span, const Compiler_Type_Id type_id, const Compiler_HIR_Function *source_function, const DynArray< Compiler_HIR_Stmt * > &statements)
Lowers a sequence of statements into a new CFG function.
Compiler_CFG_Block & block(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id id) const
Accesses a block by ID within a function.
void set_unreachable(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id id, const Source_Span &span={}) const
Sets an Unreachable terminator on a block.
size_t while_counter_
ID generator for loop blocks.
const Compiler_Type_Context * type_context() const noexcept
Returns the optional type context used by this lowerer.
size_t if_counter_
ID generator for conditional blocks.
const Compiler_Type_Context * types_
Type metadata source.
void append_linear_stmt(Compiler_CFG_Function *function, const Compiler_CFG_Block_Id id, const Compiler_HIR_Stmt *stmt) const
Appends a linear statement to a block and updates its span.
Compiler_CFG_Block_Id create_block(Compiler_CFG_Function *function, std::string label) const
Creates a new basic block in the given function.
Compiler_CFG_Block_Id lower_stmt(Compiler_CFG_Function *function, const Compiler_HIR_Stmt *stmt, Compiler_CFG_Block_Id current, DynArray< Loop_Targets > &loops)
Recursively lowers an HIR statement into the CFG.
Context owning all compiler type nodes.
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 contains_block_id(const DynArray< Compiler_CFG_Block_Id > &ids, const Compiler_CFG_Block_Id id) noexcept
Checks if a block ID is present in a list of IDs.
void append_indented_text(std::ostream &out, const std::string &text, const size_t indent)
Appends text to a stream with a consistent indentation.
bool is_linear_stmt(const Compiler_HIR_Stmt *stmt) noexcept
Checks if an HIR statement is linear (non-branching).
void append_hir_stmt_dump(std::ostream &out, const Compiler_HIR_Stmt *stmt, const Compiler_Type_Context *types, const size_t indent)
Dumps a HIR statement to the stream for debugging.
std::string block_name(const Compiler_CFG_Block_Id id)
Generates a standard block label (e.g., "B0").
void append_hir_expr_dump(std::ostream &out, const Compiler_HIR_Expr *expr, const Compiler_Type_Context *types, const size_t indent)
Dumps a HIR expression to the stream for debugging.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Compiler_CFG_Terminator_Kind
Categories of basic block terminators.
@ Jump
Unconditional jump to exactly one successor.
@ Unreachable
Marks code paths that should never be executed (e.g., after a break).
@ None
Unset or invalid terminator.
@ Return
Function exit with an optional return value.
@ Branch
Conditional branch to two successors (true and false targets).
@ Exit
Sentinel terminator for the canonical exit block.
@ Return
Return one register value.
@ Branch
pc <- condition ? true_target : false_target
Source_Span compiler_hir_merge_spans(const Source_Span &lhs, const Source_Span &rhs) noexcept
Merges two spans without depending on the parser-oriented AST layer.
constexpr Compiler_CFG_Block_Id compiler_cfg_invalid_block_id() noexcept
Returns the sentinel value for an invalid block ID.
std::string compiler_dump_cfg_function(const Compiler_CFG_Function *function, const Compiler_Type_Context *types=nullptr)
Dumps a single CFG function in text format for debugging.
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
size_t Compiler_Type_Id
Compiler_CFG_Validation_Report validate_cfg_function(const Compiler_CFG_Function &function)
Validates one lowered CFG function structurally.
Itor3 merge(Itor1 source1Beg, Itor1 source1End, Itor2 source2Beg, Itor2 source2End, Itor3 destBeg)
Merge two sorted ranges.
Definition ahAlgo.H:1410
Compiler_CFG_Validation_Report validate_cfg_module(const Compiler_CFG_Module &module)
Validates all CFGs in a lowered module.
std::string compiler_dump_cfg_module(const Compiler_CFG_Module *module, const Compiler_Type_Context *types=nullptr)
Dumps a complete CFG module in text format for debugging.
std::string compiler_dump_hir_expr(const Compiler_HIR_Expr *expr, const Compiler_Type_Context &types)
Dumps one HIR expression in a deterministic text format.
const char * compiler_cfg_terminator_kind_name(const Compiler_CFG_Terminator_Kind kind) noexcept
Returns a stable string name for a CFG terminator kind.
size_t Compiler_CFG_Block_Id
Typedef for unique block identifiers within a function.
std::string compiler_dump_hir_stmt(const Compiler_HIR_Stmt *stmt, const Compiler_Type_Context &types)
Dumps one HIR statement in a deterministic text format.
Represents a single basic block in a CFG.
Source_Span span
Span covering all statements and the terminator.
DynArray< const Compiler_HIR_Stmt * > statements
Ordered list of linear statements.
Compiler_CFG_Block_Id id
Unique identifier in the function.
std::string label
Human-readable debug label (e.g., "entry", "loop.body").
Compiler_CFG_Terminator terminator
How the block ends.
DynArray< Compiler_CFG_Block_Id > predecessors
List of block IDs that jump to this block.
bool is_terminated() const noexcept
Checks if the block has a valid terminator set.
Internal tracker for objects with non-trivial destructors.
CFG representation of a single function or top-level body.
Source_Span span
Overall source span.
Compiler_Type_Id type_id
Return type or function type ID.
bool is_top_level() const noexcept
Checks if this CFG represents the global top-level script.
Compiler_CFG_Block_Id exit_block
Canonical exit block ID.
const Compiler_HIR_Function * source_function
Pointer to original HIR (null for top-level).
std::string name
Function or script name.
DynArray< Compiler_CFG_Block > blocks
All basic blocks belonging to this function.
Compiler_CFG_Block_Id entry_block
Starting block ID.
Internal state for loop lowering.
Aggregates all lowered CFGs for a compilation unit.
Compiler_CFG_Function * top_level
Optional CFG for global statements.
DynArray< Compiler_CFG_Function * > functions
CFGs for all defined functions.
Detailed information about a block's termination.
const Compiler_HIR_Expr * value
Return value (valid if kind is Return).
Source_Span span
Source location of the terminator.
const Compiler_HIR_Expr * condition
Branch condition (valid if kind is Branch).
Compiler_CFG_Terminator_Kind kind
Category of terminator.
DynArray< Compiler_CFG_Block_Id > successors
List of successor block IDs.
Detailed results of a CFG structural validation.
DynArray< std::string > errors
List of critical violations.
bool valid
True if no errors were found.
DynArray< std::string > warnings
List of non-fatal issues (e.g., unreachable blocks).
Structured block statement.
DynArray< Compiler_HIR_Stmt * > statements
Nested statements.
Base class for HIR expressions.
std::string name
Function name.
Compiler_Type_Id type_id
Full function type.
Compiler_HIR_Block_Stmt * body
Function body.
Structured conditional statement.
DynArray< Compiler_HIR_Function * > functions
Lowered top-level functions.
DynArray< Compiler_HIR_Stmt * > statements
Lowered top-level statements.
Source_Span span
Source region associated with the HIR node.
Base class for HIR statements.
Compiler_HIR_Stmt_Kind kind
Runtime node kind.
Structured while-loop statement.
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
std::string label
Deterministic debug label.
Definition SSA.H:165
Compiler_SSA_Terminator terminator
Explicit block terminator.
Definition SSA.H:169
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
Represents a missing value.
Half-open byte range inside a source file.
Definition ah-source.H:100
bool is_valid() const noexcept
Returns whether the span belongs to a registered file.
Definition ah-source.H:106
Lazy and scalable dynamic array implementation.