mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
jit-ir: Add initial breakpoint support.
No memory breakpoints yet, and cache isn't cleared yet so these don't work exactly the way you might expect...
This commit is contained in:
parent
5d5f25248f
commit
6fb34d0bee
7 changed files with 88 additions and 18 deletions
|
@ -18,12 +18,12 @@
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
|
#include "Core/Debugger/Breakpoints.h"
|
||||||
|
#include "Core/Debugger/SymbolMap.h"
|
||||||
#include "Core/Reporting.h"
|
#include "Core/Reporting.h"
|
||||||
#include "Core/MemMap.h"
|
|
||||||
|
|
||||||
#include "Core/MIPS/MIPSTables.h"
|
|
||||||
#include "Core/HLE/ReplaceTables.h"
|
#include "Core/HLE/ReplaceTables.h"
|
||||||
|
#include "Core/MemMap.h"
|
||||||
|
#include "Core/MIPS/MIPSTables.h"
|
||||||
#include "Core/MIPS/IR/IRFrontend.h"
|
#include "Core/MIPS/IR/IRFrontend.h"
|
||||||
#include "Core/MIPS/IR/IRRegCache.h"
|
#include "Core/MIPS/IR/IRRegCache.h"
|
||||||
#include "Core/MIPS/IR/IRPassSimplify.h"
|
#include "Core/MIPS/IR/IRPassSimplify.h"
|
||||||
|
@ -37,6 +37,10 @@ IRFrontend::IRFrontend(bool startDefaultPrefix) {
|
||||||
js.startDefaultPrefix = true;
|
js.startDefaultPrefix = true;
|
||||||
js.hasSetRounding = false;
|
js.hasSetRounding = false;
|
||||||
// js.currentRoundingFunc = convertS0ToSCRATCH1[0];
|
// js.currentRoundingFunc = convertS0ToSCRATCH1[0];
|
||||||
|
|
||||||
|
// 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 IRFrontend::DoState(PointerWrap &p) {
|
void IRFrontend::DoState(PointerWrap &p) {
|
||||||
|
@ -51,6 +55,10 @@ void IRFrontend::DoState(PointerWrap &p) {
|
||||||
} else {
|
} else {
|
||||||
js.hasSetRounding = 1;
|
js.hasSetRounding = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 IRFrontend::FlushAll() {
|
void IRFrontend::FlushAll() {
|
||||||
|
@ -83,6 +91,7 @@ void IRFrontend::EatInstruction(MIPSOpcode op) {
|
||||||
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
|
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckBreakpoint(GetCompilerPC() + 4, 0);
|
||||||
js.numInstructions++;
|
js.numInstructions++;
|
||||||
js.compilerPC += 4;
|
js.compilerPC += 4;
|
||||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
|
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
|
||||||
|
@ -90,6 +99,7 @@ void IRFrontend::EatInstruction(MIPSOpcode op) {
|
||||||
|
|
||||||
void IRFrontend::CompileDelaySlot() {
|
void IRFrontend::CompileDelaySlot() {
|
||||||
js.inDelaySlot = true;
|
js.inDelaySlot = true;
|
||||||
|
CheckBreakpoint(GetCompilerPC() + 4, -2);
|
||||||
MIPSOpcode op = GetOffsetInstruction(1);
|
MIPSOpcode op = GetOffsetInstruction(1);
|
||||||
MIPSCompileOp(op, this);
|
MIPSCompileOp(op, this);
|
||||||
js.inDelaySlot = false;
|
js.inDelaySlot = false;
|
||||||
|
@ -129,7 +139,18 @@ void IRFrontend::Comp_ReplacementFunc(MIPSOpcode op) {
|
||||||
return;
|
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);
|
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
|
||||||
} else if (entry->replaceFunc) {
|
} else if (entry->replaceFunc) {
|
||||||
FlushAll();
|
FlushAll();
|
||||||
|
@ -210,12 +231,16 @@ void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, std::v
|
||||||
js.downcountAmount = 0;
|
js.downcountAmount = 0;
|
||||||
js.curBlock = nullptr;
|
js.curBlock = nullptr;
|
||||||
js.compiling = true;
|
js.compiling = true;
|
||||||
|
js.hadBreakpoints = false;
|
||||||
js.inDelaySlot = false;
|
js.inDelaySlot = false;
|
||||||
js.PrefixStart();
|
js.PrefixStart();
|
||||||
ir.Clear();
|
ir.Clear();
|
||||||
|
|
||||||
js.numInstructions = 0;
|
js.numInstructions = 0;
|
||||||
while (js.compiling) {
|
while (js.compiling) {
|
||||||
|
// Jit breakpoints are quite fast, so let's do them in release too.
|
||||||
|
CheckBreakpoint(GetCompilerPC(), 0);
|
||||||
|
|
||||||
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
|
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
|
||||||
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
|
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
|
||||||
MIPSCompileOp(inst, this);
|
MIPSCompileOp(inst, this);
|
||||||
|
@ -231,7 +256,7 @@ void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, std::v
|
||||||
|
|
||||||
IRWriter simplified;
|
IRWriter simplified;
|
||||||
IRWriter *code = &ir;
|
IRWriter *code = &ir;
|
||||||
if (true) {
|
if (!js.hadBreakpoints) {
|
||||||
static const IRPassFunc passes[] = {
|
static const IRPassFunc passes[] = {
|
||||||
&OptimizeFPMoves,
|
&OptimizeFPMoves,
|
||||||
&PropagateConstants,
|
&PropagateConstants,
|
||||||
|
@ -291,4 +316,21 @@ void IRFrontend::Comp_RunBlock(MIPSOpcode op) {
|
||||||
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
|
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
void IRFrontend::CheckBreakpoint(u32 addr, int downcountOffset) {
|
||||||
|
if (CBreakPoints::IsAddressBreakPoint(addr)) {
|
||||||
|
FlushAll();
|
||||||
|
|
||||||
|
RestoreRoundingMode();
|
||||||
|
ir.Write(IROp::SetPCConst, 0, ir.AddConstant(GetCompilerPC()));
|
||||||
|
int downcountAmount = js.downcountAmount + downcountOffset;
|
||||||
|
ir.Write(IROp::Downcount, 0, downcountAmount & 0xFF, downcountAmount >> 8);
|
||||||
|
// Note that this means downcount can't be metadata on the block.
|
||||||
|
js.downcountAmount = -downcountAmount;
|
||||||
|
ir.Write(IROp::Breakpoint);
|
||||||
|
ApplyRoundingMode();
|
||||||
|
|
||||||
|
js.hadBreakpoints = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
|
@ -107,6 +107,8 @@ private:
|
||||||
void EatInstruction(MIPSOpcode op);
|
void EatInstruction(MIPSOpcode op);
|
||||||
MIPSOpcode GetOffsetInstruction(int offset);
|
MIPSOpcode GetOffsetInstruction(int offset);
|
||||||
|
|
||||||
|
void CheckBreakpoint(u32 addr, int downcountOffset);
|
||||||
|
|
||||||
// Utility compilation functions
|
// Utility compilation functions
|
||||||
void BranchFPFlag(MIPSOpcode op, IRComparison cc, bool likely);
|
void BranchFPFlag(MIPSOpcode op, IRComparison cc, bool likely);
|
||||||
void BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely);
|
void BranchVFPUFlag(MIPSOpcode op, IRComparison cc, bool likely);
|
||||||
|
@ -136,4 +138,4 @@ private:
|
||||||
int logBlocks;
|
int logBlocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -156,6 +156,7 @@ static const IRMeta irMeta[] = {
|
||||||
{ IROp::SetPC, "SetPC", "_G" },
|
{ IROp::SetPC, "SetPC", "_G" },
|
||||||
{ IROp::SetPCConst, "SetPC", "_C" },
|
{ IROp::SetPCConst, "SetPC", "_C" },
|
||||||
{ IROp::CallReplacement, "CallRepl", "_C" },
|
{ IROp::CallReplacement, "CallRepl", "_C" },
|
||||||
|
{ IROp::Breakpoint, "Breakpoint", "" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const IRMeta *metaIndex[256];
|
const IRMeta *metaIndex[256];
|
||||||
|
|
|
@ -60,7 +60,7 @@ enum class IROp : u8 {
|
||||||
ShrImm,
|
ShrImm,
|
||||||
SarImm,
|
SarImm,
|
||||||
RorImm,
|
RorImm,
|
||||||
|
|
||||||
Slt,
|
Slt,
|
||||||
SltConst,
|
SltConst,
|
||||||
SltU,
|
SltU,
|
||||||
|
@ -92,7 +92,7 @@ enum class IROp : u8 {
|
||||||
MsubU,
|
MsubU,
|
||||||
Div,
|
Div,
|
||||||
DivU,
|
DivU,
|
||||||
|
|
||||||
// These take a constant from the pool as an offset.
|
// These take a constant from the pool as an offset.
|
||||||
// Loads from a constant address can be represented by using r0.
|
// Loads from a constant address can be represented by using r0.
|
||||||
Load8,
|
Load8,
|
||||||
|
@ -219,6 +219,7 @@ enum class IROp : u8 {
|
||||||
SetPCConst, // hack to make replacement know PC
|
SetPCConst, // hack to make replacement know PC
|
||||||
CallReplacement,
|
CallReplacement,
|
||||||
Break,
|
Break,
|
||||||
|
Breakpoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum IRComparison {
|
enum IRComparison {
|
||||||
|
@ -315,7 +316,7 @@ enum IRFlags {
|
||||||
struct IRMeta {
|
struct IRMeta {
|
||||||
IROp op;
|
IROp op;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char types[4]; // GGG
|
const char types[4]; // GGG
|
||||||
u32 flags;
|
u32 flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,19 @@
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Core/MemMap.h"
|
#include "Core/Core.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
|
#include "Core/Debugger/Breakpoints.h"
|
||||||
#include "Core/HLE/HLE.h"
|
#include "Core/HLE/HLE.h"
|
||||||
#include "Core/HLE/ReplaceTables.h"
|
#include "Core/HLE/ReplaceTables.h"
|
||||||
|
#include "Core/Host.h"
|
||||||
|
#include "Core/MemMap.h"
|
||||||
#include "Core/MIPS/MIPS.h"
|
#include "Core/MIPS/MIPS.h"
|
||||||
#include "Core/MIPS/MIPSTables.h"
|
#include "Core/MIPS/MIPSTables.h"
|
||||||
#include "Core/MIPS/MIPSVFPUUtils.h"
|
#include "Core/MIPS/MIPSVFPUUtils.h"
|
||||||
#include "Core/MIPS/IR/IRInst.h"
|
#include "Core/MIPS/IR/IRInst.h"
|
||||||
#include "Core/MIPS/IR/IRInterpreter.h"
|
#include "Core/MIPS/IR/IRInterpreter.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
#include "Core/CoreTiming.h"
|
|
||||||
|
|
||||||
alignas(16) float vec4InitValues[8][4] = {
|
alignas(16) float vec4InitValues[8][4] = {
|
||||||
{ 0.0f, 0.0f, 0.0f, 0.0f },
|
{ 0.0f, 0.0f, 0.0f, 0.0f },
|
||||||
|
@ -29,6 +32,21 @@ alignas(16) float vec4InitValues[8][4] = {
|
||||||
{ 0.0f, 0.0f, 0.0f, 1.0f },
|
{ 0.0f, 0.0f, 0.0f, 1.0f },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u32 RunBreakpoint(u32 pc) {
|
||||||
|
// Should we skip this breakpoint?
|
||||||
|
if (CBreakPoints::CheckSkipFirst() == pc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto cond = CBreakPoints::GetBreakPointCondition(pc);
|
||||||
|
if (cond && !cond->Evaluate())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Core_EnableStepping(true);
|
||||||
|
host->SetDebugMode(true);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
u32 IRInterpret(MIPSState *mips, const IRInst *inst, const u32 *constPool, int count) {
|
u32 IRInterpret(MIPSState *mips, const IRInst *inst, const u32 *constPool, int count) {
|
||||||
const IRInst *end = inst + count;
|
const IRInst *end = inst + count;
|
||||||
while (inst != end) {
|
while (inst != end) {
|
||||||
|
@ -783,6 +801,14 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const u32 *constPool, int c
|
||||||
memcpy(&mips->vfpuCtrl[inst->dest], &mips->f[inst->src1], 4);
|
memcpy(&mips->vfpuCtrl[inst->dest], &mips->f[inst->src1], 4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IROp::Breakpoint:
|
||||||
|
if (RunBreakpoint(mips->pc)) {
|
||||||
|
if (coreState != CORE_RUNNING)
|
||||||
|
CoreTiming::ForceCheck();
|
||||||
|
return mips->pc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Crash();
|
Crash();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,23 +20,20 @@
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/StringUtils.h"
|
#include "Common/StringUtils.h"
|
||||||
|
|
||||||
#include "Core/Reporting.h"
|
|
||||||
#include "Core/Config.h"
|
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/Debugger/SymbolMap.h"
|
#include "Core/HLE/sceKernelMemory.h"
|
||||||
#include "Core/MemMap.h"
|
#include "Core/MemMap.h"
|
||||||
|
|
||||||
#include "Core/MIPS/MIPS.h"
|
#include "Core/MIPS/MIPS.h"
|
||||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||||
#include "Core/MIPS/MIPSInt.h"
|
#include "Core/MIPS/MIPSInt.h"
|
||||||
#include "Core/MIPS/MIPSTables.h"
|
#include "Core/MIPS/MIPSTables.h"
|
||||||
#include "Core/HLE/sceKernelMemory.h"
|
|
||||||
#include "Core/MIPS/IR/IRRegCache.h"
|
#include "Core/MIPS/IR/IRRegCache.h"
|
||||||
#include "Core/MIPS/IR/IRJit.h"
|
#include "Core/MIPS/IR/IRJit.h"
|
||||||
#include "Core/MIPS/IR/IRPassSimplify.h"
|
#include "Core/MIPS/IR/IRPassSimplify.h"
|
||||||
#include "Core/MIPS/IR/IRInterpreter.h"
|
#include "Core/MIPS/IR/IRInterpreter.h"
|
||||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||||
|
#include "Core/Reporting.h"
|
||||||
|
|
||||||
namespace MIPSComp {
|
namespace MIPSComp {
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ namespace MIPSComp {
|
||||||
int downcountAmount;
|
int downcountAmount;
|
||||||
int numInstructions;
|
int numInstructions;
|
||||||
bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block
|
bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block
|
||||||
|
bool hadBreakpoints;
|
||||||
JitBlock *curBlock;
|
JitBlock *curBlock;
|
||||||
|
|
||||||
u8 hasSetRounding;
|
u8 hasSetRounding;
|
||||||
|
|
Loading…
Add table
Reference in a new issue