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.
This commit is contained in:
MerryMage 2020-04-24 10:00:58 +01:00
parent 591e7667f2
commit 94d0d33e02
12 changed files with 333 additions and 103 deletions

View file

@ -86,6 +86,14 @@ struct UserCallbacks {
struct UserConfig { struct UserConfig {
UserCallbacks* callbacks; 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 // Page Table
// The page table is used for faster memory access. If an entry in the table is nullptr, // 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. // the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks.

View file

@ -61,6 +61,10 @@ A32::LocationDescriptor A32EmitContext::Location() const {
return A32::LocationDescriptor{block.Location()}; return A32::LocationDescriptor{block.Location()};
} }
bool A32EmitContext::IsSingleStep() const {
return A32::LocationDescriptor{block.Location()}.SingleStepping();
}
FP::FPCR A32EmitContext::FPCR() const { FP::FPCR A32EmitContext::FPCR() const {
return FP::FPCR{Location().FPSCR().Value()}; return FP::FPCR{Location().FPSCR().Value()};
} }
@ -83,12 +87,6 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
code.EnableWriting(); code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); }; SCOPE_EXIT { code.DisableWriting(); };
code.align();
const u8* const entrypoint = code.getCurr();
// Start emitting.
EmitCondPrelude(block);
static const std::vector<HostLoc> gpr_order = [this]{ static const std::vector<HostLoc> gpr_order = [this]{
std::vector<HostLoc> gprs{any_gpr}; std::vector<HostLoc> gprs{any_gpr};
if (config.page_table) { if (config.page_table) {
@ -103,6 +101,12 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
RegAlloc reg_alloc{code, A32JitState::SpillCount, SpillToOpArg<A32JitState>, gpr_order, any_xmm}; RegAlloc reg_alloc{code, A32JitState::SpillCount, SpillToOpArg<A32JitState>, gpr_order, any_xmm};
A32EmitContext ctx{reg_alloc, block}; 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) { for (auto iter = block.begin(); iter != block.end(); ++iter) {
IR::Inst* inst = &*iter; IR::Inst* inst = &*iter;
@ -134,7 +138,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
reg_alloc.AssertNoMoreUses(); reg_alloc.AssertNoMoreUses();
EmitAddCycles(block.CycleCount()); EmitAddCycles(block.CycleCount());
EmitX64::EmitTerminal(block.GetTerminal(), block.Location()); EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
code.int3(); code.int3();
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint); const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
@ -159,6 +163,20 @@ void A32EmitX64::InvalidateCacheRanges(const boost::icl::interval_set<u32>& rang
InvalidateBasicBlocks(block_ranges.InvalidateRanges(ranges)); 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() { void A32EmitX64::ClearFastDispatchTable() {
if (config.enable_fast_dispatch) { if (config.enable_fast_dispatch) {
fast_dispatch_table.fill({}); fast_dispatch_table.fill({});
@ -674,7 +692,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& arg = args[0]; 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: // Pseudocode:
// if (new_pc & 1) { // if (new_pc & 1) {
@ -1387,7 +1405,7 @@ std::string A32EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr
descriptor.FPSCR().Value()); 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}.TFlag() == A32::LocationDescriptor{initial_location}.TFlag(), "Unimplemented");
ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented"); ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented");
ASSERT_MSG(terminal.num_instructions == 1, "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 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(); code.ReturnFromRunCode();
} }
void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) { void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) {
auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 { auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 {
return static_cast<u32>(desc.Value() >> 32); return static_cast<u32>(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32);
}; };
const u32 old_upper = get_upper(old_location); 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); 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); code.cmp(qword[r15 + offsetof(A32JitState, cycles_remaining)], 0);
patch_information[terminal.next].jg.emplace_back(code.getCurr()); patch_information[terminal.next].jg.emplace_back(code.getCurr());
@ -1443,9 +1467,15 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc
code.SwitchToNearCode(); 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); 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()); patch_information[terminal.next].jmp.emplace_back(code.getCurr());
if (const auto next_bb = GetBasicBlock(terminal.next)) { if (const auto next_bb = GetBasicBlock(terminal.next)) {
EmitPatchJmp(terminal.next, next_bb->entrypoint); 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); code.jmp(terminal_handler_pop_rsb_hint);
} }
void A32EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor) { void A32EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
if (config.enable_fast_dispatch) { if (config.enable_fast_dispatch && !is_single_step) {
code.jmp(terminal_handler_fast_dispatch_hint); code.jmp(terminal_handler_fast_dispatch_hint);
} else { } else {
code.ReturnFromRunCode(); 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_); Xbyak::Label pass = EmitCond(terminal.if_);
EmitTerminal(terminal.else_, initial_location); EmitTerminal(terminal.else_, initial_location, is_single_step);
code.L(pass); 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; Xbyak::Label fail;
code.cmp(code.byte[r15 + offsetof(A32JitState, check_bit)], u8(0)); code.cmp(code.byte[r15 + offsetof(A32JitState, check_bit)], u8(0));
code.jz(fail); code.jz(fail);
EmitTerminal(terminal.then_, initial_location); EmitTerminal(terminal.then_, initial_location, is_single_step);
code.L(fail); 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.cmp(code.byte[r15 + offsetof(A32JitState, halt_requested)], u8(0));
code.jne(code.GetForceReturnFromRunCodeAddress()); 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) { void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {

View file

@ -27,6 +27,7 @@ class RegAlloc;
struct A32EmitContext final : public EmitContext { struct A32EmitContext final : public EmitContext {
A32EmitContext(RegAlloc& reg_alloc, IR::Block& block); A32EmitContext(RegAlloc& reg_alloc, IR::Block& block);
A32::LocationDescriptor Location() const; A32::LocationDescriptor Location() const;
bool IsSingleStep() const;
FP::FPCR FPCR() const override; FP::FPCR FPCR() const override;
}; };
@ -50,6 +51,8 @@ protected:
A32::Jit* jit_interface; A32::Jit* jit_interface;
BlockRangeInformation<u32> block_ranges; BlockRangeInformation<u32> block_ranges;
void EmitCondPrelude(const A32EmitContext& ctx);
struct FastDispatchEntry { struct FastDispatchEntry {
u64 location_descriptor = 0xFFFF'FFFF'FFFF'FFFFull; u64 location_descriptor = 0xFFFF'FFFF'FFFF'FFFFull;
const void* code_ptr = nullptr; const void* code_ptr = nullptr;
@ -101,15 +104,15 @@ protected:
// Terminal instruction emitters // Terminal instruction emitters
void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location); 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::Interpret terminal, IR::LocationDescriptor initial_location, bool is_single_step) override;
void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location) 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) 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) 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) 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) 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) 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) 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) override; void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) override;
// Patching // Patching
void Unpatch(const IR::LocationDescriptor& target_desc) override; void Unpatch(const IR::LocationDescriptor& target_desc) override;

View file

@ -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}); 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});
if (config.enable_optimizations) {
Optimization::A32GetSetElimination(ir_block); Optimization::A32GetSetElimination(ir_block);
Optimization::DeadCodeElimination(ir_block); Optimization::DeadCodeElimination(ir_block);
Optimization::A32ConstantMemoryReads(ir_block, config.callbacks); Optimization::A32ConstantMemoryReads(ir_block, config.callbacks);
Optimization::ConstantPropagation(ir_block); Optimization::ConstantPropagation(ir_block);
Optimization::DeadCodeElimination(ir_block); Optimization::DeadCodeElimination(ir_block);
}
Optimization::VerificationPass(ir_block); Optimization::VerificationPass(ir_block);
return emitter.Emit(ir_block); return emitter.Emit(ir_block);
} }

View file

@ -23,6 +23,7 @@
#include "frontend/A64/location_descriptor.h" #include "frontend/A64/location_descriptor.h"
#include "frontend/A64/types.h" #include "frontend/A64/types.h"
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "frontend/ir/cond.h"
#include "frontend/ir/microinstruction.h" #include "frontend/ir/microinstruction.h"
#include "frontend/ir/opcodes.h" #include "frontend/ir/opcodes.h"
@ -40,6 +41,10 @@ A64::LocationDescriptor A64EmitContext::Location() const {
return A64::LocationDescriptor{block.Location()}; return A64::LocationDescriptor{block.Location()};
} }
bool A64EmitContext::IsSingleStep() const {
return Location().SingleStepping();
}
FP::FPCR A64EmitContext::FPCR() const { FP::FPCR A64EmitContext::FPCR() const {
return Location().FPCR(); return Location().FPCR();
} }
@ -63,14 +68,14 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
code.EnableWriting(); code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); }; SCOPE_EXIT { code.DisableWriting(); };
RegAlloc reg_alloc{code, A64JitState::SpillCount, SpillToOpArg<A64JitState>, any_gpr, any_xmm};
A64EmitContext ctx{conf, reg_alloc, block};
// Start emitting.
code.align(); code.align();
const u8* const entrypoint = code.getCurr(); const u8* const entrypoint = code.getCurr();
// Start emitting. ASSERT(block.GetCondition() == IR::Cond::AL);
EmitCondPrelude(block);
RegAlloc reg_alloc{code, A64JitState::SpillCount, SpillToOpArg<A64JitState>, any_gpr, any_xmm};
A64EmitContext ctx{conf, reg_alloc, block};
for (auto iter = block.begin(); iter != block.end(); ++iter) { for (auto iter = block.begin(); iter != block.end(); ++iter) {
IR::Inst* inst = &*iter; IR::Inst* inst = &*iter;
@ -103,7 +108,7 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
reg_alloc.AssertNoMoreUses(); reg_alloc.AssertNoMoreUses();
EmitAddCycles(block.CycleCount()); EmitAddCycles(block.CycleCount());
EmitX64::EmitTerminal(block.GetTerminal(), block.Location()); EmitX64::EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
code.int3(); code.int3();
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint); const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
@ -1147,7 +1152,7 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr
descriptor.FPCR().Value()); descriptor.FPCR().Value());
} }
void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) { void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor, bool) {
code.SwitchMxcsrOnExit(); code.SwitchMxcsrOnExit();
Devirtualize<&A64::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code, Devirtualize<&A64::UserCallbacks::InterpreterFallback>(conf.callbacks).EmitCall(code,
[&](RegList param) { [&](RegList param) {
@ -1158,12 +1163,12 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDesc
code.ReturnFromRunCode(true); // TODO: Check cycles 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(); code.ReturnFromRunCode();
} }
void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor) { void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
if (!conf.enable_optimizations) { if (!conf.enable_optimizations || is_single_step) {
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax); code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
code.ReturnFromRunCode(); code.ReturnFromRunCode();
@ -1183,8 +1188,8 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc
code.ForceReturnFromRunCode(); code.ForceReturnFromRunCode();
} }
void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor) { void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
if (!conf.enable_optimizations) { if (!conf.enable_optimizations || is_single_step) {
code.mov(rax, A64::LocationDescriptor{terminal.next}.PC()); code.mov(rax, A64::LocationDescriptor{terminal.next}.PC());
code.mov(qword[r15 + offsetof(A64JitState, pc)], rax); code.mov(qword[r15 + offsetof(A64JitState, pc)], rax);
code.ReturnFromRunCode(); code.ReturnFromRunCode();
@ -1199,8 +1204,8 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::Location
} }
} }
void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor) { void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
if (!conf.enable_optimizations) { if (!conf.enable_optimizations || is_single_step) {
code.ReturnFromRunCode(); code.ReturnFromRunCode();
return; return;
} }
@ -1208,42 +1213,42 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor)
code.jmp(terminal_handler_pop_rsb_hint); code.jmp(terminal_handler_pop_rsb_hint);
} }
void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor) { void A64EmitX64::EmitTerminalImpl(IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
if (conf.enable_fast_dispatch) { if (conf.enable_fast_dispatch && !is_single_step) {
code.jmp(terminal_handler_fast_dispatch_hint); code.jmp(terminal_handler_fast_dispatch_hint);
} else { } else {
code.ReturnFromRunCode(); 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_) { switch (terminal.if_) {
case IR::Cond::AL: case IR::Cond::AL:
case IR::Cond::NV: case IR::Cond::NV:
EmitTerminal(terminal.then_, initial_location); EmitTerminal(terminal.then_, initial_location, is_single_step);
break; break;
default: default:
Xbyak::Label pass = EmitCond(terminal.if_); Xbyak::Label pass = EmitCond(terminal.if_);
EmitTerminal(terminal.else_, initial_location); EmitTerminal(terminal.else_, initial_location, is_single_step);
code.L(pass); code.L(pass);
EmitTerminal(terminal.then_, initial_location); EmitTerminal(terminal.then_, initial_location, is_single_step);
break; 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; Xbyak::Label fail;
code.cmp(code.byte[r15 + offsetof(A64JitState, check_bit)], u8(0)); code.cmp(code.byte[r15 + offsetof(A64JitState, check_bit)], u8(0));
code.jz(fail); code.jz(fail);
EmitTerminal(terminal.then_, initial_location); EmitTerminal(terminal.then_, initial_location, is_single_step);
code.L(fail); 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.cmp(code.byte[r15 + offsetof(A64JitState, halt_requested)], u8(0));
code.jne(code.GetForceReturnFromRunCodeAddress()); 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) { void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {

View file

@ -23,7 +23,9 @@ class RegAlloc;
struct A64EmitContext final : public EmitContext { struct A64EmitContext final : public EmitContext {
A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block); A64EmitContext(const A64::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block);
A64::LocationDescriptor Location() const; A64::LocationDescriptor Location() const;
bool IsSingleStep() const;
FP::FPCR FPCR() const override; FP::FPCR FPCR() const override;
bool AccurateNaN() const override; bool AccurateNaN() const override;
@ -91,15 +93,15 @@ protected:
std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const override; std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const override;
// Terminal instruction emitters // Terminal instruction emitters
void EmitTerminalImpl(IR::Term::Interpret 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) 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) 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) 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) 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) 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) 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) 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) override; void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) override;
// Patching // Patching
void Unpatch(const IR::LocationDescriptor& target_desc) override; void Unpatch(const IR::LocationDescriptor& target_desc) override;

View file

@ -285,20 +285,6 @@ Xbyak::Label EmitX64::EmitCond(IR::Cond cond) {
return label; 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) { EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& descriptor, CodePtr entrypoint, size_t size) {
PerfMapRegister(entrypoint, code.getCurr(), LocationDescriptorToFriendlyName(descriptor)); PerfMapRegister(entrypoint, code.getCurr(), LocationDescriptorToFriendlyName(descriptor));
Patch(descriptor, entrypoint); Patch(descriptor, entrypoint);
@ -308,11 +294,11 @@ EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& de
return block_desc; return block_desc;
} }
void EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location) { void EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
Common::VisitVariant<void>(terminal, [this, &initial_location](auto x) { Common::VisitVariant<void>(terminal, [this, initial_location, is_single_step](auto x) {
using T = std::decay_t<decltype(x)>; using T = std::decay_t<decltype(x)>;
if constexpr (!std::is_same_v<T, IR::Term::Invalid>) { if constexpr (!std::is_same_v<T, IR::Term::Invalid>) {
this->EmitTerminalImpl(x, initial_location); this->EmitTerminalImpl(x, initial_location, is_single_step);
} else { } else {
ASSERT_MSG(false, "Invalid terminal"); ASSERT_MSG(false, "Invalid terminal");
} }

View file

@ -85,21 +85,20 @@ protected:
virtual std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const = 0; virtual std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const = 0;
void EmitAddCycles(size_t cycles); void EmitAddCycles(size_t cycles);
Xbyak::Label EmitCond(IR::Cond cond); Xbyak::Label EmitCond(IR::Cond cond);
void EmitCondPrelude(const IR::Block& block);
BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, size_t size); 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); void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target);
// Terminal instruction emitters // Terminal instruction emitters
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location); void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
virtual void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) = 0; 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) = 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) = 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) = 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) = 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) = 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) = 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) = 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) = 0; virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0;
// Patching // Patching
struct PatchInformation { struct PatchInformation {

View file

@ -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) { IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor}; IR::Block block{descriptor};
ArmTranslatorVisitor visitor{block, descriptor, options}; ArmTranslatorVisitor visitor{block, descriptor, options};
const bool single_step = descriptor.SingleStepping();
bool should_continue = true; bool should_continue = true;
do { do {
const u32 arm_pc = visitor.ir.current_location.PC(); const u32 arm_pc = visitor.ir.current_location.PC();

View file

@ -55,10 +55,11 @@ std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFu
} // local namespace } // local namespace
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor}; IR::Block block{descriptor};
ThumbTranslatorVisitor visitor{block, descriptor, options}; ThumbTranslatorVisitor visitor{block, descriptor, options};
const bool single_step = descriptor.SingleStepping();
bool should_continue = true; bool should_continue = true;
do { do {
const u32 arm_pc = visitor.ir.current_location.PC(); const u32 arm_pc = visitor.ir.current_location.PC();

View file

@ -13,10 +13,11 @@
namespace Dynarmic::A64 { namespace Dynarmic::A64 {
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) { IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor}; IR::Block block{descriptor};
TranslatorVisitor visitor{block, descriptor, std::move(options)}; TranslatorVisitor visitor{block, descriptor, std::move(options)};
const bool single_step = descriptor.SingleStepping();
bool should_continue = true; bool should_continue = true;
do { do {
const u64 pc = visitor.ir.current_location->PC(); const u64 pc = visitor.ir.current_location->PC();

View file

@ -7,6 +7,7 @@
#include <dynarmic/A32/a32.h> #include <dynarmic/A32/a32.h>
#include "A32/testenv.h" #include "A32/testenv.h"
#include "frontend/A32/location_descriptor.h"
using namespace Dynarmic; using namespace Dynarmic;
@ -238,3 +239,189 @@ TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") {
REQUIRE(jit.Regs()[15] == 0x0000000c); REQUIRE(jit.Regs()[15] == 0x0000000c);
REQUIRE(jit.Cpsr() == 0x000001d0); 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);
}