diff --git a/include/recompiler/context.h b/include/recompiler/context.h index e7b4431..a071275 100644 --- a/include/recompiler/context.h +++ b/include/recompiler/context.h @@ -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 got_offset; std::vector 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 got_offset, std::vector&& 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 got_offset, std::vector&& 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 { diff --git a/src/analysis.cpp b/src/analysis.cpp index 9eaaa30..1608da9 100644 --- a/src/analysis.cpp +++ b/src/analysis.cpp @@ -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, diff --git a/src/main.cpp b/src/main.cpp index 669c4a1..59e8f5f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -945,11 +945,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); diff --git a/src/recompilation.cpp b/src/recompilation.cpp index c228433..e6761f4 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -112,7 +112,7 @@ std::string_view ctx_gpr_prefix(int reg) { } template -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& jtbl_lw_instructions, size_t instr_index, const std::vector& 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> 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& jtbl_lw_instructions, const std::unordered_set& jtbl_addu_gp_instructions, size_t instr_index, const std::vector& 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> 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 jtbl_lw_instructions{}; + std::unordered_set 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;