From aac1f6ab1b097ef610ab87baf0827b71527c4c79 Mon Sep 17 00:00:00 2001 From: merry Date: Sun, 3 Apr 2022 15:37:15 +0100 Subject: [PATCH] Implement halt_reason * Provide reason for halting and atomically update this. * Allow user to specify a halt reason and return this information on halt. * Check if halt was requested prior to starting execution. --- src/dynarmic/backend/x64/a32_emit_x64.cpp | 2 +- src/dynarmic/backend/x64/a32_interface.cpp | 35 ++++++++------ src/dynarmic/backend/x64/a32_jitstate.h | 2 +- src/dynarmic/backend/x64/a64_emit_x64.cpp | 2 +- src/dynarmic/backend/x64/a64_interface.cpp | 38 +++++++++------- src/dynarmic/backend/x64/a64_jitstate.h | 2 +- src/dynarmic/backend/x64/block_of_code.cpp | 28 +++++++++--- src/dynarmic/backend/x64/block_of_code.h | 7 +-- src/dynarmic/backend/x64/jitstate_info.h | 4 +- src/dynarmic/interface/A32/a32.h | 7 +-- src/dynarmic/interface/A64/a64.h | 9 ++-- src/dynarmic/interface/halt_reason.h | 53 ++++++++++++++++++++++ 12 files changed, 136 insertions(+), 53 deletions(-) create mode 100644 src/dynarmic/interface/halt_reason.h diff --git a/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/backend/x64/a32_emit_x64.cpp index d85da513..3e71425a 100644 --- a/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -1206,7 +1206,7 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescr } void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - code.cmp(code.byte[r15 + offsetof(A32JitState, halt_requested)], u8(0)); + code.cmp(dword[r15 + offsetof(A32JitState, halt_reason)], 0); code.jne(code.GetForceReturnFromRunCodeAddress()); EmitTerminal(terminal.else_, initial_location, is_single_step); } diff --git a/src/dynarmic/backend/x64/a32_interface.cpp b/src/dynarmic/backend/x64/a32_interface.cpp index 3d7997b4..43ea002b 100644 --- a/src/dynarmic/backend/x64/a32_interface.cpp +++ b/src/dynarmic/backend/x64/a32_interface.cpp @@ -16,6 +16,7 @@ #include "dynarmic/backend/x64/devirtualize.h" #include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/common/assert.h" +#include "dynarmic/common/atomic.h" #include "dynarmic/common/cast_util.h" #include "dynarmic/common/common_types.h" #include "dynarmic/common/scope_exit.h" @@ -76,7 +77,7 @@ struct Jit::Impl { boost::icl::interval_set invalid_cache_ranges; bool invalidate_entire_cache = false; - void Execute() { + HaltReason Execute() { const CodePtr current_codeptr = [this] { // RSB optimization const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask; @@ -88,11 +89,15 @@ struct Jit::Impl { return GetCurrentBlock(); }(); - block_of_code.RunCode(&jit_state, current_codeptr); + return block_of_code.RunCode(&jit_state, current_codeptr); } - void Step() { - block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); + HaltReason Step() { + return block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); + } + + void HaltExecution(HaltReason hr) { + Atomic::Or(&jit_state.halt_reason, static_cast(hr)); } void ClearExclusiveState() { @@ -123,7 +128,7 @@ struct Jit::Impl { void RequestCacheInvalidation() { if (jit_interface->is_executing) { - jit_state.halt_requested = true; + HaltExecution(HaltReason::CacheInvalidation); return; } @@ -182,28 +187,28 @@ Jit::Jit(UserConfig conf) Jit::~Jit() = default; -void Jit::Run() { +HaltReason Jit::Run() { ASSERT(!is_executing); is_executing = true; SCOPE_EXIT { this->is_executing = false; }; - impl->jit_state.halt_requested = false; - - impl->Execute(); + const HaltReason hr = impl->Execute(); impl->PerformCacheInvalidation(); + + return hr; } -void Jit::Step() { +HaltReason Jit::Step() { ASSERT(!is_executing); is_executing = true; SCOPE_EXIT { this->is_executing = false; }; - impl->jit_state.halt_requested = true; - - impl->Step(); + const HaltReason hr = impl->Step(); impl->PerformCacheInvalidation(); + + return hr; } void Jit::ClearCache() { @@ -221,8 +226,8 @@ void Jit::Reset() { impl->jit_state = {}; } -void Jit::HaltExecution() { - impl->jit_state.halt_requested = true; +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); } void Jit::ClearExclusiveState() { diff --git a/src/dynarmic/backend/x64/a32_jitstate.h b/src/dynarmic/backend/x64/a32_jitstate.h index fa39e7e9..14cd5764 100644 --- a/src/dynarmic/backend/x64/a32_jitstate.h +++ b/src/dynarmic/backend/x64/a32_jitstate.h @@ -40,7 +40,7 @@ struct A32JitState { // For internal use (See: BlockOfCode::RunCode) u32 guest_MXCSR = 0x00001f80; u32 asimd_MXCSR = 0x00009fc0; - bool halt_requested = false; + volatile u32 halt_reason = 0; // Exclusive state u32 exclusive_state = 0; diff --git a/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/backend/x64/a64_emit_x64.cpp index 8a82fc4e..98769e92 100644 --- a/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -691,7 +691,7 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescr } void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - code.cmp(code.byte[r15 + offsetof(A64JitState, halt_requested)], u8(0)); + code.cmp(dword[r15 + offsetof(A64JitState, halt_reason)], 0); code.jne(code.GetForceReturnFromRunCodeAddress()); EmitTerminal(terminal.else_, initial_location, is_single_step); } diff --git a/src/dynarmic/backend/x64/a64_interface.cpp b/src/dynarmic/backend/x64/a64_interface.cpp index abcc2727..e19f9dba 100644 --- a/src/dynarmic/backend/x64/a64_interface.cpp +++ b/src/dynarmic/backend/x64/a64_interface.cpp @@ -15,6 +15,7 @@ #include "dynarmic/backend/x64/devirtualize.h" #include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/common/assert.h" +#include "dynarmic/common/atomic.h" #include "dynarmic/common/scope_exit.h" #include "dynarmic/common/x64_disassemble.h" #include "dynarmic/frontend/A64/translate/a64_translate.h" @@ -63,13 +64,12 @@ public: ~Impl() = default; - void Run() { + HaltReason Run() { ASSERT(!is_executing); PerformRequestedCacheInvalidation(); is_executing = true; SCOPE_EXIT { this->is_executing = false; }; - jit_state.halt_requested = false; // TODO: Check code alignment @@ -83,29 +83,33 @@ public: return GetCurrentBlock(); }(); - block_of_code.RunCode(&jit_state, current_code_ptr); + + const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr); PerformRequestedCacheInvalidation(); + + return hr; } - void Step() { + HaltReason Step() { ASSERT(!is_executing); PerformRequestedCacheInvalidation(); is_executing = true; SCOPE_EXIT { this->is_executing = false; }; - jit_state.halt_requested = true; - block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); + const HaltReason hr = block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); PerformRequestedCacheInvalidation(); + + return hr; } void ClearCache() { std::unique_lock lock{invalidation_mutex}; invalidate_entire_cache = true; if (is_executing) { - jit_state.halt_requested = true; + HaltExecution(HaltReason::CacheInvalidation); } } @@ -115,7 +119,7 @@ public: const auto range = boost::icl::discrete_interval::closed(start_address, end_address); invalid_cache_ranges.add(range); if (is_executing) { - jit_state.halt_requested = true; + HaltExecution(HaltReason::CacheInvalidation); } } @@ -124,8 +128,8 @@ public: jit_state = {}; } - void HaltExecution() { - jit_state.halt_requested = true; + void HaltExecution(HaltReason hr) { + Atomic::Or(&jit_state.halt_reason, static_cast(hr)); } u64 GetSP() const { @@ -279,7 +283,7 @@ private: void RequestCacheInvalidation() { if (is_executing) { - jit_state.halt_requested = true; + HaltExecution(HaltReason::CacheInvalidation); return; } @@ -321,12 +325,12 @@ Jit::Jit(UserConfig conf) Jit::~Jit() = default; -void Jit::Run() { - impl->Run(); +HaltReason Jit::Run() { + return impl->Run(); } -void Jit::Step() { - impl->Step(); +HaltReason Jit::Step() { + return impl->Step(); } void Jit::ClearCache() { @@ -341,8 +345,8 @@ void Jit::Reset() { impl->Reset(); } -void Jit::HaltExecution() { - impl->HaltExecution(); +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); } u64 Jit::GetSP() const { diff --git a/src/dynarmic/backend/x64/a64_jitstate.h b/src/dynarmic/backend/x64/a64_jitstate.h index a0e20cff..12479461 100644 --- a/src/dynarmic/backend/x64/a64_jitstate.h +++ b/src/dynarmic/backend/x64/a64_jitstate.h @@ -43,7 +43,7 @@ struct A64JitState { // For internal use (See: BlockOfCode::RunCode) u32 guest_MXCSR = 0x00001f80; u32 asimd_MXCSR = 0x00009fc0; - bool halt_requested = false; + volatile u32 halt_reason = 0; // Exclusive state static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull; diff --git a/src/dynarmic/backend/x64/block_of_code.cpp b/src/dynarmic/backend/x64/block_of_code.cpp index 4fe5767d..8fa303bc 100644 --- a/src/dynarmic/backend/x64/block_of_code.cpp +++ b/src/dynarmic/backend/x64/block_of_code.cpp @@ -201,12 +201,12 @@ size_t BlockOfCode::SpaceRemaining() const { return std::min(reinterpret_cast(far_code_begin) - current_near_ptr, &top_[maxSize_] - current_far_ptr); } -void BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const { - run_code(jit_state, code_ptr); +HaltReason BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const { + return run_code(jit_state, code_ptr); } -void BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const { - step_code(jit_state, code_ptr); +HaltReason BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const { + return step_code(jit_state, code_ptr); } void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) { @@ -224,6 +224,8 @@ void BlockOfCode::ForceReturnFromRunCode(bool mxcsr_already_exited) { } void BlockOfCode::GenRunCode(std::function rcp) { + Xbyak::Label return_to_caller, return_to_caller_mxcsr_already_exited; + align(); run_code = getCurr(); @@ -242,6 +244,9 @@ void BlockOfCode::GenRunCode(std::function rcp) { rcp(*this); + cmp(dword[r15 + jsi.offsetof_halt_reason], 0); + jne(return_to_caller_mxcsr_already_exited, T_NEAR); + SwitchMxcsrOnEntry(); jmp(rbx); @@ -257,16 +262,21 @@ void BlockOfCode::GenRunCode(std::function rcp) { rcp(*this); + cmp(dword[r15 + jsi.offsetof_halt_reason], 0); + jne(return_to_caller_mxcsr_already_exited, T_NEAR); + lock(); + or_(dword[r15 + jsi.offsetof_halt_reason], static_cast(HaltReason::Step)); + SwitchMxcsrOnEntry(); jmp(ABI_PARAM2); // Dispatcher loop - Xbyak::Label return_to_caller, return_to_caller_mxcsr_already_exited; - align(); return_from_run_code[0] = getCurr(); + cmp(dword[r15 + jsi.offsetof_halt_reason], 0); + jne(return_to_caller); cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); jng(return_to_caller); cb.LookupBlock->EmitCall(*this); @@ -275,6 +285,8 @@ void BlockOfCode::GenRunCode(std::function rcp) { align(); return_from_run_code[MXCSR_ALREADY_EXITED] = getCurr(); + cmp(dword[r15 + jsi.offsetof_halt_reason], 0); + jne(return_to_caller_mxcsr_already_exited); cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); jng(return_to_caller_mxcsr_already_exited); SwitchMxcsrOnEntry(); @@ -296,6 +308,10 @@ void BlockOfCode::GenRunCode(std::function rcp) { sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]); }); + xor_(eax, eax); + lock(); + xchg(dword[r15 + jsi.offsetof_halt_reason], eax); + ABI_PopCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout)); ret(); diff --git a/src/dynarmic/backend/x64/block_of_code.h b/src/dynarmic/backend/x64/block_of_code.h index 17aa9cd7..f21b5a90 100644 --- a/src/dynarmic/backend/x64/block_of_code.h +++ b/src/dynarmic/backend/x64/block_of_code.h @@ -20,6 +20,7 @@ #include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/common/cast_util.h" #include "dynarmic/common/common_types.h" +#include "dynarmic/interface/halt_reason.h" namespace Dynarmic::Backend::X64 { @@ -50,9 +51,9 @@ public: size_t SpaceRemaining() const; /// Runs emulated code from code_ptr. - void RunCode(void* jit_state, CodePtr code_ptr) const; + HaltReason RunCode(void* jit_state, CodePtr code_ptr) const; /// Runs emulated code from code_ptr for a single cycle. - void StepCode(void* jit_state, CodePtr code_ptr) const; + HaltReason StepCode(void* jit_state, CodePtr code_ptr) const; /// Code emitter: Returns to dispatcher void ReturnFromRunCode(bool mxcsr_already_exited = false); /// Code emitter: Returns to dispatcher, forces return to host @@ -183,7 +184,7 @@ private: CodePtr near_code_ptr; CodePtr far_code_ptr; - using RunCodeFuncType = void (*)(void*, CodePtr); + using RunCodeFuncType = HaltReason (*)(void*, CodePtr); RunCodeFuncType run_code = nullptr; RunCodeFuncType step_code = nullptr; static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0; diff --git a/src/dynarmic/backend/x64/jitstate_info.h b/src/dynarmic/backend/x64/jitstate_info.h index e8718886..654b3f04 100644 --- a/src/dynarmic/backend/x64/jitstate_info.h +++ b/src/dynarmic/backend/x64/jitstate_info.h @@ -20,7 +20,8 @@ struct JitStateInfo { , offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs)) , offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv)) , offsetof_fpsr_exc(offsetof(JitStateType, fpsr_exc)) - , offsetof_fpsr_qc(offsetof(JitStateType, fpsr_qc)) {} + , offsetof_fpsr_qc(offsetof(JitStateType, fpsr_qc)) + , offsetof_halt_reason(offsetof(JitStateType, halt_reason)) {} const size_t offsetof_guest_MXCSR; const size_t offsetof_asimd_MXCSR; @@ -31,6 +32,7 @@ struct JitStateInfo { const size_t offsetof_cpsr_nzcv; const size_t offsetof_fpsr_exc; const size_t offsetof_fpsr_qc; + const size_t offsetof_halt_reason; }; } // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/interface/A32/a32.h b/src/dynarmic/interface/A32/a32.h index c36f9fcb..48a5c342 100644 --- a/src/dynarmic/interface/A32/a32.h +++ b/src/dynarmic/interface/A32/a32.h @@ -12,6 +12,7 @@ #include #include "dynarmic/interface/A32/config.h" +#include "dynarmic/interface/halt_reason.h" namespace Dynarmic { namespace A32 { @@ -27,13 +28,13 @@ public: * Runs the emulated CPU. * Cannot be recursively called. */ - void Run(); + HaltReason Run(); /** * Steps the emulated CPU. * Cannot be recursively called. */ - void Step(); + HaltReason Step(); /** * Clears the code cache of all compiled code. @@ -58,7 +59,7 @@ public: * Stops execution in Jit::Run. * Can only be called from a callback. */ - void HaltExecution(); + void HaltExecution(HaltReason hr = HaltReason::UserDefined1); /// View and modify registers. std::array& Regs(); diff --git a/src/dynarmic/interface/A64/a64.h b/src/dynarmic/interface/A64/a64.h index d908fc21..b5e008ce 100644 --- a/src/dynarmic/interface/A64/a64.h +++ b/src/dynarmic/interface/A64/a64.h @@ -13,6 +13,7 @@ #include #include "dynarmic/interface/A64/config.h" +#include "dynarmic/interface/halt_reason.h" namespace Dynarmic { namespace A64 { @@ -28,13 +29,13 @@ public: * Runs the emulated CPU. * Cannot be recursively called. */ - void Run(); + HaltReason Run(); /** * Step the emulated CPU for one instruction. * Cannot be recursively called. */ - void Step(); + HaltReason Step(); /** * Clears the code cache of all compiled code. @@ -59,7 +60,7 @@ public: * Stops execution in Jit::Run. * Can only be called from a callback. */ - void HaltExecution(); + void HaltExecution(HaltReason hr = HaltReason::UserDefined1); /// Read Stack Pointer std::uint64_t GetSP() const; @@ -118,7 +119,7 @@ public: /// Debugging: Dump a disassembly all of compiled code to the console. void DumpDisassembly() const; - /* + /* * Disassemble the instructions following the current pc and return * the resulting instructions as a vector of their string representations. */ diff --git a/src/dynarmic/interface/halt_reason.h b/src/dynarmic/interface/halt_reason.h new file mode 100644 index 00000000..28da2b13 --- /dev/null +++ b/src/dynarmic/interface/halt_reason.h @@ -0,0 +1,53 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2020 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +namespace Dynarmic { + +enum class HaltReason : std::uint32_t { + Step = 0x00000001, + CacheInvalidation = 0x00000002, + UserDefined1 = 0x01000000, + UserDefined2 = 0x02000000, + UserDefined3 = 0x04000000, + UserDefined4 = 0x08000000, + UserDefined5 = 0x10000000, + UserDefined6 = 0x20000000, + UserDefined7 = 0x40000000, + UserDefined8 = 0x80000000, +}; + +constexpr HaltReason operator~(HaltReason hr) { + return static_cast(~static_cast(hr)); +} + +constexpr HaltReason operator|(HaltReason hr1, HaltReason hr2) { + return static_cast(static_cast(hr1) | static_cast(hr2)); +} + +constexpr HaltReason operator&(HaltReason hr1, HaltReason hr2) { + return static_cast(static_cast(hr1) & static_cast(hr2)); +} + +constexpr HaltReason operator|=(HaltReason& result, HaltReason hr) { + return result = (result | hr); +} + +constexpr HaltReason operator&=(HaltReason& result, HaltReason hr) { + return result = (result & hr); +} + +constexpr bool operator!(HaltReason hr) { + return static_cast(hr) == 0; +} + +constexpr bool Has(HaltReason hr1, HaltReason hr2) { + return (static_cast(hr1) & static_cast(hr2)) != 0; +} + +} // namespace Dynarmic