#ifndef CODEGEN_HPP #define CODEGEN_HPP #include "BasicBlock.h" #include "Constant.h" #include "Function.h" #include "IRprinter.h" #include "Instruction.h" #include "Module.h" #include "Value.h" #include "liverange.hpp" #include "regalloc.hpp" #define __RO_PART__ // #a = 8, #t = 9, reserve $t0, $t1 for temporary #define R_USABLE 17 - 2 #include #include #include #define ALIGN(x, align) ((((x) / align) + (((x) % align) ? 1 : 0)) * align) // #define STACK_ALIGN(x) (((x / 16) + (x % 16 ? 1 : 0)) * 16) #define STACK_ALIGN(x) ALIGN(x, 16) using std::map; using std::string; using std::vector; class CodeGen { public: CodeGen(Module *m_) : m(m_), LRA(m_, phi_map), RA(R_USABLE) {} string print() { string result; for (auto line : output) { if (line.find(":") == string::npos and line != "") result += "\t"; // 添加缩进 result += line + "\n"; } return result; } void run(); private: // // data member // std::map off; // stack offset to $fp std::map> func_arg_off; // to $sp // total space for args, NOTE: not aligned std::map func_arg_N; std::map>> phi_map; std::map ROdata; unsigned int stackN; // function local vars and so on Function *cur_func; Module *m; vector output; // register allocation LRA::LiveRangeAnalyzer LRA; RA::RegAllocator RA; // some instruction has lvalue, but is stack-allocated, // we need this variable to track the reg name which has rvalue. // this variable is maintain by gencopy() and LoadInst. string last_reg; // // function member // void stackMemAlloc(); void stackMemDealloc(); // In the case of register allocation, this function will return the // allocated register for that value, if the value possesses no register, // choose from from $t0 or $t1 based on id __attribute__((warn_unused_result)) string value2reg(Value *, int i = 0); // load the content in ptr to specified register. void ptrContent2reg(Value *, string); void compute_arg_info(Function *); string bool2branch(Instruction *); void getPhiMap(); void copystmt(BasicBlock *bb) { // all the phi instruction is transformed to copy-stmt for (auto © : phi_map[bb]) { output.push_back("# " + print_as_op(copy.first, false) + " = " + print_as_op(copy.second, false)); auto lvalue = static_cast(copy.first); auto src_reg = value2reg(copy.second); if (gencopy(lvalue, src_reg) == false) back2stack(lvalue); } } // if reg-allocation, store to the specific register // or is stack-allocation, set last_reg for back2stack() bool gencopy(Value *lhs, string rhs_reg) { auto [lhs_reg, find] = getRegName(lhs); if (not find) { // wait for back2stack() to store back last_reg = rhs_reg; return false; } auto is_float = lhs->get_type()->is_float_type(); if (rhs_reg != lhs_reg) { if (is_float) output.push_back("fmov.s " + lhs_reg + ", " + rhs_reg); else output.push_back("or " + lhs_reg + ", $zero, " + rhs_reg); } return true; } string label_in_assem(BasicBlock *bb) const { return cur_func->get_name() + bb->get_name().substr(5); } int typeLen(Type *type) const { if (type->is_float_type()) return 4; else if (type->is_integer_type()) { if (static_cast(type)->get_num_bits() == 32) return 4; else return 1; } else if (type->is_pointer_type()) return 8; else if (type->is_array_type()) { auto arr_tp = static_cast(type); int n = arr_tp->get_num_of_elements(); return n * typeLen(arr_tp->get_element_type()); } else { assert(false && "unexpected case while computing type-length"); } } // assert the value needed to store back is in `last_reg`, according to the // value type void back2stack(Instruction *instr) { // std::cerr << instr->print() << std::endl; auto type = instr->get_type(); string instr_ir = type->is_float_type() ? "fst" : "st"; string suff = suffix(type); // string reg = type->is_float_type() ? "$fa0" : "$a0"; string addr = "$fp, -" + std::to_string(off.at(instr)); output.push_back(instr_ir + suff + " " + last_reg + ", " + addr); } string suffix(Type *type) const { int len = typeLen(type); switch (len) { case 1: return ".b"; case 2: return ".h"; case 4: return type->is_float_type() ? ".s" : ".w"; case 8: return ".d"; } assert(false && "no such suffix"); } bool no_stack_alloca(Instruction *instr) const { if (instr->is_void()) return true; if (instr->is_fcmp() or instr->is_cmp() or instr->is_zext()) return true; if (RA.get().find(instr) != RA.get().end()) return true; return false; } string tmpregname(int i, bool is_float) const { assert(i == 0 or i == 1); return (is_float ? "$ft" : "$t") + to_string(i); } static string regname(int i, bool is_float = false) { string name; if (is_float) { assert(false && "not implemented!"); } else { if (1 <= i and i <= 8) name = "$a" + to_string(i - 1); else if (9 <= i and i <= R_USABLE) name = "$t" + to_string(i - 9 + 2); } return name; } pair getRegName(Value *, int = 0) const; void IR2assem(Instruction &, BasicBlock &); void IR2assem(LoadInst *); void IR2assem(StoreInst *); void IR2assem(ReturnInst *); void IR2assem(GetElementPtrInst *); void IR2assem(CallInst *); void IR2assem(BranchInst *); void IR2assem(BinaryInst *); void IR2assem(FpToSiInst *); void IR2assem(SiToFpInst *); void IR2assem(PhiInst *) {} // The Instructions below will do nothing void IR2assem(AllocaInst *) {} // integration with BranchInst void IR2assem(CmpInst *) {} void IR2assem(FCmpInst *) {} void IR2assem(ZextInst *) {} }; #endif