A64: Add Step
Allow for stepping instruction-by-instruction
This commit is contained in:
parent
53e23efcef
commit
f69c77391e
11 changed files with 108 additions and 32 deletions
|
@ -30,6 +30,12 @@ public:
|
||||||
*/
|
*/
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step the emulated CPU for one instruction.
|
||||||
|
* Cannot be recursively called.
|
||||||
|
*/
|
||||||
|
void Step();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the code cache of all compiled code.
|
* Clears the code cache of all compiled code.
|
||||||
* Can be called at any time. Halts execution if called within a callback.
|
* Can be called at any time. Halts execution if called within a callback.
|
||||||
|
|
|
@ -291,11 +291,11 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||||
// This calculation has to match up with A64::LocationDescriptor::UniqueHash
|
// This calculation has to match up with A64::LocationDescriptor::UniqueHash
|
||||||
// TODO: Optimization is available here based on known state of fpcr.
|
// TODO: Optimization is available here based on known state of fpcr.
|
||||||
code.mov(rbp, qword[r15 + offsetof(A64JitState, pc)]);
|
code.mov(rbp, qword[r15 + offsetof(A64JitState, pc)]);
|
||||||
code.mov(rcx, A64::LocationDescriptor::PC_MASK);
|
code.mov(rcx, A64::LocationDescriptor::pc_mask);
|
||||||
code.and_(rcx, rbp);
|
code.and_(rcx, rbp);
|
||||||
code.mov(ebx, dword[r15 + offsetof(A64JitState, fpcr)]);
|
code.mov(ebx, dword[r15 + offsetof(A64JitState, fpcr)]);
|
||||||
code.and_(ebx, A64::LocationDescriptor::FPCR_MASK);
|
code.and_(ebx, A64::LocationDescriptor::fpcr_mask);
|
||||||
code.shl(ebx, 37);
|
code.shl(ebx, A64::LocationDescriptor::fpcr_shift);
|
||||||
code.or_(rbx, rcx);
|
code.or_(rbx, rcx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -54,13 +54,28 @@ public:
|
||||||
|
|
||||||
// TODO: Check code alignment
|
// TODO: Check code alignment
|
||||||
|
|
||||||
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
const CodePtr current_code_ptr = [this]{
|
||||||
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
// RSB optimization
|
||||||
jit_state.rsb_ptr = new_rsb_ptr;
|
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
|
||||||
block_of_code.RunCodeFrom(&jit_state, reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]));
|
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
|
||||||
} else {
|
jit_state.rsb_ptr = new_rsb_ptr;
|
||||||
block_of_code.RunCode(&jit_state);
|
return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return GetCurrentBlock();
|
||||||
|
}();
|
||||||
|
block_of_code.RunCodeFrom(&jit_state, current_code_ptr);
|
||||||
|
|
||||||
|
PerformRequestedCacheInvalidation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Step() {
|
||||||
|
ASSERT(!is_executing);
|
||||||
|
is_executing = true;
|
||||||
|
SCOPE_EXIT { this->is_executing = false; };
|
||||||
|
jit_state.halt_requested = true;
|
||||||
|
|
||||||
|
block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
|
||||||
|
|
||||||
PerformRequestedCacheInvalidation();
|
PerformRequestedCacheInvalidation();
|
||||||
}
|
}
|
||||||
|
@ -185,9 +200,19 @@ private:
|
||||||
return this_->GetCurrentBlock();
|
return this_->GetCurrentBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
CodePtr GetCurrentBlock() {
|
IR::LocationDescriptor GetCurrentLocation() const {
|
||||||
IR::LocationDescriptor current_location{jit_state.GetUniqueHash()};
|
return IR::LocationDescriptor{jit_state.GetUniqueHash()};
|
||||||
|
}
|
||||||
|
|
||||||
|
CodePtr GetCurrentBlock() {
|
||||||
|
return GetBlock(GetCurrentLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
CodePtr GetCurrentSingleStep() {
|
||||||
|
return GetBlock(A64::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
CodePtr GetBlock(IR::LocationDescriptor current_location) {
|
||||||
if (auto block = emitter.GetBasicBlock(current_location))
|
if (auto block = emitter.GetBasicBlock(current_location))
|
||||||
return block->entrypoint;
|
return block->entrypoint;
|
||||||
|
|
||||||
|
@ -256,6 +281,10 @@ void Jit::Run() {
|
||||||
impl->Run();
|
impl->Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Jit::Step() {
|
||||||
|
impl->Step();
|
||||||
|
}
|
||||||
|
|
||||||
void Jit::ClearCache() {
|
void Jit::ClearCache() {
|
||||||
impl->ClearCache();
|
impl->ClearCache();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,6 @@
|
||||||
|
|
||||||
namespace Dynarmic::BackendX64 {
|
namespace Dynarmic::BackendX64 {
|
||||||
|
|
||||||
u64 A64JitState::GetUniqueHash() const noexcept {
|
|
||||||
const u64 fpcr_u64 = static_cast<u64>(fpcr & A64::LocationDescriptor::FPCR_MASK) << 37;
|
|
||||||
const u64 pc_u64 = pc & A64::LocationDescriptor::PC_MASK;
|
|
||||||
return pc_u64 | fpcr_u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comparing MXCSR and FPCR
|
* Comparing MXCSR and FPCR
|
||||||
* ========================
|
* ========================
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <xbyak.h>
|
#include <xbyak.h>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/A64/location_descriptor.h"
|
||||||
|
|
||||||
namespace Dynarmic::BackendX64 {
|
namespace Dynarmic::BackendX64 {
|
||||||
|
|
||||||
|
@ -79,7 +80,11 @@ struct A64JitState {
|
||||||
void SetFpcr(u32 value);
|
void SetFpcr(u32 value);
|
||||||
void SetFpsr(u32 value);
|
void SetFpsr(u32 value);
|
||||||
|
|
||||||
u64 GetUniqueHash() const noexcept;
|
u64 GetUniqueHash() const noexcept {
|
||||||
|
const u64 fpcr_u64 = static_cast<u64>(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
|
||||||
|
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
|
||||||
|
return pc_u64 | fpcr_u64;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
|
@ -142,6 +142,10 @@ void BlockOfCode::RunCodeFrom(void* jit_state, CodePtr code_ptr) const {
|
||||||
run_code_from(jit_state, code_ptr);
|
run_code_from(jit_state, code_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlockOfCode::StepCode(void* jit_state, CodePtr code_ptr) const {
|
||||||
|
step_code(jit_state, code_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) {
|
void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) {
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
if (mxcsr_already_exited)
|
if (mxcsr_already_exited)
|
||||||
|
@ -174,6 +178,19 @@ void BlockOfCode::GenRunCode() {
|
||||||
SwitchMxcsrOnEntry();
|
SwitchMxcsrOnEntry();
|
||||||
jmp(r14);
|
jmp(r14);
|
||||||
|
|
||||||
|
align();
|
||||||
|
step_code = getCurr<RunCodeFromFuncType>();
|
||||||
|
|
||||||
|
ABI_PushCalleeSaveRegistersAndAdjustStack(*this);
|
||||||
|
|
||||||
|
mov(r15, ABI_PARAM1);
|
||||||
|
|
||||||
|
mov(qword[r15 + jsi.offsetof_cycles_to_run], 1);
|
||||||
|
mov(qword[r15 + jsi.offsetof_cycles_remaining], 1);
|
||||||
|
|
||||||
|
SwitchMxcsrOnEntry();
|
||||||
|
jmp(ABI_PARAM2);
|
||||||
|
|
||||||
align();
|
align();
|
||||||
run_code = getCurr<RunCodeFuncType>();
|
run_code = getCurr<RunCodeFuncType>();
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,8 @@ public:
|
||||||
void RunCode(void* jit_state) const;
|
void RunCode(void* jit_state) const;
|
||||||
/// Runs emulated code from code_ptr.
|
/// Runs emulated code from code_ptr.
|
||||||
void RunCodeFrom(void* jit_state, CodePtr code_ptr) const;
|
void RunCodeFrom(void* jit_state, CodePtr code_ptr) const;
|
||||||
|
/// Runs emulated code from code_ptr for a single cycle.
|
||||||
|
void StepCode(void* jit_state, CodePtr code_ptr) const;
|
||||||
/// Code emitter: Returns to dispatcher
|
/// Code emitter: Returns to dispatcher
|
||||||
void ReturnFromRunCode(bool mxcsr_already_exited = false);
|
void ReturnFromRunCode(bool mxcsr_already_exited = false);
|
||||||
/// Code emitter: Returns to dispatcher, forces return to host
|
/// Code emitter: Returns to dispatcher, forces return to host
|
||||||
|
@ -158,6 +160,7 @@ private:
|
||||||
using RunCodeFuncType = void(*)(void*);
|
using RunCodeFuncType = void(*)(void*);
|
||||||
using RunCodeFromFuncType = void(*)(void*, CodePtr);
|
using RunCodeFromFuncType = void(*)(void*, CodePtr);
|
||||||
RunCodeFuncType run_code = nullptr;
|
RunCodeFuncType run_code = nullptr;
|
||||||
|
RunCodeFromFuncType step_code = nullptr;
|
||||||
RunCodeFromFuncType run_code_from = nullptr;
|
RunCodeFromFuncType run_code_from = nullptr;
|
||||||
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;
|
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;
|
||||||
static constexpr size_t FORCE_RETURN = 1 << 1;
|
static constexpr size_t FORCE_RETURN = 1 << 1;
|
||||||
|
|
|
@ -23,7 +23,7 @@ constexpr size_t BitSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T Ones(size_t count) {
|
constexpr T Ones(size_t count) {
|
||||||
ASSERT_MSG(count <= BitSize<T>(), "count larger than bitsize of T");
|
ASSERT_MSG(count <= BitSize<T>(), "count larger than bitsize of T");
|
||||||
if (count == BitSize<T>())
|
if (count == BitSize<T>())
|
||||||
return static_cast<T>(~static_cast<T>(0));
|
return static_cast<T>(~static_cast<T>(0));
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
namespace Dynarmic::A64 {
|
namespace Dynarmic::A64 {
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) {
|
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) {
|
||||||
o << fmt::format("{{{}, {}}}", descriptor.PC(), descriptor.FPCR().Value());
|
o << fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : "");
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,19 +24,29 @@ namespace Dynarmic::A64 {
|
||||||
*/
|
*/
|
||||||
class LocationDescriptor {
|
class LocationDescriptor {
|
||||||
public:
|
public:
|
||||||
static constexpr u64 PC_MASK = 0x00FF'FFFF'FFFF'FFFFull;
|
static constexpr size_t pc_bit_count = 56;
|
||||||
static constexpr u32 FPCR_MASK = 0x07C8'0000;
|
static constexpr u64 pc_mask = Common::Ones<u64>(pc_bit_count);
|
||||||
|
static constexpr u32 fpcr_mask = 0x07C8'0000;
|
||||||
|
static constexpr size_t fpcr_shift = 37;
|
||||||
|
static constexpr size_t single_stepping_bit = 57;
|
||||||
|
static_assert((pc_mask & (u64(fpcr_mask) << fpcr_shift) & (u64(1) << single_stepping_bit)) == 0);
|
||||||
|
|
||||||
LocationDescriptor(u64 pc, FP::FPCR fpcr) : pc(pc & PC_MASK), fpcr(fpcr.Value() & FPCR_MASK) {}
|
LocationDescriptor(u64 pc, FP::FPCR fpcr, bool single_stepping = false)
|
||||||
|
: pc(pc & pc_mask), fpcr(fpcr.Value() & fpcr_mask), single_stepping(single_stepping)
|
||||||
|
{}
|
||||||
|
|
||||||
explicit LocationDescriptor(const IR::LocationDescriptor& o)
|
explicit LocationDescriptor(const IR::LocationDescriptor& o)
|
||||||
: pc(o.Value() & PC_MASK), fpcr((o.Value() >> 37) & FPCR_MASK) {}
|
: pc(o.Value() & pc_mask)
|
||||||
|
, fpcr((o.Value() >> fpcr_shift) & fpcr_mask)
|
||||||
|
, single_stepping(Common::Bit<single_stepping_bit>(o.Value()))
|
||||||
|
{}
|
||||||
|
|
||||||
u64 PC() const { return Common::SignExtend<56>(pc); }
|
u64 PC() const { return Common::SignExtend<pc_bit_count>(pc); }
|
||||||
FP::FPCR FPCR() const { return fpcr; }
|
FP::FPCR FPCR() const { return fpcr; }
|
||||||
|
bool SingleStepping() const { return single_stepping; }
|
||||||
|
|
||||||
bool operator == (const LocationDescriptor& o) const {
|
bool operator == (const LocationDescriptor& o) const {
|
||||||
return std::tie(pc, fpcr) == std::tie(o.pc, o.fpcr);
|
return std::tie(pc, fpcr, single_stepping) == std::tie(o.pc, o.fpcr, single_stepping);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator != (const LocationDescriptor& o) const {
|
bool operator != (const LocationDescriptor& o) const {
|
||||||
|
@ -44,18 +54,23 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationDescriptor SetPC(u64 new_pc) const {
|
LocationDescriptor SetPC(u64 new_pc) const {
|
||||||
return LocationDescriptor(new_pc, fpcr);
|
return LocationDescriptor(new_pc, fpcr, single_stepping);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocationDescriptor AdvancePC(int amount) const {
|
LocationDescriptor AdvancePC(int amount) const {
|
||||||
return LocationDescriptor(static_cast<u64>(pc + amount), fpcr);
|
return LocationDescriptor(static_cast<u64>(pc + amount), fpcr, single_stepping);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
|
||||||
|
return LocationDescriptor(pc, fpcr, new_single_stepping);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 UniqueHash() const noexcept {
|
u64 UniqueHash() const noexcept {
|
||||||
// This value MUST BE UNIQUE.
|
// This value MUST BE UNIQUE.
|
||||||
// This calculation has to match up with EmitTerminalPopRSBHint
|
// This calculation has to match up with EmitTerminalPopRSBHint
|
||||||
const u64 fpcr_u64 = static_cast<u64>(fpcr.Value()) << 37;
|
const u64 fpcr_u64 = static_cast<u64>(fpcr.Value()) << fpcr_shift;
|
||||||
return pc | fpcr_u64;
|
const u64 single_stepping_u64 = static_cast<u64>(single_stepping) << single_stepping_bit;
|
||||||
|
return pc | fpcr_u64 | single_stepping_u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator IR::LocationDescriptor() const {
|
operator IR::LocationDescriptor() const {
|
||||||
|
@ -65,6 +80,7 @@ public:
|
||||||
private:
|
private:
|
||||||
u64 pc; ///< Current program counter value.
|
u64 pc; ///< Current program counter value.
|
||||||
FP::FPCR fpcr; ///< Floating point control register.
|
FP::FPCR fpcr; ///< Floating point control register.
|
||||||
|
bool single_stepping;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "frontend/A64/translate/impl/impl.h"
|
#include "frontend/A64/translate/impl/impl.h"
|
||||||
#include "frontend/A64/translate/translate.h"
|
#include "frontend/A64/translate/translate.h"
|
||||||
#include "frontend/ir/basic_block.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/terminal.h"
|
||||||
|
|
||||||
namespace Dynarmic::A64 {
|
namespace Dynarmic::A64 {
|
||||||
|
|
||||||
|
@ -16,8 +17,9 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory
|
||||||
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;
|
||||||
while (should_continue) {
|
do {
|
||||||
const u64 pc = visitor.ir.current_location->PC();
|
const u64 pc = visitor.ir.current_location->PC();
|
||||||
const u32 instruction = memory_read_code(pc);
|
const u32 instruction = memory_read_code(pc);
|
||||||
|
|
||||||
|
@ -29,6 +31,10 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory
|
||||||
|
|
||||||
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
||||||
block.CycleCount()++;
|
block.CycleCount()++;
|
||||||
|
} while (should_continue && !single_step);
|
||||||
|
|
||||||
|
if (single_step && should_continue) {
|
||||||
|
visitor.ir.SetTerm(IR::Term::LinkBlock{*visitor.ir.current_location});
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");
|
ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");
|
||||||
|
|
Loading…
Reference in a new issue