mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-17 08:20:30 +02:00
Their IR-based replacements stay. The JIT core now creates the IR JIT on both, so "JIT using IR" is the same as "JIT" on every architecture (ARM32 still uses its legacy JIT for both). The settings no longer offer it, a saved JIT_IR loads as JIT, and the JIT harness and CPUEquivalence fuzzer drop the duplicate run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
138 lines
4.6 KiB
C++
138 lines
4.6 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/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.operandSizeInBytes, 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));
|
|
|
|
// The 8-bit forms, which the JIT emits for a guest sb/lb. These used to fail to decode
|
|
// entirely, so MemFault couldn't skip or ignore a bad byte access the way it can a word one.
|
|
prevStart = emitter.GetCodePointer();
|
|
emitter.MOV(8, MDisp(RCX, 4), R(AL));
|
|
RET(CheckAnalyze(emitter, true, InstructionClass::GPR, 1));
|
|
|
|
prevStart = emitter.GetCodePointer();
|
|
emitter.MOV(8, R(AL), MDisp(RCX, 4));
|
|
RET(CheckAnalyze(emitter, false, InstructionClass::GPR, 1));
|
|
|
|
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
|