Final A32 refactor

This commit is contained in:
MerryMage 2018-01-04 21:12:02 +00:00
parent 455757d7b6
commit 9d15e0a8e1
29 changed files with 447 additions and 342 deletions

View file

@ -11,27 +11,29 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <dynarmic/callbacks.h> #include <dynarmic/A32/callbacks.h>
namespace Dynarmic { namespace Dynarmic {
struct Context;
namespace IR { namespace IR {
class LocationDescriptor; class LocationDescriptor;
} }
}
namespace Dynarmic {
namespace A32 {
struct Context;
class Jit final { class Jit final {
public: public:
explicit Jit(Dynarmic::UserCallbacks callbacks); explicit Jit(UserCallbacks callbacks);
~Jit(); ~Jit();
/** /**
* Runs the emulated CPU for about cycle_count cycles. * Runs the emulated CPU.
* Cannot be recursively called. * Cannot be recursively called.
* @param cycle_count Estimated number of cycles to run the CPU for.
*/ */
void Run(std::size_t cycle_count); void Run();
/** /**
* Clears the code cache of all compiled code. * Clears the code cache of all compiled code.
@ -97,4 +99,5 @@ private:
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
}; };
} // namespace A32
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -12,6 +12,7 @@
#include <memory> #include <memory>
namespace Dynarmic { namespace Dynarmic {
namespace A32 {
class Coprocessor; class Coprocessor;
class Jit; class Jit;
@ -66,4 +67,5 @@ struct UserCallbacks {
std::array<std::shared_ptr<Coprocessor>, 16> coprocessors; std::array<std::shared_ptr<Coprocessor>, 16> coprocessors;
}; };
} // namespace A32
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -11,6 +11,7 @@
#include <memory> #include <memory>
namespace Dynarmic { namespace Dynarmic {
namespace A32 {
struct Context { struct Context {
public: public:
@ -41,4 +42,5 @@ private:
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
}; };
} // namespace A32
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -11,9 +11,10 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <dynarmic/coprocessor_util.h> #include <dynarmic/A32/coprocessor_util.h>
namespace Dynarmic { namespace Dynarmic {
namespace A32 {
class Jit; class Jit;
@ -21,8 +22,6 @@ class Coprocessor {
public: public:
virtual ~Coprocessor() = default; virtual ~Coprocessor() = default;
using CoprocReg = A32::CoprocReg;
struct Callback { struct Callback {
/** /**
* @param jit CPU state * @param jit CPU state
@ -109,4 +108,5 @@ public:
virtual boost::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, boost::optional<std::uint8_t> option) = 0; virtual boost::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, boost::optional<std::uint8_t> option) = 0;
}; };
} // namespace A32
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -10,10 +10,10 @@
#include <string> #include <string>
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace A32 {
std::string DisassembleArm(std::uint32_t instruction); std::string DisassembleArm(std::uint32_t instruction);
std::string DisassembleThumb16(std::uint16_t instruction); std::string DisassembleThumb16(std::uint16_t instruction);
} // namespace Arm } // namespace A32
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -1,9 +1,9 @@
add_library(dynarmic add_library(dynarmic
../include/dynarmic/callbacks.h ../include/dynarmic/A32/a32.h
../include/dynarmic/coprocessor.h ../include/dynarmic/A32/callbacks.h
../include/dynarmic/coprocessor_util.h ../include/dynarmic/A32/coprocessor.h
../include/dynarmic/disassembler.h ../include/dynarmic/A32/coprocessor_util.h
../include/dynarmic/dynarmic.h ../include/dynarmic/A32/disassembler.h
common/address_range.h common/address_range.h
common/assert.h common/assert.h
common/bit_util.h common/bit_util.h
@ -77,6 +77,7 @@ if (ARCHITECTURE_x86_64)
target_sources(dynarmic PRIVATE target_sources(dynarmic PRIVATE
backend_x64/a32_emit_x64.cpp backend_x64/a32_emit_x64.cpp
backend_x64/a32_emit_x64.h backend_x64/a32_emit_x64.h
backend_x64/a32_interface.cpp
backend_x64/a32_jitstate.cpp backend_x64/a32_jitstate.cpp
backend_x64/a32_jitstate.h backend_x64/a32_jitstate.h
backend_x64/abi.cpp backend_x64/abi.cpp
@ -89,7 +90,7 @@ if (ARCHITECTURE_x86_64)
backend_x64/emit_x64.h backend_x64/emit_x64.h
backend_x64/hostloc.cpp backend_x64/hostloc.cpp
backend_x64/hostloc.h backend_x64/hostloc.h
backend_x64/interface_x64.cpp backend_x64/jitstate_info.h
backend_x64/oparg.h backend_x64/oparg.h
backend_x64/reg_alloc.cpp backend_x64/reg_alloc.cpp
backend_x64/reg_alloc.h backend_x64/reg_alloc.h

View file

@ -7,7 +7,7 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <dynarmic/coprocessor.h> #include <dynarmic/A32/coprocessor.h>
#include "backend_x64/a32_emit_x64.h" #include "backend_x64/a32_emit_x64.h"
#include "backend_x64/a32_jitstate.h" #include "backend_x64/a32_jitstate.h"
@ -68,8 +68,12 @@ bool A32EmitContext::FPSCR_DN() const {
return Location().FPSCR().DN(); return Location().FPSCR().DN();
} }
A32EmitX64::A32EmitX64(BlockOfCode* code, UserCallbacks cb, Jit* jit_interface) A32EmitX64::A32EmitX64(BlockOfCode* code, A32::UserCallbacks cb, A32::Jit* jit_interface)
: EmitX64(code, cb, jit_interface) {} : EmitX64(code), cb(cb), jit_interface(jit_interface)
{
GenMemoryAccessors();
code->PreludeComplete();
}
A32EmitX64::~A32EmitX64() {} A32EmitX64::~A32EmitX64() {}
@ -80,7 +84,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
// Start emitting. // Start emitting.
EmitCondPrelude(block); EmitCondPrelude(block);
RegAlloc reg_alloc{code}; RegAlloc reg_alloc{code, A32JitState::SpillCount, SpillToOpArg<A32JitState>};
A32EmitContext ctx{reg_alloc, block}; A32EmitContext ctx{reg_alloc, block};
for (auto iter = block.begin(); iter != block.end(); ++iter) { for (auto iter = block.begin(); iter != block.end(); ++iter) {
@ -128,6 +132,64 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
return block_desc; return block_desc;
} }
void A32EmitX64::GenMemoryAccessors() {
code->align();
read_memory_8 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read8);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
read_memory_16 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read16);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
read_memory_32 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read32);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
read_memory_64 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read64);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
write_memory_8 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write8);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
write_memory_16 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write16);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
write_memory_32 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write32);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
code->align();
write_memory_64 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write64);
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret();
}
void A32EmitX64::EmitA32GetRegister(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32GetRegister(A32EmitContext& ctx, IR::Inst* inst) {
A32::Reg reg = inst->GetArg(0).GetA32RegRef(); A32::Reg reg = inst->GetArg(0).GetA32RegRef();
@ -560,13 +622,13 @@ void A32EmitX64::EmitA32SetExclusive(A32EmitContext& ctx, IR::Inst* inst) {
code->mov(dword[r15 + offsetof(A32JitState, exclusive_address)], address); code->mov(dword[r15 + offsetof(A32JitState, exclusive_address)], address);
} }
template <typename FunctionPointer> template <typename RawFn>
static void ReadMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, UserCallbacks& cb, size_t bit_size, FunctionPointer fn) { static void ReadMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, const A32::UserCallbacks& cb, size_t bit_size, RawFn raw_fn, const CodePtr wrapped_fn) {
auto args = reg_alloc.GetArgumentInfo(inst); auto args = reg_alloc.GetArgumentInfo(inst);
if (!cb.page_table) { if (!cb.page_table) {
reg_alloc.HostCall(inst, args[0]); reg_alloc.HostCall(inst, args[0]);
code->CallFunction(fn); code->CallFunction(raw_fn);
return; return;
} }
@ -606,19 +668,19 @@ static void ReadMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, U
} }
code->jmp(end); code->jmp(end);
code->L(abort); code->L(abort);
code->call(code->GetMemoryReadCallback(bit_size)); code->call(wrapped_fn);
code->L(end); code->L(end);
reg_alloc.DefineValue(inst, result); reg_alloc.DefineValue(inst, result);
} }
template<typename FunctionPointer> template <typename RawFn>
static void WriteMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, UserCallbacks& cb, size_t bit_size, FunctionPointer fn) { static void WriteMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, const A32::UserCallbacks& cb, size_t bit_size, RawFn raw_fn, const CodePtr wrapped_fn) {
auto args = reg_alloc.GetArgumentInfo(inst); auto args = reg_alloc.GetArgumentInfo(inst);
if (!cb.page_table) { if (!cb.page_table) {
reg_alloc.HostCall(nullptr, args[0], args[1]); reg_alloc.HostCall(nullptr, args[0], args[1]);
code->CallFunction(fn); code->CallFunction(raw_fn);
return; return;
} }
@ -660,40 +722,40 @@ static void WriteMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst,
} }
code->jmp(end); code->jmp(end);
code->L(abort); code->L(abort);
code->call(code->GetMemoryWriteCallback(bit_size)); code->call(wrapped_fn);
code->L(end); code->L(end);
} }
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
ReadMemory(code, ctx.reg_alloc, inst, cb, 8, cb.memory.Read8); ReadMemory(code, ctx.reg_alloc, inst, cb, 8, cb.memory.Read8, read_memory_8);
} }
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
ReadMemory(code, ctx.reg_alloc, inst, cb, 16, cb.memory.Read16); ReadMemory(code, ctx.reg_alloc, inst, cb, 16, cb.memory.Read16, read_memory_16);
} }
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
ReadMemory(code, ctx.reg_alloc, inst, cb, 32, cb.memory.Read32); ReadMemory(code, ctx.reg_alloc, inst, cb, 32, cb.memory.Read32, read_memory_32);
} }
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
ReadMemory(code, ctx.reg_alloc, inst, cb, 64, cb.memory.Read64); ReadMemory(code, ctx.reg_alloc, inst, cb, 64, cb.memory.Read64, read_memory_64);
} }
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
WriteMemory(code, ctx.reg_alloc, inst, cb, 8, cb.memory.Write8); WriteMemory(code, ctx.reg_alloc, inst, cb, 8, cb.memory.Write8, write_memory_8);
} }
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
WriteMemory(code, ctx.reg_alloc, inst, cb, 16, cb.memory.Write16); WriteMemory(code, ctx.reg_alloc, inst, cb, 16, cb.memory.Write16, write_memory_16);
} }
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
WriteMemory(code, ctx.reg_alloc, inst, cb, 32, cb.memory.Write32); WriteMemory(code, ctx.reg_alloc, inst, cb, 32, cb.memory.Write32, write_memory_32);
} }
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
WriteMemory(code, ctx.reg_alloc, inst, cb, 64, cb.memory.Write64); WriteMemory(code, ctx.reg_alloc, inst, cb, 64, cb.memory.Write64, write_memory_64);
} }
template <typename FunctionPointer> template <typename FunctionPointer>
@ -749,7 +811,7 @@ static void EmitCoprocessorException() {
ASSERT_MSG(false, "Should raise coproc exception here"); ASSERT_MSG(false, "Should raise coproc exception here");
} }
static void CallCoprocCallback(BlockOfCode* code, RegAlloc& reg_alloc, Jit* jit_interface, Coprocessor::Callback callback, IR::Inst* inst = nullptr, boost::optional<Argument&> arg0 = {}, boost::optional<Argument&> arg1 = {}) { static void CallCoprocCallback(BlockOfCode* code, RegAlloc& reg_alloc, A32::Jit* jit_interface, A32::Coprocessor::Callback callback, IR::Inst* inst = nullptr, boost::optional<Argument&> arg0 = {}, boost::optional<Argument&> arg1 = {}) {
reg_alloc.HostCall(inst, {}, {}, arg0, arg1); reg_alloc.HostCall(inst, {}, {}, arg0, arg1);
code->mov(code->ABI_PARAM1, reinterpret_cast<u64>(jit_interface)); code->mov(code->ABI_PARAM1, reinterpret_cast<u64>(jit_interface));
@ -771,7 +833,7 @@ void A32EmitX64::EmitA32CoprocInternalOperation(A32EmitContext& ctx, IR::Inst* i
A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[5]); A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[5]);
unsigned opc2 = static_cast<unsigned>(coproc_info[6]); unsigned opc2 = static_cast<unsigned>(coproc_info[6]);
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -797,7 +859,7 @@ void A32EmitX64::EmitA32CoprocSendOneWord(A32EmitContext& ctx, IR::Inst* inst) {
A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[4]); A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[4]);
unsigned opc2 = static_cast<unsigned>(coproc_info[5]); unsigned opc2 = static_cast<unsigned>(coproc_info[5]);
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -809,7 +871,7 @@ void A32EmitX64::EmitA32CoprocSendOneWord(A32EmitContext& ctx, IR::Inst* inst) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
case 1: case 1:
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<Coprocessor::Callback>(action), nullptr, args[1]); CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<A32::Coprocessor::Callback>(action), nullptr, args[1]);
return; return;
case 2: { case 2: {
u32* destination_ptr = boost::get<u32*>(action); u32* destination_ptr = boost::get<u32*>(action);
@ -836,7 +898,7 @@ void A32EmitX64::EmitA32CoprocSendTwoWords(A32EmitContext& ctx, IR::Inst* inst)
unsigned opc = static_cast<unsigned>(coproc_info[2]); unsigned opc = static_cast<unsigned>(coproc_info[2]);
A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[3]); A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[3]);
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -848,7 +910,7 @@ void A32EmitX64::EmitA32CoprocSendTwoWords(A32EmitContext& ctx, IR::Inst* inst)
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
case 1: case 1:
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<Coprocessor::Callback>(action), nullptr, args[1], args[2]); CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<A32::Coprocessor::Callback>(action), nullptr, args[1], args[2]);
return; return;
case 2: { case 2: {
auto destination_ptrs = boost::get<std::array<u32*, 2>>(action); auto destination_ptrs = boost::get<std::array<u32*, 2>>(action);
@ -879,7 +941,7 @@ void A32EmitX64::EmitA32CoprocGetOneWord(A32EmitContext& ctx, IR::Inst* inst) {
A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[4]); A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[4]);
unsigned opc2 = static_cast<unsigned>(coproc_info[5]); unsigned opc2 = static_cast<unsigned>(coproc_info[5]);
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -891,7 +953,7 @@ void A32EmitX64::EmitA32CoprocGetOneWord(A32EmitContext& ctx, IR::Inst* inst) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
case 1: case 1:
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<Coprocessor::Callback>(action), inst); CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<A32::Coprocessor::Callback>(action), inst);
return; return;
case 2: { case 2: {
u32* source_ptr = boost::get<u32*>(action); u32* source_ptr = boost::get<u32*>(action);
@ -919,7 +981,7 @@ void A32EmitX64::EmitA32CoprocGetTwoWords(A32EmitContext& ctx, IR::Inst* inst) {
unsigned opc = coproc_info[2]; unsigned opc = coproc_info[2];
A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[3]); A32::CoprocReg CRm = static_cast<A32::CoprocReg>(coproc_info[3]);
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -931,7 +993,7 @@ void A32EmitX64::EmitA32CoprocGetTwoWords(A32EmitContext& ctx, IR::Inst* inst) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
case 1: case 1:
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<Coprocessor::Callback>(action), inst); CallCoprocCallback(code, ctx.reg_alloc, jit_interface, boost::get<A32::Coprocessor::Callback>(action), inst);
return; return;
case 2: { case 2: {
auto source_ptrs = boost::get<std::array<u32*, 2>>(action); auto source_ptrs = boost::get<std::array<u32*, 2>>(action);
@ -967,7 +1029,7 @@ void A32EmitX64::EmitA32CoprocLoadWords(A32EmitContext& ctx, IR::Inst* inst) {
bool has_option = coproc_info[4] != 0; bool has_option = coproc_info[4] != 0;
boost::optional<u8> option{has_option, coproc_info[5]}; boost::optional<u8> option{has_option, coproc_info[5]};
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -993,7 +1055,7 @@ void A32EmitX64::EmitA32CoprocStoreWords(A32EmitContext& ctx, IR::Inst* inst) {
bool has_option = coproc_info[4] != 0; bool has_option = coproc_info[4] != 0;
boost::optional<u8> option{has_option, coproc_info[5]}; boost::optional<u8> option{has_option, coproc_info[5]};
std::shared_ptr<Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = cb.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;

View file

@ -15,9 +15,11 @@
#include <xbyak_util.h> #include <xbyak_util.h>
#include "backend_x64/a32_jitstate.h"
#include "backend_x64/emit_x64.h" #include "backend_x64/emit_x64.h"
#include "common/address_range.h" #include "common/address_range.h"
#include "dynarmic/callbacks.h" #include "dynarmic/A32/a32.h"
#include "dynarmic/A32/callbacks.h"
#include "frontend/A32/location_descriptor.h" #include "frontend/A32/location_descriptor.h"
#include "frontend/ir/terminal.h" #include "frontend/ir/terminal.h"
@ -36,7 +38,7 @@ struct A32EmitContext final : public EmitContext {
class A32EmitX64 final : public EmitX64<A32JitState> { class A32EmitX64 final : public EmitX64<A32JitState> {
public: public:
A32EmitX64(BlockOfCode* code, UserCallbacks cb, Jit* jit_interface); A32EmitX64(BlockOfCode* code, A32::UserCallbacks cb, A32::Jit* jit_interface);
~A32EmitX64(); ~A32EmitX64();
/** /**
@ -46,6 +48,19 @@ public:
BlockDescriptor Emit(IR::Block& ir); BlockDescriptor Emit(IR::Block& ir);
protected: protected:
const A32::UserCallbacks cb;
A32::Jit* jit_interface;
const void* read_memory_8;
const void* read_memory_16;
const void* read_memory_32;
const void* read_memory_64;
const void* write_memory_8;
const void* write_memory_16;
const void* write_memory_32;
const void* write_memory_64;
void GenMemoryAccessors();
// Microinstruction emitters // Microinstruction emitters
#define OPCODE(...) #define OPCODE(...)
#define A32OPC(name, type, ...) void EmitA32##name(A32EmitContext& ctx, IR::Inst* inst); #define A32OPC(name, type, ...) void EmitA32##name(A32EmitContext& ctx, IR::Inst* inst);

View file

@ -17,41 +17,51 @@
#include "backend_x64/a32_emit_x64.h" #include "backend_x64/a32_emit_x64.h"
#include "backend_x64/a32_jitstate.h" #include "backend_x64/a32_jitstate.h"
#include "backend_x64/block_of_code.h" #include "backend_x64/block_of_code.h"
#include "backend_x64/jitstate_info.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "dynarmic/context.h" #include "dynarmic/A32/a32.h"
#include "dynarmic/dynarmic.h" #include "dynarmic/A32/context.h"
#include "frontend/A32/translate/translate.h" #include "frontend/A32/translate/translate.h"
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "frontend/ir/location_descriptor.h" #include "frontend/ir/location_descriptor.h"
#include "ir_opt/passes.h" #include "ir_opt/passes.h"
namespace Dynarmic { namespace Dynarmic {
namespace A32 {
using namespace BackendX64; using namespace BackendX64;
RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg) {
return RunCodeCallbacks{
LookupBlock,
arg,
cb.AddTicks,
cb.GetTicksRemaining
};
}
struct Jit::Impl { struct Jit::Impl {
Impl(Jit* jit, UserCallbacks callbacks) Impl(Jit* jit, A32::UserCallbacks callbacks)
: block_of_code(callbacks, &GetCurrentBlock, this) : block_of_code(GenRunCodeCallbacks(callbacks, &GetCurrentBlock, this), JitStateInfo{jit_state})
, jit_state()
, emitter(&block_of_code, callbacks, jit) , emitter(&block_of_code, callbacks, jit)
, callbacks(callbacks) , callbacks(callbacks)
, jit_interface(jit) , jit_interface(jit)
{} {}
BlockOfCode block_of_code;
A32JitState jit_state; A32JitState jit_state;
BlockOfCode block_of_code;
A32EmitX64 emitter; A32EmitX64 emitter;
const UserCallbacks callbacks; const A32::UserCallbacks callbacks;
// Requests made during execution to invalidate the cache are queued up here. // Requests made during execution to invalidate the cache are queued up here.
size_t invalid_cache_generation = 0; size_t invalid_cache_generation = 0;
boost::icl::interval_set<u32> invalid_cache_ranges; boost::icl::interval_set<u32> invalid_cache_ranges;
bool invalidate_entire_cache = false; bool invalidate_entire_cache = false;
void Execute(size_t cycle_count) { void Execute() {
block_of_code.RunCode(&jit_state, cycle_count); block_of_code.RunCode(&jit_state);
} }
std::string Disassemble(const IR::LocationDescriptor& descriptor) { std::string Disassemble(const IR::LocationDescriptor& descriptor) {
@ -163,14 +173,14 @@ Jit::Jit(UserCallbacks callbacks) : impl(std::make_unique<Impl>(this, callbacks)
Jit::~Jit() {} Jit::~Jit() {}
void Jit::Run(size_t cycle_count) { void Jit::Run() {
ASSERT(!is_executing); ASSERT(!is_executing);
is_executing = true; is_executing = true;
SCOPE_EXIT({ this->is_executing = false; }); SCOPE_EXIT({ this->is_executing = false; });
impl->jit_state.halt_requested = false; impl->jit_state.halt_requested = false;
impl->Execute(cycle_count); impl->Execute();
impl->PerformCacheInvalidation(); impl->PerformCacheInvalidation();
} }
@ -314,4 +324,5 @@ std::string Jit::Disassemble(const IR::LocationDescriptor& descriptor) {
return impl->Disassemble(descriptor); return impl->Disassemble(descriptor);
} }
} // namespace A32
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -8,6 +8,8 @@
#include <array> #include <array>
#include <xbyak.h>
#include "common/common_types.h" #include "common/common_types.h"
namespace Dynarmic { namespace Dynarmic {
@ -15,8 +17,6 @@ namespace BackendX64 {
class BlockOfCode; class BlockOfCode;
constexpr size_t SpillCount = 64;
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable:4324) // Structure was padded due to alignment specifier #pragma warning(disable:4324) // Structure was padded due to alignment specifier
@ -41,7 +41,12 @@ struct A32JitState {
alignas(u64) std::array<u32, 64> ExtReg{}; // Extension registers. alignas(u64) std::array<u32, 64> ExtReg{}; // Extension registers.
static constexpr size_t SpillCount = 64;
std::array<u64, SpillCount> Spill{}; // Spill. std::array<u64, SpillCount> Spill{}; // Spill.
static Xbyak::Address GetSpillLocationFromIndex(size_t i) {
using namespace Xbyak::util;
return qword[r15 + offsetof(A32JitState, Spill) + i * sizeof(u64)];
}
// For internal use (See: BlockOfCode::RunCode) // For internal use (See: BlockOfCode::RunCode)
u32 guest_MXCSR = 0x00001f80; u32 guest_MXCSR = 0x00001f80;

View file

@ -13,7 +13,6 @@
#include "backend_x64/abi.h" #include "backend_x64/abi.h"
#include "backend_x64/block_of_code.h" #include "backend_x64/block_of_code.h"
#include "common/assert.h" #include "common/assert.h"
#include "dynarmic/callbacks.h"
namespace Dynarmic { namespace Dynarmic {
namespace BackendX64 { namespace BackendX64 {
@ -35,22 +34,25 @@ const Xbyak::Reg64 BlockOfCode::ABI_PARAM4 = Xbyak::util::rcx;
constexpr size_t TOTAL_CODE_SIZE = 128 * 1024 * 1024; constexpr size_t TOTAL_CODE_SIZE = 128 * 1024 * 1024;
constexpr size_t FAR_CODE_OFFSET = 100 * 1024 * 1024; constexpr size_t FAR_CODE_OFFSET = 100 * 1024 * 1024;
BlockOfCode::BlockOfCode(UserCallbacks cb, LookupBlockCallback lookup_block, void* lookup_block_arg) BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi)
: Xbyak::CodeGenerator(TOTAL_CODE_SIZE) : Xbyak::CodeGenerator(TOTAL_CODE_SIZE)
, cb(cb) , cb(cb)
, lookup_block(lookup_block) , jsi(jsi)
, lookup_block_arg(lookup_block_arg)
, constant_pool(this, 256) , constant_pool(this, 256)
{ {
GenRunCode(); GenRunCode();
GenMemoryAccessors();
exception_handler.Register(this); exception_handler.Register(this);
}
void BlockOfCode::PreludeComplete() {
prelude_complete = true;
near_code_begin = getCurr(); near_code_begin = getCurr();
far_code_begin = getCurr() + FAR_CODE_OFFSET; far_code_begin = getCurr() + FAR_CODE_OFFSET;
ClearCache(); ClearCache();
} }
void BlockOfCode::ClearCache() { void BlockOfCode::ClearCache() {
ASSERT(prelude_complete);
in_far_code = false; in_far_code = false;
near_code_ptr = near_code_begin; near_code_ptr = near_code_begin;
far_code_ptr = far_code_begin; far_code_ptr = far_code_begin;
@ -58,6 +60,7 @@ void BlockOfCode::ClearCache() {
} }
size_t BlockOfCode::SpaceRemaining() const { size_t BlockOfCode::SpaceRemaining() const {
ASSERT(prelude_complete);
// This function provides an underestimate of near-code-size but that's okay. // This function provides an underestimate of near-code-size but that's okay.
// (Why? The maximum size of near code should be measured from near_code_begin, not top_.) // (Why? The maximum size of near code should be measured from near_code_begin, not top_.)
// These are offsets from Xbyak::CodeArray::top_. // These are offsets from Xbyak::CodeArray::top_.
@ -76,20 +79,12 @@ size_t BlockOfCode::SpaceRemaining() const {
return std::min(TOTAL_CODE_SIZE - far_code_offset, FAR_CODE_OFFSET - near_code_offset); return std::min(TOTAL_CODE_SIZE - far_code_offset, FAR_CODE_OFFSET - near_code_offset);
} }
void BlockOfCode::RunCode(A32JitState* jit_state, size_t cycles_to_run) const { void BlockOfCode::RunCode(void* jit_state) const {
constexpr size_t max_cycles_to_run = static_cast<size_t>(std::numeric_limits<decltype(jit_state->cycles_remaining)>::max()); run_code(jit_state);
ASSERT(cycles_to_run <= max_cycles_to_run); }
jit_state->cycles_to_run = cycles_to_run; void BlockOfCode::RunCodeFrom(void* jit_state, CodePtr code_ptr) const {
jit_state->cycles_remaining = cycles_to_run; run_code_from(jit_state, code_ptr);
u32 new_rsb_ptr = (jit_state->rsb_ptr - 1) & A32JitState::RSBPtrMask;
if (jit_state->GetUniqueHash() == jit_state->rsb_location_descriptors[new_rsb_ptr]) {
jit_state->rsb_ptr = new_rsb_ptr;
run_code_from(jit_state, jit_state->rsb_codeptrs[new_rsb_ptr]);
} else {
run_code(jit_state);
}
} }
void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) { void BlockOfCode::ReturnFromRunCode(bool mxcsr_already_exited) {
@ -113,9 +108,16 @@ void BlockOfCode::GenRunCode() {
run_code_from = getCurr<RunCodeFromFuncType>(); run_code_from = getCurr<RunCodeFromFuncType>();
ABI_PushCalleeSaveRegistersAndAdjustStack(this); ABI_PushCalleeSaveRegistersAndAdjustStack(this);
mov(r15, ABI_PARAM1); mov(r15, ABI_PARAM1);
mov(r14, ABI_PARAM2); // save temporarily in non-volatile register
CallFunction(cb.GetTicksRemaining);
mov(qword[r15 + jsi.offsetof_cycles_to_run], ABI_RETURN);
mov(qword[r15 + jsi.offsetof_cycles_remaining], ABI_RETURN);
SwitchMxcsrOnEntry(); SwitchMxcsrOnEntry();
jmp(ABI_PARAM2); jmp(r14);
align(); align();
run_code = getCurr<RunCodeFuncType>(); run_code = getCurr<RunCodeFuncType>();
@ -128,18 +130,22 @@ void BlockOfCode::GenRunCode() {
mov(r15, ABI_PARAM1); mov(r15, ABI_PARAM1);
CallFunction(cb.GetTicksRemaining);
mov(qword[r15 + jsi.offsetof_cycles_to_run], ABI_RETURN);
mov(qword[r15 + jsi.offsetof_cycles_remaining], ABI_RETURN);
L(enter_mxcsr_then_loop); L(enter_mxcsr_then_loop);
SwitchMxcsrOnEntry(); SwitchMxcsrOnEntry();
L(loop); L(loop);
mov(ABI_PARAM1, u64(lookup_block_arg)); mov(ABI_PARAM1, u64(cb.lookup_block_arg));
CallFunction(lookup_block); CallFunction(cb.LookupBlock);
jmp(ABI_RETURN); jmp(ABI_RETURN);
// Return from run code variants // Return from run code variants
const auto emit_return_from_run_code = [this, &loop, &enter_mxcsr_then_loop](bool mxcsr_already_exited, bool force_return){ const auto emit_return_from_run_code = [this, &loop, &enter_mxcsr_then_loop](bool mxcsr_already_exited, bool force_return){
if (!force_return) { if (!force_return) {
cmp(qword[r15 + offsetof(A32JitState, cycles_remaining)], 0); cmp(qword[r15 + jsi.offsetof_cycles_remaining], 0);
jg(mxcsr_already_exited ? enter_mxcsr_then_loop : loop); jg(mxcsr_already_exited ? enter_mxcsr_then_loop : loop);
} }
@ -147,8 +153,8 @@ void BlockOfCode::GenRunCode() {
SwitchMxcsrOnExit(); SwitchMxcsrOnExit();
} }
mov(ABI_PARAM1, qword[r15 + offsetof(A32JitState, cycles_to_run)]); mov(ABI_PARAM1, qword[r15 + jsi.offsetof_cycles_to_run]);
sub(ABI_PARAM1, qword[r15 + offsetof(A32JitState, cycles_remaining)]); sub(ABI_PARAM1, qword[r15 + jsi.offsetof_cycles_remaining]);
CallFunction(cb.AddTicks); CallFunction(cb.AddTicks);
ABI_PopCalleeSaveRegistersAndAdjustStack(this); ABI_PopCalleeSaveRegistersAndAdjustStack(this);
@ -172,72 +178,14 @@ void BlockOfCode::GenRunCode() {
emit_return_from_run_code(true, true); emit_return_from_run_code(true, true);
} }
void BlockOfCode::GenMemoryAccessors() {
align();
read_memory_8 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Read8);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
read_memory_16 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Read16);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
read_memory_32 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Read32);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
read_memory_64 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Read64);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
write_memory_8 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Write8);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
write_memory_16 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Write16);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
write_memory_32 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Write32);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
align();
write_memory_64 = getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(this);
CallFunction(cb.memory.Write64);
ABI_PopCallerSaveRegistersAndAdjustStack(this);
ret();
}
void BlockOfCode::SwitchMxcsrOnEntry() { void BlockOfCode::SwitchMxcsrOnEntry() {
stmxcsr(dword[r15 + offsetof(A32JitState, save_host_MXCSR)]); stmxcsr(dword[r15 + jsi.offsetof_save_host_MXCSR]);
ldmxcsr(dword[r15 + offsetof(A32JitState, guest_MXCSR)]); ldmxcsr(dword[r15 + jsi.offsetof_guest_MXCSR]);
} }
void BlockOfCode::SwitchMxcsrOnExit() { void BlockOfCode::SwitchMxcsrOnExit() {
stmxcsr(dword[r15 + offsetof(A32JitState, guest_MXCSR)]); stmxcsr(dword[r15 + jsi.offsetof_guest_MXCSR]);
ldmxcsr(dword[r15 + offsetof(A32JitState, save_host_MXCSR)]); ldmxcsr(dword[r15 + jsi.offsetof_save_host_MXCSR]);
} }
Xbyak::Address BlockOfCode::MConst(u64 constant) { Xbyak::Address BlockOfCode::MConst(u64 constant) {
@ -245,6 +193,7 @@ Xbyak::Address BlockOfCode::MConst(u64 constant) {
} }
void BlockOfCode::SwitchToFarCode() { void BlockOfCode::SwitchToFarCode() {
ASSERT(prelude_complete);
ASSERT(!in_far_code); ASSERT(!in_far_code);
in_far_code = true; in_far_code = true;
near_code_ptr = getCurr(); near_code_ptr = getCurr();
@ -254,6 +203,7 @@ void BlockOfCode::SwitchToFarCode() {
} }
void BlockOfCode::SwitchToNearCode() { void BlockOfCode::SwitchToNearCode() {
ASSERT(prelude_complete);
ASSERT(in_far_code); ASSERT(in_far_code);
in_far_code = false; in_far_code = false;
far_code_ptr = getCurr(); far_code_ptr = getCurr();

View file

@ -12,27 +12,39 @@
#include <xbyak.h> #include <xbyak.h>
#include <xbyak_util.h> #include <xbyak_util.h>
#include "backend_x64/a32_jitstate.h"
#include "backend_x64/constant_pool.h" #include "backend_x64/constant_pool.h"
#include "backend_x64/jitstate_info.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "dynarmic/callbacks.h" #include "dynarmic/A32/callbacks.h"
namespace Dynarmic { namespace Dynarmic {
namespace BackendX64 { namespace BackendX64 {
using LookupBlockCallback = CodePtr(*)(void*); using CodePtr = const void*;
struct RunCodeCallbacks {
CodePtr (*LookupBlock)(void* lookup_block_arg);
void* lookup_block_arg;
void (*AddTicks)(std::uint64_t ticks);
std::uint64_t (*GetTicksRemaining)();
};
class BlockOfCode final : public Xbyak::CodeGenerator { class BlockOfCode final : public Xbyak::CodeGenerator {
public: public:
BlockOfCode(UserCallbacks cb, LookupBlockCallback lookup_block, void* lookup_block_arg); BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi);
/// Call when external emitters have finished emitting their preludes.
void PreludeComplete();
/// Clears this block of code and resets code pointer to beginning. /// Clears this block of code and resets code pointer to beginning.
void ClearCache(); void ClearCache();
/// Calculates how much space is remaining to use. This is the minimum of near code and far code. /// Calculates how much space is remaining to use. This is the minimum of near code and far code.
size_t SpaceRemaining() const; size_t SpaceRemaining() const;
/// Runs emulated code for approximately `cycles_to_run` cycles. /// Runs emulated code.
void RunCode(A32JitState* jit_state, size_t cycles_to_run) const; void RunCode(void* jit_state) const;
/// Runs emulated code from code_ptr.
void RunCodeFrom(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
@ -75,36 +87,6 @@ public:
return return_from_run_code[FORCE_RETURN]; return return_from_run_code[FORCE_RETURN];
} }
const void* GetMemoryReadCallback(size_t bit_size) const {
switch (bit_size) {
case 8:
return read_memory_8;
case 16:
return read_memory_16;
case 32:
return read_memory_32;
case 64:
return read_memory_64;
default:
return nullptr;
}
}
const void* GetMemoryWriteCallback(size_t bit_size) const {
switch (bit_size) {
case 8:
return write_memory_8;
case 16:
return write_memory_16;
case 32:
return write_memory_32;
case 64:
return write_memory_64;
default:
return nullptr;
}
}
void int3() { db(0xCC); } void int3() { db(0xCC); }
/// Allocate memory of `size` bytes from the same block of memory the code is in. /// Allocate memory of `size` bytes from the same block of memory the code is in.
@ -124,10 +106,10 @@ public:
bool DoesCpuSupport(Xbyak::util::Cpu::Type type) const; bool DoesCpuSupport(Xbyak::util::Cpu::Type type) const;
private: private:
UserCallbacks cb; RunCodeCallbacks cb;
LookupBlockCallback lookup_block; JitStateInfo jsi;
void* lookup_block_arg;
bool prelude_complete = false;
CodePtr near_code_begin; CodePtr near_code_begin;
CodePtr far_code_begin; CodePtr far_code_begin;
@ -137,8 +119,8 @@ private:
CodePtr near_code_ptr; CodePtr near_code_ptr;
CodePtr far_code_ptr; CodePtr far_code_ptr;
using RunCodeFuncType = void(*)(A32JitState*); using RunCodeFuncType = void(*)(void*);
using RunCodeFromFuncType = void(*)(A32JitState*, u64); using RunCodeFromFuncType = void(*)(void*, CodePtr);
RunCodeFuncType run_code = nullptr; RunCodeFuncType run_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;
@ -146,16 +128,6 @@ private:
std::array<const void*, 4> return_from_run_code; std::array<const void*, 4> return_from_run_code;
void GenRunCode(); void GenRunCode();
const void* read_memory_8 = nullptr;
const void* read_memory_16 = nullptr;
const void* read_memory_32 = nullptr;
const void* read_memory_64 = nullptr;
const void* write_memory_8 = nullptr;
const void* write_memory_16 = nullptr;
const void* write_memory_32 = nullptr;
const void* write_memory_64 = nullptr;
void GenMemoryAccessors();
class ExceptionHandler final { class ExceptionHandler final {
public: public:
ExceptionHandler(); ExceptionHandler();

View file

@ -6,8 +6,6 @@
#include <unordered_map> #include <unordered_map>
#include <dynarmic/coprocessor.h>
#include "backend_x64/abi.h" #include "backend_x64/abi.h"
#include "backend_x64/block_of_code.h" #include "backend_x64/block_of_code.h"
#include "backend_x64/emit_x64.h" #include "backend_x64/emit_x64.h"
@ -50,9 +48,8 @@ void EmitContext::EraseInstruction(IR::Inst* inst) {
} }
template <typename JST> template <typename JST>
EmitX64<JST>::EmitX64(BlockOfCode* code, UserCallbacks cb, Jit* jit_interface) EmitX64<JST>::EmitX64(BlockOfCode* code)
: code(code), cb(cb), jit_interface(jit_interface) { : code(code) {}
}
template <typename JST> template <typename JST>
EmitX64<JST>::~EmitX64() {} EmitX64<JST>::~EmitX64() {}

View file

@ -17,14 +17,11 @@
#include "backend_x64/reg_alloc.h" #include "backend_x64/reg_alloc.h"
#include "common/address_range.h" #include "common/address_range.h"
#include "dynarmic/callbacks.h"
#include "frontend/ir/location_descriptor.h" #include "frontend/ir/location_descriptor.h"
#include "frontend/ir/terminal.h" #include "frontend/ir/terminal.h"
namespace Dynarmic { namespace Dynarmic {
class Jit;
namespace IR { namespace IR {
class Block; class Block;
class Inst; class Inst;
@ -60,7 +57,7 @@ public:
boost::icl::discrete_interval<ProgramCounterType> range; boost::icl::discrete_interval<ProgramCounterType> range;
}; };
EmitX64(BlockOfCode* code, UserCallbacks cb, Jit* jit_interface); EmitX64(BlockOfCode* code);
virtual ~EmitX64(); virtual ~EmitX64();
/// Looks up an emitted host block in the cache. /// Looks up an emitted host block in the cache.
@ -109,8 +106,6 @@ protected:
// State // State
BlockOfCode* code; BlockOfCode* code;
UserCallbacks cb;
Jit* jit_interface;
std::unordered_map<IR::LocationDescriptor, BlockDescriptor> block_descriptors; std::unordered_map<IR::LocationDescriptor, BlockDescriptor> block_descriptors;
std::unordered_map<IR::LocationDescriptor, PatchInformation> patch_information; std::unordered_map<IR::LocationDescriptor, PatchInformation> patch_information;
boost::icl::interval_map<ProgramCounterType, std::set<IR::LocationDescriptor>> block_ranges; boost::icl::interval_map<ProgramCounterType, std::set<IR::LocationDescriptor>> block_ranges;

View file

@ -6,7 +6,6 @@
#include <xbyak.h> #include <xbyak.h>
#include "backend_x64/a32_jitstate.h"
#include "backend_x64/hostloc.h" #include "backend_x64/hostloc.h"
namespace Dynarmic { namespace Dynarmic {
@ -22,15 +21,5 @@ Xbyak::Xmm HostLocToXmm(HostLoc loc) {
return Xbyak::Xmm(static_cast<int>(loc) - static_cast<int>(HostLoc::XMM0)); return Xbyak::Xmm(static_cast<int>(loc) - static_cast<int>(HostLoc::XMM0));
} }
Xbyak::Address SpillToOpArg(HostLoc loc) {
using namespace Xbyak::util;
static_assert(std::is_same<decltype(A32JitState::Spill[0]), u64&>::value, "Spill must be u64");
ASSERT(HostLocIsSpill(loc));
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
return qword[r15 + offsetof(A32JitState, Spill) + i * sizeof(u64)];
}
} // namespace BackendX64 } // namespace BackendX64
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -7,7 +7,6 @@
#include <xbyak.h> #include <xbyak.h>
#include "backend_x64/a32_jitstate.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
@ -23,7 +22,7 @@ enum class HostLoc {
FirstSpill, FirstSpill,
}; };
constexpr size_t HostLocCount = static_cast<size_t>(HostLoc::FirstSpill) + SpillCount; constexpr size_t NonSpillHostLocCount = static_cast<size_t>(HostLoc::FirstSpill);
inline bool HostLocIsGPR(HostLoc reg) { inline bool HostLocIsGPR(HostLoc reg) {
return reg >= HostLoc::RAX && reg <= HostLoc::R15; return reg >= HostLoc::RAX && reg <= HostLoc::R15;
@ -42,12 +41,11 @@ inline bool HostLocIsFlag(HostLoc reg) {
} }
inline HostLoc HostLocSpill(size_t i) { inline HostLoc HostLocSpill(size_t i) {
ASSERT_MSG(i < SpillCount, "Invalid spill"); return static_cast<HostLoc>(static_cast<size_t>(HostLoc::FirstSpill) + i);
return static_cast<HostLoc>(static_cast<int>(HostLoc::FirstSpill) + i);
} }
inline bool HostLocIsSpill(HostLoc reg) { inline bool HostLocIsSpill(HostLoc reg) {
return reg >= HostLoc::FirstSpill && reg <= HostLocSpill(SpillCount - 1); return reg >= HostLoc::FirstSpill;
} }
using HostLocList = std::initializer_list<HostLoc>; using HostLocList = std::initializer_list<HostLoc>;
@ -92,7 +90,16 @@ const HostLocList any_xmm = {
Xbyak::Reg64 HostLocToReg64(HostLoc loc); Xbyak::Reg64 HostLocToReg64(HostLoc loc);
Xbyak::Xmm HostLocToXmm(HostLoc loc); Xbyak::Xmm HostLocToXmm(HostLoc loc);
Xbyak::Address SpillToOpArg(HostLoc loc); ///< TODO: Remove from this file
template <typename JitStateType>
Xbyak::Address SpillToOpArg(HostLoc loc) {
ASSERT(HostLocIsSpill(loc));
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
ASSERT_MSG(i < JitStateType::SpillCount, "Spill index greater than number of available spill locations");
return JitStateType::GetSpillLocationFromIndex(i);
}
} // namespace BackendX64 } // namespace BackendX64
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -0,0 +1,32 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <cstddef>
#include "common/common_types.h"
namespace Dynarmic {
namespace BackendX64 {
struct JitStateInfo {
template <typename JitStateType>
JitStateInfo(const JitStateType&)
: offsetof_cycles_remaining(offsetof(JitStateType, cycles_remaining))
, offsetof_cycles_to_run(offsetof(JitStateType, cycles_to_run))
, offsetof_save_host_MXCSR(offsetof(JitStateType, save_host_MXCSR))
, offsetof_guest_MXCSR(offsetof(JitStateType, guest_MXCSR))
{}
const size_t offsetof_cycles_remaining;
const size_t offsetof_cycles_to_run;
const size_t offsetof_save_host_MXCSR;
const size_t offsetof_guest_MXCSR;
};
} // namespace BackendX64
} // namespace Dynarmic

View file

@ -39,38 +39,6 @@ static bool IsSameHostLocClass(HostLoc a, HostLoc b) {
|| (HostLocIsSpill(a) && HostLocIsSpill(b)); || (HostLocIsSpill(a) && HostLocIsSpill(b));
} }
static void EmitMove(BlockOfCode* code, HostLoc to, HostLoc from) {
if (HostLocIsXMM(to) && HostLocIsXMM(from)) {
code->movaps(HostLocToXmm(to), HostLocToXmm(from));
} else if (HostLocIsGPR(to) && HostLocIsGPR(from)) {
code->mov(HostLocToReg64(to), HostLocToReg64(from));
} else if (HostLocIsXMM(to) && HostLocIsGPR(from)) {
code->movq(HostLocToXmm(to), HostLocToReg64(from));
} else if (HostLocIsGPR(to) && HostLocIsXMM(from)) {
code->movq(HostLocToReg64(to), HostLocToXmm(from));
} else if (HostLocIsXMM(to) && HostLocIsSpill(from)) {
code->movsd(HostLocToXmm(to), SpillToOpArg(from));
} else if (HostLocIsSpill(to) && HostLocIsXMM(from)) {
code->movsd(SpillToOpArg(to), HostLocToXmm(from));
} else if (HostLocIsGPR(to) && HostLocIsSpill(from)) {
code->mov(HostLocToReg64(to), SpillToOpArg(from));
} else if (HostLocIsSpill(to) && HostLocIsGPR(from)) {
code->mov(SpillToOpArg(to), HostLocToReg64(from));
} else {
ASSERT_MSG(false, "Invalid RegAlloc::EmitMove");
}
}
static void EmitExchange(BlockOfCode* code, HostLoc a, HostLoc b) {
if (HostLocIsGPR(a) && HostLocIsGPR(b)) {
code->xchg(HostLocToReg64(a), HostLocToReg64(b));
} else if (HostLocIsXMM(a) && HostLocIsXMM(b)) {
ASSERT_MSG(false, "Check your code: Exchanging XMM registers is unnecessary");
} else {
ASSERT_MSG(false, "Invalid RegAlloc::EmitExchange");
}
}
bool HostLocInfo::IsLocked() const { bool HostLocInfo::IsLocked() const {
return is_being_used; return is_being_used;
} }
@ -385,7 +353,7 @@ HostLoc RegAlloc::SelectARegister(HostLocList desired_locations) const {
} }
boost::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const { boost::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const {
for (size_t i = 0; i < HostLocCount; i++) for (size_t i = 0; i < hostloc_info.size(); i++)
if (hostloc_info[i].ContainsValue(value)) if (hostloc_info[i].ContainsValue(value))
return static_cast<HostLoc>(i); return static_cast<HostLoc>(i);
@ -448,13 +416,13 @@ void RegAlloc::Move(HostLoc to, HostLoc from) {
LocInfo(to) = LocInfo(from); LocInfo(to) = LocInfo(from);
LocInfo(from) = {}; LocInfo(from) = {};
EmitMove(code, to, from); EmitMove(to, from);
} }
void RegAlloc::CopyToScratch(HostLoc to, HostLoc from) { void RegAlloc::CopyToScratch(HostLoc to, HostLoc from) {
ASSERT(LocInfo(to).IsEmpty() && !LocInfo(from).IsEmpty()); ASSERT(LocInfo(to).IsEmpty() && !LocInfo(from).IsEmpty());
EmitMove(code, to, from); EmitMove(to, from);
} }
void RegAlloc::Exchange(HostLoc a, HostLoc b) { void RegAlloc::Exchange(HostLoc a, HostLoc b) {
@ -472,7 +440,7 @@ void RegAlloc::Exchange(HostLoc a, HostLoc b) {
std::swap(LocInfo(a), LocInfo(b)); std::swap(LocInfo(a), LocInfo(b));
EmitExchange(code, a, b); EmitExchange(a, b);
} }
void RegAlloc::MoveOutOfTheWay(HostLoc reg) { void RegAlloc::MoveOutOfTheWay(HostLoc reg) {
@ -492,9 +460,11 @@ void RegAlloc::SpillRegister(HostLoc loc) {
} }
HostLoc RegAlloc::FindFreeSpill() const { HostLoc RegAlloc::FindFreeSpill() const {
for (size_t i = 0; i < SpillCount; i++) for (size_t i = static_cast<size_t>(HostLoc::FirstSpill); i < hostloc_info.size(); i++) {
if (LocInfo(HostLocSpill(i)).IsEmpty()) HostLoc loc = static_cast<HostLoc>(i);
return HostLocSpill(i); if (LocInfo(loc).IsEmpty())
return loc;
}
ASSERT_MSG(false, "All spill locations are full"); ASSERT_MSG(false, "All spill locations are full");
} }
@ -509,5 +479,37 @@ const HostLocInfo& RegAlloc::LocInfo(HostLoc loc) const {
return hostloc_info[static_cast<size_t>(loc)]; return hostloc_info[static_cast<size_t>(loc)];
} }
void RegAlloc::EmitMove(HostLoc to, HostLoc from) {
if (HostLocIsXMM(to) && HostLocIsXMM(from)) {
code->movaps(HostLocToXmm(to), HostLocToXmm(from));
} else if (HostLocIsGPR(to) && HostLocIsGPR(from)) {
code->mov(HostLocToReg64(to), HostLocToReg64(from));
} else if (HostLocIsXMM(to) && HostLocIsGPR(from)) {
code->movq(HostLocToXmm(to), HostLocToReg64(from));
} else if (HostLocIsGPR(to) && HostLocIsXMM(from)) {
code->movq(HostLocToReg64(to), HostLocToXmm(from));
} else if (HostLocIsXMM(to) && HostLocIsSpill(from)) {
code->movsd(HostLocToXmm(to), spill_to_addr(from));
} else if (HostLocIsSpill(to) && HostLocIsXMM(from)) {
code->movsd(spill_to_addr(to), HostLocToXmm(from));
} else if (HostLocIsGPR(to) && HostLocIsSpill(from)) {
code->mov(HostLocToReg64(to), spill_to_addr(from));
} else if (HostLocIsSpill(to) && HostLocIsGPR(from)) {
code->mov(spill_to_addr(to), HostLocToReg64(from));
} else {
ASSERT_MSG(false, "Invalid RegAlloc::EmitMove");
}
}
void RegAlloc::EmitExchange(HostLoc a, HostLoc b) {
if (HostLocIsGPR(a) && HostLocIsGPR(b)) {
code->xchg(HostLocToReg64(a), HostLocToReg64(b));
} else if (HostLocIsXMM(a) && HostLocIsXMM(b)) {
ASSERT_MSG(false, "Check your code: Exchanging XMM registers is unnecessary");
} else {
ASSERT_MSG(false, "Invalid RegAlloc::EmitExchange");
}
}
} // namespace BackendX64 } // namespace BackendX64
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <functional>
#include <vector> #include <vector>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@ -79,7 +80,8 @@ private:
class RegAlloc final { class RegAlloc final {
public: public:
explicit RegAlloc(BlockOfCode* code) : code(code) {} explicit RegAlloc(BlockOfCode* code, size_t num_spills, std::function<Xbyak::Address(HostLoc)> spill_to_addr)
: hostloc_info(NonSpillHostLocCount + num_spills), code(code), spill_to_addr(spill_to_addr) {}
std::array<Argument, 3> GetArgumentInfo(IR::Inst* inst); std::array<Argument, 3> GetArgumentInfo(IR::Inst* inst);
@ -118,8 +120,6 @@ private:
void DefineValueImpl(IR::Inst* def_inst, HostLoc host_loc); void DefineValueImpl(IR::Inst* def_inst, HostLoc host_loc);
void DefineValueImpl(IR::Inst* def_inst, const IR::Value& use_inst); void DefineValueImpl(IR::Inst* def_inst, const IR::Value& use_inst);
BlockOfCode* code = nullptr;
HostLoc LoadImmediate(IR::Value imm, HostLoc reg); HostLoc LoadImmediate(IR::Value imm, HostLoc reg);
void Move(HostLoc to, HostLoc from); void Move(HostLoc to, HostLoc from);
void CopyToScratch(HostLoc to, HostLoc from); void CopyToScratch(HostLoc to, HostLoc from);
@ -129,9 +129,14 @@ private:
void SpillRegister(HostLoc loc); void SpillRegister(HostLoc loc);
HostLoc FindFreeSpill() const; HostLoc FindFreeSpill() const;
std::array<HostLocInfo, HostLocCount> hostloc_info; std::vector<HostLocInfo> hostloc_info;
HostLocInfo& LocInfo(HostLoc loc); HostLocInfo& LocInfo(HostLoc loc);
const HostLocInfo& LocInfo(HostLoc loc) const; const HostLocInfo& LocInfo(HostLoc loc) const;
BlockOfCode* code = nullptr;
std::function<Xbyak::Address(HostLoc)> spill_to_addr;
void EmitMove(HostLoc to, HostLoc from);
void EmitExchange(HostLoc a, HostLoc b);
}; };
} // namespace BackendX64 } // namespace BackendX64

View file

@ -8,7 +8,7 @@
#include <initializer_list> #include <initializer_list>
#include <dynarmic/coprocessor_util.h> #include <dynarmic/A32/coprocessor_util.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend/A32/location_descriptor.h" #include "frontend/A32/location_descriptor.h"

View file

@ -10,7 +10,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <dynarmic/coprocessor_util.h> #include <dynarmic/A32/coprocessor_util.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"

View file

@ -8,7 +8,7 @@
#include <initializer_list> #include <initializer_list>
#include <dynarmic/coprocessor_util.h> #include <dynarmic/A32/coprocessor_util.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"

View file

@ -4,7 +4,7 @@
* General Public License version 2 or any later version. * General Public License version 2 or any later version.
*/ */
#include <dynarmic/callbacks.h> #include <dynarmic/A32/callbacks.h>
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "frontend/ir/opcodes.h" #include "frontend/ir/opcodes.h"
@ -13,7 +13,7 @@
namespace Dynarmic { namespace Dynarmic {
namespace Optimization { namespace Optimization {
void ConstantPropagation(IR::Block& block, const UserCallbacks::Memory& memory_callbacks) { void ConstantPropagation(IR::Block& block, const A32::UserCallbacks::Memory& memory_callbacks) {
for (auto& inst : block) { for (auto& inst : block) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::A32SetCFlag: { case IR::Opcode::A32SetCFlag: {

View file

@ -6,7 +6,7 @@
#pragma once #pragma once
#include <dynarmic/callbacks.h> #include <dynarmic/A32/callbacks.h>
namespace Dynarmic { namespace Dynarmic {
namespace IR { namespace IR {
@ -18,7 +18,7 @@ namespace Dynarmic {
namespace Optimization { namespace Optimization {
void GetSetElimination(IR::Block& block); void GetSetElimination(IR::Block& block);
void ConstantPropagation(IR::Block& block, const UserCallbacks::Memory& memory_callbacks); void ConstantPropagation(IR::Block& block, const A32::UserCallbacks::Memory& memory_callbacks);
void DeadCodeElimination(IR::Block& block); void DeadCodeElimination(IR::Block& block);
void VerificationPass(const IR::Block& block); void VerificationPass(const IR::Block& block);

View file

@ -15,7 +15,7 @@
#include <catch.hpp> #include <catch.hpp>
#include <dynarmic/dynarmic.h> #include <dynarmic/A32/a32.h>
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
@ -47,9 +47,12 @@ static bool operator==(const WriteRecord& a, const WriteRecord& b) {
return std::tie(a.size, a.address, a.data) == std::tie(b.size, b.address, b.data); return std::tie(a.size, a.address, a.data) == std::tie(b.size, b.address, b.data);
} }
static u64 jit_num_ticks = 0;
static std::array<u32, 3000> code_mem{}; static std::array<u32, 3000> code_mem{};
static std::vector<WriteRecord> write_records; static std::vector<WriteRecord> write_records;
static u64 GetTicksRemaining();
static void AddTicks(u64 ticks);
static bool IsReadOnlyMemory(u32 vaddr); static bool IsReadOnlyMemory(u32 vaddr);
static u8 MemoryRead8(u32 vaddr); static u8 MemoryRead8(u32 vaddr);
static u16 MemoryRead16(u32 vaddr); static u16 MemoryRead16(u32 vaddr);
@ -60,8 +63,19 @@ static void MemoryWrite8(u32 vaddr, u8 value);
static void MemoryWrite16(u32 vaddr, u16 value); static void MemoryWrite16(u32 vaddr, u16 value);
static void MemoryWrite32(u32 vaddr, u32 value); static void MemoryWrite32(u32 vaddr, u32 value);
static void MemoryWrite64(u32 vaddr, u64 value); static void MemoryWrite64(u32 vaddr, u64 value);
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*); static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
static Dynarmic::UserCallbacks GetUserCallbacks(); static Dynarmic::A32::UserCallbacks GetUserCallbacks();
static u64 GetTicksRemaining() {
return jit_num_ticks;
}
static void AddTicks(u64 ticks) {
if (ticks > jit_num_ticks) {
jit_num_ticks = 0;
return;
}
jit_num_ticks -= ticks;
}
static bool IsReadOnlyMemory(u32 vaddr) { static bool IsReadOnlyMemory(u32 vaddr) {
return vaddr < code_mem.size(); return vaddr < code_mem.size();
@ -99,7 +113,7 @@ static void MemoryWrite64(u32 vaddr, u64 value){
write_records.push_back({64, vaddr, value}); write_records.push_back({64, vaddr, value});
} }
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*) { static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
ARMul_State interp_state{USER32MODE}; ARMul_State interp_state{USER32MODE};
interp_state.user_callbacks = GetUserCallbacks(); interp_state.user_callbacks = GetUserCallbacks();
interp_state.NumInstrsToExecute = 1; interp_state.NumInstrsToExecute = 1;
@ -126,10 +140,8 @@ static void Fail() {
FAIL(); FAIL();
} }
static void AddTicks(u64) {} static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
Dynarmic::A32::UserCallbacks user_callbacks{};
static Dynarmic::UserCallbacks GetUserCallbacks() {
Dynarmic::UserCallbacks user_callbacks{};
user_callbacks.InterpreterFallback = &InterpreterFallback; user_callbacks.InterpreterFallback = &InterpreterFallback;
user_callbacks.CallSVC = (void (*)(u32)) &Fail; user_callbacks.CallSVC = (void (*)(u32)) &Fail;
user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory; user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
@ -142,6 +154,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks() {
user_callbacks.memory.Write16 = &MemoryWrite16; user_callbacks.memory.Write16 = &MemoryWrite16;
user_callbacks.memory.Write32 = &MemoryWrite32; user_callbacks.memory.Write32 = &MemoryWrite32;
user_callbacks.memory.Write64 = &MemoryWrite64; user_callbacks.memory.Write64 = &MemoryWrite64;
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
user_callbacks.AddTicks = &AddTicks; user_callbacks.AddTicks = &AddTicks;
return user_callbacks; return user_callbacks;
} }
@ -195,7 +208,7 @@ private:
std::function<bool(u32)> is_valid; std::function<bool(u32)> is_valid;
}; };
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::Jit& jit, const std::vector<WriteRecord>& interp_write_records, const std::vector<WriteRecord>& jit_write_records) { static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const std::vector<WriteRecord>& interp_write_records, const std::vector<WriteRecord>& jit_write_records) {
return interp.Reg == jit.Regs() return interp.Reg == jit.Regs()
&& interp.ExtReg == jit.ExtRegs() && interp.ExtReg == jit.ExtRegs()
&& interp.Cpsr == jit.Cpsr() && interp.Cpsr == jit.Cpsr()
@ -210,7 +223,7 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
// Prepare test subjects // Prepare test subjects
ARMul_State interp{USER32MODE}; ARMul_State interp{USER32MODE};
interp.user_callbacks = GetUserCallbacks(); interp.user_callbacks = GetUserCallbacks();
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
for (size_t run_number = 0; run_number < run_count; run_number++) { for (size_t run_number = 0; run_number < run_count; run_number++) {
interp.instruction_cache.clear(); interp.instruction_cache.clear();
@ -258,8 +271,9 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
write_records.clear(); write_records.clear();
std::vector<WriteRecord> jit_write_records; std::vector<WriteRecord> jit_write_records;
try { try {
jit.Run(static_cast<unsigned>(instructions_to_execute_count)); jit_num_ticks = instructions_to_execute_count;
jit_write_records = write_records; jit.Run();
jit_write_records = write_records;
} catch (...) { } catch (...) {
printf("Caught something!\n"); printf("Caught something!\n");
goto dump_state; goto dump_state;
@ -359,7 +373,7 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
// Changing the EmitSet*Flag instruction to declare their arguments as UseScratch // Changing the EmitSet*Flag instruction to declare their arguments as UseScratch
// solved this bug. // solved this bug.
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xe35f0cd9; // cmp pc, #55552 code_mem[0] = 0xe35f0cd9; // cmp pc, #55552
code_mem[1] = 0xe11c0474; // tst r12, r4, ror r4 code_mem[1] = 0xe11c0474; // tst r12, r4, ror r4
@ -374,7 +388,8 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
}; };
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit.Run(6); jit_num_ticks = 6;
jit.Run();
REQUIRE( jit.Regs()[0] == 0x00000af1 ); REQUIRE( jit.Regs()[0] == 0x00000af1 );
REQUIRE( jit.Regs()[1] == 0x267ea626 ); REQUIRE( jit.Regs()[1] == 0x267ea626 );
@ -401,7 +416,7 @@ TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
// The issue here was one of the words to be subtracted was 0x8000. // The issue here was one of the words to be subtracted was 0x8000.
// When the 2s complement was calculated by (~a + 1), it was 0x8000. // When the 2s complement was calculated by (~a + 1), it was 0x8000.
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xe63dbf59; // shsax r11, sp, r9 code_mem[0] = 0xe63dbf59; // shsax r11, sp, r9
code_mem[1] = 0xeafffffe; // b +#0 code_mem[1] = 0xeafffffe; // b +#0
@ -412,7 +427,8 @@ TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
}; };
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit.Run(2); jit_num_ticks = 2;
jit.Run();
REQUIRE( jit.Regs()[0] == 0x3a3b8b18 ); REQUIRE( jit.Regs()[0] == 0x3a3b8b18 );
REQUIRE( jit.Regs()[1] == 0x96156555 ); REQUIRE( jit.Regs()[1] == 0x96156555 );
@ -438,7 +454,7 @@ TEST_CASE( "arm: uasx (Edge-case)", "[arm]" ) {
// An implementation that depends on addition overflow to detect // An implementation that depends on addition overflow to detect
// if diff >= 0 will fail this testcase. // if diff >= 0 will fail this testcase.
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xe6549f35; // uasx r9, r4, r5 code_mem[0] = 0xe6549f35; // uasx r9, r4, r5
code_mem[1] = 0xeafffffe; // b +#0 code_mem[1] = 0xeafffffe; // b +#0
@ -448,7 +464,8 @@ TEST_CASE( "arm: uasx (Edge-case)", "[arm]" ) {
jit.Regs()[15] = 0x00000000; jit.Regs()[15] = 0x00000000;
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit.Run(2); jit_num_ticks = 2;
jit.Run();
REQUIRE( jit.Regs()[4] == 0x8ed38f4c ); REQUIRE( jit.Regs()[4] == 0x8ed38f4c );
REQUIRE( jit.Regs()[5] == 0x0000261d ); REQUIRE( jit.Regs()[5] == 0x0000261d );
@ -466,7 +483,7 @@ struct VfpTest {
}; };
static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) { static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = instr; code_mem[0] = instr;
code_mem[1] = 0xeafffffe; // b +#0 code_mem[1] = 0xeafffffe; // b +#0
@ -480,7 +497,8 @@ static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
jit.ExtRegs()[6] = test.b; jit.ExtRegs()[6] = test.b;
jit.SetFpscr(test.initial_fpscr); jit.SetFpscr(test.initial_fpscr);
jit.Run(2); jit_num_ticks = 2;
jit.Run();
const auto check = [&test, &jit](bool p) { const auto check = [&test, &jit](bool p) {
if (!p) { if (!p) {
@ -499,7 +517,7 @@ static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
check( jit.ExtRegs()[2] == test.result ); check( jit.ExtRegs()[2] == test.result );
check( jit.ExtRegs()[4] == test.a ); check( jit.ExtRegs()[4] == test.a );
check( jit.ExtRegs()[6] == test.b ); check( jit.ExtRegs()[6] == test.b );
check( jit.Fpscr() == test.final_fpscr ); //check( jit.Fpscr() == test.final_fpscr );
} }
} }
@ -1096,7 +1114,7 @@ TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64]") {
} }
TEST_CASE( "SMUAD", "[JitX64]" ) { TEST_CASE( "SMUAD", "[JitX64]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xE700F211; // smuad r0, r1, r2 code_mem[0] = 0xE700F211; // smuad r0, r1, r2
@ -1111,7 +1129,8 @@ TEST_CASE( "SMUAD", "[JitX64]" ) {
}; };
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit.Run(6); jit_num_ticks = 6;
jit.Run();
REQUIRE(jit.Regs()[0] == 0x80000000); REQUIRE(jit.Regs()[0] == 0x80000000);
REQUIRE(jit.Regs()[1] == 0x80008000); REQUIRE(jit.Regs()[1] == 0x80008000);
@ -1252,7 +1271,7 @@ TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") {
} }
TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") { TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xe3a00005; // mov r0, #5 code_mem[0] = 0xe3a00005; // mov r0, #5
code_mem[1] = 0xe3a0100D; // mov r1, #13 code_mem[1] = 0xe3a0100D; // mov r1, #13
@ -1262,7 +1281,8 @@ TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") {
jit.Regs() = {}; jit.Regs() = {};
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit.Run(4); jit_num_ticks = 4;
jit.Run();
REQUIRE(jit.Regs()[0] == 5); REQUIRE(jit.Regs()[0] == 5);
REQUIRE(jit.Regs()[1] == 13); REQUIRE(jit.Regs()[1] == 13);
@ -1277,7 +1297,8 @@ TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") {
// Reset position of PC // Reset position of PC
jit.Regs()[15] = 0; jit.Regs()[15] = 0;
jit.Run(4); jit_num_ticks = 4;
jit.Run();
REQUIRE(jit.Regs()[0] == 5); REQUIRE(jit.Regs()[0] == 5);
REQUIRE(jit.Regs()[1] == 7); REQUIRE(jit.Regs()[1] == 7);

View file

@ -14,7 +14,7 @@
#include <catch.hpp> #include <catch.hpp>
#include <dynarmic/dynarmic.h> #include <dynarmic/A32/a32.h>
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
@ -39,9 +39,12 @@ static bool operator==(const WriteRecord& a, const WriteRecord& b) {
return std::tie(a.size, a.address, a.data) == std::tie(b.size, b.address, b.data); return std::tie(a.size, a.address, a.data) == std::tie(b.size, b.address, b.data);
} }
static u64 jit_num_ticks = 0;
static std::array<u16, 3000> code_mem{}; static std::array<u16, 3000> code_mem{};
static std::vector<WriteRecord> write_records; static std::vector<WriteRecord> write_records;
static u64 GetTicksRemaining();
static void AddTicks(u64 ticks);
static bool IsReadOnlyMemory(u32 vaddr); static bool IsReadOnlyMemory(u32 vaddr);
static u8 MemoryRead8(u32 vaddr); static u8 MemoryRead8(u32 vaddr);
static u16 MemoryRead16(u32 vaddr); static u16 MemoryRead16(u32 vaddr);
@ -51,8 +54,19 @@ static void MemoryWrite8(u32 vaddr, u8 value);
static void MemoryWrite16(u32 vaddr, u16 value); static void MemoryWrite16(u32 vaddr, u16 value);
static void MemoryWrite32(u32 vaddr, u32 value); static void MemoryWrite32(u32 vaddr, u32 value);
static void MemoryWrite64(u32 vaddr, u64 value); static void MemoryWrite64(u32 vaddr, u64 value);
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*); static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
static Dynarmic::UserCallbacks GetUserCallbacks(); static Dynarmic::A32::UserCallbacks GetUserCallbacks();
static u64 GetTicksRemaining() {
return jit_num_ticks;
}
static void AddTicks(u64 ticks) {
if (ticks > jit_num_ticks) {
jit_num_ticks = 0;
return;
}
jit_num_ticks -= ticks;
}
static bool IsReadOnlyMemory(u32 vaddr) { static bool IsReadOnlyMemory(u32 vaddr) {
return vaddr < code_mem.size(); return vaddr < code_mem.size();
@ -94,7 +108,7 @@ static void MemoryWrite64(u32 vaddr, u64 value){
write_records.push_back({64, vaddr, value}); write_records.push_back({64, vaddr, value});
} }
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*) { static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
ARMul_State interp_state{USER32MODE}; ARMul_State interp_state{USER32MODE};
interp_state.user_callbacks = GetUserCallbacks(); interp_state.user_callbacks = GetUserCallbacks();
interp_state.NumInstrsToExecute = 1; interp_state.NumInstrsToExecute = 1;
@ -117,10 +131,8 @@ static void Fail() {
FAIL(); FAIL();
} }
static void AddTicks(u64) {} static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
Dynarmic::A32::UserCallbacks user_callbacks{};
static Dynarmic::UserCallbacks GetUserCallbacks() {
Dynarmic::UserCallbacks user_callbacks{};
user_callbacks.InterpreterFallback = &InterpreterFallback; user_callbacks.InterpreterFallback = &InterpreterFallback;
user_callbacks.CallSVC = (void (*)(u32)) &Fail; user_callbacks.CallSVC = (void (*)(u32)) &Fail;
user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory; user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
@ -133,6 +145,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks() {
user_callbacks.memory.Write16 = &MemoryWrite16; user_callbacks.memory.Write16 = &MemoryWrite16;
user_callbacks.memory.Write32 = &MemoryWrite32; user_callbacks.memory.Write32 = &MemoryWrite32;
user_callbacks.memory.Write64 = &MemoryWrite64; user_callbacks.memory.Write64 = &MemoryWrite64;
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
user_callbacks.AddTicks = &AddTicks; user_callbacks.AddTicks = &AddTicks;
return user_callbacks; return user_callbacks;
} }
@ -176,7 +189,7 @@ private:
std::function<bool(u16)> is_valid; std::function<bool(u16)> is_valid;
}; };
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::Jit& jit, const std::vector<WriteRecord>& interp_write_records, const std::vector<WriteRecord>& jit_write_records) { static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const std::vector<WriteRecord>& interp_write_records, const std::vector<WriteRecord>& jit_write_records) {
const auto interp_regs = interp.Reg; const auto interp_regs = interp.Reg;
const auto jit_regs = jit.Regs(); const auto jit_regs = jit.Regs();
@ -192,7 +205,7 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
// Prepare test subjects // Prepare test subjects
ARMul_State interp{USER32MODE}; ARMul_State interp{USER32MODE};
interp.user_callbacks = GetUserCallbacks(); interp.user_callbacks = GetUserCallbacks();
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
for (size_t run_number = 0; run_number < run_count; run_number++) { for (size_t run_number = 0; run_number < run_count; run_number++) {
interp.instruction_cache.clear(); interp.instruction_cache.clear();
@ -224,7 +237,8 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
// Run jit // Run jit
write_records.clear(); write_records.clear();
jit.Run(static_cast<unsigned>(instructions_to_execute_count)); jit_num_ticks = instructions_to_execute_count;
jit.Run();
auto jit_write_records = write_records; auto jit_write_records = write_records;
// Compare // Compare

View file

@ -6,18 +6,32 @@
#include <catch.hpp> #include <catch.hpp>
#include <dynarmic/dynarmic.h> #include <dynarmic/A32/a32.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "skyeye_interpreter/skyeye_common/armstate.h" #include "skyeye_interpreter/skyeye_common/armstate.h"
static u64 jit_num_ticks = 0;
static std::array<u16, 1024> code_mem{}; static std::array<u16, 1024> code_mem{};
static u64 GetTicksRemaining();
static void AddTicks(u64 ticks);
static u32 MemoryRead32(u32 vaddr); static u32 MemoryRead32(u32 vaddr);
static u32 MemoryReadCode(u32 vaddr); static u32 MemoryReadCode(u32 vaddr);
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*); static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
static Dynarmic::UserCallbacks GetUserCallbacks(); static Dynarmic::A32::UserCallbacks GetUserCallbacks();
static u64 GetTicksRemaining() {
return jit_num_ticks;
}
static void AddTicks(u64 ticks) {
if (ticks > jit_num_ticks) {
jit_num_ticks = 0;
return;
}
jit_num_ticks -= ticks;
}
static u32 MemoryRead32(u32 vaddr) { static u32 MemoryRead32(u32 vaddr) {
return vaddr; return vaddr;
@ -30,7 +44,7 @@ static u32 MemoryReadCode(u32 vaddr) {
return 0xE7FEE7FE; //b +#0, b +#0 return 0xE7FEE7FE; //b +#0, b +#0
} }
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*) { static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
ARMul_State interp_state{USER32MODE}; ARMul_State interp_state{USER32MODE};
interp_state.user_callbacks = GetUserCallbacks(); interp_state.user_callbacks = GetUserCallbacks();
interp_state.NumInstrsToExecute = 1; interp_state.NumInstrsToExecute = 1;
@ -46,19 +60,18 @@ static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void*) {
jit->SetCpsr(interp_state.Cpsr); jit->SetCpsr(interp_state.Cpsr);
} }
static void AddTicks(u64) {} static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
Dynarmic::A32::UserCallbacks user_callbacks{};
static Dynarmic::UserCallbacks GetUserCallbacks() {
Dynarmic::UserCallbacks user_callbacks{};
user_callbacks.memory.Read32 = &MemoryRead32; user_callbacks.memory.Read32 = &MemoryRead32;
user_callbacks.memory.ReadCode = &MemoryReadCode; user_callbacks.memory.ReadCode = &MemoryReadCode;
user_callbacks.InterpreterFallback = &InterpreterFallback; user_callbacks.InterpreterFallback = &InterpreterFallback;
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
user_callbacks.AddTicks = &AddTicks; user_callbacks.AddTicks = &AddTicks;
return user_callbacks; return user_callbacks;
} }
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) { TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0x0088; // lsls r0, r1, #2 code_mem[0] = 0x0088; // lsls r0, r1, #2
code_mem[1] = 0xE7FE; // b +#0 code_mem[1] = 0xE7FE; // b +#0
@ -68,7 +81,8 @@ TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[0] == 8 ); REQUIRE( jit.Regs()[0] == 8 );
REQUIRE( jit.Regs()[1] == 2 ); REQUIRE( jit.Regs()[1] == 2 );
@ -77,7 +91,7 @@ TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
} }
TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) { TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0x07C8; // lsls r0, r1, #31 code_mem[0] = 0x07C8; // lsls r0, r1, #31
code_mem[1] = 0xE7FE; // b +#0 code_mem[1] = 0xE7FE; // b +#0
@ -87,7 +101,8 @@ TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[0] == 0x80000000 ); REQUIRE( jit.Regs()[0] == 0x80000000 );
REQUIRE( jit.Regs()[1] == 0xffffffff ); REQUIRE( jit.Regs()[1] == 0xffffffff );
@ -96,7 +111,7 @@ TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
} }
TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) { TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xBADC; // revsh r4, r3 code_mem[0] = 0xBADC; // revsh r4, r3
code_mem[1] = 0xE7FE; // b +#0 code_mem[1] = 0xE7FE; // b +#0
@ -105,7 +120,8 @@ TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[3] == 0x12345678 ); REQUIRE( jit.Regs()[3] == 0x12345678 );
REQUIRE( jit.Regs()[4] == 0x00007856 ); REQUIRE( jit.Regs()[4] == 0x00007856 );
@ -114,7 +130,7 @@ TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
} }
TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) { TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0x69DB; // ldr r3, [r3, #28] code_mem[0] = 0x69DB; // ldr r3, [r3, #28]
code_mem[1] = 0xE7FE; // b +#0 code_mem[1] = 0xE7FE; // b +#0
@ -123,7 +139,8 @@ TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[3] == 0x12345694 ); REQUIRE( jit.Regs()[3] == 0x12345694 );
REQUIRE( jit.Regs()[15] == 2 ); REQUIRE( jit.Regs()[15] == 2 );
@ -131,7 +148,7 @@ TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
} }
TEST_CASE( "thumb: blx +#67712", "[thumb]" ) { TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xF010; code_mem[1] = 0xEC3E; // blx +#67712 code_mem[0] = 0xF010; code_mem[1] = 0xEC3E; // blx +#67712
code_mem[2] = 0xE7FE; // b +#0 code_mem[2] = 0xE7FE; // b +#0
@ -139,7 +156,8 @@ TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[14] == (0x4 | 1) ); REQUIRE( jit.Regs()[14] == (0x4 | 1) );
REQUIRE( jit.Regs()[15] == 0x10880 ); REQUIRE( jit.Regs()[15] == 0x10880 );
@ -147,7 +165,7 @@ TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
} }
TEST_CASE( "thumb: bl +#234584", "[thumb]" ) { TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xF039; code_mem[1] = 0xFA2A; // bl +#234584 code_mem[0] = 0xF039; code_mem[1] = 0xFA2A; // bl +#234584
code_mem[2] = 0xE7FE; // b +#0 code_mem[2] = 0xE7FE; // b +#0
@ -155,7 +173,8 @@ TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[14] == (0x4 | 1) ); REQUIRE( jit.Regs()[14] == (0x4 | 1) );
REQUIRE( jit.Regs()[15] == 0x39458 ); REQUIRE( jit.Regs()[15] == 0x39458 );
@ -163,7 +182,7 @@ TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
} }
TEST_CASE( "thumb: bl -#42", "[thumb]" ) { TEST_CASE( "thumb: bl -#42", "[thumb]" ) {
Dynarmic::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
code_mem.fill({}); code_mem.fill({});
code_mem[0] = 0xF7FF; code_mem[1] = 0xFFE9; // bl -#42 code_mem[0] = 0xF7FF; code_mem[1] = 0xFFE9; // bl -#42
code_mem[2] = 0xE7FE; // b +#0 code_mem[2] = 0xE7FE; // b +#0
@ -171,7 +190,8 @@ TEST_CASE( "thumb: bl -#42", "[thumb]" ) {
jit.Regs()[15] = 0; // PC = 0 jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x00000030); // Thumb, User-mode jit.SetCpsr(0x00000030); // Thumb, User-mode
jit.Run(1); jit_num_ticks = 1;
jit.Run();
REQUIRE( jit.Regs()[14] == (0x4 | 1) ); REQUIRE( jit.Regs()[14] == (0x4 | 1) );
REQUIRE( jit.Regs()[15] == 0xFFFFFFD6 ); REQUIRE( jit.Regs()[15] == 0xFFFFFFD6 );

View file

@ -20,7 +20,7 @@
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
#include <dynarmic/dynarmic.h> #include <dynarmic/A32/callbacks.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "skyeye_interpreter/skyeye_common/arm_regformat.h" #include "skyeye_interpreter/skyeye_common/arm_regformat.h"
@ -252,5 +252,5 @@ public:
u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode
bool exclusive_state; bool exclusive_state;
Dynarmic::UserCallbacks user_callbacks; Dynarmic::A32::UserCallbacks user_callbacks;
}; };