From f40efe8819115b85f44b49ea0fed8d4981358ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=BE=99=E6=88=90?= Date: Wed, 3 Dec 2025 21:19:44 +0800 Subject: [PATCH] publish lab4 --- CMakeLists.txt | 2 +- include/cminusfc/cminusf_builder.hpp | 5 - include/codegen/CodeGen.hpp | 14 - include/codegen/Register.hpp | 2 +- include/lightir/BasicBlock.hpp | 3 +- include/lightir/Instruction.hpp | 8 +- include/passes/DeadCode.hpp | 2 +- include/passes/Dominators.hpp | 34 +- include/passes/FuncInfo.hpp | 8 +- include/passes/LICM.hpp | 4 - include/passes/LoopDetection.hpp | 1 + lldb_formatters.py | 3 +- src/cminusfc/cminusf_builder.cpp | 17 - src/cminusfc/main.cpp | 89 +-- src/codegen/CodeGen.cpp | 874 ++++++++++++++++++--------- src/io/io.c | 59 +- src/io/io.h | 4 +- src/lightir/BasicBlock.cpp | 18 +- src/lightir/Function.cpp | 11 +- src/lightir/IRprinter.cpp | 2 +- src/lightir/Instruction.cpp | 20 +- src/lightir/Module.cpp | 34 +- src/lightir/User.cpp | 2 +- src/passes/DeadCode.cpp | 17 +- src/passes/Dominators.cpp | 63 +- src/passes/FuncInfo.cpp | 433 ++++++------- src/passes/LICM.cpp | 249 ++++---- src/passes/LoopDetection.cpp | 19 +- src/passes/Mem2Reg.cpp | 57 +- tests/4-opt/CMakeLists.txt | 9 + tests/4-opt/eval_lab4.cpp | 695 +++++++++++++++++++++ tests/CMakeLists.txt | 5 +- 32 files changed, 1903 insertions(+), 860 deletions(-) create mode 100644 tests/4-opt/CMakeLists.txt create mode 100644 tests/4-opt/eval_lab4.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0829065..ff119d8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,4 +63,4 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}) include_directories(${PROJECT_BINARY_DIR}) add_subdirectory(src) -# add_subdirectory(tests) +add_subdirectory(tests) diff --git a/include/cminusfc/cminusf_builder.hpp b/include/cminusfc/cminusf_builder.hpp index b38419c..dcf8e15 100755 --- a/include/cminusfc/cminusf_builder.hpp +++ b/include/cminusfc/cminusf_builder.hpp @@ -67,15 +67,10 @@ class CminusfBuilder : public ASTVisitor { auto *output_float_fun = Function::create(output_float_type, "outputFloat", module); - auto *neg_idx_except_type = FunctionType::get(TyVoid, {}); - auto *neg_idx_except_fun = Function::create( - neg_idx_except_type, "neg_idx_except", module); - scope.enter(); scope.push("input", input_fun); scope.push("output", output_fun); scope.push("outputFloat", output_float_fun); - scope.push("neg_idx_except", neg_idx_except_fun); } Module* getModule() const { return module; } diff --git a/include/codegen/CodeGen.hpp b/include/codegen/CodeGen.hpp index 8680a7c..48ab037 100755 --- a/include/codegen/CodeGen.hpp +++ b/include/codegen/CodeGen.hpp @@ -64,18 +64,6 @@ class CodeGen { void gen_fptosi(); void gen_epilogue(); - static std::string label_name(BasicBlock *bb) { - return "." + bb->get_parent()->get_name() + "_" + bb->get_name(); - } - - static std::string func_exit_label_name(Function *func) { - return func->get_name() + "_exit"; - } - - static std::string fcmp_label_name(BasicBlock *bb, unsigned cnt) { - return label_name(bb) + "_fcmp_" + std::to_string(cnt); - } - struct { /* 随着ir遍历设置 */ Function *func{nullptr}; // 当前函数 @@ -84,14 +72,12 @@ class CodeGen { /* 在allocate()中设置 */ unsigned frame_size{0}; // 当前函数的栈帧大小 std::unordered_map offset_map{}; // 指针相对 fp 的偏移 - unsigned fcmp_cnt{0}; // fcmp 的计数器, 用于创建 fcmp 需要的 label void clear() { func = nullptr; bb = nullptr; inst = nullptr; frame_size = 0; - fcmp_cnt = 0; offset_map.clear(); } diff --git a/include/codegen/Register.hpp b/include/codegen/Register.hpp index 11f6e40..4e16059 100755 --- a/include/codegen/Register.hpp +++ b/include/codegen/Register.hpp @@ -17,7 +17,7 @@ * $r23 - $r31 $s0 - $s8 static * * Floating-point Register Convention - * Name Alias Meaning + * Name Alias Meaning * $f0-$f1 $fa0-$fa1 argument/return value * $f2-$f7 $fa2-$fa7 argument * $f8-$f23 $ft0-$ft15 temporary diff --git a/include/lightir/BasicBlock.hpp b/include/lightir/BasicBlock.hpp index e4c1c02..3ec2bf6 100755 --- a/include/lightir/BasicBlock.hpp +++ b/include/lightir/BasicBlock.hpp @@ -20,8 +20,7 @@ class BasicBlock : public Value { ~BasicBlock() override; static BasicBlock *create(Module *m, const std::string &name, Function *parent) { - auto prefix = name.empty() ? "" : "label_"; - return new BasicBlock(m, prefix + name, parent); + return new BasicBlock(m, name, parent); } /****************api about cfg****************/ diff --git a/include/lightir/Instruction.hpp b/include/lightir/Instruction.hpp index 155cf87..4d0b7e9 100755 --- a/include/lightir/Instruction.hpp +++ b/include/lightir/Instruction.hpp @@ -214,6 +214,8 @@ public: Value *get_condition() const { return get_operand(0); } + void replace_all_bb_match(BasicBlock* need_replace, BasicBlock* replace_to); + std::string print() override; }; @@ -251,8 +253,8 @@ class StoreInst : public Instruction { public: static StoreInst *create_store(Value *val, Value *ptr, BasicBlock *bb); - Value *get_rval() const { return this->get_operand(0); } - Value *get_lval() const { return this->get_operand(1); } + Value *get_val() const { return this->get_operand(0); } + Value *get_ptr() const { return this->get_operand(1); } std::string print() override; }; @@ -264,7 +266,7 @@ class LoadInst : public Instruction { public: static LoadInst *create_load(Value *ptr, BasicBlock *bb, const std::string& name = ""); - Value *get_lval() const { return this->get_operand(0); } + Value *get_ptr() const { return this->get_operand(0); } Type *get_load_type() const { return get_type(); } std::string print() override; diff --git a/include/passes/DeadCode.hpp b/include/passes/DeadCode.hpp index 15f2974..f3a9dc6 100644 --- a/include/passes/DeadCode.hpp +++ b/include/passes/DeadCode.hpp @@ -15,7 +15,7 @@ class FuncInfo; class DeadCode : public TransformPass { public: - /** + /** * * @param m 所属 Module * @param remove_unreachable_bb 是否需要删除不可达的 BasicBlocks diff --git a/include/passes/Dominators.hpp b/include/passes/Dominators.hpp index b9714ac..1dc70b9 100644 --- a/include/passes/Dominators.hpp +++ b/include/passes/Dominators.hpp @@ -20,11 +20,11 @@ class Dominators : public FunctionAnalysisPass { void run() override; // 获取基本块的直接支配节点 - BasicBlock *get_idom(const BasicBlock *bb) const { return idom_.at(bb); } - const std::set &get_dominance_frontier(const BasicBlock *bb) { + BasicBlock *get_idom(BasicBlock *bb) const { return idom_.at(bb); } + const std::set &get_dominance_frontier(BasicBlock *bb) { return dom_frontier_.at(bb); } - const std::set &get_dom_tree_succ_blocks(const BasicBlock *bb) { + const std::set &get_dom_tree_succ_blocks(BasicBlock *bb) { return dom_tree_succ_blocks_.at(bb); } @@ -33,7 +33,7 @@ class Dominators : public FunctionAnalysisPass { void dump_dominator_tree(); // functions for dominance tree - bool is_dominate(const BasicBlock *bb1, const BasicBlock *bb2) const { + bool is_dominate(BasicBlock *bb1, BasicBlock *bb2) const { return dom_tree_L_.at(bb1) <= dom_tree_L_.at(bb2) && dom_tree_R_.at(bb1) >= dom_tree_L_.at(bb2); } @@ -54,33 +54,33 @@ class Dominators : public FunctionAnalysisPass { void create_dom_tree_succ(); void create_dom_dfs_order(); - BasicBlock * intersect(BasicBlock *b1, const BasicBlock *b2) const; + BasicBlock * intersect(BasicBlock *b1, BasicBlock *b2) const; void create_reverse_post_order(); - void set_idom(const BasicBlock *bb, BasicBlock *idom) { idom_[bb] = idom; } - void set_dominance_frontier(const BasicBlock *bb, std::set&df) { + void set_idom(BasicBlock *bb, BasicBlock *idom) { idom_[bb] = idom; } + void set_dominance_frontier(BasicBlock *bb, std::set&df) { dom_frontier_[bb].clear(); dom_frontier_[bb].insert(df.begin(), df.end()); } - void add_dom_tree_succ_block(const BasicBlock *bb, BasicBlock *dom_tree_succ_bb) { + void add_dom_tree_succ_block(BasicBlock *bb, BasicBlock *dom_tree_succ_bb) { dom_tree_succ_blocks_[bb].insert(dom_tree_succ_bb); } - unsigned int get_post_order(const BasicBlock *bb) const { - return post_order_.at(bb); + unsigned int get_reversed_post_order(BasicBlock *bb) const { + return reversed_post_order_.at(bb); } // for debug void print_idom() const; void print_dominance_frontier(); - std::vector post_order_vec_{}; // 逆后序 - std::map post_order_{}; // 逆后序 - std::map idom_{}; // 直接支配 - std::map> dom_frontier_{}; // 支配边界集合 - std::map> dom_tree_succ_blocks_{}; // 支配树中的后继节点 + std::vector reversed_post_order_vec_{}; // 逆后序 + std::map reversed_post_order_{}; // 逆后序索引 + std::map idom_{}; // 直接支配 + std::map> dom_frontier_{}; // 支配边界集合 + std::map> dom_tree_succ_blocks_{}; // 支配树中的后继节点 // 支配树上的dfs序L,R - std::map dom_tree_L_; - std::map dom_tree_R_; + std::map dom_tree_L_; + std::map dom_tree_R_; std::vector dom_dfs_order_; std::vector dom_post_order_; diff --git a/include/passes/FuncInfo.hpp b/include/passes/FuncInfo.hpp index 80b521f..2fb3b12 100644 --- a/include/passes/FuncInfo.hpp +++ b/include/passes/FuncInfo.hpp @@ -27,13 +27,17 @@ class FuncInfo : public ModuleAnalysisPass { void run() override; // 函数是否是纯函数 - bool is_pure(Function *func) { return !func->is_declaration() && !use_libs.count(func) && loads[func].empty() && stores[func].empty(); } + bool is_pure(Function *func) { return !func->is_declaration() && !use_libs[func] && loads[func].empty() && stores[func].empty(); } + // 函数是否使用了 io + bool use_io(Function* func) { return func->is_declaration() || use_libs[func]; } // 返回 StoreInst 存入的变量(全局/局部变量或函数参数) static Value* store_ptr(const StoreInst* st); // 返回 LoadInst 加载的变量(全局/局部变量或函数参数) static Value* load_ptr(const LoadInst* ld); // 返回 CallInst 代表的函数调用间接存入的变量(全局/局部变量或函数参数) - std::unordered_set get_stores(const CallInst* call); + std::unordered_set get_stores(const CallInst* call); + // 返回 CallInst 代表的函数调用间接加载的变量(全局/局部变量或函数参数) + std::unordered_set get_loads(const CallInst* call); private: // 函数存储的值 std::unordered_map stores; diff --git a/include/passes/LICM.hpp b/include/passes/LICM.hpp index f49bdd5..5970619 100644 --- a/include/passes/LICM.hpp +++ b/include/passes/LICM.hpp @@ -18,8 +18,4 @@ class LoopInvariantCodeMotion : public TransformPass { std::vector collect_insts(Loop* loop); void traverse_loop(Loop* loop); void run_on_loop(Loop* loop); - void collect_loop_info(Loop* loop, - std::set &loop_instructions, - std::set &updated_global, - bool &contains_impure_call); }; \ No newline at end of file diff --git a/include/passes/LoopDetection.hpp b/include/passes/LoopDetection.hpp index cd11119..11c00e5 100644 --- a/include/passes/LoopDetection.hpp +++ b/include/passes/LoopDetection.hpp @@ -40,6 +40,7 @@ class Loop { const std::vector& get_sub_loops() { return sub_loops_; } const std::unordered_set& get_latches() { return latches_; } void add_latch(BasicBlock *bb) { latches_.insert(bb); } + std::string safe_print() const; }; class LoopDetection : public FunctionAnalysisPass { diff --git a/lldb_formatters.py b/lldb_formatters.py index bd26101..2c9db2b 100644 --- a/lldb_formatters.py +++ b/lldb_formatters.py @@ -25,7 +25,8 @@ def __lldb_init_module(debugger: lldb.SBDebugger, internal_dict): , "BasicBlock" , "GlobalVariable" , "Instruction", "IBinaryInst", "FBinaryInst", "ICmpInst", "FCmpInst", "CallInst", "BranchInst", "ReturnInst", "GetElementPtrInst", "StoreInst", "LoadInst", "AllocaInst", "ZextInst", "FpToSiInst", "SiToFpInst", "PhiInst" - , "ASMInstruction", "Reg", "FReg", "CFReg" ] + , "ASMInstruction", "Reg", "FReg", "CFReg" + , "Loop" ] for i in types: debugger.HandleCommand( f"type summary add -F lldb_formatters.SafePrintSummary {i} -w my" diff --git a/src/cminusfc/cminusf_builder.cpp b/src/cminusfc/cminusf_builder.cpp index c56463e..bb04be1 100755 --- a/src/cminusfc/cminusf_builder.cpp +++ b/src/cminusfc/cminusf_builder.cpp @@ -329,27 +329,10 @@ Value* CminusfBuilder::visit(ASTVar &node) { } } else { auto *val = node.expression->accept(*this); - auto *exceptBB = BasicBlock::create(module, "", context.func); - auto *contBB = BasicBlock::create(module, "", context.func); if (val->get_type()->is_float_type()) { val = builder->create_fptosi(val, INT32_T); } - Value* is_neg = builder->create_icmp_lt(val, CONST_INT(0)); - - builder->create_cond_br(is_neg, exceptBB, contBB); - builder->set_insert_point(exceptBB); - auto *neg_idx_except_fun = scope.find("neg_idx_except"); - builder->create_call(dynamic_cast(neg_idx_except_fun), {}); - if (context.func->get_return_type()->is_void_type()) { - builder->create_void_ret(); - } else if (context.func->get_return_type()->is_float_type()) { - builder->create_ret(CONST_FP(0.)); - } else { - builder->create_ret(CONST_INT(0)); - } - - builder->set_insert_point(contBB); Value *tmp_ptr; if (is_int || is_float) { tmp_ptr = builder->create_gep(var, {val}); diff --git a/src/cminusfc/main.cpp b/src/cminusfc/main.cpp index 8bcc785..7d891f0 100755 --- a/src/cminusfc/main.cpp +++ b/src/cminusfc/main.cpp @@ -23,30 +23,30 @@ struct Config { std::filesystem::path input_file; std::filesystem::path output_file; - bool emitast{false}; - bool emitasm{false}; - bool emitllvm{false}; + bool emitast{ false }; + bool emitasm{ false }; + bool emitllvm{ false }; // optization conifg - bool mem2reg{false}; - bool licm{false}; + bool mem2reg{ false }; + bool licm{ false }; - Config(int argc, char **argv) : argc(argc), argv(argv) { + Config(int argc, char** argv) : argc(argc), argv(argv) { parse_cmd_line(); check(); } - private: - int argc{-1}; - char **argv{nullptr}; +private: + int argc{ -1 }; + char** argv{ nullptr }; void parse_cmd_line(); void check(); // print helper infomation and exit void print_help() const; - void print_err(const string &msg) const; + void print_err(const string& msg) const; }; -int main(int argc, char **argv) { +int main(int argc, char** argv) { Config config(argc, argv); auto syntax_tree = parse(config.input_file.c_str()); @@ -55,7 +55,8 @@ int main(int argc, char **argv) { if (config.emitast) { // if emit ast (lab1), print ast and return ASTPrinter printer; ast.run_visitor(printer); - } else { + } + else { Module* m; CminusfBuilder builder; ast.run_visitor(builder); @@ -63,11 +64,12 @@ int main(int argc, char **argv) { PassManager PM(m); // optimization - if(config.mem2reg) { + if (config.mem2reg) { + PM.add_pass(true); PM.add_pass(); PM.add_pass(false); } - if(config.licm) { + if (config.licm) { PM.add_pass(); PM.add_pass(false); } @@ -79,13 +81,20 @@ int main(int argc, char **argv) { output_stream << "; ModuleID = 'cminus'\n"; output_stream << "source_filename = " << abs_path << "\n\n"; output_stream << m->print(); - } else if (config.emitasm) { + } + else if (config.emitasm) { + auto abs_path = std::filesystem::canonical(config.input_file); + config.output_file.replace_extension("ll"); + std::ofstream output_stream2(config.output_file); + output_stream2 << "; ModuleID = 'cminus'\n"; + output_stream2 << "source_filename = " << abs_path << "\n\n"; + output_stream2 << m->print(); CodeGen codegen(m); codegen.run(); output_stream << codegen.print(); } - delete m; + delete m; } return 0; @@ -96,27 +105,36 @@ void Config::parse_cmd_line() { for (int i = 1; i < argc; ++i) { if (argv[i] == "-h"s || argv[i] == "--help"s) { print_help(); - } else if (argv[i] == "-o"s) { + } + else if (argv[i] == "-o"s) { if (output_file.empty() && i + 1 < argc) { output_file = argv[i + 1]; i += 1; - } else { + } + else { print_err("bad output file"); } - } else if (argv[i] == "-emit-ast"s) { + } + else if (argv[i] == "-emit-ast"s) { emitast = true; - } else if (argv[i] == "-S"s) { + } + else if (argv[i] == "-S"s) { emitasm = true; - } else if (argv[i] == "-emit-llvm"s) { + } + else if (argv[i] == "-emit-llvm"s) { emitllvm = true; - } else if (argv[i] == "-mem2reg"s) { + } + else if (argv[i] == "-mem2reg"s) { mem2reg = true; - } else if (argv[i] == "-licm"s) { + } + else if (argv[i] == "-licm"s) { licm = true; - }else { + } + else { if (input_file.empty()) { input_file = argv[i]; - } else { + } + else { string err = "unrecognized command-line option \'"s + argv[i] + "\'"s; print_err(err); @@ -143,24 +161,25 @@ void Config::check() { } if (output_file.empty()) { output_file = input_file.stem(); - if (emitllvm) { - output_file.replace_extension(".ll"); - } else if (emitasm) { - output_file.replace_extension(".s"); - } + } + if (emitllvm) { + output_file.replace_extension(".ll"); + } + else if (emitasm) { + output_file.replace_extension(".s"); } } void Config::print_help() const { std::cout << "Usage: " << exe_name - << " [-h|--help] [-o ] [-emit-llvm] [-S] [-dump-json]" - "[-mem2reg] [-licm]" - "" - << std::endl; + << " [-h|--help] [-o ] [-emit-llvm] [-S] [-dump-json]" + "[-mem2reg] [-licm]" + "" + << std::endl; exit(0); } -void Config::print_err(const string &msg) const { +void Config::print_err(const string& msg) const { std::cout << exe_name << ": " << msg << std::endl; exit(-1); } diff --git a/src/codegen/CodeGen.cpp b/src/codegen/CodeGen.cpp index 1ac3cc6..f1bbc38 100755 --- a/src/codegen/CodeGen.cpp +++ b/src/codegen/CodeGen.cpp @@ -1,182 +1,226 @@ #include "CodeGen.hpp" +#include + #include "ASMInstruction.hpp" +#include "BasicBlock.hpp" #include "CodeGenUtil.hpp" +#include "Function.hpp" +#include "Instruction.hpp" #include "Register.hpp" +#include "Type.hpp" +#include -std::string ASMInstruction::safe_print() const +void CodeGen::allocate() { - switch (type) { - case Instruction: - case Attribute: - return content; - case Label: - return content + ":"; - case Comment: - return "# " + content; - } - return ""; -} - -void CodeGen::allocate() { - // 备份 $ra $fp unsigned offset = PROLOGUE_OFFSET_BASE; - - // 为每个参数分配栈空间 - for (auto &arg : context.func->get_args()) { + for (auto& arg : context.func->get_args()) + { auto size = arg->get_type()->get_size(); - offset = offset + size; + offset = ALIGN(offset + size, size); context.offset_map[arg] = -static_cast(offset); } - - // 为指令结果分配栈空间 - for (auto bb : context.func->get_basic_blocks()) { - for (auto instr : bb->get_instructions()) { - // 每个非 void 的定值都分配栈空间 - if (not instr->is_void()) { + for (auto& bb : context.func->get_basic_blocks()) + { + for (auto& instr : bb->get_instructions()) + { + if (not instr->is_void()) + { auto size = instr->get_type()->get_size(); - offset = offset + size; + offset = ALIGN(offset + size, size > 8 ? 8 : size); context.offset_map[instr] = -static_cast(offset); } - // alloca 的副作用:分配额外空间 - if (instr->is_alloca()) { - auto *alloca_inst = dynamic_cast(instr); + if (instr->is_alloca()) + { + auto* alloca_inst = dynamic_cast(instr); auto alloc_size = alloca_inst->get_alloca_type()->get_size(); - offset += alloc_size; + offset = ALIGN(offset + alloc_size, alloc_size > 8 ? 8 : alloc_size); } } } - - // 分配栈空间,需要是 16 的整数倍 context.frame_size = ALIGN(offset, PROLOGUE_ALIGN); } -void CodeGen::copy_stmt() { - for (auto succ : context.bb->get_succ_basic_blocks()) { - for (auto inst : succ->get_instructions()) { - if (inst->is_phi()) { - // 遍历后继块中 phi 的定值 bb - for (unsigned i = 1; i < inst->get_operands().size(); i += 2) { - // phi 的定值 bb 是当前翻译块 - if (inst->get_operand(i) == context.bb) { - auto *lvalue = inst->get_operand(i - 1); - if (lvalue->get_type()->is_float_type()) { +void CodeGen::copy_stmt() +{ + for (auto& succ : context.bb->get_succ_basic_blocks()) + { + for (auto& inst : succ->get_instructions()) + { + if (inst->is_phi()) + { + for (unsigned i = 1; i < inst->get_operands().size(); i += 2) + { + if (inst->get_operand(i) == context.bb) + { + auto* lvalue = inst->get_operand(i - 1); + if (lvalue->get_type()->is_float_type()) + { load_to_freg(lvalue, FReg::fa(0)); store_from_freg(inst, FReg::fa(0)); - } else { + } + else + { load_to_greg(lvalue, Reg::a(0)); store_from_greg(inst, Reg::a(0)); } break; } - // 如果没有找到当前翻译块,说明是 undef,无事可做 } - } else { + } + else + { break; } } } } -void CodeGen::load_to_greg(Value *val, const Reg ®) { +void CodeGen::load_to_greg(Value* val, const Reg& reg) +{ assert(val->get_type()->is_integer_type() || - val->get_type()->is_pointer_type()); - - if (auto *constant = dynamic_cast(val)) { - int32_t val = constant->get_value(); - if (IS_IMM_12(val)) { - append_inst(ADDI WORD, {reg.print(), "$zero", std::to_string(val)}); - } else { - load_large_int32(val, reg); + val->get_type()->is_pointer_type()); + + if (auto* constant = dynamic_cast(val)) + { + int32_t val1 = constant->get_value(); + if (IS_IMM_12(val1)) + { + append_inst(ADDI WORD, {reg.print(), "$zero", std::to_string(val1)}); } - } else if (auto *global = dynamic_cast(val)) { + else + { + load_large_int32(val1, reg); + } + } + else if (auto* global = dynamic_cast(val)) + { append_inst(LOAD_ADDR, {reg.print(), global->get_name()}); - } else { + } + else + { load_from_stack_to_greg(val, reg); } } -void CodeGen::load_large_int32(int32_t val, const Reg ®) { - int32_t high_20 = val >> 12; // si20 +void CodeGen::load_large_int32(int32_t val, const Reg& reg) +{ + int32_t high_20 = val >> 12; uint32_t low_12 = val & LOW_12_MASK; append_inst(LU12I_W, {reg.print(), std::to_string(high_20)}); append_inst(ORI, {reg.print(), reg.print(), std::to_string(low_12)}); } -void CodeGen::load_large_int64(int64_t val, const Reg ®) { +void CodeGen::load_large_int64(int64_t val, const Reg& reg) +{ auto low_32 = static_cast(val & LOW_32_MASK); load_large_int32(low_32, reg); auto high_32 = static_cast(val >> 32); - int32_t high_32_low_20 = (high_32 << 12) >> 12; // si20 - int32_t high_32_high_12 = high_32 >> 20; // si12 + int32_t high_32_low_20 = (high_32 << 12) >> 12; + int32_t high_32_high_12 = high_32 >> 20; append_inst(LU32I_D, {reg.print(), std::to_string(high_32_low_20)}); append_inst(LU52I_D, {reg.print(), reg.print(), std::to_string(high_32_high_12)}); } -void CodeGen::load_from_stack_to_greg(Value *val, const Reg ®) { +void CodeGen::load_from_stack_to_greg(Value* val, const Reg& reg) +{ auto offset = context.offset_map.at(val); auto offset_str = std::to_string(offset); - auto *type = val->get_type(); - if (IS_IMM_12(offset)) { - if (type->is_int1_type()) { + auto* type = val->get_type(); + if (IS_IMM_12(offset)) + { + if (type->is_int1_type()) + { append_inst(LOAD BYTE, {reg.print(), "$fp", offset_str}); - } else if (type->is_int32_type()) { + } + else if (type->is_int32_type()) + { append_inst(LOAD WORD, {reg.print(), "$fp", offset_str}); - } else { // Pointer + } + else + { append_inst(LOAD DOUBLE, {reg.print(), "$fp", offset_str}); } - } else { + } + else + { load_large_int64(offset, reg); append_inst(ADD DOUBLE, {reg.print(), "$fp", reg.print()}); - if (type->is_int1_type()) { + if (type->is_int1_type()) + { append_inst(LOAD BYTE, {reg.print(), reg.print(), "0"}); - } else if (type->is_int32_type()) { + } + else if (type->is_int32_type()) + { append_inst(LOAD WORD, {reg.print(), reg.print(), "0"}); - } else { // Pointer + } + else + { append_inst(LOAD DOUBLE, {reg.print(), reg.print(), "0"}); } } } -void CodeGen::store_from_greg(Value *val, const Reg ®) { +void CodeGen::store_from_greg(Value* val, const Reg& reg) +{ auto offset = context.offset_map.at(val); auto offset_str = std::to_string(offset); - auto *type = val->get_type(); - if (IS_IMM_12(offset)) { - if (type->is_int1_type()) { + auto* type = val->get_type(); + if (IS_IMM_12(offset)) + { + if (type->is_int1_type()) + { append_inst(STORE BYTE, {reg.print(), "$fp", offset_str}); - } else if (type->is_int32_type()) { + } + else if (type->is_int32_type()) + { append_inst(STORE WORD, {reg.print(), "$fp", offset_str}); - } else { // Pointer + } + else + { append_inst(STORE DOUBLE, {reg.print(), "$fp", offset_str}); } - } else { + } + else + { auto addr = Reg::t(8); load_large_int64(offset, addr); append_inst(ADD DOUBLE, {addr.print(), "$fp", addr.print()}); - if (type->is_int1_type()) { + if (type->is_int1_type()) + { append_inst(STORE BYTE, {reg.print(), addr.print(), "0"}); - } else if (type->is_int32_type()) { + } + else if (type->is_int32_type()) + { append_inst(STORE WORD, {reg.print(), addr.print(), "0"}); - } else { // Pointer + } + else + { append_inst(STORE DOUBLE, {reg.print(), addr.print(), "0"}); } } } -void CodeGen::load_to_freg(Value *val, const FReg &freg) { +void CodeGen::load_to_freg(Value* val, const FReg& freg) +{ assert(val->get_type()->is_float_type()); - if (auto *constant = dynamic_cast(val)) { - float val = constant->get_value(); - load_float_imm(val, freg); - } else { + if (auto* constant = dynamic_cast(val)) + { + float val1 = constant->get_value(); + load_float_imm(val1, freg); + } + else + { auto offset = context.offset_map.at(val); auto offset_str = std::to_string(offset); - if (IS_IMM_12(offset)) { + if (IS_IMM_12(offset)) + { append_inst(FLOAD SINGLE, {freg.print(), "$fp", offset_str}); - } else { + } + else + { auto addr = Reg::t(8); load_large_int64(offset, addr); append_inst(ADD DOUBLE, {addr.print(), "$fp", addr.print()}); @@ -185,18 +229,24 @@ void CodeGen::load_to_freg(Value *val, const FReg &freg) { } } -void CodeGen::load_float_imm(float val, const FReg &r) { - int32_t bytes = *reinterpret_cast(&val); +void CodeGen::load_float_imm(float val, const FReg& r) +{ + int32_t bytes = 0; + memcpy(&bytes, &val, sizeof(float)); load_large_int32(bytes, Reg::t(8)); append_inst(GR2FR WORD, {r.print(), Reg::t(8).print()}); } -void CodeGen::store_from_freg(Value *val, const FReg &r) { +void CodeGen::store_from_freg(Value* val, const FReg& r) +{ auto offset = context.offset_map.at(val); - if (IS_IMM_12(offset)) { + if (IS_IMM_12(offset)) + { auto offset_str = std::to_string(offset); append_inst(FSTORE SINGLE, {r.print(), "$fp", offset_str}); - } else { + } + else + { auto addr = Reg::t(8); load_large_int64(offset, addr); append_inst(ADD DOUBLE, {addr.print(), "$fp", addr.print()}); @@ -204,14 +254,18 @@ void CodeGen::store_from_freg(Value *val, const FReg &r) { } } -void CodeGen::gen_prologue() { - if (IS_IMM_12(-static_cast(context.frame_size))) { +void CodeGen::gen_prologue() +{ + if (IS_IMM_12(-static_cast(context.frame_size))) + { append_inst("st.d $ra, $sp, -8"); append_inst("st.d $fp, $sp, -16"); append_inst("addi.d $fp, $sp, 0"); append_inst("addi.d $sp, $sp, " + std::to_string(-static_cast(context.frame_size))); - } else { + } + else + { load_large_int64(context.frame_size, Reg::t(0)); append_inst("st.d $ra, $sp, -8"); append_inst("st.d $fp, $sp, -16"); @@ -221,177 +275,405 @@ void CodeGen::gen_prologue() { int garg_cnt = 0; int farg_cnt = 0; - for (auto arg : context.func->get_args()) { - if (arg->get_type()->is_float_type()) { + for (auto arg : context.func->get_args()) + { + if (arg->get_type()->is_float_type()) + { store_from_freg(arg, FReg::fa(farg_cnt++)); - } else { // int or pointer + } + else + { store_from_greg(arg, Reg::a(garg_cnt++)); } } } -void CodeGen::gen_epilogue() { - // TODO 根据你的理解设定函数的 epilogue - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_epilogue() +{ + append_inst(context.func->get_name() + "_exit", ASMInstruction::Label); + if (IS_IMM_12(-static_cast(context.frame_size))) + { + append_inst("addi.d $sp, $sp, " + std::to_string(static_cast(context.frame_size))); + append_inst("ld.d $ra, $sp, -8"); + append_inst("ld.d $fp, $sp, -16"); + append_inst("jr $ra"); + } + else + { + load_large_int64(context.frame_size, Reg::t(0)); + append_inst("add.d $sp, $sp, $t0"); + append_inst("ld.d $ra, $sp, -8"); + append_inst("ld.d $fp, $sp, -16"); + append_inst("jr $ra"); + } } -void CodeGen::gen_ret() { - // TODO 函数返回,思考如何处理返回值、寄存器备份,如何返回调用者地址 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_ret() +{ + auto* retInst = dynamic_cast(context.inst); + auto* retType = context.func->get_return_type(); + if (retType->is_void_type()) + { + append_inst("addi.w $a0, $zero, 0"); + } + else if (retType->is_float_type()) + { + load_to_freg(retInst->get_operand(0), FReg::fa(0)); + } + else + { + load_to_greg(retInst->get_operand(0), Reg::a(0)); + } + std::string label = context.func->get_name() + "_exit"; + append_inst("b " + label); } -void CodeGen::gen_br() { - auto *branchInst = static_cast(context.inst); - if (branchInst->is_cond_br()) { - // TODO 补全条件跳转的情况 - throw not_implemented_error{__FUNCTION__}; - } else { - auto *branchbb = static_cast(branchInst->get_operand(0)); - append_inst("b " + label_name(branchbb)); +void CodeGen::gen_br() +{ + auto* branchInst = dynamic_cast(context.inst); + if (branchInst->is_cond_br()) + { + load_to_greg(branchInst->get_operand(0), Reg::t(0)); + auto* trueBB = dynamic_cast(branchInst->get_operand(1)); + auto* falseBB = dynamic_cast(branchInst->get_operand(2)); + append_inst("bnez", {Reg::t(0).print(), trueBB->get_name()}); + append_inst("b", {falseBB->get_name()}); + } + else + { + auto* branchbb = dynamic_cast(branchInst->get_operand(0)); + append_inst("b " + branchbb->get_name()); } } -void CodeGen::gen_binary() { +void CodeGen::gen_binary() +{ load_to_greg(context.inst->get_operand(0), Reg::t(0)); load_to_greg(context.inst->get_operand(1), Reg::t(1)); - switch (context.inst->get_instr_type()) { - case Instruction::add: - append_inst("add.w $t2, $t0, $t1"); - break; - case Instruction::sub: - append_inst("sub.w $t2, $t0, $t1"); - break; - case Instruction::mul: - append_inst("mul.w $t2, $t0, $t1"); - break; - case Instruction::sdiv: - append_inst("div.w $t2, $t0, $t1"); - break; - default: - assert(false); + switch (context.inst->get_instr_type()) + { + case Instruction::add: + output.emplace_back("add.w $t2, $t0, $t1"); + break; + case Instruction::sub: + output.emplace_back("sub.w $t2, $t0, $t1"); + break; + case Instruction::mul: + output.emplace_back("mul.w $t2, $t0, $t1"); + break; + case Instruction::sdiv: + output.emplace_back("div.w $t2, $t0, $t1"); + break; + default: + assert(false); } store_from_greg(context.inst, Reg::t(2)); + if (context.inst->get_instr_type() == Instruction::mul) + { + load_to_greg(ConstantInt::get(1, m), Reg::a(0)); + load_to_greg(ConstantInt::get(1, m), Reg::a(1)); + append_inst("bl add_lab4_flag"); + } + if (context.inst->get_instr_type() == Instruction::sdiv) + { + load_to_greg(ConstantInt::get(1, m), Reg::a(0)); + load_to_greg(ConstantInt::get(4, m), Reg::a(1)); + append_inst("bl add_lab4_flag"); + } } -void CodeGen::gen_float_binary() { - // TODO 浮点类型的二元指令 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_float_binary() +{ + auto* floatInst = dynamic_cast(context.inst); + auto op = floatInst->get_instr_type(); + auto firstNum = floatInst->get_operand(0); + load_to_freg(firstNum, FReg::ft(1)); + auto secondNum = floatInst->get_operand(1); + load_to_freg(secondNum, FReg::ft(2)); + switch (op) + { + case Instruction::fadd: + append_inst("fadd.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()}); + break; + case Instruction::fsub: + append_inst("fsub.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()}); + break; + case Instruction::fmul: + append_inst("fmul.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()}); + break; + case Instruction::fdiv: + append_inst("fdiv.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()}); + break; + default: + std::cout << "wrong gen_float_binary\n"; + break; + } + store_from_freg(context.inst, FReg::ft(0)); + if (context.inst->get_instr_type() == Instruction::fmul) + { + load_to_greg(ConstantInt::get(1, m), Reg::a(0)); + load_to_greg(ConstantInt::get(1, m), Reg::a(1)); + append_inst("bl add_lab4_flag"); + } + if (context.inst->get_instr_type() == Instruction::fdiv) + { + load_to_greg(ConstantInt::get(1, m), Reg::a(0)); + load_to_greg(ConstantInt::get(4, m), Reg::a(1)); + append_inst("bl add_lab4_flag"); + } } -void CodeGen::gen_alloca() { - /* 我们已经为 alloca 的内容分配空间,在此我们还需保存 alloca - * 指令自身产生的定值,即指向 alloca 空间起始地址的指针 - */ - // TODO 将 alloca 出空间的起始地址保存在栈帧上 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_alloca() +{ + auto* allocaInst = dynamic_cast(context.inst); + auto offset = context.offset_map[allocaInst]; + auto trueOffset = offset - static_cast(allocaInst->get_alloca_type()->get_size()); + if (IS_IMM_12(trueOffset)) + append_inst("addi.d", {Reg::t(0).print(), "$fp", std::to_string(trueOffset)}); + else + { + load_to_greg(ConstantInt::get(trueOffset, m), Reg::t(1)); + append_inst("add.d", {Reg::t(0).print(), "$fp", Reg::t(1).print()}); + } + store_from_greg(allocaInst, Reg::t(0)); + load_to_greg(ConstantInt::get(0, m), Reg::a(0)); + load_to_greg(ConstantInt::get(static_cast(allocaInst->get_alloca_type()->get_size()), m), Reg::a(1)); + append_inst("bl add_lab4_flag"); } -void CodeGen::gen_load() { - auto *ptr = context.inst->get_operand(0); - auto *type = context.inst->get_type(); +void CodeGen::gen_load() +{ + auto* ptr = context.inst->get_operand(0); + auto* type = context.inst->get_type(); load_to_greg(ptr, Reg::t(0)); - if (type->is_float_type()) { + if (type->is_float_type()) + { append_inst("fld.s $ft0, $t0, 0"); store_from_freg(context.inst, FReg::ft(0)); - } else { - // TODO load 整数类型的数据 - throw not_implemented_error{__FUNCTION__}; } + else if (type->is_int32_type()) + { + append_inst("ld.w $t0, $t0, 0"); + store_from_greg(context.inst, Reg::t(0)); + } + else if (type->is_int1_type()) + { + append_inst("ld.b $t0, $t0, 0"); + store_from_greg(context.inst, Reg::t(0)); + } + else + { + append_inst("ld.d $t0, $t0, 0"); + store_from_greg(context.inst, Reg::t(0)); + } + if (ptr->is() && !((ptr->as())->get_alloca_type()->is_array_type())) return; + load_to_greg(ConstantInt::get(1, m), Reg::a(0)); + load_to_greg(ConstantInt::get(3, m), Reg::a(1)); + append_inst("bl add_lab4_flag"); } -void CodeGen::gen_store() { - // TODO 翻译 store 指令 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_store() +{ + auto* storeInst = dynamic_cast(context.inst); + auto addr = storeInst->get_operand(1); + auto value = storeInst->get_operand(0); + load_to_greg(addr, Reg::t(0)); + if (value->get_type()->is_float_type()) + { + load_to_freg(value, FReg::ft(0)); + append_inst("fst.s $ft0, $t0, 0"); + } + else if (value->get_type()->is_int32_type()) + { + load_to_greg(value, Reg::t(1)); + append_inst("st.w $t1, $t0, 0"); + } + else if (value->get_type()->is_int1_type()) + { + load_to_greg(value, Reg::t(1)); + append_inst("st.b $t1, $t0, 0"); + } + else + { + load_to_greg(value, Reg::t(1)); + append_inst("st.d $t1, $t0, 0"); + } } -void CodeGen::gen_icmp() { - // TODO 处理各种整数比较的情况 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_icmp() +{ + auto* icmpInst = dynamic_cast(context.inst); + auto op = icmpInst->get_instr_type(); + load_to_greg(icmpInst->get_operand(0), Reg::t(0)); + load_to_greg(icmpInst->get_operand(1), Reg::t(1)); + switch (op) + { + case Instruction::ge: + append_inst("slt $t0, $t0, $t1"); + append_inst("addi.d $t1, $zero, 1"); + append_inst("xor $t0, $t0, $t1"); + break; + case Instruction::gt: + append_inst("slt $t0, $t1, $t0"); + break; + case Instruction::le: + append_inst("slt $t0, $t1, $t0"); + append_inst("addi.d $t1, $zero, 1"); + append_inst("xor $t0, $t0, $t1"); + break; + case Instruction::lt: + append_inst("slt $t0, $t0, $t1"); + break; + case Instruction::eq: + append_inst("xor $t0, $t0, $t1"); + append_inst("sltu $t0, $zero, $t0"); + append_inst("addi.d $t1, $zero, 1"); + append_inst("xor $t0, $t0, $t1"); + break; + case Instruction::ne: + append_inst("xor $t0, $t0, $t1"); + append_inst("sltu $t0, $zero, $t0"); + break; + default: + std::cout << "wrong icmp\n"; + break; + } + store_from_greg(icmpInst, Reg::t(0)); } -void CodeGen::gen_fcmp() { - // TODO 处理各种浮点数比较的情况 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_fcmp() +{ + auto* fcmpInst = dynamic_cast(context.inst); + auto op = fcmpInst->get_instr_type(); + load_to_freg(fcmpInst->get_operand(0), FReg::ft(0)); + load_to_freg(fcmpInst->get_operand(1), FReg::ft(1)); + switch (op) + { + case Instruction::fge: + append_inst("fcmp.sle.s $fcc0, $ft1, $ft0"); + break; + case Instruction::fgt: + append_inst("fcmp.slt.s $fcc0, $ft1, $ft0"); + break; + case Instruction::fle: + append_inst("fcmp.sle.s $fcc0, $ft0, $ft1"); + break; + case Instruction::flt: + append_inst("fcmp.slt.s $fcc0, $ft0, $ft1"); + break; + case Instruction::feq: + append_inst("fcmp.seq.s $fcc0, $ft0, $ft1"); + break; + case Instruction::fne: + append_inst("fcmp.sne.s $fcc0, $ft0, $ft1"); + break; + default: + break; + } + append_inst("bceqz", {"$fcc0", "0XC"}); + append_inst("addi.w $t0, $zero, 1"); + append_inst("b 0x8"); + append_inst("addi.w $t0, $zero, 0"); + store_from_greg(context.inst, Reg::t(0)); } -void CodeGen::gen_zext() { - // TODO 将窄位宽的整数数据进行零扩展 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_zext() +{ + auto* zextInst = dynamic_cast(context.inst); + load_to_greg(zextInst->get_operand(0), Reg::t(0)); + append_inst("bstrpick.w $t0, $t0, 7, 0"); + store_from_greg(context.inst, Reg::t(0)); } -void CodeGen::gen_call() { - // TODO 函数调用,注意我们只需要通过寄存器传递参数,即不需考虑栈上传参的情况 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_call() +{ + auto* callInst = dynamic_cast(context.inst); + auto* functionType = static_cast(callInst->get_function_type()); + auto argsNum = functionType->get_num_of_args(); + unsigned int j = 0; + unsigned int k = 0; + for (unsigned int i = 0; i < argsNum; i++) + { + if (functionType->get_param_type(i)->is_float_type()) + { + load_to_freg(callInst->get_operand(i + 1), FReg::fa(k)); + k++; + } + else + { + load_to_greg(callInst->get_operand(i + 1), Reg::a(j)); + j++; + } + } + auto* func = dynamic_cast(callInst->get_operand(0)); + append_inst("bl", {func->get_name()}); + auto retType = functionType->get_return_type(); + if (retType->is_integer_type()) + { + store_from_greg(context.inst, Reg::a(0)); + } + else if (retType->is_float_type()) + { + store_from_freg(context.inst, FReg::fa(0)); + } } -/* - * %op = getelementptr [10 x i32], [10 x i32]* %op, i32 0, i32 %op - * %op = getelementptr i32, i32* %op, i32 %op - * - * Memory layout - * - ^ - * +-----------+ | Smaller address - * | arg ptr |---+ | - * +-----------+ | | - * | | | | - * +-----------+ / | - * | |<-- | - * | | \ | - * | | | | - * | Array | | | - * | | | | - * | | | | - * | | | | - * +-----------+ | | - * | Pointer |---+ | - * +-----------+ | - * | | | - * +-----------+ | - * | | | - * +-----------+ | - * | | | - * +-----------+ | Larger address - * + - */ -void CodeGen::gen_gep() { - // TODO 计算内存地址 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_gep() +{ + auto* getElementPtrInst = dynamic_cast(context.inst); + unsigned int num = getElementPtrInst->get_num_operand(); + load_to_greg(getElementPtrInst->get_operand(0), Reg::t(0)); + load_to_greg(getElementPtrInst->get_operand(num - 1), Reg::t(1)); + auto elementType = getElementPtrInst->get_element_type(); + append_inst("addi.d $t2, $zero, 4"); + if (elementType->is_float_type() || elementType->is_int32_type()) + { + append_inst("mul.d $t1, $t1, $t2"); + } + else + { + append_inst("addi.d $t2, $zero, 8"); + append_inst("mul.d $t1, $t1, $t2"); + } + append_inst("add.d $t2, $t1, $t0"); + store_from_greg(context.inst, Reg::t(2)); + load_to_greg(ConstantInt::get(1, m), Reg::a(0)); + load_to_greg(ConstantInt::get(1, m), Reg::a(1)); + append_inst("bl add_lab4_flag"); } -void CodeGen::gen_sitofp() { - // TODO 整数转向浮点数 - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_sitofp() +{ + auto* sitofpInst = dynamic_cast(context.inst); + load_to_greg(sitofpInst->get_operand(0), Reg::t(0)); + append_inst("movgr2fr.w $ft0, $t0"); + append_inst("ffint.s.w $ft1, $ft0"); + store_from_freg(context.inst, FReg::ft(1)); } -void CodeGen::gen_fptosi() { - // TODO 浮点数转向整数,注意向下取整(round to zero) - throw not_implemented_error{__FUNCTION__}; +void CodeGen::gen_fptosi() +{ + auto* fptosiInst = dynamic_cast(context.inst); + load_to_freg(fptosiInst->get_operand(0), FReg::ft(0)); + append_inst("ftintrz.w.s $ft1, $ft0"); + append_inst("movfr2gr.s $t0, $ft1"); + store_from_greg(context.inst, Reg::t(0)); } -void CodeGen::run() { - // 确保每个函数中基本块的名字都被设置好 +void CodeGen::run() +{ m->set_print_name(); - - /* 使用 GNU 伪指令为全局变量分配空间 - * 你可以使用 `la.local` 指令将标签 (全局变量) 的地址载入寄存器中, 比如 - * 要将 `a` 的地址载入 $t0, 只需要 `la.local $t0, a` - */ - if (!m->get_global_variable().empty()) { + if (!m->get_global_variable().empty()) + { append_inst("Global variables", ASMInstruction::Comment); - /* 虽然下面两条伪指令可以简化为一条 `.bss` 伪指令, 但是我们还是选择使用 - * `.section` 将全局变量放到可执行文件的 BSS 段, 原因如下: - * - 尽可能对齐交叉编译器 loongarch64-unknown-linux-gnu-gcc 的行为 - * - 支持更旧版本的 GNU 汇编器, 因为 `.bss` 伪指令是应该相对较新的指令, - * GNU 汇编器在 2023 年 2 月的 2.37 版本才将其引入 - */ append_inst(".text", ASMInstruction::Attribute); append_inst(".section", {".bss", "\"aw\"", "@nobits"}, ASMInstruction::Attribute); - for (auto global : m->get_global_variable()) { - auto size = - global->get_type()->get_pointer_element_type()->get_size(); + for (auto global : m->get_global_variable()) + { + auto size = global->get_type()->get_pointer_element_type()->get_size(); append_inst(".globl", {global->get_name()}, ASMInstruction::Attribute); append_inst(".type", {global->get_name(), "@object"}, @@ -404,114 +686,106 @@ void CodeGen::run() { } } - // 函数代码段 output.emplace_back(".text", ASMInstruction::Attribute); - for (auto func : m->get_functions()) { - if (not func->is_declaration()) { - // 更新 context + for (auto func : m->get_functions()) + { + if (not func->is_declaration()) + { context.clear(); context.func = func; - // 函数信息 append_inst(".globl", {func->get_name()}, ASMInstruction::Attribute); append_inst(".type", {func->get_name(), "@function"}, ASMInstruction::Attribute); append_inst(func->get_name(), ASMInstruction::Label); - // 分配函数栈帧 allocate(); - // 生成 prologue gen_prologue(); - for (auto bb : func->get_basic_blocks()) { + for (auto& bb : func->get_basic_blocks()) + { context.bb = bb; - append_inst(label_name(context.bb), ASMInstruction::Label); - for (auto instr : bb->get_instructions()) { - // For debug + append_inst(context.bb->get_name(), ASMInstruction::Label); + for (auto& instr : bb->get_instructions()) + { append_inst(instr->print(), ASMInstruction::Comment); - context.inst = instr; // 更新 context - switch (instr->get_instr_type()) { - case Instruction::ret: - gen_ret(); - break; - case Instruction::br: - copy_stmt(); - gen_br(); - break; - case Instruction::add: - case Instruction::sub: - case Instruction::mul: - case Instruction::sdiv: - gen_binary(); - break; - case Instruction::fadd: - case Instruction::fsub: - case Instruction::fmul: - case Instruction::fdiv: - gen_float_binary(); - break; - case Instruction::alloca: - /* 对于 alloca 指令,我们已经为 alloca - * 的内容分配空间,在此我们还需保存 alloca - * 指令自身产生的定值,即指向 alloca 空间起始地址的指针 - */ - gen_alloca(); - break; - case Instruction::load: - gen_load(); - break; - case Instruction::store: - gen_store(); - break; - case Instruction::ge: - case Instruction::gt: - case Instruction::le: - case Instruction::lt: - case Instruction::eq: - case Instruction::ne: - gen_icmp(); - break; - case Instruction::fge: - case Instruction::fgt: - case Instruction::fle: - case Instruction::flt: - case Instruction::feq: - case Instruction::fne: - gen_fcmp(); - break; - case Instruction::phi: - /* for phi, just convert to a series of - * copy-stmts */ - /* we can collect all phi and deal them at - * the end */ - break; - case Instruction::call: - gen_call(); - break; - case Instruction::getelementptr: - gen_gep(); - break; - case Instruction::zext: - gen_zext(); - break; - case Instruction::fptosi: - gen_fptosi(); - break; - case Instruction::sitofp: - gen_sitofp(); - break; + context.inst = instr; + switch (instr->get_instr_type()) + { + case Instruction::ret: + gen_ret(); + break; + case Instruction::br: + copy_stmt(); + gen_br(); + break; + case Instruction::add: + case Instruction::sub: + case Instruction::mul: + case Instruction::sdiv: + gen_binary(); + break; + case Instruction::fadd: + case Instruction::fsub: + case Instruction::fmul: + case Instruction::fdiv: + gen_float_binary(); + break; + case Instruction::alloca: + gen_alloca(); + break; + case Instruction::load: + gen_load(); + break; + case Instruction::store: + gen_store(); + break; + case Instruction::ge: + case Instruction::gt: + case Instruction::le: + case Instruction::lt: + case Instruction::eq: + case Instruction::ne: + gen_icmp(); + break; + case Instruction::fge: + case Instruction::fgt: + case Instruction::fle: + case Instruction::flt: + case Instruction::feq: + case Instruction::fne: + gen_fcmp(); + break; + case Instruction::phi: + break; + case Instruction::call: + gen_call(); + break; + case Instruction::getelementptr: + gen_gep(); + break; + case Instruction::zext: + gen_zext(); + break; + case Instruction::fptosi: + gen_fptosi(); + break; + case Instruction::sitofp: + gen_sitofp(); + break; } } } - // 生成 epilogue gen_epilogue(); } } } -std::string CodeGen::print() const { +std::string CodeGen::print() const +{ std::string result; - for (const auto& inst : output) { + for (const auto& inst : output) + { result += inst.format(); } return result; diff --git a/src/io/io.c b/src/io/io.c index 11b25c0..f424cdc 100755 --- a/src/io/io.c +++ b/src/io/io.c @@ -1,16 +1,63 @@ #include #include -int input() { +#include + +struct timeval time_start, time_end; +int end_set; +int flags[2]; + +int input(void) +{ int a; scanf("%d", &a); + gettimeofday(&time_start, NULL); + end_set = 0; return a; } -void output(int a) { printf("%d\n", a); } +void output(int a) +{ + if (end_set == 0) + { + gettimeofday(&time_end, NULL); + end_set = 1; + } + printf("%d\n", a); +} + +void outputFloat(float a) +{ + if (end_set == 0) + { + gettimeofday(&time_end, NULL); + end_set = 1; + } + printf("%f\n", a); +} + +void add_lab4_flag(int idx, int val) +{ + flags[idx] += val; +} -void outputFloat(float a) { printf("%f\n", a); } +__attribute((constructor)) void before_main(void) +{ + flags[0] = 0; + flags[1] = 0; + end_set = 0; + gettimeofday(&time_start, NULL); +} -void neg_idx_except() { - printf("negative index exception\n"); - exit(0); +__attribute((destructor)) void after_main(void) +{ + long time_us = 0; + fprintf(stderr, "Allocate Size (bytes):\n%d\n", flags[0]); + fprintf(stderr, "Execute Cost:\n%d\n", flags[1]); + if (end_set == 0) + { + gettimeofday(&time_end, NULL); + } + time_us += 1000000L * (time_end.tv_sec - time_start.tv_sec) + + time_end.tv_usec - time_start.tv_usec; + fprintf(stderr, "Take Times (us):\n%ld\n", time_us); } diff --git a/src/io/io.h b/src/io/io.h index 0b04739..42e3f2e 100755 --- a/src/io/io.h +++ b/src/io/io.h @@ -1,7 +1,7 @@ -int input(); +int input(void); void output(int a); void outputFloat(float a); -void neg_idx_except(); +void add_lab4_flag(int idx, int val); \ No newline at end of file diff --git a/src/lightir/BasicBlock.cpp b/src/lightir/BasicBlock.cpp index 6804d37..9da2da4 100755 --- a/src/lightir/BasicBlock.cpp +++ b/src/lightir/BasicBlock.cpp @@ -11,14 +11,14 @@ BasicBlock::BasicBlock(const Module* m, const std::string& name = "", Function* parent = nullptr) - : Value(m->get_label_type(), -parent == nullptr ? GLOBAL_BASICBLOCK_NAMES_.get_name(name) : parent->names4blocks_.get_name(name)) -, parent_(parent) { + : Value(m->get_label_type(), + parent == nullptr ? GLOBAL_BASICBLOCK_NAMES_.get_name(name) : parent->names4blocks_.get_name(name)) + , parent_(parent) { assert(parent && "currently parent should not be nullptr"); parent_->add_basic_block(this); } -Module *BasicBlock::get_module() const { return get_parent()->get_parent(); } +Module* BasicBlock::get_module() const { return get_parent()->get_parent(); } void BasicBlock::erase_from_parent() { this->get_parent()->remove(this); } bool BasicBlock::is_terminated() const { @@ -34,18 +34,18 @@ bool BasicBlock::is_terminated() const { } } -Instruction *BasicBlock::get_terminator() const +Instruction* BasicBlock::get_terminator() const { assert(is_terminated() && - "Trying to get terminator from an bb which is not terminated"); + "Trying to get terminator from an bb which is not terminated"); return instr_list_.back(); } -void BasicBlock::add_instruction(Instruction *instr) { +void BasicBlock::add_instruction(Instruction* instr) { if (instr->is_alloca() || instr->is_phi()) { auto it = instr_list_.begin(); - for (; it != instr_list_.end() && ((*it)->is_alloca() || (*it)->is_phi()); ++it){} + for (; it != instr_list_.end() && ((*it)->is_alloca() || (*it)->is_phi()); ++it) {} instr_list_.emplace(it, instr); return; } @@ -146,4 +146,4 @@ BasicBlock* BasicBlock::get_entry_block_of_same_function() const return parent_->get_entry_block(); } -Names GLOBAL_BASICBLOCK_NAMES_{"label", "_"}; \ No newline at end of file +Names GLOBAL_BASICBLOCK_NAMES_{ "label", "_" }; \ No newline at end of file diff --git a/src/lightir/Function.cpp b/src/lightir/Function.cpp index 00ce82e..27ad36a 100755 --- a/src/lightir/Function.cpp +++ b/src/lightir/Function.cpp @@ -7,17 +7,8 @@ #include #include -namespace -{ - std::string chopName(std::string name) - { - if (name.size() > 3) return { name.begin(), name.begin() + 3 }; - return name; - } -} - Function::Function(FunctionType* ty, const std::string& name, Module* parent) - : Value(ty, name), names4blocks_("label", chopName(name) + "_"), names4insts_("op", ""), parent_(parent), seq_cnt_(0) { + : Value(ty, name), names4blocks_("", name + "_"), names4insts_("op", ""), parent_(parent), seq_cnt_(0) { // num_args_ = ty->getNumParams(); parent->add_function(this); // build args diff --git a/src/lightir/IRprinter.cpp b/src/lightir/IRprinter.cpp index 23a1012..a77a176 100755 --- a/src/lightir/IRprinter.cpp +++ b/src/lightir/IRprinter.cpp @@ -382,7 +382,7 @@ std::string Instruction::safe_print() const } else { - if (auto fty = dynamic_cast(ty)) + if (auto fty = dynamic_cast(op0->get_type())) { auto ty3 = fty->get_return_type(); if (ty3 == nullptr) diff --git a/src/lightir/Instruction.cpp b/src/lightir/Instruction.cpp index 22ef75b..a8e5663 100755 --- a/src/lightir/Instruction.cpp +++ b/src/lightir/Instruction.cpp @@ -197,6 +197,23 @@ BranchInst *BranchInst::create_br(BasicBlock *if_true, BasicBlock *bb) { return new BranchInst(nullptr, if_true, nullptr, bb); } +void BranchInst::replace_all_bb_match(BasicBlock* need_replace, BasicBlock* replace_to) +{ + if (need_replace == nullptr || replace_to == nullptr || need_replace == replace_to) return; + int size = static_cast(get_operands().size()); + for (int i = 0; i < size; i ++) + { + auto op = get_operand(i); + if (op == need_replace) set_operand(i, replace_to); + } + auto parent = get_parent(); + if (parent == nullptr) return; + parent->remove_succ_basic_block(need_replace); + need_replace->remove_pre_basic_block(parent); + parent->add_succ_basic_block(replace_to); + replace_to->add_pre_basic_block(parent); +} + ReturnInst::ReturnInst(Value *val, BasicBlock *bb) : Instruction(bb->get_module()->get_void_type(), ret, "", bb) { if (val == nullptr) { @@ -352,14 +369,13 @@ SiToFpInst *SiToFpInst::create_sitofp(Value *val, BasicBlock *bb, const std::str PhiInst::PhiInst(Type *ty, const std::vector& vals, const std::vector& val_bbs, BasicBlock *bb, const std::string& name) - : Instruction(ty, phi, name) { + : Instruction(ty, phi, name, bb) { assert(vals.size() == val_bbs.size() && "Unmatched vals and bbs"); for (unsigned i = 0; i < vals.size(); i++) { assert(ty == vals[i]->get_type() && "Bad type for phi"); add_operand(vals[i]); add_operand(val_bbs[i]); } - this->set_parent(bb); } PhiInst *PhiInst::create_phi(Type *ty, BasicBlock *bb, diff --git a/src/lightir/Module.cpp b/src/lightir/Module.cpp index 944d308..cb667e2 100755 --- a/src/lightir/Module.cpp +++ b/src/lightir/Module.cpp @@ -27,48 +27,48 @@ Module::~Module() for (auto i : global_list_) delete i; } -Type *Module::get_void_type() const { return void_ty_; } -Type *Module::get_label_type() const { return label_ty_; } -IntegerType *Module::get_int1_type() const { return int1_ty_; } -IntegerType *Module::get_int32_type() const { return int32_ty_; } -FloatType *Module::get_float_type() const { return float32_ty_; } -PointerType *Module::get_int32_ptr_type() { +Type* Module::get_void_type() const { return void_ty_; } +Type* Module::get_label_type() const { return label_ty_; } +IntegerType* Module::get_int1_type() const { return int1_ty_; } +IntegerType* Module::get_int32_type() const { return int32_ty_; } +FloatType* Module::get_float_type() const { return float32_ty_; } +PointerType* Module::get_int32_ptr_type() { return get_pointer_type(int32_ty_); } -PointerType *Module::get_float_ptr_type() { +PointerType* Module::get_float_ptr_type() { return get_pointer_type(float32_ty_); } -PointerType *Module::get_pointer_type(Type *contained) { +PointerType* Module::get_pointer_type(Type* contained) { if (pointer_map_.find(contained) == pointer_map_.end()) { pointer_map_[contained] = new PointerType(contained); } return pointer_map_[contained]; } -ArrayType *Module::get_array_type(Type *contained, unsigned num_elements) { - if (array_map_.find({contained, num_elements}) == array_map_.end()) { +ArrayType* Module::get_array_type(Type* contained, unsigned num_elements) { + if (array_map_.find({ contained, num_elements }) == array_map_.end()) { array_map_[{contained, num_elements}] = new ArrayType(contained, num_elements); } return array_map_[{contained, num_elements}]; } -FunctionType *Module::get_function_type(Type *retty, - std::vector &args) { - if (not function_map_.count({retty, args})) { +FunctionType* Module::get_function_type(Type* retty, + std::vector& args) { + if (not function_map_.count({ retty, args })) { function_map_[{retty, args}] = new FunctionType(retty, args); } return function_map_[{retty, args}]; } -void Module::add_function(Function *f) { function_list_.push_back(f); } -std::list &Module::get_functions() { return function_list_; } -void Module::add_global_variable(GlobalVariable *g) { +void Module::add_function(Function* f) { function_list_.push_back(f); } +std::list& Module::get_functions() { return function_list_; } +void Module::add_global_variable(GlobalVariable* g) { global_list_.push_back(g); } -std::list &Module::get_global_variable() { +std::list& Module::get_global_variable() { return global_list_; } diff --git a/src/lightir/User.cpp b/src/lightir/User.cpp index 8c531e2..420ccdf 100755 --- a/src/lightir/User.cpp +++ b/src/lightir/User.cpp @@ -42,6 +42,6 @@ void User::remove_operand(unsigned idx) { } // remove the designated operand if (operands_[idx]) - operands_[idx]->remove_use(this, idx); + operands_[idx]->remove_use(this, idx); operands_.erase(operands_.begin() + idx); } diff --git a/src/passes/DeadCode.cpp b/src/passes/DeadCode.cpp index ed753df..c11ee78 100644 --- a/src/passes/DeadCode.cpp +++ b/src/passes/DeadCode.cpp @@ -10,11 +10,12 @@ // 处理流程:两趟处理,mark 标记有用变量,sweep 删除无用指令 void DeadCode::run() { bool changed; - func_info = new FuncInfo(m_); + func_info = new FuncInfo(m_); func_info->run(); do { changed = false; for (auto func : m_->get_functions()) { + if (func->is_declaration()) continue; if (remove_bb_) changed |= clear_basic_blocks(func); mark(func); changed |= sweep(func); @@ -133,7 +134,7 @@ bool DeadCode::sweep(Function *func) { for (auto bb : func->get_basic_blocks()) { for (auto inst : bb->get_instructions()) { if (marked[inst]) continue; - wait_del.emplace(inst); + wait_del.emplace(inst); } bb->get_instructions().remove_if([&wait_del](Instruction* i) -> bool {return wait_del.count(i); }); if (!wait_del.empty()) rm = true; @@ -172,12 +173,12 @@ void DeadCode::sweep_globally() const { // changed |= unused_funcs.size() or unused_globals.size(); for (auto func : unused_funcs) { - m_->get_functions().remove(func); - delete func; - } + m_->get_functions().remove(func); + delete func; + } for (auto glob : unused_globals) { - m_->get_global_variable().remove(glob); - delete glob; - } + m_->get_global_variable().remove(glob); + delete glob; + } } diff --git a/src/passes/Dominators.cpp b/src/passes/Dominators.cpp index 232f24c..6a7d4cc 100644 --- a/src/passes/Dominators.cpp +++ b/src/passes/Dominators.cpp @@ -62,13 +62,13 @@ void Dominators::run() { * 该函数使用后序号来查找两个节点的最近公共支配者。 * 通过在支配树上向上遍历直到找到交点。 */ -BasicBlock *Dominators::intersect(BasicBlock *b1, const BasicBlock *b2) const +BasicBlock *Dominators::intersect(BasicBlock *b1, BasicBlock *b2) const { while (b1 != b2) { - while (get_post_order(b1) < get_post_order(b2)) { + while (get_reversed_post_order(b1) > get_reversed_post_order(b2)) { b1 = get_idom(b1); } - while (get_post_order(b2) < get_post_order(b1)) { + while (get_reversed_post_order(b2) > get_reversed_post_order(b1)) { b2 = get_idom(b2); } } @@ -84,6 +84,12 @@ BasicBlock *Dominators::intersect(BasicBlock *b1, const BasicBlock *b2) const void Dominators::create_reverse_post_order() { std::set visited; dfs(f_->get_entry_block(), visited); + int size = static_cast(reversed_post_order_vec_.size()) - 1; + for (auto& it : reversed_post_order_) + { + it.second = size - it.second; + reversed_post_order_vec_[it.second] = it.first; + } } /** @@ -100,8 +106,8 @@ void Dominators::dfs(BasicBlock *bb, std::set &visited) { dfs(succ, visited); } } - post_order_vec_.push_back(bb); - post_order_.insert({bb, post_order_.size()}); + reversed_post_order_vec_.push_back(bb); + reversed_post_order_.insert({bb, reversed_post_order_.size()}); } /** @@ -111,38 +117,28 @@ void Dominators::dfs(BasicBlock *bb, std::set &visited) { */ void Dominators::create_idom() { // 可能有用的数据结构 - // post_order_vec_ vector, 其中基本块按照逆后续排列 - // post_order_ map, 每个基本块在 post_order_vec_ 中的索引 - // 可能有用的函数 + // reversed_post_order_vec_ vector, 其中基本块按照逆后续排列 + // reversed_post_order_ map, 每个基本块在 reversed_post_order_vec_ 中的索引 + // 可能有用的函数 // intersect(BasicBlock *, BasicBlock *) 寻找两个基本块在支配树上的最近祖先 - // 需要填写 + // 需要填写 // idom_ map 直接支配者, idom_[a] = b 代表 b 直接支配 a (或者 idom_[a] = a 代表 a 没有直接支配者) - // 代表基本块的直接支配者是否已经确定, 不会在迭代中改变 (默认初始化为 false) - bool* already_determined = new bool[post_order_vec_.size()]{}; + int bb_count = static_cast(reversed_post_order_vec_.size()); - int bb_count = static_cast(post_order_vec_.size()); - for (int i = 0; i < bb_count; i++) - { - auto bb = post_order_vec_[i]; - // TODO 填写可以直接确定直接支配者, 无需参与迭代的基本块的 idom_ 和 already_determined - throw "Unimplemented create_idom"; - } + idom_[reversed_post_order_vec_[0]] = reversed_post_order_vec_[0]; bool changed; do { changed = false; - for (int i = 0; i < bb_count; i++) + for (int i = 1; i < bb_count; i++) { - if (already_determined[i]) continue; - auto bb = post_order_vec_[i]; + auto bb = reversed_post_order_vec_[i]; // TODO 更新基本块 bb 的 idom_, 设置 changed(如果 idom_ 有变化) - throw "Unimplemented create_idom"; + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); } } while (changed); - - delete[] already_determined; } /** @@ -154,19 +150,19 @@ void Dominators::create_idom() { */ void Dominators::create_dominance_frontier() { // 可能有用的数据结构 - // post_order_vec_ vector, 其中基本块按照逆后续排列 - // post_order_ map, 每个基本块在 post_order_vec_ 中的索引 - // idom_ map 直接支配者, idom_[a] = b 代表 b 直接支配 a, 支配树中 b 是 a 的 parent - // (或者 idom_[a] = a 代表 a 没有直接支配者) + // reversed_post_order_vec_ vector, 其中基本块按照逆后续排列 + // reversed_post_order_ map, 每个基本块在 reversed_post_order_vec_ 中的索引 + // idom_ map 直接支配者, idom_[a] = b 代表 b 直接支配 a, 支配树中 b 是 a 的 parent + // (或者 idom_[a] = a 代表 a 没有直接支配者) // 需要填写 // dom_frontier_ map> 支配边界 - int bb_count = static_cast(post_order_vec_.size()); + int bb_count = static_cast(reversed_post_order_vec_.size()); for (int i = 0; i < bb_count; i++) { - auto bb = post_order_vec_[i]; + auto bb = reversed_post_order_vec_[i]; // TODO 计算 bb 的支配边界集合, 填入 dom_frontier_ - throw "Unimplemented create_dominance_frontier"; + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); } } @@ -178,12 +174,13 @@ void Dominators::create_dominance_frontier() { */ void Dominators::create_dom_tree_succ() { // 可能有用的数据结构 - // idom_ map 直接支配者, idom_[a] = b 代表 b 直接支配 a, 支配树中 b 是 a 的 parent + // idom_ map 直接支配者, idom_[a] = b 代表 b 直接支配 a, 支配树中 b 是 a 的 parent // 需要填写 // dom_tree_succ_blocks_ map> 支配树中后继(孩子)节点 // TODO 分析得到 f_ 中各个基本块的支配树后继 - throw "Unimplemented create_dom_tree_succ"; + // 注意如果 idom_[n] = n, 这意味着 n 没有直接支配者,因此 n 的后继中没有 n + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); } /** diff --git a/src/passes/FuncInfo.cpp b/src/passes/FuncInfo.cpp index 501526d..79034ba 100644 --- a/src/passes/FuncInfo.cpp +++ b/src/passes/FuncInfo.cpp @@ -8,251 +8,266 @@ void FuncInfo::UseMessage::add(Value* val) { - auto g = dynamic_cast(val); - if (g != nullptr) - { - globals_.emplace(g); - return; - } - auto arg = dynamic_cast(val); - arguments_.emplace(arg); + auto g = dynamic_cast(val); + if (g != nullptr) + { + globals_.emplace(g); + return; + } + auto arg = dynamic_cast(val); + arguments_.emplace(arg); } bool FuncInfo::UseMessage::have(Value* val) const { - auto g = dynamic_cast(val); - if (g != nullptr) return globals_.count(g); - return arguments_.count(dynamic_cast(val)); + auto g = dynamic_cast(val); + if (g != nullptr) return globals_.count(g); + return arguments_.count(dynamic_cast(val)); } bool FuncInfo::UseMessage::empty() const { - return globals_.empty() && arguments_.empty(); + return globals_.empty() && arguments_.empty(); } void FuncInfo::run() { - std::unordered_map val_to_var; - // 计算对全局变量的 load/store - for (auto glob : m_->get_global_variable()) - cal_val_2_var(glob, val_to_var); - std::queue worklist; - std::unordered_set in_worklist; - // 计算对函数参数的 load/store - // 本质上来说,对局部变量的 load/store 不会在函数外产生副作用,它也不会被用作 func_info 的信息 - for (auto func : m_->get_functions()) - { - if (func->is_declaration()) continue; - use_libs[func] = false; - worklist.emplace(func); - in_worklist.emplace(func); - for (auto arg : func->get_args()) - { - if (arg->get_type()->is_pointer_type()) - { - cal_val_2_var(arg, val_to_var); - } - } - } + std::unordered_map val_to_var; + // 计算对全局变量的 load/store + for (auto glob : m_->get_global_variable()) + cal_val_2_var(glob, val_to_var); + std::queue worklist; + std::unordered_set in_worklist; + // 计算对函数参数的 load/store + // 本质上来说,对局部变量的 load/store 不会在函数外产生副作用,它也不会被用作 func_info 的信息 + for (auto func : m_->get_functions()) + { + if (func->is_declaration()) continue; + use_libs[func] = false; + worklist.emplace(func); + in_worklist.emplace(func); + for (auto arg : func->get_args()) + { + if (arg->get_type()->is_pointer_type()) + { + cal_val_2_var(arg, val_to_var); + } + } + } - // 处理函数相互调用导致的隐式 load/store - while (!worklist.empty()) - { - // 被调用的函数 - auto calleeF = worklist.front(); - worklist.pop(); - in_worklist.erase(calleeF); - for (auto& use : calleeF->get_use_list()) - { - // 函数调用指令 - auto inst = dynamic_cast(use.val_); - // 调用 f 的函数 - auto callerF = inst->get_parent()->get_parent(); - auto& callerLoads = loads[callerF]; - auto& calleeLoads = loads[calleeF]; - auto& callerStores = stores[callerF]; - auto& calleeStores = stores[calleeF]; - // caller 的 load store 数量 - auto caller_old_load_store_count = callerLoads.globals_.size() + callerLoads.arguments_.size() + - callerStores.globals_.size() + callerStores.arguments_.size(); - // caller 同时 load 了 callee load 的全局变量 - for (auto i : calleeLoads.globals_) callerLoads.globals_.emplace(i); - // caller 同时 store 了 callee store 的全局变量 - for (auto i : calleeStores.globals_) callerStores.globals_.emplace(i); - auto& ops = inst->get_operands(); - // 形式参数 - for (auto calleArg : calleeF->get_args()) - { - if (calleArg->get_type()->is_pointer_type()) - { - // 传入的实参 - auto trueArg = ops[calleArg->get_arg_no() + 1]; - // 实参来自哪个临时变量 - auto trueArgVar = val_to_var.find(trueArg); - // 来自局部变量,跳过,因为对局部变量的 load/store 不会在函数外产生副作用 - if (trueArgVar == val_to_var.end()) continue; - auto trace = trueArgVar->second; - // 添加 load - if (calleeLoads.have(calleArg)) callerLoads.add(trace); - // 添加 store - if (calleeStores.have(calleArg)) callerStores.add(trace); - } - } - // caller f 的 load/store 产生了变化,可能会影响其它调用 f 的函数的 load/store - if (callerLoads.globals_.size() + callerLoads.arguments_.size() + callerStores.globals_.size() + - callerStores.arguments_.size() != caller_old_load_store_count) - { - if (!in_worklist.count(callerF)) - { - in_worklist.emplace(callerF); - worklist.emplace(callerF); - } - } - } - } + // 处理函数相互调用导致的隐式 load/store + while (!worklist.empty()) + { + // 被调用的函数 + auto calleeF = worklist.front(); + worklist.pop(); + in_worklist.erase(calleeF); + for (auto& use : calleeF->get_use_list()) + { + // 函数调用指令 + auto inst = dynamic_cast(use.val_); + // 调用 f 的函数 + auto callerF = inst->get_parent()->get_parent(); + auto& callerLoads = loads[callerF]; + auto& calleeLoads = loads[calleeF]; + auto& callerStores = stores[callerF]; + auto& calleeStores = stores[calleeF]; + // caller 的 load store 数量 + auto caller_old_load_store_count = callerLoads.globals_.size() + callerLoads.arguments_.size() + + callerStores.globals_.size() + callerStores.arguments_.size(); + // caller 同时 load 了 callee load 的全局变量 + for (auto i : calleeLoads.globals_) callerLoads.globals_.emplace(i); + // caller 同时 store 了 callee store 的全局变量 + for (auto i : calleeStores.globals_) callerStores.globals_.emplace(i); + auto& ops = inst->get_operands(); + // 形式参数 + for (auto calleArg : calleeF->get_args()) + { + if (calleArg->get_type()->is_pointer_type()) + { + // 传入的实参 + auto trueArg = ops[calleArg->get_arg_no() + 1]; + // 实参来自哪个临时变量 + auto trueArgVar = val_to_var.find(trueArg); + // 来自局部变量,跳过,因为对局部变量的 load/store 不会在函数外产生副作用 + if (trueArgVar == val_to_var.end()) continue; + auto trace = trueArgVar->second; + // 添加 load + if (calleeLoads.have(calleArg)) callerLoads.add(trace); + // 添加 store + if (calleeStores.have(calleArg)) callerStores.add(trace); + } + } + // caller f 的 load/store 产生了变化,可能会影响其它调用 f 的函数的 load/store + if (callerLoads.globals_.size() + callerLoads.arguments_.size() + callerStores.globals_.size() + + callerStores.arguments_.size() != caller_old_load_store_count) + { + if (!in_worklist.count(callerF)) + { + in_worklist.emplace(callerF); + worklist.emplace(callerF); + } + } + } + } - // 没有 load/store,但是调用了库函数的函数是非纯函数(因为库函数是 IO) - for (auto& func : m_->get_functions()) - { - if (func->is_declaration()) - { - for (auto& use : func->get_use_list()) - { - auto call = dynamic_cast(use.val_); - use_libs[call->get_parent()->get_parent()] = true; - } - } - worklist.emplace(func); - in_worklist.emplace(func); - } - while (!worklist.empty()) - { - auto f = worklist.front(); - worklist.pop(); - in_worklist.erase(f); - if (use_libs[f]) - for (auto& use : f->get_use_list()) - { - auto inst = dynamic_cast(use.val_); - auto cf = inst->get_parent()->get_parent(); - if (!use_libs[cf]) - { - use_libs[cf] = true; - if (!in_worklist.count(cf)) - { - in_worklist.emplace(cf); - worklist.emplace(cf); - } - } - } - } - log(); + // 没有 load/store,但是调用了库函数的函数是非纯函数(因为库函数是 IO) + for (auto& func : m_->get_functions()) + { + if (func->is_declaration()) + { + for (auto& use : func->get_use_list()) + { + auto call = dynamic_cast(use.val_); + use_libs[call->get_parent()->get_parent()] = true; + } + } + worklist.emplace(func); + in_worklist.emplace(func); + } + while (!worklist.empty()) + { + auto f = worklist.front(); + worklist.pop(); + in_worklist.erase(f); + if (use_libs[f]) + for (auto& use : f->get_use_list()) + { + auto inst = dynamic_cast(use.val_); + auto cf = inst->get_parent()->get_parent(); + if (!use_libs[cf]) + { + use_libs[cf] = true; + if (!in_worklist.count(cf)) + { + in_worklist.emplace(cf); + worklist.emplace(cf); + } + } + } + } + log(); } Value* FuncInfo::store_ptr(const StoreInst* st) { - return trace_ptr(st->get_operand(1)); + return trace_ptr(st->get_operand(1)); } Value* FuncInfo::load_ptr(const LoadInst* ld) { - return trace_ptr(ld->get_operand(0)); + return trace_ptr(ld->get_operand(0)); } std::unordered_set FuncInfo::get_stores(const CallInst* call) { - auto func = call->get_operand(0)->as(); - if (func->is_declaration()) return {}; - std::unordered_set ret; - for (auto i : stores[func].globals_) ret.emplace(i); - for (auto arg : stores[func].arguments_) - { - int arg_no = static_cast(arg->get_arg_no()); - auto in = call->get_operand(arg_no + 1); - ret.emplace(trace_ptr(in)); - } - return ret; + auto func = call->get_operand(0)->as(); + if (func->is_declaration()) return {}; + std::unordered_set ret; + for (auto i : stores[func].globals_) ret.emplace(i); + for (auto arg : stores[func].arguments_) + { + int arg_no = static_cast(arg->get_arg_no()); + auto in = call->get_operand(arg_no + 1); + ret.emplace(trace_ptr(in)); + } + return ret; +} + +std::unordered_set FuncInfo::get_loads(const CallInst* call) +{ + auto func = call->get_operand(0)->as(); + if (func->is_declaration()) return {}; + std::unordered_set ret; + for (auto i : loads[func].globals_) ret.emplace(i); + for (auto arg : loads[func].arguments_) + { + int arg_no = static_cast(arg->get_arg_no()); + auto in = call->get_operand(arg_no + 1); + ret.emplace(trace_ptr(in)); + } + return ret; } void FuncInfo::log() const { - for (auto it : use_libs) - { - LOG_INFO << it.first->get_name() << " is pure? " << it.second; - } + for (auto it : use_libs) + { + LOG_INFO << it.first->get_name() << " is pure? " << it.second; + } } void FuncInfo::cal_val_2_var(Value* var, std::unordered_map& val_2_var) { - auto global = dynamic_cast(var); - if (global != nullptr && global->is_const()) return; - std::unordered_set handled; - std::queue wait_to_handle; - handled.emplace(var); - val_2_var[var] = var; - wait_to_handle.emplace(var); - while (!wait_to_handle.empty()) - { - Value* v = wait_to_handle.front(); - wait_to_handle.pop(); - for (auto& use : v->get_use_list()) - { - auto inst = dynamic_cast(use.val_); - auto f = inst->get_parent()->get_parent(); - switch (inst->get_instr_type()) - { - case Instruction::load: - { - loads[f].add(var); - break; - } - case Instruction::store: - { - stores[f].add(var); - break; - } - case Instruction::getelementptr: - case Instruction::phi: - { - if (!handled.count(inst)) - { - handled.emplace(inst); - val_2_var[inst] = var; - wait_to_handle.emplace(inst); - } - break; - } - // case Instruction::call: - // { - // 如果遇到不能确定行为的库函数,需要指定其其对参数同时进行了读写,目前这里遇不到 - // auto callF = dynamic_cast(inst->get_operand(0)); - // if (callF->is_declaration()) - // { - // stores[f].add(var); - // loads[f].add(var); - // } - // break; - // } - default: - break; - } - } - } + auto global = dynamic_cast(var); + if (global != nullptr && global->is_const()) return; + std::unordered_set handled; + std::queue wait_to_handle; + handled.emplace(var); + val_2_var[var] = var; + wait_to_handle.emplace(var); + while (!wait_to_handle.empty()) + { + Value* v = wait_to_handle.front(); + wait_to_handle.pop(); + for (auto& use : v->get_use_list()) + { + auto inst = dynamic_cast(use.val_); + auto f = inst->get_parent()->get_parent(); + switch (inst->get_instr_type()) + { + case Instruction::load: + { + loads[f].add(var); + break; + } + case Instruction::store: + { + stores[f].add(var); + break; + } + case Instruction::getelementptr: + case Instruction::phi: + { + if (!handled.count(inst)) + { + handled.emplace(inst); + val_2_var[inst] = var; + wait_to_handle.emplace(inst); + } + break; + } + // case Instruction::call: + // { + // 如果遇到不能确定行为的库函数,需要指定其其对参数同时进行了读写,目前这里遇不到 + // auto callF = dynamic_cast(inst->get_operand(0)); + // if (callF->is_declaration()) + // { + // stores[f].add(var); + // loads[f].add(var); + // } + // break; + // } + default: + break; + } + } + } } Value* FuncInfo::trace_ptr(Value* val) { - assert(val != nullptr); - if (dynamic_cast(val) != nullptr - || dynamic_cast(val) != nullptr - || dynamic_cast(val) != nullptr - ) - return val; - auto inst = dynamic_cast(val); - assert(inst != nullptr); - if (inst->is_gep()) return trace_ptr(inst->get_operand(0)); - // 这意味着栈里面存在指针,你需要运行 Mem2Reg;或者你给 trace_ptr 传入了非指针参数 - assert(!inst->is_load()); - assert(inst->is_alloca()); - return inst; + assert(val != nullptr); + if (dynamic_cast(val) != nullptr + || dynamic_cast(val) != nullptr + || dynamic_cast(val) != nullptr + ) + return val; + auto inst = dynamic_cast(val); + assert(inst != nullptr); + if (inst->is_gep()) return trace_ptr(inst->get_operand(0)); + // 这意味着栈里面存在指针,你需要运行 Mem2Reg;或者你给 trace_ptr 传入了非指针参数 + assert(!inst->is_load()); + assert(inst->is_alloca()); + return inst; } diff --git a/src/passes/LICM.cpp b/src/passes/LICM.cpp index 87f0f81..0e520ae 100644 --- a/src/passes/LICM.cpp +++ b/src/passes/LICM.cpp @@ -14,23 +14,23 @@ */ void LoopInvariantCodeMotion::run() { - func_info_ = new FuncInfo(m_); - func_info_->run(); - for (auto func : m_->get_functions()) - { - if (func->is_declaration()) continue; - loop_detection_ = new LoopDetection(func); - loop_detection_->run(); - for (auto loop : loop_detection_->get_loops()) - { - // 遍历处理顶层循环 - if (loop->get_parent() == nullptr) traverse_loop(loop); - } - delete loop_detection_; - loop_detection_ = nullptr; - } - delete func_info_; - func_info_ = nullptr; + func_info_ = new FuncInfo(m_); + func_info_->run(); + for (auto func : m_->get_functions()) + { + if (func->is_declaration()) continue; + loop_detection_ = new LoopDetection(func); + loop_detection_->run(); + for (auto loop : loop_detection_->get_loops()) + { + // 遍历处理顶层循环 + if (loop->get_parent() == nullptr) traverse_loop(loop); + } + delete loop_detection_; + loop_detection_ = nullptr; + } + delete func_info_; + func_info_ = nullptr; } /** @@ -40,12 +40,12 @@ void LoopInvariantCodeMotion::run() */ void LoopInvariantCodeMotion::traverse_loop(Loop* loop) { - // 先外层再内层,这样不用在插入 preheader 后更改循环 - run_on_loop(loop); - for (auto sub_loop : loop->get_sub_loops()) - { - traverse_loop(sub_loop); - } + // 先外层再内层,这样不用在插入 preheader 后更改循环 + run_on_loop(loop); + for (auto sub_loop : loop->get_sub_loops()) + { + traverse_loop(sub_loop); + } } // TODO: 收集并返回循环 store 过的变量 @@ -56,35 +56,20 @@ void LoopInvariantCodeMotion::traverse_loop(Loop* loop) // 则你应该返回 %a 而非 %b std::unordered_set LoopInvariantCodeMotion::collect_loop_store_vars(Loop* loop) { - // 可能用到 - // FuncInfo::store_ptr, FuncInfo::get_stores - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); + // 可能用到 + // FuncInfo::store_ptr, FuncInfo::get_stores + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); } // TODO: 收集并返回循环中的所有指令 std::vector LoopInvariantCodeMotion::collect_insts(Loop* loop) { - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); -} - - -// TODO: 实现collect_loop_info函数 -// 1. 遍历当前循环及其子循环的所有指令 -// 2. 收集所有指令到loop_instructions中 -// 3. 检查store指令是否修改了全局变量,如果是则添加到updated_global中 -// 4. 检查是否包含非纯函数调用,如果有则设置contains_impure_call为true -void LoopInvariantCodeMotion::collect_loop_info( - Loop* loop, - std::set& loop_instructions, - std::set& updated_global, - bool& contains_impure_call) -{ - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); } enum InstructionType: std::uint8_t { - UNKNOWN, VARIANT, INVARIANT + UNKNOWN, VARIANT, INVARIANT }; /** @@ -94,91 +79,95 @@ enum InstructionType: std::uint8_t */ void LoopInvariantCodeMotion::run_on_loop(Loop* loop) { - // 循环 store 过的变量 - std::unordered_set loop_stores_var = collect_loop_store_vars(loop); - // 循环中的所有指令 - std::vector instructions = collect_insts(loop); - int insts_count = static_cast(instructions.size()); - // 循环的所有基本块 - std::unordered_set bbs; - for (auto i : loop->get_blocks()) bbs.emplace(i); - // val 是否在循环内定义,可以当成函数进行调用 - auto is_val_in_loop = [&bbs](Value* val)->bool - { - auto inst = dynamic_cast(val); - if (inst == nullptr) return true; - return bbs.count(inst->get_parent()); - }; - // inst_type[i] 代表 instructions[i] 是循环变量(每次循环都会变)/ 循环不变量 还是 不知道 - std::vector inst_type; - inst_type.resize(insts_count); - - // 遍历后是不是还有指令不知道 InstructionType - bool have_inst_can_not_decide; - // 是否存在 invariant - bool have_invariant = false; - do - { - have_inst_can_not_decide = false; - for (int i = 0; i < insts_count; i++) - { - Instruction* inst = instructions[i]; - InstructionType type = inst_type[i]; - if (type != UNKNOWN) continue; - // 可能有用的函数 - // FuncInfo::load_ptr - - // TODO: 识别循环不变式指令 - // - 将 store、ret、br、phi 等指令与非纯函数调用标记为 VARIANT - // - 如果 load 指令加载的变量是循环 store 过的变量,标记为 VARIANT - // - 如果指令有 VARIANT 操作数,标记为 VARIANT - // - 如果指令所有操作数都是 INVARIANT (或者不在循环内),标记为 INVARIANT, 设置 have_invariant - // - 否则设置 have_inst_can_not_decide - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); - } - } - while (have_inst_can_not_decide); - - if (!have_invariant) return; - - auto header = loop->get_header(); - - if (header->get_pre_basic_blocks().size() > 1 || header->get_pre_basic_blocks().front()->get_succ_basic_blocks().size() > 1) - { - // 插入 preheader - auto bb = BasicBlock::create(m_, "", loop->get_header()->get_parent()); - loop->set_preheader(bb); - - for (auto phi : loop->get_header()->get_instructions()) - { - if (phi->get_instr_type() != Instruction::phi) break; - // TODO: 分裂 phi 指令 - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); - } - - // TODO: 维护 bb, header, 与 header 前驱块的基本块关系 - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); - - bb->add_instruction(BranchInst::create_br(header, bb)); - - // 若你想维护 LoopDetection 在 LICM 后保持正确 - // auto loop2 = loop->get_parent(); - // while (loop2 != nullptr) - // { - // loop2->get_parent()->add_block(bb); - // loop2 = loop2->get_parent(); - // } - } - else loop->set_preheader(header->get_pre_basic_blocks().front()); - - // insert preheader - auto preheader = loop->get_preheader(); - - auto terminator = preheader->get_instructions().back(); - preheader->get_instructions().pop_back(); - - // TODO: 外提循环不变指令 - throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); - - preheader->add_instruction(terminator); + // 循环 store 过的变量 + std::unordered_set loop_stores_var = collect_loop_store_vars(loop); + // 循环中的所有指令 + std::vector instructions = collect_insts(loop); + int insts_count = static_cast(instructions.size()); + // Value* 在 map 内说明它是循环内的指令,InstructionType 指示它是循环变量(每次循环都会变)/ 循环不变量 还是 不知道 + std::unordered_map inst_type; + for (auto i : instructions) inst_type[i] = UNKNOWN; + + // 遍历后是不是还有指令不知道 InstructionType + bool have_inst_can_not_decide; + // 是否存在 invariant + bool have_invariant = false; + do + { + have_inst_can_not_decide = false; + for (int i = 0; i < insts_count; i++) + { + Instruction* inst = instructions[i]; + InstructionType type = inst_type[inst]; + if (type != UNKNOWN) continue; + // 可能有用的函数 + // FuncInfo::load_ptr, FuncInfo::get_stores, FuncInfo::use_io + + // TODO: 识别循环不变式指令 + // - 将 store、ret、br、phi 等指令与非纯函数调用标记为 VARIANT + // - 如果 load 指令加载的变量是循环 store 过的变量,标记为 VARIANT + // - 如果指令有 VARIANT 操作数,标记为 VARIANT + // - 如果指令所有操作数都是 INVARIANT (或者不在循环内),标记为 INVARIANT, 设置 have_invariant + // - 否则设置 have_inst_can_not_decide + + // TODO: 外提循环不变的非纯函数调用 + // 注意: 你不应该外提使用了 io 的函数调用 + + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); + } + } + while (have_inst_can_not_decide); + + if (!have_invariant) return; + + auto header = loop->get_header(); + + if (header->get_pre_basic_blocks().size() > 1 || header->get_pre_basic_blocks().front()->get_succ_basic_blocks().size() > 1) + { + // 插入 preheader + auto bb = BasicBlock::create(m_, "", loop->get_header()->get_parent()); + loop->set_preheader(bb); + + for (auto phi : loop->get_header()->get_instructions()) + { + if (phi->get_instr_type() != Instruction::phi) break; + // 可能有用的函数 + // PhiInst::create_phi + + // TODO: 分裂 phi 指令 + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); + } + + // 可能有用的函数 + // BranchInst::replace_all_bb_match + + // TODO: 维护 bb, header, 与 header 前驱块的基本块关系 + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); + + BranchInst::create_br(header, bb); + + // 若你想维护 LoopDetection 在 LICM 后保持正确 + // auto loop2 = loop->get_parent(); + // while (loop2 != nullptr) + // { + // loop2->add_block(bb); + // loop2 = loop2->get_parent(); + // } + } + else loop->set_preheader(header->get_pre_basic_blocks().front()); + + // insert preheader + auto preheader = loop->get_preheader(); + + auto terminator = preheader->get_instructions().back(); + preheader->get_instructions().pop_back(); + + // 可以使用 Function::check_for_block_relation_error 检查基本块间的关系是否正确维护 + + // TODO: 外提循环不变指令 + throw std::runtime_error("Lab4: 你有一个TODO需要完成!"); + + preheader->add_instruction(terminator); + + std::cerr << "licm done\n"; } diff --git a/src/passes/LoopDetection.cpp b/src/passes/LoopDetection.cpp index d9a27ac..6e14188 100644 --- a/src/passes/LoopDetection.cpp +++ b/src/passes/LoopDetection.cpp @@ -54,6 +54,21 @@ void LoopDetection::run() { delete dominators_; } +std::string Loop::safe_print() const +{ + std::string ret; + if (header_ == nullptr) ret += "b"; + else ret += header_->get_name(); + ret += " "; + ret += std::to_string(blocks_.size()); + ret += "b "; + ret += std::to_string(latches_.size()); + ret += "latch "; + ret += std::to_string(sub_loops_.size()); + ret += "sub"; + return ret; +} + /** * @brief 发现循环及其子循环 * @param bb 循环的header块 @@ -69,10 +84,12 @@ void LoopDetection::discover_loop_and_sub_loops(BasicBlock *bb, std::set work_list = {latches.begin(), latches.end()}; // 初始化工作表 + std::unordered_set already_in_work_list = { latches.begin(), latches.end() }; // 已经在工作表,防止重复加入 while (!work_list.empty()) { // 当工作表非空时继续处理 auto bb2 = work_list.back(); work_list.pop_back(); + already_in_work_list.erase(bb2); // TODO-1: 处理未分配给任何循环的节点 if (bb_to_loop_.find(bb2) == bb_to_loop_.end()) { @@ -85,7 +102,7 @@ void LoopDetection::discover_loop_and_sub_loops(BasicBlock *bb, std::set(l_val); - return alloca != nullptr && !alloca->get_alloca_type()->is_array_type(); + auto alloca = dynamic_cast(ptr); + if (alloca != nullptr && !alloca->get_alloca_type()->is_array_type()) return alloca; + return nullptr; } /** * @brief Mem2Reg Pass的主入口函数 - * + * * 该函数执行内存到寄存器的提升过程,将栈上的局部变量提升到SSA格式。 * 主要步骤: * 1. 创建并运行支配树分析 @@ -21,7 +22,7 @@ static bool is_not_array_alloca(Value* l_val) * - 清空相关数据结构 * - 插入必要的phi指令 * - 执行变量重命名 - * + * * 注意:函数执行后,冗余的局部变量分配指令将由后续的死代码删除Pass处理 */ void Mem2Reg::run() { @@ -43,7 +44,7 @@ void Mem2Reg::run() { generate_phi(); // 确保每个局部变量的栈都有初始值 for (auto var : allocas_) - var_val_stack[var].emplace_back(ConstantZero::get(var->get_alloca_type(), m_)); + var_val_stack[var].emplace_back(var->get_alloca_type()->is_float_type() ? static_cast(ConstantFP::get(0, m_)) : static_cast(ConstantInt::get(0, m_))); // 对应伪代码中重命名阶段 rename(func_->get_entry_block()); } @@ -55,17 +56,17 @@ void Mem2Reg::run() { /** * @brief 在必要的位置插入phi指令 - * + * * 该函数实现了经典的phi节点插入算法: * 1. 收集全局活跃变量: * - 扫描所有store指令 * - 识别在多个基本块中被赋值的变量 - * + * * 2. 插入phi指令: * - 对每个全局活跃变量 * - 在其定值点的支配边界处插入phi指令 * - 使用工作表法处理迭代式的phi插入 - * + * * phi指令的插入遵循最小化原则,只在必要的位置插入phi节点 */ void Mem2Reg::generate_phi() { @@ -80,11 +81,13 @@ void Mem2Reg::generate_phi() { if (instr->is_store()) { // store i32 a, i32 *b // a is r_val, b is l_val - auto l_val = dynamic_cast(instr)->get_lval(); - if (is_not_array_alloca(l_val)) { - auto lalloca = dynamic_cast(instr); - not_array_allocas.insert(lalloca); - allocas_.emplace_back(lalloca); + auto l_val = dynamic_cast(instr)->get_ptr(); + if (auto lalloca = is_not_array_alloca(l_val)) { + if (!not_array_allocas.count(lalloca)) + { + not_array_allocas.insert(lalloca); + allocas_.emplace_back(lalloca); + } allocas_stored_bbs[lalloca].emplace_back(bb); } } @@ -105,7 +108,7 @@ void Mem2Reg::generate_phi() { if (already_handled.count(bb)) continue; already_handled.emplace(bb); for (auto bb_dominance_frontier_bb : - dominators_->get_dominance_frontier(bb)) { + dominators_->get_dominance_frontier(bb)) { if (bb_has_var_phi.find({bb_dominance_frontier_bb, var}) == bb_has_var_phi.end()) { // generate phi for bb_dominance_frontier_bb & add @@ -115,7 +118,6 @@ void Mem2Reg::generate_phi() { bb_dominance_frontier_bb); phi_to_alloca_.emplace(phi, var); bb_to_phi_[bb_dominance_frontier_bb].emplace_back(phi); - bb_dominance_frontier_bb->add_instr_begin(phi); work_list.push_back(bb_dominance_frontier_bb); bb_has_var_phi.emplace(bb_dominance_frontier_bb, var); } @@ -128,20 +130,23 @@ void Mem2Reg::rename(BasicBlock *bb) { // 可能用到的数据结构 // list allocas_ 所有 Mem2Reg 需要消除的局部变量,用于遍历 // map> var_val_stack 每个局部变量的存储值栈,还未进行任何操作时已经存进去了 0,不会为空 - // map; Phi 对应的局部变量 - // map>; 在某个基本块的 Phi + // map phi_to_alloca_; Phi 对应的局部变量 + // map> bb_to_phi_; 在某个基本块的 Phi // 可能用到的函数 // Value::replace_all_use_with(Value* a) 将所有用到 this 的指令对应 this 的操作数都替换为 a // BasicBlock::erase_instrs(set) 移除并 delete 列表中的指令 + // StoreInst / LoadInst get_ptr 这些 Inst 所操作的变量 + // is_not_array_alloca(Value* ptr) 一个变量是不是 Mem2Reg 所关心的非数组局部变量 // TODO // 步骤一:对每个 alloca 非数组变量(局部变量), 在其存储值栈存入其当前的最新值(也就是目前的栈顶值) - // 步骤二:遍历基本块所有指令,执行操作并记录需要删除的 load/store/alloca 指令 - // - 步骤三: 将 store 指令存储的值,作为其对应局部变量的最新值(更新栈顶) - // - 步骤四: 将 load 指令的所有使用替换为其读取的局部变量的最新值 - // 步骤五:为所有后继块的 phi 添加参数 - // 步骤六:对 bb 在支配树上的所有后继节点,递归执行 rename 操作 - // 步骤七:pop 出所有局部变量的最新值 - // 步骤八:删除需要删除的冗余指令 + // 步骤二:遍历基本块所有指令,执行操作并记录需要删除的 load/store/alloca 指令(注意: 并非所有 load/store/alloca 都是 Mem2Reg 需要处理的) + // - 步骤三: 将 store 指令存储的值,作为其对应局部变量的最新值(更新栈顶) + // - 步骤四: 将 phi 指令的所有使用替换为其对应的局部变量的最新值 + // - 步骤五: 将 load 指令的所有使用替换为其读取的局部变量的最新值 + // 步骤六:为所有后继块的 phi 添加参数 + // 步骤七:对 bb 在支配树上的所有后继节点,递归执行 rename 操作 + // 步骤八:pop 出所有局部变量的最新值 + // 步骤九:删除需要删除的冗余指令 } diff --git a/tests/4-opt/CMakeLists.txt b/tests/4-opt/CMakeLists.txt new file mode 100644 index 0000000..db8524b --- /dev/null +++ b/tests/4-opt/CMakeLists.txt @@ -0,0 +1,9 @@ +add_executable( + eval_lab4 + eval_lab4.cpp +) + + +install( + TARGETS eval_lab4 +) \ No newline at end of file diff --git a/tests/4-opt/eval_lab4.cpp b/tests/4-opt/eval_lab4.cpp new file mode 100644 index 0000000..888bcce --- /dev/null +++ b/tests/4-opt/eval_lab4.cpp @@ -0,0 +1,695 @@ +// ReSharper disable CppClangTidyPerformanceInefficientStringConcatenation +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +enum evaluate_result : uint8_t { SUCCESS, FAIL, PASS }; + +static enum stage : uint8_t +{ + raw, mem2reg, licm, all +} STAGE; + +static string TEST_PATH; + +static enum test_type : uint8_t +{ + debug, test +} TYPE; + +struct cmd_result +{ + string out_str; + string err_str; + int ret_val; + + bool have_err_message() const + { + return !err_str.empty(); + } +}; + +struct cmd_result_mix +{ + string str; + int ret_val; +}; + +static string red(const string& str) { return "\033[31;1m" + str + "\033[0m"; } + +static string green(const string& str) { return "\033[32;1m" + str + "\033[0m"; } + +static ofstream ost; + +static void ext(int flag) +{ + ost.close(); + exit(flag); +} + +static void out(const string& str, bool to_std) +{ + if (to_std) cout << str; + else ost << str; +} + +static void out2(const string& str) +{ + cout << str; + ost << str; +} + +static void out2e(const string& str) +{ + cout << red(str); + ost << str; +} + +static std::pair readPipeLine(int outpipe[], int errpipe[], int limit) +{ + fd_set readfds; + int maxFd = std::max(outpipe[0], errpipe[0]); + char buffer[1024]; + std::string stdout_result; + std::string stderr_result; + int totalRead = 0; + bool have_stdout = true; + bool have_stderr = true; + + while (true) + { + FD_ZERO(&readfds); + + if (have_stdout) + FD_SET(outpipe[0], &readfds); + if (have_stderr) + FD_SET(errpipe[0], &readfds); + + int ret = select(maxFd + 1, &readfds, nullptr, nullptr, nullptr); + if (ret == -1) + { + out("select() 调用失败!\n", true); + out("select() 调用失败!\n", false); + ext(-1); + } + if (ret == 0) + continue; + + if (FD_ISSET(outpipe[0], &readfds)) + { + int bytes = static_cast(read(outpipe[0], buffer, sizeof(buffer) - 1)); + if (bytes > 0) + { + int writeLen = std::min(bytes, limit - totalRead); + buffer[writeLen] = '\0'; + stdout_result += buffer; + totalRead += writeLen; + + if (totalRead >= limit) + break; + } + else + { + FD_CLR(outpipe[0], &readfds); + close(outpipe[0]); + have_stdout = false; + maxFd = have_stderr ? errpipe[0] : -1; + } + } + + if (FD_ISSET(errpipe[0], &readfds)) + { + int bytes = static_cast(read(errpipe[0], buffer, sizeof(buffer) - 1)); + if (bytes > 0) + { + int writeLen = std::min(bytes, limit - totalRead); + buffer[writeLen] = '\0'; + stderr_result += buffer; + totalRead += writeLen; + + if (totalRead >= limit) + break; + } + else + { + FD_CLR(errpipe[0], &readfds); + close(errpipe[0]); + have_stderr = false; + maxFd = have_stdout ? outpipe[0] : -1; + } + } + + if (!FD_ISSET(outpipe[0], &readfds) && !FD_ISSET(errpipe[0], &readfds)) + break; + } + + if (!stdout_result.empty() && stdout_result.back() != '\n') stdout_result += '\n'; + if (!stderr_result.empty() && stderr_result.back() != '\n') stderr_result += '\n'; + + return {stdout_result, stderr_result}; +} + +static std::string readPipeLineMix(int outpipe[], int errpipe[], int limit) +{ + fd_set readfds; + int maxFd = std::max(outpipe[0], errpipe[0]); + char buffer[1024]; + std::string result; + int totalRead = 0; + bool have_stdout = true; + bool have_stderr = true; + + while (true) + { + FD_ZERO(&readfds); + + if (have_stdout) + FD_SET(outpipe[0], &readfds); + if (have_stderr) + FD_SET(errpipe[0], &readfds); + + int ret = select(maxFd + 1, &readfds, nullptr, nullptr, nullptr); + if (ret == -1) + { + out("select() 调用失败!\n", true); + out("select() 调用失败!\n", false); + ext(-1); + } + if (ret == 0) + continue; + + if (FD_ISSET(outpipe[0], &readfds)) + { + int bytes = static_cast(read(outpipe[0], buffer, sizeof(buffer) - 1)); + if (bytes > 0) + { + int writeLen = std::min(bytes, limit - totalRead); + buffer[writeLen] = '\0'; + result += buffer; + totalRead += writeLen; + + if (totalRead >= limit) + break; + } + else + { + FD_CLR(outpipe[0], &readfds); + close(outpipe[0]); + have_stdout = false; + maxFd = have_stderr ? errpipe[0] : -1; + } + } + + if (FD_ISSET(errpipe[0], &readfds)) + { + int bytes = static_cast(read(errpipe[0], buffer, sizeof(buffer) - 1)); + if (bytes > 0) + { + int writeLen = std::min(bytes, limit - totalRead); + buffer[writeLen] = '\0'; + result += buffer; + totalRead += writeLen; + + if (totalRead >= limit) + break; + } + else + { + FD_CLR(errpipe[0], &readfds); + close(errpipe[0]); + have_stderr = false; + maxFd = have_stdout ? outpipe[0] : -1; + } + } + + if (!FD_ISSET(outpipe[0], &readfds) && !FD_ISSET(errpipe[0], &readfds)) + break; + } + + if (!result.empty() && result.back() != '\n') result += '\n'; + + return result; +} + +static cmd_result runCommand(const std::string& command, int limit = INT_MAX) +{ + int outpipe[2]; + int errpipe[2]; + if (pipe(outpipe) != 0 || pipe(errpipe) != 0) + { + out2("pipe() 调用失败!\n"); + ext(-1); + } + + pid_t pid = fork(); + if (pid == -1) + { + out2("fork() 调用失败!\n"); + ext(-1); + } + + if (pid == 0) + { + close(outpipe[0]); + close(errpipe[0]); + dup2(outpipe[1], STDOUT_FILENO); + dup2(errpipe[1], STDERR_FILENO); + execlp("sh", "sh", "-c", command.c_str(), static_cast(nullptr)); + perror("execlp"); + ext(127); + } + + close(outpipe[1]); + close(errpipe[1]); + + auto res = readPipeLine(outpipe, errpipe, limit); + + int status; + waitpid(pid, &status, 0); + + cmd_result ret; + + ret.out_str = res.first; + ret.err_str = res.second; + ret.ret_val = WEXITSTATUS(status); + + return ret; +} + +static cmd_result_mix runCommandMix(const std::string& command, int limit = INT_MAX) +{ + int outpipe[2]; + int errpipe[2]; + if (pipe(outpipe) != 0 || pipe(errpipe) != 0) + { + out2("pipe() 调用失败!\n"); + ext(-1); + } + + pid_t pid = fork(); + if (pid == -1) + { + out2("fork() 调用失败!\n"); + ext(-1); + } + + if (pid == 0) + { + close(outpipe[0]); + close(errpipe[0]); + dup2(outpipe[1], STDOUT_FILENO); + dup2(errpipe[1], STDERR_FILENO); + execlp("sh", "sh", "-c", command.c_str(), static_cast(nullptr)); + perror("execlp"); + ext(127); + } + + close(outpipe[1]); + close(errpipe[1]); + + auto res = readPipeLineMix(outpipe, errpipe, limit); + + int status; + waitpid(pid, &status, 0); + + cmd_result_mix ret; + + ret.str = res; + ret.ret_val = WEXITSTATUS(status); + + return ret; +} + +static list splitString(const string& str) +{ + list lines; + std::istringstream stream(str); + std::string line; + while (std::getline(stream, line)) + { + if (!line.empty()) lines.push_back(line); + } + return lines; +} + +static std::string readFile(const std::string& filename) +{ + std::ifstream file(filename); + if (!file.is_open()) + { + cerr << "无法打开文件 " + filename << '\n'; + ext(-1); + } + + std::string content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + return content; +} + +static void writeFile(const std::string& content, const std::string& filename) +{ + std::ofstream file(filename); + if (!file.is_open()) + { + cerr << "无法打开文件 " + filename << '\n'; + ext(-1); + } + + file << content; + file.close(); +} + +static string lastDotLeft(const string& str) +{ + size_t lastDotPos = str.find_last_of('.'); + if (lastDotPos == string::npos) + { + return str; + } + return str.substr(0, lastDotPos); +} + +static string lastLineRight(const string& str) +{ + size_t lastSlashPos = str.find_last_of('/'); + if (lastSlashPos == string::npos) + { + return str; + } + return str.substr(lastSlashPos + 1); +} + +static void makeDir(const string& dirPath) +{ + if (filesystem::exists(dirPath)) + return; + if (!filesystem::create_directories(dirPath)) + { + out2("目录 " + dirPath + " 创建失败!\n"); + ext(-1); + } +} + +static const char* ERR_LOG = R"(Usage: ./eval_lab4.sh [test-stage] [path-to-testcases] [type] +test-stage: 'raw' or 'licm' or 'mem2reg' or 'all' +path-to-testcases: './testcases/functional-cases' or '../testcases_general' or 'self made cases' +type: 'debug' or 'test', debug will output .ll file +)"; + +static int parseCmd(int argc, char* argv[]) +{ + if (argc != 4) + { + out(ERR_LOG, true); + return -1; + } + if (std::strcmp(argv[1], "licm") == 0) + { + STAGE = licm; + ost.open("licm_log.txt", ios::out); + } + else if (std::strcmp(argv[1], "mem2reg") == 0) + { + STAGE = mem2reg; + ost.open("mem2reg_log.txt", ios::out); + } + else if (std::strcmp(argv[1], "raw") == 0) + { + STAGE = raw; + ost.open("raw_log.txt", ios::out); + } + else if (std::strcmp(argv[1], "all") == 0) + { + STAGE = all; + ost.open("all_log.txt", ios::out); + } + else + { + out(ERR_LOG, true); + return -1; + } + TEST_PATH = argv[2]; + if (TEST_PATH.empty() || TEST_PATH.back() != '/') TEST_PATH += '/'; + if (!std::filesystem::exists(TEST_PATH)) + { + out2("测评路径 " + TEST_PATH + " 不存在\n"); + return -1; + } + if (!std::filesystem::is_directory(TEST_PATH)) + { + out2("测评路径 " + TEST_PATH + " 不是文件夹\n"); + return -1; + } + if (std::strcmp(argv[3], "debug") == 0) + TYPE = debug; + else if (std::strcmp(argv[3], "test") == 0) + TYPE = test; + else + { + out2(ERR_LOG); + return -1; + } + return 0; +} + +static string pad(int count) +{ + return count > 0 ? std::string(count, ' ') : std::string(); +} + +static string pad(int count, char target) +{ + return count > 0 ? std::string(count, target) : std::string(); +} + +static int allmain(int argc, char* argv[]) +{ + auto cmd = R"(ls )" + TEST_PATH + R"(*.cminus | sort -V)"; + auto result = runCommand(cmd); + string flags[3] = {"", "-mem2reg ", "-mem2reg -licm "}; + string tys[3] = {"raw", "mem2reg", "licm"}; + if (result.have_err_message()) out2e(result.err_str); + auto io_c = runCommand("realpath ../../").out_str; + io_c.pop_back(); + io_c += "/src/io/io.c"; + auto io_h = io_c; + io_h.back() = 'h'; + auto tests = splitString(result.out_str); + string out_path = "./output/"; + out2("[info] Start testing, using testcase dir: " + TEST_PATH + "\n"); + int maxLen = 0; + for (const auto& line : tests) maxLen = std::max(maxLen, static_cast(line.size())); + for (const auto& line : tests) + { + auto no_path_have_suffix = lastLineRight(line); + auto no_path_no_suffix = lastDotLeft(no_path_have_suffix); + makeDir(out_path + no_path_no_suffix); + auto in_file = TEST_PATH + no_path_no_suffix + ".in"; + auto std_out_file = TEST_PATH + no_path_no_suffix + ".out"; + out2("==========" + no_path_have_suffix + pad(maxLen - static_cast(line.length()), '=') + "==========\n"); + int sz[2] = {}; + for (int i = 0; i < 3; i++) + { + const auto& arg = flags[i]; + const auto& ty = tys[i]; + auto ll_file = out_path + no_path_no_suffix + "/" + ty + ".ll"; + auto asm_file = out_path + no_path_no_suffix + "/" + ty + ".s"; + auto exe_file = out_path + no_path_no_suffix + "/" + ty + "o"; + auto out_file = out_path + no_path_no_suffix + "/" + ty + ".out"; + auto eval_file = out_path + no_path_no_suffix + "/eval_" + ty + ".txt"; + out(ty + pad(7 - static_cast(ty.length())) + " ", true); + out("==========" + ty + pad(7 - static_cast(ty.length()), '=') + "==========\n", + false); + cout.flush(); + ost.flush(); + auto cmd2 = runCommandMix("cminusfc -S " + arg + line + " -o " + asm_file); + out(cmd2.str, false); + if (cmd2.ret_val) + { + out2e("CE: cminusfc compiler .cminus error\n"); + continue; + } + if (TYPE == debug) + { + cmd2 = runCommandMix( + "loongarch64-unknown-linux-gnu-gcc -g -static " + asm_file + " " + io_c + " -o " + exe_file); + out(cmd2.str, false); + if (cmd2.ret_val) + { + out2e("CE: gcc compiler .s error\n"); + continue; + } + } + else + { + cmd2 = runCommandMix( + "loongarch64-unknown-linux-gnu-gcc -static " + asm_file + " " + io_c + " -o " + exe_file); + out(cmd2.str, false); + if (cmd2.ret_val) + { + out2e("CE: gcc compiler .s error\n"); + continue; + } + } + cmd_result ret; + if (filesystem::exists(in_file)) + ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file + " <" + in_file); + else ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file); + auto o = readFile(out_file); + writeFile(o + to_string(ret.ret_val) + "\n", out_file); + writeFile(ret.err_str, eval_file); + cmd2 = runCommandMix("diff --strip-trailing-cr " + std_out_file + " " + out_file + " -y"); + out(cmd2.str, false); + if (cmd2.ret_val != 0) + { + out2e("WA: output differ, check " + std_out_file + " and " + out_file + "\n"); + continue; + } + out(green(" OK"), true); + out("OK\n", false); + auto strs = splitString(ret.err_str); + if (strs.size() >= 6) + { + out(" Take Time (us): " + green(strs.back()) + pad( + sz[0] == 0 ? 1 : (sz[0] - static_cast(strs.back().size()))), true); + out(" Take Time (us): " + strs.back() + pad( + sz[0] == 0 ? 1 : (sz[0] - static_cast(strs.back().size()))), false); + if (sz[0] == 0) sz[0] = static_cast(strs.back().size()) + 1; + strs.pop_back(); + strs.pop_back(); + out(" Inst Execute Cost: " + green(strs.back()) + pad(sz[1] - static_cast(strs.back().size())), + true); + out(" Inst Execute Cost: " + strs.back() + pad(sz[1] - static_cast(strs.back().size())), false); + if (sz[1] == 0) sz[1] = static_cast(strs.back().size()); + strs.pop_back(); + strs.pop_back(); + out(" Allocate Size (bytes): " + green(strs.back()) + "\n", true); + out(" Allocate Size (bytes): " + strs.back() + "\n", false); + } + else cout << "\n"; + filesystem::remove(exe_file); + filesystem::remove(out_file); + filesystem::remove(asm_file); + } + } + return 0; +} + +int main(int argc, char* argv[]) +{ + if (parseCmd(argc, argv)) ext(-1); + if (STAGE == all) + { + return allmain(argc, argv); + } + auto cmd = R"(ls )" + TEST_PATH + R"(*.cminus | sort -V)"; + auto result = runCommand(cmd); + string flags = (STAGE == mem2reg ? "-mem2reg " : (STAGE == raw ? "" : "-mem2reg -licm ")); + if (result.have_err_message()) out2e(result.err_str); + auto io_c = runCommand("realpath ../../").out_str; + io_c.pop_back(); + io_c += "/src/io/io.c"; + auto io_h = io_c; + io_h.back() = 'h'; + auto tests = splitString(result.out_str); + string out_path = "./output/"; + out2("[info] Start testing, using testcase dir: " + TEST_PATH + "\n"); + int maxLen = 0; + for (const auto& line : tests) maxLen = std::max(maxLen, static_cast(line.size())); + for (const auto& line : tests) + { + auto no_path_have_suffix = lastLineRight(line); + auto no_path_no_suffix = lastDotLeft(no_path_have_suffix); + makeDir(out_path + no_path_no_suffix); + auto ll_file = out_path + no_path_no_suffix + "/" + argv[1] + ".ll"; + auto asm_file = out_path + no_path_no_suffix + "/" + argv[1] + ".s"; + auto exe_file = out_path + no_path_no_suffix + "/" + argv[1] + "o"; + auto out_file = out_path + no_path_no_suffix + "/" + argv[1] + ".out"; + auto eval_file = out_path + no_path_no_suffix + "/eval_" + argv[1] + ".txt"; + auto in_file = TEST_PATH + no_path_no_suffix + ".in"; + auto std_out_file = TEST_PATH + no_path_no_suffix + ".out"; + out(no_path_have_suffix + pad(maxLen - static_cast(line.length())) + " ", true); + out("==========" + no_path_have_suffix + pad(maxLen - static_cast(line.length()), '=') + "==========\n", + false); + cout.flush(); + ost.flush(); + auto cmd2 = runCommandMix("cminusfc -S " + flags + line + " -o " + asm_file); + out(cmd2.str, false); + if (cmd2.ret_val) + { + out2e("CE: cminusfc compiler .cminus error\n"); + continue; + } + if (TYPE == debug) + { + cmd2 = runCommandMix( + "loongarch64-unknown-linux-gnu-gcc -g -static " + asm_file + " " + io_c + " -o " + exe_file); + out(cmd2.str, false); + if (cmd2.ret_val) + { + out2e("CE: gcc compiler .s error\n"); + continue; + } + } + else + { + cmd2 = runCommandMix( + "loongarch64-unknown-linux-gnu-gcc -static " + asm_file + " " + io_c + " -o " + exe_file); + out(cmd2.str, false); + if (cmd2.ret_val) + { + out2e("CE: gcc compiler .s error\n"); + continue; + } + } + cmd_result ret; + if (filesystem::exists(in_file)) + ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file + " <" + in_file); + else ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file); + auto o = readFile(out_file); + writeFile(o + to_string(ret.ret_val) + "\n", out_file); + writeFile(ret.err_str, eval_file); + cmd2 = runCommandMix("diff --strip-trailing-cr " + std_out_file + " " + out_file + " -y"); + out(cmd2.str, false); + if (cmd2.ret_val != 0) + { + out2e("WA: output differ, check " + std_out_file + " and " + out_file + "\n"); + continue; + } + out(green(" OK"), true); + out("OK\n", false); + auto strs = splitString(ret.err_str); + if (strs.size() >= 6) + { + out(" Take Time (us): " + green(strs.back()), true); + out(" Take Time (us): " + strs.back(), false); + strs.pop_back(); + strs.pop_back(); + out(" Inst Execute Cost: " + green(strs.back()), true); + out(" Inst Execute Cost: " + strs.back(), false); + strs.pop_back(); + strs.pop_back(); + out(" Allocate Size (bytes): " + green(strs.back()) + "\n", true); + out(" Allocate Size (bytes): " + strs.back() + "\n", false); + } + else cout << "\n"; + filesystem::remove(exe_file); + filesystem::remove(out_file); + filesystem::remove(asm_file); + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c9ad38..6f3199c 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,2 +1,3 @@ -add_subdirectory("2-ir-gen/warmup") -add_subdirectory("3-codegen/warmup") \ No newline at end of file +# add_subdirectory("2-ir-gen/warmup") +# add_subdirectory("3-codegen/warmup") +add_subdirectory("4-opt") \ No newline at end of file -- GitLab