Plumb through the PC value for syscalls, so we can get better diagnostics for unresolved ones.

This commit is contained in:
Henrik Rydgård committed 2026-08-15 19:14:13 +02:00
1 parent ae14ebb6ac
commit 67ddf899ba
23 files changed
+208 -89

No files matched your search

+39 -22
View File
@@ -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
View File
@@ -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);
+2 -2
View File
@@ -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();
+14 -11
View File
@@ -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();
+13 -8
View File
@@ -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();
+23 -6
View File
@@ -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();
+1
View File
@@ -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;
+1 -1
View File
@@ -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;
+8 -1
View File
@@ -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);
+1
View File
@@ -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" },
+2 -1
View File
@@ -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,
+16 -2
View File
@@ -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).
+1
View File
@@ -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);
+2 -1
View File
@@ -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
+1 -1
View File
@@ -408,4 +408,4 @@ LoongArch64Reg LoongArch64JitBackend::NormalizeR(IRReg rs, IRReg rd, LoongArch64
}
}
} // namespace MIPSComp
} // namespace MIPSComp
+5 -4
View File
@@ -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;
+13 -6
View File
@@ -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
+11 -5
View File
@@ -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();
+20 -4
View File
@@ -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();
+4
View File
@@ -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
+2 -1
View File
@@ -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) {