From 41af6ea645620dbd4669d3bae28d8908cbf22157 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 26 Nov 2025 22:28:19 +0100 Subject: [PATCH] [dynarmic] force devirtualisation of terminal handlers (#3033) Should result in very marginally small performance gains. Basically removes the deref of vtable for EmitX64 on lto builds, so in THEORY it should be better than having to defer w.r.t all terminal handlers. aka. we just like, inline them in one big function and keep CPU away from deference hell. Signed-off-by: lizzie lizzie@eden-emu.dev Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3033 Reviewed-by: Caio Oliveira Reviewed-by: MaranBr Co-authored-by: lizzie Co-committed-by: lizzie --- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 158 +++++++++--------- .../src/dynarmic/backend/x64/a32_emit_x64.h | 23 +-- .../src/dynarmic/backend/x64/a64_emit_x64.cpp | 139 ++++++++------- .../src/dynarmic/backend/x64/a64_emit_x64.h | 15 +- .../src/dynarmic/backend/x64/emit_x64.cpp | 26 +-- .../src/dynarmic/backend/x64/emit_x64.h | 18 +- 6 files changed, 181 insertions(+), 198 deletions(-) diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index 1191e443d1..56dec9725f 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -162,7 +162,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { if (conf.enable_cycle_counting) { EmitAddCycles(block.CycleCount()); } - EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); + EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.int3(); for (auto& deferred_emit : ctx.deferred_emits) { @@ -1124,26 +1124,9 @@ std::string A32EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr descriptor.FPSCR().Value()); } -void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool) { - ASSERT(A32::LocationDescriptor{terminal.next}.TFlag() == A32::LocationDescriptor{initial_location}.TFlag() && "Unimplemented"); - ASSERT(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag() && "Unimplemented"); - ASSERT(terminal.num_instructions == 1 && "Unimplemented"); - - code.mov(code.ABI_PARAM2.cvt32(), A32::LocationDescriptor{terminal.next}.PC()); - code.mov(code.ABI_PARAM3.cvt32(), 1); - code.mov(MJitStateReg(A32::Reg::PC), code.ABI_PARAM2.cvt32()); - code.SwitchMxcsrOnExit(); - Devirtualize<&A32::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code); - code.ReturnFromRunCode(true); // TODO: Check cycles -} - -void A32EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { - code.ReturnFromRunCode(); -} - void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) { auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 { - return static_cast(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32); + return u32(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32); }; const u32 old_upper = get_upper(old_location); @@ -1157,90 +1140,115 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat } } -void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - EmitSetUpperLocationDescriptor(terminal.next, initial_location); +namespace { +void EmitTerminalImpl(A32EmitX64& e, IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool) { + ASSERT(A32::LocationDescriptor{terminal.next}.TFlag() == A32::LocationDescriptor{initial_location}.TFlag() && "Unimplemented"); + ASSERT(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag() && "Unimplemented"); + ASSERT(terminal.num_instructions == 1 && "Unimplemented"); + + e.code.mov(e.code.ABI_PARAM2.cvt32(), A32::LocationDescriptor{terminal.next}.PC()); + e.code.mov(e.code.ABI_PARAM3.cvt32(), 1); + e.code.mov(MJitStateReg(A32::Reg::PC), e.code.ABI_PARAM2.cvt32()); + e.code.SwitchMxcsrOnExit(); + Devirtualize<&A32::UserCallbacks::InterpreterFallback>(e.conf.callbacks).EmitCall(e.code); + e.code.ReturnFromRunCode(true); // TODO: Check cycles +} + +void EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { + e.code.ReturnFromRunCode(); +} - if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) { - code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); - code.ReturnFromRunCode(); +void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + e.EmitSetUpperLocationDescriptor(terminal.next, initial_location); + if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) { + e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); + e.code.ReturnFromRunCode(); } else { - if (conf.enable_cycle_counting) { - code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); - patch_information[terminal.next].jg.push_back(code.getCurr()); - if (const auto next_bb = GetBasicBlock(terminal.next)) { - EmitPatchJg(terminal.next, next_bb->entrypoint); + if (e.conf.enable_cycle_counting) { + e.code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); + e.patch_information[terminal.next].jg.push_back(e.code.getCurr()); + if (const auto next_bb = e.GetBasicBlock(terminal.next)) { + e.EmitPatchJg(terminal.next, next_bb->entrypoint); } else { - EmitPatchJg(terminal.next); + e.EmitPatchJg(terminal.next); } } else { - code.cmp(dword[code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0); - patch_information[terminal.next].jz.push_back(code.getCurr()); - if (const auto next_bb = GetBasicBlock(terminal.next)) { - EmitPatchJz(terminal.next, next_bb->entrypoint); + e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0); + e.patch_information[terminal.next].jz.push_back(e.code.getCurr()); + if (const auto next_bb = e.GetBasicBlock(terminal.next)) { + e.EmitPatchJz(terminal.next, next_bb->entrypoint); } else { - EmitPatchJz(terminal.next); + e.EmitPatchJz(terminal.next); } } - code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); - PushRSBHelper(rax, rbx, terminal.next); - code.ForceReturnFromRunCode(); + e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); + e.PushRSBHelper(rax, rbx, terminal.next); + e.code.ForceReturnFromRunCode(); } } -void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - EmitSetUpperLocationDescriptor(terminal.next, initial_location); - - if (!conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) { - code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); - code.ReturnFromRunCode(); +void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + e.EmitSetUpperLocationDescriptor(terminal.next, initial_location); + if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) { + e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); + e.code.ReturnFromRunCode(); } else { - patch_information[terminal.next].jmp.push_back(code.getCurr()); - if (const auto next_bb = GetBasicBlock(terminal.next)) { - EmitPatchJmp(terminal.next, next_bb->entrypoint); + e.patch_information[terminal.next].jmp.push_back(e.code.getCurr()); + if (const auto next_bb = e.GetBasicBlock(terminal.next)) { + e.EmitPatchJmp(terminal.next, next_bb->entrypoint); } else { - EmitPatchJmp(terminal.next); + e.EmitPatchJmp(terminal.next); } } } -void A32EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { - if (!conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) { - code.ReturnFromRunCode(); - return; +void EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { + if (!e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) { + e.code.ReturnFromRunCode(); + } else { + e.code.jmp(e.terminal_handler_pop_rsb_hint); } - - code.jmp(terminal_handler_pop_rsb_hint); } -void A32EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { - if (!conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) { - code.ReturnFromRunCode(); - return; +void EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { + if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) { + e.code.ReturnFromRunCode(); + } else { + e.code.jmp(e.terminal_handler_fast_dispatch_hint); } - - code.jmp(terminal_handler_fast_dispatch_hint); } -void A32EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - Xbyak::Label pass = EmitCond(terminal.if_); - EmitTerminal(terminal.else_, initial_location, is_single_step); - code.L(pass); - EmitTerminal(terminal.then_, initial_location, is_single_step); +void EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + Xbyak::Label pass = e.EmitCond(terminal.if_); + e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.code.L(pass); + e.EmitTerminal(terminal.then_, initial_location, is_single_step); } -void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label fail; - code.cmp(code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0)); - code.jz(fail); - EmitTerminal(terminal.then_, initial_location, is_single_step); - code.L(fail); - EmitTerminal(terminal.else_, initial_location, is_single_step); + e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0)); + e.code.jz(fail); + e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.code.L(fail); + e.EmitTerminal(terminal.else_, initial_location, is_single_step); +} + +void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0); + e.code.jne(e.code.GetForceReturnFromRunCodeAddress()); + e.EmitTerminal(terminal.else_, initial_location, is_single_step); +} + +void EmitTerminalImpl(A32EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) { + UNREACHABLE(); +} } -void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - code.cmp(dword[code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0); - code.jne(code.GetForceReturnFromRunCodeAddress()); - EmitTerminal(terminal.else_, initial_location, is_single_step); +void A32EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { + boost::apply_visitor([this, initial_location, is_single_step](auto x) { + EmitTerminalImpl(*this, x, initial_location, is_single_step); + }, terminal); } void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h index c009931d4a..5ec78ff50e 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -55,7 +58,7 @@ public: void InvalidateCacheRanges(const boost::icl::interval_set& ranges); -protected: +//protected: void EmitCondPrelude(const A32EmitContext& ctx); struct FastDispatchEntry { @@ -109,15 +112,7 @@ protected: // Terminal instruction emitters void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location); - void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; + void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; // Patching void Unpatch(const IR::LocationDescriptor& target_desc) override; @@ -131,10 +126,10 @@ protected: BlockRangeInformation block_ranges; std::array fast_dispatch_table; ankerl::unordered_dense::map fastmem_patch_info; - std::set do_not_fastmem; - std::map, void (*)()> read_fallbacks; - std::map, void (*)()> write_fallbacks; - std::map, void (*)()> exclusive_write_fallbacks; + ankerl::unordered_dense::map, void (*)()> read_fallbacks; + ankerl::unordered_dense::map, void (*)()> write_fallbacks; + ankerl::unordered_dense::map, void (*)()> exclusive_write_fallbacks; + ankerl::unordered_dense::set do_not_fastmem; void (*memory_read_128)() = nullptr; // Dummy void (*memory_write_128)() = nullptr; // Dummy const void* terminal_handler_pop_rsb_hint; diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index 79ee56b3dd..7fd01daa29 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -149,7 +149,7 @@ finish_this_inst: if (conf.enable_cycle_counting) { EmitAddCycles(block.CycleCount()); } - EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); + EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.int3(); for (auto& deferred_emit : ctx.deferred_emits) { @@ -615,110 +615,121 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr descriptor.FPCR().Value()); } -void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor, bool) { - code.SwitchMxcsrOnExit(); - Devirtualize<&A64::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code, [&](RegList param) { - code.mov(param[0], A64::LocationDescriptor{terminal.next}.PC()); - code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], param[0]); - code.mov(param[1].cvt32(), terminal.num_instructions); +namespace { +void EmitTerminalImpl(A64EmitX64& e, IR::Term::Interpret terminal, IR::LocationDescriptor, bool) { + e.code.SwitchMxcsrOnExit(); + Devirtualize<&A64::UserCallbacks::InterpreterFallback>(e.conf.callbacks).EmitCall(e.code, [&](RegList param) { + e.code.mov(param[0], A64::LocationDescriptor{terminal.next}.PC()); + e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], param[0]); + e.code.mov(param[1].cvt32(), terminal.num_instructions); }); - code.ReturnFromRunCode(true); // TODO: Check cycles + e.code.ReturnFromRunCode(true); // TODO: Check cycles } -void A64EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { - code.ReturnFromRunCode(); +void EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { + e.code.ReturnFromRunCode(); } -void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) { +void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) { // Used for patches and linking - if (conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { - if (conf.enable_cycle_counting) { - code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); - patch_information[terminal.next].jg.push_back(code.getCurr()); - if (const auto next_bb = GetBasicBlock(terminal.next)) { - EmitPatchJg(terminal.next, next_bb->entrypoint); + if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { + if (e.conf.enable_cycle_counting) { + e.code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); + e.patch_information[terminal.next].jg.push_back(e.code.getCurr()); + if (const auto next_bb = e.GetBasicBlock(terminal.next)) { + e.EmitPatchJg(terminal.next, next_bb->entrypoint); } else { - EmitPatchJg(terminal.next); + e.EmitPatchJg(terminal.next); } } else { - code.cmp(dword[code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0); - patch_information[terminal.next].jz.push_back(code.getCurr()); - if (const auto next_bb = GetBasicBlock(terminal.next)) { - EmitPatchJz(terminal.next, next_bb->entrypoint); + e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0); + e.patch_information[terminal.next].jz.push_back(e.code.getCurr()); + if (const auto next_bb = e.GetBasicBlock(terminal.next)) { + e.EmitPatchJz(terminal.next, next_bb->entrypoint); } else { - EmitPatchJz(terminal.next); + e.EmitPatchJz(terminal.next); } } - code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); - code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); - code.ForceReturnFromRunCode(); + e.code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); + e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); + e.code.ForceReturnFromRunCode(); } else { - code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); - code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); - code.ReturnFromRunCode(); + e.code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); + e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); + e.code.ReturnFromRunCode(); } } -void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { - if (conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { - patch_information[terminal.next].jmp.push_back(code.getCurr()); - if (auto next_bb = GetBasicBlock(terminal.next)) { - EmitPatchJmp(terminal.next, next_bb->entrypoint); +void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { + if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { + e.patch_information[terminal.next].jmp.push_back(e.code.getCurr()); + if (auto next_bb = e.GetBasicBlock(terminal.next)) { + e.EmitPatchJmp(terminal.next, next_bb->entrypoint); } else { - EmitPatchJmp(terminal.next); + e.EmitPatchJmp(terminal.next); } } else { - code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); - code.mov(qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); - code.ReturnFromRunCode(); + e.code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); + e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); + e.code.ReturnFromRunCode(); } } -void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { - if (conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) { - code.jmp(terminal_handler_pop_rsb_hint); +void EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { + if (e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) { + e.code.jmp(e.terminal_handler_pop_rsb_hint); } else { - code.ReturnFromRunCode(); + e.code.ReturnFromRunCode(); } } -void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { - if (!conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) { - code.ReturnFromRunCode(); - return; +void EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { + if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) { + e.code.ReturnFromRunCode(); + } else { + e.code.jmp(e.terminal_handler_fast_dispatch_hint); } - - code.jmp(terminal_handler_fast_dispatch_hint); } -void A64EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +void EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { switch (terminal.if_) { case IR::Cond::AL: case IR::Cond::NV: - EmitTerminal(terminal.then_, initial_location, is_single_step); + e.EmitTerminal(terminal.then_, initial_location, is_single_step); break; default: - Xbyak::Label pass = EmitCond(terminal.if_); - EmitTerminal(terminal.else_, initial_location, is_single_step); - code.L(pass); - EmitTerminal(terminal.then_, initial_location, is_single_step); + Xbyak::Label pass = e.EmitCond(terminal.if_); + e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.code.L(pass); + e.EmitTerminal(terminal.then_, initial_location, is_single_step); break; } } -void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label fail; - code.cmp(code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0)); - code.jz(fail); - EmitTerminal(terminal.then_, initial_location, is_single_step); - code.L(fail); - EmitTerminal(terminal.else_, initial_location, is_single_step); + e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0)); + e.code.jz(fail); + e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.code.L(fail); + e.EmitTerminal(terminal.else_, initial_location, is_single_step); +} + +void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0); + e.code.jne(e.code.GetForceReturnFromRunCodeAddress()); + e.EmitTerminal(terminal.else_, initial_location, is_single_step); +} + +void EmitTerminalImpl(A64EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) { + UNREACHABLE(); +} } -void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - code.cmp(dword[code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0); - code.jne(code.GetForceReturnFromRunCodeAddress()); - EmitTerminal(terminal.else_, initial_location, is_single_step); +void A64EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { + boost::apply_visitor([this, initial_location, is_single_step](auto x) { + EmitTerminalImpl(*this, x, initial_location, is_single_step); + }, terminal); } void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h index a1917a3594..dd556e36ce 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -50,7 +53,7 @@ public: void InvalidateCacheRanges(const boost::icl::interval_set& ranges); -protected: +//protected: struct FastDispatchEntry { u64 location_descriptor = 0xFFFF'FFFF'FFFF'FFFFull; const void* code_ptr = nullptr; @@ -104,15 +107,7 @@ protected: void EmitExclusiveWriteMemoryInline(A64EmitContext& ctx, IR::Inst* inst); // Terminal instruction emitters - void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; - void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) override; + void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; // Patching void Unpatch(const IR::LocationDescriptor& target_desc) override; diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index 0160e8ab78..0ef14128ae 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp @@ -346,17 +346,6 @@ EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& de return block_desc; } -void EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - boost::apply_visitor([this, initial_location, is_single_step](auto x) { - using T = std::decay_t; - if constexpr (!std::is_same_v) { - this->EmitTerminalImpl(x, initial_location, is_single_step); - } else { - ASSERT(false && "Invalid terminal"); - } - }, terminal); -} - void EmitX64::Patch(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { const CodePtr save_code_ptr = code.getCurr(); const PatchInformation& patch_info = patch_information[target_desc]; @@ -399,20 +388,13 @@ void EmitX64::ClearCache() { void EmitX64::InvalidateBasicBlocks(const ankerl::unordered_dense::set& locations) { code.EnableWriting(); - SCOPE_EXIT { - code.DisableWriting(); - }; - for (const auto& descriptor : locations) { - const auto it = block_descriptors.find(descriptor); - if (it == block_descriptors.end()) { - continue; + if (auto const it = block_descriptors.find(descriptor); it != block_descriptors.end()) { + Unpatch(descriptor); + block_descriptors.erase(it); } - - Unpatch(descriptor); - - block_descriptors.erase(it); } + code.DisableWriting(); } } // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h index fbe749b2ab..1531d14968 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -92,7 +95,7 @@ public: /// Invalidates a selection of basic blocks. void InvalidateBasicBlocks(const ankerl::unordered_dense::set& locations); -protected: +//protected: // Microinstruction emitters #define OPCODE(name, type, ...) void Emit##name(EmitContext& ctx, IR::Inst* inst); #define A32OPC(...) @@ -111,18 +114,7 @@ protected: void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target); void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc); - - // Terminal instruction emitters - void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); - virtual void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; - virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; + virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0; // Patching struct PatchInformation {