mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-05-28 17:07:07 +02:00
[dynarmic] fix multicore macOS issue
Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
parent
8678cb06eb
commit
535a80e8e3
10 changed files with 110 additions and 263 deletions
|
|
@ -26,15 +26,42 @@ CpuManager::~CpuManager() = default;
|
||||||
void CpuManager::Initialize() {
|
void CpuManager::Initialize() {
|
||||||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||||
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
||||||
for (std::size_t core = 0; core < num_cores; core++)
|
for (std::size_t core = 0; core < num_cores; core++) {
|
||||||
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) { RunThread(token, core); });
|
core_data[core].host_thread = std::jthread([this, core](std::stop_token token) {
|
||||||
|
// Initialization
|
||||||
|
system.RegisterCoreThread(core);
|
||||||
|
Common::SetCurrentThreadName(("CPUCore_" + std::to_string(core)).c_str());
|
||||||
|
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
// Aimed specifically for Snapdragon 8 Elite devices
|
||||||
|
// This kills performance on desktop, but boosts perf for UMA devices
|
||||||
|
// like the S8E. Mediatek and Mali likely won't suffer.
|
||||||
|
Common::PinCurrentThreadToPerformanceCore(core);
|
||||||
|
#endif
|
||||||
|
auto& data = core_data[core];
|
||||||
|
data.host_context = Common::Fiber::ThreadToFiber();
|
||||||
|
// Running
|
||||||
|
if (gpu_barrier->Sync(token)) {
|
||||||
|
if (!is_async_gpu && !is_multicore) {
|
||||||
|
system.GPU().ObtainContext();
|
||||||
|
}
|
||||||
|
auto& kernel = system.Kernel();
|
||||||
|
auto& scheduler = *kernel.CurrentScheduler();
|
||||||
|
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||||
|
Kernel::SetCurrentThread(kernel, thread);
|
||||||
|
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||||
|
}
|
||||||
|
// Cleanup
|
||||||
|
data.host_context->Exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::Shutdown() {
|
void CpuManager::Shutdown() {
|
||||||
for (std::size_t core = 0; core < num_cores; core++) {
|
for (auto it = core_data.begin(); it != core_data.end(); ++it) {
|
||||||
if (core_data[core].host_thread.joinable()) {
|
if (it->host_thread.joinable()) {
|
||||||
core_data[core].host_thread.request_stop();
|
it->host_thread.request_stop();
|
||||||
core_data[core].host_thread.join();
|
it->host_thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,41 +210,4 @@ void CpuManager::ShutdownThread() {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
|
||||||
/// Initialization
|
|
||||||
system.RegisterCoreThread(core);
|
|
||||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
|
||||||
Common::SetCurrentThreadName(name.c_str());
|
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
// Aimed specifically for Snapdragon 8 Elite devices
|
|
||||||
// This kills performance on desktop, but boosts perf for UMA devices
|
|
||||||
// like the S8E. Mediatek and Mali likely won't suffer.
|
|
||||||
Common::PinCurrentThreadToPerformanceCore(core);
|
|
||||||
#endif
|
|
||||||
auto& data = core_data[core];
|
|
||||||
data.host_context = Common::Fiber::ThreadToFiber();
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
SCOPE_EXIT {
|
|
||||||
data.host_context->Exit();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Running
|
|
||||||
if (!gpu_barrier->Sync(token)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_async_gpu && !is_multicore) {
|
|
||||||
system.GPU().ObtainContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& kernel = system.Kernel();
|
|
||||||
auto& scheduler = *kernel.CurrentScheduler();
|
|
||||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
|
||||||
Kernel::SetCurrentThread(kernel, thread);
|
|
||||||
|
|
||||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,6 @@ private:
|
||||||
void GuestActivate();
|
void GuestActivate();
|
||||||
void HandleInterrupt();
|
void HandleInterrupt();
|
||||||
void ShutdownThread();
|
void ShutdownThread();
|
||||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
|
||||||
|
|
||||||
struct CoreData {
|
struct CoreData {
|
||||||
std::shared_ptr<Common::Fiber> host_context;
|
std::shared_ptr<Common::Fiber> host_context;
|
||||||
|
|
|
||||||
|
|
@ -119,10 +119,9 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||||
|
|
||||||
// Since scheduling may occur here, we cannot use any cached
|
// Since scheduling may occur here, we cannot use any cached
|
||||||
// state after returning from calls we make.
|
// state after returning from calls we make.
|
||||||
|
|
||||||
// Notify the debugger and go to sleep if a breakpoint was hit,
|
|
||||||
// or if the thread is unable to continue for any reason.
|
|
||||||
if (breakpoint || (prefetch_abort && may_abort)) {
|
if (breakpoint || (prefetch_abort && may_abort)) {
|
||||||
|
// Notify the debugger and go to sleep if a breakpoint was hit,
|
||||||
|
// or if the thread is unable to continue for any reason.
|
||||||
if (breakpoint) {
|
if (breakpoint) {
|
||||||
interface->RewindBreakpointInstruction();
|
interface->RewindBreakpointInstruction();
|
||||||
}
|
}
|
||||||
|
|
@ -133,26 +132,19 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||||
}
|
}
|
||||||
thread->RequestSuspend(SuspendType::Debug);
|
thread->RequestSuspend(SuspendType::Debug);
|
||||||
return;
|
return;
|
||||||
}
|
} else if (data_abort) {
|
||||||
|
// Notify the debugger and go to sleep on data abort.
|
||||||
// Notify the debugger and go to sleep on data abort.
|
|
||||||
if (data_abort) {
|
|
||||||
if (system.DebuggerEnabled()) {
|
if (system.DebuggerEnabled()) {
|
||||||
system.GetDebugger().NotifyThreadWatchpoint(thread, *interface->HaltedWatchpoint());
|
system.GetDebugger().NotifyThreadWatchpoint(thread, *interface->HaltedWatchpoint());
|
||||||
}
|
}
|
||||||
thread->RequestSuspend(SuspendType::Debug);
|
thread->RequestSuspend(SuspendType::Debug);
|
||||||
return;
|
return;
|
||||||
}
|
} else if (supervisor_call) {
|
||||||
|
// Handle system calls: Perform call.
|
||||||
// Handle system calls.
|
|
||||||
if (supervisor_call) {
|
|
||||||
// Perform call.
|
|
||||||
Svc::Call(system, interface->GetSvcNumber());
|
Svc::Call(system, interface->GetSvcNumber());
|
||||||
return;
|
return;
|
||||||
}
|
} else if (interrupt || m_is_single_core) {
|
||||||
|
// Handle external interrupt sources.
|
||||||
// Handle external interrupt sources.
|
|
||||||
if (interrupt || m_is_single_core) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,16 +152,14 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
|
||||||
|
|
||||||
void PhysicalCore::LoadContext(const KThread* thread) {
|
void PhysicalCore::LoadContext(const KThread* thread) {
|
||||||
auto* const process = thread->GetOwnerProcess();
|
auto* const process = thread->GetOwnerProcess();
|
||||||
if (!process) {
|
if (process) {
|
||||||
// Kernel threads do not run on emulated CPU cores.
|
// Kernel threads do not run on emulated CPU cores.
|
||||||
return;
|
auto* interface = process->GetArmInterface(m_core_index);
|
||||||
}
|
if (interface) {
|
||||||
|
interface->SetContext(thread->GetContext());
|
||||||
auto* interface = process->GetArmInterface(m_core_index);
|
interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress()));
|
||||||
if (interface) {
|
interface->SetWatchpointArray(&process->GetWatchpoints());
|
||||||
interface->SetContext(thread->GetContext());
|
}
|
||||||
interface->SetTpidrroEl0(GetInteger(thread->GetTlsAddress()));
|
|
||||||
interface->SetWatchpointArray(&process->GetWatchpoints());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,14 +169,12 @@ void PhysicalCore::LoadSvcArguments(const KProcess& process, std::span<const uin
|
||||||
|
|
||||||
void PhysicalCore::SaveContext(KThread* thread) const {
|
void PhysicalCore::SaveContext(KThread* thread) const {
|
||||||
auto* const process = thread->GetOwnerProcess();
|
auto* const process = thread->GetOwnerProcess();
|
||||||
if (!process) {
|
if (process) {
|
||||||
// Kernel threads do not run on emulated CPU cores.
|
// Kernel threads do not run on emulated CPU cores.
|
||||||
return;
|
auto* interface = process->GetArmInterface(m_core_index);
|
||||||
}
|
if (interface) {
|
||||||
|
interface->GetContext(thread->GetContext());
|
||||||
auto* interface = process->GetArmInterface(m_core_index);
|
}
|
||||||
if (interface) {
|
|
||||||
interface->GetContext(thread->GetContext());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,13 +194,11 @@ void PhysicalCore::CloneFpuStatus(KThread* dst) const {
|
||||||
|
|
||||||
void PhysicalCore::LogBacktrace() {
|
void PhysicalCore::LogBacktrace() {
|
||||||
auto* process = GetCurrentProcessPointer(m_kernel);
|
auto* process = GetCurrentProcessPointer(m_kernel);
|
||||||
if (!process) {
|
if (process) {
|
||||||
return;
|
auto* interface = process->GetArmInterface(m_core_index);
|
||||||
}
|
if (interface) {
|
||||||
|
interface->LogBacktrace(process);
|
||||||
auto* interface = process->GetArmInterface(m_core_index);
|
}
|
||||||
if (interface) {
|
|
||||||
interface->LogBacktrace(process);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,12 +226,10 @@ void PhysicalCore::Interrupt() {
|
||||||
m_on_interrupt.notify_one();
|
m_on_interrupt.notify_one();
|
||||||
|
|
||||||
// If there is no thread running, we are done.
|
// If there is no thread running, we are done.
|
||||||
if (arm_interface == nullptr) {
|
if (arm_interface != nullptr) {
|
||||||
return;
|
// Interrupt the CPU.
|
||||||
|
arm_interface->SignalInterrupt(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interrupt the CPU.
|
|
||||||
arm_interface->SignalInterrupt(thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalCore::ClearInterrupt() {
|
void PhysicalCore::ClearInterrupt() {
|
||||||
|
|
|
||||||
|
|
@ -32,45 +32,31 @@ struct Jit::Impl final {
|
||||||
, core(conf) {}
|
, core(conf) {}
|
||||||
|
|
||||||
HaltReason Run() {
|
HaltReason Run() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||||
|
is_executing = true;
|
||||||
jit_interface->is_executing = true;
|
|
||||||
SCOPE_EXIT {
|
|
||||||
jit_interface->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason Step() {
|
HaltReason Step() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||||
|
is_executing = true;
|
||||||
jit_interface->is_executing = true;
|
|
||||||
SCOPE_EXIT {
|
|
||||||
jit_interface->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache() {
|
void ClearCache() {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalidate_entire_cache = true;
|
invalidate_entire_cache = true;
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
@ -80,12 +66,12 @@ struct Jit::Impl final {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaltExecution(HaltReason hr) {
|
void HaltExecution(HaltReason hr) {
|
||||||
Atomic::Or(&halt_reason, static_cast<u32>(hr));
|
Atomic::Or(&halt_reason, u32(hr));
|
||||||
Atomic::Barrier();
|
Atomic::Barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearHalt(HaltReason hr) {
|
void ClearHalt(HaltReason hr) {
|
||||||
Atomic::And(&halt_reason, ~static_cast<u32>(hr));
|
Atomic::And(&halt_reason, ~u32(hr));
|
||||||
Atomic::Barrier();
|
Atomic::Barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,21 +118,15 @@ struct Jit::Impl final {
|
||||||
private:
|
private:
|
||||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
|
|
||||||
ClearHalt(HaltReason::CacheInvalidation);
|
ClearHalt(HaltReason::CacheInvalidation);
|
||||||
|
|
||||||
if (invalidate_entire_cache) {
|
if (invalidate_entire_cache) {
|
||||||
current_address_space.ClearCache();
|
current_address_space.ClearCache();
|
||||||
|
|
||||||
invalidate_entire_cache = false;
|
invalidate_entire_cache = false;
|
||||||
invalid_cache_ranges.clear();
|
invalid_cache_ranges.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!invalid_cache_ranges.empty()) {
|
if (!invalid_cache_ranges.empty()) {
|
||||||
current_address_space.InvalidateCacheRanges(invalid_cache_ranges);
|
current_address_space.InvalidateCacheRanges(invalid_cache_ranges);
|
||||||
|
|
||||||
invalid_cache_ranges.clear();
|
invalid_cache_ranges.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -160,10 +140,9 @@ private:
|
||||||
A32Core core;
|
A32Core core;
|
||||||
|
|
||||||
volatile u32 halt_reason = 0;
|
volatile u32 halt_reason = 0;
|
||||||
|
|
||||||
std::mutex invalidation_mutex;
|
|
||||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||||
bool invalidate_entire_cache = false;
|
bool invalidate_entire_cache = false;
|
||||||
|
bool is_executing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Jit::Jit(UserConfig conf)
|
Jit::Jit(UserConfig conf)
|
||||||
|
|
|
||||||
|
|
@ -27,50 +27,37 @@ using namespace Backend::Arm64;
|
||||||
|
|
||||||
struct Jit::Impl final {
|
struct Jit::Impl final {
|
||||||
Impl(Jit*, A64::UserConfig conf)
|
Impl(Jit*, A64::UserConfig conf)
|
||||||
: conf(conf)
|
: conf(conf)
|
||||||
, current_address_space(conf)
|
, current_address_space(conf)
|
||||||
, core(conf) {}
|
, core(conf)
|
||||||
|
{}
|
||||||
|
|
||||||
HaltReason Run() {
|
HaltReason Run() {
|
||||||
ASSERT(!is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
|
||||||
|
|
||||||
is_executing = true;
|
is_executing = true;
|
||||||
SCOPE_EXIT {
|
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||||
is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason Step() {
|
HaltReason Step() {
|
||||||
ASSERT(!is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&halt_reason)));
|
|
||||||
|
|
||||||
is_executing = true;
|
is_executing = true;
|
||||||
SCOPE_EXIT {
|
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&halt_reason)));
|
||||||
is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
HaltReason hr = core.Step(current_address_space, current_state, &halt_reason);
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache() {
|
void ClearCache() {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalidate_entire_cache = true;
|
invalidate_entire_cache = true;
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateCacheRange(std::uint64_t start_address, std::size_t length) {
|
void InvalidateCacheRange(std::uint64_t start_address, std::size_t length) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u64>::closed(start_address, start_address + length - 1));
|
invalid_cache_ranges.add(boost::icl::discrete_interval<u64>::closed(start_address, start_address + length - 1));
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
@ -80,11 +67,13 @@ struct Jit::Impl final {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaltExecution(HaltReason hr) {
|
void HaltExecution(HaltReason hr) {
|
||||||
Atomic::Or(&halt_reason, static_cast<u32>(hr));
|
Atomic::Or(&halt_reason, u32(hr));
|
||||||
|
Atomic::Barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearHalt(HaltReason hr) {
|
void ClearHalt(HaltReason hr) {
|
||||||
Atomic::And(&halt_reason, ~static_cast<u32>(hr));
|
Atomic::And(&halt_reason, ~u32(hr));
|
||||||
|
Atomic::Barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t PC() const {
|
std::uint64_t PC() const {
|
||||||
|
|
@ -147,10 +136,6 @@ struct Jit::Impl final {
|
||||||
current_state.exclusive_state = false;
|
current_state.exclusive_state = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsExecuting() const {
|
|
||||||
return is_executing;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Disassemble() const {
|
std::string Disassemble() const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -158,21 +143,15 @@ struct Jit::Impl final {
|
||||||
private:
|
private:
|
||||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
|
|
||||||
ClearHalt(HaltReason::CacheInvalidation);
|
ClearHalt(HaltReason::CacheInvalidation);
|
||||||
|
|
||||||
if (invalidate_entire_cache) {
|
if (invalidate_entire_cache) {
|
||||||
current_address_space.ClearCache();
|
current_address_space.ClearCache();
|
||||||
|
|
||||||
invalidate_entire_cache = false;
|
invalidate_entire_cache = false;
|
||||||
invalid_cache_ranges.clear();
|
invalid_cache_ranges.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!invalid_cache_ranges.empty()) {
|
if (!invalid_cache_ranges.empty()) {
|
||||||
current_address_space.InvalidateCacheRanges(invalid_cache_ranges);
|
current_address_space.InvalidateCacheRanges(invalid_cache_ranges);
|
||||||
|
|
||||||
invalid_cache_ranges.clear();
|
invalid_cache_ranges.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -185,8 +164,6 @@ private:
|
||||||
A64Core core;
|
A64Core core;
|
||||||
|
|
||||||
volatile u32 halt_reason = 0;
|
volatile u32 halt_reason = 0;
|
||||||
|
|
||||||
std::mutex invalidation_mutex;
|
|
||||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||||
bool invalidate_entire_cache = false;
|
bool invalidate_entire_cache = false;
|
||||||
bool is_executing = false;
|
bool is_executing = false;
|
||||||
|
|
@ -307,10 +284,6 @@ void Jit::ClearExclusiveState() {
|
||||||
impl->ClearExclusiveState();
|
impl->ClearExclusiveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Jit::IsExecuting() const {
|
|
||||||
return impl->IsExecuting();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Jit::Disassemble() const {
|
std::string Jit::Disassemble() const {
|
||||||
return impl->Disassemble();
|
return impl->Disassemble();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,41 +32,29 @@ struct Jit::Impl final {
|
||||||
, core(conf) {}
|
, core(conf) {}
|
||||||
|
|
||||||
HaltReason Run() {
|
HaltReason Run() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
jit_interface->is_executing = true;
|
is_executing = true;
|
||||||
SCOPE_EXIT {
|
|
||||||
jit_interface->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
HaltReason hr = core.Run(current_address_space, current_state, &halt_reason);
|
||||||
|
|
||||||
RequestCacheInvalidation();
|
RequestCacheInvalidation();
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason Step() {
|
HaltReason Step() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
jit_interface->is_executing = true;
|
is_executing = true;
|
||||||
SCOPE_EXIT {
|
|
||||||
jit_interface->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
|
|
||||||
RequestCacheInvalidation();
|
RequestCacheInvalidation();
|
||||||
|
is_executing = false;
|
||||||
return HaltReason{};
|
return HaltReason{};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache() {
|
void ClearCache() {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalidate_entire_cache = true;
|
invalidate_entire_cache = true;
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateCacheRange(u32 start_address, size_t length) {
|
void InvalidateCacheRange(u32 start_address, size_t length) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
@ -132,12 +120,10 @@ private:
|
||||||
A32JitState current_state{};
|
A32JitState current_state{};
|
||||||
A32AddressSpace current_address_space;
|
A32AddressSpace current_address_space;
|
||||||
A32Core core;
|
A32Core core;
|
||||||
|
|
||||||
volatile u32 halt_reason = 0;
|
volatile u32 halt_reason = 0;
|
||||||
|
|
||||||
std::mutex invalidation_mutex;
|
|
||||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||||
bool invalidate_entire_cache = false;
|
bool invalidate_entire_cache = false;
|
||||||
|
bool is_executing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Jit::Jit(UserConfig conf)
|
Jit::Jit(UserConfig conf)
|
||||||
|
|
|
||||||
|
|
@ -75,14 +75,9 @@ struct Jit::Impl {
|
||||||
~Impl() = default;
|
~Impl() = default;
|
||||||
|
|
||||||
HaltReason Run() {
|
HaltReason Run() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||||
|
is_executing = true;
|
||||||
jit_interface->is_executing = true;
|
|
||||||
SCOPE_EXIT {
|
|
||||||
jit_interface->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CodePtr current_codeptr = [this] {
|
const CodePtr current_codeptr = [this] {
|
||||||
// RSB optimization
|
// RSB optimization
|
||||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
|
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
|
||||||
|
|
@ -93,44 +88,34 @@ struct Jit::Impl {
|
||||||
|
|
||||||
return GetCurrentBlock();
|
return GetCurrentBlock();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const HaltReason hr = block_of_code.RunCode(&jit_state, current_codeptr);
|
const HaltReason hr = block_of_code.RunCode(&jit_state, current_codeptr);
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason Step() {
|
HaltReason Step() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&jit_state.halt_reason)));
|
||||||
|
is_executing = true;
|
||||||
jit_interface->is_executing = true;
|
|
||||||
SCOPE_EXIT {
|
|
||||||
jit_interface->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache() {
|
void ClearCache() {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalidate_entire_cache = true;
|
invalidate_entire_cache = true;
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, u32(start_address + length - 1)));
|
||||||
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
|
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
ASSERT(!jit_interface->is_executing);
|
ASSERT(!is_executing);
|
||||||
jit_state = {};
|
jit_state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,14 +208,10 @@ private:
|
||||||
|
|
||||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
|
|
||||||
ClearHalt(HaltReason::CacheInvalidation);
|
ClearHalt(HaltReason::CacheInvalidation);
|
||||||
|
|
||||||
if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
|
if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit_state.ResetRSB();
|
jit_state.ResetRSB();
|
||||||
if (invalidate_entire_cache) {
|
if (invalidate_entire_cache) {
|
||||||
block_of_code.ClearCache();
|
block_of_code.ClearCache();
|
||||||
|
|
@ -251,11 +232,10 @@ private:
|
||||||
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
|
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
|
||||||
const A32::UserConfig conf;
|
const A32::UserConfig conf;
|
||||||
Jit* jit_interface;
|
Jit* jit_interface;
|
||||||
|
|
||||||
// Requests made during execution to invalidate the cache are queued up here.
|
// Requests made during execution to invalidate the cache are queued up here.
|
||||||
bool invalidate_entire_cache = false;
|
|
||||||
boost::icl::interval_set<u32> invalid_cache_ranges;
|
boost::icl::interval_set<u32> invalid_cache_ranges;
|
||||||
std::mutex invalidation_mutex;
|
bool invalidate_entire_cache = false;
|
||||||
|
bool is_executing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Jit::Jit(UserConfig conf)
|
Jit::Jit(UserConfig conf)
|
||||||
|
|
|
||||||
|
|
@ -75,14 +75,8 @@ public:
|
||||||
HaltReason Run() {
|
HaltReason Run() {
|
||||||
ASSERT(!is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
||||||
|
|
||||||
is_executing = true;
|
is_executing = true;
|
||||||
SCOPE_EXIT {
|
|
||||||
this->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Check code alignment
|
// TODO: Check code alignment
|
||||||
|
|
||||||
const CodePtr current_code_ptr = [this] {
|
const CodePtr current_code_ptr = [this] {
|
||||||
// RSB optimization
|
// RSB optimization
|
||||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
||||||
|
|
@ -94,37 +88,28 @@ public:
|
||||||
}();
|
}();
|
||||||
|
|
||||||
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
|
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaltReason Step() {
|
HaltReason Step() {
|
||||||
ASSERT(!is_executing);
|
ASSERT(!is_executing);
|
||||||
PerformRequestedCacheInvalidation(static_cast<HaltReason>(Atomic::Load(&jit_state.halt_reason)));
|
PerformRequestedCacheInvalidation(HaltReason(Atomic::Load(&jit_state.halt_reason)));
|
||||||
|
|
||||||
is_executing = true;
|
is_executing = true;
|
||||||
SCOPE_EXIT {
|
|
||||||
this->is_executing = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation(hr);
|
PerformRequestedCacheInvalidation(hr);
|
||||||
|
is_executing = false;
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache() {
|
void ClearCache() {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
invalidate_entire_cache = true;
|
invalidate_entire_cache = true;
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateCacheRange(u64 start_address, size_t length) {
|
void InvalidateCacheRange(u64 start_address, size_t length) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
const auto end_address = u64(start_address + length - 1);
|
||||||
const auto end_address = static_cast<u64>(start_address + length - 1);
|
|
||||||
const auto range = boost::icl::discrete_interval<u64>::closed(start_address, end_address);
|
const auto range = boost::icl::discrete_interval<u64>::closed(start_address, end_address);
|
||||||
invalid_cache_ranges.add(range);
|
invalid_cache_ranges.add(range);
|
||||||
HaltExecution(HaltReason::CacheInvalidation);
|
HaltExecution(HaltReason::CacheInvalidation);
|
||||||
|
|
@ -136,11 +121,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaltExecution(HaltReason hr) {
|
void HaltExecution(HaltReason hr) {
|
||||||
Atomic::Or(&jit_state.halt_reason, static_cast<u32>(hr));
|
Atomic::Or(&jit_state.halt_reason, u32(hr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearHalt(HaltReason hr) {
|
void ClearHalt(HaltReason hr) {
|
||||||
Atomic::And(&jit_state.halt_reason, ~static_cast<u32>(hr));
|
Atomic::And(&jit_state.halt_reason, ~u32(hr));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetSP() const {
|
u64 GetSP() const {
|
||||||
|
|
@ -228,10 +213,6 @@ public:
|
||||||
jit_state.exclusive_state = 0;
|
jit_state.exclusive_state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsExecuting() const {
|
|
||||||
return is_executing;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Disassemble() const {
|
std::string Disassemble() const {
|
||||||
const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
const size_t size = reinterpret_cast<const char*>(block_of_code.getCurr()) - reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||||
auto const* p = reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
auto const* p = reinterpret_cast<const char*>(block_of_code.GetCodeBegin());
|
||||||
|
|
@ -280,14 +261,10 @@ private:
|
||||||
|
|
||||||
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
void PerformRequestedCacheInvalidation(HaltReason hr) {
|
||||||
if (Has(hr, HaltReason::CacheInvalidation)) {
|
if (Has(hr, HaltReason::CacheInvalidation)) {
|
||||||
std::unique_lock lock{invalidation_mutex};
|
|
||||||
|
|
||||||
ClearHalt(HaltReason::CacheInvalidation);
|
ClearHalt(HaltReason::CacheInvalidation);
|
||||||
|
|
||||||
if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
|
if (!invalidate_entire_cache && invalid_cache_ranges.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit_state.ResetRSB();
|
jit_state.ResetRSB();
|
||||||
if (invalidate_entire_cache) {
|
if (invalidate_entire_cache) {
|
||||||
block_of_code.ClearCache();
|
block_of_code.ClearCache();
|
||||||
|
|
@ -306,10 +283,9 @@ private:
|
||||||
BlockOfCode block_of_code;
|
BlockOfCode block_of_code;
|
||||||
A64EmitX64 emitter;
|
A64EmitX64 emitter;
|
||||||
Optimization::PolyfillOptions polyfill_options;
|
Optimization::PolyfillOptions polyfill_options;
|
||||||
bool is_executing = false;
|
|
||||||
bool invalidate_entire_cache = false;
|
|
||||||
boost::icl::interval_set<u64> invalid_cache_ranges;
|
boost::icl::interval_set<u64> invalid_cache_ranges;
|
||||||
std::mutex invalidation_mutex;
|
bool invalidate_entire_cache = false;
|
||||||
|
bool is_executing = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Jit::Jit(UserConfig conf)
|
Jit::Jit(UserConfig conf)
|
||||||
|
|
@ -421,10 +397,6 @@ void Jit::ClearExclusiveState() {
|
||||||
impl->ClearExclusiveState();
|
impl->ClearExclusiveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Jit::IsExecuting() const {
|
|
||||||
return impl->IsExecuting();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Jit::Disassemble() const {
|
std::string Jit::Disassemble() const {
|
||||||
return impl->Disassemble();
|
return impl->Disassemble();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,21 +84,11 @@ public:
|
||||||
/// Clears exclusive state for this core.
|
/// Clears exclusive state for this core.
|
||||||
void ClearExclusiveState();
|
void ClearExclusiveState();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if Jit::Run was called but hasn't returned yet.
|
|
||||||
* i.e.: We're in a callback.
|
|
||||||
*/
|
|
||||||
bool IsExecuting() const {
|
|
||||||
return is_executing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Disassemble the instructions following the current pc and return
|
/// @brief Disassemble the instructions following the current pc and return
|
||||||
/// the resulting instructions as a vector of their string representations.
|
/// the resulting instructions as a vector of their string representations.
|
||||||
std::string Disassemble() const;
|
std::string Disassemble() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_executing = false;
|
|
||||||
|
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl;
|
std::unique_ptr<Impl> impl;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -116,12 +116,6 @@ public:
|
||||||
/// Clears exclusive state for this core.
|
/// Clears exclusive state for this core.
|
||||||
void ClearExclusiveState();
|
void ClearExclusiveState();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if Jit::Run was called but hasn't returned yet.
|
|
||||||
* i.e.: We're in a callback.
|
|
||||||
*/
|
|
||||||
bool IsExecuting() const;
|
|
||||||
|
|
||||||
/// @brief Disassemble the instructions following the current pc and return
|
/// @brief Disassemble the instructions following the current pc and return
|
||||||
/// the resulting instructions as a vector of their string representations.
|
/// the resulting instructions as a vector of their string representations.
|
||||||
std::string Disassemble() const;
|
std::string Disassemble() const;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue