From 67a32e446e1acb0721e37ea9f8e9a5f0b01d24a5 Mon Sep 17 00:00:00 2001 From: ridethepig Date: Tue, 23 May 2023 23:38:48 +0800 Subject: [PATCH] machine code gen part 1 --- include/common.h | 16 +++- include/llir_value.h | 4 +- include/machcode.h | 2 + include/mc_inst.h | 200 +++++++++++++++++++++++++++++++++------ src/algo_dominance.cpp | 6 +- src/main.cpp | 17 +++- src/mc_codegen.cpp | 108 +++++++++++++++++++++ src/pass_build_cfg.cpp | 16 ++-- src/pass_mem2reg.cpp | 12 +-- src/visitor_llir_gen.cpp | 4 +- 10 files changed, 332 insertions(+), 53 deletions(-) create mode 100644 include/machcode.h create mode 100644 src/mc_codegen.cpp diff --git a/include/common.h b/include/common.h index bb12664..58182a5 100644 --- a/include/common.h +++ b/include/common.h @@ -2,18 +2,26 @@ #include "3rdparty/easylogging++.h" #include #include +#include +#include #include #include #include -#include -#include -#include -#include #include +#include +#include +#include #define uptr(type) std::unique_ptr #define sptr(type) std::shared_ptr +template +inline sptr(DST) shared_cast(SRC src) { + return std::dynamic_pointer_cast(src); +} + +#define FIND(container, val) std::find(container.begin(), container.end(), val) + #define panic(message) \ do { \ throw GrammarException(__FILE__, __LINE__, (message)); \ diff --git a/include/llir_value.h b/include/llir_value.h index a26213d..c522657 100644 --- a/include/llir_value.h +++ b/include/llir_value.h @@ -159,8 +159,8 @@ public: std::list inst_list; std::shared_ptr parent; BasicBlockListNode_t itr; - std::list successors; - std::list predecessors; + std::list succ_list; + std::list pred_list; BasicBlockPtr_t IDOM; // dominating node std::unordered_set idom_set; // immediate dominated nodes diff --git a/include/machcode.h b/include/machcode.h new file mode 100644 index 0000000..f65b153 --- /dev/null +++ b/include/machcode.h @@ -0,0 +1,2 @@ +#pragma once +#include "mc_inst.h" \ No newline at end of file diff --git a/include/mc_inst.h b/include/mc_inst.h index 6a29d11..e0c8bab 100644 --- a/include/mc_inst.h +++ b/include/mc_inst.h @@ -1,69 +1,215 @@ #pragma once #include "common.h" +#include "llir.h" -namespace codegen{ +using std::make_shared; + +namespace codegen { // a?, t?, ra are caller-saved // s? are callee-saved // x0,gp,tp are preserved in user-space enum class RV64Reg { - x0 = 0, // zero - x1, // ra, caller - x2, // sp, callee - x3, // gp - x4, // tp - x5, // t0, caller - x6,x7, // t1-2, caller - x8, // s0/fp, callee - x9, // s1, callee - x10,x11, // a0-1,caller - x12,x13,x14,x15,x16,x17, // a2-7,caller - x18,x19,x20,x21,x22,x23,x24,x25,x26,x27, // s2-11,callee - x28,x29,x30,x31, // t3-6,caller + x0 = 0, // zero + ra, // ra, caller + sp, // sp, callee + gp, // gp + tp, // tp + t0, // t0, caller + t1, + t2, // t1-2, caller + s0, // s0/fp, callee + s1, // s1, callee + a0, + a1, // a0-1,caller + a2, + a3, + a4, + a5, + a6, + a7, // a2-7,caller + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, // s2-11,callee + t3, + t4, + t5, + t6, // t3-6,caller }; -inline std::string Reg2Name(RV64Reg reg) { +// riscv calling convention see: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + +const int XLEN = 8; + +inline std::string RV64_RegName(RV64Reg reg) { std::string regname = "x" + std::to_string((int)reg); return regname; } +inline RV64Reg RV64_RegOffset(RV64Reg reg, int offset) { + auto xi = (int)reg + offset; + return (RV64Reg)xi; +} + +class MOperand; +class MInst; +class MBasicBlock; +class MFunction; + class MOperand { public: - enum class type { + enum class OpTag { Virt, Imm, Reg, - } op_type; - int value; + } op_type = OpTag::Virt; + int value = ~0; +private: + MOperand(OpTag tag, int val) : op_type(tag), value(val) {} + +public: + static sptr(MOperand) NewVirtReg(int reg_no) { + auto mop = std::make_shared(OpTag::Virt, reg_no); + return mop; + } + + static sptr(MOperand) NewImm(int imm_val) { + auto mop = std::make_shared(OpTag::Imm, imm_val); + return mop; + } + + static sptr(MOperand) NewReg(RV64Reg phy_reg) { + auto mop = std::make_shared(OpTag::Reg, (int)phy_reg); + return mop; + } }; class MInst { public: enum class MInstTag { - Add, Sub, Rsb, Mul, Div, Mod, Lt, Le, Ge, Gt, Eq, Ne, And, Or, - LongMul, FMA, Mv, Branch, Jump, Return, Load, Store,Compare, Call, Global, + Add, + Sub, + Rsb, + Mul, + Div, + Mod, + Lt, + Le, + Ge, + Gt, + Eq, + Ne, + And, + Or, + Move, // actually a pseudo, mv = addi rt, rs, 0 + Branch, + Jump, + Return, + Load, + Store, + Compare, + Call, + Global, } tag; - MInst(MInstTag tag) : tag(tag) {} + sptr(MBasicBlock) parent_bb; + MInst(MInstTag tag, sptr(MBasicBlock) parent_bb) : tag(tag), parent_bb(parent_bb) {} }; class MBasicBlock { +public: + sptr(antlrSysY::BasicBlock) ir_bb; + sptr(MFunction) parent_func; + std::list inst_list; + std::list pred_list; + std::list succ_list; + std::unordered_set livein; + std::unordered_set liveout; }; class MFunction { - std::list bbs; - +public: + std::list bb_list; + sptr(antlrSysY::Function) ir_func; + + std::list stack_arg_reloc; + + unsigned virt_reg_cnt = 0; }; class MGlobalVar { - +public: }; class MCModule { - std::list functions; - std::list globals; +public: + std::list function_list; + std::list global_list; + void IR2MC(const antlrSysY::Module &ir_module); }; -} \ No newline at end of file +class MInstBinary : public MInst { +public: +}; + +class MInstJump : public MInst { +public: + sptr(MBasicBlock) target; + MInstJump(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Jump, parent_bb) {} + + static sptr(MInstJump) New(sptr(MBasicBlock) parent_bb) { + auto inst = make_shared(parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; + } +}; + +class MInstLoad : public MInst { +public: + sptr(MOperand) dst; + sptr(MOperand) addr; + sptr(MOperand) offset; + MInstLoad(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {} + + static sptr(MInstLoad) New(sptr(MBasicBlock) parent_bb) { + auto inst = make_shared(parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; + } +}; + +class MInstMove : public MInst { +public: + sptr(MOperand) dst; + sptr(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); + if (insert_begin) + parent_bb->inst_list.push_front(inst); + else + parent_bb->inst_list.push_back(inst); + return inst; + } + 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); + 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; + } +}; + +} // namespace codegen \ No newline at end of file diff --git a/src/algo_dominance.cpp b/src/algo_dominance.cpp index 9076861..f5768c8 100644 --- a/src/algo_dominance.cpp +++ b/src/algo_dominance.cpp @@ -51,7 +51,7 @@ void gen_dominance(FunctionPtr_t func) { auto cur_bb = bb_list[i]; std::vector temp(N, 1); // temp = {i} union (intersect Dom(j)), j in pred(i) - for (auto pred : cur_bb->predecessors) { + for (auto pred : cur_bb->pred_list) { _bitwise_and(temp, dom[pred->_dom_helper_index]); } temp[i] = true; @@ -112,8 +112,8 @@ void gen_dominance_frontier(FunctionPtr_t func) { bb->DF_set.clear(); } for (auto n : func->bb_list) { - if (n->predecessors.size() >= 2) { - for (auto pred : n->predecessors) { + if (n->pred_list.size() >= 2) { + for (auto pred : n->pred_list) { auto runner = pred; while (runner != n->IDOM) { runner->DF_set.insert(n); diff --git a/src/main.cpp b/src/main.cpp index 9e91e1d..e507fbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include "llir.h" #include "passes.h" #include "visitor.h" +#include "machcode.h" #include <3rdparty/argparse.hpp> #include #include @@ -18,6 +19,7 @@ INITIALIZE_EASYLOGGINGPP using namespace antlrSysY; using namespace antlr4; +using namespace codegen; class AbortErrorListener : public BaseErrorListener { public: @@ -42,6 +44,7 @@ int main(int argc, const char **argv) { arg_parser.add_argument("-S").implicit_value(true).help("Useless but required by the contest").required(); arg_parser.add_argument("-o").help("Output file name").required(); 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("--v").default_value(0); try { @@ -54,6 +57,7 @@ int main(int argc, const char **argv) { auto source_file = arg_parser.get("source"); auto output_file = arg_parser.get("-o"); auto flg_O1 = arg_parser["-O1"] == true; + auto flg_O0 = arg_parser["-O0"] == true; auto emit_llvm = arg_parser["-emit-llvm"] == true; // std::cout << source_file << " " << output_file << " " << flg_O1 << // std::endl; @@ -96,12 +100,20 @@ int main(int argc, const char **argv) { std::make_shared(), std::make_shared(), }; - if (flg_O1) { + + if (!flg_O0) { for (auto pass : passes) { pass->run(visitor.module); } } + std::vector optpasses = {}; + if (flg_O1) { + for (auto pass : optpasses) { + pass->run(visitor.module); + } + } + if (emit_llvm) { auto llir_file = output_file.substr(0, output_file.rfind(".")) + ".ll"; std::ofstream ofs_llir_file(llir_file); @@ -114,5 +126,8 @@ int main(int argc, const char **argv) { // std::cout << tree->toStringTree(&parser) << std::endl << std::endl; + MCModule mc_module; + mc_module.IR2MC(visitor.module); + return 0; } \ No newline at end of file diff --git a/src/mc_codegen.cpp b/src/mc_codegen.cpp new file mode 100644 index 0000000..fe78fd5 --- /dev/null +++ b/src/mc_codegen.cpp @@ -0,0 +1,108 @@ +#include "common.h" +#include "llir.h" +#include "mc_inst.h" +#include "visitor.h" + +using std::make_shared; + +namespace codegen { + +static sptr(MOperand) value2moperand( + sptr(antlrSysY::Value) ir_value, + sptr(MBasicBlock) mc_bb, + std::unordered_map &val2mop +) { + if (auto fparam = shared_cast(ir_value)) { + auto itr = val2mop.find(ir_value); + if (itr != val2mop.end()) { + return itr->second; + } + else { + auto vr = MOperand::NewVirtReg(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); + 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->dst = vr; + } + else { + // read from stack, sp + (i - 8) * 8, since ABI requires the stack to be aligned by XLEN=64 + // 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 + // 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->dst = vr; + mc_bb->parent_func->stack_arg_reloc.push_back(inst_load); + } + return vr; + } + } + else { + // plain situation + auto itr = val2mop.find(ir_value); + if (itr != val2mop.end()) { + return itr->second; + } + else { + auto vr = MOperand::NewVirtReg(mc_bb->parent_func->virt_reg_cnt++); + val2mop.insert({ir_value, vr}); + return vr; + } + } +} + +void MCModule::IR2MC(const antlrSysY::Module &ir_module) { + for (auto glob : ir_module.global_var_list) { + // TODO copy globals from ir + } + + 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; + + // copy pred/succ info + std::unordered_map bb_ir2mc; + for (auto bb : func->bb_list) { + auto mc_bb = make_shared(); + mc_func->bb_list.push_back(mc_bb); + mc_bb->ir_bb = bb; + bb_ir2mc.insert({bb, mc_bb}); + } + for (auto bb : func->bb_list) { + auto mc_bb = bb_ir2mc.at(bb); + assert(bb->succ_list.size() <= 2 && "At most 2 successors"); + for (auto succ : bb->succ_list) { + auto mc_succ = bb_ir2mc.at(succ); + mc_bb->succ_list.push_back(mc_succ); + } + for (auto pred : bb->pred_list) { + auto mc_pred = bb_ir2mc.at(pred); + mc_bb->pred_list.push_back(mc_pred); + } + } + + // instruction translate + for (auto bb : func->bb_list) { + auto mc_bb = bb_ir2mc[bb]; + for (auto inst : bb->inst_list) { + if (auto li = shared_cast(inst)) { + auto mc_li = MInstLoad::New(mc_bb); + } + } + } + } +} + +} // namespace codegen \ No newline at end of file diff --git a/src/pass_build_cfg.cpp b/src/pass_build_cfg.cpp index a8f0b70..ca25ccf 100644 --- a/src/pass_build_cfg.cpp +++ b/src/pass_build_cfg.cpp @@ -9,12 +9,12 @@ namespace antlrSysY { static void print_cfg_info(BasicBlockPtr_t bb) { std::cout << "---" << bb->name << "---\n"; std::cout << " pred: ["; - for (auto pred : bb->predecessors) { + for (auto pred : bb->pred_list) { std::cout << pred->name << ", "; } std::cout << "]\n"; std::cout << " succ: ["; - for (auto succ : bb->successors) { + for (auto succ : bb->succ_list) { std::cout << succ->name << ", "; } std::cout << "]\n"; @@ -25,7 +25,7 @@ static void print_cfg(BasicBlockPtr_t rt, bool rst = false) { static std::unordered_set vis; if (rst) vis.clear(); print_cfg_info(rt); - for (auto succ : rt->successors) { + for (auto succ : rt->succ_list) { if (!vis.count(succ)) { vis.insert(succ); print_cfg(succ); @@ -34,8 +34,8 @@ static void print_cfg(BasicBlockPtr_t rt, bool rst = false) { } static void connect(BasicBlockPtr_t pred, BasicBlockPtr_t succ) { - pred->successors.push_back(succ); - succ->predecessors.push_back(pred); + pred->succ_list.push_back(succ); + succ->pred_list.push_back(pred); } static void _build_cfg(const FunctionPtr_t func) { @@ -73,9 +73,9 @@ static void _build_cfg(const FunctionPtr_t func) { // remove unreachable blocks for (auto itr = std::next(func->bb_list.begin()); itr != func->bb_list.end();) { auto bb = *itr; - if (bb->predecessors.size() == 0) { - for (auto succ : bb->successors) { - succ->predecessors.remove(bb); + if (bb->pred_list.size() == 0) { + for (auto succ : bb->succ_list) { + succ->pred_list.remove(bb); } VLOG(6) << "remove unreachable block:" << bb->name; itr = func->bb_list.erase(itr); diff --git a/src/pass_mem2reg.cpp b/src/pass_mem2reg.cpp index c3c6457..f1ed4be 100644 --- a/src/pass_mem2reg.cpp +++ b/src/pass_mem2reg.cpp @@ -94,7 +94,7 @@ static void live_in_blocks( if (!livein_blocks.insert(bb).second) continue; // already done // 如果alloca没有在某个块中被定义,那么它一定是从前面的某个块里面live in过来的 // 所以把它也塞进队列进行处理 - for (auto pred : bb->predecessors) { + for (auto pred : bb->pred_list) { if (def_blocks.count(pred)) continue; worklist.push_back(pred); } @@ -111,12 +111,12 @@ static void print_dom_info(BasicBlockPtr_t bb) { std::cout << bb->name << ":\n"; std::cout << " level: " << bb->dom_level << "\n"; std::cout << " pred: ["; - for (auto pred : bb->predecessors) { + for (auto pred : bb->pred_list) { std::cout << pred->name << ", "; } std::cout << "]\n"; std::cout << " succ: ["; - for (auto succ : bb->successors) { + for (auto succ : bb->succ_list) { std::cout << succ->name << ", "; } std::cout << "]\n"; @@ -236,7 +236,7 @@ static void _mem_2_reg(FunctionPtr_t func) { visited[frontier_index] = true; // livein-block opt is workable in IDF, but not here // if (livein_blocks.count(frontier)) { - auto inst_phi = build_InstPhi(TypeHelper::TYPE_I32, frontier->predecessors, frontier, ai->name); + auto inst_phi = build_InstPhi(TypeHelper::TYPE_I32, frontier->pred_list, frontier, ai->name); phi_to_allocaid.insert({inst_phi, i}); // } if (!def_blocks.count(frontier)) { @@ -263,7 +263,7 @@ static void _mem_2_reg(FunctionPtr_t func) { auto phi = Value::as(inst); auto alloca_index = phi_to_allocaid.at(phi); int pred_index = -1; - for (auto pred : rename_info.bb->predecessors) { + for (auto pred : rename_info.bb->pred_list) { pred_index++; if (pred == rename_info.pred) break; } @@ -315,7 +315,7 @@ static void _mem_2_reg(FunctionPtr_t func) { rename_info.value_list[alloca_index] = phi; } } - for (auto succ : rename_info.bb->successors) { + for (auto succ : rename_info.bb->succ_list) { rename_list.push_back({succ, rename_info.bb, rename_info.value_list}); } } diff --git a/src/visitor_llir_gen.cpp b/src/visitor_llir_gen.cpp index 5ff0c06..a5702f3 100644 --- a/src/visitor_llir_gen.cpp +++ b/src/visitor_llir_gen.cpp @@ -150,7 +150,7 @@ static void _gen_blocks(std::ostream &ostr, const std::list &bl if (block_itr != block_list.begin()) { ostr << block->ir_seqno << ":"; ostr << " ;" << block->name << " pred=["; - for (auto pred : block->predecessors) { + for (auto pred : block->pred_list) { ostr << pred->ir_seqno << ", "; } ostr << "]" << std::endl; @@ -460,7 +460,7 @@ static void _gen_blocks(std::ostream &ostr, const std::list &bl LOG(WARNING) << "Unexpected type of op: " << op->to_string(); assert(0); } - auto pred = inst->parent_bb->predecessors.begin(); + auto pred = inst->parent_bb->pred_list.begin(); std::advance(pred, i); ostr << ", %" << (*pred)->ir_seqno << "]"; if (i < inst->operand_list.size() - 1) {