| 1587 | } |
| 1588 | |
| 1589 | void N64Recomp::LiveGenerator::emit_switch(const Context& recompiler_context, const JumpTable& jtbl, int reg) const { |
| 1590 | // Populate the switch's labels. |
| 1591 | std::vector<std::string> cur_labels{}; |
| 1592 | cur_labels.resize(jtbl.entries.size()); |
| 1593 | for (size_t i = 0; i < cur_labels.size(); i++) { |
| 1594 | cur_labels[i] = fmt::format("L_{:08X}", jtbl.entries[i]); |
| 1595 | } |
| 1596 | context->switch_jump_labels.emplace_back(std::move(cur_labels)); |
| 1597 | |
| 1598 | // Allocate the jump table. |
| 1599 | std::unique_ptr<void* []> cur_jump_table = std::make_unique<void* []>(jtbl.entries.size()); |
| 1600 | |
| 1601 | /// Codegen |
| 1602 | |
| 1603 | // Load the jump target register. The lw instruction was patched into an addiu, so this holds |
| 1604 | // the address of the jump table entry instead of the actual jump target. |
| 1605 | sljit_emit_op1(compiler, SLJIT_MOV, Registers::arithmetic_temp1, 0, SLJIT_MEM1(Registers::ctx), get_gpr_context_offset(reg)); |
| 1606 | // Subtract the jump table's address from the jump target to get the jump table addend. |
| 1607 | // 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. |
| 1608 | const auto& jtbl_section = recompiler_context.sections[jtbl.section_index]; |
| 1609 | if (jtbl_section.relocatable) { |
| 1610 | // Make a dummy instruction context to pass to `load_relocated_address`. |
| 1611 | InstructionContext dummy_context{}; |
| 1612 | |
| 1613 | // Get the relocated address of the jump table. |
| 1614 | uint32_t section_offset = jtbl.vram - jtbl_section.ram_addr; |
| 1615 | |
| 1616 | // Get the section index to use for relocation at runtime. |
| 1617 | uint16_t reloc_section_index = jtbl.section_index; |
| 1618 | if (!inputs.original_section_indices.empty()) { |
| 1619 | reloc_section_index = inputs.original_section_indices[reloc_section_index]; |
| 1620 | } |
| 1621 | |
| 1622 | // Populate the necessary fields of the dummy context and load the relocated address into temp2. |
| 1623 | dummy_context.reloc_section_index = reloc_section_index; |
| 1624 | dummy_context.reloc_target_section_offset = section_offset; |
| 1625 | load_relocated_address(dummy_context, Registers::arithmetic_temp2); |
| 1626 | |
| 1627 | // Subtract the relocated jump table start address from the loaded address. |
| 1628 | sljit_emit_op2(compiler, SLJIT_SUB, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp2, 0); |
| 1629 | } |
| 1630 | else { |
| 1631 | sljit_emit_op2(compiler, SLJIT_SUB, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0, SLJIT_IMM, (sljit_sw)((int32_t)jtbl.vram)); |
| 1632 | } |
| 1633 | |
| 1634 | // 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. |
| 1635 | sljit_jump* switch_error_jump = sljit_emit_cmp(compiler, SLJIT_GREATER_EQUAL, Registers::arithmetic_temp1, 0, SLJIT_IMM, jtbl.entries.size() * sizeof(uint32_t)); |
| 1636 | context->switch_error_jumps.emplace_back(SwitchErrorJump{.instr_vram = jtbl.jr_vram, .jtbl_vram = jtbl.vram, .jump = switch_error_jump}); |
| 1637 | |
| 1638 | // Multiply the jump table addend by 2 to get the addend for the real jump table. (4 bytes per entry to 8 bytes per entry). |
| 1639 | sljit_emit_op2(compiler, SLJIT_ADD, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0, Registers::arithmetic_temp1, 0); |
| 1640 | // Load the real jump table address. |
| 1641 | sljit_emit_op1(compiler, SLJIT_MOV, Registers::arithmetic_temp2, 0, SLJIT_IMM, (sljit_sw)cur_jump_table.get()); |
| 1642 | // Load the real jump entry. |
| 1643 | sljit_emit_op1(compiler, SLJIT_MOV, Registers::arithmetic_temp1, 0, SLJIT_MEM2(Registers::arithmetic_temp1, Registers::arithmetic_temp2), 0); |
| 1644 | // Jump to the loaded entry. |
| 1645 | sljit_emit_ijump(compiler, SLJIT_JUMP, Registers::arithmetic_temp1, 0); |
| 1646 |
nothing calls this directly
no test coverage detected