mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-05-30 15:23:17 +00:00
Properly handle relocated jump tables in the live recompiler
This commit is contained in:
parent
75f2abdb7d
commit
f09587bf1f
7 changed files with 32 additions and 9 deletions
|
@ -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<std::string> 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));
|
||||
|
|
|
@ -44,10 +44,11 @@ namespace N64Recomp {
|
|||
uint32_t lw_vram;
|
||||
uint32_t addu_vram;
|
||||
uint32_t jr_vram;
|
||||
uint16_t section_index;
|
||||
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, 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), 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<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), entries(std::move(entries)) {}
|
||||
};
|
||||
|
||||
enum class RelocType : uint8_t {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<uint32_t>{}
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue