From 94d0d33e026701b0a848cdd3b362202c59f54927 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Fri, 24 Apr 2020 10:00:58 +0100 Subject: [PATCH] Fix single stepping for certain instructions Several issues: 1. Several terminal instructions did not stop at the end of a single-step block 2. x64 backend for the A32 frontend sometimes polluted upper_location_descriptor with the single-stepping flag We also introduce the enable_optimizations parameter to the A32 frontend. --- include/dynarmic/A32/config.h | 8 + src/backend/x64/a32_emit_x64.cpp | 83 +++++--- src/backend/x64/a32_emit_x64.h | 21 +- src/backend/x64/a32_interface.cpp | 12 +- src/backend/x64/a64_emit_x64.cpp | 55 +++--- src/backend/x64/a64_emit_x64.h | 20 +- src/backend/x64/emit_x64.cpp | 20 +- src/backend/x64/emit_x64.h | 21 +- src/frontend/A32/translate/translate_arm.cpp | 3 +- .../A32/translate/translate_thumb.cpp | 3 +- src/frontend/A64/translate/translate.cpp | 3 +- tests/A32/test_arm_instructions.cpp | 187 ++++++++++++++++++ 12 files changed, 333 insertions(+), 103 deletions(-) diff --git a/include/dynarmic/A32/config.h b/include/dynarmic/A32/config.h index cde728a6..8117de47 100644 --- a/include/dynarmic/A32/config.h +++ b/include/dynarmic/A32/config.h @@ -86,6 +86,14 @@ struct UserCallbacks { struct UserConfig { UserCallbacks* callbacks; + /// When set to false, this disables all optimizations than can't otherwise be disabled + /// by setting other configuration options. This includes: + /// - IR optimizations + /// - Block linking optimizations + /// - RSB optimizations + /// This is intended to be used for debugging. + bool enable_optimizations = true; + // Page Table // The page table is used for faster memory access. If an entry in the table is nullptr, // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks. diff --git a/src/backend/x64/a32_emit_x64.cpp b/src/backend/x64/a32_emit_x64.cpp index 03faafff..64dc5f9c 100644 --- a/src/backend/x64/a32_emit_x64.cpp +++ b/src/backend/x64/a32_emit_x64.cpp @@ -61,6 +61,10 @@ A32::LocationDescriptor A32EmitContext::Location() const { return A32::LocationDescriptor{block.Location()}; } +bool A32EmitContext::IsSingleStep() const { + return A32::LocationDescriptor{block.Location()}.SingleStepping(); +} + FP::FPCR A32EmitContext::FPCR() const { return FP::FPCR{Location().FPSCR().Value()}; } @@ -83,12 +87,6 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { code.EnableWriting(); SCOPE_EXIT { code.DisableWriting(); }; - code.align(); - const u8* const entrypoint = code.getCurr(); - - // Start emitting. - EmitCondPrelude(block); - static const std::vector gpr_order = [this]{ std::vector gprs{any_gpr}; if (config.page_table) { @@ -103,6 +101,12 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { RegAlloc reg_alloc{code, A32JitState::SpillCount, SpillToOpArg, gpr_order, any_xmm}; A32EmitContext ctx{reg_alloc, block}; + // Start emitting. + code.align(); + const u8* const entrypoint = code.getCurr(); + + EmitCondPrelude(ctx); + for (auto iter = block.begin(); iter != block.end(); ++iter) { IR::Inst* inst = &*iter; @@ -134,7 +138,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { reg_alloc.AssertNoMoreUses(); EmitAddCycles(block.CycleCount()); - EmitX64::EmitTerminal(block.GetTerminal(), block.Location()); + EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.int3(); const size_t size = static_cast(code.getCurr() - entrypoint); @@ -159,6 +163,20 @@ void A32EmitX64::InvalidateCacheRanges(const boost::icl::interval_set& rang InvalidateBasicBlocks(block_ranges.InvalidateRanges(ranges)); } +void A32EmitX64::EmitCondPrelude(const A32EmitContext& ctx) { + if (ctx.block.GetCondition() == IR::Cond::AL) { + ASSERT(!ctx.block.HasConditionFailedLocation()); + return; + } + + ASSERT(ctx.block.HasConditionFailedLocation()); + + Xbyak::Label pass = EmitCond(ctx.block.GetCondition()); + EmitAddCycles(ctx.block.ConditionFailedCycleCount()); + EmitTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); + code.L(pass); +} + void A32EmitX64::ClearFastDispatchTable() { if (config.enable_fast_dispatch) { fast_dispatch_table.fill({}); @@ -674,7 +692,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto& arg = args[0]; - const u32 upper_without_t = (ctx.Location().UniqueHash() >> 32) & 0xFFFFFFFE; + const u32 upper_without_t = (ctx.Location().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE; // Pseudocode: // if (new_pc & 1) { @@ -1387,7 +1405,7 @@ std::string A32EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr descriptor.FPSCR().Value()); } -void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) { +void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool) { ASSERT_MSG(A32::LocationDescriptor{terminal.next}.TFlag() == A32::LocationDescriptor{initial_location}.TFlag(), "Unimplemented"); ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented"); ASSERT_MSG(terminal.num_instructions == 1, "Unimplemented"); @@ -1400,13 +1418,13 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDesc code.ReturnFromRunCode(true); // TODO: Check cycles } -void A32EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescriptor) { +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(desc.Value() >> 32); + return static_cast(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32); }; const u32 old_upper = get_upper(old_location); @@ -1420,9 +1438,15 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat } } -void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location) { +void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) { EmitSetUpperLocationDescriptor(terminal.next, initial_location); + if (!config.enable_optimizations || is_single_step) { + code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); + code.ReturnFromRunCode(); + return; + } + code.cmp(qword[r15 + offsetof(A32JitState, cycles_remaining)], 0); patch_information[terminal.next].jg.emplace_back(code.getCurr()); @@ -1443,9 +1467,15 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc code.SwitchToNearCode(); } -void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) { +void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) { EmitSetUpperLocationDescriptor(terminal.next, initial_location); + if (!config.enable_optimizations || is_single_step) { + code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); + code.ReturnFromRunCode(); + return; + } + patch_information[terminal.next].jmp.emplace_back(code.getCurr()); if (const auto next_bb = GetBasicBlock(terminal.next)) { EmitPatchJmp(terminal.next, next_bb->entrypoint); @@ -1454,38 +1484,43 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::Location } } -void A32EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor) { +void A32EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { + if (!config.enable_optimizations || is_single_step) { + code.ReturnFromRunCode(); + return; + } + code.jmp(terminal_handler_pop_rsb_hint); } -void A32EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor) { - if (config.enable_fast_dispatch) { +void A32EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { + if (config.enable_fast_dispatch && !is_single_step) { code.jmp(terminal_handler_fast_dispatch_hint); } else { code.ReturnFromRunCode(); } } -void A32EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) { +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); + EmitTerminal(terminal.else_, initial_location, is_single_step); code.L(pass); - EmitTerminal(terminal.then_, initial_location); + EmitTerminal(terminal.then_, initial_location, is_single_step); } -void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) { +void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label fail; code.cmp(code.byte[r15 + offsetof(A32JitState, check_bit)], u8(0)); code.jz(fail); - EmitTerminal(terminal.then_, initial_location); + EmitTerminal(terminal.then_, initial_location, is_single_step); code.L(fail); - EmitTerminal(terminal.else_, initial_location); + EmitTerminal(terminal.else_, initial_location, is_single_step); } -void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) { +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.jne(code.GetForceReturnFromRunCodeAddress()); - EmitTerminal(terminal.else_, initial_location); + EmitTerminal(terminal.else_, initial_location, is_single_step); } void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { diff --git a/src/backend/x64/a32_emit_x64.h b/src/backend/x64/a32_emit_x64.h index a9bb0d19..20bb0780 100644 --- a/src/backend/x64/a32_emit_x64.h +++ b/src/backend/x64/a32_emit_x64.h @@ -27,6 +27,7 @@ class RegAlloc; struct A32EmitContext final : public EmitContext { A32EmitContext(RegAlloc& reg_alloc, IR::Block& block); A32::LocationDescriptor Location() const; + bool IsSingleStep() const; FP::FPCR FPCR() const override; }; @@ -50,6 +51,8 @@ protected: A32::Jit* jit_interface; BlockRangeInformation block_ranges; + void EmitCondPrelude(const A32EmitContext& ctx); + struct FastDispatchEntry { u64 location_descriptor = 0xFFFF'FFFF'FFFF'FFFFull; const void* code_ptr = nullptr; @@ -101,15 +104,15 @@ protected: // Terminal instruction emitters void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location); - void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override; + 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; // Patching void Unpatch(const IR::LocationDescriptor& target_desc) override; diff --git a/src/backend/x64/a32_interface.cpp b/src/backend/x64/a32_interface.cpp index 8ad3a528..b8b3f671 100644 --- a/src/backend/x64/a32_interface.cpp +++ b/src/backend/x64/a32_interface.cpp @@ -159,11 +159,13 @@ private: } IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return config.callbacks->MemoryReadCode(vaddr); }, {config.define_unpredictable_behaviour, config.hook_hint_instructions}); - Optimization::A32GetSetElimination(ir_block); - Optimization::DeadCodeElimination(ir_block); - Optimization::A32ConstantMemoryReads(ir_block, config.callbacks); - Optimization::ConstantPropagation(ir_block); - Optimization::DeadCodeElimination(ir_block); + if (config.enable_optimizations) { + Optimization::A32GetSetElimination(ir_block); + Optimization::DeadCodeElimination(ir_block); + Optimization::A32ConstantMemoryReads(ir_block, config.callbacks); + Optimization::ConstantPropagation(ir_block); + Optimization::DeadCodeElimination(ir_block); + } Optimization::VerificationPass(ir_block); return emitter.Emit(ir_block); } diff --git a/src/backend/x64/a64_emit_x64.cpp b/src/backend/x64/a64_emit_x64.cpp index 1bad13b0..d8019531 100644 --- a/src/backend/x64/a64_emit_x64.cpp +++ b/src/backend/x64/a64_emit_x64.cpp @@ -23,6 +23,7 @@ #include "frontend/A64/location_descriptor.h" #include "frontend/A64/types.h" #include "frontend/ir/basic_block.h" +#include "frontend/ir/cond.h" #include "frontend/ir/microinstruction.h" #include "frontend/ir/opcodes.h" @@ -40,6 +41,10 @@ A64::LocationDescriptor A64EmitContext::Location() const { return A64::LocationDescriptor{block.Location()}; } +bool A64EmitContext::IsSingleStep() const { + return Location().SingleStepping(); +} + FP::FPCR A64EmitContext::FPCR() const { return Location().FPCR(); } @@ -63,14 +68,14 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { code.EnableWriting(); SCOPE_EXIT { code.DisableWriting(); }; + RegAlloc reg_alloc{code, A64JitState::SpillCount, SpillToOpArg, any_gpr, any_xmm}; + A64EmitContext ctx{conf, reg_alloc, block}; + + // Start emitting. code.align(); const u8* const entrypoint = code.getCurr(); - // Start emitting. - EmitCondPrelude(block); - - RegAlloc reg_alloc{code, A64JitState::SpillCount, SpillToOpArg, any_gpr, any_xmm}; - A64EmitContext ctx{conf, reg_alloc, block}; + ASSERT(block.GetCondition() == IR::Cond::AL); for (auto iter = block.begin(); iter != block.end(); ++iter) { IR::Inst* inst = &*iter; @@ -103,7 +108,7 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { reg_alloc.AssertNoMoreUses(); EmitAddCycles(block.CycleCount()); - EmitX64::EmitTerminal(block.GetTerminal(), block.Location()); + EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.int3(); const size_t size = static_cast(code.getCurr() - entrypoint); @@ -1147,7 +1152,7 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr descriptor.FPCR().Value()); } -void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) { +void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor, bool) { code.SwitchMxcsrOnExit(); Devirtualize<&A64::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code, [&](RegList param) { @@ -1158,12 +1163,12 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDesc code.ReturnFromRunCode(true); // TODO: Check cycles } -void A64EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescriptor) { +void A64EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { code.ReturnFromRunCode(); } -void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor) { - if (!conf.enable_optimizations) { +void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) { + if (!conf.enable_optimizations || is_single_step) { code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); code.mov(qword[r15 + offsetof(A64JitState, pc)], rax); code.ReturnFromRunCode(); @@ -1183,8 +1188,8 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc code.ForceReturnFromRunCode(); } -void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor) { - if (!conf.enable_optimizations) { +void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { + if (!conf.enable_optimizations || is_single_step) { code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); code.mov(qword[r15 + offsetof(A64JitState, pc)], rax); code.ReturnFromRunCode(); @@ -1199,8 +1204,8 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::Location } } -void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor) { - if (!conf.enable_optimizations) { +void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { + if (!conf.enable_optimizations || is_single_step) { code.ReturnFromRunCode(); return; } @@ -1208,42 +1213,42 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor) code.jmp(terminal_handler_pop_rsb_hint); } -void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor) { - if (conf.enable_fast_dispatch) { +void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { + if (conf.enable_fast_dispatch && !is_single_step) { code.jmp(terminal_handler_fast_dispatch_hint); } else { code.ReturnFromRunCode(); } } -void A64EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) { +void A64EmitX64::EmitTerminalImpl(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); + EmitTerminal(terminal.then_, initial_location, is_single_step); break; default: Xbyak::Label pass = EmitCond(terminal.if_); - EmitTerminal(terminal.else_, initial_location); + EmitTerminal(terminal.else_, initial_location, is_single_step); code.L(pass); - EmitTerminal(terminal.then_, initial_location); + EmitTerminal(terminal.then_, initial_location, is_single_step); break; } } -void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) { +void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label fail; code.cmp(code.byte[r15 + offsetof(A64JitState, check_bit)], u8(0)); code.jz(fail); - EmitTerminal(terminal.then_, initial_location); + EmitTerminal(terminal.then_, initial_location, is_single_step); code.L(fail); - EmitTerminal(terminal.else_, initial_location); + EmitTerminal(terminal.else_, initial_location, is_single_step); } -void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) { +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.jne(code.GetForceReturnFromRunCodeAddress()); - EmitTerminal(terminal.else_, initial_location); + EmitTerminal(terminal.else_, initial_location, is_single_step); } void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { diff --git a/src/backend/x64/a64_emit_x64.h b/src/backend/x64/a64_emit_x64.h index f4d9ce33..c8fd2566 100644 --- a/src/backend/x64/a64_emit_x64.h +++ b/src/backend/x64/a64_emit_x64.h @@ -23,7 +23,9 @@ class RegAlloc; struct A64EmitContext final : public EmitContext { A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block); + A64::LocationDescriptor Location() const; + bool IsSingleStep() const; FP::FPCR FPCR() const override; bool AccurateNaN() const override; @@ -91,15 +93,15 @@ protected: std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const override; // Terminal instruction emitters - void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) override; - void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override; + 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; // Patching void Unpatch(const IR::LocationDescriptor& target_desc) override; diff --git a/src/backend/x64/emit_x64.cpp b/src/backend/x64/emit_x64.cpp index 373ecd2d..41a2b285 100644 --- a/src/backend/x64/emit_x64.cpp +++ b/src/backend/x64/emit_x64.cpp @@ -285,20 +285,6 @@ Xbyak::Label EmitX64::EmitCond(IR::Cond cond) { return label; } -void EmitX64::EmitCondPrelude(const IR::Block& block) { - if (block.GetCondition() == IR::Cond::AL) { - ASSERT(!block.HasConditionFailedLocation()); - return; - } - - ASSERT(block.HasConditionFailedLocation()); - - Xbyak::Label pass = EmitCond(block.GetCondition()); - EmitAddCycles(block.ConditionFailedCycleCount()); - EmitTerminal(IR::Term::LinkBlock{block.ConditionFailedLocation()}, block.Location()); - code.L(pass); -} - EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& descriptor, CodePtr entrypoint, size_t size) { PerfMapRegister(entrypoint, code.getCurr(), LocationDescriptorToFriendlyName(descriptor)); Patch(descriptor, entrypoint); @@ -308,11 +294,11 @@ EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& de return block_desc; } -void EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location) { - Common::VisitVariant(terminal, [this, &initial_location](auto x) { +void EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + Common::VisitVariant(terminal, [this, initial_location, is_single_step](auto x) { using T = std::decay_t; if constexpr (!std::is_same_v) { - this->EmitTerminalImpl(x, initial_location); + this->EmitTerminalImpl(x, initial_location, is_single_step); } else { ASSERT_MSG(false, "Invalid terminal"); } diff --git a/src/backend/x64/emit_x64.h b/src/backend/x64/emit_x64.h index 7b144e84..1a50c468 100644 --- a/src/backend/x64/emit_x64.h +++ b/src/backend/x64/emit_x64.h @@ -85,21 +85,20 @@ protected: virtual std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const = 0; void EmitAddCycles(size_t cycles); Xbyak::Label EmitCond(IR::Cond cond); - void EmitCondPrelude(const IR::Block& block); BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, size_t size); void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target); // Terminal instruction emitters - void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location); - virtual void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) = 0; - virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) = 0; + 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; // Patching struct PatchInformation { diff --git a/src/frontend/A32/translate/translate_arm.cpp b/src/frontend/A32/translate/translate_arm.cpp index 13422969..66f6eafd 100644 --- a/src/frontend/A32/translate/translate_arm.cpp +++ b/src/frontend/A32/translate/translate_arm.cpp @@ -29,10 +29,11 @@ static bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& i } IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { + const bool single_step = descriptor.SingleStepping(); + IR::Block block{descriptor}; ArmTranslatorVisitor visitor{block, descriptor, options}; - const bool single_step = descriptor.SingleStepping(); bool should_continue = true; do { const u32 arm_pc = visitor.ir.current_location.PC(); diff --git a/src/frontend/A32/translate/translate_thumb.cpp b/src/frontend/A32/translate/translate_thumb.cpp index 7eb98156..288df579 100644 --- a/src/frontend/A32/translate/translate_thumb.cpp +++ b/src/frontend/A32/translate/translate_thumb.cpp @@ -55,10 +55,11 @@ std::tuple ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFu } // local namespace IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { + const bool single_step = descriptor.SingleStepping(); + IR::Block block{descriptor}; ThumbTranslatorVisitor visitor{block, descriptor, options}; - const bool single_step = descriptor.SingleStepping(); bool should_continue = true; do { const u32 arm_pc = visitor.ir.current_location.PC(); diff --git a/src/frontend/A64/translate/translate.cpp b/src/frontend/A64/translate/translate.cpp index 7fdeeaed..1e4059a5 100644 --- a/src/frontend/A64/translate/translate.cpp +++ b/src/frontend/A64/translate/translate.cpp @@ -13,10 +13,11 @@ namespace Dynarmic::A64 { IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) { + const bool single_step = descriptor.SingleStepping(); + IR::Block block{descriptor}; TranslatorVisitor visitor{block, descriptor, std::move(options)}; - const bool single_step = descriptor.SingleStepping(); bool should_continue = true; do { const u64 pc = visitor.ir.current_location->PC(); diff --git a/tests/A32/test_arm_instructions.cpp b/tests/A32/test_arm_instructions.cpp index 443a15c4..223b1bc8 100644 --- a/tests/A32/test_arm_instructions.cpp +++ b/tests/A32/test_arm_instructions.cpp @@ -7,6 +7,7 @@ #include #include "A32/testenv.h" +#include "frontend/A32/location_descriptor.h" using namespace Dynarmic; @@ -238,3 +239,189 @@ TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") { REQUIRE(jit.Regs()[15] == 0x0000000c); REQUIRE(jit.Cpsr() == 0x000001d0); } + +TEST_CASE("arm: Step blx", "[arm]") { + ArmTestEnv test_env; + A32::UserConfig config = GetUserConfig(&test_env); + config.enable_fast_dispatch = true; + Dynarmic::A32::Jit jit{config}; + test_env.code_mem = { + 0xe12fff30, // blx r0 + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xeafffffe, // b +#0 (infinite loop) + }; + + jit.Regs()[0] = 8; + jit.Regs()[15] = 0; // PC = 0 + jit.SetCpsr(0x000001d0); // User-mode + + test_env.ticks_left = 10; + jit.Step(); + + REQUIRE(jit.Regs()[0] == 8); + REQUIRE(jit.Regs()[14] == 4); + REQUIRE(jit.Regs()[15] == 8); + REQUIRE(jit.Cpsr() == 0x000001d0); +} + +TEST_CASE("arm: Step bx", "[arm]") { + ArmTestEnv test_env; + A32::UserConfig config = GetUserConfig(&test_env); + config.enable_fast_dispatch = true; + Dynarmic::A32::Jit jit{config}; + test_env.code_mem = { + 0xe12fff10, // bx r0 + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xeafffffe, // b +#0 (infinite loop) + }; + + jit.Regs()[0] = 8; + jit.Regs()[15] = 0; // PC = 0 + jit.SetCpsr(0x000001d0); // User-mode + + test_env.ticks_left = 10; + jit.Step(); + + REQUIRE(jit.Regs()[0] == 8); + REQUIRE(jit.Regs()[15] == 8); + REQUIRE(jit.Cpsr() == 0x000001d0); +} + + +TEST_CASE("arm: Test stepping", "[arm]") { + ArmTestEnv test_env; + Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; + test_env.code_mem = { + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xeafffffe, // b +#0 (infinite loop) + }; + + jit.Regs()[0] = 8; + jit.Regs()[15] = 0; // PC = 0 + jit.SetCpsr(0x000001d0); // User-mode + + for (size_t i = 0; i < 5; ++i) { + test_env.ticks_left = 10; + jit.Step(); + + REQUIRE(jit.Regs()[15] == (i + 1) * 4); + REQUIRE(jit.Cpsr() == 0x000001d0); + } + + test_env.ticks_left = 20; + jit.Run(); + + REQUIRE(jit.Regs()[15] == 80); + REQUIRE(jit.Cpsr() == 0x000001d0); +} + +TEST_CASE("arm: Test stepping 2", "[arm]") { + ArmTestEnv test_env; + Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; + test_env.code_mem = { + 0xe12fff10, // bx r0 + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xeafffffe, // b +#0 (infinite loop) + }; + + jit.Regs()[0] = 4; + jit.Regs()[15] = 0; // PC = 0 + jit.SetCpsr(0x000001d0); // User-mode + + for (size_t i = 0; i < 5; ++i) { + test_env.ticks_left = 10; + jit.Step(); + + REQUIRE(jit.Regs()[15] == (i + 1) * 4); + REQUIRE(jit.Cpsr() == 0x000001d0); + } + + test_env.ticks_left = 20; + jit.Run(); + + REQUIRE(jit.Regs()[15] == 80); + REQUIRE(jit.Cpsr() == 0x000001d0); +} + +TEST_CASE("arm: Test stepping 3", "[arm]") { + ArmTestEnv test_env; + Dynarmic::A32::Jit jit{GetUserConfig(&test_env)}; + test_env.code_mem = { + 0xe12fff10, // bx r0 + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + 0xe320f000, // nop + + 0xeafffffe, // b +#0 (infinite loop) + }; + + jit.Regs()[0] = 4; + jit.Regs()[15] = 0; // PC = 0 + jit.SetCpsr(0x000001d0); // User-mode + + test_env.ticks_left = 10; + jit.Step(); + + REQUIRE(jit.Regs()[15] == 4); + REQUIRE(jit.Cpsr() == 0x000001d0); + + test_env.ticks_left = 20; + jit.Run(); + + REQUIRE(jit.Regs()[15] == 20); + REQUIRE(jit.Cpsr() == 0x000001d0); +}