mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
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.
130 lines
4.2 KiB
C++
130 lines
4.2 KiB
C++
#include "ppsspp_config.h"
|
|
|
|
#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"
|
|
#include "Core/MIPS/JitCommon/JitState.h"
|
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
|
#include "Core/MIPS/MIPSVFPUUtils.h"
|
|
#include "ext/disarm.h"
|
|
|
|
#include "UnitTest.h"
|
|
|
|
static const u8 *prevStart = NULL;
|
|
|
|
static bool CheckLast(const Gen::XEmitter &emit, const char *comp) {
|
|
auto vec = DisassembleX86(prevStart, (int)(emit.GetCodePointer() - prevStart));
|
|
EXPECT_EQ_STR(vec[0], std::string(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);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
bool TestX64Emitter() {
|
|
using namespace Gen;
|
|
|
|
u32 code[512];
|
|
XEmitter emitter((u8 *)code);
|
|
|
|
bool prevAVX = cpu_info.bAVX;
|
|
cpu_info.bAVX = true;
|
|
|
|
prevStart = emitter.GetCodePointer();
|
|
emitter.VADDSD(XMM0, XMM1, R(XMM7));
|
|
RET(CheckLast(emitter, "vaddsd xmm0, xmm1, xmm7"));
|
|
|
|
prevStart = emitter.GetCodePointer();
|
|
emitter.VMULSD(XMM0, XMM1, R(XMM7));
|
|
RET(CheckLast(emitter, "vmulsd xmm0, xmm1, xmm7"));
|
|
|
|
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;
|
|
}
|
|
|
|
#else
|
|
|
|
bool TestX64Emitter() {
|
|
return true;
|
|
}
|
|
|
|
#endif
|