Play around with function replacement. Turned off by default of course.

This commit is contained in:
Henrik Rydgard 2013-12-17 23:40:27 +01:00
parent 832c933cb8
commit 2eab4aa1bf
25 changed files with 458 additions and 96 deletions

View File

@ -869,6 +869,7 @@ if(ARM)
Core/MIPS/ARM/ArmCompLoadStore.cpp
Core/MIPS/ARM/ArmCompVFPU.cpp
Core/MIPS/ARM/ArmCompVFPUNEON.cpp
Core/MIPS/ARM/ArmCompReplace.cpp
Core/MIPS/ARM/ArmJit.cpp
Core/MIPS/ARM/ArmJit.h
Core/MIPS/ARM/ArmRegCache.cpp
@ -887,6 +888,7 @@ elseif(X86)
Core/MIPS/x86/CompFPU.cpp
Core/MIPS/x86/CompLoadStore.cpp
Core/MIPS/x86/CompVFPU.cpp
Core/MIPS/x86/CompReplace.cpp
Core/MIPS/x86/Jit.cpp
Core/MIPS/x86/Jit.h
Core/MIPS/x86/RegCache.cpp

View File

@ -291,6 +291,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompReplace.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompVFPU.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -359,6 +365,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\PPC\PpcCompReplace.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\PPC\PpcCompVFPU.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -382,6 +394,7 @@
<ClCompile Include="MIPS\x86\CompBranch.cpp" />
<ClCompile Include="MIPS\x86\CompFPU.cpp" />
<ClCompile Include="MIPS\x86\CompLoadStore.cpp" />
<ClCompile Include="MIPS\x86\CompReplace.cpp" />
<ClCompile Include="MIPS\x86\CompVFPU.cpp" />
<ClCompile Include="MIPS\x86\RegCacheFPU.cpp" />
<ClCompile Include="MIPS\x86\Jit.cpp" />

View File

@ -508,6 +508,15 @@
<ClCompile Include="HLE\ReplaceTables.cpp">
<Filter>HLE</Filter>
</ClCompile>
<ClCompile Include="MIPS\x86\CompReplace.cpp">
<Filter>MIPS\x86</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompReplace.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\PPC\PpcCompReplace.cpp">
<Filter>MIPS\PPC</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -949,4 +958,4 @@
<None Include="..\android\jni\Android.mk" />
<None Include="GameLogNotes.txt" />
</ItemGroup>
</Project>
</Project>

View File

@ -15,28 +15,114 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <map>
#include "base/basictypes.h"
#include "base/logging.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/HLE/FunctionWrappers.h"
void NormalizeVector(u32 vecPtr) {
// TODO
#include "GPU/Math3D.h"
// I think these have to be pretty accurate, but we can probably
// get away with approximating the VFPU vsin/vcos and vrot pretty roughly.
static int Replace_sinf() {
float f = PARAMF(0);
RETURNF(sinf(f));
return 80; // guess number of cycles
}
static int Replace_cosf() {
float f = PARAMF(0);
RETURNF(cosf(f));
return 80; // guess number of cycles
}
// Should probably do JIT versions of this, possibly ones that only delegate
// large copies to a C function.
static int Replace_memcpy() {
u32 destPtr = PARAM(0);
u8 *dst = Memory::GetPointer(destPtr);
u8 *src = Memory::GetPointer(PARAM(1));
u32 bytes = PARAM(2);
if (dst && src) {
memcpy(dst, src, bytes);
}
RETURN(destPtr);
return 10 + bytes / 4; // approximation
}
static int Replace_memset() {
u32 destPtr = PARAM(0);
u8 *dst = Memory::GetPointer(destPtr);
u8 value = PARAM(1);
u32 bytes = PARAM(2);
if (dst) {
memset(dst, value, bytes);
}
RETURN(destPtr);
return 10 + bytes / 4; // approximation
}
static int Replace_strlen() {
u32 srcPtr = PARAM(0);
const char *src = (const char *)Memory::GetPointer(srcPtr);
u32 len = (u32)strlen(src);
RETURN(len);
return 4 + len; // approximation
}
static int Replace_vmmul_q_transp() {
float *out = (float *)Memory::GetPointerUnchecked(PARAM(0));
const float *a = (const float *)Memory::GetPointerUnchecked(PARAM(1));
const float *b = (const float *)Memory::GetPointerUnchecked(PARAM(2));
// TODO: Actually use an optimized matrix multiply here...
Matrix4ByMatrix4(out, b, a);
return 16;
}
// Can either replace with C functions or functions emitted in Asm/ArmAsm.
// The latter is recommended for fast math functions.
static const ReplacementTableEntry entries[] = {
{ 0x0, 64, "NormalizeVector", WrapV_U<NormalizeVector>, 20 }
// TODO: I think some games can be helped quite a bit by implementing the
// double-precision soft-float routines: __adddf3, __subdf3 and so on. These
// should of course be implemented JIT style, inline.
{ "sinf", &Replace_sinf, 0, 0},
{ "cosf", &Replace_cosf, 0, 0},
{ "memcpy", &Replace_memcpy, 0, 0},
{ "memset", &Replace_memset, 0, 0},
{ "strlen", &Replace_strlen, 0, 0},
{ "fabsf", 0, &MIPSComp::Jit::Replace_fabsf, REPFLAG_ALLOWINLINE},
{ "vmmul_q_transp", &Replace_vmmul_q_transp, 0, 0},
{}
};
static std::map<u32, u32> replacedInstructions;
void Replacement_Init() {
}
void Replacement_Shutdown() {
replacedInstructions.clear();
}
int GetNumReplacementFuncs() {
return ARRAY_SIZE(entries);
}
int GetReplacementFuncIndex(u64 hash, int size) {
int GetReplacementFuncIndex(u64 hash, int funcSize) {
const char *name = MIPSAnalyst::LookupHash(hash, funcSize);
if (!name) {
return -1;
}
// TODO: Build a lookup and keep it around
for (int i = 0; i < ARRAY_SIZE(entries); i++) {
if (entries[i].hash == hash && entries[i].size == size) {
if (!entries[i].name)
continue;
if (!strcmp(name, entries[i].name)) {
return i;
}
}
@ -45,4 +131,13 @@ int GetReplacementFuncIndex(u64 hash, int size) {
const ReplacementTableEntry *GetReplacementFunc(int i) {
return &entries[i];
}
}
void WriteReplaceInstruction(u32 address, u64 hash, int size) {
int index = GetReplacementFuncIndex(hash, size);
if (index >= 0) {
replacedInstructions[address] = Memory::Read_U32(address);
ILOG("Replaced %s at %08x", entries[index].name, address);
Memory::Write_U32(MIPS_EMUHACK_CALL_REPLACEMENT | (int)index, address);
}
}

View File

@ -15,22 +15,45 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// Regular replacement funcs are just C functions. These take care of their
// own parameter parsing using the old school PARAM macros.
// The return value is the number of cycles to eat.
// JIT replacefuncs can be for inline or "outline" replacement.
// With inline replacement, we recognize the call to the functions
// at jal time already. With outline replacement, we just replace the
// implementation.
// In both cases the jit needs to know how much to subtract downcount.
//
// If the replacement func returned a positive number, this will be treated
// as the number of cycles to subtract.
// If the replacement func returns -1, it will be assumed that the subtraction
// was done by the replacement func.
#pragma once
#include "Common/CommonTypes.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
typedef void (* ReplaceFunc)();
typedef int (* ReplaceFunc)();
enum {
REPFLAG_ALLOWINLINE = 1,
};
// Kind of similar to HLE functions but with different data.
struct ReplacementTableEntry {
u32 hash;
int size;
const char *name;
ReplaceFunc replaceFunc;
int cyclesToEat;
MIPSComp::MIPSReplaceFunc jitReplaceFunc;
int flags;
};
void Replacement_Init();
void Replacement_Shutdown();
int GetNumReplacementFuncs();
int GetReplacementFuncIndex(u64 hash, int size);
const ReplacementTableEntry *GetReplacementFunc(int index);
int GetReplacementFuncIndex(u64 hash, int funcSize);
const ReplacementTableEntry *GetReplacementFunc(int index);
void WriteReplaceInstruction(u32 address, u64 hash, int size);

View File

@ -821,8 +821,8 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
module->nm.data_size -= textSize;
#if !defined(USING_GLES2)
if (!reader.LoadSymbols())
MIPSAnalyst::ScanForFunctions(textStart, textStart + textSize, true);
bool gotSymbols = reader.LoadSymbols();
MIPSAnalyst::ScanForFunctions(textStart, textStart + textSize, !gotSymbols);
#else
// Scan for functions (for the analysis results which can help the JIT).
// But don't insert into the symbol map.
@ -981,8 +981,8 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
u32 textStart = reader.GetVaddr();
u32 textEnd = firstImportStubAddr - 4;
#if !defined(USING_GLES2)
if (!reader.LoadSymbols())
MIPSAnalyst::ScanForFunctions(textStart, textEnd, true);
bool gotSymbols = reader.LoadSymbols();
MIPSAnalyst::ScanForFunctions(textStart, textEnd, !gotSymbols);
#endif
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2013- 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/CPUDetect.h"
#include "Core/MemMap.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/ARM/ArmJit.h"
#include "Core/MIPS/ARM/ArmRegCache.h"
namespace MIPSComp {
int Jit::Replace_fabsf() {
fpr.MapDirtyIn(0, 13);
VABS(fpr.R(0), fpr.R(13));
return 6; // Number of instructions in the MIPS function
}
}

View File

@ -341,21 +341,45 @@ void Jit::Comp_RunBlock(MIPSOpcode op)
void Jit::Comp_ReplacementFunc(MIPSOpcode op)
{
// None of the code of this function is relevant so we'll just
// call the replacement and move RA to PC.
int index = op.encoding & 0xFFFFFF;
// We get here if we execute the first instruction of a replaced function. This means
// that we do need to return to RA.
// Inlined function calls (caught in jal) are handled differently.
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (!entry) {
ERROR_LOG(HLE, "Invalid replacement op %08x", op.encoding);
return;
}
BL((void *)(entry->replaceFunc));
// Alternatively, we could inline it here, instead of calling out, if it's a function
// we can emit.
// JIT goes first.
if (entry->jitReplaceFunc) {
MIPSReplaceFunc repl = entry->jitReplaceFunc;
int cycles = (this->*repl)();
FlushAll();
LDR(R1, CTXREG, MIPS_REG_RA * 4);
js.downcountAmount = cycles;
WriteExitDestInR(R1);
} else if (entry->replaceFunc) {
FlushAll();
// Standard function call, nothing fancy.
// The function returns the number of cycles it took in EAX.
BL((const void *)(entry->replaceFunc));
// Alternatively, we could inline it here, instead of calling out, if it's a function
// we can emit.
LDR(R1, CTXREG, MIPS_REG_RA * 4);
WriteDownCountR(R0);
js.downcountAmount = 0; // we just subtracted most of it
WriteExitDestInR(R1);
} else {
ERROR_LOG(HLE, "Replacement function has neither jit nor regular impl");
}
MOVI2R(R0, currentMIPS->r[MIPS_REG_RA], R1);
MovToPC(R0);
js.compiling = false;
js.downcountAmount = entry->cyclesToEat;
WriteDownCount(0);
// We could even do this in the jal that is branching to the function
// but having the op is necessary for the interpreter anyway.
}
@ -430,6 +454,18 @@ void Jit::WriteDownCount(int offset)
}
}
// Abuses R1
void Jit::WriteDownCountR(ARMReg reg)
{
if (jo.downcountInRegister) {
SUBS(DOWNCOUNTREG, DOWNCOUNTREG, reg);
} else {
LDR(R2, CTXREG, offsetof(MIPSState, downcount));
SUBS(R2, R2, reg);
STR(R2, CTXREG, offsetof(MIPSState, downcount));
}
}
// IDEA - could have a WriteDualExit that takes two destinations and two condition flags,
// and just have conditional that set PC "twice". This only works when we fall back to dispatcher
// though, as we need to have the SUBS flag set in the end. So with block linking in the mix,

View File

@ -177,6 +177,7 @@ public:
void CompNEON_Vsgn(MIPSOpcode op);
void CompNEON_Vocp(MIPSOpcode op);
int Replace_fabsf();
JitBlockCache *GetBlockCache() { return &blocks; }
@ -191,6 +192,7 @@ private:
void FlushPrefixV();
void WriteDownCount(int offset = 0);
void WriteDownCountR(ARMReg reg);
void MovFromPC(ARMReg r);
void MovToPC(ARMReg r);
@ -260,6 +262,7 @@ public:
};
typedef void (Jit::*MIPSCompileFunc)(MIPSOpcode opcode);
typedef int (Jit::*MIPSReplaceFunc)();
} // namespace MIPSComp

View File

@ -39,9 +39,12 @@ struct JitBlock;
#define MIPS_EMUHACK_OPCODE 0x68000000
#define MIPS_EMUHACK_MASK 0xFC000000
#define MIPS_EMUHACK_VALUE_MASK 0x03FFFFFF
#define MIPS_JITBLOCK_MASK 0xFF000000
#define MIPS_EMUHACK_VALUE_MASK 0x00FFFFFF
#define MIPS_IS_EMUHACK(op) (((op) & 0xFC000000) == MIPS_EMUHACK_OPCODE) // masks away the subop
#define MIPS_IS_RUNBLOCK(op) (((op) & 0xFF000000) == MIPS_EMUHACK_OPCODE) // masks away the subop
#define MIPS_IS_REPLACEMENT(op) (((op) & 0xFF000000) == (MIPS_EMUHACK_OPCODE | (EMUOP_CALL_REPLACEMENT << 24))) // masks away the subop
// There are 2 bits available for sub-opcodes, 0x03000000.
#define EMUOP_RUNBLOCK 0 // Runs a JIT block

View File

@ -433,12 +433,45 @@ namespace MIPSAnalyst {
HashFunctions();
std::string hashMapFilename = GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini";
if (g_Config.bFuncHashMap) {
LoadHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini");
StoreHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini");
LoadHashMap(hashMapFilename);
StoreHashMap(hashMapFilename);
if (insertSymbols) {
ApplyHashMap();
}
if (g_Config.bFuncHashMap) {
ReplaceFunctions();
}
}
}
void AnalyzeFunction(u32 startAddr, u32 size, const char *name) {
// Check if we have this already
for (auto iter = functions.begin(); iter != functions.end(); iter++) {
if (iter->start == startAddr) {
// Let's just add it to the hashmap.
if (iter->hasHash) {
HashMapFunc hfun;
hfun.hash = iter->hash;
strncpy(hfun.name, name, 64);
hfun.size = iter->size;
hashMap.insert(hfun);
}
return;
}
}
// ReplaceFunctions();
// Cheats a little.
AnalyzedFunction fun;
fun.start = startAddr;
fun.end = startAddr + size - 4;
fun.isStraightLeaf = false; // dunno really
strncpy(fun.name, name, 64);
fun.name[63] = 0;
functions.push_back(fun);
HashFunctions();
}
void ForgetFunctions(u32 startAddr, u32 endAddr) {
@ -463,10 +496,7 @@ namespace MIPSAnalyst {
void ReplaceFunctions() {
for (size_t i = 0; i < functions.size(); i++) {
int index = GetReplacementFuncIndex(functions[i].hash, functions[i].size);
if (index >= 0) {
Memory::Write_U32(MIPS_EMUHACK_CALL_REPLACEMENT | (int)i, functions[i].start);
}
WriteReplaceInstruction(functions[i].start, functions[i].hash, functions[i].size);
}
}
@ -489,10 +519,30 @@ namespace MIPSAnalyst {
}
}
const char *LookupHash(u64 hash, int funcsize) {
for (auto it = hashMap.begin(), end = hashMap.end(); it != end; ++it) {
if (it->hash == hash && it->size == funcsize) {
return it->name;
}
}
return 0;
}
void SetHashMapFilename(std::string filename) {
if (filename.empty())
hashmapFileName = GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini";
else
hashmapFileName = filename;
}
void StoreHashMap(std::string filename) {
if (filename.empty())
filename = hashmapFileName;
if (!hashMap.size()) {
return;
}
FILE *file = File::OpenCFile(filename, "wt");
if (!file) {
WARN_LOG(LOADER, "Could not store hash map: %s", filename.c_str());
@ -557,8 +607,6 @@ namespace MIPSAnalyst {
hashMap.insert(mf);
}
fclose(file);
ApplyHashMap();
}
std::vector<MIPSGPReg> GetInputRegs(MIPSOpcode op) {

View File

@ -19,7 +19,6 @@
#include "Globals.h"
#include "Core/MIPS/MIPS.h"
#include "Core/HLE/ReplaceTables.h"
class DebugInterface;
@ -88,16 +87,26 @@ namespace MIPSAnalyst
char name[64];
};
struct ReplacementTableEntry;
void Reset();
bool IsRegisterUsed(u32 reg, u32 addr);
// This will not only create a database of "AnalyzedFunction" structs, it also
// will insert all the functions it finds into the symbol map, if insertSymbols is true.
// If we have loaded symbols from the elf, we'll register functions as they are touched
// so that we don't just dump them all in the cache.
void AnalyzeFunction(u32 startAddr, u32 size, const char *name);
void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols);
void ForgetFunctions(u32 startAddr, u32 endAddr);
void CompileLeafs();
void SetHashMapFilename(std::string filename = "");
void LoadHashMap(std::string filename);
void StoreHashMap(std::string filename = "");
const char *LookupHash(u64 hash, int funcSize);
void ReplaceFunctions(const ReplacementTableEntry *e, int numEntries);
void UpdateHashMap();

View File

@ -917,8 +917,6 @@ const MIPSInstruction *MIPSGetInstruction(MIPSOpcode op)
return instr;
}
void MIPSCompileOp(MIPSOpcode op)
{
if (op == 0)
@ -927,12 +925,10 @@ void MIPSCompileOp(MIPSOpcode op)
const MIPSInfo info = MIPSGetInfo(op);
if (instr)
{
if (instr->compile)
(MIPSComp::jit->*(instr->compile))(op); // woohoo, member functions pointers!
else
{
if (instr->compile) {
(MIPSComp::jit->*(instr->compile))(op);
} else {
ERROR_LOG_REPORT(CPU,"MIPSCompileOp %08x failed",op.encoding);
//MessageBox(0,"ARGH2",0,0);//compile an interpreter call
}
if (info & OUT_EAT_PREFIX)
@ -944,15 +940,11 @@ void MIPSCompileOp(MIPSOpcode op)
}
}
void MIPSDisAsm(MIPSOpcode op, u32 pc, char *out, bool tabsToSpaces)
{
if (op == 0)
{
if (op == 0) {
sprintf(out,"nop");
}
else
{
} else {
disPC = pc;
const MIPSInstruction *instr = MIPSGetInstruction(op);
if (instr && instr->disasm) {
@ -966,26 +958,16 @@ void MIPSDisAsm(MIPSOpcode op, u32 pc, char *out, bool tabsToSpaces)
}
} else {
strcpy(out, "no instruction :(");
//__asm int 3
MIPSGetInstruction(op);
}
}
}
void MIPSInterpret(MIPSOpcode op) //only for those rare ones
{
//if ((op&0xFFFFF000) == 0xd0110000)
// Crash();
//if (atable[CRUNCH_MIPS_OP(op)].interpret)
// atable[CRUNCH_MIPS_OP(op)].interpret(op);
// else
// _dbg_assert_msg_(MIPS,0,"Trying to interpret instruction that can't be interpreted");
void MIPSInterpret(MIPSOpcode op) {
const MIPSInstruction *instr = MIPSGetInstruction(op);
if (instr && instr->interpret)
if (instr && instr->interpret) {
instr->interpret(op);
else
{
} else {
ERROR_LOG_REPORT(CPU, "Unknown instruction %08x at %08x", op.encoding, currentMIPS->pc);
// Try to disassemble it
char disasm[256];

View File

@ -0,0 +1,39 @@
// 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 <cmath>
#include "math/math_util.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Common/CPUDetect.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
namespace MIPSComp {
int Jit::Replace_fabsf() {
return -1;
// fpr.MapDirtyIn(0, 13);
// VABS(fpr.R(0), fpr.R(13));
// return 6; // Number of instructions in the MIPS function
}
}

View File

@ -36,8 +36,7 @@ const bool disablePrefixes = false;
using namespace PpcGen;
//#define USE_VMX128
// #define USE_VMX128
namespace MIPSComp
{

View File

@ -240,6 +240,7 @@ namespace MIPSComp
void Comp_Vsgn(MIPSOpcode op);
void Comp_Vocp(MIPSOpcode op);
int Replace_fabsf();
// Utility compilation functions
void BranchFPFlag(MIPSOpcode op, PpcGen::FixupBranchType cc, bool likely);
@ -325,6 +326,7 @@ namespace MIPSComp
};
typedef void (Jit::*MIPSCompileFunc)(MIPSOpcode opcode);
typedef int (Jit::*MIPSReplaceFunc)();
} // namespace MIPSComp

View File

@ -110,7 +110,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit)
MOV(32, R(EAX), MComplex(RBX, RAX, SCALE_1, 0));
#endif
MOV(32, R(EDX), R(EAX));
AND(32, R(EDX), Imm32(MIPS_EMUHACK_MASK));
AND(32, R(EDX), Imm32(MIPS_JITBLOCK_MASK));
CMP(32, R(EDX), Imm32(MIPS_EMUHACK_OPCODE));
FixupBranch notfound = J_CC(CC_NZ);
// IDEA - we have 24 bits, why not just use offsets from base of code?

View File

@ -0,0 +1,33 @@
// Copyright (c) 2013- 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 "Core/MemMap.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/x86/RegCache.h"
static const u64 MEMORY_ALIGNED16(ssNoSignMask[2]) = {0x7FFFFFFF7FFFFFFFULL, 0x7FFFFFFF7FFFFFFFULL};
namespace MIPSComp {
int Jit::Replace_fabsf() {
fpr.MapReg(0, MAP_DIRTY | MAP_NOINIT);
MOVSS(fpr.RX(0), fpr.R(12));
ANDPS(fpr.RX(0), M((void *)&ssNoSignMask));
return 4; // Number of instructions in the MIPS function
}
}

View File

@ -18,6 +18,7 @@
#include <cmath>
#include <limits>
#include <xmmintrin.h>
#include "base/logging.h"
#include "math/math_util.h"
@ -30,7 +31,6 @@
#include "Core/MIPS/x86/Jit.h"
#include "Core/MIPS/x86/RegCache.h"
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
// Currently known non working ones should have DISABLE.
@ -38,7 +38,6 @@
#define CONDITIONAL_DISABLE ;
#define DISABLE { fpr.ReleaseSpillLocks(); Comp_Generic(op); return; }
#define _RS MIPS_GET_RS(op)
#define _RT MIPS_GET_RT(op)
#define _RD MIPS_GET_RD(op)

View File

@ -374,24 +374,45 @@ void Jit::Comp_RunBlock(MIPSOpcode op)
void Jit::Comp_ReplacementFunc(MIPSOpcode op)
{
// None of the code of this function is relevant so we'll just
// call the replacement and move RA to PC.
int index = op.encoding & 0xFFFFFF;
// We get here if we execute the first instruction of a replaced function. This means
// that we do need to return to RA.
// Inlined function calls (caught in jal) are handled differently.
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (!entry) {
ERROR_LOG(HLE, "Invalid replacement op %08x", op.encoding);
return;
}
CALL((const void *)entry->replaceFunc);
// Alternatively, we could inline it here, instead of calling out, if it's a function
// we can emit.
// JIT goes first.
if (entry->jitReplaceFunc) {
MIPSReplaceFunc repl = entry->jitReplaceFunc;
int cycles = (this->*repl)();
FlushAll();
MOV(32, R(ECX), M(&currentMIPS->r[MIPS_REG_RA]));
js.downcountAmount = cycles;
WriteExitDestInReg(ECX);
js.compiling = false;
} else if (entry->replaceFunc) {
FlushAll();
// Standard function call, nothing fancy.
// The function returns the number of cycles it took in EAX.
CALL((const void *)entry->replaceFunc);
// Alternatively, we could inline it here, instead of calling out, if it's a function
// we can emit.
MOV(32, R(EAX), Imm32(currentMIPS->r[MIPS_REG_RA]));
MOV(32, M(&mips_->pc), R(EAX));
SUB(32, M(&currentMIPS->downcount), Imm32(entry->cyclesToEat));
JMP(asm_.dispatcher, true);
MOV(32, R(ECX), M(&currentMIPS->r[MIPS_REG_RA]));
SUB(32, M(&currentMIPS->downcount - 1), R(EAX));
js.downcountAmount = 1; // we just subtracted most of it
WriteExitDestInReg(ECX);
js.compiling = false;
// We could even do this in the jal that is branching to the function
// but having the op is necessary for the interpreter anyway.
js.compiling = false;
} else {
ERROR_LOG(HLE, "Replacement function has neither jit nor regular impl");
}
}
void Jit::Comp_Generic(MIPSOpcode op)
@ -460,10 +481,10 @@ void Jit::WriteExit(u32 destination, int exit_num)
}
}
void Jit::WriteExitDestInEAX()
void Jit::WriteExitDestInReg(X64Reg reg)
{
// TODO: Some wasted potential, dispatcher will always read this back into EAX.
MOV(32, M(&mips_->pc), R(EAX));
MOV(32, M(&mips_->pc), R(reg));
// If we need to verify coreState and rewind, we may not jump yet.
if (js.afterOp & (JitState::AFTER_CORE_STATE | JitState::AFTER_REWIND_PC_BAD_STATE))
@ -483,9 +504,9 @@ void Jit::WriteExitDestInEAX()
// Validate the jump to avoid a crash?
if (!g_Config.bFastMemory)
{
CMP(32, R(EAX), Imm32(PSP_GetKernelMemoryBase()));
CMP(32, R(reg), Imm32(PSP_GetKernelMemoryBase()));
FixupBranch tooLow = J_CC(CC_B);
CMP(32, R(EAX), Imm32(PSP_GetUserMemoryEnd()));
CMP(32, R(reg), Imm32(PSP_GetUserMemoryEnd()));
FixupBranch tooHigh = J_CC(CC_AE);
// Need to set neg flag again if necessary.
@ -495,8 +516,8 @@ void Jit::WriteExitDestInEAX()
SetJumpTarget(tooLow);
SetJumpTarget(tooHigh);
CallProtectedFunction((void *) Memory::GetPointer, R(EAX));
CMP(32, R(EAX), Imm32(0));
CallProtectedFunction((void *) Memory::GetPointer, R(reg));
CMP(32, R(reg), Imm32(0));
FixupBranch skip = J_CC(CC_NE);
// TODO: "Ignore" this so other threads can continue?

View File

@ -28,7 +28,6 @@
#include "Common/x64Emitter.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/MIPS/JitCommon/JitState.h"
#include "Core/HLE/ReplaceTables.h"
#include "RegCache.h"
#include "RegCacheFPU.h"
@ -146,6 +145,8 @@ public:
void Comp_DoNothing(MIPSOpcode op);
int Replace_fabsf();
void ApplyPrefixST(u8 *vregs, u32 prefix, VectorSize sz);
void ApplyPrefixD(const u8 *vregs, VectorSize sz);
void GetVectorRegsPrefixS(u8 *regs, VectorSize sz, int vectorReg) {
@ -181,7 +182,9 @@ private:
void EatInstruction(MIPSOpcode op);
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInEAX();
void WriteExitDestInReg(X64Reg reg);
void WriteExitDestInEAX() { WriteExitDestInReg(EAX); }
// void WriteRfiExitDestInEAX();
void WriteSyscallExit();
bool CheckJitBreakpoint(u32 addr, int downcountOffset);
@ -294,6 +297,7 @@ private:
};
typedef void (Jit::*MIPSCompileFunc)(MIPSOpcode opcode);
typedef int (Jit::*MIPSReplaceFunc)();
} // namespace MIPSComp

View File

@ -173,7 +173,7 @@ void Clear()
Opcode Read_Instruction(u32 address)
{
Opcode inst = Opcode(Read_U32(address));
if (MIPS_IS_EMUHACK(inst) && MIPSComp::jit)
if (MIPS_IS_RUNBLOCK(inst) && MIPSComp::jit)
{
JitBlockCache *bc = MIPSComp::jit->GetBlockCache();
int block_num = bc->GetBlockNumberFromEmuHackOp(inst, true);
@ -182,6 +182,8 @@ Opcode Read_Instruction(u32 address)
} else {
return inst;
}
} else if (MIPS_IS_REPLACEMENT(inst)) {
return inst;
} else {
return inst;
}

View File

@ -36,6 +36,7 @@
#include "Core/System.h"
#include "Core/PSPMixer.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceAudio.h"
@ -167,6 +168,7 @@ void CPU_Init() {
IdentifiedFileType type = Identify_File(filename);
MIPSAnalyst::Reset();
Replacement_Init();
switch (type) {
case FILETYPE_PSP_ISO:
@ -215,6 +217,8 @@ void CPU_Shutdown() {
host->SaveSymbolMap();
}
Replacement_Shutdown();
CoreTiming::Shutdown();
__KernelShutdown();
HLEShutdown();

View File

@ -916,9 +916,10 @@ void CtrlDisAsmView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)
char name[256];
std::string newname;
strncpy_s(name, symbolMap.GetLabelName(funcBegin),_TRUNCATE);
if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"New function name", name, newname))
{
if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"New function name", name, newname)) {
symbolMap.SetLabelName(newname.c_str(),funcBegin);
u32 funcSize = symbolMap.GetFunctionSize(curAddress);
MIPSAnalyst::AnalyzeFunction(funcBegin, funcSize, newname.c_str());
MIPSAnalyst::UpdateHashMap();
MIPSAnalyst::ApplyHashMap();
SendMessage(GetParent(wnd),WM_DEB_MAPLOADED,0,0);

View File

@ -39,6 +39,7 @@ ARCH_FILES := \
$(SRC)/Core/MIPS/x86/CompFPU.cpp \
$(SRC)/Core/MIPS/x86/CompLoadStore.cpp \
$(SRC)/Core/MIPS/x86/CompVFPU.cpp \
$(SRC)/Core/MIPS/x86/CompReplace.cpp \
$(SRC)/Core/MIPS/x86/Asm.cpp \
$(SRC)/Core/MIPS/x86/Jit.cpp \
$(SRC)/Core/MIPS/x86/RegCache.cpp \
@ -58,6 +59,7 @@ ARCH_FILES := \
$(SRC)/Core/MIPS/ARM/ArmCompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPUNEON.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompReplace.cpp \
$(SRC)/Core/MIPS/ARM/ArmAsm.cpp \
$(SRC)/Core/MIPS/ARM/ArmJit.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \
@ -77,6 +79,7 @@ ARCH_FILES := \
$(SRC)/Core/MIPS/ARM/ArmCompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPUNEON.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompReplace.cpp \
$(SRC)/Core/MIPS/ARM/ArmAsm.cpp \
$(SRC)/Core/MIPS/ARM/ArmJit.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \