#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;
}
if (ImGui::BeginPopup("Options")) {
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
int numColumns = 3;
if (core == CPUCore::JIT_IR) {
numColumns = 4;
}
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
if (core_ != (int)core) {
refresh_ = true;
core_ = (int)core;
}
if (sortSpecs_ && sortSpecs_->SpecsDirty) {
refresh_ = true;
sortSpecs_->SpecsDirty = false;
}
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->ColumnUserID) {
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->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 (core == CPUCore::JIT_IR || core == CPUCore::IR_INTERPRETER) {
ImGui::TableSetupColumn("IR");
}
if (core == CPUCore::JIT_IR || core == CPUCore::JIT) {
ImGui::TableSetupColumn(TARGET);
}
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
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 (core == CPUCore::JIT_IR || core == CPUCore::IR_INTERPRETER) {
ImGui::TableNextColumn();
ImGui::BeginChild("IRPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
for (const auto &line : debugInfo_.irDisasm) {
ImGui::TextUnformatted(line.c_str());
}
ImGui::EndChild();
}
if (core == CPUCore::JIT_IR || core == CPUCore::JIT) {
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();
}