RISC-V and LoongArch64 IR JITs: Name the fixed code routines, and profile on Linux

Both now list their routines (GetFixedCodeLabels), so the disassembly
names jump targets and --jit-profile charges routine samples to the
calling block. The profiler samples them on Linux, and its report
disassembles their host code per instruction (RISC-V's in 2- and 4-byte
steps).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-15 15:17:50 +02:00
1 parent 08b398bbd1
commit d4a2cd3e43
7 files changed
+55 -2

No files matched your search

+17
View File
@@ -656,6 +656,23 @@ static std::vector<std::string> DisassembleHost(const u8 *p, int size, std::vect
}
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
lines = DisassembleX86(p, size, &offsets);
#elif PPSSPP_ARCH(RISCV64)
for (int i = 0; i + 2 <= size; ) {
// Compressed instructions are 2 bytes.
const int len = (p[i] & 3) == 3 ? 4 : 2;
if (i + len > size)
break;
std::vector<std::string> one = DisassembleRV64(p + i, len);
lines.push_back(one.empty() ? "?" : one[0]);
offsets.push_back(i);
i += len;
}
#elif PPSSPP_ARCH(LOONGARCH64)
for (int i = 0; i + 4 <= size; i += 4) {
std::vector<std::string> one = DisassembleLA64(p + i, 4);
lines.push_back(one.empty() ? "?" : one[0]);
offsets.push_back(i);
}
#else
lines.push_back("(no per-instruction disassembly for this host)");
offsets.push_back(0);
+7 -1
View File
@@ -117,7 +117,7 @@ std::string DescribeHostAddress(uintptr_t pc, uintptr_t *symbolStart) {
return "";
}
#elif !PPSSPP_PLATFORM(WINDOWS) && (PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(ARM)) && (defined(__APPLE__) || defined(__linux__))
#elif !PPSSPP_PLATFORM(WINDOWS) && (((PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(ARM)) && (defined(__APPLE__) || defined(__linux__))) || ((PPSSPP_ARCH(RISCV64) || PPSSPP_ARCH(LOONGARCH64)) && defined(__linux__)))
static pthread_t g_target;
static struct sigaction g_oldAction;
@@ -140,6 +140,12 @@ static void SignalHandler(int, siginfo_t *, void *context) {
#elif PPSSPP_ARCH(ARM)
pc = (uintptr_t)uc->uc_mcontext.arm_pc;
caller = (uintptr_t)uc->uc_mcontext.arm_lr;
#elif PPSSPP_ARCH(RISCV64)
pc = (uintptr_t)uc->uc_mcontext.__gregs[REG_PC];
caller = (uintptr_t)uc->uc_mcontext.__gregs[REG_RA];
#elif PPSSPP_ARCH(LOONGARCH64)
pc = (uintptr_t)uc->uc_mcontext.__pc;
caller = (uintptr_t)uc->uc_mcontext.__gregs[1]; // ra
#endif
Record(pc, caller);
}
+13
View File
@@ -246,4 +246,17 @@ void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
EndWrite();
}
void LoongArch64JitBackend::GetFixedCodeLabels(std::vector<IRNativeFixedCodeLabel> &labels) const {
labels.push_back({ outerLoop_, "outerLoop", false });
labels.push_back({ outerLoopPCInSCRATCH1_, "outerLoopPCInSCRATCH1", false });
labels.push_back({ dispatcherCheckCoreState_, "dispatcherCheckCoreState", false });
labels.push_back({ dispatcherPCInSCRATCH1_, "dispatcherPCInSCRATCH1", false });
labels.push_back({ dispatcherNoCheck_, "dispatcherNoCheck", false });
labels.push_back({ applyRoundingMode_, "applyRoundingMode", true });
labels.push_back({ saveStaticRegisters_, "saveStaticRegisters", true });
labels.push_back({ loadStaticRegisters_, "loadStaticRegisters", true });
labels.push_back({ vfpuDot_, "vfpuDot", true });
labels.push_back({ vfpuDotSpecial_, "vfpuDotSpecial", true });
}
} // namespace MIPSComp
+2
View File
@@ -41,6 +41,8 @@ public:
void InvalidateBlock(IRBlockCache *irBlockCache, int block_num) override;
protected:
void GetFixedCodeLabels(std::vector<IRNativeFixedCodeLabel> &labels) const override;
const CodeBlockCommon &CodeBlock() const override {
return *this;
}
+13
View File
@@ -259,4 +259,17 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) {
EndWrite();
}
void RiscVJitBackend::GetFixedCodeLabels(std::vector<IRNativeFixedCodeLabel> &labels) const {
labels.push_back({ outerLoop_, "outerLoop", false });
labels.push_back({ outerLoopPCInSCRATCH1_, "outerLoopPCInSCRATCH1", false });
labels.push_back({ dispatcherCheckCoreState_, "dispatcherCheckCoreState", false });
labels.push_back({ dispatcherPCInSCRATCH1_, "dispatcherPCInSCRATCH1", false });
labels.push_back({ dispatcherNoCheck_, "dispatcherNoCheck", false });
labels.push_back({ applyRoundingMode_, "applyRoundingMode", true });
labels.push_back({ saveStaticRegisters_, "saveStaticRegisters", true });
labels.push_back({ loadStaticRegisters_, "loadStaticRegisters", true });
labels.push_back({ vfpuDot_, "vfpuDot", true });
labels.push_back({ vfpuDotSpecial_, "vfpuDotSpecial", true });
}
} // namespace MIPSComp
+2
View File
@@ -41,6 +41,8 @@ public:
void InvalidateBlock(IRBlockCache *irBlockCache, int block_num) override;
protected:
void GetFixedCodeLabels(std::vector<IRNativeFixedCodeLabel> &labels) const override;
const CodeBlockCommon &CodeBlock() const override {
return *this;
}
+1 -1
View File
@@ -266,7 +266,7 @@ goes away or clears its cache, appends a report to FILE:
- the hot fixed-code routines, per instruction.
It costs nothing when off. The native JITs (macOS, Linux and Android by signal, Windows by
`SuspendThread`) sample the host PC; the IR interpreter instead times every block, which used to be the
`SuspendThread`; RISC-V and LoongArch on Linux only) sample the host PC; the IR interpreter instead times every block, which used to be the
`IR_PROFILING` define. Short runs from a savestate include compiling everything after the load, which
shows as emitter and IR pass functions; profile 20 emulated seconds or more to see the game itself.
To compare two builds by where the time goes, profile one game at a time: several at once wait on each