mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-19 14:36:15 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b4b6976b6 | |||
| 7361124602 | |||
| 19e2dba35a | |||
| 0634b4a278 | |||
| ee428deb1e | |||
| 07bc77c7e7 | |||
| 72973fe582 | |||
| c263b6af6f | |||
| e46576b4c3 |
@@ -84,7 +84,11 @@ function(SystemPackageViable JSON_NAME)
|
||||
parse_object(${object})
|
||||
|
||||
string(REPLACE " " ";" find_args "${find_args}")
|
||||
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
|
||||
if (${package}_FORCE_BUNDLED)
|
||||
set(${package}_FOUND OFF)
|
||||
else()
|
||||
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
|
||||
endif()
|
||||
|
||||
set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
|
||||
set(${pkg}_PACKAGE ${package} PARENT_SCOPE)
|
||||
|
||||
Vendored
+1
-3
@@ -150,7 +150,7 @@
|
||||
"package": "SDL2",
|
||||
"name": "SDL2",
|
||||
"repo": "crueter-ci/SDL2",
|
||||
"version": "2.32.10-a65111bd2d",
|
||||
"version": "2.32.10-cf5dabd6ea",
|
||||
"min_version": "2.26.4"
|
||||
},
|
||||
"catch2": {
|
||||
@@ -184,7 +184,6 @@
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"hash": "d5622d6bb7266f7942a7b8ad43e8a22524893bf0c2ea1af91204838d9b78d32768843f6faa248757427b8404b8c6443776d4afa6b672cd8571a4e0c03a829383",
|
||||
"key": "generic",
|
||||
"bundled": true,
|
||||
"git_version": "2.32.10",
|
||||
"skip_updates": true
|
||||
@@ -194,7 +193,6 @@
|
||||
"repo": "libsdl-org/SDL",
|
||||
"sha": "cc016b0046",
|
||||
"hash": "b8d9873446cdb922387471df9968e078714683046674ef0d0edddf8e25da65a539a3bae83d635496b970237f90b07b36a69f8d7855d450de59311d6d6e8c3dbc",
|
||||
"key": "steamdeck",
|
||||
"bundled": true,
|
||||
"skip_updates": "true"
|
||||
},
|
||||
|
||||
@@ -507,7 +507,7 @@
|
||||
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
|
||||
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
||||
<string name="fix_bloom_effects">Fix Bloom Effects</string>
|
||||
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout</string>
|
||||
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
|
||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||
|
||||
@@ -568,13 +568,7 @@ struct Values {
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
|
||||
#if defined (_WIN32)
|
||||
ExtendedDynamicState::EDS3,
|
||||
#elif defined (__FreeBSD__)
|
||||
ExtendedDynamicState::EDS3,
|
||||
#elif defined (ANDROID)
|
||||
ExtendedDynamicState::Disabled,
|
||||
#elif defined (__APPLE__)
|
||||
#if defined (ANDROID) || defined (__APPLE__)
|
||||
ExtendedDynamicState::Disabled,
|
||||
#else
|
||||
ExtendedDynamicState::EDS2,
|
||||
|
||||
+11
-14
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,27 +13,16 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef __ANDROID__
|
||||
template <typename T>
|
||||
T* LookupLibcSymbol(const char* name) {
|
||||
#if defined(__BIONIC__)
|
||||
Common::DynamicLibrary provider("libc.so");
|
||||
if (!provider.IsOpen()) {
|
||||
UNREACHABLE_MSG("Failed to open libc!");
|
||||
}
|
||||
#else
|
||||
// For other operating environments, we assume the symbol is not overridden.
|
||||
const char* base = nullptr;
|
||||
Common::DynamicLibrary provider(base);
|
||||
#endif
|
||||
|
||||
ASSERT_MSG(provider.IsOpen(), "Failed to open libc!");
|
||||
void* sym = provider.GetSymbolAddress(name);
|
||||
if (sym == nullptr) {
|
||||
sym = dlsym(RTLD_DEFAULT, name);
|
||||
}
|
||||
if (sym == nullptr) {
|
||||
UNREACHABLE_MSG("Unable to find symbol {}!", name);
|
||||
}
|
||||
|
||||
ASSERT_MSG(sym != nullptr, "Unable to find symbol {}!", name);
|
||||
return reinterpret_cast<T*>(sym);
|
||||
}
|
||||
|
||||
@@ -38,5 +30,10 @@ int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact)
|
||||
static auto libc_sigaction = LookupLibcSymbol<decltype(sigaction)>("sigaction");
|
||||
return libc_sigaction(signum, act, oldact);
|
||||
}
|
||||
#else
|
||||
int SigAction(int signum, const struct sigaction* act, struct sigaction* oldact) {
|
||||
return sigaction(signum, act, oldact);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -38,10 +38,16 @@ u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read64(vaddr);
|
||||
}
|
||||
|
||||
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
|
||||
return std::nullopt;
|
||||
return m_memory.Read32(vaddr);
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
|
||||
last_code_addr = aligned_vaddr;
|
||||
}
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
|
||||
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
@@ -49,6 +50,9 @@ public:
|
||||
u64 GetTicksRemaining() override;
|
||||
bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type);
|
||||
void ReturnException(u32 pc, Dynarmic::HaltReason hr);
|
||||
//
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
ArmDynarmic32& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
Kernel::KProcess* m_process{};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -41,10 +41,17 @@ Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||
return {m_memory.Read64(vaddr), m_memory.Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
||||
if (!m_memory.IsValidVirtualAddressRange(vaddr, sizeof(u32)))
|
||||
return std::nullopt;
|
||||
return m_memory.Read32(vaddr);
|
||||
// return m_memory.Read32(vaddr);
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
m_memory.ReadBlock(aligned_vaddr, &cached_code_page, sizeof(cached_code_page));
|
||||
last_code_addr = aligned_vaddr;
|
||||
}
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
@@ -61,6 +62,8 @@ public:
|
||||
bool CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type);
|
||||
void ReturnException(u64 pc, Dynarmic::HaltReason hr);
|
||||
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
ArmDynarmic64& m_parent;
|
||||
Core::Memory::Memory& m_memory;
|
||||
u64 m_tpidrro_el0{};
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include "core/arm/nce/patcher.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
|
||||
#include "dynarmic/common/context.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -33,100 +35,70 @@ static_assert(offsetof(NativeExecutionParameters, native_context) == TpidrEl0Nat
|
||||
static_assert(offsetof(NativeExecutionParameters, lock) == TpidrEl0Lock);
|
||||
static_assert(offsetof(NativeExecutionParameters, magic) == TpidrEl0TlsMagic);
|
||||
|
||||
fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
||||
_aarch64_ctx* header = reinterpret_cast<_aarch64_ctx*>(&host_ctx.__reserved);
|
||||
while (header->magic != FPSIMD_MAGIC) {
|
||||
header = reinterpret_cast<_aarch64_ctx*>(reinterpret_cast<char*>(header) + header->size);
|
||||
}
|
||||
return reinterpret_cast<fpsimd_context*>(header);
|
||||
}
|
||||
|
||||
using namespace Common::Literals;
|
||||
constexpr u32 StackSize = 128_KiB;
|
||||
|
||||
} // namespace
|
||||
|
||||
void* ArmNce::RestoreGuestContext(void* raw_context) {
|
||||
// Retrieve the host context.
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
|
||||
// Thread-local parameters will be located in x9.
|
||||
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(host_ctx.regs[9]);
|
||||
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
|
||||
|
||||
// Retrieve the host floating point state.
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
|
||||
// Save host callee-saved registers.
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &fpctx->vregs[8],
|
||||
sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &host_ctx.regs[19],
|
||||
sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
|
||||
// Save stack pointer.
|
||||
guest_ctx->host_ctx.host_sp = host_ctx.sp;
|
||||
|
||||
CTX_DECLARE(raw_context);
|
||||
// Restore all guest state except tpidr_el0.
|
||||
host_ctx.sp = guest_ctx->sp;
|
||||
host_ctx.pc = guest_ctx->pc;
|
||||
host_ctx.pstate = guest_ctx->pstate;
|
||||
fpctx->fpcr = guest_ctx->fpcr;
|
||||
fpctx->fpsr = guest_ctx->fpsr;
|
||||
std::memcpy(host_ctx.regs, guest_ctx->cpu_registers.data(), sizeof(host_ctx.regs));
|
||||
std::memcpy(fpctx->vregs, guest_ctx->vector_registers.data(), sizeof(fpctx->vregs));
|
||||
|
||||
// Thread-local parameters will be located in x9.
|
||||
auto* tpidr = reinterpret_cast<NativeExecutionParameters*>(CTX_X(9));
|
||||
auto* guest_ctx = static_cast<GuestContext*>(tpidr->native_context);
|
||||
// Save host callee-saved registers.
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_regs.data(), &CTX_X(19), sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(guest_ctx->host_ctx.host_saved_vregs.data(), &CTX_Q(8), sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
// Save stack pointer.
|
||||
guest_ctx->host_ctx.host_sp = CTX_SP;
|
||||
CTX_PC = guest_ctx->sp;
|
||||
CTX_SP = guest_ctx->pc;
|
||||
CTX_PSTATE = guest_ctx->pstate;
|
||||
CTX_FPCR = guest_ctx->fpcr;
|
||||
CTX_FPSR = guest_ctx->fpsr;
|
||||
std::memcpy(&CTX_X(0), guest_ctx->cpu_registers.data(), sizeof(guest_ctx->cpu_registers));
|
||||
std::memcpy(&CTX_Q(0), guest_ctx->vector_registers.data(), sizeof(guest_ctx->vector_registers));
|
||||
// Return the new thread-local storage pointer.
|
||||
return tpidr;
|
||||
}
|
||||
|
||||
void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
|
||||
// Retrieve the host context.
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
|
||||
// Retrieve the host floating point state.
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
|
||||
CTX_DECLARE(raw_context);
|
||||
// Save all guest registers except tpidr_el0.
|
||||
std::memcpy(guest_ctx->cpu_registers.data(), host_ctx.regs, sizeof(host_ctx.regs));
|
||||
std::memcpy(guest_ctx->vector_registers.data(), fpctx->vregs, sizeof(fpctx->vregs));
|
||||
guest_ctx->fpsr = fpctx->fpsr;
|
||||
guest_ctx->fpcr = fpctx->fpcr;
|
||||
guest_ctx->pstate = static_cast<u32>(host_ctx.pstate);
|
||||
guest_ctx->pc = host_ctx.pc;
|
||||
guest_ctx->sp = host_ctx.sp;
|
||||
|
||||
std::memcpy(guest_ctx->cpu_registers.data(), &CTX_X(0), sizeof(guest_ctx->cpu_registers));
|
||||
std::memcpy(guest_ctx->vector_registers.data(), &CTX_Q(0), sizeof(guest_ctx->vector_registers));
|
||||
guest_ctx->fpsr = CTX_FPSR;
|
||||
guest_ctx->fpcr = CTX_FPCR;
|
||||
guest_ctx->pc = CTX_PC;
|
||||
guest_ctx->sp = CTX_SP;
|
||||
guest_ctx->pstate = u32(CTX_PSTATE);
|
||||
// Restore stack pointer.
|
||||
host_ctx.sp = guest_ctx->host_ctx.host_sp;
|
||||
CTX_SP = guest_ctx->host_ctx.host_sp;
|
||||
|
||||
// Restore host callee-saved registers.
|
||||
std::memcpy(&host_ctx.regs[19], guest_ctx->host_ctx.host_saved_regs.data(),
|
||||
sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(&fpctx->vregs[8], guest_ctx->host_ctx.host_saved_vregs.data(),
|
||||
sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
|
||||
std::memcpy(&CTX_X(19), guest_ctx->host_ctx.host_saved_regs.data(), sizeof(guest_ctx->host_ctx.host_saved_regs));
|
||||
std::memcpy(&CTX_Q(8), guest_ctx->host_ctx.host_saved_vregs.data(), sizeof(guest_ctx->host_ctx.host_saved_vregs));
|
||||
// Return from the call on exit by setting pc to x30.
|
||||
host_ctx.pc = guest_ctx->host_ctx.host_saved_regs[11];
|
||||
|
||||
CTX_PC = guest_ctx->host_ctx.host_saved_regs[11];
|
||||
// Clear esr_el1 and return it.
|
||||
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
|
||||
CTX_X(0) = guest_ctx->esr_el1.exchange(0);
|
||||
}
|
||||
|
||||
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
CTX_DECLARE(raw_context);
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
// We can't handle the access, so determine why we crashed.
|
||||
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
|
||||
|
||||
auto const is_prefetch_abort = CTX_PC == reinterpret_cast<u64>(info->si_addr);
|
||||
// For data aborts, skip the instruction and return to guest code.
|
||||
// This will allow games to continue in many scenarios where they would otherwise crash.
|
||||
if (!is_prefetch_abort) {
|
||||
host_ctx.pc += 4;
|
||||
CTX_PC += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is a prefetch abort.
|
||||
guest_ctx->esr_el1.fetch_or(static_cast<u64>(HaltReason::PrefetchAbort));
|
||||
guest_ctx->esr_el1.fetch_or(u64(HaltReason::PrefetchAbort));
|
||||
|
||||
// Forcibly mark the context as locked. We are still running.
|
||||
// We may race with SignalInterrupt here:
|
||||
@@ -142,17 +114,13 @@ bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, voi
|
||||
}
|
||||
|
||||
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||
CTX_DECLARE(raw_context);
|
||||
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
|
||||
|
||||
// Match and execute an instruction.
|
||||
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
|
||||
if (next_pc) {
|
||||
host_ctx.pc = *next_pc;
|
||||
if (auto next_pc = MatchAndExecuteOneInstruction(memory, raw_context); next_pc) {
|
||||
CTX_PC = *next_pc;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We couldn't handle the access.
|
||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||
}
|
||||
@@ -198,7 +166,7 @@ void ArmNce::UnlockThread(Kernel::KThread* thread) {
|
||||
HaltReason ArmNce::RunThread(Kernel::KThread* thread) {
|
||||
// Check if we're already interrupted.
|
||||
// If we are, we can just return immediately.
|
||||
HaltReason hr = static_cast<HaltReason>(m_guest_ctx.esr_el1.exchange(0));
|
||||
auto hr = HaltReason(m_guest_ctx.esr_el1.exchange(0));
|
||||
if (True(hr)) {
|
||||
return hr;
|
||||
}
|
||||
@@ -276,9 +244,51 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
|
||||
|
||||
ArmNce::~ArmNce() = default;
|
||||
|
||||
// Borrowed from libusb
|
||||
static unsigned int posix_gettid(void) {
|
||||
static thread_local unsigned int tl_tid;
|
||||
int tid;
|
||||
if (tl_tid)
|
||||
return tl_tid;
|
||||
#if defined(__ANDROID__)
|
||||
tid = gettid();
|
||||
#elif defined(__APPLE__)
|
||||
#ifdef HAVE_PTHREAD_THREADID_NP
|
||||
uint64_t thread_id;
|
||||
if (pthread_threadid_np(NULL, &thread_id) == 0)
|
||||
tid = (int)thread_id;
|
||||
else
|
||||
tid = -1;
|
||||
#else
|
||||
tid = (int)pthread_mach_thread_np(pthread_self());
|
||||
#endif
|
||||
#elif defined(__HAIKU__)
|
||||
tid = get_pthread_thread_id(pthread_self());
|
||||
#elif defined(__linux__)
|
||||
tid = (int)syscall(SYS_gettid);
|
||||
#elif defined(__NetBSD__)
|
||||
tid = _lwp_self();
|
||||
#elif defined(__OpenBSD__)
|
||||
/* The following only works with OpenBSD > 5.1 as it requires
|
||||
* real thread support. For 5.1 and earlier, -1 is returned. */
|
||||
tid = syscall(SYS_getthrid);
|
||||
#elif defined(__sun__)
|
||||
tid = _lwp_self();
|
||||
#else
|
||||
tid = -1;
|
||||
#endif
|
||||
if (tid == -1) {
|
||||
/* If we don't have a thread ID, at least return a unique
|
||||
* value that can be used to distinguish individual
|
||||
* threads. */
|
||||
tid = (int)(intptr_t)pthread_self();
|
||||
}
|
||||
return tl_tid = (unsigned int)tid;
|
||||
}
|
||||
|
||||
void ArmNce::Initialize() {
|
||||
if (m_thread_id == -1) {
|
||||
m_thread_id = gettid();
|
||||
m_thread_id = posix_gettid();
|
||||
}
|
||||
|
||||
// Configure signal stack.
|
||||
@@ -309,7 +319,7 @@ void ArmNce::Initialize() {
|
||||
&ArmNce::ReturnToRunCodeByExceptionLevelChangeSignalHandler);
|
||||
return_to_run_code_action.sa_mask = signal_mask;
|
||||
Common::SigAction(ReturnToRunCodeByExceptionLevelChangeSignal, &return_to_run_code_action,
|
||||
nullptr);
|
||||
nullptr);
|
||||
|
||||
struct sigaction break_from_run_code_action {};
|
||||
break_from_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
@@ -370,7 +380,7 @@ void ArmNce::SetContext(const Kernel::Svc::ThreadContext& ctx) {
|
||||
|
||||
void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||
// Add break loop condition.
|
||||
m_guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
|
||||
m_guest_ctx.esr_el1.fetch_or(u64(HaltReason::BreakLoop));
|
||||
|
||||
auto* params = &thread->GetNativeExecutionParameters();
|
||||
LockThreadParameters(params);
|
||||
@@ -381,7 +391,11 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||
if (params->is_running) {
|
||||
// We should signal to the running thread.
|
||||
// The running thread will unlock the thread context.
|
||||
#ifdef __linux__
|
||||
syscall(SYS_tkill, m_thread_id, BreakFromRunCodeSignal);
|
||||
#else
|
||||
pthread_kill(pthread_t(m_thread_id), int(BreakFromRunCodeSignal));
|
||||
#endif
|
||||
} else {
|
||||
// If the thread is no longer running, we have nothing to do.
|
||||
UnlockThreadParameters(params);
|
||||
@@ -391,12 +405,11 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||
const std::size_t CACHE_PAGE_SIZE = 4096;
|
||||
|
||||
void ArmNce::ClearInstructionCache() {
|
||||
#ifdef __aarch64__
|
||||
// Ensure all previous memory operations complete
|
||||
asm volatile("dsb ish\n"
|
||||
"dsb ish\n"
|
||||
"isb" ::: "memory");
|
||||
#endif
|
||||
asm volatile(
|
||||
"dsb ish\r\n"
|
||||
"dsb ish\r\n"
|
||||
"isb\r\n" ::: "memory");
|
||||
}
|
||||
|
||||
void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <numeric>
|
||||
#include "core/arm/nce/interpreter_visitor.h"
|
||||
#include "core/memory.h"
|
||||
#include "dynarmic/common/context.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -59,7 +61,7 @@ static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) {
|
||||
if (regsize == 64) {
|
||||
return SignExtendToLong(value, bitsize);
|
||||
} else {
|
||||
return SignExtendToWord(static_cast<u32>(value), bitsize);
|
||||
return SignExtendToWord(u32(value), bitsize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,11 +149,11 @@ u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shi
|
||||
}
|
||||
|
||||
u128 InterpreterVisitor::GetVec(Vec v) {
|
||||
return m_fpsimd_regs[static_cast<u32>(v)];
|
||||
return m_fpsimd_regs[u32(v)];
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetReg(Reg r) {
|
||||
return m_regs[static_cast<u32>(r)];
|
||||
return m_regs[u32(r)];
|
||||
}
|
||||
|
||||
u64 InterpreterVisitor::GetSp() {
|
||||
@@ -163,11 +165,11 @@ u64 InterpreterVisitor::GetPc() {
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetVec(Vec v, u128 value) {
|
||||
m_fpsimd_regs[static_cast<u32>(v)] = value;
|
||||
m_fpsimd_regs[u32(v)] = value;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetReg(Reg r, u64 value) {
|
||||
m_regs[static_cast<u32>(r)] = value;
|
||||
m_regs[u32(r)] = value;
|
||||
}
|
||||
|
||||
void InterpreterVisitor::SetSp(u64 value) {
|
||||
@@ -674,7 +676,7 @@ bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> opti
|
||||
Reg Rt) {
|
||||
const Imm<1> opc_0{0};
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -686,7 +688,7 @@ bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> opti
|
||||
Reg Rt) {
|
||||
const Imm<1> opc_0{1};
|
||||
const size_t scale = size.ZeroExtend<size_t>();
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -737,7 +739,7 @@ bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -753,7 +755,7 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
}
|
||||
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||
const u8 shift = S ? u8(scale) : 0;
|
||||
if (!option.Bit<1>()) {
|
||||
// Unallocated encoding
|
||||
return false;
|
||||
@@ -761,24 +763,25 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
|
||||
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
|
||||
}
|
||||
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||
fpsimd_context* fpsimd_context) {
|
||||
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
|
||||
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
|
||||
u64& sp = *reinterpret_cast<u64*>(&context->sp);
|
||||
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, void* raw_context) {
|
||||
CTX_DECLARE(raw_context);
|
||||
std::span<u64, 31> regs(reinterpret_cast<u64*>(&CTX_X(0)), 31);
|
||||
std::span<u128, 32> vregs(reinterpret_cast<u128*>(&CTX_Q(0)), 32);
|
||||
|
||||
InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
|
||||
u32 instruction = memory.Read32(pc);
|
||||
// Store temporal to not break aliasing rules :)
|
||||
u64 tmp_sp = CTX_SP;
|
||||
u64 tmp_pc = CTX_PC;
|
||||
u32 instruction = memory.Read32(tmp_pc);
|
||||
bool was_executed = false;
|
||||
|
||||
InterpreterVisitor visitor(memory, regs, vregs, tmp_sp, tmp_pc);
|
||||
if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
|
||||
}
|
||||
|
||||
return was_executed ? std::optional<u64>(pc + 4) : std::nullopt;
|
||||
CTX_SP = tmp_sp;
|
||||
CTX_PC = tmp_pc;
|
||||
return was_executed ? std::optional<u64>(tmp_pc + 4) : std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -105,7 +105,6 @@ private:
|
||||
const u64& m_pc;
|
||||
};
|
||||
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||
fpsimd_context* fpsimd_context);
|
||||
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, void* raw_context);
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <dynarmic/interface/A64/a64.h>
|
||||
#include <dynarmic/interface/A64/config.h>
|
||||
#include <dynarmic/interface/code_page.h>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
@@ -46,6 +47,15 @@ public:
|
||||
: memory{memory_}, local_memory{local_memory_},
|
||||
mapped_ranges{mapped_ranges_}, parent{parent_} {}
|
||||
|
||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
|
||||
static_assert(Core::Memory::YUZU_PAGESIZE == Dynarmic::CODE_PAGE_SIZE);
|
||||
auto const aligned_vaddr = vaddr & ~Core::Memory::YUZU_PAGEMASK;
|
||||
if (last_code_addr != aligned_vaddr) {
|
||||
cached_code_page = ReadMemory<Dynarmic::CodePage>(aligned_vaddr);
|
||||
last_code_addr = aligned_vaddr;
|
||||
}
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return ReadMemory<u8>(vaddr);
|
||||
}
|
||||
@@ -116,8 +126,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T ReadMemory(u64 vaddr) {
|
||||
template<typename T> T ReadMemory(u64 vaddr) {
|
||||
T ret{};
|
||||
if (boost::icl::contains(mapped_ranges, vaddr)) {
|
||||
memory.ReadBlock(vaddr, &ret, sizeof(T));
|
||||
@@ -146,6 +155,9 @@ private:
|
||||
std::vector<u8>& local_memory;
|
||||
IntervalSet& mapped_ranges;
|
||||
JITContextImpl& parent;
|
||||
|
||||
Dynarmic::CodePage cached_code_page;
|
||||
u64 last_code_addr = 0;
|
||||
};
|
||||
|
||||
class JITContextImpl {
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
||||
@@ -36,8 +36,7 @@
|
||||
|
||||
namespace Core::Memory {
|
||||
|
||||
static inline bool AddressSpaceContains(const Common::PageTable& table, const Common::ProcessAddress addr,
|
||||
const std::size_t size) {
|
||||
static inline bool AddressSpaceContains(const Common::PageTable& table, const Common::ProcessAddress addr, const std::size_t size) {
|
||||
const Common::ProcessAddress max_addr = 1ULL << table.GetAddressSpaceBits();
|
||||
return addr + size >= addr && addr + size <= max_addr;
|
||||
}
|
||||
|
||||
@@ -65,11 +65,10 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
|
||||
|
||||
struct Jit::Impl {
|
||||
Impl(Jit* jit, A32::UserConfig conf) noexcept
|
||||
: ir_block{LocationDescriptor(0, PSR(0), FPSCR(0), false)}
|
||||
, conf(std::move(conf))
|
||||
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||
: block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||
, emitter(block_of_code, conf, jit)
|
||||
, polyfill_options(GenPolyfillOptions(block_of_code))
|
||||
, conf(std::move(conf))
|
||||
, jit_interface(jit)
|
||||
{}
|
||||
|
||||
@@ -204,8 +203,7 @@ private:
|
||||
}
|
||||
|
||||
A32EmitX64::BlockDescriptor GetBasicBlock(IR::LocationDescriptor descriptor) {
|
||||
auto block = emitter.GetBasicBlock(descriptor);
|
||||
if (block)
|
||||
if (auto block = emitter.GetBasicBlock(descriptor))
|
||||
return *block;
|
||||
|
||||
constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
|
||||
@@ -215,8 +213,10 @@ private:
|
||||
}
|
||||
block_of_code.EnsureMemoryCommitted(MINIMUM_REMAINING_CODESIZE);
|
||||
|
||||
ir_block.Reset(descriptor);
|
||||
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
// LocationDescriptor ctor() does important ops (like tflags) do not skip
|
||||
auto const arch_descriptor = A32::LocationDescriptor{descriptor};
|
||||
ir_block.Reset(arch_descriptor);
|
||||
A32::Translate(ir_block, arch_descriptor, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
|
||||
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||
return emitter.Emit(ir_block);
|
||||
}
|
||||
@@ -243,12 +243,13 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
IR::Block ir_block;
|
||||
const A32::UserConfig conf;
|
||||
IR::Block ir_block = {LocationDescriptor(0, PSR(0), FPSCR(0), false)};
|
||||
A32JitState jit_state;
|
||||
BlockOfCode block_of_code;
|
||||
A32EmitX64 emitter;
|
||||
Optimization::PolyfillOptions polyfill_options;
|
||||
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
|
||||
const A32::UserConfig conf;
|
||||
Jit* jit_interface;
|
||||
|
||||
// Requests made during execution to invalidate the cache are queued up here.
|
||||
|
||||
@@ -62,8 +62,7 @@ static Optimization::PolyfillOptions GenPolyfillOptions(const BlockOfCode& code)
|
||||
struct Jit::Impl final {
|
||||
public:
|
||||
Impl(Jit* jit, UserConfig conf)
|
||||
: ir_block{LocationDescriptor(0, FP::FPCR(0), false)}
|
||||
, conf(conf)
|
||||
: conf(conf)
|
||||
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this, conf), JitStateInfo{jit_state}, conf.code_cache_size, GenRCP(conf))
|
||||
, emitter(block_of_code, conf, jit)
|
||||
, polyfill_options(GenPolyfillOptions(block_of_code))
|
||||
@@ -257,8 +256,8 @@ private:
|
||||
return GetBlock(A64::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true));
|
||||
}
|
||||
|
||||
CodePtr GetBlock(IR::LocationDescriptor current_location) {
|
||||
if (auto block = emitter.GetBasicBlock(current_location))
|
||||
CodePtr GetBlock(IR::LocationDescriptor descriptor) {
|
||||
if (auto block = emitter.GetBasicBlock(descriptor))
|
||||
return block->entrypoint;
|
||||
|
||||
constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
|
||||
@@ -271,8 +270,10 @@ private:
|
||||
|
||||
// JIT Compile
|
||||
const auto get_code = [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); };
|
||||
ir_block.Reset(current_location);
|
||||
A64::Translate(ir_block, A64::LocationDescriptor{current_location}, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
// LocationDescriptor ctor() does important ops (like tflags) do not skip
|
||||
auto const arch_descriptor = A64::LocationDescriptor{descriptor};
|
||||
ir_block.Reset(arch_descriptor);
|
||||
A64::Translate(ir_block, arch_descriptor, get_code, {conf.define_unpredictable_behaviour, conf.wall_clock_cntpct});
|
||||
Optimization::Optimize(ir_block, conf, polyfill_options);
|
||||
return emitter.Emit(ir_block).entrypoint;
|
||||
}
|
||||
@@ -299,7 +300,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
IR::Block ir_block;
|
||||
IR::Block ir_block = {LocationDescriptor(0, FP::FPCR(0), false)};
|
||||
const UserConfig conf;
|
||||
A64JitState jit_state;
|
||||
BlockOfCode block_of_code;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Dynarmic {
|
||||
|
||||
/// @brief Smallest valid page (may change for Apple?)
|
||||
constexpr inline uint64_t CODE_PAGE_SIZE = 4096;
|
||||
struct CodePage {
|
||||
uint32_t inst[CODE_PAGE_SIZE / sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,13 +22,10 @@
|
||||
|
||||
namespace Dynarmic::IR {
|
||||
|
||||
Block::Block(const LocationDescriptor& location)
|
||||
: location{location},
|
||||
end_location{location},
|
||||
cond{Cond::AL}
|
||||
{
|
||||
|
||||
}
|
||||
Block::Block(LocationDescriptor location) noexcept
|
||||
: location{location}
|
||||
, end_location{location}
|
||||
{}
|
||||
|
||||
/// Prepends a new instruction to this basic block before the insertion point,
|
||||
/// handling any allocations necessary to do so.
|
||||
@@ -60,6 +57,21 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, s
|
||||
return instructions.insert_before(insertion_point, inst);
|
||||
}
|
||||
|
||||
void Block::Reset(LocationDescriptor location_) noexcept {
|
||||
mcl::intrusive_list<IR::Inst> tmp = {};
|
||||
instructions.swap(tmp);
|
||||
inlined_inst.clear();
|
||||
pooled_inst.clear();
|
||||
cond_failed.reset();
|
||||
location = location_;
|
||||
end_location = location_;
|
||||
cond = Cond::AL;
|
||||
terminal = Term::Invalid{};
|
||||
cond_failed_cycle_count = 0;
|
||||
cycle_count = 0;
|
||||
ASSERT(instructions.size() == 0);
|
||||
}
|
||||
|
||||
static std::string TerminalToString(const Terminal& terminal_variant) noexcept {
|
||||
struct : boost::static_visitor<std::string> {
|
||||
std::string operator()(const Term::Invalid&) const {
|
||||
@@ -123,11 +135,11 @@ std::string DumpBlock(const IR::Block& block) noexcept {
|
||||
case Type::A32ExtReg: return A32::ExtRegToString(arg.GetA32ExtRegRef());
|
||||
case Type::A64Reg: return A64::RegToString(arg.GetA64RegRef());
|
||||
case Type::A64Vec: return A64::VecToString(arg.GetA64VecRef());
|
||||
case Type::CoprocInfo: return fmt::format("#<coproc>");
|
||||
case Type::NZCVFlags: return fmt::format("#<NZCV flags>");
|
||||
case Type::Cond: return fmt::format("#<cond={}>", A32::CondToString(arg.GetCond()));
|
||||
case Type::Table: return fmt::format("#<table>");
|
||||
case Type::AccType: return fmt::format("#<acc-type={}>", u32(arg.GetAccType()));
|
||||
case Type::CoprocInfo: return fmt::format("$coproc{}", arg.GetCoprocInfo()[0]);
|
||||
case Type::NZCVFlags: return fmt::format("$nzcv");
|
||||
case Type::Cond: return fmt::format("$cond={}", A32::CondToString(arg.GetCond()));
|
||||
case Type::Table: return fmt::format("$table");
|
||||
case Type::AccType: return fmt::format("$acc-type={}", u32(arg.GetAccType()));
|
||||
default: return fmt::format("<unknown immediate type {}>", arg.GetType());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
using reverse_iterator = instruction_list_type::reverse_iterator;
|
||||
using const_reverse_iterator = instruction_list_type::const_reverse_iterator;
|
||||
|
||||
explicit Block(const LocationDescriptor& location);
|
||||
Block(LocationDescriptor location) noexcept;
|
||||
~Block() = default;
|
||||
Block(const Block&) = delete;
|
||||
Block& operator=(const Block&) = delete;
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
return PrependNewInst(instructions.end(), opcode, args);
|
||||
}
|
||||
iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args) noexcept;
|
||||
void Reset(LocationDescriptor location_) noexcept;
|
||||
|
||||
/// Gets a mutable reference to the instruction list for this basic block.
|
||||
inline instruction_list_type& Instructions() noexcept {
|
||||
@@ -140,18 +141,6 @@ public:
|
||||
return cycle_count;
|
||||
}
|
||||
|
||||
inline void Reset(LocationDescriptor location_) noexcept {
|
||||
inlined_inst.clear();
|
||||
pooled_inst.clear();
|
||||
cond_failed.reset();
|
||||
location = location_;
|
||||
terminal = Term::Invalid{};
|
||||
cond_failed_cycle_count = 0;
|
||||
cycle_count = 0;
|
||||
instruction_list_type tmp{};
|
||||
instructions.swap(tmp);
|
||||
}
|
||||
|
||||
/// "Hot cache" for small blocks so we don't call global allocator
|
||||
boost::container::static_vector<Inst, 30> inlined_inst;
|
||||
/// List of instructions in this block.
|
||||
@@ -165,7 +154,7 @@ public:
|
||||
/// Description of the end location of this block
|
||||
LocationDescriptor end_location;
|
||||
/// Conditional to pass in order to execute this block
|
||||
Cond cond;
|
||||
Cond cond = Cond::AL;
|
||||
/// Terminal instruction of this block.
|
||||
Terminal terminal = Term::Invalid{};
|
||||
/// Number of cycles this block takes to execute if the conditional fails.
|
||||
|
||||
@@ -1042,6 +1042,11 @@ static void FoldZeroExtendXToLong(IR::Inst& inst) {
|
||||
static void ConstantPropagation(IR::Block& block) {
|
||||
for (auto& inst : block.instructions) {
|
||||
auto const opcode = inst.GetOpcode();
|
||||
// skip NZCV so we dont end up discarding side effects :)
|
||||
// TODO(lizzie): hey stupid maybe fix the A64 codegen for folded constants AND
|
||||
// redirect the mfer properly?!??! just saying :)
|
||||
if (IR::MayGetNZCVFromOp(opcode) && inst.GetAssociatedPseudoOperation(IR::Opcode::GetNZCVFromOp))
|
||||
continue;
|
||||
switch (opcode) {
|
||||
case Op::LeastSignificantWord:
|
||||
FoldLeastSignificantWord(inst);
|
||||
|
||||
Reference in New Issue
Block a user