Merge pull request #20503 from hrydgard/remove-preload-experiment

JIT/IRJit: Delete an old "function preloading" experiment
This commit is contained in:
Henrik Rydgård committed 2025-06-15 13:24:49 +02:00
1 parent f48a08e050
commit 80671b7b6f
21 files changed
+29 -180

No files matched your search

-1
View File
@@ -391,7 +391,6 @@ static const ConfigSetting cpuSettings[] = {
ConfigSetting("FunctionReplacements", &g_Config.bFuncReplacements, true, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("HideSlowWarnings", &g_Config.bHideSlowWarnings, false, CfgFlag::DEFAULT),
ConfigSetting("HideStateWarnings", &g_Config.bHideStateWarnings, false, CfgFlag::DEFAULT),
ConfigSetting("PreloadFunctions", &g_Config.bPreloadFunctions, false, CfgFlag::PER_GAME),
ConfigSetting("JitDisableFlags", &g_Config.uJitDisableFlags, (uint32_t)0, CfgFlag::PER_GAME),
ConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),
};
-1
View File
@@ -115,7 +115,6 @@ public:
bool bFuncReplacements;
bool bHideSlowWarnings;
bool bHideStateWarnings;
bool bPreloadFunctions;
uint32_t uJitDisableFlags;
bool bDisableHTTPS;
-11
View File
@@ -661,9 +661,6 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char
// TODO: There's some double lookup going on here (we already did the lookup in GetHLEFunc above).
WriteHLESyscall(func.moduleName, func.nid, func.stubAddr);
currentMIPS->InvalidateICache(func.stubAddr, 8);
if (g_Config.bPreloadFunctions) {
MIPSAnalyst::PrecompileFunction(func.stubAddr, 8);
}
return;
}
@@ -682,9 +679,6 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting, const char
}
WriteFuncStub(func.stubAddr, it->symAddr);
currentMIPS->InvalidateICache(func.stubAddr, 8);
if (g_Config.bPreloadFunctions) {
MIPSAnalyst::PrecompileFunction(func.stubAddr, 8);
}
return;
}
}
@@ -725,9 +719,6 @@ void ExportFuncSymbol(const FuncSymbolExport &func) {
INFO_LOG(Log::Loader, "Resolving function %s/%08x", func.moduleName, func.nid);
WriteFuncStub(it->stubAddr, func.symAddr);
currentMIPS->InvalidateICache(it->stubAddr, 8);
if (g_Config.bPreloadFunctions) {
MIPSAnalyst::PrecompileFunction(it->stubAddr, 8);
}
}
}
}
@@ -1543,8 +1534,6 @@ static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 load
// use module_start_func instead of entry_addr if entry_addr is 0
if (module->nm.entry_addr == 0)
module->nm.entry_addr = module->nm.module_start_func;
MIPSAnalyst::PrecompileFunctions();
} else {
module->nm.entry_addr = -1;
}
-1
View File
@@ -747,7 +747,6 @@ static void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos)
*ptr = pos;
pos += 8;
WriteHLESyscall("FakeSysCalls", nid, *ptr);
MIPSAnalyst::PrecompileFunction(*ptr, 8);
}
u32 HLEMipsCallReturnAddress() {
+1 -1
View File
@@ -65,7 +65,7 @@ static void NoBlockExits() {
_assert_msg_(false, "Never exited block, invalid IR?");
}
bool Arm64JitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) {
bool Arm64JitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num) {
if (GetSpaceLeft() < 0x800)
return false;
+1 -1
View File
@@ -40,7 +40,7 @@ public:
bool DescribeCodePtr(const u8 *ptr, std::string &name) const override;
void GenerateFixedCode(MIPSState *mipsState) override;
bool CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) override;
bool CompileBlock(IRBlockCache *irBlockCache, int block_num) override;
void ClearAllBlocks() override;
void InvalidateBlock(IRBlockCache *irBlockCache, int block_num) override;
+1 -5
View File
@@ -334,11 +334,7 @@ void IRFrontend::Comp_Jump(MIPSOpcode op) {
// Might be a stubbed address or something?
if (!Memory::IsValidAddress(targetAddr)) {
// If preloading, flush - this block will likely be fixed later.
if (js.preloading) {
js.cancel = true;
} else {
ERROR_LOG(Log::JIT, "Jump to invalid address: %08x", targetAddr);
}
ERROR_LOG(Log::JIT, "Jump to invalid address: %08x", targetAddr);
// TODO: Mark this block dirty or something? May be indication it will be changed by imports.
// Continue so the block gets completed and crashes properly.
}
+1 -2
View File
@@ -239,9 +239,8 @@ MIPSOpcode IRFrontend::GetOffsetInstruction(int offset) {
return Memory::Read_Instruction(GetCompilerPC() + 4 * offset);
}
void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes, bool preload) {
void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes) {
js.cancel = false;
js.preloading = preload;
js.blockStart = em_address;
js.compilerPC = em_address;
js.lastContinuedPC = 0;
+1 -1
View File
@@ -89,7 +89,7 @@ public:
void DoState(PointerWrap &p);
bool CheckRounding(u32 blockAddress); // returns true if we need a do-over
void DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes, bool preload);
void DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes);
void EatPrefix() override {
js.EatPrefix();
+13 -112
View File
@@ -116,48 +116,28 @@ void IRJit::Compile(u32 em_address) {
PROFILE_THIS_SCOPE("jitc");
if (g_Config.bPreloadFunctions) {
// Look to see if we've preloaded this block.
int block_num = blocks_.FindPreloadBlock(em_address);
if (block_num != -1) {
IRBlock *block = blocks_.GetBlock(block_num);
// Okay, let's link and finalize the block now.
int cookie = compileToNative_ ? block->GetNativeOffset() : block->GetIRArenaOffset();
block->Finalize(cookie);
if (block->IsValid()) {
// Success, we're done.
FinalizeNativeBlock(&blocks_, block_num);
return;
}
}
}
std::vector<IRInst> instructions;
u32 mipsBytes;
if (!CompileBlock(em_address, instructions, mipsBytes, false)) {
if (!CompileBlock(em_address, instructions, mipsBytes)) {
// Ran out of block numbers - need to reset.
ERROR_LOG(Log::JIT, "Ran out of block numbers, clearing cache");
ClearCache();
CompileBlock(em_address, instructions, mipsBytes, false);
CompileBlock(em_address, instructions, mipsBytes);
}
if (frontend_.CheckRounding(em_address)) {
// Our assumptions are all wrong so it's clean-slate time.
ClearCache();
CompileBlock(em_address, instructions, mipsBytes, false);
CompileBlock(em_address, instructions, mipsBytes);
}
}
// WARNING! This can be called from IRInterpret / the JIT, through the function preload stuff!
bool IRJit::CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes, bool preload) {
bool IRJit::CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes) {
_dbg_assert_(compilerEnabled_);
frontend_.DoJit(em_address, instructions, mipsBytes, preload);
if (instructions.empty()) {
_dbg_assert_(preload);
// We return true when preloading so it doesn't abort.
return preload;
}
frontend_.DoJit(em_address, instructions, mipsBytes);
_dbg_assert_(!instructions.empty());
int block_num = blocks_.AllocateBlock(em_address, mipsBytes, instructions);
if ((block_num & ~MIPS_EMUHACK_VALUE_MASK) != 0) {
@@ -167,13 +147,13 @@ bool IRJit::CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32
}
IRBlock *b = blocks_.GetBlock(block_num);
if (preload || mipsTracer.tracing_enabled) {
if (mipsTracer.tracing_enabled) {
// Hash, then only update page stats, don't link yet.
// TODO: Should we always hash? Then we can reuse blocks.
b->UpdateHash();
}
if (!CompileNativeBlock(&blocks_, block_num, preload))
if (!CompileNativeBlock(&blocks_, block_num))
return false;
if (mipsTracer.tracing_enabled) {
@@ -181,88 +161,11 @@ bool IRJit::CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32
}
// Updates stats, also patches the first MIPS instruction into an emuhack if 'preload == false'
blocks_.FinalizeBlock(block_num, preload);
if (!preload)
FinalizeNativeBlock(&blocks_, block_num);
blocks_.FinalizeBlock(block_num);
FinalizeNativeBlock(&blocks_, block_num);
return true;
}
void IRJit::CompileFunction(u32 start_address, u32 length) {
_dbg_assert_(compilerEnabled_);
PROFILE_THIS_SCOPE("jitc");
// Note: we don't actually write emuhacks yet, so we can validate hashes.
// This way, if the game changes the code afterward, we'll catch even without icache invalidation.
// We may go up and down from branches, so track all block starts done here.
std::set<u32> doneAddresses;
std::vector<u32> pendingAddresses;
pendingAddresses.reserve(16);
pendingAddresses.push_back(start_address);
while (!pendingAddresses.empty()) {
u32 em_address = pendingAddresses.back();
pendingAddresses.pop_back();
// To be safe, also check if a real block is there. This can be a runtime module load.
u32 inst = Memory::ReadUnchecked_U32(em_address);
if (MIPS_IS_RUNBLOCK(inst) || doneAddresses.find(em_address) != doneAddresses.end()) {
// Already compiled this address.
continue;
}
std::vector<IRInst> instructions;
u32 mipsBytes;
if (!CompileBlock(em_address, instructions, mipsBytes, true)) {
// Ran out of block numbers - let's hope there's no more code it needs to run.
// Will flush when actually compiling.
ERROR_LOG(Log::JIT, "Ran out of block numbers while compiling function");
return;
}
doneAddresses.insert(em_address);
for (const IRInst &inst : instructions) {
u32 exit = 0;
switch (inst.op) {
case IROp::ExitToConst:
case IROp::ExitToConstIfEq:
case IROp::ExitToConstIfNeq:
case IROp::ExitToConstIfGtZ:
case IROp::ExitToConstIfGeZ:
case IROp::ExitToConstIfLtZ:
case IROp::ExitToConstIfLeZ:
case IROp::ExitToConstIfFpTrue:
case IROp::ExitToConstIfFpFalse:
exit = inst.constant;
break;
case IROp::ExitToPC:
case IROp::Break:
// Don't add any, we'll do block end anyway (for jal, etc.)
exit = 0;
break;
default:
exit = 0;
break;
}
// Only follow jumps internal to the function.
if (exit != 0 && exit >= start_address && exit < start_address + length) {
// Even if it's a duplicate, we check at loop start.
pendingAddresses.push_back(exit);
}
}
// Also include after the block for jal returns.
if (em_address + mipsBytes < start_address + length) {
pendingAddresses.push_back(em_address + mipsBytes);
}
}
}
void IRJit::RunLoopUntil(u64 globalticks) {
PROFILE_THIS_SCOPE("jit");
@@ -430,13 +333,11 @@ std::vector<int> IRBlockCache::FindInvalidatedBlockNumbers(u32 address, u32 leng
return found;
}
void IRBlockCache::FinalizeBlock(int blockIndex, bool preload) {
void IRBlockCache::FinalizeBlock(int blockIndex) {
// TODO: What's different about preload blocks?
IRBlock &block = blocks_[blockIndex];
if (!preload) {
int cookie = compileToNative_ ? block.GetNativeOffset() : block.GetIRArenaOffset();
block.Finalize(cookie);
}
int cookie = compileToNative_ ? block.GetNativeOffset() : block.GetIRArenaOffset();
block.Finalize(cookie);
u32 startAddr, size;
block.GetRange(&startAddr, &size);
+3 -4
View File
@@ -122,7 +122,7 @@ public:
void Clear();
std::vector<int> FindInvalidatedBlockNumbers(u32 address, u32 length);
void FinalizeBlock(int blockNum, bool preload = false);
void FinalizeBlock(int blockNum);
int GetNumBlocks() const override { return (int)blocks_.size(); }
int AllocateBlock(int emAddr, u32 origSize, const std::vector<IRInst> &inst);
IRBlock *GetBlock(int blockNum) {
@@ -212,7 +212,6 @@ public:
void RunLoopUntil(u64 globalticks) override;
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
void CompileFunction(u32 start_address, u32 length) override;
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
// Not using a regular block cache.
@@ -238,8 +237,8 @@ public:
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
protected:
bool CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes, bool preload);
virtual bool CompileNativeBlock(IRBlockCache *irBlockCache, int block_num, bool preload) { return true; }
bool CompileBlock(u32 em_address, std::vector<IRInst> &instructions, u32 &mipsBytes);
virtual bool CompileNativeBlock(IRBlockCache *irBlockCache, int block_num) { return true; }
virtual void FinalizeNativeBlock(IRBlockCache *irBlockCache, int block_num) {}
bool compileToNative_;
+2 -2
View File
@@ -507,8 +507,8 @@ void IRNativeJit::Init(IRNativeBackend &backend) {
}
}
bool IRNativeJit::CompileNativeBlock(IRBlockCache *irblockCache, int block_num, bool preload) {
return backend_->CompileBlock(irblockCache, block_num, preload);
bool IRNativeJit::CompileNativeBlock(IRBlockCache *irblockCache, int block_num) {
return backend_->CompileBlock(irblockCache, block_num);
}
void IRNativeJit::FinalizeNativeBlock(IRBlockCache *irblockCache, int block_num) {
+2 -2
View File
@@ -71,7 +71,7 @@ public:
int OffsetFromCodePtr(const u8 *ptr);
virtual void GenerateFixedCode(MIPSState *mipsState) = 0;
virtual bool CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) = 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);
@@ -198,7 +198,7 @@ public:
protected:
void Init(IRNativeBackend &backend);
bool CompileNativeBlock(IRBlockCache *irBlockCache, int block_num, bool preload) override;
bool CompileNativeBlock(IRBlockCache *irBlockCache, int block_num) override;
void FinalizeNativeBlock(IRBlockCache *irBlockCache, int block_num) override;
IRNativeBackend *backend_ = nullptr;
-1
View File
@@ -136,7 +136,6 @@ namespace MIPSComp {
virtual void DoState(PointerWrap &p) = 0;
virtual void RunLoopUntil(u64 globalticks) = 0;
virtual void Compile(u32 em_address) = 0;
virtual void CompileFunction(u32 start_address, u32 length) { }
virtual void ClearCache() = 0;
virtual void UpdateFCR31() = 0;
virtual MIPSOpcode GetOriginalOp(MIPSOpcode op) = 0;
-1
View File
@@ -68,7 +68,6 @@ namespace MIPSComp {
int numInstructions;
bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block
bool hadBreakpoints;
bool preloading = false;
JitBlock *curBlock;
u8 hasSetRounding = 0;
-27
View File
@@ -906,33 +906,6 @@ skip:
}
}
void PrecompileFunction(u32 startAddr, u32 length) {
// Direct calls to this ignore the bPreloadFunctions flag, since it's just for stubs.
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
if (MIPSComp::jit) {
MIPSComp::jit->CompileFunction(startAddr, length);
}
}
void PrecompileFunctions() {
if (!g_Config.bPreloadFunctions) {
return;
}
std::lock_guard<std::recursive_mutex> guard(functions_lock);
// TODO: Load from cache file if available instead.
double st = time_now_d();
for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) {
const AnalyzedFunction &f = *iter;
PrecompileFunction(f.start, f.end - f.start + 4);
}
double et = time_now_d();
NOTICE_LOG(Log::JIT, "Precompiled %d MIPS functions in %0.2f milliseconds", (int)functions.size(), (et - st) * 1000.0);
}
static const char *DefaultFunctionName(char buffer[256], u32 startAddr) {
snprintf(buffer, 256, "z_un_%08x", startAddr);
return buffer;
-3
View File
@@ -97,7 +97,6 @@ namespace MIPSAnalyst {
void Reset();
bool IsRegisterUsed(u32 reg, u32 addr);
// This will not only create a database of "AnalyzedFunction" structs, it also
// will insert all the functions it finds into the symbol map, if insertSymbols is true.
@@ -108,8 +107,6 @@ namespace MIPSAnalyst {
bool ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols);
void FinalizeScan(bool insertSymbols);
void ForgetFunctions(u32 startAddr, u32 endAddr);
void PrecompileFunctions();
void PrecompileFunction(u32 startAddr, u32 length);
void SetHashMapFilename(const std::string& filename = "");
void LoadBuiltinHashMap();
+1 -1
View File
@@ -56,7 +56,7 @@ static void NoBlockExits() {
_assert_msg_(false, "Never exited block, invalid IR?");
}
bool RiscVJitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) {
bool RiscVJitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num) {
if (GetSpaceLeft() < 0x800)
return false;
+1 -1
View File
@@ -36,7 +36,7 @@ public:
bool DescribeCodePtr(const u8 *ptr, std::string &name) const override;
void GenerateFixedCode(MIPSState *mipsState) override;
bool CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) override;
bool CompileBlock(IRBlockCache *irBlockCache, int block_num) override;
void ClearAllBlocks() override;
void InvalidateBlock(IRBlockCache *irBlockCache, int block_num) override;
+1 -1
View File
@@ -55,7 +55,7 @@ static void NoBlockExits() {
_assert_msg_(false, "Never exited block, invalid IR?");
}
bool X64JitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) {
bool X64JitBackend::CompileBlock(IRBlockCache *irBlockCache, int block_num) {
if (GetSpaceLeft() < 0x800)
return false;
+1 -1
View File
@@ -52,7 +52,7 @@ public:
bool DescribeCodePtr(const u8 *ptr, std::string &name) const override;
void GenerateFixedCode(MIPSState *mipsState) override;
bool CompileBlock(IRBlockCache *irBlockCache, int block_num, bool preload) override;
bool CompileBlock(IRBlockCache *irBlockCache, int block_num) override;
void ClearAllBlocks() override;
void InvalidateBlock(IRBlockCache *irBlockCache, int block_num) override;