mirror of
https://github.com/open-goal/jak-project
synced 2026-08-16 13:17:34 -04:00
[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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user