mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Introduce a Fake JIT for generic builds.
This commit is contained in:
parent
cbef4fae26
commit
a0086f6412
475
Common/FakeEmitter.h
Normal file
475
Common/FakeEmitter.h
Normal file
@ -0,0 +1,475 @@
|
||||
// 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 <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "MsgHandler.h"
|
||||
|
||||
// TODO: Check if Pandora still needs signal.h/kill here. Symbian doesn't.
|
||||
|
||||
// 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)
|
||||
{
|
||||
Type = type;
|
||||
Value = imm;
|
||||
Rotation = 0;
|
||||
}
|
||||
|
||||
Operand2(FakeReg Reg)
|
||||
{
|
||||
Type = TYPE_REG;
|
||||
Value = Reg;
|
||||
Rotation = 0;
|
||||
}
|
||||
Operand2(u8 imm, u8 rotation)
|
||||
{
|
||||
Type = TYPE_IMM;
|
||||
Value = imm;
|
||||
Rotation = rotation;
|
||||
}
|
||||
Operand2(FakeReg base, ShiftType type, FakeReg shift) // RSR
|
||||
{
|
||||
Type = TYPE_RSR;
|
||||
_assert_msg_(JIT, 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)// For IMM shifted register
|
||||
{
|
||||
if(shift == 32) shift = 0;
|
||||
switch (type)
|
||||
{
|
||||
case ST_LSL:
|
||||
_assert_msg_(JIT, shift < 32, "Invalid Operand2: LSL %u", shift);
|
||||
break;
|
||||
case ST_LSR:
|
||||
_assert_msg_(JIT, shift <= 32, "Invalid Operand2: LSR %u", shift);
|
||||
if (!shift)
|
||||
type = ST_LSL;
|
||||
if (shift == 32)
|
||||
shift = 0;
|
||||
break;
|
||||
case ST_ASR:
|
||||
_assert_msg_(JIT, shift < 32, "Invalid Operand2: ASR %u", shift);
|
||||
if (!shift)
|
||||
type = ST_LSL;
|
||||
if (shift == 32)
|
||||
shift = 0;
|
||||
break;
|
||||
case ST_ROR:
|
||||
_assert_msg_(JIT, shift < 32, "Invalid Operand2: ROR %u", shift);
|
||||
if (!shift)
|
||||
type = ST_LSL;
|
||||
break;
|
||||
case ST_RRX:
|
||||
_assert_msg_(JIT, 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_(JIT, false, "GetData with Invalid Type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
u32 IMMSR() // IMM shifted register
|
||||
{
|
||||
_assert_msg_(JIT, Type == TYPE_IMMSREG, "IMMSR must be imm shifted register");
|
||||
return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value);
|
||||
}
|
||||
u32 RSR() // Register shifted register
|
||||
{
|
||||
_assert_msg_(JIT, Type == TYPE_RSR, "RSR must be RSR Of Course");
|
||||
return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value;
|
||||
}
|
||||
u32 Rm()
|
||||
{
|
||||
_assert_msg_(JIT, Type == TYPE_REG, "Rm must be with Reg");
|
||||
return Value;
|
||||
}
|
||||
|
||||
u32 Imm5()
|
||||
{
|
||||
_assert_msg_(JIT, (Type == TYPE_IMM), "Imm5 not IMM value");
|
||||
return ((Value & 0x0000001F) << 7);
|
||||
}
|
||||
u32 Imm8()
|
||||
{
|
||||
_assert_msg_(JIT, (Type == TYPE_IMM), "Imm8Rot not IMM value");
|
||||
return Value & 0xFF;
|
||||
}
|
||||
u32 Imm8Rot() // IMM8 with Rotation
|
||||
{
|
||||
_assert_msg_(JIT, (Type == TYPE_IMM), "Imm8Rot not IMM value");
|
||||
_assert_msg_(JIT, (Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation);
|
||||
return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF);
|
||||
}
|
||||
u32 Imm12()
|
||||
{
|
||||
_assert_msg_(JIT, (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_(JIT, (Type == TYPE_IMM), "Imm12Mod not IMM");
|
||||
return ((Rotation & 0xF) << 8) | (Value & 0xFF);
|
||||
}
|
||||
u32 Imm16()
|
||||
{
|
||||
_assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM");
|
||||
return ( (Value & 0xF000) << 4) | (Value & 0x0FFF);
|
||||
}
|
||||
u32 Imm16Low()
|
||||
{
|
||||
return Imm16();
|
||||
}
|
||||
u32 Imm16High() // Returns high 16bits
|
||||
{
|
||||
_assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM");
|
||||
return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF);
|
||||
}
|
||||
u32 Imm24()
|
||||
{
|
||||
_assert_msg_(JIT, (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
|
||||
};
|
||||
|
||||
struct LiteralPool
|
||||
{
|
||||
intptr_t loc;
|
||||
u8* ldr_address;
|
||||
u32 val;
|
||||
};
|
||||
|
||||
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 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 FakeXEmitter
|
||||
{
|
||||
protected:
|
||||
u8 *region;
|
||||
size_t region_size;
|
||||
|
||||
public:
|
||||
FakeXCodeBlock() : region(NULL), region_size(0) {}
|
||||
virtual ~FakeXCodeBlock() { if (region) FreeCodeSpace(); }
|
||||
|
||||
// Call this before you generate any code.
|
||||
void AllocCodeSpace(int size) { }
|
||||
|
||||
// Always clear code space with breakpoints, so that if someone accidentally executes
|
||||
// uninitialized, it just breaks into the debugger.
|
||||
void ClearCodeSpace() { }
|
||||
|
||||
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
||||
void FreeCodeSpace() { }
|
||||
|
||||
bool IsInSpace(const u8 *ptr) const
|
||||
{
|
||||
return ptr >= region && ptr < region + region_size;
|
||||
}
|
||||
|
||||
// Cannot currently be undone. Will write protect the entire code region.
|
||||
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
|
||||
void WriteProtect() { }
|
||||
void UnWriteProtect() { }
|
||||
|
||||
void ResetCodePtr()
|
||||
{
|
||||
SetCodePtr(region);
|
||||
}
|
||||
|
||||
size_t GetSpaceLeft() const
|
||||
{
|
||||
return region_size - (GetCodePtr() - region);
|
||||
}
|
||||
|
||||
u8 *GetBasePtr() {
|
||||
return region;
|
||||
}
|
||||
|
||||
size_t GetOffset(const u8 *ptr) const {
|
||||
return ptr - region;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // _DOLPHIN_FAKE_CODEGEN_
|
@ -44,20 +44,15 @@
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
|
||||
#if defined(ARM)
|
||||
#include "Common/ArmEmitter.h"
|
||||
#include "Core/MIPS/ARM/ArmAsm.h"
|
||||
using namespace ArmGen;
|
||||
#elif defined(_M_IX86) || defined(_M_X64)
|
||||
#include "Common/x64Emitter.h"
|
||||
#include "Common/x64Analyzer.h"
|
||||
#include "Core/MIPS/x86/Asm.h"
|
||||
using namespace Gen;
|
||||
#elif defined(PPC)
|
||||
#include "Common/ppcEmitter.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
using namespace PpcGen;
|
||||
#else
|
||||
#error "Unsupported arch!"
|
||||
#warning "Unsupported arch!"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#endif
|
||||
// #include "JitBase.h"
|
||||
|
||||
|
@ -50,7 +50,11 @@ namespace PpcGen { class PPCXEmitter; }
|
||||
using namespace PpcGen;
|
||||
typedef PpcGen::PPCXCodeBlock CodeBlock;
|
||||
#else
|
||||
#error "Unsupported arch!"
|
||||
#warning "Unsupported arch!"
|
||||
#include "Common/FakeEmitter.h"
|
||||
namespace FakeGen { class FakeXEmitter; }
|
||||
using namespace FakeGen;
|
||||
typedef FakeGen::FakeXCodeBlock CodeBlock;
|
||||
#endif
|
||||
|
||||
#if defined(ARM)
|
||||
|
@ -32,8 +32,10 @@ struct JitBlock;
|
||||
#include "../PPC/PpcJit.h"
|
||||
#elif defined(ARM)
|
||||
#include "../ARM/ArmJit.h"
|
||||
#else
|
||||
#elif defined(_M_IX86) || defined(_M_X64)
|
||||
#include "../x86/Jit.h"
|
||||
#else
|
||||
#include "../fake/FakeJit.h"
|
||||
#endif
|
||||
|
||||
// Unlike on the PPC, opcode 0 is not unused and thus we have to choose another fake
|
||||
|
@ -32,12 +32,14 @@
|
||||
#include "Core/System.h"
|
||||
#include "Core/HLE/sceDisplay.h"
|
||||
|
||||
#if defined(ARM)
|
||||
#include "ARM/ArmJit.h"
|
||||
#elif defined(PPC)
|
||||
#if defined(PPC)
|
||||
#include "PPC/PpcJit.h"
|
||||
#else
|
||||
#elif defined(ARM)
|
||||
#include "ARM/ArmJit.h"
|
||||
#elif defined(_M_IX86) || defined(_M_X64)
|
||||
#include "x86/Jit.h"
|
||||
#else
|
||||
#include "fake/FakeJit.h"
|
||||
#endif
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
|
243
Core/MIPS/fake/FakeJit.cpp
Normal file
243
Core/MIPS/fake/FakeJit.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
// 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 "base/logging.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/Reporting.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/MIPSCodeUtils.h"
|
||||
#include "Core/MIPS/MIPSInt.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
|
||||
#include "FakeJit.h"
|
||||
#include "CPUDetect.h"
|
||||
|
||||
void DisassembleFake(const u8 *data, int size) {
|
||||
}
|
||||
|
||||
namespace MIPSComp
|
||||
{
|
||||
|
||||
FakeJitOptions::FakeJitOptions() {
|
||||
enableBlocklink = true;
|
||||
downcountInRegister = true;
|
||||
useBackJump = false;
|
||||
useForwardJump = false;
|
||||
cachePointers = true;
|
||||
immBranches = false;
|
||||
continueBranches = false;
|
||||
continueJumps = false;
|
||||
continueMaxInstructions = 300;
|
||||
}
|
||||
|
||||
Jit::Jit(MIPSState *mips) : blocks(mips, this), mips_(mips)
|
||||
{
|
||||
logBlocks = 0;
|
||||
dontLogBlocks = 0;
|
||||
blocks.Init();
|
||||
}
|
||||
|
||||
void Jit::DoState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("Jit", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
p.Do(js.startDefaultPrefix);
|
||||
if (s >= 2) {
|
||||
p.Do(js.hasSetRounding);
|
||||
js.lastSetRounding = 0;
|
||||
} else {
|
||||
js.hasSetRounding = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// This is here so the savestate matches between jit and non-jit.
|
||||
void Jit::DoDummyState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("Jit", 1, 2);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
bool dummy = false;
|
||||
p.Do(dummy);
|
||||
if (s >= 2) {
|
||||
dummy = true;
|
||||
p.Do(dummy);
|
||||
}
|
||||
}
|
||||
|
||||
void Jit::FlushAll()
|
||||
{
|
||||
FlushPrefixV();
|
||||
}
|
||||
|
||||
void Jit::FlushPrefixV()
|
||||
{
|
||||
}
|
||||
|
||||
void Jit::ClearCache()
|
||||
{
|
||||
blocks.Clear();
|
||||
ClearCodeSpace();
|
||||
GenerateFixedCode();
|
||||
}
|
||||
|
||||
void Jit::InvalidateCache()
|
||||
{
|
||||
blocks.Clear();
|
||||
}
|
||||
|
||||
void Jit::InvalidateCacheAt(u32 em_address, int length)
|
||||
{
|
||||
blocks.InvalidateICache(em_address, length);
|
||||
}
|
||||
|
||||
void Jit::EatInstruction(MIPSOpcode op) {
|
||||
}
|
||||
|
||||
void Jit::CompileDelaySlot(int flags)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Jit::Compile(u32 em_address) {
|
||||
}
|
||||
|
||||
void Jit::RunLoopUntil(u64 globalticks)
|
||||
{
|
||||
((void (*)())enterCode)();
|
||||
}
|
||||
|
||||
const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
|
||||
{
|
||||
return b->normalEntry;
|
||||
}
|
||||
|
||||
void Jit::AddContinuedBlock(u32 dest)
|
||||
{
|
||||
}
|
||||
|
||||
bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name)
|
||||
{
|
||||
// TODO: Not used by anything yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
void Jit::Comp_RunBlock(MIPSOpcode op)
|
||||
{
|
||||
// This shouldn't be necessary, the dispatcher should catch us before we get here.
|
||||
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
|
||||
}
|
||||
|
||||
bool Jit::ReplaceJalTo(u32 dest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Jit::Comp_ReplacementFunc(MIPSOpcode op)
|
||||
{
|
||||
}
|
||||
|
||||
void Jit::Comp_Generic(MIPSOpcode op)
|
||||
{
|
||||
FlushAll();
|
||||
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
|
||||
if (func)
|
||||
{
|
||||
SaveDowncount();
|
||||
QuickCallFunction(R1, (void *)func);
|
||||
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 Jit::MovFromPC(FakeReg r) {
|
||||
}
|
||||
|
||||
void Jit::MovToPC(FakeReg r) {
|
||||
}
|
||||
|
||||
void Jit::SaveDowncount() {
|
||||
}
|
||||
|
||||
void Jit::RestoreDowncount() {
|
||||
}
|
||||
|
||||
void Jit::WriteDownCount(int offset) {
|
||||
}
|
||||
|
||||
// Abuses R2
|
||||
void Jit::WriteDownCountR(FakeReg reg) {
|
||||
}
|
||||
|
||||
void Jit::RestoreRoundingMode(bool force) {
|
||||
}
|
||||
|
||||
void Jit::ApplyRoundingMode(bool force) {
|
||||
}
|
||||
|
||||
void Jit::UpdateRoundingMode() {
|
||||
}
|
||||
|
||||
void Jit::WriteExit(u32 destination, int exit_num)
|
||||
{
|
||||
}
|
||||
|
||||
void Jit::WriteExitDestInR(FakeReg Reg)
|
||||
{
|
||||
}
|
||||
|
||||
void Jit::WriteSyscallExit()
|
||||
{
|
||||
}
|
||||
|
||||
void Jit::Comp_DoNothing(MIPSOpcode op) { }
|
||||
|
||||
#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]
|
||||
|
||||
}
|
258
Core/MIPS/fake/FakeJit.h
Normal file
258
Core/MIPS/fake/FakeJit.h
Normal file
@ -0,0 +1,258 @@
|
||||
// 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 "Core/MIPS/JitCommon/JitState.h"
|
||||
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
||||
#include "../MIPSVFPUUtils.h"
|
||||
|
||||
#ifndef offsetof
|
||||
#include "stddef.h"
|
||||
#endif
|
||||
|
||||
namespace MIPSComp
|
||||
{
|
||||
|
||||
struct FakeJitOptions
|
||||
{
|
||||
FakeJitOptions();
|
||||
|
||||
bool useNEONVFPU;
|
||||
bool enableBlocklink;
|
||||
bool downcountInRegister;
|
||||
bool useBackJump;
|
||||
bool useForwardJump;
|
||||
bool cachePointers;
|
||||
bool immBranches;
|
||||
bool continueBranches;
|
||||
bool continueJumps;
|
||||
int continueMaxInstructions;
|
||||
};
|
||||
|
||||
class Jit : public FakeGen::FakeXCodeBlock
|
||||
{
|
||||
public:
|
||||
Jit(MIPSState *mips);
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
static void DoDummyState(PointerWrap &p);
|
||||
|
||||
// Compiled ops should ignore delay slots
|
||||
// the compiler will take care of them by itself
|
||||
// OR NOT
|
||||
void Comp_Generic(MIPSOpcode op);
|
||||
|
||||
void RunLoopUntil(u64 globalticks);
|
||||
|
||||
void Compile(u32 em_address); // Compiles a block at current MIPS PC
|
||||
const u8 *DoJit(u32 em_address, JitBlock *b);
|
||||
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name);
|
||||
|
||||
void CompileDelaySlot(int flags);
|
||||
void EatInstruction(MIPSOpcode op);
|
||||
void AddContinuedBlock(u32 dest);
|
||||
|
||||
void Comp_RunBlock(MIPSOpcode op);
|
||||
void Comp_ReplacementFunc(MIPSOpcode op);
|
||||
|
||||
// Ops
|
||||
void Comp_ITypeMem(MIPSOpcode op);
|
||||
void Comp_Cache(MIPSOpcode op);
|
||||
|
||||
void Comp_RelBranch(MIPSOpcode op);
|
||||
void Comp_RelBranchRI(MIPSOpcode op);
|
||||
void Comp_FPUBranch(MIPSOpcode op);
|
||||
void Comp_FPULS(MIPSOpcode op);
|
||||
void Comp_FPUComp(MIPSOpcode op);
|
||||
void Comp_Jump(MIPSOpcode op);
|
||||
void Comp_JumpReg(MIPSOpcode op);
|
||||
void Comp_Syscall(MIPSOpcode op);
|
||||
void Comp_Break(MIPSOpcode op);
|
||||
|
||||
void Comp_IType(MIPSOpcode op);
|
||||
void Comp_RType2(MIPSOpcode op);
|
||||
void Comp_RType3(MIPSOpcode op);
|
||||
void Comp_ShiftType(MIPSOpcode op);
|
||||
void Comp_Allegrex(MIPSOpcode op);
|
||||
void Comp_Allegrex2(MIPSOpcode op);
|
||||
void Comp_VBranch(MIPSOpcode op);
|
||||
void Comp_MulDivType(MIPSOpcode op);
|
||||
void Comp_Special3(MIPSOpcode op);
|
||||
|
||||
void Comp_FPU3op(MIPSOpcode op);
|
||||
void Comp_FPU2op(MIPSOpcode op);
|
||||
void Comp_mxc1(MIPSOpcode op);
|
||||
|
||||
void Comp_DoNothing(MIPSOpcode op);
|
||||
|
||||
void Comp_SV(MIPSOpcode op);
|
||||
void Comp_SVQ(MIPSOpcode op);
|
||||
void Comp_VPFX(MIPSOpcode op);
|
||||
void Comp_VVectorInit(MIPSOpcode op);
|
||||
void Comp_VMatrixInit(MIPSOpcode op);
|
||||
void Comp_VDot(MIPSOpcode op);
|
||||
void Comp_VecDo3(MIPSOpcode op);
|
||||
void Comp_VV2Op(MIPSOpcode op);
|
||||
void Comp_Mftv(MIPSOpcode op);
|
||||
void Comp_Vmfvc(MIPSOpcode op);
|
||||
void Comp_Vmtvc(MIPSOpcode op);
|
||||
void Comp_Vmmov(MIPSOpcode op);
|
||||
void Comp_VScl(MIPSOpcode op);
|
||||
void Comp_Vmmul(MIPSOpcode op);
|
||||
void Comp_Vmscl(MIPSOpcode op);
|
||||
void Comp_Vtfm(MIPSOpcode op);
|
||||
void Comp_VHdp(MIPSOpcode op);
|
||||
void Comp_VCrs(MIPSOpcode op);
|
||||
void Comp_VDet(MIPSOpcode op);
|
||||
void Comp_Vi2x(MIPSOpcode op);
|
||||
void Comp_Vx2i(MIPSOpcode op);
|
||||
void Comp_Vf2i(MIPSOpcode op);
|
||||
void Comp_Vi2f(MIPSOpcode op);
|
||||
void Comp_Vh2f(MIPSOpcode op);
|
||||
void Comp_Vcst(MIPSOpcode op);
|
||||
void Comp_Vhoriz(MIPSOpcode op);
|
||||
void Comp_VRot(MIPSOpcode op);
|
||||
void Comp_VIdt(MIPSOpcode op);
|
||||
void Comp_Vcmp(MIPSOpcode op);
|
||||
void Comp_Vcmov(MIPSOpcode op);
|
||||
void Comp_Viim(MIPSOpcode op);
|
||||
void Comp_Vfim(MIPSOpcode op);
|
||||
void Comp_VCrossQuat(MIPSOpcode op);
|
||||
void Comp_Vsgn(MIPSOpcode op);
|
||||
void Comp_Vocp(MIPSOpcode op);
|
||||
|
||||
// Non-NEON: VPFX
|
||||
|
||||
// NEON implementations of the VFPU ops.
|
||||
void CompNEON_SV(MIPSOpcode op);
|
||||
void CompNEON_SVQ(MIPSOpcode op);
|
||||
void CompNEON_VVectorInit(MIPSOpcode op);
|
||||
void CompNEON_VMatrixInit(MIPSOpcode op);
|
||||
void CompNEON_VDot(MIPSOpcode op);
|
||||
void CompNEON_VecDo3(MIPSOpcode op);
|
||||
void CompNEON_VV2Op(MIPSOpcode op);
|
||||
void CompNEON_Mftv(MIPSOpcode op);
|
||||
void CompNEON_Vmfvc(MIPSOpcode op);
|
||||
void CompNEON_Vmtvc(MIPSOpcode op);
|
||||
void CompNEON_Vmmov(MIPSOpcode op);
|
||||
void CompNEON_VScl(MIPSOpcode op);
|
||||
void CompNEON_Vmmul(MIPSOpcode op);
|
||||
void CompNEON_Vmscl(MIPSOpcode op);
|
||||
void CompNEON_Vtfm(MIPSOpcode op);
|
||||
void CompNEON_VHdp(MIPSOpcode op);
|
||||
void CompNEON_VCrs(MIPSOpcode op);
|
||||
void CompNEON_VDet(MIPSOpcode op);
|
||||
void CompNEON_Vi2x(MIPSOpcode op);
|
||||
void CompNEON_Vx2i(MIPSOpcode op);
|
||||
void CompNEON_Vf2i(MIPSOpcode op);
|
||||
void CompNEON_Vi2f(MIPSOpcode op);
|
||||
void CompNEON_Vh2f(MIPSOpcode op);
|
||||
void CompNEON_Vcst(MIPSOpcode op);
|
||||
void CompNEON_Vhoriz(MIPSOpcode op);
|
||||
void CompNEON_VRot(MIPSOpcode op);
|
||||
void CompNEON_VIdt(MIPSOpcode op);
|
||||
void CompNEON_Vcmp(MIPSOpcode op);
|
||||
void CompNEON_Vcmov(MIPSOpcode op);
|
||||
void CompNEON_Viim(MIPSOpcode op);
|
||||
void CompNEON_Vfim(MIPSOpcode op);
|
||||
void CompNEON_VCrossQuat(MIPSOpcode op);
|
||||
void CompNEON_Vsgn(MIPSOpcode op);
|
||||
void CompNEON_Vocp(MIPSOpcode op);
|
||||
|
||||
int Replace_fabsf();
|
||||
|
||||
JitBlockCache *GetBlockCache() { return &blocks; }
|
||||
|
||||
void ClearCache();
|
||||
void InvalidateCache();
|
||||
void InvalidateCacheAt(u32 em_address, int length = 4);
|
||||
|
||||
void EatPrefix() { 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);
|
||||
|
||||
bool ReplaceJalTo(u32 dest);
|
||||
|
||||
void SaveDowncount();
|
||||
void RestoreDowncount();
|
||||
|
||||
void WriteExit(u32 destination, int exit_num);
|
||||
void WriteExitDestInR(FakeReg Reg);
|
||||
void WriteSyscallExit();
|
||||
|
||||
// Utility compilation functions
|
||||
void BranchFPFlag(MIPSOpcode op, FakeGen::CCFlags cc, bool likely);
|
||||
void BranchVFPUFlag(MIPSOpcode op, FakeGen::CCFlags cc, bool likely);
|
||||
void BranchRSZeroComp(MIPSOpcode op, FakeGen::CCFlags cc, bool andLink, bool likely);
|
||||
void BranchRSRTComp(MIPSOpcode op, FakeGen::CCFlags cc, bool likely);
|
||||
|
||||
// Utilities to reduce duplicated code
|
||||
void CompImmLogic(MIPSGPReg rs, MIPSGPReg rt, u32 uimm, void (FakeXEmitter::*arith)(FakeReg dst, FakeReg src, Operand2 op2), bool (FakeXEmitter::*tryArithI2R)(FakeReg dst, FakeReg src, u32 val), u32 (*eval)(u32 a, u32 b));
|
||||
void CompType3(MIPSGPReg rd, MIPSGPReg rs, MIPSGPReg rt, void (FakeXEmitter::*arithOp2)(FakeReg dst, FakeReg rm, Operand2 rn), bool (FakeXEmitter::*tryArithI2R)(FakeReg dst, FakeReg rm, u32 val), u32 (*eval)(u32 a, u32 b), bool symmetric = false);
|
||||
|
||||
void CompShiftImm(MIPSOpcode op, FakeGen::ShiftType shiftType, int sa);
|
||||
void CompShiftVar(MIPSOpcode op, FakeGen::ShiftType shiftType);
|
||||
|
||||
// Utils
|
||||
void SetR0ToEffectiveAddress(MIPSGPReg rs, s16 offset);
|
||||
void SetCCAndR0ForSafeAddress(MIPSGPReg rs, s16 offset, FakeReg tempReg, bool reverse = false);
|
||||
void Comp_ITypeMemLR(MIPSOpcode op, bool load);
|
||||
|
||||
JitBlockCache blocks;
|
||||
FakeJitOptions jo;
|
||||
JitState js;
|
||||
|
||||
// FakeRegCache gpr;
|
||||
// FakeRegCacheFPU fpr;
|
||||
|
||||
MIPSState *mips_;
|
||||
|
||||
int dontLogBlocks;
|
||||
int logBlocks;
|
||||
|
||||
public:
|
||||
// Code pointers
|
||||
const u8 *enterCode;
|
||||
|
||||
const u8 *outerLoop;
|
||||
const u8 *outerLoopPCInR0;
|
||||
const u8 *dispatcherCheckCoreState;
|
||||
const u8 *dispatcherPCInR0;
|
||||
const u8 *dispatcher;
|
||||
const u8 *dispatcherNoCheck;
|
||||
|
||||
const u8 *breakpointBailout;
|
||||
};
|
||||
|
||||
typedef void (Jit::*MIPSCompileFunc)(MIPSOpcode opcode);
|
||||
typedef int (Jit::*MIPSReplaceFunc)();
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
@ -13,10 +13,14 @@ arm {
|
||||
$$P/ext/disarm.cpp
|
||||
HEADERS += $$P/Core/MIPS/ARM/*.h
|
||||
}
|
||||
else {
|
||||
else:i86 {
|
||||
SOURCES += $$P/Core/MIPS/x86/*.cpp
|
||||
HEADERS += $$P/Core/MIPS/x86/*.h
|
||||
}
|
||||
else {
|
||||
SOURCES += $$P/Core/MIPS/fake/*.cpp
|
||||
HEADERS += $$P/Core/MIPS/fake/*.h
|
||||
}
|
||||
|
||||
SOURCES += $$P/Core/*.cpp \ # Core
|
||||
$$P/Core/Debugger/*.cpp \
|
||||
|
@ -57,7 +57,7 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU
|
||||
armv7: SOURCES += $$P/GPU/Common/TextureDecoderNEON.cpp
|
||||
|
||||
arm: SOURCES += $$P/GPU/Common/VertexDecoderArm.cpp
|
||||
else: SOURCES += $$P/GPU/Common/VertexDecoderX86.cpp
|
||||
i86: SOURCES += $$P/GPU/Common/VertexDecoderX86.cpp
|
||||
|
||||
HEADERS += $$P/GPU/GLES/*.h \
|
||||
$$P/GPU/Software/*.h \
|
||||
|
Loading…
Reference in New Issue
Block a user