Files
ppsspp/Core/MIPS/IR/IRNativeCommon.h
T
Henrik RydgårdandClaude Opus 5.5 20063fe526 IR JITs: Name the fixed code routines that jumps go to in the disassembly
DescribeCodePtr() called everything in the fixed code other than a few
routines "fixedCode". It now uses the routine list the profiler has
(moved to the backend as GetAllFixedCodeLabels()), with an offset when
not at the start, so a block ends in "b dispatcherCheckCoreState".

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

267 lines
9.2 KiB
C++

// Copyright (c) 2023- 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 <map>
#include <unordered_map>
#include "Common/CodeBlock.h"
#include "Core/MIPS/IR/IRJit.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
namespace MIPSComp {
typedef void (*IRNativeFuncNoArg)();
enum class IRProfilerStatus : int32_t {
NOT_RUNNING,
IN_JIT,
TIMER_ADVANCE,
COMPILING,
MATH_HELPER,
REPLACEMENT,
SYSCALL,
INTERPRET,
IR_INTERPRET,
};
struct IRNativeHooks {
IRNativeFuncNoArg enterDispatcher = nullptr;
const uint8_t *dispatcher = nullptr;
const uint8_t *dispatchFetch = nullptr;
const uint8_t *crashHandler = nullptr;
// The dispatcher's compile path, where a replacement hook's block shadow entry goes.
const uint8_t *blockShadowHook = nullptr;
uint32_t *profilerPC = nullptr;
IRProfilerStatus *profilerStatus = nullptr;
};
struct IRNativeBlockExit {
int offset;
int len;
uint32_t dest;
};
// Names a piece of the fixed code, for the profiler.
struct IRNativeFixedCodeLabel {
const u8 *ptr;
const char *name;
// A leaf routine blocks call, so the return address (LR, [rsp]) says which block.
bool calledFromBlocks;
};
struct IRNativeBlock {
int checkedOffset = 0;
std::vector<IRNativeBlockExit> exits;
// Code offset of each IR instruction, only kept when profiling.
std::vector<int> irOffsets;
};
class IRNativeBackend {
public:
IRNativeBackend(IRBlockCache &blocks);
virtual ~IRNativeBackend();
void CompileIRInst(IRInst inst);
virtual bool DescribeCodePtr(const u8 *ptr, std::string &name) const;
bool CodeInRange(const u8 *ptr) const;
int OffsetFromCodePtr(const u8 *ptr);
virtual void GenerateFixedCode(MIPSState *mipsState) = 0;
virtual bool CompileBlock(IRBlockCache *irBlockCache, int block_num) = 0;
virtual void ClearAllBlocks() = 0;
virtual void InvalidateBlock(IRBlockCache *irBlockCache, int block_num) = 0;
void FinalizeBlock(IRBlockCache *irBlockCache, int block_num, const JitOptions &jo);
virtual void UpdateFCR31(MIPSState *mipsState) {}
const IRNativeHooks &GetNativeHooks() const {
return hooks_;
}
const IRNativeBlock *GetNativeBlock(int block_num) const;
void SetBlockCheckedOffset(int block_num, int offset);
void SetProfiling(bool profiling) {
profiling_ = profiling;
}
virtual const CodeBlockCommon &CodeBlock() const = 0;
// The backend's own routines, without the dispatcher hooks.
virtual void GetFixedCodeLabels(std::vector<IRNativeFixedCodeLabel> &labels) const {}
// Those and the dispatcher hooks, by address.
void GetAllFixedCodeLabels(std::vector<IRNativeFixedCodeLabel> &labels) const;
// The name of the fixed code routine ptr is in, with an offset if not at its start, for the
// disassembly and profilers (so no spaces).
std::string DescribeFixedCode(const u8 *ptr) const;
protected:
virtual void CompIR_Arith(IRInst inst) = 0;
virtual void CompIR_Assign(IRInst inst) = 0;
virtual void CompIR_Basic(IRInst inst) = 0;
virtual void CompIR_Bits(IRInst inst) = 0;
virtual void CompIR_Breakpoint(IRInst inst) = 0;
virtual void CompIR_Compare(IRInst inst) = 0;
virtual void CompIR_CondAssign(IRInst inst) = 0;
virtual void CompIR_CondStore(IRInst inst) = 0;
virtual void CompIR_Div(IRInst inst) = 0;
virtual void CompIR_Exit(IRInst inst) = 0;
virtual void CompIR_ExitIf(IRInst inst) = 0;
virtual void CompIR_FArith(IRInst inst) = 0;
virtual void CompIR_FAssign(IRInst inst) = 0;
virtual void CompIR_FCompare(IRInst inst) = 0;
virtual void CompIR_FCondAssign(IRInst inst) = 0;
virtual void CompIR_FCvt(IRInst inst) = 0;
virtual void CompIR_FLoad(IRInst inst) = 0;
virtual void CompIR_FRound(IRInst inst) = 0;
virtual void CompIR_FSat(IRInst inst) = 0;
virtual void CompIR_FSpecial(IRInst inst) = 0;
virtual void CompIR_FStore(IRInst inst) = 0;
virtual void CompIR_Generic(IRInst inst) = 0;
virtual void CompIR_HiLo(IRInst inst) = 0;
virtual void CompIR_Interpret(IRInst inst) = 0;
virtual void CompIR_Load(IRInst inst) = 0;
virtual void CompIR_LoadShift(IRInst inst) = 0;
virtual void CompIR_Logic(IRInst inst) = 0;
virtual void CompIR_Mult(IRInst inst) = 0;
virtual void CompIR_RoundingMode(IRInst inst) = 0;
virtual void CompIR_Shift(IRInst inst) = 0;
virtual void CompIR_Store(IRInst inst) = 0;
virtual void CompIR_StoreShift(IRInst inst) = 0;
virtual void CompIR_System(IRInst inst) = 0;
virtual void CompIR_Transfer(IRInst inst) = 0;
virtual void CompIR_VecArith(IRInst inst) = 0;
virtual void CompIR_VecAssign(IRInst inst) = 0;
virtual void CompIR_VecHoriz(IRInst inst) = 0;
virtual void CompIR_VecLoad(IRInst inst) = 0;
virtual void CompIR_VecPack(IRInst inst) = 0;
virtual void CompIR_VecStore(IRInst inst) = 0;
virtual void CompIR_ValidateAddress(IRInst inst) = 0;
virtual void OverwriteExit(int srcOffset, int len, int block_num) = 0;
// Returns true when debugging statistics should be compiled in.
bool DebugStatsEnabled() const;
bool DebugProfilerEnabled() const;
// Callback (compile when DebugStatsEnabled()) to log a base interpreter hit.
// Call the func returned by MIPSGetInterpretFunc(op) directly for interpret.
static void NotifyMIPSInterpret(const char *name);
// Callback to log AND perform a base interpreter op. Alternative to NotifyMIPSInterpret().
static void DoMIPSInst(uint32_t op);
// Callback to log AND perform an IR interpreter inst. Returns 0 or a PC to jump to.
static uint32_t DoIRInst(uint64_t inst);
static int ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite);
void AddLinkableExit(int block_num, uint32_t pc, int exitStartOffset, int exitLen);
void EraseAllLinks(int block_num);
// For the profiler's report. Does nothing unless profiling.
void RecordIRAddresses(int block_num, const std::vector<const u8 *> &addresses);
IRNativeHooks hooks_;
IRBlockCache &blocks_;
std::vector<IRNativeBlock> nativeBlocks_;
std::unordered_multimap<uint32_t, int> linksTo_;
bool profiling_ = false;
};
class IRNativeBlockCacheDebugInterface : public JitBlockCacheDebugInterface {
public:
IRNativeBlockCacheDebugInterface(const MIPSComp::IRBlockCache &irBlocks);
void Init(const IRNativeBackend *backend);
int GetNumBlocks() const override;
int GetBlockNumberFromStartAddress(u32 em_address) const override;
JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const override;
JitBlockMeta GetBlockMeta(int blockNum) const override;
JitBlockProfileStats GetBlockProfileStats(int blockNum) const override;
void ComputeStats(BlockCacheStats &bcStats) const override;
bool IsValidBlock(int blockNum) const override;
private:
void GetBlockCodeRange(int blockNum, int *startOffset, int *size) const;
const MIPSComp::IRBlockCache &irBlocks_;
const CodeBlockCommon *codeBlock_ = nullptr;
const IRNativeBackend *backend_ = nullptr;
};
class IRNativeJit : public IRJit {
public:
IRNativeJit(MIPSState *mipsState);
void RunLoopUntil(u64 globalticks) override;
void ClearCache() override;
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
bool CodeInRange(const u8 *ptr) const override;
bool IsAtDispatchFetch(const u8 *ptr) const override;
u32 GetBlockShadowHookValue() const override;
const u8 *GetDispatcher() const override;
const u8 *GetCrashHandler() const override;
void UpdateFCR31() override;
void FlushProfile() override;
JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override;
const u8 *GetCodeBase() const override;
protected:
void Init(IRNativeBackend &backend);
bool CompileNativeBlock(IRBlockCache *irBlockCache, int block_num) override;
void FinalizeNativeBlock(IRBlockCache *irBlockCache, int block_num) override;
void InvalidateNativeBlock(IRBlockCache *irBlockCache, int block_num) override;
void CollectProfileSamples();
int FindBlockByCodeOffset(int offset) const;
int FindBlockOffsetEnd() const;
void WriteProfileReport() override;
struct BlockProfile {
int64_t samples = 0;
// Samples in a routine the block called (like the dot product).
int64_t routineSamples = 0;
std::map<int, int64_t> byOffset;
// routineSamples by the code offset the routine returns to.
std::map<int, int64_t> routineByReturn;
};
std::unordered_map<int, BlockProfile> blockProfiles_;
std::unordered_map<uintptr_t, int64_t> hostProfile_;
// Sorted by ptr. Samples by label, split by whether a block was charged for them.
std::vector<IRNativeFixedCodeLabel> fixedLabels_;
std::map<std::string, int64_t> fixedCodeByLabel_;
std::map<std::string, int64_t> routinesByLabel_;
// All fixed code samples, by code offset.
std::map<int, int64_t> fixedCodeByOffset_;
int64_t fixedCodeSamples_ = 0;
int64_t totalSamples_ = 0;
IRNativeBackend *backend_ = nullptr;
IRNativeHooks hooks_;
IRNativeBlockCacheDebugInterface debugInterface_;
};
} // namespace MIPSComp