From b03bbdbca3f5a4723d213997da019fc8587a750e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 19 Sep 2026 10:02:54 -0600 Subject: [PATCH] MIPSTracer: don't index trace_info with stale block indices flush_to_file() ends with clear(), but tracing stays on and the blocks already compiled keep the index baked into their LogIRBlock instruction. A second flush then indexed an emptied trace_info out of bounds. Skip indices that no longer refer to anything and say so in the log, and give the LogIRBlock placeholder an explicit invalid index so a block that prepare_block failed to record doesn't dump block 0 instead. Co-Authored-By: Claude Opus 5 (1M context) --- Core/MIPS/IR/IRFrontend.cpp | 3 ++- Core/MIPS/MIPSTracer.cpp | 10 ++++++++++ Core/MIPS/MIPSTracer.h | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 08667325f1..d98b49dbc5 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -313,7 +313,8 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, u32 &m instructions.reserve(block_instructions.size() + 2); // +2 for Downcount and LogIRBlock // The first instruction is "Downcount" instructions.push_back(block_instructions.front()); - instructions.push_back({ IROp::LogIRBlock, {0}, 0, 0, 0 }); + // prepare_block() fills in the constant, unless it fails to record the block. + instructions.push_back({ IROp::LogIRBlock, {0}, 0, 0, INVALID_TRACE_INDEX }); std::copy(block_instructions.begin() + 1, block_instructions.end(), std::back_inserter(instructions)); } diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 27e62892a0..a04d00c2ff 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -113,10 +113,20 @@ bool MIPSTracer::flush_to_file() { return false; } auto trace = executed_blocks.get_content(); + int skipped = 0; for (auto index : trace) { + // Blocks compiled before an earlier flush (or before a clear) still carry their old index, + // which no longer refers to anything. Clearing the jit cache gets rid of them for good. + if (index >= trace_info.size()) { + skipped++; + continue; + } auto& block_info = trace_info[index]; flush_block_to_file(block_info); } + if (skipped != 0) { + WARN_LOG(Log::JIT, "Skipped %d stale block(s) in the trace, clear the jit cache to avoid this", skipped); + } INFO_LOG(Log::JIT, "Trace flushed, closing the file..."); fclose(output); diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index d9c873323d..6741929c7c 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -29,6 +29,9 @@ #include "Common/Data/Collections/CyclicBuffer.h" +// Stored in the LogIRBlock instruction's constant field for a block the tracer never recorded. +constexpr u32 INVALID_TRACE_INDEX = 0xFFFFFFFF; + struct TraceBlockInfo { u32 virt_address; u32 storage_index;