mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-17 08:20:30 +02:00
Merge pull request #22573 from hrydgard/remove-fakejit
Remove FakeJit and the pre-IR JitBlockCache
This commit is contained in:
31 files changed
+17
-1620
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -337,7 +337,6 @@ add_library(Common STATIC
|
||||
CommonTypes.h
|
||||
CommonWindows.h
|
||||
DbgNew.h
|
||||
FakeEmitter.h
|
||||
FakeCPUDetect.cpp
|
||||
ExceptionHandlerSetup.cpp
|
||||
ExceptionHandlerSetup.h
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Common/CodeBlock.h"
|
||||
#include "Core/MIPS/IR/IRJit.h"
|
||||
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in new issue
Block a user