Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Compiler_IR_Builder.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
41#ifndef COMPILER_IR_BUILDER_H
42#define COMPILER_IR_BUILDER_H
43
44#include <Compiler_IR_Model.H>
45
46namespace Aleph {
53{
55
56 static void merge_span(Source_Span &dst, const Source_Span &src) noexcept
57 {
58 if (not src.is_valid())
59 return;
60 if (not dst.is_valid())
61 {
62 dst = src;
63 return;
64 }
66 }
67
69 {
70 ah_runtime_error_unless(id < function.blocks.size())
71 << "Compiler_IR_Builder: invalid block id " << id;
72 return function.blocks.access(id);
73 }
74
75 static void merge_function_span(Compiler_IR_Function &function, const Source_Span &span) noexcept
76 {
77 merge_span(function.span, span);
78 }
79
80 static void add_edge(Compiler_IR_Function &function,
81 const Compiler_IR_Block_Id from,
82 const Compiler_IR_Block_Id to)
83 {
84 auto &src = block(function, from);
85 auto &dst = block(function, to);
86 if (not Compiler_IR_Detail::contains_block_id(src.terminator.successors, to))
87 src.terminator.successors.append(to);
88 if (not Compiler_IR_Detail::contains_block_id(dst.predecessors, from))
89 dst.predecessors.append(from);
90 }
91
93 const Compiler_IR_Block_Id block_id,
95 {
96 auto &blk = block(function, block_id);
97 ah_runtime_error_unless(not blk.is_terminated())
98 << "Compiler_IR_Builder::append_instruction(): block "
99 << Compiler_IR_Detail::block_name(block_id) << " is already terminated";
100 merge_span(blk.span, instruction.span);
102 blk.instructions.append(std::move(instruction));
103 return blk.instructions.access(blk.instructions.size() - 1);
104 }
105
108 const Source_Span &span) noexcept
109 {
110 blk.terminator.span = span;
111 merge_span(blk.span, span);
112 merge_function_span(function, span);
113 }
114
115public:
118
121 {
122 return *ir_;
123 }
124
127 {
128 return ir_->make<Compiler_IR_Module>();
129 }
130
133 std::string name,
134 const Compiler_Type_Id type_id,
135 const Source_Span &span = {})
136 {
138 slot.id = module.global_slots.size();
140 slot.name = std::move(name);
141 slot.type_id = type_id;
142 slot.span = span;
143 module.global_slots.append(std::move(slot));
144 return module.global_slots.size() - 1;
145 }
146
149 const Compiler_Type_Id type_id = 0,
150 const Source_Span &span = {},
151 const Compiler_HIR_Function *source_function = nullptr) const
152 {
153 auto *function = ir_->make<Compiler_IR_Function>();
154 function->name = std::move(name);
155 function->type_id = type_id;
156 function->span = span;
157 function->source_function = source_function;
158 function->next_value_id = 0;
159 return function;
160 }
161
164 Compiler_IR_Function *function)
165 {
166 ah_runtime_error_unless(function != nullptr)
167 << "Compiler_IR_Builder::append_function(): null function";
168 function->id = module.functions.size();
169 module.functions.append(function);
170 return function->id;
171 }
172
175 Compiler_IR_Function *function)
176 {
177 ah_runtime_error_unless(function != nullptr)
178 << "Compiler_IR_Builder::set_top_level(): null function";
179 function->id = compiler_ir_invalid_id();
180 module.top_level = function;
181 return function;
182 }
183
186 const Compiler_IR_Slot_Kind kind,
187 std::string name,
188 const Compiler_Type_Id type_id,
189 const Source_Span &span = {})
190 {
192 slot.id = function.local_slots.size();
193 slot.kind = kind;
194 slot.name = std::move(name);
195 slot.type_id = type_id;
196 slot.span = span;
197 function.local_slots.append(std::move(slot));
198 merge_function_span(function, span);
199 return function.local_slots.size() - 1;
200 }
201
203 static Compiler_IR_Block_Id create_block(Compiler_IR_Function &function, std::string label)
204 {
206 blk.id = function.blocks.size();
207 blk.label = std::move(label);
208 function.blocks.append(std::move(blk));
209 return function.blocks.size() - 1;
210 }
211
213 static void set_entry_block(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id)
214 {
215 (void) block(function, block_id);
216 function.entry_block = block_id;
217 }
218
220 static void set_exit_block(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id)
221 {
222 (void) block(function, block_id);
223 function.exit_block = block_id;
224 }
225
228 {
229 ++function.next_value_id;
230 return function.next_value_id;
231 }
232
235 const Compiler_IR_Block_Id block_id,
236 const Compiler_Type_Id type_id,
237 std::string text,
238 const Source_Span &span = {},
239 const bool bool_value = false)
240 {
243 const auto result_id = allocate_value(function);
244 inst.result_id = result_id;
245 inst.type_id = type_id;
246 inst.span = span;
247 inst.text = std::move(text);
248 inst.bool_value = bool_value;
249 (void) append_instruction(function, block_id, std::move(inst));
250 return result_id;
251 }
252
255 const Compiler_IR_Block_Id block_id,
256 const Compiler_IR_Local_Slot_Id slot_id,
257 const Compiler_Type_Id type_id,
258 const Source_Span &span = {})
259 {
260 ah_runtime_error_unless(slot_id < function.local_slots.size())
261 << "Compiler_IR_Builder::emit_load_local(): invalid slot id " << slot_id;
264 const auto result_id = allocate_value(function);
265 inst.result_id = result_id;
266 inst.type_id = type_id;
267 inst.span = span;
268 inst.local_slot_id = slot_id;
269 (void) append_instruction(function, block_id, std::move(inst));
270 return result_id;
271 }
272
275 const Compiler_IR_Block_Id block_id,
276 const Compiler_IR_Global_Slot_Id slot_id,
277 const Compiler_Type_Id type_id,
278 const Source_Span &span = {})
279 {
282 const auto result_id = allocate_value(function);
283 inst.result_id = result_id;
284 inst.type_id = type_id;
285 inst.span = span;
286 inst.global_slot_id = slot_id;
287 (void) append_instruction(function, block_id, std::move(inst));
288 return result_id;
289 }
290
293 const Compiler_IR_Block_Id block_id,
294 const Compiler_IR_Local_Slot_Id slot_id,
295 const Compiler_IR_Value_Id value_id,
296 const Compiler_Type_Id type_id,
297 const Source_Span &span = {})
298 {
299 ah_runtime_error_unless(slot_id < function.local_slots.size())
300 << "Compiler_IR_Builder::emit_store_local(): invalid slot id " << slot_id;
303 inst.type_id = type_id;
304 inst.span = span;
305 inst.local_slot_id = slot_id;
306 inst.operands.append(value_id);
307 (void) append_instruction(function, block_id, std::move(inst));
308 }
309
312 const Compiler_IR_Block_Id block_id,
313 const Compiler_IR_Global_Slot_Id slot_id,
314 const Compiler_IR_Value_Id value_id,
315 const Compiler_Type_Id type_id,
316 const Source_Span &span = {})
317 {
320 inst.type_id = type_id;
321 inst.span = span;
322 inst.global_slot_id = slot_id;
323 inst.operands.append(value_id);
324 (void) append_instruction(function, block_id, std::move(inst));
325 }
326
329 const Compiler_IR_Block_Id block_id,
330 const Compiler_Operator_Kind op,
331 const Compiler_IR_Value_Id operand,
332 const Compiler_Type_Id type_id,
333 const Source_Span &span = {})
334 {
337 const auto result_id = allocate_value(function);
338 inst.result_id = result_id;
339 inst.type_id = type_id;
340 inst.span = span;
341 inst.op = op;
342 inst.operands.append(operand);
343 (void) append_instruction(function, block_id, std::move(inst));
344 return result_id;
345 }
346
349 const Compiler_IR_Block_Id block_id,
350 const Compiler_Operator_Kind op,
351 const Compiler_IR_Value_Id lhs,
352 const Compiler_IR_Value_Id rhs,
353 const Compiler_Type_Id type_id,
354 const Source_Span &span = {})
355 {
358 const auto result_id = allocate_value(function);
359 inst.result_id = result_id;
360 inst.type_id = type_id;
361 inst.span = span;
362 inst.op = op;
363 inst.operands.append(lhs);
364 inst.operands.append(rhs);
365 (void) append_instruction(function, block_id, std::move(inst));
366 return result_id;
367 }
368
371 const Compiler_IR_Block_Id block_id,
372 const Compiler_IR_Function_Id function_id,
373 const Compiler_Type_Id type_id,
374 const Source_Span &span = {})
375 {
378 const auto result_id = allocate_value(function);
379 inst.result_id = result_id;
380 inst.type_id = type_id;
381 inst.span = span;
382 inst.function_id = function_id;
383 (void) append_instruction(function, block_id, std::move(inst));
384 return result_id;
385 }
386
389 const Compiler_IR_Block_Id block_id,
390 const Compiler_IR_Value_Id callee,
391 const DynArray<Compiler_IR_Value_Id> &arguments,
392 const Compiler_Type_Id type_id,
393 const Source_Span &span = {})
394 {
397 const auto result_id = allocate_value(function);
398 inst.result_id = result_id;
399 inst.type_id = type_id;
400 inst.span = span;
401 inst.operands.append(callee);
402 for (size_t i = 0; i < arguments.size(); ++i)
403 inst.operands.append(arguments.access(i));
404 (void) append_instruction(function, block_id, std::move(inst));
405 return result_id;
406 }
407
409 static void set_jump(Compiler_IR_Function &function,
410 const Compiler_IR_Block_Id from,
411 const Compiler_IR_Block_Id to,
412 const Source_Span &span = {})
413 {
414 auto &blk = block(function, from);
415 (void) block(function, to);
416 ah_runtime_error_unless(not blk.is_terminated())
417 << "Compiler_IR_Builder::set_jump(): block already terminated";
419 set_terminator_span(function, blk, span);
420 add_edge(function, from, to);
421 }
422
424 static void set_branch(Compiler_IR_Function &function,
425 const Compiler_IR_Block_Id from,
426 const Compiler_IR_Value_Id condition_value,
429 const Source_Span &span = {})
430 {
431 auto &blk = block(function, from);
432 (void) block(function, then_block);
433 (void) block(function, else_block);
434 ah_runtime_error_unless(not blk.is_terminated())
435 << "Compiler_IR_Builder::set_branch(): block already terminated";
437 blk.terminator.condition_value = condition_value;
438 set_terminator_span(function, blk, span);
439 add_edge(function, from, then_block);
440 add_edge(function, from, else_block);
441 }
442
444 static void set_return(Compiler_IR_Function &function,
445 const Compiler_IR_Block_Id from,
446 const Compiler_IR_Value_Id return_value,
447 const Source_Span &span = {})
448 {
449 ah_runtime_error_unless(function.exit_block < function.blocks.size())
450 << "Compiler_IR_Builder::set_return(): exit block not set";
451 auto &blk = block(function, from);
452 ah_runtime_error_unless(not blk.is_terminated())
453 << "Compiler_IR_Builder::set_return(): block already terminated";
455 blk.terminator.return_value = return_value;
456 set_terminator_span(function, blk, span);
457 add_edge(function, from, function.exit_block);
458 }
459
461 static void set_exit(Compiler_IR_Function &function,
462 const Compiler_IR_Block_Id block_id,
463 const Source_Span &span = {})
464 {
465 auto &blk = block(function, block_id);
466 ah_runtime_error_unless(not blk.is_terminated())
467 << "Compiler_IR_Builder::set_exit(): block already terminated";
468 if (function.exit_block != compiler_ir_invalid_id())
469 ah_runtime_error_unless(function.exit_block == block_id)
470 << "Compiler_IR_Builder::set_exit(): exit block already set to a different block";
471 function.exit_block = block_id;
472 blk.terminator.kind = Compiler_IR_Terminator_Kind::Exit;
473 blk.terminator.successors.clear();
474 set_terminator_span(function, blk, span);
475 }
476
479 const Compiler_IR_Block_Id block_id,
480 const Source_Span &span = {})
481 {
482 auto &blk = block(function, block_id);
483 ah_runtime_error_unless(not blk.is_terminated())
484 << "Compiler_IR_Builder::set_unreachable(): block already terminated";
486 set_terminator_span(function, blk, span);
487 }
488};
489} // namespace Aleph
490
491#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
Small reusable helper that allocates and connects IR nodes.
static void set_terminator_span(Compiler_IR_Function &function, Compiler_IR_Block &blk, const Source_Span &span) noexcept
static void emit_store_global(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_IR_Global_Slot_Id slot_id, const Compiler_IR_Value_Id value_id, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one store into a global slot.
static Compiler_IR_Value_Id emit_constant(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_Type_Id type_id, std::string text, const Source_Span &span={}, const bool bool_value=false)
Emits one constant instruction.
static void set_unreachable(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Source_Span &span={})
Marks one block as unreachable.
static Compiler_IR_Function_Id append_function(Compiler_IR_Module &module, Compiler_IR_Function *function)
Appends one non-top-level function to module and assigns its id.
static void emit_store_local(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_IR_Local_Slot_Id slot_id, const Compiler_IR_Value_Id value_id, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one store into a local or parameter slot.
static Compiler_IR_Local_Slot_Id add_local_slot(Compiler_IR_Function &function, const Compiler_IR_Slot_Kind kind, std::string name, const Compiler_Type_Id type_id, const Source_Span &span={})
Appends one local or parameter slot to function.
static Compiler_IR_Value_Id allocate_value(Compiler_IR_Function &function) noexcept
Allocates the next explicit IR value id in function.
static void merge_span(Source_Span &dst, const Source_Span &src) noexcept
static Compiler_IR_Value_Id emit_load_global(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_IR_Global_Slot_Id slot_id, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one load from a global slot.
static void set_exit_block(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id)
Marks one block as the canonical exit block.
static Compiler_IR_Block_Id create_block(Compiler_IR_Function &function, std::string label)
Creates one empty block and returns its id.
static Compiler_IR_Value_Id emit_load_local(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_IR_Local_Slot_Id slot_id, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one load from a local or parameter slot.
static void set_entry_block(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id)
Marks one block as the canonical entry block.
static Compiler_IR_Block & block(Compiler_IR_Function &function, const Compiler_IR_Block_Id id)
static void add_edge(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Block_Id to)
Compiler_IR_Module * make_module() const
Allocates one empty IR module.
static Compiler_IR_Global_Slot_Id add_global_slot(Compiler_IR_Module &module, std::string name, const Compiler_Type_Id type_id, const Source_Span &span={})
Appends one global slot to module.
static void set_branch(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Value_Id condition_value, const Compiler_IR_Block_Id then_block, const Compiler_IR_Block_Id else_block, const Source_Span &span={})
Sets one conditional branch terminator and both outgoing edges.
static void set_exit(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Source_Span &span={})
Sets one block terminator to Exit.
static Compiler_IR_Value_Id emit_call(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_IR_Value_Id callee, const DynArray< Compiler_IR_Value_Id > &arguments, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one call instruction.
static void set_return(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Value_Id return_value, const Source_Span &span={})
Sets one return terminator and connects it to the exit block.
static void merge_function_span(Compiler_IR_Function &function, const Source_Span &span) noexcept
Compiler_IR_Context & context() const noexcept
Returns the underlying ownership context.
static void set_jump(Compiler_IR_Function &function, const Compiler_IR_Block_Id from, const Compiler_IR_Block_Id to, const Source_Span &span={})
Sets one jump terminator and predecessor/successor edge.
Compiler_IR_Function * make_function(std::string name, const Compiler_Type_Id type_id=0, const Source_Span &span={}, const Compiler_HIR_Function *source_function=nullptr) const
Allocates one function without inserting it into a module.
static Compiler_IR_Value_Id emit_binary(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_Operator_Kind op, const Compiler_IR_Value_Id lhs, const Compiler_IR_Value_Id rhs, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one binary instruction.
static Compiler_IR_Value_Id emit_unary(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_Operator_Kind op, const Compiler_IR_Value_Id operand, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one unary instruction.
static Compiler_IR_Value_Id emit_function_ref(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, const Compiler_IR_Function_Id function_id, const Compiler_Type_Id type_id, const Source_Span &span={})
Emits one function reference instruction.
static Compiler_IR_Function * set_top_level(Compiler_IR_Module &module, Compiler_IR_Function *function)
Installs one top-level function body in module.
static Compiler_IR_Instruction & append_instruction(Compiler_IR_Function &function, const Compiler_IR_Block_Id block_id, Compiler_IR_Instruction instruction)
Compiler_IR_Builder(Compiler_IR_Context &context) noexcept
Builds IR nodes inside context.
Arena-backed ownership context for IR nodes.
T * make(Args &&...args)
Allocates and constructs one IR object.
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.
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
std::string block_name(const Compiler_IR_Block_Id id)
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.
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
Stable operator kinds shared by reusable compiler layers.
size_t Compiler_IR_Local_Slot_Id
Compiler_IR_Slot_Kind
Storage-space classification for slots.
One basic block of IR instructions.
Compiler_IR_Terminator terminator
Explicit block terminator.
Compiler_IR_Block_Id id
Stable block id.
Lowered IR for one function or top-level body.
Compiler_IR_Value_Id next_value_id
Next value id to allocate while lowering.
DynArray< Compiler_IR_Block > blocks
Basic blocks in deterministic id order.
std::string name
Debug or source-level name.
Compiler_IR_Block_Id exit_block
Canonical exit block.
Compiler_IR_Block_Id entry_block
Canonical entry block.
DynArray< Compiler_IR_Slot > local_slots
Parameter/local slots in stable order.
Compiler_IR_Function_Id id
Stable function id within the module.
Source_Span span
Source span of the function/body.
One instruction producing an optional explicit result value.
Compiler_IR_Instruction_Kind kind
Instruction category.
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.
Compiler_IR_Terminator_Kind kind
Terminator category.
Half-open byte range inside a source file.
Definition ah-source.H:100