1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
/* Source code for RCC-Legacy.
Copyright (C) 2026 Richard Qin
This file is part of RCC.
RCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
RCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with RCC; see the file LICENSE. If not see
<http://www.gnu.org/licenses/>. */
#pragma once
/// @file irgen.h
/// @brief AST -> SSA IR (Braun Algorithm)
///
/// Produces Fn/Blk/Ins/Phi structures defined in all.h.
/// Memory is managed via the injected Ctx (SubPool-backed).
#include "rcc/all.h"
#include "rcc/ast.h"
#include "rcc/symtab.h"
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
namespace rcc {
/// Global variable data (for data segment emission)
struct GlobalData {
std::string name;
int elem_cls = Kw; // Kw / Ks
size_t total_elems = 0;
bool is_const = false;
bool is_zeroinit = false;
std::vector<std::variant<int32_t, float>> init_vals;
};
/// IRGen output
struct IROutput {
std::vector<Fn*> functions;
std::vector<GlobalData> globals;
};
/// Export global variables list as data segment syntax.
void print_globals(const std::vector<GlobalData>& globals, FILE* f);
class IRGen {
public:
explicit IRGen(Ctx* ctx) : ctx_(ctx) {}
/// Translate AST to IR
IROutput generate(CompUnitNode* root);
private:
Ctx* ctx_;
IROutput output_;
Fn* cur_fn_ = nullptr;
Blk* cur_blk_ = nullptr;
SymbolTable sym_;
// Current function's return K class: Kw / Ks (Kx=-1 for void).
// Do not reuse Fn::retty.
int cur_fn_ret_cls_ = Kx;
// Fn/Blk constructors
/// Create new function
Fn* new_fn(const char* name);
/// Create new basic block
Blk* new_blk(const char* name);
/// Get basic block (by id)
Blk* get_blk(uint id);
/// All blocks in current function (indexed by id)
std::vector<Blk*> blk_vec_;
// constant/Ref constructors
/// Create int constant Ref
Ref make_int_ref(int32_t val);
/// Create float constant Ref
Ref make_float_ref(float val);
/// Create global symbol RCon (CAddr).
/// For call target use only; data access should uniformly go through make_global_addr_ref.
Ref make_global_ref(const char* name);
/// Materialize global symbol into a Tmp (Oaddr Kl).
Ref make_global_addr_ref(const char* name);
/// Create new temporary
Ref new_tmp_ref(int cls);
// Braun SSA algorithm
//
// Index using Symbol::uid rather than variable name to prevent scope shadowing bugs.
std::unordered_map<int, std::unordered_map<uint, Ref>> current_def_;
struct IncompletePhi {
int var_uid;
Phi* phi;
uint block_id;
int cls;
};
std::vector<IncompletePhi> incomplete_phis_;
void write_variable(int uid, Blk* blk, Ref val);
Ref read_variable(int uid, Blk* blk, int cls);
Ref read_variable_recursive(int uid, Blk* blk, int cls);
void seal_block(Blk* blk);
void add_phi_operands(int uid, Blk* blk, Phi* phi, int cls);
Ref try_remove_trivial_phi(Phi* phi);
// Block sealing
std::unordered_map<uint, bool> sealed_;
bool is_sealed(Blk* b) const;
// Loop context
struct LoopCtx {
Blk* cond_block;
Blk* exit_block;
};
std::vector<LoopCtx> loop_stack_;
// AST traversal
void visit_comp_unit(CompUnitNode* node);
void visit_func_def(FuncDefNode* node);
void visit_global_decl(VarDeclNode* node);
void visit_local_decl(VarDeclNode* node);
void visit_stmt(ASTNode* node);
void visit_block(BlockNode* node);
void visit_if(IfStmtNode* node);
void visit_while(WhileStmtNode* node);
void visit_assign(AssignStmtNode* node);
void visit_return(ReturnStmtNode* node);
Ref visit_expr(ASTNode* node);
void visit_cond(ASTNode* cond, Blk* true_bb, Blk* false_bb);
void visit_cond_and(BinaryExprNode* node, Blk* true_bb, Blk* false_bb);
void visit_cond_or(BinaryExprNode* node, Blk* true_bb, Blk* false_bb);
// Instruction emission
Ref emit_ins(int op, int cls, Ref a0, Ref a1);
void emit_store(Ref val, Ref addr, int cls);
Ref emit_load(Ref addr, int cls);
/// IR-level zero-fill loop. Clears [addr, addr+size_bytes) entirely.
void emit_bulk_zero(Ref addr, int size_bytes);
void emit_jmp(Blk* target);
void emit_jnz(Ref cond, Blk* true_bb, Blk* false_bb);
void emit_ret(Ref val, int cls);
void emit_ret_void();
void push_ins(Ins i);
bool cur_blk_terminated() const;
void add_edge(Blk* from, Blk* to);
// Helpers
Ref compute_array_addr(LValNode* node);
Ref get_var_addr_ref(Symbol* sym);
int32_t eval_const_int(ASTNode* node);
float eval_const_float(ASTNode* node);
void flatten_init(const std::vector<int>& dims, ASTNode* init, std::vector<ASTNode*>& out);
/// BType -> K class (Kw / Ks)
static int btype_to_cls(BType t);
/// Infer actual K class of a Ref.
static int ref_cls_of(Ref r, Fn* fn);
/// Type coercion: Emit Oswtof/Ostosi when Ref's current cls differs from target.
Ref coerce(Ref v, int target_cls);
};
} // namespace rcc
|