From f69c77391efc03d6de9d34d4ebefc3d72c9bf3b7 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Mon, 6 Apr 2020 15:05:50 +0100 Subject: [PATCH] A64: Add Step Allow for stepping instruction-by-instruction --- include/dynarmic/A64/a64.h | 6 +++ src/backend/x64/a64_emit_x64.cpp | 6 +-- src/backend/x64/a64_interface.cpp | 47 +++++++++++++++++++----- src/backend/x64/a64_jitstate.cpp | 6 --- src/backend/x64/a64_jitstate.h | 7 +++- src/backend/x64/block_of_code.cpp | 17 +++++++++ src/backend/x64/block_of_code.h | 3 ++ src/common/bit_util.h | 2 +- src/frontend/A64/location_descriptor.cpp | 2 +- src/frontend/A64/location_descriptor.h | 36 +++++++++++++----- src/frontend/A64/translate/translate.cpp | 8 +++- 11 files changed, 108 insertions(+), 32 deletions(-) diff --git a/include/dynarmic/A64/a64.h b/include/dynarmic/A64/a64.h index 02123f6a..86b33c34 100644 --- a/include/dynarmic/A64/a64.h +++ b/include/dynarmic/A64/a64.h @@ -30,6 +30,12 @@ public: */ void Run(); + /** + * Step the emulated CPU for one instruction. + * Cannot be recursively called. + */ + void Step(); + /** * Clears the code cache of all compiled code. * Can be called at any time. Halts execution if called within a callback. diff --git a/src/backend/x64/a64_emit_x64.cpp b/src/backend/x64/a64_emit_x64.cpp index 6dec97f5..879780dc 100644 --- a/src/backend/x64/a64_emit_x64.cpp +++ b/src/backend/x64/a64_emit_x64.cpp @@ -291,11 +291,11 @@ void A64EmitX64::GenTerminalHandlers() { // This calculation has to match up with A64::LocationDescriptor::UniqueHash // TODO: Optimization is available here based on known state of fpcr. code.mov(rbp, qword[r15 + offsetof(A64JitState, pc)]); - code.mov(rcx, A64::LocationDescriptor::PC_MASK); + code.mov(rcx, A64::LocationDescriptor::pc_mask); code.and_(rcx, rbp); code.mov(ebx, dword[r15 + offsetof(A64JitState, fpcr)]); - code.and_(ebx, A64::LocationDescriptor::FPCR_MASK); - code.shl(ebx, 37); + code.and_(ebx, A64::LocationDescriptor::fpcr_mask); + code.shl(ebx, A64::LocationDescriptor::fpcr_shift); code.or_(rbx, rcx); }; diff --git a/src/backend/x64/a64_interface.cpp b/src/backend/x64/a64_interface.cpp index 2a8029ba..5ac60112 100644 --- a/src/backend/x64/a64_interface.cpp +++ b/src/backend/x64/a64_interface.cpp @@ -54,13 +54,28 @@ public: // TODO: Check code alignment - const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask; - if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) { - jit_state.rsb_ptr = new_rsb_ptr; - block_of_code.RunCodeFrom(&jit_state, reinterpret_cast(jit_state.rsb_codeptrs[new_rsb_ptr])); - } else { - block_of_code.RunCode(&jit_state); - } + const CodePtr current_code_ptr = [this]{ + // RSB optimization + const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask; + if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) { + jit_state.rsb_ptr = new_rsb_ptr; + return reinterpret_cast(jit_state.rsb_codeptrs[new_rsb_ptr]); + } + + return GetCurrentBlock(); + }(); + block_of_code.RunCodeFrom(&jit_state, current_code_ptr); + + PerformRequestedCacheInvalidation(); + } + + void Step() { + ASSERT(!is_executing); + is_executing = true; + SCOPE_EXIT { this->is_executing = false; }; + jit_state.halt_requested = true; + + block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); PerformRequestedCacheInvalidation(); } @@ -185,9 +200,19 @@ private: return this_->GetCurrentBlock(); } - CodePtr GetCurrentBlock() { - IR::LocationDescriptor current_location{jit_state.GetUniqueHash()}; + IR::LocationDescriptor GetCurrentLocation() const { + return IR::LocationDescriptor{jit_state.GetUniqueHash()}; + } + CodePtr GetCurrentBlock() { + return GetBlock(GetCurrentLocation()); + } + + CodePtr GetCurrentSingleStep() { + return GetBlock(A64::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true)); + } + + CodePtr GetBlock(IR::LocationDescriptor current_location) { if (auto block = emitter.GetBasicBlock(current_location)) return block->entrypoint; @@ -256,6 +281,10 @@ void Jit::Run() { impl->Run(); } +void Jit::Step() { + impl->Step(); +} + void Jit::ClearCache() { impl->ClearCache(); } diff --git a/src/backend/x64/a64_jitstate.cpp b/src/backend/x64/a64_jitstate.cpp index 351ecf69..054c9fce 100644 --- a/src/backend/x64/a64_jitstate.cpp +++ b/src/backend/x64/a64_jitstate.cpp @@ -10,12 +10,6 @@ namespace Dynarmic::BackendX64 { -u64 A64JitState::GetUniqueHash() const noexcept { - const u64 fpcr_u64 = static_cast(fpcr & A64::LocationDescriptor::FPCR_MASK) << 37; - const u64 pc_u64 = pc & A64::LocationDescriptor::PC_MASK; - return pc_u64 | fpcr_u64; -} - /** * Comparing MXCSR and FPCR * ======================== diff --git a/src/backend/x64/a64_jitstate.h b/src/backend/x64/a64_jitstate.h index b9e909d5..f926d0d7 100644 --- a/src/backend/x64/a64_jitstate.h +++ b/src/backend/x64/a64_jitstate.h @@ -11,6 +11,7 @@ #include #include "common/common_types.h" +#include "frontend/A64/location_descriptor.h" namespace Dynarmic::BackendX64 { @@ -79,7 +80,11 @@ struct A64JitState { void SetFpcr(u32 value); void SetFpsr(u32 value); - u64 GetUniqueHash() const noexcept; + u64 GetUniqueHash() const noexcept { + const u64 fpcr_u64 = static_cast(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift; + const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask; + return pc_u64 | fpcr_u64; + } }; #ifdef _MSC_VER diff --git a/src/backend/x64/block_of_code.cpp b/src/backend/x64/block_of_code.cpp index 55c0f204..c69c94df 100644 --- a/src/backend/x64/block_of_code.cpp +++ b/src/backend/x64/block_of_code.cpp @@ -142,6 +142,10 @@ void BlockOfCode::RunCodeFrom(void* jit_state, CodePtr code_ptr) const { run_code_from(jit_state, code_ptr); } +void BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const { + step_code(jit_state, code_ptr); +} + void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) { size_t index = 0; if (mxcsr_already_exited) @@ -174,6 +178,19 @@ void BlockOfCode::GenRunCode() { SwitchMxcsrOnEntry(); jmp(r14); + align(); + step_code = getCurr(); + + ABI_PushCalleeSaveRegistersAndAdjustStack(*this); + + mov(r15, ABI_PARAM1); + + mov(qword[r15 + jsi.offsetof_cycles_to_run], 1); + mov(qword[r15 + jsi.offsetof_cycles_remaining], 1); + + SwitchMxcsrOnEntry(); + jmp(ABI_PARAM2); + align(); run_code = getCurr(); diff --git a/src/backend/x64/block_of_code.h b/src/backend/x64/block_of_code.h index 2d944946..6380bb7c 100644 --- a/src/backend/x64/block_of_code.h +++ b/src/backend/x64/block_of_code.h @@ -51,6 +51,8 @@ public: void RunCode(void* jit_state) const; /// Runs emulated code from code_ptr. void RunCodeFrom(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; /// Code emitter: Returns to dispatcher void ReturnFromRunCode(bool mxcsr_already_exited = false); /// Code emitter: Returns to dispatcher, forces return to host @@ -158,6 +160,7 @@ private: using RunCodeFuncType = void(*)(void*); using RunCodeFromFuncType = void(*)(void*, CodePtr); RunCodeFuncType run_code = nullptr; + RunCodeFromFuncType step_code = nullptr; RunCodeFromFuncType run_code_from = nullptr; static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0; static constexpr size_t FORCE_RETURN = 1 << 1; diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 05da355f..14201541 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -23,7 +23,7 @@ constexpr size_t BitSize() { } template -inline T Ones(size_t count) { +constexpr T Ones(size_t count) { ASSERT_MSG(count <= BitSize(), "count larger than bitsize of T"); if (count == BitSize()) return static_cast(~static_cast(0)); diff --git a/src/frontend/A64/location_descriptor.cpp b/src/frontend/A64/location_descriptor.cpp index 60999fbf..66f053f8 100644 --- a/src/frontend/A64/location_descriptor.cpp +++ b/src/frontend/A64/location_descriptor.cpp @@ -12,7 +12,7 @@ namespace Dynarmic::A64 { std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { - o << fmt::format("{{{}, {}}}", descriptor.PC(), descriptor.FPCR().Value()); + o << fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : ""); return o; } diff --git a/src/frontend/A64/location_descriptor.h b/src/frontend/A64/location_descriptor.h index d2ff4de0..30f8703d 100644 --- a/src/frontend/A64/location_descriptor.h +++ b/src/frontend/A64/location_descriptor.h @@ -24,19 +24,29 @@ namespace Dynarmic::A64 { */ class LocationDescriptor { public: - static constexpr u64 PC_MASK = 0x00FF'FFFF'FFFF'FFFFull; - static constexpr u32 FPCR_MASK = 0x07C8'0000; + static constexpr size_t pc_bit_count = 56; + static constexpr u64 pc_mask = Common::Ones(pc_bit_count); + static constexpr u32 fpcr_mask = 0x07C8'0000; + static constexpr size_t fpcr_shift = 37; + static constexpr size_t single_stepping_bit = 57; + static_assert((pc_mask & (u64(fpcr_mask) << fpcr_shift) & (u64(1) << single_stepping_bit)) == 0); - LocationDescriptor(u64 pc, FP::FPCR fpcr) : pc(pc & PC_MASK), fpcr(fpcr.Value() & FPCR_MASK) {} + LocationDescriptor(u64 pc, FP::FPCR fpcr, bool single_stepping = false) + : pc(pc & pc_mask), fpcr(fpcr.Value() & fpcr_mask), single_stepping(single_stepping) + {} explicit LocationDescriptor(const IR::LocationDescriptor& o) - : pc(o.Value() & PC_MASK), fpcr((o.Value() >> 37) & FPCR_MASK) {} + : pc(o.Value() & pc_mask) + , fpcr((o.Value() >> fpcr_shift) & fpcr_mask) + , single_stepping(Common::Bit(o.Value())) + {} - u64 PC() const { return Common::SignExtend<56>(pc); } + u64 PC() const { return Common::SignExtend(pc); } FP::FPCR FPCR() const { return fpcr; } + bool SingleStepping() const { return single_stepping; } bool operator == (const LocationDescriptor& o) const { - return std::tie(pc, fpcr) == std::tie(o.pc, o.fpcr); + return std::tie(pc, fpcr, single_stepping) == std::tie(o.pc, o.fpcr, single_stepping); } bool operator != (const LocationDescriptor& o) const { @@ -44,18 +54,23 @@ public: } LocationDescriptor SetPC(u64 new_pc) const { - return LocationDescriptor(new_pc, fpcr); + return LocationDescriptor(new_pc, fpcr, single_stepping); } LocationDescriptor AdvancePC(int amount) const { - return LocationDescriptor(static_cast(pc + amount), fpcr); + return LocationDescriptor(static_cast(pc + amount), fpcr, single_stepping); + } + + LocationDescriptor SetSingleStepping(bool new_single_stepping) const { + return LocationDescriptor(pc, fpcr, new_single_stepping); } u64 UniqueHash() const noexcept { // This value MUST BE UNIQUE. // This calculation has to match up with EmitTerminalPopRSBHint - const u64 fpcr_u64 = static_cast(fpcr.Value()) << 37; - return pc | fpcr_u64; + const u64 fpcr_u64 = static_cast(fpcr.Value()) << fpcr_shift; + const u64 single_stepping_u64 = static_cast(single_stepping) << single_stepping_bit; + return pc | fpcr_u64 | single_stepping_u64; } operator IR::LocationDescriptor() const { @@ -65,6 +80,7 @@ public: private: u64 pc; ///< Current program counter value. FP::FPCR fpcr; ///< Floating point control register. + bool single_stepping; }; /** diff --git a/src/frontend/A64/translate/translate.cpp b/src/frontend/A64/translate/translate.cpp index e9cb59ff..f9619b36 100644 --- a/src/frontend/A64/translate/translate.cpp +++ b/src/frontend/A64/translate/translate.cpp @@ -9,6 +9,7 @@ #include "frontend/A64/translate/impl/impl.h" #include "frontend/A64/translate/translate.h" #include "frontend/ir/basic_block.h" +#include "frontend/ir/terminal.h" namespace Dynarmic::A64 { @@ -16,8 +17,9 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory IR::Block block{descriptor}; TranslatorVisitor visitor{block, descriptor, std::move(options)}; + const bool single_step = descriptor.SingleStepping(); bool should_continue = true; - while (should_continue) { + do { const u64 pc = visitor.ir.current_location->PC(); const u32 instruction = memory_read_code(pc); @@ -29,6 +31,10 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4); block.CycleCount()++; + } while (should_continue && !single_step); + + if (single_step && should_continue) { + visitor.ir.SetTerm(IR::Term::LinkBlock{*visitor.ir.current_location}); } ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");