buggy buggy reg alloc
This commit is contained in:
parent
7a74685838
commit
8b0f0f63a1
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -9,7 +9,7 @@
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/build/sysy",
|
||||
"args": ["../sysytests/functional_2022/34_arr_expr_len.sy", "-S", "-o", "build/21_my.ll", "-O1", "-emit-llvm"],
|
||||
"args": ["../sysytests/functional_2022/28_while_test3.sy", "-S", "-o", "build/my.s", "-O1", "-emit-llvm"],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
|
||||
@ -37,6 +37,9 @@ inline sptr(DST) strict_shared_cast(SRC src) {
|
||||
#define STD_FOUND(container, val) (STD_FIND(container, val) != container.end())
|
||||
#define ASSOC_FOUND(cont, val) (cont.find(val) != cont.end())
|
||||
|
||||
#define INF (0x3f3f3f3f)
|
||||
#define BTWN(v, l, r) (l <= v && v <= r)
|
||||
|
||||
#define panic(message) \
|
||||
do { \
|
||||
throw GrammarException(__FILE__, __LINE__, (message)); \
|
||||
|
||||
@ -228,6 +228,10 @@ public:
|
||||
str += "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
int get_memory_size() {
|
||||
return value_list.size() * 4;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Constant> ConstantPtr_t;
|
||||
|
||||
@ -11,7 +11,7 @@ namespace CompSysY {
|
||||
// x0,gp,tp are preserved in user-space
|
||||
enum class RV64Reg {
|
||||
x0 = 0, // zero N/A
|
||||
ra, // ra, caller
|
||||
ra, // ra, caller, treated as callee-saved
|
||||
sp, // sp, callee
|
||||
gp, // gp, N/A
|
||||
tp, // tp, N/A
|
||||
@ -43,9 +43,18 @@ enum class RV64Reg {
|
||||
t5,
|
||||
t6, // t3-6,caller
|
||||
};
|
||||
inline const char* enum_to_string(const RV64Reg& tag) {
|
||||
const static char* _str_tab[] = {"x0","ra","sp","gp","tp","t0","t1","t2","s0","s1","a0","a1","a2","a3","a4","a5","a6","a7","s2","s3","s4","s5","s6","s7","s8","s9","s1","s1","t3","t4","t5","t6",};
|
||||
return _str_tab[(unsigned)tag];
|
||||
inline const char *enum_to_string(const RV64Reg ®) {
|
||||
const static char *_str_tab[] = {
|
||||
"x0", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
|
||||
"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6",
|
||||
};
|
||||
assert((unsigned)reg < 32);
|
||||
return _str_tab[(unsigned)reg];
|
||||
}
|
||||
|
||||
inline bool is_callee_saved(const RV64Reg ®) {
|
||||
return reg == RV64Reg::sp || reg == RV64Reg::ra || reg == RV64Reg::s0 || reg == RV64Reg::s1 ||
|
||||
(RV64Reg::s2 <= reg && reg <= RV64Reg::s11);
|
||||
}
|
||||
|
||||
// riscv calling convention see: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc
|
||||
@ -55,6 +64,9 @@ const unsigned XLEN_MASK = XLEN - 1;
|
||||
inline unsigned xlen_rnd_up(unsigned src) {
|
||||
return (src & XLEN_MASK) ? (src & (~XLEN_MASK)) + XLEN : src;
|
||||
}
|
||||
inline bool is_in_imm_range(int imm) {
|
||||
return -2048 <= imm && imm <= 2047;
|
||||
}
|
||||
|
||||
inline std::string RV64_RegName(RV64Reg reg) {
|
||||
std::string regname = "x" + std::to_string((int)reg);
|
||||
@ -76,16 +88,23 @@ enum class MOpTag {
|
||||
Imm,
|
||||
PreColor,
|
||||
Colored,
|
||||
Glob,
|
||||
};
|
||||
inline const char* enum_to_string(const MOpTag& tag) {
|
||||
const static char* _str_tab[] = { "Invalid", "Virt", "Imm", "PreColor", "Colored" };
|
||||
inline const char *enum_to_string(const MOpTag &tag) {
|
||||
const static char *_str_tab[] = {"Invalid", "Virt", "Imm", "PreColor", "Colored", "Glob"};
|
||||
return _str_tab[(unsigned)tag];
|
||||
}
|
||||
|
||||
// TODO Basic idea: value = id of globalvar in glob_list
|
||||
|
||||
class MOperand {
|
||||
public:
|
||||
MOpTag op_type = MOpTag::Invalid;
|
||||
int value = ~0;
|
||||
|
||||
static inline std::unordered_map<sptr(GlobalVar), int> *mcmod_global_id_map = nullptr;
|
||||
static inline std::vector<sptr(GlobalVar)> *mcmod_global_list = nullptr;
|
||||
|
||||
static MOperand VirtReg(int reg_no) {
|
||||
auto mop = MOperand({MOpTag::Virt, reg_no});
|
||||
return mop;
|
||||
@ -104,6 +123,11 @@ public:
|
||||
static MOperand AllocReg(RV64Reg phy_reg) {
|
||||
return MOperand({MOpTag::Colored, (int)phy_reg});
|
||||
}
|
||||
|
||||
static MOperand Glob(sptr(GlobalVar) glob) {
|
||||
return MOperand({MOpTag::Glob, mcmod_global_id_map->at(glob)});
|
||||
}
|
||||
|
||||
bool operator==(const MOperand &op2) const {
|
||||
return op_type == op2.op_type && value == op2.value;
|
||||
}
|
||||
@ -113,21 +137,36 @@ public:
|
||||
bool operator<(const MOperand &op2) const {
|
||||
return op_type == op2.op_type ? value < op2.value : op_type < op2.op_type;
|
||||
}
|
||||
bool is_precolored() const {
|
||||
return op_type == MOpTag::PreColor;
|
||||
}
|
||||
bool need_clr() const {
|
||||
return op_type == MOpTag::PreColor || op_type == MOpTag::Virt;
|
||||
return is_precolored() || op_type == MOpTag::Virt;
|
||||
}
|
||||
std::string to_string() {
|
||||
std::string ret = enum_to_string(op_type);
|
||||
ret += "(";
|
||||
switch (op_type) {
|
||||
case MOpTag::Imm:
|
||||
case MOpTag::Virt: ret += std::to_string(value); break;
|
||||
case MOpTag::PreColor: ret += enum_to_string((RV64Reg)value); break;
|
||||
case MOpTag::Colored: ret += enum_to_string((RV64Reg)value); break;
|
||||
case MOpTag::Invalid: assert(0);
|
||||
bool is_imm() const {
|
||||
return op_type == MOpTag::Imm;
|
||||
}
|
||||
ret += ")";
|
||||
return ret;
|
||||
bool is_glob() const {
|
||||
return op_type == MOpTag::Glob;
|
||||
}
|
||||
bool is_reg() const {
|
||||
return op_type == MOpTag::PreColor || op_type == MOpTag::Virt || op_type == MOpTag::Colored;
|
||||
}
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
struct MOperandHash {
|
||||
std::size_t operator()(MOperand const &op) const {
|
||||
using std::hash;
|
||||
auto h1 = std::hash<int>{}((int)op.op_type);
|
||||
auto h2 = std::hash<int>{}(op.value);
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
std::size_t operator()(std::pair<MOperand, MOperand> const &op) const {
|
||||
using std::hash;
|
||||
auto h1 = MOperandHash{}(op.first);
|
||||
auto h2 = MOperandHash{}(op.second);
|
||||
return h1 ^ (h2 << 1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -143,8 +182,6 @@ enum class MInstTag {
|
||||
Gt,
|
||||
Eq,
|
||||
Ne,
|
||||
And,
|
||||
Or,
|
||||
Lsh, // sll
|
||||
Rsh, // srl,sra
|
||||
Move, // actually a pseudo, mv = addi rt, rs, 0
|
||||
@ -153,9 +190,7 @@ enum class MInstTag {
|
||||
Ret,
|
||||
Load,
|
||||
Store,
|
||||
Compare,
|
||||
Call,
|
||||
Globsym,
|
||||
Comment,
|
||||
};
|
||||
|
||||
@ -174,10 +209,17 @@ inline MInstTag inverse_cond(MInstTag src_tag) {
|
||||
class MInst {
|
||||
public:
|
||||
MInstTag inst_tag;
|
||||
// Notes about WIDTH:
|
||||
// width is important when load/store to an array address
|
||||
// in other cases, since stack is XLEN-padded, for single signed int variable, ld/lw sd/sw do not make much difference
|
||||
// Similarly, it makes difference when computing address
|
||||
int width = 4;
|
||||
|
||||
sptr(MBasicBlock) parent_bb;
|
||||
MInst(MInstTag tag, sptr(MBasicBlock) parent_bb) : inst_tag(tag), parent_bb(parent_bb) {}
|
||||
virtual ~MInst() = default;
|
||||
virtual std::vector<MOperand> get_def() = 0;
|
||||
virtual std::vector<MOperand> get_use() = 0;
|
||||
};
|
||||
|
||||
class MBasicBlock {
|
||||
@ -194,15 +236,19 @@ public:
|
||||
std::set<MOperand> def;
|
||||
std::set<MOperand> livein;
|
||||
std::set<MOperand> liveout;
|
||||
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
class MFunction {
|
||||
public:
|
||||
int id;
|
||||
std::list<sptr(MBasicBlock)> bb_list;
|
||||
sptr(Function) ir_func;
|
||||
|
||||
std::list<sptr(MInst)> stack_arg_reloc;
|
||||
unsigned stack_size;
|
||||
std::unordered_set<RV64Reg> regs_to_save;
|
||||
unsigned stack_size; // only for local var, excluding the callee-saved regs
|
||||
|
||||
unsigned virt_reg_cnt = 0;
|
||||
};
|
||||
@ -211,9 +257,11 @@ class MCModule {
|
||||
public:
|
||||
std::string file_name;
|
||||
std::list<sptr(MFunction)> function_list;
|
||||
std::list<sptr(GlobalVar)> global_list;
|
||||
std::vector<sptr(GlobalVar)> global_list;
|
||||
std::unordered_map<sptr(GlobalVar), int> global_id_map;
|
||||
void IR2MC(const Module &ir_module);
|
||||
void MC2ASM(std::ostream& ostr);
|
||||
void MC2ASM(std::ostream &ostr) const;
|
||||
bool debug1 = false;
|
||||
};
|
||||
|
||||
class MInstBinary : public MInst {
|
||||
@ -223,11 +271,14 @@ public:
|
||||
MOperand op2;
|
||||
MInstBinary(MInstTag type, sptr(MBasicBlock) parent_bb) : MInst(type, parent_bb) {}
|
||||
|
||||
static sptr(MInstBinary) New(MInstTag type, sptr(MBasicBlock) parent_bb) {
|
||||
static sptr(MInstBinary) New(MInstTag type, sptr(MBasicBlock) parent_bb, int width) {
|
||||
auto inst = make_shared<MInstBinary>(type, parent_bb);
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
inst->width = width;
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstJump : public MInst {
|
||||
@ -240,6 +291,8 @@ public:
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstBranch : public MInst {
|
||||
@ -254,6 +307,8 @@ public:
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstLoad : public MInst {
|
||||
@ -263,20 +318,24 @@ public:
|
||||
MOperand offset;
|
||||
MInstLoad(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {}
|
||||
|
||||
static sptr(MInstLoad) New(sptr(MBasicBlock) parent_bb) {
|
||||
static sptr(MInstLoad) New(sptr(MBasicBlock) parent_bb, int width) {
|
||||
auto inst = make_shared<MInstLoad>(parent_bb);
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
inst->width = width;
|
||||
return inst;
|
||||
}
|
||||
static sptr(MInstLoad) New(sptr(MInst) rel_inst, bool insert_after = false) {
|
||||
static sptr(MInstLoad) New(sptr(MInst) rel_inst, int width, 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);
|
||||
inst->width = width;
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstStore : public MInst {
|
||||
@ -284,22 +343,26 @@ public:
|
||||
MOperand data;
|
||||
MOperand addr;
|
||||
MOperand offset;
|
||||
MInstStore(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Load, parent_bb) {}
|
||||
MInstStore(sptr(MBasicBlock) parent_bb) : MInst(MInstTag::Store, parent_bb) {}
|
||||
|
||||
static sptr(MInstStore) New(sptr(MBasicBlock) parent_bb) {
|
||||
static sptr(MInstStore) New(sptr(MBasicBlock) parent_bb, int width) {
|
||||
auto inst = make_shared<MInstStore>(parent_bb);
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
inst->width = width;
|
||||
return inst;
|
||||
}
|
||||
static sptr(MInstStore) New(sptr(MInst) rel_inst, bool insert_after = false) {
|
||||
static sptr(MInstStore) New(sptr(MInst) rel_inst, int width, 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);
|
||||
inst->width = width;
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstMove : public MInst {
|
||||
@ -324,22 +387,24 @@ public:
|
||||
parent_bb->inst_list.insert(rel_inst_itr, inst);
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstSymbol : public MInst {
|
||||
public:
|
||||
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<MInstSymbol>(parent_bb);
|
||||
if (insert_begin)
|
||||
parent_bb->inst_list.push_front(inst);
|
||||
else
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
return inst;
|
||||
}
|
||||
};
|
||||
// class MInstSymbol : public MInst {
|
||||
// public:
|
||||
// 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<MInstSymbol>(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:
|
||||
@ -352,6 +417,8 @@ public:
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstComment : public MInst {
|
||||
@ -363,6 +430,8 @@ public:
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
class MInstCall : public MInst {
|
||||
@ -374,6 +443,8 @@ public:
|
||||
parent_bb->inst_list.push_back(inst);
|
||||
return inst;
|
||||
}
|
||||
virtual std::vector<MOperand> get_def() override;
|
||||
virtual std::vector<MOperand> get_use() override;
|
||||
};
|
||||
|
||||
void get_inst_defuse(sptr(MInst) inst, std::vector<MOperand> &def, std::vector<MOperand> &use);
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include "llir_module.h"
|
||||
#include "machcode.h"
|
||||
|
||||
#define DEBUG_REGALLOC
|
||||
// #define DEBUG_REGALLOC
|
||||
|
||||
namespace CompSysY {
|
||||
class Pass {
|
||||
@ -39,8 +39,19 @@ 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
|
||||
const static int K = 32 - 5; // Not Allocate-able: x0, gp, tp; Reserve: sp, ra
|
||||
private:
|
||||
/*以下这些集合**各不相交**
|
||||
precolored:机器寄存器集合,每个寄存器都预先指派了一种颜色
|
||||
initial:临时寄存器集合,其中的元素既没有预着色,也没有被处理
|
||||
simplifyWorklist:低度数的传送无关的结点表。
|
||||
freezeWorklist:低度数的传送有关的结点表。
|
||||
spillWorklist:高度数的结点表。
|
||||
spilledNodes:在本轮中要被溢出的结点集合,初始为空。
|
||||
coalescedNodes:已合并的寄存器集合。当合并uV时,将y加人到这个集合中,w则被放回到某个工作表中(或反之)。
|
||||
coloredNodes :已成功着色的结点集合。
|
||||
selectstack:一个包含从图中删除的临时变量的栈
|
||||
*/
|
||||
void reg_alloc(sptr(MFunction));
|
||||
void build(sptr(MFunction));
|
||||
void add_edge(const MOperand &u, const MOperand &v);
|
||||
@ -68,6 +79,7 @@ private:
|
||||
std::map<MOperand, std::set<sptr(MInstMove)>> move_list;
|
||||
std::map<MOperand, MOperand> color;
|
||||
std::map<MOperand, MOperand> alias;
|
||||
std::set<MOperand> initial;
|
||||
std::set<MOperand> simplify_worklist;
|
||||
std::set<MOperand> freeze_worklist;
|
||||
std::set<MOperand> spill_worklist;
|
||||
@ -81,26 +93,7 @@ private:
|
||||
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();
|
||||
}
|
||||
void clear();
|
||||
};
|
||||
|
||||
} // namespace CompSysY
|
||||
@ -197,13 +197,13 @@ class BatTest:
|
||||
self.label = f"{compiler.scheme['name']}_{compiler_rf.scheme['name']}"
|
||||
self.diffout_template = f"{compiler.target_dir}/{{testcase}}/{self.label}.diff"
|
||||
|
||||
def bat_case(self, testcase, compile=False):
|
||||
def bat_case(self, testcase, compile=False, emit_llvm=True):
|
||||
Print_C.print_subheader(f"Diff test {self.label} on {testcase}")
|
||||
if compile:
|
||||
self.compiler.clean_case(testcase)
|
||||
self.compiler_rf.clean_case(testcase)
|
||||
self.compiler.compile_case(testcase)
|
||||
self.compiler_rf.compile_case(testcase)
|
||||
self.compiler.compile_case(testcase, emit_llvm)
|
||||
self.compiler_rf.compile_case(testcase, emit_llvm)
|
||||
if not compiler.run_case(testcase, 0): return False
|
||||
if not compiler_rf.run_case(testcase, 0): return False
|
||||
myout = compiler.myout_template.format(testcase=testcase)
|
||||
@ -217,10 +217,10 @@ class BatTest:
|
||||
return False
|
||||
return True
|
||||
|
||||
def bat_all_tests(self, compile=False):
|
||||
def bat_all_tests(self, compile=False, emit_llvm=True):
|
||||
|
||||
for testcase in self.testcases:
|
||||
if not self.bat_case(testcase, compile): break
|
||||
if not self.bat_case(testcase, compile, emit_llvm): break
|
||||
|
||||
scheme_ref = {
|
||||
"name": "ref",
|
||||
@ -228,7 +228,7 @@ scheme_ref = {
|
||||
"ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}",
|
||||
"asm_obj": "as --32 {asm} -o {obj}",
|
||||
"obj_bin": f"clang -m32 -Ofast -fPIE {{obj}} {LIB_PATH} -o {{bin}}",
|
||||
"sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}"
|
||||
"sy_asm": f"clang -x c -c -fPIE -m32 -S -no-integrated-as -include {HDR_PATH} {{sy}} -o {{asm}}"
|
||||
}
|
||||
|
||||
scheme_my = {
|
||||
@ -237,7 +237,17 @@ scheme_my = {
|
||||
"ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}",
|
||||
"asm_obj": "as --32 {asm} -o {obj}",
|
||||
"obj_bin": f"clang -m32 -Ofast -fPIE {{obj}} {LIB_PATH} -o {{bin}}",
|
||||
"sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}"
|
||||
# "sy_asm": f"clang -x c -c -fPIE -m32 -S -include {HDR_PATH} {{sy}} -o {{asm}}"
|
||||
}
|
||||
|
||||
scheme_my_rv64 = {
|
||||
"name": "my_rv64",
|
||||
"sy_ir": f"build/sysy -S {{sy}} -o {{ir}} -emit-llvm -O1 -no-asm",
|
||||
"ir_asm": "llc --march=x86 --relocation-model=pic {ir} -o {asm}",
|
||||
"asm_obj": "riscv64-linux-gnu-as {asm} -o {obj}",
|
||||
"obj_bin": f"riscv64-linux-gnu-gcc -static {{obj}} {LIB_PATH_RV64} -o {{bin}}",
|
||||
"sy_asm": f"build/sysy -S {{sy}} -o {{asm}} -emit-llvm -O1",
|
||||
"run": f"spike /usr/local/riscv64-linux-gnu/bin/pk {{bin}}",
|
||||
}
|
||||
|
||||
# it is really annoying to debug compile commands
|
||||
@ -248,11 +258,11 @@ scheme_my = {
|
||||
# https://discourse.llvm.org/t/cant-link-soft-float-modules-with-double-float-modules/67521
|
||||
scheme_ref_rv64 = {
|
||||
"name" : "ref_rv64",
|
||||
"sy_ir": f"clang -x c -c -fPIE -S -emit-llvm --target=riscv64-linux-gnu -include {HDR_PATH} {{sy}} -o {{ir}}",
|
||||
"sy_ir": f"clang -x c -c -fPIE -fno-stack-protector -S -emit-llvm --target=riscv64-linux-gnu -include {HDR_PATH} {{sy}} -o {{ir}}",
|
||||
"ir_asm": "llc --march=riscv64 --relocation-model=pic --float-abi=hard -mcpu=sifive-7-rv64 -mattr=+f,+m,+d {ir} -o {asm}",
|
||||
"asm_obj": "riscv64-linux-gnu-as {asm} -o {obj}",
|
||||
"obj_bin": f"riscv64-linux-gnu-gcc -static {{obj}} {LIB_PATH_RV64} -o {{bin}}",
|
||||
"sy_asm": f"clang -x c -S -no-integrated-as --target=riscv64-linux-gnu -march=rv64gc -fPIE -mfloat-abi=hard -mcpu=sifive-7-rv64 -include {HDR_PATH} {{sy}} -o {{asm}}",
|
||||
"sy_asm": f"clang -x c -S -fno-stack-protector -no-integrated-as --target=riscv64-linux-gnu -march=rv64gc -fPIE -mfloat-abi=hard -mcpu=sifive-7-rv64 -include {HDR_PATH} {{sy}} -o {{asm}}",
|
||||
"run": f"spike /usr/local/riscv64-linux-gnu/bin/pk {{bin}}",
|
||||
}
|
||||
|
||||
@ -260,6 +270,7 @@ schemes = {
|
||||
"my": scheme_my,
|
||||
"ref": scheme_ref,
|
||||
"ref_rv64": scheme_ref_rv64,
|
||||
"my_rv64": scheme_my_rv64,
|
||||
}
|
||||
# subprocess.run(f"llc -O3 -march=arm -mcpu=cortex-a72 -float-abi=hard -filetype=asm {ir} -o {asm}".split(), stdout=log_file, stderr=log_file, bufsize=1)
|
||||
# subprocess.run(f"as -march=armv7-a -mfloat-abi=hard {asm} -o {obj}".split(), stdout=log_file, stderr=log_file, bufsize=1)
|
||||
@ -271,10 +282,12 @@ if __name__ == "__main__":
|
||||
parser.add_argument('--scheme', default='ref')
|
||||
parser.add_argument('--case_prefix', default='testcases/functional')
|
||||
parser.add_argument('--case_selector', default='.*\.sy')
|
||||
parser.add_argument('--ref', default='ref')
|
||||
parser.add_argument("--clean", action='store_true', default=False)
|
||||
parser.add_argument('--compile', action='store_true', default=False)
|
||||
parser.add_argument('--run', action='store_true', default=False)
|
||||
parser.add_argument('--bat', action='store_true', default=False)
|
||||
parser.add_argument('--emit_llvm', action='store_true', default=False)
|
||||
args = parser.parse_args()
|
||||
print(args)
|
||||
testcases = case_collector(args.case_selector, args.case_prefix)
|
||||
@ -292,13 +305,13 @@ if __name__ == "__main__":
|
||||
current_schem = schemes[args.scheme]
|
||||
if args.bat:
|
||||
compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases)
|
||||
compiler_rf = Compiler(scheme=schemes["ref"],target_dir=target_dir, testcases=testcases)
|
||||
compiler_rf = Compiler(scheme=schemes[args.ref],target_dir=target_dir, testcases=testcases)
|
||||
battester = BatTest(compiler, compiler_rf, testcases)
|
||||
battester.bat_all_tests(args.compile)
|
||||
battester.bat_all_tests(args.compile, args.emit_llvm)
|
||||
elif args.compile:
|
||||
compiler = Compiler(scheme=current_schem,target_dir=target_dir, testcases=testcases)
|
||||
compiler.prepare_dir()
|
||||
compiler.compile_all_tests(error_tolerance=1, emit_llvm_ir=True)
|
||||
compiler.compile_all_tests(error_tolerance=1, emit_llvm_ir=args.emit_llvm)
|
||||
if args.run:
|
||||
compiler.run_all_tests()
|
||||
elif args.run:
|
||||
|
||||
19
src/main.cpp
19
src/main.cpp
@ -46,6 +46,9 @@ int main(int argc, const char **argv) {
|
||||
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("-no-asm").implicit_value(true).default_value(false).help(
|
||||
"Disable asm output for frontend test"
|
||||
);
|
||||
arg_parser.add_argument("--v").default_value(0);
|
||||
try {
|
||||
arg_parser.parse_args(argc, argv);
|
||||
@ -59,6 +62,7 @@ int main(int argc, const char **argv) {
|
||||
auto flg_O1 = arg_parser["-O1"] == true;
|
||||
auto flg_O0 = arg_parser["-O0"] == true;
|
||||
auto emit_llvm = arg_parser["-emit-llvm"] == true;
|
||||
auto no_asm = arg_parser["-no-asm"] == true;
|
||||
// std::cout << source_file << " " << output_file << " " << flg_O1 <<
|
||||
// std::endl;
|
||||
#pragma endregion
|
||||
@ -127,12 +131,27 @@ int main(int argc, const char **argv) {
|
||||
|
||||
// std::cout << tree->toStringTree(&parser) << std::endl << std::endl;
|
||||
|
||||
if (!no_asm) {
|
||||
MCModule mc_module;
|
||||
mc_module.file_name = source_file;
|
||||
mc_module.IR2MC(visitor.module);
|
||||
// asm before reg alloc
|
||||
auto virt_asm_file = output_file.substr(0, output_file.rfind(".")) + "_virt.asm";
|
||||
std::ofstream ofs_virt_asm_file(virt_asm_file);
|
||||
mc_module.debug1 = true;
|
||||
mc_module.MC2ASM(ofs_virt_asm_file);
|
||||
mc_module.debug1 = false;
|
||||
|
||||
std::vector<sptr(MCPass)> mc_passes = {std::make_shared<PassRegAlloc>()};
|
||||
for (auto pass : mc_passes) {
|
||||
pass->run(mc_module);
|
||||
}
|
||||
std::ofstream ofs_asm_file(output_file);
|
||||
if (!ofs_asm_file.good()) {
|
||||
LOG(ERROR) << "Failed to create/overwrite asm output file " << output_file;
|
||||
return 1;
|
||||
}
|
||||
mc_module.MC2ASM(ofs_asm_file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
#include "mc_inst.h"
|
||||
#include "common.h"
|
||||
#include "mc_inst.h"
|
||||
|
||||
namespace CompSysY {
|
||||
|
||||
@ -28,54 +28,400 @@ static void bb_debug(std::ostream &ostr, sptr(MBasicBlock) bb) {
|
||||
ostr << "\n";
|
||||
}
|
||||
|
||||
void MCModule::MC2ASM(std::ostream &ostr) {
|
||||
// header, specifying align and arch
|
||||
ostr << "\t.text\n" << "\t.attribute\t4,\t16\n"
|
||||
<< "\t.attribute\t5\t\"rv64i2p0_m2p0_f2p0_d2p0\"\n";
|
||||
ostr << "\t.file\t\"" << this->file_name << "\"\n" << endl;
|
||||
for (auto func : this->function_list) {
|
||||
// function header
|
||||
ostr << "\n\n";
|
||||
ostr << ".global\t" << func->ir_func->name << endl;
|
||||
ostr << "\t.type\t" << func->ir_func->name << "\tSTT_FUNC" << endl;
|
||||
ostr << func->ir_func->name << ":" << endl;
|
||||
//TODO entry code
|
||||
for (auto bb : func->bb_list) {
|
||||
ostr << "L_BB_" << bb->id << ":" << endl;
|
||||
ostr << "/*\n";
|
||||
bb_debug(ostr, bb);
|
||||
ostr << "\n*/" << endl;
|
||||
for (auto inst : bb->inst_list) {
|
||||
static void emit_add(std::ostream &ostr, sptr(MInstBinary) inst) {
|
||||
std::string opcode;
|
||||
std::string rd, rs1, rs2;
|
||||
rd = inst->dst.to_string();
|
||||
if (inst->op1.is_imm() && inst->op2.is_imm()) {
|
||||
LOG(WARNING) << "Unexpected ConstExpr";
|
||||
assert(0);
|
||||
}
|
||||
else if (inst->op1.is_imm()) {
|
||||
opcode = "addi";
|
||||
rs1 = inst->op2.to_string();
|
||||
rs2 = inst->op1.to_string();
|
||||
}
|
||||
else if (inst->op2.is_imm()) {
|
||||
opcode = "addi";
|
||||
rs1 = inst->op1.to_string();
|
||||
rs2 = inst->op2.to_string();
|
||||
}
|
||||
else {
|
||||
opcode = "add";
|
||||
rs1 = inst->op1.to_string();
|
||||
rs2 = inst->op2.to_string();
|
||||
}
|
||||
if (inst->width == 4) opcode += "w";
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl;
|
||||
}
|
||||
|
||||
static void emit_sub(std::ostream &ostr, sptr(MInstBinary) inst) {
|
||||
std::string opcode;
|
||||
std::string rd, rs1, rs2;
|
||||
rd = inst->dst.to_string();
|
||||
if (inst->op1.is_imm() && inst->op2.is_imm()) {
|
||||
LOG(WARNING) << "Unexpected ConstExpr";
|
||||
assert(0);
|
||||
}
|
||||
else if (inst->op1.is_imm()) {
|
||||
opcode = "addi";
|
||||
rs1 = inst->op2.to_string();
|
||||
rs2 = "-" + inst->op1.to_string();
|
||||
}
|
||||
else if (inst->op2.is_imm()) {
|
||||
opcode = "addi";
|
||||
rs1 = inst->op1.to_string();
|
||||
rs2 = "-" + inst->op2.to_string();
|
||||
}
|
||||
else {
|
||||
opcode = "sub";
|
||||
rs1 = inst->op1.to_string();
|
||||
rs2 = inst->op2.to_string();
|
||||
}
|
||||
if (inst->width == 4) opcode += "w";
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl;
|
||||
}
|
||||
|
||||
static void emit_muldivrem(std::ostream &ostr, sptr(MInstBinary) inst) {
|
||||
std::string opcode;
|
||||
std::string rd, rs1, rs2;
|
||||
assert(!inst->op1.is_imm());
|
||||
assert(!inst->op2.is_imm());
|
||||
|
||||
// immediate should have been separatedly loaded in a mv
|
||||
rd = inst->dst.to_string();
|
||||
rs1 = inst->op1.to_string();
|
||||
rs2 = inst->op2.to_string();
|
||||
switch (inst->inst_tag) {
|
||||
case MInstTag::Add: break;
|
||||
case MInstTag::Sub: break;
|
||||
case MInstTag::Mul: break;
|
||||
case MInstTag::Div: break;
|
||||
case MInstTag::Mod: break;
|
||||
case MInstTag::Lt: break;
|
||||
case MInstTag::Le: break;
|
||||
case MInstTag::Ge: break;
|
||||
case MInstTag::Gt: break;
|
||||
case MInstTag::Eq: break;
|
||||
case MInstTag::Ne: break;
|
||||
case MInstTag::And: break;
|
||||
case MInstTag::Or: break;
|
||||
case MInstTag::Lsh: break;
|
||||
case MInstTag::Rsh: break;
|
||||
case MInstTag::Move: break;
|
||||
case MInstTag::Branch: break;
|
||||
case MInstTag::Jmp: break;
|
||||
case MInstTag::Ret: break;
|
||||
case MInstTag::Load: break;
|
||||
case MInstTag::Store: break;
|
||||
case MInstTag::Compare: break;
|
||||
case MInstTag::Call: break;
|
||||
case MInstTag::Globsym: break;
|
||||
case MInstTag::Comment: break;
|
||||
case MInstTag::Mul: opcode = "mul"; break;
|
||||
case MInstTag::Div: opcode = "div"; break;
|
||||
case MInstTag::Mod: opcode = "rem"; break;
|
||||
default: assert(0);
|
||||
}
|
||||
if (inst->width == 4) opcode += "w";
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl;
|
||||
}
|
||||
|
||||
static void emit_shamt(std::ostream &ostr, sptr(MInstBinary) inst) {
|
||||
std::string opcode;
|
||||
std::string rd, rs1, shamt;
|
||||
assert(inst->op2.is_imm());
|
||||
assert(inst->width == 4);
|
||||
rd = inst->dst.to_string();
|
||||
rs1 = inst->op1.to_string();
|
||||
shamt = inst->op2.to_string();
|
||||
switch (inst->inst_tag) {
|
||||
case MInstTag::Lsh: opcode = "slliw"; break;
|
||||
case MInstTag::Rsh: opcode = "sraiw"; break;
|
||||
default: assert(0);
|
||||
}
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, shamt) << endl;
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
static void emit_cmp(std::ostream &ostr, sptr(MInstBinary) inst) {
|
||||
std::string opcode;
|
||||
std::string rd = inst->dst.to_string();
|
||||
std::string rs1 = inst->op1.to_string();
|
||||
std::string rs2 = inst->op2.to_string();
|
||||
assert(!inst->op1.is_imm());
|
||||
bool is_imm = inst->op2.is_imm();
|
||||
switch (inst->inst_tag) {
|
||||
case MInstTag::Lt: opcode = is_imm ? "slti" : "slt"; break;
|
||||
case MInstTag::Gt: opcode = is_imm ? "slti" : "slt"; break;
|
||||
case MInstTag::Le: opcode = is_imm ? "slti" : "slt"; break;
|
||||
case MInstTag::Ge: opcode = is_imm ? "slti" : "slt"; break;
|
||||
case MInstTag::Eq: opcode = is_imm ? "xori" : "xor"; break;
|
||||
case MInstTag::Ne: opcode = is_imm ? "xori" : "xor"; break;
|
||||
default: assert(0);
|
||||
}
|
||||
// mind the order of rs1,rs2
|
||||
switch (inst->inst_tag) {
|
||||
case MInstTag::Lt: ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl; break;
|
||||
case MInstTag::Gt: ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs2, rs1) << endl; break;
|
||||
case MInstTag::Le:
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs2, rs1) << endl;
|
||||
ostr << fmt::format("\txori\t{}, {}, 1", rd, rd) << endl;
|
||||
break;
|
||||
case MInstTag::Ge:
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl;
|
||||
ostr << fmt::format("\txori\t{}, {}, 1", rd, rd) << endl;
|
||||
break;
|
||||
case MInstTag::Eq:
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl;
|
||||
ostr << fmt::format("\tseqz\t{}, {}, 1", rd, rd) << endl;
|
||||
break;
|
||||
case MInstTag::Ne:
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rd, rs1, rs2) << endl;
|
||||
ostr << fmt::format("\tsnez\t{}, {}, 1", rd, rd) << endl;
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void emit_branch(std::ostream &ostr, sptr(MInstBranch) inst) {
|
||||
std::string opcode;
|
||||
std::string rs1, rs2, tgt;
|
||||
assert(!inst->op1.is_imm() && !inst->op2.is_imm());
|
||||
switch (inst->branch_tag) {
|
||||
case MInstTag::Lt: opcode = "blt"; break;
|
||||
case MInstTag::Le: opcode = "ble"; break;
|
||||
case MInstTag::Ge: opcode = "bge"; break;
|
||||
case MInstTag::Gt: opcode = "bgt"; break;
|
||||
case MInstTag::Eq: opcode = "beq"; break;
|
||||
case MInstTag::Ne: opcode = "bne"; break;
|
||||
default: assert(0);
|
||||
}
|
||||
rs1 = inst->op1.to_string();
|
||||
rs2 = inst->op2.to_string();
|
||||
tgt = inst->target->to_string();
|
||||
ostr << fmt::format("\t{}\t{}, {}, {}", opcode, rs1, rs2, tgt) << endl;
|
||||
}
|
||||
|
||||
static void emit_move(std::ostream &ostr, sptr(MInstMove) inst) {
|
||||
std::string opcode;
|
||||
std::string rd, rs1;
|
||||
if (inst->src.is_imm()) {
|
||||
opcode = "li";
|
||||
}
|
||||
else if (inst->src.is_reg()) {
|
||||
opcode = "mv";
|
||||
}
|
||||
else if (inst->src.is_glob()) {
|
||||
opcode = "la";
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
}
|
||||
rd = inst->dst.to_string();
|
||||
rs1 = inst->src.to_string();
|
||||
ostr << fmt::format("\t{}\t{}, {}", opcode, rd, rs1) << endl;
|
||||
}
|
||||
|
||||
static void emit_jmp(std::ostream &ostr, sptr(MInstJump) inst) {
|
||||
ostr << "\tj\t" << inst->target->to_string() << endl;
|
||||
}
|
||||
|
||||
static void emit_load(std::ostream &ostr, sptr(MInstLoad) inst) {
|
||||
assert(inst->offset.is_imm() && !inst->addr.is_imm());
|
||||
auto dst = inst->dst.to_string();
|
||||
auto off = inst->offset.to_string();
|
||||
auto addr = inst->addr.to_string();
|
||||
std::string opcode;
|
||||
if (inst->width == 4)
|
||||
opcode = "lw";
|
||||
else if (inst->width == 8)
|
||||
opcode = "ld";
|
||||
ostr << fmt::format("\t{}\t{}, {}({})", opcode, dst, off, addr) << endl;
|
||||
}
|
||||
|
||||
static void emit_store(std::ostream &ostr, sptr(MInstStore) inst) {
|
||||
assert(inst->offset.is_imm() && !inst->addr.is_imm() && !inst->data.is_imm());
|
||||
auto data = inst->data.to_string();
|
||||
auto offset = inst->offset.to_string();
|
||||
auto addr = inst->addr.to_string();
|
||||
std::string opcode;
|
||||
if (inst->width == 4)
|
||||
opcode = "sw";
|
||||
else if (inst->width == 8)
|
||||
opcode = "sd";
|
||||
ostr << fmt::format("\t{}\t{}, {}({})", opcode, data, offset, addr) << endl;
|
||||
}
|
||||
|
||||
static void emit_call(std::ostream &ostr, sptr(MInstCall) inst) {
|
||||
ostr << fmt::format("\tcall\t{}", inst->ir_func->name) << endl;
|
||||
}
|
||||
|
||||
static void emit_comment(std::ostream &ostr, sptr(MInstComment) inst) {
|
||||
ostr << fmt::format("/*{}*/", inst->comment) << endl;
|
||||
}
|
||||
|
||||
static void emit_return(std::ostream &ostr, sptr(MInstReturn) inst) {
|
||||
auto func = inst->parent_bb->parent_func;
|
||||
if (func->stack_size) {
|
||||
int stk_imm = func->stack_size;
|
||||
if (is_in_imm_range(stk_imm)) {
|
||||
ostr << fmt::format("\taddi\tsp, sp, {}\n", stk_imm);
|
||||
}
|
||||
else {
|
||||
ostr << fmt::format("\tli\tt0, {}\n", stk_imm);
|
||||
ostr << "\tadd\tsp, sp, t0\n";
|
||||
}
|
||||
}
|
||||
if (!func->regs_to_save.empty()) {
|
||||
int pop_cnt = 0;
|
||||
for (auto reg_to_save : func->regs_to_save) {
|
||||
ostr << fmt::format("\tld\t{}, {}(sp)\n", enum_to_string(reg_to_save), pop_cnt * XLEN);
|
||||
pop_cnt++;
|
||||
}
|
||||
ostr << fmt::format("\taddi\tsp, sp, {}\n", func->regs_to_save.size() * XLEN);
|
||||
}
|
||||
ostr << "\tret" << endl;
|
||||
}
|
||||
|
||||
static void emit_array(std::ostream &ostr, sptr(ConstantArr) arr) {
|
||||
int zero_cnt = 0;
|
||||
for (auto val : arr->value_list) {
|
||||
if (val) {
|
||||
if (zero_cnt) {
|
||||
ostr << fmt::format("\t.zero\t{}\n", zero_cnt * 4);
|
||||
zero_cnt = 0;
|
||||
}
|
||||
auto init_val = shared_cast<ConstantInt>(val);
|
||||
assert(init_val);
|
||||
ostr << fmt::format("\t.word\t{0}\t\t# {0:#x}\n", init_val->value);
|
||||
}
|
||||
else {
|
||||
zero_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stack_postprocess(sptr(MFunction) func) {
|
||||
for (auto bb : func->bb_list) {
|
||||
for (auto inst : bb->inst_list) {
|
||||
auto defs = inst->get_def();
|
||||
for (auto def : defs) {
|
||||
if (is_callee_saved((RV64Reg)def.value)) {
|
||||
func->regs_to_save.insert((RV64Reg)def.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto inst : func->stack_arg_reloc) {
|
||||
auto inst_load = shared_cast<MInstLoad>(inst);
|
||||
assert(inst_load && inst_load->offset.is_imm());
|
||||
inst_load->offset.value += func->stack_size + func->regs_to_save.size() * XLEN;
|
||||
}
|
||||
}
|
||||
|
||||
void emit_function(std::ostream &ostr, sptr(MFunction) func) {
|
||||
// function header
|
||||
ostr << "\t.globl\t" << func->ir_func->name << "\n";
|
||||
ostr << "\t.p2align\t1\n";
|
||||
ostr << "\t.type\t" << func->ir_func->name << ",@function\n";
|
||||
ostr << func->ir_func->name << ":" << endl;
|
||||
// entry code
|
||||
// First push callee-saved regs
|
||||
if (!func->regs_to_save.empty()) {
|
||||
ostr << fmt::format("\taddi\tsp, sp, -{}\n", func->regs_to_save.size() * XLEN);
|
||||
int push_cnt = 0;
|
||||
for (auto reg_to_save : func->regs_to_save) {
|
||||
ostr << fmt::format("\tsd\t{}, {}(sp)\n", enum_to_string(reg_to_save), push_cnt * XLEN);
|
||||
push_cnt++;
|
||||
}
|
||||
}
|
||||
// Then deal with stack allocation
|
||||
if (func->stack_size) {
|
||||
int stk_imm = func->stack_size;
|
||||
if (is_in_imm_range(-stk_imm)) {
|
||||
ostr << fmt::format("\taddi\tsp, sp, {}\n", -stk_imm);
|
||||
}
|
||||
else {
|
||||
ostr << fmt::format("\tli\tt0, {}\n", stk_imm);
|
||||
ostr << "\tsub\tsp, sp, t0\n";
|
||||
}
|
||||
}
|
||||
// emit every instruction
|
||||
for (auto bb : func->bb_list) {
|
||||
ostr << bb->to_string() << ":" << endl;
|
||||
ostr << "/*\n";
|
||||
bb_debug(ostr, bb);
|
||||
ostr << "*/" << endl;
|
||||
for (auto inst : bb->inst_list) {
|
||||
switch (inst->inst_tag) {
|
||||
case MInstTag::Add: emit_add(ostr, shared_cast<MInstBinary>(inst)); break;
|
||||
case MInstTag::Sub: emit_sub(ostr, shared_cast<MInstBinary>(inst)); break;
|
||||
case MInstTag::Mul:
|
||||
case MInstTag::Div:
|
||||
case MInstTag::Mod: emit_muldivrem(ostr, shared_cast<MInstBinary>(inst)); break;
|
||||
case MInstTag::Lsh:
|
||||
case MInstTag::Rsh: emit_shamt(ostr, shared_cast<MInstBinary>(inst)); break;
|
||||
case MInstTag::Lt:
|
||||
case MInstTag::Le:
|
||||
case MInstTag::Ge:
|
||||
case MInstTag::Gt:
|
||||
case MInstTag::Eq:
|
||||
case MInstTag::Ne: emit_cmp(ostr, shared_cast<MInstBinary>(inst)); break;
|
||||
case MInstTag::Move: emit_move(ostr, shared_cast<MInstMove>(inst)); break;
|
||||
case MInstTag::Branch: emit_branch(ostr, shared_cast<MInstBranch>(inst)); break;
|
||||
case MInstTag::Jmp: emit_jmp(ostr, shared_cast<MInstJump>(inst)); break;
|
||||
case MInstTag::Ret: emit_return(ostr, shared_cast<MInstReturn>(inst)); break;
|
||||
case MInstTag::Call: emit_call(ostr, shared_cast<MInstCall>(inst)); break;
|
||||
case MInstTag::Load: emit_load(ostr, shared_cast<MInstLoad>(inst)); break;
|
||||
case MInstTag::Store: emit_store(ostr, shared_cast<MInstStore>(inst)); break;
|
||||
case MInstTag::Comment: emit_comment(ostr, shared_cast<MInstComment>(inst)); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ostr << fmt::format(".Lfunc_end{}:\n", func->id);
|
||||
ostr << fmt::format("\t.size\t{}, .Lfunc_end{}-{}\n", func->ir_func->name, func->id, func->ir_func->name);
|
||||
ostr << fmt::format("/* -- End Function {} --*/", func->ir_func->name) << endl;
|
||||
}
|
||||
|
||||
void MCModule::MC2ASM(std::ostream &ostr) const {
|
||||
// header, specifying align and arch
|
||||
ostr << "\t.text\n"
|
||||
<< "\t.attribute\t4,\t16\n"
|
||||
<< "\t.attribute\t5,\t\"rv64i2p0_m2p0_f2p0_d2p0\"\n";
|
||||
ostr << "\t.file\t\"" << this->file_name << "\"\n" << endl;
|
||||
for (auto func : this->function_list) {
|
||||
if (!debug1) stack_postprocess(func);
|
||||
emit_function(ostr, func);
|
||||
}
|
||||
|
||||
ostr << "\n/* -- data section -- */\n.data\n";
|
||||
for (auto glob : this->global_list) {
|
||||
if (!glob->init_value) continue;
|
||||
ostr << fmt::format(
|
||||
"\t.type\t{0},@object\n"
|
||||
"\t.globl\t{0}\n"
|
||||
"\t.p2align\t2\n"
|
||||
"{0}:\n",
|
||||
glob->name
|
||||
);
|
||||
int glob_size = 0;
|
||||
if (auto initval = shared_cast<ConstantInt>(glob->init_value)) {
|
||||
ostr << fmt::format("\t.word\t{0}\t\t# {0:#x}\n", initval->value);
|
||||
glob_size = 4;
|
||||
}
|
||||
else {
|
||||
auto init_arr = shared_cast<ConstantArr>(glob->init_value);
|
||||
emit_array(ostr, init_arr);
|
||||
glob_size = init_arr->get_memory_size();
|
||||
}
|
||||
ostr << fmt::format("\t.size\t{}, {}\n", glob->name, glob_size) << endl;
|
||||
}
|
||||
ostr << "/* -- bss section -- */\n.bss\n";
|
||||
for (auto glob : this->global_list) {
|
||||
if (glob->init_value) continue;
|
||||
ostr << fmt::format(
|
||||
"\t.type\t{0},@object\n"
|
||||
"\t.globl\t{0}\n"
|
||||
"\t.p2align\t2\n"
|
||||
"{0}:\n",
|
||||
glob->name
|
||||
);
|
||||
int glob_size = 0;
|
||||
if (shared_cast<IntegerType>(shared_cast<PointerType>(glob->type)->pointed_type)) {
|
||||
glob_size = 4;
|
||||
}
|
||||
else {
|
||||
glob_size = get_type_size(shared_cast<ArrayType>(shared_cast<PointerType>(glob->type)->pointed_type));
|
||||
}
|
||||
ostr << fmt::format("\t.zero\t{}", glob_size) << endl;
|
||||
ostr << fmt::format("\t.size\t{}, {}", glob->name, glob_size) << endl;
|
||||
}
|
||||
ostr << "\n\t.ident\t\"CompSysY\"\n\t.section\t\".note.GNU-stack\",\"\",@progbits" << endl;
|
||||
}
|
||||
|
||||
} // namespace CompSysY
|
||||
@ -7,14 +7,17 @@ using std::make_shared;
|
||||
|
||||
namespace CompSysY {
|
||||
|
||||
static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb) {
|
||||
static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb, bool force_reg = false) {
|
||||
auto operand = MOperand::Imm(imm);
|
||||
// 12 bit signed imm for I/S-type
|
||||
if (-2048 <= imm && imm <= 2047) {
|
||||
if (!force_reg && -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?
|
||||
if (imm == 0) {
|
||||
return MOperand::PreClrReg(RV64Reg::x0);
|
||||
}
|
||||
auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++);
|
||||
auto inst_move = MInstMove::New(mc_bb);
|
||||
inst_move->src = operand;
|
||||
@ -25,7 +28,8 @@ static auto gen_imm(int imm, sptr(MBasicBlock) mc_bb) {
|
||||
static MOperand value2moperand(
|
||||
sptr(Value) ir_value,
|
||||
sptr(MBasicBlock) mc_bb,
|
||||
std::unordered_map<sptr(Value), MOperand> &val2mop
|
||||
std::unordered_map<sptr(Value), MOperand> &val2mop,
|
||||
bool force_reg = false
|
||||
) {
|
||||
if (auto fparam = shared_cast<FParam>(ir_value)) {
|
||||
auto itr = val2mop.find(ir_value);
|
||||
@ -50,9 +54,15 @@ static MOperand value2moperand(
|
||||
// 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
|
||||
// TODO Trivial Compiler(THU2020) use an addition move, ~but I am not sure why, deleted temporally~
|
||||
// In-case the stack is too large that exceeds limit of imm field, but We first skip this
|
||||
// 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)
|
||||
int width = 0;
|
||||
if (shared_cast<IntegerType>(fparam->type))
|
||||
width = 4;
|
||||
else if (shared_cast<PointerType>(fparam->type))
|
||||
width = XLEN;
|
||||
auto inst_load = MInstLoad::New(mc_bb, width); // ld vr, (i-8)*8(sp)
|
||||
// auto inst_move = MInstMove::New(inst_load); // lui vr_t,
|
||||
inst_load->addr = MOperand::PreClrReg(RV64Reg::sp);
|
||||
inst_load->offset = MOperand::Imm((fparam_ndx - 8) * XLEN);
|
||||
@ -66,16 +76,26 @@ static MOperand value2moperand(
|
||||
if (itr != val2mop.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
auto inst_symld = MInstSymbol::New(mc_bb->parent_func->bb_list.front(), true);
|
||||
// auto inst_symld = MInstSymbol::New(mc_bb->parent_func->bb_list.front(), true);
|
||||
// auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++);
|
||||
// val2mop.insert({ir_value, vr});
|
||||
// inst_symld->symbol = glob;
|
||||
// inst_symld->dst = vr;
|
||||
// return vr;
|
||||
auto op_glob = MOperand::Glob(glob);
|
||||
if (force_reg) {
|
||||
auto inst_la = MInstMove::New(mc_bb);
|
||||
auto vr = MOperand::VirtReg(mc_bb->parent_func->virt_reg_cnt++);
|
||||
val2mop.insert({ir_value, vr});
|
||||
inst_symld->symbol = glob;
|
||||
inst_symld->dst = vr;
|
||||
inst_la->dst = vr;
|
||||
inst_la->src = op_glob;
|
||||
return vr;
|
||||
}
|
||||
return op_glob;
|
||||
}
|
||||
else if (auto constant = shared_cast<ConstantInt>(ir_value)) {
|
||||
auto imm = constant->value;
|
||||
return gen_imm(imm, mc_bb);
|
||||
return gen_imm(imm, mc_bb, force_reg);
|
||||
}
|
||||
else {
|
||||
// plain situation
|
||||
@ -94,7 +114,12 @@ static MOperand value2moperand(
|
||||
void MCModule::IR2MC(const Module &ir_module) {
|
||||
// Simply copy globals, since they don't need any translation
|
||||
int bb_id_cnt = 0;
|
||||
int fn_id_cnt = 0;
|
||||
MOperand::mcmod_global_id_map = &global_id_map;
|
||||
MOperand::mcmod_global_list = &global_list;
|
||||
|
||||
for (auto glob : ir_module.global_var_list) {
|
||||
this->global_id_map.insert({glob, global_list.size()});
|
||||
this->global_list.push_back(glob);
|
||||
}
|
||||
|
||||
@ -103,12 +128,13 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
auto mc_func = make_shared<MFunction>();
|
||||
this->function_list.push_back(mc_func);
|
||||
mc_func->ir_func = func;
|
||||
mc_func->id = fn_id_cnt++;
|
||||
|
||||
// copy pred/succ info
|
||||
std::unordered_map<sptr(BasicBlock), sptr(MBasicBlock)> bb_ir2mc;
|
||||
for (auto bb : func->bb_list) {
|
||||
auto mc_bb = make_shared<MBasicBlock>();
|
||||
mc_bb->id = bb_id_cnt ++;
|
||||
mc_bb->id = bb_id_cnt++;
|
||||
mc_bb->itr = mc_func->bb_list.insert(mc_func->bb_list.end(), mc_bb);
|
||||
mc_bb->ir_bb = bb;
|
||||
mc_bb->parent_func = mc_func;
|
||||
@ -133,39 +159,44 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
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);
|
||||
auto addr = value2moperand(ld->operand_list[0], mc_bb, mp_val2op, true);
|
||||
// width is important when load/store to an array address, so be careful
|
||||
int width = XLEN;
|
||||
if (shared_cast<IntegerType>(ld->type)) width = 4;
|
||||
auto mc_li = MInstLoad::New(mc_bb, width);
|
||||
mc_li->addr = addr;
|
||||
mc_li->dst = value2moperand(ld, mc_bb, mp_val2op);
|
||||
// mc_li->offset = nullptr;
|
||||
mc_li->offset = MOperand::Imm(0);
|
||||
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);
|
||||
auto data = value2moperand(st->operand_list[0], mc_bb, mp_val2op, true);
|
||||
auto addr = value2moperand(st->operand_list[1], mc_bb, mp_val2op, true);
|
||||
int width = XLEN;
|
||||
if (shared_cast<IntegerType>(st->operand_list[0]->type)) width = 4;
|
||||
auto mc_li = MInstStore::New(mc_bb, width);
|
||||
mc_li->addr = addr;
|
||||
mc_li->data = data;
|
||||
// mc_li->offset = nullptr;
|
||||
mc_li->offset = MOperand::Imm(0);
|
||||
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 ptr = value2moperand(inst->operand_list[0], mc_bb, mp_val2op, true);
|
||||
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 == MOpTag::Imm && index0.value == 0);
|
||||
assert(index0.is_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 ((inst->operand_list.size() < 3 && !index0.value) || (inst->operand_list.size() == 3 && !index1.value)) {
|
||||
if ((inst->operand_list.size() < 3 && !index0.value) || (inst->operand_list.size() == 3 && index1.is_imm() && !index1.value)) {
|
||||
// a shortcut for zero gep
|
||||
auto inst_mv = MInstMove::New(mc_bb);
|
||||
inst_mv->dst = dst;
|
||||
@ -174,36 +205,39 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
}
|
||||
else if (inst->operand_list.size() < 3) {
|
||||
// index on same dim: addr = ptr + index0 * sizeof(elem_ty)
|
||||
if (index0.is_imm()) index0 = gen_imm(index0.value, mc_bb, true);
|
||||
auto elem_size = get_type_size(elem_type);
|
||||
auto elem_size_imm = gen_imm(elem_size, mc_bb);
|
||||
auto elem_size_imm = gen_imm(elem_size, mc_bb, true);
|
||||
auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++);
|
||||
auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb);
|
||||
auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb, XLEN);
|
||||
inst_mul->dst = vr;
|
||||
inst_mul->op1 = index0;
|
||||
inst_mul->op2 = elem_size_imm;
|
||||
auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb);
|
||||
auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb, XLEN);
|
||||
inst_add->dst = dst;
|
||||
inst_add->op1 = ptr;
|
||||
inst_add->op2 = vr;
|
||||
inst_add->op1 = vr;
|
||||
inst_add->op2 = ptr;
|
||||
VLOG(6) << "gep ptr + elem_ty * index";
|
||||
}
|
||||
else {
|
||||
// index on sub dim: addr = ptr + index1 * sizeof(elem_ty.elem_ty)
|
||||
if (index1.is_imm()) index1 = gen_imm(index1.value, mc_bb, true);
|
||||
auto elem_elem_size = get_pointed_type_size(elem_type);
|
||||
auto elem_elem_size_imm = gen_imm(elem_elem_size, mc_bb);
|
||||
auto elem_elem_size_imm = gen_imm(elem_elem_size, mc_bb, true);
|
||||
auto vr = MOperand::VirtReg(mc_func->virt_reg_cnt++);
|
||||
auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb);
|
||||
auto inst_mul = MInstBinary::New(MInstTag::Mul, mc_bb, XLEN);
|
||||
inst_mul->dst = vr;
|
||||
inst_mul->op1 = index0;
|
||||
inst_mul->op1 = index1;
|
||||
inst_mul->op2 = elem_elem_size_imm;
|
||||
auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb);
|
||||
auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb, XLEN);
|
||||
inst_add->dst = dst;
|
||||
inst_add->op1 = ptr;
|
||||
inst_add->op2 = vr;
|
||||
inst_add->op1 = vr;
|
||||
inst_add->op2 = ptr;
|
||||
VLOG(6) << "gep ptr + elem_ty.elem_ty * index";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (auto alc = shared_cast<InstAlloca>(inst)) {
|
||||
if (auto alc = shared_cast<InstAlloca>(inst)) {
|
||||
unsigned alloca_size = 0;
|
||||
auto allocated_type = get_pointed_type(alc->type);
|
||||
if (shared_cast<IntegerType>(allocated_type) || shared_cast<PointerType>(allocated_type)) {
|
||||
@ -220,14 +254,17 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
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);
|
||||
auto inst_add = MInstBinary::New(MInstTag::Add, mc_bb, XLEN);
|
||||
inst_add->dst = dst;
|
||||
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;
|
||||
LOG(TRACE) << "Allocated " << alloca_size << "Bytes on stack for " << alc->name
|
||||
<< ", stack size=" << mc_func->stack_size;
|
||||
continue;
|
||||
}
|
||||
else if (auto ret = shared_cast<InstReturn>(inst)) {
|
||||
if (auto ret = shared_cast<InstReturn>(inst)) {
|
||||
if (ret->operand_list.size()) {
|
||||
auto retval = value2moperand(ret->operand_list[0], mc_bb, mp_val2op);
|
||||
auto inst_mv = MInstMove::New(mc_bb);
|
||||
@ -235,8 +272,9 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
inst_mv->dst = MOperand::PreClrReg(RV64Reg::a0);
|
||||
}
|
||||
MInstReturn::New(mc_bb);
|
||||
continue;
|
||||
}
|
||||
else if (auto cal = shared_cast<InstCall>(inst)) {
|
||||
if (auto cal = shared_cast<InstCall>(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) {
|
||||
@ -249,7 +287,7 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
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);
|
||||
auto inst_store = MInstStore::New(mc_bb, XLEN);
|
||||
inst_store->addr = MOperand::PreClrReg(RV64Reg::sp);
|
||||
inst_store->offset = st_off_imm;
|
||||
inst_store->data = rparam;
|
||||
@ -280,8 +318,9 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
inst_mv->src = MOperand::PreClrReg(RV64Reg::a0);
|
||||
inst_mv->dst = dst;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (auto br = shared_cast<InstBranch>(inst)) {
|
||||
if (auto br = shared_cast<InstBranch>(inst)) {
|
||||
if (br->operand_list.size() == 1) {
|
||||
auto inst_jump = MInstJump::New(mc_bb);
|
||||
auto target = br->operand_list.front();
|
||||
@ -290,8 +329,8 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
else {
|
||||
if (mp_br2icmp.find(dynamic_cast<User *>(br.get())) != mp_br2icmp.end()) {
|
||||
auto cond = mp_br2icmp.at(dynamic_cast<User *>(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 op1 = value2moperand(cond->operand_list[0], mc_bb, mp_val2op, true);
|
||||
auto op2 = value2moperand(cond->operand_list[1], mc_bb, mp_val2op, true);
|
||||
auto inst_br = MInstBranch::New(mc_bb);
|
||||
inst_br->op1 = op1;
|
||||
inst_br->op2 = op2;
|
||||
@ -308,42 +347,59 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
// 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->target = bb_ir2mc.at(strict_shared_cast<BasicBlock>(br->operand_list[2]));
|
||||
}
|
||||
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]));
|
||||
inst_br->target = bb_ir2mc.at(strict_shared_cast<BasicBlock>(br->operand_list[1]));
|
||||
}
|
||||
else {
|
||||
panic("Unexpected branch pattern");
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto cond = value2moperand(br->operand_list[0], mc_bb, mp_val2op);
|
||||
auto cond = value2moperand(br->operand_list[0], mc_bb, mp_val2op, true);
|
||||
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->target = bb_ir2mc.at(strict_shared_cast<BasicBlock>(br->operand_list[2]));
|
||||
}
|
||||
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]));
|
||||
inst_br->target = bb_ir2mc.at(strict_shared_cast<BasicBlock>(br->operand_list[1]));
|
||||
}
|
||||
else {
|
||||
panic("Unexpected branch pattern");
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (auto bin = shared_cast<InstBinary>(inst)) {
|
||||
if (auto bin = shared_cast<InstBinary>(inst)) {
|
||||
auto op1 = inst->operand_list[0];
|
||||
auto op2 = inst->operand_list[1];
|
||||
// Frontend should promise constant expr to be eliminated
|
||||
// assert(!(shared_cast<ConstantInt>(op1) && shared_cast<ConstantInt>(op2)));
|
||||
if (shared_cast<ConstantInt>(op1) && shared_cast<ConstantInt>(op2)) {
|
||||
LOG(WARNING) << "Constant expr not eliminated: " << bin->to_string();
|
||||
auto res = shared_cast<ConstantInt>(op1)->value + shared_cast<ConstantInt>(op2)->value;
|
||||
auto val1 = shared_cast<ConstantInt>(op1)->value;
|
||||
auto val2 = shared_cast<ConstantInt>(op2)->value;
|
||||
auto res = ~0;
|
||||
switch (inst->tag) {
|
||||
case InstTag::Add: res = val1 + val2; break;
|
||||
case InstTag::Sub: res = val1 - val2; break;
|
||||
case InstTag::Mod: res = val1 % val2; break;
|
||||
case InstTag::Mul: res = val1 * val2; break;
|
||||
case InstTag::Div: res = val1 / val2; break;
|
||||
case InstTag::Lt: res = val1 < val2; break;
|
||||
case InstTag::Le: res = val1 <= val2; break;
|
||||
case InstTag::Ge: res = val1 >= val2; break;
|
||||
case InstTag::Gt: res = val1 > val2; break;
|
||||
case InstTag::Eq: res = val1 == val2; break;
|
||||
case InstTag::Ne: res = val1 != val2; break;
|
||||
default: assert(0);
|
||||
}
|
||||
auto src_imm = gen_imm(res, mc_bb);
|
||||
auto dst = value2moperand(inst, mc_bb, mp_val2op);
|
||||
auto inst_mv = MInstMove::New(mc_bb);
|
||||
@ -379,7 +435,7 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
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);
|
||||
auto inst_rsh = MInstBinary::New(MInstTag::Lsh, mc_bb, 4);
|
||||
inst_rsh->dst = dst;
|
||||
inst_rsh->op1 = mc_op;
|
||||
inst_rsh->op2 = MOperand::Imm(exp);
|
||||
@ -389,7 +445,7 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
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);
|
||||
auto inst_rsh = MInstBinary::New(MInstTag::Rsh, mc_bb, 4);
|
||||
inst_rsh->dst = dst;
|
||||
inst_rsh->op1 = mc_op1;
|
||||
inst_rsh->op2 = MOperand::Imm(exp);
|
||||
@ -407,16 +463,6 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
}
|
||||
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)
|
||||
@ -437,21 +483,23 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
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);
|
||||
auto mc_op1 = value2moperand(op1, mc_bb, mp_val2op, true);
|
||||
auto mc_op2 = value2moperand(op2, mc_bb, mp_val2op, BTWN(minst_tag, MInstTag::Mul, MInstTag::Mod));
|
||||
auto inst_bin = MInstBinary::New(minst_tag, mc_bb, 4);
|
||||
inst_bin->dst = dst;
|
||||
inst_bin->op1 = mc_op1;
|
||||
inst_bin->op2 = mc_op2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (auto zxt = shared_cast<InstZext>(inst)) {
|
||||
if (auto zxt = shared_cast<InstZext>(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;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -464,7 +512,8 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
for (auto ir_bb : func->bb_list) {
|
||||
auto mc_bb = bb_ir2mc.at(ir_bb);
|
||||
std::list<pmv> par_mv_cur; // parallel move in current bb
|
||||
std::unordered_map<sptr(MBasicBlock), pmv> par_mv_pred; // parallel move in each pred bb
|
||||
std::unordered_multimap<sptr(MBasicBlock), pmv> par_mv_pred; // parallel move in each pred bb
|
||||
// This multimap makes great sense here
|
||||
/* 某个bb的开头有一个phi指令
|
||||
%phi_dst = phi [val1 bb1] [val2 bb2] [val3 bb3] ...
|
||||
为这东西新建一个虚拟寄存器vr1
|
||||
@ -492,7 +541,7 @@ void MCModule::IR2MC(const Module &ir_module) {
|
||||
inst_mv->dst = pmv.dst;
|
||||
}
|
||||
for (auto &pmv_pair : par_mv_pred) {
|
||||
auto inst_mv = MInstMove::New(pmv_pair.first, true);
|
||||
auto inst_mv = MInstMove::New(pmv_pair.first->inst_list.back());
|
||||
inst_mv->src = pmv_pair.second.src;
|
||||
inst_mv->dst = pmv_pair.second.dst;
|
||||
}
|
||||
@ -515,13 +564,13 @@ void get_inst_defuse(sptr(MInst) inst, std::vector<MOperand> &def, std::vector<M
|
||||
if (auto ld = shared_cast<MInstLoad>(inst)) {
|
||||
def.push_back(ld->dst);
|
||||
use.push_back(ld->addr);
|
||||
use.push_back(ld->offset);
|
||||
// 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);
|
||||
// use.push_back(st->offset);
|
||||
return;
|
||||
}
|
||||
if (auto cal = shared_cast<MInstCall>(inst)) {
|
||||
@ -553,10 +602,10 @@ void get_inst_defuse(sptr(MInst) inst, std::vector<MOperand> &def, std::vector<M
|
||||
use.push_back(MOperand::PreClrReg(RV64Reg::a0));
|
||||
return;
|
||||
}
|
||||
if (auto sym = shared_cast<MInstSymbol>(inst)) {
|
||||
def.push_back(sym->dst);
|
||||
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) {
|
||||
@ -588,10 +637,10 @@ void get_inst_defuse(sptr(MInst) inst, std::vector<MOperand *> &def, std::vector
|
||||
use.push_back(&br->op2);
|
||||
return;
|
||||
}
|
||||
if (auto sym = shared_cast<MInstSymbol>(inst)) {
|
||||
def.push_back(&sym->dst);
|
||||
return;
|
||||
}
|
||||
// if (auto sym = shared_cast<MInstSymbol>(inst)) {
|
||||
// def.push_back(&sym->dst);
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
|
||||
void set_bb_def_use(sptr(MFunction) func) {
|
||||
@ -607,7 +656,7 @@ void set_bb_def_use(sptr(MFunction) func) {
|
||||
for (auto inst : bb->inst_list) {
|
||||
std::vector<MOperand> def;
|
||||
std::vector<MOperand> use;
|
||||
get_inst_defuse(inst, use, def);
|
||||
get_inst_defuse(inst, def, use);
|
||||
for (auto &elem : def) add_def(elem);
|
||||
for (auto &elem : use) add_use(elem);
|
||||
}
|
||||
|
||||
104
src/mc_inst.cpp
Normal file
104
src/mc_inst.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "mc_inst.h"
|
||||
|
||||
namespace CompSysY {
|
||||
|
||||
std::string MBasicBlock::to_string() {
|
||||
std::string str = "LBB" + std::to_string(parent_func->id) + "_" + std::to_string(id);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string MOperand::to_string() {
|
||||
std::string ret;
|
||||
switch (op_type) {
|
||||
case MOpTag::Imm: ret = fmt::format("{}", value); break;
|
||||
case MOpTag::Virt: ret = fmt::format("VReg_{}", value); break;
|
||||
case MOpTag::PreColor: ret = enum_to_string((RV64Reg)value); break;
|
||||
case MOpTag::Colored: ret = enum_to_string((RV64Reg)value); break;
|
||||
case MOpTag::Glob: ret = mcmod_global_list->at(value)->name; break;
|
||||
case MOpTag::Invalid: assert(0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstBinary::get_def() {
|
||||
return {dst};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstJump::get_def() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstBranch::get_def() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstLoad::get_def() {
|
||||
return {dst};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstStore::get_def() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstMove::get_def() {
|
||||
return {dst};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstReturn::get_def() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstComment::get_def() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstCall::get_def() {
|
||||
std::vector<MOperand> def;
|
||||
// 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 def;
|
||||
}
|
||||
|
||||
std::vector<MOperand> MInstBinary::get_use() {
|
||||
return {op1, op2};
|
||||
}
|
||||
std::vector<MOperand> MInstJump::get_use() {
|
||||
return {};
|
||||
}
|
||||
std::vector<MOperand> MInstBranch::get_use() {
|
||||
return {op1, op2};
|
||||
}
|
||||
std::vector<MOperand> MInstLoad::get_use() {
|
||||
return {addr};
|
||||
}
|
||||
std::vector<MOperand> MInstStore::get_use() {
|
||||
return {addr, data};
|
||||
}
|
||||
std::vector<MOperand> MInstMove::get_use() {
|
||||
return {src};
|
||||
}
|
||||
std::vector<MOperand> MInstReturn::get_use() {
|
||||
return {MOperand::PreClrReg(RV64Reg::a0)};
|
||||
}
|
||||
std::vector<MOperand> MInstComment::get_use() {
|
||||
return {};
|
||||
}
|
||||
std::vector<MOperand> MInstCall::get_use() {
|
||||
std::vector<MOperand> use;
|
||||
for (int i = 0; i < ir_func->fparam_list.size() && i < 8; ++i) {
|
||||
use.push_back(MOperand::PreClrReg(RV64_RegOffset(RV64Reg::a0, i)));
|
||||
}
|
||||
return use;
|
||||
}
|
||||
} // namespace CompSysY
|
||||
@ -1,7 +1,6 @@
|
||||
#include "pass.h"
|
||||
|
||||
namespace CompSysY {
|
||||
using std::cout, std::endl;
|
||||
|
||||
template <typename T>
|
||||
static std::set<T> set_union(const std::set<T> &u1, const std::set<T> &u2) {
|
||||
@ -52,10 +51,12 @@ static void liveness_analysis(sptr(MFunction) func) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_REGALLOC
|
||||
LOG(TRACE) << "Live Analysis done for " << func->ir_func->name;
|
||||
#if 0
|
||||
using std::cout, std::endl;
|
||||
LOG(TRACE) << "SLA info in " << func->ir_func->name;
|
||||
for (auto bb : func->bb_list) {
|
||||
cout << "BB "<< bb->ir_bb->name << endl;
|
||||
cout << "BB " << bb->ir_bb->name << endl;
|
||||
cout << " def: ";
|
||||
for (auto def : bb->def) {
|
||||
cout << def.to_string() << ", ";
|
||||
@ -86,18 +87,25 @@ void PassRegAlloc::add_edge(const MOperand &u, const MOperand &v) {
|
||||
adj_set.insert({u, v});
|
||||
adj_set.insert({v, u});
|
||||
// if u not-in precolored then
|
||||
if (u.op_type != MOpTag::PreColor) {
|
||||
if (!u.is_precolored()) {
|
||||
adj_list[u].insert(v);
|
||||
degree[u]++;
|
||||
}
|
||||
if (v.op_type != MOpTag::PreColor) {
|
||||
if (!v.is_precolored()) {
|
||||
adj_list[v].insert(u);
|
||||
degree[v]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use SLA result to build interference map and init worklist_moves
|
||||
/*
|
||||
用活跃信息构造冲突图.思路也还算是直观:
|
||||
从bb的liveout开始,倒序扫描指令,把当前指令的def和当前live连边;
|
||||
然后把当前指令的use加入live集合中,相当于当前指令的livein传递到前一条指令的liveout里面
|
||||
在构造的时候,需要考虑move指令,根据书中前面提到的策略,把mv的src从live中移除,这样就不会构造src和dst之间的冲突边
|
||||
然后move_list是将操作数node映射到对应的mv指令上,在这里就加进去了
|
||||
然后worklist_move是"有可能合并的move指令的集合",这里初始默认认为所有的move都可能合并
|
||||
*/
|
||||
void PassRegAlloc::build(sptr(MFunction) func) {
|
||||
for (auto bb : func->bb_list) {
|
||||
// let live = liveout(b)
|
||||
@ -121,8 +129,8 @@ void PassRegAlloc::build(sptr(MFunction) func) {
|
||||
}
|
||||
}
|
||||
// live <- live UNION def(I)
|
||||
for (auto &elem : def_I) {
|
||||
if (elem.need_clr()) live.insert(elem);
|
||||
for (auto &d : def_I) {
|
||||
if (d.need_clr()) live.insert(d);
|
||||
}
|
||||
// forall d in def(I)
|
||||
for (auto &d : def_I) {
|
||||
@ -132,26 +140,27 @@ void PassRegAlloc::build(sptr(MFunction) func) {
|
||||
}
|
||||
// live <- use(I) UNION (live - def(I))
|
||||
for (auto &d : def_I) {
|
||||
if (!d.need_clr()) continue;
|
||||
live.erase(d);
|
||||
if (d.need_clr()) live.erase(d);
|
||||
}
|
||||
for (auto &u : use_I) {
|
||||
if (!u.need_clr()) continue;
|
||||
live.insert(u);
|
||||
if (u.need_clr()) live.insert(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
主要就是把待染色的virt_reg分类
|
||||
*/
|
||||
void PassRegAlloc::make_work_list(sptr(MFunction) func) {
|
||||
/*
|
||||
forall n in initial
|
||||
initial <- initial - {n}
|
||||
if degree[n] >= K then
|
||||
if degree[n] >= K then // 高度数节点表
|
||||
spillWorklist <- spillWorklist U {n}
|
||||
else if MoveRelated(n then
|
||||
else if MoveRelated(n) then // 低度数mv有关节点表
|
||||
freezeWorklist <- freezeWorklist U {n}
|
||||
else
|
||||
else // 低度数mv无关节点表
|
||||
simplifyWorklist <- simplifyWorklist U {n}
|
||||
*/
|
||||
// initial 其实就是所有的虚拟寄存器
|
||||
@ -171,8 +180,10 @@ void PassRegAlloc::make_work_list(sptr(MFunction) func) {
|
||||
|
||||
// movelist[n] INTERSECT (activemoves UNION worklistmoves)
|
||||
std::set<sptr(MInstMove)> PassRegAlloc::node_moves(const MOperand &n) {
|
||||
auto ret = move_list.at(n);
|
||||
// for empty set, it does not even exists, so do not use `.at(n)`
|
||||
auto ret = move_list[n];
|
||||
for (auto itr = ret.begin(); itr != ret.end();) {
|
||||
// not found in either set, then remove from movelist
|
||||
if (!ASSOC_FOUND(active_moves, *itr) && !ASSOC_FOUND(worklist_moves, *itr))
|
||||
itr = ret.erase(itr);
|
||||
else
|
||||
@ -183,7 +194,7 @@ std::set<sptr(MInstMove)> PassRegAlloc::node_moves(const MOperand &n) {
|
||||
|
||||
// adjList[n] - (selectStak UNION coalescedNodes)
|
||||
std::set<MOperand> PassRegAlloc::adjacent(const MOperand &n) {
|
||||
auto ret = adj_list.at(n);
|
||||
auto ret = adj_list[n];
|
||||
for (auto itr = ret.begin(); itr != ret.end();) {
|
||||
if (STD_FOUND(select_stack, *itr) || ASSOC_FOUND(coalesced_nodes, *itr))
|
||||
itr = ret.erase(itr);
|
||||
@ -194,12 +205,12 @@ std::set<MOperand> PassRegAlloc::adjacent(const MOperand &n) {
|
||||
}
|
||||
|
||||
bool PassRegAlloc::move_related(const MOperand &n) {
|
||||
std::set<sptr(MInstMove)> res = node_moves(n);
|
||||
return !res.empty();
|
||||
return !node_moves(n).empty();
|
||||
}
|
||||
|
||||
void PassRegAlloc::enable_moves(const MOperand &n) {
|
||||
for (auto &m : node_moves(n)) {
|
||||
auto node_moves_n = node_moves(n);
|
||||
for (auto &m : node_moves_n) {
|
||||
if (ASSOC_FOUND(active_moves, m)) {
|
||||
active_moves.erase(m);
|
||||
worklist_moves.insert(m);
|
||||
@ -208,7 +219,8 @@ void PassRegAlloc::enable_moves(const MOperand &n) {
|
||||
}
|
||||
|
||||
void PassRegAlloc::decrement_degree(const MOperand &m) {
|
||||
int deg = degree.at(m)--;
|
||||
int deg = degree.at(m);
|
||||
degree[m] = deg - 1;
|
||||
if (deg == K) {
|
||||
/* manual inlined
|
||||
EnableMoves({m} U Adjacent(m))
|
||||
@ -218,7 +230,8 @@ void PassRegAlloc::decrement_degree(const MOperand &m) {
|
||||
enable_moves(adj);
|
||||
}
|
||||
|
||||
spill_worklist.insert(m);
|
||||
//? spill_worklist.insert(m);
|
||||
spill_worklist.erase(m);
|
||||
if (move_related(m))
|
||||
freeze_worklist.insert(m);
|
||||
else
|
||||
@ -226,6 +239,9 @@ void PassRegAlloc::decrement_degree(const MOperand &m) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
从simplify_list随便挑一个节点删掉,然后丢进stack,最后给他的相邻节点减少deg
|
||||
*/
|
||||
void PassRegAlloc::simplify() {
|
||||
auto n = *simplify_worklist.begin();
|
||||
simplify_worklist.erase(simplify_worklist.begin());
|
||||
@ -359,20 +375,17 @@ 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);
|
||||
|
||||
// exclude: x0, ra, sp, gp, tp (0, 1, 2, 3, 4)
|
||||
std::unordered_set<int> ok_colors = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
|
||||
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);
|
||||
else if (ASSOC_FOUND(color, alias)) {
|
||||
auto color_alias = color[alias];
|
||||
ok_colors.erase(color_alias.value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,6 +421,7 @@ void PassRegAlloc::assign_colors(sptr(MFunction) func) {
|
||||
|
||||
void PassRegAlloc::rewrite_program(sptr(MFunction) func) {
|
||||
for (auto v : spilled_nodes) {
|
||||
LOG(TRACE) << "Spill node " << v.to_string();
|
||||
for (auto bb : func->bb_list) {
|
||||
sptr(MInst) firstuse = nullptr;
|
||||
sptr(MInst) lastdef = nullptr;
|
||||
@ -417,11 +431,13 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) {
|
||||
std::vector<MOperand *> use;
|
||||
get_inst_defuse(inst, def, use);
|
||||
for (auto d : def) {
|
||||
if (*d != v) continue;
|
||||
if (vr < 0) vr = func->virt_reg_cnt++;
|
||||
d->value = vr;
|
||||
lastdef = inst;
|
||||
}
|
||||
for (auto u : use) {
|
||||
if (*u != v) continue;
|
||||
if (vr < 0) vr = func->virt_reg_cnt++;
|
||||
u->value = vr;
|
||||
if (!lastdef && !firstuse) firstuse = inst;
|
||||
@ -430,7 +446,7 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) {
|
||||
}
|
||||
auto gen_off = [&](sptr(MInst) inst) {
|
||||
auto off_imm = MOperand::Imm(func->stack_size);
|
||||
if (off_imm.value < 2048) {
|
||||
if (is_in_imm_range(off_imm.value)) {
|
||||
return off_imm;
|
||||
}
|
||||
else {
|
||||
@ -441,13 +457,13 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) {
|
||||
}
|
||||
};
|
||||
if (firstuse) {
|
||||
auto inst_ld = MInstLoad::New(firstuse);
|
||||
auto inst_ld = MInstLoad::New(firstuse, XLEN);
|
||||
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);
|
||||
auto inst_st = MInstStore::New(lastdef, XLEN, true);
|
||||
inst_st->addr = MOperand::PreClrReg(RV64Reg::sp);
|
||||
inst_st->data = MOperand::VirtReg(vr);
|
||||
inst_st->offset = gen_off(inst_st);
|
||||
@ -457,6 +473,33 @@ void PassRegAlloc::rewrite_program(sptr(MFunction) func) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Internal util for initialize data structures
|
||||
*/
|
||||
void PassRegAlloc::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();
|
||||
|
||||
// pre-define each pre-colored register's degree as inf
|
||||
for (int reg = 0; reg < 32; ++reg) degree.insert({MOperand::PreClrReg((RV64Reg)reg), INF});
|
||||
}
|
||||
|
||||
/*
|
||||
SLA()
|
||||
Build()
|
||||
@ -469,6 +512,8 @@ if !spilledNodes.empty() then
|
||||
RewriteProgram
|
||||
Main()
|
||||
*/
|
||||
void emit_function(std::ostream &ostr, sptr(MFunction) func);
|
||||
|
||||
void PassRegAlloc::reg_alloc(sptr(MFunction) func) {
|
||||
clear();
|
||||
set_bb_def_use(func);
|
||||
@ -495,9 +540,11 @@ void PassRegAlloc::reg_alloc(sptr(MFunction) func) {
|
||||
flag = true;
|
||||
}
|
||||
} while (flag);
|
||||
LOG(TRACE) << "Simplify done for " << func->ir_func->name;
|
||||
assign_colors(func);
|
||||
if (!spilled_nodes.empty()) {
|
||||
rewrite_program(func);
|
||||
emit_function(std::cout, func);
|
||||
reg_alloc(func);
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +151,8 @@ std::any Visitor::visitConstDef(SysyParser::ConstDefContext *ctx) {
|
||||
if (_scope_tab.get_level()) {
|
||||
// local const array
|
||||
auto const_arr = ConstantArr::make_shared("const_arr", array_value, array_type);
|
||||
auto glob_name = fmt::format("__const.{}.{}{}", _state.current_func->name, const_name, _state.local_const_arr_cnt++);
|
||||
auto glob_name =
|
||||
fmt::format("__const.{}.{}{}", _state.current_func->name, const_name, _state.local_const_arr_cnt++);
|
||||
auto global_var = GlobalVar::make_shared(glob_name, array_type, const_arr, true);
|
||||
module.global_var_list.push_back(global_var);
|
||||
_scope_tab.push_name(const_name, global_var);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user