diff --git a/CMakeLists.txt b/CMakeLists.txt index 199cc0e0ae..bb96a1b3fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index fb115cb355..6ce530e53a 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -291,6 +291,12 @@ true true + + true + true + true + true + true true @@ -359,6 +365,12 @@ true true + + true + true + true + true + true true @@ -382,6 +394,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 8dedeb712a..6782cdded8 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -508,6 +508,15 @@ HLE + + MIPS\x86 + + + MIPS\ARM + + + MIPS\PPC + @@ -949,4 +958,4 @@ - + \ No newline at end of file diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index de9dba3c4b..6ec7ba91c3 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -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 + #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, 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 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]; -} \ No newline at end of file +} + +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); + } +} diff --git a/Core/HLE/ReplaceTables.h b/Core/HLE/ReplaceTables.h index 94c057225d..aa307c800d 100644 --- a/Core/HLE/ReplaceTables.h +++ b/Core/HLE/ReplaceTables.h @@ -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); \ No newline at end of file +int GetReplacementFuncIndex(u64 hash, int funcSize); +const ReplacementTableEntry *GetReplacementFunc(int index); +void WriteReplaceInstruction(u32 address, u64 hash, int size); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 2156eb936c..95a40d7817 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -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 } diff --git a/Core/MIPS/ARM/ArmCompReplace.cpp b/Core/MIPS/ARM/ArmCompReplace.cpp new file mode 100644 index 0000000000..ca3aa5baca --- /dev/null +++ b/Core/MIPS/ARM/ArmCompReplace.cpp @@ -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 +} + +} \ No newline at end of file diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index e6434ac9af..0b87d9e87c 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -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, diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index f32aaaa193..f638c470be 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -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 diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index 0f470be7fd..c23b596c29 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -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 diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 9c571ac7e0..5c7a0efb50 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -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 GetInputRegs(MIPSOpcode op) { diff --git a/Core/MIPS/MIPSAnalyst.h b/Core/MIPS/MIPSAnalyst.h index a32c511578..ce5851990a 100644 --- a/Core/MIPS/MIPSAnalyst.h +++ b/Core/MIPS/MIPSAnalyst.h @@ -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(); diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 152b4c9161..d6603c7a31 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -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]; diff --git a/Core/MIPS/PPC/PpcCompReplace.cpp b/Core/MIPS/PPC/PpcCompReplace.cpp new file mode 100644 index 0000000000..a000093a85 --- /dev/null +++ b/Core/MIPS/PPC/PpcCompReplace.cpp @@ -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 +#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 +} + +} \ No newline at end of file diff --git a/Core/MIPS/PPC/PpcCompVFPU.cpp b/Core/MIPS/PPC/PpcCompVFPU.cpp index 80cdd53926..d90146cf6c 100644 --- a/Core/MIPS/PPC/PpcCompVFPU.cpp +++ b/Core/MIPS/PPC/PpcCompVFPU.cpp @@ -36,8 +36,7 @@ const bool disablePrefixes = false; using namespace PpcGen; - -//#define USE_VMX128 +// #define USE_VMX128 namespace MIPSComp { diff --git a/Core/MIPS/PPC/PpcJit.h b/Core/MIPS/PPC/PpcJit.h index 4b5ed4cd89..793d926775 100644 --- a/Core/MIPS/PPC/PpcJit.h +++ b/Core/MIPS/PPC/PpcJit.h @@ -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 diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 6a94931954..1b0dd876af 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -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? diff --git a/Core/MIPS/x86/CompReplace.cpp b/Core/MIPS/x86/CompReplace.cpp new file mode 100644 index 0000000000..dc4c5d4990 --- /dev/null +++ b/Core/MIPS/x86/CompReplace.cpp @@ -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 +} + +} diff --git a/Core/MIPS/x86/CompVFPU.cpp b/Core/MIPS/x86/CompVFPU.cpp index ded917e03a..d5595b5845 100644 --- a/Core/MIPS/x86/CompVFPU.cpp +++ b/Core/MIPS/x86/CompVFPU.cpp @@ -18,6 +18,7 @@ #include #include #include + #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) diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 323625724c..ef453aeef6 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -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(¤tMIPS->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(¤tMIPS->downcount), Imm32(entry->cyclesToEat)); - JMP(asm_.dispatcher, true); + MOV(32, R(ECX), M(¤tMIPS->r[MIPS_REG_RA])); + SUB(32, M(¤tMIPS->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? diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index c4fab0b5b6..26c0346dc9 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -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 diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 7222504465..3bc417596d 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -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; } diff --git a/Core/System.cpp b/Core/System.cpp index bf7a6cd628..7cec4c165f 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -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(); diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index 2051636571..e11a768ac1 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -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); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 6e2f57842d..090907bd33 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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 \