Commit 1631b1c3 authored by lxq's avatar lxq

ready to start register allocation

parent 25f19f51
# Lab5 报告
## 实验任务
基于前4个实验,完成对LightIR的翻译,目标架构为龙芯LA64架构。
## 实验流程
1. 使用栈式内存分配,优先追求功能性
这一步主要完成了指令选择,所有变量(local or global)均在栈中储存,参数通过栈传递。常量保存在只读区(模拟gcc)。
这里对于phi指令的处理是:将phi指令还原为前驱块的`copy-statement`,需要将其插入在基本块的最后一条指令(跳转指令)之前。
这一步可以完成所有测试样例,但是生成的代码效率较差。
2. 活跃变量分析
先确定指令的遍历顺序,这里使用常规的BFS遍历,phi指令的处理和上述相同,例如对于`5-while.ll`
```llvm
define i32 @main() {
label_entry:
br label %label0
label0: ; preds = %label_entry, %label5
%op1 = phi i32 [ 0, %label_entry ], [ %op6, %label5 ]
%op2 = icmp slt i32 %op1, 10
%op3 = zext i1 %op2 to i32
%op4 = icmp ne i32 %op3, 0
br i1 %op4, label %label5, label %label7
label5: ; preds = %label0
call void @output(i32 %op1)
%op6 = add i32 %op1, 1
br label %label0
label7: ; preds = %label0
ret i32 0
}
```
指令遍历顺序如下,第1条与第9条指令就是phi指令的还原。
```llvm
1. op1 = 0
2. br label %label0
3. %op2 = icmp slt i32 %op1, 10
4. %op3 = zext i1 %op2 to i32
5. %op4 = icmp ne i32 %op3, 0
6. br i1 %op4, label %label5, label %label7
7. call void @output(i32 %op1)
8. %op6 = add i32 %op1, 1
9. op1 = op6
10. br label %label0
11. ret i32 0
```
用编号代替指令,获得每个程序点的IN和OUT:
```llvm
1. op1 = 0
in-set: [ ]
out-set: [ op1 ]
2. br label %label0
in-set: [ op1 ]
out-set: [ op1 ]
3. %op2 = icmp slt i32 %op1, 10
in-set: [ op1 ]
out-set: [ op2 op1 ]
4. %op3 = zext i1 %op2 to i32
in-set: [ op2 op1 ]
out-set: [ op3 op1 ]
5. %op4 = icmp ne i32 %op3, 0
in-set: [ op3 op1 ]
out-set: [ op4 op1 ]
6. br i1 %op4, label %label5, label %label7
in-set: [ op4 op1 ]
out-set: [ op1 ]
7. call void @output(i32 %op1)
in-set: [ op1 ]
out-set: [ op1 ]
8. %op6 = add i32 %op1, 1
in-set: [ op1 ]
out-set: [ op6 ]
9. op1 = op6
in-set: [ op6 ]
out-set: [ op1 ]
10. br label %label0
in-set: [ op1 ]
out-set: [ op1 ]
11. ret i32 0
in-set: [ ]
out-set: [ ]
```
获得活跃区间:编号为i的指令,涉及两个端点:i-1和i,分别对应IN和OUT。由此得到各个变量的活跃区间是:
```llvm
op1: <1, 10>
op2: <3, 3>
op3: <4, 4>
op4: <5, 5>
op6: <8, 8>
```
3. 寄存器分配
使用线性扫描算法实现寄存器分配,参考:
- http://web.cs.ucla.edu/~palsberg/course/cs132/linearscan.pdf
- [Documentations/5-bonus/寄存器分配.md · master · compiler_staff / 2022fall-Compiler_CMinus · GitLab](https://cscourse.ustc.edu.cn/vdir/Gitlab/compiler_staff/2022fall-compiler_cminus/-/blob/master/Documentations/5-bonus/%E5%AF%84%E5%AD%98%E5%99%A8%E5%88%86%E9%85%8D.md#poletto)
...@@ -156,7 +156,7 @@ class CodeGen { ...@@ -156,7 +156,7 @@ class CodeGen {
vector<string> output; vector<string> output;
// register allocation // register allocation
LiveRangeAnalyzer LRA; LRA::LiveRangeAnalyzer LRA;
}; };
#endif #endif
...@@ -13,52 +13,75 @@ ...@@ -13,52 +13,75 @@
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <utility>
#include <vector>
using std::map; using std::map;
using std::pair; using std::pair;
using std::set; using std::set;
using std::string; using std::string;
using std::to_string;
using std::vector; using std::vector;
#define __LRA_PRINT__ #define __LRA_PRINT__
namespace LRA {
struct Interval {
Interval(int a = 0, int b = 0) : i(a), j(b) {}
int i; // 0 means uninitialized
int j;
};
using LiveSet = set<Value *>;
using PhiMap = map<BasicBlock *, vector<pair<Value *, Value *>>>;
using LiveInterval = pair<Interval, Value *>;
struct LiveIntervalCMP {
bool operator()(LiveInterval const &lhs, LiveInterval const &rhs) const {
if (lhs.first.i != rhs.first.i)
return lhs.first.i < rhs.first.i;
else
return lhs.second < rhs.second;
}
};
class LiveRangeAnalyzer { class LiveRangeAnalyzer {
public:
friend class CodeGen; friend class CodeGen;
using LiveSet = set<Value *>;
using PhiMap = map<BasicBlock *, vector<pair<Value *, Value *>>>;
public:
struct Interval {
Interval(int a, int b) : i(a), j(b) {}
Interval() = delete;
int i, j;
};
LiveRangeAnalyzer(Module *m_, PhiMap &phi_map_) LiveRangeAnalyzer(Module *m_, PhiMap &phi_map_)
: m(m_), phi_map(phi_map_) {} : m(m_), phi_map(phi_map_) {}
LiveRangeAnalyzer() = delete; LiveRangeAnalyzer() = delete;
void run(); void run();
void run(Function *);
void clear(); void clear();
void print(Function *func, bool printSet = true); void print(Function *func, bool printSet = true);
string print_liveSet(LiveSet &ls) { string print_liveSet(const LiveSet &ls) {
string s = "[ "; string s = "[ ";
for (auto k : ls) for (auto k : ls)
s += k->get_name() + " "; s += k->get_name() + " ";
s += "]"; s += "]";
return s; return s;
} }
string print_interval(Interval &i) {
return "<" + to_string(i.i) + ", " + to_string(i.j) + ">";
}
private: private:
Module *m; Module *m;
// Function *func; // Function *func;
map<Value *, Interval> liverange; int ir_cnt;
map<int, LiveSet> IN, OUT; map<int, LiveSet> IN, OUT;
map<Value *, int> instr_id; map<Value *, int> instr_id;
map<pair<Value *, Value *>, int> cpstmt_id; map<pair<Value *, Value *>, int> cpstmt_id;
const PhiMap &phi_map; const PhiMap &phi_map;
set<LiveInterval, LiveIntervalCMP> liveIntervals;
void make_id(Function *); void make_id(Function *);
void run(Function *); void make_interval(Function *);
LiveSet joinFor(BasicBlock *bb); LiveSet joinFor(BasicBlock *bb);
void union_ip(LiveSet &dest, LiveSet &src) { void union_ip(LiveSet &dest, LiveSet &src) {
LiveSet res; LiveSet res;
...@@ -73,4 +96,5 @@ class LiveRangeAnalyzer { ...@@ -73,4 +96,5 @@ class LiveRangeAnalyzer {
// Return: the in-set(will not set IN-map) // Return: the in-set(will not set IN-map)
LiveSet transferFunction(Instruction *); LiveSet transferFunction(Instruction *);
}; };
} // namespace LRA
#endif #endif
...@@ -34,7 +34,7 @@ main(int argc, char **argv) { ...@@ -34,7 +34,7 @@ main(int argc, char **argv) {
bool gvn = false; bool gvn = false;
bool dump_json = false; bool dump_json = false;
bool emit = false; bool emit = false;
bool assembly = false; bool assembly = true;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (argv[i] == "-h"s || argv[i] == "--help"s) { if (argv[i] == "-h"s || argv[i] == "--help"s) {
...@@ -128,7 +128,7 @@ main(int argc, char **argv) { ...@@ -128,7 +128,7 @@ main(int argc, char **argv) {
std::ofstream target_file(target_path + ".s"); std::ofstream target_file(target_path + ".s");
target_file << codegen.print(); target_file << codegen.print();
target_file.close(); target_file.close();
return 0; // return 0;
} }
std::ofstream output_stream; std::ofstream output_stream;
......
...@@ -6,15 +6,21 @@ ...@@ -6,15 +6,21 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
using std::cout;
using std::endl;
using namespace LRA;
void void
LiveRangeAnalyzer::clear() { LiveRangeAnalyzer::clear() {
ir_cnt = 0;
IN.clear(); IN.clear();
OUT.clear(); OUT.clear();
liverange.clear();
instr_id.clear(); instr_id.clear();
cpstmt_id.clear();
liveIntervals.clear();
} }
LiveRangeAnalyzer::LiveSet LiveSet
LiveRangeAnalyzer::joinFor(BasicBlock *bb) { LiveRangeAnalyzer::joinFor(BasicBlock *bb) {
LiveSet out; LiveSet out;
for (auto succ : bb->get_succ_basic_blocks()) { for (auto succ : bb->get_succ_basic_blocks()) {
...@@ -24,9 +30,9 @@ LiveRangeAnalyzer::joinFor(BasicBlock *bb) { ...@@ -24,9 +30,9 @@ LiveRangeAnalyzer::joinFor(BasicBlock *bb) {
++it; ++it;
assert(it != irs.end() && "need to find first_ir from copy-stmt"); assert(it != irs.end() && "need to find first_ir from copy-stmt");
union_ip(out, IN[instr_id.at(&(*it))]); union_ip(out, IN[instr_id.at(&(*it))]);
// std::cout << "# " + it->print() << std::endl; // cout << "# " + it->print() << endl;
} }
// std::cout << "\tget out: " << print_liveSet(out) << std::endl; // cout << "\tget out: " << print_liveSet(out) << endl;
return out; return out;
} }
...@@ -35,7 +41,6 @@ LiveRangeAnalyzer::make_id(Function *func) { ...@@ -35,7 +41,6 @@ LiveRangeAnalyzer::make_id(Function *func) {
// instruction numbering // instruction numbering
// this is also the structure of the IR logically: // this is also the structure of the IR logically:
// ignore phi, add copy-statement // ignore phi, add copy-statement
int ir_cnt = 0;
for (auto &bb : func->get_basic_blocks()) { for (auto &bb : func->get_basic_blocks()) {
for (auto &instr : bb.get_instructions()) { for (auto &instr : bb.get_instructions()) {
if (instr.is_phi()) if (instr.is_phi())
...@@ -125,12 +130,37 @@ LiveRangeAnalyzer::run(Function *func) { ...@@ -125,12 +130,37 @@ LiveRangeAnalyzer::run(Function *func) {
} }
} }
} }
make_interval(func);
#ifdef __LRA_PRINT__ #ifdef __LRA_PRINT__
print(func); print(func);
#endif #endif
} }
LiveRangeAnalyzer::LiveSet void
LiveRangeAnalyzer::make_interval(Function *) {
map<Value *, Interval> liverange;
for (int time = 1; time <= ir_cnt; ++time) {
for (auto op : IN.at(time)) {
auto &interval = liverange[op];
if (interval.i == 0) // uninitialized
interval.i = time - 1;
else
interval.j = time - 1;
}
for (auto op : OUT.at(time)) {
auto &interval = liverange[op];
if (interval.i == 0) // uninitialized
interval.i = time;
else
interval.j = time;
}
}
for (auto [op, interval] : liverange)
liveIntervals.insert({interval, op});
}
LiveSet
LiveRangeAnalyzer::transferFunction(Instruction *instr) { LiveRangeAnalyzer::transferFunction(Instruction *instr) {
LiveSet in, out = OUT[instr_id.at(instr)]; LiveSet in, out = OUT[instr_id.at(instr)];
LiveSet use; LiveSet use;
...@@ -165,6 +195,7 @@ LiveRangeAnalyzer::transferFunction(Instruction *instr) { ...@@ -165,6 +195,7 @@ LiveRangeAnalyzer::transferFunction(Instruction *instr) {
void void
LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug
cout << "Function " << func->get_name() << endl;
for (auto &bb : func->get_basic_blocks()) { for (auto &bb : func->get_basic_blocks()) {
for (auto &instr : bb.get_instructions()) { for (auto &instr : bb.get_instructions()) {
if (instr.is_phi()) // ignore phi if (instr.is_phi()) // ignore phi
...@@ -175,44 +206,33 @@ LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug ...@@ -175,44 +206,33 @@ LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug
for (auto pr : phi_map.find(&bb)->second) { for (auto pr : phi_map.find(&bb)->second) {
auto [lv, rv] = pr; auto [lv, rv] = pr;
auto idx = cpstmt_id.at(pr); auto idx = cpstmt_id.at(pr);
std::cout cout << cpstmt_id[pr] << ". " << lv->get_name() << " = "
<< cpstmt_id[pr] << ". " << lv->get_name() << " = " << (rv->get_name() == "" ? rv->print()
<< (rv->get_name() == "" ? rv->print() : rv->get_name()) : rv->get_name())
<< std::endl; << endl;
if (not printSet) if (not printSet)
continue; continue;
auto &in = IN.at(idx); auto &in = IN.at(idx);
auto &out = OUT.at(idx); auto &out = OUT.at(idx);
std::cout << "\tin-set: " + print_liveSet(in) << "\n"; cout << "\tin-set: " + print_liveSet(in) << "\n";
std::cout << "\tout-set: " + print_liveSet(out) << "\n"; cout << "\tout-set: " + print_liveSet(out) << "\n";
} }
} }
// normal ir // normal ir
std::cout << instr_id[&instr] << ". " << instr.print() << " # " cout << instr_id[&instr] << ". " << instr.print() << " # " << &instr
<< &instr << std::endl; << endl;
if (not printSet) if (not printSet)
continue; continue;
auto idx = instr_id.at(&instr); auto idx = instr_id.at(&instr);
auto &in = IN.at(idx); auto &in = IN.at(idx);
auto &out = OUT.at(idx); auto &out = OUT.at(idx);
std::cout << "\tin-set: " + print_liveSet(in) << "\n"; cout << "\tin-set: " + print_liveSet(in) << "\n";
std::cout << "\tout-set: " + print_liveSet(out) << "\n"; cout << "\tout-set: " + print_liveSet(out) << "\n";
} }
/* if (phi_map.find(&bb) != phi_map.end()) { }
* for (auto pr : phi_map.find(&bb)->second) {
* auto [lv, rv] = pr; if (printSet) {
* auto idx = cpstmt_id.at(pr); for (auto [interval, op] : liveIntervals)
* std::cout << cpstmt_id[pr] << ". " << lv->get_name() << " = " cout << op->get_name() << ": " << print_interval(interval) << endl;
* << (rv->get_name() == "" ? rv->print()
* : rv->get_name())
* << std::endl;
* if (not printSet)
* continue;
* auto &in = IN.at(idx);
* auto &out = OUT.at(idx);
* std::cout << "\tin-set: " + print_liveSet(in) << "\n";
* std::cout << "\tout-set: " + print_liveSet(out) << "\n";
* }
* } */
} }
} }
...@@ -68,7 +68,7 @@ def eval(console=False, test_dir=testfile_dir, use_clang=False): ...@@ -68,7 +68,7 @@ def eval(console=False, test_dir=testfile_dir, use_clang=False):
if not use_clang: if not use_clang:
try: try:
# ===可修改=== # ===可修改===
compile_res = subprocess.run([cminus, filepath, '-S', 'a.s'], compile_res = subprocess.run([cminus, '-mem2reg', filepath, '-S', 'a.s'],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
timeout=300) timeout=300)
......
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