From e3506ead69366c0ed749bf5621eab902003ccfaf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Mar 2014 21:01:03 -0800 Subject: [PATCH 1/4] Clear the symbol map between each test. This way we could, for example, test function replacement properly. Actually slows down tests because it spends more time correctly determining the function boundaries. --- Core/Debugger/SymbolMap.cpp | 14 ++++++++++---- headless/StubHost.h | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Core/Debugger/SymbolMap.cpp b/Core/Debugger/SymbolMap.cpp index e3a0b56dae..9e5f07587b 100644 --- a/Core/Debugger/SymbolMap.cpp +++ b/Core/Debugger/SymbolMap.cpp @@ -615,15 +615,21 @@ void SymbolMap::AssignFunctionIndices() { void SymbolMap::UpdateActiveSymbols() { // return; (slow in debug mode) lock_guard guard(lock_); - std::map activeModuleIndexes; - for (auto it = activeModuleEnds.begin(), end = activeModuleEnds.end(); it != end; ++it) { - activeModuleIndexes[it->second.index] = it->second.start; - } activeFunctions.clear(); activeLabels.clear(); activeData.clear(); + // On startup and shutdown, we can skip the rest. Tiny optimization. + if (activeModuleEnds.empty() || (functions.empty() && labels.empty() && data.empty())) { + return; + } + + std::map activeModuleIndexes; + for (auto it = activeModuleEnds.begin(), end = activeModuleEnds.end(); it != end; ++it) { + activeModuleIndexes[it->second.index] = it->second.start; + } + for (auto it = functions.begin(), end = functions.end(); it != end; ++it) { const auto mod = activeModuleIndexes.find(it->second.module); if (it->second.module == 0) { diff --git a/headless/StubHost.h b/headless/StubHost.h index 47ad6886d1..3247abccf1 100644 --- a/headless/StubHost.h +++ b/headless/StubHost.h @@ -17,7 +17,8 @@ #pragma once -#include "../Core/Host.h" +#include "Core/Host.h" +#include "Core/Debugger/SymbolMap.h" #define HEADLESSHOST_CLASS HeadlessHost @@ -44,7 +45,7 @@ public: virtual void BootDone() {} virtual bool IsDebuggingEnabled() {return false;} - virtual bool AttemptLoadSymbolMap() {return false;} + virtual bool AttemptLoadSymbolMap() { symbolMap.Clear(); return false; } virtual bool ShouldSkipUI() { return true; } From eca06c60c71afcfaf41a1797986d5a3c4739f257 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Mar 2014 21:15:17 -0800 Subject: [PATCH 2/4] Spend a bit less time in Read_Instruction on load. --- Core/MIPS/MIPSAnalyst.cpp | 6 +++--- Core/MIPS/MIPSCodeUtils.cpp | 5 +++++ Core/MIPS/MIPSCodeUtils.h | 1 + Core/MemMap.cpp | 19 +++++++++++++++++-- Core/MemMap.h | 1 + 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 28bc9135c4..7f013d5768 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -337,7 +337,7 @@ skip: for (u32 ahead = fromAddr; ahead < fromAddr + MAX_AHEAD_SCAN; ahead += 4) { MIPSOpcode aheadOp = Memory::Read_Instruction(ahead); - u32 target = GetBranchTargetNoRA(ahead); + u32 target = GetBranchTargetNoRA(ahead, aheadOp); if (target == INVALIDTARGET && ((aheadOp & 0xFC000000) == 0x08000000)) { target = GetJumpTarget(ahead); } @@ -358,7 +358,7 @@ skip: if (closestJumpbackAddr != INVALIDTARGET && furthestJumpbackAddr == INVALIDTARGET) { for (u32 behind = closestJumpbackTarget; behind < fromAddr; behind += 4) { MIPSOpcode behindOp = Memory::Read_Instruction(behind); - u32 target = GetBranchTargetNoRA(behind); + u32 target = GetBranchTargetNoRA(behind, behindOp); if (target == INVALIDTARGET && ((behindOp & 0xFC000000) == 0x08000000)) { target = GetJumpTarget(behind); } @@ -409,7 +409,7 @@ skip: } MIPSOpcode op = Memory::Read_Instruction(addr); - u32 target = GetBranchTargetNoRA(addr); + u32 target = GetBranchTargetNoRA(addr, op); if (target != INVALIDTARGET) { isStraightLeaf = false; if (target > furthestBranch) { diff --git a/Core/MIPS/MIPSCodeUtils.cpp b/Core/MIPS/MIPSCodeUtils.cpp index 727469fccb..7d52278f66 100644 --- a/Core/MIPS/MIPSCodeUtils.cpp +++ b/Core/MIPS/MIPSCodeUtils.cpp @@ -67,6 +67,11 @@ namespace MIPSCodeUtils u32 GetBranchTargetNoRA(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr); + return GetBranchTargetNoRA(addr, op); + } + + u32 GetBranchTargetNoRA(u32 addr, MIPSOpcode op) + { if (op != 0) { MIPSInfo info = MIPSGetInfo(op); diff --git a/Core/MIPS/MIPSCodeUtils.h b/Core/MIPS/MIPSCodeUtils.h index 502c42f637..4e2d20d437 100644 --- a/Core/MIPS/MIPSCodeUtils.h +++ b/Core/MIPS/MIPSCodeUtils.h @@ -53,6 +53,7 @@ namespace MIPSCodeUtils u32 GetBranchTarget(u32 addr); // Ignores bltzal/etc. instructions that change RA. u32 GetBranchTargetNoRA(u32 addr); + u32 GetBranchTargetNoRA(u32 addr, MIPSOpcode op); u32 GetJumpTarget(u32 addr); u32 GetSureBranchTarget(u32 addr); bool IsVFPUBranch(MIPSOpcode op); diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 039203b497..298aedc5ce 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -176,9 +176,12 @@ void Clear() memset(m_pVRAM, 0, VRAM_SIZE); } -Opcode Read_Instruction(u32 address, bool resolveReplacements) +static Opcode Read_Instruction(u32 address, bool resolveReplacements, Opcode inst) { - Opcode inst = Opcode(Read_U32(address)); + if (!MIPS_IS_EMUHACK(inst.encoding)) { + return inst; + } + if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) { JitBlockCache *bc = MIPSComp::jit->GetBlockCache(); int block_num = bc->GetBlockNumberFromEmuHackOp(inst, true); @@ -218,6 +221,18 @@ Opcode Read_Instruction(u32 address, bool resolveReplacements) } } +Opcode Read_Instruction(u32 address, bool resolveReplacements) +{ + Opcode inst = Opcode(Read_U32(address)); + return Read_Instruction(address, resolveReplacements, inst); +} + +Opcode ReadUnchecked_Instruction(u32 address, bool resolveReplacements) +{ + Opcode inst = Opcode(ReadUnchecked_U32(address)); + return Read_Instruction(address, resolveReplacements, inst); +} + Opcode Read_Opcode_JIT(u32 address) { Opcode inst = Opcode(Read_U32(address)); diff --git a/Core/MemMap.h b/Core/MemMap.h index c312ac95f8..81f56df1ee 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -140,6 +140,7 @@ void Write_Opcode_JIT(const u32 _Address, const Opcode _Value); // Should be used by analyzers, disassemblers etc. Does resolve replacements. Opcode Read_Instruction(const u32 _Address, bool resolveReplacements = false); +Opcode ReadUnchecked_Instruction(const u32 _Address, bool resolveReplacements = false); u8 Read_U8(const u32 _Address); u16 Read_U16(const u32 _Address); From f300f46b9c72756d5af0bfc69a4132e9641d9476 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Mar 2014 21:34:41 -0800 Subject: [PATCH 3/4] Speed up MIPSGetInstruction() by 35%. It's not that hot, but gets called a lot when loading modules / running tests. Well, and also interpreter. Some of the previous checks were just not necessary (we never have Rese in the tables, for example, and let's just not.) --- Core/MIPS/MIPSTables.cpp | 97 +++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 5a29d34903..224baf988b 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -60,11 +60,14 @@ enum MipsEncoding Emu, Rese, NumEncodings, + + Instruc = -1, + Inval = -2, }; struct MIPSInstruction { - int altEncoding; + MipsEncoding altEncoding; const char *name; MIPSComp::MIPSCompileFunc compile; #ifndef FINAL @@ -75,16 +78,16 @@ struct MIPSInstruction MIPSInfo flags; }; -#define INVALID {-2} +#define INVALID {Inval} #define INVALID_X_8 INVALID,INVALID,INVALID,INVALID,INVALID,INVALID,INVALID,INVALID #define N(a) a #ifndef FINAL #define ENCODING(a) {a} -#define INSTR(name, comp, dis, inter, flags) {-1, N(name), comp, dis, inter, MIPSInfo(flags)} +#define INSTR(name, comp, dis, inter, flags) {Instruc, N(name), comp, dis, inter, MIPSInfo(flags)} #else #define ENCODING(a) {a} -#define INSTR(name, comp, dis, inter, flags) {-1, comp, inter, flags} +#define INSTR(name, comp, dis, inter, flags) {Instruc, comp, inter, flags} #endif @@ -816,36 +819,44 @@ const MIPSInstruction tableEMU[4] = { INVALID, }; -const int encodingBits[NumEncodings][2] = -{ - {26, 6}, //IMME - {0, 6}, //Special - {0, 6}, //special2 - {0, 6}, //special3 - {16, 5}, //RegImm - {21, 5}, //Cop0 - {0, 6}, //Cop0CO - {21, 5}, //Cop1 - {16, 5}, //Cop1BC - {0, 6}, //Cop1S - {0, 6}, //Cop1W - {21, 5}, //Cop2 - {16, 2}, //Cop2BC2 - {0, 0}, //Cop2Rese - {23, 3}, //VFPU0 - {23, 3}, //VFPU1 - {23, 3}, //VFPU3 - {21, 5}, //VFPU4Jump - {16, 5}, //VFPU7 - {16, 5}, //VFPU4 - {23, 3}, //VFPU5 - {21, 5}, //VFPU6 - {16, 4}, //VFPUMatrix1 - {16, 5}, //VFPU9 - {6, 5}, //ALLEGREX0 - {24, 2}, //EMUHACK +struct EncodingBitsInfo { + EncodingBitsInfo(u8 shift_, u8 maskBits_) : shift(shift_) { + mask = (1 << maskBits_) - 1; + } + u8 shift; + u32 mask; }; +const EncodingBitsInfo encodingBits[NumEncodings] = +{ + EncodingBitsInfo(26, 6), //IMME + EncodingBitsInfo(0, 6), //Special + EncodingBitsInfo(0, 6), //special2 + EncodingBitsInfo(0, 6), //special3 + EncodingBitsInfo(16, 5), //RegImm + EncodingBitsInfo(21, 5), //Cop0 + EncodingBitsInfo(0, 6), //Cop0CO + EncodingBitsInfo(21, 5), //Cop1 + EncodingBitsInfo(16, 5), //Cop1BC + EncodingBitsInfo(0, 6), //Cop1S + EncodingBitsInfo(0, 6), //Cop1W + EncodingBitsInfo(21, 5), //Cop2 + EncodingBitsInfo(16, 2), //Cop2BC2 + EncodingBitsInfo(0, 0), //Cop2Rese + EncodingBitsInfo(23, 3), //VFPU0 + EncodingBitsInfo(23, 3), //VFPU1 + EncodingBitsInfo(23, 3), //VFPU3 + EncodingBitsInfo(21, 5), //VFPU4Jump + EncodingBitsInfo(16, 5), //VFPU7 + EncodingBitsInfo(16, 5), //VFPU4 + EncodingBitsInfo(23, 3), //VFPU5 + EncodingBitsInfo(21, 5), //VFPU6 + EncodingBitsInfo(16, 4), //VFPUMatrix1 + EncodingBitsInfo(16, 5), //VFPU9 + EncodingBitsInfo(6, 5), //ALLEGREX0 + EncodingBitsInfo(24, 2), //EMUHACK + EncodingBitsInfo(0, 0), //Rese +}; const MIPSInstruction *mipsTables[NumEncodings] = { @@ -874,7 +885,8 @@ const MIPSInstruction *mipsTables[NumEncodings] = tableVFPUMatrixSet1, tableVFPU9, tableALLEGREX0, - tableEMU + tableEMU, + 0, }; @@ -896,23 +908,18 @@ const MIPSInstruction *MIPSGetInstruction(MIPSOpcode op) { MipsEncoding encoding = Imme; const MIPSInstruction *instr = &tableImmediate[op>>26]; - while (instr->altEncoding != -1) + while (instr->altEncoding != Instruc) { - const MIPSInstruction *table = mipsTables[encoding]; - int mask = ((1<> shift) & mask; - instr = &table[subop]; - if (encoding == Rese) - return 0; //invalid instruction - if (!instr) - return 0; - if (instr->altEncoding == -2) + if (instr->altEncoding == Inval) { //ERROR_LOG(CPU, "Invalid instruction %08x in table %i, entry %i", op, (int)encoding, subop); return 0; //invalid instruction } - encoding = (MipsEncoding)instr->altEncoding; + encoding = instr->altEncoding; + + const MIPSInstruction *table = mipsTables[encoding]; + const u32 subop = (op >> encodingBits[encoding].shift) & encodingBits[encoding].mask; + instr = &table[subop]; } //alright, we have a valid MIPS instruction! return instr; From ca938bf35390e4da4dedbac3596a8b7853ae6c2a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 2 Mar 2014 22:03:02 -0800 Subject: [PATCH 4/4] Cut down on locking in DEBUG_LOG. Reduces GenericLog() from 13.8% to 1.5% in Where Is My Heart?, will probably help other games to a lesser extent. --- Common/LogManager.cpp | 13 +++++++------ Common/LogManager.h | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index 89539f57c1..d701750a0c 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -172,13 +172,11 @@ void LogManager::LoadConfig(IniFile::Section *section) { } void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char *file, int line, const char *format, va_list args) { - std::lock_guard lk(log_lock_); - - char msg[MAX_MSGLEN * 2]; LogChannel *log = log_[type]; - if (!log || !log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners()) + if (level > log->GetLevel() || !log->IsEnabled() || !log->HasListeners()) return; + std::lock_guard lk(log_lock_); static const char level_to_char[8] = "-NEWIDV"; char formattedTime[13]; Common::Timer::GetTimeFormatted(formattedTime); @@ -196,7 +194,8 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const if (fileshort != file) file = fileshort + 1; } - + + char msg[MAX_MSGLEN * 2]; char *msgPos = msg; if (hleCurrentThreadName != NULL) { msgPos += sprintf(msgPos, "%s %-12.12s %c[%s]: %s:%d ", @@ -228,7 +227,7 @@ void LogManager::Shutdown() { } LogChannel::LogChannel(const char* shortName, const char* fullName, bool enable) - : enable_(enable) { + : enable_(enable), m_hasListeners(false) { strncpy(m_fullName, fullName, 128); strncpy(m_shortName, shortName, 32); #if defined(_DEBUG) @@ -242,11 +241,13 @@ LogChannel::LogChannel(const char* shortName, const char* fullName, bool enable) void LogChannel::AddListener(LogListener *listener) { std::lock_guard lk(m_listeners_lock); m_listeners.insert(listener); + m_hasListeners = true; } void LogChannel::RemoveListener(LogListener *listener) { std::lock_guard lk(m_listeners_lock); m_listeners.erase(listener); + m_hasListeners = !m_listeners.empty(); } void LogChannel::Trigger(LogTypes::LOG_LEVELS level, const char *msg) { diff --git a/Common/LogManager.h b/Common/LogManager.h index d7ded3de8e..3726552f85 100644 --- a/Common/LogManager.h +++ b/Common/LogManager.h @@ -83,7 +83,7 @@ public: LogTypes::LOG_LEVELS GetLevel() const { return (LogTypes::LOG_LEVELS)level_; } void SetLevel(LogTypes::LOG_LEVELS level) { level_ = level; } - bool HasListeners() const { return !m_listeners.empty(); } + bool HasListeners() const { return m_hasListeners; } // Although not elegant, easy to set with a PopupMultiChoice... int level_; @@ -94,6 +94,7 @@ private: char m_shortName[32]; std::mutex m_listeners_lock; std::set m_listeners; + bool m_hasListeners; }; class ConsoleListener;