mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Plumb through the PC value for syscalls, so we can get better diagnostics for unresolved ones.
This commit is contained in:
1 parent
ae14ebb6ac
commit
67ddf899ba
23 files changed
+208
-89
No files matched your search
+39
-22
@@ -78,7 +78,7 @@ static const HLEFunction *g_stack[MAX_SYSCALL_RECURSION];
|
||||
u32 g_syscallPC;
|
||||
int g_stackSize;
|
||||
|
||||
static int idleOp;
|
||||
static int g_idleOp;
|
||||
|
||||
// Split syscall support. NOTE: This needs to be saved in DoState somehow!
|
||||
static int splitSyscallEatCycles = 0;
|
||||
@@ -266,7 +266,7 @@ void HLEInit() {
|
||||
RegisterAllModules();
|
||||
g_stackSize = 0;
|
||||
delayedResultEvent = CoreTiming::RegisterEvent("HLEDelayedResult", hleDelayResultFinish);
|
||||
idleOp = GetSyscallOp("FakeSysCalls", NID_IDLE);
|
||||
g_idleOp = GetSyscallOp("FakeSysCalls", NID_IDLE);
|
||||
}
|
||||
|
||||
void HLEDoState(PointerWrap &p) {
|
||||
@@ -895,59 +895,68 @@ static void CallSyscallWithoutFlags(const HLEFunction *info) {
|
||||
g_stackSize = 0;
|
||||
}
|
||||
|
||||
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op) {
|
||||
void LogBadSyscallAtPC(u32 pc, bool compilePhase) {
|
||||
const LogLevel level = compilePhase ? LogLevel::LWARNING : LogLevel::LERROR;
|
||||
const char *phase = compilePhase ? "compile" : "run";
|
||||
std::string importModuleName, importingModuleName;
|
||||
u32 nid = 0;
|
||||
if (pc && KernelFindImportByStubAddr(pc, &importModuleName, &nid, &importingModuleName)) {
|
||||
const char *funcName = GetHLEFuncName(importModuleName, nid);
|
||||
GENERIC_LOG(Log::HLE, level, "Unknown syscall (%s) at %08x: unresolved import %s/%08x (%s), called from '%s'", phase, pc, importModuleName.c_str(), nid, funcName ? funcName : "(unknown)", importingModuleName.c_str());
|
||||
} else {
|
||||
char buffer[256];
|
||||
DescribeAddress(currentDebugMIPS, pc, buffer, sizeof(buffer));
|
||||
GENERIC_LOG(Log::HLE, level, "Unknown syscall (%s) at %08x (%s): was unable to determine more information", phase, pc, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
const HLEFunction *GetSyscallFunctionData(MIPSOpcode op, u32 pcForDiagnostics) {
|
||||
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
|
||||
int funcnum = callno & 0xFFF;
|
||||
int modulenum = (callno & 0xFF000) >> 12;
|
||||
if (funcnum == 0xfff) {
|
||||
std::string_view modName = modulenum >= (int)moduleDB.size() ? "(unknown)" : moduleDB[modulenum].name;
|
||||
// This is what a still-unresolved import looks like once written as a syscall opcode -
|
||||
// the original module name/NID aren't recoverable from the opcode itself (see
|
||||
// WriteFuncMissingStub), but the calling address is a stub we may still be tracking.
|
||||
std::string importModuleName, importingModuleName;
|
||||
u32 nid = 0;
|
||||
if (currentMIPS->pc >= 8 && KernelFindImportByStubAddr(currentMIPS->pc - 8, &importModuleName, &nid, &importingModuleName)) {
|
||||
const char *funcName = GetHLEFuncName(importModuleName, nid);
|
||||
ERROR_LOG(Log::HLE, "Unknown syscall: unresolved import %s/%08x (%s), called from '%s'", importModuleName.c_str(), nid, funcName ? funcName : "(unknown)", importingModuleName.c_str());
|
||||
} else {
|
||||
ERROR_LOG(Log::HLE, "Unknown syscall: Module: '%.*s' (module: %d func: %d)", (int)modName.size(), modName.data(), modulenum, funcnum);
|
||||
}
|
||||
return NULL;
|
||||
LogBadSyscallAtPC(pcForDiagnostics, PSP_CoreParameter().cpuCore != CPUCore::INTERPRETER);
|
||||
return nullptr;
|
||||
}
|
||||
if (modulenum >= (int)moduleDB.size()) {
|
||||
ERROR_LOG(Log::HLE, "Syscall had bad module number %d - probably executing garbage", modulenum);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
if (funcnum >= moduleDB[modulenum].numFunctions) {
|
||||
ERROR_LOG(Log::HLE, "Syscall had bad function number %d in module %d - probably executing garbage", funcnum, modulenum);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
return &moduleDB[modulenum].funcTable[funcnum];
|
||||
}
|
||||
|
||||
void *GetQuickSyscallFunc(MIPSOpcode op) {
|
||||
void *GetQuickSyscallFunc(const HLEFunction *info, MIPSOpcode op) {
|
||||
if (g_coreCollectDebugStats)
|
||||
return nullptr;
|
||||
|
||||
const HLEFunction *info = GetSyscallFuncPointer(op);
|
||||
if (!info || !info->func)
|
||||
return nullptr;
|
||||
|
||||
VERBOSE_LOG(Log::HLE, "Compiling syscall to '%s'", info->name);
|
||||
|
||||
// TODO: Do this with a flag?
|
||||
if (op == idleOp)
|
||||
if (op == g_idleOp) {
|
||||
return (void *)info->func;
|
||||
if (info->flags != 0)
|
||||
} else if (info->flags != 0) {
|
||||
return (void *)&CallSyscallWithFlags;
|
||||
return (void *)&CallSyscallWithoutFlags;
|
||||
} else {
|
||||
return (void *)&CallSyscallWithoutFlags;
|
||||
}
|
||||
}
|
||||
|
||||
void hleSetFlipTime(double t) {
|
||||
hleFlipTime = t;
|
||||
}
|
||||
|
||||
void CallSyscall(MIPSOpcode op) {
|
||||
void CallSyscallWithPC(MIPSOpcode op, u32 pc) {
|
||||
PROFILE_THIS_SCOPE("syscall");
|
||||
const bool collectStats = g_coreCollectDebugStats;
|
||||
double start = 0.0;
|
||||
@@ -955,7 +964,7 @@ void CallSyscall(MIPSOpcode op) {
|
||||
start = time_now_d();
|
||||
}
|
||||
|
||||
const HLEFunction *info = GetSyscallFuncPointer(op);
|
||||
const HLEFunction *info = GetSyscallFunctionData(op, pc);
|
||||
if (!info) {
|
||||
// We haven't incremented the stack yet.
|
||||
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
|
||||
@@ -963,7 +972,7 @@ void CallSyscall(MIPSOpcode op) {
|
||||
}
|
||||
|
||||
if (info->func) {
|
||||
if (op == idleOp)
|
||||
if (op == g_idleOp)
|
||||
info->func();
|
||||
else if (info->flags != 0)
|
||||
CallSyscallWithFlags(info);
|
||||
@@ -988,6 +997,14 @@ void CallSyscall(MIPSOpcode op) {
|
||||
}
|
||||
}
|
||||
|
||||
void CallSyscall(MIPSOpcode op) {
|
||||
CallSyscallWithPC(op, 0);
|
||||
}
|
||||
|
||||
void CallSyscallUnresolvedAtPC(u32 pc) {
|
||||
LogBadSyscallAtPC(pc, false);
|
||||
}
|
||||
|
||||
void hlePushFuncDesc(std::string_view module, std::string_view funcName) {
|
||||
const HLEModule *mod = GetHLEModuleByName(module);
|
||||
_dbg_assert_(mod != nullptr);
|
||||
|
||||
+5
-3
@@ -184,14 +184,16 @@ size_t HLEFormatLogArgs(const MIPSState *mips, char *message, size_t sz, const c
|
||||
u32 GetSyscallOp(std::string_view module, u32 nib);
|
||||
bool WriteHLESyscall(std::string_view module, u32 nib, u32 address);
|
||||
void CallSyscall(MIPSOpcode op);
|
||||
void CallSyscallWithPC(MIPSOpcode op, u32 pc); // better diagnostics
|
||||
void CallSyscallUnresolvedAtPC(u32 pc);
|
||||
void WriteFuncStub(u32 stubAddr, u32 symAddr);
|
||||
void WriteFuncMissingStub(u32 stubAddr, u32 nid);
|
||||
|
||||
void HLEReturnFromMipsCall();
|
||||
|
||||
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op);
|
||||
// For jit, takes arg: const HLEFunction *
|
||||
void *GetQuickSyscallFunc(MIPSOpcode op);
|
||||
const HLEFunction *GetSyscallFunctionData(MIPSOpcode op, u32 pcForDiagnostics);
|
||||
// For jit, the returned function takes the arg: const HLEFunction *
|
||||
void *GetQuickSyscallFunc(const HLEFunction *info, MIPSOpcode op);
|
||||
|
||||
void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, const char *reason, const char *formatted_reason);
|
||||
|
||||
|
||||
@@ -780,7 +780,7 @@ void UnexportFuncSymbol(const FuncSymbolExport &func) {
|
||||
}
|
||||
}
|
||||
|
||||
// Used to add detail to the "Unknown syscall" log in HLE.cpp's GetSyscallFuncPointer - a call
|
||||
// Used to add detail to the "Unknown syscall" log in HLE.cpp's GetSyscallFunctionData - a call
|
||||
// through a still-unresolved import ends up as a generic "invalid syscall" opcode that no
|
||||
// longer carries the original module name/NID, but the (fixed, unique) address of the syscall
|
||||
// instruction itself does - it's exactly the stubAddr every pending FuncSymbolImport recorded
|
||||
@@ -793,7 +793,7 @@ bool KernelFindImportByStubAddr(u32 stubAddr, std::string *importModuleName, u32
|
||||
continue;
|
||||
}
|
||||
for (const auto &func : module->importedFuncs) {
|
||||
if (func.stubAddr == stubAddr) {
|
||||
if (Memory::AddressesEqualAfterMask(func.stubAddr, stubAddr)) {
|
||||
*importModuleName = func.moduleName;
|
||||
*nid = func.nid;
|
||||
*importingModuleName = module->GetName();
|
||||
|
||||
@@ -620,17 +620,20 @@ void ArmJit::Comp_Syscall(MIPSOpcode op)
|
||||
QuickCallFunction(R1, (void *)&CallSyscall);
|
||||
#else
|
||||
// Skip the CallSyscall where possible.
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc)
|
||||
{
|
||||
gpr.SetRegImm(R0, (u32)(intptr_t)GetSyscallFuncPointer(op));
|
||||
// Already flushed, so R1 is safe.
|
||||
QuickCallFunction(R1, quickFunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.SetRegImm(R0, op.encoding);
|
||||
QuickCallFunction(R1, (void *)&CallSyscall);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, js.compilerPC);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
gpr.SetRegImm(R0, (uintptr_t)func);
|
||||
// Already flushed, so R1 is safe.
|
||||
QuickCallFunction(R1, quickFunc);
|
||||
} else {
|
||||
gpr.SetRegImm(R0, op.encoding);
|
||||
QuickCallFunction(R1, (void *)&CallSyscall);
|
||||
}
|
||||
} else {
|
||||
gpr.SetRegImm(R0, js.compilerPC);
|
||||
QuickCallFunction(R1, (void *)&CallSyscallUnresolvedAtPC);
|
||||
}
|
||||
#endif
|
||||
ApplyRoundingMode();
|
||||
|
||||
@@ -593,7 +593,6 @@ void Arm64Jit::Comp_JumpReg(MIPSOpcode op)
|
||||
WriteExitDestInR(destReg);
|
||||
js.compiling = false;
|
||||
}
|
||||
|
||||
|
||||
void Arm64Jit::Comp_Syscall(MIPSOpcode op)
|
||||
{
|
||||
@@ -636,14 +635,20 @@ void Arm64Jit::Comp_Syscall(MIPSOpcode op)
|
||||
QuickCallFunction(X1, (void *)&CallSyscall);
|
||||
#else
|
||||
// Skip the CallSyscall where possible.
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc) {
|
||||
MOVI2R(X0, (uintptr_t)GetSyscallFuncPointer(op));
|
||||
// Already flushed, so X1 is safe.
|
||||
QuickCallFunction(X1, quickFunc);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, js.compilerPC);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
MOVI2R(X0, (uintptr_t)func);
|
||||
// Already flushed, so X1 is safe.
|
||||
QuickCallFunction(X1, quickFunc);
|
||||
} else {
|
||||
MOVI2R(W0, op.encoding);
|
||||
QuickCallFunction(X1, (void *)&CallSyscall);
|
||||
}
|
||||
} else {
|
||||
MOVI2R(W0, op.encoding);
|
||||
QuickCallFunction(X1, (void *)&CallSyscall);
|
||||
MOVI2R(W0, js.compilerPC);
|
||||
QuickCallFunction(X1, (void *)&CallSyscallUnresolvedAtPC);
|
||||
}
|
||||
#endif
|
||||
LoadStaticRegisters();
|
||||
|
||||
@@ -219,13 +219,20 @@ void Arm64JitBackend::CompIR_System(IRInst inst) {
|
||||
// Skip the CallSyscall where possible.
|
||||
{
|
||||
MIPSOpcode op(inst.constant);
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc) {
|
||||
MOVP2R(X0, GetSyscallFuncPointer(op));
|
||||
QuickCallFunction(SCRATCH2_64, (const u8 *)quickFunc);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, 0);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
MOVP2R(X0, func);
|
||||
QuickCallFunction(SCRATCH2_64, (const u8 *)quickFunc);
|
||||
} else {
|
||||
MOVI2R(W0, inst.constant);
|
||||
QuickCallFunction(SCRATCH2_64, &CallSyscall);
|
||||
}
|
||||
} else {
|
||||
MOVI2R(W0, inst.constant);
|
||||
QuickCallFunction(SCRATCH2_64, &CallSyscall);
|
||||
// Shouldn't get here.
|
||||
MOVI2R(W0, 0);
|
||||
QuickCallFunction(SCRATCH2_64, &CallSyscallUnresolvedAtPC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -235,6 +242,16 @@ void Arm64JitBackend::CompIR_System(IRInst inst) {
|
||||
// This is always followed by an ExitToPC, where we check coreState.
|
||||
break;
|
||||
|
||||
case IROp::SyscallUnresolved:
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);
|
||||
MOVI2R(W0, inst.constant);
|
||||
QuickCallFunction(SCRATCH2_64, &CallSyscallUnresolvedAtPC);
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
|
||||
LoadStaticRegisters();
|
||||
break;
|
||||
|
||||
case IROp::CallReplacement:
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
|
||||
@@ -65,6 +65,7 @@ static void NoBlockExits() {
|
||||
_assert_msg_(false, "Never exited block, invalid IR?");
|
||||
}
|
||||
|
||||
// TODO: Much of this function should be merged with the same function for the other backends.
|
||||
bool Arm64JitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num) {
|
||||
if (GetSpaceLeft() < 0x800)
|
||||
return false;
|
||||
|
||||
@@ -77,7 +77,7 @@ static int IRReadsFromList(const IRInstMeta &inst, IRReg regs[4], char type) {
|
||||
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && inst.m.types[0] == type)
|
||||
regs[c++] = inst.src3;
|
||||
|
||||
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break)
|
||||
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::SyscallUnresolved ||inst.op == IROp::Break)
|
||||
return -1;
|
||||
if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck)
|
||||
return -1;
|
||||
|
||||
@@ -437,6 +437,8 @@ void IRFrontend::Comp_Syscall(MIPSOpcode op) {
|
||||
js.downcountAmount = 0;
|
||||
|
||||
// If not in a delay slot, we need to update PC.
|
||||
// However we also need the PC for some diagnostics so let's just always do it.
|
||||
// Not exactly a bottleneck.
|
||||
if (!js.inDelaySlot) {
|
||||
ir.Write(IROp::SetPCConst, 0, 0, 0, GetCompilerPC() + 4);
|
||||
}
|
||||
@@ -444,7 +446,12 @@ void IRFrontend::Comp_Syscall(MIPSOpcode op) {
|
||||
FlushAll();
|
||||
|
||||
RestoreRoundingMode();
|
||||
ir.Write(IROp::Syscall, 0, 0, 0, op.encoding);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, js.compilerPC);
|
||||
if (func) {
|
||||
ir.Write(IROp::Syscall, 0, 0, 0, op.encoding);
|
||||
} else {
|
||||
ir.Write(IROp::SyscallUnresolved, 0, 0, 0, js.compilerPC);
|
||||
}
|
||||
ApplyRoundingMode();
|
||||
ir.Write(IROp::ExitToPC);
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ static const IRMeta irMeta[] = {
|
||||
{ IROp::ExitToConstIfLtZ, "ExitIfLtZ", "CG", IRFLAG_EXIT },
|
||||
{ IROp::ExitToReg, "ExitToReg", "_G", IRFLAG_EXIT },
|
||||
{ IROp::Syscall, "Syscall", "_C", IRFLAG_EXIT },
|
||||
{ IROp::SyscallUnresolved, "SyscallUnresolved", "C", IRFLAG_EXIT },
|
||||
{ IROp::Break, "Break", "", IRFLAG_EXIT },
|
||||
{ IROp::SetPC, "SetPC", "_G" },
|
||||
{ IROp::SetPCConst, "SetPC", "_C" },
|
||||
|
||||
@@ -222,7 +222,8 @@ enum class IROp : uint8_t {
|
||||
ExitToConstIfFpFalse,
|
||||
ExitToPC, // Used after a syscall to give us a way to do things before returning.
|
||||
|
||||
Syscall, // puts the address of the syscall instruction in the constant - we need both, but we can use the address to look up the value.
|
||||
Syscall, // Needs the syscall instruction work in the constant.
|
||||
SyscallUnresolved, // Used when the syscall is not resolved at compile time. PC in the constant.
|
||||
SetPC, // hack to make syscall returns work
|
||||
SetPCConst, // hack to make replacement know PC
|
||||
CallReplacement,
|
||||
|
||||
@@ -1203,10 +1203,24 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
case IROp::Syscall:
|
||||
// IROp::SetPC was (hopefully) executed before.
|
||||
{
|
||||
// If we get here, the syscall is valid.
|
||||
MIPSOpcode op(inst->constant);
|
||||
CallSyscall(op);
|
||||
if (coreState != CORE_RUNNING_CPU)
|
||||
if (coreState != CORE_RUNNING_CPU) {
|
||||
CoreTiming::ForceCheck(mips);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case IROp::SyscallUnresolved:
|
||||
{
|
||||
// If we get here, the syscall is invalid.
|
||||
u32 pc = inst->constant;
|
||||
CallSyscallUnresolvedAtPC(pc);
|
||||
if (coreState != CORE_RUNNING_CPU) {
|
||||
// hm, what's this for?
|
||||
CoreTiming::ForceCheck(mips);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1300,7 +1314,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Nop: // TODO: This shouldn't crash, but for now we should not emit nops, so...
|
||||
case IROp::Nop: // Unused, add a break if we start using it to avoid UNREACHABLE.
|
||||
case IROp::Bad:
|
||||
default:
|
||||
// Unimplemented IR op. Bad. We define it as unreachable so the compiler can optimize better (remove the range check).
|
||||
|
||||
@@ -440,6 +440,7 @@ void IRNativeBackend::CompileIRInst(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::Syscall:
|
||||
case IROp::SyscallUnresolved:
|
||||
case IROp::CallReplacement:
|
||||
case IROp::Break:
|
||||
CompIR_System(inst);
|
||||
|
||||
@@ -184,6 +184,7 @@ namespace MIPSInt {
|
||||
}
|
||||
|
||||
void Int_Syscall(MIPSState *mips, MIPSOpcode op) {
|
||||
const u32 syscallPC = mips->pc - 4;
|
||||
// Need to pre-move PC, as CallSyscall may result in a rescheduling!
|
||||
// To do this neater, we'll need a little generated kernel loop that syscall can jump to and then RFI from
|
||||
// but I don't see a need to bother.
|
||||
@@ -193,7 +194,7 @@ namespace MIPSInt {
|
||||
mips->pc += 4;
|
||||
}
|
||||
mips->inDelaySlot = false;
|
||||
CallSyscall(op);
|
||||
CallSyscallWithPC(op, syscallPC);
|
||||
}
|
||||
|
||||
void Int_Sync(MIPSState *mips, MIPSOpcode op) {
|
||||
|
||||
@@ -185,22 +185,38 @@ void LoongArch64JitBackend::CompIR_System(IRInst inst) {
|
||||
// Skip the CallSyscall where possible.
|
||||
{
|
||||
MIPSOpcode op(inst.constant);
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc) {
|
||||
LI(R4, (uintptr_t)GetSyscallFuncPointer(op));
|
||||
QuickCallFunction((const u8 *)quickFunc, SCRATCH2);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, 0);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
LI(R4, func);
|
||||
QuickCallFunction((const u8 *)quickFunc, SCRATCH2);
|
||||
} else {
|
||||
LI(R4, (int32_t)inst.constant);
|
||||
QuickCallFunction(&CallSyscall, SCRATCH2);
|
||||
}
|
||||
} else {
|
||||
LI(R4, (int32_t)inst.constant);
|
||||
QuickCallFunction(&CallSyscall, SCRATCH2);
|
||||
// Shouldn't get here.
|
||||
LI(R4, 0);
|
||||
QuickCallFunction(&CallSyscallUnresolvedAtPC, SCRATCH2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
|
||||
LoadStaticRegisters();
|
||||
// This is always followed by an ExitToPC, where we check coreState.
|
||||
break;
|
||||
|
||||
case IROp::SyscallUnresolved:
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);
|
||||
LI(R4, inst.constant);
|
||||
QuickCallFunction(&CallSyscallUnresolvedAtPC, SCRATCH2);
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
|
||||
LoadStaticRegisters();
|
||||
break;
|
||||
|
||||
case IROp::CallReplacement:
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
@@ -275,4 +291,4 @@ void LoongArch64JitBackend::CompIR_ValidateAddress(IRInst inst) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
} // namespace MIPSComp
|
||||
@@ -408,4 +408,4 @@ LoongArch64Reg LoongArch64JitBackend::NormalizeR(IRReg rs, IRReg rd, LoongArch64
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
} // namespace MIPSComp
|
||||
@@ -1116,18 +1116,19 @@ static void RunUntilDowncountZeroFast(MIPSState *mips) {
|
||||
int cycleCount = 0;
|
||||
// Don't stop in a delay slot!
|
||||
do {
|
||||
if (!Memory::IsValid4AlignedAddress(mips->pc)) {
|
||||
Core_ExecException(mips->pc, mips->pc, ExecExceptionType::JUMP);
|
||||
const u32 pc = mips->pc;
|
||||
if (!Memory::IsValid4AlignedAddress(pc)) {
|
||||
Core_ExecException(pc, pc, ExecExceptionType::JUMP);
|
||||
return;
|
||||
}
|
||||
const MIPSOpcode op = MIPSOpcode(Memory::ReadUnchecked_U32(mips->pc));
|
||||
const MIPSOpcode op = MIPSOpcode(Memory::ReadUnchecked_U32(pc));
|
||||
|
||||
const bool wasInDelaySlot = mips->inDelaySlot;
|
||||
const int cycles = ExecInstruction(mips, op);
|
||||
if (cycles < 0) {
|
||||
// Not a recognized instruction (invalid encoding, or an unimplemented kernel-mode only instruction
|
||||
// with no interpreter implementation, e.g. tge/tlt/teq/).
|
||||
Core_ExecException(mips->pc, mips->pc, ExecExceptionType::ILLEGAL);
|
||||
Core_ExecException(pc, pc, ExecExceptionType::ILLEGAL);
|
||||
return;
|
||||
}
|
||||
cycleCount += cycles;
|
||||
|
||||
@@ -193,13 +193,20 @@ void RiscVJitBackend::CompIR_System(IRInst inst) {
|
||||
// Skip the CallSyscall where possible.
|
||||
{
|
||||
MIPSOpcode op(inst.constant);
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc) {
|
||||
LI(X10, (uintptr_t)GetSyscallFuncPointer(op));
|
||||
QuickCallFunction((const u8 *)quickFunc, SCRATCH2);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, 0);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
LI(X10, (uintptr_t)func);
|
||||
QuickCallFunction((const u8 *)quickFunc, SCRATCH2);
|
||||
} else {
|
||||
LI(X10, (int32_t)inst.constant);
|
||||
QuickCallFunction(&CallSyscall, SCRATCH2);
|
||||
}
|
||||
} else {
|
||||
LI(X10, (int32_t)inst.constant);
|
||||
QuickCallFunction(&CallSyscall, SCRATCH2);
|
||||
// Shouldn't get here.
|
||||
LI(X10, 0);
|
||||
QuickCallFunction(&CallSyscallUnresolvedAtPC, SCRATCH2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -681,11 +681,17 @@ void Jit::Comp_Syscall(MIPSOpcode op)
|
||||
ABI_CallFunctionC(&CallSyscall, op.encoding);
|
||||
#else
|
||||
// Skip the CallSyscall where possible.
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc)
|
||||
ABI_CallFunctionP(quickFunc, (void *)GetSyscallFuncPointer(op));
|
||||
else
|
||||
ABI_CallFunctionC(&CallSyscall, op.encoding);
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, js.compilerPC);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
ABI_CallFunctionP(quickFunc, (void *)func);
|
||||
} else {
|
||||
ABI_CallFunctionC(&CallSyscall, op.encoding);
|
||||
}
|
||||
} else {
|
||||
ABI_CallFunctionC(&CallSyscallUnresolvedAtPC, js.compilerPC);
|
||||
}
|
||||
#endif
|
||||
|
||||
ApplyRoundingMode();
|
||||
|
||||
@@ -211,11 +211,18 @@ void X64JitBackend::CompIR_System(IRInst inst) {
|
||||
// Skip the CallSyscall where possible.
|
||||
{
|
||||
MIPSOpcode op(inst.constant);
|
||||
void *quickFunc = GetQuickSyscallFunc(op);
|
||||
if (quickFunc) {
|
||||
ABI_CallFunctionP((const u8 *)quickFunc, (void *)GetSyscallFuncPointer(op));
|
||||
const HLEFunction *func = GetSyscallFunctionData(op, 0);
|
||||
if (func) {
|
||||
void *quickFunc = GetQuickSyscallFunc(func, op);
|
||||
if (quickFunc) {
|
||||
ABI_CallFunctionP((const u8 *)quickFunc, (void *)func);
|
||||
} else {
|
||||
ABI_CallFunctionC((const u8 *)&CallSyscall, inst.constant);
|
||||
}
|
||||
} else {
|
||||
ABI_CallFunctionC((const u8 *)&CallSyscall, inst.constant);
|
||||
_dbg_assert_(false);
|
||||
// Shouldn't get here, this should be resolved during ->IR compilation.
|
||||
ABI_CallFunctionC((const u8 *)&CallSyscallUnresolvedAtPC, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -225,6 +232,15 @@ void X64JitBackend::CompIR_System(IRInst inst) {
|
||||
// This is always followed by an ExitToPC, where we check coreState.
|
||||
break;
|
||||
|
||||
case IROp::SyscallUnresolved:
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);
|
||||
ABI_CallFunctionC((const u8 *)&CallSyscallUnresolvedAtPC, inst.constant);
|
||||
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
|
||||
LoadStaticRegisters();
|
||||
break;
|
||||
|
||||
case IROp::CallReplacement:
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
|
||||
@@ -296,6 +296,10 @@ inline void MemcpyUnchecked(const u32 to_address, const u32 from_address, const
|
||||
MemcpyUnchecked(GetPointerWriteUnchecked(to_address), from_address, len);
|
||||
}
|
||||
|
||||
inline bool AddressesEqualAfterMask(const u32 address1, const u32 address2) {
|
||||
return (address1 & 0x3FFFFFFF) == (address2 & 0x3FFFFFFF);
|
||||
}
|
||||
|
||||
// Applies to user mode.
|
||||
// Without a length, IsValidAddress is generally semi-meaningless, unless it's about a single byte access. For larger accesses, use IsValid4AlignedAddress
|
||||
// etc when appropriate, or for longer sizes, use IsValidRange or IsValid4AlignedRange for example. Checking aligned-ness helps avoid the problem
|
||||
|
||||
@@ -154,7 +154,8 @@ void DeveloperToolsScreen::CreateGeneralTab(UI::LinearLayout *list) {
|
||||
core->HideChoice(1);
|
||||
core->HideChoice(3);
|
||||
}
|
||||
// TODO: Enable "JIT using IR" on more architectures.
|
||||
// TODO: Enable "JIT using IR" on more architectures. ARM32 needs more testing.
|
||||
// Note also that Loongarch and RISC-V only have a jit-ir backend, and it's used for the JIT option, so the fourth option isn't shown.
|
||||
#if !PPSSPP_ARCH(X86) && !PPSSPP_ARCH(AMD64) && !PPSSPP_ARCH(ARM64)
|
||||
core->HideChoice(3);
|
||||
#endif
|
||||
|
||||
@@ -1603,8 +1603,6 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
return true;
|
||||
} else if (command.equals("showKeyboard") && surfView != null) {
|
||||
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
// No idea what the point of the ApplicationWindowToken is or if it
|
||||
// matters where we get it from...
|
||||
inputMethodManager.showSoftInput(surfView, InputMethodManager.SHOW_IMPLICIT);
|
||||
return true;
|
||||
} else if (command.equals("hideKeyboard") && surfView != null) {
|
||||
|
||||
Reference in new issue
Block a user