diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 12f22c0e0..3346906f7 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -105,8 +105,9 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi) result = Compile_lui(cbi); break; + case InstructionOp::addi: case InstructionOp::addiu: - result = Compile_addiu(cbi); + result = Compile_Add(cbi); break; case InstructionOp::funct: @@ -132,6 +133,11 @@ bool CodeGenerator::CompileInstruction(const CodeBlockInstruction& cbi) result = Compile_MoveHiLo(cbi); break; + case InstructionFunct::add: + case InstructionFunct::addu: + result = Compile_Add(cbi); + break; + case InstructionFunct::mult: case InstructionFunct::multu: result = Compile_Multiply(cbi); @@ -261,10 +267,10 @@ void CodeGenerator::ConvertValueSizeInPlace(Value* value, RegSize size, bool sig value->size = size; } -Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs) +Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs, bool set_flags) { DebugAssert(lhs.size == rhs.size); - if (lhs.IsConstant() && rhs.IsConstant()) + if (lhs.IsConstant() && rhs.IsConstant() && !set_flags) { // compile-time u64 new_cv = lhs.constant_value + rhs.constant_value; @@ -288,12 +294,12 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs) } Value res = m_register_cache.AllocateScratch(lhs.size); - if (lhs.HasConstantValue(0)) + if (lhs.HasConstantValue(0) && !set_flags) { EmitCopyValue(res.host_reg, rhs); return res; } - else if (rhs.HasConstantValue(0)) + else if (rhs.HasConstantValue(0) && !set_flags) { EmitCopyValue(res.host_reg, lhs); return res; @@ -301,7 +307,7 @@ Value CodeGenerator::AddValues(const Value& lhs, const Value& rhs) else { EmitCopyValue(res.host_reg, lhs); - EmitAdd(res.host_reg, rhs); + EmitAdd(res.host_reg, rhs, set_flags); return res; } } @@ -903,7 +909,7 @@ bool CodeGenerator::Compile_Load(const CodeBlockInstruction& cbi) // rt <- mem[rs + sext(imm)] Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs); Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32()); - Value address = AddValues(base, offset); + Value address = AddValues(base, offset, false); Value result; switch (cbi.instruction.op) @@ -942,7 +948,7 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi) // mem[rs + sext(imm)] <- rt Value base = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs); Value offset = Value::FromConstantU32(cbi.instruction.i.imm_sext32()); - Value address = AddValues(base, offset); + Value address = AddValues(base, offset, false); Value value = m_register_cache.ReadGuestRegister(cbi.instruction.i.rt); switch (cbi.instruction.op) @@ -999,6 +1005,52 @@ bool CodeGenerator::Compile_MoveHiLo(const CodeBlockInstruction& cbi) return true; } +bool CodeGenerator::Compile_Add(const CodeBlockInstruction& cbi) +{ + InstructionPrologue(cbi, 1); + + const bool check_overflow = + (cbi.instruction.op == InstructionOp::addi || + (cbi.instruction.op == InstructionOp::funct && cbi.instruction.r.funct == InstructionFunct::add)); + + Value lhs, rhs; + Reg dest; + switch (cbi.instruction.op) + { + case InstructionOp::addi: + case InstructionOp::addiu: + { + // rt <- rs + sext(imm) + dest = cbi.instruction.i.rt; + lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs); + rhs = Value::FromConstantU32(cbi.instruction.i.imm_sext32()); + } + break; + + case InstructionOp::funct: + { + Assert(cbi.instruction.r.funct == InstructionFunct::add || cbi.instruction.r.funct == InstructionFunct::addu); + dest = cbi.instruction.r.rd; + lhs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rs); + rhs = m_register_cache.ReadGuestRegister(cbi.instruction.r.rt); + } + break; + + default: + UnreachableCode(); + return false; + } + + Value result = AddValues(lhs, rhs, check_overflow); + if (check_overflow) + EmitRaiseException(Exception::Ov, Condition::Overflow); + + m_register_cache.WriteGuestRegister(dest, std::move(result)); + + InstructionEpilogue(cbi); + return true; +} + bool CodeGenerator::Compile_Multiply(const CodeBlockInstruction& cbi) { InstructionPrologue(cbi, 1); @@ -1052,7 +1104,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi) { // npc = pc + (sext(imm) << 2) Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false), - Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2)); + Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false); // branch <- rs op rt Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true); @@ -1069,7 +1121,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi) { // npc = pc + (sext(imm) << 2) Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false), - Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2)); + Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false); // branch <- rs op 0 Value lhs = m_register_cache.ReadGuestRegister(cbi.instruction.i.rs, true, true); @@ -1085,7 +1137,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi) { // npc = pc + (sext(imm) << 2) Value branch_target = AddValues(m_register_cache.ReadGuestRegister(Reg::pc, false), - Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2)); + Value::FromConstantU32(cbi.instruction.i.imm_sext32() << 2), false); const u8 rt = static_cast(cbi.instruction.i.rt.GetValue()); const bool bgez = ConvertToBoolUnchecked(rt & u8(1)); @@ -1118,18 +1170,4 @@ bool CodeGenerator::Compile_lui(const CodeBlockInstruction& cbi) InstructionEpilogue(cbi); return true; } - -bool CodeGenerator::Compile_addiu(const CodeBlockInstruction& cbi) -{ - InstructionPrologue(cbi, 1); - - // rt <- rs + sext(imm) - m_register_cache.WriteGuestRegister(cbi.instruction.i.rt, - AddValues(m_register_cache.ReadGuestRegister(cbi.instruction.i.rs), - Value::FromConstantU32(cbi.instruction.i.imm_sext32()))); - - InstructionEpilogue(cbi); - return true; -} - } // namespace CPU::Recompiler diff --git a/src/core/cpu_recompiler_code_generator.h b/src/core/cpu_recompiler_code_generator.h index 3f0809e40..162a973eb 100644 --- a/src/core/cpu_recompiler_code_generator.h +++ b/src/core/cpu_recompiler_code_generator.h @@ -47,7 +47,7 @@ public: void EmitSignExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size); void EmitZeroExtend(HostReg to_reg, RegSize to_size, HostReg from_reg, RegSize from_size); void EmitCopyValue(HostReg to_reg, const Value& value); - void EmitAdd(HostReg to_reg, const Value& value); + void EmitAdd(HostReg to_reg, const Value& value, bool set_flags); void EmitSub(HostReg to_reg, const Value& value); void EmitCmp(HostReg to_reg, const Value& value); void EmitMul(HostReg to_reg_hi, HostReg to_reg_lo, const Value& lhs, const Value& rhs, bool signed_multiply); @@ -76,6 +76,9 @@ public: // Branching, generates two paths. void EmitBranch(Condition condition, Reg lr_reg, bool always_link, Value&& branch_target); + // Raising exception if condition is true. + void EmitRaiseException(Exception excode, Condition condition = Condition::Always); + u32 PrepareStackForCall(); void RestoreStackAfterCall(u32 adjust_size); @@ -130,7 +133,7 @@ public: #endif // Value ops - Value AddValues(const Value& lhs, const Value& rhs); + Value AddValues(const Value& lhs, const Value& rhs, bool set_flags); std::pair MulValues(const Value& lhs, const Value& rhs, bool signed_multiply); Value ShlValues(const Value& lhs, const Value& rhs); Value ShrValues(const Value& lhs, const Value& rhs); @@ -175,10 +178,10 @@ private: bool Compile_Load(const CodeBlockInstruction& cbi); bool Compile_Store(const CodeBlockInstruction& cbi); bool Compile_MoveHiLo(const CodeBlockInstruction& cbi); + bool Compile_Add(const CodeBlockInstruction& cbi); bool Compile_Multiply(const CodeBlockInstruction& cbi); bool Compile_Branch(const CodeBlockInstruction& cbi); bool Compile_lui(const CodeBlockInstruction& cbi); - bool Compile_addiu(const CodeBlockInstruction& cbi); Core* m_cpu; JitCodeBuffer* m_code_buffer; diff --git a/src/core/cpu_recompiler_code_generator_generic.cpp b/src/core/cpu_recompiler_code_generator_generic.cpp index 1436cdef1..cafa90ed6 100644 --- a/src/core/cpu_recompiler_code_generator_generic.cpp +++ b/src/core/cpu_recompiler_code_generator_generic.cpp @@ -44,6 +44,10 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool relative, c { Panic("Not implemented"); } +void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */) +{ + Panic("Not implemented"); +} #endif } // namespace CPU::Recompiler \ No newline at end of file diff --git a/src/core/cpu_recompiler_code_generator_x64.cpp b/src/core/cpu_recompiler_code_generator_x64.cpp index 1212a6a7c..bc8d0f4b0 100644 --- a/src/core/cpu_recompiler_code_generator_x64.cpp +++ b/src/core/cpu_recompiler_code_generator_x64.cpp @@ -186,7 +186,7 @@ void CodeGenerator::EmitExceptionExit() // the interpreter load delay might have its own value, but we'll overwrite it here anyway // technically RaiseException() and FlushPipeline() have already been called, but that should be okay - m_register_cache.FlushLoadDelayForException(); + m_register_cache.FlushLoadDelay(false); m_register_cache.PopCalleeSavedRegisters(false); m_emit->ret(); @@ -339,7 +339,7 @@ void CodeGenerator::EmitCopyValue(HostReg to_reg, const Value& value) } } -void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value) +void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags) { DebugAssert(value.IsConstant() || value.IsInHostRegister()); @@ -1601,7 +1601,8 @@ void CodeGenerator::EmitDelaySlotUpdate(bool skip_check_for_delay, bool skip_che } } -static void EmitConditionalJump(Condition condition, bool invert, Xbyak::CodeGenerator* emit, const Xbyak::Label& label) +template +static void EmitConditionalJump(Condition condition, bool invert, Xbyak::CodeGenerator* emit, const T& label) { switch (condition) { @@ -1712,6 +1713,28 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, bool always_link m_emit->L(skip_branch); } +void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */) +{ + if (condition == Condition::Always) + { + // no need to use far code if we're always raising the exception + EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), + Value::FromConstantU8(static_cast(excode))); + m_register_cache.FlushAllGuestRegisters(true, true); + m_register_cache.FlushLoadDelay(true); + return; + } + + const void* far_code_ptr = GetCurrentFarCodePointer(); + EmitConditionalJump(condition, false, m_emit, far_code_ptr); + + SwitchToFarCode(); + EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), + Value::FromConstantU8(static_cast(excode))); + EmitExceptionExit(); + SwitchToNearCode(); +} + #if 0 class ThunkGenerator { diff --git a/src/core/cpu_recompiler_register_cache.cpp b/src/core/cpu_recompiler_register_cache.cpp index e33999896..955a5a168 100644 --- a/src/core/cpu_recompiler_register_cache.cpp +++ b/src/core/cpu_recompiler_register_cache.cpp @@ -570,7 +570,7 @@ void RegisterCache::WriteLoadDelayToCPU(bool clear) } } -void RegisterCache::FlushLoadDelayForException() +void RegisterCache::FlushLoadDelay(bool clear) { Assert(m_next_load_delay_register == Reg::count); if (m_load_delay_register == Reg::count) @@ -579,6 +579,12 @@ void RegisterCache::FlushLoadDelayForException() // if this is an exception exit, write the new value to the CPU register file, but keep it tracked for the next // non-exception-raised path. TODO: push/pop whole state would avoid this issue m_code_generator.EmitStoreGuestRegister(m_load_delay_register, m_load_delay_value); + + if (clear) + { + m_load_delay_register = Reg::count; + m_load_delay_value.ReleaseAndClear(); + } } void RegisterCache::FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty) diff --git a/src/core/cpu_recompiler_register_cache.h b/src/core/cpu_recompiler_register_cache.h index b30bf7082..b8e068beb 100644 --- a/src/core/cpu_recompiler_register_cache.h +++ b/src/core/cpu_recompiler_register_cache.h @@ -252,7 +252,7 @@ public: void WriteLoadDelayToCPU(bool clear); /// Flushes the load delay, i.e. writes it to the destination register. - void FlushLoadDelayForException(); + void FlushLoadDelay(bool clear); void FlushGuestRegister(Reg guest_reg, bool invalidate, bool clear_dirty); void InvalidateGuestRegister(Reg guest_reg); diff --git a/src/core/cpu_recompiler_thunks.cpp b/src/core/cpu_recompiler_thunks.cpp index 77652fceb..f51a4e3d3 100644 --- a/src/core/cpu_recompiler_thunks.cpp +++ b/src/core/cpu_recompiler_thunks.cpp @@ -78,6 +78,11 @@ void Thunks::UpdateLoadDelay(Core* cpu) cpu->UpdateLoadDelay(); } +void Thunks::RaiseException(Core* cpu, u8 excode) +{ + cpu->RaiseException(static_cast(excode)); +} + void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch) { cpu->m_cop0_regs.BadVaddr = address; diff --git a/src/core/cpu_recompiler_thunks.h b/src/core/cpu_recompiler_thunks.h index e5c903d7f..9e858ceb1 100644 --- a/src/core/cpu_recompiler_thunks.h +++ b/src/core/cpu_recompiler_thunks.h @@ -21,6 +21,7 @@ public: static bool WriteMemoryWord(Core* cpu, u32 address, u32 value); static bool InterpretInstruction(Core* cpu); static void UpdateLoadDelay(Core* cpu); + static void RaiseException(Core* cpu, u8 excode); static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch); };