diff --git a/Common/x64Analyzer.cpp b/Common/x64Analyzer.cpp index 2ad7b09935..06689f86ec 100644 --- a/Common/x64Analyzer.cpp +++ b/Common/x64Analyzer.cpp @@ -28,7 +28,7 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) u8 codeByte2 = 0; //Check for regular prefix - info.operandSize = 4; + info.operandSizeInBytes = 4; info.zeroExtend = false; info.signExtend = false; info.hasImmediate = false; @@ -47,7 +47,7 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) if (*codePtr == 0x66) { - info.operandSize = 2; + info.operandSizeInBytes = 2; codePtr++; } else if (*codePtr == 0x67) @@ -68,7 +68,7 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) rex = *codePtr; if (rex & 8) //REX.W { - info.operandSize = 8; + info.operandSizeInBytes = 8; } codePtr++; } @@ -167,19 +167,19 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) case MOVE_16_32BIT: //move 16 or 32-bit immediate, easiest case for writes { - if (info.operandSize == 2) + if (info.operandSizeInBytes == 2) { info.hasImmediate = true; info.immediate = *(u16*)codePtr; codePtr += 2; } - else if (info.operandSize == 4) + else if (info.operandSizeInBytes == 4) { info.hasImmediate = true; info.immediate = *(u32*)codePtr; codePtr += 4; } - else if (info.operandSize == 8) + else if (info.operandSizeInBytes == 8) { info.zeroExtend = true; info.immediate = *(u32*)codePtr; @@ -213,36 +213,36 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) { case MOVZX_BYTE: //movzx on byte info.zeroExtend = true; - info.operandSize = 1; + info.operandSizeInBytes = 1; break; case MOVZX_SHORT: //movzx on short info.zeroExtend = true; - info.operandSize = 2; + info.operandSizeInBytes = 2; break; case MOVSX_BYTE: //movsx on byte info.signExtend = true; - info.operandSize = 1; + info.operandSizeInBytes = 1; break; case MOVSX_SHORT: //movsx on short info.signExtend = true; - info.operandSize = 2; + info.operandSizeInBytes = 2; break; case MOVUPS_MOVSS_FROM_RM: //movups/movss xmm, xmm/m (load) info.instructionClass = hasF3Prefix ? InstructionClass::FP : InstructionClass::FP_SIMD; - info.operandSize = hasF3Prefix ? 4 : 16; + info.operandSizeInBytes = hasF3Prefix ? 4 : 16; break; case MOVUPS_MOVSS_TO_RM: //movups/movss xmm/m, xmm (store) info.instructionClass = hasF3Prefix ? InstructionClass::FP : InstructionClass::FP_SIMD; - info.operandSize = hasF3Prefix ? 4 : 16; + info.operandSizeInBytes = hasF3Prefix ? 4 : 16; info.isMemoryWrite = true; break; case MOVAPS_FROM_RM: //movaps xmm, xmm/m (load) info.instructionClass = InstructionClass::FP_SIMD; - info.operandSize = 16; + info.operandSizeInBytes = 16; break; case MOVAPS_TO_RM: //movaps xmm/m, xmm (store) info.instructionClass = InstructionClass::FP_SIMD; - info.operandSize = 16; + info.operandSizeInBytes = 16; info.isMemoryWrite = true; break; default: @@ -250,9 +250,9 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info) } break; case 0x8a: - if (info.operandSize == 4) + if (info.operandSizeInBytes == 4) { - info.operandSize = 1; + info.operandSizeInBytes = 1; break; } else diff --git a/Common/x64Analyzer.h b/Common/x64Analyzer.h index db964fb7bd..4bcdb7bd78 100644 --- a/Common/x64Analyzer.h +++ b/Common/x64Analyzer.h @@ -27,9 +27,8 @@ enum class InstructionClass { FP_SIMD, // Full vector register (movups, movaps, movdqa, ...) }; -struct LSInstructionInfo -{ - int operandSize; //1, 2, 4, 8 (in bytes, despite the field name suggesting bits) +struct LSInstructionInfo { + int operandSizeInBytes; // 1, 2, 4, 8 (in bytes, despite the field name suggesting bits) int instructionSize; int regOperandReg; int otherReg; @@ -41,6 +40,8 @@ struct LSInstructionInfo u64 immediate; s32 displacement; InstructionClass instructionClass; + + int OperandSizeInBytes() const { return operandSizeInBytes; } }; struct ModRM diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index 6b22d561eb..d0e9411ec9 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -242,10 +242,11 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe PRINT_STDOUT(" -v set the log level to verbose\n"); PRINT_STDOUT(" --loglevel=INTEGER set the log level to specified value\n"); if (mode == CmdLineMode::Application) { - PRINT_STDOUT(" --log=FILE output log to FILE\n"); + PRINT_STDOUT(" --log=FILE output log to FILE\n"); } PRINT_STDOUT(" --state=FILE load state from FILE\n"); + PRINT_STDOUT(" --cpu=CPU use the specified CPU core (interpreter, ir, jit, jit-ir)\n"); PRINT_STDOUT(" -i use the interpreter\n"); PRINT_STDOUT(" -r use IR interpreter\n"); PRINT_STDOUT(" -j use JIT\n"); @@ -290,6 +291,7 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe // Actually might want to reconsider given Android... CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], CmdLineMode mode) { this->mode = mode; + constexpr std::string_view cpuBackendStr = "--cpu="; constexpr std::string_view gpuBackendStr = "--graphics="; constexpr std::string_view configOption = "--config="; constexpr std::string_view controlsOption = "--controlconfig="; @@ -413,7 +415,27 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C } else { // Bad value, report error and exit. PRINT_STDERR("Invalid value for --graphics=: %s", restOfOption.c_str()); - return CommandLineParseResult::Exit; + return CommandLineParseResult::Error; + } + } else if (startsWith(argv[i], cpuBackendStr)) { + const std::string restOfOption = argv[i] + cpuBackendStr.size(); + // Force software rendering off, as picking gles implies HW acceleration. + // We could add more options for software such as "software-gles", + // "software-vulkan" and "software-d3d11", or something similar. + // For now, software rendering force-activates OpenGL. + double glVersionTemp = 0.0f; + if (restOfOption == "interpreter") { + cpuCore = CPUCore::INTERPRETER; + } else if (restOfOption == "jit") { + cpuCore = CPUCore::JIT; + } else if (restOfOption == "jit-ir") { + cpuCore = CPUCore::JIT_IR; + } else if (restOfOption == "ir") { + cpuCore = CPUCore::IR_INTERPRETER; + } else { + // Bad value, report error and exit. + PRINT_STDERR("Invalid value for --cpu=: %s", restOfOption.c_str()); + return CommandLineParseResult::Error; } } else if (startsWith(argv[i], configOption)) { configFilename = std::string(argv[i] + configOption.size()); @@ -429,10 +451,11 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C continue; } else { PRINT_STDERR("Error: --ignore requires an argument.\n"); - return CommandLineParseResult::Exit; + return CommandLineParseResult::Error; } - } else { - // Report unknown argument later once this is complete. + } else if (startsWith(argv[i], "--")) { + PRINT_STDERR("Error: Unknown parameter: %s\n", argv[i]); + return CommandLineParseResult::Error; } // To the next argument. i++; diff --git a/Core/Core.cpp b/Core/Core.cpp index c6c7d861e3..67133ecb60 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -665,13 +665,25 @@ static std::string ModuleAddressSuffix(u32 address) { void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo) { // In jit, we only flush PC when bIgnoreBadMemAccess is off. - char pcDetails[128]; pcDetails[0] = 0; - if ((CPUCore)g_Config.iCpuCore == CPUCore::INTERPRETER) { - snprintf(pcDetails, sizeof(pcDetails), " PC %08x%s RA %08x%s", - pc, ModuleAddressSuffix(pc).c_str(), + switch ((CPUCore)g_Config.iCpuCore) { + case CPUCore::INTERPRETER: + snprintf(pcDetails, sizeof(pcDetails), "Interpreter: PC %08x%s RA %08x%s", + currentMIPS->pc, ModuleAddressSuffix(currentMIPS->pc).c_str(), currentMIPS->r[MIPS_REG_RA], ModuleAddressSuffix(currentMIPS->r[MIPS_REG_RA]).c_str()); + break; + case CPUCore::JIT: + snprintf(pcDetails, sizeof(pcDetails), "JIT: (PC approximate)=%08x%s", pc, ModuleAddressSuffix(pc).c_str()); + break; + case CPUCore::JIT_IR: + snprintf(pcDetails, sizeof(pcDetails), "JIT_IR: (PC approximate)=%08x%s", pc, ModuleAddressSuffix(pc).c_str()); + break; + case CPUCore::IR_INTERPRETER: + snprintf(pcDetails, sizeof(pcDetails), "IR_INTERPRETER: (PC approximate)=%08x%s", pc, ModuleAddressSuffix(pc).c_str()); + break; + default: + break; } const std::string addressSuffix = ModuleAddressSuffix(address); @@ -691,14 +703,14 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy const char *desc = MemoryExceptionTypeAsString(type); char msg[512]; - snprintf(msg, sizeof(msg), "%s: SIGSEGV pc=%08x%s (size %08x) %sHost:%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, STR_VIEW(additionalInfo)); + snprintf(msg, sizeof(msg), "%s: SIGSEGV at %08x%s (size: %d bytes) %s\nHost:%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, STR_VIEW(additionalInfo)); if (action == ExceptionAction::Ignore) { Core_SendDebugOutput(LogLevel::LWARNING, msg); return; } const std::string stackTrace = FormatStackTrace(WalkCurrentStack(-1)); // Do the most detailed logging we can. - Core_SendDebugOutput(LogLevel::LERROR, StringFromFormat("%sCall stack:\n%s", msg, stackTrace.c_str())); + Core_SendDebugOutput(LogLevel::LERROR, StringFromFormat("%sMIPS call stack:\n%s", msg, stackTrace.c_str())); if (action == ExceptionAction::Break) { MIPSExceptionInfo &e = g_exceptionInfo; e = {}; diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index 8a946a2275..21b1a8aef4 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -255,11 +255,11 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { std::string infoString = ""; std::string temp; if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr(codePtr, temp)) { - infoString += temp + "\n"; + infoString += temp + " "; } temp.clear(); if (DisassembleNativeAt(codePtr, instructionSize, &temp)) { - infoString += temp + "\n"; + infoString += "(" + temp + ") "; } // Either bIgnoreBadMemAccess is off, or we failed recovery analysis. @@ -267,7 +267,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { uint32_t approximatePC = currentMIPS->pc; // TODO: Determine access size from the disassembled native instruction. We have some partial info already, // just need to clean it up. - Core_MemoryException(guestAddress, 0, approximatePC, type, infoString); + Core_MemoryException(guestAddress, info.OperandSizeInBytes(), approximatePC, type, infoString); // There's a small chance we can resume from this type of crash. g_lastCrashAddress = codePtr; @@ -320,16 +320,13 @@ std::vector WalkCurrentStack(int threadID) { std::string FormatStackTrace(const std::vector &frames) { std::stringstream str; for (const auto &frame : frames) { - if (frame.pc == 0xFFFFFFFF) { - // Bottom of stack, probably. - continue; - } + const u32 frameEntry = frame.entry == 0xFFFFFFFF ? 0 : frame.entry; std::string desc = g_symbolMap->GetDescription(frame.entry); char moduleDesc[96]; if (DescribeKernelModuleAddress(frame.entry, moduleDesc, sizeof(moduleDesc))) { - str << StringFromFormat("%s [%s] (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), moduleDesc, frame.entry, frame.pc - frame.entry, frame.pc, frame.sp); + str << StringFromFormat("%s [%s] (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), moduleDesc, frameEntry, frame.pc - frameEntry, frame.pc, frame.sp); } else { - str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frame.entry, frame.pc - frame.entry, frame.pc, frame.sp); + str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frameEntry, frame.pc - frameEntry, frame.pc, frame.sp); } } return str.str(); diff --git a/Core/Util/DisArm64.h b/Core/Util/DisArm64.h index feb98ea9bc..c408965240 100644 --- a/Core/Util/DisArm64.h +++ b/Core/Util/DisArm64.h @@ -41,7 +41,7 @@ struct Arm64LSInstructionInfo { int Rn; int Rm; - // TODO: more. + int OperandSizeInBytes() const { return 1 << size; } }; bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t op, Arm64LSInstructionInfo *info); diff --git a/ext/disarm.h b/ext/disarm.h index 901e74cd4f..5384baf4c1 100644 --- a/ext/disarm.h +++ b/ext/disarm.h @@ -45,6 +45,7 @@ struct ArmLSInstructionInfo { int Rm; // TODO: more. + int OperandSizeInBytes() const { return 1 << size; } }; bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info); diff --git a/ext/loongarch-disasm.h b/ext/loongarch-disasm.h index 2cef32edb7..5140d05bd3 100644 --- a/ext/loongarch-disasm.h +++ b/ext/loongarch-disasm.h @@ -2761,6 +2761,8 @@ struct LoongArch64LSInstructionInfo { bool isFPLoadStore; int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit bool isMemoryWrite; + + int OperandSizeInBytes() const { return size; } }; uint32_t la_assemble(Ins *ins); diff --git a/ext/riscv-disas.h b/ext/riscv-disas.h index 1d43fc72c4..53cc475b9c 100644 --- a/ext/riscv-disas.h +++ b/ext/riscv-disas.h @@ -601,6 +601,7 @@ struct RiscVLSInstructionInfo { bool isFPLoadStore; int size; // Size of the access, in bytes (1, 2, 4, or 8). bool isMemoryWrite; + int OperandSizeInBytes() const { return size; } }; bool RiscVAnalyzeLoadStore(uint64_t addr, uint32_t word, RiscVLSInstructionInfo *info); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 74c93fd68a..2658f9ecdc 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -630,7 +630,7 @@ int main(int argc, const char* argv[]) { // Somehow this affects the test execution of pspautotests/tests/gpu/vertices/morph.prx, even though // we actually set the cpu core in CoreParameter below. Probably because we end up using the JIT vs non-JIT // vertex decoder. - g_Config.iCpuCore = 0; + g_Config.iCpuCore = (int)cpuCore; // NOTE: In headless mode, we never save the config. This is just for this run. g_Config.iDumpFileTypes = 0; diff --git a/unittest/TestX64Emitter.cpp b/unittest/TestX64Emitter.cpp index 51d9015495..a846461deb 100644 --- a/unittest/TestX64Emitter.cpp +++ b/unittest/TestX64Emitter.cpp @@ -32,7 +32,7 @@ static bool CheckAnalyze(const Gen::XEmitter &emit, bool expectWrite, Instructio EXPECT_EQ_INT(info.instructionSize, (int)(emit.GetCodePointer() - prevStart)); EXPECT_EQ_INT(info.isMemoryWrite, expectWrite); EXPECT_EQ_INT((int)info.instructionClass, (int)expectClass); - EXPECT_EQ_INT(info.operandSize, expectOperandSize); + EXPECT_EQ_INT(info.operandSizeInBytes, expectOperandSize); EXPECT_EQ_INT(info.zeroExtend, expectZeroExtend); EXPECT_EQ_INT(info.signExtend, expectSignExtend); return true;