diff --git a/CMakeLists.txt b/CMakeLists.txt index 279f669c86..199cc0e0ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -968,6 +968,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/FunctionWrappers.h Core/HLE/HLE.cpp Core/HLE/HLE.h + Core/HLE/ReplaceTables.cpp + Core/HLE/ReplaceTables.h Core/HLE/HLETables.cpp Core/HLE/HLETables.h Core/HLE/KernelWaitHelpers.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 3fd32fd53d..fb115cb355 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -196,6 +196,7 @@ + @@ -454,6 +455,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 715e6ec4f0..8dedeb712a 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -505,6 +505,9 @@ Util + + HLE + @@ -936,6 +939,9 @@ Util + + HLE + diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index 1482cd4d22..6176116567 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -50,6 +50,7 @@ bool SymbolMap::LoadSymbolMap(const char *filename) { FILE *f = File::OpenCFile(filename, "r"); if (!f) return false; + //char temp[256]; //fgets(temp,255,f); //.text section layout //fgets(temp,255,f); // Starting Virtual @@ -130,14 +131,14 @@ bool SymbolMap::LoadSymbolMap(const char *filename) { return true; } - -void SymbolMap::SaveSymbolMap(const char *filename) const -{ +void SymbolMap::SaveSymbolMap(const char *filename) const { lock_guard guard(lock_); FILE *f = File::OpenCFile(filename, "w"); if (!f) return; + fprintf(f,".text\n"); + for (auto it = functions.begin(), end = functions.end(); it != end; ++it) { const FunctionEntry& e = it->second; fprintf(f,"%08x %08x %08x %i %s\n",it->first,e.size,it->first,ST_FUNCTION,GetLabelName(it->first)); @@ -150,8 +151,7 @@ void SymbolMap::SaveSymbolMap(const char *filename) const fclose(f); } -bool SymbolMap::LoadNocashSym(const char *filename) -{ +bool SymbolMap::LoadNocashSym(const char *filename) { lock_guard guard(lock_); FILE *f = File::OpenCFile(filename, "r"); if (!f) @@ -191,7 +191,7 @@ bool SymbolMap::LoadNocashSym(const char *filename) } } else { // labels int size = 1; - char* seperator = strchr(value,','); + char* seperator = strchr(value, ','); if (seperator != NULL) { *seperator = 0; sscanf(seperator+1,"%08X",&size); @@ -217,8 +217,7 @@ SymbolType SymbolMap::GetSymbolType(u32 address) const { return ST_NONE; } -bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) const -{ +bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) const { u32 functionAddress = INVALID_ADDRESS; u32 dataAddress = INVALID_ADDRESS; @@ -228,12 +227,9 @@ bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) if (symmask & ST_DATA) dataAddress = GetDataStart(address); - if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS) - { - if (functionAddress != INVALID_ADDRESS) - { - if (info != NULL) - { + if (functionAddress == INVALID_ADDRESS || dataAddress == INVALID_ADDRESS) { + if (functionAddress != INVALID_ADDRESS) { + if (info != NULL) { info->type = ST_FUNCTION; info->address = functionAddress; info->size = GetFunctionSize(functionAddress); @@ -242,10 +238,8 @@ bool SymbolMap::GetSymbolInfo(SymbolInfo *info, u32 address, SymbolType symmask) return true; } - if (dataAddress != INVALID_ADDRESS) - { - if (info != NULL) - { + if (dataAddress != INVALID_ADDRESS) { + if (info != NULL) { info->type = ST_DATA; info->address = dataAddress; info->size = GetDataSize(dataAddress); diff --git a/Core/Debugger/SymbolMap.h b/Core/Debugger/SymbolMap.h index 284eb5d006..963b390dd3 100644 --- a/Core/Debugger/SymbolMap.h +++ b/Core/Debugger/SymbolMap.h @@ -89,6 +89,7 @@ public: DataType GetDataType(u32 startAddress) const; static const u32 INVALID_ADDRESS = (u32)-1; + private: void AssignFunctionIndices(); diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index bd5f672a30..e00f0c6e7e 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -17,7 +17,9 @@ #pragma once -#include "../../Globals.h" +#include "Globals.h" +#include "Core/HLE/HLE.h" + // For easy parameter parsing and return value processing. // 64bit wrappers diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp new file mode 100644 index 0000000000..de9dba3c4b --- /dev/null +++ b/Core/HLE/ReplaceTables.cpp @@ -0,0 +1,48 @@ +// 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 "base/basictypes.h" +#include "Core/HLE/ReplaceTables.h" +#include "Core/HLE/FunctionWrappers.h" + +void NormalizeVector(u32 vecPtr) { + // TODO +} + +// 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 } +}; + +int GetNumReplacementFuncs() { + return ARRAY_SIZE(entries); +} + +int GetReplacementFuncIndex(u64 hash, int size) { + // 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) { + return i; + } + } + return -1; +} + +const ReplacementTableEntry *GetReplacementFunc(int i) { + return &entries[i]; +} \ No newline at end of file diff --git a/Core/HLE/ReplaceTables.h b/Core/HLE/ReplaceTables.h new file mode 100644 index 0000000000..94c057225d --- /dev/null +++ b/Core/HLE/ReplaceTables.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + + +#pragma once + +#include "Common/CommonTypes.h" + +typedef void (* ReplaceFunc)(); + +// Kind of similar to HLE functions but with different data. +struct ReplacementTableEntry { + u32 hash; + int size; + const char *name; + ReplaceFunc replaceFunc; + int cyclesToEat; +}; + +int GetNumReplacementFuncs(); +int GetReplacementFuncIndex(u64 hash, int size); +const ReplacementTableEntry *GetReplacementFunc(int index); \ No newline at end of file diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 2098084205..2156eb936c 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -315,6 +315,11 @@ public: std::vector importedVars; std::set impExpModuleNames; + // Keep track of the code region so we can throw out analysis results + // when unloaded. + u32 textStart; + u32 textEnd; + u32 memoryBlockAddr; u32 memoryBlockSize; bool isFake; @@ -406,7 +411,7 @@ void __KernelModuleDoState(PointerWrap &p) void __KernelModuleShutdown() { loadedModules.clear(); - MIPSAnalyst::Shutdown(); + MIPSAnalyst::Reset(); } // Sometimes there are multiple LO16's or HI16's per pair, even though the ABI says nothing of this. @@ -671,6 +676,8 @@ void UnexportFuncSymbol(const FuncSymbolExport &func) { } void Module::Cleanup() { + MIPSAnalyst::ForgetFunctions(textStart, textEnd); + loadedModules.erase(GetUID()); for (auto it = exportedVars.begin(), end = exportedVars.end(); it != end; ++it) { @@ -753,8 +760,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro module->memoryBlockAddr = reader.GetVaddr(); module->memoryBlockSize = reader.GetTotalSize(); - struct PspModuleInfo - { + struct PspModuleInfo { // 0, 0, 1, 1 ? u16_le moduleAttrs; //0x0000 User Mode, 0x1000 Kernel Mode u16_le moduleVersion; @@ -816,14 +822,17 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro #if !defined(USING_GLES2) if (!reader.LoadSymbols()) - MIPSAnalyst::ScanForFunctions(textStart, textStart+textSize); + MIPSAnalyst::ScanForFunctions(textStart, textStart + textSize, true); +#else + // Scan for functions (for the analysis results which can help the JIT). + // But don't insert into the symbol map. + // MIPSAnalyst::ScanForFunctions(textStart, textStart + textSize, false); #endif } INFO_LOG(LOADER,"Module %s: %08x %08x %08x", modinfo->name, modinfo->gp, modinfo->libent,modinfo->libstub); - struct PspLibStubEntry - { + struct PspLibStubEntry { u32_le name; u16_le version; u16_le flags; @@ -973,7 +982,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro u32 textEnd = firstImportStubAddr - 4; #if !defined(USING_GLES2) if (!reader.LoadSymbols()) - MIPSAnalyst::ScanForFunctions(textStart, textEnd); + MIPSAnalyst::ScanForFunctions(textStart, textEnd, true); #endif } diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 0a2174f102..3f66ecc35d 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -189,6 +189,7 @@ void Jit::GenerateFixedCode() POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns + // Uncomment if you want to see the output... // INFO_LOG(JIT, "THE DISASM ========================"); // DisassembleArm(enterCode, GetCodePtr() - enterCode); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 35eeb95e22..e6434ac9af 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -24,6 +24,7 @@ #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSInt.h" #include "Core/MIPS/MIPSTables.h" +#include "Core/HLE/ReplaceTables.h" #include "ArmRegCache.h" #include "ArmJit.h" @@ -338,6 +339,27 @@ void Jit::Comp_RunBlock(MIPSOpcode op) ERROR_LOG(JIT, "Comp_RunBlock should never be reached!"); } +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; + const ReplacementTableEntry *entry = GetReplacementFunc(index); + + BL((void *)(entry->replaceFunc)); + // Alternatively, we could inline it here, instead of calling out, if it's a function + // we can emit. + + 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. +} + void Jit::Comp_Generic(MIPSOpcode op) { FlushAll(); diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 994e4e1a59..f32aaaa193 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -73,6 +73,7 @@ public: void CompileDelaySlot(int flags); void EatInstruction(MIPSOpcode op); void Comp_RunBlock(MIPSOpcode op); + void Comp_ReplacementFunc(MIPSOpcode op); // Ops void Comp_ITypeMem(MIPSOpcode op); diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index 1c121d09fc..0f470be7fd 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -43,10 +43,12 @@ struct JitBlock; #define MIPS_IS_EMUHACK(op) (((op) & 0xFC000000) == MIPS_EMUHACK_OPCODE) // masks away the subop - // There are 2 bits available for sub-opcodes, 0x03000000. #define EMUOP_RUNBLOCK 0 // Runs a JIT block #define EMUOP_RETKERNEL 1 // Returns to the simulated PSP kernel from a thread +#define EMUOP_CALL_REPLACEMENT 2 + +#define MIPS_EMUHACK_CALL_REPLACEMENT (MIPS_EMUHACK_OPCODE | (EMUOP_CALL_REPLACEMENT << 24)) namespace MIPSComp { extern Jit *jit; diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 9d2c5314be..9c571ac7e0 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -27,9 +27,33 @@ #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/DebugInterface.h" +#include "Core/HLE/ReplaceTables.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "ext/xxhash.h" using namespace MIPSCodeUtils; -using namespace std; + +// Not in a namespace because MSVC's debugger doesn't like it +static std::vector functions; + +// TODO: Try multimap instead +// One function can appear in multiple copies in memory, and they will all have +// the same hash and should all be replaced if possible. +static std::map> hashToFunction; + +struct HashMapFunc { + char name[64]; + u64 hash; + u32 size; //number of bytes + + bool operator < (const HashMapFunc &other) const { + return hash < other.hash || (hash == other.hash && size < other.size); + } +}; + +static std::set hashMap; + +static std::string hashmapFileName; namespace MIPSAnalyst { // Only can ever output a single reg. @@ -132,73 +156,48 @@ namespace MIPSAnalyst { results.r[outReg].MarkWrite(addr); } - if (info & DELAYSLOT) - { + if (info & DELAYSLOT) { // Let's just finish the delay slot before bailing. endAddr = addr + 4; } } - int numUsedRegs=0; - static int totalUsedRegs=0; - static int numAnalyzings=0; + int numUsedRegs = 0; + static int totalUsedRegs = 0; + static int numAnalyzings = 0; for (int i = 0; i < MIPS_NUM_GPRS; i++) { if (results.r[i].used) { numUsedRegs++; } } - totalUsedRegs+=numUsedRegs; + totalUsedRegs += numUsedRegs; numAnalyzings++; - DEBUG_LOG(CPU,"[ %08x ] Used regs: %i Average: %f",address,numUsedRegs,(float)totalUsedRegs/(float)numAnalyzings); + VERBOSE_LOG(CPU, "[ %08x ] Used regs: %i Average: %f", address, numUsedRegs, (float)totalUsedRegs / (float)numAnalyzings); return results; } - - - struct Function - { - u32 start; - u32 end; - u64 hash; - u32 size; - bool isStraightLeaf; - bool hasHash; - bool usesVFPU; - char name[64]; - }; - - vector functions; - - map hashToFunction; - - void Shutdown() - { + + void Reset() { functions.clear(); hashToFunction.clear(); } - // hm pointless :P - void UpdateHashToFunctionMap() - { + void UpdateHashToFunctionMap() { hashToFunction.clear(); - for (vector::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - Function &f = *iter; - if (f.hasHash) - { - hashToFunction[f.hash] = &f; + for (auto iter = functions.begin(); iter != functions.end(); iter++) { + AnalyzedFunction &f = *iter; + if (f.hasHash) { + hashToFunction[f.hash].push_back(&f); } } } - - bool IsRegisterUsed(MIPSGPReg reg, u32 addr) - { - while (true) - { + // Look forwards to find if a register is used again in this block. + // Don't think we use this yet. + bool IsRegisterUsed(MIPSGPReg reg, u32 addr) { + while (true) { MIPSOpcode op = Memory::Read_Instruction(addr); MIPSInfo info = MIPSGetInfo(op); - if ((info & IN_RS) && (MIPS_GET_RS(op) == reg)) return true; if ((info & IN_RT) && (MIPS_GET_RT(op) == reg)) @@ -213,7 +212,7 @@ namespace MIPSAnalyst { return false; //the reg got clobbed! yay! if ((info & OUT_RA) && (reg == MIPS_REG_RA)) return false; //the reg got clobbed! yay! - addr+=4; + addr += 4; } return true; } @@ -222,7 +221,7 @@ namespace MIPSAnalyst { std::vector buffer; for (auto iter = functions.begin(), end = functions.end(); iter != end; iter++) { - Function &f = *iter; + AnalyzedFunction &f = *iter; // This is unfortunate. In case of emuhacks or relocs, we have to make a copy. buffer.resize((f.end - f.start + 4) / 4); @@ -315,15 +314,19 @@ namespace MIPSAnalyst { return furthestJumpbackAddr; } - void ScanForFunctions(u32 startAddr, u32 endAddr /*, std::vector knownEntries*/) { - Function currentFunction = {startAddr}; + void ReplaceFunctions(); + + void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols) { + AnalyzedFunction currentFunction = {startAddr}; u32 furthestBranch = 0; bool looking = false; bool end = false; bool isStraightLeaf = true; + u32 addr; - for (addr = startAddr; addr <= endAddr; addr+=4) { + for (addr = startAddr; addr <= endAddr; addr += 4) { + // Use pre-existing symbol map info if available. May be more reliable. SymbolInfo syminfo; if (symbolMap.GetSymbolInfo(&syminfo, addr, ST_FUNCTION)) { addr = syminfo.address + syminfo.size - 4; @@ -403,6 +406,7 @@ namespace MIPSAnalyst { } } } + if (end) { currentFunction.end = addr + 4; currentFunction.isStraightLeaf = isStraightLeaf; @@ -411,17 +415,20 @@ namespace MIPSAnalyst { addr += 4; looking = false; end = false; - isStraightLeaf=true; + isStraightLeaf = true; currentFunction.start = addr+4; } } + currentFunction.end = addr + 4; functions.push_back(currentFunction); - for (auto iter = functions.begin(); iter != functions.end(); iter++) { - iter->size = iter->end - iter->start + 4; - char temp[256]; - symbolMap.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4); + if (insertSymbols) { + for (auto iter = functions.begin(); iter != functions.end(); iter++) { + iter->size = iter->end - iter->start + 4; + char temp[256]; + symbolMap.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4); + } } HashFunctions(); @@ -430,30 +437,42 @@ namespace MIPSAnalyst { LoadHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini"); StoreHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini"); } + + // ReplaceFunctions(); } - struct HashMapFunc { - char name[64]; - u64 hash; - u32 size; //number of bytes + void ForgetFunctions(u32 startAddr, u32 endAddr) { + StoreHashMap(GetSysDirectory(DIRECTORY_SYSTEM) + "knownfuncs.ini"); - bool operator < (const HashMapFunc &other) const { - return hash < other.hash || (hash == other.hash && size < other.size); - } - }; - std::set hashMap; + // It makes sense to forget functions as modules are unloaded but it breaks + // the easy way of saving a hashmap by unloading and loading a game. I added + // an alternative way. - void StoreHashMap(const std::string &filename) { - HashFunctions(); - - FILE *file = File::OpenCFile(filename, "wt"); - if (!file) { - WARN_LOG(LOADER, "Could not store hash map: %s", filename.c_str()); - return; + // TODO: speedup + auto iter = functions.begin(); + while (iter != functions.end()) { + if (iter->start >= startAddr && iter->start <= endAddr) { + iter = functions.erase(iter); + } else { + iter++; + } } + // TODO: Also wipe them from hash->function map + } + + 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); + } + } + } + + void UpdateHashMap() { for (auto it = functions.begin(), end = functions.end(); it != end; ++it) { - const Function &f = *it; + const AnalyzedFunction &f = *it; // Small functions aren't very interesting. if (!f.hasHash || f.size < 12) { continue; @@ -468,6 +487,19 @@ namespace MIPSAnalyst { strncpy(mf.name, name, sizeof(mf.name) - 1); hashMap.insert(mf); } + } + + void StoreHashMap(std::string filename) { + if (filename.empty()) + filename = hashmapFileName; + + FILE *file = File::OpenCFile(filename, "wt"); + if (!file) { + WARN_LOG(LOADER, "Could not store hash map: %s", filename.c_str()); + return; + } + + UpdateHashMap(); for (auto it = hashMap.begin(), end = hashMap.end(); it != end; ++it) { const HashMapFunc &mf = *it; @@ -480,7 +512,6 @@ namespace MIPSAnalyst { } void ApplyHashMap() { - HashFunctions(); UpdateHashToFunctionMap(); for (auto mf = hashMap.begin(), end = hashMap.end(); mf != end; ++mf) { @@ -490,26 +521,30 @@ namespace MIPSAnalyst { } // Yay, found a function. - Function &f = *(iter->second); - if (f.hash == mf->hash && f.size == mf->size) { - strncpy(f.name, mf->name, sizeof(mf->name) - 1); - const char *existingLabel = symbolMap.GetLabelName(f.start); - char defaultLabel[256]; - // If it was renamed, keep it. Only change the name if it's still the default. - if (existingLabel == NULL || !strcmp(existingLabel, DefaultFunctionName(defaultLabel, f.start))) { - symbolMap.SetLabelName(mf->name, f.start); + for (int i = 0; i < iter->second.size(); i++) { + AnalyzedFunction &f = *(iter->second[i]); + if (f.hash == mf->hash && f.size == mf->size) { + strncpy(f.name, mf->name, sizeof(mf->name) - 1); + + const char *existingLabel = symbolMap.GetLabelName(f.start); + char defaultLabel[256]; + // If it was renamed, keep it. Only change the name if it's still the default. + if (existingLabel == NULL || !strcmp(existingLabel, DefaultFunctionName(defaultLabel, f.start))) { + symbolMap.SetLabelName(mf->name, f.start); + } } } } } - void LoadHashMap(const std::string &filename) { + void LoadHashMap(std::string filename) { FILE *file = File::OpenCFile(filename, "rt"); if (!file) { WARN_LOG(LOADER, "Could not load hash map: %s", filename.c_str()); return; } + hashmapFileName = filename; while (!feof(file)) { HashMapFunc mf = { "" }; @@ -561,11 +596,9 @@ namespace MIPSAnalyst { // gather relevant address for alu operations // that's usually the value of the dest register - switch (MIPS_GET_OP(op)) - { + switch (MIPS_GET_OP(op)) { case 0: // special - switch (MIPS_GET_FUNC(op)) - { + switch (MIPS_GET_FUNC(op)) { case 0x20: // add case 0x21: // addu info.hasRelevantAddress = true; diff --git a/Core/MIPS/MIPSAnalyst.h b/Core/MIPS/MIPSAnalyst.h index 4ae3c6be11..a32c511578 100644 --- a/Core/MIPS/MIPSAnalyst.h +++ b/Core/MIPS/MIPSAnalyst.h @@ -19,6 +19,7 @@ #include "Globals.h" #include "Core/MIPS/MIPS.h" +#include "Core/HLE/ReplaceTables.h" class DebugInterface; @@ -74,12 +75,33 @@ namespace MIPSAnalyst AnalysisResults Analyze(u32 address); + bool IsRegisterUsed(MIPSGPReg reg, u32 addr); + + struct AnalyzedFunction { + u32 start; + u32 end; + u64 hash; + u32 size; + bool isStraightLeaf; + bool hasHash; + bool usesVFPU; + char name[64]; + }; + + void Reset(); bool IsRegisterUsed(u32 reg, u32 addr); - void ScanForFunctions(u32 startAddr, u32 endAddr); + // 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. + void ScanForFunctions(u32 startAddr, u32 endAddr, bool insertSymbols); + void ForgetFunctions(u32 startAddr, u32 endAddr); void CompileLeafs(); - void LoadHashMap(const std::string &filename); - void StoreHashMap(const std::string &filename); + void LoadHashMap(std::string filename); + void StoreHashMap(std::string filename = ""); + void ReplaceFunctions(const ReplacementTableEntry *e, int numEntries); + + void UpdateHashMap(); + void ApplyHashMap(); std::vector GetInputRegs(MIPSOpcode op); std::vector GetOutputRegs(MIPSOpcode op); @@ -93,8 +115,7 @@ namespace MIPSAnalyst void Shutdown(); - typedef struct - { + typedef struct { DebugInterface* cpu; u32 opcodeAddress; MIPSOpcode encodedOpcode; diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 471a6f0e6b..74a1590dad 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -31,6 +31,7 @@ #include "Core/Config.h" #include "Core/HLE/HLE.h" #include "Core/HLE/HLETables.h" +#include "Core/HLE/ReplaceTables.h" #include "Core/System.h" #define R(i) (currentMIPS->r[i]) @@ -1010,7 +1011,19 @@ namespace MIPSInt void Int_Emuhack(MIPSOpcode op) { - _dbg_assert_msg_(CPU,0,"Trying to interpret emuhack instruction that can't be interpreted"); + if (((op >> 24) & 3) != 1) { + _dbg_assert_msg_(CPU,0,"Trying to interpret emuhack instruction that can't be interpreted"); + } + // It's a replacement func! + int index = op.encoding & 0xFFFFFF; + const ReplacementTableEntry *entry = GetReplacementFunc(index); + if (entry) { + entry->replaceFunc(); + } else { + ERROR_LOG(CPU, "Bad replacement function index %i", index); + } + + PC = currentMIPS->r[MIPS_REG_RA]; } diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 3bc8cbedba..152b4c9161 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -808,11 +808,10 @@ const MIPSInstruction tableALLEGREX0[32] = // 011111 ..... ..... ..... xxxxx 10 }; -const MIPSInstruction tableEMU[4] = -{ +const MIPSInstruction tableEMU[4] = { INSTR("RUNBLOCK", &Jit::Comp_RunBlock, Dis_Emuhack, Int_Emuhack, 0xFFFFFFFF), INSTR("RetKrnl", 0, Dis_Emuhack, Int_Emuhack, 0), - INVALID, + INSTR("CallRepl", &Jit::Comp_ReplacementFunc, Dis_Emuhack, Int_Emuhack, 0), INVALID, }; diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h index 986fd61483..2ae1287686 100644 --- a/Core/MIPS/MIPSTables.h +++ b/Core/MIPS/MIPSTables.h @@ -47,16 +47,16 @@ struct MIPSInfo { #define CONDTYPE_FPUTRUE CONDTYPE_NE // as long as the other flags are checked, -// there is no way to misinterprete these +// there is no way to misinterpret these // as CONDTYPE_X -#define MEMTYPE_MASK 0x00000007 -#define MEMTYPE_BYTE 0x00000001 -#define MEMTYPE_HWORD 0x00000002 -#define MEMTYPE_WORD 0x00000003 +#define MEMTYPE_MASK 0x00000007 +#define MEMTYPE_BYTE 0x00000001 +#define MEMTYPE_HWORD 0x00000002 +#define MEMTYPE_WORD 0x00000003 #define MEMTYPE_FLOAT 0x00000004 #define MEMTYPE_VQUAD 0x00000005 -#define IS_CONDMOVE 0x00000008 +#define IS_CONDMOVE 0x00000008 #define DELAYSLOT 0x00000010 #define BAD_INSTRUCTION 0x00000020 #define LIKELY 0x00000040 diff --git a/Core/MIPS/PPC/PpcJit.cpp b/Core/MIPS/PPC/PpcJit.cpp index f9ece053da..7f32a89b85 100644 --- a/Core/MIPS/PPC/PpcJit.cpp +++ b/Core/MIPS/PPC/PpcJit.cpp @@ -161,6 +161,17 @@ void Jit::Comp_RunBlock(MIPSOpcode op) { ERROR_LOG(JIT, "Comp_RunBlock should never be reached!"); } +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 replacementFunc = op & 0xFFFFFFF; + + + // We could even do this in the jal that is branching to the function + // but having the op is necessary for the interpreter anyway. +} + void Jit::Comp_DoNothing(MIPSOpcode op) { } diff --git a/Core/MIPS/PPC/PpcJit.h b/Core/MIPS/PPC/PpcJit.h index c3a2587262..4b5ed4cd89 100644 --- a/Core/MIPS/PPC/PpcJit.h +++ b/Core/MIPS/PPC/PpcJit.h @@ -170,6 +170,7 @@ namespace MIPSComp void EatInstruction(MIPSOpcode op); void Comp_RunBlock(MIPSOpcode op); + void Comp_ReplacementFunc(MIPSOpcode op); // TODO: Eat VFPU prefixes here. void EatPrefix() { js.EatPrefix(); } diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index b81503869d..5295e3f9cc 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -30,6 +30,7 @@ #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSInt.h" #include "Core/MIPS/MIPSTables.h" +#include "Core/HLE/ReplaceTables.h" #include "RegCache.h" #include "Jit.h" @@ -371,6 +372,28 @@ void Jit::Comp_RunBlock(MIPSOpcode op) ERROR_LOG(JIT, "Comp_RunBlock"); } +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; + const ReplacementTableEntry *entry = GetReplacementFunc(index); + + CALL(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); + + 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. +} + void Jit::Comp_Generic(MIPSOpcode op) { FlushAll(); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index c4a80ec764..c4fab0b5b6 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -28,6 +28,7 @@ #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" @@ -78,6 +79,7 @@ public: } void Comp_RunBlock(MIPSOpcode op); + void Comp_ReplacementFunc(MIPSOpcode op); // Ops void Comp_ITypeMem(MIPSOpcode op); diff --git a/Core/System.cpp b/Core/System.cpp index 161af350ca..7c959ab4a9 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -30,6 +30,7 @@ #include "Core/MemMap.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/System.h" @@ -165,6 +166,8 @@ void CPU_Init() { std::string filename = coreParameter.fileToStart; IdentifiedFileType type = Identify_File(filename); + MIPSAnalyst::Reset(); + switch (type) { case FILETYPE_PSP_ISO: case FILETYPE_PSP_ISO_NP: @@ -319,6 +322,7 @@ bool PSP_IsInited() { } void PSP_Shutdown() { + MIPSAnalyst::StoreHashMap(); if (coreState == CORE_RUNNING) Core_UpdateState(CORE_ERROR); Core_NotifyShutdown(); diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index b842893c28..2051636571 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -919,6 +919,8 @@ void CtrlDisAsmView::onMouseUp(WPARAM wParam, LPARAM lParam, int button) if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"New function name", name, newname)) { symbolMap.SetLabelName(newname.c_str(),funcBegin); + MIPSAnalyst::UpdateHashMap(); + MIPSAnalyst::ApplyHashMap(); SendMessage(GetParent(wnd),WM_DEB_MAPLOADED,0,0); redraw(); } diff --git a/android/jni/Android.mk b/android/jni/Android.mk index f82259caf0..6e2f57842d 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -190,6 +190,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Dialog/SavedataParam.cpp \ $(SRC)/Core/Font/PGF.cpp \ $(SRC)/Core/HLE/HLETables.cpp \ + $(SRC)/Core/HLE/ReplaceTables.cpp \ $(SRC)/Core/HLE/HLE.cpp \ $(SRC)/Core/HLE/sceAtrac.cpp \ $(SRC)/Core/HLE/__sceAudio.cpp.arm \