JitArm64: Skip accurate single/double conversion if store-safe

This commit is contained in:
JosJuice 2021-02-02 21:43:50 +01:00
parent 1d106ceaf5
commit 2a9d88739c
10 changed files with 62 additions and 21 deletions

View file

@ -982,6 +982,7 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
js.compilerPC = op.address;
js.op = &op;
js.fpr_is_store_safe = op.fprIsStoreSafeBeforeInst;
js.instructionNumber = i;
js.instructionsLeft = (code_block.m_num_instructions - 1) - i;
const GekkoOPInfo* opinfo = op.opinfo;
@ -1118,6 +1119,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
CompileInstruction(op);
js.fpr_is_store_safe = op.fprIsStoreSafeAfterInst;
if (jo.memcheck && (opinfo->flags & FL_LOADSTORE))
{
// If we have a fastmem loadstore, we can omit the exception check and let fastmem handle

View file

@ -105,7 +105,7 @@ void Jit64::stfXXX(UGeckoInstruction inst)
if (single)
{
if (js.op->fprIsStoreSafe[s])
if (js.fpr_is_store_safe[s])
{
RCOpArg Rs = fpr.Use(s, RCMode::Read);
RegCache::Realize(Rs);

View file

@ -695,6 +695,7 @@ void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
js.compilerPC = op.address;
js.op = &op;
js.fpr_is_store_safe = op.fprIsStoreSafeBeforeInst;
js.instructionNumber = i;
js.instructionsLeft = (code_block.m_num_instructions - 1) - i;
const GekkoOPInfo* opinfo = op.opinfo;
@ -830,6 +831,9 @@ void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
}
CompileInstruction(op);
js.fpr_is_store_safe = op.fprIsStoreSafeAfterInst;
if (!CanMergeNextInstructions(1) || js.op[1].opinfo->type != ::OpType::Integer)
FlushCarry();

View file

@ -152,11 +152,15 @@ public:
void psq_l(UGeckoInstruction inst);
void psq_st(UGeckoInstruction inst);
void ConvertDoubleToSingleLower(Arm64Gen::ARM64Reg dest_reg, Arm64Gen::ARM64Reg src_reg);
void ConvertDoubleToSinglePair(Arm64Gen::ARM64Reg dest_reg, Arm64Gen::ARM64Reg src_reg);
void ConvertSingleToDoubleLower(Arm64Gen::ARM64Reg dest_reg, Arm64Gen::ARM64Reg src_reg,
void ConvertDoubleToSingleLower(size_t guest_reg, Arm64Gen::ARM64Reg dest_reg,
Arm64Gen::ARM64Reg src_reg);
void ConvertDoubleToSinglePair(size_t guest_reg, Arm64Gen::ARM64Reg dest_reg,
Arm64Gen::ARM64Reg src_reg);
void ConvertSingleToDoubleLower(size_t guest_reg, Arm64Gen::ARM64Reg dest_reg,
Arm64Gen::ARM64Reg src_reg,
Arm64Gen::ARM64Reg scratch_reg = Arm64Gen::ARM64Reg::INVALID_REG);
void ConvertSingleToDoublePair(Arm64Gen::ARM64Reg dest_reg, Arm64Gen::ARM64Reg src_reg,
void ConvertSingleToDoublePair(size_t guest_reg, Arm64Gen::ARM64Reg dest_reg,
Arm64Gen::ARM64Reg src_reg,
Arm64Gen::ARM64Reg scratch_reg = Arm64Gen::ARM64Reg::INVALID_REG);
private:

View file

@ -389,8 +389,14 @@ void JitArm64::fctiwzx(UGeckoInstruction inst)
// instructions, they must convert floats bitexact and never flush denormals to zero or turn SNaNs
// into QNaNs. This means we can't just use FCVT/FCVTL/FCVTN.
void JitArm64::ConvertDoubleToSingleLower(ARM64Reg dest_reg, ARM64Reg src_reg)
void JitArm64::ConvertDoubleToSingleLower(size_t guest_reg, ARM64Reg dest_reg, ARM64Reg src_reg)
{
if (js.fpr_is_store_safe[guest_reg])
{
m_float_emit.FCVT(32, 64, EncodeRegToDouble(dest_reg), EncodeRegToDouble(src_reg));
return;
}
FlushCarry();
const BitSet32 gpr_saved = gpr.GetCallerSavedUsed() & BitSet32{0, 1, 2, 3, 30};
@ -403,8 +409,14 @@ void JitArm64::ConvertDoubleToSingleLower(ARM64Reg dest_reg, ARM64Reg src_reg)
ABI_PopRegisters(gpr_saved);
}
void JitArm64::ConvertDoubleToSinglePair(ARM64Reg dest_reg, ARM64Reg src_reg)
void JitArm64::ConvertDoubleToSinglePair(size_t guest_reg, ARM64Reg dest_reg, ARM64Reg src_reg)
{
if (js.fpr_is_store_safe[guest_reg])
{
m_float_emit.FCVTN(32, EncodeRegToDouble(dest_reg), EncodeRegToDouble(src_reg));
return;
}
FlushCarry();
const BitSet32 gpr_saved = gpr.GetCallerSavedUsed() & BitSet32{0, 1, 2, 3, 30};
@ -421,10 +433,17 @@ void JitArm64::ConvertDoubleToSinglePair(ARM64Reg dest_reg, ARM64Reg src_reg)
ABI_PopRegisters(gpr_saved);
}
void JitArm64::ConvertSingleToDoubleLower(ARM64Reg dest_reg, ARM64Reg src_reg, ARM64Reg scratch_reg)
void JitArm64::ConvertSingleToDoubleLower(size_t guest_reg, ARM64Reg dest_reg, ARM64Reg src_reg,
ARM64Reg scratch_reg)
{
ASSERT(scratch_reg != src_reg);
if (js.fpr_is_store_safe[guest_reg])
{
m_float_emit.FCVT(64, 32, EncodeRegToDouble(dest_reg), EncodeRegToDouble(src_reg));
return;
}
const bool switch_to_farcode = !IsInFarCode();
FlushCarry();
@ -476,10 +495,17 @@ void JitArm64::ConvertSingleToDoubleLower(ARM64Reg dest_reg, ARM64Reg src_reg, A
}
}
void JitArm64::ConvertSingleToDoublePair(ARM64Reg dest_reg, ARM64Reg src_reg, ARM64Reg scratch_reg)
void JitArm64::ConvertSingleToDoublePair(size_t guest_reg, ARM64Reg dest_reg, ARM64Reg src_reg,
ARM64Reg scratch_reg)
{
ASSERT(scratch_reg != src_reg);
if (js.fpr_is_store_safe[guest_reg])
{
m_float_emit.FCVTL(64, EncodeRegToDouble(dest_reg), EncodeRegToDouble(src_reg));
return;
}
const bool switch_to_farcode = !IsInFarCode();
FlushCarry();

View file

@ -258,7 +258,7 @@ void JitArm64::stfXX(UGeckoInstruction inst)
if (want_single && !have_single)
{
const ARM64Reg single_reg = fpr.GetReg();
ConvertDoubleToSingleLower(single_reg, V0);
ConvertDoubleToSingleLower(inst.FS, single_reg, V0);
V0 = single_reg;
}

View file

@ -469,7 +469,7 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
// Else convert this register back to doubles.
const ARM64Reg tmp_reg = GetReg();
m_jit->ConvertSingleToDoublePair(host_reg, host_reg, tmp_reg);
m_jit->ConvertSingleToDoublePair(preg, host_reg, host_reg, tmp_reg);
UnlockRegister(tmp_reg);
reg.Load(host_reg, RegType::Register);
@ -487,7 +487,7 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
// Else convert this register back to a double.
const ARM64Reg tmp_reg = GetReg();
m_jit->ConvertSingleToDoubleLower(host_reg, host_reg, tmp_reg);
m_jit->ConvertSingleToDoubleLower(preg, host_reg, host_reg, tmp_reg);
UnlockRegister(tmp_reg);
reg.Load(host_reg, RegType::LowerPair);
@ -524,7 +524,7 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
}
const ARM64Reg tmp_reg = GetReg();
m_jit->ConvertSingleToDoubleLower(host_reg, host_reg, tmp_reg);
m_jit->ConvertSingleToDoubleLower(preg, host_reg, host_reg, tmp_reg);
UnlockRegister(tmp_reg);
reg.Load(host_reg, RegType::Duplicated);
@ -594,7 +594,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type)
if ((type == RegType::LowerPair || type == RegType::LowerPairSingle) && was_dirty)
{
// We must *not* change host_reg as this register might still be in use. So it's fine to
// store this register, but it's *not* fine to convert it to double. So for double convertion,
// store this register, but it's *not* fine to convert it to double. So for double conversion,
// a temporary register needs to be used.
ARM64Reg host_reg = reg.GetReg();
ARM64Reg flush_reg = host_reg;
@ -603,7 +603,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type)
{
case RegType::Single:
flush_reg = GetReg();
m_jit->ConvertSingleToDoublePair(flush_reg, host_reg, flush_reg);
m_jit->ConvertSingleToDoublePair(preg, flush_reg, host_reg, flush_reg);
[[fallthrough]];
case RegType::Register:
// We are doing a full 128bit store because it takes 2 cycles on a Cortex-A57 to do a 128bit
@ -614,7 +614,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type)
break;
case RegType::DuplicatedSingle:
flush_reg = GetReg();
m_jit->ConvertSingleToDoubleLower(flush_reg, host_reg, flush_reg);
m_jit->ConvertSingleToDoubleLower(preg, flush_reg, host_reg, flush_reg);
[[fallthrough]];
case RegType::Duplicated:
// Store PSR1 (which is equal to PSR0) in memory.
@ -725,13 +725,13 @@ void Arm64FPRCache::FlushRegister(size_t preg, bool maintain_state)
if (type == RegType::Single)
{
if (dirty)
m_jit->ConvertSingleToDoublePair(host_reg, host_reg, tmp_reg);
m_jit->ConvertSingleToDoublePair(preg, host_reg, host_reg, tmp_reg);
type = RegType::Register;
}
if (type == RegType::DuplicatedSingle || type == RegType::LowerPairSingle)
{
if (dirty)
m_jit->ConvertSingleToDoubleLower(host_reg, host_reg, tmp_reg);
m_jit->ConvertSingleToDoubleLower(preg, host_reg, host_reg, tmp_reg);
if (type == RegType::DuplicatedSingle)
type = RegType::Duplicated;
@ -822,7 +822,7 @@ void Arm64FPRCache::FixSinglePrecision(size_t preg)
m_float_emit->FCVT(32, 64, EncodeRegToDouble(host_reg), EncodeRegToDouble(host_reg));
reg.Load(host_reg, RegType::DuplicatedSingle);
break;
case RegType::Register: // PS0 and PS1 needs to be converted
case RegType::Register: // PS0 and PS1 need to be converted
m_float_emit->FCVTN(32, EncodeRegToDouble(host_reg), EncodeRegToDouble(host_reg));
reg.Load(host_reg, RegType::Single);
break;

View file

@ -8,6 +8,7 @@
#include <map>
#include <unordered_set>
#include "Common/BitSet.h"
#include "Common/CommonTypes.h"
#include "Common/x64Emitter.h"
#include "Core/ConfigManager.h"
@ -98,6 +99,7 @@ protected:
PPCAnalyst::BlockRegStats gpa;
PPCAnalyst::BlockRegStats fpa;
PPCAnalyst::CodeOp* op;
BitSet32 fpr_is_store_safe;
JitBlock* curBlock;

View file

@ -976,7 +976,7 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
op.fprIsSingle = fprIsSingle;
op.fprIsDuplicated = fprIsDuplicated;
op.fprIsStoreSafe = fprIsStoreSafe;
op.fprIsStoreSafeBeforeInst = fprIsStoreSafe;
if (op.fregOut >= 0)
{
if (op.opinfo->type == OpType::SingleFP)
@ -1036,6 +1036,7 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock* block, CodeBuffer* buffer, std:
(op.opinfo->type == OpType::SingleFP || op.opinfo->type == OpType::PS);
}
}
op.fprIsStoreSafeAfterInst = fprIsStoreSafe;
if (op.opinfo->type == OpType::StorePS || op.opinfo->type == OpType::LoadPS)
{

View file

@ -66,7 +66,8 @@ struct CodeOp // 16B
// convert between single and double formats by just using the host machine's instruction for it.
// (The reason why we can't always do this is because some games rely on the exact bits of
// denormals and SNaNs being preserved as long as no arithmetic operation is performed on them.)
BitSet32 fprIsStoreSafe;
BitSet32 fprIsStoreSafeBeforeInst;
BitSet32 fprIsStoreSafeAfterInst;
BitSet32 GetFregsOut() const
{