Add "JitKvm" (copy of Cached Interpreter for now)
This commit is contained in:
parent
62046d93ce
commit
17fc254342
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -31,6 +31,7 @@ enum class CPUCore
|
|||
JIT64 = 1,
|
||||
JITARM64 = 4,
|
||||
CachedInterpreter = 5,
|
||||
JITKVM = 6,
|
||||
};
|
||||
|
||||
// For reading from and writing to our config.
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue