buggy reg alloc

This commit is contained in:
ridethepig 2023-05-26 21:51:41 +08:00
parent 32bb8f38a7
commit 9b9a96881a
11 changed files with 826 additions and 112 deletions

View File

@ -1,7 +1,7 @@
#pragma once
#include "llir.h"
#include "passes.h"
#include "pass.h"
namespace CompSysY {
void gen_dominance(FunctionPtr_t func);

View File

@ -1,12 +1,16 @@
#pragma once
#include "3rdparty/easylogging++.h"
#include <algorithm>
#include <any>
#include <bitset>
#include <cassert>
#include <exception>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
@ -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 { \

View File

@ -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<MOperand>(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<MOperand>(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<MOperand>(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<sptr(MBasicBlock)> pred_list;
std::list<sptr(MBasicBlock)> succ_list;
std::unordered_set<sptr(MOperand)> livein;
std::unordered_set<sptr(MOperand)> liveout;
std::set<MOperand> use;
std::set<MOperand> def;
std::set<MOperand> livein;
std::set<MOperand> 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<MInstLoad>(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<MInstStore>(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<MInstMove>(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<MInstMove>(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<MOperand> &def, std::vector<MOperand> &use);
void get_inst_defuse(sptr(MInst) inst, std::vector<MOperand*> &def, std::vector<MOperand*> &use);
void set_bb_def_use(sptr(MFunction) func);
} // namespace CompSysY

View File

@ -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<sptr(MInstMove)> node_moves(const MOperand &n);
std::set<MOperand> 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<MOperand> &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<MOperand, std::set<MOperand>> adj_list;
std::set<std::pair<MOperand, MOperand>> adj_set;
std::map<MOperand, unsigned> degree;
std::map<MOperand, std::set<sptr(MInstMove)>> move_list;
std::map<MOperand, MOperand> color;
std::map<MOperand, MOperand> alias;
std::set<MOperand> simplify_worklist;
std::set<MOperand> freeze_worklist;
std::set<MOperand> spill_worklist;
std::set<MOperand> spilled_nodes;
std::set<MOperand> coalesced_nodes;
std::set<MOperand> colored_nodes;
std::vector<MOperand> select_stack;
std::set<sptr(MInstMove)> coalesced_moves;
std::set<sptr(MInstMove)> constrained_moves;
std::set<sptr(MInstMove)> frozen_moves;
std::set<sptr(MInstMove)> worklist_moves;
std::set<sptr(MInstMove)> 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

View File

@ -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

View File

@ -1,4 +0,0 @@
#pragma once
#include "pass.h"
#include "pass_build_cfg.h"

View File

@ -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 <fstream>
@ -128,6 +128,11 @@ int main(int argc, const char **argv) {
MCModule mc_module;
mc_module.IR2MC(visitor.module);
std::vector<sptr(MCPass)> mc_passes = {
std::make_shared<PassRegAlloc>()
};
for (auto pass : mc_passes) {
pass->run(mc_module);
}
return 0;
}

View File

@ -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<sptr(Value), sptr(MOperand)> &val2mop
std::unordered_map<sptr(Value), MOperand> &val2mop
) {
if (auto fparam = shared_cast<FParam>(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<MBasicBlock>();
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<sptr(Value), sptr(MOperand)> mp_val2op;
std::unordered_map<sptr(Value), MOperand> mp_val2op;
std::unordered_map<User *, sptr(Instruction)> 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<InstLoad>(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<InstStore>(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<InstGEP>(inst)) {
// gep <elem_ty> <elem_ty*> 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<PointerType>(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<IntegerType>(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<BasicBlock>(br->operand_list[1]));
inst_br->branch_tag = inverse_cond(inst_br->branch_tag);
inst_br->target = bb_ir2mc.at(strict_shared_cast<BasicBlock>(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<BasicBlock>(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<BasicBlock>(br->operand_list[1]));
inst_br->branch_tag = inverse_cond(inst_br->branch_tag);
inst_br->target = bb_ir2mc.at(strict_shared_cast<BasicBlock>(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<BasicBlock>(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<InstPhi>(inst)) break;
auto phi = shared_cast<InstPhi>(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<MOperand> &def, std::vector<MOperand> &use) {
if (auto bin = shared_cast<MInstBinary>(inst)) {
def.push_back(bin->dst);
use.push_back(bin->op1);
use.push_back(bin->op2);
return;
}
if (auto mov = shared_cast<MInstMove>(inst)) {
def.push_back(mov->dst);
use.push_back(mov->src);
return;
}
if (auto ld = shared_cast<MInstLoad>(inst)) {
def.push_back(ld->dst);
use.push_back(ld->addr);
use.push_back(ld->offset);
return;
}
if (auto st = shared_cast<MInstStore>(inst)) {
use.push_back(st->addr);
use.push_back(st->data);
use.push_back(st->offset);
return;
}
if (auto cal = shared_cast<MInstCall>(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<MInstBranch>(inst)) {
use.push_back(br->op1);
use.push_back(br->op2);
return;
}
if (auto bin = shared_cast<MInstReturn>(inst)) {
use.push_back(MOperand::PreClrReg(RV64Reg::a0));
return;
}
if (auto sym = shared_cast<MInstSymbol>(inst)) {
def.push_back(sym->dst);
return;
}
}
void get_inst_defuse(sptr(MInst) inst, std::vector<MOperand*> &def, std::vector<MOperand*> &use) {
if (auto bin = shared_cast<MInstBinary>(inst)) {
def.push_back(&bin->dst);
use.push_back(&bin->op1);
use.push_back(&bin->op2);
return;
}
if (auto mov = shared_cast<MInstMove>(inst)) {
def.push_back(&mov->dst);
use.push_back(&mov->src);
return;
}
if (auto ld = shared_cast<MInstLoad>(inst)) {
def.push_back(&ld->dst);
use.push_back(&ld->addr);
use.push_back(&ld->offset);
return;
}
if (auto st = shared_cast<MInstStore>(inst)) {
use.push_back(&st->addr);
use.push_back(&st->data);
use.push_back(&st->offset);
return;
}
if (auto br = shared_cast<MInstBranch>(inst)) {
use.push_back(&br->op1);
use.push_back(&br->op2);
return;
}
if (auto sym = shared_cast<MInstSymbol>(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<MOperand> def;
std::vector<MOperand> use;
get_inst_defuse(inst, use, def);
for (auto &elem : def) add_def(elem);
for (auto &elem : use) add_use(elem);
}
}
}
} // namespace CompSysY

View File

@ -1,7 +1,7 @@
#include "3rdparty/easylogging++.h"
#include "common.h"
#include "llir.h"
#include "passes.h"
#include "pass.h"
#include <unordered_set>
namespace CompSysY {

View File

@ -2,7 +2,7 @@
#include "algos.h"
#include "common.h"
#include "llir.h"
#include "passes.h"
#include "pass.h"
#include "visitor.h"
#include <map>
#include <queue>

481
src/pass_reg_alloc.cpp Normal file
View File

@ -0,0 +1,481 @@
#include "pass.h"
namespace CompSysY {
template <typename T>
static std::set<T> set_union(const std::set<T> &u1, const std::set<T> &u2) {
std::set<T> 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的一个usedef
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<MOperand> 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<MOperand> 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<MOperand> def_I;
std::vector<MOperand> use_I;
get_inst_defuse(*inst_it, def_I, use_I);
if (auto inst_mv = shared_cast<MInstMove>(*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<sptr(MInstMove)> 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<MOperand> 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<sptr(MInstMove)> 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<MOperand> &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<int> 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<MOperand*> def;
std::vector<MOperand*> 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<MOperand*> def;
std::vector<MOperand*> 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