buggy mem2reg
This commit is contained in:
parent
7140d0bf2b
commit
2a36406e3d
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -9,7 +9,7 @@
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/build/sysy",
|
||||
"args": ["testcases/functional/64_calculator.sy", "-S", "-o", "build/manual-test/21_my.ll"],
|
||||
"args": ["../sysytests/functional_2022/21_if_test2.sy", "-S", "-o", "build/21_my.ll", "-emit-llvm"],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
|
||||
@ -21,7 +21,8 @@ file(GLOB MY_HEADERS "include/*.h")
|
||||
include(formatting.cmake)
|
||||
clang_format(format ${MY_SOURCES} ${MY_HEADERS})
|
||||
# clang_format_check(format_check ${MY_SOURCES} ${MY_HEADERS})
|
||||
|
||||
# add_compile_options(-fsanitize=address)
|
||||
# add_link_options(-fsanitize=address)
|
||||
add_executable(sysy ${SOURCES})
|
||||
# message(STATUS "${SOURCES}")
|
||||
target_link_libraries(sysy antlr4_static)
|
||||
target_link_libraries(sysy antlr4_static)
|
||||
|
||||
1201
docs/LLVM-promoteAlloca.cpp
Normal file
1201
docs/LLVM-promoteAlloca.cpp
Normal file
File diff suppressed because it is too large
Load Diff
11
include/algos.h
Normal file
11
include/algos.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "llir.h"
|
||||
#include "passes.h"
|
||||
|
||||
namespace antlrSysY {
|
||||
void gen_dominance(FunctionPtr_t func);
|
||||
void gen_dominance_frontier(FunctionPtr_t func);
|
||||
void update_dfs_numbers(BasicBlockPtr_t bb, bool rst);
|
||||
|
||||
} // namespace antlrSysY
|
||||
@ -16,6 +16,10 @@
|
||||
if (!(cond)) throw GrammarException(__FILE__, __LINE__, #cond); \
|
||||
} while (0)
|
||||
|
||||
#define DEF_PTR_T(type) \
|
||||
class type; \
|
||||
typedef std::shared_ptr<type> type##Ptr_t
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
template <typename T>
|
||||
|
||||
@ -7,6 +7,18 @@
|
||||
#include <string>
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
DEF_PTR_T(InstAlloca);
|
||||
DEF_PTR_T(InstStore);
|
||||
DEF_PTR_T(InstLoad);
|
||||
DEF_PTR_T(InstBinary);
|
||||
DEF_PTR_T(InstZext);
|
||||
DEF_PTR_T(InstBranch);
|
||||
DEF_PTR_T(InstReturn);
|
||||
DEF_PTR_T(InstCall);
|
||||
DEF_PTR_T(InstGEP);
|
||||
DEF_PTR_T(InstPhi);
|
||||
|
||||
enum class InstTag {
|
||||
Add,
|
||||
Sub,
|
||||
@ -41,6 +53,7 @@ public:
|
||||
int ir_seqno = -1;
|
||||
InstTag tag;
|
||||
BasicBlockPtr_t parent_bb;
|
||||
// decltype(parent_bb->inst_list.begin()) inst_itr_in_parent;
|
||||
Instruction(InstTag inst_tag, TypePtr_t type, BasicBlockPtr_t parent_bb)
|
||||
: User("", type), tag(inst_tag), parent_bb(parent_bb) {}
|
||||
virtual std::string to_string() override {
|
||||
@ -235,4 +248,28 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class InstPhi : public Instruction {
|
||||
public:
|
||||
InstPhi(TypePtr_t type, const decltype(Function::bb_list) &incoming_vals, BasicBlockPtr_t parent_bb)
|
||||
: Instruction(InstTag::Phi, type, parent_bb) {
|
||||
for (auto incoming : incoming_vals) {
|
||||
Add_Operand(incoming);
|
||||
}
|
||||
}
|
||||
void set_incoming_val(unsigned index, ValuePtr_t val) {
|
||||
auto old_op = operand_list[index];
|
||||
operand_list[index] = val;
|
||||
if (val) val->use_list.push_back({val, this, index});
|
||||
if (old_op) {
|
||||
if (std::find(operand_list.begin(), operand_list.end(), old_op) == operand_list.end()) {
|
||||
old_op->u_remove_use(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual std::string to_IR_string() override {
|
||||
std::string str = type->to_IR_string() + " %" + std::to_string(ir_seqno);
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace antlrSysY
|
||||
@ -4,10 +4,17 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
class Type;
|
||||
typedef std::shared_ptr<Type> TypePtr_t;
|
||||
DEF_PTR_T(Type);
|
||||
DEF_PTR_T(IntegerType);
|
||||
DEF_PTR_T(VoidType);
|
||||
DEF_PTR_T(LabelType);
|
||||
DEF_PTR_T(ArrayType);
|
||||
DEF_PTR_T(PointerType);
|
||||
DEF_PTR_T(FunctionType);
|
||||
|
||||
class Type {
|
||||
public:
|
||||
@ -139,6 +146,12 @@ public:
|
||||
virtual std::string to_IR_string() override {
|
||||
return pointed_type->to_IR_string() + "*";
|
||||
}
|
||||
|
||||
static TypePtr_t pointedType(TypePtr_t ptr) {
|
||||
assert(Type::isType<PointerType>(ptr));
|
||||
auto pointer_type = Type::asType<PointerType>(ptr);
|
||||
return pointer_type->pointed_type;
|
||||
}
|
||||
};
|
||||
|
||||
class FunctionType : public Type {
|
||||
|
||||
@ -8,29 +8,33 @@
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
class Value;
|
||||
class BasicBlock;
|
||||
class User;
|
||||
class Function;
|
||||
class Instruction;
|
||||
typedef std::shared_ptr<Value> ValuePtr_t;
|
||||
typedef std::shared_ptr<BasicBlock> BasicBlockPtr_t;
|
||||
typedef std::shared_ptr<Function> FunctionPtr_t;
|
||||
typedef std::shared_ptr<User> UserPtr_t;
|
||||
typedef std::shared_ptr<Instruction> InstructionPtr_t;
|
||||
typedef std::tuple<ValuePtr_t, UserPtr_t, int> UseEdge_t;
|
||||
DEF_PTR_T(Value);
|
||||
DEF_PTR_T(BasicBlock);
|
||||
DEF_PTR_T(User);
|
||||
DEF_PTR_T(Function);
|
||||
DEF_PTR_T(Instruction);
|
||||
DEF_PTR_T(Constant);
|
||||
DEF_PTR_T(ConstantInt);
|
||||
|
||||
// typedef std::tuple<ValuePtr_t, UserPtr_t, int> UseEdge_t;
|
||||
struct Use {
|
||||
ValuePtr_t value;
|
||||
User* user;
|
||||
unsigned op_index;
|
||||
};
|
||||
// Define, User, operand-index
|
||||
// for Instruction, inst `uses` op, so inst is the user
|
||||
|
||||
class Value {
|
||||
public:
|
||||
std::string name;
|
||||
TypePtr_t type;
|
||||
std::list<UseEdge_t> use_list;
|
||||
std::list<Use> use_list; // a list of use-edge from this value
|
||||
|
||||
Value(const std::string &name, TypePtr_t type) : name(name), type(type) {}
|
||||
virtual ~Value() = default;
|
||||
@ -60,8 +64,24 @@ public:
|
||||
virtual std::string to_IR_string() {
|
||||
panic("No applicable for IR gen");
|
||||
}
|
||||
};
|
||||
|
||||
void u_remove_use(User* user) {
|
||||
// use_list.erase(
|
||||
// std::remove_if(
|
||||
// use_list.begin(), use_list.end(), [user](const UseEdge_t &use) { return std::get<1>(use) == user; }
|
||||
// ),
|
||||
// use_list.end()
|
||||
// );
|
||||
for (auto itr = use_list.begin(); itr != use_list.end();) {
|
||||
if (itr->user == user) {
|
||||
itr = use_list.erase(itr);
|
||||
}
|
||||
else {
|
||||
++ itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class User : public Value {
|
||||
public:
|
||||
@ -69,14 +89,34 @@ public:
|
||||
|
||||
User(const std::string &name, TypePtr_t type) : Value(name, type) {}
|
||||
void Add_Operand(ValuePtr_t op) {
|
||||
op->use_list.push_back({op, this, (unsigned)operand_list.size()});
|
||||
operand_list.push_back(op);
|
||||
use_list.push_back({op, std::make_shared<User>(this), operand_list.size()});
|
||||
}
|
||||
// make anything that use this value use the new value
|
||||
void u_replace_users(ValuePtr_t value) {
|
||||
if (value == nullptr) {
|
||||
assert(!use_list.size() && "No one should use this");
|
||||
return;
|
||||
}
|
||||
for (auto use : use_list) {
|
||||
auto user = use.user;
|
||||
auto index = use.op_index;
|
||||
user->operand_list[index] = value;
|
||||
assert(value);
|
||||
value->use_list.push_back({value, user, index});
|
||||
}
|
||||
// all original uses are gone
|
||||
use_list.clear();
|
||||
}
|
||||
// remove this user from its operands
|
||||
void u_remove_from_usees() {
|
||||
for (auto op : operand_list) {
|
||||
assert(op);
|
||||
op->u_remove_use(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BasicBlock;
|
||||
class Instruction;
|
||||
|
||||
class FParam : public Value {
|
||||
public:
|
||||
int ir_seqno = -1;
|
||||
@ -115,18 +155,20 @@ public:
|
||||
class BasicBlock : public Value {
|
||||
public:
|
||||
int ir_seqno = -1;
|
||||
std::vector<std::shared_ptr<Instruction>> inst_list;
|
||||
std::list<InstructionPtr_t> inst_list;
|
||||
std::shared_ptr<Function> parent;
|
||||
BasicBlockListNode_t itr;
|
||||
std::list<std::shared_ptr<BasicBlock>> successors;
|
||||
std::list<std::shared_ptr<BasicBlock>> predecessors;
|
||||
std::list<BasicBlockPtr_t> successors;
|
||||
std::list<BasicBlockPtr_t> predecessors;
|
||||
|
||||
BasicBlockPtr_t idomer;
|
||||
std::list<BasicBlockPtr_t> idomee_list;
|
||||
std::list<BasicBlockPtr_t> domer_list;
|
||||
BasicBlockPtr_t idomer; // dominating node
|
||||
std::list<BasicBlockPtr_t> idom_list; // immediate dominated nodes
|
||||
std::list<BasicBlockPtr_t> dom_list; // dominated nodes
|
||||
std::list<BasicBlockPtr_t> dom_frontier;
|
||||
int dom_level;
|
||||
int _dom_helper_index;
|
||||
int dom_dfs_in;
|
||||
int dom_dfs_out;
|
||||
|
||||
BasicBlock(const std::string &name, std::shared_ptr<Function> parent) : Value(name, TypeHelper::TYPE_LABEL) {
|
||||
this->parent = parent;
|
||||
|
||||
@ -12,7 +12,7 @@ public:
|
||||
virtual void run(const Module &module) = 0;
|
||||
};
|
||||
|
||||
class PassMem2Reg : public Pass {
|
||||
class PassMem2Reg : public Pass {
|
||||
public:
|
||||
PassMem2Reg() : Pass("mem2reg") {}
|
||||
virtual void run(const Module &module) override;
|
||||
|
||||
@ -81,6 +81,8 @@ std::shared_ptr<InstGEP> build_InstGEP(
|
||||
BasicBlockPtr_t parent_bb
|
||||
);
|
||||
|
||||
InstPhiPtr_t build_InstPhi(TypePtr_t type, const decltype(Function::bb_list) &incoming_vals, BasicBlockPtr_t parent_bb);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
class Visitor : public antlrSysY::SysyBaseVisitor {
|
||||
|
||||
137
src/algo_dominance.cpp
Normal file
137
src/algo_dominance.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
#include "llir.h"
|
||||
#include "visitor.h"
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
static void _bitwise_and(std::vector<bool> &op1, const std::vector<bool> &op2) {
|
||||
for (int i = 0; i < op1.size(); ++i) {
|
||||
op1[i] = op1[i] & op2[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void _bitwise_set(std::vector<bool> &op1, int l, int r, bool val) {
|
||||
for (int i = l; i < r; ++i) {
|
||||
op1[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void _gen_dom_level(BasicBlockPtr_t bb, int level) {
|
||||
bb->dom_level = level;
|
||||
for (auto succ : bb->idom_list) {
|
||||
_gen_dom_level(succ, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_dominance(FunctionPtr_t func) {
|
||||
// 编译器设计 2E 352 | Engineering A Compiler P479
|
||||
// Note: n \in Dom(n)
|
||||
// Basic iterative idea: Dom(n) = {n} union (intersect Dom(pred(n)))
|
||||
std::vector<BasicBlockPtr_t> bb_list;
|
||||
const int N = func->bb_list.size();
|
||||
auto itr = func->bb_list.begin();
|
||||
for (auto basicblock : func->bb_list) {
|
||||
basicblock->idom_list.clear();
|
||||
basicblock->dom_list.clear();
|
||||
basicblock->_dom_helper_index = bb_list.size();
|
||||
bb_list.push_back(basicblock);
|
||||
}
|
||||
std::vector<std::vector<bool>> dom(N);
|
||||
dom[0].resize(N);
|
||||
dom[0][0] = 1; // Dom(0) = {0}
|
||||
// Dom(i) <- N (= {0, 1, 2,...})
|
||||
for (int i = 1; i < N; ++i) {
|
||||
dom[i].resize(N, 1);
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
int i = 0;
|
||||
for (int i = 1; i < N; ++i) {
|
||||
auto cur_bb = bb_list[i];
|
||||
std::vector<bool> temp(true, N);
|
||||
// temp = {i} union (intersect Dom(j)), j in pred(i)
|
||||
for (auto pred : cur_bb->predecessors) {
|
||||
_bitwise_and(temp, dom[pred->_dom_helper_index]);
|
||||
}
|
||||
temp[i] = true;
|
||||
// if temp != Dom(i)
|
||||
if (temp != dom[i]) {
|
||||
dom[i] = temp; // Dom(i) <- temp
|
||||
changed = true; // changed <- true
|
||||
}
|
||||
}
|
||||
}
|
||||
// set each basicblock's domer
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (int j = 0; j < N; ++j) {
|
||||
if (dom[i][j]) {
|
||||
bb_list[i]->dom_list.push_back(bb_list[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// get domees and immediate domer
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (auto domer1 : bb_list[i]->dom_list) {
|
||||
if (domer1 == bb_list[i]) continue;
|
||||
bool flag = true;
|
||||
// if dom(i)[j] dom dom(i)[k], it cannot be the immediate domer of i
|
||||
for (auto domer2 : bb_list[i]->dom_list) {
|
||||
if (domer2 == bb_list[i] || domer2 == domer1) continue;
|
||||
if (std::find(domer2->dom_list.begin(), domer2->dom_list.end(), domer1) != domer2->dom_list.end()) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
bb_list[i]->idomer = domer1;
|
||||
domer1->idom_list.push_back(bb_list[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_gen_dom_level(bb_list[0], 0);
|
||||
}
|
||||
|
||||
// DF is defined as:
|
||||
// DF(n) = {m | (Exists q where n Dom q && q Pred m) && !(n SDom m)}
|
||||
void gen_dominance_frontier(FunctionPtr_t func) {
|
||||
// 编译器设计 2E 368
|
||||
/*
|
||||
for all node in CFG:
|
||||
DF(n) <- Empty
|
||||
for all node in CFG:
|
||||
if n has multiple pred:
|
||||
foreach pred of n:
|
||||
runner <- p
|
||||
while runner != idom(n):
|
||||
DF(runner) <- DF(runner) union n
|
||||
runner <- IDOM(runner)
|
||||
*/
|
||||
for (auto bb : func->bb_list) {
|
||||
bb->dom_frontier.clear();
|
||||
}
|
||||
for (auto n : func->bb_list) {
|
||||
if (n->predecessors.size() >= 2) {
|
||||
for (auto pred : n->predecessors) {
|
||||
auto runner = pred;
|
||||
while (runner != n->idomer) {
|
||||
runner->dom_frontier.push_back(n);
|
||||
runner = runner->idomer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_dfs_numbers(BasicBlockPtr_t bb, bool rst) {
|
||||
static int dfs_num;
|
||||
if (rst) dfs_num = 0;
|
||||
bb->dom_dfs_in = dfs_num++;
|
||||
for (auto child : bb->dom_list) {
|
||||
update_dfs_numbers(child, false);
|
||||
}
|
||||
bb->dom_dfs_out = dfs_num++;
|
||||
}
|
||||
|
||||
} // namespace antlrSysY
|
||||
@ -1,106 +0,0 @@
|
||||
#include "llir.h"
|
||||
#include "visitor.h"
|
||||
#include <bitset>
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
static void _bitwise_and(std::vector<bool>& op1, const std::vector<bool>& op2) {
|
||||
for (int i = 0; i < op1.size(); ++ i) {
|
||||
op1[i] = op1[i] & op2[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void _bitwise_set(std::vector<bool>& op1, int l, int r,bool val) {
|
||||
for (int i = l; i < r; ++ i) {
|
||||
op1[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void _gen_dom_level(BasicBlockPtr_t bb, int level) {
|
||||
bb->dom_level = level;
|
||||
for (auto succ : bb->idomee_list) {
|
||||
_gen_dom_level(succ, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_dominance(FunctionPtr_t func) {
|
||||
// 编译器设计 2E 352 | Engineering A Compiler P479
|
||||
// Note: n \in Dom(n)
|
||||
// Basic iterative idea: Dom(n) = {n} union (intersect Dom(pred(n)))
|
||||
std::vector<std::vector<bool>> domers;
|
||||
std::vector<BasicBlockPtr_t> bb_list;
|
||||
const int N = func->bb_list.size();
|
||||
auto itr = func->bb_list.begin();
|
||||
for (auto basicblock : func->bb_list) {
|
||||
basicblock->idomee_list.clear();
|
||||
basicblock->domer_list.clear();
|
||||
domers.push_back({});
|
||||
domers.back().resize(N, true); // Dom(i) <- N
|
||||
basicblock->_dom_helper_index = bb_list.size();
|
||||
bb_list.push_back(basicblock);
|
||||
}
|
||||
_bitwise_set(domers[0], 1, N, false); // Dom(0) <- {0}
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
int i = 0;
|
||||
for (int i = 1; i < N; ++ i) {
|
||||
auto cur_bb = bb_list[i];
|
||||
std::vector<bool> temp(true, N);
|
||||
// temp = {i} union (intersect Dom(j)), j in pred(i)
|
||||
for (auto pred : cur_bb->predecessors) {
|
||||
_bitwise_and(temp, domers[pred->_dom_helper_index]);
|
||||
}
|
||||
temp[i] = true;
|
||||
// if temp != Dom(i)
|
||||
if (!(temp == domers[i])) {
|
||||
domers[i] = temp; // Dom(i) <- temp
|
||||
changed = true; // changed <- true
|
||||
}
|
||||
}
|
||||
}
|
||||
// set each basicblock's domer
|
||||
for (int i = 0; i < N; ++ i) {
|
||||
for (int j = 0; j < N; ++ j) {
|
||||
if (domers[i][j]) {
|
||||
bb_list[i]->domer_list.push_back(bb_list[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// get domees and immediate domer
|
||||
for (int i = 0; i < N; ++ i) {
|
||||
for (auto domer1 : bb_list[i]->domer_list) {
|
||||
if (domer1 == bb_list[i])
|
||||
continue;
|
||||
bool flag = true;
|
||||
// if dom(i)[j] dom dom(i)[k], it cannot be the immediate domer of i
|
||||
for (auto domer2 : bb_list[i]->domer_list) {
|
||||
if (domer2 == bb_list[i] || domer2 == domer1)
|
||||
continue;
|
||||
if (std::find(domer2->domer_list.begin(), domer2->domer_list.end(), domer1) != domer2->domer_list.end()) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
bb_list[i]->idomer = domer1;
|
||||
domer1->idomee_list.push_back(bb_list[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_gen_dom_level(bb_list[0], 0);
|
||||
}
|
||||
|
||||
void gen_dominance_frontier(FunctionPtr_t func) {
|
||||
// 编译器设计 2E 368
|
||||
// for all node in CFG: DF(n) <- Empty
|
||||
for (auto bb : func->bb_list) {
|
||||
bb->dom_frontier.clear();
|
||||
}
|
||||
// for all node in CFG:
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
17
src/main.cpp
17
src/main.cpp
@ -66,6 +66,7 @@ int main(int argc, const char **argv) {
|
||||
defaultConf.set(el::Level::Warning, el::ConfigurationType::Format, "%level %loc %msg");
|
||||
defaultConf.set(el::Level::Error, el::ConfigurationType::Format, "%level %loc %msg");
|
||||
defaultConf.set(el::Level::Info, el::ConfigurationType::Format, "%level %loc %msg");
|
||||
defaultConf.set(el::Level::Verbose, el::ConfigurationType::Format, "%level %loc %msg");
|
||||
el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput);
|
||||
el::Loggers::reconfigureLogger("default", defaultConf);
|
||||
#pragma endregion
|
||||
@ -90,6 +91,16 @@ int main(int argc, const char **argv) {
|
||||
auto tree = parser.program();
|
||||
Visitor visitor(lexer);
|
||||
visitor.visitProgram(tree);
|
||||
|
||||
std::vector<std::shared_ptr<Pass>> passes = {
|
||||
std::make_shared<PassBuildCFG>(),
|
||||
std::make_shared<PassMem2Reg>(),
|
||||
};
|
||||
|
||||
for (auto pass : passes) {
|
||||
pass->run(visitor.module);
|
||||
}
|
||||
|
||||
if (emit_llvm) {
|
||||
auto llir_file = output_file.substr(0, output_file.rfind(".")) + ".ll";
|
||||
std::ofstream ofs_llir_file(llir_file);
|
||||
@ -100,12 +111,6 @@ int main(int argc, const char **argv) {
|
||||
visitor.llir_gen(ofs_llir_file);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Pass>> passes = {std::make_shared<PassBuildCFG>()};
|
||||
|
||||
for (auto pass : passes) {
|
||||
pass->run(visitor.module);
|
||||
}
|
||||
|
||||
// std::cout << tree->toStringTree(&parser) << std::endl << std::endl;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1,14 +1,293 @@
|
||||
#include "passes.h"
|
||||
#include "llir.h"
|
||||
#include "3rdparty/easylogging++.h"
|
||||
#include "algos.h"
|
||||
#include "common.h"
|
||||
#include "llir.h"
|
||||
#include "passes.h"
|
||||
#include "visitor.h"
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace antlrSysY {
|
||||
|
||||
void PassMem2Reg::run(const Module& module) {
|
||||
for (auto func : module.function_list) {
|
||||
if (func->is_libfunc()) continue;
|
||||
|
||||
static bool is_alloca_promotable(InstAllocaPtr_t inst) {
|
||||
for (const auto use : inst->use_list) {
|
||||
auto user = use.user;
|
||||
if (dynamic_cast<InstLoad*>(user)) {
|
||||
const auto li = dynamic_cast<InstLoad*>(user);
|
||||
if (li->type != inst->type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<InstStore*>(user)) {
|
||||
const auto si = dynamic_cast<InstStore*>(user);
|
||||
if (si->operand_list[1] == inst || si->type != inst->type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<InstGEP*>(user)) {
|
||||
const auto gep = dynamic_cast<InstGEP*>(user);
|
||||
for (int i = 1; i < gep->operand_list.size(); ++i) {
|
||||
if (!Value::is<ConstantInt>(gep->operand_list[i]) || !Value::as<ConstantInt>(gep->operand_list[i])->value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct AllocaInfo {
|
||||
std::vector<BasicBlockPtr_t> def_blocks = {};
|
||||
std::vector<BasicBlockPtr_t> use_blocks = {};
|
||||
bool only_in_1_block = true;
|
||||
BasicBlockPtr_t only_block = nullptr;
|
||||
InstStore* only_store = nullptr;
|
||||
};
|
||||
|
||||
static void analyze_alloca(InstAllocaPtr_t ai, AllocaInfo &info) {
|
||||
for (auto use : ai->use_list) {
|
||||
auto user = dynamic_cast<Instruction*>(use.user);
|
||||
if (dynamic_cast<InstStore*>(user)) {
|
||||
const auto si = dynamic_cast<InstStore*>(user);
|
||||
info.def_blocks.push_back(si->parent_bb);
|
||||
info.only_store = si;
|
||||
}
|
||||
else {
|
||||
assert(dynamic_cast<InstLoad*>(user));
|
||||
const auto li = dynamic_cast<InstLoad*>(user);
|
||||
info.use_blocks.push_back(li->parent_bb);
|
||||
}
|
||||
if (info.only_in_1_block) {
|
||||
if (!info.only_block)
|
||||
info.only_block = user->parent_bb;
|
||||
else if (info.only_block != user->parent_bb)
|
||||
info.only_in_1_block = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// static bool rewrite_single_store(InstAllocaPtr_t ai, AllocaInfo& alloca_info) {
|
||||
|
||||
// }
|
||||
|
||||
// live in analysis
|
||||
static void live_in_blocks(
|
||||
InstAllocaPtr_t ai,
|
||||
AllocaInfo &alloca_info,
|
||||
const std::unordered_set<BasicBlockPtr_t> &def_blocks,
|
||||
std::unordered_set<BasicBlockPtr_t> &livein_blocks
|
||||
) {
|
||||
std::vector<BasicBlockPtr_t> worklist(alloca_info.use_blocks.begin(), alloca_info.use_blocks.end());
|
||||
// for each block, compute whether alloca live in the block
|
||||
// 检查每个block,如果store before load,就说明dead;如果load before store,说明live,因为被用到了
|
||||
for (int i = 0; i < worklist.size(); ++i) {
|
||||
auto bb = worklist[i];
|
||||
if (def_blocks.count(bb) == 0) continue;
|
||||
for (auto itr : bb->inst_list) {
|
||||
// a store to this alloca(variable) before a load, it is dead in this block
|
||||
if (Value::is<InstStore>(itr) && Value::as<InstStore>(itr)->operand_list[1] == ai) {
|
||||
worklist[i] = worklist.back();
|
||||
worklist.pop_back();
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
else if (Value::is<InstLoad>(itr)) {
|
||||
// a load before store, it live in this block
|
||||
if (Value::as<InstLoad>(itr)->operand_list[1] == ai) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// add predecessors to get the full region where the var is live
|
||||
// 好像cs143里面就有讲过这个东西,liveness分析好像是从后往前推的
|
||||
while (!worklist.empty()) {
|
||||
auto bb = worklist.back();
|
||||
worklist.pop_back();
|
||||
if (!livein_blocks.insert(bb).second) continue; // already done
|
||||
// 如果alloca没有在某个块中被定义,那么它一定是从前面的某个块里面live in过来的
|
||||
// 所以把它也塞进队列进行处理
|
||||
for (auto pred : bb->predecessors) {
|
||||
if (def_blocks.count(pred)) continue;
|
||||
worklist.push_back(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RenameInfo {
|
||||
BasicBlockPtr_t bb;
|
||||
BasicBlockPtr_t pred;
|
||||
std::vector<ValuePtr_t> value_list;
|
||||
};
|
||||
|
||||
// llvm:PromoteMemoryToRegister.cpp
|
||||
// https://roife.github.io/2022/02/07/mem2reg/
|
||||
// https://github.com/Enna1/LLVM-Study-Notes/blob/master/source/ssa/SSA-Construction.rst
|
||||
static void _mem_2_reg(FunctionPtr_t func) {
|
||||
VLOG(4) << " Gen Dominance Tree & Frontier";
|
||||
gen_dominance(func);
|
||||
gen_dominance_frontier(func);
|
||||
// actually, all variable alloca is placed at block head, so we collect them first
|
||||
std::vector<InstAllocaPtr_t> alloca_list;
|
||||
std::unordered_map<InstAllocaPtr_t, int> alloca_to_id;
|
||||
std::unordered_map<BasicBlockPtr_t, int> bb_to_id;
|
||||
std::unordered_map<InstPhiPtr_t, int> phi_to_allocaid;
|
||||
|
||||
std::queue<BasicBlockPtr_t> worklist;
|
||||
|
||||
for (auto bb : func->bb_list) {
|
||||
for (auto inst : bb->inst_list) {
|
||||
if (Value::is<InstAlloca>(inst)) {
|
||||
assert(inst->parent_bb == bb);
|
||||
assert(bb == func->bb_list.front() && "Alloca should be at front of a func");
|
||||
auto ai = Value::as<InstAlloca>(inst);
|
||||
// assert(is_alloca_promotable(ai) && "Invalid alloca");
|
||||
if (!Type::isType<IntegerType>(PointerType::pointedType(ai->type)))
|
||||
continue;
|
||||
alloca_list.push_back(ai);
|
||||
}
|
||||
}
|
||||
}
|
||||
VLOG(4) << " alloca pruning & phi insertion";
|
||||
for (unsigned i = 0; i != alloca_list.size(); ++i) {
|
||||
auto ai = alloca_list[i];
|
||||
// remove empty use
|
||||
if (ai->use_list.empty()) {
|
||||
ai->parent_bb->inst_list.remove(ai);
|
||||
alloca_list[i] = alloca_list.back();
|
||||
alloca_list.pop_back();
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
AllocaInfo alloca_info;
|
||||
analyze_alloca(ai, alloca_info);
|
||||
if (alloca_info.def_blocks.size() == 1) {
|
||||
LOG(WARNING) << "To rewrite single store";
|
||||
}
|
||||
if (alloca_info.only_in_1_block) {
|
||||
LOG(WARNING) << "To promote single block alloca";
|
||||
}
|
||||
// numbering bb
|
||||
if (bb_to_id.empty()) {
|
||||
int id = 0;
|
||||
for (auto bb : func->bb_list) {
|
||||
bb_to_id[bb] = id++;
|
||||
}
|
||||
}
|
||||
alloca_to_id[alloca_list[i]] = i;
|
||||
std::unordered_set<BasicBlockPtr_t> def_blocks(alloca_info.def_blocks.begin(), alloca_info.def_blocks.end());
|
||||
std::unordered_set<BasicBlockPtr_t> livein_blocks;
|
||||
live_in_blocks(ai, alloca_info, def_blocks, livein_blocks);
|
||||
// llvm use IDF to calculate phi blocks.
|
||||
// But that is too complicated
|
||||
// SSA book Algo 3.1
|
||||
std::vector<bool> visited(func->bb_list.size(), false);
|
||||
for (auto bb : def_blocks) {
|
||||
worklist.push(bb);
|
||||
}
|
||||
while (!worklist.empty()) {
|
||||
auto bb = worklist.front();
|
||||
worklist.pop();
|
||||
for (auto frontier : bb->dom_frontier) {
|
||||
auto frontier_index = bb_to_id.at(frontier);
|
||||
if (!visited[frontier_index]) {
|
||||
visited[frontier_index] = true;
|
||||
if (livein_blocks.count(frontier)) {
|
||||
auto inst_phi = build_InstPhi(TypeHelper::TYPE_I32, bb->predecessors, bb);
|
||||
phi_to_allocaid.insert({inst_phi, i});
|
||||
}
|
||||
if (!def_blocks.count(frontier)) {
|
||||
worklist.push(frontier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alloca_list.empty()) return;
|
||||
// renaming
|
||||
VLOG(4) << " variable renaming";
|
||||
std::vector<ValuePtr_t> _init_values;
|
||||
for (int i = 0; i < alloca_list.size(); ++ i)
|
||||
_init_values.push_back(ConstantInt::make_shared(0));
|
||||
std::vector<RenameInfo> rename_list = {{func->bb_list.front(), nullptr, _init_values}};
|
||||
std::vector<bool> visited(bb_to_id.size(), 0);
|
||||
while (!rename_list.empty()) {
|
||||
auto rename_info = rename_list.back();
|
||||
rename_list.pop_back();
|
||||
// replace block with more specific alloca
|
||||
for (auto inst : rename_info.bb->inst_list) {
|
||||
// phi only appear at block head
|
||||
if (!Value::is<InstPhi>(inst)) break;
|
||||
auto phi = Value::as<InstPhi>(inst);
|
||||
auto alloca_index = phi_to_allocaid.at(phi);
|
||||
int pred_index = -1;
|
||||
for (auto pred : rename_info.bb->predecessors) {
|
||||
pred_index++;
|
||||
if (pred == rename_info.pred) break;
|
||||
}
|
||||
phi->set_incoming_val(pred_index, rename_info.value_list[alloca_index]);
|
||||
}
|
||||
// already processed, skip
|
||||
if (visited[bb_to_id.at(rename_info.bb)]) continue;
|
||||
visited[bb_to_id.at(rename_info.bb)] = true;
|
||||
// process instruction
|
||||
for (auto itr = rename_info.bb->inst_list.begin(); itr != rename_info.bb->inst_list.end();) {
|
||||
auto inst = *itr++;
|
||||
if (Value::is<InstAlloca>(inst)) {
|
||||
assert(alloca_to_id.count(Value::as<InstAlloca>(inst)));
|
||||
rename_info.bb->inst_list.remove(inst);
|
||||
}
|
||||
else if (Value::is<InstLoad>(inst)) {
|
||||
auto li = Value::as<InstLoad>(inst);
|
||||
if (!(Value::is<InstAlloca>(li->operand_list[0]))) {
|
||||
continue;
|
||||
}
|
||||
auto ai = Value::as<InstAlloca>(li->operand_list[0]);
|
||||
if (!Type::isType<IntegerType>(PointerType::pointedType(ai->type))) {
|
||||
continue;
|
||||
}
|
||||
int alloca_index = alloca_to_id.at(ai);
|
||||
rename_info.bb->inst_list.remove(inst);
|
||||
li->u_replace_users(rename_info.value_list[alloca_index]);
|
||||
inst->u_remove_from_usees();
|
||||
}
|
||||
else if (Value::is<InstStore>(inst)) {
|
||||
auto si = Value::as<InstStore>(inst);
|
||||
if (!(Value::is<InstAlloca>(si->operand_list[1]))) {
|
||||
continue;
|
||||
}
|
||||
auto ai = Value::as<InstAlloca>(si->operand_list[1]);
|
||||
if (!Type::isType<IntegerType>(PointerType::pointedType(ai->type))) {
|
||||
continue;
|
||||
}
|
||||
int alloca_index = alloca_to_id.at(ai);
|
||||
rename_info.value_list[alloca_index] = si->operand_list[0];
|
||||
inst->u_remove_from_usees();
|
||||
// I dont think anyone will use a store?
|
||||
si->u_replace_users(nullptr);
|
||||
rename_info.bb->inst_list.remove(inst);
|
||||
}
|
||||
else if (Value::is<InstPhi>(inst)) {
|
||||
auto phi = Value::as<InstPhi>(inst);
|
||||
int alloca_index = phi_to_allocaid.at(phi);
|
||||
rename_info.value_list[alloca_index] = phi;
|
||||
}
|
||||
}
|
||||
for (auto succ : rename_info.bb->successors) {
|
||||
rename_list.push_back({succ, rename_info.bb, rename_info.value_list});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PassMem2Reg::run(const Module &module) {
|
||||
LOG(INFO) << "Run pass " << pass_name;
|
||||
for (auto func : module.function_list) {
|
||||
if (func->is_libfunc()) continue;
|
||||
_mem_2_reg(func);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace antlrSysY
|
||||
@ -139,4 +139,14 @@ std::shared_ptr<InstGEP> build_InstGEP(
|
||||
return inst;
|
||||
}
|
||||
|
||||
InstPhiPtr_t build_InstPhi(
|
||||
TypePtr_t type,
|
||||
const decltype(Function::bb_list) &incoming_vals,
|
||||
BasicBlockPtr_t parent_bb
|
||||
) {
|
||||
auto inst = std::make_shared<InstPhi>(type, incoming_vals, parent_bb);
|
||||
parent_bb->inst_list.insert(parent_bb->inst_list.begin(), inst);
|
||||
return inst;
|
||||
}
|
||||
|
||||
} // namespace antlrSysY
|
||||
@ -107,8 +107,7 @@ static void _gen_blocks(std::ostream &ostr, const std::list<BasicBlockPtr_t> &bl
|
||||
auto block = *block_itr;
|
||||
sysy_assert(block->ir_seqno == -1); // multi-alloc is error
|
||||
block->ir_seqno = reg_count++;
|
||||
for (int j = 0; j < block->inst_list.size(); ++j) {
|
||||
auto inst = block->inst_list[j];
|
||||
for (auto inst : block->inst_list) {
|
||||
sysy_assert(inst->ir_seqno == -1); // multi-alloc is error
|
||||
switch (inst->tag) {
|
||||
// These are not to get a seqno
|
||||
@ -138,6 +137,7 @@ static void _gen_blocks(std::ostream &ostr, const std::list<BasicBlockPtr_t> &bl
|
||||
case InstTag::Load:
|
||||
case InstTag::GEP:
|
||||
case InstTag::Alloca:
|
||||
case InstTag::Phi:
|
||||
case InstTag::Zext: inst->ir_seqno = reg_count++; break;
|
||||
// These should not be generated in frontend stage
|
||||
default: panic("Unexpected instruction");
|
||||
@ -150,9 +150,8 @@ static void _gen_blocks(std::ostream &ostr, const std::list<BasicBlockPtr_t> &bl
|
||||
if (block_itr != block_list.begin()) {
|
||||
ostr << block->ir_seqno << ":" << std::endl;
|
||||
}
|
||||
for (int j = 0; j < block->inst_list.size(); ++j) {
|
||||
for (auto _inst : block->inst_list) {
|
||||
ostr << " ";
|
||||
auto _inst = block->inst_list[j];
|
||||
VLOG(5) << "Build inst" << _inst->ir_seqno << ": " << _inst->to_string();
|
||||
switch (_inst->tag) {
|
||||
case InstTag::Br: {
|
||||
@ -351,13 +350,13 @@ static void _gen_blocks(std::ostream &ostr, const std::list<BasicBlockPtr_t> &bl
|
||||
assert(Type::isType<PointerType>(inst->operand_list[0]->type));
|
||||
ostr << "%" << inst->ir_seqno << " = load " << inst->type->to_IR_string() << ", ";
|
||||
if (Value::is<GlobalVar>(inst->operand_list[0])) {
|
||||
auto op1 = Value::as<GlobalVar>(inst->operand_list[0]);
|
||||
ostr << op1->type->to_IR_string() << " @" << op1->name;
|
||||
auto op = Value::as<GlobalVar>(inst->operand_list[0]);
|
||||
ostr << op->type->to_IR_string() << " @" << op->name;
|
||||
}
|
||||
else if (Value::is<Instruction>(inst->operand_list[0])) {
|
||||
auto op1 = Value::as<Instruction>(inst->operand_list[0]);
|
||||
assert(op1->ir_seqno >= 0);
|
||||
ostr << op1->type->to_IR_string() << " %" << op1->ir_seqno;
|
||||
auto op = Value::as<Instruction>(inst->operand_list[0]);
|
||||
assert(op->ir_seqno >= 0);
|
||||
ostr << op->type->to_IR_string() << " %" << op->ir_seqno;
|
||||
}
|
||||
else if (Value::is<FParam>(inst->operand_list[0])) {
|
||||
auto op0 = Value::as<FParam>(inst->operand_list[0]);
|
||||
@ -428,6 +427,42 @@ static void _gen_blocks(std::ostream &ostr, const std::list<BasicBlockPtr_t> &bl
|
||||
ostr << "i32";
|
||||
break;
|
||||
}
|
||||
case InstTag::Phi: {
|
||||
auto inst = Value::as<InstPhi>(_inst);
|
||||
assert(inst->ir_seqno >= 0);
|
||||
ostr << "%" << inst->ir_seqno << " = phi " << inst->type->to_IR_string() << " ";
|
||||
for (int i = 0; i < inst->operand_list.size(); ++i) {
|
||||
auto op = inst->operand_list[i];
|
||||
ostr << "[";
|
||||
if (Value::is<GlobalVar>(op)) {
|
||||
auto op0 = Value::as<GlobalVar>(op);
|
||||
ostr << "@" << op0->name;
|
||||
}
|
||||
else if (Value::is<Instruction>(op)) {
|
||||
auto op0 = Value::as<Instruction>(op);
|
||||
ostr << "%" << op0->ir_seqno;
|
||||
}
|
||||
else if (Value::is<FParam>(op)) {
|
||||
auto op0 = Value::as<FParam>(op);
|
||||
ostr << "%" << op0->ir_seqno;
|
||||
}
|
||||
else if (Value::is<ConstantInt>(op)) {
|
||||
auto op0 = Value::as<ConstantInt>(op);
|
||||
ostr << op0->value;
|
||||
}
|
||||
else {
|
||||
LOG(WARNING) << "Unexpected type of op: " << op->to_string();
|
||||
assert(0);
|
||||
}
|
||||
auto pred = inst->parent_bb->predecessors.begin();
|
||||
std::advance(pred, i);
|
||||
ostr << ", %" << (*pred)->ir_seqno << "]";
|
||||
if (i < inst->operand_list.size() - 1) {
|
||||
ostr << ", ";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// These should not be generated in frontend stage
|
||||
default: panic("Unexpected instruction");
|
||||
}
|
||||
@ -439,7 +474,7 @@ static void _gen_blocks(std::ostream &ostr, const std::list<BasicBlockPtr_t> &bl
|
||||
void Visitor::llir_gen(std::ostream &ostr) {
|
||||
#pragma region GenLibFuncDecl
|
||||
for (auto &lib_func_name : libfunc_list) {
|
||||
LOG(DEBUG) << "Gen LibFunc " << lib_func_name;
|
||||
VLOG(6) << "Gen LibFunc " << lib_func_name;
|
||||
auto lib_func = _func_tab.get_name(lib_func_name).value();
|
||||
auto lib_func_type = std::dynamic_pointer_cast<FunctionType>(lib_func->type);
|
||||
ostr << "declare"
|
||||
@ -458,7 +493,7 @@ void Visitor::llir_gen(std::ostream &ostr) {
|
||||
#pragma region GenGlobDecl
|
||||
for (auto &global_var : module.global_var_list) {
|
||||
// both int and arr have the same leading part
|
||||
LOG(DEBUG) << "Gen Global " << global_var->name;
|
||||
VLOG(5) << "Gen Global " << global_var->name;
|
||||
ostr << "@" << global_var->name << " = "
|
||||
<< "dso_local"
|
||||
<< " " << (global_var->is_const ? "constant" : "global") << " ";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user