140 changes: 80 additions & 60 deletions Source/Core/Core/PowerPC/JitInterface.cpp
Expand Up @@ -40,62 +40,67 @@
#include "Core/PowerPC/JitArm64/Jit.h"
#endif

namespace JitInterface
JitInterface::JitInterface(Core::System& system) : m_system(system)
{
static JitBase* g_jit = nullptr;
void SetJit(JitBase* jit)
}

JitInterface::~JitInterface() = default;

void JitInterface::SetJit(std::unique_ptr<JitBase> jit)
{
g_jit = jit;
m_jit = std::move(jit);
}
void DoState(PointerWrap& p)

void JitInterface::DoState(PointerWrap& p)
{
if (g_jit && p.IsReadMode())
g_jit->ClearCache();
if (m_jit && p.IsReadMode())
m_jit->ClearCache();
}
CPUCoreBase* InitJitCore(PowerPC::CPUCore core)

CPUCoreBase* JitInterface::InitJitCore(PowerPC::CPUCore core)
{
auto& system = Core::System::GetInstance();

switch (core)
{
#if _M_X86
case PowerPC::CPUCore::JIT64:
g_jit = new Jit64(system);
m_jit = std::make_unique<Jit64>(system);
break;
#endif
#if _M_ARM_64
case PowerPC::CPUCore::JITARM64:
g_jit = new JitArm64(system);
m_jit = std::make_unique<JitArm64>(system);
break;
#endif
case PowerPC::CPUCore::CachedInterpreter:
g_jit = new CachedInterpreter(system);
m_jit = std::make_unique<CachedInterpreter>(system);
break;

default:
// Under this case the caller overrides the CPU core to the default and logs that
// it performed the override.
g_jit = nullptr;
m_jit.reset();
return nullptr;
}
g_jit->Init();
return g_jit;
m_jit->Init();
return m_jit.get();
}

CPUCoreBase* GetCore()
CPUCoreBase* JitInterface::GetCore() const
{
return g_jit;
return m_jit.get();
}

void SetProfilingState(ProfilingState state)
void JitInterface::SetProfilingState(ProfilingState state)
{
if (!g_jit)
if (!m_jit)
return;

g_jit->jo.profile_blocks = state == ProfilingState::Enabled;
m_jit->jo.profile_blocks = state == ProfilingState::Enabled;
}

void WriteProfileResults(const std::string& filename)
void JitInterface::WriteProfileResults(const std::string& filename) const
{
Profiler::ProfileStats prof_stats;
GetProfileResults(&prof_stats);
Expand All @@ -122,19 +127,19 @@ void WriteProfileResults(const std::string& filename)
}
}

void GetProfileResults(Profiler::ProfileStats* prof_stats)
void JitInterface::GetProfileResults(Profiler::ProfileStats* prof_stats) const
{
// Can't really do this with no g_jit core available
if (!g_jit)
// Can't really do this with no m_jit core available
if (!m_jit)
return;

prof_stats->cost_sum = 0;
prof_stats->timecost_sum = 0;
prof_stats->block_stats.clear();

Core::RunAsCPUThread([&prof_stats] {
Core::RunAsCPUThread([this, &prof_stats] {
QueryPerformanceFrequency((LARGE_INTEGER*)&prof_stats->countsPerSec);
g_jit->GetBlockCache()->RunOnBlocks([&prof_stats](const JitBlock& block) {
m_jit->GetBlockCache()->RunOnBlocks([&prof_stats](const JitBlock& block) {
const auto& data = block.profile_data;
u64 cost = data.downcountCounter;
u64 timecost = data.ticCounter;
Expand All @@ -150,20 +155,21 @@ void GetProfileResults(Profiler::ProfileStats* prof_stats)
});
}

std::variant<GetHostCodeError, GetHostCodeResult> GetHostCode(u32 address)
std::variant<JitInterface::GetHostCodeError, JitInterface::GetHostCodeResult>
JitInterface::GetHostCode(u32 address) const
{
if (!g_jit)
if (!m_jit)
{
return GetHostCodeError::NoJitActive;
}

JitBlock* block =
g_jit->GetBlockCache()->GetBlockFromStartAddress(address, PowerPC::ppcState.msr.Hex);
m_jit->GetBlockCache()->GetBlockFromStartAddress(address, PowerPC::ppcState.msr.Hex);
if (!block)
{
for (int i = 0; i < 500; i++)
{
block = g_jit->GetBlockCache()->GetBlockFromStartAddress(address - 4 * i,
block = m_jit->GetBlockCache()->GetBlockFromStartAddress(address - 4 * i,
PowerPC::ppcState.msr.Hex);
if (block)
break;
Expand All @@ -190,51 +196,52 @@ std::variant<GetHostCodeError, GetHostCodeResult> GetHostCode(u32 address)
return result;
}

bool HandleFault(uintptr_t access_address, SContext* ctx)
bool JitInterface::HandleFault(uintptr_t access_address, SContext* ctx)
{
// Prevent nullptr dereference on a crash with no JIT present
if (!g_jit)
if (!m_jit)
{
return false;
}

return g_jit->HandleFault(access_address, ctx);
return m_jit->HandleFault(access_address, ctx);
}

bool HandleStackFault()
bool JitInterface::HandleStackFault()
{
if (!g_jit)
if (!m_jit)
{
return false;
}

return g_jit->HandleStackFault();
return m_jit->HandleStackFault();
}

void ClearCache()
void JitInterface::ClearCache()
{
if (g_jit)
g_jit->ClearCache();
if (m_jit)
m_jit->ClearCache();
}
void ClearSafe()

void JitInterface::ClearSafe()
{
if (g_jit)
g_jit->GetBlockCache()->Clear();
if (m_jit)
m_jit->GetBlockCache()->Clear();
}

void InvalidateICache(u32 address, u32 size, bool forced)
void JitInterface::InvalidateICache(u32 address, u32 size, bool forced)
{
if (g_jit)
g_jit->GetBlockCache()->InvalidateICache(address, size, forced);
if (m_jit)
m_jit->GetBlockCache()->InvalidateICache(address, size, forced);
}

void InvalidateICacheLine(u32 address)
void JitInterface::InvalidateICacheLine(u32 address)
{
if (g_jit)
g_jit->GetBlockCache()->InvalidateICacheLine(address);
if (m_jit)
m_jit->GetBlockCache()->InvalidateICacheLine(address);
}

void InvalidateICacheLines(u32 address, u32 count)
void JitInterface::InvalidateICacheLines(u32 address, u32 count)
{
// This corresponds to a PPC code loop that:
// - calls some form of dcb* instruction on 'address'
Expand All @@ -250,23 +257,33 @@ void InvalidateICacheLines(u32 address, u32 count)
InvalidateICache(address & ~0x1f, 32 * count, false);
}

void CompileExceptionCheck(ExceptionType type)
void JitInterface::InvalidateICacheLineFromJIT(u32 address, u32 dummy, JitInterface& jit_interface)
{
if (!g_jit)
jit_interface.InvalidateICacheLine(address);
}

void JitInterface::InvalidateICacheLinesFromJIT(u32 address, u32 count, JitInterface& jit_interface)
{
jit_interface.InvalidateICacheLines(address, count);
}

void JitInterface::CompileExceptionCheck(ExceptionType type)
{
if (!m_jit)
return;

std::unordered_set<u32>* exception_addresses = nullptr;

switch (type)
{
case ExceptionType::FIFOWrite:
exception_addresses = &g_jit->js.fifoWriteAddresses;
exception_addresses = &m_jit->js.fifoWriteAddresses;
break;
case ExceptionType::PairedQuantize:
exception_addresses = &g_jit->js.pairedQuantizeAddresses;
exception_addresses = &m_jit->js.pairedQuantizeAddresses;
break;
case ExceptionType::SpeculativeConstants:
exception_addresses = &g_jit->js.noSpeculativeConstantsAddresses;
exception_addresses = &m_jit->js.noSpeculativeConstantsAddresses;
break;
}

Expand All @@ -288,17 +305,20 @@ void CompileExceptionCheck(ExceptionType type)

// Invalidate the JIT block so that it gets recompiled with the external exception check
// included.
g_jit->GetBlockCache()->InvalidateICache(PowerPC::ppcState.pc, 4, true);
m_jit->GetBlockCache()->InvalidateICache(PowerPC::ppcState.pc, 4, true);
}
}

void Shutdown()
void JitInterface::CompileExceptionCheckFromJIT(JitInterface& jit_interface, ExceptionType type)
{
jit_interface.CompileExceptionCheck(type);
}

void JitInterface::Shutdown()
{
if (g_jit)
if (m_jit)
{
g_jit->Shutdown();
delete g_jit;
g_jit = nullptr;
m_jit->Shutdown();
m_jit.reset();
}
}
} // namespace JitInterface
138 changes: 78 additions & 60 deletions Source/Core/Core/PowerPC/JitInterface.h
Expand Up @@ -3,6 +3,7 @@

#pragma once

#include <memory>
#include <string>
#include <variant>

Expand All @@ -13,6 +14,10 @@ class CPUCoreBase;
class PointerWrap;
class JitBase;

namespace Core
{
class System;
}
namespace PowerPC
{
enum class CPUCore;
Expand All @@ -23,65 +28,78 @@ namespace Profiler
struct ProfileStats;
}

namespace JitInterface
{
enum class ExceptionType
{
FIFOWrite,
PairedQuantize,
SpeculativeConstants
};

void DoState(PointerWrap& p);

CPUCoreBase* InitJitCore(PowerPC::CPUCore core);
CPUCoreBase* GetCore();

// Debugging
enum class ProfilingState
{
Enabled,
Disabled
};

enum class GetHostCodeError
class JitInterface
{
NoJitActive,
NoTranslation,
public:
explicit JitInterface(Core::System& system);
JitInterface(const JitInterface&) = delete;
JitInterface(JitInterface&&) = delete;
JitInterface& operator=(const JitInterface&) = delete;
JitInterface& operator=(JitInterface&&) = delete;
~JitInterface();

void DoState(PointerWrap& p);

CPUCoreBase* InitJitCore(PowerPC::CPUCore core);
CPUCoreBase* GetCore() const;

// Debugging
enum class ProfilingState
{
Enabled,
Disabled
};
enum class GetHostCodeError
{
NoJitActive,
NoTranslation,
};
struct GetHostCodeResult
{
const u8* code;
u32 code_size;
u32 entry_address;
};

void SetProfilingState(ProfilingState state);
void WriteProfileResults(const std::string& filename) const;
void GetProfileResults(Profiler::ProfileStats* prof_stats) const;
std::variant<GetHostCodeError, GetHostCodeResult> GetHostCode(u32 address) const;

// Memory Utilities
bool HandleFault(uintptr_t access_address, SContext* ctx);
bool HandleStackFault();

// Clearing CodeCache
void ClearCache();

// This clear is "safe" in the sense that it's okay to run from
// inside a JIT'ed block: it clears the instruction cache, but not
// the JIT'ed code.
void ClearSafe();

// If "forced" is true, a recompile is being requested on code that hasn't been modified.
void InvalidateICache(u32 address, u32 size, bool forced);
void InvalidateICacheLine(u32 address);
void InvalidateICacheLines(u32 address, u32 count);
static void InvalidateICacheLineFromJIT(u32 address, u32 dummy, JitInterface& jit_interface);
static void InvalidateICacheLinesFromJIT(u32 address, u32 count, JitInterface& jit_interface);

enum class ExceptionType
{
FIFOWrite,
PairedQuantize,
SpeculativeConstants
};
void CompileExceptionCheck(ExceptionType type);
static void CompileExceptionCheckFromJIT(JitInterface& jit_interface, ExceptionType type);

/// used for the page fault unit test, don't use outside of tests!
void SetJit(std::unique_ptr<JitBase> jit);

void Shutdown();

private:
std::unique_ptr<JitBase> m_jit;
Core::System& m_system;
};
struct GetHostCodeResult
{
const u8* code;
u32 code_size;
u32 entry_address;
};

void SetProfilingState(ProfilingState state);
void WriteProfileResults(const std::string& filename);
void GetProfileResults(Profiler::ProfileStats* prof_stats);
std::variant<GetHostCodeError, GetHostCodeResult> GetHostCode(u32 address);

// Memory Utilities
bool HandleFault(uintptr_t access_address, SContext* ctx);
bool HandleStackFault();

// Clearing CodeCache
void ClearCache();

// This clear is "safe" in the sense that it's okay to run from
// inside a JIT'ed block: it clears the instruction cache, but not
// the JIT'ed code.
void ClearSafe();

// If "forced" is true, a recompile is being requested on code that hasn't been modified.
void InvalidateICache(u32 address, u32 size, bool forced);
void InvalidateICacheLine(u32 address);
void InvalidateICacheLines(u32 address, u32 count);

void CompileExceptionCheck(ExceptionType type);

/// used for the page fault unit test, don't use outside of tests!
void SetJit(JitBase* jit);

void Shutdown();
} // namespace JitInterface
4 changes: 2 additions & 2 deletions Source/Core/Core/PowerPC/MMU.cpp
Expand Up @@ -1770,7 +1770,7 @@ void DBATUpdated()
#endif

// IsOptimizable*Address and dcbz depends on the BAT mapping, so we need a flush here.
JitInterface::ClearSafe();
system.GetJitInterface().ClearSafe();
}

void IBATUpdated()
Expand All @@ -1789,7 +1789,7 @@ void IBATUpdated()
UpdateFakeMMUBat(ibat_table, 0x40000000);
UpdateFakeMMUBat(ibat_table, 0x70000000);
}
JitInterface::ClearSafe();
system.GetJitInterface().ClearSafe();
}

// Translate effective address using BAT or PAT. Returns 0 if the address cannot be translated.
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/Core/PowerPC/PPCCache.cpp
Expand Up @@ -107,7 +107,7 @@ void Cache::Reset()
void InstructionCache::Reset()
{
Cache::Reset();
JitInterface::ClearSafe();
Core::System::GetInstance().GetJitInterface().ClearSafe();
}

void Cache::Init()
Expand Down Expand Up @@ -424,7 +424,7 @@ void InstructionCache::Invalidate(u32 addr)
valid[set] = 0;
modified[set] = 0;

JitInterface::InvalidateICacheLine(addr);
Core::System::GetInstance().GetJitInterface().InvalidateICacheLine(addr);
}

void InstructionCache::RefreshConfig()
Expand Down
19 changes: 11 additions & 8 deletions Source/Core/Core/PowerPC/PowerPC.cpp
Expand Up @@ -153,7 +153,7 @@ void DoState(PointerWrap& p)
// SystemTimers::DecrementerSet();
// SystemTimers::TimeBaseSet();

JitInterface::DoState(p);
Core::System::GetInstance().GetJitInterface().DoState(p);
}

static void ResetRegisters()
Expand Down Expand Up @@ -219,7 +219,8 @@ static void InitializeCPUCore(CPUCore cpu_core)
{
// We initialize the interpreter because
// it is used on boot and code window independently.
auto& interpreter = Core::System::GetInstance().GetInterpreter();
auto& system = Core::System::GetInstance();
auto& interpreter = system.GetInterpreter();
interpreter.Init();

switch (cpu_core)
Expand All @@ -229,12 +230,12 @@ static void InitializeCPUCore(CPUCore cpu_core)
break;

default:
s_cpu_core_base = JitInterface::InitJitCore(cpu_core);
s_cpu_core_base = system.GetJitInterface().InitJitCore(cpu_core);
if (!s_cpu_core_base) // Handle Situations where JIT core isn't available
{
WARN_LOG_FMT(POWERPC, "CPU core {} not available. Falling back to default.",
static_cast<int>(cpu_core));
s_cpu_core_base = JitInterface::InitJitCore(DefaultCPUCore());
s_cpu_core_base = system.GetJitInterface().InitJitCore(DefaultCPUCore());
}
break;
}
Expand Down Expand Up @@ -315,8 +316,9 @@ void ScheduleInvalidateCacheThreadSafe(u32 address)
void Shutdown()
{
InjectExternalCPUCore(nullptr);
JitInterface::Shutdown();
auto& interpreter = Core::System::GetInstance().GetInterpreter();
auto& system = Core::System::GetInstance();
system.GetJitInterface().Shutdown();
auto& interpreter = system.GetInterpreter();
interpreter.Shutdown();
s_cpu_core_base = nullptr;
}
Expand All @@ -328,7 +330,8 @@ CoreMode GetMode()

static void ApplyMode()
{
auto& interpreter = Core::System::GetInstance().GetInterpreter();
auto& system = Core::System::GetInstance();
auto& interpreter = system.GetInterpreter();

switch (s_mode)
{
Expand All @@ -338,7 +341,7 @@ static void ApplyMode()

case CoreMode::JIT: // Switching from interpreter to JIT.
// Don't really need to do much. It'll work, the cache will refill itself.
s_cpu_core_base = JitInterface::GetCore();
s_cpu_core_base = system.GetJitInterface().GetCore();
if (!s_cpu_core_base) // Has a chance to not get a working JIT core if one isn't active on host
s_cpu_core_base = &interpreter;
break;
Expand Down
9 changes: 8 additions & 1 deletion Source/Core/Core/System.cpp
Expand Up @@ -23,6 +23,7 @@
#include "Core/HW/Sram.h"
#include "Core/HW/VideoInterface.h"
#include "Core/PowerPC/Interpreter/Interpreter.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/PowerPC.h"
#include "IOS/USB/Emulated/Skylander.h"
#include "VideoCommon/CommandProcessor.h"
Expand All @@ -40,7 +41,7 @@ struct System::Impl
: m_audio_interface(system), m_core_timing(system), m_dsp(system), m_dvd_interface(system),
m_dvd_thread(system), m_expansion_interface(system), m_gp_fifo(system), m_memory(system),
m_ppc_state(PowerPC::ppcState), m_processor_interface(system), m_serial_interface(system),
m_video_interface(system), m_interpreter(system, m_ppc_state)
m_video_interface(system), m_interpreter(system, m_ppc_state), m_jit_interface(system)
{
}

Expand Down Expand Up @@ -72,6 +73,7 @@ struct System::Impl
VertexShaderManager m_vertex_shader_manager;
VideoInterface::VideoInterfaceManager m_video_interface;
Interpreter m_interpreter;
JitInterface m_jit_interface;
};

System::System() : m_impl{std::make_unique<Impl>(*this)}
Expand Down Expand Up @@ -182,6 +184,11 @@ Interpreter& System::GetInterpreter() const
return m_impl->m_interpreter;
}

JitInterface& System::GetJitInterface() const
{
return m_impl->m_jit_interface;
}

IOS::HLE::USB::SkylanderPortal& System::GetSkylanderPortal() const
{
return m_impl->m_skylander_portal;
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/System.h
Expand Up @@ -7,6 +7,7 @@

class GeometryShaderManager;
class Interpreter;
class JitInterface;
class PixelShaderManager;
class SoundStream;
struct Sram;
Expand Down Expand Up @@ -133,6 +134,7 @@ class System
GPFifo::GPFifoManager& GetGPFifo() const;
HSP::HSPManager& GetHSP() const;
Interpreter& GetInterpreter() const;
JitInterface& GetJitInterface() const;
IOS::HLE::USB::SkylanderPortal& GetSkylanderPortal() const;
Memory::MemoryManager& GetMemory() const;
MemoryInterface::MemoryInterfaceManager& GetMemoryInterface() const;
Expand Down
11 changes: 6 additions & 5 deletions Source/Core/DolphinQt/Debugger/CodeDiffDialog.cpp
Expand Up @@ -140,7 +140,8 @@ void CodeDiffDialog::ClearData()
// Swap is used instead of clear for efficiency in the case of huge m_include/m_exclude
std::vector<Diff>().swap(m_include);
std::vector<Diff>().swap(m_exclude);
JitInterface::SetProfilingState(JitInterface::ProfilingState::Disabled);
Core::System::GetInstance().GetJitInterface().SetProfilingState(
JitInterface::ProfilingState::Disabled);
}

void CodeDiffDialog::ClearBlockCache()
Expand All @@ -150,7 +151,7 @@ void CodeDiffDialog::ClearBlockCache()
if (old_state == Core::State::Running)
Core::SetState(Core::State::Paused);

JitInterface::ClearCache();
Core::System::GetInstance().GetJitInterface().ClearCache();

if (old_state == Core::State::Running)
Core::SetState(Core::State::Running);
Expand Down Expand Up @@ -205,7 +206,7 @@ void CodeDiffDialog::OnRecord(bool enabled)
}

m_record_btn->update();
JitInterface::SetProfilingState(state);
Core::System::GetInstance().GetJitInterface().SetProfilingState(state);
}

void CodeDiffDialog::OnInclude()
Expand Down Expand Up @@ -271,7 +272,7 @@ std::vector<Diff> CodeDiffDialog::CalculateSymbolsFromProfile()
{
Profiler::ProfileStats prof_stats;
auto& blockstats = prof_stats.block_stats;
JitInterface::GetProfileResults(&prof_stats);
Core::System::GetInstance().GetJitInterface().GetProfileResults(&prof_stats);
std::vector<Diff> current;
current.reserve(20000);

Expand Down Expand Up @@ -391,7 +392,7 @@ void CodeDiffDialog::Update(bool include)
m_exclude_size_label->setText(tr("Excluded: %1").arg(m_exclude.size()));
m_include_size_label->setText(tr("Included: %1").arg(m_include.size()));

JitInterface::ClearCache();
Core::System::GetInstance().GetJitInterface().ClearCache();
if (old_state == Core::State::Running)
Core::SetState(Core::State::Running);
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/DolphinQt/MenuBar.cpp
Expand Up @@ -1671,7 +1671,7 @@ void MenuBar::PatchHLEFunctions()

void MenuBar::ClearCache()
{
Core::RunAsCPUThread(JitInterface::ClearCache);
Core::RunAsCPUThread([] { Core::System::GetInstance().GetJitInterface().ClearCache(); });
}

void MenuBar::LogInstructions()
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/UICommon/Disassembler.cpp
Expand Up @@ -16,6 +16,7 @@
#include "Common/Assert.h"
#include "Common/VariantUtil.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/System.h"

#if defined(HAVE_LLVM)
class HostDisassemblerLLVM : public HostDisassembler
Expand Down Expand Up @@ -170,7 +171,7 @@ std::unique_ptr<HostDisassembler> GetNewDisassembler(const std::string& arch)

DisassembleResult DisassembleBlock(HostDisassembler* disasm, u32 address)
{
auto res = JitInterface::GetHostCode(address);
auto res = Core::System::GetInstance().GetJitInterface().GetHostCode(address);

return std::visit(overloaded{[&](JitInterface::GetHostCodeError error) {
DisassembleResult result;
Expand Down
9 changes: 6 additions & 3 deletions Source/UnitTests/Core/PageFaultTest.cpp
Expand Up @@ -75,8 +75,10 @@ TEST(PageFault, PageFault)
EXPECT_NE(data, nullptr);
Common::WriteProtectMemory(data, PAGE_GRAN, false);

PageFaultFakeJit pfjit(Core::System::GetInstance());
JitInterface::SetJit(&pfjit);
auto& system = Core::System::GetInstance();
auto unique_pfjit = std::make_unique<PageFaultFakeJit>(system);
auto& pfjit = *unique_pfjit;
system.GetJitInterface().SetJit(std::move(unique_pfjit));
pfjit.m_data = data;

auto start = std::chrono::high_resolution_clock::now();
Expand All @@ -88,7 +90,6 @@ TEST(PageFault, PageFault)
};

EMM::UninstallExceptionHandler();
JitInterface::SetJit(nullptr);

fmt::print("page fault timing:\n");
fmt::print("start->HandleFault {} ns\n",
Expand All @@ -98,4 +99,6 @@ TEST(PageFault, PageFault)
fmt::print("HandleFault->end {} ns\n",
difference_in_nanoseconds(pfjit.m_post_unprotect_time, end));
fmt::print("total {} ns\n", difference_in_nanoseconds(start, end));

system.GetJitInterface().SetJit(nullptr);
}