Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Compiler_IR_Lowering_MVP.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
42#ifndef COMPILER_IR_LOWERING_MVP_H
43#define COMPILER_IR_LOWERING_MVP_H
44
45#include <Compiler_IR_Model.H>
46
47namespace Aleph {
48namespace Compiler_IR_Detail {
50struct Binding
51{
52 enum class Kind
53 {
57 };
58
62};
63
69} // namespace Compiler_IR_Detail
70
73{
75 {
76 Compiler_IR_Module *module = nullptr;
77 const Compiler_HIR_Module *hir = nullptr;
79 const Compiler_Type_Context *types = nullptr;
80 size_t if_counter = 0;
81 size_t while_counter = 0;
82 size_t dead_counter = 0;
84
86 {
87 return *module->functions.access(id);
88 }
89
90 Compiler_IR_Function_Id find_function_id(const std::string &name) const noexcept
91 {
92 for (size_t i = 0; i < function_ids.size(); ++i)
93 if (function_ids.access(i).first == name)
94 return function_ids.access(i).second;
96 }
97
98 Compiler_IR_Global_Slot_Id find_global_slot_id(const std::string &name) const noexcept
99 {
100 for (size_t i = 0; i < module->global_slots.size(); ++i)
101 if (module->global_slots.access(i).name == name)
102 return i;
103 return compiler_ir_invalid_id();
104 }
105
107 const Compiler_Type_Id type_id,
108 const Source_Span &span) const
109 {
111 slot.id = module->global_slots.size();
113 slot.name = name;
114 slot.type_id = type_id;
115 slot.span = span;
116 module->global_slots.append(std::move(slot));
117 return module->global_slots.size() - 1;
118 }
119
121 {
122 if (hir_function == nullptr)
123 return compiler_ir_invalid_id();
125 ir_function->id = module->functions.size();
126 ir_function->name = hir_function->name;
127 ir_function->span = hir_function->span;
128 ir_function->type_id = hir_function->type_id;
129 ir_function->source_function = hir_function;
130 ir_function->next_value_id = 0;
131 module->functions.append(ir_function);
133 return ir_function->id;
134 }
135
137 const Compiler_IR_Block_Id id)
138 {
139 return function.blocks.access(id);
140 }
141
142 static void merge_span(Source_Span &dst, const Source_Span &src)
143 {
144 if (not src.is_valid())
145 return;
146 if (not dst.is_valid())
147 {
148 dst = src;
149 return;
150 }
152 }
153
155 {
157 block.id = function.blocks.size();
158 block.label = std::move(label);
159 function.blocks.append(std::move(block));
160 return function.blocks.size() - 1;
161 }
162
164 const Compiler_IR_Block_Id from,
165 const Compiler_IR_Block_Id to)
166 {
167 auto &src = block(function, from);
168 auto &dst = block(function, to);
169 if (not Compiler_IR_Detail::contains_block_id(src.terminator.successors, to))
170 src.terminator.successors.append(to);
171 if (not Compiler_IR_Detail::contains_block_id(dst.predecessors, from))
172 dst.predecessors.append(from);
173 }
174
180
182 const Compiler_IR_Slot_Kind kind,
183 const std::string &name,
184 const Compiler_Type_Id type_id,
185 const Source_Span &span)
186 {
188 slot.id = function.local_slots.size();
189 slot.kind = kind;
190 slot.name = name;
191 slot.type_id = type_id;
192 slot.span = span;
193 function.local_slots.append(std::move(slot));
194 return function.local_slots.size() - 1;
195 }
196
198 const Compiler_IR_Block_Id block_id,
200 {
201 auto &blk = block(function, block_id);
202 merge_span(blk.span, instruction.span);
203 blk.instructions.append(std::move(instruction));
204 }
205
207 const Compiler_IR_Block_Id from,
208 const Compiler_IR_Block_Id to,
209 const Source_Span &span = {})
210 {
211 auto &blk = block(function, from);
212 ah_runtime_error_unless(not blk.is_terminated())
213 << "Compiler_IR_Lowering: block already terminated";
214 blk.terminator.kind = Compiler_IR_Terminator_Kind::Jump;
215 blk.terminator.span = span;
216 merge_span(blk.span, span);
217 add_edge(function, from, to);
218 }
219
221 const Compiler_IR_Block_Id from,
222 const Compiler_IR_Value_Id condition,
223 const Source_Span &span,
226 {
227 auto &blk = block(function, from);
228 ah_runtime_error_unless(not blk.is_terminated())
229 << "Compiler_IR_Lowering: block already terminated";
231 blk.terminator.condition_value = condition;
232 blk.terminator.span = span;
233 merge_span(blk.span, span);
236 }
237
239 const Compiler_IR_Block_Id from,
241 const Source_Span &span = {})
242 {
243 auto &blk = block(function, from);
244 ah_runtime_error_unless(not blk.is_terminated())
245 << "Compiler_IR_Lowering: block already terminated";
247 blk.terminator.return_value = value;
248 blk.terminator.span = span;
249 merge_span(blk.span, span);
251 }
252
254 {
255 auto &blk = block(function, id);
256 blk.terminator.kind = Compiler_IR_Terminator_Kind::Exit;
257 }
258
260 const Compiler_IR_Block_Id id,
261 const Source_Span &span = {})
262 {
263 auto &blk = block(function, id);
264 ah_runtime_error_unless(not blk.is_terminated())
265 << "Compiler_IR_Lowering: block already terminated";
267 blk.terminator.span = span;
268 merge_span(blk.span, span);
269 }
270
272 {
273 if (current != compiler_ir_invalid_id())
274 return current;
275 return create_block(function, "dead." + std::to_string(dead_counter++));
276 }
277
279 {
280 scopes.append({});
281 }
282
284 {
286 << "Compiler_IR_Lowering: attempted to leave empty scope stack";
287 (void) scopes.pop();
288 }
289
291 const std::string &name,
292 const Compiler_IR_Detail::Binding &binding)
293 {
295 << "Compiler_IR_Lowering: no active scope frame";
296 scopes.access(scopes.size() - 1).bindings.append({name, binding});
297 }
298
300 const std::string &name)
301 {
302 for (size_t i = scopes.size(); i > 0; --i)
303 {
304 const auto &[bindings] = scopes.access(i - 1);
305 for (size_t j = bindings.size(); j > 0; --j)
306 if (bindings.access(j - 1).first == name)
307 return bindings.access(j - 1).second;
308 }
309
310 return {};
311 }
312
314 const Compiler_IR_Block_Id block_id,
315 const Source_Span &span) const
316 {
319 inst.result_id = next_value(function);
320 inst.type_id = types != nullptr ? types->unit_type() : 0;
321 inst.span = span;
322 inst.text = "unit";
323 emit_instruction(function, block_id, std::move(inst));
324 return function.next_value_id;
325 }
326
328 const Compiler_HIR_Expr *expr,
329 const Compiler_IR_Block_Id block_id,
331 {
332 ah_runtime_error_unless(expr != nullptr) << "Compiler_IR_Lowering: null HIR expression";
333
334 switch (expr->kind)
335 {
337 return emit_unit_constant(function, block_id, expr->span);
338
340 {
341 const auto *node = static_cast<const Compiler_HIR_Constant_Expr *>(expr);
344 inst.result_id = next_value(function);
345 inst.type_id = node->type_id;
346 inst.span = node->span;
347 inst.text = node->text;
348 inst.bool_value = node->bool_value;
349 emit_instruction(function, block_id, std::move(inst));
350 return function.next_value_id;
351 }
352
354 {
355 const auto *node = static_cast<const Compiler_HIR_Variable_Expr *>(expr);
356 const auto binding = lookup(scopes, node->name);
357 if (binding.id == compiler_ir_invalid_id())
358 return emit_unit_constant(function, block_id, node->span);
360 inst.type_id = node->type_id;
361 inst.span = node->span;
362
363 switch (binding.kind)
364 {
367 inst.result_id = next_value(function);
368 inst.local_slot_id = binding.id;
369 emit_instruction(function, block_id, std::move(inst));
370 return function.next_value_id;
371
374 inst.result_id = next_value(function);
375 inst.global_slot_id = binding.id;
376 emit_instruction(function, block_id, std::move(inst));
377 return function.next_value_id;
378
381 inst.result_id = next_value(function);
382 inst.function_id = binding.id;
383 emit_instruction(function, block_id, std::move(inst));
384 return function.next_value_id;
385 }
386
387 return emit_unit_constant(function, block_id, node->span);
388 }
389
391 {
392 const auto *node = static_cast<const Compiler_HIR_Unary_Expr *>(expr);
393 const auto operand = lower_expr(function, node->operand, block_id, scopes);
396 inst.result_id = next_value(function);
397 inst.type_id = node->type_id;
398 inst.span = node->span;
399 inst.op = node->op;
400 inst.operands.append(operand);
401 emit_instruction(function, block_id, std::move(inst));
402 return function.next_value_id;
403 }
404
406 {
407 const auto *node = static_cast<const Compiler_HIR_Binary_Expr *>(expr);
409
411 {
412 ah_runtime_error_unless(node->left != nullptr
413 and node->left->kind == Compiler_HIR_Expr_Kind::Variable)
414 << "Compiler_IR_Lowering: assignment expects variable lhs";
415
416 const auto *lhs = static_cast<const Compiler_HIR_Variable_Expr *>(node->left);
417 const auto binding = lookup(scopes, lhs->name);
418
419 if (binding.id == compiler_ir_invalid_id()
421 return emit_unit_constant(function, block_id, node->span);
422
424 if (node->op == Compiler_Operator_Kind::Assign)
425 stored_value = lower_expr(function, node->right, block_id, scopes);
426 else
427 {
431 load.type_id = lhs->type_id;
432 load.span = lhs->span;
434 load.global_slot_id = binding.id;
435 else
436 load.local_slot_id = binding.id;
437 emit_instruction(function, block_id, std::move(load));
438 const auto lhs_value = function.next_value_id;
439 const auto rhs_value = lower_expr(function, node->right, block_id, scopes);
440
443 binary.result_id = next_value(function);
444 binary.type_id = node->type_id;
445 binary.span = node->span;
447 binary.operands.append(lhs_value);
448 binary.operands.append(rhs_value);
449 emit_instruction(function, block_id, std::move(binary));
451 }
452
455 store.type_id = node->type_id;
456 store.span = node->span;
459 store.global_slot_id = binding.id;
460 else
461 store.local_slot_id = binding.id;
462 emit_instruction(function, block_id, std::move(store));
463 return stored_value;
464 }
465
466 const auto lhs = lower_expr(function, node->left, block_id, scopes);
467 const auto rhs = lower_expr(function, node->right, block_id, scopes);
468
471 inst.result_id = next_value(function);
472 inst.type_id = node->type_id;
473 inst.span = node->span;
474 inst.op = node->op;
475 inst.operands.append(lhs);
476 inst.operands.append(rhs);
477 emit_instruction(function, block_id, std::move(inst));
478 return function.next_value_id;
479 }
480
482 {
483 const auto *node = static_cast<const Compiler_HIR_Call_Expr *>(expr);
484 const auto callee = lower_expr(function, node->callee, block_id, scopes);
486 for (size_t i = 0; i < node->arguments.size(); ++i)
487 arguments.append(lower_expr(function, node->arguments.access(i), block_id, scopes));
488
491 inst.result_id = next_value(function);
492 inst.type_id = node->type_id;
493 inst.span = node->span;
494 inst.operands.append(callee);
495 for (size_t i = 0; i < arguments.size(); ++i)
496 inst.operands.append(arguments.access(i));
497 emit_instruction(function, block_id, std::move(inst));
498 return function.next_value_id;
499 }
500 }
501
502 return emit_unit_constant(function, block_id, expr->span);
503 }
504
506 const Compiler_HIR_Stmt *stmt,
507 Compiler_IR_Block_Id current,
509 DynArray<std::pair<Compiler_IR_Block_Id, Compiler_IR_Block_Id>> &loops)
510 {
511 if (stmt == nullptr)
512 return current;
513
514 switch (stmt->kind)
515 {
517 current = ensure_block(function, current);
518 (void) emit_unit_constant(function, current, stmt->span);
519 return current;
520
522 {
523 current = ensure_block(function, current);
524 const auto *node = static_cast<const Compiler_HIR_Eval_Stmt *>(stmt);
525 (void) lower_expr(function, node->expr, current, scopes);
526 return current;
527 }
528
530 {
531 current = ensure_block(function, current);
532 const auto *node = static_cast<const Compiler_HIR_Let_Stmt *>(stmt);
533
535 if (function.is_top_level() and scopes.size() == 2)
536 {
538 binding.id = find_global_slot_id(node->name);
539 binding.type_id = node->binding_type;
540 }
541 else
542 {
544 binding.id = add_local_slot(function,
546 node->name,
547 node->binding_type,
548 node->name_span);
549 binding.type_id = node->binding_type;
550 }
551
552 const auto value = node->initializer != nullptr
553 ? lower_expr(function, node->initializer, current, scopes)
554 : emit_unit_constant(function, current, node->span);
555
558 store.type_id = node->binding_type;
559 store.span = node->span;
560 store.operands.append(value);
562 store.global_slot_id = binding.id;
563 else
564 store.local_slot_id = binding.id;
565 emit_instruction(function, current, std::move(store));
566
567 bind(scopes, node->name, binding);
568 return current;
569 }
570
572 {
573 current = ensure_block(function, current);
574 const auto *node = static_cast<const Compiler_HIR_Return_Stmt *>(stmt);
575 const auto value = node->value != nullptr
576 ? lower_expr(function, node->value, current, scopes)
577 : emit_unit_constant(function, current, node->span);
578 set_return(function, current, value, node->span);
579 return compiler_ir_invalid_id();
580 }
581
583 {
584 const auto *node = static_cast<const Compiler_HIR_Block_Stmt *>(stmt);
585 enter_scope(scopes);
586 for (size_t i = 0; i < node->statements.size(); ++i)
587 current = lower_stmt(function, node->statements.access(i), current, scopes, loops);
588 leave_scope(scopes);
589 return current;
590 }
591
593 {
594 current = ensure_block(function, current);
595 const auto *node = static_cast<const Compiler_HIR_If_Stmt *>(stmt);
596
597 const auto condition = lower_expr(function, node->condition, current, scopes);
598 const auto id = if_counter++;
599 const auto then_block = create_block(function, "if.then." + std::to_string(id));
600 const auto join_block = create_block(function, "if.end." + std::to_string(id));
602 if (node->else_branch != nullptr)
603 else_block = create_block(function, "if.else." + std::to_string(id));
604
606 current,
607 condition,
608 node->condition != nullptr ? node->condition->span : node->span,
610 else_block);
611
612 auto then_end = lower_stmt(function, node->then_branch, then_block, scopes, loops);
615
616 if (node->else_branch != nullptr)
617 {
618 auto else_end = lower_stmt(function, node->else_branch, else_block, scopes, loops);
621 }
622
623 return join_block;
624 }
625
627 {
628 current = ensure_block(function, current);
629 const auto *node = static_cast<const Compiler_HIR_While_Stmt *>(stmt);
630 const auto id = while_counter++;
631 const auto cond_block = create_block(function, "while.cond." + std::to_string(id));
632 const auto body_block = create_block(function, "while.body." + std::to_string(id));
633 const auto end_block = create_block(function, "while.end." + std::to_string(id));
634
635 set_jump(function, current, cond_block, node->keyword_span);
636 const auto condition = lower_expr(function, node->condition, cond_block, scopes);
639 condition,
640 node->condition != nullptr ? node->condition->span : node->span,
642 end_block);
643
644 loops.append({end_block, cond_block});
645 auto body_end = lower_stmt(function, node->body, body_block, scopes, loops);
646 (void) loops.pop();
647
650 return end_block;
651 }
652
654 {
655 current = ensure_block(function, current);
656 if (loops.is_empty())
657 {
658 set_unreachable(function, current, stmt->span);
659 return compiler_ir_invalid_id();
660 }
661 set_jump(function, current, loops.access(loops.size() - 1).first, stmt->span);
662 return compiler_ir_invalid_id();
663 }
664
666 {
667 current = ensure_block(function, current);
668 if (loops.is_empty())
669 {
670 set_unreachable(function, current, stmt->span);
671 return compiler_ir_invalid_id();
672 }
673 set_jump(function, current, loops.access(loops.size() - 1).second, stmt->span);
674 return compiler_ir_invalid_id();
675 }
676 }
677
678 return current;
679 }
680
682 {
683 enter_scope(scopes);
684 for (size_t i = 0; i < module->global_slots.size(); ++i)
685 bind(scopes,
686 module->global_slots.access(i).name,
687 {Compiler_IR_Detail::Binding::Kind::Global_Slot,
688 i,
689 module->global_slots.access(i).type_id});
690
691 for (size_t i = 0; i < function_ids.size(); ++i)
692 bind(scopes,
693 function_ids.access(i).first,
694 {Compiler_IR_Detail::Binding::Kind::Function,
695 function_ids.access(i).second,
696 function(function_ids.access(i).second).type_id});
697 }
698
700 const DynArray<Compiler_HIR_Stmt *> &statements)
701 {
704
707
708 if (function.source_function != nullptr)
709 {
710 enter_scope(scopes);
711 for (size_t i = 0; i < function.source_function->parameters.size(); ++i)
712 {
713 const auto &param = function.source_function->parameters.access(i);
714 const auto slot_id = add_local_slot(
716 bind(scopes,
717 param.name,
718 {Compiler_IR_Detail::Binding::Kind::Local_Slot, slot_id, param.type_id});
719 }
720 }
721 else
722 enter_scope(scopes);
723
726 for (size_t i = 0; i < statements.size(); ++i)
727 current = lower_stmt(function, statements.access(i), current, scopes, loops);
728
729 if (current != compiler_ir_invalid_id() and not block(function, current).is_terminated())
731
733 leave_scope(scopes);
734 leave_scope(scopes);
735 }
736 };
737
739 const Compiler_Type_Context *types = nullptr;
740
741public:
744 const Compiler_Type_Context *type_ctx = nullptr) noexcept
745 : ir(&ctx), types(type_ctx)
746 {}
747
750 {
751 return types;
752 }
753
756 {
757 ah_runtime_error_unless(module != nullptr)
758 << "Compiler_IR_Lowering::lower_module(): null HIR module";
759
761 builder.module = ir->make<Compiler_IR_Module>();
762 builder.hir = module;
763 builder.ctx = ir;
764 builder.types = types;
765
766 for (size_t i = 0; i < module->functions.size(); ++i)
767 (void) builder.predeclare_function(module->functions.access(i));
768
769 for (size_t i = 0; i < module->statements.size(); ++i)
770 if (module->statements.access(i)->kind == Compiler_HIR_Stmt_Kind::Let)
771 {
772 const auto *stmt = static_cast<const Compiler_HIR_Let_Stmt *>(module->statements.access(i));
773 if (builder.find_global_slot_id(stmt->name) == compiler_ir_invalid_id())
774 (void) builder.add_global_slot(stmt->name, stmt->binding_type, stmt->name_span);
775 }
776
777 for (size_t i = 0; i < builder.module->functions.size(); ++i)
778 {
779 auto &function = builder.function(i);
780 if (function.source_function->body != nullptr)
781 builder.lower_function_body(function, function.source_function->body->statements);
782 else
783 {
785 builder.lower_function_body(function, empty_stmts);
786 }
787 }
788
789 if (not module->statements.is_empty())
790 {
791 auto *top = ir->make<Compiler_IR_Function>();
792 top->id = compiler_ir_invalid_id();
793 top->name = "<top-level>";
794 top->span = module->span;
795 top->next_value_id = 0;
796 builder.module->top_level = top;
797 builder.lower_function_body(*top, module->statements);
798 }
799
800 return builder.module;
801 }
802};
803} // namespace Aleph
804
805#endif
Reusable explicit-value IR model, validation, and deterministic dumps.
#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
Arena-backed ownership context for IR nodes.
T * make(Args &&...args)
Allocates and constructs one IR object.
Lowers typed HIR into explicit-value IR.
const Compiler_Type_Context * types
Compiler_IR_Module * lower_module(const Compiler_HIR_Module *module) const
Lowers one HIR module into explicit-value IR.
Compiler_IR_Lowering(Compiler_IR_Context &ctx, const Compiler_Type_Context *type_ctx=nullptr) noexcept
Builds an IR lowerer over ctx.
const Compiler_Type_Context * type_context() const noexcept
Returns the optional type context used by this lowerer.
Context owning all compiler type nodes.
Compiler_Type_Id unit_type() const noexcept
Returns the preloaded Unit type id.
size_t size() const noexcept
Return the current dimension of array.
T pop()
Remove the last item of array (as if this was a stack)
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
bool contains_block_id(const DynArray< Compiler_IR_Block_Id > &ids, const Compiler_IR_Block_Id id) noexcept
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
size_t Compiler_IR_Function_Id
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.
and
Check uniqueness with explicit hash + equality functors.
size_t Compiler_IR_Value_Id
constexpr size_t compiler_ir_invalid_id() noexcept
Returns the sentinel invalid IR id.
size_t Compiler_Type_Id
size_t Compiler_IR_Block_Id
size_t Compiler_IR_Global_Slot_Id
Compiler_Operator_Kind compiler_operator_compound_base(const Compiler_Operator_Kind kind) noexcept
Returns the arithmetic operator underlying one compound assignment.
bool compiler_operator_is_compound_assignment(const Compiler_Operator_Kind kind) noexcept
Returns whether kind is one compound-assignment operator.
size_t Compiler_IR_Local_Slot_Id
Compiler_IR_Slot_Kind
Storage-space classification for slots.
Structured block statement.
DynArray< Compiler_HIR_Stmt * > statements
Nested statements.
Typed function call expression.
Expression statement in HIR.
Base class for HIR expressions.
Compiler_HIR_Expr_Kind kind
Runtime node kind.
DynArray< Compiler_HIR_Param > parameters
Parameters in declaration order.
Compiler_HIR_Block_Stmt * body
Function body.
Structured conditional statement.
Typed lexical binding 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.
Compiler_HIR_Expr * value
Returned value, or nullptr.
Base class for HIR statements.
Compiler_HIR_Stmt_Kind kind
Runtime node kind.
Typed variable reference expression.
Structured while-loop statement.
One basic block of IR instructions.
std::string label
Deterministic debug label.
Compiler_IR_Block_Id id
Stable block id.
Storage class used by the lowerer for one name binding.
One lexical scope frame used during IR lowering.
DynArray< std::pair< std::string, Binding > > bindings
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.
bool is_top_level() const noexcept
Returns whether this IR function represents the top-level body.
std::string name
Debug or source-level name.
Compiler_IR_Block_Id exit_block
Canonical exit block.
Compiler_IR_Block_Id entry_block
Canonical entry block.
DynArray< Compiler_IR_Slot > local_slots
Parameter/local slots in stable order.
Compiler_IR_Function_Id id
Stable function id within the module.
const Compiler_HIR_Function * source_function
Originating HIR function, or nullptr for top-level.
Source_Span span
Source span of the function/body.
One instruction producing an optional explicit result value.
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.
Source_Span span
Source region associated with the instruction.
Compiler_Type_Id type_id
Result type or stored value type.
Compiler_IR_Local_Slot_Id local_slot_id
Referenced local/parameter slot, when relevant.
Compiler_IR_Global_Slot_Id global_slot_id
Referenced global slot, when relevant.
static Compiler_IR_Detail::Binding lookup(const DynArray< Compiler_IR_Detail::Scope_Frame > &scopes, const std::string &name)
static void enter_scope(DynArray< Compiler_IR_Detail::Scope_Frame > &scopes)
DynArray< std::pair< std::string, Compiler_IR_Function_Id > > function_ids
static void bind(DynArray< Compiler_IR_Detail::Scope_Frame > &scopes, const std::string &name, const Compiler_IR_Detail::Binding &binding)
static Compiler_IR_Block & block(Compiler_IR_Function &function, const Compiler_IR_Block_Id id)
static void merge_span(Source_Span &dst, const Source_Span &src)
Compiler_IR_Global_Slot_Id find_global_slot_id(const std::string &name) const noexcept
static void leave_scope(DynArray< Compiler_IR_Detail::Scope_Frame > &scopes)
Compiler_IR_Value_Id emit_unit_constant(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Source_Span &span) const
void lower_function_body(Compiler_IR_Function &function, const DynArray< Compiler_HIR_Stmt * > &statements)
Compiler_IR_Function_Id find_function_id(const std::string &name) const noexcept
Compiler_IR_Global_Slot_Id add_global_slot(const std::string &name, const Compiler_Type_Id type_id, const Source_Span &span) const
void preload_global_bindings(DynArray< Compiler_IR_Detail::Scope_Frame > &scopes) const
static void set_branch(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Value_Id condition, const Source_Span &span, const Compiler_IR_Block_Id then_block, const Compiler_IR_Block_Id else_block)
static void set_unreachable(Compiler_IR_Function &function, const Compiler_IR_Block_Id id, const Source_Span &span={})
static void add_edge(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Block_Id to)
static void set_exit(Compiler_IR_Function &function, const Compiler_IR_Block_Id id)
static void set_return(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Value_Id value, const Source_Span &span={})
Compiler_IR_Value_Id lower_expr(Compiler_IR_Function &function, const Compiler_HIR_Expr *expr, const Compiler_IR_Block_Id block_id, const DynArray< Compiler_IR_Detail::Scope_Frame > &scopes)
static Compiler_IR_Block_Id create_block(Compiler_IR_Function &function, std::string label)
Compiler_IR_Block_Id ensure_block(Compiler_IR_Function &function, Compiler_IR_Block_Id current)
static void emit_instruction(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, Compiler_IR_Instruction instruction)
Compiler_IR_Block_Id lower_stmt(Compiler_IR_Function &function, const Compiler_HIR_Stmt *stmt, Compiler_IR_Block_Id current, DynArray< Compiler_IR_Detail::Scope_Frame > &scopes, DynArray< std::pair< Compiler_IR_Block_Id, Compiler_IR_Block_Id > > &loops)
Compiler_IR_Function_Id predeclare_function(const Compiler_HIR_Function *hir_function)
static Compiler_IR_Value_Id next_value(Compiler_IR_Function &function) noexcept
static Compiler_IR_Local_Slot_Id add_local_slot(Compiler_IR_Function &function, const Compiler_IR_Slot_Kind kind, const std::string &name, const Compiler_Type_Id type_id, const Source_Span &span)
Compiler_IR_Function & function(Compiler_IR_Function_Id id) const
static void set_jump(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Block_Id to, const Source_Span &span={})
Lowered IR module with shared global slots and functions.
One storage slot used by the IR.
size_t id
Stable slot id within its storage space.
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