From 9b9a96881a50ca55104ab6e98fe076e1aa8a7bc0 Mon Sep 17 00:00:00 2001 From: ridethepig Date: Fri, 26 May 2023 21:51:41 +0800 Subject: [PATCH] buggy reg alloc --- include/algos.h | 2 +- include/common.h | 8 +- include/mc_inst.h | 111 ++++++--- include/pass.h | 83 +++++++ include/pass_build_cfg.h | 12 - include/passes.h | 4 - src/main.cpp | 9 +- src/mc_codegen.cpp | 224 +++++++++++++----- src/pass_build_cfg.cpp | 2 +- src/pass_mem2reg.cpp | 2 +- src/pass_reg_alloc.cpp | 481 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 826 insertions(+), 112 deletions(-) delete mode 100644 include/pass_build_cfg.h delete mode 100644 include/passes.h create mode 100644 src/pass_reg_alloc.cpp diff --git a/include/algos.h b/include/algos.h index ae26110..7f31acf 100644 --- a/include/algos.h +++ b/include/algos.h @@ -1,7 +1,7 @@ #pragma once #include "llir.h" -#include "passes.h" +#include "pass.h" namespace CompSysY { void gen_dominance(FunctionPtr_t func); diff --git a/include/common.h b/include/common.h index 86293f6..7426049 100644 --- a/include/common.h +++ b/include/common.h @@ -1,12 +1,16 @@ #pragma once #include "3rdparty/easylogging++.h" +#include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -28,7 +32,9 @@ inline sptr(DST) strict_shared_cast(SRC src) { return dst; } -#define FIND(container, val) std::find(container.begin(), container.end(), val) +#define STD_FIND(container, val) std::find(container.begin(), container.end(), val) +#define STD_FOUND(container, val) (STD_FIND(container, val) != container.end()) +#define ASSOC_FOUND(cont, val) (cont.find(val) != cont.end()) #define panic(message) \ do { \ diff --git a/include/mc_inst.h b/include/mc_inst.h index 7150e50..1e37003 100644 --- a/include/mc_inst.h +++ b/include/mc_inst.h @@ -10,11 +10,11 @@ namespace CompSysY { // s? are callee-saved // x0,gp,tp are preserved in user-space enum class RV64Reg { - x0 = 0, // zero + x0 = 0, // zero N/A ra, // ra, caller sp, // sp, callee - gp, // gp - tp, // tp + gp, // gp, N/A + tp, // tp, N/A t0, // t0, caller t1, t2, // t1-2, caller @@ -66,31 +66,47 @@ class MOperand; class MInst; class MBasicBlock; class MFunction; - +enum class MOpTag { + Invalid, + Virt, + Imm, + PreColor, + Colored, +}; class MOperand { public: - enum class OpTag { - Virt, - Imm, - PreColor, - } op_type = OpTag::Virt; - int value = ~0; - - MOperand(OpTag tag, int val) : op_type(tag), value(val) {} - static sptr(MOperand) NewVirtReg(int reg_no) { - auto mop = std::make_shared(OpTag::Virt, reg_no); + MOpTag op_type = MOpTag::Invalid; + int value = ~0; + static MOperand VirtReg(int reg_no) { + auto mop = MOperand({MOpTag::Virt, reg_no}); return mop; } - static sptr(MOperand) NewImm(int imm_val) { - auto mop = std::make_shared(OpTag::Imm, imm_val); + static MOperand Imm(int imm_val) { + auto mop = MOperand({MOpTag::Imm, imm_val}); return mop; } - static sptr(MOperand) NewReg(RV64Reg phy_reg) { - auto mop = std::make_shared(OpTag::PreColor, (int)phy_reg); + static MOperand PreClrReg(RV64Reg phy_reg) { + auto mop = MOperand({MOpTag::PreColor, (int)phy_reg}); return mop; } + + static MOperand AllocReg(RV64Reg phy_reg) { + return MOperand({MOpTag::Colored, (int)phy_reg}); + } + bool operator==(const MOperand &op2) const { + return op_type == op2.op_type && value == op2.value; + } + bool operator!=(const MOperand &op2) const { + return op_type != op2.op_type || value != op2.value; + } + 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; + } }; enum class MInstTag { @@ -139,6 +155,7 @@ public: sptr(MBasicBlock) parent_bb; MInst(MInstTag tag, sptr(MBasicBlock) parent_bb) : inst_tag(tag), parent_bb(parent_bb) {} + virtual ~MInst() = default; }; class MBasicBlock { @@ -149,8 +166,10 @@ public: std::list pred_list; std::list succ_list; - std::unordered_set livein; - std::unordered_set liveout; + std::set use; + std::set def; + std::set livein; + std::set liveout; }; class MFunction { @@ -173,9 +192,9 @@ public: class MInstBinary : public MInst { public: - sptr(MOperand) dst; - sptr(MOperand) op1; - sptr(MOperand) op2; + MOperand dst; + MOperand op1; + MOperand op2; MInstBinary(MInstTag type, sptr(MBasicBlock) parent_bb) : MInst(type, parent_bb) {} static sptr(MInstBinary) New(MInstTag type, sptr(MBasicBlock) parent_bb) { @@ -200,8 +219,8 @@ public: class MInstBranch : public MInst { public: sptr(MBasicBlock) target; - sptr(MOperand) op1; - sptr(MOperand) op2; + MOperand op1; + MOperand op2; MInstTag branch_tag; MInstBranch(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Branch, parent_bb) {} static sptr(MInstBranch) New(sptr(MBasicBlock) parent_bb) { @@ -213,9 +232,9 @@ public: class MInstLoad : public MInst { public: - sptr(MOperand) dst; - sptr(MOperand) addr; - sptr(MOperand) offset; + MOperand dst; + MOperand addr; + MOperand offset; MInstLoad(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {} static sptr(MInstLoad) New(sptr(MBasicBlock) parent_bb) { @@ -223,13 +242,22 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + static sptr(MInstLoad) New(sptr(MInst) rel_inst, 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); + return inst; + } }; class MInstStore : public MInst { public: - sptr(MOperand) data; - sptr(MOperand) addr; - sptr(MOperand) offset; + MOperand data; + MOperand addr; + MOperand offset; MInstStore(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {} static sptr(MInstStore) New(sptr(MBasicBlock) parent_bb) { @@ -237,12 +265,21 @@ public: parent_bb->inst_list.push_back(inst); return inst; } + static sptr(MInstStore) New(sptr(MInst) rel_inst, 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); + return inst; + } }; class MInstMove : public MInst { public: - sptr(MOperand) dst; - sptr(MOperand) src; + MOperand dst; + MOperand src; MInstMove(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Move, parent_bb) {} static sptr(MInstMove) New(sptr(MBasicBlock) parent_bb, bool insert_begin = false) { auto inst = make_shared(parent_bb); @@ -255,7 +292,7 @@ public: static sptr(MInstMove) New(sptr(MInst) rel_inst, bool insert_after = false) { auto parent_bb = rel_inst->parent_bb; auto inst = make_shared(parent_bb); - auto rel_inst_itr = FIND(parent_bb->inst_list, rel_inst); + 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); @@ -265,7 +302,7 @@ public: class MInstSymbol : public MInst { public: - sptr(MOperand) dst; + 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) { @@ -313,4 +350,8 @@ public: } }; +void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector &use); +void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector &use); +void set_bb_def_use(sptr(MFunction) func); + } // namespace CompSysY \ No newline at end of file diff --git a/include/pass.h b/include/pass.h index 05b09b9..bae206c 100644 --- a/include/pass.h +++ b/include/pass.h @@ -2,6 +2,7 @@ #include "common.h" #include "llir_module.h" +#include "machcode.h" namespace CompSysY { class Pass { @@ -18,4 +19,86 @@ public: virtual void run(const Module &module) override; }; +class PassBuildCFG : public Pass { +public: + PassBuildCFG() : Pass("build_cfg") {} + virtual void run(const Module &module) override; +}; + +class MCPass { +public: + std::string pass_name; + MCPass(const std::string &name) : pass_name(name) {} + + virtual void run(const MCModule &module) = 0; +}; + +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 +private: + void reg_alloc(sptr(MFunction)); + void build(sptr(MFunction)); + void add_edge(const MOperand &u, const MOperand &v); + void make_work_list(sptr(MFunction) func); + bool move_related(const MOperand &n); + std::set node_moves(const MOperand &n); + std::set adjacent(const MOperand &n); + void decrement_degree(const MOperand &m); + void enable_moves(const MOperand &n); + void simplify(); + void coalesce(); + void add_work_list(const MOperand &u); + bool OK(const MOperand &t, const MOperand &r); + bool conservative(const std::set &nodes); + MOperand get_alias(MOperand n); + void combine(const MOperand &u, const MOperand &v); + void freeze(); + void freeze_moves(const MOperand &u); + void select_spill(); + void assign_colors(sptr(MFunction) func); + void rewrite_program(sptr(MFunction) func); + std::map> adj_list; + std::set> adj_set; + std::map degree; + std::map> move_list; + std::map color; + std::map alias; + std::set simplify_worklist; + std::set freeze_worklist; + std::set spill_worklist; + std::set spilled_nodes; + std::set coalesced_nodes; + std::set colored_nodes; + std::vector select_stack; + std::set coalesced_moves; + std::set constrained_moves; + std::set frozen_moves; + 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(); + } +}; + } // namespace CompSysY \ No newline at end of file diff --git a/include/pass_build_cfg.h b/include/pass_build_cfg.h deleted file mode 100644 index 2160cc1..0000000 --- a/include/pass_build_cfg.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "llir_module.h" -#include "passes.h" - -namespace CompSysY { -class PassBuildCFG : public Pass { -public: - PassBuildCFG() : Pass("build_cfg") {} - virtual void run(const Module &module) override; -}; -} // namespace CompSysY \ No newline at end of file diff --git a/include/passes.h b/include/passes.h deleted file mode 100644 index f8f400d..0000000 --- a/include/passes.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "pass.h" -#include "pass_build_cfg.h" \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 817e065..3586d3a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,7 @@ #include "common.h" #include "llir.h" #include "machcode.h" -#include "passes.h" +#include "pass.h" #include "visitor.h" #include <3rdparty/argparse.hpp> #include @@ -128,6 +128,11 @@ int main(int argc, const char **argv) { MCModule mc_module; mc_module.IR2MC(visitor.module); - + std::vector mc_passes = { + std::make_shared() + }; + for (auto pass : mc_passes) { + pass->run(mc_module); + } return 0; } \ No newline at end of file diff --git a/src/mc_codegen.cpp b/src/mc_codegen.cpp index a86f0ef..4792d4a 100644 --- a/src/mc_codegen.cpp +++ b/src/mc_codegen.cpp @@ -8,41 +8,41 @@ using std::make_shared; namespace CompSysY { static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb) { - auto operand = MOperand::NewImm(imm); + auto operand = MOperand::Imm(imm); // 12 bit signed imm for I/S-type if (-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? - auto vr = MOperand::NewVirtReg(mc_bb->parent_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); auto inst_move = MInstMove::New(mc_bb); inst_move->src = operand; inst_move->dst = vr; return vr; } -static sptr(MOperand) value2moperand( +static MOperand value2moperand( sptr(Value) ir_value, sptr(MBasicBlock) mc_bb, - std::unordered_map &val2mop + std::unordered_map &val2mop ) { if (auto fparam = shared_cast(ir_value)) { auto itr = val2mop.find(ir_value); if (itr != val2mop.end()) { return itr->second; } - auto vr = MOperand::NewVirtReg(mc_bb->parent_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); val2mop.insert({ir_value, vr}); auto ir_func = mc_bb->parent_func->ir_func; - auto fparam_itr = FIND(ir_func->fparam_list, fparam); + auto fparam_itr = STD_FIND(ir_func->fparam_list, fparam); assert(fparam_itr != ir_func->fparam_list.end()); auto fparam_ndx = std::distance(ir_func->fparam_list.begin(), fparam_itr); // according to RV call conv, we can have a0-a7 for params if (fparam_ndx < 8) { // copy param as an vr in func entry auto inst_move = MInstMove::New(mc_bb->parent_func->bb_list.front(), true); - inst_move->src = MOperand::NewReg(RV64_RegOffset(RV64Reg::a0, fparam_ndx)); + inst_move->src = MOperand::PreClrReg(RV64_RegOffset(RV64Reg::a0, fparam_ndx)); inst_move->dst = vr; } else { @@ -54,8 +54,8 @@ static sptr(MOperand) value2moperand( // 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) // auto inst_move = MInstMove::New(inst_load); // lui vr_t, - inst_load->addr = MOperand::NewReg(RV64Reg::sp); - inst_load->offset = MOperand::NewImm((fparam_ndx - 8) * XLEN); + inst_load->addr = MOperand::PreClrReg(RV64Reg::sp); + inst_load->offset = MOperand::Imm((fparam_ndx - 8) * XLEN); inst_load->dst = vr; mc_bb->parent_func->stack_arg_reloc.push_back(inst_load); } @@ -67,7 +67,7 @@ static sptr(MOperand) value2moperand( return itr->second; } auto inst_symld = MInstSymbol::New(mc_bb->parent_func->bb_list.front(), true); - auto vr = MOperand::NewVirtReg(mc_bb->parent_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); val2mop.insert({ir_value, vr}); inst_symld->symbol = glob; inst_symld->dst = vr; @@ -84,7 +84,7 @@ static sptr(MOperand) value2moperand( return itr->second; } else { - auto vr = MOperand::NewVirtReg(mc_bb->parent_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++); val2mop.insert({ir_value, vr}); return vr; } @@ -108,7 +108,7 @@ void MCModule::IR2MC(const Module &ir_module) { for (auto bb : func->bb_list) { auto mc_bb = make_shared(); mc_func->bb_list.push_back(mc_bb); - mc_bb->ir_bb = bb; + mc_bb->ir_bb = bb; mc_bb->parent_func = mc_func; bb_ir2mc.insert({bb, mc_bb}); } @@ -124,57 +124,57 @@ void MCModule::IR2MC(const Module &ir_module) { mc_bb->pred_list.push_back(mc_pred); } } - std::unordered_map mp_val2op; + std::unordered_map mp_val2op; std::unordered_map mp_br2icmp; // instruction translate for (auto bb : func->bb_list) { 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); + auto mc_li = MInstLoad::New(mc_bb); + mc_li->addr = addr; + mc_li->dst = value2moperand(ld, mc_bb, mp_val2op); + // mc_li->offset = nullptr; 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); + 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; 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 index0 = value2moperand(inst->operand_list[1], mc_bb, mp_val2op); - decltype(index0) index1 = nullptr; + auto dst = value2moperand(inst, mc_bb, mp_val2op); + auto ptr = value2moperand(inst->operand_list[0], mc_bb, mp_val2op); + 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 == MOperand::OpTag::Imm && index0->value == 0); + assert(index0.op_type == MOpTag::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 (!index0->value || !index1 || !index1->value) { + if ((inst->operand_list.size() < 3 && !index0.value) || (inst->operand_list.size() == 3 && !index1.value)) { // a shortcut for zero gep auto inst_mv = MInstMove::New(mc_bb); inst_mv->dst = dst; inst_mv->src = ptr; VLOG(6) << "trivial gep"; } - else if (!index1) { + else if (inst->operand_list.size() < 3) { // index on same dim: addr = ptr + index0 * sizeof(elem_ty) auto elem_size = get_type_size(elem_type); auto elem_size_imm = gen_imm(elem_size, mc_bb); - auto vr = MOperand::NewVirtReg(mc_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++); auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb); inst_mul->dst = vr; inst_mul->op1 = index0; @@ -189,7 +189,7 @@ void MCModule::IR2MC(const Module &ir_module) { // index on sub dim: addr = ptr + index1 * sizeof(elem_ty.elem_ty) auto elem_elem_size = get_pointed_type_size(elem_type); auto elem_elem_size_imm = gen_imm(elem_elem_size, mc_bb); - auto vr = MOperand::NewVirtReg(mc_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++); auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb); inst_mul->dst = vr; inst_mul->op1 = index0; @@ -220,7 +220,7 @@ void MCModule::IR2MC(const Module &ir_module) { auto stk_sz_imm = gen_imm(mc_func->stack_size, mc_bb); auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb); inst_add->dst = dst; - inst_add->op1 = MOperand::NewReg(RV64Reg::sp); + 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; @@ -230,7 +230,7 @@ void MCModule::IR2MC(const Module &ir_module) { auto retval = value2moperand(ret->operand_list[0], mc_bb, mp_val2op); auto inst_mv = MInstMove::New(mc_bb); inst_mv->src = retval; - inst_mv->dst = MOperand::NewReg(RV64Reg::a0); + inst_mv->dst = MOperand::PreClrReg(RV64Reg::a0); } MInstReturn::New(mc_bb); } @@ -242,13 +242,13 @@ void MCModule::IR2MC(const Module &ir_module) { if (i <= 8) { auto inst_move = MInstMove::New(mc_bb); inst_move->src = rparam; - inst_move->dst = MOperand::NewReg(RV64_RegOffset(RV64Reg::a0, i - 1)); + inst_move->dst = MOperand::PreClrReg(RV64_RegOffset(RV64Reg::a0, i - 1)); } 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); - inst_store->addr = MOperand::NewReg(RV64Reg::sp); + inst_store->addr = MOperand::PreClrReg(RV64Reg::sp); inst_store->offset = st_off_imm; inst_store->data = rparam; } @@ -256,8 +256,8 @@ void MCModule::IR2MC(const Module &ir_module) { if (nparams > 8) { // sp -= (n-8)*8 auto add_inst = new MInstBinary(MInstTag::Sub, mc_bb); - add_inst->dst = MOperand::NewReg(RV64Reg::sp); - add_inst->op1 = MOperand::NewReg(RV64Reg::sp); + add_inst->dst = MOperand::PreClrReg(RV64Reg::sp); + add_inst->op1 = MOperand::PreClrReg(RV64Reg::sp); add_inst->op2 = gen_imm(XLEN * (nparams - 8), mc_bb); } @@ -267,15 +267,15 @@ void MCModule::IR2MC(const Module &ir_module) { if (nparams > 8) { // sp += (n-8)*8 auto add_inst = new MInstBinary(MInstTag::Add, mc_bb); - add_inst->dst = MOperand::NewReg(RV64Reg::sp); - add_inst->op1 = MOperand::NewReg(RV64Reg::sp); + add_inst->dst = MOperand::PreClrReg(RV64Reg::sp); + add_inst->op1 = MOperand::PreClrReg(RV64Reg::sp); add_inst->op2 = gen_imm(XLEN * (nparams - 8), mc_bb); } // handle return value, if exist if (shared_cast(cal->type)) { auto dst = value2moperand(inst, mc_bb, mp_val2op); auto inst_mv = MInstMove::New(mc_bb); - inst_mv->src = MOperand::NewReg(RV64Reg::a0); + inst_mv->src = MOperand::PreClrReg(RV64Reg::a0); inst_mv->dst = dst; } } @@ -305,8 +305,8 @@ void MCModule::IR2MC(const Module &ir_module) { if (*std::next(mc_bb->ir_bb->itr) == br->operand_list[1]) { // 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->branch_tag = inverse_cond(inst_br->branch_tag); + inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[1])); } 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])); @@ -316,14 +316,14 @@ void MCModule::IR2MC(const Module &ir_module) { } } else { - auto cond = value2moperand(br->operand_list[0], mc_bb, mp_val2op); - auto inst_br = MInstBranch::New(mc_bb); - inst_br->op1 = cond; - inst_br->op2 = MOperand::NewReg(RV64Reg::x0); + auto cond = value2moperand(br->operand_list[0], mc_bb, mp_val2op); + 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->branch_tag = inverse_cond(inst_br->branch_tag); + inst_br->target = bb_ir2mc.at(strict_shared_cast(br->operand_list[1])); } 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])); @@ -380,7 +380,7 @@ void MCModule::IR2MC(const Module &ir_module) { auto inst_rsh = MInstBinary::New(MInstTag::Lsh, mc_bb); inst_rsh->dst = dst; inst_rsh->op1 = mc_op; - inst_rsh->op2 = MOperand::NewImm(exp); + inst_rsh->op2 = MOperand::Imm(exp); continue; } if (auto const_op = _is_const_div_power2()) { @@ -390,7 +390,7 @@ void MCModule::IR2MC(const Module &ir_module) { auto inst_rsh = MInstBinary::New(MInstTag::Rsh, mc_bb); inst_rsh->dst = dst; inst_rsh->op1 = mc_op1; - inst_rsh->op2 = MOperand::NewImm(exp); + inst_rsh->op2 = MOperand::Imm(exp); continue; } // no opt for modulo, since they are all signed int, it's wrong @@ -454,8 +454,8 @@ void MCModule::IR2MC(const Module &ir_module) { } } struct pmv { - sptr(MOperand) dst; - sptr(MOperand) src; + MOperand dst; + MOperand src; }; // phi elimination: SSA Book Algo 3.6 // Use some more redundency to save the complicated critical edge split @@ -472,7 +472,7 @@ void MCModule::IR2MC(const Module &ir_module) { for (auto inst : ir_bb->inst_list) { if (!shared_cast(inst)) break; auto phi = shared_cast(inst); - auto vr = MOperand::NewVirtReg(mc_func->virt_reg_cnt++); + auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++); auto dst = value2moperand(inst, mc_bb, mp_val2op); par_mv_cur.push_back({dst, vr}); auto pred_itr = phi->parent_bb->pred_list.begin(); @@ -498,4 +498,118 @@ void MCModule::IR2MC(const Module &ir_module) { } } +void get_inst_defuse(sptr(MInst) inst, std::vector &def, std::vector &use) { + if (auto bin = shared_cast(inst)) { + def.push_back(bin->dst); + use.push_back(bin->op1); + use.push_back(bin->op2); + return; + } + if (auto mov = shared_cast(inst)) { + def.push_back(mov->dst); + use.push_back(mov->src); + return; + } + if (auto ld = shared_cast(inst)) { + def.push_back(ld->dst); + use.push_back(ld->addr); + 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); + return; + } + if (auto cal = shared_cast(inst)) { + // arg-occupied regs get implicitly used here + for (int i = 0; i < cal->ir_func->fparam_list.size() && i < 8; ++i) { + use.push_back(MOperand::PreClrReg(RV64_RegOffset(RV64Reg::a0, i))); + } + // 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; + } + if (auto br = shared_cast(inst)) { + use.push_back(br->op1); + use.push_back(br->op2); + return; + } + if (auto bin = shared_cast(inst)) { + use.push_back(MOperand::PreClrReg(RV64Reg::a0)); + 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) { + if (auto bin = shared_cast(inst)) { + def.push_back(&bin->dst); + use.push_back(&bin->op1); + use.push_back(&bin->op2); + return; + } + if (auto mov = shared_cast(inst)) { + def.push_back(&mov->dst); + use.push_back(&mov->src); + return; + } + if (auto ld = shared_cast(inst)) { + def.push_back(&ld->dst); + use.push_back(&ld->addr); + 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); + return; + } + if (auto br = shared_cast(inst)) { + use.push_back(&br->op1); + use.push_back(&br->op2); + return; + } + if (auto sym = shared_cast(inst)) { + def.push_back(&sym->dst); + return; + } +} + +void set_bb_def_use(sptr(MFunction) func) { + for (auto bb : func->bb_list) { + bb->def.clear(); + bb->use.clear(); + auto add_def = [&](MOperand &def) { + if (def.need_clr()) bb->def.insert(def); + }; + auto add_use = [&](MOperand &use) { + if (use.need_clr()) bb->use.insert(use); + }; + for (auto inst : bb->inst_list) { + std::vector def; + std::vector use; + get_inst_defuse(inst, use, def); + for (auto &elem : def) add_def(elem); + for (auto &elem : use) add_use(elem); + } + } +} + } // namespace CompSysY \ No newline at end of file diff --git a/src/pass_build_cfg.cpp b/src/pass_build_cfg.cpp index 6f982d2..2c3f420 100644 --- a/src/pass_build_cfg.cpp +++ b/src/pass_build_cfg.cpp @@ -1,7 +1,7 @@ #include "3rdparty/easylogging++.h" #include "common.h" #include "llir.h" -#include "passes.h" +#include "pass.h" #include namespace CompSysY { diff --git a/src/pass_mem2reg.cpp b/src/pass_mem2reg.cpp index 5f0b0fc..461ee9a 100644 --- a/src/pass_mem2reg.cpp +++ b/src/pass_mem2reg.cpp @@ -2,7 +2,7 @@ #include "algos.h" #include "common.h" #include "llir.h" -#include "passes.h" +#include "pass.h" #include "visitor.h" #include #include diff --git a/src/pass_reg_alloc.cpp b/src/pass_reg_alloc.cpp new file mode 100644 index 0000000..88a97f1 --- /dev/null +++ b/src/pass_reg_alloc.cpp @@ -0,0 +1,481 @@ +#include "pass.h" + +namespace CompSysY { + +template +static std::set set_union(const std::set &u1, const std::set &u2) { + std::set ret = u1; + ret.insert(u2.begin(), u2.end()); + return ret; +} + +/* Some Notes +pred/succ和前面LLIR里面的定义一致,直接是从那边复制的 + +def和use集合,说的是在一个bb里面定值/使用的变量的集合 +def[bb] = {var | var is assigned in this bb} +use[bb] = {var | var is used in this bb } +live(on one edge) 存在一条从该edge通向var的一个use,且中间不经过任何的def +live-in 一个变量在所有的in-edge上都是live的 +live-out 一个变量在所有的out-edge上都是live的 + +data-flow equation: + livein[n] = use[n] union (liveout[n] - def[n]) + liveout[n] = union livein[s], for s in succ[n] +*/ +static void liveness_analysis(sptr(MFunction) func) { + // this will be run for multiple times, thus have to clear + for (auto bb : func->bb_list) { + bb->livein = bb->use; + bb->liveout.clear(); + } + // iterative algorithm, refer to TigerBook Ch10 + bool changed = true; + while (changed) { + changed = false; + for (auto bb : func->bb_list) { + std::set newout; + // out[n] = UNION (in[s]) for s in succ[n] + for (auto succ : bb->succ_list) { + newout.insert(succ->livein.begin(), succ->livein.end()); + } + if (newout != bb->liveout) { + changed = true; + bb->liveout = newout; + // in[n] <- use[n] U (out[n] - def[n]) + std::set newin = bb->use; + for (auto elem : bb->liveout) { + if (bb->def.find(elem) == bb->def.end()) newin.insert(elem); + } + bb->livein = newin; + } + } + } +} + +void PassRegAlloc::add_edge(const MOperand &u, const MOperand &v) { + // if (u,v) not-in adjSet and u!=v then + if (adj_set.find({u, v}) == adj_set.end() && u != v) { + adj_set.insert({u, v}); + adj_set.insert({v, u}); + // if u not-in precolored then + if (u.op_type != MOpTag::PreColor) { + adj_list[u].insert(v); + degree[u]++; + } + if (v.op_type != MOpTag::PreColor) { + adj_list[v].insert(u); + degree[v]++; + } + } +} + +// use SLA result to build interference map and init worklist_moves +void PassRegAlloc::build(sptr(MFunction) func) { + for (auto bb : func->bb_list) { + // let live = liveout(b) + auto live = bb->liveout; + // forall I \in instructions(b) in reversed-order + for (auto inst_it = bb->inst_list.rbegin(); inst_it != bb->inst_list.rend(); ++inst_it) { + // if isMoveInst(I) + std::vector def_I; + std::vector use_I; + get_inst_defuse(*inst_it, def_I, use_I); + if (auto inst_mv = shared_cast(*inst_it)) { + if (inst_mv->dst.need_clr() && inst_mv->src.need_clr()) { + // live <- live - use(I) + live.erase(inst_mv->src); + // forall n in def(I) UNION use(I) + // moveList[n] <- moveList[n] UNION {I} + move_list[inst_mv->dst].insert(inst_mv); + move_list[inst_mv->src].insert(inst_mv); + // worklistMoves <- worklistMoves UNION {I} + worklist_moves.insert(inst_mv); + } + } + // live <- live UNION def(I) + for (auto &elem : def_I) { + if (elem.need_clr()) live.insert(elem); + } + // forall d in def(I) + for (auto &d : def_I) { + if (!d.need_clr()) continue; + // forall l in live + for (auto &l : live) add_edge(l, d); + } + // live <- use(I) UNION (live - def(I)) + for (auto &d : def_I) { + if (!d.need_clr()) continue; + live.erase(d); + } + for (auto &u : use_I) { + if (!u.need_clr()) continue; + live.insert(u); + } + } + } +} + +void PassRegAlloc::make_work_list(sptr(MFunction) func) { + /* + forall n in initial + initial <- initial - {n} + if degree[n] >= K then + spillWorklist <- spillWorklist U {n} + else if MoveRelated(n then + freezeWorklist <- freezeWorklist U {n} + else + simplifyWorklist <- simplifyWorklist U {n} + */ + // initial 其实就是所有的虚拟寄存器 + for (auto i = 0; i < func->virt_reg_cnt; ++i) { + auto vr = MOperand::VirtReg(i); + if (degree[vr] >= K) { + spill_worklist.insert(vr); + } + else if (move_related(vr)) { + freeze_worklist.insert(vr); + } + else { + simplify_worklist.insert(vr); + } + } +} + +// movelist[n] INTERSECT (activemoves UNION worklistmoves) +std::set PassRegAlloc::node_moves(const MOperand &n) { + auto ret = move_list.at(n); + for (auto itr = ret.begin(); itr != ret.end();) { + if (!ASSOC_FOUND(active_moves, *itr) && !ASSOC_FOUND(worklist_moves, *itr)) + itr = ret.erase(itr); + else + ++itr; + } + return ret; +} + +// adjList[n] - (selectStak UNION coalescedNodes) +std::set PassRegAlloc::adjacent(const MOperand &n) { + auto ret = adj_list.at(n); + for (auto itr = ret.begin(); itr != ret.end();) { + if (STD_FOUND(select_stack, *itr) || ASSOC_FOUND(coalesced_nodes, *itr)) + itr = ret.erase(itr); + else + ++itr; + } + return ret; +} + +bool PassRegAlloc::move_related(const MOperand &n) { + std::set res = node_moves(n); + return !res.empty(); +} + +void PassRegAlloc::enable_moves(const MOperand &n) { + for (auto &m : node_moves(n)) { + if (ASSOC_FOUND(active_moves, m)) { + active_moves.erase(m); + worklist_moves.insert(m); + } + } +} + +void PassRegAlloc::decrement_degree(const MOperand &m) { + int deg = degree.at(m)--; + if (deg == K) { + /* manual inlined + EnableMoves({m} U Adjacent(m)) + */ + enable_moves(m); + for (auto &adj : adjacent(m)) { + enable_moves(adj); + } + + spill_worklist.insert(m); + if (move_related(m)) + freeze_worklist.insert(m); + else + simplify_worklist.insert(m); + } +} + +void PassRegAlloc::simplify() { + auto n = *simplify_worklist.begin(); + simplify_worklist.erase(simplify_worklist.begin()); + select_stack.push_back(n); + auto adjacent_n = adjacent(n); + for (auto &m : adjacent_n) { + decrement_degree(m); + } +} + +void PassRegAlloc::coalesce() { + auto _adj_ok = [&](const MOperand &u, const MOperand &v) -> bool { + auto adj_v = adjacent(v); + for (auto &t : adj_v) + if (!OK(t, u)) return false; + return true; + }; + auto m = *worklist_moves.begin(); + auto u = get_alias(m->dst); + auto v = get_alias(m->src); + if (v.op_type == MOpTag::PreColor) { + std::swap(u, v); + } + worklist_moves.erase(m); + if (u == v) { + coalesced_moves.insert(m); + add_work_list(u); + } + else if (v.op_type == MOpTag::PreColor || ASSOC_FOUND(adj_set, std::make_pair(u, v))) { + constrained_moves.insert(m); + add_work_list(u); + add_work_list(v); + } + else if ((u.op_type == MOpTag::PreColor && _adj_ok(u, v)) || (u.op_type != MOpTag::PreColor && conservative(set_union(adjacent(u), adjacent(v))))) { + coalesced_moves.insert(m); + combine(u, v); + add_work_list(u); + } + else { + active_moves.insert(m); + } +} + +void PassRegAlloc::add_work_list(const MOperand &u) { + if (u.op_type != MOpTag::PreColor && !move_related(u) && degree.at(u) < K) { + freeze_worklist.erase(u); + simplify_worklist.insert(u); + } +} + +bool PassRegAlloc::OK(const MOperand &t, const MOperand &r) { + return degree[t] < K || t.op_type == MOpTag::PreColor || ASSOC_FOUND(adj_set, std::make_pair(t, r)); +} + +MOperand PassRegAlloc::get_alias(MOperand n) { + while (ASSOC_FOUND(coalesced_nodes, n)) n = alias[n]; + return n; +} + +bool PassRegAlloc::conservative(const std::set &nodes) { + int k = 0; + for (auto &n : nodes) { + if (degree[n] >= K) k++; + } + return k < K; +} + +void PassRegAlloc::combine(const MOperand &u, const MOperand &v) { + if (ASSOC_FOUND(freeze_worklist, v)) + freeze_worklist.erase(v); + else + spill_worklist.erase(v); + + coalesced_nodes.insert(v); + alias[v] = u; + // movelist[u] <- movelist[u] UNION movelist[v] + auto &movelist_u = move_list.at(u); + auto &movelist_v = move_list.at(v); + for (auto mv : movelist_v) { + movelist_u.insert(mv); + } + // TODO: TrivialCompiler skipped the enablemoves below + enable_moves(v); + for (auto t : adjacent(v)) { + add_edge(t, u); + decrement_degree(t); + } + if (degree.at(u) >= K && ASSOC_FOUND(freeze_worklist, u)) { + freeze_worklist.erase(u); + spill_worklist.insert(u); + } +} + +void PassRegAlloc::freeze() { + auto u = *freeze_worklist.begin(); + freeze_worklist.erase(u); + simplify_worklist.insert(u); + freeze_moves(u); +} + +void PassRegAlloc::freeze_moves(const MOperand &u) { + for (auto &m : node_moves(u)) { + auto x = get_alias(m->src); + auto v = get_alias(m->dst); + if (v == get_alias(u)) v = x; + active_moves.erase(m); + frozen_moves.insert(m); + if (node_moves(v).empty() && degree[v] < K) { + freeze_worklist.erase(v); + simplify_worklist.insert(v); + } + } +} + +void PassRegAlloc::select_spill() { + MOperand m = *spill_worklist.begin(); + int max_deg = 0; + for (auto &node : spill_worklist) { + if (degree[node] > max_deg) { + m = node; + max_deg = degree[node]; + } + } + // TODO: heuristic to be improved, other than simply select the max degree + simplify_worklist.insert(m); + freeze_moves(m); + spill_worklist.erase(m); +} + +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); + + 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); + } + } + + if (ok_colors.empty()) + spilled_nodes.insert(n); + else { + colored_nodes.insert(n); + color[n] = MOperand::AllocReg((RV64Reg)*ok_colors.begin()); + } + } + for (auto n : coalesced_nodes) { + auto n_alias = get_alias(n); + if (n_alias.op_type == MOpTag::PreColor) + color[n] = n_alias; + else + color[n] = color[n_alias]; + } + // patch all the color info back to the MCIR tree, note the pointer type here + for (auto bb : func->bb_list) { + for (auto inst : bb->inst_list) { + std::vector def; + std::vector use; + get_inst_defuse(inst, def, use); + for (auto d : def) { + if (ASSOC_FOUND(color, *d)) *d = color.at(*d); + } + for (auto u : use) { + if (ASSOC_FOUND(color, *u)) *u = color.at(*u); + } + } + } +} + +void PassRegAlloc::rewrite_program(sptr(MFunction) func) { + for (auto v : spilled_nodes) { + for (auto bb : func->bb_list) { + sptr(MInst) firstuse = nullptr; + sptr(MInst) lastdef = nullptr; + int vr = -1; + for (auto inst : bb->inst_list) { + std::vector def; + std::vector use; + get_inst_defuse(inst, def, use); + for (auto d : def) { + if (vr < 0) vr = func->virt_reg_cnt ++; + d->value = vr; + lastdef = inst; + } + for (auto u : use) { + if (vr < 0) vr = func->virt_reg_cnt ++; + u->value = vr; + if (!lastdef && !firstuse) firstuse = inst; + } + // TODO: TrivialCompiler count bb size and add intermediate load/store + } + auto gen_off = [&](sptr(MInst) inst) { + auto off_imm = MOperand::Imm(func->stack_size); + if (off_imm.value < 2048) { + return off_imm; + } + else { + auto inst_mv = MInstMove::New(inst); + inst_mv->src = off_imm; + inst_mv->dst = MOperand::VirtReg(func->virt_reg_cnt++); + return inst_mv->dst; + } + }; + if (firstuse) { + auto inst_ld = MInstLoad::New(firstuse); + 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); + inst_st->addr = MOperand::PreClrReg(RV64Reg::sp); + inst_st->data = MOperand::VirtReg(vr); + inst_st->offset = gen_off(inst_st); + } + } + func->stack_size += XLEN; + } +} + +/* +SLA() +Build() +MakeWorklist() +repeat + if !simplifyWorklist.empty then simplify() +until all worklist empty +AssignColors() +if !spilledNodes.empty() then + RewriteProgram + Main() +*/ +void PassRegAlloc::reg_alloc(sptr(MFunction) func) { + clear(); + set_bb_def_use(func); + liveness_analysis(func); + build(func); + make_work_list(func); + bool flag = true; + do { + flag = false; + if (!simplify_worklist.empty()) { + simplify(); flag = true; + } + if (!worklist_moves.empty()) { + coalesce(); flag = true; + } + if (!freeze_worklist.empty()) { + freeze(); flag = true; + } + if (!spill_worklist.empty()) { + select_spill(); flag = true; + } + } while (flag); + assign_colors(func); + if (!spilled_nodes.empty()) { + rewrite_program(func); + reg_alloc(func); + } +} + +void PassRegAlloc::run(const MCModule &module) { + for (auto func : module.function_list) { + LOG(INFO) << "Run " << pass_name << " for func " << func->ir_func->name; + reg_alloc(func); + } +} + +} // namespace CompSysY \ No newline at end of file