| 57 | using RegId = rabbitizer::Registers::Cpu::GprO32; |
| 58 | |
| 59 | bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const N64Recomp::Function& func, N64Recomp::FunctionStats& stats, |
| 60 | RegState reg_states[32], std::vector<RegState>& stack_states, bool is_got_addr_defined) { |
| 61 | // Temporary register state for tracking the register being operated on |
| 62 | RegState temp{}; |
| 63 | |
| 64 | int rd = (int)instr.GetO32_rd(); |
| 65 | int rs = (int)instr.GetO32_rs(); |
| 66 | int base = rs; |
| 67 | int rt = (int)instr.GetO32_rt(); |
| 68 | int sa = (int)instr.Get_sa(); |
| 69 | |
| 70 | uint16_t imm = instr.Get_immediate(); |
| 71 | |
| 72 | auto check_move = [&]() { |
| 73 | if (rs == 0) { |
| 74 | // rs is zero so copy rt to rd |
| 75 | reg_states[rd] = reg_states[rt]; |
| 76 | } else if (rt == 0) { |
| 77 | // rt is zero so copy rs to rd |
| 78 | reg_states[rd] = reg_states[rs]; |
| 79 | } else { |
| 80 | // Not a move, invalidate rd |
| 81 | reg_states[rd].invalidate(); |
| 82 | } |
| 83 | }; |
| 84 | |
| 85 | switch (instr.getUniqueId()) { |
| 86 | case InstrId::cpu_lui: |
| 87 | // rt has been completely overwritten, so invalidate it |
| 88 | reg_states[rt].invalidate(); |
| 89 | reg_states[rt].prev_lui = (int16_t)imm << 16; |
| 90 | reg_states[rt].valid_lui = true; |
| 91 | break; |
| 92 | case InstrId::cpu_addiu: |
| 93 | // The target reg is a copy of the source reg plus an immediate, so copy the source reg's state |
| 94 | reg_states[rt] = reg_states[rs]; |
| 95 | // Set the addiu state if and only if there hasn't been an addiu already |
| 96 | if (!reg_states[rt].valid_addiu) { |
| 97 | reg_states[rt].prev_addiu_vram = (int16_t)imm; |
| 98 | reg_states[rt].valid_addiu = true; |
| 99 | } else { |
| 100 | // Otherwise, there have been 2 or more consecutive addius so invalidate the whole register |
| 101 | reg_states[rt].invalidate(); |
| 102 | } |
| 103 | break; |
| 104 | case InstrId::cpu_addu: |
| 105 | // rd has been completely overwritten, so invalidate it |
| 106 | temp.invalidate(); |
| 107 | if (reg_states[rs].valid_got_offset != reg_states[rt].valid_got_offset) { |
| 108 | // Track which of the two registers has the valid GOT offset state and which is the addend |
| 109 | int valid_got_offset_reg = reg_states[rs].valid_got_offset ? rs : rt; |
| 110 | int addend_reg = reg_states[rs].valid_got_offset ? rt : rs; |
| 111 | |
| 112 | // Copy the got offset reg's state into the destination reg, then set the destination reg's addend to the other operand |
| 113 | temp = reg_states[valid_got_offset_reg]; |
| 114 | temp.valid_addend = true; |
| 115 | temp.prev_addend_reg = addend_reg; |
| 116 | temp.prev_addu_vram = instr.getVram(); |
no test coverage detected