From 17fc25434242e6b453e34141daf3c0cd70b3b6db Mon Sep 17 00:00:00 2001 From: lif Date: Sat, 15 Feb 2020 21:07:42 -0800 Subject: [PATCH] Add "JitKvm" (copy of Cached Interpreter for now) --- CMakeLists.txt | 4 + Source/Core/Core/CMakeLists.txt | 7 + Source/Core/Core/MachineContext.h | 2 +- Source/Core/Core/MemTools.cpp | 2 +- Source/Core/Core/PowerPC/JitInterface.cpp | 9 + Source/Core/Core/PowerPC/JitKvm/JitKvm.cpp | 298 ++++++++++++++++++ Source/Core/Core/PowerPC/JitKvm/JitKvm.h | 45 +++ .../Core/PowerPC/JitKvm/KvmBlockCache.cpp | 15 + .../Core/Core/PowerPC/JitKvm/KvmBlockCache.h | 18 ++ Source/Core/Core/PowerPC/PowerPC.cpp | 4 + Source/Core/Core/PowerPC/PowerPC.h | 1 + .../Core/DolphinQt/Settings/AdvancedPane.cpp | 1 + 12 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 Source/Core/Core/PowerPC/JitKvm/JitKvm.cpp create mode 100644 Source/Core/Core/PowerPC/JitKvm/JitKvm.h create mode 100644 Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.cpp create mode 100644 Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 02d6916d86..1893ca53e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,6 +209,10 @@ elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") add_definitions(-D_M_ARM_64=1) # CRC instruction set is used in the CRC32 hash function check_and_add_flag(HAVE_ARCH_ARMV8 -march=armv8-a+crc) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc|ppc64|ppc64le") + # TODO: also check that we're on a platform with KVM + set(_M_PPC_KVM 1) + add_definitions(-D_M_PPC_KVM=1) else() message(FATAL_ERROR "You're building on an unsupported platform: " "'${CMAKE_SYSTEM_PROCESSOR}' with ${CMAKE_SIZEOF_VOID_P}-byte pointers." diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 2b7c141e68..dec22ab4d0 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -539,6 +539,13 @@ elseif(_M_ARM_64) PowerPC/JitArm64/JitArm64_Tables.cpp PowerPC/JitArmCommon/BackPatch.h ) +elseif(_M_PPC_KVM) + target_sources(core PRIVATE + PowerPC/JitKvm/JitKvm.cpp + PowerPC/JitKvm/JitKvm.h + PowerPC/JitKvm/KvmBlockCache.cpp + PowerPC/JitKvm/KvmBlockCache.h + ) endif() target_link_libraries(core diff --git a/Source/Core/Core/MachineContext.h b/Source/Core/Core/MachineContext.h index af077cf813..9f7b050424 100644 --- a/Source/Core/Core/MachineContext.h +++ b/Source/Core/Core/MachineContext.h @@ -7,7 +7,7 @@ #include "Common/CommonTypes.h" // meh. -#if defined(_M_GENERIC) +#if defined(_M_GENERIC) || defined(_M_PPC_KVM) // JitBase uses SContext; it should have no concrete implementations in a // generic build. struct FakeGenericContext; diff --git a/Source/Core/Core/MemTools.cpp b/Source/Core/Core/MemTools.cpp index 41328efc3b..27644f9de8 100644 --- a/Source/Core/Core/MemTools.cpp +++ b/Source/Core/Core/MemTools.cpp @@ -230,7 +230,7 @@ void UninstallExceptionHandler() { } -#elif defined(_POSIX_VERSION) && !defined(_M_GENERIC) +#elif defined(_POSIX_VERSION) && !defined(_M_GENERIC) && !defined(_M_PPC_KVM) static struct sigaction old_sa_segv; static struct sigaction old_sa_bus; diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index 10ebd1ac9f..bc91892189 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -38,6 +38,10 @@ #include "Core/PowerPC/JitArm64/Jit.h" #endif +#if _M_PPC_KVM +#include "Core/PowerPC/JitKvm/JitKvm.h" +#endif + namespace JitInterface { static JitBase* g_jit = nullptr; @@ -63,6 +67,11 @@ CPUCoreBase* InitJitCore(PowerPC::CPUCore core) case PowerPC::CPUCore::JITARM64: g_jit = new JitArm64(); break; +#endif +#if _M_PPC_KVM + case PowerPC::CPUCore::JITKVM: + g_jit = new JitKvm(); + break; #endif case PowerPC::CPUCore::CachedInterpreter: g_jit = new CachedInterpreter(); diff --git a/Source/Core/Core/PowerPC/JitKvm/JitKvm.cpp b/Source/Core/Core/PowerPC/JitKvm/JitKvm.cpp new file mode 100644 index 0000000000..25c78a342b --- /dev/null +++ b/Source/Core/Core/PowerPC/JitKvm/JitKvm.cpp @@ -0,0 +1,298 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/PowerPC/JitKvm/JitKvm.h" + +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" +#include "Core/ConfigManager.h" +#include "Core/CoreTiming.h" +#include "Core/HLE/HLE.h" +#include "Core/HW/CPU.h" +#include "Core/PowerPC/Gekko.h" +#include "Core/PowerPC/Jit64Common/Jit64Constants.h" +#include "Core/PowerPC/PPCAnalyst.h" +#include "Core/PowerPC/PowerPC.h" + +struct JitKvm::Instruction +{ + using CommonCallback = void (*)(UGeckoInstruction); + using ConditionalCallback = bool (*)(u32); + + Instruction() {} + Instruction(const CommonCallback c, UGeckoInstruction i) + : common_callback(c), data(i.hex), type(Type::Common) + { + } + + Instruction(const ConditionalCallback c, u32 d) + : conditional_callback(c), data(d), type(Type::Conditional) + { + } + + enum class Type + { + Abort, + Common, + Conditional, + }; + + union + { + const CommonCallback common_callback; + const ConditionalCallback conditional_callback; + }; + + u32 data = 0; + Type type = Type::Abort; +}; + +JitKvm::JitKvm() = default; + +JitKvm::~JitKvm() = default; + +void JitKvm::Init() +{ + m_code.reserve(CODE_SIZE / sizeof(Instruction)); + + jo.enableBlocklink = false; + + m_block_cache.Init(); + UpdateMemoryOptions(); + + code_block.m_stats = &js.st; + code_block.m_gpa = &js.gpa; + code_block.m_fpa = &js.fpa; +} + +void JitKvm::Shutdown() +{ + m_block_cache.Shutdown(); +} + +u8* JitKvm::GetCodePtr() +{ + return reinterpret_cast(m_code.data() + m_code.size()); +} + +void JitKvm::ExecuteOneBlock() +{ + const u8* normal_entry = m_block_cache.Dispatch(); + if (!normal_entry) + { + Jit(PC); + return; + } + + const Instruction* code = reinterpret_cast(normal_entry); + + for (; code->type != Instruction::Type::Abort; ++code) + { + switch (code->type) + { + case Instruction::Type::Common: + code->common_callback(UGeckoInstruction(code->data)); + break; + + case Instruction::Type::Conditional: + if (code->conditional_callback(code->data)) + return; + break; + + default: + ERROR_LOG(POWERPC, "Unknown JitKvm Instruction: %d", static_cast(code->type)); + break; + } + } +} + +void JitKvm::Run() +{ + const CPU::State* state_ptr = CPU::GetStatePtr(); + while (CPU::GetState() == CPU::State::Running) + { + // Start new timing slice + // NOTE: Exceptions may change PC + CoreTiming::Advance(); + + do + { + ExecuteOneBlock(); + } while (PowerPC::ppcState.downcount > 0 && *state_ptr == CPU::State::Running); + } +} + +void JitKvm::SingleStep() +{ + // Enter new timing slice + CoreTiming::Advance(); + ExecuteOneBlock(); +} + +static void EndBlock(UGeckoInstruction data) +{ + PC = NPC; + PowerPC::ppcState.downcount -= data.hex; +} + +static void WritePC(UGeckoInstruction data) +{ + PC = data.hex; + NPC = data.hex + 4; +} + +static void WriteBrokenBlockNPC(UGeckoInstruction data) +{ + NPC = data.hex; +} + +static bool CheckFPU(u32 data) +{ + if (!MSR.FP) + { + PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; + PowerPC::CheckExceptions(); + PowerPC::ppcState.downcount -= data; + return true; + } + return false; +} + +static bool CheckDSI(u32 data) +{ + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + { + PowerPC::CheckExceptions(); + PowerPC::ppcState.downcount -= data; + return true; + } + return false; +} + +static bool CheckBreakpoint(u32 data) +{ + PowerPC::CheckBreakPoints(); + if (CPU::GetState() != CPU::State::Running) + { + PowerPC::ppcState.downcount -= data; + return true; + } + return false; +} + +static bool CheckIdle(u32 idle_pc) +{ + if (PowerPC::ppcState.npc == idle_pc) + { + CoreTiming::Idle(); + } + return false; +} + +bool JitKvm::HandleFunctionHooking(u32 address) +{ + return HLE::ReplaceFunctionIfPossible(address, [&](u32 function, HLE::HookType type) { + m_code.emplace_back(WritePC, address); + m_code.emplace_back(Interpreter::HLEFunction, function); + + if (type != HLE::HookType::Replace) + return false; + + m_code.emplace_back(EndBlock, js.downcountAmount); + m_code.emplace_back(); + return true; + }); +} + +void JitKvm::Jit(u32 address) +{ + if (m_code.size() >= CODE_SIZE / sizeof(Instruction) - 0x1000 || + SConfig::GetInstance().bJITNoBlockCache) + { + ClearCache(); + } + + const u32 nextPC = analyzer.Analyze(PC, &code_block, &m_code_buffer, m_code_buffer.size()); + 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; + } + + JitBlock* b = m_block_cache.AllocateBlock(PC); + + js.blockStart = PC; + js.firstFPInstructionFound = false; + js.fifoBytesSinceCheck = 0; + js.downcountAmount = 0; + js.curBlock = b; + + b->checkedEntry = GetCodePtr(); + b->normalEntry = GetCodePtr(); + + for (u32 i = 0; i < code_block.m_num_instructions; i++) + { + PPCAnalyst::CodeOp& op = m_code_buffer[i]; + + js.downcountAmount += op.opinfo->numCycles; + + if (HandleFunctionHooking(op.address)) + break; + + if (!op.skip) + { + const bool breakpoint = SConfig::GetInstance().bEnableDebugging && + PowerPC::breakpoints.IsAddressBreakPoint(op.address); + const bool check_fpu = (op.opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound; + const bool endblock = (op.opinfo->flags & FL_ENDBLOCK) != 0; + const bool memcheck = (op.opinfo->flags & FL_LOADSTORE) && jo.memcheck; + const bool idle_loop = op.branchIsIdleLoop; + + if (breakpoint) + { + m_code.emplace_back(WritePC, op.address); + m_code.emplace_back(CheckBreakpoint, js.downcountAmount); + } + + if (check_fpu) + { + m_code.emplace_back(WritePC, op.address); + m_code.emplace_back(CheckFPU, js.downcountAmount); + js.firstFPInstructionFound = true; + } + + if (endblock || memcheck) + m_code.emplace_back(WritePC, op.address); + m_code.emplace_back(PPCTables::GetInterpreterOp(op.inst), op.inst); + if (memcheck) + m_code.emplace_back(CheckDSI, js.downcountAmount); + if (idle_loop) + m_code.emplace_back(CheckIdle, js.blockStart); + if (endblock) + m_code.emplace_back(EndBlock, js.downcountAmount); + } + } + if (code_block.m_broken) + { + m_code.emplace_back(WriteBrokenBlockNPC, 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; + + m_block_cache.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses); +} + +void JitKvm::ClearCache() +{ + m_code.clear(); + m_block_cache.Clear(); + UpdateMemoryOptions(); +} diff --git a/Source/Core/Core/PowerPC/JitKvm/JitKvm.h b/Source/Core/Core/PowerPC/JitKvm/JitKvm.h new file mode 100644 index 0000000000..fbb9c29def --- /dev/null +++ b/Source/Core/Core/PowerPC/JitKvm/JitKvm.h @@ -0,0 +1,45 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" +#include "Core/PowerPC/JitKvm/KvmBlockCache.h" +#include "Core/PowerPC/JitCommon/JitBase.h" +#include "Core/PowerPC/PPCAnalyst.h" + +class JitKvm : public JitBase +{ +public: + JitKvm(); + ~JitKvm(); + + 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 &m_block_cache; } + const char* GetName() const override { return "KVM"; } + const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; } + +private: + struct Instruction; + + u8* GetCodePtr(); + void ExecuteOneBlock(); + + bool HandleFunctionHooking(u32 address); + + KvmBlockCache m_block_cache{*this}; + std::vector m_code; +}; diff --git a/Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.cpp b/Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.cpp new file mode 100644 index 0000000000..db45897445 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.cpp @@ -0,0 +1,15 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Core/PowerPC/JitKvm/KvmBlockCache.h" + +#include "Core/PowerPC/JitCommon/JitBase.h" + +KvmBlockCache::KvmBlockCache(JitBase& jit) : JitBaseBlockCache{jit} +{ +} + +void KvmBlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) +{ +} diff --git a/Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.h b/Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.h new file mode 100644 index 0000000000..49d5f2dacc --- /dev/null +++ b/Source/Core/Core/PowerPC/JitKvm/KvmBlockCache.h @@ -0,0 +1,18 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Core/PowerPC/JitCommon/JitCache.h" + +class JitBase; + +class KvmBlockCache final : public JitBaseBlockCache +{ +public: + explicit KvmBlockCache(JitBase& jit); + +private: + void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override; +}; diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp index ebac1a07d9..e07eacac5a 100644 --- a/Source/Core/Core/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/PowerPC/PowerPC.cpp @@ -225,6 +225,8 @@ const std::vector& AvailableCPUCores() CPUCore::JIT64, #elif defined(_M_ARM_64) CPUCore::JITARM64, +#elif defined(_M_PPC_KVM) + CPUCore::JITKVM, #endif CPUCore::CachedInterpreter, CPUCore::Interpreter, @@ -239,6 +241,8 @@ CPUCore DefaultCPUCore() return CPUCore::JIT64; #elif defined(_M_ARM_64) return CPUCore::JITARM64; +#elif defined(_M_PPC_KVM) + return CPUCore::JITKVM; #else return CPUCore::CachedInterpreter; #endif diff --git a/Source/Core/Core/PowerPC/PowerPC.h b/Source/Core/Core/PowerPC/PowerPC.h index fe1e11ef03..a41d6141ae 100644 --- a/Source/Core/Core/PowerPC/PowerPC.h +++ b/Source/Core/Core/PowerPC/PowerPC.h @@ -31,6 +31,7 @@ enum class CPUCore JIT64 = 1, JITARM64 = 4, CachedInterpreter = 5, + JITKVM = 6, }; // For reading from and writing to our config. diff --git a/Source/Core/DolphinQt/Settings/AdvancedPane.cpp b/Source/Core/DolphinQt/Settings/AdvancedPane.cpp index cf7493a619..4abd572cbe 100644 --- a/Source/Core/DolphinQt/Settings/AdvancedPane.cpp +++ b/Source/Core/DolphinQt/Settings/AdvancedPane.cpp @@ -30,6 +30,7 @@ static const std::map CPU_CORE_NAMES = { {PowerPC::CPUCore::CachedInterpreter, QT_TR_NOOP("Cached Interpreter (slower)")}, {PowerPC::CPUCore::JIT64, QT_TR_NOOP("JIT Recompiler (recommended)")}, {PowerPC::CPUCore::JITARM64, QT_TR_NOOP("JIT Arm64 (experimental)")}, + {PowerPC::CPUCore::JITKVM, QT_TR_NOOP("PowerPC KVM (experimental)")}, }; AdvancedPane::AdvancedPane(QWidget* parent) : QWidget(parent)