buggy buggy reg alloc

This commit is contained in:
ridethepig 2023-06-12 20:50:05 +08:00
parent 7a74685838
commit 8b0f0f63a1
13 changed files with 911 additions and 261 deletions

2
.vscode/launch.json vendored
View File

@ -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}"
}
]

View File

@ -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)); \

View File

@ -228,6 +228,10 @@ public:
str += "}";
return str;
}
int get_memory_size() {
return value_list.size() * 4;
}
};
typedef std::shared_ptr<Constant> ConstantPtr_t;

View File

@ -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 &reg) {
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 &reg) {
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 need_clr() const {
return op_type == MOpTag::PreColor || op_type == MOpTag::Virt;
bool is_precolored() const {
return op_type == MOpTag::PreColor;
}
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);
}
ret += ")";
return ret;
bool need_clr() const {
return is_precolored() || op_type == MOpTag::Virt;
}
bool is_imm() const {
return op_type == MOpTag::Imm;
}
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 ~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);

View File

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

View File

@ -101,7 +101,7 @@ private:
std::shared_ptr<BasicBlock> current_bb = {};
std::vector<int> *arr_dim_list = nullptr;
int arr_dim_index = 0;
int local_const_arr_cnt = 0;
int local_const_arr_cnt = 0;
struct loop_record {
BasicBlockPtr_t cond, body, next;
int id;

View File

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

View File

@ -15,7 +15,7 @@
#include <memory>
#include <string>
#define ELPP_NO_LOG_TO_FILE
#define ELPP_NO_LOG_TO_FILE
INITIALIZE_EASYLOGGINGPP
using namespace CompSysY;
@ -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;
MCModule mc_module;
mc_module.file_name = source_file;
mc_module.IR2MC(visitor.module);
std::vector<sptr(MCPass)> mc_passes = {std::make_shared<PassRegAlloc>()};
for (auto pass : mc_passes) {
pass->run(mc_module);
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;
}

View File

@ -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) {
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;
}
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::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

View File

@ -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 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 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_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
@ -93,23 +113,29 @@ 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 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);
}
for (auto func : ir_module.function_list) {
if (func->is_libfunc()) continue;
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->itr = mc_func->bb_list.insert(mc_func->bb_list.end(), mc_bb);
auto mc_bb = make_shared<MBasicBlock>();
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;
bb_ir2mc.insert({bb, mc_bb});
@ -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);
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, 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 = 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);
mc_li->addr = addr;
mc_li->data = data;
// mc_li->offset = nullptr;
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 = 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;
}
}
}
@ -463,8 +511,9 @@ void MCModule::IR2MC(const Module &ir_module) {
// Use some more redundency to save the complicated critical edge split
for (auto ir_bb : func->bb_list) {
auto mc_bb = bb_ir2mc.at(ir_bb);
std::list<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::list<pmv> par_mv_cur; // parallel move in current 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
View 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

View File

@ -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);
}
}

View File

@ -150,8 +150,9 @@ 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 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 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);