mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-06-10 18:12:16 +00:00
Live recompiler support for PIC jump tables
This commit is contained in:
parent
81213c1831
commit
1c2569e2e3
4 changed files with 28 additions and 7 deletions
|
|
@ -44,13 +44,14 @@ namespace N64Recomp {
|
|||
uint32_t rom;
|
||||
uint32_t lw_vram;
|
||||
uint32_t addu_vram;
|
||||
uint32_t addu_gp_vram;
|
||||
uint32_t jr_vram;
|
||||
uint16_t section_index;
|
||||
std::optional<uint32_t> got_offset;
|
||||
std::vector<uint32_t> entries;
|
||||
|
||||
JumpTable(uint32_t vram, uint32_t addend_reg, uint32_t rom, uint32_t lw_vram, uint32_t addu_vram, uint32_t jr_vram, uint16_t section_index, std::optional<uint32_t> got_offset, std::vector<uint32_t>&& entries)
|
||||
: vram(vram), addend_reg(addend_reg), rom(rom), lw_vram(lw_vram), addu_vram(addu_vram), jr_vram(jr_vram), section_index(section_index), got_offset(got_offset), entries(std::move(entries)) {}
|
||||
JumpTable(uint32_t vram, uint32_t addend_reg, uint32_t rom, uint32_t lw_vram, uint32_t addu_vram, uint32_t addu_gp_vram, uint32_t jr_vram, uint16_t section_index, std::optional<uint32_t> got_offset, std::vector<uint32_t>&& entries)
|
||||
: vram(vram), addend_reg(addend_reg), rom(rom), lw_vram(lw_vram), addu_vram(addu_vram), addu_gp_vram(addu_gp_vram), jr_vram(jr_vram), section_index(section_index), got_offset(got_offset), entries(std::move(entries)) {}
|
||||
};
|
||||
|
||||
enum class RelocType : uint8_t {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct RegState {
|
|||
uint8_t loaded_addend_reg;
|
||||
bool valid_loaded;
|
||||
bool valid_got_loaded; // valid load through the GOT
|
||||
uint32_t addu_gp_vram;
|
||||
|
||||
RegState() = default;
|
||||
|
||||
|
|
@ -50,6 +51,8 @@ struct RegState {
|
|||
|
||||
valid_loaded = false;
|
||||
valid_got_loaded = false;
|
||||
|
||||
addu_gp_vram = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -121,6 +124,7 @@ bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const N64Recom
|
|||
int valid_got_loaded_reg = reg_states[rs].valid_got_loaded ? rs : rt;
|
||||
|
||||
temp = reg_states[valid_got_loaded_reg];
|
||||
temp.addu_gp_vram = instr.getVram();
|
||||
}
|
||||
// Exactly one of the two addend register states should have a valid lui at this time
|
||||
else if (reg_states[rs].valid_lui != reg_states[rt].valid_lui) {
|
||||
|
|
@ -232,6 +236,7 @@ bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const N64Recom
|
|||
0,
|
||||
reg_states[rs].loaded_lw_vram,
|
||||
reg_states[rs].loaded_addu_vram,
|
||||
reg_states[rs].addu_gp_vram,
|
||||
instr.getVram(),
|
||||
0, // section index gets filled in later
|
||||
std::nullopt,
|
||||
|
|
@ -244,6 +249,7 @@ bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const N64Recom
|
|||
0,
|
||||
reg_states[rs].loaded_lw_vram,
|
||||
reg_states[rs].loaded_addu_vram,
|
||||
reg_states[rs].addu_gp_vram,
|
||||
instr.getVram(),
|
||||
0, // section index gets filled in later
|
||||
reg_states[rs].prev_got_offset,
|
||||
|
|
|
|||
|
|
@ -920,11 +920,13 @@ int main(int argc, char** argv) {
|
|||
std::string section_funcs_array_name = fmt::format("section_{}_{}_funcs", section_index, section_name_trimmed);
|
||||
std::string section_relocs_array_name = section_relocs.empty() ? "nullptr" : fmt::format("section_{}_{}_relocs", section_index, section_name_trimmed);
|
||||
std::string section_relocs_array_size = section_relocs.empty() ? "0" : fmt::format("ARRLEN({})", section_relocs_array_name);
|
||||
std::string section_got_ram_addr = !section.got_ram_addr.has_value() ? "std::nullopt" : fmt::format("0x{:08X}", section.got_ram_addr.value());
|
||||
|
||||
// Write the section's table entry.
|
||||
section_load_table += fmt::format(" {{ .rom_addr = 0x{0:08X}, .ram_addr = 0x{1:08X}, .size = 0x{2:08X}, .funcs = {3}, .num_funcs = ARRLEN({3}), .relocs = {4}, .num_relocs = {5}, .index = {6} }},\n",
|
||||
section_load_table += fmt::format(" {{ .rom_addr = 0x{0:08X}, .ram_addr = 0x{1:08X}, .size = 0x{2:08X}, .funcs = {3}, .num_funcs = ARRLEN({3}), .relocs = {4}, .num_relocs = {5}, .index = {6}, .got_ram_addr = {7} }},\n",
|
||||
section.rom_addr, section.ram_addr, section.size, section_funcs_array_name,
|
||||
section_relocs_array_name, section_relocs_array_size, section_index);
|
||||
section_relocs_array_name, section_relocs_array_size, section_index,
|
||||
section_got_ram_addr);
|
||||
|
||||
// Write the section's functions.
|
||||
fmt::print(overlay_file, "static FuncEntry {}[] = {{\n", section_funcs_array_name);
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ std::string_view ctx_gpr_prefix(int reg) {
|
|||
}
|
||||
|
||||
template <typename GeneratorType>
|
||||
bool process_instruction(GeneratorType& generator, const N64Recomp::Context& context, const N64Recomp::Function& func, size_t func_index, const N64Recomp::FunctionStats& stats, const std::unordered_set<uint32_t>& jtbl_lw_instructions, size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ostream& output_file, bool indent, bool emit_link_branch, int link_branch_index, size_t reloc_index, bool& needs_link_branch, bool& is_branch_likely, bool tag_reference_relocs, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||
bool process_instruction(GeneratorType& generator, const N64Recomp::Context& context, const N64Recomp::Function& func, size_t func_index, const N64Recomp::FunctionStats& stats, const std::unordered_set<uint32_t>& jtbl_lw_instructions, const std::unordered_set<uint32_t>& jtbl_addu_gp_instructions, size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ostream& output_file, bool indent, bool emit_link_branch, int link_branch_index, size_t reloc_index, bool& needs_link_branch, bool& is_branch_likely, bool tag_reference_relocs, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||
using namespace N64Recomp;
|
||||
|
||||
const auto& section = context.sections[func.section_index];
|
||||
|
|
@ -150,6 +150,12 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
assert(instr_id == InstrId::cpu_lw);
|
||||
instr_id = InstrId::cpu_addiu;
|
||||
}
|
||||
// For PIC, the instruction after the jump table load should be an addu with the $gp register. This will interfere
|
||||
// with calculating the jump table value later, so turn it into a nop.
|
||||
if (section.got_ram_addr.has_value() && jtbl_addu_gp_instructions.contains(instr_vram)) {
|
||||
assert(instr_id == InstrId::cpu_addu);
|
||||
instr_id = InstrId::cpu_nop;
|
||||
}
|
||||
|
||||
N64Recomp::RelocType reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
|
||||
bool has_reloc = false;
|
||||
|
|
@ -215,7 +221,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
||||
next_reloc_index++;
|
||||
}
|
||||
if (!process_instruction(generator, context, func, func_index, stats, jtbl_lw_instructions, instr_index + 1, instructions, output_file, use_indent, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, tag_reference_relocs, static_funcs_out)) {
|
||||
if (!process_instruction(generator, context, func, func_index, stats, jtbl_lw_instructions, jtbl_addu_gp_instructions, instr_index + 1, instructions, output_file, use_indent, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, tag_reference_relocs, static_funcs_out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -807,6 +813,7 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
|||
}
|
||||
|
||||
std::unordered_set<uint32_t> jtbl_lw_instructions{};
|
||||
std::unordered_set<uint32_t> jtbl_addu_gp_instructions{};
|
||||
|
||||
// Add jump table labels into function
|
||||
for (const auto& jtbl : stats.jump_tables) {
|
||||
|
|
@ -816,6 +823,11 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
|||
}
|
||||
}
|
||||
|
||||
// Collect jump table addu reg, reg, $gp instructions for PIC
|
||||
for (const auto& jtbl : stats.jump_tables) {
|
||||
jtbl_addu_gp_instructions.insert(jtbl.addu_gp_vram);
|
||||
}
|
||||
|
||||
// Second pass, emit code for each instruction and emit labels
|
||||
auto cur_label = branch_labels.cbegin();
|
||||
vram = func.vram;
|
||||
|
|
@ -844,7 +856,7 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
|||
}
|
||||
|
||||
// Process the current instruction and check for errors
|
||||
if (process_instruction(generator, context, func, func_index, stats, jtbl_lw_instructions, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, tag_reference_relocs, static_funcs_out) == false) {
|
||||
if (process_instruction(generator, context, func, func_index, stats, jtbl_lw_instructions, jtbl_addu_gp_instructions, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, tag_reference_relocs, static_funcs_out) == false) {
|
||||
fmt::print(stderr, "Error in recompiling {}, clearing output file\n", func.name);
|
||||
output_file.clear();
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue