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:
parent
591e7667f2
commit
94d0d33e02
12 changed files with 333 additions and 103 deletions
|
@ -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.
|
||||
|
|
|
@ -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<HostLoc> gpr_order = [this]{
|
||||
std::vector<HostLoc> 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<A32JitState>, 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<size_t>(code.getCurr() - entrypoint);
|
||||
|
@ -159,6 +163,20 @@ void A32EmitX64::InvalidateCacheRanges(const boost::icl::interval_set<u32>& 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<u32>(desc.Value() >> 32);
|
||||
return static_cast<u32>(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) {
|
||||
|
|
|
@ -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<u32> 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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<A64JitState>, 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<A64JitState>, 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<size_t>(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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<void>(terminal, [this, &initial_location](auto x) {
|
||||
void EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Common::VisitVariant<void>(terminal, [this, initial_location, is_single_step](auto x) {
|
||||
using T = std::decay_t<decltype(x)>;
|
||||
if constexpr (!std::is_same_v<T, IR::Term::Invalid>) {
|
||||
this->EmitTerminalImpl(x, initial_location);
|
||||
this->EmitTerminalImpl(x, initial_location, is_single_step);
|
||||
} else {
|
||||
ASSERT_MSG(false, "Invalid terminal");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -55,10 +55,11 @@ std::tuple<u32, ThumbInstSize> 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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <dynarmic/A32/a32.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue