[decompiler] cleanup to get atomic ops working on jak2 inputs (#1426)

* [decompiler] cleanup to get atomic ops working on jak2 inputs

* clang format
This commit is contained in:
water111
2022-06-08 18:34:52 -04:00
committed by GitHub
parent aff2f2e10c
commit ecb2781a89
30 changed files with 319 additions and 130 deletions
+34
View File
@@ -1,10 +1,13 @@
#pragma once
#include "common/versions.h"
/*!
* @file symbols.h
* The location of fixed symbols in the GOAL symbol table.
*/
namespace jak1_symbols {
constexpr int FIX_SYM_EMPTY_CAR = -0xc;
constexpr int FIX_SYM_EMPTY_PAIR = -0xa;
constexpr int FIX_SYM_EMPTY_CDR = -0x8;
@@ -76,3 +79,34 @@ constexpr int FIX_SYM_SOUND = 0x1b8; // ??
constexpr int FIX_SYM_DGO = 0x1c0; // ??
constexpr int FIX_SYM_TOP_LEVEL = 0x1c8; // ??
constexpr int FIX_FIXED_SYM_END_OFFSET = 0x1d0;
} // namespace jak1_symbols
namespace jak2_symbols {
constexpr int FIX_SYM_EMPTY_CAR = -0x8;
constexpr int FIX_SYM_EMPTY_PAIR = -0x7;
constexpr int FIX_SYM_EMPTY_CDR = -0x4;
constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register)
constexpr int FIX_SYM_TRUE = 0x4; // GOAL boolean #t
} // namespace jak2_symbols
constexpr int false_symbol_offset() {
return jak1_symbols::FIX_SYM_FALSE;
}
constexpr int true_symbol_offset(GameVersion version) {
switch (version) {
case GameVersion::Jak1:
return jak1_symbols::FIX_SYM_TRUE;
case GameVersion::Jak2:
return jak2_symbols::FIX_SYM_TRUE;
}
}
constexpr int empty_pair_offset(GameVersion version) {
switch (version) {
case GameVersion::Jak1:
return jak1_symbols::FIX_SYM_EMPTY_PAIR;
case GameVersion::Jak2:
return jak2_symbols::FIX_SYM_EMPTY_PAIR;
}
}
+1 -1
View File
@@ -2612,7 +2612,7 @@ std::shared_ptr<ControlFlowGraph> build_cfg(
Function& func,
const CondWithElseLengthHack& cond_with_else_hack,
const std::unordered_set<int>& blocks_ending_in_asm_br) {
// fmt::print("START {}\n", func.guessed_name.to_string());
// fmt::print("START {}\n", func.guessed_name.to_string());
auto cfg = std::make_shared<ControlFlowGraph>();
const auto& blocks = cfg->create_blocks(func.basic_blocks.size());
+37 -17
View File
@@ -508,27 +508,47 @@ void LinkedObjectFile::process_fp_relative_links() {
case InstructionKind::DADDU:
case InstructionKind::ADDU: {
ASSERT(prev_instr);
if (prev_instr->kind != InstructionKind::ORI) {
if (prev_instr->kind == InstructionKind::ORI) {
ASSERT(prev_instr->kind == InstructionKind::ORI);
int offset_reg_src_id = instr.kind == InstructionKind::DADDU ? 0 : 1;
auto offset_reg = instr.get_src(offset_reg_src_id).get_reg();
ASSERT(offset_reg == prev_instr->get_dst(0).get_reg());
ASSERT(offset_reg == prev_instr->get_src(0).get_reg());
auto& atom = prev_instr->get_imm_src();
int additional_offset = 0;
if (pprev_instr && pprev_instr->kind == InstructionKind::LUI) {
ASSERT(pprev_instr->get_dst(0).get_reg() == offset_reg);
additional_offset = (1 << 16) * pprev_instr->get_imm_src().get_imm();
pprev_instr->get_imm_src().set_label(
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
}
atom.set_label(
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
stats.n_fp_reg_use_resolved++;
} else if (prev_instr->kind == InstructionKind::DADDIU) {
/*
* Jak 2 has a new use of fp to access elements of a static array that looks like
* this:
* (set! v1 (* idx stride))
* daddiu v1, v1, 8128
* daddu v1, v1, fp
*/
auto val_plus_off_reg = prev_instr->get_dst(0).get_reg();
// it's possible that this isn't always the case, but works for all of jak 2
ASSERT(val_plus_off_reg == prev_instr->get_src(0).get_reg());
ASSERT(val_plus_off_reg == instr.get_src(0).get_reg());
ASSERT(val_plus_off_reg == instr.get_dst(0).get_reg());
auto& atom = prev_instr->get_imm_src();
atom.set_label(get_label_id_for(seg, current_fp + atom.get_imm()));
stats.n_fp_reg_use_resolved++;
} else {
lg::error("Failed to process fp relative links for (d)addu preceded by: {}",
prev_instr->to_string(labels));
return;
}
ASSERT(prev_instr->kind == InstructionKind::ORI);
int offset_reg_src_id = instr.kind == InstructionKind::DADDU ? 0 : 1;
auto offset_reg = instr.get_src(offset_reg_src_id).get_reg();
ASSERT(offset_reg == prev_instr->get_dst(0).get_reg());
ASSERT(offset_reg == prev_instr->get_src(0).get_reg());
auto& atom = prev_instr->get_imm_src();
int additional_offset = 0;
if (pprev_instr && pprev_instr->kind == InstructionKind::LUI) {
ASSERT(pprev_instr->get_dst(0).get_reg() == offset_reg);
additional_offset = (1 << 16) * pprev_instr->get_imm_src().get_imm();
pprev_instr->get_imm_src().set_label(
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
}
atom.set_label(
get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset));
stats.n_fp_reg_use_resolved++;
} break;
default:
+11 -11
View File
@@ -39,12 +39,6 @@ void ObjectFileDB::analyze_functions_ir2(
const Config& config,
const std::unordered_set<std::string>& skip_functions,
const std::unordered_map<std::string, std::unordered_set<std::string>>& skip_states) {
// First, do basic analysis on the top level:
lg::info("Using IR2 analysis...");
lg::info("Processing top-level functions...");
ir2_top_level_pass(config);
int total_file_count = 0;
for (auto& f : obj_files_by_name) {
total_file_count += f.second.size();
@@ -178,12 +172,12 @@ void ObjectFileDB::ir2_run_mips2c(const Config& config, ObjectFileData& data) {
for_each_function_def_order_in_obj(data, [&](Function& func, int) {
if (config.hacks.mips2c_functions_by_name.count(func.name())) {
lg::info("MIPS2C on {}", func.name());
run_mips2c(&func);
run_mips2c(&func, config.game_version);
}
auto it = config.hacks.mips2c_jump_table_functions.find(func.name());
if (it != config.hacks.mips2c_jump_table_functions.end()) {
run_mips2c_jump_table(&func, it->second);
run_mips2c_jump_table(&func, it->second, config.game_version);
}
});
}
@@ -390,8 +384,9 @@ void ObjectFileDB::ir2_atomic_op_pass(int seg, const Config& config, ObjectFileD
blocks_ending_in_asm_branch = asm_branch_it->second;
}
auto ops = convert_function_to_atomic_ops(func, data.linked_data.labels, func.warnings,
inline_asm, blocks_ending_in_asm_branch);
auto ops =
convert_function_to_atomic_ops(func, data.linked_data.labels, func.warnings, inline_asm,
blocks_ending_in_asm_branch, config.game_version);
func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(std::move(ops));
func.ir2.atomic_ops_succeeded = true;
func.ir2.env.set_end_var(func.ir2.atomic_ops->end_op().return_var());
@@ -877,7 +872,12 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function&
init_types = &func.ir2.env.get_types_at_block_entry(block_id);
}
for (int instr_id = block.start_word; instr_id < block.end_word; instr_id++) {
int start_word = block.start_word;
// if we have no prologue, skip the type tag.
if (start_word == 0) {
start_word = 1;
}
for (int instr_id = start_word; instr_id < block.end_word; instr_id++) {
print_instr_start(instr_id);
bool printed_comment = false;
+102 -70
View File
@@ -15,7 +15,8 @@ namespace {
std::unique_ptr<AtomicOp> convert_1(const Instruction& i0,
int idx,
bool hint_inline_asm,
bool force_asm_branch);
bool force_asm_branch,
GameVersion version);
//////////////////////
// Register Helpers
@@ -368,10 +369,10 @@ std::unique_ptr<AtomicOp> make_asm_op(const Instruction& i0, int idx) {
}
}
std::unique_ptr<AtomicOp> convert_1_allow_asm(const Instruction& i0, int idx) {
std::unique_ptr<AtomicOp> convert_1_allow_asm(const Instruction& i0, int idx, GameVersion version) {
// only used for delay slots, so fine to assume that this can never be an asm branch itself
// as there are no branches in delay slots anywhere.
auto as_normal = convert_1(i0, idx, false, false);
auto as_normal = convert_1(i0, idx, false, false, version);
if (as_normal) {
return as_normal;
}
@@ -382,7 +383,7 @@ std::unique_ptr<AtomicOp> convert_1_allow_asm(const Instruction& i0, int idx) {
// Branch Helpers
////////////////////////
IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx) {
IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx, GameVersion version) {
if (is_nop(i0)) {
return IR2_BranchDelay(IR2_BranchDelay::Kind::NOP);
} else if (is_gpr_3(i0, InstructionKind::OR, {}, rs7(), rr0())) {
@@ -391,7 +392,7 @@ IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx) {
return IR2_BranchDelay(IR2_BranchDelay::Kind::SET_REG_REG, make_dst_var(i0, idx),
make_src_var(i0.get_src(0).get_reg(), idx));
} else if (i0.kind == InstructionKind::DADDIU && i0.get_src(0).is_reg(rs7()) &&
i0.get_src(1).is_imm(FIX_SYM_TRUE)) {
i0.get_src(1).is_imm(true_symbol_offset(version))) {
return IR2_BranchDelay(IR2_BranchDelay::Kind::SET_REG_TRUE, make_dst_var(i0, idx));
} else if (i0.kind == InstructionKind::LW && i0.get_src(1).is_reg(rs7()) &&
i0.get_src(0).is_sym()) {
@@ -419,13 +420,14 @@ std::unique_ptr<AtomicOp> make_branch(const IR2_Condition& condition,
bool likely,
int dest_label,
int my_idx,
bool force_asm) {
bool force_asm,
GameVersion version) {
ASSERT(!likely);
auto branch_delay = get_branch_delay(delay, my_idx);
auto branch_delay = get_branch_delay(delay, my_idx, version);
if (!force_asm && branch_delay.is_known()) {
return std::make_unique<BranchOp>(likely, condition, dest_label, branch_delay, my_idx);
} else {
auto delay_op = std::shared_ptr<AtomicOp>(convert_1_allow_asm(delay, my_idx));
auto delay_op = std::shared_ptr<AtomicOp>(convert_1_allow_asm(delay, my_idx, version));
if (!delay_op) {
throw std::runtime_error(
fmt::format("Failed to convert branch delay slot instruction for branch at {}", my_idx));
@@ -459,9 +461,10 @@ std::unique_ptr<AtomicOp> make_asm_branch(const IR2_Condition& condition,
const Instruction& delay,
bool likely,
int dest_label,
int my_idx) {
int my_idx,
GameVersion version) {
ASSERT(!likely);
auto delay_op = std::shared_ptr<AtomicOp>(convert_1_allow_asm(delay, my_idx));
auto delay_op = std::shared_ptr<AtomicOp>(convert_1_allow_asm(delay, my_idx, version));
if (!delay_op) {
throw std::runtime_error(
fmt::format("Failed to convert branch delay slot instruction for branch at {}", my_idx));
@@ -561,12 +564,12 @@ std::unique_ptr<AtomicOp> convert_lw_1(const Instruction& i0, int idx) {
}
}
std::unique_ptr<AtomicOp> convert_daddiu_1(const Instruction& i0, int idx) {
std::unique_ptr<AtomicOp> convert_daddiu_1(const Instruction& i0, int idx, GameVersion version) {
if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym()) {
// get symbol pointer
return std::make_unique<SetVarOp>(
make_dst_var(i0, idx), SimpleAtom::make_sym_ptr(i0.get_src(1).get_sym()).as_expr(), idx);
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(FIX_SYM_EMPTY_PAIR)) {
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(empty_pair_offset(version))) {
// get empty pair
return std::make_unique<SetVarOp>(make_dst_var(i0, idx),
SimpleAtom::make_empty_list().as_expr(), idx);
@@ -574,8 +577,7 @@ std::unique_ptr<AtomicOp> convert_daddiu_1(const Instruction& i0, int idx) {
// get pointer to beginning of symbol table (this is a bit of a hack)
return std::make_unique<SetVarOp>(
make_dst_var(i0, idx), SimpleAtom::make_sym_val("__START-OF-TABLE__").as_expr(), idx);
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(FIX_SYM_TRUE)) {
// get pointer to beginning of symbol table (this is a bit of a hack)
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(true_symbol_offset(version))) {
return std::make_unique<SetVarOp>(make_dst_var(i0, idx),
SimpleAtom::make_sym_ptr("#t").as_expr(), idx);
} else if (i0.get_src(0).is_reg(rfp()) && i0.get_src(1).is_label()) {
@@ -805,7 +807,8 @@ std::unique_ptr<AtomicOp> convert_subu_1(const Instruction& i0, int idx) {
std::unique_ptr<AtomicOp> convert_1(const Instruction& i0,
int idx,
bool hint_inline_asm,
bool force_asm_branch) {
bool force_asm_branch,
GameVersion version) {
switch (i0.kind) {
case InstructionKind::OR:
return convert_or_1(i0, idx);
@@ -870,7 +873,7 @@ std::unique_ptr<AtomicOp> convert_1(const Instruction& i0,
case InstructionKind::MAXS:
return make_3reg_op(i0, SimpleExpression::Kind::MAX_S, idx);
case InstructionKind::DADDIU:
return convert_daddiu_1(i0, idx);
return convert_daddiu_1(i0, idx, version);
case InstructionKind::DADDU:
return convert_daddu_1(i0, idx);
case InstructionKind::DSUBU:
@@ -1003,7 +1006,8 @@ std::unique_ptr<AtomicOp> convert_bne_2(const Instruction& i0,
const Instruction& i1,
int idx,
bool likely,
bool force_asm) {
bool force_asm,
GameVersion version) {
auto s0 = i0.get_src(0).get_reg();
auto s1 = i0.get_src(1).get_reg();
auto dest = i0.get_src(2).get_label();
@@ -1021,14 +1025,15 @@ std::unique_ptr<AtomicOp> convert_bne_2(const Instruction& i0,
make_src_atom(s1, idx));
condition.make_flipped();
}
return make_branch(condition, i1, likely, dest, idx, force_asm);
return make_branch(condition, i1, likely, dest, idx, force_asm, version);
}
std::unique_ptr<AtomicOp> convert_beq_2(const Instruction& i0,
const Instruction& i1,
int idx,
bool likely,
bool force_asm) {
bool force_asm,
GameVersion version) {
auto s0 = i0.get_src(0).get_reg();
auto s1 = i0.get_src(1).get_reg();
auto dest = i0.get_src(2).get_label();
@@ -1053,10 +1058,13 @@ std::unique_ptr<AtomicOp> convert_beq_2(const Instruction& i0,
IR2_Condition(IR2_Condition::Kind::EQUAL, make_src_atom(s0, idx), make_src_atom(s1, idx));
condition.make_flipped();
}
return make_branch(condition, i1, likely, dest, idx, force_asm);
return make_branch(condition, i1, likely, dest, idx, force_asm, version);
}
std::unique_ptr<AtomicOp> convert_daddiu_2(const Instruction& i0, const Instruction& i1, int idx) {
std::unique_ptr<AtomicOp> convert_daddiu_2(const Instruction& i0,
const Instruction& i1,
int idx,
GameVersion version) {
// daddiu dest, s7, 8
// mov{n,z} dest, s7, src
if (i1.kind == InstructionKind::MOVN || i1.kind == InstructionKind::MOVZ) {
@@ -1066,7 +1074,7 @@ std::unique_ptr<AtomicOp> convert_daddiu_2(const Instruction& i0, const Instruct
return nullptr;
}
ASSERT(i0.get_src(0).is_reg(rs7()));
ASSERT(i0.get_src(1).is_imm(8));
ASSERT(i0.get_src(1).is_imm(true_symbol_offset(version)));
ASSERT(i1.get_dst(0).is_reg(dest));
ASSERT(i1.get_src(0).is_reg(rs7()));
auto kind =
@@ -1143,42 +1151,55 @@ std::unique_ptr<AtomicOp> convert_slt_2(const Instruction& i0,
return result;
}
std::unique_ptr<AtomicOp> convert_bltz_2(const Instruction& i0, const Instruction& i1, int idx) {
std::unique_ptr<AtomicOp> convert_bltz_2(const Instruction& i0,
const Instruction& i1,
int idx,
GameVersion version) {
// bltz is never emitted outside of inline asm.
auto dest = i0.get_src(1).get_label();
return make_asm_branch(IR2_Condition(IR2_Condition::Kind::LESS_THAN_ZERO_SIGNED,
make_src_atom(i0.get_src(0).get_reg(), idx)),
i1, false, dest, idx);
i1, false, dest, idx, version);
}
std::unique_ptr<AtomicOp> convert_bgez_2(const Instruction& i0, const Instruction& i1, int idx) {
std::unique_ptr<AtomicOp> convert_bgez_2(const Instruction& i0,
const Instruction& i1,
int idx,
GameVersion version) {
// bgez is never emitted outside of inline asm.
auto dest = i0.get_src(1).get_label();
return make_asm_branch(IR2_Condition(IR2_Condition::Kind::GEQ_ZERO_SIGNED,
make_src_atom(i0.get_src(0).get_reg(), idx)),
i1, false, dest, idx);
i1, false, dest, idx, version);
}
std::unique_ptr<AtomicOp> convert_blez_2(const Instruction& i0, const Instruction& i1, int idx) {
std::unique_ptr<AtomicOp> convert_blez_2(const Instruction& i0,
const Instruction& i1,
int idx,
GameVersion version) {
// blez is never emitted outside of inline asm.
auto dest = i0.get_src(1).get_label();
return make_asm_branch(IR2_Condition(IR2_Condition::Kind::LEQ_ZERO_SIGNED,
make_src_atom(i0.get_src(0).get_reg(), idx)),
i1, false, dest, idx);
i1, false, dest, idx, version);
}
std::unique_ptr<AtomicOp> convert_bgtz_2(const Instruction& i0, const Instruction& i1, int idx) {
std::unique_ptr<AtomicOp> convert_bgtz_2(const Instruction& i0,
const Instruction& i1,
int idx,
GameVersion version) {
// bgtz is never emitted outside of inline asm.
auto dest = i0.get_src(1).get_label();
return make_asm_branch(IR2_Condition(IR2_Condition::Kind::GREATER_THAN_ZERO_SIGNED,
make_src_atom(i0.get_src(0).get_reg(), idx)),
i1, false, dest, idx);
i1, false, dest, idx, version);
}
std::unique_ptr<AtomicOp> convert_2(const Instruction& i0,
const Instruction& i1,
int idx,
bool force_asm_branch) {
bool force_asm_branch,
GameVersion version) {
switch (i0.kind) {
case InstructionKind::DIV:
return convert_division_2(i0, i1, idx, true);
@@ -1187,11 +1208,11 @@ std::unique_ptr<AtomicOp> convert_2(const Instruction& i0,
case InstructionKind::JALR:
return convert_jalr_2(i0, i1, idx);
case InstructionKind::BNE:
return convert_bne_2(i0, i1, idx, false, force_asm_branch);
return convert_bne_2(i0, i1, idx, false, force_asm_branch, version);
case InstructionKind::BEQ:
return convert_beq_2(i0, i1, idx, false, force_asm_branch);
return convert_beq_2(i0, i1, idx, false, force_asm_branch, version);
case InstructionKind::DADDIU:
return convert_daddiu_2(i0, i1, idx);
return convert_daddiu_2(i0, i1, idx, version);
case InstructionKind::LUI:
return convert_lui_2(i0, i1, idx);
case InstructionKind::SLT:
@@ -1201,13 +1222,13 @@ std::unique_ptr<AtomicOp> convert_2(const Instruction& i0,
case InstructionKind::CLTS:
return convert_fp_branch_asm(i0, i1, IR2_Condition::Kind::FLOAT_LESS_THAN, idx);
case InstructionKind::BLTZ:
return convert_bltz_2(i0, i1, idx);
return convert_bltz_2(i0, i1, idx, version);
case InstructionKind::BGEZ:
return convert_bgez_2(i0, i1, idx);
return convert_bgez_2(i0, i1, idx, version);
case InstructionKind::BGTZ:
return convert_bgtz_2(i0, i1, idx);
return convert_bgtz_2(i0, i1, idx, version);
case InstructionKind::BLEZ:
return convert_blez_2(i0, i1, idx);
return convert_blez_2(i0, i1, idx, version);
default:
return nullptr;
}
@@ -1264,7 +1285,8 @@ std::unique_ptr<AtomicOp> convert_lui_3(const Instruction& i0,
std::unique_ptr<AtomicOp> convert_dsubu_3(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
int idx) {
int idx,
GameVersion version) {
if (i1.kind == InstructionKind::DADDIU &&
(i2.kind == InstructionKind::MOVN || i2.kind == InstructionKind::MOVZ)) {
// dsubu temp, a, b
@@ -1275,7 +1297,7 @@ std::unique_ptr<AtomicOp> convert_dsubu_3(const Instruction& i0,
auto b = i0.get_src(1).get_reg();
auto dest = i1.get_dst(0).get_reg();
ASSERT(i1.get_src(0).is_reg(rs7()));
ASSERT(i1.get_src(1).is_imm(FIX_SYM_TRUE));
ASSERT(i1.get_src(1).is_imm(true_symbol_offset(version)));
ASSERT(i2.get_dst(0).get_reg() == dest);
ASSERT(i2.get_src(0).is_reg(rs7()));
ASSERT(i2.get_src(1).get_reg() == temp);
@@ -1321,7 +1343,8 @@ std::unique_ptr<AtomicOp> convert_slt_3(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
bool is_signed,
int idx) {
int idx,
GameVersion version) {
auto s0 = i0.get_src(0).get_reg();
auto s1 = i0.get_src(1).get_reg();
std::unique_ptr<AtomicOp> result;
@@ -1354,7 +1377,7 @@ std::unique_ptr<AtomicOp> convert_slt_3(const Instruction& i0,
if (i1.kind == InstructionKind::BEQ) {
condition.invert();
}
result = make_branch(condition, i2, false, dest, idx, false);
result = make_branch(condition, i2, false, dest, idx, false, version);
add_clobber_if_unwritten(*result, temp);
return result;
} else if (i1.kind == InstructionKind::DADDIU &&
@@ -1366,7 +1389,7 @@ std::unique_ptr<AtomicOp> convert_slt_3(const Instruction& i0,
auto temp = i0.get_dst(0).get_reg();
auto dest = i1.get_dst(0).get_reg();
ASSERT(i1.get_src(0).is_reg(rs7()));
ASSERT(i1.get_src(1).is_imm(FIX_SYM_TRUE));
ASSERT(i1.get_src(1).is_imm(true_symbol_offset(version)));
ASSERT(i2.get_dst(0).get_reg() == dest);
ASSERT(i2.get_src(0).is_reg(rs7()));
ASSERT(i2.get_src(1).get_reg() == temp);
@@ -1400,7 +1423,8 @@ std::unique_ptr<AtomicOp> convert_slti_3(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
bool is_signed,
int idx) {
int idx,
GameVersion version) {
auto s0 = i0.get_src(0).get_reg();
auto s1 = SimpleAtom::make_int_constant(i0.get_src(1).get_imm());
std::unique_ptr<AtomicOp> result;
@@ -1419,7 +1443,7 @@ std::unique_ptr<AtomicOp> convert_slti_3(const Instruction& i0,
if (i1.kind == InstructionKind::BEQ) {
condition.invert();
}
result = make_branch(condition, i2, false, dest, idx, false);
result = make_branch(condition, i2, false, dest, idx, false, version);
add_clobber_if_unwritten(*result, temp);
return result;
} else if (i1.kind == InstructionKind::DADDIU &&
@@ -1431,7 +1455,7 @@ std::unique_ptr<AtomicOp> convert_slti_3(const Instruction& i0,
auto temp = i0.get_dst(0).get_reg();
auto dest = i1.get_dst(0).get_reg();
ASSERT(i1.get_src(0).is_reg(rs7()));
ASSERT(i1.get_src(1).is_imm(FIX_SYM_TRUE));
ASSERT(i1.get_src(1).is_imm(true_symbol_offset(version)));
ASSERT(i2.get_dst(0).get_reg() == dest);
ASSERT(i2.get_src(0).is_reg(rs7()));
ASSERT(i2.get_src(1).get_reg() == temp);
@@ -1455,14 +1479,15 @@ std::unique_ptr<AtomicOp> convert_fp_branch(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
IR2_Condition::Kind kind,
int idx) {
int idx,
GameVersion version) {
if (i1.kind == InstructionKind::BC1T || i1.kind == InstructionKind::BC1F) {
IR2_Condition condition(kind, make_src_atom(i0.get_src(0).get_reg(), idx),
make_src_atom(i0.get_src(1).get_reg(), idx));
if (i1.kind == InstructionKind::BC1F) {
condition.invert();
}
return make_branch(condition, i2, false, i1.get_src(0).get_label(), idx, false);
return make_branch(condition, i2, false, i1.get_src(0).get_label(), idx, false, version);
}
return nullptr;
}
@@ -1470,26 +1495,27 @@ std::unique_ptr<AtomicOp> convert_fp_branch(const Instruction& i0,
std::unique_ptr<AtomicOp> convert_3(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
int idx) {
int idx,
GameVersion version) {
switch (i0.kind) {
case InstructionKind::LUI:
return convert_lui_3(i0, i1, i2, idx);
case InstructionKind::DSUBU:
return convert_dsubu_3(i0, i1, i2, idx);
return convert_dsubu_3(i0, i1, i2, idx, version);
case InstructionKind::SLT:
return convert_slt_3(i0, i1, i2, true, idx);
return convert_slt_3(i0, i1, i2, true, idx, version);
case InstructionKind::SLTU:
return convert_slt_3(i0, i1, i2, false, idx);
return convert_slt_3(i0, i1, i2, false, idx, version);
case InstructionKind::SLTI:
return convert_slti_3(i0, i1, i2, true, idx);
return convert_slti_3(i0, i1, i2, true, idx, version);
case InstructionKind::SLTIU:
return convert_slti_3(i0, i1, i2, false, idx);
return convert_slti_3(i0, i1, i2, false, idx, version);
case InstructionKind::CEQS:
return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_EQUAL, idx);
return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_EQUAL, idx, version);
case InstructionKind::CLTS:
return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LESS_THAN, idx);
return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LESS_THAN, idx, version);
case InstructionKind::CLES:
return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LEQ, idx);
return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LEQ, idx, version);
default:
return nullptr;
}
@@ -1503,7 +1529,8 @@ std::unique_ptr<AtomicOp> convert_dsll32_4(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
const Instruction& i3,
int idx) {
int idx,
GameVersion version) {
if (i1.kind == InstructionKind::SLT && i2.kind == InstructionKind::BEQ) {
// dsll32 temp, a0, 30
// slt temp, temp, r0
@@ -1524,7 +1551,7 @@ std::unique_ptr<AtomicOp> convert_dsll32_4(const Instruction& i0,
ASSERT(i2.get_src(1).is_reg(rr0()));
IR2_Condition condition(IR2_Condition::Kind::IS_NOT_PAIR, make_src_atom(arg, idx));
auto result = make_branch(condition, i3, false, i2.get_src(2).get_label(), idx, false);
auto result = make_branch(condition, i3, false, i2.get_src(2).get_label(), idx, false, version);
result->add_clobber_reg(temp);
return result;
}
@@ -1536,7 +1563,8 @@ std::unique_ptr<AtomicOp> convert_fp_branch_with_nop(const Instruction& i0,
const Instruction& i2,
const Instruction& i3,
IR2_Condition::Kind kind,
int idx) {
int idx,
GameVersion version) {
if (i1.kind != InstructionKind::VNOP) {
return nullptr;
}
@@ -1546,7 +1574,7 @@ std::unique_ptr<AtomicOp> convert_fp_branch_with_nop(const Instruction& i0,
if (i2.kind == InstructionKind::BC1F) {
condition.invert();
}
return make_branch(condition, i3, false, i2.get_src(0).get_label(), idx, false);
return make_branch(condition, i3, false, i2.get_src(0).get_label(), idx, false, version);
}
return nullptr;
}
@@ -1555,12 +1583,14 @@ std::unique_ptr<AtomicOp> convert_4(const Instruction& i0,
const Instruction& i1,
const Instruction& i2,
const Instruction& i3,
int idx) {
int idx,
GameVersion version) {
switch (i0.kind) {
case InstructionKind::DSLL32:
return convert_dsll32_4(i0, i1, i2, i3, idx);
return convert_dsll32_4(i0, i1, i2, i3, idx, version);
case InstructionKind::CEQS:
return convert_fp_branch_with_nop(i0, i1, i2, i3, IR2_Condition::Kind::FLOAT_EQUAL, idx);
return convert_fp_branch_with_nop(i0, i1, i2, i3, IR2_Condition::Kind::FLOAT_EQUAL, idx,
version);
default:
return nullptr;
}
@@ -2035,6 +2065,7 @@ int convert_block_to_atomic_ops(int begin_idx,
const std::vector<DecompilerLabel>& labels,
FunctionAtomicOps* container,
DecompWarnings& warnings,
GameVersion version,
bool hint_inline_asm,
bool block_ends_in_asm_branch) {
container->block_id_to_first_atomic_op.push_back(container->ops.size());
@@ -2089,7 +2120,7 @@ int convert_block_to_atomic_ops(int begin_idx,
if (!converted && n_instr >= 4) {
// try 4 instructions
op = convert_4(instr[0], instr[1], instr[2], instr[3], op_idx);
op = convert_4(instr[0], instr[1], instr[2], instr[3], op_idx, version);
if (op) {
converted = true;
length = 4;
@@ -2098,7 +2129,7 @@ int convert_block_to_atomic_ops(int begin_idx,
if (!converted && n_instr >= 3) {
// try 3 instructions
op = convert_3(instr[0], instr[1], instr[2], op_idx);
op = convert_3(instr[0], instr[1], instr[2], op_idx, version);
if (op) {
converted = true;
length = 3;
@@ -2107,7 +2138,7 @@ int convert_block_to_atomic_ops(int begin_idx,
if (!converted && n_instr >= 2) {
// try 2 instructions
op = convert_2(instr[0], instr[1], op_idx, block_ends_in_asm_branch);
op = convert_2(instr[0], instr[1], op_idx, block_ends_in_asm_branch, version);
if (op) {
converted = true;
length = 2;
@@ -2117,7 +2148,7 @@ int convert_block_to_atomic_ops(int begin_idx,
if (!converted) {
// try 1 instruction
bool force_asm_branch = n_instr == 1 && block_ends_in_asm_branch;
op = convert_1(*instr, op_idx, hint_inline_asm, force_asm_branch);
op = convert_1(*instr, op_idx, hint_inline_asm, force_asm_branch, version);
if (op) {
converted = true;
length = 1;
@@ -2162,7 +2193,8 @@ FunctionAtomicOps convert_function_to_atomic_ops(
const std::vector<DecompilerLabel>& labels,
DecompWarnings& warnings,
bool hint_inline_asm,
const std::unordered_set<int>& blocks_ending_in_asm_branches) {
const std::unordered_set<int>& blocks_ending_in_asm_branches,
GameVersion version) {
FunctionAtomicOps result;
int last_op = 0;
@@ -2173,7 +2205,7 @@ FunctionAtomicOps convert_function_to_atomic_ops(
auto begin = func.instructions.begin() + block.start_word;
auto end = func.instructions.begin() + block.end_word;
last_op = convert_block_to_atomic_ops(
block.start_word, begin, end, labels, &result, warnings, hint_inline_asm,
block.start_word, begin, end, labels, &result, warnings, version, hint_inline_asm,
blocks_ending_in_asm_branches.find(i) != blocks_ending_in_asm_branches.end());
if (i == int(func.basic_blocks.size()) - 1) {
// we're the last block. insert the function end op.
+3 -1
View File
@@ -49,6 +49,7 @@ int convert_block_to_atomic_ops(int begin_idx,
const std::vector<DecompilerLabel>& labels,
FunctionAtomicOps* container,
DecompWarnings& warnings,
GameVersion version,
bool inline_asm_hint = false,
bool block_ends_in_asm_branch = false);
@@ -60,5 +61,6 @@ FunctionAtomicOps convert_function_to_atomic_ops(
const std::vector<DecompilerLabel>& labels,
DecompWarnings& warnings,
bool hint_inline_asm,
const std::unordered_set<int>& blocks_ending_in_asm_branches);
const std::unordered_set<int>& blocks_ending_in_asm_branches,
GameVersion version);
} // namespace decompiler
+13
View File
@@ -6,6 +6,7 @@
#include "cfg_builder.h"
#include "decompiler/Function/Function.h"
#include "decompiler/IR2/Form.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "decompiler/util/MatchParam.h"
namespace decompiler {
@@ -107,6 +108,13 @@ void clean_up_cond_with_else(FormPool& pool, FormElement* ir, const Env& env) {
void clean_up_until_loop(FormPool& pool, UntilElement* ir, const Env& env) {
auto condition_branch = get_condition_branch(ir->condition);
ASSERT(condition_branch.first);
if (condition_branch.first->op()->branch_delay().kind() != IR2_BranchDelay::Kind::NOP) {
ASSERT_MSG(
false,
fmt::format(
"bad delay slot in until loop: {} in {}\n", env.func->name(),
condition_branch.first->op()->branch_delay().to_form(env.file->labels, env).print()));
}
ASSERT(condition_branch.first->op()->branch_delay().kind() == IR2_BranchDelay::Kind::NOP);
auto replacement = condition_branch.first->op()->get_condition_as_form(pool, env);
replacement->invert();
@@ -708,6 +716,7 @@ void clean_up_cond_no_else_final(Function& func, CondNoElseElement* cne) {
ASSERT(fr.has_value());
cne->final_destination = *fr;
} else {
fmt::print("failed to clean up cond_no_else_final: {}\n", func.name());
ASSERT(false);
}
}
@@ -1634,6 +1643,10 @@ Form* cfg_to_ir_helper(FormPool& pool, Function& f, const CfgVtx* vtx) {
ASSERT(delay_end - delay_start == 1);
auto& op = f.ir2.atomic_ops->ops.at(delay_start);
auto op_as_expr = dynamic_cast<SetVarOp*>(op.get());
if (!op_as_expr) {
fmt::print("bad in {}\n", f.name());
fmt::print("{}\n", op->to_string(f.ir2.env));
}
ASSERT(op_as_expr);
e.branch_delay = *op_as_expr;
}
+28 -20
View File
@@ -591,12 +591,15 @@ Mips2C_Line handle_generic_op2_u16(const Instruction& i0, const std::string& ins
instr_str};
}
Mips2C_Line handle_daddiu(Mips2C_Output& out, const Instruction& i0, const std::string& instr_str) {
Mips2C_Line handle_daddiu(Mips2C_Output& out,
const Instruction& i0,
const std::string& instr_str,
GameVersion version) {
if (i0.get_src(1).is_label()) {
return {instr_str, instr_str};
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym("#t")) {
return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)),
reg_to_name(i0.get_src(0)), FIX_SYM_TRUE),
reg_to_name(i0.get_src(0)), true_symbol_offset(version)),
instr_str};
} else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym()) {
out.require_symbol(i0.get_src(1).get_sym());
@@ -931,7 +934,8 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
const Instruction& i0,
const std::string& instr_str,
int& unknown_count,
const LinkedObjectFile* file) {
const LinkedObjectFile* file,
GameVersion version) {
switch (i0.kind) {
case InstructionKind::CTC2:
return handle_ctc2(i0, instr_str);
@@ -1073,7 +1077,7 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output,
case InstructionKind::AND:
return handle_generic_op3(i0, instr_str, "and_"); // and isn't allowed in C++
case InstructionKind::DADDIU:
return handle_daddiu(output, i0, instr_str);
return handle_daddiu(output, i0, instr_str, version);
case InstructionKind::ADDIU:
return handle_generic_op2_u16(i0, instr_str);
case InstructionKind::QMTC2:
@@ -1172,7 +1176,9 @@ struct JumpTableBlock {
bool branch_always = false; // that branch is always taken?
};
void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locations) {
void run_mips2c_jump_table(Function* f,
const std::vector<int>& jump_table_locations,
GameVersion version) {
fmt::print("mips2c-jump on {}\n", f->name());
u32 magic_code = std::hash<std::string>()(f->name());
std::unordered_map<int, int> loc_to_block;
@@ -1212,8 +1218,8 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
ASSERT(delay_block.end_instr - delay_block.start_instr == 1); // only 1 instr.
auto& delay_instr = f->instructions.at(delay_block.start_instr);
auto delay_instr_str = delay_instr.to_string(file->labels);
auto delay_instr_line =
handle_normal_instr(output, delay_instr, delay_instr_str, unknown_count, file);
auto delay_instr_line = handle_normal_instr(output, delay_instr, delay_instr_str,
unknown_count, file, version);
output.lines.emplace_back(fmt::format(" {}", delay_instr_line.code),
delay_instr_line.comment);
ASSERT(delay_block.succ_ft == -1);
@@ -1230,7 +1236,7 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
ASSERT(i + 1 == block.end_instr);
// then the goto
output.lines.emplace_back(fmt::format("next_block = {};", block.succ_branch),
@@ -1246,7 +1252,7 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
ASSERT(i + 1 == block.end_instr);
// then the goto
output.lines.emplace_back(
@@ -1264,7 +1270,7 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
// then the goto
output.lines.emplace_back(fmt::format("goto end_of_function;", block.succ_branch),
@@ -1279,7 +1285,7 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
output.lines.emplace_back("c->jalr(call_addr);", instr_str);
} else if (instr.kind == InstructionKind::JR) {
// special case for jr's to handle the jump tableing.
@@ -1290,7 +1296,8 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
output.lines.emplace_back("break;");
} else {
output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count, file));
output.lines.push_back(
handle_normal_instr(output, instr, instr_str, unknown_count, file, version));
}
ASSERT(output.lines.size() > old_line_count);
@@ -1312,7 +1319,7 @@ void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locat
}
}
void run_mips2c(Function* f) {
void run_mips2c(Function* f, GameVersion version) {
g_unknown = 0;
auto* file = f->ir2.env.file;
std::unordered_set<int> likely_delay_blocks;
@@ -1347,8 +1354,8 @@ void run_mips2c(Function* f) {
ASSERT(delay_block.end_instr - delay_block.start_instr == 1); // only 1 instr.
auto& delay_instr = f->instructions.at(delay_block.start_instr);
auto delay_instr_str = delay_instr.to_string(file->labels);
auto delay_instr_line =
handle_normal_instr(output, delay_instr, delay_instr_str, unknown_count, file);
auto delay_instr_line = handle_normal_instr(output, delay_instr, delay_instr_str,
unknown_count, file, version);
output.lines.emplace_back(fmt::format(" {}", delay_instr_line.code),
delay_instr_line.comment);
ASSERT(delay_block.succ_ft == -1);
@@ -1364,7 +1371,7 @@ void run_mips2c(Function* f) {
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
ASSERT(i + 1 == block.end_instr);
// then the goto
output.lines.emplace_back(fmt::format("goto block_{};", block.succ_branch),
@@ -1382,7 +1389,7 @@ void run_mips2c(Function* f) {
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
// ASSERT(i + 1 == block.end_instr);
// then the goto
output.lines.emplace_back(fmt::format("if (bc) {{goto block_{};}}", block.succ_branch),
@@ -1398,7 +1405,7 @@ void run_mips2c(Function* f) {
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
// then the goto
output.lines.emplace_back(fmt::format("goto end_of_function;", block.succ_branch),
@@ -1413,10 +1420,11 @@ void run_mips2c(Function* f) {
auto& delay_i = f->instructions.at(i);
auto delay_i_str = delay_i.to_string(file->labels);
output.lines.push_back(
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file));
handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version));
output.lines.emplace_back("c->jalr(call_addr);", instr_str);
} else {
output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count, file));
output.lines.push_back(
handle_normal_instr(output, instr, instr_str, unknown_count, file, version));
}
ASSERT(output.lines.size() > old_line_count);
+6 -2
View File
@@ -2,9 +2,13 @@
#include <vector>
#include "common/versions.h"
namespace decompiler {
class Function;
void run_mips2c(Function* f);
void run_mips2c_jump_table(Function* f, const std::vector<int>& jump_table_locations);
void run_mips2c(Function* f, GameVersion version);
void run_mips2c_jump_table(Function* f,
const std::vector<int>& jump_table_locations,
GameVersion version);
} // namespace decompiler
+1
View File
@@ -66,6 +66,7 @@ Config read_config_file(const std::string& path_to_config_file,
config.process_art_groups = cfg.at("process_art_groups").get<bool>();
config.hexdump_code = cfg.at("hexdump_code").get<bool>();
config.hexdump_data = cfg.at("hexdump_data").get<bool>();
config.find_functions = cfg.at("find_functions").get<bool>();
config.dump_objs = cfg.at("dump_objs").get<bool>();
config.print_cfgs = cfg.at("print_cfgs").get<bool>();
config.generate_symbol_definition_map = cfg.at("generate_symbol_definition_map").get<bool>();
+1
View File
@@ -104,6 +104,7 @@ struct Config {
bool process_art_groups = false;
bool rip_levels = false;
bool extract_collision = false;
bool find_functions = false;
bool write_hex_near_instructions = false;
bool hexdump_code = false;
+16
View File
@@ -0,0 +1,16 @@
;; type system setup
(define-extern object type)
(define-extern type type)
(define-extern structure type)
(define-extern uint128 type)
(define-extern basic type)
(define-extern pair type)
(define-extern array type)
;; some types we need.
(declare-type sparticle-launch-group basic)
(declare-type lightning-spec basic)
(declare-type sparticle-launcher basic)
(declare-type state basic)
@@ -20,6 +20,9 @@
// Run the decompiler
"decompile_code": false,
// run the first pass of the decompiler
"find_functions": true,
////////////////////////////
// DATA ANALYSIS OPTIONS
////////////////////////////
+28
View File
@@ -27,7 +27,35 @@
"hint_inline_assembly_functions": [],
"asm_functions_by_name": [
// checking boxed type is different now - these make the cfg stuff sad
"type?", "print", "printl", "inspect", "valid?", "name=", "(method 8 res-lump)", "joint-control-remap!", "(method 21 game-info)", "(method 12 level)",
"bg", "(anon-function 2 cam-combiner)", "cspace-inspect-tree", "command-get-process", "command-get-trans", "(method 10 script-context)",
"(method 11 script-context)", "(method 9 script-context)", "(anon-function 64 script)", "(anon-function 61 script)", "(anon-function 60 script)",
"(anon-function 54 script)", "(anon-function 52 script)", "(anon-function 49 script)", "(anon-function 33 script)", "debug-menu-func-decode",
"scene-player-init", "(method 77 spyder)", "(method 77 flamer)", "(method 77 grenadier)", "(method 224 bot)", "(method 77 rapid-gunner)",
// until loop without nop:
"rand-vu-int-count-excluding", "rand-vu-int-range-exclude", "(method 9 history)", "history-print", "history-draw",
"(method 9 sparticle-launcher)", "(method 18 tracking-spline)", "cam-string-find-position-rel!", "cam-layout-entity-volume-info-create",
"process-drawable-shock-skel-effect", "target-history-print", "display-list-control", "anim-test-anim-list-handler",
"anim-test-sequence-list-handler", "anim-tester-get-playing-item", "(method 9 mysql-nav-graph)", "(method 58 nav-graph-editor)",
"(method 120 enemy)", "start-pilot-recorder", "(anon-function 10 pilot-recorder)", "(method 0 hover-nav-control)",
"(method 24 nav-network)", "(method 11 predator-manager)", "(method 9 bot-speech-list)", "(method 9 bot-speech-list-shuffle)",
"(anon-function 10 sig-recorder)", "(method 14 trail-graph)", "(method 12 trail-graph)", "(method 11 trail-graph)",
// actual asm
"symlink2", "blerc-a-fragment", "blerc-execute", "foreground-check-longest-edge-asm", "generic-light-proc",
"shadow-add-single-edges","shadow-add-facing-single-tris", "shadow-add-double-tris", "shadow-add-double-edges",
"(method 12 collide-mesh)", "(method 17 collide-edge-work)", "(method 42 collide-shape)", "(method 12 collide-shape-prim-sphere)",
"(method 12 collide-shape-prim-mesh)", "(method 18 collide-shape-prim-mesh)", "(method 10 collide-cache-prim)",
"(method 17 collide-cache)", "(method 16 ocean)",
// unknown bug
"joint-mod-polar-look-at-guts", "(method 13 external-art-control)", "update-mood-copy-ctywide", "update-mood-copy-stadium",
"update-mood-drillb", "reset-target-tracking", "(anon-function 1 target)", "find-nearest-entity", "target-land-effect",
"(method 12 effect-control)", "(method 11 effect-control)", "(method 10 effect-control)",
"(anon-function 2 scene)", "progress-trans", "(method 10 bigmap)", "(method 9 editable-region)", "(method 57 enemy)",
"(anon-function 10 meet-brutter)", "(method 154 vehicle-racer)", "(method 188 predator)", "(anon-function 13 sig0-course)",
"(method 228 hal-sewer)", "(method 154 vehicle-city-racer)", "(method 53 squid)", "(anon-function 11 fort-floor-spike)",
"(method 29 gun-dummy)", "vehicle-explode-post", "(method 158 vehicle-guard)", "(method 207 metalhead-predator)"
],
// these functions use pairs and the decompiler
+4 -2
View File
@@ -8,7 +8,7 @@
// if you want to filter to only some object names.
// it will make the decompiler much faster.
"allowed_objects": [],
"banned_objects": [],
"banned_objects": ["effect-control"],
////////////////////////////
// CODE ANALYSIS OPTIONS
@@ -19,7 +19,9 @@
"disassemble_code": true,
// Run the decompiler
"decompile_code": false,
"decompile_code": true,
"find_functions": true,
////////////////////////////
// DATA ANALYSIS OPTIONS
+5
View File
@@ -151,6 +151,11 @@ int main(int argc, char** argv) {
db.process_labels();
fmt::print("[Mem] After code: {} MB\n", get_peak_rss() / (1024 * 1024));
// top level decompile (do this before printing asm so we get function names)
if (config.find_functions) {
db.ir2_top_level_pass(config);
}
// print disassembly
if (config.disassemble_code || config.disassemble_data) {
db.write_disassembly(out_folder, config.disassemble_data, config.disassemble_code,
+1 -1
View File
@@ -241,7 +241,7 @@ void set_fullscreen(DisplayMode mode, int screen) {
}
void input_mode_set(u32 enable) {
if (enable == s7.offset + FIX_SYM_TRUE) { // #t
if (enable == s7.offset + jak1_symbols::FIX_SYM_TRUE) { // #t
Pad::g_input_mode_mapping = g_settings.pad_mapping_info;
Pad::EnterInputMode();
} else {
+2
View File
@@ -21,6 +21,8 @@
#include "common/util/Assert.h"
#include "third-party/fmt/core.h"
using namespace jak1_symbols;
namespace {
// turn on printf's for debugging linking issues.
constexpr bool link_debug_printfs = false;
+2
View File
@@ -17,6 +17,8 @@
#include "kscheme.h"
#include "common/symbols.h"
using namespace jak1_symbols;
Ptr<Symbol> ListenerLinkBlock;
Ptr<Symbol> ListenerFunction;
Ptr<Symbol> kernel_dispatcher;
+1
View File
@@ -44,6 +44,7 @@
#include "svnrev.h"
using namespace jak1_symbols;
using namespace ee;
/*!
+2
View File
@@ -24,6 +24,8 @@
#include "common/symbols.h"
#include "common/util/Assert.h"
using namespace jak1_symbols;
///////////
// SDATA
///////////
+2
View File
@@ -25,6 +25,8 @@
#include "game/mips2c/mips2c_table.h"
#include "common/util/Assert.h"
using namespace jak1_symbols;
//! Controls link mode when EnableMethodSet = 0, MasterDebug = 1, DiskBoot = 0. Will enable a
//! warning message if EnableMethodSet = 1
u32 FastLink;
+2
View File
@@ -5,6 +5,8 @@
#include "game/kernel/kscheme.h"
#include "common/symbols.h"
using namespace jak1_symbols;
extern "C" {
void _mips2c_call_linux();
void _mips2c_call_windows();
+1 -1
View File
@@ -5,7 +5,7 @@
#include "common/symbols.h"
using namespace emitter;
using namespace jak1_symbols; // TODO jak 1 symbols
namespace {
Register get_reg(const RegVal* rv, const AllocationResult& allocs, emitter::IR_Record irec) {
if (rv->rlet_constraint().has_value()) {
+2
View File
@@ -513,6 +513,8 @@ bool Debugger::write_memory(const u8* src_buffer, int size, u32 goal_addr) {
* Read the GOAL Symbol table from an attached and halted target.
*/
void Debugger::read_symbol_table() {
// todo: this assumes many things specific to jak 1.
using namespace jak1_symbols;
ASSERT(is_valid() && is_attached() && is_halted());
u32 bytes_read = 0;
u32 reads = 0;
+2 -1
View File
@@ -173,7 +173,8 @@ std::unique_ptr<FormRegressionTest::TestData> FormRegressionTest::make_function(
// convert instruction to atomic ops
DecompWarnings warnings;
auto ops = convert_function_to_atomic_ops(test->func, program.labels, warnings, false, {});
auto ops = convert_function_to_atomic_ops(test->func, program.labels, warnings, false, {},
GameVersion::Jak1);
test->func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(std::move(ops));
test->func.ir2.atomic_ops_succeeded = true;
test->func.ir2.env.set_end_var(test->func.ir2.atomic_ops->end_op().return_var());
+3 -2
View File
@@ -24,6 +24,7 @@ std::string assembly_from_list(std::vector<std::string> assemblyLines) {
return str;
}
// jak 1 specific
void test_case(std::string assembly_lines,
std::vector<std::string> output_lines,
std::vector<std::vector<std::string>> write_regs,
@@ -45,7 +46,7 @@ void test_case(std::string assembly_lines,
// treat the entire program as a single basic block, and convert!
DecompWarnings warnings;
convert_block_to_atomic_ops(0, prg.instructions.begin(), prg.instructions.end(), prg.labels,
&container, warnings);
&container, warnings, GameVersion::Jak1);
// count operations
EXPECT_EQ(container.ops.size(), output_lines.size());
@@ -130,7 +131,7 @@ TEST(DecompilerAtomicOpBuilder, RegUseDuplication) {
FunctionAtomicOps container;
DecompWarnings warnings;
convert_block_to_atomic_ops(0, prg.instructions.begin(), prg.instructions.end(), prg.labels,
&container, warnings);
&container, warnings, GameVersion::Jak1);
ASSERT_EQ(1, container.ops.size());
auto& op = container.ops.at(0);
for (const auto& reg_group : {op->read_regs(), op->write_regs(), op->clobber_regs()}) {
+1
View File
@@ -307,6 +307,7 @@ void disassemble(Decompiler& dc) {
void decompile(Decompiler& dc, const OfflineTestConfig& config) {
dc.db->extract_art_info();
dc.db->ir2_top_level_pass(*dc.config);
dc.db->analyze_functions_ir2({}, *dc.config, config.skip_compile_functions,
config.skip_compile_states);
}
+2
View File
@@ -11,6 +11,8 @@
#include "game/kernel/kscheme.h"
#include "all_jak1_symbols.h"
using namespace jak1_symbols;
TEST(Kernel, strend) {
char test[] = "test";
char* end = strend(test);
+3 -1
View File
@@ -114,6 +114,7 @@ struct SymbolMap {
};
SymbolMap build_symbol_map(const Ram& ram, u32 s7) {
// TODO jak 1 specific
fmt::print("finding symbols...\n");
SymbolMap map;
/*
@@ -150,9 +151,10 @@ SymbolMap build_symbol_map(const Ram& ram, u32 s7) {
std::unordered_map<u32, std::string> build_type_map(const Ram& ram,
const SymbolMap& symbols,
u32 s7) {
// TODO jak 1 specific
std::unordered_map<u32, std::string> result;
fmt::print("finding types...\n");
u32 type_of_type = ram.word(s7 + FIX_SYM_TYPE_TYPE);
u32 type_of_type = ram.word(s7 + jak1_symbols::FIX_SYM_TYPE_TYPE);
ASSERT(type_of_type == ram.word(symbols.name_to_addr.at("type")));
for (const auto& [name, addr] : symbols.name_to_addr) {