CPU/Recompiler: Incorporate latest work into AArch64 backend

This commit is contained in:
Connor McLaughlin 2019-12-13 01:04:15 +10:00
parent 44c76f3bf3
commit 5e5b1b64db

View file

@ -5,15 +5,6 @@ Log_SetChannel(CPU::Recompiler);
namespace a64 = vixl::aarch64; namespace a64 = vixl::aarch64;
// Really need push/pop register allocator state...
#define REG_ALLOC_HACK() \
do \
{ \
Value temp_alloc_hack_0 = m_register_cache.AllocateScratch(RegSize_64); \
Value temp_alloc_hack_1 = m_register_cache.AllocateScratch(RegSize_64); \
Value temp_alloc_hack_2 = m_register_cache.AllocateScratch(RegSize_64); \
} while (0)
namespace CPU::Recompiler { namespace CPU::Recompiler {
constexpr HostReg RCPUPTR = 19; constexpr HostReg RCPUPTR = 19;
@ -193,6 +184,9 @@ void CodeGenerator::EmitEndBlock()
void CodeGenerator::EmitExceptionExit() void CodeGenerator::EmitExceptionExit()
{ {
// toss away our PC value since we're jumping to the exception handler
m_register_cache.InvalidateGuestRegister(Reg::pc);
// ensure all unflushed registers are written back // ensure all unflushed registers are written back
m_register_cache.FlushAllGuestRegisters(false, false); m_register_cache.FlushAllGuestRegisters(false, false);
@ -209,19 +203,24 @@ void CodeGenerator::EmitExceptionExit()
void CodeGenerator::EmitExceptionExitOnBool(const Value& value) void CodeGenerator::EmitExceptionExitOnBool(const Value& value)
{ {
Assert(!value.IsConstant() && value.IsInHostRegister()); Assert(!value.IsConstant() && value.IsInHostRegister());
REG_ALLOC_HACK();
m_register_cache.PushState();
// TODO: This is... not great. // TODO: This is... not great.
Value temp = m_register_cache.AllocateScratch(RegSize_64);
a64::Label skip_branch; a64::Label skip_branch;
m_emit->Cbz(GetHostReg64(value.host_reg), &skip_branch); m_emit->Cbz(GetHostReg64(value.host_reg), &skip_branch);
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer())); {
m_emit->Br(GetHostReg64(temp)); Value temp = m_register_cache.AllocateScratch(RegSize_64);
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer()));
m_emit->Br(GetHostReg64(temp));
}
m_emit->Bind(&skip_branch); m_emit->Bind(&skip_branch);
SwitchToFarCode(); SwitchToFarCode();
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
m_register_cache.PopState();
} }
void CodeGenerator::FinalizeBlock(CodeBlock::HostCodePointer* out_host_code, u32* out_host_code_size) void CodeGenerator::FinalizeBlock(CodeBlock::HostCodePointer* out_host_code, u32* out_host_code_size)
@ -339,7 +338,7 @@ void CodeGenerator::EmitCopyValue(HostReg to_reg, const Value& value)
} }
} }
void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags) void CodeGenerator::EmitAdd(HostReg to_reg, HostReg from_reg, const Value& value, bool set_flags)
{ {
Assert(value.IsConstant() || value.IsInHostRegister()); Assert(value.IsConstant() || value.IsInHostRegister());
@ -349,16 +348,16 @@ void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags)
if (value.size < RegSize_64) if (value.size < RegSize_64)
{ {
if (set_flags) if (set_flags)
m_emit->adds(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->adds(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
else else
m_emit->add(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->add(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
} }
else else
{ {
if (set_flags) if (set_flags)
m_emit->adds(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->adds(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
else else
m_emit->add(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->add(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
} }
return; return;
@ -371,16 +370,16 @@ void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags)
if (value.size < RegSize_64) if (value.size < RegSize_64)
{ {
if (set_flags) if (set_flags)
m_emit->adds(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); m_emit->adds(GetHostReg32(to_reg), GetHostReg32(from_reg), constant_value);
else else
m_emit->add(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); m_emit->add(GetHostReg32(to_reg), GetHostReg32(from_reg), constant_value);
} }
else else
{ {
if (set_flags) if (set_flags)
m_emit->adds(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); m_emit->adds(GetHostReg64(to_reg), GetHostReg64(from_reg), constant_value);
else else
m_emit->add(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); m_emit->add(GetHostReg64(to_reg), GetHostReg64(from_reg), constant_value);
} }
return; return;
@ -392,10 +391,10 @@ void CodeGenerator::EmitAdd(HostReg to_reg, const Value& value, bool set_flags)
m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value); m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value);
else else
m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value); m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value);
EmitAdd(to_reg, temp_value, set_flags); EmitAdd(to_reg, from_reg, temp_value, set_flags);
} }
void CodeGenerator::EmitSub(HostReg to_reg, const Value& value, bool set_flags) void CodeGenerator::EmitSub(HostReg to_reg, HostReg from_reg, const Value& value, bool set_flags)
{ {
Assert(value.IsConstant() || value.IsInHostRegister()); Assert(value.IsConstant() || value.IsInHostRegister());
@ -405,16 +404,16 @@ void CodeGenerator::EmitSub(HostReg to_reg, const Value& value, bool set_flags)
if (value.size < RegSize_64) if (value.size < RegSize_64)
{ {
if (set_flags) if (set_flags)
m_emit->subs(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->subs(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
else else
m_emit->sub(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->sub(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
} }
else else
{ {
if (set_flags) if (set_flags)
m_emit->subs(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->subs(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
else else
m_emit->sub(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->sub(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
} }
return; return;
@ -427,16 +426,16 @@ void CodeGenerator::EmitSub(HostReg to_reg, const Value& value, bool set_flags)
if (value.size < RegSize_64) if (value.size < RegSize_64)
{ {
if (set_flags) if (set_flags)
m_emit->subs(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); m_emit->subs(GetHostReg32(to_reg), GetHostReg32(from_reg), constant_value);
else else
m_emit->sub(GetHostReg32(to_reg), GetHostReg32(to_reg), constant_value); m_emit->sub(GetHostReg32(to_reg), GetHostReg32(from_reg), constant_value);
} }
else else
{ {
if (set_flags) if (set_flags)
m_emit->subs(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); m_emit->subs(GetHostReg64(to_reg), GetHostReg64(from_reg), constant_value);
else else
m_emit->sub(GetHostReg64(to_reg), GetHostReg64(to_reg), constant_value); m_emit->sub(GetHostReg64(to_reg), GetHostReg64(from_reg), constant_value);
} }
return; return;
@ -448,7 +447,7 @@ void CodeGenerator::EmitSub(HostReg to_reg, const Value& value, bool set_flags)
m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value); m_emit->Mov(GetHostReg32(temp_value.host_reg), constant_value);
else else
m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value); m_emit->Mov(GetHostReg64(temp_value.host_reg), constant_value);
EmitSub(to_reg, temp_value, set_flags); EmitSub(to_reg, from_reg, temp_value, set_flags);
} }
void CodeGenerator::EmitCmp(HostReg to_reg, const Value& value) void CodeGenerator::EmitCmp(HostReg to_reg, const Value& value)
@ -557,7 +556,7 @@ void CodeGenerator::EmitDec(HostReg to_reg, RegSize size)
#endif #endif
} }
void CodeGenerator::EmitShl(HostReg to_reg, RegSize size, const Value& amount_value) void CodeGenerator::EmitShl(HostReg to_reg, HostReg from_reg, RegSize size, const Value& amount_value)
{ {
switch (size) switch (size)
{ {
@ -566,29 +565,29 @@ void CodeGenerator::EmitShl(HostReg to_reg, RegSize size, const Value& amount_va
case RegSize_32: case RegSize_32:
{ {
if (amount_value.IsConstant()) if (amount_value.IsConstant())
m_emit->lsl(GetHostReg32(to_reg), GetHostReg32(to_reg), amount_value.constant_value & 0x1F); m_emit->lsl(GetHostReg32(to_reg), GetHostReg32(from_reg), amount_value.constant_value & 0x1F);
else else
m_emit->lslv(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(amount_value)); m_emit->lslv(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(amount_value));
if (size == RegSize_8) if (size == RegSize_8)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), 0xFF); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), 0xFF);
else if (size == RegSize_16) else if (size == RegSize_16)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), 0xFFFF); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), 0xFFFF);
} }
break; break;
case RegSize_64: case RegSize_64:
{ {
if (amount_value.IsConstant()) if (amount_value.IsConstant())
m_emit->lsl(GetHostReg64(to_reg), GetHostReg64(to_reg), amount_value.constant_value & 0x3F); m_emit->lsl(GetHostReg64(to_reg), GetHostReg64(from_reg), amount_value.constant_value & 0x3F);
else else
m_emit->lslv(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(amount_value)); m_emit->lslv(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(amount_value));
} }
break; break;
} }
} }
void CodeGenerator::EmitShr(HostReg to_reg, RegSize size, const Value& amount_value) void CodeGenerator::EmitShr(HostReg to_reg, HostReg from_reg, RegSize size, const Value& amount_value)
{ {
switch (size) switch (size)
{ {
@ -597,14 +596,14 @@ void CodeGenerator::EmitShr(HostReg to_reg, RegSize size, const Value& amount_va
case RegSize_32: case RegSize_32:
{ {
if (amount_value.IsConstant()) if (amount_value.IsConstant())
m_emit->lsr(GetHostReg32(to_reg), GetHostReg32(to_reg), amount_value.constant_value & 0x1F); m_emit->lsr(GetHostReg32(to_reg), GetHostReg32(from_reg), amount_value.constant_value & 0x1F);
else else
m_emit->lsrv(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(amount_value)); m_emit->lsrv(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(amount_value));
if (size == RegSize_8) if (size == RegSize_8)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), 0xFF); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), 0xFF);
else if (size == RegSize_16) else if (size == RegSize_16)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), 0xFFFF); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), 0xFFFF);
} }
break; break;
@ -619,7 +618,7 @@ void CodeGenerator::EmitShr(HostReg to_reg, RegSize size, const Value& amount_va
} }
} }
void CodeGenerator::EmitSar(HostReg to_reg, RegSize size, const Value& amount_value) void CodeGenerator::EmitSar(HostReg to_reg, HostReg from_reg, RegSize size, const Value& amount_value)
{ {
switch (size) switch (size)
{ {
@ -628,23 +627,23 @@ void CodeGenerator::EmitSar(HostReg to_reg, RegSize size, const Value& amount_va
case RegSize_32: case RegSize_32:
{ {
if (amount_value.IsConstant()) if (amount_value.IsConstant())
m_emit->asr(GetHostReg32(to_reg), GetHostReg32(to_reg), amount_value.constant_value & 0x1F); m_emit->asr(GetHostReg32(to_reg), GetHostReg32(from_reg), amount_value.constant_value & 0x1F);
else else
m_emit->asrv(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(amount_value)); m_emit->asrv(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(amount_value));
if (size == RegSize_8) if (size == RegSize_8)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), 0xFF); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), 0xFF);
else if (size == RegSize_16) else if (size == RegSize_16)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), 0xFFFF); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), 0xFFFF);
} }
break; break;
case RegSize_64: case RegSize_64:
{ {
if (amount_value.IsConstant()) if (amount_value.IsConstant())
m_emit->asr(GetHostReg64(to_reg), GetHostReg64(to_reg), amount_value.constant_value & 0x3F); m_emit->asr(GetHostReg64(to_reg), GetHostReg64(from_reg), amount_value.constant_value & 0x3F);
else else
m_emit->asrv(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(amount_value)); m_emit->asrv(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(amount_value));
} }
break; break;
} }
@ -657,7 +656,7 @@ static bool CanFitInBitwiseImmediate(const Value& value)
return a64::Assembler::IsImmLogical(s64(value.constant_value), reg_size, &n, &imm_s, &imm_r); return a64::Assembler::IsImmLogical(s64(value.constant_value), reg_size, &n, &imm_s, &imm_r);
} }
void CodeGenerator::EmitAnd(HostReg to_reg, const Value& value) void CodeGenerator::EmitAnd(HostReg to_reg, HostReg from_reg, const Value& value)
{ {
Assert(value.IsConstant() || value.IsInHostRegister()); Assert(value.IsConstant() || value.IsInHostRegister());
@ -665,9 +664,9 @@ void CodeGenerator::EmitAnd(HostReg to_reg, const Value& value)
if (value.IsInHostRegister()) if (value.IsInHostRegister())
{ {
if (value.size < RegSize_64) if (value.size < RegSize_64)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
else else
m_emit->and_(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->and_(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
return; return;
} }
@ -676,9 +675,9 @@ void CodeGenerator::EmitAnd(HostReg to_reg, const Value& value)
if (CanFitInBitwiseImmediate(value)) if (CanFitInBitwiseImmediate(value))
{ {
if (value.size < RegSize_64) if (value.size < RegSize_64)
m_emit->and_(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); m_emit->and_(GetHostReg32(to_reg), GetHostReg32(from_reg), s64(value.constant_value));
else else
m_emit->and_(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); m_emit->and_(GetHostReg64(to_reg), GetHostReg64(from_reg), s64(value.constant_value));
return; return;
} }
@ -689,10 +688,10 @@ void CodeGenerator::EmitAnd(HostReg to_reg, const Value& value)
m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value)); m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value));
else else
m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value)); m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value));
EmitAnd(to_reg, temp_value); EmitAnd(to_reg, from_reg, temp_value);
} }
void CodeGenerator::EmitOr(HostReg to_reg, const Value& value) void CodeGenerator::EmitOr(HostReg to_reg, HostReg from_reg, const Value& value)
{ {
Assert(value.IsConstant() || value.IsInHostRegister()); Assert(value.IsConstant() || value.IsInHostRegister());
@ -700,9 +699,9 @@ void CodeGenerator::EmitOr(HostReg to_reg, const Value& value)
if (value.IsInHostRegister()) if (value.IsInHostRegister())
{ {
if (value.size < RegSize_64) if (value.size < RegSize_64)
m_emit->orr(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->orr(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
else else
m_emit->orr(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->orr(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
return; return;
} }
@ -711,9 +710,9 @@ void CodeGenerator::EmitOr(HostReg to_reg, const Value& value)
if (CanFitInBitwiseImmediate(value)) if (CanFitInBitwiseImmediate(value))
{ {
if (value.size < RegSize_64) if (value.size < RegSize_64)
m_emit->orr(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); m_emit->orr(GetHostReg32(to_reg), GetHostReg32(from_reg), s64(value.constant_value));
else else
m_emit->orr(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); m_emit->orr(GetHostReg64(to_reg), GetHostReg64(from_reg), s64(value.constant_value));
return; return;
} }
@ -724,10 +723,10 @@ void CodeGenerator::EmitOr(HostReg to_reg, const Value& value)
m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value)); m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value));
else else
m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value)); m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value));
EmitOr(to_reg, temp_value); EmitOr(to_reg, from_reg, temp_value);
} }
void CodeGenerator::EmitXor(HostReg to_reg, const Value& value) void CodeGenerator::EmitXor(HostReg to_reg, HostReg from_reg, const Value& value)
{ {
Assert(value.IsConstant() || value.IsInHostRegister()); Assert(value.IsConstant() || value.IsInHostRegister());
@ -735,9 +734,9 @@ void CodeGenerator::EmitXor(HostReg to_reg, const Value& value)
if (value.IsInHostRegister()) if (value.IsInHostRegister())
{ {
if (value.size < RegSize_64) if (value.size < RegSize_64)
m_emit->eor(GetHostReg32(to_reg), GetHostReg32(to_reg), GetHostReg32(value.host_reg)); m_emit->eor(GetHostReg32(to_reg), GetHostReg32(from_reg), GetHostReg32(value.host_reg));
else else
m_emit->eor(GetHostReg64(to_reg), GetHostReg64(to_reg), GetHostReg64(value.host_reg)); m_emit->eor(GetHostReg64(to_reg), GetHostReg64(from_reg), GetHostReg64(value.host_reg));
return; return;
} }
@ -746,9 +745,9 @@ void CodeGenerator::EmitXor(HostReg to_reg, const Value& value)
if (CanFitInBitwiseImmediate(value)) if (CanFitInBitwiseImmediate(value))
{ {
if (value.size < RegSize_64) if (value.size < RegSize_64)
m_emit->eor(GetHostReg32(to_reg), GetHostReg32(to_reg), s64(value.constant_value)); m_emit->eor(GetHostReg32(to_reg), GetHostReg32(from_reg), s64(value.constant_value));
else else
m_emit->eor(GetHostReg64(to_reg), GetHostReg64(to_reg), s64(value.constant_value)); m_emit->eor(GetHostReg64(to_reg), GetHostReg64(from_reg), s64(value.constant_value));
return; return;
} }
@ -759,7 +758,7 @@ void CodeGenerator::EmitXor(HostReg to_reg, const Value& value)
m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value)); m_emit->Mov(GetHostReg32(temp_value.host_reg), s64(value.constant_value));
else else
m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value)); m_emit->Mov(GetHostReg64(temp_value.host_reg), s64(value.constant_value));
EmitXor(to_reg, temp_value); EmitXor(to_reg, from_reg, temp_value);
} }
void CodeGenerator::EmitTest(HostReg to_reg, const Value& value) void CodeGenerator::EmitTest(HostReg to_reg, const Value& value)
@ -1239,19 +1238,21 @@ Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size)
break; break;
} }
REG_ALLOC_HACK();
a64::Label load_okay; a64::Label load_okay;
m_emit->Tbz(GetHostReg64(result.host_reg), 63, &load_okay); m_emit->Tbz(GetHostReg64(result.host_reg), 63, &load_okay);
m_emit->Mov(GetHostReg64(result.host_reg), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer())); m_emit->Mov(GetHostReg64(result.host_reg), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer()));
m_emit->Br(GetHostReg64(result.host_reg)); m_emit->Br(GetHostReg64(result.host_reg));
m_emit->Bind(&load_okay); m_emit->Bind(&load_okay);
m_register_cache.PushState();
// load exception path // load exception path
SwitchToFarCode(); SwitchToFarCode();
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
m_register_cache.PopState();
// Downcast to ignore upper 56/48/32 bits. This should be a noop. // Downcast to ignore upper 56/48/32 bits. This should be a noop.
switch (size) switch (size)
{ {
@ -1298,18 +1299,20 @@ void CodeGenerator::EmitStoreGuestMemory(const Value& address, const Value& valu
break; break;
} }
REG_ALLOC_HACK();
a64::Label store_okay; a64::Label store_okay;
m_emit->Cbnz(GetHostReg64(result.host_reg), &store_okay); m_emit->Cbnz(GetHostReg64(result.host_reg), &store_okay);
m_emit->Mov(GetHostReg64(result.host_reg), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer())); m_emit->Mov(GetHostReg64(result.host_reg), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer()));
m_emit->Br(GetHostReg64(result.host_reg)); m_emit->Br(GetHostReg64(result.host_reg));
m_emit->Bind(&store_okay); m_emit->Bind(&store_okay);
m_register_cache.PushState();
// store exception path // store exception path
SwitchToFarCode(); SwitchToFarCode();
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
m_register_cache.PopState();
} }
void CodeGenerator::EmitFlushInterpreterLoadDelay() void CodeGenerator::EmitFlushInterpreterLoadDelay()
@ -1456,32 +1459,41 @@ static void EmitConditionalJump(Condition condition, bool invert, a64::MacroAsse
void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, Value&& branch_target) void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, Value&& branch_target)
{ {
// we have to always read the old PC.. when we can push/pop the register cache state this won't be needed // ensure the lr register is flushed, since we want it's correct value after the branch
Value old_npc; if (lr_reg != Reg::count && lr_reg != Reg::zero)
if (lr_reg != Reg::count) m_register_cache.FlushGuestRegister(lr_reg, true, true);
old_npc = m_register_cache.ReadGuestRegister(Reg::npc, false, true);
REG_ALLOC_HACK(); // compute return address, which is also set as the new pc when the branch isn't taken
Value new_pc;
if (condition != Condition::Always || lr_reg != Reg::count)
{
new_pc = AddValues(m_register_cache.ReadGuestRegister(Reg::pc), Value::FromConstantU32(4), false);
if (!new_pc.IsInHostRegister())
new_pc = GetValueInHostRegister(new_pc);
}
// condition is inverted because we want the case for skipping it
a64::Label skip_branch; a64::Label skip_branch;
if (condition != Condition::Always) if (condition != Condition::Always)
{
// condition is inverted because we want the case for skipping it
EmitConditionalJump(condition, true, m_emit, &skip_branch); EmitConditionalJump(condition, true, m_emit, &skip_branch);
}
// save the old PC if we want to // save the old PC if we want to
if (lr_reg != Reg::count) if (lr_reg != Reg::count && lr_reg != Reg::zero)
{ {
// Can't cache because we have two branches. Load delay cancel is due to the immediate flush afterwards, // Can't cache because we have two branches. Load delay cancel is due to the immediate flush afterwards,
// if we don't cancel it, at the end of the instruction the value we write can be overridden. // if we don't cancel it, at the end of the instruction the value we write can be overridden.
EmitCancelInterpreterLoadDelayForReg(lr_reg); EmitCancelInterpreterLoadDelayForReg(lr_reg);
m_register_cache.WriteGuestRegister(lr_reg, std::move(old_npc)); EmitStoreGuestRegister(lr_reg, new_pc);
m_register_cache.FlushGuestRegister(lr_reg, true, true);
} }
// we don't need to test the address of constant branches unless they're definitely misaligned, which would be // we don't need to test the address of constant branches unless they're definitely misaligned, which would be
// strange. // strange.
if (!branch_target.IsConstant() || (branch_target.constant_value & 0x3) != 0) if (!branch_target.IsConstant() || (branch_target.constant_value & 0x3) != 0)
{ {
m_register_cache.PushState();
if (branch_target.IsConstant()) if (branch_target.IsConstant())
{ {
Log_WarningPrintf("Misaligned constant target branch 0x%08X, this is strange", Log_WarningPrintf("Misaligned constant target branch 0x%08X, this is strange",
@ -1491,11 +1503,12 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, Value&& branch_t
{ {
// check the alignment of the target // check the alignment of the target
a64::Label branch_target_okay; a64::Label branch_target_okay;
Value far_code_addr = m_register_cache.AllocateScratch(RegSize_64);
m_emit->tst(GetHostReg32(branch_target), 0x3); m_emit->tst(GetHostReg32(branch_target), 0x3);
m_emit->B(a64::eq, &branch_target_okay); m_emit->B(a64::eq, &branch_target_okay);
Value far_code_addr = m_register_cache.AllocateScratch(RegSize_64);
m_emit->Mov(GetHostReg64(far_code_addr), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer())); m_emit->Mov(GetHostReg64(far_code_addr), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer()));
m_emit->br(GetHostReg64(far_code_addr)); m_emit->br(GetHostReg64(far_code_addr));
m_emit->Bind(&branch_target_okay); m_emit->Bind(&branch_target_okay);
} }
@ -1505,15 +1518,22 @@ void CodeGenerator::EmitBranch(Condition condition, Reg lr_reg, Value&& branch_t
Value::FromConstantU8(0), Value::FromConstantU8(1)); Value::FromConstantU8(0), Value::FromConstantU8(1));
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
m_register_cache.PopState();
} }
// branch taken path - write new PC and flush it, since two branches // branch taken path - change the return address/new pc
m_register_cache.WriteGuestRegister(Reg::npc, std::move(branch_target)); if (condition != Condition::Always)
m_register_cache.FlushGuestRegister(Reg::npc, true, true); EmitCopyValue(new_pc.GetHostRegister(), branch_target);
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_was_branch_taken), Value::FromConstantU8(1));
// converge point // converge point
m_emit->Bind(&skip_branch); m_emit->Bind(&skip_branch);
// update pc
if (condition != Condition::Always)
m_register_cache.WriteGuestRegister(Reg::pc, std::move(new_pc));
else
m_register_cache.WriteGuestRegister(Reg::pc, std::move(branch_target));
} }
void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */) void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /* = Condition::Always */)
@ -1521,26 +1541,26 @@ void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /*
if (condition == Condition::Always) if (condition == Condition::Always)
{ {
// no need to use far code if we're always raising the exception // no need to use far code if we're always raising the exception
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), m_register_cache.InvalidateGuestRegister(Reg::pc);
Value::FromConstantU8(static_cast<u8>(excode)));
m_register_cache.FlushAllGuestRegisters(true, true); m_register_cache.FlushAllGuestRegisters(true, true);
m_register_cache.FlushLoadDelay(true); m_register_cache.FlushLoadDelay(true);
// PC should be synced at this point. If we leave the 4 on here for this instruction, we mess up npc. EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(),
Assert(m_delayed_pc_add == 4); Value::FromConstantU8(static_cast<u8>(excode)));
m_delayed_pc_add = 0;
return; return;
} }
Value far_code_addr = m_register_cache.AllocateScratch(RegSize_64);
REG_ALLOC_HACK();
a64::Label skip_raise_exception; a64::Label skip_raise_exception;
EmitConditionalJump(condition, true, m_emit, &skip_raise_exception); EmitConditionalJump(condition, true, m_emit, &skip_raise_exception);
m_emit->Mov(GetHostReg64(far_code_addr), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer())); m_register_cache.PushState();
m_emit->Br(GetHostReg64(far_code_addr));
{
Value far_code_addr = m_register_cache.AllocateScratch(RegSize_64);
m_emit->Mov(GetHostReg64(far_code_addr), reinterpret_cast<intptr_t>(GetCurrentFarCodePointer()));
m_emit->Br(GetHostReg64(far_code_addr));
}
m_emit->Bind(&skip_raise_exception); m_emit->Bind(&skip_raise_exception);
SwitchToFarCode(); SwitchToFarCode();
@ -1548,6 +1568,8 @@ void CodeGenerator::EmitRaiseException(Exception excode, Condition condition /*
Value::FromConstantU8(static_cast<u8>(excode))); Value::FromConstantU8(static_cast<u8>(excode)));
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
m_register_cache.PopState();
} }
void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {} void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {}