Make decompiler more successful (#66)

* w/early-return-and-break

* more edge cases

* all instructions in non asm functions now convert to ir

* cleanup

* implement rarely used control flow in IR

* clang format
This commit is contained in:
water111 2020-10-11 18:27:44 -04:00 committed by GitHub
parent 30e0e4204b
commit 1e1b5e7c00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 970 additions and 22 deletions

View file

@ -116,9 +116,10 @@ bool InstructionAtom::is_link_or_label() const {
} }
/*! /*!
* Convert entire instruction to a string. * Convert just the name of the opcode to a string, omitting src/dst, but including
* suffixes (interlock, broadcasts and destination)
*/ */
std::string Instruction::to_string(const LinkedObjectFile& file) const { std::string Instruction::op_name_to_string() const {
auto& info = gOpcodeInfo[(int)kind]; auto& info = gOpcodeInfo[(int)kind];
// the name // the name
@ -162,6 +163,15 @@ std::string Instruction::to_string(const LinkedObjectFile& file) const {
if (cop2_dest & 1) if (cop2_dest & 1)
result.push_back('w'); result.push_back('w');
} }
return result;
}
/*!
* Convert entire instruction to a string.
*/
std::string Instruction::to_string(const LinkedObjectFile& file) const {
auto& info = gOpcodeInfo[(int)kind];
auto result = op_name_to_string();
// relative store and load instructions have a special syntax in MIPS // relative store and load instructions have a special syntax in MIPS
if (info.is_store) { if (info.is_store) {

View file

@ -65,6 +65,7 @@ class Instruction {
public: public:
InstructionKind kind = InstructionKind::UNKNOWN; InstructionKind kind = InstructionKind::UNKNOWN;
std::string op_name_to_string() const;
std::string to_string(const LinkedObjectFile& file) const; std::string to_string(const LinkedObjectFile& file) const;
bool is_valid() const; bool is_valid() const;

View file

@ -280,6 +280,17 @@ goos::Object GotoEnd::to_form() {
return pretty_print::build_list(forms); return pretty_print::build_list(forms);
} }
std::string Break::to_string() {
return "goto" + std::to_string(uid);
}
goos::Object Break::to_form() {
std::vector<goos::Object> forms = {pretty_print::to_symbol("break"),
pretty_print::to_symbol(std::to_string(dest_block)),
body->to_form(), unreachable_block->to_form()};
return pretty_print::build_list(forms);
}
ControlFlowGraph::ControlFlowGraph() { ControlFlowGraph::ControlFlowGraph() {
// allocate the entry and exit vertices. // allocate the entry and exit vertices.
m_entry = alloc<EntryVtx>(); m_entry = alloc<EntryVtx>();
@ -576,6 +587,31 @@ bool ControlFlowGraph::is_until_loop(CfgVtx* b1, CfgVtx* b2) {
return true; return true;
} }
bool ControlFlowGraph::is_goto_not_end_and_unreachable(CfgVtx* b0, CfgVtx* b1) {
if (!b0 || !b1) {
return false;
}
// b0 should be an always branch, not likely.
if (!b0->end_branch.has_branch || !b0->end_branch.branch_always || b0->end_branch.branch_likely) {
return false;
}
// b0 should be next to b1
if (b0->next != b1) {
return false;
}
assert(b1->prev == b0);
// b1 should have no preds and be unreachable.
if (!b1->pred.empty()) {
return false;
}
return true; // match!
}
bool ControlFlowGraph::is_goto_end_and_unreachable(CfgVtx* b0, CfgVtx* b1) { bool ControlFlowGraph::is_goto_end_and_unreachable(CfgVtx* b0, CfgVtx* b1) {
if (!b0 || !b1) { if (!b0 || !b1) {
return false; return false;
@ -919,6 +955,68 @@ bool ControlFlowGraph::find_goto_end() {
return replaced; return replaced;
} }
bool ControlFlowGraph::find_goto_not_end() {
bool replaced = false;
for_each_top_level_vtx([&](CfgVtx* vtx) {
auto* b0 = vtx;
auto* b1 = vtx->next;
if (is_goto_not_end_and_unreachable(b0, b1)) {
replaced = true;
auto* new_goto = alloc<Break>();
new_goto->body = b0;
new_goto->unreachable_block = b1;
// todo set block number
for (auto* new_pred : b0->pred) {
// printf("fix up pred %s of %s\n", new_pred->to_string().c_str(),
// b0->to_string().c_str());
new_pred->replace_succ_and_check(b0, new_goto);
}
new_goto->pred = b0->pred;
for (auto* new_succ : b1->succs()) {
// new_succ->replace_preds_with_and_check({b1}, nullptr);
new_succ->replace_pred_and_check(b1, new_goto);
}
// this is a lie, but ok
new_goto->succ_ft = b1->succ_ft;
new_goto->succ_branch = b1->succ_branch;
new_goto->end_branch = b1->end_branch;
// if(b1->next) {
// b1->next->pred.push_back(new_goto);
// }
// new_goto->succ_branch = b1->succ_branch;
// new_goto->end_branch = b1->end_branch;
new_goto->prev = b0->prev;
if (new_goto->prev) {
new_goto->prev->next = new_goto;
}
new_goto->next = b1->next;
if (new_goto->next) {
new_goto->next->prev = new_goto;
}
b0->succ_branch->replace_preds_with_and_check({b0}, nullptr);
b0->parent_claim(new_goto);
b1->parent_claim(new_goto);
return false;
}
// keep looking
return true;
});
return replaced;
}
bool ControlFlowGraph::is_sequence(CfgVtx* b0, CfgVtx* b1) { bool ControlFlowGraph::is_sequence(CfgVtx* b0, CfgVtx* b1) {
if (!b0 || !b1) if (!b0 || !b1)
return false; return false;
@ -1734,6 +1832,10 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
changed = changed || cfg->find_until1_loop(); changed = changed || cfg->find_until1_loop();
changed = changed || cfg->find_infinite_loop(); changed = changed || cfg->find_infinite_loop();
}; };
if (!changed) {
changed = changed || cfg->find_goto_not_end();
}
} }
if (!cfg->is_fully_resolved()) { if (!cfg->is_fully_resolved()) {

View file

@ -250,6 +250,15 @@ class GotoEnd : public CfgVtx {
CfgVtx* unreachable_block = nullptr; CfgVtx* unreachable_block = nullptr;
}; };
class Break : public CfgVtx {
public:
std::string to_string() override;
goos::Object to_form() override;
int dest_block = -1;
CfgVtx* body = nullptr;
CfgVtx* unreachable_block = nullptr;
};
struct BasicBlock; struct BasicBlock;
/*! /*!
@ -283,6 +292,7 @@ class ControlFlowGraph {
bool find_short_circuits(); bool find_short_circuits();
bool find_goto_end(); bool find_goto_end();
bool find_infinite_loop(); bool find_infinite_loop();
bool find_goto_not_end();
/*! /*!
* Apply a function f to each top-level vertex. * Apply a function f to each top-level vertex.
@ -324,6 +334,7 @@ class ControlFlowGraph {
bool is_while_loop(CfgVtx* b0, CfgVtx* b1, CfgVtx* b2); bool is_while_loop(CfgVtx* b0, CfgVtx* b1, CfgVtx* b2);
bool is_until_loop(CfgVtx* b1, CfgVtx* b2); bool is_until_loop(CfgVtx* b1, CfgVtx* b2);
bool is_goto_end_and_unreachable(CfgVtx* b0, CfgVtx* b1); bool is_goto_end_and_unreachable(CfgVtx* b0, CfgVtx* b1);
bool is_goto_not_end_and_unreachable(CfgVtx* b0, CfgVtx* b1);
std::vector<BlockVtx*> m_blocks; // all block nodes, in order. std::vector<BlockVtx*> m_blocks; // all block nodes, in order.
std::vector<CfgVtx*> m_node_pool; // all nodes allocated std::vector<CfgVtx*> m_node_pool; // all nodes allocated
EntryVtx* m_entry; // the entry vertex EntryVtx* m_entry; // the entry vertex

View file

@ -23,6 +23,7 @@ struct FunctionName {
std::string function_name; // only applicable for GLOBAL std::string function_name; // only applicable for GLOBAL
std::string type_name; // only applicable for METHOD std::string type_name; // only applicable for METHOD
int method_id = -1; // only applicable for METHOD int method_id = -1; // only applicable for METHOD
int unique_id = -1;
std::string to_string() const { std::string to_string() const {
switch (kind) { switch (kind) {
@ -33,7 +34,7 @@ struct FunctionName {
case FunctionKind::TOP_LEVEL_INIT: case FunctionKind::TOP_LEVEL_INIT:
return "(top-level-login)"; return "(top-level-login)";
case FunctionKind::UNIDENTIFIED: case FunctionKind::UNIDENTIFIED:
return "(?)"; return "(anon-function " + std::to_string(unique_id) + ")";
default: default:
throw std::runtime_error("Unsupported FunctionKind"); throw std::runtime_error("Unsupported FunctionKind");
} }
@ -93,6 +94,7 @@ class Function {
int epilogue_end = -1; int epilogue_end = -1;
std::string warnings; std::string warnings;
bool contains_asm_ops = false;
struct Prologue { struct Prologue {
bool decoded = false; // have we removed the prologue from basic blocks? bool decoded = false; // have we removed the prologue from basic blocks?

View file

@ -1,3 +1,12 @@
/*!
* @file BasicOpBuilder.cpp
* Convert a basic block into a sequence of IR operations.
* Build up basic set instructions from GOAL code
* Recognize common GOAL compiler idioms
* Recognize branch delay slot use
* Recognize assembly ops and pass them through as IR_Asm
*/
#include "BasicOpBuilder.h" #include "BasicOpBuilder.h"
#include "decompiler/Function/Function.h" #include "decompiler/Function/Function.h"
#include "decompiler/Function/BasicBlocks.h" #include "decompiler/Function/BasicBlocks.h"
@ -5,28 +14,129 @@
namespace { namespace {
/*!
* Create a GOAL "set!" form.
* These will later be compacted into more complicated nested expressions.
*/
std::shared_ptr<IR_Set> make_set(IR_Set::Kind kind, std::shared_ptr<IR_Set> make_set(IR_Set::Kind kind,
const std::shared_ptr<IR>& dst, const std::shared_ptr<IR>& dst,
const std::shared_ptr<IR>& src) { const std::shared_ptr<IR>& src) {
return std::make_shared<IR_Set>(kind, dst, src); return std::make_shared<IR_Set>(kind, dst, src);
} }
/*!
* Create an IR representing a register at a certain point. Idx is the instruction index.
*/
std::shared_ptr<IR_Register> make_reg(Register reg, int idx) { std::shared_ptr<IR_Register> make_reg(Register reg, int idx) {
return std::make_shared<IR_Register>(reg, idx); return std::make_shared<IR_Register>(reg, idx);
} }
/*!
* Create an IR representing a symbol. The symbol itself ('thing), not the value.
*/
std::shared_ptr<IR_Symbol> make_sym(const std::string& name) { std::shared_ptr<IR_Symbol> make_sym(const std::string& name) {
return std::make_shared<IR_Symbol>(name); return std::make_shared<IR_Symbol>(name);
} }
/*!
* Create an IR representing the value of a symbol. Can be read/written.
*/
std::shared_ptr<IR_SymbolValue> make_sym_value(const std::string& name) { std::shared_ptr<IR_SymbolValue> make_sym_value(const std::string& name) {
return std::make_shared<IR_SymbolValue>(name); return std::make_shared<IR_SymbolValue>(name);
} }
/*!
* Create an integer constant.
*/
std::shared_ptr<IR_IntegerConstant> make_int(int64_t x) { std::shared_ptr<IR_IntegerConstant> make_int(int64_t x) {
return std::make_shared<IR_IntegerConstant>(x); return std::make_shared<IR_IntegerConstant>(x);
} }
/*!
* Create an assembly passthrough in the form op dst, src, src
*/
std::shared_ptr<IR> to_asm_reg_reg_reg(const std::string& str, Instruction& instr, int idx) {
auto result = std::make_shared<IR_AsmOp>(str);
result->dst = make_reg(instr.get_dst(0).get_reg(), idx);
result->src0 = make_reg(instr.get_src(0).get_reg(), idx);
result->src1 = make_reg(instr.get_src(1).get_reg(), idx);
return result;
}
/*!
* Create an assembly passthrough for op src
*/
std::shared_ptr<IR> to_asm_src_reg(const std::string& str, Instruction& instr, int idx) {
auto result = std::make_shared<IR_AsmOp>(str);
result->src0 = make_reg(instr.get_src(0).get_reg(), idx);
return result;
}
/*!
* Create an assembly passthrough for op dst src
*/
std::shared_ptr<IR> to_asm_dst_reg_src_reg(const std::string& str, Instruction& instr, int idx) {
auto result = std::make_shared<IR_AsmOp>(str);
result->dst = make_reg(instr.get_dst(0).get_reg(), idx);
result->src0 = make_reg(instr.get_src(0).get_reg(), idx);
return result;
}
/*!
* Convert an instruction atom to IR.
*/
std::shared_ptr<IR> instr_atom_to_ir(const InstructionAtom& ia, int idx) {
switch (ia.kind) {
case InstructionAtom::REGISTER:
return make_reg(ia.get_reg(), idx);
case InstructionAtom::VU_Q:
return std::make_shared<IR_AsmReg>(IR_AsmReg::VU_Q);
case InstructionAtom::VU_ACC:
return std::make_shared<IR_AsmReg>(IR_AsmReg::VU_ACC);
case InstructionAtom::IMM:
return make_int(ia.get_imm());
default:
assert(false);
}
}
std::shared_ptr<IR> to_asm_automatic(const std::string& str, Instruction& instr, int idx) {
auto result = std::make_shared<IR_AsmOp>(str);
assert(instr.n_dst < 2);
assert(instr.n_src < 4);
if (instr.n_dst >= 1) {
result->dst = instr_atom_to_ir(instr.get_dst(0), idx);
}
if (instr.n_src >= 1) {
result->src0 = instr_atom_to_ir(instr.get_src(0), idx);
}
if (instr.n_src >= 2) {
result->src1 = instr_atom_to_ir(instr.get_src(1), idx);
}
if (instr.n_src >= 3) {
result->src1 = instr_atom_to_ir(instr.get_src(2), idx);
}
return result;
}
std::shared_ptr<IR> try_subu(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::SUBU, {}, {}, {})) {
return to_asm_reg_reg_reg("subu", instr, idx);
}
return nullptr;
}
std::shared_ptr<IR> try_sllv(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::SLLV, {}, {}, make_gpr(Reg::R0))) {
return to_asm_reg_reg_reg("sllv", instr, idx);
}
return nullptr;
}
std::shared_ptr<IR> try_or(Instruction& instr, int idx) { std::shared_ptr<IR> try_or(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::OR, {}, make_gpr(Reg::S7), make_gpr(Reg::R0))) { if (is_gpr_3(instr, InstructionKind::OR, {}, make_gpr(Reg::S7), make_gpr(Reg::R0))) {
return make_set(IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx), make_sym("#f")); return make_set(IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx), make_sym("#f"));
@ -402,7 +512,7 @@ std::shared_ptr<IR> try_daddu(Instruction& instr, int idx) {
std::make_shared<IR_IntMath2>(IR_IntMath2::ADD, make_reg(instr.get_src(0).get_reg(), idx), std::make_shared<IR_IntMath2>(IR_IntMath2::ADD, make_reg(instr.get_src(0).get_reg(), idx),
make_reg(instr.get_src(1).get_reg(), idx))); make_reg(instr.get_src(1).get_reg(), idx)));
} }
return nullptr; return to_asm_reg_reg_reg("daddu", instr, idx);
} }
std::shared_ptr<IR> try_dsubu(Instruction& instr, int idx) { std::shared_ptr<IR> try_dsubu(Instruction& instr, int idx) {
@ -459,6 +569,16 @@ std::shared_ptr<IR> try_andi(Instruction& instr, int idx) {
return nullptr; return nullptr;
} }
std::shared_ptr<IR> try_xori(Instruction& instr, int idx) {
if (instr.kind == InstructionKind::XORI) {
return make_set(
IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
std::make_shared<IR_IntMath2>(IR_IntMath2::XOR, make_reg(instr.get_src(0).get_reg(), idx),
make_int(instr.get_src(1).get_imm())));
}
return nullptr;
}
std::shared_ptr<IR> try_nor(Instruction& instr, int idx) { std::shared_ptr<IR> try_nor(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::NOR, {}, {}, {}) && if (is_gpr_3(instr, InstructionKind::NOR, {}, {}, {}) &&
!instr.get_src(0).is_reg(make_gpr(Reg::S7)) && instr.get_src(1).is_reg(make_gpr(Reg::R0))) { !instr.get_src(0).is_reg(make_gpr(Reg::S7)) && instr.get_src(1).is_reg(make_gpr(Reg::R0))) {
@ -533,6 +653,17 @@ std::shared_ptr<IR> try_dsrlv(Instruction& instr, int idx) {
return nullptr; return nullptr;
} }
std::shared_ptr<IR> try_dsllv(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::DSLLV, {}, {}, {}) &&
!instr.get_src(0).is_reg(make_gpr(Reg::S7)) && !instr.get_src(1).is_reg(make_gpr(Reg::S7))) {
return make_set(IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
std::make_shared<IR_IntMath2>(IR_IntMath2::LEFT_SHIFT,
make_reg(instr.get_src(0).get_reg(), idx),
make_reg(instr.get_src(1).get_reg(), idx)));
}
return nullptr;
}
std::shared_ptr<IR> try_sw(Instruction& instr, int idx) { std::shared_ptr<IR> try_sw(Instruction& instr, int idx) {
if (instr.kind == InstructionKind::SW && instr.get_src(1).is_sym() && if (instr.kind == InstructionKind::SW && instr.get_src(1).is_sym() &&
instr.get_src(2).is_reg(make_gpr(Reg::S7))) { instr.get_src(2).is_reg(make_gpr(Reg::S7))) {
@ -647,6 +778,22 @@ std::shared_ptr<IR> try_movs(Instruction& instr, int idx) {
return nullptr; return nullptr;
} }
std::shared_ptr<IR> try_movn(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::MOVN, {}, make_gpr(Reg::S7), {})) {
return make_set(IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
std::make_shared<IR_CMoveF>(make_reg(instr.get_src(1).get_reg(), idx), false));
}
return nullptr;
}
std::shared_ptr<IR> try_movz(Instruction& instr, int idx) {
if (is_gpr_3(instr, InstructionKind::MOVZ, {}, make_gpr(Reg::S7), {})) {
return make_set(IR_Set::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
std::make_shared<IR_CMoveF>(make_reg(instr.get_src(1).get_reg(), idx), true));
}
return nullptr;
}
// TWO Instructions // TWO Instructions
std::shared_ptr<IR> try_div(Instruction& instr, Instruction& next_instr, int idx) { std::shared_ptr<IR> try_div(Instruction& instr, Instruction& next_instr, int idx) {
if (instr.kind == InstructionKind::DIV && instr.get_src(0).is_reg() && if (instr.kind == InstructionKind::DIV && instr.get_src(0).is_reg() &&
@ -897,7 +1044,53 @@ std::shared_ptr<IR> try_lui(Instruction& i0, Instruction& i1, int idx) {
return nullptr; return nullptr;
} }
std::shared_ptr<IR> try_slt(Instruction& i0, Instruction& i1, int idx) {
if (is_gpr_3(i0, InstructionKind::SLT, {}, {}, {})) {
auto temp = i0.get_dst(0).get_reg();
auto left = i0.get_src(0).get_reg();
auto right = i0.get_src(1).get_reg();
if (is_gpr_3(i1, InstructionKind::MOVZ, left, right, temp)) {
// success!
auto result =
make_set(IR_Set::REG_64, make_reg(left, idx),
std::make_shared<IR_IntMath2>(IR_IntMath2::MIN_SIGNED, make_reg(left, idx),
make_reg(right, idx)));
result->clobber = make_reg(temp, idx);
return result;
}
if (is_gpr_3(i1, InstructionKind::MOVN, left, right, temp)) {
// success!
auto result =
make_set(IR_Set::REG_64, make_reg(left, idx),
std::make_shared<IR_IntMath2>(IR_IntMath2::MAX_SIGNED, make_reg(left, idx),
make_reg(right, idx)));
result->clobber = make_reg(temp, idx);
return result;
}
}
return nullptr;
}
// THREE OP // THREE OP
std::shared_ptr<IR> try_lui(Instruction& i0, Instruction& i1, Instruction& i2, int idx) {
if (i0.kind == InstructionKind::LUI && i1.kind == InstructionKind::ORI &&
i0.get_src(0).is_label() && i1.get_src(1).is_label() &&
is_gpr_3(i2, InstructionKind::ADDU, {}, make_gpr(Reg::FP), {})) {
assert(i0.get_dst(0).get_reg() == i1.get_src(0).get_reg());
assert(i0.get_src(0).get_label() == i1.get_src(1).get_label());
assert(i2.get_dst(0).get_reg() == i2.get_src(1).get_reg());
assert(i2.get_dst(0).get_reg() == i1.get_dst(0).get_reg());
auto op = make_set(IR_Set::REG_64, make_reg(i1.get_dst(0).get_reg(), idx),
std::make_shared<IR_StaticAddress>(i0.get_src(0).get_label()));
if (i0.get_dst(0).get_reg() != i1.get_dst(0).get_reg()) {
op->clobber = make_reg(i0.get_dst(0).get_reg(), idx);
}
return op;
}
return nullptr;
}
std::shared_ptr<IR> try_dsubu(Instruction& i0, Instruction& i1, Instruction& i2, int idx) { std::shared_ptr<IR> try_dsubu(Instruction& i0, Instruction& i1, Instruction& i2, int idx) {
if (i0.kind == InstructionKind::DSUBU && i1.kind == InstructionKind::DADDIU && if (i0.kind == InstructionKind::DSUBU && i1.kind == InstructionKind::DADDIU &&
i2.kind == InstructionKind::MOVN) { i2.kind == InstructionKind::MOVN) {
@ -1136,6 +1329,21 @@ std::shared_ptr<IR> try_clts(Instruction& i0, Instruction& i1, Instruction& i2,
return nullptr; return nullptr;
} }
std::shared_ptr<IR> try_cles(Instruction& i0, Instruction& i1, Instruction& i2, int idx) {
if (i0.kind == InstructionKind::CLES && i1.kind == InstructionKind::BC1T) {
return std::make_shared<IR_Branch>(
Condition(Condition::FLOAT_LEQ, make_reg(i0.get_src(0).get_reg(), idx),
make_reg(i0.get_src(1).get_reg(), idx), nullptr),
i1.get_src(0).get_label(), get_branch_delay(i2, idx), false);
} else if (i0.kind == InstructionKind::CLES && i1.kind == InstructionKind::BC1F) {
return std::make_shared<IR_Branch>(
Condition(Condition::FLOAT_GREATER_THAN, make_reg(i0.get_src(0).get_reg(), idx),
make_reg(i0.get_src(1).get_reg(), idx), nullptr),
i1.get_src(0).get_label(), get_branch_delay(i2, idx), false);
}
return nullptr;
}
std::shared_ptr<IR> try_sltu(Instruction& i0, Instruction& i1, Instruction& i2, int idx) { std::shared_ptr<IR> try_sltu(Instruction& i0, Instruction& i1, Instruction& i2, int idx) {
if (i0.kind == InstructionKind::SLTU && i1.kind == InstructionKind::BNE) { if (i0.kind == InstructionKind::SLTU && i1.kind == InstructionKind::BNE) {
auto clobber_reg = i0.get_dst(0).get_reg(); auto clobber_reg = i0.get_dst(0).get_reg();
@ -1268,6 +1476,12 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::CLTS: case InstructionKind::CLTS:
result = try_clts(i, next, next_next, instr); result = try_clts(i, next, next_next, instr);
break; break;
case InstructionKind::CLES:
result = try_cles(i, next, next_next, instr);
break;
case InstructionKind::LUI:
result = try_lui(i, next, next_next, instr);
break;
default: default:
result = nullptr; result = nullptr;
} }
@ -1317,6 +1531,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::LUI: case InstructionKind::LUI:
result = try_lui(i, next, instr); result = try_lui(i, next, instr);
break; break;
case InstructionKind::SLT:
result = try_slt(i, next, instr);
break;
default: default:
result = nullptr; result = nullptr;
} }
@ -1343,6 +1560,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::ANDI: case InstructionKind::ANDI:
result = try_andi(i, instr); result = try_andi(i, instr);
break; break;
case InstructionKind::XORI:
result = try_xori(i, instr);
break;
case InstructionKind::NOR: case InstructionKind::NOR:
result = try_nor(i, instr); result = try_nor(i, instr);
break; break;
@ -1393,6 +1613,10 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
break; break;
case InstructionKind::DSUBU: case InstructionKind::DSUBU:
result = try_dsubu(i, instr); result = try_dsubu(i, instr);
if (!result) {
// fails if it uses s7 register.
result = to_asm_automatic(i.op_name_to_string(), i, instr);
}
break; break;
case InstructionKind::MULT3: case InstructionKind::MULT3:
result = try_mult3(i, instr); result = try_mult3(i, instr);
@ -1402,6 +1626,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
break; break;
case InstructionKind::POR: case InstructionKind::POR:
result = try_por(i, instr); result = try_por(i, instr);
if (!result) {
result = to_asm_automatic(i.op_name_to_string(), i, instr);
}
break; break;
case InstructionKind::LBU: case InstructionKind::LBU:
result = try_lbu(i, instr); result = try_lbu(i, instr);
@ -1447,12 +1674,18 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
break; break;
case InstructionKind::ADDIU: case InstructionKind::ADDIU:
result = try_addiu(i, instr); result = try_addiu(i, instr);
if (!result) {
result = to_asm_automatic(i.op_name_to_string(), i, instr);
}
break; break;
case InstructionKind::LUI: case InstructionKind::LUI:
result = try_lui(i, instr); result = try_lui(i, instr);
break; break;
case InstructionKind::SLL: case InstructionKind::SLL:
result = try_sll(i, instr); result = try_sll(i, instr);
if (!result) {
result = to_asm_automatic(i.op_name_to_string(), i, instr);
}
break; break;
case InstructionKind::SB: case InstructionKind::SB:
result = try_sb(i, instr); result = try_sb(i, instr);
@ -1484,6 +1717,168 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
case InstructionKind::DSRLV: case InstructionKind::DSRLV:
result = try_dsrlv(i, instr); result = try_dsrlv(i, instr);
break; break;
case InstructionKind::DSLLV:
result = try_dsllv(i, instr);
break;
case InstructionKind::SUBU:
result = try_subu(i, instr);
break;
case InstructionKind::SLLV:
result = try_sllv(i, instr);
break;
case InstructionKind::MOVN:
result = try_movn(i, instr);
if (!result) {
result = to_asm_automatic(i.op_name_to_string(), i, instr);
}
break;
case InstructionKind::MOVZ:
result = try_movz(i, instr);
if (!result) {
result = to_asm_automatic(i.op_name_to_string(), i, instr);
}
break;
// Everything below here is an "asm passthrough".
case InstructionKind::JR:
result = to_asm_src_reg("jr", i, instr);
break;
// reg reg
case InstructionKind::QMFC2:
result = to_asm_dst_reg_src_reg(i.op_name_to_string(), i, instr);
break;
// VU/COP2
case InstructionKind::VMOVE:
case InstructionKind::VFTOI0:
case InstructionKind::VFTOI4:
case InstructionKind::VFTOI12:
case InstructionKind::VITOF0:
case InstructionKind::VITOF12:
case InstructionKind::VITOF15:
case InstructionKind::VABS:
case InstructionKind::VADD:
case InstructionKind::VSUB:
case InstructionKind::VMUL:
case InstructionKind::VMINI:
case InstructionKind::VMAX:
case InstructionKind::VOPMSUB:
case InstructionKind::VMADD:
case InstructionKind::VMSUB:
case InstructionKind::VADD_BC:
case InstructionKind::VSUB_BC:
case InstructionKind::VMUL_BC:
case InstructionKind::VMULA_BC:
case InstructionKind::VMADD_BC:
case InstructionKind::VADDA_BC:
case InstructionKind::VMADDA_BC:
case InstructionKind::VMSUBA_BC:
case InstructionKind::VMSUB_BC:
case InstructionKind::VMINI_BC:
case InstructionKind::VMAX_BC:
case InstructionKind::VADDQ:
case InstructionKind::VSUBQ:
case InstructionKind::VMULQ:
case InstructionKind::VMSUBQ:
case InstructionKind::VMULA:
case InstructionKind::VADDA:
case InstructionKind::VMADDA:
case InstructionKind::VOPMULA:
case InstructionKind::VDIV:
case InstructionKind::VCLIP:
case InstructionKind::VMULAQ:
case InstructionKind::VMTIR:
case InstructionKind::VIAND:
case InstructionKind::VLQI:
case InstructionKind::VIADDI:
case InstructionKind::VSQI:
case InstructionKind::VRGET:
case InstructionKind::VSQRT:
case InstructionKind::VRSQRT:
case InstructionKind::VRXOR:
case InstructionKind::VRNEXT:
case InstructionKind::VNOP:
case InstructionKind::VWAITQ:
case InstructionKind::VCALLMS:
// FPU/COP1
case InstructionKind::MULAS:
case InstructionKind::MADDAS:
case InstructionKind::MADDS:
case InstructionKind::ADDAS:
// Moves / Loads / Stores
case InstructionKind::CTC2:
case InstructionKind::CFC2:
case InstructionKind::SQC2:
case InstructionKind::LQC2:
case InstructionKind::LDR:
case InstructionKind::LDL:
case InstructionKind::QMTC2:
case InstructionKind::MFC0:
case InstructionKind::MTC0:
case InstructionKind::SYNCL:
case InstructionKind::SYNCP:
case InstructionKind::SYSCALL:
case InstructionKind::CACHE_DXWBIN:
case InstructionKind::MTPC:
case InstructionKind::MFPC:
// random math
case InstructionKind::ADDU:
case InstructionKind::SRL: // maybe bitfield ops use this?
case InstructionKind::SRA:
case InstructionKind::SLT:
case InstructionKind::SLTI:
// MMI
case InstructionKind::PSLLW:
case InstructionKind::PSRAW:
case InstructionKind::PSRAH:
case InstructionKind::PLZCW:
case InstructionKind::PMFHL_UW:
case InstructionKind::PMFHL_LW:
case InstructionKind::PMFHL_LH:
case InstructionKind::PSLLH:
case InstructionKind::PSRLH:
case InstructionKind::PEXTLW:
case InstructionKind::PPACH:
case InstructionKind::PSUBW:
case InstructionKind::PCGTW:
case InstructionKind::PEXTLH:
case InstructionKind::PEXTLB:
case InstructionKind::PMAXH:
case InstructionKind::PPACB:
case InstructionKind::PADDW:
case InstructionKind::PADDH:
case InstructionKind::PMAXW:
case InstructionKind::PPACW:
case InstructionKind::PCEQW:
case InstructionKind::PEXTUW:
case InstructionKind::PMINH:
case InstructionKind::PEXTUH:
case InstructionKind::PEXTUB:
case InstructionKind::PCEQB:
case InstructionKind::PMINW:
case InstructionKind::PABSW:
case InstructionKind::PCPYLD:
case InstructionKind::PROT3W:
case InstructionKind::PAND:
case InstructionKind::PMADDH:
case InstructionKind::PMULTH:
case InstructionKind::PEXEW:
case InstructionKind::PCPYUD:
case InstructionKind::PNOR:
case InstructionKind::PCPYH:
case InstructionKind::PINTEH:
// 128 bit integer
// case InstructionKind::LQ:
// case InstructionKind::SQ:
result = to_asm_automatic(i.op_name_to_string(), i, instr);
break;
default: default:
result = nullptr; result = nullptr;
} }
@ -1496,9 +1891,13 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
// everything failed // everything failed
if (!result) { if (!result) {
// temp hack for debug: // temp hack for debug:
// printf("Instruction -> BasicOp failed on %s\n", i.to_string(*file).c_str()); printf("Instruction -> BasicOp failed on %s\n", i.to_string(*file).c_str());
func->add_basic_op(std::make_shared<IR_Failed>(), instr, instr + 1); func->add_basic_op(std::make_shared<IR_Failed>(), instr, instr + 1);
} else { } else {
if (!func->contains_asm_ops && dynamic_cast<IR_AsmOp*>(result.get())) {
func->warnings += "Function contains asm op";
func->contains_asm_ops = true;
}
func->add_basic_op(result, instr, instr + length); func->add_basic_op(result, instr, instr + length);
instr += (length - 1); instr += (length - 1);
} }

View file

@ -86,6 +86,20 @@ std::pair<IR_Branch*, std::shared_ptr<IR>*> get_condition_branch(std::shared_ptr
condition_branch_location = &as_seq->forms.back(); condition_branch_location = &as_seq->forms.back();
} }
} }
if (!condition_branch) {
auto as_return = dynamic_cast<IR_Return*>(in->get());
if (as_return) {
return get_condition_branch(&as_return->dead_code);
}
}
if (!condition_branch) {
auto as_break = dynamic_cast<IR_Break*>(in->get());
if (as_break) {
return get_condition_branch(&as_break->dead_code);
}
}
return std::make_pair(condition_branch, condition_branch_location); return std::make_pair(condition_branch, condition_branch_location);
} }
@ -134,6 +148,63 @@ void clean_up_cond_with_else(std::shared_ptr<IR>* ir, LinkedObjectFile& file) {
} }
} }
void clean_up_until_loop(IR_UntilLoop* ir) {
auto condition_branch = get_condition_branch(&ir->condition);
assert(condition_branch.first);
assert(condition_branch.first->branch_delay.kind == BranchDelay::NOP);
auto replacement = std::make_shared<IR_Compare>(condition_branch.first->condition);
*(condition_branch.second) = replacement;
}
void clean_up_infinite_while_loop(IR_WhileLoop* ir) {
auto jump = get_condition_branch(&ir->body);
assert(jump.first);
assert(jump.first->branch_delay.kind == BranchDelay::NOP);
assert(jump.first->condition.kind == Condition::ALWAYS);
auto as_end_of_sequence = get_condition_branch_as_vector(ir->body.get());
if (as_end_of_sequence.first) {
assert(as_end_of_sequence.second->size() > 1);
as_end_of_sequence.second->pop_back();
} else {
// In the future we could consider having a more explicit "this case is empty" operator so
// this doesn't get confused with an actual MIPS nop.
*(jump.second) = std::make_shared<IR_Nop>();
}
ir->cleaned = true; // so we don't try this later...
}
void clean_up_return(IR_Return* ir) {
auto jump_to_end = get_condition_branch(&ir->return_code);
assert(jump_to_end.first);
assert(jump_to_end.first->branch_delay.kind == BranchDelay::NOP);
assert(jump_to_end.first->condition.kind == Condition::ALWAYS);
auto as_end_of_sequence = get_condition_branch_as_vector(ir->return_code.get());
if (as_end_of_sequence.first) {
assert(as_end_of_sequence.second->size() > 1);
as_end_of_sequence.second->pop_back();
} else {
// In the future we could consider having a more explicit "this case is empty" operator so
// this doesn't get confused with an actual MIPS nop.
*(jump_to_end.second) = std::make_shared<IR_Nop>();
}
}
void clean_up_break(IR_Break* ir) {
auto jump_to_end = get_condition_branch(&ir->return_code);
assert(jump_to_end.first);
assert(jump_to_end.first->branch_delay.kind == BranchDelay::NOP);
assert(jump_to_end.first->condition.kind == Condition::ALWAYS);
auto as_end_of_sequence = get_condition_branch_as_vector(ir->return_code.get());
if (as_end_of_sequence.first) {
assert(as_end_of_sequence.second->size() > 1);
as_end_of_sequence.second->pop_back();
} else {
// In the future we could consider having a more explicit "this case is empty" operator so
// this doesn't get confused with an actual MIPS nop.
*(jump_to_end.second) = std::make_shared<IR_Nop>();
}
}
/*! /*!
* Does the instruction in the delay slot set a register to false? * Does the instruction in the delay slot set a register to false?
* Note. a beql s7, x followed by a or y, x, r0 will count as this. I don't know why but * Note. a beql s7, x followed by a or y, x, r0 will count as this. I don't know why but
@ -844,6 +915,25 @@ std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx)
auto result = std::make_shared<IR_WhileLoop>(cfg_to_ir(f, file, wvtx->condition), auto result = std::make_shared<IR_WhileLoop>(cfg_to_ir(f, file, wvtx->condition),
cfg_to_ir(f, file, wvtx->body)); cfg_to_ir(f, file, wvtx->body));
return result; return result;
} else if (dynamic_cast<UntilLoop*>(vtx)) {
auto wvtx = dynamic_cast<UntilLoop*>(vtx);
auto result = std::make_shared<IR_UntilLoop>(cfg_to_ir(f, file, wvtx->condition),
cfg_to_ir(f, file, wvtx->body));
clean_up_until_loop(result.get());
return result;
} else if (dynamic_cast<UntilLoop_single*>(vtx)) {
auto wvtx = dynamic_cast<UntilLoop_single*>(vtx);
auto result =
std::make_shared<IR_UntilLoop>(cfg_to_ir(f, file, wvtx->block), std::make_shared<IR_Nop>());
clean_up_until_loop(result.get());
return result;
} else if (dynamic_cast<InfiniteLoopBlock*>(vtx)) {
auto wvtx = dynamic_cast<InfiniteLoopBlock*>(vtx);
auto result = std::make_shared<IR_WhileLoop>(
std::make_shared<IR_Compare>(Condition(Condition::ALWAYS, nullptr, nullptr, nullptr)),
cfg_to_ir(f, file, wvtx->block));
clean_up_infinite_while_loop(result.get());
return result;
} else if (dynamic_cast<CondWithElse*>(vtx)) { } else if (dynamic_cast<CondWithElse*>(vtx)) {
auto* cvtx = dynamic_cast<CondWithElse*>(vtx); auto* cvtx = dynamic_cast<CondWithElse*>(vtx);
@ -912,7 +1002,6 @@ std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx)
} }
auto result = std::make_shared<IR_ShortCircuit>(entries); auto result = std::make_shared<IR_ShortCircuit>(entries);
clean_up_sc(result, file); clean_up_sc(result, file);
// todo clean these into real and/or.
return result; return result;
} else if (dynamic_cast<CondNoElse*>(vtx)) { } else if (dynamic_cast<CondNoElse*>(vtx)) {
auto* cvtx = dynamic_cast<CondNoElse*>(vtx); auto* cvtx = dynamic_cast<CondNoElse*>(vtx);
@ -926,6 +1015,18 @@ std::shared_ptr<IR> cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx)
std::shared_ptr<IR> result = std::make_shared<IR_Cond>(entries); std::shared_ptr<IR> result = std::make_shared<IR_Cond>(entries);
clean_up_cond_no_else(&result, file); clean_up_cond_no_else(&result, file);
return result; return result;
} else if (dynamic_cast<GotoEnd*>(vtx)) {
auto* cvtx = dynamic_cast<GotoEnd*>(vtx);
auto result = std::make_shared<IR_Return>(cfg_to_ir(f, file, cvtx->body),
cfg_to_ir(f, file, cvtx->unreachable_block));
clean_up_return(result.get());
return result;
} else if (dynamic_cast<Break*>(vtx)) {
auto* cvtx = dynamic_cast<Break*>(vtx);
auto result = std::make_shared<IR_Break>(cfg_to_ir(f, file, cvtx->body),
cfg_to_ir(f, file, cvtx->unreachable_block));
clean_up_break(result.get());
return result;
} }
throw std::runtime_error("not yet implemented IR conversion."); throw std::runtime_error("not yet implemented IR conversion.");
@ -942,7 +1043,7 @@ void clean_up_while_loops(IR_Begin* sequence, LinkedObjectFile& file) {
std::vector<size_t> to_remove; // the list of branches to remove by index in this sequence std::vector<size_t> to_remove; // the list of branches to remove by index in this sequence
for (size_t i = 0; i < sequence->forms.size(); i++) { for (size_t i = 0; i < sequence->forms.size(); i++) {
auto* form_as_while = dynamic_cast<IR_WhileLoop*>(sequence->forms.at(i).get()); auto* form_as_while = dynamic_cast<IR_WhileLoop*>(sequence->forms.at(i).get());
if (form_as_while) { if (form_as_while && !form_as_while->cleaned) {
assert(i != 0); assert(i != 0);
auto prev_as_branch = dynamic_cast<IR_Branch*>(sequence->forms.at(i - 1).get()); auto prev_as_branch = dynamic_cast<IR_Branch*>(sequence->forms.at(i - 1).get());
assert(prev_as_branch); assert(prev_as_branch);
@ -989,7 +1090,6 @@ std::shared_ptr<IR> build_cfg_ir(Function& function,
try { try {
auto top_level = cfg.get_single_top_level(); auto top_level = cfg.get_single_top_level();
// todo, we should apply transformations for fixing up branch instructions for each IR.
// and possibly annotate the IR control flow structure so that we can determine if its and/or // and possibly annotate the IR control flow structure so that we can determine if its and/or
// or whatever. This may require rejecting a huge number of inline assembly functions, and // or whatever. This may require rejecting a huge number of inline assembly functions, and
// possibly resolving the min/max/ash issue. // possibly resolving the min/max/ash issue.

View file

@ -256,6 +256,12 @@ goos::Object IR_IntMath2::to_form(const LinkedObjectFile& file) const {
case RIGHT_SHIFT_LOGIC: case RIGHT_SHIFT_LOGIC:
math_operator = "shr"; math_operator = "shr";
break; break;
case MIN_SIGNED:
math_operator = "min.si";
break;
case MAX_SIGNED:
math_operator = "max.si";
break;
default: default:
assert(false); assert(false);
} }
@ -399,6 +405,8 @@ int Condition::num_args() const {
case FLOAT_NOT_EQUAL: case FLOAT_NOT_EQUAL:
case FLOAT_LESS_THAN: case FLOAT_LESS_THAN:
case FLOAT_GEQ: case FLOAT_GEQ:
case FLOAT_GREATER_THAN:
case FLOAT_LEQ:
return 2; return 2;
case ZERO: case ZERO:
case NONZERO: case NONZERO:
@ -501,6 +509,12 @@ void Condition::invert() {
case FLOAT_GEQ: case FLOAT_GEQ:
kind = FLOAT_LESS_THAN; kind = FLOAT_LESS_THAN;
break; break;
case FLOAT_GREATER_THAN:
kind = FLOAT_LEQ;
break;
case FLOAT_LEQ:
kind = FLOAT_GREATER_THAN;
break;
default: default:
assert(false); assert(false);
} }
@ -570,6 +584,12 @@ goos::Object Condition::to_form(const LinkedObjectFile& file) const {
case FLOAT_GEQ: case FLOAT_GEQ:
condtion_operator = ">=.f"; condtion_operator = ">=.f";
break; break;
case FLOAT_GREATER_THAN:
condtion_operator = ">.f";
break;
case FLOAT_LEQ:
condtion_operator = "<=.f";
break;
case GREATER_THAN_ZERO_SIGNED: case GREATER_THAN_ZERO_SIGNED:
condtion_operator = ">0.si"; condtion_operator = ">0.si";
break; break;
@ -680,6 +700,19 @@ void IR_WhileLoop::get_children(std::vector<std::shared_ptr<IR>>* output) const
output->push_back(body); output->push_back(body);
} }
goos::Object IR_UntilLoop::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> list;
list.push_back(pretty_print::to_symbol("until"));
list.push_back(condition->to_form(file));
print_inlining_begin(&list, body.get(), file);
return pretty_print::build_list(list);
}
void IR_UntilLoop::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(condition);
output->push_back(body);
}
goos::Object IR_CondWithElse::to_form(const LinkedObjectFile& file) const { goos::Object IR_CondWithElse::to_form(const LinkedObjectFile& file) const {
// for now we only turn it into an if statement if both cases won't require a begin at the top // for now we only turn it into an if statement if both cases won't require a begin at the top
// level. I think it is more common to write these as a two-case cond instead of an if with begin. // level. I think it is more common to write these as a two-case cond instead of an if with begin.
@ -801,3 +834,74 @@ void IR_Ash::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(value); output->push_back(value);
output->push_back(shift_amount); output->push_back(shift_amount);
} }
goos::Object IR_AsmOp::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol(name));
for (auto& x : {dst, src0, src1, src2}) {
if (x) {
forms.push_back(x->to_form(file));
}
}
return pretty_print::build_list(forms);
}
void IR_AsmOp::get_children(std::vector<std::shared_ptr<IR>>* output) const {
for (auto& x : {dst, src0, src1}) {
if (x) {
output->push_back(x);
}
}
}
goos::Object IR_CMoveF::to_form(const LinkedObjectFile& file) const {
return pretty_print::build_list(
pretty_print::to_symbol(on_zero ? "cmove-false-on-zero" : "cmove-false-on-nonzero"),
src->to_form(file));
}
void IR_CMoveF::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(src);
}
goos::Object IR_AsmReg::to_form(const LinkedObjectFile& file) const {
(void)file;
switch (kind) {
case VU_Q:
return pretty_print::to_symbol("Q");
case VU_ACC:
return pretty_print::to_symbol("ACC");
default:
assert(false);
}
}
void IR_AsmReg::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
goos::Object IR_Return::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("return"));
forms.push_back(pretty_print::build_list(return_code->to_form(file)));
forms.push_back(pretty_print::build_list(dead_code->to_form(file)));
return pretty_print::build_list(forms);
}
void IR_Return::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(return_code);
output->push_back(dead_code);
}
goos::Object IR_Break::to_form(const LinkedObjectFile& file) const {
std::vector<goos::Object> forms;
forms.push_back(pretty_print::to_symbol("break")); // todo break destination...
forms.push_back(pretty_print::build_list(return_code->to_form(file)));
forms.push_back(pretty_print::build_list(dead_code->to_form(file)));
return pretty_print::build_list(forms);
}
void IR_Break::get_children(std::vector<std::shared_ptr<IR>>* output) const {
output->push_back(return_code);
output->push_back(dead_code);
}

View file

@ -136,7 +136,9 @@ class IR_IntMath2 : public IR {
LEFT_SHIFT, LEFT_SHIFT,
RIGHT_SHIFT_ARITH, RIGHT_SHIFT_ARITH,
RIGHT_SHIFT_LOGIC, RIGHT_SHIFT_LOGIC,
MUL_UNSIGNED MUL_UNSIGNED,
MIN_SIGNED,
MAX_SIGNED
} kind; } kind;
IR_IntMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1) IR_IntMath2(Kind _kind, std::shared_ptr<IR> _arg0, std::shared_ptr<IR> _arg1)
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {} : kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
@ -212,7 +214,9 @@ struct Condition {
FLOAT_EQUAL, FLOAT_EQUAL,
FLOAT_NOT_EQUAL, FLOAT_NOT_EQUAL,
FLOAT_LESS_THAN, FLOAT_LESS_THAN,
FLOAT_GEQ FLOAT_GEQ,
FLOAT_LEQ,
FLOAT_GREATER_THAN,
} kind; } kind;
Condition(Kind _kind, Condition(Kind _kind,
@ -294,6 +298,16 @@ class IR_WhileLoop : public IR {
goos::Object to_form(const LinkedObjectFile& file) const override; goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override; void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
std::shared_ptr<IR> condition, body; std::shared_ptr<IR> condition, body;
bool cleaned = false;
};
class IR_UntilLoop : public IR {
public:
IR_UntilLoop(std::shared_ptr<IR> _condition, std::shared_ptr<IR> _body)
: condition(std::move(_condition)), body(std::move(_body)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
std::shared_ptr<IR> condition, body;
}; };
class IR_CondWithElse : public IR { class IR_CondWithElse : public IR {
@ -370,4 +384,54 @@ class IR_Ash : public IR {
void get_children(std::vector<std::shared_ptr<IR>>* output) const override; void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
}; };
class IR_AsmOp : public IR {
public:
std::shared_ptr<IR> dst = nullptr;
std::shared_ptr<IR> src0 = nullptr;
std::shared_ptr<IR> src1 = nullptr;
std::shared_ptr<IR> src2 = nullptr;
std::string name;
IR_AsmOp(std::string _name) : name(std::move(_name)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_CMoveF : public IR {
public:
std::shared_ptr<IR> src = nullptr;
bool on_zero = false;
explicit IR_CMoveF(std::shared_ptr<IR> _src, bool _on_zero)
: src(std::move(_src)), on_zero(_on_zero) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_AsmReg : public IR {
public:
enum Kind { VU_Q, VU_ACC } kind;
explicit IR_AsmReg(Kind _kind) : kind(_kind) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Return : public IR {
public:
std::shared_ptr<IR> return_code;
std::shared_ptr<IR> dead_code;
IR_Return(std::shared_ptr<IR> _return_code, std::shared_ptr<IR> _dead_code)
: return_code(std::move(_return_code)), dead_code(std::move(_dead_code)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
class IR_Break : public IR {
public:
std::shared_ptr<IR> return_code;
std::shared_ptr<IR> dead_code;
IR_Break(std::shared_ptr<IR> _return_code, std::shared_ptr<IR> _dead_code)
: return_code(std::move(_return_code)), dead_code(std::move(_dead_code)) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
};
#endif // JAK_IR_H #endif // JAK_IR_H

View file

@ -556,8 +556,10 @@ void ObjectFileDB::analyze_functions() {
std::unordered_set<std::string> unique_names; std::unordered_set<std::string> unique_names;
std::unordered_map<std::string, std::unordered_set<std::string>> duplicated_functions; std::unordered_map<std::string, std::unordered_set<std::string>> duplicated_functions;
int uid = 1;
for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { for_each_function([&](Function& func, int segment_id, ObjectFileData& data) {
(void)segment_id; (void)segment_id;
func.guessed_name.unique_id = uid++;
auto name = func.guessed_name.to_string(); auto name = func.guessed_name.to_string();
if (func.guessed_name.expected_unique()) { if (func.guessed_name.expected_unique()) {
if (unique_names.find(name) != unique_names.end()) { if (unique_names.find(name) != unique_names.end()) {
@ -606,12 +608,13 @@ void ObjectFileDB::analyze_functions() {
timer.start(); timer.start();
int total_basic_blocks = 0; int total_basic_blocks = 0;
for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { for_each_function([&](Function& func, int segment_id, ObjectFileData& data) {
// printf("in %s\n", func.guessed_name.to_string().c_str()); // printf("in %s\n", func.guessed_name.to_string().c_str());
auto blocks = find_blocks_in_function(data.linked_data, segment_id, func); auto blocks = find_blocks_in_function(data.linked_data, segment_id, func);
total_basic_blocks += blocks.size(); total_basic_blocks += blocks.size();
func.basic_blocks = blocks; func.basic_blocks = blocks;
total_functions++; total_functions++;
if (!func.suspected_asm) { if (!func.suspected_asm) {
func.analyze_prologue(data.linked_data); func.analyze_prologue(data.linked_data);
func.cfg = build_cfg(data.linked_data, segment_id, func); func.cfg = build_cfg(data.linked_data, segment_id, func);
@ -639,10 +642,8 @@ void ObjectFileDB::analyze_functions() {
if (func.basic_blocks.size() > 1 && !func.suspected_asm) { if (func.basic_blocks.size() > 1 && !func.suspected_asm) {
if (func.cfg->is_fully_resolved()) { if (func.cfg->is_fully_resolved()) {
} else { } else {
if (!func.guessed_name.empty()) { unresolved_by_length[func.end_word - func.start_word].push_back(
unresolved_by_length[func.end_word - func.start_word].push_back( func.guessed_name.to_string());
func.guessed_name.to_string());
}
} }
} }
@ -673,11 +674,11 @@ void ObjectFileDB::analyze_functions() {
printf(" %d/%d cfgs converted to ir (%.2f%%)\n", successful_cfg_irs, non_asm_funcs, printf(" %d/%d cfgs converted to ir (%.2f%%)\n", successful_cfg_irs, non_asm_funcs,
100.f * float(successful_cfg_irs) / float(non_asm_funcs)); 100.f * float(successful_cfg_irs) / float(non_asm_funcs));
// for (auto& kv : unresolved_by_length) { for (auto& kv : unresolved_by_length) {
// printf("LEN %d\n", kv.first); printf("LEN %d\n", kv.first);
// for (auto& x : kv.second) { for (auto& x : kv.second) {
// printf(" %s\n", x.c_str()); printf(" %s\n", x.c_str());
// } }
// } }
} }
} }

View file

@ -29,8 +29,162 @@
"find_basic_blocks":true, "find_basic_blocks":true,
"asm_functions_by_name":[ "asm_functions_by_name":[
// functions which have inline assembly
"unpack-comp-huf", "(method 29 collide-shape-prim-group)","find-knot-span","dma-send-no-scratch",
"raw-ray-sphere-intersect","(method 9 bounding-box)","(method 9 matrix)","shadow-find-single-edges",
"generic-tie-dma-to-spad-sync","ray-cylinder-intersect","shadow-scissor-edges","(method 42 collide-shape)",
"(method 9 texture-page-dir)",
"(method 24 collide-shape-prim)",
"(method 23 collide-shape-prim-group)",
"shadow-find-double-edges",
"(method 16 collide-shape-prim)",
"(method 15 collide-shape-prim-group)",
"generic-tie-upload-next",
"(method 17 collide-edge-work)",
"(method 16 drawable-tree)",
"(method 10 collide-mesh)",
"particle-adgif",
"(method 14 collide-shape-prim-group)",
"(method 12 collide-shape-prim-group)",
"(method 14 bounding-box)",
"process-drawable-birth-fuel-cell",
"(method 13 collide-shape-prim-group)",
"ray-triangle-intersect",
"(method 20 collide-shape-prim-group)",
"(method 10 collide-puss-work)",
"setup-blerc-chains-for-one-fragment",
"curve-evaluate!",
"(method 16 collide-edge-work)",
"(method 9 collide-cache-prim)",
"(method 11 collide-mesh)",
"stats-tfrag-asm",
"(method 10 collide-cache-prim)",
"high-speed-reject",
"(method 12 collide-mesh)",
"(method 19 collide-cache)",
"(method 9 collide-puss-work)",
"(method 29 collide-cache)",
"time-of-day-interp-colors-scratch",
"(method 30 collide-cache)",
"calc-animation-from-spr",
"(method 14 collide-shape-prim-mesh)",
"(method 12 collide-shape-prim-mesh)",
"(method 19 process-drawable)",
"(method 13 collide-shape-prim-mesh)",
"moving-sphere-triangle-intersect",
"(method 14 collide-mesh)",
"circle-circle-xz-intersect",
"get-string-length",
"draw-node-cull",
"collide-probe-node",
"(method 28 collide-cache)",
"(method 26 collide-cache)",
"load-game-text-info",
"(method 27 collide-cache)",
"clip-polygon-against-positive-hyperplane",
"sp-process-block-2d",
"sp-init-fields!",
"clip-polygon-against-negative-hyperplane",
"(method 9 edge-grab-info)",
"(method 18 collide-edge-work)",
"sp-process-block-3d",
"time-of-day-interp-colors",
"(method 23 collide-shape-prim-sphere)",
"(method 15 collide-edge-work)",
"(method 15 collide-mesh)",
"(method 21 collide-cache)",
"mercneric-shader-asm",
"shadow-execute",
"(method 16 level)",
"(method 40 collide-shape)",
"(method 32 collide-cache)",
"bones-mtx-calc",
"draw-inline-array-prototype-tie-near-asm",
"decompress-fixed-data-to-accumulator",
"draw-inline-array-prototype-tie-asm",
"(method 10 external-art-buffer)",
"level-update-after-load",
"draw-inline-array-prototype-tie-generic-asm",
"draw-inline-array-tfrag-near",
"collide-probe-instance-tie",
"draw-inline-array-tfrag",
"mercneric-matrix-asm",
"(method 32 nav-control)",
"(method 11 fact-info-target)",
"mercneric-convert",
"generic-envmap-only-proc",
"draw-inline-array-instance-tie",
"generic-tie-convert-proc",
"draw-string",
"draw-inline-array-instance-shrub",
"generic-tie-convert",
"(anon-function 2503)",
"(anon-function 5635)",
"(anon-function 2504)",
"(anon-function 2502)",
"(anon-function 2501)",
"(anon-function 2505)",
"(anon-function 2500)",
"(anon-function 3717)",
"rand-uint31-gen",
"dma-sync-with-count",
"ripple-create-wave-table",
"generic-debug-light-proc",
"draw-bones-hud",
"(method 27 ropebridge)",
"ocean-interp-wave",
"ocean-generate-verts",
"draw-large-polygon-ocean",
"(method 23 collide-shape-prim-mesh)",
"(method 13 collide-mesh)",
"draw-boundary-polygon",
"draw-large-polygon",
"update-mood-lava",
"update-mood-lightning",
"memcpy",
"background-upload-vu0",
"upload-vis-bits",
"generic-envmap-dproc",
"generic-prepare-dma-double",
"generic-envmap-proc",
"generic-light-proc",
"generic-merc-execute-asm",
"generic-merc-init-asm",
"shadow-calc-dual-verts",
"shadow-scissor-top",
"shadow-find-facing-single-tris",
"shadow-find-facing-double-tris",
"shadow-add-verts",
"shadow-add-facing-single-tris",
"shadow-add-single-edges",
"shadow-add-double-tris",
"shadow-add-double-edges",
"test-func",
"(method 14 collide-cache)",
"symlink2",
"draw-bones-merc",
"dma-count-until-done",
"symlink3",
"blerc-execute",
"merc-dma-chain-to-spr",
"ripple-matrix-scale",
"ripple-apply-wave-table",
"ripple-execute-init",
"bones-mtx-calc-execute",
"texscroll-execute",
"generic-light",
"generic-no-light",
"generic-no-light+envmap",
"generic-dma-from-spr",
"upload-vu0-program",
"generic-merc-execute-all",
"closest-pt-in-triangle",
"(method 11 sparticle-launch-control)",
"(anon-function 3751)",
"look-for-points-of-interest",
// gcommon // gcommon
"min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
// pskernel // pskernel
"resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception", "resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception",