mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-09 15:49:25 +01:00
JitArm64: Replace dirty flag and partially replace RegType enum
Like Jit64, JitArm64 now keeps track of the location of a guest register using three booleans: Whether it is in ppcState, whether it is in a host register, and whether it is a known immediate. The RegType enum remains only for the purpose of keeping track of what format FPRs are stored in in host registers.
This commit is contained in:
parent
6929dff016
commit
da0c9ec95e
@ -114,8 +114,7 @@ void Arm64RegCache::FlushMostStaleRegister()
|
|||||||
const auto& reg = m_guest_registers[i];
|
const auto& reg = m_guest_registers[i];
|
||||||
const u32 last_used = reg.GetLastUsed();
|
const u32 last_used = reg.GetLastUsed();
|
||||||
|
|
||||||
if (last_used > most_stale_amount && reg.GetType() != RegType::NotLoaded &&
|
if (last_used > most_stale_amount && reg.IsInHostRegister())
|
||||||
reg.GetType() != RegType::Discarded)
|
|
||||||
{
|
{
|
||||||
most_stale_preg = i;
|
most_stale_preg = i;
|
||||||
most_stale_amount = last_used;
|
most_stale_amount = last_used;
|
||||||
@ -197,10 +196,10 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg
|
|||||||
size_t bitsize = guest_reg.bitsize;
|
size_t bitsize = guest_reg.bitsize;
|
||||||
const bool is_gpr = index >= GUEST_GPR_OFFSET && index < GUEST_GPR_OFFSET + GUEST_GPR_COUNT;
|
const bool is_gpr = index >= GUEST_GPR_OFFSET && index < GUEST_GPR_OFFSET + GUEST_GPR_COUNT;
|
||||||
|
|
||||||
if (reg.GetType() == RegType::Register)
|
if (reg.IsInHostRegister())
|
||||||
{
|
{
|
||||||
ARM64Reg host_reg = reg.GetReg();
|
ARM64Reg host_reg = reg.GetReg();
|
||||||
if (reg.IsDirty())
|
if (!reg.IsInPPCState())
|
||||||
m_emit->STR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
m_emit->STR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
||||||
|
|
||||||
if (mode == FlushMode::All)
|
if (mode == FlushMode::All)
|
||||||
@ -211,7 +210,7 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg
|
|||||||
}
|
}
|
||||||
else if (is_gpr && IsImm(index - GUEST_GPR_OFFSET))
|
else if (is_gpr && IsImm(index - GUEST_GPR_OFFSET))
|
||||||
{
|
{
|
||||||
if (reg.IsDirty())
|
if (!reg.IsInPPCState())
|
||||||
{
|
{
|
||||||
const u32 imm = GetImm(index - GUEST_GPR_OFFSET);
|
const u32 imm = GetImm(index - GUEST_GPR_OFFSET);
|
||||||
if (imm == 0)
|
if (imm == 0)
|
||||||
@ -255,10 +254,10 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
|
|||||||
for (auto iter = regs.begin(); iter != regs.end(); ++iter)
|
for (auto iter = regs.begin(); iter != regs.end(); ++iter)
|
||||||
{
|
{
|
||||||
const int i = *iter;
|
const int i = *iter;
|
||||||
|
OpArg& reg = m_guest_registers[GUEST_GPR_OFFSET + i];
|
||||||
ASSERT_MSG(DYNA_REC,
|
ASSERT_MSG(DYNA_REC,
|
||||||
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
|
ignore_discarded_registers != IgnoreDiscardedRegisters::No || reg.IsInPPCState() ||
|
||||||
m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded ||
|
reg.IsInHostRegister() || IsImm(i),
|
||||||
IsImm(i),
|
|
||||||
"Attempted to flush discarded register");
|
"Attempted to flush discarded register");
|
||||||
|
|
||||||
if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1])
|
if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1])
|
||||||
@ -271,9 +270,9 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
|
|||||||
const bool reg1_zero = reg1_imm && GetImm(i) == 0;
|
const bool reg1_zero = reg1_imm && GetImm(i) == 0;
|
||||||
const bool reg2_zero = reg2_imm && GetImm(i + 1) == 0;
|
const bool reg2_zero = reg2_imm && GetImm(i + 1) == 0;
|
||||||
const bool flush_all = mode == FlushMode::All;
|
const bool flush_all = mode == FlushMode::All;
|
||||||
if (reg1.IsDirty() && reg2.IsDirty() &&
|
if (!reg1.IsInPPCState() && !reg2.IsInPPCState() &&
|
||||||
(reg1.GetType() == RegType::Register || (reg1_imm && (reg1_zero || flush_all))) &&
|
(reg1.IsInHostRegister() || (reg1_imm && (reg1_zero || flush_all))) &&
|
||||||
(reg2.GetType() == RegType::Register || (reg2_imm && (reg2_zero || flush_all))))
|
(reg2.IsInHostRegister() || (reg2_imm && (reg2_zero || flush_all))))
|
||||||
{
|
{
|
||||||
const size_t ppc_offset = GetGuestByIndex(i).ppc_offset;
|
const size_t ppc_offset = GetGuestByIndex(i).ppc_offset;
|
||||||
if (ppc_offset <= 252)
|
if (ppc_offset <= 252)
|
||||||
@ -283,9 +282,9 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
|
|||||||
m_emit->STP(IndexType::Signed, RX1, RX2, PPC_REG, u32(ppc_offset));
|
m_emit->STP(IndexType::Signed, RX1, RX2, PPC_REG, u32(ppc_offset));
|
||||||
if (flush_all)
|
if (flush_all)
|
||||||
{
|
{
|
||||||
if (reg1.GetType() == RegType::Register)
|
if (reg1.IsInHostRegister())
|
||||||
UnlockRegister(reg1.GetReg());
|
UnlockRegister(reg1.GetReg());
|
||||||
if (reg2.GetType() == RegType::Register)
|
if (reg2.IsInHostRegister())
|
||||||
UnlockRegister(reg2.GetReg());
|
UnlockRegister(reg2.GetReg());
|
||||||
reg1.Flush();
|
reg1.Flush();
|
||||||
reg2.Flush();
|
reg2.Flush();
|
||||||
@ -305,9 +304,10 @@ void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_
|
|||||||
{
|
{
|
||||||
for (int i : regs)
|
for (int i : regs)
|
||||||
{
|
{
|
||||||
|
OpArg& reg = m_guest_registers[GUEST_CR_OFFSET + i];
|
||||||
ASSERT_MSG(DYNA_REC,
|
ASSERT_MSG(DYNA_REC,
|
||||||
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
|
ignore_discarded_registers != IgnoreDiscardedRegisters::No || reg.IsInPPCState() ||
|
||||||
m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
|
reg.IsInHostRegister(),
|
||||||
"Attempted to flush discarded register");
|
"Attempted to flush discarded register");
|
||||||
|
|
||||||
FlushRegister(GUEST_CR_OFFSET + i, mode, tmp_reg);
|
FlushRegister(GUEST_CR_OFFSET + i, mode, tmp_reg);
|
||||||
@ -350,44 +350,33 @@ ARM64Reg Arm64GPRCache::BindForRead(size_t index)
|
|||||||
IncrementAllUsed();
|
IncrementAllUsed();
|
||||||
reg.ResetLastUsed();
|
reg.ResetLastUsed();
|
||||||
|
|
||||||
switch (reg.GetType())
|
if (reg.IsInHostRegister())
|
||||||
{
|
{
|
||||||
case RegType::Register: // already in a reg
|
|
||||||
return reg.GetReg();
|
return reg.GetReg();
|
||||||
case RegType::Discarded: // Is an immediate or discarded
|
}
|
||||||
|
else if (is_gpr && IsImm(index - GUEST_GPR_OFFSET))
|
||||||
{
|
{
|
||||||
ASSERT_MSG(DYNA_REC, is_gpr && IsImm(index - GUEST_GPR_OFFSET),
|
|
||||||
"Attempted to read discarded register");
|
|
||||||
ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
||||||
m_emit->MOVI2R(host_reg, GetImm(index - GUEST_GPR_OFFSET));
|
m_emit->MOVI2R(host_reg, GetImm(index - GUEST_GPR_OFFSET));
|
||||||
reg.Load(host_reg);
|
reg.Load(host_reg);
|
||||||
return host_reg;
|
return host_reg;
|
||||||
}
|
}
|
||||||
case RegType::NotLoaded: // Register isn't loaded at /all/
|
else // Register isn't loaded at /all/
|
||||||
{
|
{
|
||||||
// This is a bit annoying. We try to keep these preloaded as much as possible
|
ASSERT_MSG(DYNA_REC, reg.IsInPPCState(), "Attempted to read discarded register");
|
||||||
// This can also happen on cases where PPCAnalyst isn't feeing us proper register usage
|
|
||||||
// statistics
|
|
||||||
ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
||||||
reg.Load(host_reg);
|
reg.Load(host_reg);
|
||||||
reg.SetDirty(false);
|
reg.SetDirty(false);
|
||||||
m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
||||||
return host_reg;
|
return host_reg;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR_LOG_FMT(DYNA_REC, "Invalid OpArg Type!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// We've got an issue if we end up here
|
|
||||||
return ARM64Reg::INVALID_REG;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arm64GPRCache::SetImmediateInternal(size_t index, u32 imm, bool dirty)
|
void Arm64GPRCache::SetImmediateInternal(size_t index, u32 imm, bool dirty)
|
||||||
{
|
{
|
||||||
GuestRegInfo guest_reg = GetGuestByIndex(index);
|
GuestRegInfo guest_reg = GetGuestByIndex(index);
|
||||||
OpArg& reg = guest_reg.reg;
|
OpArg& reg = guest_reg.reg;
|
||||||
if (reg.GetType() == RegType::Register)
|
if (reg.IsInHostRegister())
|
||||||
UnlockRegister(EncodeRegTo32(reg.GetReg()));
|
UnlockRegister(EncodeRegTo32(reg.GetReg()));
|
||||||
reg.Discard();
|
reg.Discard();
|
||||||
reg.SetDirty(dirty);
|
reg.SetDirty(dirty);
|
||||||
@ -403,8 +392,7 @@ void Arm64GPRCache::BindForWrite(size_t index, bool will_read, bool will_write)
|
|||||||
|
|
||||||
reg.ResetLastUsed();
|
reg.ResetLastUsed();
|
||||||
|
|
||||||
const RegType reg_type = reg.GetType();
|
if (!reg.IsInHostRegister())
|
||||||
if (reg_type != RegType::Register)
|
|
||||||
{
|
{
|
||||||
if (is_gpr && IsImm(index - GUEST_GPR_OFFSET))
|
if (is_gpr && IsImm(index - GUEST_GPR_OFFSET))
|
||||||
{
|
{
|
||||||
@ -420,14 +408,12 @@ void Arm64GPRCache::BindForWrite(size_t index, bool will_read, bool will_write)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, !will_read || reg.IsInPPCState(), "Attempted to load a discarded value");
|
||||||
const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
const ARM64Reg host_reg = bitsize != 64 ? GetReg() : EncodeRegTo64(GetReg());
|
||||||
reg.Load(host_reg);
|
reg.Load(host_reg);
|
||||||
reg.SetDirty(will_write);
|
reg.SetDirty(will_write);
|
||||||
if (will_read)
|
if (will_read)
|
||||||
{
|
|
||||||
ASSERT_MSG(DYNA_REC, reg_type != RegType::Discarded, "Attempted to load a discarded value");
|
|
||||||
m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
m_emit->LDR(IndexType::Unsigned, host_reg, PPC_REG, u32(guest_reg.ppc_offset));
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,7 +484,7 @@ BitSet32 Arm64GPRCache::GetDirtyGPRs() const
|
|||||||
for (size_t i = 0; i < GUEST_GPR_COUNT; ++i)
|
for (size_t i = 0; i < GUEST_GPR_COUNT; ++i)
|
||||||
{
|
{
|
||||||
const OpArg& arg = m_guest_registers[GUEST_GPR_OFFSET + i];
|
const OpArg& arg = m_guest_registers[GUEST_GPR_OFFSET + i];
|
||||||
registers[i] = arg.GetType() != RegType::NotLoaded && arg.IsDirty();
|
registers[i] = !arg.IsInPPCState();
|
||||||
}
|
}
|
||||||
return registers;
|
return registers;
|
||||||
}
|
}
|
||||||
@ -508,7 +494,7 @@ void Arm64GPRCache::FlushByHost(ARM64Reg host_reg, ARM64Reg tmp_reg)
|
|||||||
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
||||||
{
|
{
|
||||||
const OpArg& reg = m_guest_registers[i];
|
const OpArg& reg = m_guest_registers[i];
|
||||||
if (reg.GetType() == RegType::Register && DecodeReg(reg.GetReg()) == DecodeReg(host_reg))
|
if (reg.IsInHostRegister() && DecodeReg(reg.GetReg()) == DecodeReg(host_reg))
|
||||||
{
|
{
|
||||||
FlushRegister(i, FlushMode::All, tmp_reg);
|
FlushRegister(i, FlushMode::All, tmp_reg);
|
||||||
return;
|
return;
|
||||||
@ -528,17 +514,17 @@ void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
|
|||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
||||||
{
|
{
|
||||||
const RegType reg_type = m_guest_registers[i].GetType();
|
if (m_guest_registers[i].IsInHostRegister())
|
||||||
|
|
||||||
if (reg_type == RegType::Discarded)
|
|
||||||
{
|
|
||||||
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
|
|
||||||
"Attempted to flush discarded register");
|
|
||||||
}
|
|
||||||
else if (reg_type != RegType::NotLoaded)
|
|
||||||
{
|
{
|
||||||
FlushRegister(i, mode, tmp_reg);
|
FlushRegister(i, mode, tmp_reg);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC,
|
||||||
|
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
|
||||||
|
m_guest_registers[i].IsInPPCState(),
|
||||||
|
"Attempted to flush discarded register");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,9 +533,32 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
|
|||||||
OpArg& reg = m_guest_registers[preg];
|
OpArg& reg = m_guest_registers[preg];
|
||||||
IncrementAllUsed();
|
IncrementAllUsed();
|
||||||
reg.ResetLastUsed();
|
reg.ResetLastUsed();
|
||||||
|
|
||||||
|
if (!reg.IsInHostRegister())
|
||||||
|
{
|
||||||
|
ASSERT_MSG(DYNA_REC, reg.IsInPPCState(), "Attempted to read discarded register");
|
||||||
|
|
||||||
|
ARM64Reg host_reg = GetReg();
|
||||||
|
u32 load_size;
|
||||||
|
if (type == RegType::Register)
|
||||||
|
{
|
||||||
|
load_size = 128;
|
||||||
|
reg.Load(host_reg, RegType::Register);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
load_size = 64;
|
||||||
|
reg.Load(host_reg, RegType::LowerPair);
|
||||||
|
}
|
||||||
|
reg.SetDirty(false);
|
||||||
|
m_float_emit->LDR(load_size, IndexType::Unsigned, host_reg, PPC_REG,
|
||||||
|
static_cast<s32>(PPCSTATE_OFF_PS0(preg)));
|
||||||
|
return host_reg;
|
||||||
|
}
|
||||||
|
|
||||||
ARM64Reg host_reg = reg.GetReg();
|
ARM64Reg host_reg = reg.GetReg();
|
||||||
|
|
||||||
switch (reg.GetType())
|
switch (reg.GetFPRType())
|
||||||
{
|
{
|
||||||
case RegType::Single:
|
case RegType::Single:
|
||||||
{
|
{
|
||||||
@ -632,28 +641,6 @@ ARM64Reg Arm64FPRCache::R(size_t preg, RegType type)
|
|||||||
}
|
}
|
||||||
return host_reg;
|
return host_reg;
|
||||||
}
|
}
|
||||||
case RegType::Discarded:
|
|
||||||
ASSERT_MSG(DYNA_REC, false, "Attempted to read discarded register");
|
|
||||||
break;
|
|
||||||
case RegType::NotLoaded: // Register isn't loaded at /all/
|
|
||||||
{
|
|
||||||
host_reg = GetReg();
|
|
||||||
u32 load_size;
|
|
||||||
if (type == RegType::Register)
|
|
||||||
{
|
|
||||||
load_size = 128;
|
|
||||||
reg.Load(host_reg, RegType::Register);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
load_size = 64;
|
|
||||||
reg.Load(host_reg, RegType::LowerPair);
|
|
||||||
}
|
|
||||||
reg.SetDirty(false);
|
|
||||||
m_float_emit->LDR(load_size, IndexType::Unsigned, host_reg, PPC_REG,
|
|
||||||
static_cast<s32>(PPCSTATE_OFF_PS0(preg)));
|
|
||||||
return host_reg;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
DEBUG_ASSERT_MSG(DYNA_REC, false, "Invalid OpArg Type!");
|
DEBUG_ASSERT_MSG(DYNA_REC, false, "Invalid OpArg Type!");
|
||||||
break;
|
break;
|
||||||
@ -669,16 +656,17 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type, bool set_dirty)
|
|||||||
IncrementAllUsed();
|
IncrementAllUsed();
|
||||||
reg.ResetLastUsed();
|
reg.ResetLastUsed();
|
||||||
|
|
||||||
// Only the lower value will be overwritten, so we must be extra careful to store PSR1 if dirty.
|
// If PS1 is dirty, but the caller wants a RegType with only PS0, we must write PS1 to m_ppc_state
|
||||||
if (reg.IsDirty() && (type == RegType::LowerPair || type == RegType::LowerPairSingle))
|
// now so the contents of PS1 aren't lost.
|
||||||
|
if (!reg.IsInPPCState() && (type == RegType::LowerPair || type == RegType::LowerPairSingle))
|
||||||
{
|
{
|
||||||
// We must *not* change host_reg as this register might still be in use. So it's fine to
|
// We must *not* modify host_reg, as the current guest instruction might want to read its old
|
||||||
// store this register, but it's *not* fine to convert it to double. So for double conversion,
|
// value before overwriting it. So it's fine to store this register, but it's *not* fine to
|
||||||
// a temporary register needs to be used.
|
// convert it to double in place. For double conversion, a temporary register needs to be used.
|
||||||
ARM64Reg host_reg = reg.GetReg();
|
ARM64Reg host_reg = reg.GetReg();
|
||||||
ARM64Reg flush_reg = host_reg;
|
ARM64Reg flush_reg = host_reg;
|
||||||
|
|
||||||
switch (reg.GetType())
|
switch (reg.GetFPRType())
|
||||||
{
|
{
|
||||||
case RegType::Single:
|
case RegType::Single:
|
||||||
// For a store-safe register, conversion is just one instruction regardless of whether
|
// For a store-safe register, conversion is just one instruction regardless of whether
|
||||||
@ -720,7 +708,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type, bool set_dirty)
|
|||||||
// Store PSR1 (which is equal to PSR0) in memory.
|
// Store PSR1 (which is equal to PSR0) in memory.
|
||||||
m_float_emit->STR(64, IndexType::Unsigned, flush_reg, PPC_REG,
|
m_float_emit->STR(64, IndexType::Unsigned, flush_reg, PPC_REG,
|
||||||
static_cast<s32>(PPCSTATE_OFF_PS1(preg)));
|
static_cast<s32>(PPCSTATE_OFF_PS1(preg)));
|
||||||
reg.Load(host_reg, reg.GetType() == RegType::DuplicatedSingle ? RegType::LowerPairSingle :
|
reg.Load(host_reg, reg.GetFPRType() == RegType::DuplicatedSingle ? RegType::LowerPairSingle :
|
||||||
RegType::LowerPair);
|
RegType::LowerPair);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -732,7 +720,7 @@ ARM64Reg Arm64FPRCache::RW(size_t preg, RegType type, bool set_dirty)
|
|||||||
Unlock(flush_reg);
|
Unlock(flush_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg.GetType() == RegType::NotLoaded || reg.GetType() == RegType::Discarded)
|
if (!reg.IsInHostRegister())
|
||||||
{
|
{
|
||||||
// If not loaded at all, just alloc a new one.
|
// If not loaded at all, just alloc a new one.
|
||||||
reg.Load(GetReg(), type);
|
reg.Load(GetReg(), type);
|
||||||
@ -796,10 +784,8 @@ void Arm64FPRCache::FlushByHost(ARM64Reg host_reg, ARM64Reg tmp_reg)
|
|||||||
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
||||||
{
|
{
|
||||||
const OpArg& reg = m_guest_registers[i];
|
const OpArg& reg = m_guest_registers[i];
|
||||||
const RegType reg_type = reg.GetType();
|
|
||||||
|
|
||||||
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
|
if (reg.IsInHostRegister() && reg.GetReg() == host_reg)
|
||||||
reg.GetReg() == host_reg)
|
|
||||||
{
|
{
|
||||||
FlushRegister(i, FlushMode::All, tmp_reg);
|
FlushRegister(i, FlushMode::All, tmp_reg);
|
||||||
return;
|
return;
|
||||||
@ -816,8 +802,8 @@ bool Arm64FPRCache::IsTopHalfUsed(ARM64Reg reg) const
|
|||||||
{
|
{
|
||||||
for (const OpArg& r : m_guest_registers)
|
for (const OpArg& r : m_guest_registers)
|
||||||
{
|
{
|
||||||
if (r.GetReg() != ARM64Reg::INVALID_REG && DecodeReg(r.GetReg()) == DecodeReg(reg))
|
if (r.IsInHostRegister() && DecodeReg(r.GetReg()) == DecodeReg(reg))
|
||||||
return r.GetType() == RegType::Register;
|
return r.GetFPRType() == RegType::Register;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -827,8 +813,8 @@ void Arm64FPRCache::FlushRegister(size_t preg, FlushMode mode, ARM64Reg tmp_reg)
|
|||||||
{
|
{
|
||||||
OpArg& reg = m_guest_registers[preg];
|
OpArg& reg = m_guest_registers[preg];
|
||||||
const ARM64Reg host_reg = reg.GetReg();
|
const ARM64Reg host_reg = reg.GetReg();
|
||||||
const bool dirty = reg.IsDirty();
|
const bool dirty = !reg.IsInPPCState();
|
||||||
RegType type = reg.GetType();
|
RegType type = reg.GetFPRType();
|
||||||
|
|
||||||
bool allocated_tmp_reg = false;
|
bool allocated_tmp_reg = false;
|
||||||
if (tmp_reg != ARM64Reg::INVALID_REG)
|
if (tmp_reg != ARM64Reg::INVALID_REG)
|
||||||
@ -935,7 +921,7 @@ BitSet32 Arm64FPRCache::GetCallerSavedUsed() const
|
|||||||
|
|
||||||
bool Arm64FPRCache::IsSingle(size_t preg, bool lower_only) const
|
bool Arm64FPRCache::IsSingle(size_t preg, bool lower_only) const
|
||||||
{
|
{
|
||||||
const RegType type = m_guest_registers[preg].GetType();
|
const RegType type = m_guest_registers[preg].GetFPRType();
|
||||||
return type == RegType::Single || type == RegType::DuplicatedSingle ||
|
return type == RegType::Single || type == RegType::DuplicatedSingle ||
|
||||||
(lower_only && type == RegType::LowerPairSingle);
|
(lower_only && type == RegType::LowerPairSingle);
|
||||||
}
|
}
|
||||||
@ -943,18 +929,18 @@ bool Arm64FPRCache::IsSingle(size_t preg, bool lower_only) const
|
|||||||
void Arm64FPRCache::FixSinglePrecision(size_t preg)
|
void Arm64FPRCache::FixSinglePrecision(size_t preg)
|
||||||
{
|
{
|
||||||
OpArg& reg = m_guest_registers[preg];
|
OpArg& reg = m_guest_registers[preg];
|
||||||
|
if (!reg.IsInHostRegister())
|
||||||
|
return;
|
||||||
|
|
||||||
ARM64Reg host_reg = reg.GetReg();
|
ARM64Reg host_reg = reg.GetReg();
|
||||||
switch (reg.GetType())
|
if (reg.GetFPRType() == RegType::Duplicated) // only PS0 needs to be converted
|
||||||
{
|
{
|
||||||
case RegType::Duplicated: // only PS0 needs to be converted
|
|
||||||
m_float_emit->FCVT(32, 64, EncodeRegToDouble(host_reg), EncodeRegToDouble(host_reg));
|
m_float_emit->FCVT(32, 64, EncodeRegToDouble(host_reg), EncodeRegToDouble(host_reg));
|
||||||
reg.Load(host_reg, RegType::DuplicatedSingle);
|
reg.Load(host_reg, RegType::DuplicatedSingle);
|
||||||
break;
|
}
|
||||||
case RegType::Register: // PS0 and PS1 need to be converted
|
else if (reg.GetFPRType() == RegType::Register) // PS0 and PS1 need to be converted
|
||||||
|
{
|
||||||
m_float_emit->FCVTN(32, EncodeRegToDouble(host_reg), EncodeRegToDouble(host_reg));
|
m_float_emit->FCVTN(32, EncodeRegToDouble(host_reg), EncodeRegToDouble(host_reg));
|
||||||
reg.Load(host_reg, RegType::Single);
|
reg.Load(host_reg, RegType::Single);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,15 +60,12 @@ static_assert(PPCSTATE_OFF(xer_so_ov) < 4096, "STRB can't store xer_so_ov!");
|
|||||||
|
|
||||||
enum class RegType
|
enum class RegType
|
||||||
{
|
{
|
||||||
NotLoaded,
|
Register, // PS0 and PS1, each 64-bit
|
||||||
Discarded, // Reg is in ConstantPropagation, or isn't loaded at all
|
LowerPair, // PS0 only, 64-bit
|
||||||
Register, // Reg type is register
|
Duplicated, // PS0 and PS1 are identical, host register only stores one lane (64-bit)
|
||||||
LowerPair, // Only the lower pair of a paired register
|
Single, // PS0 and PS1, each 32-bit
|
||||||
Duplicated, // The lower reg is the same as the upper one (physical upper doesn't actually have
|
LowerPairSingle, // PS0 only, 32-bit
|
||||||
// the duplicated value)
|
DuplicatedSingle, // PS0 and PS1 are identical, host register only stores one lane (32-bit)
|
||||||
Single, // Both registers are loaded as single
|
|
||||||
LowerPairSingle, // Only the lower pair of a paired register, as single
|
|
||||||
DuplicatedSingle, // The lower one contains both registers, as single
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class FlushMode : bool
|
enum class FlushMode : bool
|
||||||
@ -91,19 +88,21 @@ class OpArg
|
|||||||
public:
|
public:
|
||||||
OpArg() = default;
|
OpArg() = default;
|
||||||
|
|
||||||
RegType GetType() const { return m_type; }
|
RegType GetFPRType() const { return m_fpr_type; }
|
||||||
Arm64Gen::ARM64Reg GetReg() const { return m_reg; }
|
Arm64Gen::ARM64Reg GetReg() const { return m_reg; }
|
||||||
void Load(Arm64Gen::ARM64Reg reg, RegType type = RegType::Register)
|
void Load(Arm64Gen::ARM64Reg reg, RegType format = RegType::Register)
|
||||||
{
|
{
|
||||||
m_type = type;
|
|
||||||
m_reg = reg;
|
m_reg = reg;
|
||||||
|
m_fpr_type = format;
|
||||||
|
m_in_host_register = true;
|
||||||
}
|
}
|
||||||
void Discard()
|
void Discard()
|
||||||
{
|
{
|
||||||
// Invalidate any previous information
|
// Invalidate any previous information
|
||||||
m_type = RegType::Discarded;
|
|
||||||
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
||||||
m_dirty = true;
|
m_fpr_type = RegType::Register;
|
||||||
|
m_in_ppc_state = false;
|
||||||
|
m_in_host_register = false;
|
||||||
|
|
||||||
// Arbitrarily large value that won't roll over on a lot of increments
|
// Arbitrarily large value that won't roll over on a lot of increments
|
||||||
m_last_used = 0xFFFF;
|
m_last_used = 0xFFFF;
|
||||||
@ -111,9 +110,10 @@ public:
|
|||||||
void Flush()
|
void Flush()
|
||||||
{
|
{
|
||||||
// Invalidate any previous information
|
// Invalidate any previous information
|
||||||
m_type = RegType::NotLoaded;
|
|
||||||
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
m_reg = Arm64Gen::ARM64Reg::INVALID_REG;
|
||||||
m_dirty = false;
|
m_fpr_type = RegType::Register;
|
||||||
|
m_in_ppc_state = true;
|
||||||
|
m_in_host_register = false;
|
||||||
|
|
||||||
// Arbitrarily large value that won't roll over on a lot of increments
|
// Arbitrarily large value that won't roll over on a lot of increments
|
||||||
m_last_used = 0xFFFF;
|
m_last_used = 0xFFFF;
|
||||||
@ -122,17 +122,18 @@ public:
|
|||||||
u32 GetLastUsed() const { return m_last_used; }
|
u32 GetLastUsed() const { return m_last_used; }
|
||||||
void ResetLastUsed() { m_last_used = 0; }
|
void ResetLastUsed() { m_last_used = 0; }
|
||||||
void IncrementLastUsed() { ++m_last_used; }
|
void IncrementLastUsed() { ++m_last_used; }
|
||||||
void SetDirty(bool dirty) { m_dirty = dirty; }
|
void SetDirty(bool dirty) { m_in_ppc_state = !dirty; }
|
||||||
bool IsDirty() const { return m_dirty; }
|
bool IsInPPCState() const { return m_in_ppc_state; }
|
||||||
|
bool IsInHostRegister() const { return m_in_host_register; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// For REG_REG
|
|
||||||
RegType m_type = RegType::NotLoaded; // store type
|
|
||||||
Arm64Gen::ARM64Reg m_reg = Arm64Gen::ARM64Reg::INVALID_REG; // host register we are in
|
Arm64Gen::ARM64Reg m_reg = Arm64Gen::ARM64Reg::INVALID_REG; // host register we are in
|
||||||
|
RegType m_fpr_type = RegType::Register; // for FPRs only
|
||||||
|
|
||||||
u32 m_last_used = 0;
|
u32 m_last_used = 0;
|
||||||
|
|
||||||
bool m_dirty = false;
|
bool m_in_ppc_state = true;
|
||||||
|
bool m_in_host_register = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HostReg
|
class HostReg
|
||||||
@ -444,9 +445,9 @@ public:
|
|||||||
|
|
||||||
// Returns a guest register inside of a host register
|
// Returns a guest register inside of a host register
|
||||||
// Will dump an immediate to the host register as well
|
// Will dump an immediate to the host register as well
|
||||||
Arm64Gen::ARM64Reg R(size_t preg, RegType type);
|
Arm64Gen::ARM64Reg R(size_t preg, RegType format);
|
||||||
|
|
||||||
Arm64Gen::ARM64Reg RW(size_t preg, RegType type, bool set_dirty = true);
|
Arm64Gen::ARM64Reg RW(size_t preg, RegType format, bool set_dirty = true);
|
||||||
|
|
||||||
BitSet32 GetCallerSavedUsed() const override;
|
BitSet32 GetCallerSavedUsed() const override;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user