From 49d584a3c0997dd8193a038fcc63c0233e8f7853 Mon Sep 17 00:00:00 2001 From: Lee Date: Mon, 24 Jun 2024 14:23:29 +0800 Subject: [PATCH 01/15] Add is_global member to AST node --- include/ast.hpp | 1 + src/type_checker.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/ast.hpp b/include/ast.hpp index c302544e..5b9f58a8 100644 --- a/include/ast.hpp +++ b/include/ast.hpp @@ -36,6 +36,7 @@ struct AstNode { AstNode& operator=(AstNode&&) = delete; Location loc; + bool is_global{false}; }; // NOLINTBEGIN(cppcoreguidelines-special-member-functions): diff --git a/src/type_checker.cpp b/src/type_checker.cpp index b43d6813..c025a3e4 100644 --- a/src/type_checker.cpp +++ b/src/type_checker.cpp @@ -52,6 +52,13 @@ std::string MangleRecordTypeId(const std::string& id, } } +auto + id_is_global // NOLINT(cppcoreguidelines-avoid-non-const-global-variables): + // Accessible only within this translation unit; + // declaring as a data member introduces unnecessary + // dependency. + = std::map{}; + } // namespace void TypeChecker::Visit(DeclStmtNode& decl_stmt) { @@ -62,6 +69,10 @@ void TypeChecker::Visit(DeclStmtNode& decl_stmt) { void TypeChecker::Visit(VarDeclNode& decl) { if (decl.init) { + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + decl.init->is_global = true; + } + decl.init->Accept(*this); if (decl.init->type != decl.type) { // TODO: incompatible types when initializing type 'type' using type @@ -74,6 +85,11 @@ void TypeChecker::Visit(VarDeclNode& decl) { } else { auto symbol = std::make_unique(decl.id, decl.type->Clone()); env_.AddSymbol(std::move(symbol), env_.CurrentScopeKind()); + + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + decl.is_global = true; + id_is_global[decl.id] = true; + } } } @@ -89,8 +105,17 @@ void TypeChecker::Visit(ArrDeclNode& arr_decl) { if (!init->type->IsEqual(*symbol->type)) { // TODO: element unmatches array element type } + + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + init->is_global = true; + } } env_.AddSymbol(std::move(symbol), env_.CurrentScopeKind()); + + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + arr_decl.is_global = true; + id_is_global[arr_decl.id] = true; + } } // TODO: Check initializer type @@ -142,8 +167,15 @@ void TypeChecker::Visit(RecordVarDeclNode& record_var_decl) { // TODO: type check between fields and initialized members. for (auto& init : record_var_decl.inits) { init->Accept(*this); + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + init->is_global = true; + } } env_.AddSymbol(std::move(symbol), env_.CurrentScopeKind()); + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + record_var_decl.is_global = true; + id_is_global[record_var_decl.id] = true; + } record_var_decl.type = record_type->type->Clone(); } @@ -404,6 +436,9 @@ void TypeChecker::Visit(NullExprNode&) { void TypeChecker::Visit(IdExprNode& id_expr) { if (auto symbol = env_.LookUpSymbol(id_expr.id)) { id_expr.type = symbol->type->Clone(); + if (id_is_global.count(id_expr.id) != 0) { + id_expr.is_global = true; + } } else { // TODO: 'id' undeclared assert(false); From 4e11cc55f73f24b51b2a0d5a8a25158128678934 Mon Sep 17 00:00:00 2001 From: Lee Date: Mon, 24 Jun 2024 14:23:46 +0800 Subject: [PATCH 02/15] Code generation for global variable and initialization --- src/qbe_ir_generator.cpp | 113 +++++++++++++++++++++++------------ test/codegen/global_decl.c | 16 +++++ test/codegen/global_decl.exp | 2 + 3 files changed, 92 insertions(+), 39 deletions(-) create mode 100644 test/codegen/global_decl.c create mode 100644 test/codegen/global_decl.exp diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index 5baaa55e..7dd2128a 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -133,6 +133,14 @@ auto label_views_of_jumpable_blocks // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) = std::vector{}; +union GlobalVarInitVal { + int ival; +}; + +auto + global_var_init_val // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + = std::vector{}; + } // namespace void QbeIrGenerator::Visit(const DeclStmtNode& decl_stmt) { @@ -144,33 +152,48 @@ void QbeIrGenerator::Visit(const DeclStmtNode& decl_stmt) { } void QbeIrGenerator::Visit(const VarDeclNode& decl) { - int id_num = NextLocalNum(); - WriteInstr_("{} =l alloc{} {}", FuncScopeTemp{id_num}, decl.type->size(), - decl.type->size()); - if (decl.init) { - decl.init->Accept(*this); - int init_num = num_recorder.NumOfPrevExpr(); - // A pointer declaration may have two options for its right hand side: - if (decl.init->type->IsPtr() || decl.init->type->IsFunc()) { - // 1. int* a = &b; rhs is a reference of integer. We need to store b's - // address to a, where we need to map b's reg_num back to its id_num. - if (dynamic_cast((decl.init).get())) { - WriteInstr_("storel {}, {}", - FuncScopeTemp{reg_num_to_id_num.at(init_num)}, - FuncScopeTemp{id_num}); + if (decl.is_global) { + // TODO: support different data types. + if (decl.init) { + global_var_init_val.clear(); + decl.init->Accept(*this); + Write_("export data {0} = align {1} {{ w {2} }}\n", + user_defined::GlobalPointer{decl.id}, decl.type->size(), + global_var_init_val.at(0).ival); + } else { + // `z` in QBE stands for allocating n bytes of memory space. + Write_("export data {0} = align {1} {{ z {1} }}\n", + user_defined::GlobalPointer{decl.id}, decl.type->size()); + } + } else { + int id_num = NextLocalNum(); + WriteInstr_("{} =l alloc{} {}", FuncScopeTemp{id_num}, decl.type->size(), + decl.type->size()); + if (decl.init) { + decl.init->Accept(*this); + int init_num = num_recorder.NumOfPrevExpr(); + // A pointer declaration may have two options for its right hand side: + if (decl.init->type->IsPtr() || decl.init->type->IsFunc()) { + // 1. int* a = &b; rhs is a reference of integer. We need to store b's + // address to a, where we need to map b's reg_num back to its id_num. + if (dynamic_cast((decl.init).get())) { + WriteInstr_("storel {}, {}", + FuncScopeTemp{reg_num_to_id_num.at(init_num)}, + FuncScopeTemp{id_num}); + } else { + // 2. int* a = c; c itself stores the address of another integer. We + // can directly use the address c currently holds. + WriteInstr_("storel {}, {}", FuncScopeTemp{init_num}, + FuncScopeTemp{id_num}); + } } else { - // 2. int* a = c; c itself stores the address of another integer. We can - // directly use the address c currently holds. - WriteInstr_("storel {}, {}", FuncScopeTemp{init_num}, + WriteInstr_("storew {}, {}", FuncScopeTemp{init_num}, FuncScopeTemp{id_num}); } - } else { - WriteInstr_("storew {}, {}", FuncScopeTemp{init_num}, - FuncScopeTemp{id_num}); } + // Set up the number of the id so we know were to load it back. + id_to_num[decl.id] = id_num; } - // Set up the number of the id so we know were to load it back. - id_to_num[decl.id] = id_num; } void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { @@ -644,28 +667,40 @@ void QbeIrGenerator::Visit(const IdExprNode& id_expr) { num_recorder.Record(res_num); return; } - assert(id_to_num.count(id_expr.id) != 0); - /// @brief Plays the role of a "pointer". Its value has to be loaded to - /// the register before use. - int id_num = id_to_num.at(id_expr.id); - int reg_num = NextLocalNum(); - if (id_expr.type->IsPtr() || id_expr.type->IsFunc()) { - WriteInstr_("{} =l loadl {}", FuncScopeTemp{reg_num}, - FuncScopeTemp{id_num}); - } else { + if (id_expr.is_global) { + int reg_num = NextLocalNum(); WriteInstr_("{} =w loadw {}", FuncScopeTemp{reg_num}, - FuncScopeTemp{id_num}); + user_defined::GlobalPointer{id_expr.id}); + num_recorder.Record(reg_num); + } else { + assert(id_to_num.count(id_expr.id) != 0); + /// @brief Plays the role of a "pointer". Its value has to be loaded to + /// the register before use. + int id_num = id_to_num.at(id_expr.id); + int reg_num = NextLocalNum(); + if (id_expr.type->IsPtr() || id_expr.type->IsFunc()) { + WriteInstr_("{} =l loadl {}", FuncScopeTemp{reg_num}, + FuncScopeTemp{id_num}); + } else { + WriteInstr_("{} =w loadw {}", FuncScopeTemp{reg_num}, + FuncScopeTemp{id_num}); + } + num_recorder.Record(reg_num); + // Map the temporary reg_num to id_num, so that upper level nodes can store + // value to id_num instead of reg_num. + reg_num_to_id_num[reg_num] = id_num; } - num_recorder.Record(reg_num); - // Map the temporary reg_num to id_num, so that upper level nodes can store - // value to id_num instead of reg_num. - reg_num_to_id_num[reg_num] = id_num; } void QbeIrGenerator::Visit(const IntConstExprNode& int_expr) { - int num = NextLocalNum(); - WriteInstr_("{} =w copy {}", FuncScopeTemp{num}, int_expr.val); - num_recorder.Record(num); + if (int_expr.is_global) { + GlobalVarInitVal val = {.ival = int_expr.val}; + global_var_init_val.push_back(val); + } else { + int num = NextLocalNum(); + WriteInstr_("{} =w copy {}", FuncScopeTemp{num}, int_expr.val); + num_recorder.Record(num); + } } void QbeIrGenerator::Visit(const ArgExprNode& arg_expr) { diff --git a/test/codegen/global_decl.c b/test/codegen/global_decl.c new file mode 100644 index 00000000..510be805 --- /dev/null +++ b/test/codegen/global_decl.c @@ -0,0 +1,16 @@ +int c; +int d = 6; +// int a[3] = {1, 2, 3}; +//struct animal { +// int lion; +// int hippo; +//} cat; + +int main() { + __builtin_print(c); + __builtin_print(d); + //__builtin_print(a[1]); + //__builtin_print(cat.lion); + //__builtin_print(cat.hippo); + return 0; +} \ No newline at end of file diff --git a/test/codegen/global_decl.exp b/test/codegen/global_decl.exp new file mode 100644 index 00000000..690cb2a0 --- /dev/null +++ b/test/codegen/global_decl.exp @@ -0,0 +1,2 @@ +0 +6 From 400d706d98891b111eb922085fc6c4ad0bcbced0 Mon Sep 17 00:00:00 2001 From: Lee Date: Mon, 24 Jun 2024 15:16:28 +0800 Subject: [PATCH 03/15] Code generation for global array variable --- src/qbe_ir_generator.cpp | 97 +++++++++++++++++++++++++----------- src/type_checker.cpp | 6 +++ test/codegen/global_decl.c | 16 +++--- test/codegen/global_decl.exp | 4 ++ 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index 7dd2128a..8f712a99 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -94,6 +94,12 @@ auto // dependency. = std::map{}; +auto + reg_num_to_id // NOLINT(cppcoreguidelines-avoid-non-const-global-variables): + // Accessible only within this translation unit; declaring as + // a data member introduces unnecessary dependency. + = std::map{}; + /// @brief Every expression generates a temporary. The local number of such /// temporary should be stored, so can propagate to later uses. class PrevExprNumRecorder { @@ -157,7 +163,7 @@ void QbeIrGenerator::Visit(const VarDeclNode& decl) { if (decl.init) { global_var_init_val.clear(); decl.init->Accept(*this); - Write_("export data {0} = align {1} {{ w {2} }}\n", + Write_("export data {} = align {} {{ w {} }}\n", user_defined::GlobalPointer{decl.id}, decl.type->size(), global_var_init_val.at(0).ival); } else { @@ -197,35 +203,59 @@ void QbeIrGenerator::Visit(const VarDeclNode& decl) { } void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { - int base_addr_num = NextLocalNum(); - assert(arr_decl.type->IsArr()); - const auto* arr_type = dynamic_cast((arr_decl.type).get()); - auto element_size = arr_type->element_type().size(); - WriteInstr_("{} =l alloc{} {}", FuncScopeTemp{base_addr_num}, element_size, - arr_decl.type->size()); - id_to_num[arr_decl.id] = base_addr_num; - - for (auto i = std::size_t{0}, e = arr_type->len(); i < e; ++i) { - if (i < arr_decl.init_list.size()) { - auto& arr_init = arr_decl.init_list.at(i); - arr_init->Accept(*this); + if (arr_decl.is_global) { + const auto* arr_type = dynamic_cast((arr_decl.type).get()); + assert(arr_type); + if (arr_decl.init_list.size() != 0) { + global_var_init_val.clear(); + Write_("export data {} = align {} {{ ", + user_defined::GlobalPointer{arr_decl.id}, + arr_type->element_type().size()); + for (auto i = std::size_t{0}, e = arr_type->len(); i < e; ++i) { + auto& arr_init = arr_decl.init_list.at(i); + arr_init->Accept(*this); + Write_("w {}", global_var_init_val.at(i).ival); + if (i != e - 1) { + Write_(", "); + } + } + Write_(" }}\n"); + } else { + Write_("export data {} = align {} {{ z {} }}\n", + user_defined::GlobalPointer{arr_decl.id}, + arr_type->element_type().size(), arr_decl.type->size()); } + } else { + int base_addr_num = NextLocalNum(); + assert(arr_decl.type->IsArr()); + const auto* arr_type = dynamic_cast((arr_decl.type).get()); + auto element_size = arr_type->element_type().size(); + WriteInstr_("{} =l alloc{} {}", FuncScopeTemp{base_addr_num}, element_size, + arr_decl.type->size()); + id_to_num[arr_decl.id] = base_addr_num; + + for (auto i = std::size_t{0}, e = arr_type->len(); i < e; ++i) { + if (i < arr_decl.init_list.size()) { + auto& arr_init = arr_decl.init_list.at(i); + arr_init->Accept(*this); + } - const int offset = NextLocalNum(); - WriteInstr_("{} =l extsw {}", FuncScopeTemp{offset}, i * element_size); + const int offset = NextLocalNum(); + WriteInstr_("{} =l extsw {}", FuncScopeTemp{offset}, i * element_size); - // res_addr = base_addr + offset - const int res_addr_num = NextLocalNum(); - WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num}, - FuncScopeTemp{base_addr_num}, FuncScopeTemp{offset}); + // res_addr = base_addr + offset + const int res_addr_num = NextLocalNum(); + WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num}, + FuncScopeTemp{base_addr_num}, FuncScopeTemp{offset}); - if (i < arr_decl.init_list.size()) { - int init_val_num = num_recorder.NumOfPrevExpr(); - WriteInstr_("storew {}, {}", FuncScopeTemp{init_val_num}, - FuncScopeTemp{res_addr_num}); - } else { - // set remaining elements as 0 - WriteInstr_("storew 0, {}", FuncScopeTemp{res_addr_num}); + if (i < arr_decl.init_list.size()) { + int init_val_num = num_recorder.NumOfPrevExpr(); + WriteInstr_("storew {}, {}", FuncScopeTemp{init_val_num}, + FuncScopeTemp{res_addr_num}); + } else { + // set remaining elements as 0 + WriteInstr_("storew 0, {}", FuncScopeTemp{res_addr_num}); + } } } } @@ -672,6 +702,7 @@ void QbeIrGenerator::Visit(const IdExprNode& id_expr) { WriteInstr_("{} =w loadw {}", FuncScopeTemp{reg_num}, user_defined::GlobalPointer{id_expr.id}); num_recorder.Record(reg_num); + reg_num_to_id[reg_num] = id_expr.id; } else { assert(id_to_num.count(id_expr.id) != 0); /// @brief Plays the role of a "pointer". Its value has to be loaded to @@ -710,8 +741,6 @@ void QbeIrGenerator::Visit(const ArgExprNode& arg_expr) { void QbeIrGenerator::Visit(const ArrSubExprNode& arr_sub_expr) { arr_sub_expr.arr->Accept(*this); const int reg_num = num_recorder.NumOfPrevExpr(); - // address of the first element - const int base_addr = reg_num_to_id_num.at(reg_num); arr_sub_expr.index->Accept(*this); const int index_num = num_recorder.NumOfPrevExpr(); @@ -731,8 +760,16 @@ void QbeIrGenerator::Visit(const ArrSubExprNode& arr_sub_expr) { // res_addr = base_addr + offset const int res_addr_num = NextLocalNum(); - WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num}, - FuncScopeTemp{base_addr}, FuncScopeTemp{offset}); + if (arr_sub_expr.is_global) { + const auto id = reg_num_to_id.at(reg_num); + WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num}, + user_defined::GlobalPointer{id}, FuncScopeTemp{offset}); + } else { + // address of the first element + const int base_addr = reg_num_to_id_num.at(reg_num); + WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num}, + FuncScopeTemp{base_addr}, FuncScopeTemp{offset}); + } // load value from res_addr const int res_num = NextLocalNum(); diff --git a/src/type_checker.cpp b/src/type_checker.cpp index c025a3e4..47fd15ee 100644 --- a/src/type_checker.cpp +++ b/src/type_checker.cpp @@ -416,6 +416,9 @@ void TypeChecker::Visit(InitExprNode& init_expr) { } init_expr.expr->Accept(*this); init_expr.type = init_expr.expr->type->Clone(); + if (env_.CurrentScopeKind() == ScopeKind::kFile) { + init_expr.expr->is_global = true; + } } void TypeChecker::Visit(ArrDesNode& arr_des) { @@ -461,6 +464,9 @@ void TypeChecker::Visit(ArrSubExprNode& arr_sub_expr) { assert(arr_type); // arr_sub_expr should have the element type of the array. arr_sub_expr.type = arr_type->element_type().Clone(); + if (arr_sub_expr.arr->is_global) { + arr_sub_expr.is_global = true; + } } void TypeChecker::Visit(CondExprNode& cond_expr) { diff --git a/test/codegen/global_decl.c b/test/codegen/global_decl.c index 510be805..5b5c7070 100644 --- a/test/codegen/global_decl.c +++ b/test/codegen/global_decl.c @@ -1,16 +1,16 @@ int c; int d = 6; -// int a[3] = {1, 2, 3}; -//struct animal { -// int lion; -// int hippo; -//} cat; + +int b[2]; +int a[3] = {6, 5, 3}; int main() { __builtin_print(c); __builtin_print(d); - //__builtin_print(a[1]); - //__builtin_print(cat.lion); - //__builtin_print(cat.hippo); + + __builtin_print(b[0]); + __builtin_print(a[0]); + __builtin_print(a[1]); + __builtin_print(a[2]); return 0; } \ No newline at end of file diff --git a/test/codegen/global_decl.exp b/test/codegen/global_decl.exp index 690cb2a0..2f112792 100644 --- a/test/codegen/global_decl.exp +++ b/test/codegen/global_decl.exp @@ -1,2 +1,6 @@ 0 6 +0 +6 +5 +3 From d8d5b5a5a637e5e84f2d3d7f0a1eef706460a82b Mon Sep 17 00:00:00 2001 From: Lee Date: Mon, 24 Jun 2024 15:57:27 +0800 Subject: [PATCH 04/15] Refactor global variable initialize value approach --- src/qbe_ir_generator.cpp | 38 ++++++++++++++++++++++++++++---------- test/codegen/global_decl.c | 2 +- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index 8f712a99..f1c39973 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -139,14 +139,32 @@ auto label_views_of_jumpable_blocks // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) = std::vector{}; -union GlobalVarInitVal { - int ival; +struct GlobalVarInitVal { + // TODO: support other types, such as float, long, short + std::variant value; + std::shared_ptr type; }; +/// @brief Stores values from bottom level nodes and pass them to upper leve +/// nodes because global variables are required to initialize exact values at +/// declaration. auto - global_var_init_val // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + global_var_init_vals // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) = std::vector{}; +/// @brief Generate corresponding QBE string based on `type` and `value`. +/// @return formatted QBE string with type and value, e.g. w 4 (word of value 4) +std::string GenerateQBEInit(const GlobalVarInitVal& init) { + return std::visit( + [&init](auto&& arg) { + if (init.type->IsEqual(PrimitiveType::kInt)) { + return "w " + std::to_string(arg); + } + return std::string(""); + }, + init.value); +} + } // namespace void QbeIrGenerator::Visit(const DeclStmtNode& decl_stmt) { @@ -161,11 +179,11 @@ void QbeIrGenerator::Visit(const VarDeclNode& decl) { if (decl.is_global) { // TODO: support different data types. if (decl.init) { - global_var_init_val.clear(); + global_var_init_vals.clear(); decl.init->Accept(*this); - Write_("export data {} = align {} {{ w {} }}\n", + Write_("export data {} = align {} {{ {} }}\n", user_defined::GlobalPointer{decl.id}, decl.type->size(), - global_var_init_val.at(0).ival); + GenerateQBEInit(global_var_init_vals.at(0))); } else { // `z` in QBE stands for allocating n bytes of memory space. Write_("export data {0} = align {1} {{ z {1} }}\n", @@ -207,14 +225,14 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { const auto* arr_type = dynamic_cast((arr_decl.type).get()); assert(arr_type); if (arr_decl.init_list.size() != 0) { - global_var_init_val.clear(); + global_var_init_vals.clear(); Write_("export data {} = align {} {{ ", user_defined::GlobalPointer{arr_decl.id}, arr_type->element_type().size()); for (auto i = std::size_t{0}, e = arr_type->len(); i < e; ++i) { auto& arr_init = arr_decl.init_list.at(i); arr_init->Accept(*this); - Write_("w {}", global_var_init_val.at(i).ival); + Write_("{}", GenerateQBEInit(global_var_init_vals.at(i))); if (i != e - 1) { Write_(", "); } @@ -725,8 +743,8 @@ void QbeIrGenerator::Visit(const IdExprNode& id_expr) { void QbeIrGenerator::Visit(const IntConstExprNode& int_expr) { if (int_expr.is_global) { - GlobalVarInitVal val = {.ival = int_expr.val}; - global_var_init_val.push_back(val); + global_var_init_vals.push_back( + {int_expr.val, std::make_shared(PrimitiveType::kInt)}); } else { int num = NextLocalNum(); WriteInstr_("{} =w copy {}", FuncScopeTemp{num}, int_expr.val); diff --git a/test/codegen/global_decl.c b/test/codegen/global_decl.c index 5b5c7070..f982dc5d 100644 --- a/test/codegen/global_decl.c +++ b/test/codegen/global_decl.c @@ -13,4 +13,4 @@ int main() { __builtin_print(a[1]); __builtin_print(a[2]); return 0; -} \ No newline at end of file +} From 5681be24e8b7f553ec0b00747076c5b4b51b3d01 Mon Sep 17 00:00:00 2001 From: Lee Date: Mon, 24 Jun 2024 16:20:30 +0800 Subject: [PATCH 05/15] Fix clang-tidy --- src/type_checker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/type_checker.cpp b/src/type_checker.cpp index 47fd15ee..eaddec82 100644 --- a/src/type_checker.cpp +++ b/src/type_checker.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From d50c2a7ceebd198affbbbc1b99fbbacb6424850a Mon Sep 17 00:00:00 2001 From: Lee Date: Tue, 2 Jul 2024 17:32:04 +0800 Subject: [PATCH 06/15] Move is_global from AstNode to DeclNode and ExprNode --- include/ast.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/ast.hpp b/include/ast.hpp index 5b9f58a8..33131e33 100644 --- a/include/ast.hpp +++ b/include/ast.hpp @@ -36,7 +36,6 @@ struct AstNode { AstNode& operator=(AstNode&&) = delete; Location loc; - bool is_global{false}; }; // NOLINTBEGIN(cppcoreguidelines-special-member-functions): @@ -72,6 +71,7 @@ struct DeclNode // NOLINT(cppcoreguidelines-special-member-functions) std::string id; std::unique_ptr type; + bool is_global{false}; }; /// @note This is an abstract class. @@ -87,6 +87,7 @@ struct ExprNode // NOLINT(cppcoreguidelines-special-member-functions) std::unique_ptr type = std::make_unique(PrimitiveType::kUnknown); + bool is_global{false}; }; /// @brief A designator node is used to explicitly reference a member for From 1626eb2c678977161467cca4b27e069c914a008c Mon Sep 17 00:00:00 2001 From: Lee Date: Sat, 6 Jul 2024 18:11:11 +0800 Subject: [PATCH 07/15] Implement global variable and array code generation --- src/llvm_ir_generator.cpp | 71 ++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/src/llvm_ir_generator.cpp b/src/llvm_ir_generator.cpp index 62eb4093..199dd9f1 100644 --- a/src/llvm_ir_generator.cpp +++ b/src/llvm_ir_generator.cpp @@ -163,39 +163,84 @@ void LLVMIRGenerator::Visit(const DeclStmtNode& decl_stmt) { void LLVMIRGenerator::Visit(const VarDeclNode& decl) { auto var_type = builder_helper_.GetLLVMType(*(decl.type)); - auto addr = builder_.CreateAlloca(var_type); - if (decl.init) { - decl.init->Accept(*this); - auto val = val_recorder.ValOfPrevExpr(); - builder_.CreateStore(val, addr); + if (decl.is_global) { + auto global_var = new llvm::GlobalVariable( + module_, var_type, true, + llvm::GlobalValue::LinkageTypes::ExternalLinkage, nullptr, decl.id); + llvm::Constant* const_val = nullptr; + if (decl.init) { + decl.init->Accept(*this); + auto val = val_recorder.ValOfPrevExpr(); + const_val = llvm::dyn_cast(val); + assert(const_val); + } else { + const_val = llvm::ConstantInt::get(builder_.getInt32Ty(), 0, true); + } + global_var->setInitializer(const_val); + id_to_val[decl.id] = global_var; + } else { + auto addr = builder_.CreateAlloca(var_type); + if (decl.init) { + decl.init->Accept(*this); + auto val = val_recorder.ValOfPrevExpr(); + builder_.CreateStore(val, addr); + } + id_to_val[decl.id] = addr; } - id_to_val[decl.id] = addr; } void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { - auto arr_type = builder_helper_.GetLLVMType(*(arr_decl.type)); - auto base_addr = builder_.CreateAlloca(arr_type); - id_to_val[arr_decl.id] = base_addr; + auto type = builder_helper_.GetLLVMType(*(arr_decl.type)); + llvm::AllocaInst* base_addr = nullptr; + if (arr_decl.is_global) { + auto global_arr = new llvm::GlobalVariable( + module_, type, true, llvm::GlobalValue::LinkageTypes::ExternalLinkage, + nullptr, arr_decl.id); + id_to_val[arr_decl.id] = global_arr; + } else { + auto addr = builder_.CreateAlloca(type); + id_to_val[arr_decl.id] = addr; + base_addr = addr; + } auto arr_decl_type = dynamic_cast(arr_decl.type.get()); + std::vector arr_elems{}; for (auto i = std::size_t{0}, e = arr_decl_type->len(); i < e; ++i) { if (i < arr_decl.init_list.size()) { auto& arr_init = arr_decl.init_list.at(i); arr_init->Accept(*this); } - auto res_addr = - builder_.CreateConstInBoundsGEP2_32(arr_type, base_addr, 0, i); + llvm::Value* res_addr = nullptr; + if (!arr_decl.is_global) { + res_addr = builder_.CreateConstInBoundsGEP2_32(type, base_addr, 0, i); + } if (i < arr_decl.init_list.size()) { auto init_val = val_recorder.ValOfPrevExpr(); - builder_.CreateStore(init_val, res_addr); + if (arr_decl.is_global) { + auto const_val = llvm::dyn_cast(init_val); + arr_elems.push_back(const_val); + } else { + builder_.CreateStore(init_val, res_addr); + } } else { // set remaining elements as 0 auto zero = llvm::ConstantInt::get(builder_.getInt32Ty(), 0, true); - builder_.CreateStore(zero, res_addr); + if (arr_decl.is_global) { + arr_elems.push_back(zero); + } else { + builder_.CreateStore(zero, res_addr); + } } } + + if (arr_decl.is_global) { + auto arr_type = llvm::dyn_cast(type); + llvm::Constant* arr_init = llvm::ConstantArray::get(arr_type, arr_elems); + auto global_arr = module_.getGlobalVariable(arr_decl.id); + global_arr->setInitializer(arr_init); + } } void LLVMIRGenerator::Visit(const RecordDeclNode& record_decl) { From a5a5746232020825885cf4e7d3cdf3540329f838 Mon Sep 17 00:00:00 2001 From: Lee Date: Sat, 6 Jul 2024 18:30:36 +0800 Subject: [PATCH 08/15] Refactor type checker logic --- src/type_checker.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/type_checker.cpp b/src/type_checker.cpp index eaddec82..5f55a220 100644 --- a/src/type_checker.cpp +++ b/src/type_checker.cpp @@ -465,9 +465,7 @@ void TypeChecker::Visit(ArrSubExprNode& arr_sub_expr) { assert(arr_type); // arr_sub_expr should have the element type of the array. arr_sub_expr.type = arr_type->element_type().Clone(); - if (arr_sub_expr.arr->is_global) { - arr_sub_expr.is_global = true; - } + arr_sub_expr.is_global = arr_sub_expr.arr->is_global; } void TypeChecker::Visit(CondExprNode& cond_expr) { From 7163a9f6a3a4e8ffb906311cfb7a6d1eda37a7ee Mon Sep 17 00:00:00 2001 From: Lee Date: Sat, 6 Jul 2024 20:49:26 +0800 Subject: [PATCH 09/15] Remove new operator with getOrInsertGlobal --- src/llvm_ir_generator.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/llvm_ir_generator.cpp b/src/llvm_ir_generator.cpp index 199dd9f1..3f28ed83 100644 --- a/src/llvm_ir_generator.cpp +++ b/src/llvm_ir_generator.cpp @@ -164,9 +164,8 @@ void LLVMIRGenerator::Visit(const DeclStmtNode& decl_stmt) { void LLVMIRGenerator::Visit(const VarDeclNode& decl) { auto var_type = builder_helper_.GetLLVMType(*(decl.type)); if (decl.is_global) { - auto global_var = new llvm::GlobalVariable( - module_, var_type, true, - llvm::GlobalValue::LinkageTypes::ExternalLinkage, nullptr, decl.id); + auto global = module_.getOrInsertGlobal(decl.id, var_type); + auto global_var = llvm::dyn_cast(global); llvm::Constant* const_val = nullptr; if (decl.init) { decl.init->Accept(*this); @@ -193,9 +192,7 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { auto type = builder_helper_.GetLLVMType(*(arr_decl.type)); llvm::AllocaInst* base_addr = nullptr; if (arr_decl.is_global) { - auto global_arr = new llvm::GlobalVariable( - module_, type, true, llvm::GlobalValue::LinkageTypes::ExternalLinkage, - nullptr, arr_decl.id); + auto global_arr = module_.getOrInsertGlobal(arr_decl.id, type); id_to_val[arr_decl.id] = global_arr; } else { auto addr = builder_.CreateAlloca(type); From 7dcb63077200030ecada8c8d28102b2cdb9ec38c Mon Sep 17 00:00:00 2001 From: Lee Date: Sat, 6 Jul 2024 22:34:54 +0800 Subject: [PATCH 10/15] Update fixes --- src/llvm_ir_generator.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/llvm_ir_generator.cpp b/src/llvm_ir_generator.cpp index 3f28ed83..bd129636 100644 --- a/src/llvm_ir_generator.cpp +++ b/src/llvm_ir_generator.cpp @@ -190,17 +190,16 @@ void LLVMIRGenerator::Visit(const VarDeclNode& decl) { void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { auto type = builder_helper_.GetLLVMType(*(arr_decl.type)); - llvm::AllocaInst* base_addr = nullptr; if (arr_decl.is_global) { auto global_arr = module_.getOrInsertGlobal(arr_decl.id, type); id_to_val[arr_decl.id] = global_arr; } else { auto addr = builder_.CreateAlloca(type); id_to_val[arr_decl.id] = addr; - base_addr = addr; } auto arr_decl_type = dynamic_cast(arr_decl.type.get()); + // This vector stores the initialize values for an array. std::vector arr_elems{}; for (auto i = std::size_t{0}, e = arr_decl_type->len(); i < e; ++i) { if (i < arr_decl.init_list.size()) { @@ -208,17 +207,14 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { arr_init->Accept(*this); } - llvm::Value* res_addr = nullptr; - if (!arr_decl.is_global) { - res_addr = builder_.CreateConstInBoundsGEP2_32(type, base_addr, 0, i); - } - if (i < arr_decl.init_list.size()) { auto init_val = val_recorder.ValOfPrevExpr(); if (arr_decl.is_global) { auto const_val = llvm::dyn_cast(init_val); arr_elems.push_back(const_val); } else { + auto res_addr = builder_.CreateConstInBoundsGEP2_32( + type, id_to_val.at(arr_decl.id), 0, i); builder_.CreateStore(init_val, res_addr); } } else { @@ -227,6 +223,8 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { if (arr_decl.is_global) { arr_elems.push_back(zero); } else { + auto res_addr = builder_.CreateConstInBoundsGEP2_32( + type, id_to_val.at(arr_decl.id), 0, i); builder_.CreateStore(zero, res_addr); } } @@ -234,7 +232,7 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { if (arr_decl.is_global) { auto arr_type = llvm::dyn_cast(type); - llvm::Constant* arr_init = llvm::ConstantArray::get(arr_type, arr_elems); + auto arr_init = llvm::ConstantArray::get(arr_type, arr_elems); auto global_arr = module_.getGlobalVariable(arr_decl.id); global_arr->setInitializer(arr_init); } From 34cab81bf4fd8a395d9eedf33128feb835cd9128 Mon Sep 17 00:00:00 2001 From: Lee Date: Sat, 6 Jul 2024 22:41:32 +0800 Subject: [PATCH 11/15] Update global variable assignment expression and test case --- src/qbe_ir_generator.cpp | 21 +++++++++++++++++---- test/codegen/global_decl.c | 4 ++++ test/codegen/global_decl.exp | 6 +++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index f1c39973..374e53d1 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -1084,11 +1084,24 @@ void QbeIrGenerator::Visit(const SimpleAssignmentExprNode& assign_expr) { int rhs_num = num_recorder.NumOfPrevExpr(); if (assign_expr.lhs->type->IsPtr()) { // Assign pointer address to another pointer. - WriteInstr_("storel {}, {}", FuncScopeTemp{rhs_num}, - FuncScopeTemp{reg_num_to_id_num.at(lhs_num)}); + if (assign_expr.lhs->is_global) { + WriteInstr_("storel {}, {}", FuncScopeTemp{rhs_num}, + user_defined::GlobalPointer{reg_num_to_id.at(lhs_num)}); + } else { + WriteInstr_("storel {}, {}", FuncScopeTemp{rhs_num}, + FuncScopeTemp{reg_num_to_id_num.at(lhs_num)}); + } } else { - WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, - FuncScopeTemp{reg_num_to_id_num.at(lhs_num)}); + // Global array subscripting will return the target address instead of the + // address of `id`. + if (assign_expr.lhs->is_global && + !dynamic_cast(assign_expr.lhs.get())) { + WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, + user_defined::GlobalPointer{reg_num_to_id.at(lhs_num)}); + } else { + WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, + FuncScopeTemp{reg_num_to_id_num.at(lhs_num)}); + } } num_recorder.Record(rhs_num); } diff --git a/test/codegen/global_decl.c b/test/codegen/global_decl.c index f982dc5d..439163a7 100644 --- a/test/codegen/global_decl.c +++ b/test/codegen/global_decl.c @@ -6,9 +6,13 @@ int a[3] = {6, 5, 3}; int main() { __builtin_print(c); + d = 4; __builtin_print(d); __builtin_print(b[0]); + + a[0] = 7; + a[2] = 4; __builtin_print(a[0]); __builtin_print(a[1]); __builtin_print(a[2]); diff --git a/test/codegen/global_decl.exp b/test/codegen/global_decl.exp index 2f112792..e5a38575 100644 --- a/test/codegen/global_decl.exp +++ b/test/codegen/global_decl.exp @@ -1,6 +1,6 @@ 0 -6 +4 0 -6 +7 5 -3 +4 From b2b191035332a8b1199a943cee8ff143eccb721e Mon Sep 17 00:00:00 2001 From: Lee Date: Sun, 7 Jul 2024 21:06:04 +0800 Subject: [PATCH 12/15] Set default value for remaining elements in array initialization --- src/qbe_ir_generator.cpp | 39 ++++++++++++++++++++---------------- test/codegen/global_decl.c | 6 ++++++ test/codegen/global_decl.exp | 4 ++++ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index 374e53d1..61922a45 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -224,25 +224,30 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { if (arr_decl.is_global) { const auto* arr_type = dynamic_cast((arr_decl.type).get()); assert(arr_type); - if (arr_decl.init_list.size() != 0) { - global_var_init_vals.clear(); - Write_("export data {} = align {} {{ ", - user_defined::GlobalPointer{arr_decl.id}, - arr_type->element_type().size()); - for (auto i = std::size_t{0}, e = arr_type->len(); i < e; ++i) { - auto& arr_init = arr_decl.init_list.at(i); - arr_init->Accept(*this); - Write_("{}", GenerateQBEInit(global_var_init_vals.at(i))); - if (i != e - 1) { - Write_(", "); - } + Write_("export data {} = align {} {{ ", + user_defined::GlobalPointer{arr_decl.id}, + arr_type->element_type().size()); + + global_var_init_vals.clear(); + auto arr_size = arr_type->len(); + auto init_len = arr_decl.init_list.size(); + // The predicate of this loop guarantees that it doesn't go out of bound if + // the number of initialized elements is less than the array declaration + // size. + for (auto i = std::size_t{0}; i < arr_size && i < init_len; ++i) { + auto& arr_init = arr_decl.init_list.at(i); + arr_init->Accept(*this); + Write_("{}", GenerateQBEInit(global_var_init_vals.at(i))); + if (i != arr_size - 1) { + Write_(", "); } - Write_(" }}\n"); - } else { - Write_("export data {} = align {} {{ z {} }}\n", - user_defined::GlobalPointer{arr_decl.id}, - arr_type->element_type().size(), arr_decl.type->size()); } + + // set remaining elements as 0 + if (init_len < arr_size) { + Write_("z {}", (arr_size - init_len) * arr_type->element_type().size()); + } + Write_(" }}\n"); } else { int base_addr_num = NextLocalNum(); assert(arr_decl.type->IsArr()); diff --git a/test/codegen/global_decl.c b/test/codegen/global_decl.c index 439163a7..8dc52d33 100644 --- a/test/codegen/global_decl.c +++ b/test/codegen/global_decl.c @@ -3,6 +3,7 @@ int d = 6; int b[2]; int a[3] = {6, 5, 3}; +int e[4] = {8, 9}; int main() { __builtin_print(c); @@ -16,5 +17,10 @@ int main() { __builtin_print(a[0]); __builtin_print(a[1]); __builtin_print(a[2]); + + __builtin_print(e[0]); + __builtin_print(e[1]); + __builtin_print(e[2]); + __builtin_print(e[3]); return 0; } diff --git a/test/codegen/global_decl.exp b/test/codegen/global_decl.exp index e5a38575..5f247f2f 100644 --- a/test/codegen/global_decl.exp +++ b/test/codegen/global_decl.exp @@ -4,3 +4,7 @@ 7 5 4 +8 +9 +0 +0 From 1a6c0f8ef49932ca828c5dd8e6d80d78c0fafa38 Mon Sep 17 00:00:00 2001 From: Lee Date: Sun, 7 Jul 2024 21:47:00 +0800 Subject: [PATCH 13/15] Skip default values for local scope array that doesn't have initialized values --- src/llvm_ir_generator.cpp | 12 ++++++++---- src/qbe_ir_generator.cpp | 12 +++++++++--- test/codegen/array.c | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/llvm_ir_generator.cpp b/src/llvm_ir_generator.cpp index bd129636..acc17c6a 100644 --- a/src/llvm_ir_generator.cpp +++ b/src/llvm_ir_generator.cpp @@ -201,13 +201,15 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { auto arr_decl_type = dynamic_cast(arr_decl.type.get()); // This vector stores the initialize values for an array. std::vector arr_elems{}; - for (auto i = std::size_t{0}, e = arr_decl_type->len(); i < e; ++i) { - if (i < arr_decl.init_list.size()) { + for (auto i = std::size_t{0}, e = arr_decl_type->len(), + init_len = arr_decl.init_list.size(); + i < e; ++i) { + if (i < init_len) { auto& arr_init = arr_decl.init_list.at(i); arr_init->Accept(*this); } - if (i < arr_decl.init_list.size()) { + if (i < init_len) { auto init_val = val_recorder.ValOfPrevExpr(); if (arr_decl.is_global) { auto const_val = llvm::dyn_cast(init_val); @@ -222,7 +224,9 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { auto zero = llvm::ConstantInt::get(builder_.getInt32Ty(), 0, true); if (arr_decl.is_global) { arr_elems.push_back(zero); - } else { + } else if (!arr_decl.is_global && 0 < init_len) { + // If array is in local scope and its initialized values is not + // declared, then compiler will not set element values to 0. auto res_addr = builder_.CreateConstInBoundsGEP2_32( type, id_to_val.at(arr_decl.id), 0, i); builder_.CreateStore(zero, res_addr); diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index 61922a45..df7d5a8b 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -257,8 +257,14 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { arr_decl.type->size()); id_to_num[arr_decl.id] = base_addr_num; - for (auto i = std::size_t{0}, e = arr_type->len(); i < e; ++i) { - if (i < arr_decl.init_list.size()) { + // NOTE: Compiler will not set elements to 0 if `init_len` is 0. + // 6.7.9 Initialization + // 10. If an object that has automatic storage duration is not initialized + // explicitly, its value is indeterminate. + for (auto i = std::size_t{0}, e = arr_type->len(), + init_len = arr_decl.init_list.size(); + i < e && 0 < init_len; ++i) { + if (i < init_len) { auto& arr_init = arr_decl.init_list.at(i); arr_init->Accept(*this); } @@ -271,7 +277,7 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { WriteInstr_("{} =l add {}, {}", FuncScopeTemp{res_addr_num}, FuncScopeTemp{base_addr_num}, FuncScopeTemp{offset}); - if (i < arr_decl.init_list.size()) { + if (i < init_len) { int init_val_num = num_recorder.NumOfPrevExpr(); WriteInstr_("storew {}, {}", FuncScopeTemp{init_val_num}, FuncScopeTemp{res_addr_num}); diff --git a/test/codegen/array.c b/test/codegen/array.c index 3e6b375a..4c90c991 100644 --- a/test/codegen/array.c +++ b/test/codegen/array.c @@ -22,5 +22,6 @@ int main() { __builtin_print(e[1]); __builtin_print(e[2]); + int f[2]; return 0; } From dcff243e2ce6cf47b93202dbc1f584f139519f5a Mon Sep 17 00:00:00 2001 From: Lee Date: Sun, 7 Jul 2024 22:38:55 +0800 Subject: [PATCH 14/15] Update comments and small fixes --- src/llvm_ir_generator.cpp | 9 ++++----- src/qbe_ir_generator.cpp | 8 +++----- test/codegen/array.c | 1 + 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/llvm_ir_generator.cpp b/src/llvm_ir_generator.cpp index acc17c6a..c66db671 100644 --- a/src/llvm_ir_generator.cpp +++ b/src/llvm_ir_generator.cpp @@ -199,7 +199,7 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { } auto arr_decl_type = dynamic_cast(arr_decl.type.get()); - // This vector stores the initialize values for an array. + // This vector stores the initialize values for a global array. std::vector arr_elems{}; for (auto i = std::size_t{0}, e = arr_decl_type->len(), init_len = arr_decl.init_list.size(); @@ -220,13 +220,12 @@ void LLVMIRGenerator::Visit(const ArrDeclNode& arr_decl) { builder_.CreateStore(init_val, res_addr); } } else { - // set remaining elements as 0 auto zero = llvm::ConstantInt::get(builder_.getInt32Ty(), 0, true); + // A global array is always initialized to 0, + // but a local array remains uninitialized if no values are provided. if (arr_decl.is_global) { arr_elems.push_back(zero); - } else if (!arr_decl.is_global && 0 < init_len) { - // If array is in local scope and its initialized values is not - // declared, then compiler will not set element values to 0. + } else if (!arr_decl.is_global && init_len != 0) { auto res_addr = builder_.CreateConstInBoundsGEP2_32( type, id_to_val.at(arr_decl.id), 0, i); builder_.CreateStore(zero, res_addr); diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index df7d5a8b..9c6770cb 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -231,10 +231,8 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { global_var_init_vals.clear(); auto arr_size = arr_type->len(); auto init_len = arr_decl.init_list.size(); - // The predicate of this loop guarantees that it doesn't go out of bound if - // the number of initialized elements is less than the array declaration - // size. - for (auto i = std::size_t{0}; i < arr_size && i < init_len; ++i) { + assert(init_len <= arr_size); + for (auto i = std::size_t{0}; i < init_len; ++i) { auto& arr_init = arr_decl.init_list.at(i); arr_init->Accept(*this); Write_("{}", GenerateQBEInit(global_var_init_vals.at(i))); @@ -263,7 +261,7 @@ void QbeIrGenerator::Visit(const ArrDeclNode& arr_decl) { // explicitly, its value is indeterminate. for (auto i = std::size_t{0}, e = arr_type->len(), init_len = arr_decl.init_list.size(); - i < e && 0 < init_len; ++i) { + i < e && init_len != 0; ++i) { if (i < init_len) { auto& arr_init = arr_decl.init_list.at(i); arr_init->Accept(*this); diff --git a/test/codegen/array.c b/test/codegen/array.c index 4c90c991..9b4f0a0a 100644 --- a/test/codegen/array.c +++ b/test/codegen/array.c @@ -22,6 +22,7 @@ int main() { __builtin_print(e[1]); __builtin_print(e[2]); + // NOTE: Local scope array should not be 0-initialized, and the generated IR has to be checked manually. int f[2]; return 0; } From 4b0e2c75b4bf51fed03f9ba3e965f049d0de1f4e Mon Sep 17 00:00:00 2001 From: Lee Date: Sun, 7 Jul 2024 22:47:18 +0800 Subject: [PATCH 15/15] Update assignment expression logic for array subscripting --- src/qbe_ir_generator.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/qbe_ir_generator.cpp b/src/qbe_ir_generator.cpp index 9c6770cb..36434878 100644 --- a/src/qbe_ir_generator.cpp +++ b/src/qbe_ir_generator.cpp @@ -1101,12 +1101,16 @@ void QbeIrGenerator::Visit(const SimpleAssignmentExprNode& assign_expr) { FuncScopeTemp{reg_num_to_id_num.at(lhs_num)}); } } else { - // Global array subscripting will return the target address instead of the - // address of `id`. - if (assign_expr.lhs->is_global && - !dynamic_cast(assign_expr.lhs.get())) { - WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, - user_defined::GlobalPointer{reg_num_to_id.at(lhs_num)}); + if (assign_expr.lhs->is_global) { + // Global array subscripting will return the target address instead of the + // address of `id`. + if (dynamic_cast(assign_expr.lhs.get())) { + WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, + FuncScopeTemp{reg_num_to_id_num.at(lhs_num)}); + } else { + WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, + user_defined::GlobalPointer{reg_num_to_id.at(lhs_num)}); + } } else { WriteInstr_("storew {}, {}", FuncScopeTemp{rhs_num}, FuncScopeTemp{reg_num_to_id_num.at(lhs_num)});