Commit f40efe88 authored by Yang's avatar Yang

publish lab4

parent 93702b62
......@@ -63,4 +63,4 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_BINARY_DIR})
add_subdirectory(src)
# add_subdirectory(tests)
add_subdirectory(tests)
......@@ -67,15 +67,10 @@ class CminusfBuilder : public ASTVisitor {
auto *output_float_fun =
Function::create(output_float_type, "outputFloat", module);
auto *neg_idx_except_type = FunctionType::get(TyVoid, {});
auto *neg_idx_except_fun = Function::create(
neg_idx_except_type, "neg_idx_except", module);
scope.enter();
scope.push("input", input_fun);
scope.push("output", output_fun);
scope.push("outputFloat", output_float_fun);
scope.push("neg_idx_except", neg_idx_except_fun);
}
Module* getModule() const { return module; }
......
......@@ -64,18 +64,6 @@ class CodeGen {
void gen_fptosi();
void gen_epilogue();
static std::string label_name(BasicBlock *bb) {
return "." + bb->get_parent()->get_name() + "_" + bb->get_name();
}
static std::string func_exit_label_name(Function *func) {
return func->get_name() + "_exit";
}
static std::string fcmp_label_name(BasicBlock *bb, unsigned cnt) {
return label_name(bb) + "_fcmp_" + std::to_string(cnt);
}
struct {
/* 随着ir遍历设置 */
Function *func{nullptr}; // 当前函数
......@@ -84,14 +72,12 @@ class CodeGen {
/* 在allocate()中设置 */
unsigned frame_size{0}; // 当前函数的栈帧大小
std::unordered_map<Value *, int> offset_map{}; // 指针相对 fp 的偏移
unsigned fcmp_cnt{0}; // fcmp 的计数器, 用于创建 fcmp 需要的 label
void clear() {
func = nullptr;
bb = nullptr;
inst = nullptr;
frame_size = 0;
fcmp_cnt = 0;
offset_map.clear();
}
......
......@@ -20,8 +20,7 @@ class BasicBlock : public Value {
~BasicBlock() override;
static BasicBlock *create(Module *m, const std::string &name,
Function *parent) {
auto prefix = name.empty() ? "" : "label_";
return new BasicBlock(m, prefix + name, parent);
return new BasicBlock(m, name, parent);
}
/****************api about cfg****************/
......
......@@ -214,6 +214,8 @@ public:
Value *get_condition() const { return get_operand(0); }
void replace_all_bb_match(BasicBlock* need_replace, BasicBlock* replace_to);
std::string print() override;
};
......@@ -251,8 +253,8 @@ class StoreInst : public Instruction {
public:
static StoreInst *create_store(Value *val, Value *ptr, BasicBlock *bb);
Value *get_rval() const { return this->get_operand(0); }
Value *get_lval() const { return this->get_operand(1); }
Value *get_val() const { return this->get_operand(0); }
Value *get_ptr() const { return this->get_operand(1); }
std::string print() override;
};
......@@ -264,7 +266,7 @@ class LoadInst : public Instruction {
public:
static LoadInst *create_load(Value *ptr, BasicBlock *bb, const std::string& name = "");
Value *get_lval() const { return this->get_operand(0); }
Value *get_ptr() const { return this->get_operand(0); }
Type *get_load_type() const { return get_type(); }
std::string print() override;
......
......@@ -20,11 +20,11 @@ class Dominators : public FunctionAnalysisPass {
void run() override;
// 获取基本块的直接支配节点
BasicBlock *get_idom(const BasicBlock *bb) const { return idom_.at(bb); }
const std::set<BasicBlock*> &get_dominance_frontier(const BasicBlock *bb) {
BasicBlock *get_idom(BasicBlock *bb) const { return idom_.at(bb); }
const std::set<BasicBlock*> &get_dominance_frontier(BasicBlock *bb) {
return dom_frontier_.at(bb);
}
const std::set<BasicBlock*> &get_dom_tree_succ_blocks(const BasicBlock *bb) {
const std::set<BasicBlock*> &get_dom_tree_succ_blocks(BasicBlock *bb) {
return dom_tree_succ_blocks_.at(bb);
}
......@@ -33,7 +33,7 @@ class Dominators : public FunctionAnalysisPass {
void dump_dominator_tree();
// functions for dominance tree
bool is_dominate(const BasicBlock *bb1, const BasicBlock *bb2) const {
bool is_dominate(BasicBlock *bb1, BasicBlock *bb2) const {
return dom_tree_L_.at(bb1) <= dom_tree_L_.at(bb2) &&
dom_tree_R_.at(bb1) >= dom_tree_L_.at(bb2);
}
......@@ -54,33 +54,33 @@ class Dominators : public FunctionAnalysisPass {
void create_dom_tree_succ();
void create_dom_dfs_order();
BasicBlock * intersect(BasicBlock *b1, const BasicBlock *b2) const;
BasicBlock * intersect(BasicBlock *b1, BasicBlock *b2) const;
void create_reverse_post_order();
void set_idom(const BasicBlock *bb, BasicBlock *idom) { idom_[bb] = idom; }
void set_dominance_frontier(const BasicBlock *bb, std::set<BasicBlock*>&df) {
void set_idom(BasicBlock *bb, BasicBlock *idom) { idom_[bb] = idom; }
void set_dominance_frontier(BasicBlock *bb, std::set<BasicBlock*>&df) {
dom_frontier_[bb].clear();
dom_frontier_[bb].insert(df.begin(), df.end());
}
void add_dom_tree_succ_block(const BasicBlock *bb, BasicBlock *dom_tree_succ_bb) {
void add_dom_tree_succ_block(BasicBlock *bb, BasicBlock *dom_tree_succ_bb) {
dom_tree_succ_blocks_[bb].insert(dom_tree_succ_bb);
}
unsigned int get_post_order(const BasicBlock *bb) const {
return post_order_.at(bb);
unsigned int get_reversed_post_order(BasicBlock *bb) const {
return reversed_post_order_.at(bb);
}
// for debug
void print_idom() const;
void print_dominance_frontier();
std::vector<BasicBlock *> post_order_vec_{}; // 逆后序
std::map<const BasicBlock *, unsigned int> post_order_{}; // 逆后序
std::map<const BasicBlock *, BasicBlock *> idom_{}; // 直接支配
std::map<const BasicBlock *, std::set<BasicBlock*>> dom_frontier_{}; // 支配边界集合
std::map<const BasicBlock *, std::set<BasicBlock*>> dom_tree_succ_blocks_{}; // 支配树中的后继节点
std::vector<BasicBlock *> reversed_post_order_vec_{}; // 逆后序
std::map<BasicBlock *, unsigned int> reversed_post_order_{}; // 逆后序索引
std::map<BasicBlock *, BasicBlock *> idom_{}; // 直接支配
std::map<BasicBlock *, std::set<BasicBlock*>> dom_frontier_{}; // 支配边界集合
std::map<BasicBlock *, std::set<BasicBlock*>> dom_tree_succ_blocks_{}; // 支配树中的后继节点
// 支配树上的dfs序L,R
std::map<const BasicBlock *, unsigned int> dom_tree_L_;
std::map<const BasicBlock *, unsigned int> dom_tree_R_;
std::map<BasicBlock *, unsigned int> dom_tree_L_;
std::map<BasicBlock *, unsigned int> dom_tree_R_;
std::vector<BasicBlock *> dom_dfs_order_;
std::vector<BasicBlock *> dom_post_order_;
......
......@@ -27,13 +27,17 @@ class FuncInfo : public ModuleAnalysisPass {
void run() override;
// 函数是否是纯函数
bool is_pure(Function *func) { return !func->is_declaration() && !use_libs.count(func) && loads[func].empty() && stores[func].empty(); }
bool is_pure(Function *func) { return !func->is_declaration() && !use_libs[func] && loads[func].empty() && stores[func].empty(); }
// 函数是否使用了 io
bool use_io(Function* func) { return func->is_declaration() || use_libs[func]; }
// 返回 StoreInst 存入的变量(全局/局部变量或函数参数)
static Value* store_ptr(const StoreInst* st);
// 返回 LoadInst 加载的变量(全局/局部变量或函数参数)
static Value* load_ptr(const LoadInst* ld);
// 返回 CallInst 代表的函数调用间接存入的变量(全局/局部变量或函数参数)
std::unordered_set<Value*> get_stores(const CallInst* call);
// 返回 CallInst 代表的函数调用间接加载的变量(全局/局部变量或函数参数)
std::unordered_set<Value*> get_loads(const CallInst* call);
private:
// 函数存储的值
std::unordered_map<Function*, UseMessage> stores;
......
......@@ -18,8 +18,4 @@ class LoopInvariantCodeMotion : public TransformPass {
std::vector<Instruction*> collect_insts(Loop* loop);
void traverse_loop(Loop* loop);
void run_on_loop(Loop* loop);
void collect_loop_info(Loop* loop,
std::set<Value *> &loop_instructions,
std::set<Value *> &updated_global,
bool &contains_impure_call);
};
\ No newline at end of file
......@@ -40,6 +40,7 @@ class Loop {
const std::vector<Loop*>& get_sub_loops() { return sub_loops_; }
const std::unordered_set<BasicBlock *>& get_latches() { return latches_; }
void add_latch(BasicBlock *bb) { latches_.insert(bb); }
std::string safe_print() const;
};
class LoopDetection : public FunctionAnalysisPass {
......
......@@ -25,7 +25,8 @@ def __lldb_init_module(debugger: lldb.SBDebugger, internal_dict):
, "BasicBlock"
, "GlobalVariable"
, "Instruction", "IBinaryInst", "FBinaryInst", "ICmpInst", "FCmpInst", "CallInst", "BranchInst", "ReturnInst", "GetElementPtrInst", "StoreInst", "LoadInst", "AllocaInst", "ZextInst", "FpToSiInst", "SiToFpInst", "PhiInst"
, "ASMInstruction", "Reg", "FReg", "CFReg" ]
, "ASMInstruction", "Reg", "FReg", "CFReg"
, "Loop" ]
for i in types:
debugger.HandleCommand(
f"type summary add -F lldb_formatters.SafePrintSummary {i} -w my"
......
......@@ -329,27 +329,10 @@ Value* CminusfBuilder::visit(ASTVar &node) {
}
} else {
auto *val = node.expression->accept(*this);
auto *exceptBB = BasicBlock::create(module, "", context.func);
auto *contBB = BasicBlock::create(module, "", context.func);
if (val->get_type()->is_float_type()) {
val = builder->create_fptosi(val, INT32_T);
}
Value* is_neg = builder->create_icmp_lt(val, CONST_INT(0));
builder->create_cond_br(is_neg, exceptBB, contBB);
builder->set_insert_point(exceptBB);
auto *neg_idx_except_fun = scope.find("neg_idx_except");
builder->create_call(dynamic_cast<Function *>(neg_idx_except_fun), {});
if (context.func->get_return_type()->is_void_type()) {
builder->create_void_ret();
} else if (context.func->get_return_type()->is_float_type()) {
builder->create_ret(CONST_FP(0.));
} else {
builder->create_ret(CONST_INT(0));
}
builder->set_insert_point(contBB);
Value *tmp_ptr;
if (is_int || is_float) {
tmp_ptr = builder->create_gep(var, {val});
......
......@@ -23,30 +23,30 @@ struct Config {
std::filesystem::path input_file;
std::filesystem::path output_file;
bool emitast{false};
bool emitasm{false};
bool emitllvm{false};
bool emitast{ false };
bool emitasm{ false };
bool emitllvm{ false };
// optization conifg
bool mem2reg{false};
bool licm{false};
bool mem2reg{ false };
bool licm{ false };
Config(int argc, char **argv) : argc(argc), argv(argv) {
Config(int argc, char** argv) : argc(argc), argv(argv) {
parse_cmd_line();
check();
}
private:
int argc{-1};
char **argv{nullptr};
private:
int argc{ -1 };
char** argv{ nullptr };
void parse_cmd_line();
void check();
// print helper infomation and exit
void print_help() const;
void print_err(const string &msg) const;
void print_err(const string& msg) const;
};
int main(int argc, char **argv) {
int main(int argc, char** argv) {
Config config(argc, argv);
auto syntax_tree = parse(config.input_file.c_str());
......@@ -55,7 +55,8 @@ int main(int argc, char **argv) {
if (config.emitast) { // if emit ast (lab1), print ast and return
ASTPrinter printer;
ast.run_visitor(printer);
} else {
}
else {
Module* m;
CminusfBuilder builder;
ast.run_visitor(builder);
......@@ -63,11 +64,12 @@ int main(int argc, char **argv) {
PassManager PM(m);
// optimization
if(config.mem2reg) {
if (config.mem2reg) {
PM.add_pass<DeadCode>(true);
PM.add_pass<Mem2Reg>();
PM.add_pass<DeadCode>(false);
}
if(config.licm) {
if (config.licm) {
PM.add_pass<LoopInvariantCodeMotion>();
PM.add_pass<DeadCode>(false);
}
......@@ -79,7 +81,14 @@ int main(int argc, char **argv) {
output_stream << "; ModuleID = 'cminus'\n";
output_stream << "source_filename = " << abs_path << "\n\n";
output_stream << m->print();
} else if (config.emitasm) {
}
else if (config.emitasm) {
auto abs_path = std::filesystem::canonical(config.input_file);
config.output_file.replace_extension("ll");
std::ofstream output_stream2(config.output_file);
output_stream2 << "; ModuleID = 'cminus'\n";
output_stream2 << "source_filename = " << abs_path << "\n\n";
output_stream2 << m->print();
CodeGen codegen(m);
codegen.run();
output_stream << codegen.print();
......@@ -96,27 +105,36 @@ void Config::parse_cmd_line() {
for (int i = 1; i < argc; ++i) {
if (argv[i] == "-h"s || argv[i] == "--help"s) {
print_help();
} else if (argv[i] == "-o"s) {
}
else if (argv[i] == "-o"s) {
if (output_file.empty() && i + 1 < argc) {
output_file = argv[i + 1];
i += 1;
} else {
}
else {
print_err("bad output file");
}
} else if (argv[i] == "-emit-ast"s) {
}
else if (argv[i] == "-emit-ast"s) {
emitast = true;
} else if (argv[i] == "-S"s) {
}
else if (argv[i] == "-S"s) {
emitasm = true;
} else if (argv[i] == "-emit-llvm"s) {
}
else if (argv[i] == "-emit-llvm"s) {
emitllvm = true;
} else if (argv[i] == "-mem2reg"s) {
}
else if (argv[i] == "-mem2reg"s) {
mem2reg = true;
} else if (argv[i] == "-licm"s) {
}
else if (argv[i] == "-licm"s) {
licm = true;
}else {
}
else {
if (input_file.empty()) {
input_file = argv[i];
} else {
}
else {
string err =
"unrecognized command-line option \'"s + argv[i] + "\'"s;
print_err(err);
......@@ -143,11 +161,12 @@ void Config::check() {
}
if (output_file.empty()) {
output_file = input_file.stem();
}
if (emitllvm) {
output_file.replace_extension(".ll");
} else if (emitasm) {
output_file.replace_extension(".s");
}
else if (emitasm) {
output_file.replace_extension(".s");
}
}
......@@ -160,7 +179,7 @@ void Config::print_help() const {
exit(0);
}
void Config::print_err(const string &msg) const {
void Config::print_err(const string& msg) const {
std::cout << exe_name << ": " << msg << std::endl;
exit(-1);
}
#include "CodeGen.hpp"
#include <cstring>
#include "ASMInstruction.hpp"
#include "BasicBlock.hpp"
#include "CodeGenUtil.hpp"
#include "Function.hpp"
#include "Instruction.hpp"
#include "Register.hpp"
#include "Type.hpp"
#include <string>
std::string ASMInstruction::safe_print() const
void CodeGen::allocate()
{
switch (type) {
case Instruction:
case Attribute:
return content;
case Label:
return content + ":";
case Comment:
return "# " + content;
}
return "<error>";
}
void CodeGen::allocate() {
// 备份 $ra $fp
unsigned offset = PROLOGUE_OFFSET_BASE;
// 为每个参数分配栈空间
for (auto &arg : context.func->get_args()) {
for (auto& arg : context.func->get_args())
{
auto size = arg->get_type()->get_size();
offset = offset + size;
offset = ALIGN(offset + size, size);
context.offset_map[arg] = -static_cast<int>(offset);
}
// 为指令结果分配栈空间
for (auto bb : context.func->get_basic_blocks()) {
for (auto instr : bb->get_instructions()) {
// 每个非 void 的定值都分配栈空间
if (not instr->is_void()) {
for (auto& bb : context.func->get_basic_blocks())
{
for (auto& instr : bb->get_instructions())
{
if (not instr->is_void())
{
auto size = instr->get_type()->get_size();
offset = offset + size;
offset = ALIGN(offset + size, size > 8 ? 8 : size);
context.offset_map[instr] = -static_cast<int>(offset);
}
// alloca 的副作用:分配额外空间
if (instr->is_alloca()) {
auto *alloca_inst = dynamic_cast<AllocaInst *>(instr);
if (instr->is_alloca())
{
auto* alloca_inst = dynamic_cast<AllocaInst*>(instr);
auto alloc_size = alloca_inst->get_alloca_type()->get_size();
offset += alloc_size;
offset = ALIGN(offset + alloc_size, alloc_size > 8 ? 8 : alloc_size);
}
}
}
// 分配栈空间,需要是 16 的整数倍
context.frame_size = ALIGN(offset, PROLOGUE_ALIGN);
}
void CodeGen::copy_stmt() {
for (auto succ : context.bb->get_succ_basic_blocks()) {
for (auto inst : succ->get_instructions()) {
if (inst->is_phi()) {
// 遍历后继块中 phi 的定值 bb
for (unsigned i = 1; i < inst->get_operands().size(); i += 2) {
// phi 的定值 bb 是当前翻译块
if (inst->get_operand(i) == context.bb) {
auto *lvalue = inst->get_operand(i - 1);
if (lvalue->get_type()->is_float_type()) {
void CodeGen::copy_stmt()
{
for (auto& succ : context.bb->get_succ_basic_blocks())
{
for (auto& inst : succ->get_instructions())
{
if (inst->is_phi())
{
for (unsigned i = 1; i < inst->get_operands().size(); i += 2)
{
if (inst->get_operand(i) == context.bb)
{
auto* lvalue = inst->get_operand(i - 1);
if (lvalue->get_type()->is_float_type())
{
load_to_freg(lvalue, FReg::fa(0));
store_from_freg(inst, FReg::fa(0));
} else {
}
else
{
load_to_greg(lvalue, Reg::a(0));
store_from_greg(inst, Reg::a(0));
}
break;
}
// 如果没有找到当前翻译块,说明是 undef,无事可做
}
} else {
}
else
{
break;
}
}
}
}
void CodeGen::load_to_greg(Value *val, const Reg &reg) {
void CodeGen::load_to_greg(Value* val, const Reg& reg)
{
assert(val->get_type()->is_integer_type() ||
val->get_type()->is_pointer_type());
if (auto *constant = dynamic_cast<ConstantInt *>(val)) {
int32_t val = constant->get_value();
if (IS_IMM_12(val)) {
append_inst(ADDI WORD, {reg.print(), "$zero", std::to_string(val)});
} else {
load_large_int32(val, reg);
if (auto* constant = dynamic_cast<ConstantInt*>(val))
{
int32_t val1 = constant->get_value();
if (IS_IMM_12(val1))
{
append_inst(ADDI WORD, {reg.print(), "$zero", std::to_string(val1)});
}
} else if (auto *global = dynamic_cast<GlobalVariable *>(val)) {
else
{
load_large_int32(val1, reg);
}
}
else if (auto* global = dynamic_cast<GlobalVariable*>(val))
{
append_inst(LOAD_ADDR, {reg.print(), global->get_name()});
} else {
}
else
{
load_from_stack_to_greg(val, reg);
}
}
void CodeGen::load_large_int32(int32_t val, const Reg &reg) {
int32_t high_20 = val >> 12; // si20
void CodeGen::load_large_int32(int32_t val, const Reg& reg)
{
int32_t high_20 = val >> 12;
uint32_t low_12 = val & LOW_12_MASK;
append_inst(LU12I_W, {reg.print(), std::to_string(high_20)});
append_inst(ORI, {reg.print(), reg.print(), std::to_string(low_12)});
}
void CodeGen::load_large_int64(int64_t val, const Reg &reg) {
void CodeGen::load_large_int64(int64_t val, const Reg& reg)
{
auto low_32 = static_cast<int32_t>(val & LOW_32_MASK);
load_large_int32(low_32, reg);
auto high_32 = static_cast<int32_t>(val >> 32);
int32_t high_32_low_20 = (high_32 << 12) >> 12; // si20
int32_t high_32_high_12 = high_32 >> 20; // si12
int32_t high_32_low_20 = (high_32 << 12) >> 12;
int32_t high_32_high_12 = high_32 >> 20;
append_inst(LU32I_D, {reg.print(), std::to_string(high_32_low_20)});
append_inst(LU52I_D,
{reg.print(), reg.print(), std::to_string(high_32_high_12)});
}
void CodeGen::load_from_stack_to_greg(Value *val, const Reg &reg) {
void CodeGen::load_from_stack_to_greg(Value* val, const Reg& reg)
{
auto offset = context.offset_map.at(val);
auto offset_str = std::to_string(offset);
auto *type = val->get_type();
if (IS_IMM_12(offset)) {
if (type->is_int1_type()) {
auto* type = val->get_type();
if (IS_IMM_12(offset))
{
if (type->is_int1_type())
{
append_inst(LOAD BYTE, {reg.print(), "$fp", offset_str});
} else if (type->is_int32_type()) {
}
else if (type->is_int32_type())
{
append_inst(LOAD WORD, {reg.print(), "$fp", offset_str});
} else { // Pointer
}
else
{
append_inst(LOAD DOUBLE, {reg.print(), "$fp", offset_str});
}
} else {
}
else
{
load_large_int64(offset, reg);
append_inst(ADD DOUBLE, {reg.print(), "$fp", reg.print()});
if (type->is_int1_type()) {
if (type->is_int1_type())
{
append_inst(LOAD BYTE, {reg.print(), reg.print(), "0"});
} else if (type->is_int32_type()) {
}
else if (type->is_int32_type())
{
append_inst(LOAD WORD, {reg.print(), reg.print(), "0"});
} else { // Pointer
}
else
{
append_inst(LOAD DOUBLE, {reg.print(), reg.print(), "0"});
}
}
}
void CodeGen::store_from_greg(Value *val, const Reg &reg) {
void CodeGen::store_from_greg(Value* val, const Reg& reg)
{
auto offset = context.offset_map.at(val);
auto offset_str = std::to_string(offset);
auto *type = val->get_type();
if (IS_IMM_12(offset)) {
if (type->is_int1_type()) {
auto* type = val->get_type();
if (IS_IMM_12(offset))
{
if (type->is_int1_type())
{
append_inst(STORE BYTE, {reg.print(), "$fp", offset_str});
} else if (type->is_int32_type()) {
}
else if (type->is_int32_type())
{
append_inst(STORE WORD, {reg.print(), "$fp", offset_str});
} else { // Pointer
}
else
{
append_inst(STORE DOUBLE, {reg.print(), "$fp", offset_str});
}
} else {
}
else
{
auto addr = Reg::t(8);
load_large_int64(offset, addr);
append_inst(ADD DOUBLE, {addr.print(), "$fp", addr.print()});
if (type->is_int1_type()) {
if (type->is_int1_type())
{
append_inst(STORE BYTE, {reg.print(), addr.print(), "0"});
} else if (type->is_int32_type()) {
}
else if (type->is_int32_type())
{
append_inst(STORE WORD, {reg.print(), addr.print(), "0"});
} else { // Pointer
}
else
{
append_inst(STORE DOUBLE, {reg.print(), addr.print(), "0"});
}
}
}
void CodeGen::load_to_freg(Value *val, const FReg &freg) {
void CodeGen::load_to_freg(Value* val, const FReg& freg)
{
assert(val->get_type()->is_float_type());
if (auto *constant = dynamic_cast<ConstantFP *>(val)) {
float val = constant->get_value();
load_float_imm(val, freg);
} else {
if (auto* constant = dynamic_cast<ConstantFP*>(val))
{
float val1 = constant->get_value();
load_float_imm(val1, freg);
}
else
{
auto offset = context.offset_map.at(val);
auto offset_str = std::to_string(offset);
if (IS_IMM_12(offset)) {
if (IS_IMM_12(offset))
{
append_inst(FLOAD SINGLE, {freg.print(), "$fp", offset_str});
} else {
}
else
{
auto addr = Reg::t(8);
load_large_int64(offset, addr);
append_inst(ADD DOUBLE, {addr.print(), "$fp", addr.print()});
......@@ -185,18 +229,24 @@ void CodeGen::load_to_freg(Value *val, const FReg &freg) {
}
}
void CodeGen::load_float_imm(float val, const FReg &r) {
int32_t bytes = *reinterpret_cast<int32_t *>(&val);
void CodeGen::load_float_imm(float val, const FReg& r)
{
int32_t bytes = 0;
memcpy(&bytes, &val, sizeof(float));
load_large_int32(bytes, Reg::t(8));
append_inst(GR2FR WORD, {r.print(), Reg::t(8).print()});
}
void CodeGen::store_from_freg(Value *val, const FReg &r) {
void CodeGen::store_from_freg(Value* val, const FReg& r)
{
auto offset = context.offset_map.at(val);
if (IS_IMM_12(offset)) {
if (IS_IMM_12(offset))
{
auto offset_str = std::to_string(offset);
append_inst(FSTORE SINGLE, {r.print(), "$fp", offset_str});
} else {
}
else
{
auto addr = Reg::t(8);
load_large_int64(offset, addr);
append_inst(ADD DOUBLE, {addr.print(), "$fp", addr.print()});
......@@ -204,14 +254,18 @@ void CodeGen::store_from_freg(Value *val, const FReg &r) {
}
}
void CodeGen::gen_prologue() {
if (IS_IMM_12(-static_cast<int>(context.frame_size))) {
void CodeGen::gen_prologue()
{
if (IS_IMM_12(-static_cast<int>(context.frame_size)))
{
append_inst("st.d $ra, $sp, -8");
append_inst("st.d $fp, $sp, -16");
append_inst("addi.d $fp, $sp, 0");
append_inst("addi.d $sp, $sp, " +
std::to_string(-static_cast<int>(context.frame_size)));
} else {
}
else
{
load_large_int64(context.frame_size, Reg::t(0));
append_inst("st.d $ra, $sp, -8");
append_inst("st.d $fp, $sp, -16");
......@@ -221,177 +275,405 @@ void CodeGen::gen_prologue() {
int garg_cnt = 0;
int farg_cnt = 0;
for (auto arg : context.func->get_args()) {
if (arg->get_type()->is_float_type()) {
for (auto arg : context.func->get_args())
{
if (arg->get_type()->is_float_type())
{
store_from_freg(arg, FReg::fa(farg_cnt++));
} else { // int or pointer
}
else
{
store_from_greg(arg, Reg::a(garg_cnt++));
}
}
}
void CodeGen::gen_epilogue() {
// TODO 根据你的理解设定函数的 epilogue
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_epilogue()
{
append_inst(context.func->get_name() + "_exit", ASMInstruction::Label);
if (IS_IMM_12(-static_cast<int>(context.frame_size)))
{
append_inst("addi.d $sp, $sp, " + std::to_string(static_cast<int>(context.frame_size)));
append_inst("ld.d $ra, $sp, -8");
append_inst("ld.d $fp, $sp, -16");
append_inst("jr $ra");
}
else
{
load_large_int64(context.frame_size, Reg::t(0));
append_inst("add.d $sp, $sp, $t0");
append_inst("ld.d $ra, $sp, -8");
append_inst("ld.d $fp, $sp, -16");
append_inst("jr $ra");
}
}
void CodeGen::gen_ret() {
// TODO 函数返回,思考如何处理返回值、寄存器备份,如何返回调用者地址
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_ret()
{
auto* retInst = dynamic_cast<ReturnInst*>(context.inst);
auto* retType = context.func->get_return_type();
if (retType->is_void_type())
{
append_inst("addi.w $a0, $zero, 0");
}
else if (retType->is_float_type())
{
load_to_freg(retInst->get_operand(0), FReg::fa(0));
}
else
{
load_to_greg(retInst->get_operand(0), Reg::a(0));
}
std::string label = context.func->get_name() + "_exit";
append_inst("b " + label);
}
void CodeGen::gen_br() {
auto *branchInst = static_cast<BranchInst *>(context.inst);
if (branchInst->is_cond_br()) {
// TODO 补全条件跳转的情况
throw not_implemented_error{__FUNCTION__};
} else {
auto *branchbb = static_cast<BasicBlock *>(branchInst->get_operand(0));
append_inst("b " + label_name(branchbb));
void CodeGen::gen_br()
{
auto* branchInst = dynamic_cast<BranchInst*>(context.inst);
if (branchInst->is_cond_br())
{
load_to_greg(branchInst->get_operand(0), Reg::t(0));
auto* trueBB = dynamic_cast<BasicBlock*>(branchInst->get_operand(1));
auto* falseBB = dynamic_cast<BasicBlock*>(branchInst->get_operand(2));
append_inst("bnez", {Reg::t(0).print(), trueBB->get_name()});
append_inst("b", {falseBB->get_name()});
}
else
{
auto* branchbb = dynamic_cast<BasicBlock*>(branchInst->get_operand(0));
append_inst("b " + branchbb->get_name());
}
}
void CodeGen::gen_binary() {
void CodeGen::gen_binary()
{
load_to_greg(context.inst->get_operand(0), Reg::t(0));
load_to_greg(context.inst->get_operand(1), Reg::t(1));
switch (context.inst->get_instr_type()) {
switch (context.inst->get_instr_type())
{
case Instruction::add:
append_inst("add.w $t2, $t0, $t1");
output.emplace_back("add.w $t2, $t0, $t1");
break;
case Instruction::sub:
append_inst("sub.w $t2, $t0, $t1");
output.emplace_back("sub.w $t2, $t0, $t1");
break;
case Instruction::mul:
append_inst("mul.w $t2, $t0, $t1");
output.emplace_back("mul.w $t2, $t0, $t1");
break;
case Instruction::sdiv:
append_inst("div.w $t2, $t0, $t1");
output.emplace_back("div.w $t2, $t0, $t1");
break;
default:
assert(false);
}
store_from_greg(context.inst, Reg::t(2));
if (context.inst->get_instr_type() == Instruction::mul)
{
load_to_greg(ConstantInt::get(1, m), Reg::a(0));
load_to_greg(ConstantInt::get(1, m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
if (context.inst->get_instr_type() == Instruction::sdiv)
{
load_to_greg(ConstantInt::get(1, m), Reg::a(0));
load_to_greg(ConstantInt::get(4, m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
}
void CodeGen::gen_float_binary() {
// TODO 浮点类型的二元指令
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_float_binary()
{
auto* floatInst = dynamic_cast<FBinaryInst*>(context.inst);
auto op = floatInst->get_instr_type();
auto firstNum = floatInst->get_operand(0);
load_to_freg(firstNum, FReg::ft(1));
auto secondNum = floatInst->get_operand(1);
load_to_freg(secondNum, FReg::ft(2));
switch (op)
{
case Instruction::fadd:
append_inst("fadd.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()});
break;
case Instruction::fsub:
append_inst("fsub.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()});
break;
case Instruction::fmul:
append_inst("fmul.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()});
break;
case Instruction::fdiv:
append_inst("fdiv.s", {FReg::ft(0).print(), FReg::ft(1).print(), FReg::ft(2).print()});
break;
default:
std::cout << "wrong gen_float_binary\n";
break;
}
store_from_freg(context.inst, FReg::ft(0));
if (context.inst->get_instr_type() == Instruction::fmul)
{
load_to_greg(ConstantInt::get(1, m), Reg::a(0));
load_to_greg(ConstantInt::get(1, m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
if (context.inst->get_instr_type() == Instruction::fdiv)
{
load_to_greg(ConstantInt::get(1, m), Reg::a(0));
load_to_greg(ConstantInt::get(4, m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
}
void CodeGen::gen_alloca() {
/* 我们已经为 alloca 的内容分配空间,在此我们还需保存 alloca
* 指令自身产生的定值,即指向 alloca 空间起始地址的指针
*/
// TODO 将 alloca 出空间的起始地址保存在栈帧上
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_alloca()
{
auto* allocaInst = dynamic_cast<AllocaInst*>(context.inst);
auto offset = context.offset_map[allocaInst];
auto trueOffset = offset - static_cast<int>(allocaInst->get_alloca_type()->get_size());
if (IS_IMM_12(trueOffset))
append_inst("addi.d", {Reg::t(0).print(), "$fp", std::to_string(trueOffset)});
else
{
load_to_greg(ConstantInt::get(trueOffset, m), Reg::t(1));
append_inst("add.d", {Reg::t(0).print(), "$fp", Reg::t(1).print()});
}
store_from_greg(allocaInst, Reg::t(0));
load_to_greg(ConstantInt::get(0, m), Reg::a(0));
load_to_greg(ConstantInt::get(static_cast<int>(allocaInst->get_alloca_type()->get_size()), m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
void CodeGen::gen_load() {
auto *ptr = context.inst->get_operand(0);
auto *type = context.inst->get_type();
void CodeGen::gen_load()
{
auto* ptr = context.inst->get_operand(0);
auto* type = context.inst->get_type();
load_to_greg(ptr, Reg::t(0));
if (type->is_float_type()) {
if (type->is_float_type())
{
append_inst("fld.s $ft0, $t0, 0");
store_from_freg(context.inst, FReg::ft(0));
} else {
// TODO load 整数类型的数据
throw not_implemented_error{__FUNCTION__};
}
else if (type->is_int32_type())
{
append_inst("ld.w $t0, $t0, 0");
store_from_greg(context.inst, Reg::t(0));
}
else if (type->is_int1_type())
{
append_inst("ld.b $t0, $t0, 0");
store_from_greg(context.inst, Reg::t(0));
}
else
{
append_inst("ld.d $t0, $t0, 0");
store_from_greg(context.inst, Reg::t(0));
}
if (ptr->is<AllocaInst>() && !((ptr->as<AllocaInst>())->get_alloca_type()->is_array_type())) return;
load_to_greg(ConstantInt::get(1, m), Reg::a(0));
load_to_greg(ConstantInt::get(3, m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
void CodeGen::gen_store() {
// TODO 翻译 store 指令
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_store()
{
auto* storeInst = dynamic_cast<StoreInst*>(context.inst);
auto addr = storeInst->get_operand(1);
auto value = storeInst->get_operand(0);
load_to_greg(addr, Reg::t(0));
if (value->get_type()->is_float_type())
{
load_to_freg(value, FReg::ft(0));
append_inst("fst.s $ft0, $t0, 0");
}
else if (value->get_type()->is_int32_type())
{
load_to_greg(value, Reg::t(1));
append_inst("st.w $t1, $t0, 0");
}
else if (value->get_type()->is_int1_type())
{
load_to_greg(value, Reg::t(1));
append_inst("st.b $t1, $t0, 0");
}
else
{
load_to_greg(value, Reg::t(1));
append_inst("st.d $t1, $t0, 0");
}
}
void CodeGen::gen_icmp() {
// TODO 处理各种整数比较的情况
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_icmp()
{
auto* icmpInst = dynamic_cast<ICmpInst*>(context.inst);
auto op = icmpInst->get_instr_type();
load_to_greg(icmpInst->get_operand(0), Reg::t(0));
load_to_greg(icmpInst->get_operand(1), Reg::t(1));
switch (op)
{
case Instruction::ge:
append_inst("slt $t0, $t0, $t1");
append_inst("addi.d $t1, $zero, 1");
append_inst("xor $t0, $t0, $t1");
break;
case Instruction::gt:
append_inst("slt $t0, $t1, $t0");
break;
case Instruction::le:
append_inst("slt $t0, $t1, $t0");
append_inst("addi.d $t1, $zero, 1");
append_inst("xor $t0, $t0, $t1");
break;
case Instruction::lt:
append_inst("slt $t0, $t0, $t1");
break;
case Instruction::eq:
append_inst("xor $t0, $t0, $t1");
append_inst("sltu $t0, $zero, $t0");
append_inst("addi.d $t1, $zero, 1");
append_inst("xor $t0, $t0, $t1");
break;
case Instruction::ne:
append_inst("xor $t0, $t0, $t1");
append_inst("sltu $t0, $zero, $t0");
break;
default:
std::cout << "wrong icmp\n";
break;
}
store_from_greg(icmpInst, Reg::t(0));
}
void CodeGen::gen_fcmp() {
// TODO 处理各种浮点数比较的情况
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_fcmp()
{
auto* fcmpInst = dynamic_cast<FCmpInst*>(context.inst);
auto op = fcmpInst->get_instr_type();
load_to_freg(fcmpInst->get_operand(0), FReg::ft(0));
load_to_freg(fcmpInst->get_operand(1), FReg::ft(1));
switch (op)
{
case Instruction::fge:
append_inst("fcmp.sle.s $fcc0, $ft1, $ft0");
break;
case Instruction::fgt:
append_inst("fcmp.slt.s $fcc0, $ft1, $ft0");
break;
case Instruction::fle:
append_inst("fcmp.sle.s $fcc0, $ft0, $ft1");
break;
case Instruction::flt:
append_inst("fcmp.slt.s $fcc0, $ft0, $ft1");
break;
case Instruction::feq:
append_inst("fcmp.seq.s $fcc0, $ft0, $ft1");
break;
case Instruction::fne:
append_inst("fcmp.sne.s $fcc0, $ft0, $ft1");
break;
default:
break;
}
append_inst("bceqz", {"$fcc0", "0XC"});
append_inst("addi.w $t0, $zero, 1");
append_inst("b 0x8");
append_inst("addi.w $t0, $zero, 0");
store_from_greg(context.inst, Reg::t(0));
}
void CodeGen::gen_zext() {
// TODO 将窄位宽的整数数据进行零扩展
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_zext()
{
auto* zextInst = dynamic_cast<ZextInst*>(context.inst);
load_to_greg(zextInst->get_operand(0), Reg::t(0));
append_inst("bstrpick.w $t0, $t0, 7, 0");
store_from_greg(context.inst, Reg::t(0));
}
void CodeGen::gen_call() {
// TODO 函数调用,注意我们只需要通过寄存器传递参数,即不需考虑栈上传参的情况
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_call()
{
auto* callInst = dynamic_cast<CallInst*>(context.inst);
auto* functionType = static_cast<FunctionType*>(callInst->get_function_type());
auto argsNum = functionType->get_num_of_args();
unsigned int j = 0;
unsigned int k = 0;
for (unsigned int i = 0; i < argsNum; i++)
{
if (functionType->get_param_type(i)->is_float_type())
{
load_to_freg(callInst->get_operand(i + 1), FReg::fa(k));
k++;
}
else
{
load_to_greg(callInst->get_operand(i + 1), Reg::a(j));
j++;
}
}
auto* func = dynamic_cast<Function*>(callInst->get_operand(0));
append_inst("bl", {func->get_name()});
auto retType = functionType->get_return_type();
if (retType->is_integer_type())
{
store_from_greg(context.inst, Reg::a(0));
}
else if (retType->is_float_type())
{
store_from_freg(context.inst, FReg::fa(0));
}
}
/*
* %op = getelementptr [10 x i32], [10 x i32]* %op, i32 0, i32 %op
* %op = getelementptr i32, i32* %op, i32 %op
*
* Memory layout
* - ^
* +-----------+ | Smaller address
* | arg ptr |---+ |
* +-----------+ | |
* | | | |
* +-----------+ / |
* | |<-- |
* | | \ |
* | | | |
* | Array | | |
* | | | |
* | | | |
* | | | |
* +-----------+ | |
* | Pointer |---+ |
* +-----------+ |
* | | |
* +-----------+ |
* | | |
* +-----------+ |
* | | |
* +-----------+ | Larger address
* +
*/
void CodeGen::gen_gep() {
// TODO 计算内存地址
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_gep()
{
auto* getElementPtrInst = dynamic_cast<GetElementPtrInst*>(context.inst);
unsigned int num = getElementPtrInst->get_num_operand();
load_to_greg(getElementPtrInst->get_operand(0), Reg::t(0));
load_to_greg(getElementPtrInst->get_operand(num - 1), Reg::t(1));
auto elementType = getElementPtrInst->get_element_type();
append_inst("addi.d $t2, $zero, 4");
if (elementType->is_float_type() || elementType->is_int32_type())
{
append_inst("mul.d $t1, $t1, $t2");
}
else
{
append_inst("addi.d $t2, $zero, 8");
append_inst("mul.d $t1, $t1, $t2");
}
append_inst("add.d $t2, $t1, $t0");
store_from_greg(context.inst, Reg::t(2));
load_to_greg(ConstantInt::get(1, m), Reg::a(0));
load_to_greg(ConstantInt::get(1, m), Reg::a(1));
append_inst("bl add_lab4_flag");
}
void CodeGen::gen_sitofp() {
// TODO 整数转向浮点数
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_sitofp()
{
auto* sitofpInst = dynamic_cast<SiToFpInst*>(context.inst);
load_to_greg(sitofpInst->get_operand(0), Reg::t(0));
append_inst("movgr2fr.w $ft0, $t0");
append_inst("ffint.s.w $ft1, $ft0");
store_from_freg(context.inst, FReg::ft(1));
}
void CodeGen::gen_fptosi() {
// TODO 浮点数转向整数,注意向下取整(round to zero)
throw not_implemented_error{__FUNCTION__};
void CodeGen::gen_fptosi()
{
auto* fptosiInst = dynamic_cast<FpToSiInst*>(context.inst);
load_to_freg(fptosiInst->get_operand(0), FReg::ft(0));
append_inst("ftintrz.w.s $ft1, $ft0");
append_inst("movfr2gr.s $t0, $ft1");
store_from_greg(context.inst, Reg::t(0));
}
void CodeGen::run() {
// 确保每个函数中基本块的名字都被设置好
void CodeGen::run()
{
m->set_print_name();
/* 使用 GNU 伪指令为全局变量分配空间
* 你可以使用 `la.local` 指令将标签 (全局变量) 的地址载入寄存器中, 比如
* 要将 `a` 的地址载入 $t0, 只需要 `la.local $t0, a`
*/
if (!m->get_global_variable().empty()) {
if (!m->get_global_variable().empty())
{
append_inst("Global variables", ASMInstruction::Comment);
/* 虽然下面两条伪指令可以简化为一条 `.bss` 伪指令, 但是我们还是选择使用
* `.section` 将全局变量放到可执行文件的 BSS 段, 原因如下:
* - 尽可能对齐交叉编译器 loongarch64-unknown-linux-gnu-gcc 的行为
* - 支持更旧版本的 GNU 汇编器, 因为 `.bss` 伪指令是应该相对较新的指令,
* GNU 汇编器在 2023 年 2 月的 2.37 版本才将其引入
*/
append_inst(".text", ASMInstruction::Attribute);
append_inst(".section", {".bss", "\"aw\"", "@nobits"},
ASMInstruction::Attribute);
for (auto global : m->get_global_variable()) {
auto size =
global->get_type()->get_pointer_element_type()->get_size();
for (auto global : m->get_global_variable())
{
auto size = global->get_type()->get_pointer_element_type()->get_size();
append_inst(".globl", {global->get_name()},
ASMInstruction::Attribute);
append_inst(".type", {global->get_name(), "@object"},
......@@ -404,33 +686,32 @@ void CodeGen::run() {
}
}
// 函数代码段
output.emplace_back(".text", ASMInstruction::Attribute);
for (auto func : m->get_functions()) {
if (not func->is_declaration()) {
// 更新 context
for (auto func : m->get_functions())
{
if (not func->is_declaration())
{
context.clear();
context.func = func;
// 函数信息
append_inst(".globl", {func->get_name()}, ASMInstruction::Attribute);
append_inst(".type", {func->get_name(), "@function"},
ASMInstruction::Attribute);
append_inst(func->get_name(), ASMInstruction::Label);
// 分配函数栈帧
allocate();
// 生成 prologue
gen_prologue();
for (auto bb : func->get_basic_blocks()) {
for (auto& bb : func->get_basic_blocks())
{
context.bb = bb;
append_inst(label_name(context.bb), ASMInstruction::Label);
for (auto instr : bb->get_instructions()) {
// For debug
append_inst(context.bb->get_name(), ASMInstruction::Label);
for (auto& instr : bb->get_instructions())
{
append_inst(instr->print(), ASMInstruction::Comment);
context.inst = instr; // 更新 context
switch (instr->get_instr_type()) {
context.inst = instr;
switch (instr->get_instr_type())
{
case Instruction::ret:
gen_ret();
break;
......@@ -451,10 +732,6 @@ void CodeGen::run() {
gen_float_binary();
break;
case Instruction::alloca:
/* 对于 alloca 指令,我们已经为 alloca
* 的内容分配空间,在此我们还需保存 alloca
* 指令自身产生的定值,即指向 alloca 空间起始地址的指针
*/
gen_alloca();
break;
case Instruction::load:
......@@ -480,10 +757,6 @@ void CodeGen::run() {
gen_fcmp();
break;
case Instruction::phi:
/* for phi, just convert to a series of
* copy-stmts */
/* we can collect all phi and deal them at
* the end */
break;
case Instruction::call:
gen_call();
......@@ -503,15 +776,16 @@ void CodeGen::run() {
}
}
}
// 生成 epilogue
gen_epilogue();
}
}
}
std::string CodeGen::print() const {
std::string CodeGen::print() const
{
std::string result;
for (const auto& inst : output) {
for (const auto& inst : output)
{
result += inst.format();
}
return result;
......
#include <stdio.h>
#include <stdlib.h>
int input() {
#include <sys/time.h>
struct timeval time_start, time_end;
int end_set;
int flags[2];
int input(void)
{
int a;
scanf("%d", &a);
gettimeofday(&time_start, NULL);
end_set = 0;
return a;
}
void output(int a) { printf("%d\n", a); }
void output(int a)
{
if (end_set == 0)
{
gettimeofday(&time_end, NULL);
end_set = 1;
}
printf("%d\n", a);
}
void outputFloat(float a)
{
if (end_set == 0)
{
gettimeofday(&time_end, NULL);
end_set = 1;
}
printf("%f\n", a);
}
void add_lab4_flag(int idx, int val)
{
flags[idx] += val;
}
void outputFloat(float a) { printf("%f\n", a); }
__attribute((constructor)) void before_main(void)
{
flags[0] = 0;
flags[1] = 0;
end_set = 0;
gettimeofday(&time_start, NULL);
}
void neg_idx_except() {
printf("negative index exception\n");
exit(0);
__attribute((destructor)) void after_main(void)
{
long time_us = 0;
fprintf(stderr, "Allocate Size (bytes):\n%d\n", flags[0]);
fprintf(stderr, "Execute Cost:\n%d\n", flags[1]);
if (end_set == 0)
{
gettimeofday(&time_end, NULL);
}
time_us += 1000000L * (time_end.tv_sec - time_start.tv_sec) +
time_end.tv_usec - time_start.tv_usec;
fprintf(stderr, "Take Times (us):\n%ld\n", time_us);
}
int input();
int input(void);
void output(int a);
void outputFloat(float a);
void neg_idx_except();
void add_lab4_flag(int idx, int val);
\ No newline at end of file
......@@ -12,13 +12,13 @@
BasicBlock::BasicBlock(const Module* m, const std::string& name = "",
Function* parent = nullptr)
: Value(m->get_label_type(),
parent == nullptr ? GLOBAL_BASICBLOCK_NAMES_.get_name(name) : parent->names4blocks_.get_name(name))
, parent_(parent) {
parent == nullptr ? GLOBAL_BASICBLOCK_NAMES_.get_name(name) : parent->names4blocks_.get_name(name))
, parent_(parent) {
assert(parent && "currently parent should not be nullptr");
parent_->add_basic_block(this);
}
Module *BasicBlock::get_module() const { return get_parent()->get_parent(); }
Module* BasicBlock::get_module() const { return get_parent()->get_parent(); }
void BasicBlock::erase_from_parent() { this->get_parent()->remove(this); }
bool BasicBlock::is_terminated() const {
......@@ -34,18 +34,18 @@ bool BasicBlock::is_terminated() const {
}
}
Instruction *BasicBlock::get_terminator() const
Instruction* BasicBlock::get_terminator() const
{
assert(is_terminated() &&
"Trying to get terminator from an bb which is not terminated");
return instr_list_.back();
}
void BasicBlock::add_instruction(Instruction *instr) {
void BasicBlock::add_instruction(Instruction* instr) {
if (instr->is_alloca() || instr->is_phi())
{
auto it = instr_list_.begin();
for (; it != instr_list_.end() && ((*it)->is_alloca() || (*it)->is_phi()); ++it){}
for (; it != instr_list_.end() && ((*it)->is_alloca() || (*it)->is_phi()); ++it) {}
instr_list_.emplace(it, instr);
return;
}
......@@ -146,4 +146,4 @@ BasicBlock* BasicBlock::get_entry_block_of_same_function() const
return parent_->get_entry_block();
}
Names GLOBAL_BASICBLOCK_NAMES_{"label", "_"};
\ No newline at end of file
Names GLOBAL_BASICBLOCK_NAMES_{ "label", "_" };
\ No newline at end of file
......@@ -7,17 +7,8 @@
#include <unordered_set>
#include <queue>
namespace
{
std::string chopName(std::string name)
{
if (name.size() > 3) return { name.begin(), name.begin() + 3 };
return name;
}
}
Function::Function(FunctionType* ty, const std::string& name, Module* parent)
: Value(ty, name), names4blocks_("label", chopName(name) + "_"), names4insts_("op", ""), parent_(parent), seq_cnt_(0) {
: Value(ty, name), names4blocks_("", name + "_"), names4insts_("op", ""), parent_(parent), seq_cnt_(0) {
// num_args_ = ty->getNumParams();
parent->add_function(this);
// build args
......
......@@ -382,7 +382,7 @@ std::string Instruction::safe_print() const
}
else
{
if (auto fty = dynamic_cast<FunctionType*>(ty))
if (auto fty = dynamic_cast<FunctionType*>(op0->get_type()))
{
auto ty3 = fty->get_return_type();
if (ty3 == nullptr)
......
......@@ -197,6 +197,23 @@ BranchInst *BranchInst::create_br(BasicBlock *if_true, BasicBlock *bb) {
return new BranchInst(nullptr, if_true, nullptr, bb);
}
void BranchInst::replace_all_bb_match(BasicBlock* need_replace, BasicBlock* replace_to)
{
if (need_replace == nullptr || replace_to == nullptr || need_replace == replace_to) return;
int size = static_cast<int>(get_operands().size());
for (int i = 0; i < size; i ++)
{
auto op = get_operand(i);
if (op == need_replace) set_operand(i, replace_to);
}
auto parent = get_parent();
if (parent == nullptr) return;
parent->remove_succ_basic_block(need_replace);
need_replace->remove_pre_basic_block(parent);
parent->add_succ_basic_block(replace_to);
replace_to->add_pre_basic_block(parent);
}
ReturnInst::ReturnInst(Value *val, BasicBlock *bb)
: Instruction(bb->get_module()->get_void_type(), ret, "", bb) {
if (val == nullptr) {
......@@ -352,14 +369,13 @@ SiToFpInst *SiToFpInst::create_sitofp(Value *val, BasicBlock *bb, const std::str
PhiInst::PhiInst(Type *ty, const std::vector<Value *>& vals,
const std::vector<BasicBlock *>& val_bbs, BasicBlock *bb, const std::string& name)
: Instruction(ty, phi, name) {
: Instruction(ty, phi, name, bb) {
assert(vals.size() == val_bbs.size() && "Unmatched vals and bbs");
for (unsigned i = 0; i < vals.size(); i++) {
assert(ty == vals[i]->get_type() && "Bad type for phi");
add_operand(vals[i]);
add_operand(val_bbs[i]);
}
this->set_parent(bb);
}
PhiInst *PhiInst::create_phi(Type *ty, BasicBlock *bb,
......
......@@ -27,48 +27,48 @@ Module::~Module()
for (auto i : global_list_) delete i;
}
Type *Module::get_void_type() const { return void_ty_; }
Type *Module::get_label_type() const { return label_ty_; }
IntegerType *Module::get_int1_type() const { return int1_ty_; }
IntegerType *Module::get_int32_type() const { return int32_ty_; }
FloatType *Module::get_float_type() const { return float32_ty_; }
PointerType *Module::get_int32_ptr_type() {
Type* Module::get_void_type() const { return void_ty_; }
Type* Module::get_label_type() const { return label_ty_; }
IntegerType* Module::get_int1_type() const { return int1_ty_; }
IntegerType* Module::get_int32_type() const { return int32_ty_; }
FloatType* Module::get_float_type() const { return float32_ty_; }
PointerType* Module::get_int32_ptr_type() {
return get_pointer_type(int32_ty_);
}
PointerType *Module::get_float_ptr_type() {
PointerType* Module::get_float_ptr_type() {
return get_pointer_type(float32_ty_);
}
PointerType *Module::get_pointer_type(Type *contained) {
PointerType* Module::get_pointer_type(Type* contained) {
if (pointer_map_.find(contained) == pointer_map_.end()) {
pointer_map_[contained] = new PointerType(contained);
}
return pointer_map_[contained];
}
ArrayType *Module::get_array_type(Type *contained, unsigned num_elements) {
if (array_map_.find({contained, num_elements}) == array_map_.end()) {
ArrayType* Module::get_array_type(Type* contained, unsigned num_elements) {
if (array_map_.find({ contained, num_elements }) == array_map_.end()) {
array_map_[{contained, num_elements}] =
new ArrayType(contained, num_elements);
}
return array_map_[{contained, num_elements}];
}
FunctionType *Module::get_function_type(Type *retty,
std::vector<Type *> &args) {
if (not function_map_.count({retty, args})) {
FunctionType* Module::get_function_type(Type* retty,
std::vector<Type*>& args) {
if (not function_map_.count({ retty, args })) {
function_map_[{retty, args}] =
new FunctionType(retty, args);
}
return function_map_[{retty, args}];
}
void Module::add_function(Function *f) { function_list_.push_back(f); }
std::list<Function*> &Module::get_functions() { return function_list_; }
void Module::add_global_variable(GlobalVariable *g) {
void Module::add_function(Function* f) { function_list_.push_back(f); }
std::list<Function*>& Module::get_functions() { return function_list_; }
void Module::add_global_variable(GlobalVariable* g) {
global_list_.push_back(g);
}
std::list<GlobalVariable*> &Module::get_global_variable() {
std::list<GlobalVariable*>& Module::get_global_variable() {
return global_list_;
}
......
......@@ -15,6 +15,7 @@ void DeadCode::run() {
do {
changed = false;
for (auto func : m_->get_functions()) {
if (func->is_declaration()) continue;
if (remove_bb_) changed |= clear_basic_blocks(func);
mark(func);
changed |= sweep(func);
......
......@@ -62,13 +62,13 @@ void Dominators::run() {
* 该函数使用后序号来查找两个节点的最近公共支配者。
* 通过在支配树上向上遍历直到找到交点。
*/
BasicBlock *Dominators::intersect(BasicBlock *b1, const BasicBlock *b2) const
BasicBlock *Dominators::intersect(BasicBlock *b1, BasicBlock *b2) const
{
while (b1 != b2) {
while (get_post_order(b1) < get_post_order(b2)) {
while (get_reversed_post_order(b1) > get_reversed_post_order(b2)) {
b1 = get_idom(b1);
}
while (get_post_order(b2) < get_post_order(b1)) {
while (get_reversed_post_order(b2) > get_reversed_post_order(b1)) {
b2 = get_idom(b2);
}
}
......@@ -84,6 +84,12 @@ BasicBlock *Dominators::intersect(BasicBlock *b1, const BasicBlock *b2) const
void Dominators::create_reverse_post_order() {
std::set<BasicBlock*> visited;
dfs(f_->get_entry_block(), visited);
int size = static_cast<int>(reversed_post_order_vec_.size()) - 1;
for (auto& it : reversed_post_order_)
{
it.second = size - it.second;
reversed_post_order_vec_[it.second] = it.first;
}
}
/**
......@@ -100,8 +106,8 @@ void Dominators::dfs(BasicBlock *bb, std::set<BasicBlock *> &visited) {
dfs(succ, visited);
}
}
post_order_vec_.push_back(bb);
post_order_.insert({bb, post_order_.size()});
reversed_post_order_vec_.push_back(bb);
reversed_post_order_.insert({bb, reversed_post_order_.size()});
}
/**
......@@ -111,38 +117,28 @@ void Dominators::dfs(BasicBlock *bb, std::set<BasicBlock *> &visited) {
*/
void Dominators::create_idom() {
// 可能有用的数据结构
// post_order_vec_ vector<BasicBlock*>, 其中基本块按照逆后续排列
// post_order_ map<BasicBlock*, unsigned int>, 每个基本块在 post_order_vec_ 中的索引
// reversed_post_order_vec_ vector<BasicBlock*>, 其中基本块按照逆后续排列
// reversed_post_order_ map<BasicBlock*, unsigned int>, 每个基本块在 reversed_post_order_vec_ 中的索引
// 可能有用的函数
// intersect(BasicBlock *, BasicBlock *) 寻找两个基本块在支配树上的最近祖先
// 需要填写
// idom_ map<BasicBlock *, BasicBlock *> 直接支配者, idom_[a] = b 代表 b 直接支配 a (或者 idom_[a] = a 代表 a 没有直接支配者)
// 代表基本块的直接支配者是否已经确定, 不会在迭代中改变 (默认初始化为 false)
bool* already_determined = new bool[post_order_vec_.size()]{};
int bb_count = static_cast<int>(reversed_post_order_vec_.size());
int bb_count = static_cast<int>(post_order_vec_.size());
for (int i = 0; i < bb_count; i++)
{
auto bb = post_order_vec_[i];
// TODO 填写可以直接确定直接支配者, 无需参与迭代的基本块的 idom_ 和 already_determined
throw "Unimplemented create_idom";
}
idom_[reversed_post_order_vec_[0]] = reversed_post_order_vec_[0];
bool changed;
do
{
changed = false;
for (int i = 0; i < bb_count; i++)
for (int i = 1; i < bb_count; i++)
{
if (already_determined[i]) continue;
auto bb = post_order_vec_[i];
auto bb = reversed_post_order_vec_[i];
// TODO 更新基本块 bb 的 idom_, 设置 changed(如果 idom_ 有变化)
throw "Unimplemented create_idom";
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
} while (changed);
delete[] already_determined;
}
/**
......@@ -154,19 +150,19 @@ void Dominators::create_idom() {
*/
void Dominators::create_dominance_frontier() {
// 可能有用的数据结构
// post_order_vec_ vector<BasicBlock*>, 其中基本块按照逆后续排列
// post_order_ map<BasicBlock*, unsigned int>, 每个基本块在 post_order_vec_ 中的索引
// reversed_post_order_vec_ vector<BasicBlock*>, 其中基本块按照逆后续排列
// reversed_post_order_ map<BasicBlock*, unsigned int>, 每个基本块在 reversed_post_order_vec_ 中的索引
// idom_ map<BasicBlock *, BasicBlock *> 直接支配者, idom_[a] = b 代表 b 直接支配 a, 支配树中 b 是 a 的 parent
// (或者 idom_[a] = a 代表 a 没有直接支配者)
// 需要填写
// dom_frontier_ map<BasicBlock*, set<BasicBlock*>> 支配边界
int bb_count = static_cast<int>(post_order_vec_.size());
int bb_count = static_cast<int>(reversed_post_order_vec_.size());
for (int i = 0; i < bb_count; i++)
{
auto bb = post_order_vec_[i];
auto bb = reversed_post_order_vec_[i];
// TODO 计算 bb 的支配边界集合, 填入 dom_frontier_
throw "Unimplemented create_dominance_frontier";
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
}
......@@ -183,7 +179,8 @@ void Dominators::create_dom_tree_succ() {
// dom_tree_succ_blocks_ map<BasicBlock*, set<BasicBlock*>> 支配树中后继(孩子)节点
// TODO 分析得到 f_ 中各个基本块的支配树后继
throw "Unimplemented create_dom_tree_succ";
// 注意如果 idom_[n] = n, 这意味着 n 没有直接支配者,因此 n 的后继中没有 n
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
/**
......
......@@ -174,6 +174,21 @@ std::unordered_set<Value*> FuncInfo::get_stores(const CallInst* call)
return ret;
}
std::unordered_set<Value*> FuncInfo::get_loads(const CallInst* call)
{
auto func = call->get_operand(0)->as<Function>();
if (func->is_declaration()) return {};
std::unordered_set<Value*> ret;
for (auto i : loads[func].globals_) ret.emplace(i);
for (auto arg : loads[func].arguments_)
{
int arg_no = static_cast<int>(arg->get_arg_no());
auto in = call->get_operand(arg_no + 1);
ret.emplace(trace_ptr(in));
}
return ret;
}
void FuncInfo::log() const
{
for (auto it : use_libs)
......
......@@ -67,21 +67,6 @@ std::vector<Instruction*> LoopInvariantCodeMotion::collect_insts(Loop* loop)
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
// TODO: 实现collect_loop_info函数
// 1. 遍历当前循环及其子循环的所有指令
// 2. 收集所有指令到loop_instructions中
// 3. 检查store指令是否修改了全局变量,如果是则添加到updated_global中
// 4. 检查是否包含非纯函数调用,如果有则设置contains_impure_call为true
void LoopInvariantCodeMotion::collect_loop_info(
Loop* loop,
std::set<Value*>& loop_instructions,
std::set<Value*>& updated_global,
bool& contains_impure_call)
{
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
enum InstructionType: std::uint8_t
{
UNKNOWN, VARIANT, INVARIANT
......@@ -99,19 +84,9 @@ void LoopInvariantCodeMotion::run_on_loop(Loop* loop)
// 循环中的所有指令
std::vector<Instruction*> instructions = collect_insts(loop);
int insts_count = static_cast<int>(instructions.size());
// 循环的所有基本块
std::unordered_set<BasicBlock*> bbs;
for (auto i : loop->get_blocks()) bbs.emplace(i);
// val 是否在循环内定义,可以当成函数进行调用
auto is_val_in_loop = [&bbs](Value* val)->bool
{
auto inst = dynamic_cast<Instruction*>(val);
if (inst == nullptr) return true;
return bbs.count(inst->get_parent());
};
// inst_type[i] 代表 instructions[i] 是循环变量(每次循环都会变)/ 循环不变量 还是 不知道
std::vector<InstructionType> inst_type;
inst_type.resize(insts_count);
// Value* 在 map 内说明它是循环内的指令,InstructionType 指示它是循环变量(每次循环都会变)/ 循环不变量 还是 不知道
std::unordered_map<Value*, InstructionType> inst_type;
for (auto i : instructions) inst_type[i] = UNKNOWN;
// 遍历后是不是还有指令不知道 InstructionType
bool have_inst_can_not_decide;
......@@ -123,10 +98,10 @@ void LoopInvariantCodeMotion::run_on_loop(Loop* loop)
for (int i = 0; i < insts_count; i++)
{
Instruction* inst = instructions[i];
InstructionType type = inst_type[i];
InstructionType type = inst_type[inst];
if (type != UNKNOWN) continue;
// 可能有用的函数
// FuncInfo::load_ptr
// FuncInfo::load_ptr, FuncInfo::get_stores, FuncInfo::use_io
// TODO: 识别循环不变式指令
// - 将 store、ret、br、phi 等指令与非纯函数调用标记为 VARIANT
......@@ -134,6 +109,10 @@ void LoopInvariantCodeMotion::run_on_loop(Loop* loop)
// - 如果指令有 VARIANT 操作数,标记为 VARIANT
// - 如果指令所有操作数都是 INVARIANT (或者不在循环内),标记为 INVARIANT, 设置 have_invariant
// - 否则设置 have_inst_can_not_decide
// TODO: 外提循环不变的非纯函数调用
// 注意: 你不应该外提使用了 io 的函数调用
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
}
......@@ -152,20 +131,26 @@ void LoopInvariantCodeMotion::run_on_loop(Loop* loop)
for (auto phi : loop->get_header()->get_instructions())
{
if (phi->get_instr_type() != Instruction::phi) break;
// 可能有用的函数
// PhiInst::create_phi
// TODO: 分裂 phi 指令
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
}
// 可能有用的函数
// BranchInst::replace_all_bb_match
// TODO: 维护 bb, header, 与 header 前驱块的基本块关系
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
bb->add_instruction(BranchInst::create_br(header, bb));
BranchInst::create_br(header, bb);
// 若你想维护 LoopDetection 在 LICM 后保持正确
// auto loop2 = loop->get_parent();
// while (loop2 != nullptr)
// {
// loop2->get_parent()->add_block(bb);
// loop2->add_block(bb);
// loop2 = loop2->get_parent();
// }
}
......@@ -177,8 +162,12 @@ void LoopInvariantCodeMotion::run_on_loop(Loop* loop)
auto terminator = preheader->get_instructions().back();
preheader->get_instructions().pop_back();
// 可以使用 Function::check_for_block_relation_error 检查基本块间的关系是否正确维护
// TODO: 外提循环不变指令
throw std::runtime_error("Lab4: 你有一个TODO需要完成!");
preheader->add_instruction(terminator);
std::cerr << "licm done\n";
}
......@@ -54,6 +54,21 @@ void LoopDetection::run() {
delete dominators_;
}
std::string Loop::safe_print() const
{
std::string ret;
if (header_ == nullptr) ret += "b<null>";
else ret += header_->get_name();
ret += " ";
ret += std::to_string(blocks_.size());
ret += "b ";
ret += std::to_string(latches_.size());
ret += "latch ";
ret += std::to_string(sub_loops_.size());
ret += "sub";
return ret;
}
/**
* @brief 发现循环及其子循环
* @param bb 循环的header块
......@@ -69,10 +84,12 @@ void LoopDetection::discover_loop_and_sub_loops(BasicBlock *bb, std::set<BasicBl
// 5. 建立正确的循环嵌套关系
std::vector<BasicBlock*> work_list = {latches.begin(), latches.end()}; // 初始化工作表
std::unordered_set<BasicBlock*> already_in_work_list = { latches.begin(), latches.end() }; // 已经在工作表,防止重复加入
while (!work_list.empty()) { // 当工作表非空时继续处理
auto bb2 = work_list.back();
work_list.pop_back();
already_in_work_list.erase(bb2);
// TODO-1: 处理未分配给任何循环的节点
if (bb_to_loop_.find(bb2) == bb_to_loop_.end()) {
......
......@@ -4,11 +4,12 @@
#include "Value.hpp"
// l_val 是否是非数组 alloca 变量
static bool is_not_array_alloca(Value* l_val)
// ptr 是否是非数组 alloca 变量(是则转换为 AllocaInst)
static AllocaInst* is_not_array_alloca(Value* ptr)
{
auto alloca = dynamic_cast<AllocaInst*>(l_val);
return alloca != nullptr && !alloca->get_alloca_type()->is_array_type();
auto alloca = dynamic_cast<AllocaInst*>(ptr);
if (alloca != nullptr && !alloca->get_alloca_type()->is_array_type()) return alloca;
return nullptr;
}
/**
......@@ -43,7 +44,7 @@ void Mem2Reg::run() {
generate_phi();
// 确保每个局部变量的栈都有初始值
for (auto var : allocas_)
var_val_stack[var].emplace_back(ConstantZero::get(var->get_alloca_type(), m_));
var_val_stack[var].emplace_back(var->get_alloca_type()->is_float_type() ? static_cast<Value*>(ConstantFP::get(0, m_)) : static_cast<Value*>(ConstantInt::get(0, m_)));
// 对应伪代码中重命名阶段
rename(func_->get_entry_block());
}
......@@ -80,11 +81,13 @@ void Mem2Reg::generate_phi() {
if (instr->is_store()) {
// store i32 a, i32 *b
// a is r_val, b is l_val
auto l_val = dynamic_cast<StoreInst *>(instr)->get_lval();
if (is_not_array_alloca(l_val)) {
auto lalloca = dynamic_cast<AllocaInst*>(instr);
auto l_val = dynamic_cast<StoreInst*>(instr)->get_ptr();
if (auto lalloca = is_not_array_alloca(l_val)) {
if (!not_array_allocas.count(lalloca))
{
not_array_allocas.insert(lalloca);
allocas_.emplace_back(lalloca);
}
allocas_stored_bbs[lalloca].emplace_back(bb);
}
}
......@@ -115,7 +118,6 @@ void Mem2Reg::generate_phi() {
bb_dominance_frontier_bb);
phi_to_alloca_.emplace(phi, var);
bb_to_phi_[bb_dominance_frontier_bb].emplace_back(phi);
bb_dominance_frontier_bb->add_instr_begin(phi);
work_list.push_back(bb_dominance_frontier_bb);
bb_has_var_phi.emplace(bb_dominance_frontier_bb, var);
}
......@@ -128,20 +130,23 @@ void Mem2Reg::rename(BasicBlock *bb) {
// 可能用到的数据结构
// list<AllocaInst*> allocas_ 所有 Mem2Reg 需要消除的局部变量,用于遍历
// map<AllocaInst*,vector<Value *>> var_val_stack 每个局部变量的存储值栈,还未进行任何操作时已经存进去了 0,不会为空
// map<PhiInst *, AllocaInst*>; Phi 对应的局部变量
// map<BasicBlock*, list<PhiInst*>>; 在某个基本块的 Phi
// map<PhiInst *, AllocaInst*> phi_to_alloca_; Phi 对应的局部变量
// map<BasicBlock*, list<PhiInst*>> bb_to_phi_; 在某个基本块的 Phi
// 可能用到的函数
// Value::replace_all_use_with(Value* a) 将所有用到 this 的指令对应 this 的操作数都替换为 a
// BasicBlock::erase_instrs(set<Instruction*>) 移除并 delete 列表中的指令
// StoreInst / LoadInst get_ptr 这些 Inst 所操作的变量
// is_not_array_alloca(Value* ptr) 一个变量是不是 Mem2Reg 所关心的非数组局部变量
// TODO
// 步骤一:对每个 alloca 非数组变量(局部变量), 在其存储值栈存入其当前的最新值(也就是目前的栈顶值)
// 步骤二:遍历基本块所有指令,执行操作并记录需要删除的 load/store/alloca 指令
// 步骤二:遍历基本块所有指令,执行操作并记录需要删除的 load/store/alloca 指令(注意: 并非所有 load/store/alloca 都是 Mem2Reg 需要处理的)
// - 步骤三: 将 store 指令存储的值,作为其对应局部变量的最新值(更新栈顶)
// - 步骤四: 将 load 指令的所有使用替换为其读取的局部变量的最新值
// 步骤五:为所有后继块的 phi 添加参数
// 步骤六:对 bb 在支配树上的所有后继节点,递归执行 rename 操作
// 步骤七:pop 出所有局部变量的最新值
// 步骤八:删除需要删除的冗余指令
// - 步骤四: 将 phi 指令的所有使用替换为其对应的局部变量的最新值
// - 步骤五: 将 load 指令的所有使用替换为其读取的局部变量的最新值
// 步骤六:为所有后继块的 phi 添加参数
// 步骤七:对 bb 在支配树上的所有后继节点,递归执行 rename 操作
// 步骤八:pop 出所有局部变量的最新值
// 步骤九:删除需要删除的冗余指令
}
add_executable(
eval_lab4
eval_lab4.cpp
)
install(
TARGETS eval_lab4
)
\ No newline at end of file
// ReSharper disable CppClangTidyPerformanceInefficientStringConcatenation
#include <climits>
#include <complex>
#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <list>
#include <optional>
#include <regex>
#include <string>
#include <unistd.h>
#include <sys/select.h>
#include <sys/wait.h>
using namespace std;
enum evaluate_result : uint8_t { SUCCESS, FAIL, PASS };
static enum stage : uint8_t
{
raw, mem2reg, licm, all
} STAGE;
static string TEST_PATH;
static enum test_type : uint8_t
{
debug, test
} TYPE;
struct cmd_result
{
string out_str;
string err_str;
int ret_val;
bool have_err_message() const
{
return !err_str.empty();
}
};
struct cmd_result_mix
{
string str;
int ret_val;
};
static string red(const string& str) { return "\033[31;1m" + str + "\033[0m"; }
static string green(const string& str) { return "\033[32;1m" + str + "\033[0m"; }
static ofstream ost;
static void ext(int flag)
{
ost.close();
exit(flag);
}
static void out(const string& str, bool to_std)
{
if (to_std) cout << str;
else ost << str;
}
static void out2(const string& str)
{
cout << str;
ost << str;
}
static void out2e(const string& str)
{
cout << red(str);
ost << str;
}
static std::pair<std::string, std::string> readPipeLine(int outpipe[], int errpipe[], int limit)
{
fd_set readfds;
int maxFd = std::max(outpipe[0], errpipe[0]);
char buffer[1024];
std::string stdout_result;
std::string stderr_result;
int totalRead = 0;
bool have_stdout = true;
bool have_stderr = true;
while (true)
{
FD_ZERO(&readfds);
if (have_stdout)
FD_SET(outpipe[0], &readfds);
if (have_stderr)
FD_SET(errpipe[0], &readfds);
int ret = select(maxFd + 1, &readfds, nullptr, nullptr, nullptr);
if (ret == -1)
{
out("select() 调用失败!\n", true);
out("select() 调用失败!\n", false);
ext(-1);
}
if (ret == 0)
continue;
if (FD_ISSET(outpipe[0], &readfds))
{
int bytes = static_cast<int>(read(outpipe[0], buffer, sizeof(buffer) - 1));
if (bytes > 0)
{
int writeLen = std::min(bytes, limit - totalRead);
buffer[writeLen] = '\0';
stdout_result += buffer;
totalRead += writeLen;
if (totalRead >= limit)
break;
}
else
{
FD_CLR(outpipe[0], &readfds);
close(outpipe[0]);
have_stdout = false;
maxFd = have_stderr ? errpipe[0] : -1;
}
}
if (FD_ISSET(errpipe[0], &readfds))
{
int bytes = static_cast<int>(read(errpipe[0], buffer, sizeof(buffer) - 1));
if (bytes > 0)
{
int writeLen = std::min(bytes, limit - totalRead);
buffer[writeLen] = '\0';
stderr_result += buffer;
totalRead += writeLen;
if (totalRead >= limit)
break;
}
else
{
FD_CLR(errpipe[0], &readfds);
close(errpipe[0]);
have_stderr = false;
maxFd = have_stdout ? outpipe[0] : -1;
}
}
if (!FD_ISSET(outpipe[0], &readfds) && !FD_ISSET(errpipe[0], &readfds))
break;
}
if (!stdout_result.empty() && stdout_result.back() != '\n') stdout_result += '\n';
if (!stderr_result.empty() && stderr_result.back() != '\n') stderr_result += '\n';
return {stdout_result, stderr_result};
}
static std::string readPipeLineMix(int outpipe[], int errpipe[], int limit)
{
fd_set readfds;
int maxFd = std::max(outpipe[0], errpipe[0]);
char buffer[1024];
std::string result;
int totalRead = 0;
bool have_stdout = true;
bool have_stderr = true;
while (true)
{
FD_ZERO(&readfds);
if (have_stdout)
FD_SET(outpipe[0], &readfds);
if (have_stderr)
FD_SET(errpipe[0], &readfds);
int ret = select(maxFd + 1, &readfds, nullptr, nullptr, nullptr);
if (ret == -1)
{
out("select() 调用失败!\n", true);
out("select() 调用失败!\n", false);
ext(-1);
}
if (ret == 0)
continue;
if (FD_ISSET(outpipe[0], &readfds))
{
int bytes = static_cast<int>(read(outpipe[0], buffer, sizeof(buffer) - 1));
if (bytes > 0)
{
int writeLen = std::min(bytes, limit - totalRead);
buffer[writeLen] = '\0';
result += buffer;
totalRead += writeLen;
if (totalRead >= limit)
break;
}
else
{
FD_CLR(outpipe[0], &readfds);
close(outpipe[0]);
have_stdout = false;
maxFd = have_stderr ? errpipe[0] : -1;
}
}
if (FD_ISSET(errpipe[0], &readfds))
{
int bytes = static_cast<int>(read(errpipe[0], buffer, sizeof(buffer) - 1));
if (bytes > 0)
{
int writeLen = std::min(bytes, limit - totalRead);
buffer[writeLen] = '\0';
result += buffer;
totalRead += writeLen;
if (totalRead >= limit)
break;
}
else
{
FD_CLR(errpipe[0], &readfds);
close(errpipe[0]);
have_stderr = false;
maxFd = have_stdout ? outpipe[0] : -1;
}
}
if (!FD_ISSET(outpipe[0], &readfds) && !FD_ISSET(errpipe[0], &readfds))
break;
}
if (!result.empty() && result.back() != '\n') result += '\n';
return result;
}
static cmd_result runCommand(const std::string& command, int limit = INT_MAX)
{
int outpipe[2];
int errpipe[2];
if (pipe(outpipe) != 0 || pipe(errpipe) != 0)
{
out2("pipe() 调用失败!\n");
ext(-1);
}
pid_t pid = fork();
if (pid == -1)
{
out2("fork() 调用失败!\n");
ext(-1);
}
if (pid == 0)
{
close(outpipe[0]);
close(errpipe[0]);
dup2(outpipe[1], STDOUT_FILENO);
dup2(errpipe[1], STDERR_FILENO);
execlp("sh", "sh", "-c", command.c_str(), static_cast<char*>(nullptr));
perror("execlp");
ext(127);
}
close(outpipe[1]);
close(errpipe[1]);
auto res = readPipeLine(outpipe, errpipe, limit);
int status;
waitpid(pid, &status, 0);
cmd_result ret;
ret.out_str = res.first;
ret.err_str = res.second;
ret.ret_val = WEXITSTATUS(status);
return ret;
}
static cmd_result_mix runCommandMix(const std::string& command, int limit = INT_MAX)
{
int outpipe[2];
int errpipe[2];
if (pipe(outpipe) != 0 || pipe(errpipe) != 0)
{
out2("pipe() 调用失败!\n");
ext(-1);
}
pid_t pid = fork();
if (pid == -1)
{
out2("fork() 调用失败!\n");
ext(-1);
}
if (pid == 0)
{
close(outpipe[0]);
close(errpipe[0]);
dup2(outpipe[1], STDOUT_FILENO);
dup2(errpipe[1], STDERR_FILENO);
execlp("sh", "sh", "-c", command.c_str(), static_cast<char*>(nullptr));
perror("execlp");
ext(127);
}
close(outpipe[1]);
close(errpipe[1]);
auto res = readPipeLineMix(outpipe, errpipe, limit);
int status;
waitpid(pid, &status, 0);
cmd_result_mix ret;
ret.str = res;
ret.ret_val = WEXITSTATUS(status);
return ret;
}
static list<string> splitString(const string& str)
{
list<string> lines;
std::istringstream stream(str);
std::string line;
while (std::getline(stream, line))
{
if (!line.empty()) lines.push_back(line);
}
return lines;
}
static std::string readFile(const std::string& filename)
{
std::ifstream file(filename);
if (!file.is_open())
{
cerr << "无法打开文件 " + filename << '\n';
ext(-1);
}
std::string content((std::istreambuf_iterator(file)),
std::istreambuf_iterator<char>());
file.close();
return content;
}
static void writeFile(const std::string& content, const std::string& filename)
{
std::ofstream file(filename);
if (!file.is_open())
{
cerr << "无法打开文件 " + filename << '\n';
ext(-1);
}
file << content;
file.close();
}
static string lastDotLeft(const string& str)
{
size_t lastDotPos = str.find_last_of('.');
if (lastDotPos == string::npos)
{
return str;
}
return str.substr(0, lastDotPos);
}
static string lastLineRight(const string& str)
{
size_t lastSlashPos = str.find_last_of('/');
if (lastSlashPos == string::npos)
{
return str;
}
return str.substr(lastSlashPos + 1);
}
static void makeDir(const string& dirPath)
{
if (filesystem::exists(dirPath))
return;
if (!filesystem::create_directories(dirPath))
{
out2("目录 " + dirPath + " 创建失败!\n");
ext(-1);
}
}
static const char* ERR_LOG = R"(Usage: ./eval_lab4.sh [test-stage] [path-to-testcases] [type]
test-stage: 'raw' or 'licm' or 'mem2reg' or 'all'
path-to-testcases: './testcases/functional-cases' or '../testcases_general' or 'self made cases'
type: 'debug' or 'test', debug will output .ll file
)";
static int parseCmd(int argc, char* argv[])
{
if (argc != 4)
{
out(ERR_LOG, true);
return -1;
}
if (std::strcmp(argv[1], "licm") == 0)
{
STAGE = licm;
ost.open("licm_log.txt", ios::out);
}
else if (std::strcmp(argv[1], "mem2reg") == 0)
{
STAGE = mem2reg;
ost.open("mem2reg_log.txt", ios::out);
}
else if (std::strcmp(argv[1], "raw") == 0)
{
STAGE = raw;
ost.open("raw_log.txt", ios::out);
}
else if (std::strcmp(argv[1], "all") == 0)
{
STAGE = all;
ost.open("all_log.txt", ios::out);
}
else
{
out(ERR_LOG, true);
return -1;
}
TEST_PATH = argv[2];
if (TEST_PATH.empty() || TEST_PATH.back() != '/') TEST_PATH += '/';
if (!std::filesystem::exists(TEST_PATH))
{
out2("测评路径 " + TEST_PATH + " 不存在\n");
return -1;
}
if (!std::filesystem::is_directory(TEST_PATH))
{
out2("测评路径 " + TEST_PATH + " 不是文件夹\n");
return -1;
}
if (std::strcmp(argv[3], "debug") == 0)
TYPE = debug;
else if (std::strcmp(argv[3], "test") == 0)
TYPE = test;
else
{
out2(ERR_LOG);
return -1;
}
return 0;
}
static string pad(int count)
{
return count > 0 ? std::string(count, ' ') : std::string();
}
static string pad(int count, char target)
{
return count > 0 ? std::string(count, target) : std::string();
}
static int allmain(int argc, char* argv[])
{
auto cmd = R"(ls )" + TEST_PATH + R"(*.cminus | sort -V)";
auto result = runCommand(cmd);
string flags[3] = {"", "-mem2reg ", "-mem2reg -licm "};
string tys[3] = {"raw", "mem2reg", "licm"};
if (result.have_err_message()) out2e(result.err_str);
auto io_c = runCommand("realpath ../../").out_str;
io_c.pop_back();
io_c += "/src/io/io.c";
auto io_h = io_c;
io_h.back() = 'h';
auto tests = splitString(result.out_str);
string out_path = "./output/";
out2("[info] Start testing, using testcase dir: " + TEST_PATH + "\n");
int maxLen = 0;
for (const auto& line : tests) maxLen = std::max(maxLen, static_cast<int>(line.size()));
for (const auto& line : tests)
{
auto no_path_have_suffix = lastLineRight(line);
auto no_path_no_suffix = lastDotLeft(no_path_have_suffix);
makeDir(out_path + no_path_no_suffix);
auto in_file = TEST_PATH + no_path_no_suffix + ".in";
auto std_out_file = TEST_PATH + no_path_no_suffix + ".out";
out2("==========" + no_path_have_suffix + pad(maxLen - static_cast<int>(line.length()), '=') + "==========\n");
int sz[2] = {};
for (int i = 0; i < 3; i++)
{
const auto& arg = flags[i];
const auto& ty = tys[i];
auto ll_file = out_path + no_path_no_suffix + "/" + ty + ".ll";
auto asm_file = out_path + no_path_no_suffix + "/" + ty + ".s";
auto exe_file = out_path + no_path_no_suffix + "/" + ty + "o";
auto out_file = out_path + no_path_no_suffix + "/" + ty + ".out";
auto eval_file = out_path + no_path_no_suffix + "/eval_" + ty + ".txt";
out(ty + pad(7 - static_cast<int>(ty.length())) + " ", true);
out("==========" + ty + pad(7 - static_cast<int>(ty.length()), '=') + "==========\n",
false);
cout.flush();
ost.flush();
auto cmd2 = runCommandMix("cminusfc -S " + arg + line + " -o " + asm_file);
out(cmd2.str, false);
if (cmd2.ret_val)
{
out2e("CE: cminusfc compiler .cminus error\n");
continue;
}
if (TYPE == debug)
{
cmd2 = runCommandMix(
"loongarch64-unknown-linux-gnu-gcc -g -static " + asm_file + " " + io_c + " -o " + exe_file);
out(cmd2.str, false);
if (cmd2.ret_val)
{
out2e("CE: gcc compiler .s error\n");
continue;
}
}
else
{
cmd2 = runCommandMix(
"loongarch64-unknown-linux-gnu-gcc -static " + asm_file + " " + io_c + " -o " + exe_file);
out(cmd2.str, false);
if (cmd2.ret_val)
{
out2e("CE: gcc compiler .s error\n");
continue;
}
}
cmd_result ret;
if (filesystem::exists(in_file))
ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file + " <" + in_file);
else ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file);
auto o = readFile(out_file);
writeFile(o + to_string(ret.ret_val) + "\n", out_file);
writeFile(ret.err_str, eval_file);
cmd2 = runCommandMix("diff --strip-trailing-cr " + std_out_file + " " + out_file + " -y");
out(cmd2.str, false);
if (cmd2.ret_val != 0)
{
out2e("WA: output differ, check " + std_out_file + " and " + out_file + "\n");
continue;
}
out(green(" OK"), true);
out("OK\n", false);
auto strs = splitString(ret.err_str);
if (strs.size() >= 6)
{
out(" Take Time (us): " + green(strs.back()) + pad(
sz[0] == 0 ? 1 : (sz[0] - static_cast<int>(strs.back().size()))), true);
out(" Take Time (us): " + strs.back() + pad(
sz[0] == 0 ? 1 : (sz[0] - static_cast<int>(strs.back().size()))), false);
if (sz[0] == 0) sz[0] = static_cast<int>(strs.back().size()) + 1;
strs.pop_back();
strs.pop_back();
out(" Inst Execute Cost: " + green(strs.back()) + pad(sz[1] - static_cast<int>(strs.back().size())),
true);
out(" Inst Execute Cost: " + strs.back() + pad(sz[1] - static_cast<int>(strs.back().size())), false);
if (sz[1] == 0) sz[1] = static_cast<int>(strs.back().size());
strs.pop_back();
strs.pop_back();
out(" Allocate Size (bytes): " + green(strs.back()) + "\n", true);
out(" Allocate Size (bytes): " + strs.back() + "\n", false);
}
else cout << "\n";
filesystem::remove(exe_file);
filesystem::remove(out_file);
filesystem::remove(asm_file);
}
}
return 0;
}
int main(int argc, char* argv[])
{
if (parseCmd(argc, argv)) ext(-1);
if (STAGE == all)
{
return allmain(argc, argv);
}
auto cmd = R"(ls )" + TEST_PATH + R"(*.cminus | sort -V)";
auto result = runCommand(cmd);
string flags = (STAGE == mem2reg ? "-mem2reg " : (STAGE == raw ? "" : "-mem2reg -licm "));
if (result.have_err_message()) out2e(result.err_str);
auto io_c = runCommand("realpath ../../").out_str;
io_c.pop_back();
io_c += "/src/io/io.c";
auto io_h = io_c;
io_h.back() = 'h';
auto tests = splitString(result.out_str);
string out_path = "./output/";
out2("[info] Start testing, using testcase dir: " + TEST_PATH + "\n");
int maxLen = 0;
for (const auto& line : tests) maxLen = std::max(maxLen, static_cast<int>(line.size()));
for (const auto& line : tests)
{
auto no_path_have_suffix = lastLineRight(line);
auto no_path_no_suffix = lastDotLeft(no_path_have_suffix);
makeDir(out_path + no_path_no_suffix);
auto ll_file = out_path + no_path_no_suffix + "/" + argv[1] + ".ll";
auto asm_file = out_path + no_path_no_suffix + "/" + argv[1] + ".s";
auto exe_file = out_path + no_path_no_suffix + "/" + argv[1] + "o";
auto out_file = out_path + no_path_no_suffix + "/" + argv[1] + ".out";
auto eval_file = out_path + no_path_no_suffix + "/eval_" + argv[1] + ".txt";
auto in_file = TEST_PATH + no_path_no_suffix + ".in";
auto std_out_file = TEST_PATH + no_path_no_suffix + ".out";
out(no_path_have_suffix + pad(maxLen - static_cast<int>(line.length())) + " ", true);
out("==========" + no_path_have_suffix + pad(maxLen - static_cast<int>(line.length()), '=') + "==========\n",
false);
cout.flush();
ost.flush();
auto cmd2 = runCommandMix("cminusfc -S " + flags + line + " -o " + asm_file);
out(cmd2.str, false);
if (cmd2.ret_val)
{
out2e("CE: cminusfc compiler .cminus error\n");
continue;
}
if (TYPE == debug)
{
cmd2 = runCommandMix(
"loongarch64-unknown-linux-gnu-gcc -g -static " + asm_file + " " + io_c + " -o " + exe_file);
out(cmd2.str, false);
if (cmd2.ret_val)
{
out2e("CE: gcc compiler .s error\n");
continue;
}
}
else
{
cmd2 = runCommandMix(
"loongarch64-unknown-linux-gnu-gcc -static " + asm_file + " " + io_c + " -o " + exe_file);
out(cmd2.str, false);
if (cmd2.ret_val)
{
out2e("CE: gcc compiler .s error\n");
continue;
}
}
cmd_result ret;
if (filesystem::exists(in_file))
ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file + " <" + in_file);
else ret = runCommand("qemu-loongarch64 " + exe_file + " >" + out_file);
auto o = readFile(out_file);
writeFile(o + to_string(ret.ret_val) + "\n", out_file);
writeFile(ret.err_str, eval_file);
cmd2 = runCommandMix("diff --strip-trailing-cr " + std_out_file + " " + out_file + " -y");
out(cmd2.str, false);
if (cmd2.ret_val != 0)
{
out2e("WA: output differ, check " + std_out_file + " and " + out_file + "\n");
continue;
}
out(green(" OK"), true);
out("OK\n", false);
auto strs = splitString(ret.err_str);
if (strs.size() >= 6)
{
out(" Take Time (us): " + green(strs.back()), true);
out(" Take Time (us): " + strs.back(), false);
strs.pop_back();
strs.pop_back();
out(" Inst Execute Cost: " + green(strs.back()), true);
out(" Inst Execute Cost: " + strs.back(), false);
strs.pop_back();
strs.pop_back();
out(" Allocate Size (bytes): " + green(strs.back()) + "\n", true);
out(" Allocate Size (bytes): " + strs.back() + "\n", false);
}
else cout << "\n";
filesystem::remove(exe_file);
filesystem::remove(out_file);
filesystem::remove(asm_file);
}
}
add_subdirectory("2-ir-gen/warmup")
add_subdirectory("3-codegen/warmup")
\ No newline at end of file
# add_subdirectory("2-ir-gen/warmup")
# add_subdirectory("3-codegen/warmup")
add_subdirectory("4-opt")
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment