Files
ppsspp/Core/MIPS/MIPSTracer.h
T
Henrik RydgårdandClaude Opus 5.5 39e6e7b29b IR JITs: Split the IR interpreter out, and keep IR to it
IRJit is now the base the IR JITs share: the frontend, the table of guest
blocks, and compiling and invalidating them. IRInterpreterJit and
IRNativeJit are the two kinds. Only IRInterpreterJit keeps IR, in its arena,
along with its dispatch loop and per-block profile.

The native JITs no longer keep the IR they compiled from. The debugger and
the --jit-profile report compile a native block's IR again when they want it,
on a copy of the frontend's state, which gives what ran unless the code or
that state has changed since.

IRBlockCache and IRBlock become JitBlockTable and JitBlock: where each block
came from, the page lookup for invalidation, and the block shadow entry (an
arena or native code offset, depending on the JIT). One IRJitDebugInterface
serves both JITs, taking the MIPS code from the table and the IR and host code
from the JIT, in place of the native JIT's own debug interface.

The native backends don't see the table at all. They compile a span of IR for
a block number and start PC, and keep what they need about their own blocks
(start PC, code offset, which block starts where, for linking) next to the
exits they already tracked. The register cache takes the instructions
directly.

The tracer's block index now goes into the IR before the block is stored, so
native blocks get it too. The unused FindByCookie goes. TestJit checks that
the native JIT keeps no IR, and can still show it. The JIT stats overlay no
longer shows IR for the native JITs. MIPSState can't be copied, now that it
owns its JIT.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 12:09:16 +02:00

114 lines
3.2 KiB
C++

// Copyright (c) 2024- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <unordered_map>
#include <vector>
#include <string>
#include "Common/CommonTypes.h"
#include "Core/Opcode.h"
#include "Core/MIPS/IR/IRJit.h"
#include "Common/Log.h"
#include "Common/File/Path.h"
#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;
};
struct TraceBlockStorage {
std::vector<u32> raw_instructions;
u32 cur_index;
u32* cur_data_ptr;
TraceBlockStorage(u32 capacity):
raw_instructions(capacity, 0),
cur_index(0),
cur_data_ptr(raw_instructions.data())
{}
TraceBlockStorage(): raw_instructions(), cur_index(0), cur_data_ptr(nullptr) {}
bool save_block(const u32* instructions, u32 size);
void initialize(u32 capacity);
void clear();
u32 operator[](u32 index) {
return raw_instructions[index];
}
Memory::Opcode read_asm(u32 index) {
return Memory::Opcode(raw_instructions[index]);
}
};
// This system is meant for trace recording.
// A trace here stands for a sequence of instructions and their respective addresses in RAM.
// The register/memory changes (or thread switches) are not included!
// Note: the tracer stores the basic blocks inside, which causes the last block to be dumped as a whole,
// despite the fact that it may not have executed to its end by the time the tracer is stopped.
struct MIPSTracer {
std::vector<TraceBlockInfo> trace_info;
// The trace might be very big, in that case I don't mind losing the oldest entries.
CyclicBuffer<u32> executed_blocks;
std::unordered_map<u64, u32> hash_to_storage_index;
TraceBlockStorage storage;
Path logging_path;
FILE* output;
bool tracing_enabled = false;
int in_storage_capacity = 0x10'0000;
int in_max_trace_size = 0x10'0000;
void start_tracing();
void stop_tracing();
// Records a block about to be compiled. Returns its trace index, for its LogIRBlock.
u32 prepare_block(u32 virt_addr, u32 size);
void set_logging_path(std::string path) {
logging_path = Path(path);
}
std::string get_logging_path() const {
return logging_path.ToString();
}
bool flush_to_file();
void flush_block_to_file(const TraceBlockInfo& block);
void initialize(u32 storage_capacity, u32 max_trace_size);
void clear();
inline void print_stats() const;
MIPSTracer(): trace_info(), executed_blocks(), hash_to_storage_index(), storage(), logging_path() {}
};
extern MIPSTracer mipsTracer;