JitArm64: FIFO optimization improvements

JitArm64 port of 789975e.
This commit is contained in:
JosJuice 2021-08-20 14:56:24 +02:00
parent 3af74d82a0
commit dbf5dca11c
4 changed files with 87 additions and 28 deletions

View File

@ -320,6 +320,50 @@ void JitArm64::FreeStack()
#endif #endif
} }
void JitArm64::IntializeSpeculativeConstants()
{
// If the block depends on an input register which looks like a gather pipe or MMIO related
// constant, guess that it is actually a constant input, and specialize the block based on this
// assumption. This happens when there are branches in code writing to the gather pipe, but only
// the first block loads the constant.
// Insert a check at the start of the block to verify that the value is actually constant.
// This can save a lot of backpatching and optimize gather pipe writes in more places.
const u8* fail = nullptr;
for (auto i : code_block.m_gpr_inputs)
{
u32 compile_time_value = PowerPC::ppcState.gpr[i];
if (PowerPC::IsOptimizableGatherPipeWrite(compile_time_value) ||
PowerPC::IsOptimizableGatherPipeWrite(compile_time_value - 0x8000) ||
compile_time_value == 0xCC000000)
{
if (!fail)
{
SwitchToFarCode();
fail = GetCodePtr();
MOVI2R(DISPATCHER_PC, js.blockStart);
STR(IndexType::Unsigned, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(pc));
MOVP2R(ARM64Reg::X8, &JitInterface::CompileExceptionCheck);
MOVI2R(ARM64Reg::W0, static_cast<u32>(JitInterface::ExceptionType::SpeculativeConstants));
BLR(ARM64Reg::X8);
B(dispatcher_no_check);
SwitchToNearCode();
}
ARM64Reg tmp = gpr.GetReg();
ARM64Reg value = gpr.R(i);
MOVI2R(tmp, compile_time_value);
CMP(value, tmp);
gpr.Unlock(tmp);
FixupBranch no_fail = B(CCFlags::CC_EQ);
B(fail);
SetJumpTarget(no_fail);
gpr.SetImmediate(i, compile_time_value, true);
}
}
}
void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return) void JitArm64::WriteExit(u32 destination, bool LK, u32 exit_address_after_return)
{ {
Cleanup(); Cleanup();
@ -806,6 +850,12 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
gpr.Start(js.gpa); gpr.Start(js.gpa);
fpr.Start(js.fpa); fpr.Start(js.fpa);
if (js.noSpeculativeConstantsAddresses.find(js.blockStart) ==
js.noSpeculativeConstantsAddresses.end())
{
IntializeSpeculativeConstants();
}
// Translate instructions // Translate instructions
for (u32 i = 0; i < code_block.m_num_instructions; i++) for (u32 i = 0; i < code_block.m_num_instructions; i++)
{ {

View File

@ -285,6 +285,8 @@ protected:
void ResetFreeMemoryRanges(); void ResetFreeMemoryRanges();
void IntializeSpeculativeConstants();
// AsmRoutines // AsmRoutines
void GenerateAsm(); void GenerateAsm();
void GenerateCommonAsm(); void GenerateCommonAsm();

View File

@ -202,37 +202,40 @@ void Arm64GPRCache::FlushRegister(size_t index, bool maintain_state, ARM64Reg tm
} }
else if (reg.GetType() == RegType::Immediate) else if (reg.GetType() == RegType::Immediate)
{ {
if (!reg.GetImm()) if (reg.IsDirty())
{ {
m_emit->STR(IndexType::Unsigned, bitsize == 64 ? ARM64Reg::ZR : ARM64Reg::WZR, PPC_REG, if (!reg.GetImm())
u32(guest_reg.ppc_offset));
}
else
{
bool allocated_tmp_reg = false;
if (tmp_reg != ARM64Reg::INVALID_REG)
{ {
ASSERT(IsGPR(tmp_reg)); m_emit->STR(IndexType::Unsigned, bitsize == 64 ? ARM64Reg::ZR : ARM64Reg::WZR, PPC_REG,
u32(guest_reg.ppc_offset));
} }
else else
{ {
ASSERT_MSG(DYNA_REC, !maintain_state, bool allocated_tmp_reg = false;
"Flushing immediate while maintaining state requires temporary register"); if (tmp_reg != ARM64Reg::INVALID_REG)
tmp_reg = GetReg(); {
allocated_tmp_reg = true; ASSERT(IsGPR(tmp_reg));
}
else
{
ASSERT_MSG(DYNA_REC, !maintain_state,
"Flushing immediate while maintaining state requires temporary register");
tmp_reg = GetReg();
allocated_tmp_reg = true;
}
const ARM64Reg encoded_tmp_reg = bitsize != 64 ? tmp_reg : EncodeRegTo64(tmp_reg);
m_emit->MOVI2R(encoded_tmp_reg, reg.GetImm());
m_emit->STR(IndexType::Unsigned, encoded_tmp_reg, PPC_REG, u32(guest_reg.ppc_offset));
if (allocated_tmp_reg)
UnlockRegister(tmp_reg);
} }
const ARM64Reg encoded_tmp_reg = bitsize != 64 ? tmp_reg : EncodeRegTo64(tmp_reg); if (!maintain_state)
reg.Flush();
m_emit->MOVI2R(encoded_tmp_reg, reg.GetImm());
m_emit->STR(IndexType::Unsigned, encoded_tmp_reg, PPC_REG, u32(guest_reg.ppc_offset));
if (allocated_tmp_reg)
UnlockRegister(tmp_reg);
} }
if (!maintain_state)
reg.Flush();
} }
} }
@ -335,12 +338,13 @@ ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
return ARM64Reg::INVALID_REG; return ARM64Reg::INVALID_REG;
} }
void Arm64GPRCache::SetImmediate(const GuestRegInfo& guest_reg, u32 imm) void Arm64GPRCache::SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty)
{ {
OpArg& reg = guest_reg.reg; OpArg& reg = guest_reg.reg;
if (reg.GetType() == RegType::Register) if (reg.GetType() == RegType::Register)
UnlockRegister(EncodeRegTo32(reg.GetReg())); UnlockRegister(EncodeRegTo32(reg.GetReg()));
reg.LoadToImm(imm); reg.LoadToImm(imm);
reg.SetDirty(dirty);
} }
void Arm64GPRCache::BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write) void Arm64GPRCache::BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write)
@ -373,8 +377,8 @@ void Arm64GPRCache::BindToRegister(const GuestRegInfo& guest_reg, bool will_read
m_emit->MOVI2R(host_reg, reg.GetImm()); m_emit->MOVI2R(host_reg, reg.GetImm());
} }
reg.Load(host_reg); reg.Load(host_reg);
// If the register had an immediate value, the register was effectively already dirty if (will_write)
reg.SetDirty(true); reg.SetDirty(true);
} }
else if (will_write) else if (will_write)
{ {

View File

@ -262,7 +262,10 @@ public:
Arm64Gen::ARM64Reg CR(size_t preg) { return R(GetGuestCR(preg)); } Arm64Gen::ARM64Reg CR(size_t preg) { return R(GetGuestCR(preg)); }
// Set a register to an immediate. Only valid for guest GPRs. // Set a register to an immediate. Only valid for guest GPRs.
void SetImmediate(size_t preg, u32 imm) { SetImmediate(GetGuestGPR(preg), imm); } void SetImmediate(size_t preg, u32 imm, bool dirty = true)
{
SetImmediate(GetGuestGPR(preg), imm, dirty);
}
// Returns if a register is set as an immediate. Only valid for guest GPRs. // Returns if a register is set as an immediate. Only valid for guest GPRs.
bool IsImm(size_t preg) const { return GetGuestGPROpArg(preg).GetType() == RegType::Immediate; } bool IsImm(size_t preg) const { return GetGuestGPROpArg(preg).GetType() == RegType::Immediate; }
@ -345,7 +348,7 @@ private:
GuestRegInfo GetGuestByIndex(size_t index); GuestRegInfo GetGuestByIndex(size_t index);
Arm64Gen::ARM64Reg R(const GuestRegInfo& guest_reg); Arm64Gen::ARM64Reg R(const GuestRegInfo& guest_reg);
void SetImmediate(const GuestRegInfo& guest_reg, u32 imm); void SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty);
void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true); void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true);
void FlushRegisters(BitSet32 regs, bool maintain_state, Arm64Gen::ARM64Reg tmp_reg); void FlushRegisters(BitSet32 regs, bool maintain_state, Arm64Gen::ARM64Reg tmp_reg);