diff --git a/Core/MIPS/JitCommon/JitBlockCache.cpp b/Core/MIPS/JitCommon/JitBlockCache.cpp index 29f12d24cb..b3a09e3b8b 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.cpp +++ b/Core/MIPS/JitCommon/JitBlockCache.cpp @@ -71,7 +71,8 @@ op_agent_t agent; #endif -#define INVALID_EXIT 0xFFFFFFFF +const u32 INVALID_EXIT = 0xFFFFFFFF; +const MIPSOpcode INVALID_ORIGINAL_OP = MIPSOpcode(0x00000001); JitBlockCache::JitBlockCache(MIPSState *mips_, CodeBlock *codeBlock) : mips(mips_), codeBlock_(codeBlock), blocks(0), num_blocks(0) { @@ -328,6 +329,49 @@ void JitBlockCache::UnlinkBlock(int i) } } +std::vector JitBlockCache::SaveAndClearEmuHackOps() +{ + std::vector result; + result.resize(num_blocks); + + for (int block_num = 0; block_num < num_blocks; ++block_num) + { + JitBlock &b = blocks[block_num]; + if (b.invalid) + continue; + + const u32 emuhack = GetEmuHackOpForBlock(block_num).encoding; + result[block_num] = emuhack; + // The goal here is to prevent restoring it if it did not match (in case originalFirstOpcode does match.) + if (Memory::ReadUnchecked_U32(b.originalAddress) != emuhack) + b.originalFirstOpcode = INVALID_ORIGINAL_OP; + else + Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode); + } + + return result; +} + +void JitBlockCache::RestoreSavedEmuHackOps(std::vector saved) +{ + if (num_blocks != (int)saved.size()) + { + ERROR_LOG(JIT, "RestoreSavedEmuHackOps: Wrong saved block size."); + return; + } + + for (int block_num = 0; block_num < num_blocks; ++block_num) + { + const JitBlock &b = blocks[block_num]; + if (b.invalid) + continue; + + // Only if we restored it, write it back. + if (Memory::ReadUnchecked_U32(b.originalAddress) == b.originalFirstOpcode.encoding) + Memory::Write_Opcode_JIT(b.originalAddress, MIPSOpcode(saved[block_num])); + } +} + void JitBlockCache::DestroyBlock(int block_num, bool invalidate) { if (block_num < 0 || block_num >= num_blocks) { diff --git a/Core/MIPS/JitCommon/JitBlockCache.h b/Core/MIPS/JitCommon/JitBlockCache.h index f7937b128f..eb417f09e6 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.h +++ b/Core/MIPS/JitCommon/JitBlockCache.h @@ -115,6 +115,11 @@ public: void InvalidateICache(u32 address, const u32 length); void DestroyBlock(int block_num, bool invalidate); + // No jit operations may be run between these calls. + // Meant to be used to make memory safe for savestates, memcpy, etc. + std::vector SaveAndClearEmuHackOps(); + void RestoreSavedEmuHackOps(std::vector saved); + int GetNumBlocks() const { return num_blocks; } private: diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 2760937bdc..52675015d2 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -75,7 +75,17 @@ namespace SaveState // Gotta do CoreTiming first since we'll restore into it. CoreTiming::DoState(p); - Memory::DoState(p); + // Memory is a bit tricky when jit is enabled, since there's emuhacks in it. + if (MIPSComp::jit && p.mode == p.MODE_WRITE) + { + auto blocks = MIPSComp::jit->GetBlockCache(); + auto saved = blocks->SaveAndClearEmuHackOps(); + Memory::DoState(p); + blocks->RestoreSavedEmuHackOps(saved); + } + else + Memory::DoState(p); + MemoryStick_DoState(p); currentMIPS->DoState(p); HLEDoState(p); @@ -245,8 +255,6 @@ namespace SaveState switch (op.type) { case SAVESTATE_LOAD: - if (MIPSComp::jit) - MIPSComp::jit->ClearCache(); INFO_LOG(COMMON, "Loading state from %s", op.filename.c_str()); result = CChunkFileReader::Load(op.filename, REVISION, PPSSPP_GIT_VERSION, state, &reason); if (result == CChunkFileReader::ERROR_NONE) { @@ -262,8 +270,6 @@ namespace SaveState break; case SAVESTATE_SAVE: - if (MIPSComp::jit) - MIPSComp::jit->ClearCache(); INFO_LOG(COMMON, "Saving state to %s", op.filename.c_str()); result = CChunkFileReader::Save(op.filename, REVISION, PPSSPP_GIT_VERSION, state); if (result == CChunkFileReader::ERROR_NONE) {