mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-16 16:00: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>
258 lines
7.2 KiB
C++
258 lines
7.2 KiB
C++
#include "ppsspp_config.h"
|
|
|
|
#include "UI/ImDebugger/ImJitViewer.h"
|
|
#include "UI/ImDebugger/ImDebugger.h"
|
|
#include "Core/Config.h"
|
|
|
|
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
|
#include "Core/MIPS/JitCommon/JitState.h"
|
|
#include "Core/MIPS/IR/IRJit.h"
|
|
|
|
void ImJitViewerWindow::GoToBlockAtAddr(u32 addr) {
|
|
for (auto &block : blockList_) {
|
|
if (addr >= block.addr && addr < block.addr + (u32)block.sizeInBytes) {
|
|
curBlockNum_ = block.blockNum;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImJitViewerWindow::Draw(ImConfig &cfg, ImControl &control) {
|
|
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("JitViewer", &cfg.jitViewerOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const CPUCore core = (CPUCore)g_Config.iCpuCore;
|
|
|
|
if (core == CPUCore::INTERPRETER) {
|
|
ImGui::TextUnformatted("JIT Viewer is only available with JIT or IR-based CPU cores.");
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Every native JIT except ARM32's is IR-based.
|
|
#if PPSSPP_ARCH(ARM)
|
|
const bool hasIR = core == CPUCore::IR_INTERPRETER;
|
|
#else
|
|
const bool hasIR = true;
|
|
#endif
|
|
const bool hasTarget = core == CPUCore::JIT || core == CPUCore::JIT_IR;
|
|
|
|
// Options menu
|
|
if (ImGui::BeginPopup("Options")) {
|
|
// ImGui::Checkbox("Auto-scroll", &AutoScroll);
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
#if PPSSPP_ARCH(AMD64)
|
|
const char *TARGET = "X86-64";
|
|
#elif PPSSPP_ARCH(X86)
|
|
const char *TARGET = "X86";
|
|
#elif PPSSPP_ARCH(ARM64)
|
|
|
|
const char *TARGET = "ARM64";
|
|
#elif PPSSPP_ARCH(ARM)
|
|
const char *TARGET = "ARM32";
|
|
#else
|
|
const char *TARGET = "TARGET";
|
|
#endif
|
|
|
|
// Three or four columns: One table for blocks that can be sorted in various ways,
|
|
// then the remaining are either MIPS/IR/TARGET or MIPS/TARGET or MIPS/IR depending on the JIT type.
|
|
int numColumns = 3;
|
|
if (hasIR && hasTarget) {
|
|
numColumns = 4;
|
|
}
|
|
|
|
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
|
|
|
|
if (core_ != (int)core) {
|
|
// Core changed, need to refresh.
|
|
refresh_ = true;
|
|
core_ = (int)core;
|
|
}
|
|
|
|
if (sortSpecs_ && sortSpecs_->SpecsDirty) {
|
|
refresh_ = true;
|
|
sortSpecs_->SpecsDirty = false;
|
|
}
|
|
|
|
// If the CPU moved, and we're not currently running, update the cached blocklist.
|
|
const CoreState coreStateCached = coreState;
|
|
if (coreStateCached == CORE_STEPPING_CPU || coreStateCached == CORE_NEXTFRAME || refresh_) {
|
|
const int cpuStepCount = Core_GetSteppingCounter();
|
|
if (lastCpuStepCount_ != cpuStepCount || refresh_) {
|
|
refresh_ = false;
|
|
lastCpuStepCount_ = cpuStepCount;
|
|
blockList_.clear();
|
|
curBlockNum_ = -1;
|
|
const int blockCount = blockCacheDebug->GetNumBlocks();
|
|
blockList_.reserve(blockCount);
|
|
INFO_LOG(Log::Debugger, "Updating JIT block list... %d blocks", blockCacheDebug->GetNumBlocks());
|
|
for (int i = 0; i < blockCount; i++) {
|
|
if (!blockCacheDebug->IsValidBlock(i)) {
|
|
continue;
|
|
}
|
|
JitBlockMeta meta = blockCacheDebug->GetBlockMeta(i);
|
|
if (!meta.valid) {
|
|
continue;
|
|
}
|
|
CachedBlock cb;
|
|
cb.addr = meta.addr;
|
|
cb.sizeInBytes = meta.sizeInBytes;
|
|
cb.blockNum = i;
|
|
#ifdef IR_PROFILING
|
|
JitBlockProfileStats stats = blockCacheDebug->GetBlockProfileStats(i);
|
|
cb.profileStats = stats;
|
|
#endif
|
|
blockList_.push_back(cb);
|
|
}
|
|
|
|
std::sort(blockList_.begin(), blockList_.end(), [this](const CachedBlock &a, const CachedBlock &b) {
|
|
if (!sortSpecs_ || sortSpecs_->SpecsCount <= 0) {
|
|
return a.addr < b.addr;
|
|
}
|
|
const ImGuiTableColumnSortSpecs *spec = &sortSpecs_->Specs[0];
|
|
int delta = 0;
|
|
switch (spec ? spec->ColumnUserID : 0) {
|
|
case 0: delta = (int)a.blockNum - (int)b.blockNum; break;
|
|
case 1: delta = (int)a.addr - (int)b.addr; break;
|
|
case 2: delta = a.sizeInBytes - b.sizeInBytes; break;
|
|
#ifdef IR_PROFILING
|
|
case 3: delta = a.profileStats.executions - b.profileStats.executions; break;
|
|
case 4: delta = a.profileStats.totalNanos - b.profileStats.totalNanos; break;
|
|
#endif
|
|
}
|
|
if (delta == 0) {
|
|
return a.addr < b.addr;
|
|
}
|
|
if (!spec || spec->SortDirection == ImGuiSortDirection_Ascending) {
|
|
return delta < 0;
|
|
} else {
|
|
return delta > 0;
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
ImGui::Text("Pause to update block list");
|
|
}
|
|
|
|
if (ImGui::BeginTable("columns", numColumns, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Blocks");
|
|
ImGui::TableSetupColumn("MIPS");
|
|
if (hasIR) {
|
|
ImGui::TableSetupColumn("IR");
|
|
}
|
|
if (hasTarget) {
|
|
ImGui::TableSetupColumn(TARGET);
|
|
}
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
|
|
// For separate scrolling.
|
|
ImGui::BeginChild("LeftPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
#ifdef IR_PROFILING
|
|
const int numColumns = (core == CPUCore::IR_INTERPRETER) ? 5 : 3;
|
|
#else
|
|
const int numColumns = 3;
|
|
#endif
|
|
if (ImGui::BeginTable("blocks", numColumns, ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Num", 0, 0, 0);
|
|
ImGui::TableSetupColumn("Addr", 0, 0, 1);
|
|
ImGui::TableSetupColumn("Size", 0, 0, 2);
|
|
#ifdef IR_PROFILING
|
|
if (numColumns == 5) {
|
|
ImGui::TableSetupColumn("Exec", 0, 0, 3);
|
|
ImGui::TableSetupColumn("Ns", 0, 0, 4);
|
|
}
|
|
#endif
|
|
ImGui::TableHeadersRow();
|
|
sortSpecs_ = ImGui::TableGetSortSpecs();
|
|
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin((int)blockList_.size());
|
|
|
|
while (clipper.Step()) {
|
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
|
const auto &block = blockList_[i];
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
|
|
char label[32];
|
|
snprintf(label, sizeof(label), "%d", block.blockNum);
|
|
|
|
if (ImGui::Selectable(label, block.blockNum == curBlockNum_, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
curBlockNum_ = block.blockNum;
|
|
if (curBlockNum_ >= 0 && curBlockNum_ < blockCacheDebug->GetNumBlocks()) {
|
|
debugInfo_ = blockCacheDebug->GetBlockDebugInfo(curBlockNum_);
|
|
}
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", block.addr);
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", block.sizeInBytes);
|
|
|
|
#ifdef IR_PROFILING
|
|
if (numColumns == 5) {
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", block.profileStats.executions);
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%0.3f ms", (double)block.profileStats.totalNanos * 0.000001);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
clipper.End();
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::BeginChild("MIPSPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
|
|
for (const auto &line : debugInfo_.origDisasm) {
|
|
ImGui::TextUnformatted(line.c_str());
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
if (hasIR) {
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::BeginChild("IRPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
// TODO : When we have both target and IR, need a third column.
|
|
for (const auto &line : debugInfo_.irDisasm) {
|
|
ImGui::TextUnformatted(line.c_str());
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
if (hasTarget) {
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::BeginChild("TargetPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
for (const auto &line : debugInfo_.targetDisasm) {
|
|
ImGui::TextUnformatted(line.c_str());
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|