From 32bb8f38a79839b1400d909717911830e419f631 Mon Sep 17 00:00:00 2001 From: ridethepig Date: Thu, 25 May 2023 17:07:36 +0800 Subject: [PATCH] llvm ir to low-level ir --- docs/ARMv7-cheat-sheet.pdf | Bin 0 -> 85516 bytes include/common.h | 8 + include/llir.h | 9 +- include/llir_instruction.h | 11 +- include/llir_type.h | 20 +- include/llir_value.h | 2 +- include/mc_inst.h | 195 +++++++++++---- src/llir.cpp | 37 +++ src/main.cpp | 2 +- src/mc_codegen.cpp | 471 ++++++++++++++++++++++++++++++++++--- src/pass_mem2reg.cpp | 6 +- src/visitor.cpp | 4 +- src/visitor_factory.cpp | 68 +++--- src/visitor_llir_gen.cpp | 26 +- 14 files changed, 698 insertions(+), 161 deletions(-) create mode 100644 docs/ARMv7-cheat-sheet.pdf create mode 100644 src/llir.cpp diff --git a/docs/ARMv7-cheat-sheet.pdf b/docs/ARMv7-cheat-sheet.pdf new file mode 100644 index 0000000000000000000000000000000000000000..56da90e9c0989b14b45362779009cbce3b4789ca GIT binary patch literal 85516 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%0AOGo!diuK2Q-PKI|cT5MfYtr=O;9(&=mz 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 j#aJny&F2a{pW|X_ +#include #include #include #include @@ -20,6 +21,13 @@ inline sptr(DST) shared_cast(SRC src) { return std::dynamic_pointer_cast(src); } +template +inline sptr(DST) strict_shared_cast(SRC src) { + sptr(DST) dst = std::dynamic_pointer_cast(src); + assert(dst); + return dst; +} + #define FIND(container, val) std::find(container.begin(), container.end(), val) #define panic(message) \ diff --git a/include/llir.h b/include/llir.h index 4ab1e7f..e1c6f23 100644 --- a/include/llir.h +++ b/include/llir.h @@ -2,4 +2,11 @@ #include "llir_instruction.h" #include "llir_module.h" #include "llir_type.h" -#include "llir_value.h" \ No newline at end of file +#include "llir_value.h" + +namespace CompSysY { +// Utilities +TypePtr_t get_pointed_type(TypePtr_t ptr); +unsigned get_type_size(TypePtr_t ir_type); +unsigned get_pointed_type_size(TypePtr_t ir_type); +} // namespace CompSysY \ No newline at end of file diff --git a/include/llir_instruction.h b/include/llir_instruction.h index 8cc09a4..8365d29 100644 --- a/include/llir_instruction.h +++ b/include/llir_instruction.h @@ -31,8 +31,7 @@ enum class InstTag { Gt, Eq, Ne, - And, - Or, + // And, Or, Br, Call, Ret, @@ -53,7 +52,7 @@ public: int ir_seqno = -1; InstTag tag; BasicBlockPtr_t parent_bb; - // decltype(parent_bb->inst_list.begin()) inst_itr_in_parent; + decltype(BasicBlock::inst_list.begin()) inst_itr; Instruction(InstTag inst_tag, TypePtr_t type, BasicBlockPtr_t parent_bb) : User("", type), tag(inst_tag), parent_bb(parent_bb) {} std::string tag_string() { @@ -69,8 +68,8 @@ public: case InstTag::Gt: return "icmp sgt"; case InstTag::Eq: return "icmp eq"; case InstTag::Ne: return "icmp ne"; - case InstTag::And: return "and"; - case InstTag::Or: return "or"; + // case InstTag::And: return "and"; + // case InstTag::Or: return "or"; case InstTag::Br: return "br"; case InstTag::Call: return "call"; case InstTag::Ret: return "ret"; @@ -244,7 +243,7 @@ public: } else if (Type::isType(pointed_type)) { for (int i = 1; i < indices.size(); ++i) { - pointed_type = shared_cast(pointed_type)->element_type; + pointed_type = shared_cast(pointed_type)->elem_type; } return pointed_type; } diff --git a/include/llir_type.h b/include/llir_type.h index ddedf57..a6bc64b 100644 --- a/include/llir_type.h +++ b/include/llir_type.h @@ -111,11 +111,12 @@ public: class ArrayType : public Type { public: - TypePtr_t element_type = TypeHelper::TYPE_I32; - int element_count = 0; + TypePtr_t elem_type = TypeHelper::TYPE_I32; + int elem_count = 0; // current dim size + int type_size = 0; // = dim_size * size(elem_type) ArrayType() : Type(TypeTag::ArrayType) {} ArrayType(TypePtr_t element_type, int element_count) - : Type(TypeTag::ArrayType), element_count(element_count), element_type(element_type) {} + : Type(TypeTag::ArrayType), elem_count(element_count), elem_type(element_type) {} static std::shared_ptr build_from_list(const std::vector &dim_list) { TypePtr_t array_type = TypeHelper::TYPE_I32; sysy_assert(dim_list.size() != 0); @@ -125,17 +126,16 @@ public: return std::dynamic_pointer_cast(array_type); } virtual std::string to_string() override { - return "Array[" + std::to_string(element_count) + " x " + element_type->to_string() + "]"; + return "Array[" + std::to_string(elem_count) + " x " + elem_type->to_string() + "]"; } virtual std::string to_IR_string() override { - return "[" + std::to_string(element_count) + " x " + element_type->to_IR_string() + "]"; + return "[" + std::to_string(elem_count) + " x " + elem_type->to_IR_string() + "]"; } }; class PointerType : public Type { public: TypePtr_t pointed_type = TypeHelper::TYPE_I32; - PointerType() : Type(TypeTag::PointerType) {} PointerType(TypePtr_t pointed_type) : Type(TypeTag::PointerType), pointed_type(pointed_type) {} static auto make_shared(TypePtr_t pointed_type) { return std::make_shared(pointed_type); @@ -146,12 +146,6 @@ public: virtual std::string to_IR_string() override { return pointed_type->to_IR_string() + "*"; } - - static TypePtr_t pointedType(TypePtr_t ptr) { - assert(Type::isType(ptr)); - auto pointer_type = Type::asType(ptr); - return pointer_type->pointed_type; - } }; class FunctionType : public Type { @@ -166,6 +160,4 @@ public: } }; -TypePtr_t pointed_type(TypePtr_t ptr); - } // namespace CompSysY \ No newline at end of file diff --git a/include/llir_value.h b/include/llir_value.h index 3e19d22..2c63b7f 100644 --- a/include/llir_value.h +++ b/include/llir_value.h @@ -204,7 +204,7 @@ public: : Constant(name, type), value_list(value_list) {} int real_size() const { - return std::dynamic_pointer_cast(type)->element_count; + return std::dynamic_pointer_cast(type)->elem_count; } static std::shared_ptr make_shared( const std::string &name, diff --git a/include/mc_inst.h b/include/mc_inst.h index e0c8bab..7150e50 100644 --- a/include/mc_inst.h +++ b/include/mc_inst.h @@ -4,7 +4,7 @@ using std::make_shared; -namespace codegen { +namespace CompSysY { // a?, t?, ra are caller-saved // s? are callee-saved @@ -46,7 +46,11 @@ enum class RV64Reg { // riscv calling convention see: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc -const int XLEN = 8; +const unsigned XLEN = 8; +const unsigned XLEN_MASK = XLEN - 1; +inline unsigned xlen_rnd_up(unsigned src) { + return (src & XLEN_MASK) ? (src & (~XLEN_MASK)) + XLEN : src; +} inline std::string RV64_RegName(RV64Reg reg) { std::string regname = "x" + std::to_string((int)reg); @@ -68,14 +72,11 @@ public: enum class OpTag { Virt, Imm, - Reg, + PreColor, } 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; @@ -87,46 +88,62 @@ public: } static sptr(MOperand) NewReg(RV64Reg phy_reg) { - auto mop = std::make_shared(OpTag::Reg, (int)phy_reg); + auto mop = std::make_shared(OpTag::PreColor, (int)phy_reg); return mop; } }; +enum class MInstTag { + Add, + Sub, + Mul, + Div, + Mod, + Lt, + Le, + Ge, + Gt, + Eq, + Ne, + And, + Or, + Lsh, // sll + Rsh, // srl,sra + Move, // actually a pseudo, mv = addi rt, rs, 0 + Branch, + Jmp, + Ret, + Load, + Store, + Compare, + Call, + Globsym, + Comment, +}; + +inline MInstTag inverse_cond(MInstTag src_tag) { + switch (src_tag) { + case MInstTag::Lt: return MInstTag::Ge; + case MInstTag::Le: return MInstTag::Gt; + case MInstTag::Ge: return MInstTag::Lt; + case MInstTag::Gt: return MInstTag::Le; + case MInstTag::Eq: return MInstTag::Ne; + case MInstTag::Ne: return MInstTag::Eq; + default: assert(0); + } +} + class MInst { public: - enum class MInstTag { - 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; + MInstTag inst_tag; sptr(MBasicBlock) parent_bb; - MInst(MInstTag tag, sptr(MBasicBlock) parent_bb) : tag(tag), parent_bb(parent_bb) {} + MInst(MInstTag tag, sptr(MBasicBlock) parent_bb) : inst_tag(tag), parent_bb(parent_bb) {} }; class MBasicBlock { public: - sptr(antlrSysY::BasicBlock) ir_bb; + sptr(BasicBlock) ir_bb; sptr(MFunction) parent_func; std::list inst_list; std::list pred_list; @@ -139,32 +156,39 @@ public: class MFunction { public: std::list bb_list; - sptr(antlrSysY::Function) ir_func; + sptr(Function) ir_func; std::list stack_arg_reloc; + unsigned stack_size; unsigned virt_reg_cnt = 0; }; -class MGlobalVar { -public: -}; - class MCModule { public: std::list function_list; - std::list global_list; - void IR2MC(const antlrSysY::Module &ir_module); + std::list global_list; + void IR2MC(const Module &ir_module); }; class MInstBinary : public MInst { public: + sptr(MOperand) dst; + sptr(MOperand) op1; + sptr(MOperand) op2; + MInstBinary(MInstTag type, sptr(MBasicBlock) parent_bb) : MInst(type, parent_bb) {} + + static sptr(MInstBinary) New(MInstTag type, sptr(MBasicBlock) parent_bb) { + auto inst = make_shared(type, parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; + } }; class MInstJump : public MInst { public: sptr(MBasicBlock) target; - MInstJump(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Jump, parent_bb) {} + MInstJump(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Jmp, parent_bb) {} static sptr(MInstJump) New(sptr(MBasicBlock) parent_bb) { auto inst = make_shared(parent_bb); @@ -173,6 +197,20 @@ public: } }; +class MInstBranch : public MInst { +public: + sptr(MBasicBlock) target; + sptr(MOperand) op1; + sptr(MOperand) op2; + MInstTag branch_tag; + MInstBranch(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Branch, parent_bb) {} + static sptr(MInstBranch) 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; @@ -187,6 +225,20 @@ public: } }; +class MInstStore : public MInst { +public: + sptr(MOperand) data; + sptr(MOperand) addr; + sptr(MOperand) offset; + MInstStore(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {} + + static sptr(MInstStore) 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; @@ -201,15 +253,64 @@ public: 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 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); + 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 +class MInstSymbol : public MInst { +public: + sptr(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: + MInstReturn(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Ret, parent_bb) {} + static sptr(MInstReturn) 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 MInstComment : public MInst { +public: + std::string comment; + MInstComment(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Comment, parent_bb) {} + static sptr(MInstComment) New(sptr(MBasicBlock) parent_bb) { + auto inst = make_shared(parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; + } +}; + +class MInstCall : public MInst { +public: + sptr(Function) ir_func; + MInstCall(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Call, parent_bb) {} + static sptr(MInstCall) New(sptr(MBasicBlock) parent_bb) { + auto inst = make_shared(parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; + } +}; + +} // namespace CompSysY \ No newline at end of file diff --git a/src/llir.cpp b/src/llir.cpp new file mode 100644 index 0000000..fd1db83 --- /dev/null +++ b/src/llir.cpp @@ -0,0 +1,37 @@ +#include "llir.h" + +/* +Utilities for LLIR +*/ + +namespace CompSysY { + +TypePtr_t get_pointed_type(TypePtr_t ptr) { + assert(Type::isType(ptr)); + auto pointer_type = Type::asType(ptr); + return pointer_type->pointed_type; +} + +unsigned get_type_size(TypePtr_t ir_type) { + if (auto arr_ty = shared_cast(ir_type)) { + if (!arr_ty->type_size) arr_ty->type_size = arr_ty->elem_count * get_type_size(arr_ty->elem_type); + return arr_ty->type_size; + } + else if (auto int_ty = shared_cast(ir_type)) { + assert(int_ty->isI32()); + return 4; + } + assert(0 && "Only IntegerType|ArrayType have size"); +} + +unsigned get_pointed_type_size(TypePtr_t ir_type) { + if (auto arr_ty = shared_cast(ir_type)) { + return get_type_size(arr_ty->elem_type); + } + else if (auto ptr_ty = shared_cast(ir_type)) { + return get_type_size(ptr_ty->pointed_type); + } + assert(0 && "Only PointerType|ArrayType has pointed type"); +} + +} // namespace CompSysY \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 53a1d5a..817e065 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -127,7 +127,7 @@ int main(int argc, const char **argv) { // std::cout << tree->toStringTree(&parser) << std::endl << std::endl; MCModule mc_module; - // mc_module.IR2MC(visitor.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 index fe78fd5..a86f0ef 100644 --- a/src/mc_codegen.cpp +++ b/src/mc_codegen.cpp @@ -5,47 +5,77 @@ using std::make_shared; -namespace codegen { +namespace CompSysY { + +static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb) { + auto operand = MOperand::NewImm(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 inst_move = MInstMove::New(mc_bb); + inst_move->src = operand; + inst_move->dst = vr; + return vr; +} static sptr(MOperand) value2moperand( - sptr(antlrSysY::Value) ir_value, + sptr(Value) ir_value, sptr(MBasicBlock) mc_bb, - std::unordered_map &val2mop + std::unordered_map &val2mop ) { - if (auto fparam = shared_cast(ir_value)) { + 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; + 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 + // TODO Trivial Compiler(THU2020) use an addition move, but I am not sure why, deleted temporally + // 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 if (auto glob = shared_cast(ir_value)) { + auto itr = val2mop.find(ir_value); + if (itr != val2mop.end()) { + 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++); + val2mop.insert({ir_value, vr}); + inst_symld->symbol = glob; + inst_symld->dst = vr; + return vr; + } + else if (auto constant = shared_cast(ir_value)) { + auto imm = constant->value; + return gen_imm(imm, mc_bb); } else { // plain situation @@ -61,9 +91,10 @@ static sptr(MOperand) value2moperand( } } -void MCModule::IR2MC(const antlrSysY::Module &ir_module) { +void MCModule::IR2MC(const Module &ir_module) { + // Simply copy globals, since they don't need any translation for (auto glob : ir_module.global_var_list) { - // TODO copy globals from ir + this->global_list.push_back(glob); } for (auto func : ir_module.function_list) { @@ -73,11 +104,12 @@ void MCModule::IR2MC(const antlrSysY::Module &ir_module) { mc_func->ir_func = func; // copy pred/succ info - std::unordered_map bb_ir2mc; + 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; + mc_bb->parent_func = mc_func; bb_ir2mc.insert({bb, mc_bb}); } for (auto bb : func->bb_list) { @@ -92,17 +124,378 @@ void MCModule::IR2MC(const antlrSysY::Module &ir_module) { mc_bb->pred_list.push_back(mc_pred); } } - + 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 li = shared_cast(inst)) { - auto mc_li = MInstLoad::New(mc_bb); + 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; + 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; + 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; + assert(inst->operand_list.size() <= 3); + if (inst->operand_list.size() == 3) { + assert(index0->op_type == MOperand::OpTag::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) { + // 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) { + // 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 inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb); + inst_mul->dst = vr; + inst_mul->op1 = index0; + inst_mul->op2 = elem_size_imm; + auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb); + inst_add->dst = dst; + inst_add->op1 = ptr; + inst_add->op2 = vr; + VLOG(6) << "gep ptr + elem_ty * index"; + } + else { + // 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 inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb); + inst_mul->dst = vr; + inst_mul->op1 = index0; + inst_mul->op2 = elem_elem_size_imm; + auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb); + inst_add->dst = dst; + inst_add->op1 = ptr; + inst_add->op2 = vr; + VLOG(6) << "gep ptr + elem_ty.elem_ty * index"; + } + } + else 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)) { + // int on stack has to be aligned + // whereas, ptr is possible when a array from fparam: `%arr = alloca [10xi32]*` + alloca_size = XLEN; + } + else { + assert(shared_cast(allocated_type)); + alloca_size = get_type_size(allocated_type); + // align array on stack to XLEN + alloca_size = xlen_rnd_up(alloca_size); + } + 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); + inst_add->dst = dst; + inst_add->op1 = MOperand::NewReg(RV64Reg::sp); + inst_add->op2 = stk_sz_imm; + // dont forget to record stack usage + mc_func->stack_size += alloca_size; + } + else 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); + inst_mv->src = retval; + inst_mv->dst = MOperand::NewReg(RV64Reg::a0); + } + MInstReturn::New(mc_bb); + } + else 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) { + auto rparam = value2moperand(cal->operand_list[i], mc_bb, mp_val2op); + 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)); + } + 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->offset = st_off_imm; + inst_store->data = rparam; + } + } + 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->op2 = gen_imm(XLEN * (nparams - 8), mc_bb); + } + + auto inst_call = MInstCall::New(mc_bb); + inst_call->ir_func = strict_shared_cast(cal->operand_list[0]); + + 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->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->dst = dst; + } + } + else 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(); + inst_jump->target = bb_ir2mc.at(strict_shared_cast(target)); + } + 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 inst_br = MInstBranch::New(mc_bb); + inst_br->op1 = op1; + inst_br->op2 = op2; + switch (cond->tag) { + case InstTag::Lt: inst_br->branch_tag = MInstTag::Lt; break; + case InstTag::Le: inst_br->branch_tag = MInstTag::Le; break; + case InstTag::Ge: inst_br->branch_tag = MInstTag::Ge; break; + case InstTag::Gt: inst_br->branch_tag = MInstTag::Gt; break; + case InstTag::Eq: inst_br->branch_tag = MInstTag::Eq; break; + case InstTag::Ne: inst_br->branch_tag = MInstTag::Ne; break; + default: assert(0); + } + 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])); + } + 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])); + } + else { + panic("Unexpected branch pattern"); + } + } + 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); + 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])); + } + 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])); + } + else { + panic("Unexpected branch pattern"); + } + } + } + } + else 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 src_imm = gen_imm(res, mc_bb); + auto dst = value2moperand(inst, mc_bb, mp_val2op); + auto inst_mv = MInstMove::New(mc_bb); + inst_mv->dst = dst; + inst_mv->src = src_imm; + continue; + } + // 2^n replacement + auto _is_const_mul_power2 = [&]() -> sptr(ConstantInt) { + if (!(bin->tag == InstTag::Mul)) return nullptr; + if (auto const_op = shared_cast(op1)) { + unsigned exp = __builtin_ctz(const_op->value); + if (const_op->value == (1U << exp)) { + return const_op; + } + } + else if (auto const_op = shared_cast(op2)) { + unsigned exp = __builtin_ctz(const_op->value); + if (const_op->value == (1U << exp)) { + return const_op; + } + } + return nullptr; + }; + auto _is_const_div_power2 = [&]() -> sptr(ConstantInt) { + if (bin->tag != InstTag::Div) return nullptr; + if (auto const_op = shared_cast(op2)) { + if (const_op->value == (1U << __builtin_ctz(const_op->value))) return const_op; + } + return nullptr; + }; + if (auto const_op = _is_const_mul_power2()) { + 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); + inst_rsh->dst = dst; + inst_rsh->op1 = mc_op; + inst_rsh->op2 = MOperand::NewImm(exp); + continue; + } + if (auto const_op = _is_const_div_power2()) { + 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); + inst_rsh->dst = dst; + inst_rsh->op1 = mc_op1; + inst_rsh->op2 = MOperand::NewImm(exp); + continue; + } + // no opt for modulo, since they are all signed int, it's wrong + // TODO try to use negative imm to reduce instructions + if (InstTag::Lt <= bin->tag && bin->tag <= InstTag::Ne) { + if (bin->use_list.size() == 1 + && dynamic_cast(bin->use_list.front().user) + && bin->use_list.front().user == (*std::next(bin->inst_itr)).get()) { + // icmp + br, and the result of icmp is only used by that br, merge into single bxx instruction + mp_br2icmp.insert({bin->use_list.front().user, bin}); + continue; + } + 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) + else { + MInstTag minst_tag; + switch (bin->tag) { + case InstTag::Add: minst_tag = MInstTag::Add; break; + case InstTag::Sub: minst_tag = MInstTag::Sub; break; + case InstTag::Mul: minst_tag = MInstTag::Mul; break; + case InstTag::Div: minst_tag = MInstTag::Div; break; + case InstTag::Mod: minst_tag = MInstTag::Mod; break; + case InstTag::Lt: minst_tag = MInstTag::Lt; break; + case InstTag::Le: minst_tag = MInstTag::Le; break; + case InstTag::Ge: minst_tag = MInstTag::Ge; break; + case InstTag::Gt: minst_tag = MInstTag::Gt; break; + case InstTag::Eq: minst_tag = MInstTag::Eq; break; + case InstTag::Ne: minst_tag = MInstTag::Ne; break; + 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); + inst_bin->dst = dst; + inst_bin->op1 = mc_op1; + inst_bin->op2 = mc_op2; + } + } + else 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; + } + } + } + struct pmv { + sptr(MOperand) dst; + sptr(MOperand) src; + }; + // phi elimination: SSA Book Algo 3.6 + // 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 + /* 某个bb的开头有一个phi指令 + %phi_dst = phi [val1 bb1] [val2 bb2] [val3 bb3] ... + 为这东西新建一个虚拟寄存器vr1 + 在当前的bb开头插入 `mv %phi_dst <- %vr1` + 然后在前驱%bb_i的末尾插入 `mv %vr1 <- %val_i` + */ + 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 dst = value2moperand(inst, mc_bb, mp_val2op); + par_mv_cur.push_back({dst, vr}); + auto pred_itr = phi->parent_bb->pred_list.begin(); + auto val_itr = phi->operand_list.begin(); + // due to sloppy design, pred_bb corresponds in order as incoming val in oplist + for (; val_itr != phi->operand_list.end(); ++pred_itr, ++val_itr) { + auto pred_mc_bb = bb_ir2mc.at(*pred_itr); + auto phi_src_op = value2moperand(*val_itr, pred_mc_bb, mp_val2op); + par_mv_pred.insert({pred_mc_bb, {vr, phi_src_op}}); + } + } + for (auto &pmv : par_mv_cur) { + auto inst_mv = MInstMove::New(mc_bb, true); + inst_mv->src = pmv.src; + inst_mv->dst = pmv.dst; + } + for (auto &pmv_pair : par_mv_pred) { + auto inst_mv = MInstMove::New(pmv_pair.first, true); + inst_mv->src = pmv_pair.second.src; + inst_mv->dst = pmv_pair.second.dst; } } } } -} // namespace codegen \ No newline at end of file +} // namespace CompSysY \ No newline at end of file diff --git a/src/pass_mem2reg.cpp b/src/pass_mem2reg.cpp index 3da947f..5f0b0fc 100644 --- a/src/pass_mem2reg.cpp +++ b/src/pass_mem2reg.cpp @@ -176,7 +176,7 @@ static void _mem_2_reg(FunctionPtr_t func) { assert(bb == func->bb_list.front() && "Alloca should be at front of a func"); auto ai = Value::as(inst); // assert(is_alloca_promotable(ai) && "Invalid alloca"); - if (!Type::isType(PointerType::pointedType(ai->type))) continue; + if (!Type::isType(get_pointed_type(ai->type))) continue; alloca_list.push_back(ai); } } @@ -285,7 +285,7 @@ static void _mem_2_reg(FunctionPtr_t func) { continue; } auto ai = Value::as(li->operand_list[0]); - if (!Type::isType(PointerType::pointedType(ai->type))) { + if (!Type::isType(get_pointed_type(ai->type))) { continue; } int alloca_index = alloca_to_id.at(ai); @@ -299,7 +299,7 @@ static void _mem_2_reg(FunctionPtr_t func) { continue; } auto ai = Value::as(si->operand_list[1]); - if (!Type::isType(PointerType::pointedType(ai->type))) { + if (!Type::isType(get_pointed_type(ai->type))) { continue; } int alloca_index = alloca_to_id.at(ai); diff --git a/src/visitor.cpp b/src/visitor.cpp index 6718663..a1e9fa0 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -819,7 +819,7 @@ std::any Visitor::visitLVal(SysyParser::LValContext *ctx) { auto exp_val = any_to_Value(visitExp(exp_list[i])); ptr = build_InstGEP(ptr, {CONST0, exp_val}, _state.current_bb); } - if (Type::isType(PointerType::pointedType(ptr->type))) { + if (Type::isType(get_pointed_type(ptr->type))) { ptr = build_InstGEP(ptr, {CONST0, CONST0}, _state.current_bb); } return ptr; @@ -869,7 +869,7 @@ std::any Visitor::visitLVal(SysyParser::LValContext *ctx) { auto exp_val = any_to_Value(visitExp(exp_list[i])); ptr = build_InstGEP(ptr, {CONST0, exp_val}, _state.current_bb); } - if (Type::isType(PointerType::pointedType(ptr->type))) { + if (Type::isType(get_pointed_type(ptr->type))) { ptr = build_InstGEP(ptr, {CONST0, CONST0}, _state.current_bb); } return ptr; diff --git a/src/visitor_factory.cpp b/src/visitor_factory.cpp index 0c627f2..f6e5a3a 100644 --- a/src/visitor_factory.cpp +++ b/src/visitor_factory.cpp @@ -27,9 +27,9 @@ std::shared_ptr build_InstLoad( TypePtr_t type, std::shared_ptr parent_bb ) { - auto inst_load = std::make_shared(value, type, parent_bb); - parent_bb->inst_list.push_back(inst_load); - return inst_load; + auto inst = std::make_shared(value, type, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); + return inst; } std::shared_ptr build_InstStore( @@ -37,9 +37,9 @@ std::shared_ptr build_InstStore( std::shared_ptr pointer, std::shared_ptr parent_bb ) { - auto inst_store = std::make_shared(value, pointer, parent_bb); - parent_bb->inst_list.push_back(inst_store); - return inst_store; + auto inst = std::make_shared(value, pointer, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); + return inst; } // a little bit different, we put all alloca in the head of each basic block @@ -48,12 +48,12 @@ std::shared_ptr build_InstAlloca( TypePtr_t type, std::shared_ptr parent_bb ) { - auto inst_alloca = std::make_shared(type, parent_bb); - inst_alloca->name = name; + auto inst = std::make_shared(type, parent_bb); + inst->name = name; auto func_head_bb = parent_bb->parent->bb_list.front(); - func_head_bb->inst_list.insert(func_head_bb->inst_list.begin(), inst_alloca); + inst->inst_itr = func_head_bb->inst_list.insert(func_head_bb->inst_list.begin(), inst); // parent_bb->inst_list.push_back(inst_alloca); - return inst_alloca; + return inst; } std::shared_ptr build_InstBinary( @@ -62,12 +62,12 @@ std::shared_ptr build_InstBinary( std::shared_ptr op2, std::shared_ptr parent_bb ) { - std::shared_ptr inst_binary; - if (InstTag::Lt <= inst_tag && inst_tag <= InstTag::Or) { - inst_binary = std::make_shared(inst_tag, TypeHelper::TYPE_I1, op1, op2, parent_bb); + std::shared_ptr inst; + if (InstTag::Lt <= inst_tag && inst_tag <= InstTag::Ne) { + inst = std::make_shared(inst_tag, TypeHelper::TYPE_I1, op1, op2, parent_bb); } else if (InstTag::Add <= inst_tag && inst_tag <= InstTag::Div) { - inst_binary = std::make_shared(inst_tag, TypeHelper::TYPE_I32, op1, op2, parent_bb); + inst = std::make_shared(inst_tag, TypeHelper::TYPE_I32, op1, op2, parent_bb); } else { panic("Invalid Binary Operation"); @@ -81,14 +81,14 @@ std::shared_ptr build_InstBinary( assert(0); } } - parent_bb->inst_list.push_back(inst_binary); - return inst_binary; + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); + return inst; } std::shared_ptr build_InstZext(std::shared_ptr op, std::shared_ptr parent_bb) { - auto inst_zext = std::make_shared(op, parent_bb); - parent_bb->inst_list.push_back(inst_zext); - return inst_zext; + auto inst = std::make_shared(op, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); + return inst; } std::shared_ptr build_InstBranch( @@ -97,26 +97,26 @@ std::shared_ptr build_InstBranch( BasicBlockPtr_t false_block, BasicBlockPtr_t parent_bb ) { - auto inst = std::make_shared(cond, true_block, false_block, parent_bb); - parent_bb->inst_list.push_back(inst); + auto inst = std::make_shared(cond, true_block, false_block, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); return inst; } std::shared_ptr build_InstBranch(BasicBlockPtr_t target_block, BasicBlockPtr_t parent_bb) { - auto inst = std::make_shared(target_block, parent_bb); - parent_bb->inst_list.push_back(inst); + auto inst = std::make_shared(target_block, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); return inst; } std::shared_ptr build_InstReturn(ValuePtr_t ret_val, BasicBlockPtr_t parent_bb) { - auto inst = std::make_shared(ret_val, parent_bb); - parent_bb->inst_list.push_back(inst); + auto inst = std::make_shared(ret_val, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); return inst; } std::shared_ptr build_InstReturn(BasicBlockPtr_t parent_bb) { - auto inst = std::make_shared(parent_bb); - parent_bb->inst_list.push_back(inst); + auto inst = std::make_shared(parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); return inst; } @@ -125,8 +125,8 @@ std::shared_ptr build_InstCall( const std::vector &args, BasicBlockPtr_t parent_bb ) { - auto inst = std::make_shared(func, args, parent_bb); - parent_bb->inst_list.push_back(inst); + auto inst = std::make_shared(func, args, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); return inst; } @@ -135,8 +135,8 @@ std::shared_ptr build_InstGEP( const std::vector &indices, BasicBlockPtr_t parent_bb ) { - auto inst = std::make_shared(pointer, indices, parent_bb); - parent_bb->inst_list.push_back(inst); + auto inst = std::make_shared(pointer, indices, parent_bb); + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.end(), inst); return inst; } @@ -146,9 +146,9 @@ InstPhiPtr_t build_InstPhi( BasicBlockPtr_t parent_bb, const std::string &name ) { - auto inst = std::make_shared(type, incoming_vals, parent_bb); - inst->name = name; - parent_bb->inst_list.insert(parent_bb->inst_list.begin(), inst); + auto inst = std::make_shared(type, incoming_vals, parent_bb); + inst->name = name; + inst->inst_itr = parent_bb->inst_list.insert(parent_bb->inst_list.begin(), inst); return inst; } diff --git a/src/visitor_llir_gen.cpp b/src/visitor_llir_gen.cpp index 82c4127..51f2cf6 100644 --- a/src/visitor_llir_gen.cpp +++ b/src/visitor_llir_gen.cpp @@ -21,10 +21,10 @@ std::shared_ptr gen_arr_hierarchy( int base, int length ) { - int dim_n = array_type->element_count; + int dim_n = array_type->elem_count; int dim_size = length / dim_n; std::vector value_list; - if (array_type->element_type->type_tag == Type::TypeTag::IntegerType) { + if (array_type->elem_type->type_tag == Type::TypeTag::IntegerType) { sysy_assert(dim_size == 1); for (int i = 0; i < dim_n; ++i) { if (const_array[base + i]) { @@ -43,7 +43,7 @@ std::shared_ptr gen_arr_hierarchy( int last_non_null = -1; for (int i = 0; i < dim_n; ++i) { auto sub_arr = gen_arr_hierarchy( - std::dynamic_pointer_cast(array_type->element_type), const_array, dim_size * i, dim_size + std::dynamic_pointer_cast(array_type->elem_type), const_array, dim_size * i, dim_size ); value_list.push_back(sub_arr); if (sub_arr) last_non_null = i; @@ -64,7 +64,7 @@ static void _build_arr_init_list( std::shared_ptr arr, std::shared_ptr arr_type ) { - if (arr_type->element_type->type_tag == Type::TypeTag::IntegerType) { + if (arr_type->elem_type->type_tag == Type::TypeTag::IntegerType) { ostr << arr_type->to_IR_string(); ostr << " ["; for (int i = 0; i < arr->value_list.size(); ++i) { @@ -73,7 +73,7 @@ static void _build_arr_init_list( ostr << ", "; } } - for (int i = 0; i < arr_type->element_count - arr->value_list.size(); ++i) { + for (int i = 0; i < arr_type->elem_count - arr->value_list.size(); ++i) { ostr << ", i32 0"; } ostr << "]"; @@ -86,16 +86,16 @@ static void _build_arr_init_list( _build_arr_init_list( ostr, std::dynamic_pointer_cast(arr->value_list[i]), - std::dynamic_pointer_cast(arr_type->element_type) + std::dynamic_pointer_cast(arr_type->elem_type) ); else - ostr << arr_type->element_type->to_IR_string() << " zeroinitializer"; + ostr << arr_type->elem_type->to_IR_string() << " zeroinitializer"; if (i < arr->value_list.size() - 1) { ostr << ", "; } } - for (int i = 0; i < arr_type->element_count - arr->value_list.size(); ++i) { - ostr << ", " << arr_type->element_type->to_IR_string() << " zeroinitializer"; + for (int i = 0; i < arr_type->elem_count - arr->value_list.size(); ++i) { + ostr << ", " << arr_type->elem_type->to_IR_string() << " zeroinitializer"; } ostr << "]"; } @@ -132,8 +132,8 @@ static void _gen_blocks(std::ostream &ostr, const std::list &bl case InstTag::Gt: case InstTag::Eq: case InstTag::Ne: - case InstTag::And: - case InstTag::Or: + // case InstTag::And: + // case InstTag::Or: case InstTag::Load: case InstTag::GEP: case InstTag::Alloca: @@ -310,8 +310,8 @@ static void _gen_blocks(std::ostream &ostr, const std::list &bl case InstTag::Gt: case InstTag::Eq: case InstTag::Ne: - case InstTag::And: - case InstTag::Or: { + + { auto inst = Value::as(_inst); ostr << "%" << inst->ir_seqno << " = " << inst->tag_string() << " "; if (Value::is(inst->operand_list[0])) {