Merge pull request #22573 from hrydgard/remove-fakejit

Remove FakeJit and the pre-IR JitBlockCache
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-15 09:47:41 +02:00
commit 9458e8e8ac
31 files changed
+17 -1620

No files matched your search

+3
View File
@@ -77,6 +77,9 @@ for it:
the next test. Concurrent runs collide in PSPLink and `host0:`: they time out, their results are
garbage, and the PSP wedges until someone resets it by hand. This has happened repeatedly; there
is no case where it is acceptable.
8. **Update submodules when you start working.** Run `git submodule update --init --recursive`
before anything else, and again after switching or rebasing branches, so that builds and tests
use the submodule commits the branch records.
## Core Safety Checks
-1
View File
@@ -337,7 +337,6 @@ add_library(Common STATIC
CommonTypes.h
CommonWindows.h
DbgNew.h
FakeEmitter.h
FakeCPUDetect.cpp
ExceptionHandlerSetup.cpp
ExceptionHandlerSetup.h
-1
View File
@@ -414,7 +414,6 @@
<ClInclude Include="Data\Text\Parsers.h" />
<ClInclude Include="Data\Text\StringWriter.h" />
<ClInclude Include="Data\Text\WrapText.h" />
<ClInclude Include="FakeEmitter.h" />
<ClInclude Include="File\AndroidContentURI.h" />
<ClInclude Include="File\AndroidStorage.h" />
<ClInclude Include="File\DirListing.h" />
-1
View File
@@ -54,7 +54,6 @@
<Filter>Serialize</Filter>
</ClInclude>
<ClInclude Include="TimeUtil.h" />
<ClInclude Include="FakeEmitter.h" />
<ClInclude Include="SysError.h" />
<ClInclude Include="..\ext\libpng17\png.h">
<Filter>ext\libpng17</Filter>
-425
View File
@@ -1,425 +0,0 @@
// Copyright (C) 2003 Dolphin 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.
// 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 SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!!
#ifndef _DOLPHIN_FAKE_CODEGEN_
#define _DOLPHIN_FAKE_CODEGEN_
#include <stdint.h>
#include "Common/CommonTypes.h"
#include "Common/CodeBlock.h"
// VCVT flags
#define TO_FLOAT 0
#define TO_INT 1 << 0
#define IS_SIGNED 1 << 1
#define ROUND_TO_ZERO 1 << 2
namespace FakeGen
{
enum FakeReg
{
// GPRs
R0 = 0, R1, R2, R3, R4, R5,
R6, R7, R8, R9, R10, R11,
// SPRs
// R13 - R15 are SP, LR, and PC.
// Almost always referred to by name instead of register number
R12 = 12, R13 = 13, R14 = 14, R15 = 15,
R_IP = 12, R_SP = 13, R_LR = 14, R_PC = 15,
// VFP single precision registers
S0, S1, S2, S3, S4, S5, S6,
S7, S8, S9, S10, S11, S12, S13,
S14, S15, S16, S17, S18, S19, S20,
S21, S22, S23, S24, S25, S26, S27,
S28, S29, S30, S31,
// VFP Double Precision registers
D0, D1, D2, D3, D4, D5, D6, D7,
D8, D9, D10, D11, D12, D13, D14, D15,
D16, D17, D18, D19, D20, D21, D22, D23,
D24, D25, D26, D27, D28, D29, D30, D31,
// ASIMD Quad-Word registers
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7,
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
// for NEON VLD/VST instructions
REG_UPDATE = R13,
INVALID_REG = 0xFFFFFFFF
};
enum CCFlags
{
CC_EQ = 0, // Equal
CC_NEQ, // Not equal
CC_CS, // Carry Set
CC_CC, // Carry Clear
CC_MI, // Minus (Negative)
CC_PL, // Plus
CC_VS, // Overflow
CC_VC, // No Overflow
CC_HI, // Unsigned higher
CC_LS, // Unsigned lower or same
CC_GE, // Signed greater than or equal
CC_LT, // Signed less than
CC_GT, // Signed greater than
CC_LE, // Signed less than or equal
CC_AL, // Always (unconditional) 14
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
};
const u32 NO_COND = 0xE0000000;
enum ShiftType
{
ST_LSL = 0,
ST_ASL = 0,
ST_LSR = 1,
ST_ASR = 2,
ST_ROR = 3,
ST_RRX = 4
};
enum IntegerSize
{
I_I8 = 0,
I_I16,
I_I32,
I_I64
};
enum
{
NUMGPRs = 13,
};
class FakeXEmitter;
enum OpType
{
TYPE_IMM = 0,
TYPE_REG,
TYPE_IMMSREG,
TYPE_RSR,
TYPE_MEM
};
// This is no longer a proper operand2 class. Need to split up.
class Operand2
{
friend class FakeXEmitter;
protected:
u32 Value;
private:
OpType Type;
// IMM types
u8 Rotation; // Only for u8 values
// Register types
u8 IndexOrShift;
ShiftType Shift;
public:
OpType GetType()
{
return Type;
}
Operand2() {}
Operand2(u32 imm, OpType type = TYPE_IMM) : IndexOrShift(), Shift()
{
Type = type;
Value = imm;
Rotation = 0;
}
Operand2(FakeReg Reg) : IndexOrShift(), Shift()
{
Type = TYPE_REG;
Value = Reg;
Rotation = 0;
}
Operand2(u8 imm, u8 rotation) : IndexOrShift(), Shift()
{
Type = TYPE_IMM;
Value = imm;
Rotation = rotation;
}
Operand2(FakeReg base, ShiftType type, FakeReg shift) : Rotation(0) // RSR
{
Type = TYPE_RSR;
_assert_msg_(type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount");
IndexOrShift = shift;
Shift = type;
Value = base;
}
Operand2(FakeReg base, ShiftType type, u8 shift) : Rotation(0) // For IMM shifted register
{
if(shift == 32) shift = 0;
switch (type)
{
case ST_LSL:
_assert_msg_(shift < 32, "Invalid Operand2: LSL %u", shift);
break;
case ST_LSR:
_assert_msg_(shift <= 32, "Invalid Operand2: LSR %u", shift);
if (!shift)
type = ST_LSL;
if (shift == 32)
shift = 0;
break;
case ST_ASR:
_assert_msg_(shift < 32, "Invalid Operand2: ASR %u", shift);
if (!shift)
type = ST_LSL;
if (shift == 32)
shift = 0;
break;
case ST_ROR:
_assert_msg_(shift < 32, "Invalid Operand2: ROR %u", shift);
if (!shift)
type = ST_LSL;
break;
case ST_RRX:
_assert_msg_(shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount");
type = ST_ROR;
break;
}
IndexOrShift = shift;
Shift = type;
Value = base;
Type = TYPE_IMMSREG;
}
u32 GetData()
{
switch(Type)
{
case TYPE_IMM:
return Imm12Mod(); // This'll need to be changed later
case TYPE_REG:
return Rm();
case TYPE_IMMSREG:
return IMMSR();
case TYPE_RSR:
return RSR();
default:
_assert_msg_(false, "GetData with Invalid Type");
return 0;
}
}
u32 IMMSR() // IMM shifted register
{
_assert_msg_(Type == TYPE_IMMSREG, "IMMSR must be imm shifted register");
return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value);
}
u32 RSR() // Register shifted register
{
_assert_msg_(Type == TYPE_RSR, "RSR must be RSR Of Course");
return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value;
}
u32 Rm()
{
_assert_msg_(Type == TYPE_REG, "Rm must be with Reg");
return Value;
}
u32 Imm5()
{
_assert_msg_(Type == TYPE_IMM, "Imm5 not IMM value");
return ((Value & 0x0000001F) << 7);
}
u32 Imm8()
{
_assert_msg_(Type == TYPE_IMM, "Imm8Rot not IMM value");
return Value & 0xFF;
}
u32 Imm8Rot() // IMM8 with Rotation
{
_assert_msg_(Type == TYPE_IMM, "Imm8Rot not IMM value");
_assert_msg_((Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation);
return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF);
}
u32 Imm12()
{
_assert_msg_(Type == TYPE_IMM, "Imm12 not IMM");
return (Value & 0x00000FFF);
}
u32 Imm12Mod()
{
// This is an IMM12 with the top four bits being rotation and the
// bottom eight being an IMM. This is for instructions that need to
// expand a 8bit IMM to a 32bit value and gives you some rotation as
// well.
// Each rotation rotates to the right by 2 bits
_assert_msg_(Type == TYPE_IMM, "Imm12Mod not IMM");
return ((Rotation & 0xF) << 8) | (Value & 0xFF);
}
u32 Imm16()
{
_assert_msg_(Type == TYPE_IMM, "Imm16 not IMM");
return ( (Value & 0xF000) << 4) | (Value & 0x0FFF);
}
u32 Imm16Low()
{
return Imm16();
}
u32 Imm16High() // Returns high 16bits
{
_assert_msg_(Type == TYPE_IMM, "Imm16 not IMM");
return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF);
}
u32 Imm24()
{
_assert_msg_(Type == TYPE_IMM, "Imm16 not IMM");
return (Value & 0x0FFFFFFF);
}
};
// Use these when you don't know if an imm can be represented as an operand2.
// This lets you generate both an optimal and a fallback solution by checking
// the return value, which will be false if these fail to find a Operand2 that
// represents your 32-bit imm value.
bool TryMakeOperand2(u32 imm, Operand2 &op2);
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse);
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated);
// Use this only when you know imm can be made into an Operand2.
Operand2 AssumeMakeOperand2(u32 imm);
inline Operand2 R(FakeReg Reg) { return Operand2(Reg, TYPE_REG); }
inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); }
inline Operand2 Mem(void *ptr) { return Operand2((u32)(uintptr_t)ptr, TYPE_IMM); }
//usage: struct {int e;} s; STRUCT_OFFSET(s,e)
#define STRUCT_OFF(str,elem) ((u32)((u32)&(str).elem-(u32)&(str)))
struct FixupBranch
{
u8 *ptr;
u32 condition; // Remembers our codition at the time
int type; //0 = B 1 = BL
};
typedef const u8* JumpTarget;
// XXX: Stop polluting the global namespace
const u32 I_8 = (1 << 0);
const u32 I_16 = (1 << 1);
const u32 I_32 = (1 << 2);
const u32 I_64 = (1 << 3);
const u32 I_SIGNED = (1 << 4);
const u32 I_UNSIGNED = (1 << 5);
const u32 F_32 = (1 << 6);
const u32 I_POLYNOMIAL = (1 << 7); // Only used in VMUL/VMULL
u32 EncodeVd(FakeReg Vd);
u32 EncodeVn(FakeReg Vn);
u32 EncodeVm(FakeReg Vm);
u32 encodedSize(u32 value);
// Subtracts the base from the register to give us the real one
FakeReg SubBase(FakeReg Reg);
// See A.7.1 in the Fakev7-A
// VMUL F32 scalars can only be up to D15[0], D15[1] - higher scalars cannot be individually addressed
FakeReg DScalar(FakeReg dreg, int subScalar);
FakeReg QScalar(FakeReg qreg, int subScalar);
enum NEONAlignment {
ALIGN_NONE = 0,
ALIGN_64 = 1,
ALIGN_128 = 2,
ALIGN_256 = 3
};
class NEONXEmitter;
class FakeXEmitter
{
friend struct OpArg; // for Write8 etc
private:
u8 *code, *startcode;
u8 *lastCacheFlushEnd;
u32 condition;
protected:
inline void Write32(u32 value) {*(u32*)code = value; code+=4;}
public:
FakeXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) {
condition = CC_AL << 28;
}
FakeXEmitter(u8 *code_ptr) {
code = code_ptr;
lastCacheFlushEnd = code_ptr;
startcode = code_ptr;
condition = CC_AL << 28;
}
virtual ~FakeXEmitter() {}
void SetCodePointer(u8 *ptr, u8 *writePtr) {}
const u8 *GetCodePointer() const { return nullptr; }
void SetCodePtr(u8 *ptr) {}
void ReserveCodeSpace(u32 bytes) {}
const u8 *AlignCode16() { return nullptr; }
const u8 *AlignCodePage() { return nullptr; }
const u8 *GetCodePtr() const { return nullptr; }
void FlushIcache() {}
void FlushIcacheSection(u8 *start, u8 *end) {}
u8 *GetWritableCodePtr() { return nullptr; }
CCFlags GetCC() { return CCFlags(condition >> 28); }
void SetCC(CCFlags cond = CC_AL) {}
// Special purpose instructions
// Do nothing
void NOP(int count = 1) {} //nop padding - TODO: fast nop slides, for amd and intel (check their manuals)
#ifdef CALL
#undef CALL
#endif
void QuickCallFunction(FakeReg scratchreg, const void *func);
template <typename T> void QuickCallFunction(FakeReg scratchreg, T func) {
QuickCallFunction(scratchreg, (const void *)func);
}
}; // class FakeXEmitter
// Everything that needs to generate machine code should inherit from this.
// You get memory management for free, plus, you can use all the MOV etc functions without
// having to prefix them with gen-> or something similar.
class FakeXCodeBlock : public CodeBlock<FakeXEmitter> {
public:
void PoisonMemory(int offset) override {
}
};
} // namespace
#endif // _DOLPHIN_FAKE_CODEGEN_
-3
View File
@@ -188,8 +188,6 @@ if(NOT LIBRETRO)
HW/StereoResampler.h
MIPS/MIPSAsm.cpp
MIPS/MIPSAsm.h
MIPS/fake/FakeJit.cpp
MIPS/fake/FakeJit.h
Util/GameManager.cpp
Util/GameManager.h
Util/VideoPlayer.cpp
@@ -632,7 +630,6 @@ add_library(Core STATIC
MIPS/MIPSTracer.h
MIPS/JitCommon/JitCommon.cpp
MIPS/JitCommon/JitCommon.h
MIPS/JitCommon/JitBlockCache.cpp
MIPS/JitCommon/JitBlockCache.h
MIPS/JitCommon/JitState.cpp
MIPS/JitCommon/JitState.h
-3
View File
@@ -508,7 +508,6 @@
<ClCompile Include="MIPS\ARM64\Arm64IRCompVec.cpp" />
<ClCompile Include="MIPS\ARM64\Arm64IRJit.cpp" />
<ClCompile Include="MIPS\ARM64\Arm64IRRegCache.cpp" />
<ClCompile Include="MIPS\fake\FakeJit.cpp" />
<ClCompile Include="MIPS\IR\IRAnalysis.cpp" />
<ClCompile Include="MIPS\IR\IRCompALU.cpp" />
<ClCompile Include="MIPS\IR\IRCompBranch.cpp" />
@@ -694,7 +693,6 @@
<ClCompile Include="MIPS\ARM\ArmIRCompVec.cpp" />
<ClCompile Include="MIPS\ARM\ArmIRJit.cpp" />
<ClCompile Include="MIPS\ARM\ArmIRRegCache.cpp" />
<ClCompile Include="MIPS\JitCommon\JitBlockCache.cpp" />
<ClCompile Include="MIPS\JitCommon\JitCommon.cpp" />
<ClCompile Include="MIPS\JitCommon\JitState.cpp" />
<ClCompile Include="MIPS\MIPS.cpp" />
@@ -871,7 +869,6 @@
<ClInclude Include="MIPS\ARM64\Arm64IRJit.h" />
<ClInclude Include="MIPS\ARM64\Arm64IRRegCache.h" />
<ClInclude Include="MIPS\ARM64\Arm64VfpuDot.h" />
<ClInclude Include="MIPS\fake\FakeJit.h" />
<ClInclude Include="MIPS\IR\IRAnalysis.h" />
<ClInclude Include="MIPS\IR\IRFrontend.h" />
<ClInclude Include="MIPS\IR\IRInst.h" />
-12
View File
@@ -85,9 +85,6 @@
<Filter Include="Ext\libzip">
<UniqueIdentifier>{0201ff27-4fa8-4cf3-bbb9-accfdc0c7046}</UniqueIdentifier>
</Filter>
<Filter Include="MIPS\fake">
<UniqueIdentifier>{678fa299-0ff7-4983-982d-2da47b52e238}</UniqueIdentifier>
</Filter>
<Filter Include="MIPS\RiscV">
<UniqueIdentifier>{067e3128-3aaf-4ed1-b19e-bab11606abe7}</UniqueIdentifier>
</Filter>
@@ -481,9 +478,6 @@
<ClCompile Include="HLE\sceMp3.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="MIPS\JitCommon\JitBlockCache.cpp">
<Filter>MIPS\JitCommon</Filter>
</ClCompile>
<ClCompile Include="Cwcheat.cpp">
<Filter>Core</Filter>
</ClCompile>
@@ -925,9 +919,6 @@
<ClCompile Include="Debugger\WebSocket\InputBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="MIPS\fake\FakeJit.cpp">
<Filter>MIPS\fake</Filter>
</ClCompile>
<ClCompile Include="Debugger\LineInfo.cpp">
<Filter>Debugger</Filter>
</ClCompile>
@@ -2079,9 +2070,6 @@
<ClInclude Include="Debugger\WebSocket\InputBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="MIPS\fake\FakeJit.h">
<Filter>MIPS\fake</Filter>
</ClInclude>
<ClInclude Include="Debugger\LineInfo.h">
<Filter>Debugger</Filter>
</ClInclude>
+1
View File
@@ -20,6 +20,7 @@
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
#include <cstddef>
#include <map>
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/ARM64/Arm64IRJit.h"
-3
View File
@@ -278,10 +278,7 @@ void IRFrontend::DoJit(u32 em_address, std::vector<IRInst> &instructions, u32 &m
js.cancel = false;
js.blockStart = em_address;
js.compilerPC = em_address;
js.initialBlockSize = 0;
js.nextExit = 0;
js.downcountAmount = 0;
js.curBlock = nullptr;
js.compiling = true;
js.hadBreakpoints = false;
js.blockWrotePrefixes = false;
-8
View File
@@ -245,14 +245,6 @@ bool IRJit::DescribeCodePtr(const u8 *ptr, std::string &name) {
return false;
}
void IRJit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
Crash();
}
void IRJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
Crash();
}
void IRBlockCache::Clear() {
for (int i = 0; i < (int)blocks_.size(); ++i) {
int cookie = compileToNative_ ? blocks_[i].GetNativeOffset() : blocks_[i].GetIRArenaOffset();
-5
View File
@@ -214,8 +214,6 @@ public:
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
// Not using a regular block cache.
JitBlockCache *GetBlockCache() override { return nullptr; }
JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks_; }
void ClearCache() override;
@@ -229,9 +227,6 @@ public:
const u8 *GetDispatcher() const override { return nullptr; }
const u8 *GetCrashHandler() const override { return nullptr; }
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override;
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
// This gets overridden by the native-backed IR jits.
const u8 *GetCodeBase() const override { return nullptr; }
+1
View File
@@ -19,6 +19,7 @@
#include <unordered_map>
#include "Common/CodeBlock.h"
#include "Core/MIPS/IR/IRJit.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
-540
View File
@@ -1,540 +0,0 @@
// Copyright (c) 2012- PPSSPP Project / Dolphin 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/.
#include "ppsspp_config.h"
#include <cstddef>
#include <algorithm>
#include "ext/xxhash.h"
#include "Common/CommonTypes.h"
#include "Common/Profiler/Profiler.h"
#ifdef _WIN32
#include "Common/CommonWindows.h"
#endif
#include "Core/Core.h"
#include "Core/MemMap.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/CoreTiming.h"
#include "Core/Reporting.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
constexpr u32 INVALID_EXIT = 0xFFFFFFFF;
constexpr u32 SENTINEL_VAL = 0xc0ffeefe;
static uint64_t HashJitBlock(const JitBlock &b) {
PROFILE_THIS_SCOPE("jithash");
if (JIT_USE_COMPILEDHASH) {
if (Memory::IsValidRange(b.originalAddress, b.originalSize * 4)) {
return XXH3_64bits(Memory::GetPointerUnchecked(b.originalAddress), b.originalSize * 4);
} else {
// Hm, this would be bad.
return 0;
}
}
return 0;
}
bool JitBlock::ContainsAddress(u32 address) const {
// WARNING - THIS DOES NOT WORK WITH JIT INLINING ENABLED.
// However, that doesn't exist yet so meh.
return address >= originalAddress && address < originalAddress + 4 * originalSize;
}
void JitBlock::DoIntegrityCheck(u32 emAddress, int num, const char *reason) const {
_assert_msg_(emAddress == originalAddress, "%s: Bad orig %08x (expected: %08x, snt: %08x) in block %d (b.num: %d) sz: %d", reason, originalAddress, emAddress, sentinel, num, blockNum, codeSize);
// Also alert just if the sentinel got corrupted.
_assert_msg_(sentinel == SENTINEL_VAL, "%s: Block %d(%d) sentinel got corrupted: %08x != %08x (origAddr: %08x)", reason, num, blockNum, sentinel, SENTINEL_VAL, originalAddress);
}
JitBlockCache::JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock) : codeBlock_(codeBlock) {}
JitBlockCache::~JitBlockCache() {
Shutdown();
}
bool JitBlockCache::IsFull() const {
// Subtract some amount to safely leave space for some proxy blocks (now obsolete, but let's still have some extra margin).
return num_blocks_ >= MAX_NUM_BLOCKS - 64;
}
void JitBlockCache::Init() {
blocks_ = new JitBlock[MAX_NUM_BLOCKS];
Clear();
}
void JitBlockCache::Shutdown() {
Clear(); // Make sure proxy block links are deleted
delete [] blocks_;
blocks_ = 0;
num_blocks_ = 0;
}
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
// is full and when saving and loading states.
void JitBlockCache::Clear() {
// Note: We intentionally clear the block_map_ first to avoid O(N^2) behavior in RemoveBlockMap
block_map_.clear();
for (int i = 0; i < num_blocks_; i++) {
DestroyBlock(i, DestroyType::CLEAR);
}
links_to_.clear();
num_blocks_ = 0;
blockMemRanges_[JITBLOCK_RANGE_SCRATCH] = std::make_pair(0xFFFFFFFF, 0x00000000);
blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM] = std::make_pair(0xFFFFFFFF, 0x00000000);
blockMemRanges_[JITBLOCK_RANGE_RAMTOP] = std::make_pair(0xFFFFFFFF, 0x00000000);
}
void JitBlockCache::Reset() {
Shutdown();
Init();
}
int JitBlockCache::AllocateBlock(u32 startAddress) {
_assert_(num_blocks_ < MAX_NUM_BLOCKS);
const int numBlocks = num_blocks_;
JitBlock &b = blocks_[numBlocks];
b.invalid = false;
b.originalAddress = startAddress;
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) {
b.exitAddress[i] = INVALID_EXIT;
b.exitPtrs[i] = 0;
b.linkStatus[i] = false;
}
b.blockNum = numBlocks;
b.sentinel = SENTINEL_VAL;
num_blocks_ = numBlocks + 1; //commit the current block
return numBlocks;
}
void JitBlockCache::AddBlockMap(int block_num) {
const JitBlock &b = blocks_[block_num];
// Convert the logical address to a physical address for the block map
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
block_map_[std::make_pair(pAddr + 4 * b.originalSize, pAddr)] = block_num;
}
void JitBlockCache::RemoveBlockMap(int block_num) {
const JitBlock &b = blocks_[block_num];
if (b.invalid) {
return;
}
const u32 pAddr = b.originalAddress & 0x1FFFFFFF;
auto it = block_map_.find(std::make_pair(pAddr + 4 * b.originalSize, pAddr));
if (it != block_map_.end() && it->second == (u32)block_num) {
block_map_.erase(it);
} else {
// It wasn't in there, or it has the wrong key. Let's search...
// TODO: This is O(n), so O(n^2) when called for every block.
for (auto it = block_map_.begin(); it != block_map_.end(); ++it) {
if (it->second == (u32)block_num) {
_dbg_assert_(false);
block_map_.erase(it);
break;
}
}
}
}
static void ExpandRange(std::pair<u32, u32> &range, u32 newStart, u32 newEnd) {
range.first = std::min(range.first, newStart);
range.second = std::max(range.second, newEnd);
}
void JitBlockCache::FinalizeBlock(int block_num, bool block_link) {
JitBlock &b = blocks_[block_num];
b.originalFirstOpcode = Memory::ReadUnchecked_Instruction(b.originalAddress);
Memory::WriteBlockShadow(b.originalAddress, GetShadowValueForBlock(block_num));
b.compiledHash = HashJitBlock(b);
AddBlockMap(block_num);
if (block_link) {
for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) {
if (b.exitAddress[i] != INVALID_EXIT) {
links_to_.emplace(b.exitAddress[i], block_num);
}
}
LinkBlock(block_num);
LinkBlockExits(block_num);
}
const u32 blockEnd = b.originalAddress + b.originalSize * 4 - 4;
if (Memory::IsScratchpadAddress(b.originalAddress)) {
ExpandRange(blockMemRanges_[JITBLOCK_RANGE_SCRATCH], b.originalAddress, blockEnd);
}
const u32 halfUserMemory = (PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()) / 2;
if (b.originalAddress < PSP_GetUserMemoryBase() + halfUserMemory) {
ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM], b.originalAddress, blockEnd);
}
// >=, not >: the bottom check above is strict, so a block sitting exactly on the midpoint
// would otherwise land in neither range.
if (blockEnd >= PSP_GetUserMemoryBase() + halfUserMemory) {
ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMTOP], b.originalAddress, blockEnd);
}
}
bool JitBlockCache::RangeMayHaveBlocks(u32 start, u32 end) const {
for (int i = 0; i < JITBLOCK_RANGE_COUNT; ++i) {
if (end >= blockMemRanges_[i].first && start <= blockMemRanges_[i].second) {
return true;
}
}
return false;
}
static int binary_search(const JitBlock blocks_[], const u8 *baseoff, int imin, int imax) {
while (imin < imax) {
int imid = (imin + imax) >> 1;
if (blocks_[imid].normalEntry < baseoff)
imin = imid + 1;
else
imax = imid;
}
if ((imax == imin) && (blocks_[imin].normalEntry == baseoff))
return imin;
else
return -1;
}
int JitBlockCache::GetBlockNumberFromShadowValue(u32 value) const {
if (!num_blocks_ || value == 0)
return -1;
const u8 *baseoff = codeBlock_->GetBasePtr() + value;
if (baseoff >= codeBlock_->GetCodePtr()) {
ERROR_LOG(Log::JIT, "JitBlockCache: Invalid block shadow value %08x", value);
return -1;
}
int bl = binary_search(blocks_, baseoff, 0, num_blocks_ - 1);
if (bl >= 0 && blocks_[bl].invalid) {
return -1;
} else {
return bl;
}
}
u32 JitBlockCache::GetShadowValueForBlock(int blockNum) const {
// Never 0, the dispatcher is at the start of the code space.
const u32 off = (u32)(blocks_[blockNum].normalEntry - codeBlock_->GetBasePtr());
_dbg_assert_(off != 0);
return off;
}
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr) const {
if (!blocks_ || !Memory::IsValid4AlignedAddress(addr))
return -1;
// A replacement hook's value is the dispatcher's compile path, which isn't any block's entry.
int bl = GetBlockNumberFromShadowValue(Memory::ReadBlockShadow(addr));
if (bl < 0) {
return -1;
}
if (blocks_[bl].originalAddress != addr)
return -1;
return bl;
}
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers) const {
for (int i = 0; i < num_blocks_; i++)
if (blocks_[i].ContainsAddress(em_address))
block_numbers->push_back(i);
}
int JitBlockCache::GetBlockNumberFromAddress(u32 em_address) const {
for (int i = 0; i < num_blocks_; i++) {
if (blocks_[i].ContainsAddress(em_address))
return i;
}
return -1;
}
u32 JitBlockCache::GetAddressFromBlockPtr(const u8 *ptr) const {
if (!codeBlock_->IsInSpace(ptr))
return (u32)-1;
for (int i = 0; i < num_blocks_; ++i) {
const auto &b = blocks_[i];
if (!b.invalid && ptr >= b.checkedEntry && ptr < b.normalEntry + b.codeSize) {
return b.originalAddress;
}
}
// It's in jit somewhere, but we must have deleted it.
return 0;
}
MIPSOpcode JitBlockCache::GetOriginalFirstOp(int block_num) const {
if (block_num >= num_blocks_ || block_num < 0) {
return MIPSOpcode(block_num);
}
return blocks_[block_num].originalFirstOpcode;
}
void JitBlockCache::LinkBlockExits(int i) {
JitBlock &b = blocks_[i];
if (b.invalid) {
// This block is dead. Don't relink it.
return;
}
for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) {
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
if (destinationBlock == -1) {
continue;
}
JitBlock &eb = blocks_[destinationBlock];
// Make sure the destination is not invalid.
if (!eb.invalid) {
MIPSComp::jit->LinkBlock(b.exitPtrs[e], eb.checkedEntry);
b.linkStatus[e] = true;
}
}
}
}
void JitBlockCache::LinkBlock(int i) {
LinkBlockExits(i);
JitBlock &b = blocks_[i];
// equal_range(b) returns pair<iterator,iterator> representing the range
// of element with key b
auto ppp = links_to_.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (auto iter = ppp.first; iter != ppp.second; ++iter) {
// INFO_LOG(Log::JIT, "Linking block %i to block %i", iter->second, i);
LinkBlockExits(iter->second);
}
}
void JitBlockCache::UnlinkBlock(int i) {
JitBlock &b = blocks_[i];
auto ppp = links_to_.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (auto iter = ppp.first; iter != ppp.second; ++iter) {
if ((size_t)iter->second >= num_blocks_) {
// Something probably went very wrong. Try to stumble along nevertheless.
ERROR_LOG(Log::JIT, "UnlinkBlock: Invalid block number %d", iter->second);
continue;
}
JitBlock &sourceBlock = blocks_[iter->second];
for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) {
if (sourceBlock.exitAddress[e] == b.originalAddress)
sourceBlock.linkStatus[e] = false;
}
}
}
void JitBlockCache::DestroyBlock(int block_num, DestroyType type) {
if (block_num < 0 || block_num >= num_blocks_) {
ERROR_LOG_REPORT(Log::JIT, "DestroyBlock: Invalid block number %d", block_num);
return;
}
JitBlock *b = &blocks_[block_num];
// No point it being in there anymore.
RemoveBlockMap(block_num);
// TODO: Handle the case when there's a proxy block and a regular JIT block at the same location.
// In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled).
if (b->invalid) {
if (type == DestroyType::INVALIDATE)
ERROR_LOG(Log::JIT, "Invalidating invalid block %d", block_num);
return;
}
b->invalid = true;
// Only clearing the shadow entry needs a valid address. The unlinking below has to
// happen either way - otherwise other blocks go on jumping straight into this one, which
// we've just marked invalid.
if (Memory::IsValid4AlignedAddress(b->originalAddress)) {
// A mirror may have replaced the entry with its own block, leave that alone.
Memory::ClearBlockShadow(b->originalAddress, GetShadowValueForBlock(block_num));
} else {
_dbg_assert_msg_(false, "Destroying block with invalid original address: %08x (block num: %d)", b->originalAddress, block_num);
}
// It's not safe to set normalEntry to 0 here, since we use a binary search
// that looks at that later to find blocks. Marking it invalid is enough.
UnlinkBlock(block_num);
if (b->checkedEntry) {
// We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive.
if (type != DestroyType::CLEAR) {
u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry);
MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress);
}
} else {
ERROR_LOG(Log::JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num);
}
}
void JitBlockCache::InvalidateICache(u32 address, const u32 length) {
// Convert the logical address to a physical address for the block map
const u32 pAddr = address & 0x1FFFFFFF;
const u32 pEnd = pAddr + length;
if (pEnd < pAddr) {
ERROR_LOG(Log::JIT, "Bad InvalidateICache: %08x with len=%d", address, length);
return;
}
if (pAddr == 0 && pEnd >= 0x1FFFFFFF) {
InvalidateChangedBlocks();
return;
}
// Blocks may start and end in overlapping ways, and destroying one invalidates iterators.
// So after destroying one, we start over.
do {
restart:
auto next = block_map_.lower_bound(std::make_pair(pAddr, 0));
// block_map_ is keyed by byte addresses, so the margin has to cover the longest
// possible block in bytes, not instructions.
auto last = block_map_.upper_bound(std::make_pair(pEnd + 4 * MAX_BLOCK_INSTRUCTIONS, 0));
// Note that if next is end(), last will be end() too (equal.)
for (; next != last; ++next) {
const u32 blockStart = next->first.second;
const u32 blockEnd = next->first.first;
if (blockStart < pEnd && blockEnd > pAddr) {
DestroyBlock(next->second, DestroyType::INVALIDATE);
// Our iterator is now invalid. Break and search again.
// Most of the time there shouldn't be a bunch of matching blocks.
goto restart;
}
}
// We got here - it wasn't in the map at all (or anymore.)
} while (false);
}
void JitBlockCache::InvalidateChangedBlocks() {
// The primary goal of this is to make sure block linking is cleared up.
for (int block_num = 0; block_num < num_blocks_; ++block_num) {
JitBlock &b = blocks_[block_num];
if (b.invalid)
continue;
bool changed = false;
if (JIT_USE_COMPILEDHASH) {
changed = b.compiledHash != HashJitBlock(b);
} else {
changed = Memory::ReadUnchecked_U32(b.originalAddress) != b.originalFirstOpcode.encoding;
}
if (changed) {
DEBUG_LOG(Log::JIT, "Invalidating changed block at %08x", b.originalAddress);
DestroyBlock(block_num, DestroyType::INVALIDATE);
}
}
}
int JitBlockCache::GetBlockExitSize() {
#if PPSSPP_ARCH(ARM)
// Will depend on the sequence found to encode the destination address.
return 0;
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
return 15;
#elif PPSSPP_ARCH(ARM64)
// Will depend on the sequence found to encode the destination address.
return 0;
#elif PPSSPP_ARCH(RISCV64)
// Will depend on the sequence found to encode the destination address.
return 0;
#elif PPSSPP_ARCH(LOONGARCH64)
// Will depend on the sequence found to encode the destination address.
return 0;
#else
#warning GetBlockExitSize unimplemented
return 0;
#endif
}
void JitBlockCache::ComputeStats(BlockCacheStats &bcStats) const {
double totalBloat = 0.0;
double maxBloat = 0.0;
double minBloat = 1000000000.0;
for (int i = 0; i < num_blocks_; i++) {
const JitBlock *b = GetBlock(i);
double codeSize = (double)b->codeSize;
if (codeSize == 0)
continue;
double origSize = (double)(4 * b->originalSize);
double bloat = codeSize / origSize;
if (bloat < minBloat) {
minBloat = bloat;
bcStats.minBloatBlock = b->originalAddress;
}
if (bloat > maxBloat) {
maxBloat = bloat;
bcStats.maxBloatBlock = b->originalAddress;
}
totalBloat += bloat;
}
bcStats.numBlocks = num_blocks_;
bcStats.minBloat = (float)minBloat;
bcStats.maxBloat = (float)maxBloat;
bcStats.avgBloat = (float)(totalBloat / (double)num_blocks_);
}
JitBlockDebugInfo JitBlockCache::GetBlockDebugInfo(int blockNum) const {
JitBlockDebugInfo debugInfo{};
const JitBlock *block = GetBlock(blockNum);
debugInfo.originalAddress = block->originalAddress;
debugInfo.origDisasm.reserve(((block->originalAddress + block->originalSize * 4) - block->originalAddress) / 4);
for (u32 addr = block->originalAddress; addr < block->originalAddress + block->originalSize * 4; addr += 4) {
char temp[256];
MIPSDisAsm(Replacement_GetDisplayOp(addr), addr, temp, sizeof(temp), true);
std::string mipsDis = temp;
debugInfo.origDisasm.push_back(mipsDis);
}
#if PPSSPP_ARCH(ARM)
debugInfo.targetDisasm = DisassembleArm2(block->normalEntry, block->codeSize);
#elif PPSSPP_ARCH(ARM64)
debugInfo.targetDisasm = DisassembleArm64(block->normalEntry, block->codeSize);
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
debugInfo.targetDisasm = DisassembleX86(block->normalEntry, block->codeSize);
#elif PPSSPP_ARCH(RISCV64)
debugInfo.targetDisasm = DisassembleRV64(block->normalEntry, block->codeSize);
#elif PPSSPP_ARCH(LOONGARCH64)
debugInfo.targetDisasm = DisassembleLA64(block->normalEntry, block->codeSize);
#endif
return debugInfo;
}
+1 -143
View File
@@ -18,20 +18,10 @@
#pragma once
#include <cstdint>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include <vector>
#include "ppsspp_config.h"
#include "Common/CommonTypes.h"
#include "Common/CodeBlock.h"
#include "Core/MIPS/MIPS.h"
// Can probably reduce to 2 now that block continuation is gone.
const int MAX_JIT_BLOCK_EXITS = 4;
constexpr bool JIT_USE_COMPILEDHASH = true;
struct BlockCacheStats {
int numBlocks;
@@ -42,41 +32,6 @@ struct BlockCacheStats {
u32 maxBloatBlock;
};
enum class DestroyType {
DESTROY,
INVALIDATE,
// Skips jit unlink, since it'll be poisoned anyway.
CLEAR,
};
// We should be careful not to access these block structures during runtime as they are large.
// Fine to mess with them at block compile time though.
struct JitBlock {
bool ContainsAddress(u32 address) const;
void DoIntegrityCheck(u32 emAddress, int blockNum, const char *reason) const;
const u8 *checkedEntry; // const, we have to translate to writable.
const u8 *normalEntry;
u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump
u32 exitAddress[MAX_JIT_BLOCK_EXITS]; // 0xFFFFFFFF == unknown
u32 sentinel;
u32 originalAddress;
MIPSOpcode originalFirstOpcode; //to be able to restore
uint64_t compiledHash;
u32 codeSize; // in bytes, so it doesn't fit in a u16 for a long block
u16 originalSize; // in instructions, bounded by MAX_BLOCK_INSTRUCTIONS
u16 blockNum;
bool invalid;
bool linkStatus[MAX_JIT_BLOCK_EXITS];
};
typedef void (*CompiledCode)();
struct JitBlockDebugInfo {
uint32_t originalAddress;
std::vector<std::string> origDisasm;
@@ -109,100 +64,3 @@ public:
virtual ~JitBlockCacheDebugInterface() {}
};
class JitBlockCache : public JitBlockCacheDebugInterface {
public:
JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock);
~JitBlockCache();
int AllocateBlock(u32 emAddress);
void FinalizeBlock(int block_num, bool block_link);
void Clear();
void Init();
void Shutdown();
void Reset();
bool IsFull() const;
void ComputeStats(BlockCacheStats &bcStats) const override;
// Code Cache
JitBlock *GetBlock(int no) { return &blocks_[no]; }
const JitBlock *GetBlock(int no) const { return &blocks_[no]; }
// Fast way to get a block. Only works on the first source-cpu instruction of a block.
int GetBlockNumberFromStartAddress(u32 em_address) const override;
// slower, but can get numbers from within blocks, not just the first instruction.
// WARNING! WILL NOT WORK WITH JIT INLINING ENABLED (not yet a feature but will be soon)
// Returns a list of block numbers - only one block can start at a particular address, but they CAN overlap.
// This one is slow so should only be used for one-shots from the debugger UI, not for anything during runtime.
void GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers) const;
// Similar to above, but only the first matching address.
int GetBlockNumberFromAddress(u32 em_address) const;
int GetBlockNumberFromShadowValue(u32 value) const;
u32 GetAddressFromBlockPtr(const u8 *ptr) const;
MIPSOpcode GetOriginalFirstOp(int block_num) const;
bool RangeMayHaveBlocks(u32 start, u32 end) const;
// DOES NOT WORK CORRECTLY WITH JIT INLINING
void InvalidateICache(u32 address, const u32 length);
void InvalidateChangedBlocks();
void DestroyBlock(int block_num, DestroyType type);
int GetNumBlocks() const override { return num_blocks_; }
bool IsValidBlock(int blockNum) const override { return blockNum >= 0 && blockNum < num_blocks_ && !blocks_[blockNum].invalid; }
JitBlockMeta GetBlockMeta(int blockNum) const override {
JitBlockMeta meta{};
if (IsValidBlock(blockNum)) {
meta.valid = true;
meta.addr = blocks_[blockNum].originalAddress;
meta.sizeInBytes = blocks_[blockNum].originalSize;
}
return meta;
}
JitBlockProfileStats GetBlockProfileStats(int blockNum) const override {
return JitBlockProfileStats{};
}
static int GetBlockExitSize();
JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const override;
enum {
MAX_BLOCK_INSTRUCTIONS = 0x4000,
};
private:
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);
void AddBlockMap(int block_num);
void RemoveBlockMap(int block_num);
// What the block shadow holds for a block: its normal entry's offset into the code space.
u32 GetShadowValueForBlock(int block_num) const;
int num_blocks_ = 0;
CodeBlockCommon *codeBlock_;
JitBlock *blocks_ = nullptr;
std::unordered_multimap<u32, int> links_to_;
std::map<std::pair<u32,u32>, u32> block_map_; // (end_addr, start_addr) -> number
enum {
JITBLOCK_RANGE_SCRATCH = 0,
JITBLOCK_RANGE_RAMBOTTOM = 1,
JITBLOCK_RANGE_RAMTOP = 2,
JITBLOCK_RANGE_COUNT = 3,
};
std::pair<u32, u32> blockMemRanges_[JITBLOCK_RANGE_COUNT];
enum {
// Where does this number come from?
MAX_NUM_BLOCKS = 65536 * 4
};
};
+2 -3
View File
@@ -48,8 +48,6 @@
#include "../RiscV/RiscVJit.h"
#elif PPSSPP_ARCH(LOONGARCH64)
#include "../LoongArch64/LoongArch64Jit.h"
#else
#include "../fake/FakeJit.h"
#endif
namespace MIPSComp {
@@ -115,7 +113,8 @@ namespace MIPSComp {
#elif PPSSPP_ARCH(LOONGARCH64)
return new MIPSComp::LoongArch64Jit(mipsState);
#else
return new MIPSComp::FakeJit(mipsState);
// No native JIT for this architecture.
return new MIPSComp::IRJit(mipsState, false);
#endif
}
-8
View File
@@ -31,8 +31,6 @@ std::vector<std::string> DisassembleX86(const u8 *data, int size);
std::vector<std::string> DisassembleRV64(const u8 *data, int size);
std::vector<std::string> DisassembleLA64(const u8 *data, int size);
struct JitBlock;
class JitBlockCache;
class JitBlockCacheDebugInterface;
class PointerWrap;
@@ -127,7 +125,6 @@ namespace MIPSComp {
}
virtual const u8 *GetDispatcher() const = 0;
virtual const u8 *GetCrashHandler() const = 0;
virtual JitBlockCache *GetBlockCache() = 0;
virtual JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() = 0;
virtual void InvalidateCacheAt(u32 em_address, int length = 4) = 0;
virtual void DoState(PointerWrap &p) = 0;
@@ -138,11 +135,6 @@ namespace MIPSComp {
// What the block shadow holds for a replacement hook without a compiled block. The
// dispatcher has to send this to the compiler.
virtual u32 GetBlockShadowHookValue() const { return Memory::BLOCK_SHADOW_HOOK_NO_JIT; }
// Block linking. This may need to work differently for whole-function JITs and stuff
// like that.
virtual void LinkBlock(u8 *exitPoint, const u8 *entryPoint) = 0;
virtual void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) = 0;
};
typedef void (MIPSFrontendInterface::*MIPSCompileFunc)(MIPSOpcode opcode);
-21
View File
@@ -19,28 +19,11 @@
#include "Common/CPUDetect.h"
#include "Core/Config.h"
#include "Core/MIPS/JitCommon/JitState.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/MemMap.h"
#include "Common/MemoryUtil.h"
namespace MIPSComp {
void JitState::Begin(JitBlock *block) {
cancel = false;
blockStart = block->originalAddress;
compilerPC = block->originalAddress;
initialBlockSize = 0;
nextExit = 0;
downcountAmount = 0;
curBlock = block;
compiling = true;
inDelaySlot = false;
blockWrotePrefixes = false;
afterOp = JitState::AFTER_NONE;
kernelMode = Memory::IsKernelCodeAddress(block->originalAddress);
PrefixStart();
}
JitOptions::JitOptions() {
disableFlags = (JitDisable)g_Config.uJitDisableFlags;
@@ -61,11 +44,7 @@ void JitState::Begin(JitBlock *block) {
// We can get block linking to work with W^X by doing even more unprotect/re-protect, but let's try without first.
// enableBlocklink = !PlatformIsWXExclusive(); // Revert to this line if block linking is slow in W^X mode
#if PPSSPP_ARCH(MIPS)
enableBlocklink = false;
#else
enableBlocklink = !Disabled(JitDisable::BLOCKLINK);
#endif
useStaticAlloc = false;
enablePointerify = false;
#if PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(RISCV64) || PPSSPP_ARCH(LOONGARCH64)
-27
View File
@@ -23,22 +23,8 @@
#include "Common/Log.h"
#include "Core/MIPS/MIPS.h"
struct JitBlock;
class JitBlockCache;
namespace MIPSComp {
enum CompileDelaySlotFlags {
// Easy, nothing extra.
DELAYSLOT_NICE = 0,
// Flush registers after delay slot.
DELAYSLOT_FLUSH = 1,
// Preserve flags.
DELAYSLOT_SAFE = 2,
// Flush registers after and preserve flags.
DELAYSLOT_SAFE_FLUSH = DELAYSLOT_FLUSH | DELAYSLOT_SAFE,
};
struct JitState {
enum PrefixState {
PREFIX_UNKNOWN = 0x00,
@@ -47,29 +33,18 @@ namespace MIPSComp {
PREFIX_KNOWN_DIRTY = 0x11,
};
enum AfterOp {
AFTER_NONE = 0x00,
AFTER_CORE_STATE = 0x01,
};
u32 compilerPC;
u32 blockStart;
u32 initialBlockSize;
int nextExit;
bool cancel;
bool inDelaySlot;
bool kernelMode; // Changes what instructions are allowed.
// See JitState::AfterOp for values.
int afterOp;
int downcountAmount;
int numInstructions;
bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block
bool hadBreakpoints;
JitBlock *curBlock;
u8 hasSetRounding = 0;
u8 lastSetRounding = 0;
const u8 *currentRoundingFunc = nullptr;
// VFPU prefix magic
bool startDefaultPrefix = true;
@@ -81,8 +56,6 @@ namespace MIPSComp {
PrefixState prefixTFlag = PREFIX_UNKNOWN;
PrefixState prefixDFlag = PREFIX_UNKNOWN;
void Begin(JitBlock *block);
void PrefixStart() {
if (startDefaultPrefix) {
EatPrefix();
+1
View File
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstddef>
#include <map>
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/LoongArch64/LoongArch64Jit.h"
+1
View File
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstddef>
#include <map>
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/RiscV/RiscVJit.h"
-227
View File
@@ -1,227 +0,0 @@
// Copyright (c) 2012- 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/.
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/Reporting.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/HLE/ReplaceTables.h"
#include "FakeEmitter.h"
#include "FakeJit.h"
#include "CPUDetect.h"
void DisassembleFake(const u8 *data, int size) {
}
namespace MIPSComp
{
FakeJit::FakeJit(MIPSState *mipsState) : blocks(mipsState, this), mips_(mipsState), js()
{
logBlocks = 0;
dontLogBlocks = 0;
blocks.Init();
}
void FakeJit::DoState(PointerWrap &p) {
auto s = p.Section("Jit", 1, 2);
if (!s)
return;
Do(p, js.startDefaultPrefix);
if (s >= 2) {
Do(p, js.hasSetRounding);
if (p.mode == PointerWrap::MODE_READ) {
js.lastSetRounding = 0;
}
} else {
js.hasSetRounding = 1;
}
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they load a state, we can end up hitting it by mistake, since it's based on PC and ticks.
g_breakpoints.SetSkipFirst(0);
}
// This is here so the savestate matches between jit and non-jit.
void FakeJit::DoDummyState(PointerWrap &p)
{
auto s = p.Section("FakeJit", 1, 2);
if (!s)
return;
bool dummy = false;
Do(p, dummy);
if (s >= 2) {
dummy = true;
Do(p, dummy);
}
}
void FakeJit::FlushAll()
{
//gpr.FlushAll();
//fpr.FlushAll();
FlushPrefixV();
}
void FakeJit::FlushPrefixV()
{
}
void FakeJit::ClearCache()
{
blocks.Clear();
ClearCodeSpace(0);
//GenerateFixedCode();
}
void FakeJit::InvalidateCacheAt(u32 em_address, int length) {
if (blocks.RangeMayHaveBlocks(em_address, em_address + length)) {
blocks.InvalidateICache(em_address, length);
}
}
void FakeJit::EatInstruction(MIPSOpcode op) {
MIPSInfo info = MIPSGetInfo(op);
if (info & DELAYSLOT) {
ERROR_LOG_REPORT_ONCE(ateDelaySlot, Log::JIT, "Ate a branch op.");
}
if (js.inDelaySlot) {
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, Log::JIT, "Ate an instruction inside a delay slot.");
}
js.numInstructions++;
js.compilerPC += 4;
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
}
void FakeJit::CompileDelaySlot(int flags)
{
}
void FakeJit::Compile(u32 em_address) {
}
void FakeJit::RunLoopUntil(u64 globalticks) {
MIPSInterpret_RunUntil(currentMIPS, globalticks);
}
void FakeJit::DoJit(u32 em_address, JitBlock *b) {
_assert_(false);
}
bool FakeJit::DescribeCodePtr(const u8 *ptr, std::string &name)
{
// TODO: Not used by anything yet.
return false;
}
void FakeJit::Comp_ReplacementFunc(MIPSOpcode op)
{
}
void FakeJit::Comp_Generic(MIPSOpcode op)
{
FlushAll();
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
if (func)
{
SaveDowncount();
RestoreDowncount();
}
const MIPSInfo info = MIPSGetInfo(op);
if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0)
{
// If it does eat them, it'll happen in MIPSCompileOp().
if ((info & OUT_EAT_PREFIX) == 0)
js.PrefixUnknown();
}
}
void FakeJit::MovFromPC(FakeReg r) {
}
void FakeJit::MovToPC(FakeReg r) {
}
void FakeJit::SaveDowncount() {
}
void FakeJit::RestoreDowncount() {
}
void FakeJit::WriteDownCount(int offset) {
}
// Abuses R2
void FakeJit::WriteDownCountR(FakeReg reg) {
}
void FakeJit::RestoreRoundingMode(bool force) {
}
void FakeJit::ApplyRoundingMode(bool force) {
}
void FakeJit::UpdateRoundingMode() {
}
void FakeJit::WriteExit(u32 destination, int exit_num)
{
}
void FakeJit::WriteExitDestInR(FakeReg Reg)
{
}
void FakeJit::WriteSyscallExit()
{
}
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6) & 0x1F)
#define _POS ((op>>6) & 0x1F)
#define _SIZE ((op>>11) & 0x1F)
//memory regions:
//
// 08-0A
// 48-4A
// 04-05
// 44-45
// mov eax, addrreg
// shr eax, 28
// mov eax, [table+eax]
// mov dreg, [eax+offreg]
}
-177
View File
@@ -1,177 +0,0 @@
// Copyright (c) 2012- 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 "Common/FakeEmitter.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/MIPS/JitCommon/JitState.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "../MIPSVFPUUtils.h"
#ifndef offsetof
#include "stddef.h"
#endif
namespace MIPSComp {
typedef int FakeReg;
class FakeJit : public FakeGen::FakeXCodeBlock, public JitInterface, public MIPSFrontendInterface {
public:
FakeJit(MIPSState *mipsState);
void DoState(PointerWrap &p) override;
static void DoDummyState(PointerWrap &p);
const JitOptions &GetJitOptions() { return jo; }
void Comp_Generic(MIPSOpcode op) override;
void RunLoopUntil(u64 globalticks) override;
void Compile(u32 em_address) override; // Compiles a block at current MIPS PC
void DoJit(u32 em_address, JitBlock *b);
const u8 *GetCrashHandler() const override { return nullptr; }
bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); }
bool DescribeCodePtr(const u8 *ptr, std::string &name) override;
void CompileDelaySlot(int flags);
void EatInstruction(MIPSOpcode op);
void Comp_ReplacementFunc(MIPSOpcode op) override;
// Ops
void Comp_ITypeMem(MIPSOpcode op) override {}
void Comp_StoreSync(MIPSOpcode op) override {}
void Comp_Cache(MIPSOpcode op) override {}
void Comp_RelBranch(MIPSOpcode op) override {}
void Comp_RelBranchRI(MIPSOpcode op) override {}
void Comp_FPUBranch(MIPSOpcode op) override {}
void Comp_FPULS(MIPSOpcode op) override {}
void Comp_FPUComp(MIPSOpcode op) override {}
void Comp_Jump(MIPSOpcode op) override {}
void Comp_JumpReg(MIPSOpcode op) override {}
void Comp_Syscall(MIPSOpcode op) override {}
void Comp_Break(MIPSOpcode op) override {}
void Comp_IType(MIPSOpcode op) override {}
void Comp_RType2(MIPSOpcode op) override {}
void Comp_RType3(MIPSOpcode op) override {}
void Comp_ShiftType(MIPSOpcode op) override {}
void Comp_Allegrex(MIPSOpcode op) override {}
void Comp_Allegrex2(MIPSOpcode op) override {}
void Comp_VBranch(MIPSOpcode op) override {}
void Comp_MulDivType(MIPSOpcode op) override {}
void Comp_Special3(MIPSOpcode op) override {}
void Comp_FPU3op(MIPSOpcode op) override {}
void Comp_FPU2op(MIPSOpcode op) override {}
void Comp_mxc1(MIPSOpcode op) override {}
void Comp_DoNothing(MIPSOpcode op) override {}
void Comp_SV(MIPSOpcode op) override {}
void Comp_SVQ(MIPSOpcode op) override {}
void Comp_VPFX(MIPSOpcode op) override {}
void Comp_VVectorInit(MIPSOpcode op) override {}
void Comp_VMatrixInit(MIPSOpcode op) override {}
void Comp_VDot(MIPSOpcode op) override {}
void Comp_VecDo3(MIPSOpcode op) override {}
void Comp_VV2Op(MIPSOpcode op) override {}
void Comp_Mftv(MIPSOpcode op) override {}
void Comp_Vmfvc(MIPSOpcode op) override {}
void Comp_Vmtvc(MIPSOpcode op) override {}
void Comp_Vmmov(MIPSOpcode op) override {}
void Comp_VScl(MIPSOpcode op) override {}
void Comp_Vmmul(MIPSOpcode op) override {}
void Comp_Vmscl(MIPSOpcode op) override {}
void Comp_Vtfm(MIPSOpcode op) override {}
void Comp_VHdp(MIPSOpcode op) override {}
void Comp_VCrs(MIPSOpcode op) override {}
void Comp_VDet(MIPSOpcode op) override {}
void Comp_Vi2x(MIPSOpcode op) override {}
void Comp_Vx2i(MIPSOpcode op) override {}
void Comp_Vf2i(MIPSOpcode op) override {}
void Comp_Vi2f(MIPSOpcode op) override {}
void Comp_Vh2f(MIPSOpcode op) override {}
void Comp_Vcst(MIPSOpcode op) override {}
void Comp_Vhoriz(MIPSOpcode op) override {}
void Comp_VRot(MIPSOpcode op) override {}
void Comp_VIdt(MIPSOpcode op) override {}
void Comp_Vcmp(MIPSOpcode op) override {}
void Comp_Vcmov(MIPSOpcode op) override {}
void Comp_Viim(MIPSOpcode op) override {}
void Comp_Vfim(MIPSOpcode op) override {}
void Comp_VCrossQuat(MIPSOpcode op) override {}
void Comp_Vsgn(MIPSOpcode op) override {}
void Comp_Vocp(MIPSOpcode op) override {}
void Comp_ColorConv(MIPSOpcode op) override {}
void Comp_Vbfy(MIPSOpcode op) override {}
int Replace_fabsf() override { return 0; }
JitBlockCache *GetBlockCache() override { return &blocks; }
JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks; }
void ClearCache() override;
void InvalidateCacheAt(u32 em_address, int length = 4) override;
void UpdateFCR31() override {}
const u8 *GetDispatcher() const override {
return nullptr;
}
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override {}
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override {}
void EatPrefix() override { js.EatPrefix(); }
private:
void GenerateFixedCode();
void FlushAll();
void FlushPrefixV();
void WriteDownCount(int offset = 0);
void WriteDownCountR(FakeReg reg);
void RestoreRoundingMode(bool force = false);
void ApplyRoundingMode(bool force = false);
void UpdateRoundingMode();
void MovFromPC(FakeReg r);
void MovToPC(FakeReg r);
void SaveDowncount();
void RestoreDowncount();
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInR(FakeReg Reg);
void WriteSyscallExit();
JitBlockCache blocks;
JitOptions jo;
JitState js;
MIPSState *mips_;
int dontLogBlocks;
int logBlocks;
};
} // namespace MIPSComp
+1
View File
@@ -19,6 +19,7 @@
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
#include <cstddef>
#include <map>
#include "Common/StringUtils.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSTables.h"
+2
View File
@@ -1629,6 +1629,7 @@ std::string VertexDecoder::GetString(DebugShaderStringType stringType) const {
}
}
#ifdef VERTEXDECODER_JIT_BACKEND
VertexDecoderJitCache::VertexDecoderJitCache()
#if PPSSPP_ARCH(ARM64)
: fp(this)
@@ -1653,6 +1654,7 @@ VertexDecoderJitCache::VertexDecoderJitCache()
void VertexDecoderJitCache::Clear() {
ClearCodeSpace(0);
}
#endif
struct StepFunctionNameEntry {
StepFunction func;
+4 -6
View File
@@ -39,8 +39,6 @@
#include "Common/RiscVEmitter.h"
#elif PPSSPP_ARCH(LOONGARCH64)
#include "Common/LoongArch64Emitter.h"
#else
#include "Common/FakeEmitter.h"
#endif
// Keep this in 4 bits.
@@ -432,13 +430,13 @@ private:
#endif
};
#else
class VertexDecoderJitCache : public FakeGen::FakeXCodeBlock {
// No vertex decoder JIT for this architecture.
class VertexDecoderJitCache {
public:
VertexDecoderJitCache();
JittedVertexDecoder Compile(const VertexDecoder &dec, int32_t *jittedSize) {
return nullptr;
}
void Clear();
void Clear() {}
bool IsInSpace(const u8 *ptr) const { return false; }
};
#endif
-2
View File
@@ -848,8 +848,6 @@ static void DrawInternals(ImConfig &cfg) {
ImGui::Text("Base pointer: %p", Memory::base);
ImGui::Text("Main memory size: %08x", Memory::g_MemorySize);
if (MIPSComp::jit) {
const JitBlockCache *bc = MIPSComp::jit->GetBlockCache();
ImGui::Text("JIT block cache: %p", bc->GetBlock(0));
ImGui::Text("JIT code cache: %p", MIPSComp::jit->GetCodeBase());
ImGui::Text("MIPS state: %p", currentMIPS);
}
-1
View File
@@ -587,7 +587,6 @@
<ClCompile Include="..\..\Core\MIPS\IR\IRAnalysis.cpp" />
<ClCompile Include="..\..\Core\MIPS\IR\IRPassSimplify.cpp" />
<ClCompile Include="..\..\Core\MIPS\IR\IRRegCache.cpp" />
<ClCompile Include="..\..\Core\MIPS\JitCommon\JitBlockCache.cpp" />
<ClCompile Include="..\..\Core\MIPS\JitCommon\JitCommon.cpp" />
<ClCompile Include="..\..\Core\MIPS\JitCommon\JitState.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPS.cpp" />
-1
View File
@@ -214,7 +214,6 @@
<ClCompile Include="..\..\Core\MIPS\IR\IRAnalysis.cpp" />
<ClCompile Include="..\..\Core\MIPS\IR\IRPassSimplify.cpp" />
<ClCompile Include="..\..\Core\MIPS\IR\IRRegCache.cpp" />
<ClCompile Include="..\..\Core\MIPS\JitCommon\JitBlockCache.cpp" />
<ClCompile Include="..\..\Core\MIPS\JitCommon\JitCommon.cpp" />
<ClCompile Include="..\..\Core\MIPS\JitCommon\JitState.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPS.cpp" />
-1
View File
@@ -844,7 +844,6 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/FileSystems/VirtualDiscFileSystem.cpp \
$(SRC)/Core/FileSystems/tlzrc.cpp \
$(SRC)/Core/MIPS/JitCommon/JitCommon.cpp \
$(SRC)/Core/MIPS/JitCommon/JitBlockCache.cpp \
$(SRC)/Core/MIPS/JitCommon/JitState.cpp \
$(SRC)/Core/Util/AtracTrack.cpp \
$(SRC)/Core/Util/AudioFormat.cpp \
-1
View File
@@ -858,7 +858,6 @@ SOURCES_CXX += \
$(COREDIR)/Loaders.cpp \
$(COREDIR)/MIPS/JitCommon/JitCommon.cpp \
$(COREDIR)/MIPS/JitCommon/JitState.cpp \
$(COREDIR)/MIPS/JitCommon/JitBlockCache.cpp \
$(COREDIR)/MIPS/IR/IRAnalysis.cpp \
$(COREDIR)/MIPS/IR/IRCompALU.cpp \
$(COREDIR)/MIPS/IR/IRCompBranch.cpp \