From d3b82d8afa2313ecc7b897cf5cbcb1fd00541aad Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:06:46 +0000 Subject: [PATCH 01/18] Finish one-d array ir generation --- project/include/ir/ast.cppm | 33 +++++++++--- project/include/ir/type.cppm | 37 +++++++++++-- project/src/frontend/sysy.y | 74 +++++++++++++++++++++---- project/src/ir/ast.cpp | 10 ++-- project/src/ir/codegen.cpp | 101 ++++++++++++++++++++++++++++++++--- project/src/main.cpp | 10 ++-- project/tests/hello.c | 9 ++-- 7 files changed, 234 insertions(+), 40 deletions(-) diff --git a/project/include/ir/ast.cppm b/project/include/ir/ast.cppm index 89cc1d6..377f3b7 100644 --- a/project/include/ir/ast.cppm +++ b/project/include/ir/ast.cppm @@ -111,10 +111,12 @@ public: auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; +class DefAST : public BaseAST {}; + /** * @brief AST node for function definitions. */ -class FuncDefAST : public BaseAST { +class FuncDefAST : public DefAST { public: ~FuncDefAST() override; std::string btype; @@ -135,10 +137,26 @@ public: auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; +class ArrayDefAST : public DefAST { +public: + bool is_const; + std::string ident; + std::unique_ptr expr; + std::vector> initialize_list; + ArrayDefAST(bool _is_const, std::string _ident, BaseAST *_expr, + std::vector> _initialize_list) + : is_const(_is_const), ident(std::move(_ident)), + initialize_list(std::move(_initialize_list)) { + expr.reset(static_cast(_expr)); + } + auto dump(int depth) const -> void override; + auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; +}; + /** * @brief AST node for a single variable definition. */ -class DefAST : public BaseAST { +class ScalarDefAST : public DefAST { public: bool is_const; std::string ident; @@ -149,7 +167,7 @@ public: * @param _ident The name of the variable. * @param _initVal The initial value expression (optional). */ - DefAST(bool _is_const, std::string _ident, BaseAST *_initVal) + ScalarDefAST(bool _is_const, std::string _ident, BaseAST *_initVal) : is_const(_is_const), ident(std::move(_ident)) { if (_initVal) { initVal.reset(static_cast(_initVal)); @@ -242,9 +260,9 @@ public: * @param _btype The type of the variables. * @param _defs The list of variable definitions. */ - DeclAST(bool _isConst, std::string _btype, + DeclAST(bool _is_const, std::string _btype, std::vector> _defs) - : is_const(_isConst), btype(std::move(_btype)), defs(std::move(_defs)) {} + : is_const(_is_const), btype(std::move(_btype)), defs(std::move(_defs)) {} auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; @@ -345,11 +363,14 @@ public: class LValAST : public ExprAST { public: std::string ident; + std::unique_ptr index; /** * @brief Constructs an LVal node. * @param _ident The variable name. */ - LValAST(std::string _ident) : ident(std::move(_ident)) {}; + LValAST(std::string _ident, BaseAST *_index) : ident(std::move(_ident)) { + index.reset(static_cast(_index)); + }; auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; auto CalcValue(ir::KoopaBuilder &builder) const -> int override; diff --git a/project/include/ir/type.cppm b/project/include/ir/type.cppm index 94bcfa7..ad2276f 100644 --- a/project/include/ir/type.cppm +++ b/project/include/ir/type.cppm @@ -6,9 +6,9 @@ module; #include +#include #include #include -#include export module ir.type; @@ -31,6 +31,7 @@ public: virtual auto is_ptr() const -> bool { return false; } virtual auto is_bool() const -> bool { return false; } virtual auto is_float() const -> bool { return false; } + virtual auto is_array() const -> bool { return false; } }; /** @@ -108,16 +109,42 @@ class PtrType : public Type { public: std::shared_ptr target; ///< The type being pointed to. - /** - * @brief Constructs a pointer type. - * @param t The base type. - */ PtrType(std::shared_ptr t) : target(t) {}; auto debug() const -> std::string override { return fmt::format("*{}", target->debug()); } auto is_ptr() const -> bool override { return true; } + + static auto get(std::shared_ptr t) -> std::shared_ptr { + return std::make_shared(t); + } +}; + +class ArrayType : public Type { +public: + std::shared_ptr base; + int len; + + ArrayType(std::shared_ptr t, int _len) : base(t), len(_len) {} + + auto debug() const -> std::string override { + return fmt::format("Array type (len : {}): {}", len, base->debug()); + } + auto is_array() const -> bool override { return true; } + + static auto get(std::shared_ptr _base, int _len) + -> std::shared_ptr { + static std::map, int>, + std::shared_ptr> + cache; + auto key = std::make_pair(_base, _len); + if (cache.contains(key)) { + return cache[key]; + } + auto instance = std::make_shared(_base, _len); + return cache[key] = instance; + } }; } // namespace type \ No newline at end of file diff --git a/project/src/frontend/sysy.y b/project/src/frontend/sysy.y index 9b020a3..8947099 100644 --- a/project/src/frontend/sysy.y +++ b/project/src/frontend/sysy.y @@ -64,7 +64,7 @@ void yyerror(std::unique_ptr &ast, const char *str); %type ConstDef VarDef BlockItem Decl ConstDecl VarDecl %type CompUnitItem CompUnit FuncFParam %type FuncFParams -%type FuncRParams +%type FuncRParams InitializeList InitVal %type CompUnitItemList %type BlockItemList %type VarDefList ConstDefList @@ -72,6 +72,14 @@ void yyerror(std::unique_ptr &ast, const char *str); %nonassoc LOWER_ELSE %nonassoc ELSE +/* +VarDef ::= IDENT ["[" ConstExp "]"] + | IDENT ["[" ConstExp "]"] "=" InitVal; +InitVal ::= Exp | "{" [Exp {"," Exp}] "}"; + +LVal ::= IDENT ["[" Exp "]"]; +*/ + %left OR // || %left AND // && %left EQ NE // == , != @@ -194,19 +202,49 @@ ConstDefList : ConstDef { $$ = new std::vector>(); // this is a pointer , so we don't need move optimize. - $$->push_back(std::unique_ptr(static_cast($1))); + $$->push_back(std::unique_ptr(static_cast($1))); } | ConstDefList ',' ConstDef { $$ = $1; - $$->push_back(std::unique_ptr(static_cast($3))); + $$->push_back(std::unique_ptr(static_cast($3))); }; ConstDef : IDENT '=' Expr { - $$ = new DefAST(true, std::move(*$1), $3); + // @param is_const, ident, exprAST + $$ = new ScalarDefAST(true, std::move(*$1), $3); + delete $1; + } + | IDENT '[' Expr ']' '=' InitVal { + $$ = new ArrayDefAST(true, std::move(*$1), $3, std::move(*$6)); delete $1; + delete $6; + }; + +InitVal + : '{' '}' { + $$ = new std::vector>(); + } + | '{' InitializeList '}' { + $$ = $2; + }; + +InitializeList + : Expr { + $$ = new std::vector>(); + $$->push_back(std::unique_ptr(static_cast($1))); + } + | InitializeList ',' Expr { + $$ = $1; + $$->push_back(std::unique_ptr(static_cast($3))); }; +/* +Initialize list +ConstDef ::= IDENT ["[" ConstExp "]"] "=" ConstInitVal; +ConstInitVal ::= ConstExp | "{" [ConstExp {"," ConstExp}] "}"; +*/ + VarDecl : Btype VarDefList ';' { $$ = new DeclAST(false, std::move(*$1), std::move(*$2)); @@ -217,23 +255,37 @@ VarDecl VarDefList : VarDef { $$ = new std::vector>(); - $$->push_back(std::unique_ptr(static_cast($1))); + $$->push_back(std::unique_ptr(static_cast($1))); }; | VarDefList ',' VarDef { $$ = $1; - $$->push_back(std::unique_ptr(static_cast($3))); + $$->push_back(std::unique_ptr(static_cast($3))); }; VarDef : IDENT '=' Expr { - $$ = new DefAST(false, std::move(*$1), $3); + $$ = new ScalarDefAST(false, std::move(*$1), $3); delete $1; }; | IDENT { - $$ = new DefAST(false, std::move(*$1), nullptr); + $$ = new ScalarDefAST(false, std::move(*$1), nullptr); delete $1; + } + | IDENT '[' Expr ']' { + $$ = new ArrayDefAST(false, std::move(*$1), $3, {}); + delete $1; + } + | IDENT '[' Expr ']' '=' InitVal { + $$ = new ArrayDefAST(false, std::move(*$1), $3, std::move(*$6)); + delete $1; + delete $6; }; +/* +VarDef ::= IDENT ["[" ConstExp "]"] + | IDENT ["[" ConstExp "]"] "=" InitVal; +*/ + Btype : INT { $$ = new std::string("int"); } | VOID { $$ = new std::string("void"); }; @@ -352,7 +404,11 @@ Number LVal : IDENT { - $$ = new LValAST(std::move(*$1)); + $$ = new LValAST(std::move(*$1), nullptr); + delete $1; + } + | IDENT '[' Expr ']' { + $$ = new LValAST(std::move(*$1), $3); delete $1; }; diff --git a/project/src/ir/ast.cpp b/project/src/ir/ast.cpp index 657b3fe..a6faf69 100644 --- a/project/src/ir/ast.cpp +++ b/project/src/ir/ast.cpp @@ -85,12 +85,16 @@ auto FuncDefAST::dump(int depth) const -> void { } } +auto ArrayDefAST::dump(int depth) const -> void { + fmt::println("{}ArrayDefAST: {}", indent(depth), ident); +} + /** - * @brief Dumps DefAST node details. + * @brief Dumps ScalarDefAST node details. * @param depth Indentation depth. */ -auto DefAST::dump(int depth) const -> void { - fmt::println("{}DefAST: {}", indent(depth), ident); +auto ScalarDefAST::dump(int depth) const -> void { + fmt::println("{}ScalarDefAST: {}", indent(depth), ident); if (initVal) { initVal->dump(depth + 1); } diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index 1ff7498..a6572c9 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -26,6 +26,7 @@ module; #include #include +#include #include #include @@ -159,6 +160,64 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } +auto ArrayDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { + int len = expr->CalcValue(builder); + + if (builder.symtab().isGlobalScope()) { + //! [Caution] : Since global variables do not have naming conflicts, we + //! will consistently use ident without the prefix. + if (ssize(initialize_list) == 0) { + builder.append( + fmt::format("global @{} = alloc [i32, {}], zeroinit\n", ident, len)); + } else { + builder.append( + fmt::format("global @{} = alloc [i32, {}], {{", ident, len)); + + for (const auto &[i, elem] : std::views::enumerate(initialize_list)) { + builder.append(fmt::format("{}", elem->CalcValue(builder))); + if (i < len - 1) { + builder.append(", "); + } + } + for (int i = ssize(initialize_list); i < len; ++i) { + builder.append("0"); + if (i < len - 1) { + builder.append(", "); + } + } + + builder.append("}\n"); + + builder.symtab().defineGlobal( + ident, "@" + ident, type::ArrayType::get(type::IntType::get(), len), + SymbolKind::Var, is_const); + } + } else { + + auto addr = builder.newVar(ident); + builder.append(fmt::format(" {} = alloc [i32, {}]\n", addr, len)); + for (const auto &[i, elem] : std::views::enumerate(initialize_list)) { + auto tmp_reg = builder.newReg(); + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", tmp_reg, addr, i)); + builder.append( + fmt::format(" store {}, {}\n", elem->CalcValue(builder), tmp_reg)); + } + for (int i = ssize(initialize_list); i < len; ++i) { + auto tmp_reg = builder.newReg(); + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", tmp_reg, addr, i)); + builder.append(fmt::format(" store 0, {}\n", tmp_reg)); + } + + builder.symtab().define(ident, addr, + type::ArrayType::get(type::IntType::get(), len), + SymbolKind::Var, is_const); + } + + return ""; +} + /** * @brief Generates IR for a single variable definition (Def). * @@ -171,11 +230,14 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @param builder The IR builder context. * @return An empty string. */ -auto DefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { +auto ScalarDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (builder.symtab().isGlobalScope()) { int val = 0; bool has_init = false; if (initVal) { + // NOTE: No need to check `initval` here, because `const int a;` and + // similar statements are not implemented and will result in an error at + // the syntax parsing stage. val = initVal->CalcValue(builder); has_init = true; } @@ -216,7 +278,6 @@ auto DefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } - /** * @brief Generates IR for a block of statements (enclosed in { }). * @@ -282,7 +343,16 @@ auto AssignStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { Log::panic( fmt::format("Error: Cannot assign to const variable '{}'", sym->name)); } - builder.append(fmt::format(" store {}, {}\n", val_reg, sym->irName)); + + if (lval->index) { + auto tmp_reg = builder.newReg(); + builder.append(fmt::format(" {} = getelemptr {}, {}\n", tmp_reg, + sym->irName, lval->index->codeGen(builder))); + builder.append(fmt::format(" store {}, {}\n", val_reg, tmp_reg)); + } else { + builder.append(fmt::format(" store {}, {}\n", val_reg, sym->irName)); + } + return ""; } @@ -441,7 +511,6 @@ auto ContinueStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } - /** * @brief Generates IR for a literal number. * @@ -470,11 +539,23 @@ auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { Log::panic(fmt::format("Undefined variable: '{}'", ident)); } //* we can calculate const value in compile time - if (sym->is_const) { + if (sym->is_const && !index) { return std::to_string(sym->constValue); } std::string reg = builder.newReg(); - builder.append(fmt::format(" {} = load {}\n", reg, sym->irName)); + if (index) { + auto lval_reg = index->codeGen(builder); + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", reg, sym->irName, lval_reg)); + + // allocate new reg + std::string old_reg = reg; + reg = builder.newReg(); + + builder.append(fmt::format(" {} = load {}\n", reg, old_reg)); + } else { + builder.append(fmt::format(" {} = load {}\n", reg, sym->irName)); + } return reg; } @@ -497,6 +578,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { for (const auto &arg : args) { arg_val.emplace_back(arg->codeGen(builder)); } + std::string ret_reg; if (sym->type->is_void()) { builder.append(fmt::format(" call @{}(", ident)); @@ -504,6 +586,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { ret_reg = builder.newReg(); builder.append(fmt::format(" {} = call @{}(", ret_reg, ident)); } + for (const auto &val : arg_val) { builder.append(val); if (&val != &arg_val.back()) { @@ -511,6 +594,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { } } builder.append(")\n"); + return ret_reg; } @@ -547,6 +631,7 @@ auto UnaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return The register name holding the result. */ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { + if (op == BinaryOp::And) { std::string tmp_addr = builder.newVar("and_res"); builder.append(fmt::format(" {} = alloc i32\n", tmp_addr)); @@ -582,6 +667,7 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ret_reg; } + if (op == BinaryOp::Or) { std::string tmp_addr = builder.newVar("or_res"); builder.append(fmt::format(" {} = alloc i32\n", tmp_addr)); @@ -618,6 +704,7 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ret_reg; } + // remain binary operator std::string lhs_reg = lhs->codeGen(builder); std::string rhs_reg = rhs->codeGen(builder); @@ -628,7 +715,6 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ret_reg; } - /** * @brief Evaluates a literal number at compile time. * @param builder The IR builder context. @@ -649,6 +735,7 @@ auto NumberAST::CalcValue([[maybe_unused]] ir::KoopaBuilder &builder) const */ auto LValAST::CalcValue(ir::KoopaBuilder &builder) const -> int { auto sym = builder.symtab().lookup(ident); + Log::trace(ident); if (!sym) { Log::panic( fmt::format("Undefined variable '{}' in constant expression", ident)); diff --git a/project/src/main.cpp b/project/src/main.cpp index d92d3d2..7929e28 100644 --- a/project/src/main.cpp +++ b/project/src/main.cpp @@ -111,12 +111,12 @@ auto main(int argc, const char *argv[]) -> int { } // 3. Generate RISC-V assembly from Koopa IR - backend::KoopaWrapper wrapper(ir); - backend::TargetCodeGen generator; - generator.visit(wrapper.getRaw()); - - const std::string asmCode = generator.getAssembly(); if (config.mode == "-riscv") { + backend::KoopaWrapper wrapper(ir); + backend::TargetCodeGen generator; + generator.visit(wrapper.getRaw()); + + const std::string asmCode = generator.getAssembly(); auto out = fmt::output_file(config.output_file); out.print("{}", asmCode); fmt::print(fmt::fg(fmt::color::cyan), "[Success] Parse riscv succeed!\n"); diff --git a/project/tests/hello.c b/project/tests/hello.c index a3bf9fa..08943f1 100644 --- a/project/tests/hello.c +++ b/project/tests/hello.c @@ -1,6 +1,5 @@ -int a = 10; - -int inc() { - a = a + 1; - return a; +int main() { + int arr[10]; + int n = getarray(arr); + return 0; } \ No newline at end of file From a07c5177987afd0d7bd255642eafb6f7274b7166 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:13:52 +0000 Subject: [PATCH 02/18] Update .clang-format --- project/.clang-format | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/project/.clang-format b/project/.clang-format index f8fa673..8428549 100644 --- a/project/.clang-format +++ b/project/.clang-format @@ -1 +1,3 @@ -AllowShortCaseLabelsOnASingleLine: true \ No newline at end of file +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortBlocksOnASingleLine: Always \ No newline at end of file From a21e076dbf1d1378203bcf11d4bb57de54015031 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Wed, 11 Feb 2026 00:36:13 +0800 Subject: [PATCH 03/18] Update test script and test resources --- project/scripts/test_runner.py | 8 ++++++-- project/tests/hello.c | 14 +++++++++----- .../35_math.in | 0 .../35_math.out | 0 .../35_math.sy | 0 .../36_rotate.in | 0 .../36_rotate.out | 0 .../36_rotate.sy | 0 .../{hidden_functional => float_support}/37_dct.in | 0 .../37_dct.out | 0 .../{hidden_functional => float_support}/37_dct.sy | 0 .../38_light2d.out | 0 .../38_light2d.sy | 0 .../39_fp_params.in | 0 .../39_fp_params.out | 0 .../39_fp_params.sy | 0 .../{functional => float_support}/95_float.in | 0 .../{functional => float_support}/95_float.out | 0 .../{functional => float_support}/95_float.sy | 0 .../96_matrix_add.out | 0 .../{functional => float_support}/96_matrix_add.sy | 0 .../97_matrix_sub.out | 0 .../{functional => float_support}/97_matrix_sub.sy | 0 .../98_matrix_mul.out | 0 .../{functional => float_support}/98_matrix_mul.sy | 0 .../99_matrix_tran.out | 0 .../99_matrix_tran.sy | 0 27 files changed, 15 insertions(+), 7 deletions(-) rename project/tests/resources/{hidden_functional => float_support}/35_math.in (100%) rename project/tests/resources/{hidden_functional => float_support}/35_math.out (100%) rename project/tests/resources/{hidden_functional => float_support}/35_math.sy (100%) rename project/tests/resources/{hidden_functional => float_support}/36_rotate.in (100%) rename project/tests/resources/{hidden_functional => float_support}/36_rotate.out (100%) rename project/tests/resources/{hidden_functional => float_support}/36_rotate.sy (100%) rename project/tests/resources/{hidden_functional => float_support}/37_dct.in (100%) rename project/tests/resources/{hidden_functional => float_support}/37_dct.out (100%) rename project/tests/resources/{hidden_functional => float_support}/37_dct.sy (100%) rename project/tests/resources/{hidden_functional => float_support}/38_light2d.out (100%) rename project/tests/resources/{hidden_functional => float_support}/38_light2d.sy (100%) rename project/tests/resources/{hidden_functional => float_support}/39_fp_params.in (100%) rename project/tests/resources/{hidden_functional => float_support}/39_fp_params.out (100%) rename project/tests/resources/{hidden_functional => float_support}/39_fp_params.sy (100%) rename project/tests/resources/{functional => float_support}/95_float.in (100%) rename project/tests/resources/{functional => float_support}/95_float.out (100%) rename project/tests/resources/{functional => float_support}/95_float.sy (100%) rename project/tests/resources/{functional => float_support}/96_matrix_add.out (100%) rename project/tests/resources/{functional => float_support}/96_matrix_add.sy (100%) rename project/tests/resources/{functional => float_support}/97_matrix_sub.out (100%) rename project/tests/resources/{functional => float_support}/97_matrix_sub.sy (100%) rename project/tests/resources/{functional => float_support}/98_matrix_mul.out (100%) rename project/tests/resources/{functional => float_support}/98_matrix_mul.sy (100%) rename project/tests/resources/{functional => float_support}/99_matrix_tran.out (100%) rename project/tests/resources/{functional => float_support}/99_matrix_tran.sy (100%) diff --git a/project/scripts/test_runner.py b/project/scripts/test_runner.py index 31a3a35..78c5b08 100755 --- a/project/scripts/test_runner.py +++ b/project/scripts/test_runner.py @@ -37,6 +37,10 @@ def read_file(path): with open(path, 'r') as f: return f.read() +def normalize(text): + lines = [line.rstrip() for line in text.strip().splitlines()] + return "\n".join(lines) + def run_cmd(cmd, input_str=None): """运行 shell 命令并获取 stdout, stderr, returncode""" try: @@ -68,7 +72,7 @@ def run_test_case(mode, src_file): # 读取输入和预期输出 stdin_content = read_file(input_file) - expected_output = read_file(expect_file).strip() + expected_output = normalize(read_file(expect_file)) print(f"Testing {name_no_ext} ... ", end='', flush=True) @@ -126,7 +130,7 @@ def run_test_case(mode, src_file): # 先检查输出值是否匹配 out, err, ret = run_cmd(cmd_run, stdin_content) - actual_output = out.strip() + actual_output = normalize(out) if actual_output != expected_output: # 检查返回值和 .out 是否匹配 actual_with_ret = (actual_output + "\n" + str(ret)).strip() diff --git a/project/tests/hello.c b/project/tests/hello.c index 08943f1..51a0e2c 100644 --- a/project/tests/hello.c +++ b/project/tests/hello.c @@ -1,5 +1,9 @@ -int main() { - int arr[10]; - int n = getarray(arr); - return 0; -} \ No newline at end of file +int main(){ + const int a[4][2] = {{1, 2}, {3, 4}, {}, 7}; + const int N = 3; + int b[4][2] = {}; + int c[4][2] = {1, 2, 3, 4, 5, 6, 7, 8}; + int d[N + 1][2] = {1, 2, {3}, {5}, a[3][0], 8}; + int e[4][2][1] = {{d[2][1], {c[2][1]}}, {3, 4}, {5, 6}, {7, 8}}; + return e[3][1][0] + e[0][0][0] + e[0][1][0] + d[3][0]; +} diff --git a/project/tests/resources/hidden_functional/35_math.in b/project/tests/resources/float_support/35_math.in similarity index 100% rename from project/tests/resources/hidden_functional/35_math.in rename to project/tests/resources/float_support/35_math.in diff --git a/project/tests/resources/hidden_functional/35_math.out b/project/tests/resources/float_support/35_math.out similarity index 100% rename from project/tests/resources/hidden_functional/35_math.out rename to project/tests/resources/float_support/35_math.out diff --git a/project/tests/resources/hidden_functional/35_math.sy b/project/tests/resources/float_support/35_math.sy similarity index 100% rename from project/tests/resources/hidden_functional/35_math.sy rename to project/tests/resources/float_support/35_math.sy diff --git a/project/tests/resources/hidden_functional/36_rotate.in b/project/tests/resources/float_support/36_rotate.in similarity index 100% rename from project/tests/resources/hidden_functional/36_rotate.in rename to project/tests/resources/float_support/36_rotate.in diff --git a/project/tests/resources/hidden_functional/36_rotate.out b/project/tests/resources/float_support/36_rotate.out similarity index 100% rename from project/tests/resources/hidden_functional/36_rotate.out rename to project/tests/resources/float_support/36_rotate.out diff --git a/project/tests/resources/hidden_functional/36_rotate.sy b/project/tests/resources/float_support/36_rotate.sy similarity index 100% rename from project/tests/resources/hidden_functional/36_rotate.sy rename to project/tests/resources/float_support/36_rotate.sy diff --git a/project/tests/resources/hidden_functional/37_dct.in b/project/tests/resources/float_support/37_dct.in similarity index 100% rename from project/tests/resources/hidden_functional/37_dct.in rename to project/tests/resources/float_support/37_dct.in diff --git a/project/tests/resources/hidden_functional/37_dct.out b/project/tests/resources/float_support/37_dct.out similarity index 100% rename from project/tests/resources/hidden_functional/37_dct.out rename to project/tests/resources/float_support/37_dct.out diff --git a/project/tests/resources/hidden_functional/37_dct.sy b/project/tests/resources/float_support/37_dct.sy similarity index 100% rename from project/tests/resources/hidden_functional/37_dct.sy rename to project/tests/resources/float_support/37_dct.sy diff --git a/project/tests/resources/hidden_functional/38_light2d.out b/project/tests/resources/float_support/38_light2d.out similarity index 100% rename from project/tests/resources/hidden_functional/38_light2d.out rename to project/tests/resources/float_support/38_light2d.out diff --git a/project/tests/resources/hidden_functional/38_light2d.sy b/project/tests/resources/float_support/38_light2d.sy similarity index 100% rename from project/tests/resources/hidden_functional/38_light2d.sy rename to project/tests/resources/float_support/38_light2d.sy diff --git a/project/tests/resources/hidden_functional/39_fp_params.in b/project/tests/resources/float_support/39_fp_params.in similarity index 100% rename from project/tests/resources/hidden_functional/39_fp_params.in rename to project/tests/resources/float_support/39_fp_params.in diff --git a/project/tests/resources/hidden_functional/39_fp_params.out b/project/tests/resources/float_support/39_fp_params.out similarity index 100% rename from project/tests/resources/hidden_functional/39_fp_params.out rename to project/tests/resources/float_support/39_fp_params.out diff --git a/project/tests/resources/hidden_functional/39_fp_params.sy b/project/tests/resources/float_support/39_fp_params.sy similarity index 100% rename from project/tests/resources/hidden_functional/39_fp_params.sy rename to project/tests/resources/float_support/39_fp_params.sy diff --git a/project/tests/resources/functional/95_float.in b/project/tests/resources/float_support/95_float.in similarity index 100% rename from project/tests/resources/functional/95_float.in rename to project/tests/resources/float_support/95_float.in diff --git a/project/tests/resources/functional/95_float.out b/project/tests/resources/float_support/95_float.out similarity index 100% rename from project/tests/resources/functional/95_float.out rename to project/tests/resources/float_support/95_float.out diff --git a/project/tests/resources/functional/95_float.sy b/project/tests/resources/float_support/95_float.sy similarity index 100% rename from project/tests/resources/functional/95_float.sy rename to project/tests/resources/float_support/95_float.sy diff --git a/project/tests/resources/functional/96_matrix_add.out b/project/tests/resources/float_support/96_matrix_add.out similarity index 100% rename from project/tests/resources/functional/96_matrix_add.out rename to project/tests/resources/float_support/96_matrix_add.out diff --git a/project/tests/resources/functional/96_matrix_add.sy b/project/tests/resources/float_support/96_matrix_add.sy similarity index 100% rename from project/tests/resources/functional/96_matrix_add.sy rename to project/tests/resources/float_support/96_matrix_add.sy diff --git a/project/tests/resources/functional/97_matrix_sub.out b/project/tests/resources/float_support/97_matrix_sub.out similarity index 100% rename from project/tests/resources/functional/97_matrix_sub.out rename to project/tests/resources/float_support/97_matrix_sub.out diff --git a/project/tests/resources/functional/97_matrix_sub.sy b/project/tests/resources/float_support/97_matrix_sub.sy similarity index 100% rename from project/tests/resources/functional/97_matrix_sub.sy rename to project/tests/resources/float_support/97_matrix_sub.sy diff --git a/project/tests/resources/functional/98_matrix_mul.out b/project/tests/resources/float_support/98_matrix_mul.out similarity index 100% rename from project/tests/resources/functional/98_matrix_mul.out rename to project/tests/resources/float_support/98_matrix_mul.out diff --git a/project/tests/resources/functional/98_matrix_mul.sy b/project/tests/resources/float_support/98_matrix_mul.sy similarity index 100% rename from project/tests/resources/functional/98_matrix_mul.sy rename to project/tests/resources/float_support/98_matrix_mul.sy diff --git a/project/tests/resources/functional/99_matrix_tran.out b/project/tests/resources/float_support/99_matrix_tran.out similarity index 100% rename from project/tests/resources/functional/99_matrix_tran.out rename to project/tests/resources/float_support/99_matrix_tran.out diff --git a/project/tests/resources/functional/99_matrix_tran.sy b/project/tests/resources/float_support/99_matrix_tran.sy similarity index 100% rename from project/tests/resources/functional/99_matrix_tran.sy rename to project/tests/resources/float_support/99_matrix_tran.sy From e23aa62832ad3b5043a71181de6ca496faca56e0 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Wed, 11 Feb 2026 00:38:44 +0800 Subject: [PATCH 04/18] Finish lv9 ir part - Removed unused count_name variable in ir_builder.cppm. - Cleaned up formatting in symbol_table.cppm for better readability. - Changed debug() method to toKoopa() in type.cppm and its derived classes for consistency in type representation. - Updated grammar rules in sysy.y to support array initialization and improved handling of parameter arrays. - Enhanced AST code generation in ast.cpp and codegen.cpp to support new initialization semantics and improved memory management for arrays. - Added flattening mechanism for initializer lists in InitValStmtAST to handle complex array initializations. - Improved error handling and semantic checks throughout the codebase. --- project/include/ir/ast.cppm | 89 ++++-- project/include/ir/ir_builder.cppm | 1 - project/include/ir/symbol_table.cppm | 16 +- project/include/ir/type.cppm | 22 +- project/src/frontend/sysy.y | 125 +++++--- project/src/ir/ast.cpp | 17 +- project/src/ir/codegen.cpp | 446 ++++++++++++++++++++------- 7 files changed, 511 insertions(+), 205 deletions(-) diff --git a/project/include/ir/ast.cppm b/project/include/ir/ast.cppm index 377f3b7..adbf8c0 100644 --- a/project/include/ir/ast.cppm +++ b/project/include/ir/ast.cppm @@ -15,6 +15,7 @@ export module ir.ast; import symbol_table; import ir_builder; +import ir.type; /** * @brief Namespace containing all AST related classes and functions. @@ -59,6 +60,7 @@ public: class LValAST; class BlockAST; +class InitValStmtAST; //* enum class // clang-format off @@ -98,17 +100,21 @@ public: std::string btype; std::string ident; bool is_const; + bool is_ptr; + std::vector> indices; /** * @brief Constructs a function parameter. * @param _btype The type of the parameter (e.g., "int"). * @param _ident The name of the parameter. * @param _is_const Whether the parameter is constant. */ - FuncParamAST(std::string _btype, std::string _ident, bool _is_const) - : btype(std::move(_btype)), ident(std::move(_ident)), - is_const(_is_const) {} + FuncParamAST(std::string _btype, std::string _ident, bool _is_const, + bool _is_ptr, std::vector> _indices) + : btype(std::move(_btype)), ident(std::move(_ident)), is_const(_is_const), + is_ptr(_is_ptr), indices(std::move(_indices)) {} auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; + auto toKoopa(ir::KoopaBuilder &builder) const -> std::string; }; class DefAST : public BaseAST {}; @@ -139,16 +145,14 @@ public: class ArrayDefAST : public DefAST { public: + ~ArrayDefAST() override; bool is_const; std::string ident; - std::unique_ptr expr; - std::vector> initialize_list; - ArrayDefAST(bool _is_const, std::string _ident, BaseAST *_expr, - std::vector> _initialize_list) - : is_const(_is_const), ident(std::move(_ident)), - initialize_list(std::move(_initialize_list)) { - expr.reset(static_cast(_expr)); - } + std::vector> array_suffix; + std::unique_ptr init_val; + ArrayDefAST(bool _is_const, std::string _ident, + std::vector> _array_suffix, + InitValStmtAST *_init_val); auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; @@ -169,9 +173,7 @@ public: */ ScalarDefAST(bool _is_const, std::string _ident, BaseAST *_initVal) : is_const(_is_const), ident(std::move(_ident)) { - if (_initVal) { - initVal.reset(static_cast(_initVal)); - } + if (_initVal) { initVal.reset(static_cast(_initVal)); } } auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; @@ -217,13 +219,43 @@ public: * @param _expr The expression to evaluate. */ ExprStmtAST(BaseAST *_expr) { - if (_expr) { - expr.reset(static_cast(_expr)); - } + if (_expr) { expr.reset(static_cast(_expr)); } } auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; + +/** + * @brief Represents an initialization value statement in the abstract syntax + * tree. + * + * This class is used to model initialization values, which can be either a + * single expression or a list of nested initializers (e.g., for arrays or + * aggregate types). + */ +class InitValStmtAST : public StmtAST { +public: + std::unique_ptr expr; + std::vector> initialize_list; + /** + * @brief Constructs an InitValStmtAST object. + * + * @param _expr Pointer to a BaseAST that will be statically cast to ExprAST. + * It represents the primary initialization expression. + * @param _initialize_list Vector of unique pointers to InitValStmtAST for + * nested initialization. This list is moved into the member to avoid copying. + */ + InitValStmtAST(BaseAST *_expr, + std::vector> _initialize_list) + : initialize_list(std::move(_initialize_list)) { + expr.reset(static_cast(_expr)); + } + + auto dump(int depth) const -> void override; + auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; + auto flatten(std::shared_ptr, ir::KoopaBuilder &) const + -> std::vector; +}; /** @} */ /** @name Data Action @@ -280,9 +312,7 @@ public: * @param _expr The return value expression (optional). */ ReturnStmtAST(BaseAST *_expr) { - if (_expr) { - expr.reset(static_cast(_expr)); - } + if (_expr) { expr.reset(static_cast(_expr)); } } auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; @@ -363,14 +393,13 @@ public: class LValAST : public ExprAST { public: std::string ident; - std::unique_ptr index; + std::vector> indices; /** * @brief Constructs an LVal node. * @param _ident The variable name. */ - LValAST(std::string _ident, BaseAST *_index) : ident(std::move(_ident)) { - index.reset(static_cast(_index)); - }; + LValAST(std::string _ident, std::vector> _indices) + : ident(std::move(_ident)), indices(std::move(_indices)) {}; auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; auto CalcValue(ir::KoopaBuilder &builder) const -> int override; @@ -406,9 +435,7 @@ public: * @param _rhs The operand expression. */ UnaryExprAST(UnaryOp _op, BaseAST *_rhs) : op(_op) { - if (_rhs) { - rhs.reset(static_cast(_rhs)); - } + if (_rhs) { rhs.reset(static_cast(_rhs)); } } auto dump(int depth) const -> void override; @@ -429,12 +456,8 @@ public: * @param _rhs The right operand. */ BinaryExprAST(BinaryOp _op, BaseAST *_lhs, BaseAST *_rhs) : op(_op) { - if (_lhs) { - lhs.reset(static_cast(_lhs)); - } - if (_rhs) { - rhs.reset(static_cast(_rhs)); - } + if (_lhs) { lhs.reset(static_cast(_lhs)); } + if (_rhs) { rhs.reset(static_cast(_rhs)); } } auto dump(int depth) const -> void override; diff --git a/project/include/ir/ir_builder.cppm b/project/include/ir/ir_builder.cppm index 249fa5b..c6bff03 100644 --- a/project/include/ir/ir_builder.cppm +++ b/project/include/ir/ir_builder.cppm @@ -151,7 +151,6 @@ public: */ auto resetCount() -> void { count_reg = 0; - count_name = 0; count_label = 0; _is_block_closed = false; } diff --git a/project/include/ir/symbol_table.cppm b/project/include/ir/symbol_table.cppm index ac1e9b9..2da991f 100644 --- a/project/include/ir/symbol_table.cppm +++ b/project/include/ir/symbol_table.cppm @@ -33,7 +33,7 @@ struct Symbol { std::string irName; ///< IR-level name (e.g., "@x_1") std::shared_ptr type; ///< Data type of the symbol SymbolKind kind; ///< Variable or Function - bool is_const; ///< True if it's a compile-time constant + bool is_const; ///< True if it's a compile-time constant int constValue; ///< The value of the constant, if applicable }; @@ -67,9 +67,7 @@ public: * @note Will not pop the global scope (level 0). */ auto exitScope() -> void { - if (scopes.size() > 1u) { - scopes.pop_back(); - } + if (scopes.size() > 1u) { scopes.pop_back(); } } /** @@ -94,8 +92,8 @@ public: * @param val Initial value if constant. */ auto define(const std::string &name, const std::string &irName, - std::shared_ptr type, SymbolKind kind, - bool is_const, int val = 0) -> void { + std::shared_ptr type, SymbolKind kind, bool is_const, + int val = 0) -> void { if (scopes.back().contains(name)) { Log::panic("Semantic Error: Redefinition of " + name); @@ -127,11 +125,9 @@ public: * @return Symbol* Pointer to the symbol if found, else nullptr. */ auto lookup(const std::string &name) -> Symbol * { - for (auto &scope : std::views::reverse(scopes)) { + for (auto &scope : scopes | std::views::reverse) { auto it = scope.find(name); - if (it != scope.end()) { - return &(it->second); - } + if (it != scope.end()) { return &(it->second); } } return nullptr; } diff --git a/project/include/ir/type.cppm b/project/include/ir/type.cppm index ad2276f..924b8b0 100644 --- a/project/include/ir/type.cppm +++ b/project/include/ir/type.cppm @@ -24,7 +24,7 @@ public: /** * @brief Returns a string representation of the type. */ - virtual auto debug() const -> std::string = 0; + virtual auto toKoopa() const -> std::string = 0; virtual auto is_int() const -> bool { return false; } virtual auto is_void() const -> bool { return false; } @@ -39,7 +39,7 @@ public: */ class IntType : public Type { public: - auto debug() const -> std::string override { return "i32"; } + auto toKoopa() const -> std::string override { return "i32"; } auto is_int() const -> bool override { return true; } /** @@ -56,7 +56,7 @@ public: */ class VoidType : public Type { public: - auto debug() const -> std::string override { return "void"; } + auto toKoopa() const -> std::string override { return "void"; } auto is_void() const -> bool override { return true; } /** @@ -73,7 +73,7 @@ public: */ class BoolType : public Type { public: - auto debug() const -> std::string override { return "bool"; } + auto toKoopa() const -> std::string override { return "bool"; } auto is_bool() const -> bool override { return true; } /** @@ -90,7 +90,7 @@ public: */ class FloatType : public Type { public: - auto debug() const -> std::string override { return "float"; } + auto toKoopa() const -> std::string override { return "float"; } auto is_float() const -> bool override { return true; } /** @@ -111,8 +111,8 @@ public: PtrType(std::shared_ptr t) : target(t) {}; - auto debug() const -> std::string override { - return fmt::format("*{}", target->debug()); + auto toKoopa() const -> std::string override { + return fmt::format("*{}", target->toKoopa()); } auto is_ptr() const -> bool override { return true; } @@ -128,8 +128,8 @@ public: ArrayType(std::shared_ptr t, int _len) : base(t), len(_len) {} - auto debug() const -> std::string override { - return fmt::format("Array type (len : {}): {}", len, base->debug()); + auto toKoopa() const -> std::string override { + return fmt::format("[{}, {}]", base->toKoopa(), len); } auto is_array() const -> bool override { return true; } @@ -139,9 +139,7 @@ public: std::shared_ptr> cache; auto key = std::make_pair(_base, _len); - if (cache.contains(key)) { - return cache[key]; - } + if (cache.contains(key)) { return cache[key]; } auto instance = std::make_shared(_base, _len); return cache[key] = instance; } diff --git a/project/src/frontend/sysy.y b/project/src/frontend/sysy.y index 8947099..d5dbe5d 100644 --- a/project/src/frontend/sysy.y +++ b/project/src/frontend/sysy.y @@ -46,11 +46,13 @@ void yyerror(std::unique_ptr &ast, const char *str); std::string *str_val; int int_val; ast::BaseAST *ast_val; + ast::InitValStmtAST *init_val; std::vector> *items_val; std::vector> *defs_val; std::vector> *children_val; std::vector> *funcParams_val; std::vector> *args_val; + std::vector> *initialize_list_val; } // terminal letters are written in uppercase. @@ -64,7 +66,9 @@ void yyerror(std::unique_ptr &ast, const char *str); %type ConstDef VarDef BlockItem Decl ConstDecl VarDecl %type CompUnitItem CompUnit FuncFParam %type FuncFParams -%type FuncRParams InitializeList InitVal +%type FuncRParams ArraySuffix ParamArraySuffix +%type InitVal +%type InitializeList %type CompUnitItemList %type BlockItemList %type VarDefList ConstDefList @@ -72,14 +76,6 @@ void yyerror(std::unique_ptr &ast, const char *str); %nonassoc LOWER_ELSE %nonassoc ELSE -/* -VarDef ::= IDENT ["[" ConstExp "]"] - | IDENT ["[" ConstExp "]"] "=" InitVal; -InitVal ::= Exp | "{" [Exp {"," Exp}] "}"; - -LVal ::= IDENT ["[" Exp "]"]; -*/ - %left OR // || %left AND // && %left EQ NE // == , != @@ -88,6 +84,14 @@ LVal ::= IDENT ["[" Exp "]"]; %left '*' '/' '%' // * / % %right '!' PRIORITY // ! - +/* +VarDef ::= IDENT {"[" ConstExp "]"} + | IDENT {"[" ConstExp "]"} "=" InitVal; +InitVal ::= Exp | "{" [InitVal {"," InitVal}] "}"; + +LVal ::= IDENT {"[" Exp "]"}; +*/ + %% /** * @brief Top-level unit of a program. @@ -151,18 +155,41 @@ FuncFParams $$->push_back(std::unique_ptr(static_cast($3))); }; +// foo(int []) -> *i32 +// foo(int [][1]) -> **i32 FuncFParam : Btype IDENT { - $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false); + $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false, false, {}); delete $1; delete $2; } + | Btype IDENT ParamArraySuffix { + $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false, true, std::move(*$3)); + delete $1; + delete $2; + delete $3; + } | CONST Btype IDENT { - $$ = new FuncParamAST(std::move(*$2), std::move(*$3), true); + $$ = new FuncParamAST(std::move(*$2), std::move(*$3), true, false, {}); delete $2; delete $3; } + | CONST Btype IDENT ParamArraySuffix { + $$ = new FuncParamAST(std::move(*$3), std::move(*$3), true, true, std::move(*$4)); + delete $2; + delete $3; + delete $4; + }; +ParamArraySuffix + : '[' ']' { + $$ = new std::vector>(); + $$->push_back(nullptr); + } + | '[' ']' ArraySuffix { + $$ = $3; + $$->insert($$->begin(), nullptr); + }; /** * @brief A block of code enclosed in curly braces. @@ -202,11 +229,11 @@ ConstDefList : ConstDef { $$ = new std::vector>(); // this is a pointer , so we don't need move optimize. - $$->push_back(std::unique_ptr(static_cast($1))); + $$->push_back(std::unique_ptr(static_cast($1))); } | ConstDefList ',' ConstDef { $$ = $1; - $$->push_back(std::unique_ptr(static_cast($3))); + $$->push_back(std::unique_ptr(static_cast($3))); }; ConstDef @@ -215,28 +242,49 @@ ConstDef $$ = new ScalarDefAST(true, std::move(*$1), $3); delete $1; } - | IDENT '[' Expr ']' '=' InitVal { - $$ = new ArrayDefAST(true, std::move(*$1), $3, std::move(*$6)); + | IDENT ArraySuffix '=' InitVal { + $$ = new ArrayDefAST(true, std::move(*$1), std::move(*$2), $4); delete $1; - delete $6; + delete $2; }; -InitVal - : '{' '}' { +/* +ConstDef ::= IDENT {"[" ConstExp "]"} "=" ConstInitVal; +ConstInitVal ::= ConstExp | "{" [ConstInitVal {"," ConstInitVal}] "}"; +*/ + +ArraySuffix + : '[' Expr ']' { $$ = new std::vector>(); + $$->push_back(std::unique_ptr(static_cast($2))); } - | '{' InitializeList '}' { - $$ = $2; + | ArraySuffix '[' Expr ']' { + $$ = $1; + $$->push_back(std::unique_ptr(static_cast($3))); }; InitializeList - : Expr { - $$ = new std::vector>(); - $$->push_back(std::unique_ptr(static_cast($1))); + : InitVal { + $$ = new std::vector>(); + $$->push_back(std::unique_ptr($1)); } - | InitializeList ',' Expr { + | InitializeList ',' InitVal { $$ = $1; - $$->push_back(std::unique_ptr(static_cast($3))); + $$->push_back(std::unique_ptr($3)); + }; + +// {1, 2, {2, 0}} +InitVal + : Expr { + $$ = new InitValStmtAST($1, {}); + // delete $1; + } + | '{' '}' { + $$ = new InitValStmtAST(nullptr, {}); + } + | '{' InitializeList '}' { + $$ = new InitValStmtAST(nullptr, std::move(*$2)); + delete $2; }; /* @@ -255,11 +303,11 @@ VarDecl VarDefList : VarDef { $$ = new std::vector>(); - $$->push_back(std::unique_ptr(static_cast($1))); + $$->push_back(std::unique_ptr(static_cast($1))); }; | VarDefList ',' VarDef { $$ = $1; - $$->push_back(std::unique_ptr(static_cast($3))); + $$->push_back(std::unique_ptr(static_cast($3))); }; VarDef @@ -271,14 +319,16 @@ VarDef $$ = new ScalarDefAST(false, std::move(*$1), nullptr); delete $1; } - | IDENT '[' Expr ']' { - $$ = new ArrayDefAST(false, std::move(*$1), $3, {}); + | IDENT ArraySuffix { + $$ = new ArrayDefAST(false, std::move(*$1), std::move(*$2), nullptr); delete $1; + delete $2; } - | IDENT '[' Expr ']' '=' InitVal { - $$ = new ArrayDefAST(false, std::move(*$1), $3, std::move(*$6)); + | IDENT ArraySuffix '=' InitVal { + $$ = new ArrayDefAST(false, std::move(*$1), std::move(*$2), $4); delete $1; - delete $6; + delete $2; + // delete $4; }; /* @@ -335,7 +385,7 @@ Expr : Number { $$ = $1; } | '(' Expr ')' { $$ = $2; } | LVal { $$ = $1; } - /* unary experssion */ + /* unary expression */ /* function call */ | IDENT '(' ')' { auto args = std::vector>(); @@ -356,7 +406,7 @@ Expr | '-' Expr %prec PRIORITY { $$ = new UnaryExprAST(UnaryOp::Neg, $2); } - /* binary experssion */ + /* binary expression */ | Expr '+' Expr { $$ = new BinaryExprAST(BinaryOp::Add, $1, $3); } @@ -404,12 +454,13 @@ Number LVal : IDENT { - $$ = new LValAST(std::move(*$1), nullptr); + $$ = new LValAST(std::move(*$1), {}); delete $1; } - | IDENT '[' Expr ']' { - $$ = new LValAST(std::move(*$1), $3); + | IDENT ArraySuffix { + $$ = new LValAST(std::move(*$1), std::move(*$2)); delete $1; + delete $2; }; /** diff --git a/project/src/ir/ast.cpp b/project/src/ir/ast.cpp index a6faf69..615366c 100644 --- a/project/src/ir/ast.cpp +++ b/project/src/ir/ast.cpp @@ -22,6 +22,7 @@ using namespace detail; // Destructors to solve the problem of forward declaration with smart pointers AssignStmtAST::~AssignStmtAST() = default; FuncDefAST::~FuncDefAST() = default; +ArrayDefAST::~ArrayDefAST() = default; /** * @brief Constructs a FuncDefAST node. @@ -50,6 +51,11 @@ AssignStmtAST::AssignStmtAST(BaseAST *_lval, BaseAST *_expr) { } } +ArrayDefAST::ArrayDefAST(bool _is_const, std::string _ident, + std::vector> _array_suffix, + InitValStmtAST *_init_val) + : is_const(_is_const), ident(std::move(_ident)), + array_suffix(std::move(_array_suffix)), init_val(_init_val) {} /** * @brief Dumps CompUnitAST node details. @@ -85,6 +91,7 @@ auto FuncDefAST::dump(int depth) const -> void { } } +// TODO: add number auto ArrayDefAST::dump(int depth) const -> void { fmt::println("{}ArrayDefAST: {}", indent(depth), ident); } @@ -100,7 +107,6 @@ auto ScalarDefAST::dump(int depth) const -> void { } } - /** * @brief Dumps BlockAST node details. * @param depth Indentation depth. @@ -123,6 +129,12 @@ auto ExprStmtAST::dump(int depth) const -> void { } } +auto InitValStmtAST::dump(int depth) const -> void { + fmt::println("{}InitValStmtAST:", indent(depth)); + for (const auto &init_val : initialize_list) { + init_val->dump(depth + 1); + } +} /** * @brief Dumps ReturnStmtAST node details. @@ -145,7 +157,6 @@ auto AssignStmtAST::dump(int depth) const -> void { expr->dump(depth + 1); } - /** * @brief Dumps DeclAST node details. * @param depth Indentation depth. @@ -158,7 +169,6 @@ auto DeclAST::dump(int depth) const -> void { } } - /** * @brief Dumps IfStmtAST node details. * @param depth Indentation depth. @@ -206,7 +216,6 @@ auto ContinueStmtAST::dump(int depth) const -> void { fmt::println("{}ContinueAST", indent(depth)); } - /** * @brief Dumps NumberAST node details. * @param depth Indentation depth. diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index a6572c9..9fc12d2 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -26,6 +26,8 @@ module; #include #include +#include +#include #include #include #include @@ -38,6 +40,7 @@ import log; using namespace ast; using namespace detail; +using namespace std::views; /** * @brief Generates IR for a compilation unit (the top-level node). @@ -51,9 +54,7 @@ using namespace detail; auto CompUnitAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { for (const auto &child : children) { child->codeGen(builder); - if (&child != &children.back()) { - builder.append("\n"); - } + if (&child != &children.back()) { builder.append("\n"); } } return ""; } @@ -72,19 +73,44 @@ auto FuncParamAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (btype == "void") { Log::panic("Semantic Error: Variable cannot be of type 'void'"); } - if (is_const) { - builder.symtab().define(ident, "", type::IntType::get(), SymbolKind::Var, - true, 0); + + std::shared_ptr param_type; + if (is_ptr) { + std::shared_ptr base_type = type::IntType::get(); + for (const auto &dim : + indices | reverse | filter([](auto &p) { return p != nullptr; })) { + base_type = type::ArrayType::get(base_type, dim->CalcValue(builder)); + } + param_type = type::PtrType::get(base_type); } else { - std::string addr = builder.newReg(); - builder.append(fmt::format(" {} = alloc i32\n", addr)); - builder.symtab().define(ident, addr, type::IntType::get(), SymbolKind::Var, - false); - builder.append(fmt::format(" store @{}, {}\n", ident, addr)); + param_type = type::IntType::get(); } + + std::string addr = builder.newReg(); + builder.append(fmt::format(" {} = alloc {}\n", addr, param_type->toKoopa())); + builder.append(fmt::format(" store @{}, {}\n", ident, addr)); + + builder.symtab().define(ident, addr, param_type, SymbolKind::Var, is_const); return ""; } +auto FuncParamAST::toKoopa(ir::KoopaBuilder &builder) const -> std::string { + std::shared_ptr param_type; + + if (is_ptr) { + std::shared_ptr base_type = type::IntType::get(); + for (const auto &dim : + indices | reverse | filter([](auto &p) { return p != nullptr; })) { + base_type = type::ArrayType::get(base_type, dim->CalcValue(builder)); + } + param_type = type::PtrType::get(base_type); + } else { + param_type = type::IntType::get(); + } + + return param_type->toKoopa(); +} + /** * @brief Generates IR for a function definition. * @@ -101,18 +127,18 @@ auto FuncParamAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { */ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { builder.resetCount(); - auto btype2irType = [](std::string_view s) -> std::string { - return (s == "int" ? "i32" : ""); - }; + // auto btype2irType = [](std::string_view s) -> std::string { + // return (s == "int" ? "i32" : ""); + // }; builder.append(fmt::format("{} @", (block ? "fun" : "decl"))); builder.append(ident); builder.append("("); for (const auto ¶m : params) { + // builder.append( + // fmt::format("@{}: {}", param->ident, btype2irType(param->btype))); builder.append( - fmt::format("@{}: {}", param->ident, btype2irType(param->btype))); - if (¶m != ¶ms.back()) { - builder.append(", "); - } + fmt::format("@{}: {}", param->ident, param->toKoopa(builder))); + if (¶m != ¶ms.back()) { builder.append(", "); } } if (btype == "void") { @@ -120,14 +146,12 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { builder.symtab().defineGlobal(ident, "", type::VoidType::get(), SymbolKind::Func, false); } else { - builder.append(fmt::format("): {} ", btype2irType(btype))); + builder.append("): i32 "); builder.symtab().defineGlobal(ident, "", type::IntType::get(), SymbolKind::Func, false); } - if (!block) { - return ""; - } + if (!block) { return ""; } // enterScope mechanism: creates a new symbol table level for local variables // and parameters. @@ -161,58 +185,83 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { } auto ArrayDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - int len = expr->CalcValue(builder); + std::shared_ptr arr_type = type::IntType::get(); + for (const auto &dim : array_suffix | reverse) { + arr_type = type::ArrayType::get(arr_type, dim->CalcValue(builder)); + } + + auto ir_array_suffix = [&](this auto &&self, int idx) -> std::string { + if (idx == ssize(array_suffix) - 1) { + return fmt::format("[i32, {}]", array_suffix[idx]->CalcValue(builder)); + } + return fmt::format("[{}, {}]", self(idx + 1), + array_suffix[idx]->CalcValue(builder)); + }(0); + + Log::trace(ir_array_suffix); if (builder.symtab().isGlobalScope()) { //! [Caution] : Since global variables do not have naming conflicts, we //! will consistently use ident without the prefix. - if (ssize(initialize_list) == 0) { - builder.append( - fmt::format("global @{} = alloc [i32, {}], zeroinit\n", ident, len)); + //! So this avoids naming conflicts between global and local arrays. + std::string ir_name = builder.newVar(ident); + if (init_val == nullptr) { + builder.append(fmt::format("global {} = alloc {}, zeroinit\n", ir_name, + ir_array_suffix)); } else { builder.append( - fmt::format("global @{} = alloc [i32, {}], {{", ident, len)); - - for (const auto &[i, elem] : std::views::enumerate(initialize_list)) { - builder.append(fmt::format("{}", elem->CalcValue(builder))); - if (i < len - 1) { - builder.append(", "); - } - } - for (int i = ssize(initialize_list); i < len; ++i) { - builder.append("0"); - if (i < len - 1) { - builder.append(", "); + fmt::format("global {} = alloc {}, ", ir_name, ir_array_suffix)); + auto flatten_initialize_list = init_val->flatten(arr_type, builder); + + int idx = 0; + auto result = [&](this auto &&self, + std::shared_ptr type) -> std::string { + auto arr_type = std::dynamic_pointer_cast(type); + if (arr_type) { + std::string res = "{"; + + for (int i : iota(0, arr_type->len)) { + if (i > 0) { res += ", "; } + res += self(arr_type->base); + } + + return res + "}"; } - } + return flatten_initialize_list[idx++]; + }(arr_type); - builder.append("}\n"); - - builder.symtab().defineGlobal( - ident, "@" + ident, type::ArrayType::get(type::IntType::get(), len), - SymbolKind::Var, is_const); + builder.append(result + "\n"); } - } else { + builder.symtab().defineGlobal(ident, ir_name, arr_type, SymbolKind::Var, + is_const); + } else { auto addr = builder.newVar(ident); - builder.append(fmt::format(" {} = alloc [i32, {}]\n", addr, len)); - for (const auto &[i, elem] : std::views::enumerate(initialize_list)) { - auto tmp_reg = builder.newReg(); - builder.append( - fmt::format(" {} = getelemptr {}, {}\n", tmp_reg, addr, i)); - builder.append( - fmt::format(" store {}, {}\n", elem->CalcValue(builder), tmp_reg)); - } - for (int i = ssize(initialize_list); i < len; ++i) { - auto tmp_reg = builder.newReg(); - builder.append( - fmt::format(" {} = getelemptr {}, {}\n", tmp_reg, addr, i)); - builder.append(fmt::format(" store 0, {}\n", tmp_reg)); - } + // builder.append(fmt::format("[debug]: {}\n", ir_array_suffix)); + builder.append(fmt::format(" {} = alloc {}\n", addr, ir_array_suffix)); + + //! If a local variable has no initial value, its content is undefined + if (init_val != nullptr) { + auto flatten_initialize_list = init_val->flatten(arr_type, builder); + + int idx = 0; + [&](this auto &&self, std::shared_ptr type, + std::string ptr) -> void { + if (auto arr_type = std::dynamic_pointer_cast(type)) { + for (int i : iota(0, arr_type->len)) { + auto nxt_ptr = builder.newReg(); + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", nxt_ptr, ptr, i)); + self(arr_type->base, nxt_ptr); + } + return; + } - builder.symtab().define(ident, addr, - type::ArrayType::get(type::IntType::get(), len), - SymbolKind::Var, is_const); + auto value = flatten_initialize_list[idx++]; + builder.append(fmt::format(" store {}, {}\n", value, ptr)); + }(arr_type, addr); + } + builder.symtab().define(ident, addr, arr_type, SymbolKind::Var, is_const); } return ""; @@ -258,9 +307,7 @@ auto ScalarDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { // const btype var = value if (is_const) { int val = 0; - if (initVal) { - val = initVal->CalcValue(builder); - } + if (initVal) { val = initVal->CalcValue(builder); } builder.symtab().define(ident, "", type::IntType::get(), SymbolKind::Var, true, val); } else { @@ -289,13 +336,9 @@ auto ScalarDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return An empty string. */ auto BlockAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - if (this->createScope) { - builder.enterScope(); - } + if (this->createScope) { builder.enterScope(); } for (const auto &item : items) { - if (builder.isBlockClose()) { - continue; - } + if (builder.isBlockClose()) { continue; } item->codeGen(builder); } if (this->createScope) { @@ -316,12 +359,146 @@ auto BlockAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return An empty string. */ auto ExprStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - if (expr) { - expr->codeGen(builder); - } + if (expr) { expr->codeGen(builder); } + return ""; +} + +auto InitValStmtAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const + -> std::string { + //! No node should call `InitValStmtAST`'s `codeGen` function, as it is only + //! responsible for expanding the initialize list, not for generating code. + Log::panic(""); return ""; } +/** + * @brief Flattens a nested SysY initializer list into a linear vector of IR + * values. + * + * Origin: This mechanism handles the complex C-style array initialization + * rules, where braces {} force alignment and scalars (numbers) flow into + * available slots. + * + * Mechanism: + * 1. Flow Mode: Scalars fill the next available i32 slots across dimension + * boundaries. + * 2. Align Mode: Braces force the "cursor" to align with the start of the next + * sub-type. + * + * @param targetType The expected Type (Int or Array) for the current level. + * @param builder The IR builder used to generate code for expressions. + * @return std::vector A flat list of IR constants or register + * names. + */ +auto InitValStmtAST::flatten(std::shared_ptr targetType, + ir::KoopaBuilder &builder) const + -> std::vector { + + // Helper to cast Type to ArrayType + static auto make_arrType = [](std::shared_ptr t) { + return std::dynamic_pointer_cast(t); + }; + + // 'idx' tracks the current position in the initializer list at the top level. + int idx = 0; + + /** + * @brief Recursive lambda to process the hierarchy of types and initializers. + * @param flatten_impl Self-reference for recursion. + * @param type The type we are currently trying to fill. + * @param list The current list of InitVal nodes we are consuming. + * @param idx Reference to the cursor in the current list. + */ + auto result = [&](this auto &&flatten_impl, std::shared_ptr type, + const std::vector> &list, + int &idx) -> std::vector { + // Base Case: Target is a simple Integer + if (type->is_int()) { + // If no more data is provided, SysY requires implicit + // zero-initialization. + if (idx >= ssize(list)) { return {"0"}; } + + const auto &node = list[idx]; + // Semantic Check: A scalar target cannot be initialized by a brace list. + // e.g., int a[2] = {1, {2}}; is invalid. + if (!node->expr) { + Log::panic(fmt::format( + "Semantic Error: Expected scalar, but found brace list")); + } + + // Move the cursor after consuming one scalar value. + idx++; + if (builder.symtab().isGlobalScope()) { + int val = node->expr->CalcValue(builder); + return {std::to_string(val)}; + } else { + std::string reg_or_num = node->expr->codeGen(builder); + return {reg_or_num}; + } + } + + std::vector result; + auto arr_type = make_arrType(type); + + // Iterate through each element of the current array dimension. + for (int i = 0; i < arr_type->len; ++i) { + std::vector tmp_res; + + // Scenario 1: The input list is exhausted before the array is full. + if (ssize(list) <= idx) { + int dummy = 0; + static const std::vector> empty; + // Recursively fill the remaining slots with "0". + tmp_res = flatten_impl(arr_type->base, empty, dummy); + result.insert(result.end(), tmp_res.begin(), tmp_res.end()); + continue; + } + + const auto &node = list[idx]; + + if (node->expr) { + // Scenario 2 [Flow Mode]: The current item is a scalar. + // It might fill a part of the sub-type (if the sub-type is an array). + // We pass the current list and the global idx down. + tmp_res = flatten_impl(arr_type->base, list, idx); + } else { + // Scenario 3 [Align Mode]: The current item is a brace list { ... }. + // This forces alignment to the sub-type boundary. + int sub_idx = 0; + // We pass the nested list and a local sub_idx (starting at 0). + tmp_res = flatten_impl(arr_type->base, node->initialize_list, sub_idx); + + // Semantic Check: Verify that the brace list does not contain more + // elements than the sub-type can hold. + if (sub_idx < ssize(node->initialize_list)) { + Log::panic(fmt::format("Semantic Error: Excess elements in array " + "initializer (expected {}, got {}).", + arr_type->base->toKoopa(), + ssize(node->initialize_list))); + } + + // After processing the entire nested list, move the outer cursor by 1. + idx++; + } + + // Append the results of the sub-type filling to the current result. + result.insert(result.end(), std::make_move_iterator(tmp_res.begin()), + std::make_move_iterator(tmp_res.end())); + } + + return result; + }(targetType, this->initialize_list, idx); + + // Final Semantic Check: The top-level initializer list must not have + // more elements than the total capacity of the array. + if (idx < ssize(this->initialize_list)) { + Log::panic( + fmt::format("Semantic Error: Excess elements in initializer list")); + } + + return result; +} + /** * @brief Generates IR for an assignment statement. * @@ -332,25 +509,49 @@ auto ExprStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return An empty string. */ auto AssignStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - std::string val_reg = expr->codeGen(builder); auto sym = builder.symtab().lookup(lval->ident); if (!sym) { Log::panic( fmt::format("Assignment to undefined variable '{}'", lval->ident)); } + //* lval(sym) = val_reg if (sym->is_const) { Log::panic( fmt::format("Error: Cannot assign to const variable '{}'", sym->name)); } - if (lval->index) { - auto tmp_reg = builder.newReg(); - builder.append(fmt::format(" {} = getelemptr {}, {}\n", tmp_reg, - sym->irName, lval->index->codeGen(builder))); - builder.append(fmt::format(" store {}, {}\n", val_reg, tmp_reg)); - } else { - builder.append(fmt::format(" store {}, {}\n", val_reg, sym->irName)); + auto cur_type = sym->type; + std::string cur_ptr = sym->irName; + + if (cur_type->is_ptr()) { + std::string loaded_ptr = builder.newReg(); + builder.append(fmt::format(" {} = load {}\n", loaded_ptr, cur_ptr)); + cur_ptr = loaded_ptr; + cur_type = std::static_pointer_cast(cur_type)->target; + } + + for (const auto &[i, elem] : lval->indices | enumerate) { + std::string idx_val = elem->codeGen(builder); + std::string nxt_ptr = builder.newReg(); + + if (i == 0 && sym->type->is_ptr()) { + builder.append( + fmt::format(" {} = getptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + } else { + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + } + cur_ptr = nxt_ptr; + + if (cur_type->is_array()) { + cur_type = std::static_pointer_cast(cur_type)->base; + } + } + + if (cur_type->is_int()) { + std::string expr_res = expr->codeGen(builder); + builder.append(fmt::format(" store {}, {}\n", expr_res, cur_ptr)); } return ""; @@ -369,6 +570,7 @@ auto DeclAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (btype == "void") { Log::panic("Semantic Error: Variable cannot be of type 'void'"); } + for (const auto &def : defs) { def->codeGen(builder); } @@ -386,9 +588,8 @@ auto DeclAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { */ auto ReturnStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string ret_val; - if (expr) { - ret_val = expr->codeGen(builder); - } + if (expr) { ret_val = expr->codeGen(builder); } + builder.setBlockClose(); builder.append(fmt::format(" ret {}\n", ret_val)); return ""; @@ -412,6 +613,7 @@ auto IfStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string else_label = builder.newLabel("else", id); std::string end_label = builder.newLabel("end", id); // clang-format on + if (elseS) { builder.append( fmt::format(" br {}, {}, {}\n", cond_reg, then_label, else_label)); @@ -437,6 +639,7 @@ auto IfStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { builder.append(fmt::format(" jump {}\n", end_label)); } } + builder.append(fmt::format("{}:\n", end_label)); // every basic block (entry) need to pair a block close. builder.clearBlockClose(); @@ -533,30 +736,58 @@ auto NumberAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const * @param builder The IR builder context. * @return The register name or constant value. */ + auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto sym = builder.symtab().lookup(ident); - if (!sym) { - Log::panic(fmt::format("Undefined variable: '{}'", ident)); - } + if (!sym) { Log::panic(fmt::format("Undefined variable: '{}'", ident)); } + //* we can calculate const value in compile time - if (sym->is_const && !index) { + if (sym->is_const && indices.empty() && sym->type->is_int()) { return std::to_string(sym->constValue); } - std::string reg = builder.newReg(); - if (index) { - auto lval_reg = index->codeGen(builder); - builder.append( - fmt::format(" {} = getelemptr {}, {}\n", reg, sym->irName, lval_reg)); - // allocate new reg - std::string old_reg = reg; - reg = builder.newReg(); + std::string cur_ptr = sym->irName; + auto cur_type = sym->type; - builder.append(fmt::format(" {} = load {}\n", reg, old_reg)); - } else { - builder.append(fmt::format(" {} = load {}\n", reg, sym->irName)); + if (cur_type->is_ptr()) { + std::string loaded_ptr = builder.newReg(); + builder.append(fmt::format(" {} = load {}\n", loaded_ptr, cur_ptr)); + cur_ptr = loaded_ptr; + cur_type = std::static_pointer_cast(cur_type)->target; + } + + for (const auto &[i, elem] : indices | enumerate) { + std::string idx_val = elem->codeGen(builder); + std::string nxt_ptr = builder.newReg(); + + if (i == 0 && sym->type->is_ptr()) { + builder.append( + fmt::format(" {} = getptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + } else { + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + + if (cur_type->is_array()) { + cur_type = std::static_pointer_cast(cur_type)->base; + } + } + + cur_ptr = nxt_ptr; + } + + bool is_bare_ptr_param = sym->type->is_ptr() && indices.empty(); + + if (cur_type->is_int() && !is_bare_ptr_param) { + std::string res_val = builder.newReg(); + builder.append(fmt::format(" {} = load {}\n", res_val, cur_ptr)); + return res_val; } - return reg; + + if (is_bare_ptr_param) { return cur_ptr; } + + std::string decay_ptr = builder.newReg(); + builder.append(fmt::format(" {} = getelemptr {}, 0\n", decay_ptr, cur_ptr)); + return decay_ptr; } /** @@ -570,9 +801,7 @@ auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { */ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto sym = builder.symtab().lookup(ident); - if (!sym) { - Log::panic(fmt::format("Undefined function '{}'", ident)); - } + if (!sym) { Log::panic(fmt::format("Undefined function '{}'", ident)); } std::vector arg_val; for (const auto &arg : args) { @@ -589,9 +818,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { for (const auto &val : arg_val) { builder.append(val); - if (&val != &arg_val.back()) { - builder.append(", "); - } + if (&val != &arg_val.back()) { builder.append(", "); } } builder.append(")\n"); @@ -609,6 +836,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto UnaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string rhs_reg = rhs->codeGen(builder); std::string ret_reg = builder.newReg(); + switch (op) { case UnaryOp::Neg: builder.append(fmt::format(" {} = sub 0, {}\n", ret_reg, rhs_reg)); @@ -618,6 +846,7 @@ auto UnaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { break; default: Log::panic("Code Gen Error: Unknown unary op"); } + return ret_reg; } @@ -735,11 +964,11 @@ auto NumberAST::CalcValue([[maybe_unused]] ir::KoopaBuilder &builder) const */ auto LValAST::CalcValue(ir::KoopaBuilder &builder) const -> int { auto sym = builder.symtab().lookup(ident); - Log::trace(ident); if (!sym) { Log::panic( fmt::format("Undefined variable '{}' in constant expression", ident)); } + if (!sym->is_const) { Log::panic(fmt::format("Variable '{}' is not a constant, cannot be used in " "constant expression", @@ -788,6 +1017,7 @@ auto UnaryExprAST::CalcValue(ir::KoopaBuilder &builder) const -> int { auto BinaryExprAST::CalcValue(ir::KoopaBuilder &builder) const -> int { int rhs_val = rhs->CalcValue(builder); int lhs_val = lhs->CalcValue(builder); + switch (op) { case BinaryOp::Add: return lhs_val + rhs_val; case BinaryOp::Sub: return lhs_val - rhs_val; From 3c5af5d1f03948fc9950b0a50a9921522071ada4 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:00:20 +0000 Subject: [PATCH 05/18] Fix bugs --- project/src/frontend/sysy.y | 2 +- project/src/ir/codegen.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/project/src/frontend/sysy.y b/project/src/frontend/sysy.y index d5dbe5d..06e291b 100644 --- a/project/src/frontend/sysy.y +++ b/project/src/frontend/sysy.y @@ -175,7 +175,7 @@ FuncFParam delete $3; } | CONST Btype IDENT ParamArraySuffix { - $$ = new FuncParamAST(std::move(*$3), std::move(*$3), true, true, std::move(*$4)); + $$ = new FuncParamAST(std::move(*$2), std::move(*$3), true, true, std::move(*$4)); delete $2; delete $3; delete $4; diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index 9fc12d2..8e35615 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -367,7 +367,9 @@ auto InitValStmtAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const -> std::string { //! No node should call `InitValStmtAST`'s `codeGen` function, as it is only //! responsible for expanding the initialize list, not for generating code. - Log::panic(""); + Log::panic("InitValStmtAST::codeGen was called unexpectedly. This node is " + "only used to expand initializer lists and must not generate " + "IR directly."); return ""; } From a0ec9a7ef8ed200f6b1004f538fb221eb9310c54 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Wed, 11 Feb 2026 09:04:17 +0000 Subject: [PATCH 06/18] Adjust clang format --- project/.clang-format | 3 +- project/include/ir/ast.cppm | 24 +++++++++--- project/include/ir/symbol_table.cppm | 8 +++- project/include/ir/type.cppm | 4 +- project/src/backend/backend.cpp | 3 +- project/src/ir/codegen.cpp | 56 +++++++++++++++++++++------- 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/project/.clang-format b/project/.clang-format index 8428549..75066e4 100644 --- a/project/.clang-format +++ b/project/.clang-format @@ -1,3 +1,2 @@ AllowShortCaseLabelsOnASingleLine: true -AllowShortIfStatementsOnASingleLine: AllIfsAndElse -AllowShortBlocksOnASingleLine: Always \ No newline at end of file +AllowShortIfStatementsOnASingleLine: AllIfsAndElse \ No newline at end of file diff --git a/project/include/ir/ast.cppm b/project/include/ir/ast.cppm index adbf8c0..a586583 100644 --- a/project/include/ir/ast.cppm +++ b/project/include/ir/ast.cppm @@ -173,7 +173,9 @@ public: */ ScalarDefAST(bool _is_const, std::string _ident, BaseAST *_initVal) : is_const(_is_const), ident(std::move(_ident)) { - if (_initVal) { initVal.reset(static_cast(_initVal)); } + if (_initVal) { + initVal.reset(static_cast(_initVal)); + } } auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; @@ -219,7 +221,9 @@ public: * @param _expr The expression to evaluate. */ ExprStmtAST(BaseAST *_expr) { - if (_expr) { expr.reset(static_cast(_expr)); } + if (_expr) { + expr.reset(static_cast(_expr)); + } } auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; @@ -312,7 +316,9 @@ public: * @param _expr The return value expression (optional). */ ReturnStmtAST(BaseAST *_expr) { - if (_expr) { expr.reset(static_cast(_expr)); } + if (_expr) { + expr.reset(static_cast(_expr)); + } } auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; @@ -435,7 +441,9 @@ public: * @param _rhs The operand expression. */ UnaryExprAST(UnaryOp _op, BaseAST *_rhs) : op(_op) { - if (_rhs) { rhs.reset(static_cast(_rhs)); } + if (_rhs) { + rhs.reset(static_cast(_rhs)); + } } auto dump(int depth) const -> void override; @@ -456,8 +464,12 @@ public: * @param _rhs The right operand. */ BinaryExprAST(BinaryOp _op, BaseAST *_lhs, BaseAST *_rhs) : op(_op) { - if (_lhs) { lhs.reset(static_cast(_lhs)); } - if (_rhs) { rhs.reset(static_cast(_rhs)); } + if (_lhs) { + lhs.reset(static_cast(_lhs)); + } + if (_rhs) { + rhs.reset(static_cast(_rhs)); + } } auto dump(int depth) const -> void override; diff --git a/project/include/ir/symbol_table.cppm b/project/include/ir/symbol_table.cppm index 2da991f..fc1d79d 100644 --- a/project/include/ir/symbol_table.cppm +++ b/project/include/ir/symbol_table.cppm @@ -67,7 +67,9 @@ public: * @note Will not pop the global scope (level 0). */ auto exitScope() -> void { - if (scopes.size() > 1u) { scopes.pop_back(); } + if (scopes.size() > 1u) { + scopes.pop_back(); + } } /** @@ -127,7 +129,9 @@ public: auto lookup(const std::string &name) -> Symbol * { for (auto &scope : scopes | std::views::reverse) { auto it = scope.find(name); - if (it != scope.end()) { return &(it->second); } + if (it != scope.end()) { + return &(it->second); + } } return nullptr; } diff --git a/project/include/ir/type.cppm b/project/include/ir/type.cppm index 924b8b0..e981692 100644 --- a/project/include/ir/type.cppm +++ b/project/include/ir/type.cppm @@ -139,7 +139,9 @@ public: std::shared_ptr> cache; auto key = std::make_pair(_base, _len); - if (cache.contains(key)) { return cache[key]; } + if (cache.contains(key)) { + return cache[key]; + } auto instance = std::make_shared(_base, _len); return cache[key] = instance; } diff --git a/project/src/backend/backend.cpp b/project/src/backend/backend.cpp index 91855c8..7d0d010 100644 --- a/project/src/backend/backend.cpp +++ b/project/src/backend/backend.cpp @@ -158,8 +158,7 @@ auto TargetCodeGen::visit(const koopa_raw_program_t &program) -> void { * @param func The Koopa function to process. */ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { - if (func->bbs.len == 0) - return; + if (func->bbs.len == 0) return; reset(); diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index 8e35615..426bb10 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -54,7 +54,9 @@ using namespace std::views; auto CompUnitAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { for (const auto &child : children) { child->codeGen(builder); - if (&child != &children.back()) { builder.append("\n"); } + if (&child != &children.back()) { + builder.append("\n"); + } } return ""; } @@ -138,7 +140,9 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { // fmt::format("@{}: {}", param->ident, btype2irType(param->btype))); builder.append( fmt::format("@{}: {}", param->ident, param->toKoopa(builder))); - if (¶m != ¶ms.back()) { builder.append(", "); } + if (¶m != ¶ms.back()) { + builder.append(", "); + } } if (btype == "void") { @@ -151,7 +155,9 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { SymbolKind::Func, false); } - if (!block) { return ""; } + if (!block) { + return ""; + } // enterScope mechanism: creates a new symbol table level for local variables // and parameters. @@ -221,7 +227,9 @@ auto ArrayDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string res = "{"; for (int i : iota(0, arr_type->len)) { - if (i > 0) { res += ", "; } + if (i > 0) { + res += ", "; + } res += self(arr_type->base); } @@ -307,7 +315,9 @@ auto ScalarDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { // const btype var = value if (is_const) { int val = 0; - if (initVal) { val = initVal->CalcValue(builder); } + if (initVal) { + val = initVal->CalcValue(builder); + } builder.symtab().define(ident, "", type::IntType::get(), SymbolKind::Var, true, val); } else { @@ -336,9 +346,13 @@ auto ScalarDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return An empty string. */ auto BlockAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - if (this->createScope) { builder.enterScope(); } + if (this->createScope) { + builder.enterScope(); + } for (const auto &item : items) { - if (builder.isBlockClose()) { continue; } + if (builder.isBlockClose()) { + continue; + } item->codeGen(builder); } if (this->createScope) { @@ -359,7 +373,9 @@ auto BlockAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return An empty string. */ auto ExprStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - if (expr) { expr->codeGen(builder); } + if (expr) { + expr->codeGen(builder); + } return ""; } @@ -418,7 +434,9 @@ auto InitValStmtAST::flatten(std::shared_ptr targetType, if (type->is_int()) { // If no more data is provided, SysY requires implicit // zero-initialization. - if (idx >= ssize(list)) { return {"0"}; } + if (idx >= ssize(list)) { + return {"0"}; + } const auto &node = list[idx]; // Semantic Check: A scalar target cannot be initialized by a brace list. @@ -590,7 +608,9 @@ auto DeclAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { */ auto ReturnStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string ret_val; - if (expr) { ret_val = expr->codeGen(builder); } + if (expr) { + ret_val = expr->codeGen(builder); + } builder.setBlockClose(); builder.append(fmt::format(" ret {}\n", ret_val)); @@ -741,7 +761,9 @@ auto NumberAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto sym = builder.symtab().lookup(ident); - if (!sym) { Log::panic(fmt::format("Undefined variable: '{}'", ident)); } + if (!sym) { + Log::panic(fmt::format("Undefined variable: '{}'", ident)); + } //* we can calculate const value in compile time if (sym->is_const && indices.empty() && sym->type->is_int()) { @@ -785,7 +807,9 @@ auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return res_val; } - if (is_bare_ptr_param) { return cur_ptr; } + if (is_bare_ptr_param) { + return cur_ptr; + } std::string decay_ptr = builder.newReg(); builder.append(fmt::format(" {} = getelemptr {}, 0\n", decay_ptr, cur_ptr)); @@ -803,7 +827,9 @@ auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { */ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto sym = builder.symtab().lookup(ident); - if (!sym) { Log::panic(fmt::format("Undefined function '{}'", ident)); } + if (!sym) { + Log::panic(fmt::format("Undefined function '{}'", ident)); + } std::vector arg_val; for (const auto &arg : args) { @@ -820,7 +846,9 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { for (const auto &val : arg_val) { builder.append(val); - if (&val != &arg_val.back()) { builder.append(", "); } + if (&val != &arg_val.back()) { + builder.append(", "); + } } builder.append(")\n"); From 15a5a9eb4f09ffec66f4142a726ec6f467d145ec Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Wed, 11 Feb 2026 09:05:14 +0000 Subject: [PATCH 07/18] Adjust devcontainer configuraion --- .devcontainer/devcontainer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f921b81..6512fc4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -16,7 +16,9 @@ "ms-vscode.cmake-tools", "twxs.cmake", "daohong-emilio.yash", - "mikawi.flex-language" + "mikawi.flex-language", + "aaron-bond.better-comments", + "eamodio.gitlens" ], "settings": { "clangd.path": "/usr/bin/clangd-21", From cc4f675e21879f7faa1c02f7f866ba4c068f5308 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:28:18 +0000 Subject: [PATCH 08/18] Update devcontainer configuration --- .devcontainer/devcontainer.json | 12 +++--------- project/ninja | Bin 290928 -> 0 bytes 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100755 project/ninja diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6512fc4..fca425f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,14 +1,11 @@ { "name": "SysY Compiler Dev", "image": "maxxing/compiler-dev", - "workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind", "workspaceFolder": "${localWorkspaceFolder}", - "mounts": [ "source=${localEnv:HOME}/.ssh,target=/home/ubuntu/.ssh,type=bind,consistency=cached" ], - "customizations": { "vscode": { "extensions": [ @@ -37,9 +34,7 @@ } } }, - "remoteUser": "ubuntu", - "features": { "ghcr.io/devcontainers/features/common-utils:2": { "username": "ubuntu", @@ -48,13 +43,12 @@ "configureZshAsDefaultShell": true } }, - - "postCreateCommand": "sudo apt-get update && sudo apt-get install -y clangd-21", - + // "postCreateCommand": "sudo apt-get update && sudo apt-get install -y clangd-21", + "postCreateCommand": "sudo apt-get update && sudo apt-get install -y clangd-21 python3-pip && sudo pip3 install ninja --break-system-packages", "runArgs": [ "--network=host", "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ] -} +} \ No newline at end of file diff --git a/project/ninja b/project/ninja deleted file mode 100755 index 7b0bdf3435ada8f40add7c70407040fa594e39c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 290928 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

vf9MJZ8gNC~U@?rmvWl1#A?&psk+H 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

^Bi&X*H)8$f_dQ!&abLV@raE{@CoqnVwc^ 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ë!@-BO1o4-=u4fmwb}059O)$biFrE z9g=0=of(qxrx|+lSoKPVZp%}TAEECbqh9Jgwkc0tbClklr!Ma!d2Z^XIm`6pKKh9~ z^_xBv{Zk*^nx{VRqu=NR2MRy7{G!?$gWx&hOFwC|tC zYo7mlg1$b_*V0d4HO9C7M13&NcT@kOY`hxjeTs4`Yct>t-1OW$j;R~|9ZH5Z5<(B_m9-K zK=Rwx>Fj zq94FF++!r4ccrc+{f?fJNrppz>%98de+$l_V#SR0ajW(NMH9fuI@HRtXTK(6KdCYDz1$3*86>G%2vwE!#A9&b!~6 z1gn!K?T=}iuax(>KhF1_`_B7u-!Dv9^Mag1y!J-?*j~oxt)>3uGqHCqXu0CCpW$VQcC1kEh3Ul=>V`#^S2(%;!=O48rauSO{xI#W zyp!#(R;n{$evG4zhbdik$$5@_J|L^P!;+QSWyyNHV#%oGuaNq*PltsJ%6O@GQ!JKS_2 zSMn!v)jl`9o2!1}rmKA7bJH90A%&hjay%hXQxyHGaPi(V(U9`<@zdf8jalgr)T9`KDF?>Xg;JKd_rF-;wD zIF|Ej$Oh|jsE-`f1hNsxMj#u3Yy`3q$VMO=f&c3WJoseLUjLErXnIioUkeI9m9SGL zAI_+^e@B7qj9oL@`BQE?xdTC)lXQ`~>Q>r0{`S#Mg^$(QoQ?8SP<~@YQVH7mQx-e9 z!B6?~e93k@g973QB6jM@588C;BEPTIWq#Ohr?vbJS(1EjElIv9mNZRcbji1Y$PlM!}c1Z_|gEme5&LUkl6Ktz7|NN^C_WA@DX#1Nen{kYw ze0`p7hy1^WA#9h-0a)1H)nE+l1e0J2>;?P4esB;R2GgMHUcLU)!6GmKT3|I813SSa zm;!sjKCmAg1c$*i=vo5*U=bJqEwCDlft_FyOo6>%AJ`8Lg2P}MblnI4U=bJqEwCDl zft_FyOo6>%AJ`8Lg2P}MbS;H{um}u*7FZ3&z)mm;rodjX59|jA!C^2Bx|YE|SOf+@ z3#EAbA!moH_KCtIHo)1Em^Bwm@{AA|JA7hS`IqDXhR=?kW57(jn zY0%7dG6{z&%bYT2%;7&f>HbwD9&g#$828lI)@H1W{a*&&V%yr(QO~CFNON?l2+AQA zqs)HWI^yxhc>Ndln=vjIzZ0R1I(Y*%I(O^w&$XvYbG?9dvev9!x61kk+0*cJ8Hgl% z-R=r~eO1-U_3LXYAA0Bkwpz2A|0(I{{0@C+%_CH%SY2QXjkc^dR@2(nl8DwO8e8JB zQ8M~&X>Lr^#M(F#HLWd;@kCT!e@wZhkGt9vk=ji)wM~)s1|nQuU~F@BTU#{J)L0)E zne;E)R9*;&D!+K_J>Lzo1yKjKuf$?ZQ)XguQ*4awOVIoaTRU!MzcKxix4oe~(Uyp8 zB(J@0t>le!FuavjtI89RdX(zp9o~%{jZJmsjdkReQiH6jw{CO1eRH#BqD{ASM%&sY zlC~KNHQd$~ZHkBhxz?tHxNPKQBKia`V_e|2mbyqHLf&WtV%|{4T2taqYinzwPt-N|jrO~Xb zH+Y{9Q6Z%K2xG+v)@2JQ9)^z-e~*dkNEh?~-U4Ql*%JA<}yH!3#RrLkk#K8R5# zSiyf11Ec@k8RQcGZaXSo#z7_L2U&NcFBBtA7XQh4&FFjbgIwbMUrs8V%yqpk={`(e z$p6HK-(QZ6-&{vupQo)>BjDyf!uT=#CY#AMx+$CM^xy)me-L^Zzl_t2U$+u{i!%~a zqc`^jJBzf1C&RzdoApmXU!~1Q?=ub->k4yUBI6fLrv6E;6BjZ*NoKBb2XuM1#^^G0 z`)}r!@p@k%XxsXr?=d$t=)s=1IB&R>^W*gcm}dv>*Me8)OVq}a(V3cOI5S>v?jyVE zv~G01fFN`HJE1r6@4>mX2YO$F@ucawwMgAu7XL;+aCW6_F>qGjTT*B4KZl?<8)kI> zhS03n2$#qEAF8RdErx3J`g>q+VEh|?$_?Z7gy#aZLF_4v1%0|N%S)>vo6ZmgP4)@#cW;|OgTMfxXNA$f)X From 12f42b9033f0d7c07b4ea1b1ffb4d3b0e6052c15 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:28:35 +0000 Subject: [PATCH 09/18] Update CI --- .github/workflows/ci.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e1a431..eb6661a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,19 +17,6 @@ jobs: - name: Check out repository code uses: actions/checkout@v5 - name: test koopa - run: | - autotest -w debug -koopa -s lv1 . - autotest -w debug -koopa -s lv3 . - autotest -w debug -koopa -s lv4 . - autotest -w debug -koopa -s lv5 . - autotest -w debug -koopa -s lv6 . - autotest -w debug -koopa -s lv7 . - autotest -w debug -koopa -s lv8 . + run: autotest -w debug -koopa . - name: test riscv - run: | - autotest -w debug -riscv -s lv1 . - autotest -w debug -riscv -s lv3 . - autotest -w debug -riscv -s lv4 . - autotest -w debug -riscv -s lv5 . - autotest -w debug -riscv -s lv6 . - autotest -w debug -riscv -s lv7 . + run: autotest -w debug -riscv . \ No newline at end of file From 4876eac27bfb01a16c7764e1d7552f9f59d75bbd Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:28:52 +0000 Subject: [PATCH 10/18] Update makefile --- project/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/project/Makefile b/project/Makefile index efcc69c..39db569 100644 --- a/project/Makefile +++ b/project/Makefile @@ -8,7 +8,7 @@ PWD := $(shell pwd) all: build config: - cmake -S . -B $(BUILD_DIR) -G Ninja -DCMAKE_MAKE_PROGRAM=$(PWD)/ninja + cmake -S . -B cmake-build -G Ninja -DCMAKE_CXX_COMPILER=clang++ build: config cmake --build $(BUILD_DIR) -j12 @@ -28,7 +28,8 @@ run: ./cmake-build/compiler -riscv ./tests/hello.c -o ./debug/hello.asm test: - cd cmake-build && ctest && cd .. + python3 scripts/test_runner.py koopa + python3 scripts/test_runner.py riscv docker-build: docker run --rm \ From 58e3178f214233055bb26223604ea13c7cc335be Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 19:30:09 +0800 Subject: [PATCH 11/18] Enhance backend with array support and improve code generation for RISC-V --- project/include/backend/backend.cppm | 4 +- project/include/ir/ir_builder.cppm | 1 - project/src/backend/backend.cpp | 233 +++++++++++++++++++-------- project/src/ir/codegen.cpp | 6 +- project/tests/hello.c | 20 ++- 5 files changed, 185 insertions(+), 79 deletions(-) diff --git a/project/include/backend/backend.cppm b/project/include/backend/backend.cppm index cc72a90..c6c5149 100644 --- a/project/include/backend/backend.cppm +++ b/project/include/backend/backend.cppm @@ -81,14 +81,14 @@ private: */ auto visit(const koopa_raw_return_t &ret) -> void; auto visit(const koopa_raw_binary_t &binary) -> void; - auto visit(const koopa_raw_integer_t &integer) -> void; auto visit(const koopa_raw_jump_t &jump) -> void; auto visit(const koopa_raw_branch_t &branch) -> void; auto visit(const koopa_raw_load_t &load) -> void; auto visit(const koopa_raw_store_t &store) -> void; auto visit(const koopa_raw_call_t &call) -> void; - auto visit(const koopa_raw_func_arg_ref_t &func_arg_ref) -> void; auto visit(const koopa_raw_global_alloc_t &global_alloc) -> void; + auto visit(const koopa_raw_get_elem_ptr_t &get_elem_ptr) -> void; + auto visit(const koopa_raw_get_ptr_t &get_ptr) -> void; /** @} */ }; } // namespace backend \ No newline at end of file diff --git a/project/include/ir/ir_builder.cppm b/project/include/ir/ir_builder.cppm index c6bff03..d986463 100644 --- a/project/include/ir/ir_builder.cppm +++ b/project/include/ir/ir_builder.cppm @@ -151,7 +151,6 @@ public: */ auto resetCount() -> void { count_reg = 0; - count_label = 0; _is_block_closed = false; } diff --git a/project/src/backend/backend.cpp b/project/src/backend/backend.cpp index 7d0d010..55f1955 100644 --- a/project/src/backend/backend.cpp +++ b/project/src/backend/backend.cpp @@ -104,6 +104,7 @@ module; #include "koopa.h" #include #include +#include #include #include @@ -134,9 +135,58 @@ template auto make_span(const koopa_raw_slice_t &slice) { return std::span( reinterpret_cast(slice.buffer), slice.len); } + +auto get_type_size(koopa_raw_type_t ty) -> u_int32_t { + switch (ty->tag) { + + case KOOPA_RTT_INT32: return 4; + case KOOPA_RTT_POINTER: return 4; + + case KOOPA_RTT_ARRAY: + return ty->data.array.len * get_type_size(ty->data.array.base); + + default: return 0; + } +} + +auto isIn12BitRange(int val) -> bool { return val >= -2048 && val <= 2047; } + +auto emitAddi(std::string &buffer, std::string_view rd, std::string_view rs, + int imm) -> void { + if (isIn12BitRange(imm)) { + buffer += fmt::format(" addi {}, {}, {}\n", rd, rs, imm); + } else { + buffer += fmt::format(" li t2, {}\n", imm); + buffer += fmt::format(" add {}, {}, t2\n", rd, rs); + } +} + +auto emitLw(std::string &buffer, std::string_view rd, std::string_view rs, + int offset) -> void { + if (isIn12BitRange(offset)) { + buffer += fmt::format(" lw {}, {}({})\n", rd, offset, rs); + } else { + buffer += fmt::format(" li t2, {}\n", offset); + buffer += fmt::format(" add t2, t2, {}\n", rs); + buffer += fmt::format(" lw {}, 0(t2)\n", rd); + } +} + +auto emitSw(std::string &buffer, std::string_view src, std::string_view base, + int offset) -> void { + if (isIn12BitRange(offset)) { + buffer += fmt::format(" sw {}, {}({})\n", src, offset, base); + } else { + buffer += fmt::format(" li t2, {}\n", offset); + buffer += fmt::format(" add t2, t2, {}\n", base); + buffer += fmt::format(" sw {}, 0(t2)\n", src); + } +} + } // namespace backend using namespace backend; +using namespace std::views; auto TargetCodeGen::visit(const koopa_raw_program_t &program) -> void { for (const auto value : make_span(program.values)) { @@ -166,20 +216,26 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { bool has_callee = false; for (const auto bb : make_span(func->bbs)) { for (const auto inst : make_span(bb->insts)) { - // If the instruction produces a value (not void), allocate space on - // stack. In this simple backend, every value-producing instruction gets - // its own slot. - if (inst->ty->tag != KOOPA_RTT_UNIT) { - stkMap[inst] = local_frame_size; - local_frame_size += 4; - } // If this function calls another, we need to save RA and potentially // allocate space for outgoing arguments. if (inst->kind.tag == KOOPA_RVT_CALL) { has_callee = true; ra_size = 4; // Keep track of the maximum number of arguments in any call. - args_size = std::max(args_size, inst->kind.data.call.args.len); + args_size = std::max(args_size, inst->kind.data.call.args.len); + } + + if (inst->ty->tag == KOOPA_RTT_UNIT) continue; + // If the instruction produces a value (not void), allocate space on + // stack. In this simple backend, every value-producing instruction gets + // its own slot. + stkMap[inst] = local_frame_size; + + if (inst->kind.tag == KOOPA_RVT_ALLOC) { + int size = get_type_size(inst->ty->data.pointer.base); + local_frame_size += size; + } else { + local_frame_size += 4; } } } @@ -200,12 +256,12 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { buffer += fmt::format("{}:\n", func->name + 1); if (stk_frame_size > 0) { - buffer += fmt::format(" addi sp, sp, -{}\n", stk_frame_size); + emitAddi(buffer, "sp", "sp", -stk_frame_size); } if (has_callee) { // Save RA at the top of the frame (below the caller's frame). - buffer += fmt::format(" sw ra, {}(sp)\n", stk_frame_size - 4); + emitSw(buffer, "ra", "sp", stk_frame_size - 4); } // Offset local variable storage by the size allocated for outgoing arguments. @@ -214,18 +270,19 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { } // --- Parameter Handling --- - for (size_t i = 0; - const auto param : make_span(func->params)) { + + for (const auto [i, param] : + make_span(func->params) | enumerate) { if (i < 8) { // Store input parameters (a0-a7) into their allocated stack slots. int offset = stkMap[param] = i * 4 + args_size; - buffer += fmt::format(" sw a{}, {}(sp)\n", i, offset); + auto src = fmt::format("a{}", i); + emitSw(buffer, src, "sp", offset); } else { // Parameters passed on stack by caller are located ABOVE the current SP. int offset = stk_frame_size + (i - 8) * 4; stkMap[param] = offset; } - ++i; } // --- Function Body --- @@ -238,6 +295,7 @@ auto TargetCodeGen::visit(koopa_raw_basic_block_t bb) -> void { if (bb->name) { buffer += fmt::format("{}:\n", bb->name + 1); } + for (const auto inst : make_span(bb->insts)) { visit(inst); } @@ -259,11 +317,6 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { break; } - case KOOPA_RVT_INTEGER: { - visit(kind.data.integer); - break; - } - case KOOPA_RVT_STORE: { visit(kind.data.store); break; @@ -274,6 +327,7 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { break; } + // br case KOOPA_RVT_BRANCH: { visit(kind.data.branch); break; @@ -284,9 +338,21 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { break; } - case KOOPA_RVT_FUNC_ARG_REF: { - // Reference to a function argument. - visit(kind.data.func_arg_ref); + case KOOPA_RVT_GET_ELEM_PTR: { + visit(kind.data.get_elem_ptr); + if (value->ty->tag != KOOPA_RTT_UNIT) { + int offset = stkMap[value]; + emitSw(buffer, "t0", "sp", offset); + } + break; + } + + case KOOPA_RVT_GET_PTR: { + visit(kind.data.get_ptr); + if (value->ty->tag != KOOPA_RTT_UNIT) { + int offset = stkMap[value]; + emitSw(buffer, "t0", "sp", offset); + } break; } @@ -294,9 +360,9 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { visit(kind.data.call); // If the function returns an int, it's in a0. Save it to the stack slot // assigned to this 'call' value. - if (value->ty->tag == KOOPA_RTT_INT32) { + if (value->ty->tag != KOOPA_RTT_UNIT) { int offset = stkMap[value]; - buffer += fmt::format(" sw a0, {}(sp)\n", offset); + emitSw(buffer, "a0", "sp", offset); } break; } @@ -315,7 +381,7 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { // Binary operations result in a value in t0. Save it to stack. if (value->ty->tag != KOOPA_RTT_UNIT) { int offset = stkMap[value]; - buffer += fmt::format(" sw t0, {}(sp)\n", offset); + emitSw(buffer, "t0", "sp", offset); } break; } @@ -325,12 +391,15 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { // Load result is in t0. Save it to stack. if (value->ty->tag != KOOPA_RTT_UNIT) { int offset = stkMap[value]; - buffer += fmt::format(" sw t0, {}(sp)\n", offset); + emitSw(buffer, "t0", "sp", offset); } break; } - default: assert(false); + default: { + Log::panic("Let's explore the world ahead another time~\n" + "(Unhandled value tag in visit value)"); + } } } @@ -349,26 +418,14 @@ auto TargetCodeGen::visit(const koopa_raw_jump_t &jump) -> void { auto TargetCodeGen::visit(const koopa_raw_load_t &load) -> void { // src is a pointer value (either a local alloc or a global). load_to(load.src, "t0"); - // If it's a global, t0 now contains the address. We need to load from it. - if (load.src->kind.tag == KOOPA_RVT_GLOBAL_ALLOC) { - buffer += " lw t0, 0(t0)\n"; - } - // Note: if it was a local alloc, load_to already performed the lw t0, - // offset(sp) because local allocs are just pointers to stack slots. + buffer += " lw t0, 0(t0)\n"; } auto TargetCodeGen::visit(const koopa_raw_store_t &store) -> void { - // Store 'value' into 'dest'. load_to(store.value, "t0"); - if (store.dest->kind.tag == KOOPA_RVT_GLOBAL_ALLOC) { - std::string name = store.dest->name + 1; - buffer += fmt::format(" la t1, {}\n", name); - buffer += " sw t0, 0(t1)\n"; - } else if (stkMap.contains(store.dest)) { - int offset = stkMap[store.dest]; - // Store the value into the destination's stack slot. - buffer += fmt::format(" sw t0, {}(sp)\n", offset); - } + load_to(store.dest, "t1"); + + buffer += " sw t0, 0(t1)\n"; } /** @@ -381,11 +438,13 @@ auto TargetCodeGen::visit(const koopa_raw_return_t &ret) -> void { } // Function Epilogue: Restore RA and SP. if (ra_size > 0) { - buffer += fmt::format(" lw ra, {}(sp)\n", stk_frame_size - ra_size); + emitLw(buffer, "ra", "sp", stk_frame_size - ra_size); } + if (stk_frame_size > 0) { - buffer += fmt::format(" addi sp, sp, {}\n", stk_frame_size); + emitAddi(buffer, "sp", "sp", stk_frame_size); } + buffer += " ret\n"; } @@ -398,6 +457,7 @@ auto TargetCodeGen::visit(const koopa_raw_return_t &ret) -> void { * @param value The Koopa value to load. * @param reg The target register name (e.g., "t0"). */ + auto TargetCodeGen::load_to(const koopa_raw_value_t &value, const std::string ®) -> void { switch (value->kind.tag) { @@ -416,20 +476,27 @@ auto TargetCodeGen::load_to(const koopa_raw_value_t &value, break; } + case KOOPA_RVT_ALLOC: { + int offset = stkMap[value]; + emitAddi(buffer, reg, "sp", offset); + break; + } + // Local allocations and instruction results are all stored in the stack // frame. + case KOOPA_RVT_GET_ELEM_PTR: + case KOOPA_RVT_GET_PTR: case KOOPA_RVT_CALL: case KOOPA_RVT_FUNC_ARG_REF: case KOOPA_RVT_BINARY: - case KOOPA_RVT_LOAD: - case KOOPA_RVT_ALLOC: { + case KOOPA_RVT_LOAD: { int offset = stkMap[value]; - buffer += fmt::format(" lw {}, {}(sp)\n", reg, offset); + emitLw(buffer, reg, "sp", offset); break; } default: { - buffer += "Wait! Do you realy think you arguments should go here ?\n"; + Log::panic("Unhandled value tag in load_to"); break; } } @@ -454,7 +521,8 @@ auto TargetCodeGen::visit(const koopa_raw_call_t &call) -> void { // Args 9+ go onto the stack at the very bottom of the current frame. load_to(param, "t0"); int offset = (i - 8) * 4; - buffer += fmt::format(" sw t0, {}(sp)\n", offset); + emitSw(buffer, "t0", "sp", offset); + // buffer += fmt::format(" sw t0, {}(sp)\n", offset); } ++i; } @@ -462,25 +530,62 @@ auto TargetCodeGen::visit(const koopa_raw_call_t &call) -> void { buffer += fmt::format(" call {}\n", call.callee->name + 1); } -auto TargetCodeGen::visit(const koopa_raw_func_arg_ref_t &) -> void {} - /** * @brief Handles global variable allocation. */ auto TargetCodeGen::visit(const koopa_raw_global_alloc_t &global_alloc) -> void { - if (global_alloc.init->kind.tag == KOOPA_RVT_ZERO_INIT) { - // Uninitialized/Zero-initialized global. - buffer += " .zero 4\n"; - } else if (global_alloc.init->kind.tag == KOOPA_RVT_INTEGER) { - // Constant-initialized global. - buffer += - fmt::format(" .word {}\n", global_alloc.init->kind.data.integer.value); - } else if (global_alloc.init->kind.tag == KOOPA_RVT_AGGREGATE) { - /* TODO: Implement aggregate (array) initialization */ - } + [&](this auto &&self, koopa_raw_value_t value) -> void { + const auto &kind = value->kind; + switch (kind.tag) { + case KOOPA_RVT_INTEGER: { + buffer += fmt::format(" .word {}\n", kind.data.integer.value); + break; + } + + case KOOPA_RVT_ZERO_INIT: { + buffer += fmt::format(" .zero {}\n", get_type_size(value->ty)); + break; + } + + case KOOPA_RVT_AGGREGATE: { + for (const auto &sub_val : + make_span(kind.data.aggregate.elems)) { + self(sub_val); + } + break; + } + + default: assert(false); + } + }(global_alloc.init); } +auto TargetCodeGen::visit(const koopa_raw_get_elem_ptr_t &get_elem_ptr) + -> void { + load_to(get_elem_ptr.src, "t0"); + load_to(get_elem_ptr.index, "t1"); + + auto stride = + get_type_size(get_elem_ptr.src->ty->data.pointer.base->data.array.base); + + buffer += fmt::format(" li t2, {}\n", stride); + buffer += " mul t1, t1, t2\n"; + buffer += " add t0, t0, t1\n"; +}; + +auto TargetCodeGen::visit(const koopa_raw_get_ptr_t &get_ptr) -> void { + + load_to(get_ptr.src, "t0"); + load_to(get_ptr.index, "t1"); + + auto stride = get_type_size(get_ptr.src->ty->data.pointer.base); + + buffer += fmt::format(" li t2, {}\n", stride); + buffer += " mul t1, t1, t2\n"; + buffer += " add t0, t0, t1\n"; +}; + /** * @brief Generates assembly for binary operations. * @@ -530,6 +635,4 @@ auto TargetCodeGen::visit(const koopa_raw_binary_t &binary) -> void { default: assert(false); } // clang-format on -} - -auto TargetCodeGen::visit(const koopa_raw_integer_t &) -> void {} \ No newline at end of file +} \ No newline at end of file diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index 426bb10..578f0e8 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -911,12 +911,12 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string rhs_reg = rhs->codeGen(builder); std::string rhs_bool = builder.newReg(); builder.append(fmt::format(" {} = ne {}, 0\n", rhs_bool, rhs_reg)); - builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); + builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); builder.append(fmt::format(" jump {}\n", end_label)); // false branch builder.append(fmt::format("{}:\n", false_label)); - builder.append(fmt::format(" store 0, {}\n", tmp_addr)); + builder.append(fmt::format(" store 0, {}\n", tmp_addr)); builder.append(fmt::format(" jump {}\n", end_label)); // end @@ -953,7 +953,7 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string rhs_reg = rhs->codeGen(builder); std::string rhs_bool = builder.newReg(); builder.append(fmt::format(" {} = ne {}, 0\n", rhs_bool, rhs_reg)); - builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); + builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); builder.append(fmt::format(" jump {}\n", end_label)); // end diff --git a/project/tests/hello.c b/project/tests/hello.c index 51a0e2c..ceac27e 100644 --- a/project/tests/hello.c +++ b/project/tests/hello.c @@ -1,9 +1,13 @@ -int main(){ - const int a[4][2] = {{1, 2}, {3, 4}, {}, 7}; - const int N = 3; - int b[4][2] = {}; - int c[4][2] = {1, 2, 3, 4, 5, 6, 7, 8}; - int d[N + 1][2] = {1, 2, {3}, {5}, a[3][0], 8}; - int e[4][2][1] = {{d[2][1], {c[2][1]}}, {3, 4}, {5, 6}, {7, 8}}; - return e[3][1][0] + e[0][0][0] + e[0][1][0] + d[3][0]; +int x; + +int t() { + x = x + 1; + return 1; } + +int main() { + int sum = 0; + t(); + putint(x); + return sum; +} \ No newline at end of file From f4bb09167de53dc6c57f19247a49af63423b041a Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:41:05 +0000 Subject: [PATCH 12/18] Update CI --- .github/workflows/ci.yml | 52 +++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb6661a..09f4a72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,22 +1,58 @@ name: compiler auto test run-name: ${{ github.actor }} is testing out compiler function 🚀 + on: push: + paths: + - 'project/**' pull_request: workflow_dispatch: - + jobs: - build: + build_compiler: runs-on: ubuntu-latest container: image: maxxing/compiler-dev defaults: run: working-directory: ./project + outputs: + artifact_name: compiler-binary steps: - - name: Check out repository code - uses: actions/checkout@v5 - - name: test koopa - run: autotest -w debug -koopa . - - name: test riscv - run: autotest -w debug -riscv . \ No newline at end of file + - uses: actions/checkout@v5 + + - name: Install Ninja + run: | + wget https://github.com/ninja-build/ninja/releases/latest/download/ninja-linux.zip + unzip ninja-linux.zip -d /usr/local/bin/ + + - name: Build Binary + run: make + + test_compiler: + needs: build_compiler + runs-on: ubuntu-latest + container: + image: maxxing/compiler-dev + strategy: + fail-fast: false + matrix: + test_type: [koopa, riscv] + defaults: + run: + working-directory: ./project + steps: + - uses: actions/checkout@v5 + + - name: Set Permission + run: chmod +x compiler + + - name: Run Test + run: autotest -w debug -${{ matrix.test_type }} . + + - name: Upload Test Logs + if: failure() + uses: actions/upload-artifact@v5 + with: + name: test-logs-${{ matrix.test_type }} + path: project/debug/ \ No newline at end of file From 30da72637baa1a267a5828d2fdc76cefa3fe43fa Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:46:12 +0000 Subject: [PATCH 13/18] Update CI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09f4a72..43d6ff1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,8 @@ jobs: - name: Install Ninja run: | - wget https://github.com/ninja-build/ninja/releases/latest/download/ninja-linux.zip - unzip ninja-linux.zip -d /usr/local/bin/ + apt-get update && apt-get install -y python3-pip + pip3 install ninja - name: Build Binary run: make From 8e01817e407c740c4c5aff08dffb63aeb89b9e5d Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:51:19 +0000 Subject: [PATCH 14/18] Update CI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43d6ff1..d3b6a45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,8 @@ jobs: - name: Install Ninja run: | - apt-get update && apt-get install -y python3-pip - pip3 install ninja + apt-get update && apt-get install -y ninja-build + ninja --version - name: Build Binary run: make From 6cd6336520b1676f55036f92a6aa0e76daf28abc Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:56:30 +0000 Subject: [PATCH 15/18] Update CI --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3b6a45..e6e16c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,9 +44,6 @@ jobs: steps: - uses: actions/checkout@v5 - - name: Set Permission - run: chmod +x compiler - - name: Run Test run: autotest -w debug -${{ matrix.test_type }} . From 8c7f7cce062d650f71ad942c080a6534fa5b5ed8 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:00:05 +0000 Subject: [PATCH 16/18] Update CI --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6e16c4..34de4cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,11 @@ jobs: steps: - uses: actions/checkout@v5 + - name: Install Ninja + run: | + apt-get update && apt-get install -y ninja-build + ninja --version + - name: Run Test run: autotest -w debug -${{ matrix.test_type }} . From 9311d5353361c8918e76057bf82e25749980f3c7 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:13:19 +0000 Subject: [PATCH 17/18] Fix bug --- project/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Makefile b/project/Makefile index 39db569..6982fb6 100644 --- a/project/Makefile +++ b/project/Makefile @@ -8,7 +8,7 @@ PWD := $(shell pwd) all: build config: - cmake -S . -B cmake-build -G Ninja -DCMAKE_CXX_COMPILER=clang++ + cmake -S . -B $(BUILD_DIR) -G Ninja -DCMAKE_CXX_COMPILER=clang++ build: config cmake --build $(BUILD_DIR) -j12 From 417b0efb3aaab5d46121712d833eb9336a12f8b5 Mon Sep 17 00:00:00 2001 From: Anfsity <186920685+anfsity@users.noreply.github.com> Date: Thu, 12 Feb 2026 12:17:41 +0000 Subject: [PATCH 18/18] Enhance comments --- project/include/ir/ast.cppm | 8 +++ project/src/backend/backend.cpp | 112 ++++++++++++++++++++++++++++++++ project/src/frontend/sysy.y | 45 ++++++++----- project/src/ir/ast.cpp | 16 ++++- project/src/ir/codegen.cpp | 26 +++++++- 5 files changed, 190 insertions(+), 17 deletions(-) diff --git a/project/include/ir/ast.cppm b/project/include/ir/ast.cppm index a586583..9c42650 100644 --- a/project/include/ir/ast.cppm +++ b/project/include/ir/ast.cppm @@ -117,6 +117,9 @@ public: auto toKoopa(ir::KoopaBuilder &builder) const -> std::string; }; +/** + * @brief Base class for definition AST nodes (variables, functions, arrays). + */ class DefAST : public BaseAST {}; /** @@ -143,6 +146,11 @@ public: auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; +/** + * @brief AST node for array definitions. + * + * Handles both constant and non-constant array definitions, including their sizes and initializers. + */ class ArrayDefAST : public DefAST { public: ~ArrayDefAST() override; diff --git a/project/src/backend/backend.cpp b/project/src/backend/backend.cpp index 55f1955..be17bbe 100644 --- a/project/src/backend/backend.cpp +++ b/project/src/backend/backend.cpp @@ -136,6 +136,13 @@ template auto make_span(const koopa_raw_slice_t &slice) { reinterpret_cast(slice.buffer), slice.len); } + +/** + * @brief Calculates the size (in bytes) of a given Koopa type. + * + * @param ty The Koopa type to measure. + * @return u_int32_t The size in bytes. Returns 0 for unknown types. + */ auto get_type_size(koopa_raw_type_t ty) -> u_int32_t { switch (ty->tag) { @@ -149,8 +156,28 @@ auto get_type_size(koopa_raw_type_t ty) -> u_int32_t { } } +/** + * @brief Checks if an integer value fits within a 12-bit signed range. + * + * RISC-V immediate values for many instructions (like ADDI, LW, SW) are + * 12-bit signed integers [-2048, 2047]. + * + * @param val The value to check. + * @return true if -2048 <= val <= 2047, false otherwise. + */ auto isIn12BitRange(int val) -> bool { return val >= -2048 && val <= 2047; } +/** + * @brief Emits a RISC-V `addi` instruction (or equivalent sequence). + * + * If `imm` fits in 12 bits, emits a single `addi`. + * Otherwise, loads `imm` into a temporary register and adds it. + * + * @param buffer The output assembly buffer. + * @param rd Destination register. + * @param rs Source register. + * @param imm Immediate value to add. + */ auto emitAddi(std::string &buffer, std::string_view rd, std::string_view rs, int imm) -> void { if (isIn12BitRange(imm)) { @@ -161,6 +188,16 @@ auto emitAddi(std::string &buffer, std::string_view rd, std::string_view rs, } } +/** + * @brief Emits a RISC-V `lw` instruction (or equivalent sequence). + * + * Handles large offsets by calculating the address in a temporary register first. + * + * @param buffer The output assembly buffer. + * @param rd Destination register. + * @param rs Base address register. + * @param offset Byte offset from base. + */ auto emitLw(std::string &buffer, std::string_view rd, std::string_view rs, int offset) -> void { if (isIn12BitRange(offset)) { @@ -172,6 +209,16 @@ auto emitLw(std::string &buffer, std::string_view rd, std::string_view rs, } } +/** + * @brief Emits a RISC-V `sw` instruction (or equivalent sequence). + * + * Handles large offsets by calculating the address in a temporary register first. + * + * @param buffer The output assembly buffer. + * @param src Source register (value to store). + * @param base Base address register. + * @param offset Byte offset from base. + */ auto emitSw(std::string &buffer, std::string_view src, std::string_view base, int offset) -> void { if (isIn12BitRange(offset)) { @@ -188,6 +235,14 @@ auto emitSw(std::string &buffer, std::string_view src, std::string_view base, using namespace backend; using namespace std::views; +/** + * @brief Entry point for code generation. + * + * Traverses the `program`'s global values and function definitions, + * generating code for each. All output is appended to the internal `buffer`. + * + * @param program The root node of the Koopa IR. + */ auto TargetCodeGen::visit(const koopa_raw_program_t &program) -> void { for (const auto value : make_span(program.values)) { visit(value); @@ -291,6 +346,14 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { } } + +/** + * @brief Generates assembly for a basic block. + * + * Emits the block label (if any) and visits all instructions in the block sequentially. + * + * @param bb The Koopa basic block to process. + */ auto TargetCodeGen::visit(koopa_raw_basic_block_t bb) -> void { if (bb->name) { buffer += fmt::format("{}:\n", bb->name + 1); @@ -403,6 +466,12 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { } } + +/** + * @brief Generates assembly for a conditional branch. + * + * @param branch The Koopa branch instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_branch_t &branch) -> void { load_to(branch.cond, "t0"); // bnez: branch if not equal to zero. @@ -410,17 +479,37 @@ auto TargetCodeGen::visit(const koopa_raw_branch_t &branch) -> void { buffer += fmt::format(" j {}\n", branch.false_bb->name + 1); } +/** + * @brief Generates assembly for an unconditional jump. + * + * @param jump The Koopa jump instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_jump_t &jump) -> void { std::string target_name = jump.target->name + 1; buffer += fmt::format(" j {}\n", target_name); } +/** + * @brief Generates assembly for a load instruction. + * + * Loads a value from the memory address specified by `load.src`. + * The result is stored into `t0`, which will be saved to the stack by the caller. + * + * @param load The Koopa load instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_load_t &load) -> void { // src is a pointer value (either a local alloc or a global). load_to(load.src, "t0"); buffer += " lw t0, 0(t0)\n"; } +/** + * @brief Generates assembly for a store instruction. + * + * Stores the value in `store.value` to the memory address `store.dest`. + * + * @param store The Koopa store instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_store_t &store) -> void { load_to(store.value, "t0"); load_to(store.dest, "t1"); @@ -532,6 +621,11 @@ auto TargetCodeGen::visit(const koopa_raw_call_t &call) -> void { /** * @brief Handles global variable allocation. + * + * Emits `.data` section directives for global variables. + * Recursively handles aggregate types (arrays) using a lambda. + * + * @param global_alloc The global allocation instruction data. */ auto TargetCodeGen::visit(const koopa_raw_global_alloc_t &global_alloc) -> void { @@ -561,6 +655,15 @@ auto TargetCodeGen::visit(const koopa_raw_global_alloc_t &global_alloc) }(global_alloc.init); } +/** + * @brief Generates assembly for `getelementptr` (array element access). + * + * Compute address of `src[index]`. + * `src` is expected to be a pointer to an array (e.g., `[[i32, 10], 5]*`). + * The stride is the size of the array's element type. + * + * @param get_elem_ptr The Koopa GEP instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_get_elem_ptr_t &get_elem_ptr) -> void { load_to(get_elem_ptr.src, "t0"); @@ -574,6 +677,15 @@ auto TargetCodeGen::visit(const koopa_raw_get_elem_ptr_t &get_elem_ptr) buffer += " add t0, t0, t1\n"; }; +/** + * @brief Generates assembly for `getptr` (pointer arithmetic). + * + * Compute address of `src + index`. + * `src` is a pointer (e.g., `i32*`). + * The stride is the size of the type pointed to. + * + * @param get_ptr The Koopa getptr instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_get_ptr_t &get_ptr) -> void { load_to(get_ptr.src, "t0"); diff --git a/project/src/frontend/sysy.y b/project/src/frontend/sysy.y index 06e291b..e0eb61e 100644 --- a/project/src/frontend/sysy.y +++ b/project/src/frontend/sysy.y @@ -155,8 +155,6 @@ FuncFParams $$->push_back(std::unique_ptr(static_cast($3))); }; -// foo(int []) -> *i32 -// foo(int [][1]) -> **i32 FuncFParam : Btype IDENT { $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false, false, {}); @@ -210,15 +208,22 @@ BlockItemList $$->push_back(std::unique_ptr($2)); }; +/** + * @brief Block item (declaration or statement). + */ BlockItem : Decl { $$ = $1; } | Stmt { $$ = $1; }; +/** + * @brief Declaration (constant or variable). + */ Decl : ConstDecl { $$ = $1; } | VarDecl { $$ = $1; }; - -ConstDecl +/** + * @brief Constant declaration. + */ConstDecl : CONST Btype ConstDefList ';' { $$ = new DeclAST(true, std::move(*$2), std::move(*$3)); delete $2; @@ -236,6 +241,9 @@ ConstDefList $$->push_back(std::unique_ptr(static_cast($3))); }; +/** + * @brief Constant definition. + */ ConstDef : IDENT '=' Expr { // @param is_const, ident, exprAST @@ -248,11 +256,9 @@ ConstDef delete $2; }; -/* -ConstDef ::= IDENT {"[" ConstExp "]"} "=" ConstInitVal; -ConstInitVal ::= ConstExp | "{" [ConstInitVal {"," ConstInitVal}] "}"; -*/ - +/** + * @brief Array dimension suffix (e.g. `[2][3]`). + */ ArraySuffix : '[' Expr ']' { $$ = new std::vector>(); @@ -274,6 +280,9 @@ InitializeList }; // {1, 2, {2, 0}} +/** + * @brief Initializer value (expression or initializer list). + */ InitVal : Expr { $$ = new InitValStmtAST($1, {}); @@ -287,12 +296,9 @@ InitVal delete $2; }; -/* -Initialize list -ConstDef ::= IDENT ["[" ConstExp "]"] "=" ConstInitVal; -ConstInitVal ::= ConstExp | "{" [ConstExp {"," ConstExp}] "}"; -*/ - +/** + * @brief Variable declaration. + */ VarDecl : Btype VarDefList ';' { $$ = new DeclAST(false, std::move(*$1), std::move(*$2)); @@ -310,6 +316,9 @@ VarDefList $$->push_back(std::unique_ptr(static_cast($3))); }; +/** + * @brief Variable definition (with optional initialization). + */ VarDef : IDENT '=' Expr { $$ = new ScalarDefAST(false, std::move(*$1), $3); @@ -447,11 +456,17 @@ Expr $$ = new BinaryExprAST(BinaryOp::Or, $1, $3); }; +/** + * @brief Integer constant wrapper. + */ Number : INT_CONST { $$ = new NumberAST($1); }; +/** + * @brief Left-value expression (variable / array access). + */ LVal : IDENT { $$ = new LValAST(std::move(*$1), {}); diff --git a/project/src/ir/ast.cpp b/project/src/ir/ast.cpp index 615366c..c709c55 100644 --- a/project/src/ir/ast.cpp +++ b/project/src/ir/ast.cpp @@ -51,6 +51,13 @@ AssignStmtAST::AssignStmtAST(BaseAST *_lval, BaseAST *_expr) { } } +/** + * @brief Constructs an ArrayDefAST node. + * @param _is_const Whether the array is constant. + * @param _ident Array identifier name. + * @param _array_suffix List of expressions defining array dimensions. + * @param _init_val Optional initialization value(s). + */ ArrayDefAST::ArrayDefAST(bool _is_const, std::string _ident, std::vector> _array_suffix, InitValStmtAST *_init_val) @@ -91,7 +98,10 @@ auto FuncDefAST::dump(int depth) const -> void { } } -// TODO: add number +/** + * @brief Dumps ArrayDefAST node details. + * @param depth Indentation depth. + */ auto ArrayDefAST::dump(int depth) const -> void { fmt::println("{}ArrayDefAST: {}", indent(depth), ident); } @@ -129,6 +139,10 @@ auto ExprStmtAST::dump(int depth) const -> void { } } +/** + * @brief Dumps InitValStmtAST node details. + * @param depth Indentation depth. + */ auto InitValStmtAST::dump(int depth) const -> void { fmt::println("{}InitValStmtAST:", indent(depth)); for (const auto &init_val : initialize_list) { diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index 578f0e8..c120296 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -96,6 +96,10 @@ auto FuncParamAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } +/** + * @brief Converts function parameter type to Koopa IR string. + * @return The Koopa type string (e.g., `i32` or `*i32`). + */ auto FuncParamAST::toKoopa(ir::KoopaBuilder &builder) const -> std::string { std::shared_ptr param_type; @@ -190,6 +194,20 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } +/** + * @brief Generates IR for an array definition (global or local). + * + * Handles: + * 1. Type Construction: Recursively builds the multidimensional array type. + * 2. IR String Generation: Format string for the array type (e.g., `[[i32, 2], 3]`). + * 3. Allocation: + * - Global: Allocates in `.data` section, handles initialization. + * - Local: Allocates on stack, handles initialization via `getelemptr` and `store`. + * 4. Initialization: Flattens the initializer list and fills the array. + * + * @param builder The IR builder context. + * @return An empty string. + */ auto ArrayDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::shared_ptr arr_type = type::IntType::get(); for (const auto &dim : array_suffix | reverse) { @@ -282,7 +300,7 @@ auto ArrayDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * - Globals: Allocated in the global space, possibly with an initializer. * - Locals: Allocated on the stack using 'alloc'. Constants are tracked in the * symbol table but don't result in 'alloc' instructions unless they are - * non-const. + * non-const. * * @param builder The IR builder context. * @return An empty string. @@ -379,6 +397,12 @@ auto ExprStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } +/** + * @brief Dummy code generation method for initializer lists. + * + * Helper node `InitValStmtAST` is only used during initialization flattening. + * Calling this method is a logical error. + */ auto InitValStmtAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const -> std::string { //! No node should call `InitValStmtAST`'s `codeGen` function, as it is only