175 lines
4.1 KiB
C++
175 lines
4.1 KiB
C++
#include "Function.h"
|
|
#include "decompiler/IR/IR.h"
|
|
|
|
namespace decompiler {
|
|
namespace {
|
|
bool in_set(RegSet& set, const Register& obj) {
|
|
return set.find(obj) != set.end();
|
|
}
|
|
|
|
void phase1(Function& f, BasicBlock& block) {
|
|
for (int i = block.end_basic_op; i-- > block.start_basic_op;) {
|
|
auto& instr = f.basic_ops.at(i);
|
|
auto& lv = block.live.at(i - block.start_basic_op);
|
|
auto& dd = block.dead.at(i - block.start_basic_op);
|
|
|
|
// make all read live out
|
|
auto read = instr->read_regs;
|
|
lv.clear();
|
|
for (auto& x : read) {
|
|
lv.insert(x);
|
|
}
|
|
|
|
// kill things which are overwritten
|
|
dd.clear();
|
|
auto write = instr->write_regs;
|
|
for (auto& x : write) {
|
|
if (!in_set(lv, x)) {
|
|
dd.insert(x);
|
|
}
|
|
}
|
|
|
|
// b.use = i.liveout
|
|
RegSet use_old = block.use;
|
|
block.use.clear();
|
|
for (auto& x : lv) {
|
|
block.use.insert(x);
|
|
}
|
|
// | (bu.use & !i.dead)
|
|
for (auto& x : use_old) {
|
|
if (!in_set(dd, x)) {
|
|
block.use.insert(x);
|
|
}
|
|
}
|
|
|
|
// b.defs = i.dead
|
|
RegSet defs_old = block.defs;
|
|
block.defs.clear();
|
|
for (auto& x : dd) {
|
|
block.defs.insert(x);
|
|
}
|
|
// | b.defs & !i.lv
|
|
for (auto& x : defs_old) {
|
|
if (!in_set(lv, x)) {
|
|
block.defs.insert(x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool phase2(std::vector<BasicBlock>& blocks, BasicBlock& block) {
|
|
bool changed = false;
|
|
auto out = block.defs;
|
|
|
|
for (auto s : {block.succ_branch, block.succ_ft}) {
|
|
if (s == -1) {
|
|
continue;
|
|
}
|
|
for (auto in : blocks.at(s).input) {
|
|
out.insert(in);
|
|
}
|
|
}
|
|
|
|
RegSet in = block.use;
|
|
for (auto x : out) {
|
|
if (!in_set(block.defs, x)) {
|
|
in.insert(x);
|
|
}
|
|
}
|
|
|
|
if (in != block.input || out != block.output) {
|
|
changed = true;
|
|
block.input = in;
|
|
block.output = out;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
void phase3(std::vector<BasicBlock>& blocks, BasicBlock& block) {
|
|
RegSet live_local;
|
|
for (auto s : {block.succ_branch, block.succ_ft}) {
|
|
if (s == -1) {
|
|
continue;
|
|
}
|
|
for (auto i : blocks.at(s).input) {
|
|
live_local.insert(i);
|
|
}
|
|
}
|
|
|
|
for (int i = block.end_basic_op; i-- > block.start_basic_op;) {
|
|
auto& lv = block.live.at(i - block.start_basic_op);
|
|
auto& dd = block.dead.at(i - block.start_basic_op);
|
|
|
|
RegSet new_live = lv;
|
|
for (auto x : live_local) {
|
|
if (!in_set(dd, x)) {
|
|
new_live.insert(x);
|
|
}
|
|
}
|
|
lv = live_local;
|
|
live_local = new_live;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
/*!
|
|
* Analyze the function use of registers to determine which are live where.
|
|
*/
|
|
void Function::run_reg_usage() {
|
|
// phase 1
|
|
for (auto& block : basic_blocks) {
|
|
block.live.resize(block.basic_op_size());
|
|
block.dead.resize(block.basic_op_size());
|
|
phase1(*this, block);
|
|
}
|
|
|
|
// phase 2
|
|
bool changed = false;
|
|
do {
|
|
changed = false;
|
|
for (auto& block : basic_blocks) {
|
|
if (phase2(basic_blocks, block)) {
|
|
changed = true;
|
|
}
|
|
}
|
|
} while (changed);
|
|
|
|
// phase 3
|
|
for (auto& block : basic_blocks) {
|
|
phase3(basic_blocks, block);
|
|
}
|
|
|
|
// we want to know if an op "consumes" a register.
|
|
// this means that the value of the register coming in to the operation is:
|
|
// A. read by the operation.
|
|
// B. no longer read after the operation.
|
|
for (auto& block : basic_blocks) {
|
|
for (int i = block.start_basic_op; i < block.end_basic_op; i++) {
|
|
auto& op = basic_ops.at(i);
|
|
// look at each register that we read
|
|
for (auto reg : op->read_regs) {
|
|
if (!block.op_has_reg_live_out(i, reg)) {
|
|
// if the register is not live out, we definitely consume it.
|
|
op->consumed.insert(reg);
|
|
} else {
|
|
// it's live out... but it could be a new value.
|
|
for (auto wr : op->write_regs) {
|
|
if (wr == reg) {
|
|
op->consumed.insert(reg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto reg : op->write_regs) {
|
|
if (!block.op_has_reg_live_out(i, reg)) {
|
|
// we wrote it, but it is immediately dead. this is nice to know for things like
|
|
// "is this if/and/or expression used as a value?"
|
|
op->written_and_unused.insert(reg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // namespace decompiler
|