mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-26 08:55:53 +00:00
Add CFG to IR decompiler pass (#60)
* add some more cfg ir stuff * add cond with else * add type of recognition * add cond to compare conversion * finally all conds are passing * started sc recognize, but ash min and max should be recognized first * fix ash showing up as sc * add abs * fix merge issues * try building goos with optimizations on * sc mostly working, still need to fix right aligned nesting * ands and ors are converting correctly now * clean up
This commit is contained in:
parent
b102d22dd9
commit
a64dd6c90b
@ -6,7 +6,7 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Set default compile flags for GCC
|
||||
# optimization level can be set here. Note that game/ overwrites this for building game C++ code.
|
||||
|
@ -1,2 +1,9 @@
|
||||
|
||||
IF (WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2")
|
||||
ELSE()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
ENDIF()
|
||||
|
||||
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp PrettyPrinter.cpp)
|
||||
target_link_libraries(goos common_util fmt)
|
@ -15,6 +15,7 @@ add_executable(decompiler
|
||||
Disasm/InstructionMatching.cpp
|
||||
Function/CfgVtx.cpp Function/CfgVtx.h
|
||||
IR/BasicOpBuilder.cpp
|
||||
IR/CfgBuilder.cpp
|
||||
IR/IR.cpp)
|
||||
|
||||
target_link_libraries(decompiler
|
||||
|
@ -1721,8 +1721,9 @@ std::shared_ptr<ControlFlowGraph> build_cfg(const LinkedObjectFile& file, int se
|
||||
// printf("%s\n", cfg->to_dot().c_str());
|
||||
// printf("%s\n", cfg->to_form()->toStringPretty().c_str());
|
||||
|
||||
changed = changed || cfg->find_cond_w_else();
|
||||
changed = changed || cfg->find_cond_n_else();
|
||||
changed = changed || cfg->find_cond_w_else();
|
||||
|
||||
changed = changed || cfg->find_while_loop_top_level();
|
||||
changed = changed || cfg->find_seq_top_level();
|
||||
changed = changed || cfg->find_short_circuits();
|
||||
|
@ -572,8 +572,8 @@ bool Function::instr_starts_basic_op(int idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IR* Function::get_basic_op_at_instr(int idx) {
|
||||
return basic_ops.at(instruction_to_basic_op.at(idx)).get();
|
||||
std::shared_ptr<IR> Function::get_basic_op_at_instr(int idx) {
|
||||
return basic_ops.at(instruction_to_basic_op.at(idx));
|
||||
}
|
||||
|
||||
int Function::get_basic_op_count() {
|
||||
|
@ -68,10 +68,12 @@ class Function {
|
||||
void add_basic_op(std::shared_ptr<IR> op, int start_instr, int end_instr);
|
||||
bool has_basic_ops() { return !basic_ops.empty(); }
|
||||
bool instr_starts_basic_op(int idx);
|
||||
IR* get_basic_op_at_instr(int idx);
|
||||
std::shared_ptr<IR> get_basic_op_at_instr(int idx);
|
||||
int get_basic_op_count();
|
||||
int get_failed_basic_op_count();
|
||||
|
||||
std::shared_ptr<IR> ir = nullptr;
|
||||
|
||||
int segment = -1;
|
||||
int start_word = -1;
|
||||
int end_word = -1; // not inclusive, but does include padding.
|
||||
|
@ -511,6 +511,28 @@ std::shared_ptr<IR> try_sll(Instruction& instr, int idx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<IR> try_dsrav(Instruction& instr, int idx) {
|
||||
if (is_gpr_3(instr, InstructionKind::DSRAV, {}, {}, {}) &&
|
||||
!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::RIGHT_SHIFT_ARITH,
|
||||
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_dsrlv(Instruction& instr, int idx) {
|
||||
if (is_gpr_3(instr, InstructionKind::DSRLV, {}, {}, {}) &&
|
||||
!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::RIGHT_SHIFT_LOGIC,
|
||||
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) {
|
||||
if (instr.kind == InstructionKind::SW && instr.get_src(1).is_sym() &&
|
||||
instr.get_src(2).is_reg(make_gpr(Reg::S7))) {
|
||||
@ -691,6 +713,28 @@ BranchDelay get_branch_delay(Instruction& i, int idx) {
|
||||
BranchDelay b(BranchDelay::SET_REG_TRUE);
|
||||
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
|
||||
return b;
|
||||
} else if (i.kind == InstructionKind::LW && i.get_src(1).is_reg(make_gpr(Reg::S7)) &&
|
||||
i.get_src(0).is_sym()) {
|
||||
if (i.get_src(0).get_sym() == "binteger") {
|
||||
BranchDelay b(BranchDelay::SET_BINTEGER);
|
||||
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
|
||||
return b;
|
||||
} else if (i.get_src(0).get_sym() == "pair") {
|
||||
BranchDelay b(BranchDelay::SET_PAIR);
|
||||
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
|
||||
return b;
|
||||
}
|
||||
} else if (i.kind == InstructionKind::DSLLV) {
|
||||
BranchDelay b(BranchDelay::DSLLV);
|
||||
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
|
||||
b.source = make_reg(i.get_src(0).get_reg(), idx);
|
||||
b.source2 = make_reg(i.get_src(1).get_reg(), idx);
|
||||
return b;
|
||||
} else if (is_gpr_3(i, InstructionKind::DSUBU, {}, make_gpr(Reg::R0), {})) {
|
||||
BranchDelay b(BranchDelay::NEGATE);
|
||||
b.destination = make_reg(i.get_dst(0).get_reg(), idx);
|
||||
b.source = make_reg(i.get_src(1).get_reg(), idx);
|
||||
return b;
|
||||
}
|
||||
BranchDelay b(BranchDelay::UNKNOWN);
|
||||
return b;
|
||||
@ -719,6 +763,10 @@ std::shared_ptr<IR> try_bnel(Instruction& instr, Instruction& next_instr, int id
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::TRUTHY, make_reg(instr.get_src(1).get_reg(), idx), nullptr, nullptr),
|
||||
instr.get_src(2).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
} else if (instr.kind == InstructionKind::BNEL && instr.get_src(1).is_reg(make_gpr(Reg::R0))) {
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::NONZERO, make_reg(instr.get_src(0).get_reg(), idx), nullptr, nullptr),
|
||||
instr.get_src(2).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
} else if (instr.kind == InstructionKind::BNEL) {
|
||||
// return std::make_shared<IR_Branch2>(IR_Branch2::NOT_EQUAL, instr.get_src(2).get_label(),
|
||||
// make_reg(instr.get_src(0).get_reg(), idx),
|
||||
@ -733,7 +781,13 @@ std::shared_ptr<IR> try_beql(Instruction& instr, Instruction& next_instr, int id
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::FALSE, make_reg(instr.get_src(1).get_reg(), idx), nullptr, nullptr),
|
||||
instr.get_src(2).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
} else if (instr.kind == InstructionKind::BEQL) {
|
||||
} else if (instr.kind == InstructionKind::BEQL && instr.get_src(1).is_reg(make_gpr(Reg::R0))) {
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::ZERO, make_reg(instr.get_src(0).get_reg(), idx), nullptr, nullptr),
|
||||
instr.get_src(2).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
}
|
||||
|
||||
else if (instr.kind == InstructionKind::BEQL) {
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::EQUAL, make_reg(instr.get_src(0).get_reg(), idx),
|
||||
make_reg(instr.get_src(1).get_reg(), idx), nullptr),
|
||||
@ -761,6 +815,36 @@ std::shared_ptr<IR> try_beq(Instruction& instr, Instruction& next_instr, int idx
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<IR> try_bgtzl(Instruction& instr, Instruction& next_instr, int idx) {
|
||||
if (instr.kind == InstructionKind::BGTZL) {
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::GREATER_THAN_ZERO_SIGNED, make_reg(instr.get_src(0).get_reg(), idx),
|
||||
nullptr, nullptr),
|
||||
instr.get_src(1).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<IR> try_bgezl(Instruction& instr, Instruction& next_instr, int idx) {
|
||||
if (instr.kind == InstructionKind::BGEZL) {
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::GEQ_ZERO_SIGNED, make_reg(instr.get_src(0).get_reg(), idx), nullptr,
|
||||
nullptr),
|
||||
instr.get_src(1).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<IR> try_bltzl(Instruction& instr, Instruction& next_instr, int idx) {
|
||||
if (instr.kind == InstructionKind::BLTZL) {
|
||||
return std::make_shared<IR_Branch>(
|
||||
Condition(Condition::LESS_THAN_ZERO, make_reg(instr.get_src(0).get_reg(), idx), nullptr,
|
||||
nullptr),
|
||||
instr.get_src(1).get_label(), get_branch_delay(next_instr, idx), true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<IR> try_daddiu(Instruction& i0, Instruction& i1, int idx) {
|
||||
if (i0.kind == InstructionKind::DADDIU && i1.kind == InstructionKind::MOVN &&
|
||||
i0.get_src(0).get_reg() == make_gpr(Reg::S7)) {
|
||||
@ -1135,6 +1219,7 @@ std::shared_ptr<IR> try_lwu(Instruction& i0,
|
||||
} // namespace
|
||||
|
||||
void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjectFile* file) {
|
||||
(void)file;
|
||||
for (int instr = block.start_word; instr < block.end_word; instr++) {
|
||||
auto& i = func->instructions.at(instr);
|
||||
|
||||
@ -1214,6 +1299,15 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
|
||||
case InstructionKind::BEQ:
|
||||
result = try_beq(i, next, instr);
|
||||
break;
|
||||
case InstructionKind::BGTZL:
|
||||
result = try_bgtzl(i, next, instr);
|
||||
break;
|
||||
case InstructionKind::BGEZL:
|
||||
result = try_bgezl(i, next, instr);
|
||||
break;
|
||||
case InstructionKind::BLTZL:
|
||||
result = try_bltzl(i, next, instr);
|
||||
break;
|
||||
case InstructionKind::BEQL:
|
||||
result = try_beql(i, next, instr);
|
||||
break;
|
||||
@ -1384,6 +1478,12 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
|
||||
case InstructionKind::CVTSW:
|
||||
result = try_cvtsw(i, instr);
|
||||
break;
|
||||
case InstructionKind::DSRAV:
|
||||
result = try_dsrav(i, instr);
|
||||
break;
|
||||
case InstructionKind::DSRLV:
|
||||
result = try_dsrlv(i, instr);
|
||||
break;
|
||||
default:
|
||||
result = nullptr;
|
||||
}
|
||||
@ -1396,7 +1496,7 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec
|
||||
// everything failed
|
||||
if (!result) {
|
||||
// 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);
|
||||
} else {
|
||||
func->add_basic_op(result, instr, instr + length);
|
||||
|
1012
decompiler/IR/CfgBuilder.cpp
Normal file
1012
decompiler/IR/CfgBuilder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10
decompiler/IR/CfgBuilder.h
Normal file
10
decompiler/IR/CfgBuilder.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class IR;
|
||||
class Function;
|
||||
class LinkedObjectFile;
|
||||
class ControlFlowGraph;
|
||||
|
||||
std::shared_ptr<IR> build_cfg_ir(Function& function, ControlFlowGraph& cfg, LinkedObjectFile& file);
|
@ -1,21 +1,61 @@
|
||||
#include "IR.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
|
||||
std::vector<std::shared_ptr<IR>> IR::get_all_ir(LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
std::vector<std::shared_ptr<IR>> result;
|
||||
get_children(&result);
|
||||
size_t last_checked = 0;
|
||||
size_t last_last_checked = -1;
|
||||
|
||||
while (last_checked != last_last_checked) {
|
||||
last_last_checked = last_checked;
|
||||
auto end_of_check = result.size();
|
||||
for (size_t i = last_checked; i < end_of_check; i++) {
|
||||
auto it = result.at(i).get();
|
||||
assert(it);
|
||||
it->get_children(&result);
|
||||
}
|
||||
last_checked = end_of_check;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string IR::print(const LinkedObjectFile& file) const {
|
||||
// return to_form(file)->toStringPretty();
|
||||
return pretty_print::to_string(to_form(file));
|
||||
}
|
||||
|
||||
goos::Object IR_Failed::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::build_list("INVALID-OPERATION");
|
||||
}
|
||||
|
||||
void IR_Failed::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_Register::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::to_symbol(reg.to_charp());
|
||||
}
|
||||
|
||||
void IR_Register::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_Set::to_form(const LinkedObjectFile& file) const {
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), dst->to_form(file),
|
||||
src->to_form(file));
|
||||
}
|
||||
|
||||
void IR_Set::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
// note that we are not returning clobber here because it shouldn't contain anything that
|
||||
// the IR simplification code should touch.
|
||||
output->push_back(dst);
|
||||
output->push_back(src);
|
||||
}
|
||||
|
||||
goos::Object IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
std::string store_operator;
|
||||
switch (kind) {
|
||||
@ -51,26 +91,33 @@ goos::Object IR_Store::to_form(const LinkedObjectFile& file) const {
|
||||
src->to_form(file));
|
||||
}
|
||||
|
||||
goos::Object IR_Failed::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::build_list("INVALID-OPERATION");
|
||||
}
|
||||
|
||||
goos::Object IR_Symbol::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::to_symbol("'" + name);
|
||||
}
|
||||
|
||||
void IR_Symbol::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_SymbolValue::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::to_symbol(name);
|
||||
}
|
||||
|
||||
void IR_SymbolValue::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_StaticAddress::to_form(const LinkedObjectFile& file) const {
|
||||
// return pretty_print::build_list(pretty_print::to_symbol("&"), file.get_label_name(label_id));
|
||||
return pretty_print::to_symbol(file.get_label_name(label_id));
|
||||
}
|
||||
|
||||
void IR_StaticAddress::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
std::string load_operator;
|
||||
switch (kind) {
|
||||
@ -119,6 +166,10 @@ goos::Object IR_Load::to_form(const LinkedObjectFile& file) const {
|
||||
return pretty_print::build_list(pretty_print::to_symbol(load_operator), location->to_form(file));
|
||||
}
|
||||
|
||||
void IR_Load::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(location);
|
||||
}
|
||||
|
||||
goos::Object IR_FloatMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
@ -148,6 +199,15 @@ goos::Object IR_FloatMath2::to_form(const LinkedObjectFile& file) const {
|
||||
arg1->to_form(file));
|
||||
}
|
||||
|
||||
void IR_FloatMath2::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(arg0);
|
||||
output->push_back(arg1);
|
||||
}
|
||||
|
||||
void IR_FloatMath1::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(arg);
|
||||
}
|
||||
|
||||
goos::Object IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
@ -203,18 +263,30 @@ goos::Object IR_IntMath2::to_form(const LinkedObjectFile& file) const {
|
||||
arg1->to_form(file));
|
||||
}
|
||||
|
||||
void IR_IntMath2::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(arg0);
|
||||
output->push_back(arg1);
|
||||
}
|
||||
|
||||
goos::Object IR_IntMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
case NOT:
|
||||
math_operator = "lognot";
|
||||
break;
|
||||
case ABS:
|
||||
math_operator = "abs.si";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return pretty_print::build_list(pretty_print::to_symbol(math_operator), arg->to_form(file));
|
||||
}
|
||||
|
||||
void IR_IntMath1::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(arg);
|
||||
}
|
||||
|
||||
goos::Object IR_FloatMath1::to_form(const LinkedObjectFile& file) const {
|
||||
std::string math_operator;
|
||||
switch (kind) {
|
||||
@ -244,11 +316,19 @@ goos::Object IR_Call::to_form(const LinkedObjectFile& file) const {
|
||||
return pretty_print::build_list("call!");
|
||||
}
|
||||
|
||||
void IR_Call::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_IntegerConstant::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::to_symbol(std::to_string(value));
|
||||
}
|
||||
|
||||
void IR_IntegerConstant::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
switch (kind) {
|
||||
@ -263,6 +343,20 @@ goos::Object BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
case SET_REG_REG:
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
source->to_form(file));
|
||||
case SET_BINTEGER:
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
"binteger");
|
||||
case SET_PAIR:
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
"pair");
|
||||
case DSLLV:
|
||||
return pretty_print::build_list(
|
||||
pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
pretty_print::build_list(pretty_print::to_symbol("shl"), source->to_form(file),
|
||||
source2->to_form(file)));
|
||||
case NEGATE:
|
||||
return pretty_print::build_list(pretty_print::to_symbol("set!"), destination->to_form(file),
|
||||
pretty_print::build_list("-", source->to_form(file)));
|
||||
case UNKNOWN:
|
||||
return pretty_print::build_list("unknown-branch-delay");
|
||||
default:
|
||||
@ -270,6 +364,20 @@ goos::Object BranchDelay::to_form(const LinkedObjectFile& file) const {
|
||||
}
|
||||
}
|
||||
|
||||
void BranchDelay::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
if (destination) {
|
||||
output->push_back(destination);
|
||||
}
|
||||
|
||||
if (source) {
|
||||
output->push_back(source);
|
||||
}
|
||||
|
||||
if (source2) {
|
||||
output->push_back(source2);
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object IR_Nop::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::build_list("nop!");
|
||||
@ -296,14 +404,108 @@ int Condition::num_args() const {
|
||||
case NONZERO:
|
||||
case FALSE:
|
||||
case TRUTHY:
|
||||
case GREATER_THAN_ZERO_SIGNED:
|
||||
case GEQ_ZERO_SIGNED:
|
||||
case LESS_THAN_ZERO:
|
||||
case LEQ_ZERO_SIGNED:
|
||||
return 1;
|
||||
case ALWAYS:
|
||||
case NEVER:
|
||||
return 0;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Condition::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
if (src0) {
|
||||
output->push_back(src0);
|
||||
}
|
||||
|
||||
if (src1) {
|
||||
output->push_back(src1);
|
||||
}
|
||||
}
|
||||
|
||||
void Condition::invert() {
|
||||
switch (kind) {
|
||||
case NOT_EQUAL:
|
||||
kind = EQUAL;
|
||||
break;
|
||||
case EQUAL:
|
||||
kind = NOT_EQUAL;
|
||||
break;
|
||||
case LESS_THAN_SIGNED:
|
||||
kind = GEQ_SIGNED;
|
||||
break;
|
||||
case GREATER_THAN_SIGNED:
|
||||
kind = LEQ_SIGNED;
|
||||
break;
|
||||
case LEQ_SIGNED:
|
||||
kind = GREATER_THAN_SIGNED;
|
||||
break;
|
||||
case GEQ_SIGNED:
|
||||
kind = LESS_THAN_SIGNED;
|
||||
break;
|
||||
case GREATER_THAN_ZERO_SIGNED:
|
||||
kind = LEQ_ZERO_SIGNED;
|
||||
break;
|
||||
case LEQ_ZERO_SIGNED:
|
||||
kind = GREATER_THAN_ZERO_SIGNED;
|
||||
break;
|
||||
case LESS_THAN_ZERO:
|
||||
kind = GEQ_ZERO_SIGNED;
|
||||
break;
|
||||
case GEQ_ZERO_SIGNED:
|
||||
kind = LESS_THAN_ZERO;
|
||||
break;
|
||||
case LESS_THAN_UNSIGNED:
|
||||
kind = GEQ_UNSIGNED;
|
||||
break;
|
||||
case GREATER_THAN_UNSIGNED:
|
||||
kind = LEQ_UNSIGNED;
|
||||
break;
|
||||
case LEQ_UNSIGNED:
|
||||
kind = GREATER_THAN_UNSIGNED;
|
||||
break;
|
||||
case GEQ_UNSIGNED:
|
||||
kind = LESS_THAN_UNSIGNED;
|
||||
break;
|
||||
case ZERO:
|
||||
kind = NONZERO;
|
||||
break;
|
||||
case NONZERO:
|
||||
kind = ZERO;
|
||||
break;
|
||||
case FALSE:
|
||||
kind = TRUTHY;
|
||||
break;
|
||||
case TRUTHY:
|
||||
kind = FALSE;
|
||||
break;
|
||||
case ALWAYS:
|
||||
kind = NEVER;
|
||||
break;
|
||||
case NEVER:
|
||||
kind = ALWAYS;
|
||||
break;
|
||||
case FLOAT_EQUAL:
|
||||
kind = FLOAT_NOT_EQUAL;
|
||||
break;
|
||||
case FLOAT_NOT_EQUAL:
|
||||
kind = FLOAT_EQUAL;
|
||||
break;
|
||||
case FLOAT_LESS_THAN:
|
||||
kind = FLOAT_GEQ;
|
||||
break;
|
||||
case FLOAT_GEQ:
|
||||
kind = FLOAT_LESS_THAN;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object Condition::to_form(const LinkedObjectFile& file) const {
|
||||
int nargs = num_args();
|
||||
std::string condtion_operator;
|
||||
@ -353,6 +555,9 @@ goos::Object Condition::to_form(const LinkedObjectFile& file) const {
|
||||
case ALWAYS:
|
||||
condtion_operator = "'#t";
|
||||
break;
|
||||
case NEVER:
|
||||
condtion_operator = "'#f";
|
||||
break;
|
||||
case FLOAT_EQUAL:
|
||||
condtion_operator = "=.f";
|
||||
break;
|
||||
@ -365,6 +570,18 @@ goos::Object Condition::to_form(const LinkedObjectFile& file) const {
|
||||
case FLOAT_GEQ:
|
||||
condtion_operator = ">=.f";
|
||||
break;
|
||||
case GREATER_THAN_ZERO_SIGNED:
|
||||
condtion_operator = ">0.si";
|
||||
break;
|
||||
case GEQ_ZERO_SIGNED:
|
||||
condtion_operator = ">=0.si";
|
||||
break;
|
||||
case LESS_THAN_ZERO:
|
||||
condtion_operator = "<0.si";
|
||||
break;
|
||||
case LEQ_ZERO_SIGNED:
|
||||
condtion_operator = "<=0.si";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@ -392,11 +609,195 @@ goos::Object IR_Branch::to_form(const LinkedObjectFile& file) const {
|
||||
pretty_print::to_symbol(file.get_label_name(dest_label_idx)), branch_delay.to_form(file));
|
||||
}
|
||||
|
||||
void IR_Branch::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
condition.get_children(output);
|
||||
branch_delay.get_children(output);
|
||||
}
|
||||
|
||||
goos::Object IR_Compare::to_form(const LinkedObjectFile& file) const {
|
||||
return condition.to_form(file);
|
||||
}
|
||||
|
||||
void IR_Compare::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
condition.get_children(output);
|
||||
}
|
||||
|
||||
goos::Object IR_Suspend::to_form(const LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
return pretty_print::build_list("suspend!");
|
||||
}
|
||||
}
|
||||
|
||||
void IR_Nop::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
void IR_Suspend::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
(void)output;
|
||||
}
|
||||
|
||||
goos::Object IR_Begin::to_form(const LinkedObjectFile& file) const {
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("begin"));
|
||||
for (auto& x : forms) {
|
||||
list.push_back(x->to_form(file));
|
||||
}
|
||||
return pretty_print::build_list(list);
|
||||
}
|
||||
|
||||
void IR_Begin::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
for (auto& x : forms) {
|
||||
output->push_back(x);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void print_inlining_begin(std::vector<goos::Object>* output, IR* ir, const LinkedObjectFile& file) {
|
||||
auto as_begin = dynamic_cast<IR_Begin*>(ir);
|
||||
if (as_begin) {
|
||||
for (auto& x : as_begin->forms) {
|
||||
output->push_back(x->to_form(file));
|
||||
}
|
||||
} else {
|
||||
output->push_back(ir->to_form(file));
|
||||
}
|
||||
}
|
||||
|
||||
bool is_single_expression(IR* in) {
|
||||
return !dynamic_cast<IR_Begin*>(in);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
goos::Object IR_WhileLoop::to_form(const LinkedObjectFile& file) const {
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("while"));
|
||||
list.push_back(condition->to_form(file));
|
||||
print_inlining_begin(&list, body.get(), file);
|
||||
return pretty_print::build_list(list);
|
||||
}
|
||||
|
||||
void IR_WhileLoop::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 {
|
||||
// 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.
|
||||
if (entries.size() == 1 && is_single_expression(entries.front().body.get()) &&
|
||||
is_single_expression(else_ir.get())) {
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("if"));
|
||||
list.push_back(entries.front().condition->to_form(file));
|
||||
list.push_back(entries.front().body->to_form(file));
|
||||
list.push_back(else_ir->to_form(file));
|
||||
return pretty_print::build_list(list);
|
||||
} else {
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("cond"));
|
||||
for (auto& e : entries) {
|
||||
std::vector<goos::Object> entry;
|
||||
entry.push_back(e.condition->to_form(file));
|
||||
print_inlining_begin(&entry, e.body.get(), file);
|
||||
list.push_back(pretty_print::build_list(entry));
|
||||
}
|
||||
std::vector<goos::Object> else_form;
|
||||
else_form.push_back(pretty_print::to_symbol("else"));
|
||||
print_inlining_begin(&else_form, else_ir.get(), file);
|
||||
list.push_back(pretty_print::build_list(else_form));
|
||||
return pretty_print::build_list(list);
|
||||
}
|
||||
}
|
||||
|
||||
void IR_CondWithElse::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
for (auto& e : entries) {
|
||||
output->push_back(e.condition);
|
||||
output->push_back(e.body);
|
||||
}
|
||||
output->push_back(else_ir);
|
||||
}
|
||||
|
||||
goos::Object IR_GetRuntimeType::to_form(const LinkedObjectFile& file) const {
|
||||
std::vector<goos::Object> list = {pretty_print::to_symbol("type-of"), object->to_form(file)};
|
||||
return pretty_print::build_list(list);
|
||||
}
|
||||
|
||||
void IR_GetRuntimeType::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(object);
|
||||
}
|
||||
|
||||
goos::Object IR_Cond::to_form(const LinkedObjectFile& file) const {
|
||||
if (entries.size() == 1 && is_single_expression(entries.front().body.get())) {
|
||||
// print as an if statement if we can put the body in a single form.
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("if"));
|
||||
list.push_back(entries.front().condition->to_form(file));
|
||||
list.push_back(entries.front().body->to_form(file));
|
||||
return pretty_print::build_list(list);
|
||||
} else if (entries.size() == 1) {
|
||||
// turn into a when if the body requires multiple forms
|
||||
// todo check to see if the condition starts with a NOT and this can be simplified to an
|
||||
// unless.
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("when"));
|
||||
list.push_back(entries.front().condition->to_form(file));
|
||||
print_inlining_begin(&list, entries.front().body.get(), file);
|
||||
return pretty_print::build_list(list);
|
||||
} else {
|
||||
std::vector<goos::Object> list;
|
||||
list.push_back(pretty_print::to_symbol("cond"));
|
||||
for (auto& e : entries) {
|
||||
std::vector<goos::Object> entry;
|
||||
entry.push_back(e.condition->to_form(file));
|
||||
print_inlining_begin(&entry, e.body.get(), file);
|
||||
list.push_back(pretty_print::build_list(entry));
|
||||
}
|
||||
return pretty_print::build_list(list);
|
||||
}
|
||||
}
|
||||
|
||||
void IR_Cond::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
for (auto& e : entries) {
|
||||
output->push_back(e.condition);
|
||||
output->push_back(e.body);
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object IR_ShortCircuit::to_form(const LinkedObjectFile& file) const {
|
||||
std::vector<goos::Object> forms;
|
||||
switch (kind) {
|
||||
case UNKNOWN:
|
||||
forms.push_back(pretty_print::to_symbol("unknown-sc"));
|
||||
break;
|
||||
case AND:
|
||||
forms.push_back(pretty_print::to_symbol("and"));
|
||||
break;
|
||||
case OR:
|
||||
forms.push_back(pretty_print::to_symbol("or"));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
for (auto& x : entries) {
|
||||
forms.push_back(x.condition->to_form(file));
|
||||
}
|
||||
return pretty_print::build_list(forms);
|
||||
}
|
||||
|
||||
void IR_ShortCircuit::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
for (auto& x : entries) {
|
||||
output->push_back(x.condition);
|
||||
if (x.output) {
|
||||
output->push_back(x.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goos::Object IR_Ash::to_form(const LinkedObjectFile& file) const {
|
||||
return pretty_print::build_list(pretty_print::to_symbol(is_signed ? "ash.si" : "ash.ui"),
|
||||
value->to_form(file), shift_amount->to_form(file));
|
||||
}
|
||||
|
||||
void IR_Ash::get_children(std::vector<std::shared_ptr<IR>>* output) const {
|
||||
output->push_back(value);
|
||||
output->push_back(shift_amount);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define JAK_IR_H
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
@ -10,7 +11,9 @@ class LinkedObjectFile;
|
||||
class IR {
|
||||
public:
|
||||
virtual goos::Object to_form(const LinkedObjectFile& file) const = 0;
|
||||
std::vector<std::shared_ptr<IR>> get_all_ir(LinkedObjectFile& file) const;
|
||||
std::string print(const LinkedObjectFile& file) const;
|
||||
virtual void get_children(std::vector<std::shared_ptr<IR>>* output) const = 0;
|
||||
|
||||
bool is_basic_op = false;
|
||||
};
|
||||
@ -19,12 +22,14 @@ class IR_Failed : public IR {
|
||||
public:
|
||||
IR_Failed() = default;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Register : public IR {
|
||||
public:
|
||||
IR_Register(Register _reg, int _instr_idx) : reg(_reg), instr_idx(_instr_idx) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
Register reg;
|
||||
int instr_idx = -1;
|
||||
};
|
||||
@ -45,6 +50,7 @@ class IR_Set : public IR {
|
||||
IR_Set(Kind _kind, std::shared_ptr<IR> _dst, std::shared_ptr<IR> _src)
|
||||
: kind(_kind), dst(std::move(_dst)), src(std::move(_src)) {}
|
||||
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> dst, src;
|
||||
std::shared_ptr<IR> clobber = nullptr;
|
||||
};
|
||||
@ -60,34 +66,38 @@ class IR_Store : public IR_Set {
|
||||
|
||||
class IR_Symbol : public IR {
|
||||
public:
|
||||
IR_Symbol(std::string _name) : name(std::move(_name)) {}
|
||||
explicit IR_Symbol(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_SymbolValue : public IR {
|
||||
public:
|
||||
IR_SymbolValue(std::string _name) : name(std::move(_name)) {}
|
||||
explicit IR_SymbolValue(std::string _name) : name(std::move(_name)) {}
|
||||
std::string name;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_StaticAddress : public IR {
|
||||
public:
|
||||
IR_StaticAddress(int _label_id) : label_id(_label_id) {}
|
||||
explicit IR_StaticAddress(int _label_id) : label_id(_label_id) {}
|
||||
int label_id = -1;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Load : public IR {
|
||||
public:
|
||||
enum Kind { UNSIGNED, SIGNED, FLOAT } kind;
|
||||
|
||||
IR_Load(Kind _kind, int _size, const std::shared_ptr<IR>& _location)
|
||||
: kind(_kind), size(_size), location(_location) {}
|
||||
IR_Load(Kind _kind, int _size, std::shared_ptr<IR> _location)
|
||||
: kind(_kind), size(_size), location(std::move(_location)) {}
|
||||
int size;
|
||||
std::shared_ptr<IR> location;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath2 : public IR {
|
||||
@ -97,6 +107,7 @@ class IR_FloatMath2 : public IR {
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_FloatMath1 : public IR {
|
||||
@ -105,6 +116,7 @@ class IR_FloatMath1 : public IR {
|
||||
IR_FloatMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath2 : public IR {
|
||||
@ -130,20 +142,23 @@ class IR_IntMath2 : public IR {
|
||||
: kind(_kind), arg0(std::move(_arg0)), arg1(std::move(_arg1)) {}
|
||||
std::shared_ptr<IR> arg0, arg1;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_IntMath1 : public IR {
|
||||
public:
|
||||
enum Kind { NOT } kind;
|
||||
enum Kind { NOT, ABS } kind;
|
||||
IR_IntMath1(Kind _kind, std::shared_ptr<IR> _arg) : kind(_kind), arg(std::move(_arg)) {}
|
||||
std::shared_ptr<IR> arg;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Call : public IR {
|
||||
public:
|
||||
IR_Call() = default;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_IntegerConstant : public IR {
|
||||
@ -151,13 +166,25 @@ class IR_IntegerConstant : public IR {
|
||||
int64_t value;
|
||||
explicit IR_IntegerConstant(int64_t _value) : value(_value) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
struct BranchDelay {
|
||||
enum Kind { NOP, SET_REG_FALSE, SET_REG_TRUE, SET_REG_REG, UNKNOWN } kind;
|
||||
std::shared_ptr<IR> destination = nullptr, source = nullptr;
|
||||
BranchDelay(Kind _kind) : kind(_kind) {}
|
||||
enum Kind {
|
||||
NOP,
|
||||
SET_REG_FALSE,
|
||||
SET_REG_TRUE,
|
||||
SET_REG_REG,
|
||||
SET_BINTEGER,
|
||||
SET_PAIR,
|
||||
DSLLV,
|
||||
NEGATE,
|
||||
UNKNOWN
|
||||
} kind;
|
||||
std::shared_ptr<IR> destination = nullptr, source = nullptr, source2 = nullptr;
|
||||
explicit BranchDelay(Kind _kind) : kind(_kind) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const;
|
||||
};
|
||||
|
||||
struct Condition {
|
||||
@ -168,6 +195,10 @@ struct Condition {
|
||||
GREATER_THAN_SIGNED,
|
||||
LEQ_SIGNED,
|
||||
GEQ_SIGNED,
|
||||
GREATER_THAN_ZERO_SIGNED,
|
||||
LEQ_ZERO_SIGNED,
|
||||
LESS_THAN_ZERO,
|
||||
GEQ_ZERO_SIGNED,
|
||||
LESS_THAN_UNSIGNED,
|
||||
GREATER_THAN_UNSIGNED,
|
||||
LEQ_UNSIGNED,
|
||||
@ -177,6 +208,7 @@ struct Condition {
|
||||
FALSE,
|
||||
TRUTHY,
|
||||
ALWAYS,
|
||||
NEVER,
|
||||
FLOAT_EQUAL,
|
||||
FLOAT_NOT_EQUAL,
|
||||
FLOAT_LESS_THAN,
|
||||
@ -201,6 +233,8 @@ struct Condition {
|
||||
int num_args() const;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const;
|
||||
std::shared_ptr<IR> src0, src1, clobber;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const;
|
||||
void invert();
|
||||
};
|
||||
|
||||
class IR_Branch : public IR {
|
||||
@ -217,6 +251,7 @@ class IR_Branch : public IR {
|
||||
bool likely;
|
||||
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Compare : public IR {
|
||||
@ -226,18 +261,113 @@ class IR_Compare : public IR {
|
||||
Condition condition;
|
||||
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Nop : public IR {
|
||||
public:
|
||||
IR_Nop() = default;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Suspend : public IR {
|
||||
public:
|
||||
IR_Suspend() = default;
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Begin : public IR {
|
||||
public:
|
||||
IR_Begin() = default;
|
||||
explicit IR_Begin(const std::vector<std::shared_ptr<IR>>& _forms) : forms(std::move(_forms)) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
std::vector<std::shared_ptr<IR>> forms;
|
||||
};
|
||||
|
||||
class IR_WhileLoop : public IR {
|
||||
public:
|
||||
IR_WhileLoop(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 {
|
||||
public:
|
||||
struct Entry {
|
||||
std::shared_ptr<IR> condition = nullptr;
|
||||
std::shared_ptr<IR> body = nullptr;
|
||||
bool cleaned = false;
|
||||
};
|
||||
std::vector<Entry> entries;
|
||||
std::shared_ptr<IR> else_ir;
|
||||
IR_CondWithElse(std::vector<Entry> _entries, std::shared_ptr<IR> _else_ir)
|
||||
: entries(std::move(_entries)), else_ir(std::move(_else_ir)) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
// this one doesn't have an else statement. Will return false if none of the cases are taken.
|
||||
class IR_Cond : public IR {
|
||||
public:
|
||||
struct Entry {
|
||||
std::shared_ptr<IR> condition = nullptr;
|
||||
std::shared_ptr<IR> body = nullptr;
|
||||
std::shared_ptr<IR> false_destination = nullptr;
|
||||
bool cleaned = false;
|
||||
};
|
||||
std::vector<Entry> entries;
|
||||
explicit IR_Cond(std::vector<Entry> _entries) : entries(std::move(_entries)) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
// this will work on pairs, bintegers, or basics
|
||||
class IR_GetRuntimeType : public IR {
|
||||
public:
|
||||
std::shared_ptr<IR> object, clobber;
|
||||
IR_GetRuntimeType(std::shared_ptr<IR> _object, std::shared_ptr<IR> _clobber)
|
||||
: object(std::move(_object)), clobber(std::move(_clobber)) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_ShortCircuit : public IR {
|
||||
public:
|
||||
struct Entry {
|
||||
std::shared_ptr<IR> condition = nullptr;
|
||||
std::shared_ptr<IR> output = nullptr; // where the delay slot writes to.
|
||||
bool cleaned = false;
|
||||
};
|
||||
|
||||
enum Kind { UNKNOWN, AND, OR } kind = UNKNOWN;
|
||||
|
||||
std::shared_ptr<IR> final_result = nullptr; // the register that the final result goes in.
|
||||
|
||||
std::vector<Entry> entries;
|
||||
explicit IR_ShortCircuit(std::vector<Entry> _entries) : entries(std::move(_entries)) {}
|
||||
goos::Object to_form(const LinkedObjectFile& file) const override;
|
||||
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
|
||||
};
|
||||
|
||||
class IR_Ash : public IR {
|
||||
public:
|
||||
std::shared_ptr<IR> shift_amount, value, clobber;
|
||||
bool is_signed = true;
|
||||
IR_Ash(std::shared_ptr<IR> _shift_amount,
|
||||
std::shared_ptr<IR> _value,
|
||||
std::shared_ptr<IR> _clobber,
|
||||
bool _is_signed)
|
||||
: shift_amount(std::move(_shift_amount)),
|
||||
value(std::move(_value)),
|
||||
clobber(std::move(_clobber)),
|
||||
is_signed(_is_signed) {}
|
||||
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
|
||||
|
@ -622,6 +622,11 @@ std::string LinkedObjectFile::print_disassembly() {
|
||||
*/
|
||||
}
|
||||
|
||||
if (func.ir) {
|
||||
result += ";; ir\n";
|
||||
result += func.ir->print(*this);
|
||||
}
|
||||
|
||||
result += "\n\n\n";
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "decompiler/Function/BasicBlocks.h"
|
||||
#include "decompiler/IR/BasicOpBuilder.h"
|
||||
#include "decompiler/IR/CfgBuilder.h"
|
||||
|
||||
/*!
|
||||
* Get a unique name for this object file.
|
||||
@ -591,17 +592,21 @@ void ObjectFileDB::analyze_functions() {
|
||||
// }
|
||||
}
|
||||
|
||||
int total_nontrivial_functions = 0;
|
||||
int total_resolved_nontrivial_functions = 0;
|
||||
int total_trivial_cfg_functions = 0;
|
||||
int total_named_functions = 0;
|
||||
int total_basic_ops = 0;
|
||||
int total_failed_basic_ops = 0;
|
||||
|
||||
int asm_funcs = 0;
|
||||
int non_asm_funcs = 0;
|
||||
int successful_cfg_irs = 0;
|
||||
|
||||
std::map<int, std::vector<std::string>> unresolved_by_length;
|
||||
if (get_config().find_basic_blocks) {
|
||||
timer.start();
|
||||
int total_basic_blocks = 0;
|
||||
for_each_function([&](Function& func, int segment_id, ObjectFileData& data) {
|
||||
// printf("in %s\n", func.guessed_name.to_string().c_str());
|
||||
auto blocks = find_blocks_in_function(data.linked_data, segment_id, func);
|
||||
total_basic_blocks += blocks.size();
|
||||
func.basic_blocks = blocks;
|
||||
@ -618,17 +623,21 @@ void ObjectFileDB::analyze_functions() {
|
||||
total_basic_ops += func.get_basic_op_count();
|
||||
total_failed_basic_ops += func.get_failed_basic_op_count();
|
||||
|
||||
func.ir = build_cfg_ir(func, *func.cfg, data.linked_data);
|
||||
non_asm_funcs++;
|
||||
if (func.ir) {
|
||||
successful_cfg_irs++;
|
||||
}
|
||||
|
||||
if (func.cfg->is_fully_resolved()) {
|
||||
resolved_cfg_functions++;
|
||||
}
|
||||
} else {
|
||||
resolved_cfg_functions++;
|
||||
asm_funcs++;
|
||||
}
|
||||
|
||||
if (func.basic_blocks.size() > 1 && !func.suspected_asm) {
|
||||
total_nontrivial_functions++;
|
||||
if (func.cfg->is_fully_resolved()) {
|
||||
total_resolved_nontrivial_functions++;
|
||||
} else {
|
||||
if (!func.guessed_name.empty()) {
|
||||
unresolved_by_length[func.end_word - func.start_word].push_back(
|
||||
@ -637,25 +646,32 @@ void ObjectFileDB::analyze_functions() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!func.suspected_asm && func.basic_blocks.size() <= 1) {
|
||||
total_trivial_cfg_functions++;
|
||||
}
|
||||
|
||||
if (!func.guessed_name.empty()) {
|
||||
total_named_functions++;
|
||||
}
|
||||
|
||||
// if (func.guessed_name.to_string() == "inspect") {
|
||||
// assert(false);
|
||||
// }
|
||||
});
|
||||
|
||||
printf("Found %d functions (%d with nontrivial cfgs)\n", total_functions,
|
||||
total_nontrivial_functions);
|
||||
printf("Found %d functions (%d with no control flow)\n", total_functions,
|
||||
total_trivial_cfg_functions);
|
||||
printf("Named %d/%d functions (%.2f%%)\n", total_named_functions, total_functions,
|
||||
100.f * float(total_named_functions) / float(total_functions));
|
||||
printf("Excluding %d asm functions\n", asm_funcs);
|
||||
printf("Found %d basic blocks in %.3f ms\n", total_basic_blocks, timer.getMs());
|
||||
printf(" %d/%d functions passed cfg analysis stage (%.2f%%)\n", resolved_cfg_functions,
|
||||
total_functions, 100.f * float(resolved_cfg_functions) / float(total_functions));
|
||||
printf(" %d/%d nontrivial cfg's resolved (%.2f%%)\n", total_resolved_nontrivial_functions,
|
||||
total_nontrivial_functions,
|
||||
100.f * float(total_resolved_nontrivial_functions) / float(total_nontrivial_functions));
|
||||
|
||||
non_asm_funcs, 100.f * float(resolved_cfg_functions) / float(non_asm_funcs));
|
||||
int successful_basic_ops = total_basic_ops - total_failed_basic_ops;
|
||||
printf(" %d/%d basic ops converted successfully (%.2f%%)\n", successful_basic_ops,
|
||||
total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops));
|
||||
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));
|
||||
|
||||
// for (auto& kv : unresolved_by_length) {
|
||||
// printf("LEN %d\n", kv.first);
|
||||
|
@ -30,12 +30,45 @@
|
||||
|
||||
"asm_functions_by_name":[
|
||||
// gcommon
|
||||
"ash", "abs", "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
|
||||
"min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!",
|
||||
|
||||
// pskernel
|
||||
"resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception",
|
||||
"kernel-read", "kernel-read-function", "kernel-write", "kernel-write-function", "kernel-copy-to-kernel-ram",
|
||||
|
||||
// this one needs more investigation. nothing looks weird about it but it fails...
|
||||
"camera-change-to",
|
||||
|
||||
// two back to back arithmetic shifts...
|
||||
"texture-relocate",
|
||||
|
||||
// this one fails due to false compaction where an else case has only a not expression in it.
|
||||
"master-is-hopeful-better?",
|
||||
|
||||
// fails for unknown reason
|
||||
"target-falling-anim-trans", "change-brother",
|
||||
|
||||
// merged right typecase... can probably handle this
|
||||
"cspace-inspect-tree",
|
||||
|
||||
// these are all valid, but use short circuiting branches in strange ways. There's probably a few compiler uses that we're not
|
||||
"(method 21 actor-link-info)","(method 20 actor-link-info)","(method 28 collide-shape-prim-mesh)", "(method 35 collide-shape)",
|
||||
"debug-menu-item-var-render", "(method 14 level)","add-blue-motion","anim-tester-add-newobj","(method 27 orb-cache-top)",
|
||||
|
||||
// real asm
|
||||
"cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", "render-boundary-quad",
|
||||
"(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp", "(method 11 collide-mesh-cache)",
|
||||
"(method 13 collide-edge-work)", "ambient-inspect",
|
||||
|
||||
"(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", "vblank-handler", "vif1-handler",
|
||||
"vif1-handler-debug", "entity-actor-count", "decompress-frame-data-pair-to-accumulator",
|
||||
"decompress-frame-data-to-accumulator", "normalize-frame-quaternions", "clear-frame-accumulator",
|
||||
"generic-copy-vtx-dclr-dtex", "generic-no-light-dproc-only", "generic-no-light-proc", "mercneric-bittable-asm",
|
||||
"generic-tie-decompress", "matrix-axis-sin-cos!", "matrix-axis-sin-cos-vu!", "generic-prepare-dma-single",
|
||||
"(method 13 collide-shape-prim-sphere)", "(method 14 collide-shape-prim-sphere)", "(method 12 collide-shape-prim-sphere)",
|
||||
"adgif-shader<-texture-with-update!", "generic-interp-dproc", "sprite-draw-distorters", "draw-bones", "(method 9 collide-mesh-cache)",
|
||||
"(method 18 collide-shape-prim-sphere)","birth-pickup-at-point",
|
||||
|
||||
"collide-do-primitives", "draw-bones-check-longest-edge-asm",
|
||||
"sp-launch-particles-var", "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)",
|
||||
"(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc",
|
||||
|
@ -1,5 +1,5 @@
|
||||
# We define our own compilation flags here.
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Set default compile flags for GCC
|
||||
# optimization level can be set here. Note that game/ overwrites this for building game C++ code.
|
||||
|
Loading…
x
Reference in New Issue
Block a user