mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-04-28 04:51:43 +00:00
Fix recompiled code indentation
This commit is contained in:
parent
4a832ac2fa
commit
3f237c31f1
4 changed files with 268 additions and 261 deletions
|
|
@ -564,25 +564,25 @@ struct RSPRecompilerConfig {
|
|||
};
|
||||
|
||||
std::filesystem::path concat_if_not_empty(const std::filesystem::path& parent, const std::filesystem::path& child) {
|
||||
if (!child.empty()) {
|
||||
return parent / child;
|
||||
}
|
||||
return child;
|
||||
if (!child.empty()) {
|
||||
return parent / child;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> toml_to_vec(const toml::array* array) {
|
||||
std::vector<T> ret;
|
||||
std::vector<T> ret;
|
||||
|
||||
// Reserve room for all the funcs in the map.
|
||||
ret.reserve(array->size());
|
||||
// Reserve room for all the funcs in the map.
|
||||
ret.reserve(array->size());
|
||||
array->for_each([&ret](auto&& el) {
|
||||
if constexpr (toml::is_integer<decltype(el)>) {
|
||||
ret.push_back(*el);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -601,9 +601,9 @@ std::unordered_set<T> toml_to_set(const toml::array* array) {
|
|||
bool read_config(const std::filesystem::path& config_path, RSPRecompilerConfig& out) {
|
||||
RSPRecompilerConfig ret{};
|
||||
|
||||
try {
|
||||
try {
|
||||
const toml::table config_data = toml::parse_file(config_path.u8string());
|
||||
std::filesystem::path basedir = std::filesystem::path{ config_path }.parent_path();
|
||||
std::filesystem::path basedir = std::filesystem::path{ config_path }.parent_path();
|
||||
|
||||
std::optional<uint32_t> text_offset = config_data["text_offset"].value<uint32_t>();
|
||||
if (text_offset.has_value()) {
|
||||
|
|
@ -653,20 +653,20 @@ bool read_config(const std::filesystem::path& config_path, RSPRecompilerConfig&
|
|||
throw toml::parse_error("Missing output_function_name in config file", config_data.source());
|
||||
}
|
||||
|
||||
// Extra indirect branch targets (optional)
|
||||
// Extra indirect branch targets (optional)
|
||||
const toml::node_view branch_targets_data = config_data["extra_indirect_branch_targets"];
|
||||
if (branch_targets_data.is_array()) {
|
||||
const toml::array* branch_targets_array = branch_targets_data.as_array();
|
||||
ret.extra_indirect_branch_targets = toml_to_vec<uint32_t>(branch_targets_array);
|
||||
}
|
||||
|
||||
// Unsupported_instructions (optional)
|
||||
// Unsupported_instructions (optional)
|
||||
const toml::node_view unsupported_instructions_data = config_data["unsupported_instructions"];
|
||||
if (unsupported_instructions_data.is_array()) {
|
||||
const toml::array* unsupported_instructions_array = unsupported_instructions_data.as_array();
|
||||
ret.unsupported_instructions = toml_to_set<uint32_t>(unsupported_instructions_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const toml::parse_error& err) {
|
||||
std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
|
||||
return false;
|
||||
|
|
|
|||
476
src/analysis.cpp
476
src/analysis.cpp
|
|
@ -10,271 +10,271 @@ extern "C" const char* RabbitizerRegister_getNameGpr(uint8_t regValue);
|
|||
|
||||
// If 64-bit addressing is ever implemented, these will need to be changed to 64-bit values
|
||||
struct RegState {
|
||||
// For tracking a register that will be used to load from RAM
|
||||
uint32_t prev_lui;
|
||||
uint32_t prev_addiu_vram;
|
||||
uint32_t prev_addu_vram;
|
||||
uint8_t prev_addend_reg;
|
||||
bool valid_lui;
|
||||
bool valid_addiu;
|
||||
bool valid_addend;
|
||||
// For tracking a register that has been loaded from RAM
|
||||
uint32_t loaded_lw_vram;
|
||||
uint32_t loaded_addu_vram;
|
||||
uint32_t loaded_address;
|
||||
uint8_t loaded_addend_reg;
|
||||
bool valid_loaded;
|
||||
// For tracking a register that will be used to load from RAM
|
||||
uint32_t prev_lui;
|
||||
uint32_t prev_addiu_vram;
|
||||
uint32_t prev_addu_vram;
|
||||
uint8_t prev_addend_reg;
|
||||
bool valid_lui;
|
||||
bool valid_addiu;
|
||||
bool valid_addend;
|
||||
// For tracking a register that has been loaded from RAM
|
||||
uint32_t loaded_lw_vram;
|
||||
uint32_t loaded_addu_vram;
|
||||
uint32_t loaded_address;
|
||||
uint8_t loaded_addend_reg;
|
||||
bool valid_loaded;
|
||||
|
||||
RegState() = default;
|
||||
RegState() = default;
|
||||
|
||||
void invalidate() {
|
||||
prev_lui = 0;
|
||||
prev_addiu_vram = 0;
|
||||
prev_addu_vram = 0;
|
||||
prev_addend_reg = 0;
|
||||
void invalidate() {
|
||||
prev_lui = 0;
|
||||
prev_addiu_vram = 0;
|
||||
prev_addu_vram = 0;
|
||||
prev_addend_reg = 0;
|
||||
|
||||
valid_lui = false;
|
||||
valid_addiu = false;
|
||||
valid_addend = false;
|
||||
valid_lui = false;
|
||||
valid_addiu = false;
|
||||
valid_addend = false;
|
||||
|
||||
loaded_lw_vram = 0;
|
||||
loaded_addu_vram = 0;
|
||||
loaded_address = 0;
|
||||
loaded_addend_reg = 0;
|
||||
loaded_lw_vram = 0;
|
||||
loaded_addu_vram = 0;
|
||||
loaded_address = 0;
|
||||
loaded_addend_reg = 0;
|
||||
|
||||
valid_loaded = false;
|
||||
}
|
||||
valid_loaded = false;
|
||||
}
|
||||
};
|
||||
|
||||
using InstrId = rabbitizer::InstrId::UniqueId;
|
||||
using RegId = rabbitizer::Registers::Cpu::GprO32;
|
||||
|
||||
bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const RecompPort::Function& func, RecompPort::FunctionStats& stats,
|
||||
RegState reg_states[32], std::vector<RegState>& stack_states) {
|
||||
// Temporary register state for tracking the register being operated on
|
||||
RegState temp{};
|
||||
RegState reg_states[32], std::vector<RegState>& stack_states) {
|
||||
// Temporary register state for tracking the register being operated on
|
||||
RegState temp{};
|
||||
|
||||
int rd = (int)instr.GetO32_rd();
|
||||
int rs = (int)instr.GetO32_rs();
|
||||
int base = rs;
|
||||
int rt = (int)instr.GetO32_rt();
|
||||
int sa = (int)instr.Get_sa();
|
||||
int rd = (int)instr.GetO32_rd();
|
||||
int rs = (int)instr.GetO32_rs();
|
||||
int base = rs;
|
||||
int rt = (int)instr.GetO32_rt();
|
||||
int sa = (int)instr.Get_sa();
|
||||
|
||||
uint16_t imm = instr.Get_immediate();
|
||||
uint16_t imm = instr.Get_immediate();
|
||||
|
||||
auto check_move = [&]() {
|
||||
if (rs == 0) {
|
||||
// rs is zero so copy rt to rd
|
||||
reg_states[rd] = reg_states[rt];
|
||||
} else if (rt == 0) {
|
||||
// rt is zero so copy rs to rd
|
||||
reg_states[rd] = reg_states[rs];
|
||||
} else {
|
||||
// Not a move, invalidate rd
|
||||
reg_states[rd].invalidate();
|
||||
}
|
||||
};
|
||||
auto check_move = [&]() {
|
||||
if (rs == 0) {
|
||||
// rs is zero so copy rt to rd
|
||||
reg_states[rd] = reg_states[rt];
|
||||
} else if (rt == 0) {
|
||||
// rt is zero so copy rs to rd
|
||||
reg_states[rd] = reg_states[rs];
|
||||
} else {
|
||||
// Not a move, invalidate rd
|
||||
reg_states[rd].invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
switch (instr.getUniqueId()) {
|
||||
case InstrId::cpu_lui:
|
||||
// rt has been completely overwritten, so invalidate it
|
||||
reg_states[rt].invalidate();
|
||||
reg_states[rt].prev_lui = (int16_t)imm << 16;
|
||||
reg_states[rt].valid_lui = true;
|
||||
break;
|
||||
case InstrId::cpu_addiu:
|
||||
// The target reg is a copy of the source reg plus an immediate, so copy the source reg's state
|
||||
reg_states[rt] = reg_states[rs];
|
||||
// Set the addiu state if and only if there hasn't been an addiu already
|
||||
if (!reg_states[rt].valid_addiu) {
|
||||
reg_states[rt].prev_addiu_vram = (int16_t)imm;
|
||||
reg_states[rt].valid_addiu = true;
|
||||
} else {
|
||||
// Otherwise, there have been 2 or more consecutive addius so invalidate the whole register
|
||||
reg_states[rt].invalidate();
|
||||
}
|
||||
break;
|
||||
case InstrId::cpu_addu:
|
||||
// rd has been completely overwritten, so invalidate it
|
||||
temp.invalidate();
|
||||
// Exactly one of the two addend register states should have a valid lui at this time
|
||||
if (reg_states[rs].valid_lui != reg_states[rt].valid_lui) {
|
||||
// Track which of the two registers has the valid lui state and which is the addend
|
||||
int valid_lui_reg = reg_states[rs].valid_lui ? rs : rt;
|
||||
int addend_reg = reg_states[rs].valid_lui ? rt : rs;
|
||||
switch (instr.getUniqueId()) {
|
||||
case InstrId::cpu_lui:
|
||||
// rt has been completely overwritten, so invalidate it
|
||||
reg_states[rt].invalidate();
|
||||
reg_states[rt].prev_lui = (int16_t)imm << 16;
|
||||
reg_states[rt].valid_lui = true;
|
||||
break;
|
||||
case InstrId::cpu_addiu:
|
||||
// The target reg is a copy of the source reg plus an immediate, so copy the source reg's state
|
||||
reg_states[rt] = reg_states[rs];
|
||||
// Set the addiu state if and only if there hasn't been an addiu already
|
||||
if (!reg_states[rt].valid_addiu) {
|
||||
reg_states[rt].prev_addiu_vram = (int16_t)imm;
|
||||
reg_states[rt].valid_addiu = true;
|
||||
} else {
|
||||
// Otherwise, there have been 2 or more consecutive addius so invalidate the whole register
|
||||
reg_states[rt].invalidate();
|
||||
}
|
||||
break;
|
||||
case InstrId::cpu_addu:
|
||||
// rd has been completely overwritten, so invalidate it
|
||||
temp.invalidate();
|
||||
// Exactly one of the two addend register states should have a valid lui at this time
|
||||
if (reg_states[rs].valid_lui != reg_states[rt].valid_lui) {
|
||||
// Track which of the two registers has the valid lui state and which is the addend
|
||||
int valid_lui_reg = reg_states[rs].valid_lui ? rs : rt;
|
||||
int addend_reg = reg_states[rs].valid_lui ? rt : rs;
|
||||
|
||||
// Copy the lui reg's state into the destination reg, then set the destination reg's addend to the other operand
|
||||
temp = reg_states[valid_lui_reg];
|
||||
temp.valid_addend = true;
|
||||
temp.prev_addend_reg = addend_reg;
|
||||
temp.prev_addu_vram = instr.getVram();
|
||||
} else {
|
||||
// Check if this is a move
|
||||
check_move();
|
||||
}
|
||||
reg_states[rd] = temp;
|
||||
break;
|
||||
case InstrId::cpu_daddu:
|
||||
case InstrId::cpu_or:
|
||||
check_move();
|
||||
break;
|
||||
case InstrId::cpu_sw:
|
||||
// If this is a store to the stack, copy the state of rt into the stack at the given offset
|
||||
if (base == (int)RegId::GPR_O32_sp) {
|
||||
if ((imm & 0b11) != 0) {
|
||||
fmt::print(stderr, "Invalid alignment on offset for sw to stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
if (((int16_t)imm) < 0) {
|
||||
fmt::print(stderr, "Negative offset for sw to stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
size_t stack_offset = imm / 4;
|
||||
if (stack_offset >= stack_states.size()) {
|
||||
stack_states.resize(stack_offset + 1);
|
||||
}
|
||||
stack_states[stack_offset] = reg_states[rt];
|
||||
}
|
||||
break;
|
||||
case InstrId::cpu_lw:
|
||||
// rt has been completely overwritten, so invalidate it
|
||||
temp.invalidate();
|
||||
// If this is a load from the stack, copy the state of the stack at the given offset to rt
|
||||
if (base == (int)RegId::GPR_O32_sp) {
|
||||
if ((imm & 0b11) != 0) {
|
||||
fmt::print(stderr, "Invalid alignment on offset for lw from stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
if (((int16_t)imm) < 0) {
|
||||
fmt::print(stderr, "Negative offset for lw from stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
size_t stack_offset = imm / 4;
|
||||
if (stack_offset >= stack_states.size()) {
|
||||
stack_states.resize(stack_offset + 1);
|
||||
}
|
||||
temp = stack_states[stack_offset];
|
||||
}
|
||||
// If the base register has a valid lui state and a valid addend before this, then this may be a load from a jump table
|
||||
else if (reg_states[base].valid_lui && reg_states[base].valid_addend) {
|
||||
// Exactly one of the lw and the base reg should have a valid lo16 value
|
||||
bool nonzero_immediate = imm != 0;
|
||||
if (nonzero_immediate != reg_states[base].valid_addiu) {
|
||||
uint32_t lo16;
|
||||
if (nonzero_immediate) {
|
||||
lo16 = (int16_t)imm;
|
||||
} else {
|
||||
lo16 = reg_states[base].prev_addiu_vram;
|
||||
}
|
||||
// Copy the lui reg's state into the destination reg, then set the destination reg's addend to the other operand
|
||||
temp = reg_states[valid_lui_reg];
|
||||
temp.valid_addend = true;
|
||||
temp.prev_addend_reg = addend_reg;
|
||||
temp.prev_addu_vram = instr.getVram();
|
||||
} else {
|
||||
// Check if this is a move
|
||||
check_move();
|
||||
}
|
||||
reg_states[rd] = temp;
|
||||
break;
|
||||
case InstrId::cpu_daddu:
|
||||
case InstrId::cpu_or:
|
||||
check_move();
|
||||
break;
|
||||
case InstrId::cpu_sw:
|
||||
// If this is a store to the stack, copy the state of rt into the stack at the given offset
|
||||
if (base == (int)RegId::GPR_O32_sp) {
|
||||
if ((imm & 0b11) != 0) {
|
||||
fmt::print(stderr, "Invalid alignment on offset for sw to stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
if (((int16_t)imm) < 0) {
|
||||
fmt::print(stderr, "Negative offset for sw to stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
size_t stack_offset = imm / 4;
|
||||
if (stack_offset >= stack_states.size()) {
|
||||
stack_states.resize(stack_offset + 1);
|
||||
}
|
||||
stack_states[stack_offset] = reg_states[rt];
|
||||
}
|
||||
break;
|
||||
case InstrId::cpu_lw:
|
||||
// rt has been completely overwritten, so invalidate it
|
||||
temp.invalidate();
|
||||
// If this is a load from the stack, copy the state of the stack at the given offset to rt
|
||||
if (base == (int)RegId::GPR_O32_sp) {
|
||||
if ((imm & 0b11) != 0) {
|
||||
fmt::print(stderr, "Invalid alignment on offset for lw from stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
if (((int16_t)imm) < 0) {
|
||||
fmt::print(stderr, "Negative offset for lw from stack: {}\n", (int16_t)imm);
|
||||
return false;
|
||||
}
|
||||
size_t stack_offset = imm / 4;
|
||||
if (stack_offset >= stack_states.size()) {
|
||||
stack_states.resize(stack_offset + 1);
|
||||
}
|
||||
temp = stack_states[stack_offset];
|
||||
}
|
||||
// If the base register has a valid lui state and a valid addend before this, then this may be a load from a jump table
|
||||
else if (reg_states[base].valid_lui && reg_states[base].valid_addend) {
|
||||
// Exactly one of the lw and the base reg should have a valid lo16 value
|
||||
bool nonzero_immediate = imm != 0;
|
||||
if (nonzero_immediate != reg_states[base].valid_addiu) {
|
||||
uint32_t lo16;
|
||||
if (nonzero_immediate) {
|
||||
lo16 = (int16_t)imm;
|
||||
} else {
|
||||
lo16 = reg_states[base].prev_addiu_vram;
|
||||
}
|
||||
|
||||
uint32_t address = reg_states[base].prev_lui + lo16;
|
||||
temp.valid_loaded = true;
|
||||
temp.loaded_lw_vram = instr.getVram();
|
||||
temp.loaded_address = address;
|
||||
temp.loaded_addend_reg = reg_states[base].prev_addend_reg;
|
||||
temp.loaded_addu_vram = reg_states[base].prev_addu_vram;
|
||||
}
|
||||
}
|
||||
reg_states[rt] = temp;
|
||||
break;
|
||||
case InstrId::cpu_jr:
|
||||
// Ignore jr $ra
|
||||
if (rs == (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
|
||||
break;
|
||||
}
|
||||
// Check if the source reg has a valid loaded state and if so record that as a jump table
|
||||
if (reg_states[rs].valid_loaded) {
|
||||
stats.jump_tables.emplace_back(
|
||||
reg_states[rs].loaded_address,
|
||||
reg_states[rs].loaded_addend_reg,
|
||||
0,
|
||||
reg_states[rs].loaded_lw_vram,
|
||||
reg_states[rs].loaded_addu_vram,
|
||||
instr.getVram(),
|
||||
std::vector<uint32_t>{}
|
||||
);
|
||||
} else if (reg_states[rs].valid_lui && reg_states[rs].valid_addiu && !reg_states[rs].valid_addend && !reg_states[rs].valid_loaded) {
|
||||
uint32_t address = reg_states[rs].prev_addiu_vram + reg_states[rs].prev_lui;
|
||||
stats.absolute_jumps.emplace_back(
|
||||
address,
|
||||
instr.getVram()
|
||||
);
|
||||
}
|
||||
// Allow tail calls (TODO account for trailing nops due to bad function splits)
|
||||
else if (instr.getVram() != func.vram + (func.words.size() - 2) * sizeof(func.words[0])) {
|
||||
// Inconclusive analysis
|
||||
fmt::print(stderr, "Failed to to find jump table for `jr {}` at 0x{:08X} in {}\n", RabbitizerRegister_getNameGpr(rs), instr.getVram(), func.name);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (instr.modifiesRd()) {
|
||||
reg_states[rd].invalidate();
|
||||
}
|
||||
if (instr.modifiesRt()) {
|
||||
reg_states[rt].invalidate();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
uint32_t address = reg_states[base].prev_lui + lo16;
|
||||
temp.valid_loaded = true;
|
||||
temp.loaded_lw_vram = instr.getVram();
|
||||
temp.loaded_address = address;
|
||||
temp.loaded_addend_reg = reg_states[base].prev_addend_reg;
|
||||
temp.loaded_addu_vram = reg_states[base].prev_addu_vram;
|
||||
}
|
||||
}
|
||||
reg_states[rt] = temp;
|
||||
break;
|
||||
case InstrId::cpu_jr:
|
||||
// Ignore jr $ra
|
||||
if (rs == (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
|
||||
break;
|
||||
}
|
||||
// Check if the source reg has a valid loaded state and if so record that as a jump table
|
||||
if (reg_states[rs].valid_loaded) {
|
||||
stats.jump_tables.emplace_back(
|
||||
reg_states[rs].loaded_address,
|
||||
reg_states[rs].loaded_addend_reg,
|
||||
0,
|
||||
reg_states[rs].loaded_lw_vram,
|
||||
reg_states[rs].loaded_addu_vram,
|
||||
instr.getVram(),
|
||||
std::vector<uint32_t>{}
|
||||
);
|
||||
} else if (reg_states[rs].valid_lui && reg_states[rs].valid_addiu && !reg_states[rs].valid_addend && !reg_states[rs].valid_loaded) {
|
||||
uint32_t address = reg_states[rs].prev_addiu_vram + reg_states[rs].prev_lui;
|
||||
stats.absolute_jumps.emplace_back(
|
||||
address,
|
||||
instr.getVram()
|
||||
);
|
||||
}
|
||||
// Allow tail calls (TODO account for trailing nops due to bad function splits)
|
||||
else if (instr.getVram() != func.vram + (func.words.size() - 2) * sizeof(func.words[0])) {
|
||||
// Inconclusive analysis
|
||||
fmt::print(stderr, "Failed to to find jump table for `jr {}` at 0x{:08X} in {}\n", RabbitizerRegister_getNameGpr(rs), instr.getVram(), func.name);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (instr.modifiesRd()) {
|
||||
reg_states[rd].invalidate();
|
||||
}
|
||||
if (instr.modifiesRt()) {
|
||||
reg_states[rt].invalidate();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecompPort::analyze_function(const RecompPort::Context& context, const RecompPort::Function& func,
|
||||
const std::vector<rabbitizer::InstructionCpu>& instructions, RecompPort::FunctionStats& stats) {
|
||||
// Create a state to track each register (r0 won't be used)
|
||||
RegState reg_states[32] {};
|
||||
std::vector<RegState> stack_states{};
|
||||
const std::vector<rabbitizer::InstructionCpu>& instructions, RecompPort::FunctionStats& stats) {
|
||||
// Create a state to track each register (r0 won't be used)
|
||||
RegState reg_states[32] {};
|
||||
std::vector<RegState> stack_states{};
|
||||
|
||||
// Look for jump tables
|
||||
// A linear search through the func won't be accurate due to not taking control flow into account, but it'll work for finding jtables
|
||||
for (const auto& instr : instructions) {
|
||||
if (!analyze_instruction(instr, func, stats, reg_states, stack_states)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Look for jump tables
|
||||
// A linear search through the func won't be accurate due to not taking control flow into account, but it'll work for finding jtables
|
||||
for (const auto& instr : instructions) {
|
||||
if (!analyze_instruction(instr, func, stats, reg_states, stack_states)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort jump tables by their address
|
||||
std::sort(stats.jump_tables.begin(), stats.jump_tables.end(),
|
||||
[](const JumpTable& a, const JumpTable& b)
|
||||
{
|
||||
return a.vram < b.vram;
|
||||
});
|
||||
// Sort jump tables by their address
|
||||
std::sort(stats.jump_tables.begin(), stats.jump_tables.end(),
|
||||
[](const JumpTable& a, const JumpTable& b)
|
||||
{
|
||||
return a.vram < b.vram;
|
||||
});
|
||||
|
||||
// Determine jump table sizes
|
||||
for (size_t i = 0; i < stats.jump_tables.size(); i++) {
|
||||
JumpTable& cur_jtbl = stats.jump_tables[i];
|
||||
uint32_t end_address = (uint32_t)-1;
|
||||
uint32_t entry_count = 0;
|
||||
uint32_t vram = cur_jtbl.vram;
|
||||
// Determine jump table sizes
|
||||
for (size_t i = 0; i < stats.jump_tables.size(); i++) {
|
||||
JumpTable& cur_jtbl = stats.jump_tables[i];
|
||||
uint32_t end_address = (uint32_t)-1;
|
||||
uint32_t entry_count = 0;
|
||||
uint32_t vram = cur_jtbl.vram;
|
||||
|
||||
if (i < stats.jump_tables.size() - 1) {
|
||||
end_address = stats.jump_tables[i + 1].vram;
|
||||
}
|
||||
if (i < stats.jump_tables.size() - 1) {
|
||||
end_address = stats.jump_tables[i + 1].vram;
|
||||
}
|
||||
|
||||
// TODO this assumes that the jump table is in the same section as the function itself
|
||||
cur_jtbl.rom = cur_jtbl.vram + func.rom - func.vram;
|
||||
// TODO this assumes that the jump table is in the same section as the function itself
|
||||
cur_jtbl.rom = cur_jtbl.vram + func.rom - func.vram;
|
||||
|
||||
while (vram < end_address) {
|
||||
// Retrieve the current entry of the jump table
|
||||
// TODO same as above
|
||||
uint32_t rom_addr = vram + func.rom - func.vram;
|
||||
uint32_t jtbl_word = byteswap(*reinterpret_cast<const uint32_t*>(&context.rom[rom_addr]));
|
||||
// Check if the entry is a valid address in the current function
|
||||
if (jtbl_word < func.vram || jtbl_word > func.vram + func.words.size() * sizeof(func.words[0])) {
|
||||
// If it's not then this is the end of the jump table
|
||||
break;
|
||||
}
|
||||
cur_jtbl.entries.push_back(jtbl_word);
|
||||
vram += 4;
|
||||
}
|
||||
while (vram < end_address) {
|
||||
// Retrieve the current entry of the jump table
|
||||
// TODO same as above
|
||||
uint32_t rom_addr = vram + func.rom - func.vram;
|
||||
uint32_t jtbl_word = byteswap(*reinterpret_cast<const uint32_t*>(&context.rom[rom_addr]));
|
||||
// Check if the entry is a valid address in the current function
|
||||
if (jtbl_word < func.vram || jtbl_word > func.vram + func.words.size() * sizeof(func.words[0])) {
|
||||
// If it's not then this is the end of the jump table
|
||||
break;
|
||||
}
|
||||
cur_jtbl.entries.push_back(jtbl_word);
|
||||
vram += 4;
|
||||
}
|
||||
|
||||
if (cur_jtbl.entries.size() == 0) {
|
||||
fmt::print("Failed to determine size of jump table at 0x{:08X} for instruction at 0x{:08X}\n", cur_jtbl.vram, cur_jtbl.jr_vram);
|
||||
return false;
|
||||
}
|
||||
if (cur_jtbl.entries.size() == 0) {
|
||||
fmt::print("Failed to determine size of jump table at 0x{:08X} for instruction at 0x{:08X}\n", cur_jtbl.vram, cur_jtbl.jr_vram);
|
||||
return false;
|
||||
}
|
||||
|
||||
//fmt::print("Jtbl at 0x{:08X} (rom 0x{:08X}) with {} entries used by instr at 0x{:08X}\n", cur_jtbl.vram, cur_jtbl.rom, cur_jtbl.entries.size(), cur_jtbl.jr_vram);
|
||||
}
|
||||
//fmt::print("Jtbl at 0x{:08X} (rom 0x{:08X}) with {} entries used by instr at 0x{:08X}\n", cur_jtbl.vram, cur_jtbl.rom, cur_jtbl.entries.size(), cur_jtbl.jr_vram);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1518,7 +1518,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
// No after_vram means this will be placed at the start of the function
|
||||
size_t instruction_index = func.words.size();
|
||||
size_t instruction_index = -1;
|
||||
|
||||
// Calculate the instruction index.
|
||||
if (patch.after_vram != 0) {
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
|||
}
|
||||
};
|
||||
|
||||
auto print_func_call = [&](uint32_t target_func_vram, bool link_branch = true) {
|
||||
auto print_func_call = [&](uint32_t target_func_vram, bool link_branch = true, bool indent = false) {
|
||||
const auto matching_funcs_find = context.functions_by_vram.find(target_func_vram);
|
||||
std::string jal_target_name;
|
||||
uint32_t section_vram_start = section.ram_addr;
|
||||
|
|
@ -173,7 +173,11 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
|||
}
|
||||
}
|
||||
needs_link_branch = link_branch;
|
||||
print_unconditional_branch("{}(rdram, ctx)", jal_target_name);
|
||||
if (indent) {
|
||||
print_unconditional_branch(" {}(rdram, ctx)", jal_target_name);
|
||||
} else {
|
||||
print_unconditional_branch("{}(rdram, ctx)", jal_target_name);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
@ -183,9 +187,9 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
|||
if (context.functions_by_vram.find(branch_target) != context.functions_by_vram.end()) {
|
||||
fmt::print(output_file, "{{\n ");
|
||||
fmt::print("Tail call in {} to 0x{:08X}\n", func.name, branch_target);
|
||||
print_func_call(branch_target, false);
|
||||
print_line("return");
|
||||
fmt::print(output_file, ";\n }}\n");
|
||||
print_func_call(branch_target, false, true);
|
||||
print_line(" return");
|
||||
fmt::print(output_file, " }}\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1080,7 +1084,10 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
|
|||
|
||||
auto hook_find = func.function_hooks.find(instr_index);
|
||||
if (hook_find != func.function_hooks.end()) {
|
||||
fmt::print(output_file, "{}\n", hook_find->second);
|
||||
if (indent) {
|
||||
print_indent();
|
||||
}
|
||||
fmt::print(output_file, " {}\n", hook_find->second);
|
||||
}
|
||||
|
||||
// TODO is this used?
|
||||
|
|
@ -1108,7 +1115,7 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
|
|||
// these variables shouldn't need to be preserved across function boundaries, so make them local for more efficient output
|
||||
" uint64_t hi = 0, lo = 0, result = 0;\n"
|
||||
" unsigned int rounding_mode = DEFAULT_ROUNDING_MODE;\n"
|
||||
" int c1cs = 0; \n", // cop1 conditional signal
|
||||
" int c1cs = 0;\n", // cop1 conditional signal
|
||||
func.name);
|
||||
|
||||
// Skip analysis and recompilation of this function is stubbed.
|
||||
|
|
@ -1117,9 +1124,9 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
|
|||
std::set<uint32_t> branch_labels;
|
||||
instructions.reserve(func.words.size());
|
||||
|
||||
auto hook_find = func.function_hooks.find(func.words.size());
|
||||
auto hook_find = func.function_hooks.find(-1);
|
||||
if (hook_find != func.function_hooks.end()) {
|
||||
fmt::print(output_file, "{}\n", hook_find->second);
|
||||
fmt::print(output_file, " {}\n", hook_find->second);
|
||||
}
|
||||
|
||||
// First pass, disassemble each instruction and collect branch labels
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue