Commit 0454682c authored by lxq's avatar lxq

write reg-alloca for int values

parent 3d814ba8
......@@ -59,38 +59,38 @@ label7: ; preds = %label0
```llvm
1. op1 = 0
in-set: [ ]
out-set: [ op1 ]
in-set: [ ]
out-set: [ op1 ]
2. br label %label0
in-set: [ op1 ]
out-set: [ op1 ]
in-set: [ op1 ]
out-set: [ op1 ]
3. %op2 = icmp slt i32 %op1, 10
in-set: [ op1 ]
out-set: [ op2 op1 ]
in-set: [ op1 ]
out-set: [ op2 op1 ]
4. %op3 = zext i1 %op2 to i32
in-set: [ op2 op1 ]
out-set: [ op3 op1 ]
in-set: [ op2 op1 ]
out-set: [ op3 op1 ]
5. %op4 = icmp ne i32 %op3, 0
in-set: [ op3 op1 ]
out-set: [ op4 op1 ]
in-set: [ op3 op1 ]
out-set: [ op4 op1 ]
6. br i1 %op4, label %label5, label %label7
in-set: [ op4 op1 ]
out-set: [ op1 ]
in-set: [ op4 op1 ]
out-set: [ op1 ]
7. call void @output(i32 %op1)
in-set: [ op1 ]
out-set: [ op1 ]
in-set: [ op1 ]
out-set: [ op1 ]
8. %op6 = add i32 %op1, 1
in-set: [ op1 ]
out-set: [ op6 ]
in-set: [ op1 ]
out-set: [ op6 ]
9. op1 = op6
in-set: [ op6 ]
out-set: [ op1 ]
in-set: [ op6 ]
out-set: [ op1 ]
10. br label %label0
in-set: [ op1 ]
out-set: [ op1 ]
in-set: [ op1 ]
out-set: [ op1 ]
11. ret i32 0
in-set: [ ]
out-set: [ ]
in-set: [ ]
out-set: [ ]
```
获得活跃区间:编号为i的指令,涉及两个端点:i-1和i,分别对应IN和OUT。由此得到各个变量的活跃区间是:
......@@ -111,4 +111,28 @@ op6: <8, 8>
- [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)
i
程序有`$a`系列寄存器8个,`$t`系列9个,拿出`$t0``$t1`做IR生成汇编过程中的临时寄存器(这个方案仅在cminus下成立),所以可以自由分配的寄存器一共15个。
首先完成对于局部变量的寄存器分配,即全局变量、传参依旧通过栈进行。
程序分配寄存器时会对部分指令做特殊处理,具体如下:
- `phi`指令:还原为`copy-stmt`,所以寄存器照样分配,如果没分到使用栈内存
- `alloca`指令:这里忽略对于`alloca`指令的寄存器分配,`alloca`仍然使用栈存储,原因如下:
- 对于int|float的`alloca`,使用寄存器可以正常进行,只需将相关指令`load``store`分别成赋值即可。
- 但是对于数组的`alloca`,使用寄存器就很难模拟了。
即寄存器不能完成`alloca`的内存逻辑,同时经过`mem2reg`优化过后,不再有局部变量的声明,所以忽略对于`alloca`指令的寄存器分配。
- `cmp``fcmp``zext`指令:不做寄存器分配,也不做栈分配。
这实际是指令选择部分的内容:
因为在cminus中并没有bool变量,这些IR指令用到的i1类型都是临时的:只为分支指令服务,所以直接将这些指令集成到分支跳转的判断中。
- `call`指令:使用栈传参,caller保存自己用到的寄存器,被保存的寄存器的活跃区间覆盖`call`指令的程序点。
......@@ -9,15 +9,17 @@
#include "Module.h"
#include "Value.h"
#include "liverange.hpp"
#include "logging.hpp"
#include "regalloc.hpp"
#define __RO_PART__
// #a = 8, #t = 9, reserve $t0, $t1 for temporary
#define R_USABLE 17 - 2
#include <map>
#include <ostream>
#include <vector>
#define ALIGN(x, align) (((x / align) + (x % align ? 1 : 0)) * align)
#define ALIGN(x, align) ((((x) / align) + (((x) % align) ? 1 : 0)) * align)
// #define STACK_ALIGN(x) (((x / 16) + (x % 16 ? 1 : 0)) * 16)
#define STACK_ALIGN(x) ALIGN(x, 16)
......@@ -27,7 +29,7 @@ using std::vector;
class CodeGen {
public:
CodeGen(Module *m_) : m(m_), LRA(m_, phi_map) {}
CodeGen(Module *m_) : m(m_), LRA(m_, phi_map), RA(R_USABLE) {}
string print() {
string result;
......@@ -42,35 +44,41 @@ class CodeGen {
void run();
private:
void IR2assem(Instruction &, BasicBlock &);
void IR2assem(LoadInst *);
void IR2assem(StoreInst *);
void IR2assem(ReturnInst *);
void IR2assem(GetElementPtrInst *);
void IR2assem(CallInst *);
void IR2assem(BranchInst *);
void IR2assem(BinaryInst *);
void IR2assem(FpToSiInst *);
void IR2assem(SiToFpInst *);
void IR2assem(PhiInst *) {}
// The Instructions below will do nothing
void IR2assem(AllocaInst *) {}
// integration with BranchInst
void IR2assem(CmpInst *) {}
void IR2assem(FCmpInst *) {}
void IR2assem(ZextInst *) {}
//
// data member
//
std::map<Value *, unsigned int> off; // stack offset to $fp
std::map<Function *, std::map<int, int>> func_arg_off; // to $sp
// total space for args, NOTE: not aligned
std::map<Function *, int> func_arg_N;
std::map<BasicBlock *, std::vector<std::pair<Value *, Value *>>> phi_map;
std::map<Constant *, std::string> ROdata;
unsigned int stackN; // function local vars and so on
Function *cur_func;
Module *m;
vector<string> output;
// register allocation
LRA::LiveRangeAnalyzer LRA;
RA::RegAllocator RA;
// some instruction has lvalue, but is stack-allocated,
// we need this variable to track the reg name which has rvalue.
// this variable is maintain by gencopy() and LoadInst.
string last_reg;
//
// function member
//
void stackMemAlloc();
void stackMemDealloc();
// load value `opk` to the specified register
// - for constant number, just load into reg
// - for global variables and pointers from alloca and GEP, read through
// address
// only use register a_id and t_
void value2reg(Value *, int id = 0);
// In the case of register allocation, this function will return the
// allocated register for that value, if the value possesses no register,
// choose from from $t0 or $t1 based on id
__attribute__((warn_unused_result)) string value2reg(Value *, int i = 0);
// load the content in ptr to specified register.
// only use register a_id and t_
void ptrContent2reg(Value *, int id = 0);
void ptrContent2reg(Value *, string);
void compute_arg_info(Function *);
string bool2branch(Instruction *);
void getPhiMap();
......@@ -80,15 +88,35 @@ class CodeGen {
output.push_back("# " + print_as_op(copy.first, false) + " = " +
print_as_op(copy.second, false));
auto lvalue = static_cast<Instruction *>(copy.first);
value2reg(copy.second);
back2stack(lvalue);
auto src_reg = value2reg(copy.second);
if (gencopy(lvalue, src_reg) == false)
back2stack(lvalue);
}
}
// if reg-allocation, store to the specific register
// or is stack-allocation, set last_reg for back2stack()
bool gencopy(Value *lhs, string rhs_reg) {
auto [lhs_reg, find] = getRegName(lhs);
if (not find) {
// wait for back2stack() to store back
last_reg = rhs_reg;
return false;
}
auto is_float = lhs->get_type()->is_float_type();
if (rhs_reg != lhs_reg) {
if (is_float)
output.push_back("fmov.s " + lhs_reg + ", " + rhs_reg);
else
output.push_back("or " + lhs_reg + ", $zero, " + rhs_reg);
}
return true;
}
string label_in_assem(BasicBlock *bb) {
string label_in_assem(BasicBlock *bb) const {
return cur_func->get_name() + bb->get_name().substr(5);
}
int typeLen(Type *type) {
int typeLen(Type *type) const {
if (type->is_float_type())
return 4;
else if (type->is_integer_type()) {
......@@ -107,19 +135,19 @@ class CodeGen {
}
}
// assert the value needed to store back is in $a0 or $f0, according to the
// assert the value needed to store back is in `last_reg`, according to the
// value type
void back2stack(Instruction *instr) {
// std::cerr << instr->print() << std::endl;
auto type = instr->get_type();
string instr_ir = type->is_float_type() ? "fst" : "st";
string suff = suffix(type);
string reg = type->is_float_type() ? "$fa0" : "$a0";
string addr = "$fp, -" + std::to_string(off[instr]);
output.push_back(instr_ir + suff + " " + reg + ", " + addr);
// string reg = type->is_float_type() ? "$fa0" : "$a0";
string addr = "$fp, -" + std::to_string(off.at(instr));
output.push_back(instr_ir + suff + " " + last_reg + ", " + addr);
}
string suffix(Type *type) {
string suffix(Type *type) const {
int len = typeLen(type);
switch (len) {
case 1:
......@@ -134,29 +162,53 @@ class CodeGen {
assert(false && "no such suffix");
}
bool no_stack_alloca(Instruction *instr) {
bool no_stack_alloca(Instruction *instr) const {
if (instr->is_void())
return true;
if (instr->is_fcmp() or instr->is_cmp() or instr->is_zext())
return true;
if (RA.get().find(instr) != RA.get().end())
return true;
return false;
}
string tmpregname(int i, bool is_float) const {
assert(i == 0 or i == 1);
return (is_float ? "$ft" : "$t") + to_string(i);
}
std::map<Value *, unsigned int> off; // stack offset to $fp
std::map<Function *, std::map<int, int>> func_arg_off; // to $sp
std::map<Function *, int> func_arg_N; // total space for args
std::map<BasicBlock *, std::vector<std::pair<Value *, Value *>>> phi_map;
std::map<Constant *, std::string> ROdata;
unsigned int stackN; // function local vars and so on
Function *cur_func;
static string regname(int i, bool is_float = false) {
string name;
if (is_float) {
assert(false && "not implemented!");
} else {
if (1 <= i and i <= 8)
name = "$a" + to_string(i - 1);
else if (9 <= i and i <= R_USABLE)
name = "$t" + to_string(i - 9 + 2);
}
return name;
}
Module *m;
vector<string> output;
pair<string, bool> getRegName(Value *, int = 0) const;
// register allocation
LRA::LiveRangeAnalyzer LRA;
void IR2assem(Instruction &, BasicBlock &);
void IR2assem(LoadInst *);
void IR2assem(StoreInst *);
void IR2assem(ReturnInst *);
void IR2assem(GetElementPtrInst *);
void IR2assem(CallInst *);
void IR2assem(BranchInst *);
void IR2assem(BinaryInst *);
void IR2assem(FpToSiInst *);
void IR2assem(SiToFpInst *);
void IR2assem(PhiInst *) {}
// The Instructions below will do nothing
void IR2assem(AllocaInst *) {}
// integration with BranchInst
void IR2assem(CmpInst *) {}
void IR2assem(FCmpInst *) {}
void IR2assem(ZextInst *) {}
};
#endif
......@@ -2,6 +2,7 @@
#define LIVERANGE_HPP
#include "Module.h"
#include "Value.h"
#include <iostream>
#include <map>
......@@ -27,7 +28,6 @@ struct Interval {
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)
......@@ -36,29 +36,31 @@ struct LiveIntervalCMP {
return lhs.second < rhs.second;
}
};
using LVITS = set<LiveInterval, LiveIntervalCMP>;
class LiveRangeAnalyzer {
public:
friend class CodeGen;
public:
LiveRangeAnalyzer(Module *m_, PhiMap &phi_map_)
: m(m_), phi_map(phi_map_) {}
LiveRangeAnalyzer() = delete;
void run();
// void run();
void run(Function *);
void clear();
void print(Function *func, bool printSet = true);
string print_liveSet(const LiveSet &ls) {
void print(Function *func, bool printSet = true) const;
string print_liveSet(const LiveSet &ls) const {
string s = "[ ";
for (auto k : ls)
s += k->get_name() + " ";
s += "]";
return s;
}
string print_interval(Interval &i) {
string print_interval(Interval &i) const {
return "<" + to_string(i.i) + ", " + to_string(i.j) + ">";
}
const LVITS &get() { return liveIntervals; }
private:
Module *m;
......@@ -68,7 +70,8 @@ class LiveRangeAnalyzer {
map<Value *, int> instr_id;
map<pair<Value *, Value *>, int> cpstmt_id;
const PhiMap &phi_map;
set<LiveInterval, LiveIntervalCMP> liveIntervals;
LVITS liveIntervals;
map<Value *, Interval> intervalmap;
void make_id(Function *);
void make_interval(Function *);
......@@ -86,6 +89,10 @@ class LiveRangeAnalyzer {
// Require: out-set is already set
// Return: the in-set(will not set IN-map)
LiveSet transferFunction(Instruction *);
public:
const decltype(instr_id) &get_instr_id() { return instr_id; }
const decltype(intervalmap) &get_interval_map() { return intervalmap; }
};
} // namespace LRA
#endif
#include "Value.h"
#include "liverange.hpp"
// using std::transform;
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::to_string;
using namespace LRA;
namespace RA {
#define MAXR 32
bool no_reg_alloca(Value *v);
struct ActiveCMP {
bool operator()(LiveInterval const &lhs, LiveInterval const &rhs) const {
if (lhs.first.j != rhs.first.j)
......@@ -18,15 +27,20 @@ struct ActiveCMP {
class RegAllocator {
public:
RegAllocator(const uint R_) : R(R_), used{false} {}
RegAllocator(const uint R_) : R(R_), used{false} { assert(R <= MAXR); }
RegAllocator() = delete;
// input set is sorted by increasing start point
void LinearScan(set<LiveInterval> &liveints);
void LinearScan(const LVITS &liveints);
void reset();
const map<Value *, int> &get() const { return regmap; }
void print(string (*regname)(int)) {
for (auto [op, reg] : regmap)
cout << op->get_name() << " ~ " << regname(reg) << endl;
}
private:
const uint R;
bool used[MAXR];
bool used[MAXR + 1]; // index range: 1 ~ R
map<Value *, int> regmap;
// sorted by increasing end point
set<LiveInterval, ActiveCMP> active;
......
This diff is collapsed.
......@@ -11,6 +11,7 @@ LiveRangeAnalyzer::clear() {
OUT.clear();
instr_id.clear();
cpstmt_id.clear();
intervalmap.clear();
liveIntervals.clear();
}
......@@ -133,24 +134,23 @@ LiveRangeAnalyzer::run(Function *func) {
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];
auto &interval = intervalmap[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];
auto &interval = intervalmap[op];
if (interval.i == 0) // uninitialized
interval.i = time;
else
interval.j = time;
}
}
for (auto [op, interval] : liverange)
for (auto [op, interval] : intervalmap)
liveIntervals.insert({interval, op});
}
......@@ -188,7 +188,7 @@ LiveRangeAnalyzer::transferFunction(Instruction *instr) {
}
void
LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug
LiveRangeAnalyzer::print(Function *func, bool printSet) const { // for debug
cout << "Function " << func->get_name() << endl;
for (auto &bb : func->get_basic_blocks()) {
for (auto &instr : bb.get_instructions()) {
......@@ -200,7 +200,7 @@ LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug
for (auto pr : phi_map.find(&bb)->second) {
auto [lv, rv] = pr;
auto idx = cpstmt_id.at(pr);
cout << cpstmt_id[pr] << ". " << lv->get_name() << " = "
cout << cpstmt_id.at(pr) << ". " << lv->get_name() << " = "
<< (rv->get_name() == "" ? rv->print()
: rv->get_name())
<< endl;
......@@ -213,8 +213,8 @@ LiveRangeAnalyzer::print(Function *func, bool printSet) { // for debug
}
}
// normal ir
cout << instr_id[&instr] << ". " << instr.print() << " # " << &instr
<< endl;
cout << instr_id.at(&instr) << ". " << instr.print() << " # "
<< &instr << endl;
if (not printSet)
continue;
auto idx = instr_id.at(&instr);
......
#include "regalloc.hpp"
#include "Instruction.h"
#include "liverange.hpp"
#include <algorithm>
using std::for_each;
using namespace RA;
bool
RA::no_reg_alloca(Value *v) {
auto instr = static_cast<Instruction *>(v);
return instr->is_alloca() or instr->is_cmp() or instr->is_fcmp() or
instr->is_zext();
}
void
RegAllocator::reset() {
regmap.clear();
active.clear();
for_each(used, used + R, [](bool &u) { u = false; });
for_each(used, used + R + 1, [](bool &u) { u = false; });
}
void
RegAllocator::LinearScan(set<LiveInterval> &liveints) {
RegAllocator::LinearScan(const LVITS &liveints) {
reset();
int reg;
for (auto liveint : liveints) {
if (no_reg_alloca(liveint.second))
continue;
ExpireOldIntervals(liveint);
if (active.size() == R)
SpillAtInterval(liveint);
......
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