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.
This commit is contained in:
merry 2022-04-03 15:37:15 +01:00
parent 116297ccd5
commit aac1f6ab1b
12 changed files with 136 additions and 53 deletions

View file

@ -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) { 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()); code.jne(code.GetForceReturnFromRunCodeAddress());
EmitTerminal(terminal.else_, initial_location, is_single_step); EmitTerminal(terminal.else_, initial_location, is_single_step);
} }

View file

@ -16,6 +16,7 @@
#include "dynarmic/backend/x64/devirtualize.h" #include "dynarmic/backend/x64/devirtualize.h"
#include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/backend/x64/jitstate_info.h"
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/common/cast_util.h" #include "dynarmic/common/cast_util.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"
#include "dynarmic/common/scope_exit.h" #include "dynarmic/common/scope_exit.h"
@ -76,7 +77,7 @@ struct Jit::Impl {
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;
void Execute() { HaltReason Execute() {
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;
@ -88,11 +89,15 @@ struct Jit::Impl {
return GetCurrentBlock(); return GetCurrentBlock();
}(); }();
block_of_code.RunCode(&jit_state, current_codeptr); return block_of_code.RunCode(&jit_state, current_codeptr);
} }
void Step() { HaltReason Step() {
block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); return block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
}
void HaltExecution(HaltReason hr) {
Atomic::Or(&jit_state.halt_reason, static_cast<u32>(hr));
} }
void ClearExclusiveState() { void ClearExclusiveState() {
@ -123,7 +128,7 @@ struct Jit::Impl {
void RequestCacheInvalidation() { void RequestCacheInvalidation() {
if (jit_interface->is_executing) { if (jit_interface->is_executing) {
jit_state.halt_requested = true; HaltExecution(HaltReason::CacheInvalidation);
return; return;
} }
@ -182,28 +187,28 @@ Jit::Jit(UserConfig conf)
Jit::~Jit() = default; Jit::~Jit() = default;
void Jit::Run() { HaltReason Jit::Run() {
ASSERT(!is_executing); ASSERT(!is_executing);
is_executing = true; is_executing = true;
SCOPE_EXIT { this->is_executing = false; }; SCOPE_EXIT { this->is_executing = false; };
impl->jit_state.halt_requested = false; const HaltReason hr = impl->Execute();
impl->Execute();
impl->PerformCacheInvalidation(); impl->PerformCacheInvalidation();
return hr;
} }
void Jit::Step() { HaltReason Jit::Step() {
ASSERT(!is_executing); ASSERT(!is_executing);
is_executing = true; is_executing = true;
SCOPE_EXIT { this->is_executing = false; }; SCOPE_EXIT { this->is_executing = false; };
impl->jit_state.halt_requested = true; const HaltReason hr = impl->Step();
impl->Step();
impl->PerformCacheInvalidation(); impl->PerformCacheInvalidation();
return hr;
} }
void Jit::ClearCache() { void Jit::ClearCache() {
@ -221,8 +226,8 @@ void Jit::Reset() {
impl->jit_state = {}; impl->jit_state = {};
} }
void Jit::HaltExecution() { void Jit::HaltExecution(HaltReason hr) {
impl->jit_state.halt_requested = true; impl->HaltExecution(hr);
} }
void Jit::ClearExclusiveState() { void Jit::ClearExclusiveState() {

View file

@ -40,7 +40,7 @@ struct A32JitState {
// For internal use (See: BlockOfCode::RunCode) // For internal use (See: BlockOfCode::RunCode)
u32 guest_MXCSR = 0x00001f80; u32 guest_MXCSR = 0x00001f80;
u32 asimd_MXCSR = 0x00009fc0; u32 asimd_MXCSR = 0x00009fc0;
bool halt_requested = false; volatile u32 halt_reason = 0;
// Exclusive state // Exclusive state
u32 exclusive_state = 0; u32 exclusive_state = 0;

View file

@ -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) { 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()); code.jne(code.GetForceReturnFromRunCodeAddress());
EmitTerminal(terminal.else_, initial_location, is_single_step); EmitTerminal(terminal.else_, initial_location, is_single_step);
} }

View file

@ -15,6 +15,7 @@
#include "dynarmic/backend/x64/devirtualize.h" #include "dynarmic/backend/x64/devirtualize.h"
#include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/backend/x64/jitstate_info.h"
#include "dynarmic/common/assert.h" #include "dynarmic/common/assert.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/common/scope_exit.h" #include "dynarmic/common/scope_exit.h"
#include "dynarmic/common/x64_disassemble.h" #include "dynarmic/common/x64_disassemble.h"
#include "dynarmic/frontend/A64/translate/a64_translate.h" #include "dynarmic/frontend/A64/translate/a64_translate.h"
@ -63,13 +64,12 @@ public:
~Impl() = default; ~Impl() = default;
void Run() { HaltReason Run() {
ASSERT(!is_executing); ASSERT(!is_executing);
PerformRequestedCacheInvalidation(); PerformRequestedCacheInvalidation();
is_executing = true; is_executing = true;
SCOPE_EXIT { this->is_executing = false; }; SCOPE_EXIT { this->is_executing = false; };
jit_state.halt_requested = false;
// TODO: Check code alignment // TODO: Check code alignment
@ -83,29 +83,33 @@ public:
return GetCurrentBlock(); return GetCurrentBlock();
}(); }();
block_of_code.RunCode(&jit_state, current_code_ptr);
const HaltReason hr = block_of_code.RunCode(&jit_state, current_code_ptr);
PerformRequestedCacheInvalidation(); PerformRequestedCacheInvalidation();
return hr;
} }
void Step() { HaltReason Step() {
ASSERT(!is_executing); ASSERT(!is_executing);
PerformRequestedCacheInvalidation(); PerformRequestedCacheInvalidation();
is_executing = true; is_executing = true;
SCOPE_EXIT { this->is_executing = false; }; 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(); PerformRequestedCacheInvalidation();
return hr;
} }
void ClearCache() { void ClearCache() {
std::unique_lock lock{invalidation_mutex}; std::unique_lock lock{invalidation_mutex};
invalidate_entire_cache = true; invalidate_entire_cache = true;
if (is_executing) { if (is_executing) {
jit_state.halt_requested = true; HaltExecution(HaltReason::CacheInvalidation);
} }
} }
@ -115,7 +119,7 @@ public:
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);
if (is_executing) { if (is_executing) {
jit_state.halt_requested = true; HaltExecution(HaltReason::CacheInvalidation);
} }
} }
@ -124,8 +128,8 @@ public:
jit_state = {}; jit_state = {};
} }
void HaltExecution() { void HaltExecution(HaltReason hr) {
jit_state.halt_requested = true; Atomic::Or(&jit_state.halt_reason, static_cast<u32>(hr));
} }
u64 GetSP() const { u64 GetSP() const {
@ -279,7 +283,7 @@ private:
void RequestCacheInvalidation() { void RequestCacheInvalidation() {
if (is_executing) { if (is_executing) {
jit_state.halt_requested = true; HaltExecution(HaltReason::CacheInvalidation);
return; return;
} }
@ -321,12 +325,12 @@ Jit::Jit(UserConfig conf)
Jit::~Jit() = default; Jit::~Jit() = default;
void Jit::Run() { HaltReason Jit::Run() {
impl->Run(); return impl->Run();
} }
void Jit::Step() { HaltReason Jit::Step() {
impl->Step(); return impl->Step();
} }
void Jit::ClearCache() { void Jit::ClearCache() {
@ -341,8 +345,8 @@ void Jit::Reset() {
impl->Reset(); impl->Reset();
} }
void Jit::HaltExecution() { void Jit::HaltExecution(HaltReason hr) {
impl->HaltExecution(); impl->HaltExecution(hr);
} }
u64 Jit::GetSP() const { u64 Jit::GetSP() const {

View file

@ -43,7 +43,7 @@ struct A64JitState {
// For internal use (See: BlockOfCode::RunCode) // For internal use (See: BlockOfCode::RunCode)
u32 guest_MXCSR = 0x00001f80; u32 guest_MXCSR = 0x00001f80;
u32 asimd_MXCSR = 0x00009fc0; u32 asimd_MXCSR = 0x00009fc0;
bool halt_requested = false; volatile u32 halt_reason = 0;
// Exclusive state // Exclusive state
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull; static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;

View file

@ -201,12 +201,12 @@ size_t BlockOfCode::SpaceRemaining() const {
return std::min(reinterpret_cast<const u8*>(far_code_begin) - current_near_ptr, &top_[maxSize_] - current_far_ptr); return std::min(reinterpret_cast<const u8*>(far_code_begin) - current_near_ptr, &top_[maxSize_] - current_far_ptr);
} }
void BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const { HaltReason BlockOfCode::RunCode(void* jit_state, CodePtr code_ptr) const {
run_code(jit_state, code_ptr); return run_code(jit_state, code_ptr);
} }
void BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const { HaltReason BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const {
step_code(jit_state, code_ptr); return step_code(jit_state, code_ptr);
} }
void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) { void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) {
@ -224,6 +224,8 @@ void BlockOfCode::ForceReturnFromRunCode(bool mxcsr_already_exited) {
} }
void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) { void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
Xbyak::Label return_to_caller, return_to_caller_mxcsr_already_exited;
align(); align();
run_code = getCurr<RunCodeFuncType>(); run_code = getCurr<RunCodeFuncType>();
@ -242,6 +244,9 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
rcp(*this); rcp(*this);
cmp(dword[r15 + jsi.offsetof_halt_reason], 0);
jne(return_to_caller_mxcsr_already_exited, T_NEAR);
SwitchMxcsrOnEntry(); SwitchMxcsrOnEntry();
jmp(rbx); jmp(rbx);
@ -257,16 +262,21 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
rcp(*this); 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<u32>(HaltReason::Step));
SwitchMxcsrOnEntry(); SwitchMxcsrOnEntry();
jmp(ABI_PARAM2); jmp(ABI_PARAM2);
// Dispatcher loop // Dispatcher loop
Xbyak::Label return_to_caller, return_to_caller_mxcsr_already_exited;
align(); align();
return_from_run_code[0] = getCurr<const void*>(); return_from_run_code[0] = getCurr<const void*>();
cmp(dword[r15 + jsi.offsetof_halt_reason], 0);
jne(return_to_caller);
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
jng(return_to_caller); jng(return_to_caller);
cb.LookupBlock->EmitCall(*this); cb.LookupBlock->EmitCall(*this);
@ -275,6 +285,8 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
align(); align();
return_from_run_code[MXCSR_ALREADY_EXITED] = getCurr<const void*>(); return_from_run_code[MXCSR_ALREADY_EXITED] = getCurr<const void*>();
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); cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
jng(return_to_caller_mxcsr_already_exited); jng(return_to_caller_mxcsr_already_exited);
SwitchMxcsrOnEntry(); SwitchMxcsrOnEntry();
@ -296,6 +308,10 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]); 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)); ABI_PopCalleeSaveRegistersAndAdjustStack(*this, sizeof(StackLayout));
ret(); ret();

View file

@ -20,6 +20,7 @@
#include "dynarmic/backend/x64/jitstate_info.h" #include "dynarmic/backend/x64/jitstate_info.h"
#include "dynarmic/common/cast_util.h" #include "dynarmic/common/cast_util.h"
#include "dynarmic/common/common_types.h" #include "dynarmic/common/common_types.h"
#include "dynarmic/interface/halt_reason.h"
namespace Dynarmic::Backend::X64 { namespace Dynarmic::Backend::X64 {
@ -50,9 +51,9 @@ public:
size_t SpaceRemaining() const; size_t SpaceRemaining() const;
/// Runs emulated code from code_ptr. /// 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. /// 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 /// Code emitter: Returns to dispatcher
void ReturnFromRunCode(bool mxcsr_already_exited = false); void ReturnFromRunCode(bool mxcsr_already_exited = false);
/// Code emitter: Returns to dispatcher, forces return to host /// Code emitter: Returns to dispatcher, forces return to host
@ -183,7 +184,7 @@ private:
CodePtr near_code_ptr; CodePtr near_code_ptr;
CodePtr far_code_ptr; CodePtr far_code_ptr;
using RunCodeFuncType = void (*)(void*, CodePtr); using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
RunCodeFuncType run_code = nullptr; RunCodeFuncType run_code = nullptr;
RunCodeFuncType step_code = nullptr; RunCodeFuncType step_code = nullptr;
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0; static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;

View file

@ -20,7 +20,8 @@ struct JitStateInfo {
, offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs)) , offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs))
, offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv)) , offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv))
, offsetof_fpsr_exc(offsetof(JitStateType, fpsr_exc)) , 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_guest_MXCSR;
const size_t offsetof_asimd_MXCSR; const size_t offsetof_asimd_MXCSR;
@ -31,6 +32,7 @@ struct JitStateInfo {
const size_t offsetof_cpsr_nzcv; const size_t offsetof_cpsr_nzcv;
const size_t offsetof_fpsr_exc; const size_t offsetof_fpsr_exc;
const size_t offsetof_fpsr_qc; const size_t offsetof_fpsr_qc;
const size_t offsetof_halt_reason;
}; };
} // namespace Dynarmic::Backend::X64 } // namespace Dynarmic::Backend::X64

View file

@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include "dynarmic/interface/A32/config.h" #include "dynarmic/interface/A32/config.h"
#include "dynarmic/interface/halt_reason.h"
namespace Dynarmic { namespace Dynarmic {
namespace A32 { namespace A32 {
@ -27,13 +28,13 @@ public:
* Runs the emulated CPU. * Runs the emulated CPU.
* Cannot be recursively called. * Cannot be recursively called.
*/ */
void Run(); HaltReason Run();
/** /**
* Steps the emulated CPU. * Steps the emulated CPU.
* Cannot be recursively called. * Cannot be recursively called.
*/ */
void Step(); HaltReason Step();
/** /**
* Clears the code cache of all compiled code. * Clears the code cache of all compiled code.
@ -58,7 +59,7 @@ public:
* Stops execution in Jit::Run. * Stops execution in Jit::Run.
* Can only be called from a callback. * Can only be called from a callback.
*/ */
void HaltExecution(); void HaltExecution(HaltReason hr = HaltReason::UserDefined1);
/// View and modify registers. /// View and modify registers.
std::array<std::uint32_t, 16>& Regs(); std::array<std::uint32_t, 16>& Regs();

View file

@ -13,6 +13,7 @@
#include <vector> #include <vector>
#include "dynarmic/interface/A64/config.h" #include "dynarmic/interface/A64/config.h"
#include "dynarmic/interface/halt_reason.h"
namespace Dynarmic { namespace Dynarmic {
namespace A64 { namespace A64 {
@ -28,13 +29,13 @@ public:
* Runs the emulated CPU. * Runs the emulated CPU.
* Cannot be recursively called. * Cannot be recursively called.
*/ */
void Run(); HaltReason Run();
/** /**
* Step the emulated CPU for one instruction. * Step the emulated CPU for one instruction.
* Cannot be recursively called. * Cannot be recursively called.
*/ */
void Step(); HaltReason Step();
/** /**
* Clears the code cache of all compiled code. * Clears the code cache of all compiled code.
@ -59,7 +60,7 @@ public:
* Stops execution in Jit::Run. * Stops execution in Jit::Run.
* Can only be called from a callback. * Can only be called from a callback.
*/ */
void HaltExecution(); void HaltExecution(HaltReason hr = HaltReason::UserDefined1);
/// Read Stack Pointer /// Read Stack Pointer
std::uint64_t GetSP() const; std::uint64_t GetSP() const;
@ -118,7 +119,7 @@ public:
/// Debugging: Dump a disassembly all of compiled code to the console. /// Debugging: Dump a disassembly all of compiled code to the console.
void DumpDisassembly() const; void DumpDisassembly() const;
/* /*
* Disassemble the instructions following the current pc and return * 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.
*/ */

View file

@ -0,0 +1,53 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <cstdint>
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<HaltReason>(~static_cast<std::uint32_t>(hr));
}
constexpr HaltReason operator|(HaltReason hr1, HaltReason hr2) {
return static_cast<HaltReason>(static_cast<std::uint32_t>(hr1) | static_cast<std::uint32_t>(hr2));
}
constexpr HaltReason operator&(HaltReason hr1, HaltReason hr2) {
return static_cast<HaltReason>(static_cast<std::uint32_t>(hr1) & static_cast<std::uint32_t>(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<std::uint32_t>(hr) == 0;
}
constexpr bool Has(HaltReason hr1, HaltReason hr2) {
return (static_cast<std::uint32_t>(hr1) & static_cast<std::uint32_t>(hr2)) != 0;
}
} // namespace Dynarmic