x64Analyzer: add movss/movups/movaps support and an instruction class field

The crash handler's instruction analyzer only understood mov/movzx/movsx,
so a fault on an FP or SIMD load/store (used for lwc1/swc1 and lv.q/sv.q
in the x86 JIT) couldn't be classified. Add decoding for movss (scalar,
distinguished from movups by the mandatory 0xF3 prefix), movups, and
movaps, and add an InstructionClass field (GPR/FP/FP_SIMD) so callers
know how to interpret the decoded register operand. Covered by new
unit tests in TestX64Emitter.cpp that emit each instruction and check
the analyzer's output against it.
This commit is contained in:
Henrik Rydgård committed 2026-08-11 22:36:47 +02:00
1 parent 6a873df4ea
commit 2c315be708
3 files changed
+110 -2

No files matched your search

+68
View File
@@ -3,6 +3,7 @@
#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)
#include "Common/CPUDetect.h"
#include "Common/x64Analyzer.h"
#include "Common/x64Emitter.h"
#include "Core/MIPS/x86/RegCacheFPU.h"
#include "Core/MIPS/x86/Jit.h"
@@ -21,6 +22,22 @@ static bool CheckLast(const Gen::XEmitter &emit, const char *comp) {
return true;
}
// Emits nothing itself - runs the x64 crash-handler instruction analyzer (Common/x64Analyzer.cpp)
// on the instruction most recently emitted (from prevStart to the emitter's current position),
// and checks that it decoded it the way we expect.
static bool CheckAnalyze(const Gen::XEmitter &emit, bool expectWrite, InstructionClass expectClass, int expectOperandSize, bool expectZeroExtend = false, bool expectSignExtend = false) {
LSInstructionInfo info{};
bool success = X86AnalyzeMOV(prevStart, info);
EXPECT_TRUE(success);
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.zeroExtend, expectZeroExtend);
EXPECT_EQ_INT(info.signExtend, expectSignExtend);
return true;
}
static void PrintLast(const Gen::XEmitter &emit) {
for (const u8 *p = prevStart; p < emit.GetCodePointer(); p++) {
printf("%02x ", *p);
@@ -47,6 +64,57 @@ bool TestX64Emitter() {
cpu_info.bAVX = prevAVX;
// Exercise Common/x64Analyzer.cpp (used by the JIT crash handler to figure out what a faulting
// load/store instruction was doing) against instructions written by the emitter, for the most
// common memory access instructions.
prevStart = emitter.GetCodePointer();
emitter.MOV(32, R(EAX), MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::GPR, 4));
prevStart = emitter.GetCodePointer();
emitter.MOV(32, MDisp(RCX, 4), R(EAX));
RET(CheckAnalyze(emitter, true, InstructionClass::GPR, 4));
prevStart = emitter.GetCodePointer();
emitter.MOVZX(32, 8, EAX, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::GPR, 1, true, false));
prevStart = emitter.GetCodePointer();
emitter.MOVZX(32, 16, EAX, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::GPR, 2, true, false));
prevStart = emitter.GetCodePointer();
emitter.MOVSX(32, 8, EAX, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::GPR, 1, false, true));
prevStart = emitter.GetCodePointer();
emitter.MOVSX(32, 16, EAX, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::GPR, 2, false, true));
prevStart = emitter.GetCodePointer();
emitter.MOVSS(XMM0, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::FP, 4));
prevStart = emitter.GetCodePointer();
emitter.MOVSS(MDisp(RCX, 4), XMM0);
RET(CheckAnalyze(emitter, true, InstructionClass::FP, 4));
prevStart = emitter.GetCodePointer();
emitter.MOVUPS(XMM0, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::FP_SIMD, 16));
prevStart = emitter.GetCodePointer();
emitter.MOVUPS(MDisp(RCX, 4), XMM0);
RET(CheckAnalyze(emitter, true, InstructionClass::FP_SIMD, 16));
prevStart = emitter.GetCodePointer();
emitter.MOVAPS(XMM0, MDisp(RCX, 4));
RET(CheckAnalyze(emitter, false, InstructionClass::FP_SIMD, 16));
prevStart = emitter.GetCodePointer();
emitter.MOVAPS(MDisp(RCX, 4), XMM0);
RET(CheckAnalyze(emitter, true, InstructionClass::FP_SIMD, 16));
// Just for checking.
PrintLast(emitter);
return true;