diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index f81c7cae6a..a6fe6db123 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -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); diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index a157cf6f80..a0c39650b3 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -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); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index a550bdcb12..da0572445e 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -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(); diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 35fb62d541..f1634164d4 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -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(); diff --git a/Core/MIPS/ARM64/Arm64CompBranch.cpp b/Core/MIPS/ARM64/Arm64CompBranch.cpp index 7cd137a2dd..ccdc4913d8 100644 --- a/Core/MIPS/ARM64/Arm64CompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64CompBranch.cpp @@ -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(); diff --git a/Core/MIPS/ARM64/Arm64IRCompSystem.cpp b/Core/MIPS/ARM64/Arm64IRCompSystem.cpp index 54a759f1bd..b136408c35 100644 --- a/Core/MIPS/ARM64/Arm64IRCompSystem.cpp +++ b/Core/MIPS/ARM64/Arm64IRCompSystem.cpp @@ -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(); diff --git a/Core/MIPS/ARM64/Arm64IRJit.cpp b/Core/MIPS/ARM64/Arm64IRJit.cpp index 2a5d38d5be..6f81f4fdc5 100644 --- a/Core/MIPS/ARM64/Arm64IRJit.cpp +++ b/Core/MIPS/ARM64/Arm64IRJit.cpp @@ -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; diff --git a/Core/MIPS/IR/IRAnalysis.cpp b/Core/MIPS/IR/IRAnalysis.cpp index 8dfc6aab11..8ce6388de8 100644 --- a/Core/MIPS/IR/IRAnalysis.cpp +++ b/Core/MIPS/IR/IRAnalysis.cpp @@ -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; diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index 850db5eef8..ec686f7d5b 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -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); diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 4de2dd01f2..53a00ef8c9 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -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" }, diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index e5395845ca..313f7aa525 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -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, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 8fc7c14294..27465a1616 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -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). diff --git a/Core/MIPS/IR/IRNativeCommon.cpp b/Core/MIPS/IR/IRNativeCommon.cpp index 5dea3fedc1..20641a1c91 100644 --- a/Core/MIPS/IR/IRNativeCommon.cpp +++ b/Core/MIPS/IR/IRNativeCommon.cpp @@ -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); diff --git a/Core/MIPS/Interpreter.cpp b/Core/MIPS/Interpreter.cpp index e468dee158..2aa75a6f71 100644 --- a/Core/MIPS/Interpreter.cpp +++ b/Core/MIPS/Interpreter.cpp @@ -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) { diff --git a/Core/MIPS/LoongArch64/LoongArch64CompSystem.cpp b/Core/MIPS/LoongArch64/LoongArch64CompSystem.cpp index c5721ec700..104acba387 100644 --- a/Core/MIPS/LoongArch64/LoongArch64CompSystem.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64CompSystem.cpp @@ -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 \ No newline at end of file +} // namespace MIPSComp diff --git a/Core/MIPS/LoongArch64/LoongArch64Jit.cpp b/Core/MIPS/LoongArch64/LoongArch64Jit.cpp index 7d7b028645..eb9e56570e 100644 --- a/Core/MIPS/LoongArch64/LoongArch64Jit.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64Jit.cpp @@ -408,4 +408,4 @@ LoongArch64Reg LoongArch64JitBackend::NormalizeR(IRReg rs, IRReg rd, LoongArch64 } } -} // namespace MIPSComp \ No newline at end of file +} // namespace MIPSComp diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index e6ac3ce753..fec07b393a 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -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; diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index d3cbabf1d5..564cb5fea3 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -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 diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 8db2c28883..dff306a95c 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -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(); diff --git a/Core/MIPS/x86/X64IRCompSystem.cpp b/Core/MIPS/x86/X64IRCompSystem.cpp index b137ee176d..0fe7bcb262 100644 --- a/Core/MIPS/x86/X64IRCompSystem.cpp +++ b/Core/MIPS/x86/X64IRCompSystem.cpp @@ -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(); diff --git a/Core/MemMap.h b/Core/MemMap.h index cf554cc16f..2444f0c6e7 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -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 diff --git a/UI/DeveloperToolsScreen.cpp b/UI/DeveloperToolsScreen.cpp index 63321d4d9c..7e5eff89c2 100644 --- a/UI/DeveloperToolsScreen.cpp +++ b/UI/DeveloperToolsScreen.cpp @@ -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 diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 428f318c3d..a3d1c92ccf 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -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) {