From 53c0dc8bbc05cd859739a815875400d36322f857 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 23 Feb 2024 11:57:38 +0900 Subject: [PATCH] CPU/NewRec: Speculative execution for mult/div --- src/core/cpu_newrec_compiler.cpp | 136 +++++++++++++++++++++---------- src/core/cpu_newrec_compiler.h | 3 + 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/src/core/cpu_newrec_compiler.cpp b/src/core/cpu_newrec_compiler.cpp index 92f76dd1b..4e9a50114 100644 --- a/src/core/cpu_newrec_compiler.cpp +++ b/src/core/cpu_newrec_compiler.cpp @@ -2013,6 +2013,42 @@ void CPU::NewRec::Compiler::Compile_multu_const(CompileFlags cf) SetConstantReg(Reg::lo, static_cast(res)); } +void CPU::NewRec::Compiler::MIPSSignedDivide(s32 num, s32 denom, u32* lo, u32* hi) +{ + if (denom == 0) + { + // divide by zero + *lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1); + *hi = static_cast(num); + } + else if (static_cast(num) == UINT32_C(0x80000000) && denom == -1) + { + // unrepresentable + *lo = UINT32_C(0x80000000); + *hi = 0; + } + else + { + *lo = static_cast(num / denom); + *hi = static_cast(num % denom); + } +} + +void CPU::NewRec::Compiler::MIPSUnsignedDivide(u32 num, u32 denom, u32* lo, u32* hi) +{ + if (denom == 0) + { + // divide by zero + *lo = UINT32_C(0xFFFFFFFF); + *hi = static_cast(num); + } + else + { + *lo = num / denom; + *hi = num % denom; + } +} + void CPU::NewRec::Compiler::Compile_div_const(CompileFlags cf) { DebugAssert(HasConstantReg(cf.MipsS()) && HasConstantReg(cf.MipsT())); @@ -2020,24 +2056,8 @@ void CPU::NewRec::Compiler::Compile_div_const(CompileFlags cf) const s32 num = GetConstantRegS32(cf.MipsS()); const s32 denom = GetConstantRegS32(cf.MipsT()); - s32 lo, hi; - if (denom == 0) - { - // divide by zero - lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1); - hi = static_cast(num); - } - else if (static_cast(num) == UINT32_C(0x80000000) && denom == -1) - { - // unrepresentable - lo = UINT32_C(0x80000000); - hi = 0; - } - else - { - lo = num / denom; - hi = num % denom; - } + u32 lo, hi; + MIPSSignedDivide(num, denom, &lo, &hi); SetConstantReg(Reg::hi, hi); SetConstantReg(Reg::lo, lo); @@ -2051,18 +2071,7 @@ void CPU::NewRec::Compiler::Compile_divu_const(CompileFlags cf) const u32 denom = GetConstantRegU32(cf.MipsT()); u32 lo, hi; - - if (denom == 0) - { - // divide by zero - lo = UINT32_C(0xFFFFFFFF); - hi = static_cast(num); - } - else - { - lo = num / denom; - hi = num % denom; - } + MIPSUnsignedDivide(num, denom, &lo, &hi); SetConstantReg(Reg::hi, hi); SetConstantReg(Reg::lo, lo); @@ -2520,30 +2529,73 @@ void CPU::NewRec::Compiler::SpecExec_srav() void CPU::NewRec::Compiler::SpecExec_mult() { - // TODO - SpecInvalidateReg(Reg::hi); - SpecInvalidateReg(Reg::lo); + const SpecValue rs = SpecReadReg(inst->r.rs); + const SpecValue rt = SpecReadReg(inst->r.rt); + if (rs.has_value() && rt.has_value()) + { + const u64 result = + static_cast(static_cast(SignExtend64(rs.value())) * static_cast(SignExtend64(rt.value()))); + SpecWriteReg(Reg::hi, Truncate32(result >> 32)); + SpecWriteReg(Reg::lo, Truncate32(result)); + } + else + { + SpecInvalidateReg(Reg::hi); + SpecInvalidateReg(Reg::lo); + } } void CPU::NewRec::Compiler::SpecExec_multu() { - // TODO - SpecInvalidateReg(Reg::hi); - SpecInvalidateReg(Reg::lo); + const SpecValue rs = SpecReadReg(inst->r.rs); + const SpecValue rt = SpecReadReg(inst->r.rt); + if (rs.has_value() && rt.has_value()) + { + const u64 result = ZeroExtend64(rs.value()) * SignExtend64(rt.value()); + SpecWriteReg(Reg::hi, Truncate32(result >> 32)); + SpecWriteReg(Reg::lo, Truncate32(result)); + } + else + { + SpecInvalidateReg(Reg::hi); + SpecInvalidateReg(Reg::lo); + } } void CPU::NewRec::Compiler::SpecExec_div() { - // TODO - SpecInvalidateReg(Reg::hi); - SpecInvalidateReg(Reg::lo); + const SpecValue rs = SpecReadReg(inst->r.rs); + const SpecValue rt = SpecReadReg(inst->r.rt); + if (rs.has_value() && rt.has_value()) + { + u32 lo, hi; + MIPSSignedDivide(static_cast(rs.value()), static_cast(rt.value()), &lo, &hi); + SpecWriteReg(Reg::hi, hi); + SpecWriteReg(Reg::lo, lo); + } + else + { + SpecInvalidateReg(Reg::hi); + SpecInvalidateReg(Reg::lo); + } } void CPU::NewRec::Compiler::SpecExec_divu() { - // TODO - SpecInvalidateReg(Reg::hi); - SpecInvalidateReg(Reg::lo); + const SpecValue rs = SpecReadReg(inst->r.rs); + const SpecValue rt = SpecReadReg(inst->r.rt); + if (rs.has_value() && rt.has_value()) + { + u32 lo, hi; + MIPSUnsignedDivide(rs.value(), rt.value(), &lo, &hi); + SpecWriteReg(Reg::hi, hi); + SpecWriteReg(Reg::lo, lo); + } + else + { + SpecInvalidateReg(Reg::hi); + SpecInvalidateReg(Reg::lo); + } } void CPU::NewRec::Compiler::SpecExec_add() diff --git a/src/core/cpu_newrec_compiler.h b/src/core/cpu_newrec_compiler.h index 0f5f49104..d40a1a6c4 100644 --- a/src/core/cpu_newrec_compiler.h +++ b/src/core/cpu_newrec_compiler.h @@ -375,6 +375,9 @@ protected: static u32* GetCop0RegPtr(Cop0Reg reg); static u32 GetCop0RegWriteMask(Cop0Reg reg); + static void MIPSSignedDivide(s32 num, s32 denom, u32* lo, u32* hi); + static void MIPSUnsignedDivide(u32 num, u32 denom, u32* lo, u32* hi); + void Compile_mfc0(CompileFlags cf); virtual void Compile_mtc0(CompileFlags cf) = 0; virtual void Compile_rfe(CompileFlags cf) = 0;