Merge pull request #13991 from SuperSamus/branch-watch-optional-emit
Jit: Emit Branch Watch code only if it's enabled
This commit is contained in:
@@ -12,7 +12,10 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/Core.h"
|
||||||
#include "Core/PowerPC/Gekko.h"
|
#include "Core/PowerPC/Gekko.h"
|
||||||
|
#include "Core/PowerPC/JitInterface.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
@@ -110,7 +113,7 @@ enum class BranchWatchPhase : bool
|
|||||||
Reduction,
|
Reduction,
|
||||||
};
|
};
|
||||||
|
|
||||||
class BranchWatch final // Class is final to enforce the safety of GetOffsetOfRecordingActive().
|
class BranchWatch final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Collection = BranchWatchCollection;
|
using Collection = BranchWatchCollection;
|
||||||
@@ -119,7 +122,12 @@ public:
|
|||||||
using SelectionInspection = BranchWatchSelectionInspection;
|
using SelectionInspection = BranchWatchSelectionInspection;
|
||||||
|
|
||||||
bool GetRecordingActive() const { return m_recording_active; }
|
bool GetRecordingActive() const { return m_recording_active; }
|
||||||
void SetRecordingActive(const CPUThreadGuard& guard, bool active) { m_recording_active = active; }
|
void SetRecordingActive(const CPUThreadGuard& guard, bool active)
|
||||||
|
{
|
||||||
|
m_recording_active = active;
|
||||||
|
auto& system = guard.GetSystem();
|
||||||
|
system.GetJitInterface().ClearCache(guard);
|
||||||
|
}
|
||||||
void Clear(const CPUThreadGuard& guard);
|
void Clear(const CPUThreadGuard& guard);
|
||||||
|
|
||||||
void Save(const CPUThreadGuard& guard, std::FILE* file) const;
|
void Save(const CPUThreadGuard& guard, std::FILE* file) const;
|
||||||
@@ -226,19 +234,6 @@ public:
|
|||||||
HitPhysicalFalse(this, origin, destination, inst.hex);
|
HitPhysicalFalse(this, origin, destination, inst.hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The JIT needs this value, but doesn't need to be a full-on friend.
|
|
||||||
static constexpr int GetOffsetOfRecordingActive()
|
|
||||||
{
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
|
||||||
#endif
|
|
||||||
return offsetof(BranchWatch, m_recording_active);
|
|
||||||
#ifdef __GNUC__
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Collection& GetCollectionV(bool condition)
|
Collection& GetCollectionV(bool condition)
|
||||||
{
|
{
|
||||||
@@ -273,8 +268,4 @@ private:
|
|||||||
Collection m_collection_pf; // physical address space | false path
|
Collection m_collection_pf; // physical address space | false path
|
||||||
Selection m_selection;
|
Selection m_selection;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if _M_X86_64
|
|
||||||
static_assert(BranchWatch::GetOffsetOfRecordingActive() < 0x80); // Makes JIT code smaller.
|
|
||||||
#endif
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -1067,13 +1067,12 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
|
|
||||||
if (op.skip)
|
if (op.skip)
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
// The only thing that currently sets op.skip is the BLR following optimization.
|
// The only thing that currently sets op.skip is the BLR following optimization.
|
||||||
// If any non-branch instruction starts setting that too, this will need to be changed.
|
// If any non-branch instruction starts setting that too, this will need to be changed.
|
||||||
ASSERT(op.inst.hex == 0x4e800020);
|
ASSERT(op.inst.hex == 0x4e800020);
|
||||||
WriteBranchWatch<true>(op.address, op.branchTo, op.inst, RSCRATCH, RSCRATCH2,
|
WriteBranchWatch<true>(op.address, op.branchTo, op.inst, CallerSavedRegistersInUse());
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -110,10 +110,8 @@ public:
|
|||||||
void WriteRfiExitDestInRSCRATCH();
|
void WriteRfiExitDestInRSCRATCH();
|
||||||
void WriteIdleExit(u32 destination);
|
void WriteIdleExit(u32 destination);
|
||||||
template <bool condition>
|
template <bool condition>
|
||||||
void WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst, Gen::X64Reg reg_a,
|
void WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst, BitSet32 caller_save);
|
||||||
Gen::X64Reg reg_b, BitSet32 caller_save);
|
void WriteBranchWatchDestInRSCRATCH(u32 origin, UGeckoInstruction inst, BitSet32 caller_save);
|
||||||
void WriteBranchWatchDestInRSCRATCH(u32 origin, UGeckoInstruction inst, Gen::X64Reg reg_a,
|
|
||||||
Gen::X64Reg reg_b, BitSet32 caller_save);
|
|
||||||
|
|
||||||
bool Cleanup();
|
bool Cleanup();
|
||||||
|
|
||||||
|
|||||||
@@ -67,65 +67,42 @@ void Jit64::rfi(UGeckoInstruction inst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <bool condition>
|
template <bool condition>
|
||||||
void Jit64::WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst, X64Reg reg_a,
|
void Jit64::WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst,
|
||||||
X64Reg reg_b, BitSet32 caller_save)
|
BitSet32 caller_save)
|
||||||
{
|
{
|
||||||
MOV(64, R(reg_a), ImmPtr(&m_branch_watch));
|
if (IsBranchWatchEnabled())
|
||||||
MOVZX(32, 8, reg_b, MDisp(reg_a, Core::BranchWatch::GetOffsetOfRecordingActive()));
|
{
|
||||||
TEST(32, R(reg_b), R(reg_b));
|
ABI_PushRegistersAndAdjustStack(caller_save, 0);
|
||||||
|
MOV(64, R(ABI_PARAM1), ImmPtr(&m_branch_watch));
|
||||||
FixupBranch branch_in = J_CC(CC_NZ, Jump::Near);
|
MOV(64, R(ABI_PARAM2), Imm64(Core::FakeBranchWatchCollectionKey{origin, destination}));
|
||||||
SwitchToFarCode();
|
MOV(32, R(ABI_PARAM3), Imm32(inst.hex));
|
||||||
SetJumpTarget(branch_in);
|
ABI_CallFunction(m_ppc_state.msr.IR ? (condition ? &Core::BranchWatch::HitVirtualTrue_fk :
|
||||||
|
&Core::BranchWatch::HitVirtualFalse_fk) :
|
||||||
ABI_PushRegistersAndAdjustStack(caller_save, 0);
|
(condition ? &Core::BranchWatch::HitPhysicalTrue_fk :
|
||||||
// Some call sites have an optimization to use ABI_PARAM1 as a scratch register.
|
&Core::BranchWatch::HitPhysicalFalse_fk));
|
||||||
if (reg_a != ABI_PARAM1)
|
ABI_PopRegistersAndAdjustStack(caller_save, 0);
|
||||||
MOV(64, R(ABI_PARAM1), R(reg_a));
|
}
|
||||||
MOV(64, R(ABI_PARAM2), Imm64(Core::FakeBranchWatchCollectionKey{origin, destination}));
|
|
||||||
MOV(32, R(ABI_PARAM3), Imm32(inst.hex));
|
|
||||||
ABI_CallFunction(m_ppc_state.msr.IR ? (condition ? &Core::BranchWatch::HitVirtualTrue_fk :
|
|
||||||
&Core::BranchWatch::HitVirtualFalse_fk) :
|
|
||||||
(condition ? &Core::BranchWatch::HitPhysicalTrue_fk :
|
|
||||||
&Core::BranchWatch::HitPhysicalFalse_fk));
|
|
||||||
ABI_PopRegistersAndAdjustStack(caller_save, 0);
|
|
||||||
|
|
||||||
FixupBranch branch_out = J(Jump::Near);
|
|
||||||
SwitchToNearCode();
|
|
||||||
SetJumpTarget(branch_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template void Jit64::WriteBranchWatch<true>(u32, u32, UGeckoInstruction, X64Reg, X64Reg, BitSet32);
|
template void Jit64::WriteBranchWatch<true>(u32, u32, UGeckoInstruction, BitSet32);
|
||||||
template void Jit64::WriteBranchWatch<false>(u32, u32, UGeckoInstruction, X64Reg, X64Reg, BitSet32);
|
template void Jit64::WriteBranchWatch<false>(u32, u32, UGeckoInstruction, BitSet32);
|
||||||
|
|
||||||
void Jit64::WriteBranchWatchDestInRSCRATCH(u32 origin, UGeckoInstruction inst, X64Reg reg_a,
|
void Jit64::WriteBranchWatchDestInRSCRATCH(u32 origin, UGeckoInstruction inst, BitSet32 caller_save)
|
||||||
X64Reg reg_b, BitSet32 caller_save)
|
|
||||||
{
|
{
|
||||||
MOV(64, R(reg_a), ImmPtr(&m_branch_watch));
|
if (IsBranchWatchEnabled())
|
||||||
MOVZX(32, 8, reg_b, MDisp(reg_a, Core::BranchWatch::GetOffsetOfRecordingActive()));
|
{
|
||||||
TEST(32, R(reg_b), R(reg_b));
|
// Assert RSCRATCH won't be clobbered before it is moved from.
|
||||||
|
static_assert(ABI_PARAM1 != RSCRATCH);
|
||||||
|
|
||||||
FixupBranch branch_in = J_CC(CC_NZ, Jump::Near);
|
ABI_PushRegistersAndAdjustStack(caller_save, 0);
|
||||||
SwitchToFarCode();
|
MOV(64, R(ABI_PARAM1), ImmPtr(&m_branch_watch));
|
||||||
SetJumpTarget(branch_in);
|
MOV(32, R(ABI_PARAM3), R(RSCRATCH));
|
||||||
|
MOV(32, R(ABI_PARAM2), Imm32(origin));
|
||||||
// Assert RSCRATCH won't be clobbered before it is moved from.
|
MOV(32, R(ABI_PARAM4), Imm32(inst.hex));
|
||||||
static_assert(ABI_PARAM1 != RSCRATCH);
|
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue :
|
||||||
|
&Core::BranchWatch::HitPhysicalTrue);
|
||||||
ABI_PushRegistersAndAdjustStack(caller_save, 0);
|
ABI_PopRegistersAndAdjustStack(caller_save, 0);
|
||||||
// Some call sites have an optimization to use ABI_PARAM1 as a scratch register.
|
}
|
||||||
if (reg_a != ABI_PARAM1)
|
|
||||||
MOV(64, R(ABI_PARAM1), R(reg_a));
|
|
||||||
MOV(32, R(ABI_PARAM3), R(RSCRATCH));
|
|
||||||
MOV(32, R(ABI_PARAM2), Imm32(origin));
|
|
||||||
MOV(32, R(ABI_PARAM4), Imm32(inst.hex));
|
|
||||||
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue :
|
|
||||||
&Core::BranchWatch::HitPhysicalTrue);
|
|
||||||
ABI_PopRegistersAndAdjustStack(caller_save, 0);
|
|
||||||
|
|
||||||
FixupBranch branch_out = J(Jump::Near);
|
|
||||||
SwitchToNearCode();
|
|
||||||
SetJumpTarget(branch_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Jit64::bx(UGeckoInstruction inst)
|
void Jit64::bx(UGeckoInstruction inst)
|
||||||
@@ -143,11 +120,7 @@ void Jit64::bx(UGeckoInstruction inst)
|
|||||||
// Because PPCAnalyst::Flatten() merged the blocks.
|
// Because PPCAnalyst::Flatten() merged the blocks.
|
||||||
if (!js.isLastInstruction)
|
if (!js.isLastInstruction)
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, CallerSavedRegistersInUse());
|
||||||
{
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, RSCRATCH, RSCRATCH2,
|
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
|
||||||
if (inst.LK && !js.op->skipLRStack)
|
if (inst.LK && !js.op->skipLRStack)
|
||||||
{
|
{
|
||||||
// We have to fake the stack as the RET instruction was not
|
// We have to fake the stack as the RET instruction was not
|
||||||
@@ -161,11 +134,7 @@ void Jit64::bx(UGeckoInstruction inst)
|
|||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
#ifdef ACID_TEST
|
#ifdef ACID_TEST
|
||||||
if (inst.LK)
|
if (inst.LK)
|
||||||
AND(32, PPCSTATE(cr), Imm32(~(0xFF000000)));
|
AND(32, PPCSTATE(cr), Imm32(~(0xFF000000)));
|
||||||
@@ -216,11 +185,7 @@ void Jit64::bcx(UGeckoInstruction inst)
|
|||||||
if (!js.isLastInstruction && (inst.BO & BO_DONT_DECREMENT_FLAG) &&
|
if (!js.isLastInstruction && (inst.BO & BO_DONT_DECREMENT_FLAG) &&
|
||||||
(inst.BO & BO_DONT_CHECK_CONDITION))
|
(inst.BO & BO_DONT_CHECK_CONDITION))
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, CallerSavedRegistersInUse());
|
||||||
{
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, RSCRATCH, RSCRATCH2,
|
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
|
||||||
if (inst.LK && !js.op->skipLRStack)
|
if (inst.LK && !js.op->skipLRStack)
|
||||||
{
|
{
|
||||||
// We have to fake the stack as the RET instruction was not
|
// We have to fake the stack as the RET instruction was not
|
||||||
@@ -237,11 +202,7 @@ void Jit64::bcx(UGeckoInstruction inst)
|
|||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
if (js.op->branchIsIdleLoop)
|
if (js.op->branchIsIdleLoop)
|
||||||
{
|
{
|
||||||
WriteIdleExit(js.op->branchTo);
|
WriteIdleExit(js.op->branchTo);
|
||||||
@@ -261,18 +222,10 @@ void Jit64::bcx(UGeckoInstruction inst)
|
|||||||
{
|
{
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteExit(js.compilerPC + 4);
|
WriteExit(js.compilerPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, CallerSavedRegistersInUse());
|
||||||
{
|
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, RSCRATCH, RSCRATCH2,
|
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Jit64::bcctrx(UGeckoInstruction inst)
|
void Jit64::bcctrx(UGeckoInstruction inst)
|
||||||
@@ -296,12 +249,7 @@ void Jit64::bcctrx(UGeckoInstruction inst)
|
|||||||
if (inst.LK_3)
|
if (inst.LK_3)
|
||||||
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatchDestInRSCRATCH(js.compilerPC, inst, BitSet32{RSCRATCH});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatchDestInRSCRATCH(js.compilerPC, inst, ABI_PARAM1, RSCRATCH2,
|
|
||||||
BitSet32{RSCRATCH});
|
|
||||||
}
|
|
||||||
WriteExitDestInRSCRATCH(inst.LK_3, js.compilerPC + 4);
|
WriteExitDestInRSCRATCH(inst.LK_3, js.compilerPC + 4);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -324,12 +272,7 @@ void Jit64::bcctrx(UGeckoInstruction inst)
|
|||||||
RCForkGuard fpr_guard = fpr.Fork();
|
RCForkGuard fpr_guard = fpr.Fork();
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatchDestInRSCRATCH(js.compilerPC, inst, BitSet32{RSCRATCH});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatchDestInRSCRATCH(js.compilerPC, inst, ABI_PARAM1, RSCRATCH2,
|
|
||||||
BitSet32{RSCRATCH});
|
|
||||||
}
|
|
||||||
WriteExitDestInRSCRATCH(inst.LK_3, js.compilerPC + 4);
|
WriteExitDestInRSCRATCH(inst.LK_3, js.compilerPC + 4);
|
||||||
// Would really like to continue the block here, but it ends. TODO.
|
// Would really like to continue the block here, but it ends. TODO.
|
||||||
}
|
}
|
||||||
@@ -339,18 +282,10 @@ void Jit64::bcctrx(UGeckoInstruction inst)
|
|||||||
{
|
{
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteExit(js.compilerPC + 4);
|
WriteExit(js.compilerPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, CallerSavedRegistersInUse());
|
||||||
{
|
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, RSCRATCH, RSCRATCH2,
|
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,21 +334,12 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
|||||||
|
|
||||||
if (js.op->branchIsIdleLoop)
|
if (js.op->branchIsIdleLoop)
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteIdleExit(js.op->branchTo);
|
WriteIdleExit(js.op->branchTo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatchDestInRSCRATCH(js.compilerPC, inst, BitSet32{RSCRATCH});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatchDestInRSCRATCH(js.compilerPC, inst, ABI_PARAM1, RSCRATCH2,
|
|
||||||
BitSet32{RSCRATCH});
|
|
||||||
}
|
|
||||||
WriteBLRExit();
|
WriteBLRExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,16 +353,11 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
|||||||
{
|
{
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteExit(js.compilerPC + 4);
|
WriteExit(js.compilerPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
else
|
||||||
{
|
{
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, RSCRATCH, RSCRATCH2,
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, CallerSavedRegistersInUse());
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,11 +387,7 @@ void Jit64::DoMergedBranch()
|
|||||||
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
||||||
|
|
||||||
const u32 destination = js.op[1].branchTo;
|
const u32 destination = js.op[1].branchTo;
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(nextPC, destination, next, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<true>(nextPC, destination, next, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteIdleExit(destination);
|
WriteIdleExit(destination);
|
||||||
}
|
}
|
||||||
else if (next.OPCD == 16) // bcx
|
else if (next.OPCD == 16) // bcx
|
||||||
@@ -400,11 +396,7 @@ void Jit64::DoMergedBranch()
|
|||||||
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
||||||
|
|
||||||
const u32 destination = js.op[1].branchTo;
|
const u32 destination = js.op[1].branchTo;
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<true>(nextPC, destination, next, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<true>(nextPC, destination, next, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteExit(destination, next.LK, nextPC + 4);
|
WriteExit(destination, next.LK, nextPC + 4);
|
||||||
}
|
}
|
||||||
else if ((next.OPCD == 19) && (next.SUBOP10 == 528)) // bcctrx
|
else if ((next.OPCD == 19) && (next.SUBOP10 == 528)) // bcctrx
|
||||||
@@ -413,11 +405,7 @@ void Jit64::DoMergedBranch()
|
|||||||
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
||||||
MOV(32, R(RSCRATCH), PPCSTATE_SPR(SPR_CTR));
|
MOV(32, R(RSCRATCH), PPCSTATE_SPR(SPR_CTR));
|
||||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatchDestInRSCRATCH(nextPC, next, BitSet32{RSCRATCH});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatchDestInRSCRATCH(nextPC, next, ABI_PARAM1, RSCRATCH2, BitSet32{RSCRATCH});
|
|
||||||
}
|
|
||||||
WriteExitDestInRSCRATCH(next.LK, nextPC + 4);
|
WriteExitDestInRSCRATCH(next.LK, nextPC + 4);
|
||||||
}
|
}
|
||||||
else if ((next.OPCD == 19) && (next.SUBOP10 == 16)) // bclrx
|
else if ((next.OPCD == 19) && (next.SUBOP10 == 16)) // bclrx
|
||||||
@@ -427,11 +415,7 @@ void Jit64::DoMergedBranch()
|
|||||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||||
if (next.LK)
|
if (next.LK)
|
||||||
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
MOV(32, PPCSTATE_SPR(SPR_LR), Imm32(nextPC + 4));
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatchDestInRSCRATCH(nextPC, next, BitSet32{RSCRATCH});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatchDestInRSCRATCH(nextPC, next, ABI_PARAM1, RSCRATCH2, BitSet32{RSCRATCH});
|
|
||||||
}
|
|
||||||
WriteBLRExit();
|
WriteBLRExit();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -488,17 +472,12 @@ void Jit64::DoMergedBranchCondition()
|
|||||||
{
|
{
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(nextPC, nextPC + 4, next, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<false>(nextPC, nextPC + 4, next, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteExit(nextPC + 4);
|
WriteExit(nextPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
else
|
||||||
{
|
{
|
||||||
WriteBranchWatch<false>(nextPC, nextPC + 4, next, RSCRATCH, RSCRATCH2,
|
WriteBranchWatch<false>(nextPC, nextPC + 4, next, CallerSavedRegistersInUse());
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,17 +519,12 @@ void Jit64::DoMergedBranchImmediate(s64 val)
|
|||||||
{
|
{
|
||||||
gpr.Flush();
|
gpr.Flush();
|
||||||
fpr.Flush();
|
fpr.Flush();
|
||||||
if (IsDebuggingEnabled())
|
WriteBranchWatch<false>(nextPC, nextPC + 4, next, {});
|
||||||
{
|
|
||||||
// ABI_PARAM1 is safe to use after a GPR flush for an optimization in this function.
|
|
||||||
WriteBranchWatch<false>(nextPC, nextPC + 4, next, ABI_PARAM1, RSCRATCH, {});
|
|
||||||
}
|
|
||||||
WriteExit(nextPC + 4);
|
WriteExit(nextPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
else
|
||||||
{
|
{
|
||||||
WriteBranchWatch<false>(nextPC, nextPC + 4, next, RSCRATCH, RSCRATCH2,
|
WriteBranchWatch<false>(nextPC, nextPC + 4, next, CallerSavedRegistersInUse());
|
||||||
CallerSavedRegistersInUse());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -305,25 +305,16 @@ void Jit64::dcbx(UGeckoInstruction inst)
|
|||||||
// Load the loop_counter register with the amount of invalidations to execute.
|
// Load the loop_counter register with the amount of invalidations to execute.
|
||||||
LEA(32, loop_counter, MDisp(RSCRATCH2, 1));
|
LEA(32, loop_counter, MDisp(RSCRATCH2, 1));
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const X64Reg bw_reg_a = reg_cycle_count, bw_reg_b = reg_downcount;
|
|
||||||
const BitSet32 bw_caller_save = (CallerSavedRegistersInUse() | BitSet32{RSCRATCH2}) &
|
const BitSet32 bw_caller_save = (CallerSavedRegistersInUse() | BitSet32{RSCRATCH2}) &
|
||||||
~BitSet32{int(bw_reg_a), int(bw_reg_b)};
|
~BitSet32{int(reg_cycle_count), int(reg_downcount)};
|
||||||
|
|
||||||
MOV(64, R(bw_reg_a), ImmPtr(&m_branch_watch));
|
|
||||||
MOVZX(32, 8, bw_reg_b, MDisp(bw_reg_a, Core::BranchWatch::GetOffsetOfRecordingActive()));
|
|
||||||
TEST(32, R(bw_reg_b), R(bw_reg_b));
|
|
||||||
|
|
||||||
FixupBranch branch_in = J_CC(CC_NZ, Jump::Near);
|
|
||||||
SwitchToFarCode();
|
|
||||||
SetJumpTarget(branch_in);
|
|
||||||
|
|
||||||
// Assert RSCRATCH2 won't be clobbered before it is moved from.
|
// Assert RSCRATCH2 won't be clobbered before it is moved from.
|
||||||
static_assert(RSCRATCH2 != ABI_PARAM1);
|
static_assert(RSCRATCH2 != ABI_PARAM1);
|
||||||
|
|
||||||
ABI_PushRegistersAndAdjustStack(bw_caller_save, 0);
|
ABI_PushRegistersAndAdjustStack(bw_caller_save, 0);
|
||||||
MOV(64, R(ABI_PARAM1), R(bw_reg_a));
|
MOV(64, R(ABI_PARAM1), ImmPtr(&m_branch_watch));
|
||||||
// RSCRATCH2 holds the amount of faked branch watch hits. Move RSCRATCH2 first, because
|
// RSCRATCH2 holds the amount of faked branch watch hits. Move RSCRATCH2 first, because
|
||||||
// ABI_PARAM2 clobbers RSCRATCH2 on Windows and ABI_PARAM3 clobbers RSCRATCH2 on Linux!
|
// ABI_PARAM2 clobbers RSCRATCH2 on Windows and ABI_PARAM3 clobbers RSCRATCH2 on Linux!
|
||||||
MOV(32, R(ABI_PARAM4), R(RSCRATCH2));
|
MOV(32, R(ABI_PARAM4), R(RSCRATCH2));
|
||||||
@@ -333,10 +324,6 @@ void Jit64::dcbx(UGeckoInstruction inst)
|
|||||||
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue_fk_n :
|
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue_fk_n :
|
||||||
&Core::BranchWatch::HitPhysicalTrue_fk_n);
|
&Core::BranchWatch::HitPhysicalTrue_fk_n);
|
||||||
ABI_PopRegistersAndAdjustStack(bw_caller_save, 0);
|
ABI_PopRegistersAndAdjustStack(bw_caller_save, 0);
|
||||||
|
|
||||||
FixupBranch branch_out = J(Jump::Near);
|
|
||||||
SwitchToNearCode();
|
|
||||||
SetJumpTarget(branch_out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1296,16 +1296,13 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
|||||||
|
|
||||||
if (op.skip)
|
if (op.skip)
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
// The only thing that currently sets op.skip is the BLR following optimization.
|
// The only thing that currently sets op.skip is the BLR following optimization.
|
||||||
// If any non-branch instruction starts setting that too, this will need to be changed.
|
// If any non-branch instruction starts setting that too, this will need to be changed.
|
||||||
ASSERT(op.inst.hex == 0x4e800020);
|
ASSERT(op.inst.hex == 0x4e800020);
|
||||||
const auto bw_reg_a = gpr.GetScopedReg(), bw_reg_b = gpr.GetScopedReg();
|
WriteBranchWatch<true>(op.address, op.branchTo, op.inst, gpr.GetCallerSavedUsed(),
|
||||||
const BitSet32 gpr_caller_save =
|
fpr.GetCallerSavedUsed());
|
||||||
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(bw_reg_a), DecodeReg(bw_reg_b)};
|
|
||||||
WriteBranchWatch<true>(op.address, op.branchTo, op.inst, bw_reg_a, bw_reg_b,
|
|
||||||
gpr_caller_save, fpr.GetCallerSavedUsed());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -334,11 +334,9 @@ protected:
|
|||||||
// Branch Watch
|
// Branch Watch
|
||||||
template <bool condition>
|
template <bool condition>
|
||||||
void WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst,
|
void WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst,
|
||||||
Arm64Gen::ARM64Reg reg_a, Arm64Gen::ARM64Reg reg_b,
|
|
||||||
BitSet32 gpr_caller_save, BitSet32 fpr_caller_save);
|
BitSet32 gpr_caller_save, BitSet32 fpr_caller_save);
|
||||||
void WriteBranchWatchDestInRegister(u32 origin, Arm64Gen::ARM64Reg destination,
|
void WriteBranchWatchDestInRegister(u32 origin, Arm64Gen::ARM64Reg destination,
|
||||||
UGeckoInstruction inst, Arm64Gen::ARM64Reg reg_a,
|
UGeckoInstruction inst, BitSet32 gpr_caller_save,
|
||||||
Arm64Gen::ARM64Reg reg_b, BitSet32 gpr_caller_save,
|
|
||||||
BitSet32 fpr_caller_save);
|
BitSet32 fpr_caller_save);
|
||||||
|
|
||||||
// Exits
|
// Exits
|
||||||
|
|||||||
@@ -75,67 +75,35 @@ void JitArm64::rfi(UGeckoInstruction inst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <bool condition>
|
template <bool condition>
|
||||||
void JitArm64::WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst, ARM64Reg reg_a,
|
void JitArm64::WriteBranchWatch(u32 origin, u32 destination, UGeckoInstruction inst,
|
||||||
ARM64Reg reg_b, BitSet32 gpr_caller_save, BitSet32 fpr_caller_save)
|
BitSet32 gpr_caller_save, BitSet32 fpr_caller_save)
|
||||||
{
|
{
|
||||||
const ARM64Reg branch_watch = EncodeRegTo64(reg_a);
|
|
||||||
MOVP2R(branch_watch, &m_branch_watch);
|
|
||||||
LDRB(IndexType::Unsigned, reg_b, branch_watch, Core::BranchWatch::GetOffsetOfRecordingActive());
|
|
||||||
FixupBranch branch_over = CBZ(reg_b);
|
|
||||||
|
|
||||||
FixupBranch branch_in = B();
|
|
||||||
SwitchToFarCode();
|
|
||||||
SetJumpTarget(branch_in);
|
|
||||||
|
|
||||||
const ARM64Reg float_emit_tmp = EncodeRegTo64(reg_b);
|
|
||||||
ABI_PushRegisters(gpr_caller_save);
|
ABI_PushRegisters(gpr_caller_save);
|
||||||
m_float_emit.ABI_PushRegisters(fpr_caller_save, float_emit_tmp);
|
m_float_emit.ABI_PushRegisters(fpr_caller_save, ARM64Reg::X8);
|
||||||
ABI_CallFunction(m_ppc_state.msr.IR ? (condition ? &Core::BranchWatch::HitVirtualTrue_fk :
|
ABI_CallFunction(m_ppc_state.msr.IR ? (condition ? &Core::BranchWatch::HitVirtualTrue_fk :
|
||||||
&Core::BranchWatch::HitVirtualFalse_fk) :
|
&Core::BranchWatch::HitVirtualFalse_fk) :
|
||||||
(condition ? &Core::BranchWatch::HitPhysicalTrue_fk :
|
(condition ? &Core::BranchWatch::HitPhysicalTrue_fk :
|
||||||
&Core::BranchWatch::HitPhysicalFalse_fk),
|
&Core::BranchWatch::HitPhysicalFalse_fk),
|
||||||
branch_watch, Core::FakeBranchWatchCollectionKey{origin, destination}, inst.hex);
|
&m_branch_watch, Core::FakeBranchWatchCollectionKey{origin, destination},
|
||||||
m_float_emit.ABI_PopRegisters(fpr_caller_save, float_emit_tmp);
|
inst.hex);
|
||||||
|
m_float_emit.ABI_PopRegisters(fpr_caller_save, ARM64Reg::X8);
|
||||||
ABI_PopRegisters(gpr_caller_save);
|
ABI_PopRegisters(gpr_caller_save);
|
||||||
|
|
||||||
FixupBranch branch_out = B();
|
|
||||||
SwitchToNearCode();
|
|
||||||
SetJumpTarget(branch_out);
|
|
||||||
SetJumpTarget(branch_over);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template void JitArm64::WriteBranchWatch<true>(u32, u32, UGeckoInstruction, ARM64Reg, ARM64Reg,
|
template void JitArm64::WriteBranchWatch<true>(u32, u32, UGeckoInstruction, BitSet32, BitSet32);
|
||||||
BitSet32, BitSet32);
|
template void JitArm64::WriteBranchWatch<false>(u32, u32, UGeckoInstruction, BitSet32, BitSet32);
|
||||||
template void JitArm64::WriteBranchWatch<false>(u32, u32, UGeckoInstruction, ARM64Reg, ARM64Reg,
|
|
||||||
BitSet32, BitSet32);
|
|
||||||
|
|
||||||
void JitArm64::WriteBranchWatchDestInRegister(u32 origin, ARM64Reg destination,
|
void JitArm64::WriteBranchWatchDestInRegister(u32 origin, ARM64Reg destination,
|
||||||
UGeckoInstruction inst, ARM64Reg reg_a,
|
UGeckoInstruction inst, BitSet32 gpr_caller_save,
|
||||||
ARM64Reg reg_b, BitSet32 gpr_caller_save,
|
|
||||||
BitSet32 fpr_caller_save)
|
BitSet32 fpr_caller_save)
|
||||||
{
|
{
|
||||||
const ARM64Reg branch_watch = EncodeRegTo64(reg_a);
|
|
||||||
MOVP2R(branch_watch, &m_branch_watch);
|
|
||||||
LDRB(IndexType::Unsigned, reg_b, branch_watch, Core::BranchWatch::GetOffsetOfRecordingActive());
|
|
||||||
FixupBranch branch_over = CBZ(reg_b);
|
|
||||||
|
|
||||||
FixupBranch branch_in = B();
|
|
||||||
SwitchToFarCode();
|
|
||||||
SetJumpTarget(branch_in);
|
|
||||||
|
|
||||||
const ARM64Reg float_emit_tmp = EncodeRegTo64(reg_b);
|
|
||||||
ABI_PushRegisters(gpr_caller_save);
|
ABI_PushRegisters(gpr_caller_save);
|
||||||
m_float_emit.ABI_PushRegisters(fpr_caller_save, float_emit_tmp);
|
m_float_emit.ABI_PushRegisters(fpr_caller_save, ARM64Reg::X8);
|
||||||
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue :
|
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue :
|
||||||
&Core::BranchWatch::HitPhysicalTrue,
|
&Core::BranchWatch::HitPhysicalTrue,
|
||||||
branch_watch, origin, destination, inst.hex);
|
&m_branch_watch, origin, destination, inst.hex);
|
||||||
m_float_emit.ABI_PopRegisters(fpr_caller_save, float_emit_tmp);
|
m_float_emit.ABI_PopRegisters(fpr_caller_save, ARM64Reg::X8);
|
||||||
ABI_PopRegisters(gpr_caller_save);
|
ABI_PopRegisters(gpr_caller_save);
|
||||||
|
|
||||||
FixupBranch branch_out = B();
|
|
||||||
SwitchToNearCode();
|
|
||||||
SetJumpTarget(branch_out);
|
|
||||||
SetJumpTarget(branch_over);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JitArm64::bx(UGeckoInstruction inst)
|
void JitArm64::bx(UGeckoInstruction inst)
|
||||||
@@ -153,13 +121,12 @@ void JitArm64::bx(UGeckoInstruction inst)
|
|||||||
|
|
||||||
if (!js.isLastInstruction)
|
if (!js.isLastInstruction)
|
||||||
{
|
{
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const auto WB = gpr.GetScopedReg(), WC = gpr.GetScopedReg();
|
BitSet32 gpr_caller_save = gpr.GetCallerSavedUsed();
|
||||||
BitSet32 gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB), DecodeReg(WC)};
|
|
||||||
if (WA != ARM64Reg::INVALID_REG && js.op->skipLRStack)
|
if (WA != ARM64Reg::INVALID_REG && js.op->skipLRStack)
|
||||||
gpr_caller_save[DecodeReg(WA)] = false;
|
gpr_caller_save[DecodeReg(WA)] = false;
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WB, WC, gpr_caller_save,
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, gpr_caller_save,
|
||||||
fpr.GetCallerSavedUsed());
|
fpr.GetCallerSavedUsed());
|
||||||
}
|
}
|
||||||
if (inst.LK && !js.op->skipLRStack)
|
if (inst.LK && !js.op->skipLRStack)
|
||||||
@@ -178,15 +145,14 @@ void JitArm64::bx(UGeckoInstruction inst)
|
|||||||
|
|
||||||
if (js.op->branchIsIdleLoop)
|
if (js.op->branchIsIdleLoop)
|
||||||
{
|
{
|
||||||
|
if (IsBranchWatchEnabled())
|
||||||
|
{
|
||||||
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, {}, {});
|
||||||
|
}
|
||||||
|
|
||||||
if (WA == ARM64Reg::INVALID_REG)
|
if (WA == ARM64Reg::INVALID_REG)
|
||||||
WA = gpr.GetScopedReg();
|
WA = gpr.GetScopedReg();
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
|
||||||
{
|
|
||||||
const auto WB = gpr.GetScopedReg();
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WA, WB, {}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// make idle loops go faster
|
// make idle loops go faster
|
||||||
ARM64Reg XA = EncodeRegTo64(WA);
|
ARM64Reg XA = EncodeRegTo64(WA);
|
||||||
|
|
||||||
@@ -198,12 +164,11 @@ void JitArm64::bx(UGeckoInstruction inst)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const auto WB = gpr.GetScopedReg(), WC = gpr.GetScopedReg();
|
|
||||||
const BitSet32 gpr_caller_save =
|
const BitSet32 gpr_caller_save =
|
||||||
WA != ARM64Reg::INVALID_REG ? BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS : BitSet32{};
|
WA != ARM64Reg::INVALID_REG ? BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS : BitSet32{};
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, WB, WC, gpr_caller_save, {});
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, gpr_caller_save, {});
|
||||||
}
|
}
|
||||||
WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4, WA);
|
WriteExit(js.op->branchTo, inst.LK, js.compilerPC + 4, WA);
|
||||||
}
|
}
|
||||||
@@ -214,14 +179,11 @@ void JitArm64::bcx(UGeckoInstruction inst)
|
|||||||
JITDISABLE(bJITBranchOff);
|
JITDISABLE(bJITBranchOff);
|
||||||
|
|
||||||
auto WA = gpr.GetScopedReg();
|
auto WA = gpr.GetScopedReg();
|
||||||
auto WB = inst.LK || IsDebuggingEnabled() ? gpr.GetScopedReg() :
|
// If WA isn't needed for WriteExit, it can be safely clobbered.
|
||||||
Arm64GPRCache::ScopedARM64Reg(WA.GetReg());
|
auto WB = (inst.LK && !js.op->branchIsIdleLoop) ? gpr.GetScopedReg() :
|
||||||
|
Arm64GPRCache::ScopedARM64Reg(WA.GetReg());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto WC = IsDebuggingEnabled() && inst.LK && !js.op->branchIsIdleLoop ?
|
|
||||||
gpr.GetScopedReg() :
|
|
||||||
Arm64GPRCache::ScopedARM64Reg(ARM64Reg::INVALID_REG);
|
|
||||||
|
|
||||||
FixupBranch pCTRDontBranch;
|
FixupBranch pCTRDontBranch;
|
||||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||||
{
|
{
|
||||||
@@ -252,18 +214,11 @@ void JitArm64::bcx(UGeckoInstruction inst)
|
|||||||
gpr.Flush(FlushMode::MaintainState, WB);
|
gpr.Flush(FlushMode::MaintainState, WB);
|
||||||
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
|
fpr.Flush(FlushMode::MaintainState, ARM64Reg::INVALID_REG);
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
ARM64Reg bw_reg_a, bw_reg_b;
|
BitSet32 gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB)};
|
||||||
// WC is only allocated when WA is needed for WriteExit and cannot be clobbered.
|
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, gpr_caller_save,
|
||||||
if (WC == ARM64Reg::INVALID_REG)
|
fpr.GetCallerSavedUsed());
|
||||||
bw_reg_a = WA, bw_reg_b = WB;
|
|
||||||
else
|
|
||||||
bw_reg_a = WB, bw_reg_b = WC;
|
|
||||||
const BitSet32 gpr_caller_save =
|
|
||||||
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(bw_reg_a), DecodeReg(bw_reg_b)};
|
|
||||||
WriteBranchWatch<true>(js.compilerPC, js.op->branchTo, inst, bw_reg_a, bw_reg_b,
|
|
||||||
gpr_caller_save, fpr.GetCallerSavedUsed());
|
|
||||||
}
|
}
|
||||||
if (js.op->branchIsIdleLoop)
|
if (js.op->branchIsIdleLoop)
|
||||||
{
|
{
|
||||||
@@ -290,17 +245,15 @@ void JitArm64::bcx(UGeckoInstruction inst)
|
|||||||
{
|
{
|
||||||
gpr.Flush(FlushMode::All, WA);
|
gpr.Flush(FlushMode::All, WA);
|
||||||
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, {}, {});
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, {}, {});
|
||||||
}
|
}
|
||||||
WriteExit(js.compilerPC + 4);
|
WriteExit(js.compilerPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
else if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const BitSet32 gpr_caller_save =
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, gpr.GetCallerSavedUsed(),
|
||||||
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WA), DecodeReg(WB)};
|
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, gpr_caller_save,
|
|
||||||
fpr.GetCallerSavedUsed());
|
fpr.GetCallerSavedUsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,14 +291,13 @@ void JitArm64::bcctrx(UGeckoInstruction inst)
|
|||||||
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
|
LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF_SPR(SPR_CTR));
|
||||||
AND(WA, WA, LogicalImm(~0x3, GPRSize::B32));
|
AND(WA, WA, LogicalImm(~0x3, GPRSize::B32));
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const auto WC = gpr.GetScopedReg(), WD = gpr.GetScopedReg();
|
|
||||||
BitSet32 gpr_caller_save = BitSet32{DecodeReg(WA)};
|
BitSet32 gpr_caller_save = BitSet32{DecodeReg(WA)};
|
||||||
if (WB != ARM64Reg::INVALID_REG)
|
if (WB != ARM64Reg::INVALID_REG)
|
||||||
gpr_caller_save[DecodeReg(WB)] = true;
|
gpr_caller_save[DecodeReg(WB)] = true;
|
||||||
gpr_caller_save &= CALLER_SAVED_GPRS;
|
gpr_caller_save &= CALLER_SAVED_GPRS;
|
||||||
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, WC, WD, gpr_caller_save, {});
|
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, gpr_caller_save, {});
|
||||||
}
|
}
|
||||||
WriteExit(WA, inst.LK_3, js.compilerPC + 4, WB);
|
WriteExit(WA, inst.LK_3, js.compilerPC + 4, WB);
|
||||||
}
|
}
|
||||||
@@ -360,18 +312,10 @@ void JitArm64::bclrx(UGeckoInstruction inst)
|
|||||||
|
|
||||||
auto WA = gpr.GetScopedReg();
|
auto WA = gpr.GetScopedReg();
|
||||||
Arm64GPRCache::ScopedARM64Reg WB;
|
Arm64GPRCache::ScopedARM64Reg WB;
|
||||||
if (conditional || inst.LK || IsDebuggingEnabled())
|
if (conditional || inst.LK)
|
||||||
{
|
|
||||||
WB = gpr.GetScopedReg();
|
WB = gpr.GetScopedReg();
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Arm64GPRCache::ScopedARM64Reg WC;
|
|
||||||
if (IsDebuggingEnabled())
|
|
||||||
{
|
|
||||||
WC = gpr.GetScopedReg();
|
|
||||||
}
|
|
||||||
|
|
||||||
FixupBranch pCTRDontBranch;
|
FixupBranch pCTRDontBranch;
|
||||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||||
{
|
{
|
||||||
@@ -404,13 +348,13 @@ void JitArm64::bclrx(UGeckoInstruction inst)
|
|||||||
gpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, WB);
|
gpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, WB);
|
||||||
fpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, ARM64Reg::INVALID_REG);
|
fpr.Flush(conditional ? FlushMode::MaintainState : FlushMode::All, ARM64Reg::INVALID_REG);
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
BitSet32 gpr_caller_save;
|
BitSet32 gpr_caller_save;
|
||||||
BitSet32 fpr_caller_save;
|
BitSet32 fpr_caller_save;
|
||||||
if (conditional)
|
if (conditional)
|
||||||
{
|
{
|
||||||
gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WB), DecodeReg(WC)};
|
gpr_caller_save = gpr.GetCallerSavedUsed();
|
||||||
if (js.op->branchIsIdleLoop)
|
if (js.op->branchIsIdleLoop)
|
||||||
gpr_caller_save[DecodeReg(WA)] = false;
|
gpr_caller_save[DecodeReg(WA)] = false;
|
||||||
fpr_caller_save = fpr.GetCallerSavedUsed();
|
fpr_caller_save = fpr.GetCallerSavedUsed();
|
||||||
@@ -421,8 +365,7 @@ void JitArm64::bclrx(UGeckoInstruction inst)
|
|||||||
js.op->branchIsIdleLoop ? BitSet32{} : BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS;
|
js.op->branchIsIdleLoop ? BitSet32{} : BitSet32{DecodeReg(WA)} & CALLER_SAVED_GPRS;
|
||||||
fpr_caller_save = {};
|
fpr_caller_save = {};
|
||||||
}
|
}
|
||||||
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, WB, WC, gpr_caller_save,
|
WriteBranchWatchDestInRegister(js.compilerPC, WA, inst, gpr_caller_save, fpr_caller_save);
|
||||||
fpr_caller_save);
|
|
||||||
}
|
}
|
||||||
if (js.op->branchIsIdleLoop)
|
if (js.op->branchIsIdleLoop)
|
||||||
{
|
{
|
||||||
@@ -449,17 +392,16 @@ void JitArm64::bclrx(UGeckoInstruction inst)
|
|||||||
{
|
{
|
||||||
gpr.Flush(FlushMode::All, WA);
|
gpr.Flush(FlushMode::All, WA);
|
||||||
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, {}, {});
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, {}, {});
|
||||||
}
|
}
|
||||||
WriteExit(js.compilerPC + 4);
|
WriteExit(js.compilerPC + 4);
|
||||||
}
|
}
|
||||||
else if (IsDebuggingEnabled())
|
else if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const BitSet32 gpr_caller_save =
|
const BitSet32 gpr_caller_save = gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WA)};
|
||||||
gpr.GetCallerSavedUsed() & ~BitSet32{DecodeReg(WA), DecodeReg(WB)};
|
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, gpr_caller_save,
|
||||||
WriteBranchWatch<false>(js.compilerPC, js.compilerPC + 4, inst, WA, WB, gpr_caller_save,
|
|
||||||
fpr.GetCallerSavedUsed());
|
fpr.GetCallerSavedUsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -826,36 +826,21 @@ void JitArm64::dcbx(UGeckoInstruction inst)
|
|||||||
// Load the loop_counter register with the amount of invalidations to execute.
|
// Load the loop_counter register with the amount of invalidations to execute.
|
||||||
ADD(loop_counter, WA, 1);
|
ADD(loop_counter, WA, 1);
|
||||||
|
|
||||||
if (IsDebuggingEnabled())
|
if (IsBranchWatchEnabled())
|
||||||
{
|
{
|
||||||
const ARM64Reg branch_watch = EncodeRegTo64(reg_cycle_count);
|
|
||||||
MOVP2R(branch_watch, &m_branch_watch);
|
|
||||||
LDRB(IndexType::Unsigned, WB, branch_watch, Core::BranchWatch::GetOffsetOfRecordingActive());
|
|
||||||
FixupBranch branch_over = CBZ(WB);
|
|
||||||
|
|
||||||
FixupBranch branch_in = B();
|
|
||||||
SwitchToFarCode();
|
|
||||||
SetJumpTarget(branch_in);
|
|
||||||
|
|
||||||
const BitSet32 gpr_caller_save =
|
const BitSet32 gpr_caller_save =
|
||||||
gpr.GetCallerSavedUsed() &
|
gpr.GetCallerSavedUsed() &
|
||||||
~BitSet32{DecodeReg(WB), DecodeReg(reg_cycle_count), DecodeReg(reg_downcount)};
|
~BitSet32{DecodeReg(WB), DecodeReg(reg_cycle_count), DecodeReg(reg_downcount)};
|
||||||
ABI_PushRegisters(gpr_caller_save);
|
ABI_PushRegisters(gpr_caller_save);
|
||||||
const ARM64Reg float_emit_tmp = EncodeRegTo64(WB);
|
|
||||||
const BitSet32 fpr_caller_save = fpr.GetCallerSavedUsed();
|
const BitSet32 fpr_caller_save = fpr.GetCallerSavedUsed();
|
||||||
m_float_emit.ABI_PushRegisters(fpr_caller_save, float_emit_tmp);
|
m_float_emit.ABI_PushRegisters(fpr_caller_save, ARM64Reg::X8);
|
||||||
const PPCAnalyst::CodeOp& op = js.op[2];
|
const PPCAnalyst::CodeOp& op = js.op[2];
|
||||||
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue_fk_n :
|
ABI_CallFunction(m_ppc_state.msr.IR ? &Core::BranchWatch::HitVirtualTrue_fk_n :
|
||||||
&Core::BranchWatch::HitPhysicalTrue_fk_n,
|
&Core::BranchWatch::HitPhysicalTrue_fk_n,
|
||||||
branch_watch, Core::FakeBranchWatchCollectionKey{op.address, op.branchTo},
|
&m_branch_watch, Core::FakeBranchWatchCollectionKey{op.address, op.branchTo},
|
||||||
op.inst.hex, WA);
|
op.inst.hex, WA);
|
||||||
m_float_emit.ABI_PopRegisters(fpr_caller_save, float_emit_tmp);
|
m_float_emit.ABI_PopRegisters(fpr_caller_save, ARM64Reg::X8);
|
||||||
ABI_PopRegisters(gpr_caller_save);
|
ABI_PopRegisters(gpr_caller_save);
|
||||||
|
|
||||||
FixupBranch branch_out = B();
|
|
||||||
SwitchToNearCode();
|
|
||||||
SetJumpTarget(branch_out);
|
|
||||||
SetJumpTarget(branch_over);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
|
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
|
||||||
#include "Core/PowerPC/JitCommon/JitCache.h"
|
#include "Core/PowerPC/JitCommon/JitCache.h"
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
#include "Core/PowerPC/PPCAnalyst.h"
|
||||||
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
@@ -200,6 +201,11 @@ public:
|
|||||||
|
|
||||||
bool IsProfilingEnabled() const { return m_enable_profiling && m_enable_debugging; }
|
bool IsProfilingEnabled() const { return m_enable_profiling && m_enable_debugging; }
|
||||||
bool IsDebuggingEnabled() const { return m_enable_debugging; }
|
bool IsDebuggingEnabled() const { return m_enable_debugging; }
|
||||||
|
bool IsBranchWatchEnabled() const
|
||||||
|
{
|
||||||
|
auto& branch_watch = m_system.GetPowerPC().GetBranchWatch();
|
||||||
|
return branch_watch.GetRecordingActive();
|
||||||
|
}
|
||||||
|
|
||||||
static const u8* Dispatch(JitBase& jit);
|
static const u8* Dispatch(JitBase& jit);
|
||||||
virtual JitBaseBlockCache* GetBlockCache() = 0;
|
virtual JitBaseBlockCache* GetBlockCache() = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user