From 1c9b5efb4ca1d44715a5d566aad288a38bc2b10b Mon Sep 17 00:00:00 2001 From: degasus Date: Fri, 3 Jul 2015 00:15:01 +0200 Subject: [PATCH] CachedInterpreter: New kind of jit which always fallback to interpreter. --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/Core.vcxproj | 2 + Source/Core/Core/Core.vcxproj.filters | 6 + .../Core/Core/PowerPC/CachedInterpreter.cpp | 196 ++++++++++++++++++ Source/Core/Core/PowerPC/CachedInterpreter.h | 75 +++++++ Source/Core/Core/PowerPC/Gekko.h | 4 +- Source/Core/Core/PowerPC/JitInterface.cpp | 8 + Source/Core/Core/PowerPC/PowerPC.h | 3 +- .../DolphinWX/Config/GeneralConfigPane.cpp | 9 +- 9 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 Source/Core/Core/PowerPC/CachedInterpreter.cpp create mode 100644 Source/Core/Core/PowerPC/CachedInterpreter.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index e96be9fc1a..636d8fa928 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRCS ActionReplay.cpp PowerPC/JitCommon/JitAsmCommon.cpp PowerPC/JitCommon/JitBase.cpp PowerPC/JitCommon/JitCache.cpp + PowerPC/CachedInterpreter.cpp PowerPC/JitILCommon/IR.cpp PowerPC/JitILCommon/JitILBase_Branch.cpp PowerPC/JitILCommon/JitILBase_LoadStore.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 408a017838..eaba8a5f96 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -242,6 +242,7 @@ + @@ -425,6 +426,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index e2022c26aa..b35c0caa9c 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -609,6 +609,9 @@ HW %28Flipper/Hollywood%29\Wiimote + + PowerPC + PowerPC @@ -1174,6 +1177,9 @@ PowerPC + + PowerPC + PowerPC diff --git a/Source/Core/Core/PowerPC/CachedInterpreter.cpp b/Source/Core/Core/PowerPC/CachedInterpreter.cpp new file mode 100644 index 0000000000..d2e19c5f81 --- /dev/null +++ b/Source/Core/Core/PowerPC/CachedInterpreter.cpp @@ -0,0 +1,196 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/Common.h" + +#include "Core/PowerPC/CachedInterpreter.h" + +void CachedInterpreter::Init() +{ + m_code.reserve(CODE_SIZE / sizeof(Instruction)); + + JitBaseBlockCache::Init(); + + code_block.m_stats = &js.st; + code_block.m_gpa = &js.gpa; + code_block.m_fpa = &js.fpa; +} + +void CachedInterpreter::Shutdown() +{ + JitBaseBlockCache::Shutdown(); +} + +void CachedInterpreter::Run() +{ + while (!PowerPC::GetState()) + { + SingleStep(); + } + + // Let the waiting thread know we are done leaving + PowerPC::FinishStateMove(); +} + +void CachedInterpreter::SingleStep() +{ + int block = GetBlockNumberFromStartAddress(PC); + if (block >= 0) + { + Instruction* code = (Instruction*)GetCompiledCodeFromBlock(block); + + while (true) + { + switch (code->type) + { + case Instruction::INSTRUCTION_ABORT: + return; + + case Instruction::INSTRUCTION_TYPE_COMMON: + code->common_callback(UGeckoInstruction(code->data)); + code++; + break; + + case Instruction::INSTRUCTION_TYPE_CONDITIONAL: + bool ret = code->conditional_callback(code->data); + code++; + if (ret) + return; + break; + } + } + } + + Jit(PC); +} + +static void EndBlock(UGeckoInstruction data) +{ + PC = NPC; + PowerPC::CheckExceptions(); + PowerPC::ppcState.downcount -= data.hex; + if (PowerPC::ppcState.downcount <= 0) + { + CoreTiming::Advance(); + } +} + +static void WritePC(UGeckoInstruction data) +{ + PC = data.hex; + NPC = data.hex + 4; +} + +static bool CheckFPU(u32 data) +{ + UReg_MSR& msr = (UReg_MSR&)MSR; + if (!msr.FP) + { + PC = NPC = data; + PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; + PowerPC::CheckExceptions(); + return true; + } + return false; +} + +void CachedInterpreter::Jit(u32 address) +{ + if (m_code.size() >= CODE_SIZE / sizeof(Instruction) - 0x1000 || IsFull() || SConfig::GetInstance().bJITNoBlockCache) + { + ClearCache(); + } + + u32 nextPC = analyzer.Analyze(PC, &code_block, &code_buffer, code_buffer.GetSize()); + if (code_block.m_memory_exception) + { + // Address of instruction could not be translated + NPC = nextPC; + PowerPC::ppcState.Exceptions |= EXCEPTION_ISI; + PowerPC::CheckExceptions(); + WARN_LOG(POWERPC, "ISI exception at 0x%08x", nextPC); + return; + } + + int block_num = AllocateBlock(PC); + JitBlock *b = GetBlock(block_num); + + js.blockStart = PC; + js.firstFPInstructionFound = false; + js.fifoBytesThisBlock = 0; + js.downcountAmount = 0; + js.curBlock = b; + + PPCAnalyst::CodeOp *ops = code_buffer.codebuffer; + + b->checkedEntry = GetCodePtr(); + b->normalEntry = GetCodePtr(); + b->runCount = 0; + + for (u32 i = 0; i < code_block.m_num_instructions; i++) + { + js.downcountAmount += ops[i].opinfo->numCycles; + + u32 function = HLE::GetFunctionIndex(ops[i].address); + if (function != 0) + { + int type = HLE::GetFunctionTypeByIndex(function); + if (type == HLE::HLE_HOOK_START || type == HLE::HLE_HOOK_REPLACE) + { + int flags = HLE::GetFunctionFlagsByIndex(function); + if (HLE::IsEnabled(flags)) + { + m_code.emplace_back(WritePC, ops[i].address); + m_code.emplace_back(Interpreter::HLEFunction, ops[i].inst); + if (type == HLE::HLE_HOOK_REPLACE) + { + m_code.emplace_back(EndBlock, js.downcountAmount); + m_code.emplace_back(); + break; + } + } + } + } + + if (!ops[i].skip) + { + if ((ops[i].opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound) + { + m_code.emplace_back(CheckFPU, ops[i].address); + js.firstFPInstructionFound = true; + } + + if (ops[i].opinfo->flags & FL_ENDBLOCK) + m_code.emplace_back(WritePC, ops[i].address); + m_code.emplace_back(GetInterpreterOp(ops[i].inst), ops[i].inst); + if (ops[i].opinfo->flags & FL_ENDBLOCK) + m_code.emplace_back(EndBlock, js.downcountAmount); + } + } + if (code_block.m_broken) + { + m_code.emplace_back(WritePC, nextPC); + m_code.emplace_back(EndBlock, js.downcountAmount); + } + m_code.emplace_back(); + + b->codeSize = (u32)(GetCodePtr() - b->checkedEntry); + b->originalSize = code_block.m_num_instructions; + + FinalizeBlock(block_num, jo.enableBlocklink, b->checkedEntry); +} + +void CachedInterpreter::ClearCache() +{ + m_code.clear(); + JitBaseBlockCache::Clear(); +} + +void CachedInterpreter::WriteDestroyBlock(const u8* location, u32 address) +{ +} + +void CachedInterpreter::WriteLinkBlock(u8* location, const u8* address) +{ +} diff --git a/Source/Core/Core/PowerPC/CachedInterpreter.h b/Source/Core/Core/PowerPC/CachedInterpreter.h new file mode 100644 index 0000000000..9fd2983a85 --- /dev/null +++ b/Source/Core/Core/PowerPC/CachedInterpreter.h @@ -0,0 +1,75 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Core/PowerPC/CPUCoreBase.h" +#include "Core/PowerPC/PPCAnalyst.h" +#include "Core/PowerPC/JitCommon/JitBase.h" + +class CachedInterpreter : public JitBase, JitBaseBlockCache +{ +public: + CachedInterpreter() : code_buffer(32000) {} + ~CachedInterpreter() {} + + void Init() override; + void Shutdown() override; + + bool HandleFault(uintptr_t access_address, SContext* ctx) override { return false; } + + void ClearCache() override; + + void Run() override; + void SingleStep() override; + + void Jit(u32 address) override; + + JitBaseBlockCache* GetBlockCache() override { return this; } + + const char* GetName() override + { + return "Cached Interpreter"; + } + + void WriteLinkBlock(u8* location, const u8* address) override; + + void WriteDestroyBlock(const u8* location, u32 address) override; + + const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; }; + +private: + struct Instruction + { + typedef void (*CommonCallback)(UGeckoInstruction); + typedef bool (*ConditionalCallback)(u32 data); + + Instruction() : type(INSTRUCTION_ABORT) {}; + Instruction(const CommonCallback c, UGeckoInstruction i) : common_callback(c), data(i.hex), type(INSTRUCTION_TYPE_COMMON) {}; + Instruction(const ConditionalCallback c, u32 d) : conditional_callback(c), data(d), type(INSTRUCTION_TYPE_CONDITIONAL) {}; + + union + { + const CommonCallback common_callback; + const ConditionalCallback conditional_callback; + + }; + u32 data; + enum + { + INSTRUCTION_ABORT, + INSTRUCTION_TYPE_COMMON, + INSTRUCTION_TYPE_CONDITIONAL, + } type; + }; + + const u8* GetCodePtr() { return (u8*)(m_code.data() + m_code.size()); } + + std::vector m_code; + + PPCAnalyst::CodeBuffer code_buffer; +}; + diff --git a/Source/Core/Core/PowerPC/Gekko.h b/Source/Core/Core/PowerPC/Gekko.h index ae061baf9d..74df222068 100644 --- a/Source/Core/Core/PowerPC/Gekko.h +++ b/Source/Core/Core/PowerPC/Gekko.h @@ -17,8 +17,8 @@ union UGeckoInstruction { u32 hex; - UGeckoInstruction(u32 _hex) { hex = _hex;} - UGeckoInstruction() { hex = 0;} + UGeckoInstruction(u32 _hex) : hex(_hex) {} + UGeckoInstruction() : hex(0) {} struct { diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index 0a8b89213a..d059f08fab 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -14,6 +14,7 @@ #include "Core/ConfigManager.h" #include "Core/HW/Memmap.h" +#include "Core/PowerPC/CachedInterpreter.h" #include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PPCSymbolDB.h" @@ -63,6 +64,10 @@ namespace JitInterface ptr = new JitArm64(); break; #endif + case PowerPC::CORE_CACHEDINTERPRETER: + ptr = new CachedInterpreter(); + break; + default: PanicAlert("Unrecognizable cpu_core: %d", core); jit = nullptr; @@ -89,6 +94,9 @@ namespace JitInterface JitArm64Tables::InitTables(); break; #endif + case PowerPC::CORE_CACHEDINTERPRETER: + // has no tables + break; default: PanicAlert("Unrecognizable cpu_core: %d", core); break; diff --git a/Source/Core/Core/PowerPC/PowerPC.h b/Source/Core/Core/PowerPC/PowerPC.h index 1ad1bc8f62..dc91d488c8 100644 --- a/Source/Core/Core/PowerPC/PowerPC.h +++ b/Source/Core/Core/PowerPC/PowerPC.h @@ -27,7 +27,8 @@ enum CORE_JIT64, CORE_JITIL64, CORE_JITARM, - CORE_JITARM64 + CORE_JITARM64, + CORE_CACHEDINTERPRETER, }; enum CoreMode diff --git a/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp b/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp index df9641ae95..38485ed79e 100644 --- a/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp +++ b/Source/Core/DolphinWX/Config/GeneralConfigPane.cpp @@ -22,12 +22,13 @@ GeneralConfigPane::GeneralConfigPane(wxWindow* parent, wxWindowID id) : wxPanel(parent, id) { cpu_cores = { - { 0, _("Interpreter (VERY slow)") }, + { PowerPC::CORE_INTERPRETER, _("Interpreter (slowest)") }, + { PowerPC::CORE_CACHEDINTERPRETER, _("Cached Interpreter (slower)") }, #ifdef _M_X86_64 - { 1, _("JIT Recompiler (recommended)") }, - { 2, _("JITIL Recompiler (slower, experimental)") }, + { PowerPC::CORE_JIT64, _("JIT Recompiler (recommended)") }, + { PowerPC::CORE_JITIL64, _("JITIL Recompiler (slow, experimental)") }, #elif defined(_M_ARM_64) - { 4, _("Arm64 JIT (experimental)") }, + { PowerPC::CORE_JITARM64, _("JIT Arm64 (experimental)") }, #endif };