[Decompiler] Expressions (Part 3) (#213)

* before inserting bonus instruction

* first part of refactor for return values

* find parent method working
This commit is contained in:
water111
2021-01-25 22:08:58 -05:00
committed by GitHub
parent 2f722e6379
commit b59e33c005
22 changed files with 782 additions and 81 deletions
+20 -3
View File
@@ -1407,15 +1407,32 @@ FunctionAtomicOps convert_function_to_atomic_ops(const Function& func,
FunctionAtomicOps result;
int last_op = 0;
for (const auto& block : func.basic_blocks) {
for (int i = 0; i < int(func.basic_blocks.size()); i++) {
const auto& block = func.basic_blocks.at(i);
// we should only consider the blocks which actually have instructions:
if (block.end_word > block.start_word) {
auto begin = func.instructions.begin() + block.start_word;
auto end = func.instructions.begin() + block.end_word;
last_op = convert_block_to_atomic_ops(block.start_word, begin, end, labels, &result);
if (i == int(func.basic_blocks.size()) - 1) {
// we're the last block. insert the function end op.
result.ops.push_back(std::make_unique<FunctionEndOp>(int(result.ops.size())));
result.ops.back()->update_register_info();
// add to block.
result.block_id_to_end_atomic_op.back()++;
}
} else {
result.block_id_to_first_atomic_op.push_back(last_op);
result.block_id_to_end_atomic_op.push_back(last_op);
if (i == int(func.basic_blocks.size()) - 1) {
// we're the last block. insert the function end op.
result.ops.push_back(std::make_unique<FunctionEndOp>(int(result.ops.size())));
result.ops.back()->update_register_info();
// add block (no longer a zero-size block)
result.block_id_to_first_atomic_op.push_back(last_op);
result.block_id_to_end_atomic_op.push_back(last_op + 1);
} else {
result.block_id_to_first_atomic_op.push_back(last_op);
result.block_id_to_end_atomic_op.push_back(last_op);
}
}
}
+7
View File
@@ -14,6 +14,13 @@ struct FunctionAtomicOps {
// the actual ops, store in the correct order
std::vector<std::unique_ptr<AtomicOp>> ops;
FunctionEndOp& end_op() const {
assert(!ops.empty());
auto end = dynamic_cast<FunctionEndOp*>(ops.back().get());
assert(end);
return *end;
}
// mappings from instructions to atomic ops and back
std::unordered_map<int, int> instruction_to_atomic_op;
std::unordered_map<int, int> atomic_op_to_instruction;
+34 -3
View File
@@ -5,6 +5,31 @@
#include "decompiler/util/DecompilerTypeSystem.h"
namespace decompiler {
void clean_up_ifs(Form* top_level_form) {
top_level_form->apply([&](FormElement* elt) {
auto as_cne = dynamic_cast<CondNoElseElement*>(elt);
if (!as_cne) {
return;
}
auto top_condition = as_cne->entries.front().condition;
if (!top_condition->is_single_element() && elt->parent_form) {
auto real_condition = top_condition->back();
top_condition->pop_back();
auto& parent_vector = elt->parent_form->elts();
// find us in the parent vector
auto me = std::find_if(parent_vector.begin(), parent_vector.end(),
[&](FormElement* x) { return x == elt; });
assert(me != parent_vector.end());
// now insert the fake condition
parent_vector.insert(me, top_condition->elts().begin(), top_condition->elts().end());
top_condition->elts() = {real_condition};
}
});
}
bool convert_to_expressions(Form* top_level_form,
FormPool& pool,
const Function& f,
@@ -40,9 +65,10 @@ bool convert_to_expressions(Form* top_level_form,
}
std::vector<FormElement*> new_entries;
if (f.type.last_arg() != TypeSpec("none")) {
auto v0 = Register(Reg::GPR, Reg::V0);
new_entries = stack.rewrite_to_get_reg(pool, v0, f.ir2.env);
auto reg_return_type = f.ir2.env.get_types_after_op(f.ir2.atomic_ops->ops.size() - 1).get(v0);
auto return_var = f.ir2.atomic_ops->end_op().return_var();
new_entries = stack.rewrite_to_get_var(pool, return_var, f.ir2.env);
auto reg_return_type =
f.ir2.env.get_types_after_op(f.ir2.atomic_ops->ops.size() - 1).get(return_var.reg());
if (!dts.ts.typecheck(f.type.last_arg(), reg_return_type.typespec(), "", false, false)) {
// we need to cast the final value.
auto to_cast = new_entries.back();
@@ -59,10 +85,15 @@ bool convert_to_expressions(Form* top_level_form,
for (auto x : new_entries) {
top_level_form->push_back(x);
}
// fix up stuff
clean_up_ifs(top_level_form);
} catch (std::exception& e) {
lg::warn("Expression building failed: {}", e.what());
return false;
}
return true;
}
} // namespace decompiler
+9 -34
View File
@@ -12,27 +12,14 @@ bool in_set(RegSet& set, const Register& obj) {
return set.find(obj) != set.end();
}
void phase1(const FunctionAtomicOps& ops,
int block_id,
RegUsageInfo* out,
bool insert_v0_read_instruction_at_end) {
void phase1(const FunctionAtomicOps& ops, int block_id, RegUsageInfo* out) {
int end_op = ops.block_id_to_end_atomic_op.at(block_id);
int start_op = ops.block_id_to_first_atomic_op.at(block_id);
int loop_end = end_op;
if (insert_v0_read_instruction_at_end) {
loop_end++;
}
for (int i = loop_end; i-- > start_op;) {
std::vector<Register> read;
std::vector<Register> write;
if (i == end_op) {
read = {Register(Reg::GPR, Reg::V0)};
} else {
const auto& instr = ops.ops.at(i);
read = instr->read_regs();
write = instr->write_regs();
}
for (int i = end_op; i-- > start_op;) {
const auto& instr = ops.ops.at(i);
auto read = instr->read_regs();
auto write = instr->write_regs();
auto& lv = out->op.at(i).live;
auto& dd = out->op.at(i).dead;
@@ -114,8 +101,7 @@ bool phase2(const std::vector<BasicBlock>& blocks, int block_id, RegUsageInfo* i
void phase3(const FunctionAtomicOps& ops,
const std::vector<BasicBlock>& blocks,
int block_id,
RegUsageInfo* info,
bool insert_v0_read_instruction_at_end) {
RegUsageInfo* info) {
RegSet live_local;
const auto& block_obj = blocks.at(block_id);
for (auto s : {block_obj.succ_branch, block_obj.succ_ft}) {
@@ -130,12 +116,7 @@ void phase3(const FunctionAtomicOps& ops,
int end_op = ops.block_id_to_end_atomic_op.at(block_id);
int start_op = ops.block_id_to_first_atomic_op.at(block_id);
int loop_end = end_op;
if (insert_v0_read_instruction_at_end) {
loop_end++;
}
for (int i = loop_end; i-- > start_op;) {
for (int i = end_op; i-- > start_op;) {
auto& lv = info->op.at(i).live;
auto& dd = info->op.at(i).dead;
@@ -149,12 +130,6 @@ void phase3(const FunctionAtomicOps& ops,
live_local = new_live;
}
}
bool should_insert_v0_read(const std::vector<BasicBlock>& blocks, const Function& function, int i) {
return i == int(blocks.size()) - 1 && function.type.arg_count() > 0 &&
function.type.last_arg() != TypeSpec("none");
}
} // namespace
RegUsageInfo analyze_ir2_register_usage(const Function& function) {
@@ -163,7 +138,7 @@ RegUsageInfo analyze_ir2_register_usage(const Function& function) {
RegUsageInfo result(blocks.size(), ops->ops.size() + 1);
for (int i = 0; i < int(blocks.size()); i++) {
phase1(*ops, i, &result, should_insert_v0_read(blocks, function, i));
phase1(*ops, i, &result);
}
bool changed = false;
@@ -177,7 +152,7 @@ RegUsageInfo analyze_ir2_register_usage(const Function& function) {
} while (changed);
for (int i = 0; i < int(blocks.size()); i++) {
phase3(*ops, blocks, i, &result, should_insert_v0_read(blocks, function, i));
phase3(*ops, blocks, i, &result);
}
// we want to know if an op "consumes" a register.