Add "JitKvm" (copy of Cached Interpreter for now)

This commit is contained in:
lif 2020-02-15 21:07:42 -08:00
parent 62046d93ce
commit 17fc254342
12 changed files with 404 additions and 2 deletions

View File

@ -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."

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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<u8*>(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<const Instruction*>(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<int>(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();
}

View File

@ -0,0 +1,45 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <vector>
#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<Instruction> m_code;
};

View File

@ -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)
{
}

View File

@ -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;
};

View File

@ -225,6 +225,8 @@ const std::vector<CPUCore>& 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

View File

@ -31,6 +31,7 @@ enum class CPUCore
JIT64 = 1,
JITARM64 = 4,
CachedInterpreter = 5,
JITKVM = 6,
};
// For reading from and writing to our config.

View File

@ -30,6 +30,7 @@ static const std::map<PowerPC::CPUCore, const char*> 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)