diff --git a/LiveRecomp/live_generator.cpp b/LiveRecomp/live_generator.cpp index 0f7bea4..2c9c78d 100644 --- a/LiveRecomp/live_generator.cpp +++ b/LiveRecomp/live_generator.cpp @@ -1512,7 +1512,7 @@ void N64Recomp::LiveGenerator::emit_branch_close() const { context->cur_branch_jump = nullptr; } -void N64Recomp::LiveGenerator::emit_switch(const JumpTable& jtbl, int reg) const { +void N64Recomp::LiveGenerator::emit_switch(const Context& recompiler_context, const JumpTable& jtbl, int reg) const { // Populate the switch's labels. std::vector cur_labels{}; cur_labels.resize(jtbl.entries.size()); @@ -1531,7 +1531,25 @@ void N64Recomp::LiveGenerator::emit_switch(const JumpTable& jtbl, int reg) const sljit_emit_op1(compiler, SLJIT_MOV, Registers::arithmetic_temp1, 0, SLJIT_MEM1(Registers::ctx), get_gpr_context_offset(reg)); // Subtract the jump table's address from the jump target to get the jump table addend. // Sign extend the jump table address to 64 bits so that the entire register's contents are used instead of just the lower 32 bits. - sljit_emit_op2(compiler, SLJIT_SUB, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0, SLJIT_IMM, (sljit_sw)((int32_t)jtbl.vram)); + const auto& jtbl_section = recompiler_context.sections[jtbl.section_index]; + if (jtbl_section.relocatable) { + // Make a dummy instruction context to pass to `load_relocated_address`. + InstructionContext dummy_context{}; + + // Get the relocated address of the jump table. + uint32_t section_offset = jtbl.vram - jtbl_section.ram_addr; + + // Populate the necessary fields of the dummy context and load the relocated address into temp2. + dummy_context.reloc_section_index = jtbl.section_index; + dummy_context.reloc_target_section_offset = section_offset; + load_relocated_address(dummy_context, Registers::arithmetic_temp2); + + // Subtract the relocated jump table start address from the loaded address. + sljit_emit_op2(compiler, SLJIT_SUB, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp2, 0); + } + else { + sljit_emit_op2(compiler, SLJIT_SUB, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0, SLJIT_IMM, (sljit_sw)((int32_t)jtbl.vram)); + } // Bounds check the addend. If it's greater than or equal to the jump table size (entries * sizeof(u32)) then jump to the switch error. sljit_jump* switch_error_jump = sljit_emit_cmp(compiler, SLJIT_GREATER_EQUAL, Registers::arithmetic_temp1, 0, SLJIT_IMM, jtbl.entries.size() * sizeof(uint32_t)); diff --git a/include/recompiler/context.h b/include/recompiler/context.h index a98a9b5..df5224d 100644 --- a/include/recompiler/context.h +++ b/include/recompiler/context.h @@ -44,10 +44,11 @@ namespace N64Recomp { uint32_t lw_vram; uint32_t addu_vram; uint32_t jr_vram; + uint16_t section_index; 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, std::vector&& entries) - : vram(vram), addend_reg(addend_reg), rom(rom), lw_vram(lw_vram), addu_vram(addu_vram), jr_vram(jr_vram), 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 jr_vram, uint16_t section_index, 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), entries(std::move(entries)) {} }; enum class RelocType : uint8_t { diff --git a/include/recompiler/generator.h b/include/recompiler/generator.h index 99dd002..737e920 100644 --- a/include/recompiler/generator.h +++ b/include/recompiler/generator.h @@ -43,7 +43,7 @@ namespace N64Recomp { virtual void emit_jtbl_addend_declaration(const JumpTable& jtbl, int reg) const = 0; virtual void emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const = 0; virtual void emit_branch_close() const = 0; - virtual void emit_switch(const JumpTable& jtbl, int reg) const = 0; + virtual void emit_switch(const Context& recompiler_context, const JumpTable& jtbl, int reg) const = 0; virtual void emit_case(int case_index, const std::string& target_label) const = 0; virtual void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const = 0; virtual void emit_switch_close() const = 0; @@ -79,7 +79,7 @@ namespace N64Recomp { void emit_jtbl_addend_declaration(const JumpTable& jtbl, int reg) const final; void emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const final; void emit_branch_close() const final; - void emit_switch(const JumpTable& jtbl, int reg) const final; + void emit_switch(const Context& recompiler_context, const JumpTable& jtbl, int reg) const final; void emit_case(int case_index, const std::string& target_label) const final; void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const final; void emit_switch_close() const final; diff --git a/include/recompiler/live_recompiler.h b/include/recompiler/live_recompiler.h index 7b14107..840746d 100644 --- a/include/recompiler/live_recompiler.h +++ b/include/recompiler/live_recompiler.h @@ -104,7 +104,7 @@ namespace N64Recomp { void emit_jtbl_addend_declaration(const JumpTable& jtbl, int reg) const final; void emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const final; void emit_branch_close() const final; - void emit_switch(const JumpTable& jtbl, int reg) const final; + void emit_switch(const Context& recompiler_context, const JumpTable& jtbl, int reg) const final; void emit_case(int case_index, const std::string& target_label) const final; void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const final; void emit_switch_close() const final; diff --git a/src/analysis.cpp b/src/analysis.cpp index c790142..92a421e 100644 --- a/src/analysis.cpp +++ b/src/analysis.cpp @@ -194,6 +194,7 @@ bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const N64Recom reg_states[rs].loaded_lw_vram, reg_states[rs].loaded_addu_vram, instr.getVram(), + 0, // section index gets filled in later std::vector{} ); } @@ -245,6 +246,7 @@ bool N64Recomp::analyze_function(const N64Recomp::Context& context, const N64Rec // 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; + cur_jtbl.section_index = func.section_index; while (vram < end_address) { // Retrieve the current entry of the jump table diff --git a/src/cgenerator.cpp b/src/cgenerator.cpp index d3ebacc..4b8f3f8 100644 --- a/src/cgenerator.cpp +++ b/src/cgenerator.cpp @@ -454,7 +454,9 @@ void N64Recomp::CGenerator::emit_switch_close() const { fmt::print(output_file, "}}\n"); } -void N64Recomp::CGenerator::emit_switch(const JumpTable& jtbl, int reg) const { +void N64Recomp::CGenerator::emit_switch(const Context& recompiler_context, const JumpTable& jtbl, int reg) const { + (void)recompiler_context; + (void)reg; // TODO generate code to subtract the jump table address from the register's value instead. // Once that's done, the addend temp can be deleted to simplify the generator interface. std::string jump_variable = fmt::format("jr_addend_{:08X}", jtbl.jr_vram); diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 8500d53..d8cc16d 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -532,7 +532,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con return false; } print_indent(); - generator.emit_switch(cur_jtbl, rs); + generator.emit_switch(context, cur_jtbl, rs); for (size_t entry_index = 0; entry_index < cur_jtbl.entries.size(); entry_index++) { print_indent(); print_indent();