From 8b0f0f63a17c74844beb7e0359ed7b91c5cff809 Mon Sep 17 00:00:00 2001 From: ridethepig Date: Mon, 12 Jun 2023 20:50:05 +0800 Subject: [PATCH] buggy buggy reg alloc --- .vscode/launch.json | 2 +- include/common.h | 3 + include/llir_value.h | 4 + include/mc_inst.h | 167 +++++++++++----- include/pass.h | 37 ++-- include/visitor.h | 2 +- scripts/mytester.py | 37 ++-- src/main.cpp | 33 +++- src/mc_asmgen.cpp | 440 ++++++++++++++++++++++++++++++++++++----- src/mc_codegen.cpp | 223 +++++++++++++-------- src/mc_inst.cpp | 104 ++++++++++ src/pass_reg_alloc.cpp | 115 +++++++---- src/visitor.cpp | 5 +- 13 files changed, 911 insertions(+), 261 deletions(-) create mode 100644 src/mc_inst.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 68c7082..7e09f50 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "request": "launch", "name": "Debug", "program": "${workspaceFolder}/build/sysy", - "args": ["../sysytests/functional_2022/34_arr_expr_len.sy", "-S", "-o", "build/21_my.ll", "-O1", "-emit-llvm"], + "args": ["../sysytests/functional_2022/28_while_test3.sy", "-S", "-o", "build/my.s", "-O1", "-emit-llvm"], "cwd": "${workspaceFolder}" } ] diff --git a/include/common.h b/include/common.h index 5d5c1cd..c4c2492 100644 --- a/include/common.h +++ b/include/common.h @@ -37,6 +37,9 @@ inline sptr(DST) strict_shared_cast(SRC src) { #define STD_FOUND(container, val) (STD_FIND(container, val) != container.end()) #define ASSOC_FOUND(cont, val) (cont.find(val) != cont.end()) +#define INF (0x3f3f3f3f) +#define BTWN(v, l, r) (l <= v && v <= r) + #define panic(message) \ do { \ throw GrammarException(__FILE__, __LINE__, (message)); \ diff --git a/include/llir_value.h b/include/llir_value.h index f1032c4..b07f786 100644 --- a/include/llir_value.h +++ b/include/llir_value.h @@ -228,6 +228,10 @@ public: str += "}"; return str; } + + int get_memory_size() { + return value_list.size() * 4; + } }; typedef std::shared_ptr ConstantPtr_t; diff --git a/include/mc_inst.h b/include/mc_inst.h index b1b2b03..cd30de4 100644 --- a/include/mc_inst.h +++ b/include/mc_inst.h @@ -11,7 +11,7 @@ namespace CompSysY { // x0,gp,tp are preserved in user-space enum class RV64Reg { x0 = 0, // zero N/A - ra, // ra, caller + ra, // ra, caller, treated as callee-saved sp, // sp, callee gp, // gp, N/A tp, // tp, N/A @@ -43,9 +43,18 @@ enum class RV64Reg { t5, t6, // t3-6,caller }; -inline const char* enum_to_string(const RV64Reg& tag) { - const static char* _str_tab[] = {"x0","ra","sp","gp","tp","t0","t1","t2","s0","s1","a0","a1","a2","a3","a4","a5","a6","a7","s2","s3","s4","s5","s6","s7","s8","s9","s1","s1","t3","t4","t5","t6",}; - return _str_tab[(unsigned)tag]; +inline const char *enum_to_string(const RV64Reg ®) { + const static char *_str_tab[] = { + "x0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", + }; + assert((unsigned)reg < 32); + return _str_tab[(unsigned)reg]; +} + +inline bool is_callee_saved(const RV64Reg ®) { + return reg == RV64Reg::sp || reg == RV64Reg::ra || reg == RV64Reg::s0 || reg == RV64Reg::s1 || + (RV64Reg::s2 <= reg && reg <= RV64Reg::s11); } // riscv calling convention see: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc @@ -55,6 +64,9 @@ const unsigned XLEN_MASK = XLEN - 1; inline unsigned xlen_rnd_up(unsigned src) { return (src & XLEN_MASK) ? (src & (~XLEN_MASK)) + XLEN : src; } +inline bool is_in_imm_range(int imm) { + return -2048 <= imm && imm <= 2047; +} inline std::string RV64_RegName(RV64Reg reg) { std::string regname = "x" + std::to_string((int)reg); @@ -76,16 +88,23 @@ enum class MOpTag { Imm, PreColor, Colored, + Glob, }; -inline const char* enum_to_string(const MOpTag& tag) { - const static char* _str_tab[] = { "Invalid", "Virt", "Imm", "PreColor", "Colored" }; +inline const char *enum_to_string(const MOpTag &tag) { + const static char *_str_tab[] = {"Invalid", "Virt", "Imm", "PreColor", "Colored", "Glob"}; return _str_tab[(unsigned)tag]; } +// TODO Basic idea: value = id of globalvar in glob_list + class MOperand { public: MOpTag op_type = MOpTag::Invalid; int value = ~0; + + static inline std::unordered_map *mcmod_global_id_map = nullptr; + static inline std::vector *mcmod_global_list = nullptr; + static MOperand VirtReg(int reg_no) { auto mop = MOperand({MOpTag::Virt, reg_no}); return mop; @@ -104,6 +123,11 @@ public: static MOperand AllocReg(RV64Reg phy_reg) { return MOperand({MOpTag::Colored, (int)phy_reg}); } + + static MOperand Glob(sptr(GlobalVar) glob) { + return MOperand({MOpTag::Glob, mcmod_global_id_map->at(glob)}); + } + bool operator==(const MOperand &op2) const { return op_type == op2.op_type && value == op2.value; } @@ -113,21 +137,36 @@ public: bool operator<(const MOperand &op2) const { return op_type == op2.op_type ? value < op2.value : op_type < op2.op_type; } - bool need_clr() const { - return op_type == MOpTag::PreColor || op_type == MOpTag::Virt; + bool is_precolored() const { + return op_type == MOpTag::PreColor; } - std::string to_string() { - std::string ret = enum_to_string(op_type); - ret += "("; - switch (op_type) { - case MOpTag::Imm: - case MOpTag::Virt: ret += std::to_string(value); break; - case MOpTag::PreColor: ret += enum_to_string((RV64Reg)value); break; - case MOpTag::Colored: ret += enum_to_string((RV64Reg)value); break; - case MOpTag::Invalid: assert(0); - } - ret += ")"; - return ret; + bool need_clr() const { + return is_precolored() || op_type == MOpTag::Virt; + } + bool is_imm() const { + return op_type == MOpTag::Imm; + } + bool is_glob() const { + return op_type == MOpTag::Glob; + } + bool is_reg() const { + return op_type == MOpTag::PreColor || op_type == MOpTag::Virt || op_type == MOpTag::Colored; + } + std::string to_string(); +}; + +struct MOperandHash { + std::size_t operator()(MOperand const &op) const { + using std::hash; + auto h1 = std::hash{}((int)op.op_type); + auto h2 = std::hash{}(op.value); + return h1 ^ (h2 << 1); + } + std::size_t operator()(std::pair const &op) const { + using std::hash; + auto h1 = MOperandHash{}(op.first); + auto h2 = MOperandHash{}(op.second); + return h1 ^ (h2 << 1); } }; @@ -143,8 +182,6 @@ enum class MInstTag { Gt, Eq, Ne, - And, - Or, Lsh, // sll Rsh, // srl,sra Move, // actually a pseudo, mv = addi rt, rs, 0 @@ -153,9 +190,7 @@ enum class MInstTag { Ret, Load, Store, - Compare, Call, - Globsym, Comment, }; @@ -174,10 +209,17 @@ inline MInstTag inverse_cond(MInstTag src_tag) { class MInst { public: MInstTag inst_tag; + // Notes about WIDTH: + // width is important when load/store to an array address + // in other cases, since stack is XLEN-padded, for single signed int variable, ld/lw sd/sw do not make much difference + // Similarly, it makes difference when computing address + int width = 4; sptr(MBasicBlock) parent_bb; MInst(MInstTag tag, sptr(MBasicBlock) parent_bb) : inst_tag(tag), parent_bb(parent_bb) {} - virtual ~MInst() = default; + virtual ~MInst() = default; + virtual std::vector get_def() = 0; + virtual std::vector get_use() = 0; }; class MBasicBlock { @@ -194,15 +236,19 @@ public: std::set def; std::set livein; std::set liveout; + + std::string to_string(); }; class MFunction { public: + int id; std::list bb_list; sptr(Function) ir_func; std::list stack_arg_reloc; - unsigned stack_size; + std::unordered_set regs_to_save; + unsigned stack_size; // only for local var, excluding the callee-saved regs unsigned virt_reg_cnt = 0; }; @@ -211,9 +257,11 @@ class MCModule { public: std::string file_name; std::list function_list; - std::list global_list; + std::vector global_list; + std::unordered_map global_id_map; void IR2MC(const Module &ir_module); - void MC2ASM(std::ostream& ostr); + void MC2ASM(std::ostream &ostr) const; + bool debug1 = false; }; class MInstBinary : public MInst { @@ -223,11 +271,14 @@ public: MOperand op2; MInstBinary(MInstTag type, sptr(MBasicBlock) parent_bb) : MInst(type, parent_bb) {} - static sptr(MInstBinary) New(MInstTag type, sptr(MBasicBlock) parent_bb) { + static sptr(MInstBinary) New(MInstTag type, sptr(MBasicBlock) parent_bb, int width) { auto inst = make_shared(type, parent_bb); parent_bb->inst_list.push_back(inst); + inst->width = width; return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstJump : public MInst { @@ -240,6 +291,8 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstBranch : public MInst { @@ -254,6 +307,8 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstLoad : public MInst { @@ -263,20 +318,24 @@ public: MOperand offset; MInstLoad(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {} - static sptr(MInstLoad) New(sptr(MBasicBlock) parent_bb) { + static sptr(MInstLoad) New(sptr(MBasicBlock) parent_bb, int width) { auto inst = make_shared(parent_bb); parent_bb->inst_list.push_back(inst); + inst->width = width; return inst; } - static sptr(MInstLoad) New(sptr(MInst) rel_inst, bool insert_after = false) { + static sptr(MInstLoad) New(sptr(MInst) rel_inst, int width, bool insert_after = false) { auto parent_bb = rel_inst->parent_bb; auto inst = make_shared(parent_bb); auto rel_inst_itr = STD_FIND(parent_bb->inst_list, rel_inst); assert(rel_inst_itr != parent_bb->inst_list.end()); if (insert_after) std::advance(rel_inst_itr, 1); parent_bb->inst_list.insert(rel_inst_itr, inst); + inst->width = width; return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstStore : public MInst { @@ -284,22 +343,26 @@ public: MOperand data; MOperand addr; MOperand offset; - MInstStore(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {} + MInstStore(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Store, parent_bb) {} - static sptr(MInstStore) New(sptr(MBasicBlock) parent_bb) { + static sptr(MInstStore) New(sptr(MBasicBlock) parent_bb, int width) { auto inst = make_shared(parent_bb); parent_bb->inst_list.push_back(inst); + inst->width = width; return inst; } - static sptr(MInstStore) New(sptr(MInst) rel_inst, bool insert_after = false) { + static sptr(MInstStore) New(sptr(MInst) rel_inst, int width, bool insert_after = false) { auto parent_bb = rel_inst->parent_bb; auto inst = make_shared(parent_bb); auto rel_inst_itr = STD_FIND(parent_bb->inst_list, rel_inst); assert(rel_inst_itr != parent_bb->inst_list.end()); if (insert_after) std::advance(rel_inst_itr, 1); parent_bb->inst_list.insert(rel_inst_itr, inst); + inst->width = width; return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstMove : public MInst { @@ -324,22 +387,24 @@ public: parent_bb->inst_list.insert(rel_inst_itr, inst); return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; -class MInstSymbol : public MInst { -public: - MOperand dst; - sptr(GlobalVar) symbol; - MInstSymbol(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Globsym, parent_bb) {} - static sptr(MInstSymbol) New(sptr(MBasicBlock) parent_bb, bool insert_begin = false) { - auto inst = make_shared(parent_bb); - if (insert_begin) - parent_bb->inst_list.push_front(inst); - else - parent_bb->inst_list.push_back(inst); - return inst; - } -}; +// class MInstSymbol : public MInst { +// public: +// MOperand dst; +// sptr(GlobalVar) symbol; +// MInstSymbol(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Globsym, parent_bb) {} +// static sptr(MInstSymbol) New(sptr(MBasicBlock) parent_bb, bool insert_begin = false) { +// auto inst = make_shared(parent_bb); +// if (insert_begin) +// parent_bb->inst_list.push_front(inst); +// else +// parent_bb->inst_list.push_back(inst); +// return inst; +// } +// }; class MInstReturn : public MInst { public: @@ -352,6 +417,8 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstComment : public MInst { @@ -363,6 +430,8 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; class MInstCall : public MInst { @@ -374,6 +443,8 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + virtual std::vector get_def() override; + virtual std::vector get_use() override; }; void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector &use); diff --git a/include/pass.h b/include/pass.h index f467431..b3f6313 100644 --- a/include/pass.h +++ b/include/pass.h @@ -4,7 +4,7 @@ #include "llir_module.h" #include "machcode.h" -#define DEBUG_REGALLOC +// #define DEBUG_REGALLOC namespace CompSysY { class Pass { @@ -39,8 +39,19 @@ class PassRegAlloc : public MCPass { public: PassRegAlloc() : MCPass("regalloc") {} virtual void run(const MCModule &module) override; - const static int K = 32 - 4; // Not Allocate-able: x0, gp, tp; Reserve: sp + const static int K = 32 - 5; // Not Allocate-able: x0, gp, tp; Reserve: sp, ra private: + /*以下这些集合**各不相交** + precolored:机器寄存器集合,每个寄存器都预先指派了一种颜色 + initial:临时寄存器集合,其中的元素既没有预着色,也没有被处理 + simplifyWorklist:低度数的传送无关的结点表。 + freezeWorklist:低度数的传送有关的结点表。 + spillWorklist:高度数的结点表。 + spilledNodes:在本轮中要被溢出的结点集合,初始为空。 + coalescedNodes:已合并的寄存器集合。当合并uV时,将y加人到这个集合中,w则被放回到某个工作表中(或反之)。 + coloredNodes :已成功着色的结点集合。 + selectstack:一个包含从图中删除的临时变量的栈 + */ void reg_alloc(sptr(MFunction)); void build(sptr(MFunction)); void add_edge(const MOperand &u, const MOperand &v); @@ -68,6 +79,7 @@ private: std::map> move_list; std::map color; std::map alias; + std::set initial; std::set simplify_worklist; std::set freeze_worklist; std::set spill_worklist; @@ -81,26 +93,7 @@ private: std::set worklist_moves; std::set active_moves; - void clear() { - adj_list.clear(); - adj_set.clear(); - degree.clear(); - move_list.clear(); - color.clear(); - alias.clear(); - simplify_worklist.clear(); - freeze_worklist.clear(); - spill_worklist.clear(); - spilled_nodes.clear(); - coalesced_nodes.clear(); - colored_nodes.clear(); - select_stack.clear(); - coalesced_moves.clear(); - constrained_moves.clear(); - frozen_moves.clear(); - worklist_moves.clear(); - active_moves.clear(); - } + void clear(); }; } // namespace CompSysY \ No newline at end of file diff --git a/include/visitor.h b/include/visitor.h index cc6e652..c832491 100644 --- a/include/visitor.h +++ b/include/visitor.h @@ -101,7 +101,7 @@ private: std::shared_ptr current_bb = {}; std::vector *arr_dim_list = nullptr; int arr_dim_index = 0; - int local_const_arr_cnt = 0; + int local_const_arr_cnt = 0; struct loop_record { BasicBlockPtr_t cond, body, next; int id; diff --git a/scripts/mytester.py b/scripts/mytester.py index 20398a1..a4710be 100644 --- a/scripts/mytester.py +++ b/scripts/mytester.py @@ -197,13 +197,13 @@ class BatTest: self.label = f"{compiler.scheme['name']}_{compiler_rf.scheme['name']}" self.diffout_template = f"{compiler.target_dir}/{{testcase}}/{self.label}.diff" - def bat_case(self, testcase, compile=False): + def bat_case(self, testcase, compile=False, emit_llvm=True): Print_C.print_subheader(f"Diff test {self.label} on {testcase}") if compile: self.compiler.clean_case(testcase) self.compiler_rf.clean_case(testcase) - self.compiler.compile_case(testcase) - self.compiler_rf.compile_case(testcase) + self.compiler.compile_case(testcase, emit_llvm) + self.compiler_rf.compile_case(testcase, emit_llvm) if not compiler.run_case(testcase, 0): return False if not compiler_rf.run_case(testcase, 0): return False myout = compiler.myout_template.format(testcase=testcase) @@ -217,10 +217,10 @@ class BatTest: return False return True - def bat_all_tests(self, compile=False): + def bat_all_tests(self, compile=False, emit_llvm=True): for testcase in self.testcases: - if not self.bat_case(testcase, compile): break + if not self.bat_case(testcase, compile, emit_llvm): break scheme_ref = { "name": "ref", @@ -228,7 +228,7 @@ scheme_ref = { "ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}", "asm_obj": "as --32 {asm} -o {obj}", "obj_bin": f"clang -m32 -Ofast -fPIE {{obj}} {LIB_PATH} -o {{bin}}", - "sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}" + "sy_asm": f"clang -x c -c -fPIE -m32 -S -no-integrated-as -include {HDR_PATH} {{sy}} -o {{asm}}" } scheme_my = { @@ -237,7 +237,17 @@ scheme_my = { "ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}", "asm_obj": "as --32 {asm} -o {obj}", "obj_bin": f"clang -m32 -Ofast -fPIE {{obj}} {LIB_PATH} -o {{bin}}", - "sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}" + # "sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}" +} + +scheme_my_rv64 = { + "name": "my_rv64", + "sy_ir": f"build/sysy -S {{sy}} -o {{ir}} -emit-llvm -O1 -no-asm", + "ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}", + "asm_obj": "riscv64-linux-gnu-as {asm} -o {obj}", + "obj_bin": f"riscv64-linux-gnu-gcc -static {{obj}} {LIB_PATH_RV64} -o {{bin}}", + "sy_asm": f"build/sysy -S {{sy}} -o {{asm}} -emit-llvm -O1", + "run": f"spike /usr/local/riscv64-linux-gnu/bin/pk {{bin}}", } # it is really annoying to debug compile commands @@ -248,11 +258,11 @@ scheme_my = { # https://discourse.llvm.org/t/cant-link-soft-float-modules-with-double-float-modules/67521 scheme_ref_rv64 = { "name" : "ref_rv64", - "sy_ir": f"clang -x c -c -fPIE -S -emit-llvm --target=riscv64-linux-gnu -include {HDR_PATH} {{sy}} -o {{ir}}", + "sy_ir": f"clang -x c -c -fPIE -fno-stack-protector -S -emit-llvm --target=riscv64-linux-gnu -include {HDR_PATH} {{sy}} -o {{ir}}", "ir_asm": "llc --march=riscv64 --relocation-model=pic --float-abi=hard -mcpu=sifive-7-rv64 -mattr=+f,+m,+d {ir} -o {asm}", "asm_obj": "riscv64-linux-gnu-as {asm} -o {obj}", "obj_bin": f"riscv64-linux-gnu-gcc -static {{obj}} {LIB_PATH_RV64} -o {{bin}}", - "sy_asm": f"clang -x c -S -no-integrated-as --target=riscv64-linux-gnu -march=rv64gc -fPIE -mfloat-abi=hard -mcpu=sifive-7-rv64 -include {HDR_PATH} {{sy}} -o {{asm}}", + "sy_asm": f"clang -x c -S -fno-stack-protector -no-integrated-as --target=riscv64-linux-gnu -march=rv64gc -fPIE -mfloat-abi=hard -mcpu=sifive-7-rv64 -include {HDR_PATH} {{sy}} -o {{asm}}", "run": f"spike /usr/local/riscv64-linux-gnu/bin/pk {{bin}}", } @@ -260,6 +270,7 @@ schemes = { "my": scheme_my, "ref": scheme_ref, "ref_rv64": scheme_ref_rv64, + "my_rv64": scheme_my_rv64, } # subprocess.run(f"llc -O3 -march=arm -mcpu=cortex-a72 -float-abi=hard -filetype=asm {ir} -o {asm}".split(), stdout=log_file, stderr=log_file, bufsize=1) # subprocess.run(f"as -march=armv7-a -mfloat-abi=hard {asm} -o {obj}".split(), stdout=log_file, stderr=log_file, bufsize=1) @@ -271,10 +282,12 @@ if __name__ == "__main__": parser.add_argument('--scheme', default='ref') parser.add_argument('--case_prefix', default='testcases/functional') parser.add_argument('--case_selector', default='.*\.sy') + parser.add_argument('--ref', default='ref') parser.add_argument("--clean", action='store_true', default=False) parser.add_argument('--compile', action='store_true', default=False) parser.add_argument('--run', action='store_true', default=False) parser.add_argument('--bat', action='store_true', default=False) + parser.add_argument('--emit_llvm', action='store_true', default=False) args = parser.parse_args() print(args) testcases = case_collector(args.case_selector, args.case_prefix) @@ -292,13 +305,13 @@ if __name__ == "__main__": current_schem = schemes[args.scheme] if args.bat: compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases) - compiler_rf = Compiler(scheme=schemes["ref"],target_dir=target_dir, testcases=testcases) + compiler_rf = Compiler(scheme=schemes[args.ref],target_dir=target_dir, testcases=testcases) battester = BatTest(compiler, compiler_rf, testcases) - battester.bat_all_tests(args.compile) + battester.bat_all_tests(args.compile, args.emit_llvm) elif args.compile: compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases) compiler.prepare_dir() - compiler.compile_all_tests(error_tolerance=1, emit_llvm_ir=True) + compiler.compile_all_tests(error_tolerance=1, emit_llvm_ir=args.emit_llvm) if args.run: compiler.run_all_tests() elif args.run: diff --git a/src/main.cpp b/src/main.cpp index fe7a20f..2f0959c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,7 +15,7 @@ #include #include -#define ELPP_NO_LOG_TO_FILE +#define ELPP_NO_LOG_TO_FILE INITIALIZE_EASYLOGGINGPP using namespace CompSysY; @@ -46,6 +46,9 @@ int main(int argc, const char **argv) { arg_parser.add_argument("-O1").implicit_value(true).default_value(false).help("Performance mode"); arg_parser.add_argument("-O0").implicit_value(true).default_value(false).help("Rudimentary mode"); arg_parser.add_argument("-emit-llvm").implicit_value(true).default_value(false).help("Generate llvm ir"); + arg_parser.add_argument("-no-asm").implicit_value(true).default_value(false).help( + "Disable asm output for frontend test" + ); arg_parser.add_argument("--v").default_value(0); try { arg_parser.parse_args(argc, argv); @@ -59,6 +62,7 @@ int main(int argc, const char **argv) { auto flg_O1 = arg_parser["-O1"] == true; auto flg_O0 = arg_parser["-O0"] == true; auto emit_llvm = arg_parser["-emit-llvm"] == true; + auto no_asm = arg_parser["-no-asm"] == true; // std::cout << source_file << " " << output_file << " " << flg_O1 << // std::endl; #pragma endregion @@ -127,12 +131,27 @@ int main(int argc, const char **argv) { // std::cout << tree->toStringTree(&parser) << std::endl << std::endl; - MCModule mc_module; - mc_module.file_name = source_file; - mc_module.IR2MC(visitor.module); - std::vector mc_passes = {std::make_shared()}; - for (auto pass : mc_passes) { - pass->run(mc_module); + if (!no_asm) { + MCModule mc_module; + mc_module.file_name = source_file; + mc_module.IR2MC(visitor.module); + // asm before reg alloc + auto virt_asm_file = output_file.substr(0, output_file.rfind(".")) + "_virt.asm"; + std::ofstream ofs_virt_asm_file(virt_asm_file); + mc_module.debug1 = true; + mc_module.MC2ASM(ofs_virt_asm_file); + mc_module.debug1 = false; + + std::vector mc_passes = {std::make_shared()}; + for (auto pass : mc_passes) { + pass->run(mc_module); + } + std::ofstream ofs_asm_file(output_file); + if (!ofs_asm_file.good()) { + LOG(ERROR) << "Failed to create/overwrite asm output file " << output_file; + return 1; + } + mc_module.MC2ASM(ofs_asm_file); } return 0; } \ No newline at end of file diff --git a/src/mc_asmgen.cpp b/src/mc_asmgen.cpp index 1c7b370..e2eb896 100644 --- a/src/mc_asmgen.cpp +++ b/src/mc_asmgen.cpp @@ -1,5 +1,5 @@ -#include "mc_inst.h" #include "common.h" +#include "mc_inst.h" namespace CompSysY { @@ -28,54 +28,400 @@ static void bb_debug(std::ostream &ostr, sptr(MBasicBlock) bb) { ostr << "\n"; } -void MCModule::MC2ASM(std::ostream &ostr) { - // header, specifying align and arch - ostr << "\t.text\n" << "\t.attribute\t4,\t16\n" - << "\t.attribute\t5\t\"rv64i2p0_m2p0_f2p0_d2p0\"\n"; - ostr << "\t.file\t\"" << this->file_name << "\"\n" << endl; - for (auto func : this->function_list) { - // function header - ostr << "\n\n"; - ostr << ".global\t" << func->ir_func->name << endl; - ostr << "\t.type\t" << func->ir_func->name << "\tSTT_FUNC" << endl; - ostr << func->ir_func->name << ":" << endl; - //TODO entry code - for (auto bb : func->bb_list) { - ostr << "L_BB_" << bb->id << ":" << endl; - ostr << "/*\n"; - bb_debug(ostr, bb); - ostr << "\n*/" << endl; - for (auto inst : bb->inst_list) { - switch (inst->inst_tag) { - case MInstTag::Add: break; - case MInstTag::Sub: break; - case MInstTag::Mul: break; - case MInstTag::Div: break; - case MInstTag::Mod: break; - case MInstTag::Lt: break; - case MInstTag::Le: break; - case MInstTag::Ge: break; - case MInstTag::Gt: break; - case MInstTag::Eq: break; - case MInstTag::Ne: break; - case MInstTag::And: break; - case MInstTag::Or: break; - case MInstTag::Lsh: break; - case MInstTag::Rsh: break; - case MInstTag::Move: break; - case MInstTag::Branch: break; - case MInstTag::Jmp: break; - case MInstTag::Ret: break; - case MInstTag::Load: break; - case MInstTag::Store: break; - case MInstTag::Compare: break; - case MInstTag::Call: break; - case MInstTag::Globsym: break; - case MInstTag::Comment: break; - } +static void emit_add(std::ostream &ostr, sptr(MInstBinary) inst) { + std::string opcode; + std::string rd, rs1, rs2; + rd = inst->dst.to_string(); + if (inst->op1.is_imm() && inst->op2.is_imm()) { + LOG(WARNING) << "Unexpected ConstExpr"; + assert(0); + } + else if (inst->op1.is_imm()) { + opcode = "addi"; + rs1 = inst->op2.to_string(); + rs2 = inst->op1.to_string(); + } + else if (inst->op2.is_imm()) { + opcode = "addi"; + rs1 = inst->op1.to_string(); + rs2 = inst->op2.to_string(); + } + else { + opcode = "add"; + rs1 = inst->op1.to_string(); + rs2 = inst->op2.to_string(); + } + if (inst->width == 4) opcode += "w"; + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; +} + +static void emit_sub(std::ostream &ostr, sptr(MInstBinary) inst) { + std::string opcode; + std::string rd, rs1, rs2; + rd = inst->dst.to_string(); + if (inst->op1.is_imm() && inst->op2.is_imm()) { + LOG(WARNING) << "Unexpected ConstExpr"; + assert(0); + } + else if (inst->op1.is_imm()) { + opcode = "addi"; + rs1 = inst->op2.to_string(); + rs2 = "-" + inst->op1.to_string(); + } + else if (inst->op2.is_imm()) { + opcode = "addi"; + rs1 = inst->op1.to_string(); + rs2 = "-" + inst->op2.to_string(); + } + else { + opcode = "sub"; + rs1 = inst->op1.to_string(); + rs2 = inst->op2.to_string(); + } + if (inst->width == 4) opcode += "w"; + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; +} + +static void emit_muldivrem(std::ostream &ostr, sptr(MInstBinary) inst) { + std::string opcode; + std::string rd, rs1, rs2; + assert(!inst->op1.is_imm()); + assert(!inst->op2.is_imm()); + + // immediate should have been separatedly loaded in a mv + rd = inst->dst.to_string(); + rs1 = inst->op1.to_string(); + rs2 = inst->op2.to_string(); + switch (inst->inst_tag) { + case MInstTag::Mul: opcode = "mul"; break; + case MInstTag::Div: opcode = "div"; break; + case MInstTag::Mod: opcode = "rem"; break; + default: assert(0); + } + if (inst->width == 4) opcode += "w"; + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; +} + +static void emit_shamt(std::ostream &ostr, sptr(MInstBinary) inst) { + std::string opcode; + std::string rd, rs1, shamt; + assert(inst->op2.is_imm()); + assert(inst->width == 4); + rd = inst->dst.to_string(); + rs1 = inst->op1.to_string(); + shamt = inst->op2.to_string(); + switch (inst->inst_tag) { + case MInstTag::Lsh: opcode = "slliw"; break; + case MInstTag::Rsh: opcode = "sraiw"; break; + default: assert(0); + } + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, shamt) << endl; +} + +/* Notes about compare&set: + icmp_slt(r1 < r2) = slt r3, r1, r2 + icmp_sgt(r1 > r2) = slt r3, r2, r1 + icmp_sle(r1 <= r2) = slt r3, r2, r1; xori r3, r3, 1 + icmp_sge(r1 >= r2) = slt r3, r1, r2; xori r3, r3, 1 + icmp_eq(r1 == r2) = xor r3, r1, r2; seqz r3, r3 + icmp_ne(r1 != r2) = xor r3, r1, r2; snez r3, r3 + We can see clearly, though icmp_sge use 1 more inst than icmp_slt, + they involve the same set of registers, so it is okay to defer this to the output stage +*/ +static void emit_cmp(std::ostream &ostr, sptr(MInstBinary) inst) { + std::string opcode; + std::string rd = inst->dst.to_string(); + std::string rs1 = inst->op1.to_string(); + std::string rs2 = inst->op2.to_string(); + assert(!inst->op1.is_imm()); + bool is_imm = inst->op2.is_imm(); + switch (inst->inst_tag) { + case MInstTag::Lt: opcode = is_imm ? "slti" : "slt"; break; + case MInstTag::Gt: opcode = is_imm ? "slti" : "slt"; break; + case MInstTag::Le: opcode = is_imm ? "slti" : "slt"; break; + case MInstTag::Ge: opcode = is_imm ? "slti" : "slt"; break; + case MInstTag::Eq: opcode = is_imm ? "xori" : "xor"; break; + case MInstTag::Ne: opcode = is_imm ? "xori" : "xor"; break; + default: assert(0); + } + // mind the order of rs1,rs2 + switch (inst->inst_tag) { + case MInstTag::Lt: ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; break; + case MInstTag::Gt: ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs2, rs1) << endl; break; + case MInstTag::Le: + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs2, rs1) << endl; + ostr << fmt::format("\txori\t{}, {}, 1", rd, rd) << endl; + break; + case MInstTag::Ge: + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; + ostr << fmt::format("\txori\t{}, {}, 1", rd, rd) << endl; + break; + case MInstTag::Eq: + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; + ostr << fmt::format("\tseqz\t{}, {}, 1", rd, rd) << endl; + break; + case MInstTag::Ne: + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; + ostr << fmt::format("\tsnez\t{}, {}, 1", rd, rd) << endl; + break; + default: assert(0); + } +} + +static void emit_branch(std::ostream &ostr, sptr(MInstBranch) inst) { + std::string opcode; + std::string rs1, rs2, tgt; + assert(!inst->op1.is_imm() && !inst->op2.is_imm()); + switch (inst->branch_tag) { + case MInstTag::Lt: opcode = "blt"; break; + case MInstTag::Le: opcode = "ble"; break; + case MInstTag::Ge: opcode = "bge"; break; + case MInstTag::Gt: opcode = "bgt"; break; + case MInstTag::Eq: opcode = "beq"; break; + case MInstTag::Ne: opcode = "bne"; break; + default: assert(0); + } + rs1 = inst->op1.to_string(); + rs2 = inst->op2.to_string(); + tgt = inst->target->to_string(); + ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rs1, rs2, tgt) << endl; +} + +static void emit_move(std::ostream &ostr, sptr(MInstMove) inst) { + std::string opcode; + std::string rd, rs1; + if (inst->src.is_imm()) { + opcode = "li"; + } + else if (inst->src.is_reg()) { + opcode = "mv"; + } + else if (inst->src.is_glob()) { + opcode = "la"; + } + else { + assert(0); + } + rd = inst->dst.to_string(); + rs1 = inst->src.to_string(); + ostr << fmt::format("\t{}\t{}, {}", opcode, rd, rs1) << endl; +} + +static void emit_jmp(std::ostream &ostr, sptr(MInstJump) inst) { + ostr << "\tj\t" << inst->target->to_string() << endl; +} + +static void emit_load(std::ostream &ostr, sptr(MInstLoad) inst) { + assert(inst->offset.is_imm() && !inst->addr.is_imm()); + auto dst = inst->dst.to_string(); + auto off = inst->offset.to_string(); + auto addr = inst->addr.to_string(); + std::string opcode; + if (inst->width == 4) + opcode = "lw"; + else if (inst->width == 8) + opcode = "ld"; + ostr << fmt::format("\t{}\t{}, {}({})", opcode, dst, off, addr) << endl; +} + +static void emit_store(std::ostream &ostr, sptr(MInstStore) inst) { + assert(inst->offset.is_imm() && !inst->addr.is_imm() && !inst->data.is_imm()); + auto data = inst->data.to_string(); + auto offset = inst->offset.to_string(); + auto addr = inst->addr.to_string(); + std::string opcode; + if (inst->width == 4) + opcode = "sw"; + else if (inst->width == 8) + opcode = "sd"; + ostr << fmt::format("\t{}\t{}, {}({})", opcode, data, offset, addr) << endl; +} + +static void emit_call(std::ostream &ostr, sptr(MInstCall) inst) { + ostr << fmt::format("\tcall\t{}", inst->ir_func->name) << endl; +} + +static void emit_comment(std::ostream &ostr, sptr(MInstComment) inst) { + ostr << fmt::format("/*{}*/", inst->comment) << endl; +} + +static void emit_return(std::ostream &ostr, sptr(MInstReturn) inst) { + auto func = inst->parent_bb->parent_func; + if (func->stack_size) { + int stk_imm = func->stack_size; + if (is_in_imm_range(stk_imm)) { + ostr << fmt::format("\taddi\tsp, sp, {}\n", stk_imm); + } + else { + ostr << fmt::format("\tli\tt0, {}\n", stk_imm); + ostr << "\tadd\tsp, sp, t0\n"; + } + } + if (!func->regs_to_save.empty()) { + int pop_cnt = 0; + for (auto reg_to_save : func->regs_to_save) { + ostr << fmt::format("\tld\t{}, {}(sp)\n", enum_to_string(reg_to_save), pop_cnt * XLEN); + pop_cnt++; + } + ostr << fmt::format("\taddi\tsp, sp, {}\n", func->regs_to_save.size() * XLEN); + } + ostr << "\tret" << endl; +} + +static void emit_array(std::ostream &ostr, sptr(ConstantArr) arr) { + int zero_cnt = 0; + for (auto val : arr->value_list) { + if (val) { + if (zero_cnt) { + ostr << fmt::format("\t.zero\t{}\n", zero_cnt * 4); + zero_cnt = 0; } + auto init_val = shared_cast(val); + assert(init_val); + ostr << fmt::format("\t.word\t{0}\t\t# {0:#x}\n", init_val->value); + } + else { + zero_cnt++; } } } -} \ No newline at end of file +static void stack_postprocess(sptr(MFunction) func) { + for (auto bb : func->bb_list) { + for (auto inst : bb->inst_list) { + auto defs = inst->get_def(); + for (auto def : defs) { + if (is_callee_saved((RV64Reg)def.value)) { + func->regs_to_save.insert((RV64Reg)def.value); + } + } + } + } + for (auto inst : func->stack_arg_reloc) { + auto inst_load = shared_cast(inst); + assert(inst_load && inst_load->offset.is_imm()); + inst_load->offset.value += func->stack_size + func->regs_to_save.size() * XLEN; + } +} + +void emit_function(std::ostream &ostr, sptr(MFunction) func) { + // function header + ostr << "\t.globl\t" << func->ir_func->name << "\n"; + ostr << "\t.p2align\t1\n"; + ostr << "\t.type\t" << func->ir_func->name << ",@function\n"; + ostr << func->ir_func->name << ":" << endl; + // entry code + // First push callee-saved regs + if (!func->regs_to_save.empty()) { + ostr << fmt::format("\taddi\tsp, sp, -{}\n", func->regs_to_save.size() * XLEN); + int push_cnt = 0; + for (auto reg_to_save : func->regs_to_save) { + ostr << fmt::format("\tsd\t{}, {}(sp)\n", enum_to_string(reg_to_save), push_cnt * XLEN); + push_cnt++; + } + } + // Then deal with stack allocation + if (func->stack_size) { + int stk_imm = func->stack_size; + if (is_in_imm_range(-stk_imm)) { + ostr << fmt::format("\taddi\tsp, sp, {}\n", -stk_imm); + } + else { + ostr << fmt::format("\tli\tt0, {}\n", stk_imm); + ostr << "\tsub\tsp, sp, t0\n"; + } + } + // emit every instruction + for (auto bb : func->bb_list) { + ostr << bb->to_string() << ":" << endl; + ostr << "/*\n"; + bb_debug(ostr, bb); + ostr << "*/" << endl; + for (auto inst : bb->inst_list) { + switch (inst->inst_tag) { + case MInstTag::Add: emit_add(ostr, shared_cast(inst)); break; + case MInstTag::Sub: emit_sub(ostr, shared_cast(inst)); break; + case MInstTag::Mul: + case MInstTag::Div: + case MInstTag::Mod: emit_muldivrem(ostr, shared_cast(inst)); break; + case MInstTag::Lsh: + case MInstTag::Rsh: emit_shamt(ostr, shared_cast(inst)); break; + case MInstTag::Lt: + case MInstTag::Le: + case MInstTag::Ge: + case MInstTag::Gt: + case MInstTag::Eq: + case MInstTag::Ne: emit_cmp(ostr, shared_cast(inst)); break; + case MInstTag::Move: emit_move(ostr, shared_cast(inst)); break; + case MInstTag::Branch: emit_branch(ostr, shared_cast(inst)); break; + case MInstTag::Jmp: emit_jmp(ostr, shared_cast(inst)); break; + case MInstTag::Ret: emit_return(ostr, shared_cast(inst)); break; + case MInstTag::Call: emit_call(ostr, shared_cast(inst)); break; + case MInstTag::Load: emit_load(ostr, shared_cast(inst)); break; + case MInstTag::Store: emit_store(ostr, shared_cast(inst)); break; + case MInstTag::Comment: emit_comment(ostr, shared_cast(inst)); break; + } + } + } + ostr << fmt::format(".Lfunc_end{}:\n", func->id); + ostr << fmt::format("\t.size\t{}, .Lfunc_end{}-{}\n", func->ir_func->name, func->id, func->ir_func->name); + ostr << fmt::format("/* -- End Function {} --*/", func->ir_func->name) << endl; +} + +void MCModule::MC2ASM(std::ostream &ostr) const { + // header, specifying align and arch + ostr << "\t.text\n" + << "\t.attribute\t4,\t16\n" + << "\t.attribute\t5,\t\"rv64i2p0_m2p0_f2p0_d2p0\"\n"; + ostr << "\t.file\t\"" << this->file_name << "\"\n" << endl; + for (auto func : this->function_list) { + if (!debug1) stack_postprocess(func); + emit_function(ostr, func); + } + + ostr << "\n/* -- data section -- */\n.data\n"; + for (auto glob : this->global_list) { + if (!glob->init_value) continue; + ostr << fmt::format( + "\t.type\t{0},@object\n" + "\t.globl\t{0}\n" + "\t.p2align\t2\n" + "{0}:\n", + glob->name + ); + int glob_size = 0; + if (auto initval = shared_cast(glob->init_value)) { + ostr << fmt::format("\t.word\t{0}\t\t# {0:#x}\n", initval->value); + glob_size = 4; + } + else { + auto init_arr = shared_cast(glob->init_value); + emit_array(ostr, init_arr); + glob_size = init_arr->get_memory_size(); + } + ostr << fmt::format("\t.size\t{}, {}\n", glob->name, glob_size) << endl; + } + ostr << "/* -- bss section -- */\n.bss\n"; + for (auto glob : this->global_list) { + if (glob->init_value) continue; + ostr << fmt::format( + "\t.type\t{0},@object\n" + "\t.globl\t{0}\n" + "\t.p2align\t2\n" + "{0}:\n", + glob->name + ); + int glob_size = 0; + if (shared_cast(shared_cast(glob->type)->pointed_type)) { + glob_size = 4; + } + else { + glob_size = get_type_size(shared_cast(shared_cast(glob->type)->pointed_type)); + } + ostr << fmt::format("\t.zero\t{}", glob_size) << endl; + ostr << fmt::format("\t.size\t{}, {}", glob->name, glob_size) << endl; + } + ostr << "\n\t.ident\t\"CompSysY\"\n\t.section\t\".note.GNU-stack\",\"\",@progbits" << endl; +} + +} // namespace CompSysY \ No newline at end of file diff --git a/src/mc_codegen.cpp b/src/mc_codegen.cpp index a6f9be2..8d7a161 100644 --- a/src/mc_codegen.cpp +++ b/src/mc_codegen.cpp @@ -7,14 +7,17 @@ using std::make_shared; namespace CompSysY { -static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb) { +static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb, bool force_reg = false) { auto operand = MOperand::Imm(imm); // 12 bit signed imm for I/S-type - if (-2048 <= imm && imm <= 2047) { + if (!force_reg && -2048 <= imm && imm <= 2047) { return operand; } // load to register, should use pseudo `mv` // TODO TrivialCompiler added a opt trick here, insert before a control tansfer? + if (imm == 0) { + return MOperand::PreClrReg(RV64Reg::x0); + } auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); auto inst_move = MInstMove::New(mc_bb); inst_move->src = operand; @@ -25,7 +28,8 @@ static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb) { static MOperand value2moperand( sptr(Value) ir_value, sptr(MBasicBlock) mc_bb, - std::unordered_map &val2mop + std::unordered_map &val2mop, + bool force_reg = false ) { if (auto fparam = shared_cast(ir_value)) { auto itr = val2mop.find(ir_value); @@ -50,9 +54,15 @@ static MOperand value2moperand( // this need to be further re-located since sp may have changed // FramePtr won't get used here, for perf reason. Ref: // https://stackoverflow.com/questions/13006371/does-omitting-the-frame-pointers-really-have-a-positive-effect-on-performance-an - // TODO Trivial Compiler(THU2020) use an addition move, but I am not sure why, deleted temporally + // TODO Trivial Compiler(THU2020) use an addition move, ~but I am not sure why, deleted temporally~ + // In-case the stack is too large that exceeds limit of imm field, but We first skip this // auto vr_tmp = MOperand::NewVirtReg(mc_bb->parent_func->virt_reg_cnt++); - auto inst_load = MInstLoad::New(mc_bb); // ld vr, (i-8)*8(sp) + int width = 0; + if (shared_cast(fparam->type)) + width = 4; + else if (shared_cast(fparam->type)) + width = XLEN; + auto inst_load = MInstLoad::New(mc_bb, width); // ld vr, (i-8)*8(sp) // auto inst_move = MInstMove::New(inst_load); // lui vr_t, inst_load->addr = MOperand::PreClrReg(RV64Reg::sp); inst_load->offset = MOperand::Imm((fparam_ndx - 8) * XLEN); @@ -66,16 +76,26 @@ static MOperand value2moperand( if (itr != val2mop.end()) { return itr->second; } - auto inst_symld = MInstSymbol::New(mc_bb->parent_func->bb_list.front(), true); - auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); - val2mop.insert({ir_value, vr}); - inst_symld->symbol = glob; - inst_symld->dst = vr; - return vr; + // auto inst_symld = MInstSymbol::New(mc_bb->parent_func->bb_list.front(), true); + // auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); + // val2mop.insert({ir_value, vr}); + // inst_symld->symbol = glob; + // inst_symld->dst = vr; + // return vr; + auto op_glob = MOperand::Glob(glob); + if (force_reg) { + auto inst_la = MInstMove::New(mc_bb); + auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); + val2mop.insert({ir_value, vr}); + inst_la->dst = vr; + inst_la->src = op_glob; + return vr; + } + return op_glob; } else if (auto constant = shared_cast(ir_value)) { auto imm = constant->value; - return gen_imm(imm, mc_bb); + return gen_imm(imm, mc_bb, force_reg); } else { // plain situation @@ -93,23 +113,29 @@ static MOperand value2moperand( void MCModule::IR2MC(const Module &ir_module) { // Simply copy globals, since they don't need any translation - int bb_id_cnt = 0; + int bb_id_cnt = 0; + int fn_id_cnt = 0; + MOperand::mcmod_global_id_map = &global_id_map; + MOperand::mcmod_global_list = &global_list; + for (auto glob : ir_module.global_var_list) { + this->global_id_map.insert({glob, global_list.size()}); this->global_list.push_back(glob); } - + for (auto func : ir_module.function_list) { if (func->is_libfunc()) continue; auto mc_func = make_shared(); this->function_list.push_back(mc_func); mc_func->ir_func = func; + mc_func->id = fn_id_cnt++; // copy pred/succ info std::unordered_map bb_ir2mc; for (auto bb : func->bb_list) { - auto mc_bb = make_shared(); - mc_bb->id = bb_id_cnt ++; - mc_bb->itr = mc_func->bb_list.insert(mc_func->bb_list.end(), mc_bb); + auto mc_bb = make_shared(); + mc_bb->id = bb_id_cnt++; + mc_bb->itr = mc_func->bb_list.insert(mc_func->bb_list.end(), mc_bb); mc_bb->ir_bb = bb; mc_bb->parent_func = mc_func; bb_ir2mc.insert({bb, mc_bb}); @@ -133,39 +159,44 @@ void MCModule::IR2MC(const Module &ir_module) { auto mc_bb = bb_ir2mc[bb]; for (auto inst : bb->inst_list) { if (auto ld = shared_cast(inst)) { - auto addr = value2moperand(ld->operand_list[0], mc_bb, mp_val2op); - auto mc_li = MInstLoad::New(mc_bb); - mc_li->addr = addr; - mc_li->dst = value2moperand(ld, mc_bb, mp_val2op); - // mc_li->offset = nullptr; + auto addr = value2moperand(ld->operand_list[0], mc_bb, mp_val2op, true); + // width is important when load/store to an array address, so be careful + int width = XLEN; + if (shared_cast(ld->type)) width = 4; + auto mc_li = MInstLoad::New(mc_bb, width); + mc_li->addr = addr; + mc_li->dst = value2moperand(ld, mc_bb, mp_val2op); + mc_li->offset = MOperand::Imm(0); continue; } if (auto st = shared_cast(inst)) { - auto data = value2moperand(st->operand_list[0], mc_bb, mp_val2op); - auto addr = value2moperand(st->operand_list[1], mc_bb, mp_val2op); - auto mc_li = MInstStore::New(mc_bb); - mc_li->addr = addr; - mc_li->data = data; - // mc_li->offset = nullptr; + auto data = value2moperand(st->operand_list[0], mc_bb, mp_val2op, true); + auto addr = value2moperand(st->operand_list[1], mc_bb, mp_val2op, true); + int width = XLEN; + if (shared_cast(st->operand_list[0]->type)) width = 4; + auto mc_li = MInstStore::New(mc_bb, width); + mc_li->addr = addr; + mc_li->data = data; + mc_li->offset = MOperand::Imm(0); continue; } if (auto gep = shared_cast(inst)) { // gep ptr, index0, index1 // tgt_addr = ptr + index0 * sizeof(elem_ty) + index1 * sizeof(elem_ty->elem_ty) auto dst = value2moperand(inst, mc_bb, mp_val2op); - auto ptr = value2moperand(inst->operand_list[0], mc_bb, mp_val2op); + auto ptr = value2moperand(inst->operand_list[0], mc_bb, mp_val2op, true); auto index0 = value2moperand(inst->operand_list[1], mc_bb, mp_val2op); MOperand index1; assert(inst->operand_list.size() <= 3); if (inst->operand_list.size() == 3) { - assert(index0.op_type == MOpTag::Imm && index0.value == 0); + assert(index0.is_imm() && index0.value == 0); index1 = value2moperand(inst->operand_list[2], mc_bb, mp_val2op); } // %dst = getelementptr [2x[3xi32]] [2x[3xi32]]* %ptr, i32 0, i32 1 auto ptr_type = gep->operand_list[0]->type; auto elem_type = shared_cast(ptr_type)->pointed_type; - if ((inst->operand_list.size() < 3 && !index0.value) || (inst->operand_list.size() == 3 && !index1.value)) { + if ((inst->operand_list.size() < 3 && !index0.value) || (inst->operand_list.size() == 3 && index1.is_imm() && !index1.value)) { // a shortcut for zero gep auto inst_mv = MInstMove::New(mc_bb); inst_mv->dst = dst; @@ -174,36 +205,39 @@ void MCModule::IR2MC(const Module &ir_module) { } else if (inst->operand_list.size() < 3) { // index on same dim: addr = ptr + index0 * sizeof(elem_ty) + if (index0.is_imm()) index0 = gen_imm(index0.value, mc_bb, true); auto elem_size = get_type_size(elem_type); - auto elem_size_imm = gen_imm(elem_size, mc_bb); + auto elem_size_imm = gen_imm(elem_size, mc_bb, true); auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++); - auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb); + auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb, XLEN); inst_mul->dst = vr; inst_mul->op1 = index0; inst_mul->op2 = elem_size_imm; - auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb); + auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb, XLEN); inst_add->dst = dst; - inst_add->op1 = ptr; - inst_add->op2 = vr; + inst_add->op1 = vr; + inst_add->op2 = ptr; VLOG(6) << "gep ptr + elem_ty * index"; } else { // index on sub dim: addr = ptr + index1 * sizeof(elem_ty.elem_ty) + if (index1.is_imm()) index1 = gen_imm(index1.value, mc_bb, true); auto elem_elem_size = get_pointed_type_size(elem_type); - auto elem_elem_size_imm = gen_imm(elem_elem_size, mc_bb); + auto elem_elem_size_imm = gen_imm(elem_elem_size, mc_bb, true); auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++); - auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb); + auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb, XLEN); inst_mul->dst = vr; - inst_mul->op1 = index0; + inst_mul->op1 = index1; inst_mul->op2 = elem_elem_size_imm; - auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb); + auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb, XLEN); inst_add->dst = dst; - inst_add->op1 = ptr; - inst_add->op2 = vr; + inst_add->op1 = vr; + inst_add->op2 = ptr; VLOG(6) << "gep ptr + elem_ty.elem_ty * index"; } + continue; } - else if (auto alc = shared_cast(inst)) { + if (auto alc = shared_cast(inst)) { unsigned alloca_size = 0; auto allocated_type = get_pointed_type(alc->type); if (shared_cast(allocated_type) || shared_cast(allocated_type)) { @@ -220,14 +254,17 @@ void MCModule::IR2MC(const Module &ir_module) { assert(alloca_size && !(alloca_size & XLEN_MASK)); auto dst = value2moperand(inst, mc_bb, mp_val2op); auto stk_sz_imm = gen_imm(mc_func->stack_size, mc_bb); - auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb); + auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb, XLEN); inst_add->dst = dst; inst_add->op1 = MOperand::PreClrReg(RV64Reg::sp); inst_add->op2 = stk_sz_imm; // dont forget to record stack usage mc_func->stack_size += alloca_size; + LOG(TRACE) << "Allocated " << alloca_size << "Bytes on stack for " << alc->name + << ", stack size=" << mc_func->stack_size; + continue; } - else if (auto ret = shared_cast(inst)) { + if (auto ret = shared_cast(inst)) { if (ret->operand_list.size()) { auto retval = value2moperand(ret->operand_list[0], mc_bb, mp_val2op); auto inst_mv = MInstMove::New(mc_bb); @@ -235,8 +272,9 @@ void MCModule::IR2MC(const Module &ir_module) { inst_mv->dst = MOperand::PreClrReg(RV64Reg::a0); } MInstReturn::New(mc_bb); + continue; } - else if (auto cal = shared_cast(inst)) { + if (auto cal = shared_cast(inst)) { auto target_func = cal->operand_list[0]; int nparams = cal->operand_list.size() - 1; for (int i = 1; i < cal->operand_list.size(); ++i) { @@ -249,7 +287,7 @@ void MCModule::IR2MC(const Module &ir_module) { else { int st_off = -(nparams - (i - 1)) * XLEN; auto st_off_imm = gen_imm(st_off, mc_bb); - auto inst_store = MInstStore::New(mc_bb); + auto inst_store = MInstStore::New(mc_bb, XLEN); inst_store->addr = MOperand::PreClrReg(RV64Reg::sp); inst_store->offset = st_off_imm; inst_store->data = rparam; @@ -280,8 +318,9 @@ void MCModule::IR2MC(const Module &ir_module) { inst_mv->src = MOperand::PreClrReg(RV64Reg::a0); inst_mv->dst = dst; } + continue; } - else if (auto br = shared_cast(inst)) { + if (auto br = shared_cast(inst)) { if (br->operand_list.size() == 1) { auto inst_jump = MInstJump::New(mc_bb); auto target = br->operand_list.front(); @@ -290,8 +329,8 @@ void MCModule::IR2MC(const Module &ir_module) { else { if (mp_br2icmp.find(dynamic_cast(br.get())) != mp_br2icmp.end()) { auto cond = mp_br2icmp.at(dynamic_cast(br.get())); - auto op1 = value2moperand(cond->operand_list[0], mc_bb, mp_val2op); - auto op2 = value2moperand(cond->operand_list[1], mc_bb, mp_val2op); + auto op1 = value2moperand(cond->operand_list[0], mc_bb, mp_val2op, true); + auto op2 = value2moperand(cond->operand_list[1], mc_bb, mp_val2op, true); auto inst_br = MInstBranch::New(mc_bb); inst_br->op1 = op1; inst_br->op2 = op2; @@ -308,42 +347,59 @@ void MCModule::IR2MC(const Module &ir_module) { // true branch is the next block, while false branch is faraway // branch to false branch and inverse the condition inst_br->branch_tag = inverse_cond(inst_br->branch_tag); - inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[1])); + inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[2])); } else if (*std::next(mc_bb->ir_bb->itr) == br->operand_list[2]) { - inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[2])); + inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[1])); } else { panic("Unexpected branch pattern"); } } else { - auto cond = value2moperand(br->operand_list[0], mc_bb, mp_val2op); + auto cond = value2moperand(br->operand_list[0], mc_bb, mp_val2op, true); auto inst_br = MInstBranch::New(mc_bb); inst_br->op1 = cond; inst_br->op2 = MOperand::PreClrReg(RV64Reg::x0); inst_br->branch_tag = MInstTag::Ne; if (*std::next(mc_bb->ir_bb->itr) == br->operand_list[1]) { inst_br->branch_tag = inverse_cond(inst_br->branch_tag); - inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[1])); + inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[2])); } else if (*std::next(mc_bb->ir_bb->itr) == br->operand_list[2]) { - inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[2])); + inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[1])); } else { panic("Unexpected branch pattern"); } } } + continue; } - else if (auto bin = shared_cast(inst)) { + if (auto bin = shared_cast(inst)) { auto op1 = inst->operand_list[0]; auto op2 = inst->operand_list[1]; // Frontend should promise constant expr to be eliminated // assert(!(shared_cast(op1) && shared_cast(op2))); if (shared_cast(op1) && shared_cast(op2)) { LOG(WARNING) << "Constant expr not eliminated: " << bin->to_string(); - auto res = shared_cast(op1)->value + shared_cast(op2)->value; + auto val1 = shared_cast(op1)->value; + auto val2 = shared_cast(op2)->value; + auto res = ~0; + switch (inst->tag) { + case InstTag::Add: res = val1 + val2; break; + case InstTag::Sub: res = val1 - val2; break; + case InstTag::Mod: res = val1 % val2; break; + case InstTag::Mul: res = val1 * val2; break; + case InstTag::Div: res = val1 / val2; break; + case InstTag::Lt: res = val1 < val2; break; + case InstTag::Le: res = val1 <= val2; break; + case InstTag::Ge: res = val1 >= val2; break; + case InstTag::Gt: res = val1 > val2; break; + case InstTag::Eq: res = val1 == val2; break; + case InstTag::Ne: res = val1 != val2; break; + default: assert(0); + } auto src_imm = gen_imm(res, mc_bb); auto dst = value2moperand(inst, mc_bb, mp_val2op); auto inst_mv = MInstMove::New(mc_bb); @@ -379,7 +435,7 @@ void MCModule::IR2MC(const Module &ir_module) { auto dst = value2moperand(inst, mc_bb, mp_val2op); auto mc_op = value2moperand(const_op == op1 ? op2 : op1, mc_bb, mp_val2op); unsigned exp = __builtin_ctz(const_op->value); - auto inst_rsh = MInstBinary::New(MInstTag::Lsh, mc_bb); + auto inst_rsh = MInstBinary::New(MInstTag::Lsh, mc_bb, 4); inst_rsh->dst = dst; inst_rsh->op1 = mc_op; inst_rsh->op2 = MOperand::Imm(exp); @@ -389,7 +445,7 @@ void MCModule::IR2MC(const Module &ir_module) { auto dst = value2moperand(inst, mc_bb, mp_val2op); auto mc_op1 = value2moperand(op1, mc_bb, mp_val2op); unsigned exp = __builtin_ctz(const_op->value); - auto inst_rsh = MInstBinary::New(MInstTag::Rsh, mc_bb); + auto inst_rsh = MInstBinary::New(MInstTag::Rsh, mc_bb, 4); inst_rsh->dst = dst; inst_rsh->op1 = mc_op1; inst_rsh->op2 = MOperand::Imm(exp); @@ -407,16 +463,6 @@ void MCModule::IR2MC(const Module &ir_module) { } else { LOG(WARNING) << "Condition without branch"; - /* Notes about compare&set: - icmp_slt(r1 < r2) = slt r3, r1, r2 - icmp_sgt(r1 > r2) = slt r3, r2, r1 - icmp_sle(r1 <= r2) = slt r3, r2, r1; xori r3, r3, 1 - icmp_sge(r1 >= r2) = slt r3, r1, r2; xori r3, r3, 1 - icmp_eq(r1 == r2) = xor r3, r1, r2; seqz r3, r3 - icmp_ne(r1 != r2) = xor r3, r1, r2; snez r3, r3 - We can see clearly, though icmp_sge use 1 more inst than icmp_slt, - they involve the same set of registers, so it is okay to defer this to the output stage - */ } } // else if (InstTag::And <= bin->tag && bin->tag <= InstTag::Or) @@ -437,21 +483,23 @@ void MCModule::IR2MC(const Module &ir_module) { default: assert(0); } auto dst = value2moperand(inst, mc_bb, mp_val2op); - auto mc_op1 = value2moperand(op1, mc_bb, mp_val2op); - auto mc_op2 = value2moperand(op2, mc_bb, mp_val2op); - auto inst_bin = MInstBinary::New(minst_tag, mc_bb); + auto mc_op1 = value2moperand(op1, mc_bb, mp_val2op, true); + auto mc_op2 = value2moperand(op2, mc_bb, mp_val2op, BTWN(minst_tag, MInstTag::Mul, MInstTag::Mod)); + auto inst_bin = MInstBinary::New(minst_tag, mc_bb, 4); inst_bin->dst = dst; inst_bin->op1 = mc_op1; inst_bin->op2 = mc_op2; } + continue; } - else if (auto zxt = shared_cast(inst)) { + if (auto zxt = shared_cast(inst)) { // trivial move auto src = value2moperand(zxt->operand_list[0], mc_bb, mp_val2op); auto dst = value2moperand(inst, mc_bb, mp_val2op); auto inst_mv = MInstMove::New(mc_bb); inst_mv->src = src; inst_mv->dst = dst; + continue; } } } @@ -463,8 +511,9 @@ void MCModule::IR2MC(const Module &ir_module) { // Use some more redundency to save the complicated critical edge split for (auto ir_bb : func->bb_list) { auto mc_bb = bb_ir2mc.at(ir_bb); - std::list par_mv_cur; // parallel move in current bb - std::unordered_map par_mv_pred; // parallel move in each pred bb + std::list par_mv_cur; // parallel move in current bb + std::unordered_multimap par_mv_pred; // parallel move in each pred bb + // This multimap makes great sense here /* 某个bb的开头有一个phi指令 %phi_dst = phi [val1 bb1] [val2 bb2] [val3 bb3] ... 为这东西新建一个虚拟寄存器vr1 @@ -492,7 +541,7 @@ void MCModule::IR2MC(const Module &ir_module) { inst_mv->dst = pmv.dst; } for (auto &pmv_pair : par_mv_pred) { - auto inst_mv = MInstMove::New(pmv_pair.first, true); + auto inst_mv = MInstMove::New(pmv_pair.first->inst_list.back()); inst_mv->src = pmv_pair.second.src; inst_mv->dst = pmv_pair.second.dst; } @@ -515,13 +564,13 @@ void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector(inst)) { def.push_back(ld->dst); use.push_back(ld->addr); - use.push_back(ld->offset); + // use.push_back(ld->offset); return; } if (auto st = shared_cast(inst)) { use.push_back(st->addr); use.push_back(st->data); - use.push_back(st->offset); + // use.push_back(st->offset); return; } if (auto cal = shared_cast(inst)) { @@ -553,10 +602,10 @@ void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector(inst)) { - def.push_back(sym->dst); - return; - } + // if (auto sym = shared_cast(inst)) { + // def.push_back(sym->dst); + // return; + // } } void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector &use) { @@ -588,10 +637,10 @@ void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector use.push_back(&br->op2); return; } - if (auto sym = shared_cast(inst)) { - def.push_back(&sym->dst); - return; - } + // if (auto sym = shared_cast(inst)) { + // def.push_back(&sym->dst); + // return; + // } } void set_bb_def_use(sptr(MFunction) func) { @@ -607,7 +656,7 @@ void set_bb_def_use(sptr(MFunction) func) { for (auto inst : bb->inst_list) { std::vector def; std::vector use; - get_inst_defuse(inst, use, def); + get_inst_defuse(inst, def, use); for (auto &elem : def) add_def(elem); for (auto &elem : use) add_use(elem); } diff --git a/src/mc_inst.cpp b/src/mc_inst.cpp new file mode 100644 index 0000000..7fd9b2e --- /dev/null +++ b/src/mc_inst.cpp @@ -0,0 +1,104 @@ +#include "mc_inst.h" + +namespace CompSysY { + +std::string MBasicBlock::to_string() { + std::string str = "LBB" + std::to_string(parent_func->id) + "_" + std::to_string(id); + return str; +} + +std::string MOperand::to_string() { + std::string ret; + switch (op_type) { + case MOpTag::Imm: ret = fmt::format("{}", value); break; + case MOpTag::Virt: ret = fmt::format("VReg_{}", value); break; + case MOpTag::PreColor: ret = enum_to_string((RV64Reg)value); break; + case MOpTag::Colored: ret = enum_to_string((RV64Reg)value); break; + case MOpTag::Glob: ret = mcmod_global_list->at(value)->name; break; + case MOpTag::Invalid: assert(0); + } + return ret; +} + +std::vector MInstBinary::get_def() { + return {dst}; +} + +std::vector MInstJump::get_def() { + return {}; +} + +std::vector MInstBranch::get_def() { + return {}; +} + +std::vector MInstLoad::get_def() { + return {dst}; +} + +std::vector MInstStore::get_def() { + return {}; +} + +std::vector MInstMove::get_def() { + return {dst}; +} + +std::vector MInstReturn::get_def() { + return {}; +} + +std::vector MInstComment::get_def() { + return {}; +} + +std::vector MInstCall::get_def() { + std::vector def; + // caller-saved regs should also be considered as re-defined + // a0-a7 + for (int i = 0; i < 8; ++i) { + def.push_back(MOperand::PreClrReg(RV64_RegOffset(RV64Reg::a0, i))); + } + def.push_back(MOperand::PreClrReg(RV64Reg::t0)); + def.push_back(MOperand::PreClrReg(RV64Reg::t1)); + def.push_back(MOperand::PreClrReg(RV64Reg::t2)); + def.push_back(MOperand::PreClrReg(RV64Reg::t3)); + def.push_back(MOperand::PreClrReg(RV64Reg::t4)); + def.push_back(MOperand::PreClrReg(RV64Reg::t5)); + def.push_back(MOperand::PreClrReg(RV64Reg::t6)); + def.push_back(MOperand::PreClrReg(RV64Reg::ra)); + return def; +} + +std::vector MInstBinary::get_use() { + return {op1, op2}; +} +std::vector MInstJump::get_use() { + return {}; +} +std::vector MInstBranch::get_use() { + return {op1, op2}; +} +std::vector MInstLoad::get_use() { + return {addr}; +} +std::vector MInstStore::get_use() { + return {addr, data}; +} +std::vector MInstMove::get_use() { + return {src}; +} +std::vector MInstReturn::get_use() { + return {MOperand::PreClrReg(RV64Reg::a0)}; +} +std::vector MInstComment::get_use() { + return {}; +} +std::vector MInstCall::get_use() { + std::vector use; + for (int i = 0; i < ir_func->fparam_list.size() && i < 8; ++i) { + use.push_back(MOperand::PreClrReg(RV64_RegOffset(RV64Reg::a0, i))); + } + return use; +} +} // namespace CompSysY \ No newline at end of file diff --git a/src/pass_reg_alloc.cpp b/src/pass_reg_alloc.cpp index de7509a..60414e9 100644 --- a/src/pass_reg_alloc.cpp +++ b/src/pass_reg_alloc.cpp @@ -1,7 +1,6 @@ #include "pass.h" namespace CompSysY { -using std::cout, std::endl; template static std::set set_union(const std::set &u1, const std::set &u2) { @@ -52,10 +51,12 @@ static void liveness_analysis(sptr(MFunction) func) { } } } -#ifdef DEBUG_REGALLOC + LOG(TRACE) << "Live Analysis done for " << func->ir_func->name; +#if 0 + using std::cout, std::endl; LOG(TRACE) << "SLA info in " << func->ir_func->name; for (auto bb : func->bb_list) { - cout << "BB "<< bb->ir_bb->name << endl; + cout << "BB " << bb->ir_bb->name << endl; cout << " def: "; for (auto def : bb->def) { cout << def.to_string() << ", "; @@ -86,18 +87,25 @@ void PassRegAlloc::add_edge(const MOperand &u, const MOperand &v) { adj_set.insert({u, v}); adj_set.insert({v, u}); // if u not-in precolored then - if (u.op_type != MOpTag::PreColor) { + if (!u.is_precolored()) { adj_list[u].insert(v); degree[u]++; } - if (v.op_type != MOpTag::PreColor) { + if (!v.is_precolored()) { adj_list[v].insert(u); degree[v]++; } } } -// use SLA result to build interference map and init worklist_moves +/* +用活跃信息构造冲突图.思路也还算是直观: + 从bb的liveout开始,倒序扫描指令,把当前指令的def和当前live连边; + 然后把当前指令的use加入live集合中,相当于当前指令的livein传递到前一条指令的liveout里面 +在构造的时候,需要考虑move指令,根据书中前面提到的策略,把mv的src从live中移除,这样就不会构造src和dst之间的冲突边 + 然后move_list是将操作数node映射到对应的mv指令上,在这里就加进去了 + 然后worklist_move是"有可能合并的move指令的集合",这里初始默认认为所有的move都可能合并 +*/ void PassRegAlloc::build(sptr(MFunction) func) { for (auto bb : func->bb_list) { // let live = liveout(b) @@ -121,8 +129,8 @@ void PassRegAlloc::build(sptr(MFunction) func) { } } // live <- live UNION def(I) - for (auto &elem : def_I) { - if (elem.need_clr()) live.insert(elem); + for (auto &d : def_I) { + if (d.need_clr()) live.insert(d); } // forall d in def(I) for (auto &d : def_I) { @@ -132,26 +140,27 @@ void PassRegAlloc::build(sptr(MFunction) func) { } // live <- use(I) UNION (live - def(I)) for (auto &d : def_I) { - if (!d.need_clr()) continue; - live.erase(d); + if (d.need_clr()) live.erase(d); } for (auto &u : use_I) { - if (!u.need_clr()) continue; - live.insert(u); + if (u.need_clr()) live.insert(u); } } } } +/* +主要就是把待染色的virt_reg分类 +*/ void PassRegAlloc::make_work_list(sptr(MFunction) func) { /* forall n in initial initial <- initial - {n} - if degree[n] >= K then + if degree[n] >= K then // 高度数节点表 spillWorklist <- spillWorklist U {n} - else if MoveRelated(n then + else if MoveRelated(n) then // 低度数mv有关节点表 freezeWorklist <- freezeWorklist U {n} - else + else // 低度数mv无关节点表 simplifyWorklist <- simplifyWorklist U {n} */ // initial 其实就是所有的虚拟寄存器 @@ -171,8 +180,10 @@ void PassRegAlloc::make_work_list(sptr(MFunction) func) { // movelist[n] INTERSECT (activemoves UNION worklistmoves) std::set PassRegAlloc::node_moves(const MOperand &n) { - auto ret = move_list.at(n); + // for empty set, it does not even exists, so do not use `.at(n)` + auto ret = move_list[n]; for (auto itr = ret.begin(); itr != ret.end();) { + // not found in either set, then remove from movelist if (!ASSOC_FOUND(active_moves, *itr) && !ASSOC_FOUND(worklist_moves, *itr)) itr = ret.erase(itr); else @@ -183,7 +194,7 @@ std::set PassRegAlloc::node_moves(const MOperand &n) { // adjList[n] - (selectStak UNION coalescedNodes) std::set PassRegAlloc::adjacent(const MOperand &n) { - auto ret = adj_list.at(n); + auto ret = adj_list[n]; for (auto itr = ret.begin(); itr != ret.end();) { if (STD_FOUND(select_stack, *itr) || ASSOC_FOUND(coalesced_nodes, *itr)) itr = ret.erase(itr); @@ -194,12 +205,12 @@ std::set PassRegAlloc::adjacent(const MOperand &n) { } bool PassRegAlloc::move_related(const MOperand &n) { - std::set res = node_moves(n); - return !res.empty(); + return !node_moves(n).empty(); } void PassRegAlloc::enable_moves(const MOperand &n) { - for (auto &m : node_moves(n)) { + auto node_moves_n = node_moves(n); + for (auto &m : node_moves_n) { if (ASSOC_FOUND(active_moves, m)) { active_moves.erase(m); worklist_moves.insert(m); @@ -208,7 +219,8 @@ void PassRegAlloc::enable_moves(const MOperand &n) { } void PassRegAlloc::decrement_degree(const MOperand &m) { - int deg = degree.at(m)--; + int deg = degree.at(m); + degree[m] = deg - 1; if (deg == K) { /* manual inlined EnableMoves({m} U Adjacent(m)) @@ -218,7 +230,8 @@ void PassRegAlloc::decrement_degree(const MOperand &m) { enable_moves(adj); } - spill_worklist.insert(m); + //? spill_worklist.insert(m); + spill_worklist.erase(m); if (move_related(m)) freeze_worklist.insert(m); else @@ -226,6 +239,9 @@ void PassRegAlloc::decrement_degree(const MOperand &m) { } } +/* +从simplify_list随便挑一个节点删掉,然后丢进stack,最后给他的相邻节点减少deg +*/ void PassRegAlloc::simplify() { auto n = *simplify_worklist.begin(); simplify_worklist.erase(simplify_worklist.begin()); @@ -359,20 +375,17 @@ void PassRegAlloc::assign_colors(sptr(MFunction) func) { while (!select_stack.empty()) { auto n = select_stack.back(); select_stack.pop_back(); - std::set ok_colors; - for (int i = 0; i < 32; ++i) ok_colors.insert(i); - ok_colors.erase((int)RV64Reg::x0); - ok_colors.erase((int)RV64Reg::gp); - ok_colors.erase((int)RV64Reg::tp); - ok_colors.erase((int)RV64Reg::sp); - + // exclude: x0, ra, sp, gp, tp (0, 1, 2, 3, 4) + std::unordered_set ok_colors = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; for (auto &w : adj_list[n]) { auto alias = get_alias(w); if (alias.op_type == MOpTag::PreColor || alias.op_type == MOpTag::Colored) { ok_colors.erase(alias.value); } - else if (ASSOC_FOUND(colored_nodes, alias)) { - ok_colors.erase(alias.value); + else if (ASSOC_FOUND(color, alias)) { + auto color_alias = color[alias]; + ok_colors.erase(color_alias.value); } } @@ -408,6 +421,7 @@ void PassRegAlloc::assign_colors(sptr(MFunction) func) { void PassRegAlloc::rewrite_program(sptr(MFunction) func) { for (auto v : spilled_nodes) { + LOG(TRACE) << "Spill node " << v.to_string(); for (auto bb : func->bb_list) { sptr(MInst) firstuse = nullptr; sptr(MInst) lastdef = nullptr; @@ -417,11 +431,13 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) { std::vector use; get_inst_defuse(inst, def, use); for (auto d : def) { + if (*d != v) continue; if (vr < 0) vr = func->virt_reg_cnt++; d->value = vr; lastdef = inst; } for (auto u : use) { + if (*u != v) continue; if (vr < 0) vr = func->virt_reg_cnt++; u->value = vr; if (!lastdef && !firstuse) firstuse = inst; @@ -430,7 +446,7 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) { } auto gen_off = [&](sptr(MInst) inst) { auto off_imm = MOperand::Imm(func->stack_size); - if (off_imm.value < 2048) { + if (is_in_imm_range(off_imm.value)) { return off_imm; } else { @@ -441,13 +457,13 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) { } }; if (firstuse) { - auto inst_ld = MInstLoad::New(firstuse); + auto inst_ld = MInstLoad::New(firstuse, XLEN); inst_ld->addr = MOperand::PreClrReg(RV64Reg::sp); inst_ld->dst = MOperand::VirtReg(vr); inst_ld->offset = gen_off(inst_ld); } if (lastdef) { - auto inst_st = MInstStore::New(lastdef, true); + auto inst_st = MInstStore::New(lastdef, XLEN, true); inst_st->addr = MOperand::PreClrReg(RV64Reg::sp); inst_st->data = MOperand::VirtReg(vr); inst_st->offset = gen_off(inst_st); @@ -457,6 +473,33 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) { } } +/* +Internal util for initialize data structures +*/ +void PassRegAlloc::clear() { + adj_list.clear(); + adj_set.clear(); + degree.clear(); + move_list.clear(); + color.clear(); + alias.clear(); + simplify_worklist.clear(); + freeze_worklist.clear(); + spill_worklist.clear(); + spilled_nodes.clear(); + coalesced_nodes.clear(); + colored_nodes.clear(); + select_stack.clear(); + coalesced_moves.clear(); + constrained_moves.clear(); + frozen_moves.clear(); + worklist_moves.clear(); + active_moves.clear(); + + // pre-define each pre-colored register's degree as inf + for (int reg = 0; reg < 32; ++reg) degree.insert({MOperand::PreClrReg((RV64Reg)reg), INF}); +} + /* SLA() Build() @@ -469,6 +512,8 @@ if !spilledNodes.empty() then RewriteProgram Main() */ +void emit_function(std::ostream &ostr, sptr(MFunction) func); + void PassRegAlloc::reg_alloc(sptr(MFunction) func) { clear(); set_bb_def_use(func); @@ -495,9 +540,11 @@ void PassRegAlloc::reg_alloc(sptr(MFunction) func) { flag = true; } } while (flag); + LOG(TRACE) << "Simplify done for " << func->ir_func->name; assign_colors(func); if (!spilled_nodes.empty()) { rewrite_program(func); + emit_function(std::cout, func); reg_alloc(func); } } diff --git a/src/visitor.cpp b/src/visitor.cpp index 283cfa1..896aa66 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -150,8 +150,9 @@ std::any Visitor::visitConstDef(SysyParser::ConstDefContext *ctx) { if (_scope_tab.get_level()) { // local const array - auto const_arr = ConstantArr::make_shared("const_arr", array_value, array_type); - auto glob_name = fmt::format("__const.{}.{}{}", _state.current_func->name, const_name, _state.local_const_arr_cnt++); + auto const_arr = ConstantArr::make_shared("const_arr", array_value, array_type); + auto glob_name = + fmt::format("__const.{}.{}{}", _state.current_func->name, const_name, _state.local_const_arr_cnt++); auto global_var = GlobalVar::make_shared(glob_name, array_type, const_arr, true); module.global_var_list.push_back(global_var); _scope_tab.push_name(const_name, global_var);