Permalink
Browse files

arm64jit: Enable breakpoints.

Memory breakpoints not yet really tested.
  • Loading branch information...
unknownbrackets committed May 2, 2018
1 parent 395ac32 commit eb4b59b5301052d3b7cd84057a9a5fa25c37cb82
View
@@ -272,6 +272,8 @@ void Core_ProcessStepping() {
return;
}
// We're not inside jit now, so it's safe to clear the breakpoints.
CBreakPoints::ClearTemporaryBreakPoints();
host->UpdateDisassembly();
host->UpdateMemView();
@@ -488,17 +488,20 @@ u32 CBreakPoints::CheckSkipFirst()
return 0;
}
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
{
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges(bool write) {
std::vector<MemCheck> ranges = memChecks_;
for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it)
{
MemCheck check = *it;
for (const auto &check : memChecks_) {
if (!(check.cond & MEMCHECK_READ) && !write)
continue;
if (!(check.cond & MEMCHECK_WRITE) && write)
continue;
MemCheck copy = check;
// Toggle the cached part of the address.
check.start ^= 0x40000000;
if (check.end != 0)
check.end ^= 0x40000000;
ranges.push_back(check);
copy.start ^= 0x40000000;
if (copy.end != 0)
copy.end ^= 0x40000000;
ranges.push_back(copy);
}
return ranges;
@@ -156,7 +156,7 @@ class CBreakPoints
static u32 CheckSkipFirst();
// Includes uncached addresses.
static const std::vector<MemCheck> GetMemCheckRanges();
static const std::vector<MemCheck> GetMemCheckRanges(bool write);
static const std::vector<MemCheck> GetMemChecks();
static const std::vector<BreakPoint> GetBreakpoints();
@@ -81,14 +81,14 @@ void Arm64Jit::Comp_FPU3op(MIPSOpcode op) {
void Arm64Jit::Comp_FPULS(MIPSOpcode op)
{
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
// Surprisingly, these work fine alraedy.
s32 offset = (s16)(op & 0xFFFF);
int ft = _FT;
MIPSGPReg rs = _RS;
// u32 addr = R(rs) + offset;
// logBlocks = 1;
std::vector<FixupBranch> skips;
switch (op >> 26) {
case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1
@@ -111,6 +111,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op & 0xFFFF);
MIPSGPReg rt = _RT;
MIPSGPReg rs = _RS;
@@ -119,6 +120,7 @@ namespace MIPSComp {
if (!js.inDelaySlot) {
// Optimisation: Combine to single unaligned load/store
bool isLeft = (o == 34 || o == 42);
CheckMemoryBreakpoint(1);
MIPSOpcode nextOp = GetOffsetInstruction(1);
// Find a matching shift in opposite direction with opposite offset.
if (nextOp == (isLeft ? (op.encoding + (4 << 26) - 3) : (op.encoding - (4 << 26) + 3))) {
@@ -255,6 +257,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_ITypeMem(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op & 0xFFFF);
bool load = false;
@@ -202,6 +202,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_SV(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
s32 offset = (signed short)(op & 0xFFFC);
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
@@ -275,6 +276,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_SVQ(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int imm = (signed short)(op&0xFFFC);
int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5);
@@ -28,6 +28,7 @@
#include "Core/Config.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/MemMap.h"
@@ -44,7 +45,7 @@
using namespace Arm64JitConstants;
void DisassembleArm64Print(const u8 *data, int size) {
static void DisassembleArm64Print(const u8 *data, int size) {
std::vector<std::string> lines = DisassembleArm64(data, size);
for (auto s : lines) {
ILOG("%s", s.c_str());
@@ -60,6 +61,33 @@ void DisassembleArm64Print(const u8 *data, int size) {
ILOG("===");*/
}
static u32 JitBreakpoint() {
// Should we skip this breakpoint?
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
return 0;
BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc);
if ((result & BREAK_ACTION_PAUSE) == 0)
return 0;
return 1;
}
static u32 JitMemCheck(u32 pc) {
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
return 0;
// Note: pc may be the delay slot.
const auto op = Memory::Read_Instruction(pc, true);
s32 offset = (s16)(op & 0xFFFF);
if (MIPSGetInfo(op) & IS_VFPU)
offset &= 0xFFFC;
u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset;
CBreakPoints::ExecOpMemCheck(addr, pc);
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1;
}
namespace MIPSComp
{
using namespace Arm64Gen;
@@ -80,6 +108,10 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f
GenerateFixedCode(jo);
js.startDefaultPrefix = mips_->HasDefaultPrefix();
js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3];
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
CBreakPoints::SetSkipFirst(0);
}
Arm64Jit::~Arm64Jit() {
@@ -99,6 +131,10 @@ void Arm64Jit::DoState(PointerWrap &p) {
}
// Note: we can't update the currentRoundingFunc here because fcr31 wasn't loaded yet.
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
CBreakPoints::SetSkipFirst(0);
}
void Arm64Jit::UpdateFCR31() {
@@ -151,12 +187,16 @@ void Arm64Jit::EatInstruction(MIPSOpcode op) {
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
}
CheckJitBreakpoint(GetCompilerPC() + 4, 0);
js.numInstructions++;
js.compilerPC += 4;
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
}
void Arm64Jit::CompileDelaySlot(int flags) {
// Need to offset the downcount which was already incremented for the branch + delay slot.
CheckJitBreakpoint(GetCompilerPC() + 4, -2);
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
// delay slot, we're screwed.
@@ -285,8 +325,10 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
js.numInstructions = 0;
while (js.compiling) {
gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages
// Jit breakpoints are quite fast, so let's do them in release too.
CheckJitBreakpoint(GetCompilerPC(), 0);
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
MIPSCompileOp(inst, this);
@@ -473,7 +515,18 @@ void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op)
return;
}
if (entry->flags & REPFLAG_DISABLED) {
u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC());
bool disabled = (entry->flags & REPFLAG_DISABLED) != 0;
if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) {
// We don't need to disable hooks, the code will still run.
if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) {
// Any breakpoint at the func entry was already tripped, so we can still run the replacement.
// That's a common case - just to see how often the replacement hits.
disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32));
}
}
if (disabled) {
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
} else if (entry->jitReplaceFunc) {
MIPSReplaceFunc repl = entry->jitReplaceFunc;
@@ -646,6 +699,60 @@ void Arm64Jit::WriteSyscallExit() {
B((const void *)dispatcherCheckCoreState);
}
bool Arm64Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) {
if (CBreakPoints::IsAddressBreakPoint(addr)) {
MRS(FLAGTEMPREG, FIELD_NZCV);
FlushAll();
MOVI2R(SCRATCH1, GetCompilerPC());
MovToPC(SCRATCH1);
RestoreRoundingMode();
QuickCallFunction(SCRATCH1_64, &JitBreakpoint);
// If 0, the conditional breakpoint wasn't taken.
CMPI2R(W0, 0);
FixupBranch skip = B(CC_EQ);
WriteDownCount(downcountOffset);
ApplyRoundingMode();
B((const void *)dispatcherCheckCoreState);
SetJumpTarget(skip);
ApplyRoundingMode();
_MSR(FIELD_NZCV, FLAGTEMPREG);
return true;
}
return false;
}
bool Arm64Jit::CheckMemoryBreakpoint(int instructionOffset) {
if (CBreakPoints::HasMemChecks()) {
int off = instructionOffset + (js.inDelaySlot ? 1 : 0);
MRS(FLAGTEMPREG, FIELD_NZCV);
FlushAll();
RestoreRoundingMode();
MOVI2R(W0, GetCompilerPC());
MovToPC(W0);
if (off != 0)
ADDI2R(W0, W0, off * 4);
QuickCallFunction(SCRATCH2_64, &JitMemCheck);
// If 0, the breakpoint wasn't tripped.
CMPI2R(W0, 0);
FixupBranch skip = B(CC_EQ);
WriteDownCount(-1 - off);
ApplyRoundingMode();
B((const void *)dispatcherCheckCoreState);
SetJumpTarget(skip);
ApplyRoundingMode();
_MSR(FIELD_NZCV, FLAGTEMPREG);
return true;
}
return false;
}
void Arm64Jit::Comp_DoNothing(MIPSOpcode op) { }
MIPSOpcode Arm64Jit::GetOriginalOp(MIPSOpcode op) {
@@ -214,6 +214,8 @@ class Arm64Jit : public Arm64Gen::ARM64CodeBlock, public JitInterface, public MI
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInR(Arm64Gen::ARM64Reg Reg);
void WriteSyscallExit();
bool CheckJitBreakpoint(u32 addr, int downcountOffset);
bool CheckMemoryBreakpoint(int instructionOffset = 0);
// Utility compilation functions
void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely);
@@ -376,17 +376,10 @@ void JitSafeMem::MemCheckImm(MemoryOpType type)
void JitSafeMem::MemCheckAsm(MemoryOpType type)
{
const auto memchecks = CBreakPoints::GetMemCheckRanges();
bool possible = false;
const auto memchecks = CBreakPoints::GetMemCheckRanges(type == MEM_WRITE);
bool possible = !memchecks.empty();
for (auto it = memchecks.begin(), end = memchecks.end(); it != end; ++it)
{
if (!(it->cond & MEMCHECK_READ) && type == MEM_READ)
continue;
if (!(it->cond & MEMCHECK_WRITE) && type == MEM_WRITE)
continue;
possible = true;
FixupBranch skipNext, skipNextRange;
if (it->end != 0)
{
@@ -809,7 +809,6 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC)
if (_bDebug && GetUIState() == UISTATE_INGAME && PSP_IsInited())
{
Core_WaitInactive(TEMP_BREAKPOINT_WAIT_MS);
CBreakPoints::ClearTemporaryBreakPoints();
breakpointList->reloadBreakpoints();
threadList->reloadThreads();
stackTraceView->loadStackTrace();
@@ -913,4 +912,4 @@ void CDisasm::UpdateDialog(bool _bComplete)
// redraw. all others are updated manually
InvalidateRect (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), NULL, TRUE);
UpdateWindow (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW));
}
}

0 comments on commit eb4b59b

Please sign in to comment.