A32: Change UserCallbacks to be similar to A64's interface

This commit is contained in:
MerryMage 2018-01-27 22:36:55 +00:00
parent b9ce660113
commit 98ec9c5f90
21 changed files with 472 additions and 622 deletions

View file

@ -11,7 +11,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <dynarmic/A32/callbacks.h> #include <dynarmic/A32/config.h>
namespace Dynarmic { namespace Dynarmic {
namespace IR { namespace IR {
@ -26,7 +26,7 @@ struct Context;
class Jit final { class Jit final {
public: public:
explicit Jit(UserCallbacks callbacks); explicit Jit(UserConfig conf);
~Jit(); ~Jit();
/** /**

View file

@ -1,71 +0,0 @@
/* 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 <array>
#include <cstddef>
#include <cstdint>
#include <memory>
namespace Dynarmic {
namespace A32 {
class Coprocessor;
class Jit;
/// These function pointers may be inserted into compiled code.
struct UserCallbacks {
struct Memory {
// All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian.
std::uint32_t (*ReadCode)(std::uint32_t vaddr);
// Reads through these callbacks may not be aligned.
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
std::uint8_t (*Read8)(std::uint32_t vaddr);
std::uint16_t (*Read16)(std::uint32_t vaddr);
std::uint32_t (*Read32)(std::uint32_t vaddr);
std::uint64_t (*Read64)(std::uint32_t vaddr);
// Writes through these callbacks may not be aligned.
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
void (*Write8)(std::uint32_t vaddr, std::uint8_t value);
void (*Write16)(std::uint32_t vaddr, std::uint16_t value);
void (*Write32)(std::uint32_t vaddr, std::uint32_t value);
void (*Write64)(std::uint32_t vaddr, std::uint64_t value);
// If this callback returns true, the JIT will assume MemoryRead* callbacks will always
// return the same value at any point in time for this vaddr. The JIT may use this information
// in optimizations.
// An conservative implementation that always returns false is safe.
bool (*IsReadOnlyMemory)(std::uint32_t vaddr);
} memory = {};
/// The intrepreter must execute only one instruction at PC.
void (*InterpreterFallback)(std::uint32_t pc, Jit* jit, void* user_arg);
void* user_arg = nullptr;
// This callback is called whenever a SVC instruction is executed.
void (*CallSVC)(std::uint32_t swi);
// Timing-related callbacks
void (*AddTicks)(std::uint64_t ticks);
std::uint64_t (*GetTicksRemaining)();
// Page Table
// The page table is used for faster memory access. If an entry in the table is nullptr,
// the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks.
static constexpr std::size_t PAGE_BITS = 12;
static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>* page_table = nullptr;
// Coprocessors
std::array<std::shared_ptr<Coprocessor>, 16> coprocessors;
};
} // namespace A32
} // namespace Dynarmic

View file

@ -0,0 +1,86 @@
/* 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 <array>
#include <cstddef>
#include <cstdint>
#include <memory>
namespace Dynarmic {
namespace A32 {
using VAddr = std::uint32_t;
class Coprocessor;
enum class Exception {
/// An UndefinedFault occured due to executing instruction with an unallocated encoding
UndefinedInstruction,
/// An unpredictable instruction is to be executed. Implementation-defined behaviour should now happen.
/// This behaviour is up to the user of this library to define.
UnpredictableInstruction,
};
/// These function pointers may be inserted into compiled code.
struct UserCallbacks {
virtual ~UserCallbacks() = default;
// All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian.
virtual std::uint32_t MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
// Reads through these callbacks may not be aligned.
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
// Writes through these callbacks may not be aligned.
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
// If this callback returns true, the JIT will assume MemoryRead* callbacks will always
// return the same value at any point in time for this vaddr. The JIT may use this information
// in optimizations.
// A conservative implementation that always returns false is safe.
virtual bool IsReadOnlyMemory(VAddr /* vaddr */) { return false; }
/// The intrepreter must execute exactly num_instructions starting from PC.
virtual void InterpreterFallback(VAddr pc, size_t num_instructions) = 0;
// This callback is called whenever a SVC instruction is executed.
virtual void CallSVC(std::uint32_t swi) = 0;
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
// Timing-related callbacks
// ticks ticks have passed
virtual void AddTicks(std::uint64_t ticks) = 0;
// How many more ticks am I allowed to execute?
virtual std::uint64_t GetTicksRemaining() = 0;
};
struct UserConfig {
UserCallbacks* callbacks;
// Page Table
// The page table is used for faster memory access. If an entry in the table is nullptr,
// the JIT will fallback to calling the MemoryRead*/MemoryWrite* callbacks.
static constexpr std::size_t PAGE_BITS = 12;
static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>* page_table = nullptr;
// Coprocessors
std::array<std::shared_ptr<Coprocessor>, 16> coprocessors;
};
} // namespace A32
} // namespace Dynarmic

View file

@ -1,6 +1,6 @@
add_library(dynarmic add_library(dynarmic
../include/dynarmic/A32/a32.h ../include/dynarmic/A32/a32.h
../include/dynarmic/A32/callbacks.h ../include/dynarmic/A32/config.h
../include/dynarmic/A32/coprocessor.h ../include/dynarmic/A32/coprocessor.h
../include/dynarmic/A32/coprocessor_util.h ../include/dynarmic/A32/coprocessor_util.h
../include/dynarmic/A32/disassembler.h ../include/dynarmic/A32/disassembler.h

View file

@ -13,6 +13,7 @@
#include "backend_x64/a32_jitstate.h" #include "backend_x64/a32_jitstate.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/devirtualize.h"
#include "backend_x64/emit_x64.h" #include "backend_x64/emit_x64.h"
#include "common/address_range.h" #include "common/address_range.h"
#include "common/assert.h" #include "common/assert.h"
@ -67,8 +68,8 @@ bool A32EmitContext::FPSCR_DN() const {
return Location().FPSCR().DN(); return Location().FPSCR().DN();
} }
A32EmitX64::A32EmitX64(BlockOfCode* code, A32::UserCallbacks cb, A32::Jit* jit_interface) A32EmitX64::A32EmitX64(BlockOfCode* code, A32::UserConfig config, A32::Jit* jit_interface)
: EmitX64(code), cb(cb), jit_interface(jit_interface) : EmitX64(code), config(config), jit_interface(jit_interface)
{ {
GenMemoryAccessors(); GenMemoryAccessors();
code->PreludeComplete(); code->PreludeComplete();
@ -146,56 +147,56 @@ void A32EmitX64::GenMemoryAccessors() {
code->align(); code->align();
read_memory_8 = code->getCurr<const void*>(); read_memory_8 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read8); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryRead8).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
read_memory_16 = code->getCurr<const void*>(); read_memory_16 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read16); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryRead16).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
read_memory_32 = code->getCurr<const void*>(); read_memory_32 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read32); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryRead32).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
read_memory_64 = code->getCurr<const void*>(); read_memory_64 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Read64); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryRead64).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
write_memory_8 = code->getCurr<const void*>(); write_memory_8 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write8); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryWrite8).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
write_memory_16 = code->getCurr<const void*>(); write_memory_16 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write16); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryWrite16).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
write_memory_32 = code->getCurr<const void*>(); write_memory_32 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write32); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryWrite32).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
code->align(); code->align();
write_memory_64 = code->getCurr<const void*>(); write_memory_64 = code->getCurr<const void*>();
ABI_PushCallerSaveRegistersAndAdjustStack(code); ABI_PushCallerSaveRegistersAndAdjustStack(code);
code->CallFunction(cb.memory.Write64); DEVIRT(config.callbacks, &A32::UserCallbacks::MemoryWrite64).EmitCall(code);
ABI_PopCallerSaveRegistersAndAdjustStack(code); ABI_PopCallerSaveRegistersAndAdjustStack(code);
code->ret(); code->ret();
} }
@ -568,14 +569,14 @@ void A32EmitX64::EmitA32CallSupervisor(A32EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(nullptr); ctx.reg_alloc.HostCall(nullptr);
code->SwitchMxcsrOnExit(); code->SwitchMxcsrOnExit();
code->mov(code->ABI_PARAM1, qword[r15 + offsetof(A32JitState, cycles_to_run)]); code->mov(code->ABI_PARAM2, qword[r15 + offsetof(A32JitState, cycles_to_run)]);
code->sub(code->ABI_PARAM1, qword[r15 + offsetof(A32JitState, cycles_remaining)]); code->sub(code->ABI_PARAM2, qword[r15 + offsetof(A32JitState, cycles_remaining)]);
code->CallFunction(cb.AddTicks); DEVIRT(config.callbacks, &A32::UserCallbacks::AddTicks).EmitCall(code);
ctx.reg_alloc.EndOfAllocScope(); ctx.reg_alloc.EndOfAllocScope();
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ctx.reg_alloc.HostCall(nullptr, args[0]); ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
code->CallFunction(cb.CallSVC); DEVIRT(config.callbacks, &A32::UserCallbacks::CallSVC).EmitCall(code);
code->CallFunction(cb.GetTicksRemaining); DEVIRT(config.callbacks, &A32::UserCallbacks::GetTicksRemaining).EmitCall(code);
code->mov(qword[r15 + offsetof(A32JitState, cycles_to_run)], code->ABI_RETURN); code->mov(qword[r15 + offsetof(A32JitState, cycles_to_run)], code->ABI_RETURN);
code->mov(qword[r15 + offsetof(A32JitState, cycles_remaining)], code->ABI_RETURN); code->mov(qword[r15 + offsetof(A32JitState, cycles_remaining)], code->ABI_RETURN);
code->SwitchMxcsrOnEntry(); code->SwitchMxcsrOnEntry();
@ -632,26 +633,27 @@ 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 RawFn> template <typename T, T (A32::UserCallbacks::*raw_fn)(A32::VAddr)>
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) { static void ReadMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, const A32::UserConfig& config, const CodePtr wrapped_fn) {
constexpr size_t bit_size = Common::BitSize<T>();
auto args = reg_alloc.GetArgumentInfo(inst); auto args = reg_alloc.GetArgumentInfo(inst);
if (!cb.page_table) { if (!config.page_table) {
reg_alloc.HostCall(inst, args[0]); reg_alloc.HostCall(inst, {}, args[0]);
code->CallFunction(raw_fn); DEVIRT(config.callbacks, raw_fn).EmitCall(code);
return; return;
} }
reg_alloc.UseScratch(args[0], ABI_PARAM1); reg_alloc.UseScratch(args[0], ABI_PARAM2);
Xbyak::Reg64 result = reg_alloc.ScratchGpr({ABI_RETURN}); Xbyak::Reg64 result = reg_alloc.ScratchGpr({ABI_RETURN});
Xbyak::Reg32 vaddr = code->ABI_PARAM1.cvt32(); Xbyak::Reg32 vaddr = code->ABI_PARAM2.cvt32();
Xbyak::Reg64 page_index = reg_alloc.ScratchGpr(); Xbyak::Reg64 page_index = reg_alloc.ScratchGpr();
Xbyak::Reg64 page_offset = reg_alloc.ScratchGpr(); Xbyak::Reg64 page_offset = reg_alloc.ScratchGpr();
Xbyak::Label abort, end; Xbyak::Label abort, end;
code->mov(result, reinterpret_cast<u64>(cb.page_table)); code->mov(result, reinterpret_cast<u64>(config.page_table));
code->mov(page_index.cvt32(), vaddr); code->mov(page_index.cvt32(), vaddr);
code->shr(page_index.cvt32(), 12); code->shr(page_index.cvt32(), 12);
code->mov(result, qword[result + page_index * 8]); code->mov(result, qword[result + page_index * 8]);
@ -684,28 +686,29 @@ static void ReadMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, c
reg_alloc.DefineValue(inst, result); reg_alloc.DefineValue(inst, result);
} }
template <typename RawFn> template <typename T, void (A32::UserCallbacks::*raw_fn)(A32::VAddr, T)>
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) { static void WriteMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, const A32::UserConfig& config, const CodePtr wrapped_fn) {
constexpr size_t bit_size = Common::BitSize<T>();
auto args = reg_alloc.GetArgumentInfo(inst); auto args = reg_alloc.GetArgumentInfo(inst);
if (!cb.page_table) { if (!config.page_table) {
reg_alloc.HostCall(nullptr, args[0], args[1]); reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
code->CallFunction(raw_fn); DEVIRT(config.callbacks, raw_fn).EmitCall(code);
return; return;
} }
reg_alloc.ScratchGpr({ABI_RETURN}); reg_alloc.ScratchGpr({ABI_RETURN});
reg_alloc.UseScratch(args[0], ABI_PARAM1); reg_alloc.UseScratch(args[0], ABI_PARAM2);
reg_alloc.UseScratch(args[1], ABI_PARAM2); reg_alloc.UseScratch(args[1], ABI_PARAM3);
Xbyak::Reg32 vaddr = code->ABI_PARAM1.cvt32(); Xbyak::Reg32 vaddr = code->ABI_PARAM2.cvt32();
Xbyak::Reg64 value = code->ABI_PARAM2; Xbyak::Reg64 value = code->ABI_PARAM3;
Xbyak::Reg64 page_index = reg_alloc.ScratchGpr(); Xbyak::Reg64 page_index = reg_alloc.ScratchGpr();
Xbyak::Reg64 page_offset = reg_alloc.ScratchGpr(); Xbyak::Reg64 page_offset = reg_alloc.ScratchGpr();
Xbyak::Label abort, end; Xbyak::Label abort, end;
code->mov(rax, reinterpret_cast<u64>(cb.page_table)); code->mov(rax, reinterpret_cast<u64>(config.page_table));
code->mov(page_index.cvt32(), vaddr); code->mov(page_index.cvt32(), vaddr);
code->shr(page_index.cvt32(), 12); code->shr(page_index.cvt32(), 12);
code->mov(rax, qword[rax + page_index * 8]); code->mov(rax, qword[rax + page_index * 8]);
@ -737,44 +740,44 @@ static void WriteMemory(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst,
} }
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, read_memory_8); ReadMemory<u8, &A32::UserCallbacks::MemoryRead8>(code, ctx.reg_alloc, inst, config, 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, read_memory_16); ReadMemory<u16, &A32::UserCallbacks::MemoryRead16>(code, ctx.reg_alloc, inst, config, 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, read_memory_32); ReadMemory<u32, &A32::UserCallbacks::MemoryRead32>(code, ctx.reg_alloc, inst, config, 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, read_memory_64); ReadMemory<u64, &A32::UserCallbacks::MemoryRead64>(code, ctx.reg_alloc, inst, config, 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, write_memory_8); WriteMemory<u8, &A32::UserCallbacks::MemoryWrite8>(code, ctx.reg_alloc, inst, config, 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, write_memory_16); WriteMemory<u16, &A32::UserCallbacks::MemoryWrite16>(code, ctx.reg_alloc, inst, config, 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, write_memory_32); WriteMemory<u32, &A32::UserCallbacks::MemoryWrite32>(code, ctx.reg_alloc, inst, config, 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, write_memory_64); WriteMemory<u64, &A32::UserCallbacks::MemoryWrite64>(code, ctx.reg_alloc, inst, config, write_memory_64);
} }
template <typename FunctionPointer> template <typename T, void (A32::UserCallbacks::*fn)(A32::VAddr, T)>
static void ExclusiveWrite(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, FunctionPointer fn, bool prepend_high_word) { static void ExclusiveWrite(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* inst, const A32::UserConfig& config, bool prepend_high_word) {
auto args = reg_alloc.GetArgumentInfo(inst); auto args = reg_alloc.GetArgumentInfo(inst);
if (prepend_high_word) { if (prepend_high_word) {
reg_alloc.HostCall(nullptr, args[0], args[1], args[2]); reg_alloc.HostCall(nullptr, {}, args[0], args[1], args[2]);
} else { } else {
reg_alloc.HostCall(nullptr, args[0], args[1]); reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
} }
Xbyak::Reg32 passed = reg_alloc.ScratchGpr().cvt32(); Xbyak::Reg32 passed = reg_alloc.ScratchGpr().cvt32();
Xbyak::Reg32 tmp = code->ABI_RETURN.cvt32(); // Use one of the unusued HostCall registers. Xbyak::Reg32 tmp = code->ABI_RETURN.cvt32(); // Use one of the unusued HostCall registers.
@ -784,17 +787,17 @@ static void ExclusiveWrite(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* ins
code->mov(passed, u32(1)); code->mov(passed, u32(1));
code->cmp(code->byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0)); code->cmp(code->byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0));
code->je(end); code->je(end);
code->mov(tmp, code->ABI_PARAM1); code->mov(tmp, code->ABI_PARAM2);
code->xor_(tmp, dword[r15 + offsetof(A32JitState, exclusive_address)]); code->xor_(tmp, dword[r15 + offsetof(A32JitState, exclusive_address)]);
code->test(tmp, A32JitState::RESERVATION_GRANULE_MASK); code->test(tmp, A32JitState::RESERVATION_GRANULE_MASK);
code->jne(end); code->jne(end);
code->mov(code->byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0)); code->mov(code->byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0));
if (prepend_high_word) { if (prepend_high_word) {
code->mov(code->ABI_PARAM2.cvt32(), code->ABI_PARAM2.cvt32()); // zero extend to 64-bits code->mov(code->ABI_PARAM3.cvt32(), code->ABI_PARAM3.cvt32()); // zero extend to 64-bits
code->shl(code->ABI_PARAM3, 32); code->shl(code->ABI_PARAM4, 32);
code->or_(code->ABI_PARAM2, code->ABI_PARAM3); code->or_(code->ABI_PARAM3, code->ABI_PARAM4);
} }
code->CallFunction(fn); DEVIRT(config.callbacks, fn).EmitCall(code);
code->xor_(passed, passed); code->xor_(passed, passed);
code->L(end); code->L(end);
@ -802,19 +805,19 @@ static void ExclusiveWrite(BlockOfCode* code, RegAlloc& reg_alloc, IR::Inst* ins
} }
void A32EmitX64::EmitA32ExclusiveWriteMemory8(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveWriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
ExclusiveWrite(code, ctx.reg_alloc, inst, cb.memory.Write8, false); ExclusiveWrite<u8, &A32::UserCallbacks::MemoryWrite8>(code, ctx.reg_alloc, inst, config, false);
} }
void A32EmitX64::EmitA32ExclusiveWriteMemory16(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveWriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
ExclusiveWrite(code, ctx.reg_alloc, inst, cb.memory.Write16, false); ExclusiveWrite<u16, &A32::UserCallbacks::MemoryWrite16>(code, ctx.reg_alloc, inst, config, false);
} }
void A32EmitX64::EmitA32ExclusiveWriteMemory32(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveWriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
ExclusiveWrite(code, ctx.reg_alloc, inst, cb.memory.Write32, false); ExclusiveWrite<u32, &A32::UserCallbacks::MemoryWrite32>(code, ctx.reg_alloc, inst, config, false);
} }
void A32EmitX64::EmitA32ExclusiveWriteMemory64(A32EmitContext& ctx, IR::Inst* inst) { void A32EmitX64::EmitA32ExclusiveWriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
ExclusiveWrite(code, ctx.reg_alloc, inst, cb.memory.Write64, true); ExclusiveWrite<u64, &A32::UserCallbacks::MemoryWrite64>(code, ctx.reg_alloc, inst, config, true);
} }
static void EmitCoprocessorException() { static void EmitCoprocessorException() {
@ -843,7 +846,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -869,7 +872,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -908,7 +911,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -951,7 +954,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -991,7 +994,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -1039,7 +1042,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -1065,7 +1068,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<A32::Coprocessor> coproc = cb.coprocessors[coproc_num]; std::shared_ptr<A32::Coprocessor> coproc = config.coprocessors[coproc_num];
if (!coproc) { if (!coproc) {
EmitCoprocessorException(); EmitCoprocessorException();
return; return;
@ -1085,12 +1088,11 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDesc
ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented"); ASSERT_MSG(A32::LocationDescriptor{terminal.next}.EFlag() == A32::LocationDescriptor{initial_location}.EFlag(), "Unimplemented");
ASSERT_MSG(terminal.num_instructions == 1, "Unimplemented"); ASSERT_MSG(terminal.num_instructions == 1, "Unimplemented");
code->mov(code->ABI_PARAM1.cvt32(), A32::LocationDescriptor{terminal.next}.PC()); code->mov(code->ABI_PARAM2.cvt32(), A32::LocationDescriptor{terminal.next}.PC());
code->mov(code->ABI_PARAM2, reinterpret_cast<u64>(jit_interface)); code->mov(code->ABI_PARAM3.cvt32(), 1);
code->mov(code->ABI_PARAM3, reinterpret_cast<u64>(cb.user_arg)); code->mov(MJitStateReg(A32::Reg::PC), code->ABI_PARAM2.cvt32());
code->mov(MJitStateReg(A32::Reg::PC), code->ABI_PARAM1.cvt32());
code->SwitchMxcsrOnExit(); code->SwitchMxcsrOnExit();
code->CallFunction(cb.InterpreterFallback); DEVIRT(config.callbacks, &A32::UserCallbacks::InterpreterFallback).EmitCall(code);
code->ReturnFromRunCode(true); // TODO: Check cycles code->ReturnFromRunCode(true); // TODO: Check cycles
} }

View file

@ -12,7 +12,7 @@
#include "backend_x64/block_range_information.h" #include "backend_x64/block_range_information.h"
#include "backend_x64/emit_x64.h" #include "backend_x64/emit_x64.h"
#include "dynarmic/A32/a32.h" #include "dynarmic/A32/a32.h"
#include "dynarmic/A32/callbacks.h" #include "dynarmic/A32/config.h"
#include "frontend/A32/location_descriptor.h" #include "frontend/A32/location_descriptor.h"
#include "frontend/ir/terminal.h" #include "frontend/ir/terminal.h"
@ -30,7 +30,7 @@ struct A32EmitContext final : public EmitContext {
class A32EmitX64 final : public EmitX64 { class A32EmitX64 final : public EmitX64 {
public: public:
A32EmitX64(BlockOfCode* code, A32::UserCallbacks cb, A32::Jit* jit_interface); A32EmitX64(BlockOfCode* code, A32::UserConfig config, A32::Jit* jit_interface);
~A32EmitX64() override; ~A32EmitX64() override;
/** /**
@ -44,7 +44,7 @@ public:
void InvalidateCacheRanges(const boost::icl::interval_set<u32>& ranges); void InvalidateCacheRanges(const boost::icl::interval_set<u32>& ranges);
protected: protected:
const A32::UserCallbacks cb; const A32::UserConfig config;
A32::Jit* jit_interface; A32::Jit* jit_interface;
BlockRangeInformation<u32> block_ranges; BlockRangeInformation<u32> block_ranges;

View file

@ -18,6 +18,7 @@
#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/callback.h" #include "backend_x64/callback.h"
#include "backend_x64/devirtualize.h"
#include "backend_x64/jitstate_info.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"
@ -33,26 +34,26 @@ namespace Dynarmic::A32 {
using namespace BackendX64; using namespace BackendX64;
static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg) { static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg) {
return RunCodeCallbacks{ return RunCodeCallbacks{
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)), std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
std::make_unique<SimpleCallback>(cb.AddTicks), std::make_unique<ArgCallback>(DEVIRT(cb, &A32::UserCallbacks::AddTicks)),
std::make_unique<SimpleCallback>(cb.GetTicksRemaining), std::make_unique<ArgCallback>(DEVIRT(cb, &A32::UserCallbacks::GetTicksRemaining)),
}; };
} }
struct Jit::Impl { struct Jit::Impl {
Impl(Jit* jit, A32::UserCallbacks callbacks) Impl(Jit* jit, A32::UserConfig config)
: block_of_code(GenRunCodeCallbacks(callbacks, &GetCurrentBlock, this), JitStateInfo{jit_state}) : block_of_code(GenRunCodeCallbacks(config.callbacks, &GetCurrentBlock, this), JitStateInfo{jit_state})
, emitter(&block_of_code, callbacks, jit) , emitter(&block_of_code, config, jit)
, callbacks(callbacks) , config(config)
, jit_interface(jit) , jit_interface(jit)
{} {}
A32JitState jit_state; A32JitState jit_state;
BlockOfCode block_of_code; BlockOfCode block_of_code;
A32EmitX64 emitter; A32EmitX64 emitter;
const A32::UserCallbacks callbacks; const A32::UserConfig config;
// 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;
@ -164,10 +165,10 @@ private:
PerformCacheInvalidation(); PerformCacheInvalidation();
} }
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, callbacks.memory.ReadCode); IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return config.callbacks->MemoryReadCode(vaddr); });
Optimization::A32GetSetElimination(ir_block); Optimization::A32GetSetElimination(ir_block);
Optimization::DeadCodeElimination(ir_block); Optimization::DeadCodeElimination(ir_block);
Optimization::A32ConstantMemoryReads(ir_block, callbacks.memory); Optimization::A32ConstantMemoryReads(ir_block, config.callbacks);
Optimization::ConstantPropagation(ir_block); Optimization::ConstantPropagation(ir_block);
Optimization::DeadCodeElimination(ir_block); Optimization::DeadCodeElimination(ir_block);
Optimization::VerificationPass(ir_block); Optimization::VerificationPass(ir_block);
@ -175,7 +176,7 @@ private:
} }
}; };
Jit::Jit(UserCallbacks callbacks) : impl(std::make_unique<Impl>(this, callbacks)) {} Jit::Jit(UserConfig config) : impl(std::make_unique<Impl>(this, config)) {}
Jit::~Jit() {} Jit::~Jit() {}

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <array>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
@ -16,7 +17,6 @@
#include "backend_x64/constant_pool.h" #include "backend_x64/constant_pool.h"
#include "backend_x64/jitstate_info.h" #include "backend_x64/jitstate_info.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "dynarmic/A32/callbacks.h"
namespace Dynarmic::BackendX64 { namespace Dynarmic::BackendX64 {

View file

@ -30,7 +30,7 @@ struct ThunkBuilder<R(C::*)(Args...), mfp> {
} // namespace impl } // namespace impl
template <typename FunctionType, FunctionType mfp> template <typename FunctionType, FunctionType mfp>
ArgCallback Devirtualize(mp::class_type_t<decltype(mfp)>* this_) { ArgCallback Devirtualize(mp::class_type_t<FunctionType>* this_) {
return ArgCallback{&impl::ThunkBuilder<FunctionType, mfp>::Thunk, reinterpret_cast<u64>(this_)}; return ArgCallback{&impl::ThunkBuilder<FunctionType, mfp>::Thunk, reinterpret_cast<u64>(this_)};
} }

View file

@ -15,7 +15,7 @@ namespace Dynarmic::A32 {
class LocationDescriptor; class LocationDescriptor;
using MemoryReadCodeFuncType = u32 (*)(u32 vaddr); using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>;
/** /**
* This function translates instructions in memory into our intermediate representation. * This function translates instructions in memory into our intermediate representation.

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/A32/callbacks.h> #include <dynarmic/A32/config.h>
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "frontend/ir/opcodes.h" #include "frontend/ir/opcodes.h"
@ -12,7 +12,7 @@
namespace Dynarmic::Optimization { namespace Dynarmic::Optimization {
void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory& memory_callbacks) { void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
for (auto& inst : block) { for (auto& inst : block) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::A32SetCFlag: { case IR::Opcode::A32SetCFlag: {
@ -27,8 +27,8 @@ void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory&
break; break;
u32 vaddr = inst.GetArg(0).GetU32(); u32 vaddr = inst.GetArg(0).GetU32();
if (memory_callbacks.IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
u8 value_from_memory = memory_callbacks.Read8(vaddr); u8 value_from_memory = cb->MemoryRead8(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
break; break;
@ -38,8 +38,8 @@ void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory&
break; break;
u32 vaddr = inst.GetArg(0).GetU32(); u32 vaddr = inst.GetArg(0).GetU32();
if (memory_callbacks.IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
u16 value_from_memory = memory_callbacks.Read16(vaddr); u16 value_from_memory = cb->MemoryRead16(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
break; break;
@ -49,8 +49,8 @@ void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory&
break; break;
u32 vaddr = inst.GetArg(0).GetU32(); u32 vaddr = inst.GetArg(0).GetU32();
if (memory_callbacks.IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
u32 value_from_memory = memory_callbacks.Read32(vaddr); u32 value_from_memory = cb->MemoryRead32(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
break; break;
@ -60,8 +60,8 @@ void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory&
break; break;
u32 vaddr = inst.GetArg(0).GetU32(); u32 vaddr = inst.GetArg(0).GetU32();
if (memory_callbacks.IsReadOnlyMemory(vaddr)) { if (cb->IsReadOnlyMemory(vaddr)) {
u64 value_from_memory = memory_callbacks.Read64(vaddr); u64 value_from_memory = cb->MemoryRead64(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory}); inst.ReplaceUsesWith(IR::Value{value_from_memory});
} }
break; break;

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/A32/callbacks.h> #include <dynarmic/A32/config.h>
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "frontend/ir/opcodes.h" #include "frontend/ir/opcodes.h"

View file

@ -6,7 +6,7 @@
#pragma once #pragma once
#include <dynarmic/A32/callbacks.h> #include <dynarmic/A32/config.h>
#include <dynarmic/A64/config.h> #include <dynarmic/A64/config.h>
namespace Dynarmic::IR { namespace Dynarmic::IR {
@ -16,7 +16,7 @@ class Block;
namespace Dynarmic::Optimization { namespace Dynarmic::Optimization {
void A32GetSetElimination(IR::Block& block); void A32GetSetElimination(IR::Block& block);
void A32ConstantMemoryReads(IR::Block& block, const A32::UserCallbacks::Memory& memory_callbacks); void A32ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb);
void A64GetSetElimination(IR::Block& block); void A64GetSetElimination(IR::Block& block);
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb); void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb);
void ConstantPropagation(IR::Block& block); void ConstantPropagation(IR::Block& block);

View file

@ -28,6 +28,7 @@
#include "frontend/ir/location_descriptor.h" #include "frontend/ir/location_descriptor.h"
#include "ir_opt/passes.h" #include "ir_opt/passes.h"
#include "rand_int.h" #include "rand_int.h"
#include "testenv.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "A32/skyeye_interpreter/skyeye_common/armstate.h" #include "A32/skyeye_interpreter/skyeye_common/armstate.h"
@ -37,126 +38,10 @@
using Dynarmic::Common::Bits; using Dynarmic::Common::Bits;
struct WriteRecord { static Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
size_t size; Dynarmic::A32::UserConfig user_config;
u32 address; user_config.callbacks = testenv;
u64 data; return user_config;
};
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);
}
static u64 jit_num_ticks = 0;
static std::array<u32, 3000> code_mem{};
static std::vector<WriteRecord> write_records;
static u64 GetTicksRemaining();
static void AddTicks(u64 ticks);
static bool IsReadOnlyMemory(u32 vaddr);
static u8 MemoryRead8(u32 vaddr);
static u16 MemoryRead16(u32 vaddr);
static u32 MemoryRead32(u32 vaddr);
static u64 MemoryRead64(u32 vaddr);
static u32 MemoryReadCode(u32 vaddr);
static void MemoryWrite8(u32 vaddr, u8 value);
static void MemoryWrite16(u32 vaddr, u16 value);
static void MemoryWrite32(u32 vaddr, u32 value);
static void MemoryWrite64(u32 vaddr, u64 value);
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
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) {
return vaddr < code_mem.size();
}
static u8 MemoryRead8(u32 vaddr) {
return static_cast<u8>(vaddr);
}
static u16 MemoryRead16(u32 vaddr) {
return static_cast<u16>(vaddr);
}
static u32 MemoryRead32(u32 vaddr) {
return vaddr;
}
static u64 MemoryRead64(u32 vaddr) {
return MemoryRead32(vaddr) | (u64(MemoryRead32(vaddr+4)) << 32);
}
static u32 MemoryReadCode(u32 vaddr) {
if (vaddr < code_mem.size() * sizeof(u32)) {
size_t index = vaddr / sizeof(u32);
return code_mem[index];
}
return 0xeafffffe; // b +#0
}
static void MemoryWrite8(u32 vaddr, u8 value){
write_records.push_back({8, vaddr, value});
}
static void MemoryWrite16(u32 vaddr, u16 value){
write_records.push_back({16, vaddr, value});
}
static void MemoryWrite32(u32 vaddr, u32 value){
write_records.push_back({32, vaddr, value});
}
static void MemoryWrite64(u32 vaddr, u64 value){
write_records.push_back({64, vaddr, value});
}
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
ARMul_State interp_state{USER32MODE};
interp_state.user_callbacks = GetUserCallbacks();
interp_state.NumInstrsToExecute = 1;
interp_state.Reg = jit->Regs();
interp_state.ExtReg = jit->ExtRegs();
interp_state.Cpsr = jit->Cpsr();
interp_state.VFP[VFP_FPSCR] = jit->Fpscr();
interp_state.Reg[15] = pc;
InterpreterClearCache();
InterpreterMainLoop(&interp_state);
bool T = Dynarmic::Common::Bit<5>(interp_state.Cpsr);
interp_state.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
jit->Regs() = interp_state.Reg;
jit->ExtRegs() = interp_state.ExtReg;
jit->SetCpsr(interp_state.Cpsr);
jit->SetFpscr(interp_state.VFP[VFP_FPSCR]);
}
static void Fail() {
FAIL();
}
static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
Dynarmic::A32::UserCallbacks user_callbacks{};
user_callbacks.InterpreterFallback = &InterpreterFallback;
user_callbacks.CallSVC = (void (*)(u32)) &Fail;
user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
user_callbacks.memory.Read8 = &MemoryRead8;
user_callbacks.memory.Read16 = &MemoryRead16;
user_callbacks.memory.Read32 = &MemoryRead32;
user_callbacks.memory.Read64 = &MemoryRead64;
user_callbacks.memory.ReadCode = &MemoryReadCode;
user_callbacks.memory.Write8 = &MemoryWrite8;
user_callbacks.memory.Write16 = &MemoryWrite16;
user_callbacks.memory.Write32 = &MemoryWrite32;
user_callbacks.memory.Write64 = &MemoryWrite64;
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
user_callbacks.AddTicks = &AddTicks;
return user_callbacks;
} }
namespace { namespace {
@ -210,7 +95,9 @@ private:
}; };
} // namespace } // namespace
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) { using WriteRecords = std::map<u32, u8>;
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const WriteRecords& interp_write_records, const WriteRecords& 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()
@ -219,13 +106,15 @@ static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Ji
} }
void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) { void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) {
ArmTestEnv test_env;
// Prepare memory // Prepare memory
code_mem.fill(0xEAFFFFFE); // b +#0 test_env.code_mem.fill(0xEAFFFFFE); // b +#0
// Prepare test subjects // Prepare test subjects
ARMul_State interp{USER32MODE}; ARMul_State interp{USER32MODE};
interp.user_callbacks = GetUserCallbacks(); interp.user_callbacks = &test_env;
Dynarmic::A32::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
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();
@ -256,26 +145,25 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
jit.ExtRegs() = initial_extregs; jit.ExtRegs() = initial_extregs;
jit.SetFpscr(initial_fpscr); jit.SetFpscr(initial_fpscr);
std::generate_n(code_mem.begin(), instruction_count, instruction_generator); std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
// Run interpreter // Run interpreter
write_records.clear(); test_env.modified_memory.clear();
std::vector<WriteRecord> interp_write_records;
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count); interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
InterpreterMainLoop(&interp); InterpreterMainLoop(&interp);
interp_write_records = write_records; WriteRecords interp_write_records = test_env.modified_memory;
{ {
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr); bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC; interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
} }
// Run jit // Run jit
write_records.clear(); test_env.modified_memory.clear();
std::vector<WriteRecord> jit_write_records; WriteRecords jit_write_records;
try { try {
jit_num_ticks = instructions_to_execute_count; test_env.ticks_left = instructions_to_execute_count;
jit.Run(); jit.Run();
jit_write_records = write_records; jit_write_records = test_env.modified_memory;
} catch (...) { } catch (...) {
printf("Caught something!\n"); printf("Caught something!\n");
goto dump_state; goto dump_state;
@ -289,7 +177,7 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
printf("\nInstruction Listing: \n"); printf("\nInstruction Listing: \n");
for (size_t i = 0; i < instruction_count; i++) { for (size_t i = 0; i < instruction_count; i++) {
printf("%x: %s\n", code_mem[i], Dynarmic::A32::DisassembleArm(code_mem[i]).c_str()); printf("%x: %s\n", test_env.code_mem[i], Dynarmic::A32::DisassembleArm(test_env.code_mem[i]).c_str());
} }
printf("\nInitial Register Listing: \n"); printf("\nInitial Register Listing: \n");
@ -317,21 +205,21 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
printf("\nInterp Write Records:\n"); printf("\nInterp Write Records:\n");
for (auto& record : interp_write_records) { for (auto& record : interp_write_records) {
printf("%zu [%x] = %" PRIx64 "\n", record.size, record.address, record.data); printf("[%08x] = %02x\n", record.first, record.second);
} }
printf("\nJIT Write Records:\n"); printf("\nJIT Write Records:\n");
for (auto& record : jit_write_records) { for (auto& record : jit_write_records) {
printf("%zu [%x] = %" PRIx64 "\n", record.size, record.address, record.data); printf("[%08x] = %02x\n", record.first, record.second);
} }
size_t num_insts = 0; size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) { while (num_insts < instructions_to_execute_count) {
Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), Dynarmic::A32::PSR{}, Dynarmic::A32::FPSCR{}}; Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), Dynarmic::A32::PSR{}, Dynarmic::A32::FPSCR{}};
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, &MemoryReadCode); Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); });
Dynarmic::Optimization::A32GetSetElimination(ir_block); Dynarmic::Optimization::A32GetSetElimination(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, GetUserCallbacks().memory); Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, &test_env);
Dynarmic::Optimization::ConstantPropagation(ir_block); Dynarmic::Optimization::ConstantPropagation(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::VerificationPass(ir_block); Dynarmic::Optimization::VerificationPass(ir_block);
@ -353,7 +241,7 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
} }
} }
TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) { TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm][A32]" ) {
// This was a randomized test-case that was failing. // This was a randomized test-case that was failing.
// //
// IR produced for location {12, !T, !E} was: // IR produced for location {12, !T, !E} was:
@ -376,14 +264,15 @@ 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::A32::Jit jit{GetUserCallbacks()}; ArmTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xe35f0cd9; // cmp pc, #55552 test_env.code_mem.fill({});
code_mem[1] = 0xe11c0474; // tst r12, r4, ror r4 test_env.code_mem[0] = 0xe35f0cd9; // cmp pc, #55552
code_mem[2] = 0xe1a006a7; // mov r0, r7, lsr #13 test_env.code_mem[1] = 0xe11c0474; // tst r12, r4, ror r4
code_mem[3] = 0xe35107fa; // cmp r1, #0x3E80000 test_env.code_mem[2] = 0xe1a006a7; // mov r0, r7, lsr #13
code_mem[4] = 0xe2a54c8a; // adc r4, r5, #35328 test_env.code_mem[3] = 0xe35107fa; // cmp r1, #0x3E80000
code_mem[5] = 0xeafffffe; // b +#0 test_env.code_mem[4] = 0xe2a54c8a; // adc r4, r5, #35328
test_env.code_mem[5] = 0xeafffffe; // b +#0
jit.Regs() = { jit.Regs() = {
0x6973b6bb, 0x267ea626, 0x69debf49, 0x8f976895, 0x4ecd2d0d, 0xcf89b8c7, 0xb6713f85, 0x15e2aa5, 0x6973b6bb, 0x267ea626, 0x69debf49, 0x8f976895, 0x4ecd2d0d, 0xcf89b8c7, 0xb6713f85, 0x15e2aa5,
@ -391,7 +280,7 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
}; };
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit_num_ticks = 6; test_env.ticks_left = 6;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[0] == 0x00000af1 ); REQUIRE( jit.Regs()[0] == 0x00000af1 );
@ -413,16 +302,17 @@ TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm]" ) {
REQUIRE( jit.Cpsr() == 0x200001d0 ); REQUIRE( jit.Cpsr() == 0x200001d0 );
} }
TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) { TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm][A32]" ) {
// This was a randomized test-case that was failing. // This was a randomized test-case that was failing.
// //
// 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::A32::Jit jit{GetUserCallbacks()}; ArmTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xe63dbf59; // shsax r11, sp, r9 test_env.code_mem.fill({});
code_mem[1] = 0xeafffffe; // b +#0 test_env.code_mem[0] = 0xe63dbf59; // shsax r11, sp, r9
test_env.code_mem[1] = 0xeafffffe; // b +#0
jit.Regs() = { jit.Regs() = {
0x3a3b8b18, 0x96156555, 0xffef039f, 0xafb946f2, 0x2030a69a, 0xafe09b2a, 0x896823c8, 0xabde0ded, 0x3a3b8b18, 0x96156555, 0xffef039f, 0xafb946f2, 0x2030a69a, 0xafe09b2a, 0x896823c8, 0xabde0ded,
@ -430,7 +320,7 @@ TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
}; };
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit_num_ticks = 2; test_env.ticks_left = 2;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[0] == 0x3a3b8b18 ); REQUIRE( jit.Regs()[0] == 0x3a3b8b18 );
@ -452,22 +342,23 @@ TEST_CASE( "arm: shsax r11, sp, r9 (Edge-case)", "[arm]" ) {
REQUIRE( jit.Cpsr() == 0x000001d0 ); REQUIRE( jit.Cpsr() == 0x000001d0 );
} }
TEST_CASE( "arm: uasx (Edge-case)", "[arm]" ) { TEST_CASE( "arm: uasx (Edge-case)", "[arm][A32]" ) {
// UASX's Rm<31:16> == 0x0000. // UASX's Rm<31:16> == 0x0000.
// 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::A32::Jit jit{GetUserCallbacks()}; ArmTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xe6549f35; // uasx r9, r4, r5 test_env.code_mem.fill({});
code_mem[1] = 0xeafffffe; // b +#0 test_env.code_mem[0] = 0xe6549f35; // uasx r9, r4, r5
test_env.code_mem[1] = 0xeafffffe; // b +#0
jit.Regs()[4] = 0x8ed38f4c; jit.Regs()[4] = 0x8ed38f4c;
jit.Regs()[5] = 0x0000261d; jit.Regs()[5] = 0x0000261d;
jit.Regs()[15] = 0x00000000; jit.Regs()[15] = 0x00000000;
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit_num_ticks = 2; test_env.ticks_left = 2;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[4] == 0x8ed38f4c ); REQUIRE( jit.Regs()[4] == 0x8ed38f4c );
@ -486,10 +377,11 @@ struct VfpTest {
}; };
static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) { static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ArmTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = instr; test_env.code_mem.fill({});
code_mem[1] = 0xeafffffe; // b +#0 test_env.code_mem[0] = instr;
test_env.code_mem[1] = 0xeafffffe; // b +#0
printf("vfp test 0x%08x\r", instr); printf("vfp test 0x%08x\r", instr);
@ -500,7 +392,7 @@ 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_num_ticks = 2; test_env.ticks_left = 2;
jit.Run(); jit.Run();
const auto check = [&test, &jit](bool p) { const auto check = [&test, &jit](bool p) {
@ -524,21 +416,21 @@ static void RunVfpTests(u32 instr, std::vector<VfpTest> tests) {
} }
} }
TEST_CASE("vfp: vadd", "[vfp]") { TEST_CASE("vfp: vadd", "[vfp][A32]") {
// vadd.f32 s2, s4, s6 // vadd.f32 s2, s4, s6
RunVfpTests(0xEE321A03, { RunVfpTests(0xEE321A03, {
#include "vfp_vadd_f32.inc" #include "vfp_vadd_f32.inc"
}); });
} }
TEST_CASE("vfp: vsub", "[vfp]") { TEST_CASE("vfp: vsub", "[vfp][A32]") {
// vsub.f32 s2, s4, s6 // vsub.f32 s2, s4, s6
RunVfpTests(0xEE321A43, { RunVfpTests(0xEE321A43, {
#include "vfp_vsub_f32.inc" #include "vfp_vsub_f32.inc"
}); });
} }
TEST_CASE("VFP: VMOV", "[JitX64][vfp]") { TEST_CASE("VFP: VMOV", "[JitX64][vfp][A32]") {
const auto is_valid = [](u32 instr) -> bool { const auto is_valid = [](u32 instr) -> bool {
return Bits<0, 6>(instr) != 0b111111 return Bits<0, 6>(instr) != 0b111111
&& Bits<12, 15>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111
@ -562,7 +454,7 @@ TEST_CASE("VFP: VMOV", "[JitX64][vfp]") {
}); });
} }
TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][vfp]") { TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][vfp][A32]") {
const std::array<InstructionGenerator, 4> instructions = {{ const std::array<InstructionGenerator, 4> instructions = {{
InstructionGenerator("1111000100000001000000e000000000"), // SETEND InstructionGenerator("1111000100000001000000e000000000"), // SETEND
InstructionGenerator("cccc11101D110000dddd101z01M0mmmm"), // VMOV (reg) InstructionGenerator("cccc11101D110000dddd101z01M0mmmm"), // VMOV (reg)
@ -575,7 +467,7 @@ TEST_CASE("VFP: VMOV (reg), VLDR, VSTR", "[JitX64][vfp]") {
}); });
} }
TEST_CASE("VFP: VCMP", "[JitX64][vfp]") { TEST_CASE("VFP: VCMP", "[JitX64][vfp][A32]") {
const std::array<InstructionGenerator, 2> instructions = {{ const std::array<InstructionGenerator, 2> instructions = {{
InstructionGenerator("cccc11101D110100dddd101zE1M0mmmm"), // VCMP InstructionGenerator("cccc11101D110100dddd101zE1M0mmmm"), // VCMP
InstructionGenerator("cccc11101D110101dddd101zE1000000"), // VCMP (zero) InstructionGenerator("cccc11101D110101dddd101zE1000000"), // VCMP (zero)
@ -586,7 +478,7 @@ TEST_CASE("VFP: VCMP", "[JitX64][vfp]") {
}); });
} }
TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") { TEST_CASE("Fuzz ARM data processing instructions", "[JitX64][A32]") {
const std::array<InstructionGenerator, 16> imm_instructions = {{ const std::array<InstructionGenerator, 16> imm_instructions = {{
InstructionGenerator("cccc0010101Snnnnddddrrrrvvvvvvvv"), InstructionGenerator("cccc0010101Snnnnddddrrrrvvvvvvvv"),
InstructionGenerator("cccc0010100Snnnnddddrrrrvvvvvvvv"), InstructionGenerator("cccc0010100Snnnnddddrrrrvvvvvvvv"),
@ -709,7 +601,7 @@ TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
} }
} }
TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64]") { TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64][A32]") {
auto EXD_valid = [](u32 inst) -> bool { auto EXD_valid = [](u32 inst) -> bool {
return Bits<0, 3>(inst) % 2 == 0 && Bits<0, 3>(inst) != 14 && Bits<12, 15>(inst) != (Bits<0, 3>(inst) + 1); return Bits<0, 3>(inst) % 2 == 0 && Bits<0, 3>(inst) != 14 && Bits<12, 15>(inst) != (Bits<0, 3>(inst) + 1);
}; };
@ -808,7 +700,7 @@ TEST_CASE("Fuzz ARM load/store instructions (byte, half-word, word)", "[JitX64]"
} }
} }
TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64]") { TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64][A32]") {
const std::array<InstructionGenerator, 2> instructions = {{ const std::array<InstructionGenerator, 2> instructions = {{
InstructionGenerator("cccc100pu0w1nnnnxxxxxxxxxxxxxxxx"), // LDM InstructionGenerator("cccc100pu0w1nnnnxxxxxxxxxxxxxxxx"), // LDM
InstructionGenerator("cccc100pu0w0nnnnxxxxxxxxxxxxxxxx"), // STM InstructionGenerator("cccc100pu0w0nnnnxxxxxxxxxxxxxxxx"), // STM
@ -849,7 +741,7 @@ TEST_CASE("Fuzz ARM load/store multiple instructions", "[JitX64]") {
FuzzJitArm(1, 1, 10000, instruction_select); FuzzJitArm(1, 1, 10000, instruction_select);
} }
TEST_CASE("Fuzz ARM branch instructions", "[JitX64]") { TEST_CASE("Fuzz ARM branch instructions", "[JitX64][A32]") {
const std::array<InstructionGenerator, 6> instructions = {{ const std::array<InstructionGenerator, 6> instructions = {{
InstructionGenerator("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), InstructionGenerator("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"),
InstructionGenerator("cccc000100101111111111110011mmmm", InstructionGenerator("cccc000100101111111111110011mmmm",
@ -864,7 +756,7 @@ TEST_CASE("Fuzz ARM branch instructions", "[JitX64]") {
}); });
} }
TEST_CASE("Fuzz ARM reversal instructions", "[JitX64]") { TEST_CASE("Fuzz ARM reversal instructions", "[JitX64][A32]") {
const auto is_valid = [](u32 instr) -> bool { const auto is_valid = [](u32 instr) -> bool {
// R15 is UNPREDICTABLE // R15 is UNPREDICTABLE
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111;
@ -883,7 +775,7 @@ TEST_CASE("Fuzz ARM reversal instructions", "[JitX64]") {
} }
} }
TEST_CASE("Fuzz ARM extension instructions", "[JitX64]") { TEST_CASE("Fuzz ARM extension instructions", "[JitX64][A32]") {
const auto is_valid = [](u32 instr) -> bool { const auto is_valid = [](u32 instr) -> bool {
// R15 as Rd or Rm is UNPREDICTABLE // R15 as Rd or Rm is UNPREDICTABLE
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111;
@ -920,7 +812,7 @@ TEST_CASE("Fuzz ARM extension instructions", "[JitX64]") {
} }
} }
TEST_CASE("Fuzz ARM multiply instructions", "[JitX64]") { TEST_CASE("Fuzz ARM multiply instructions", "[JitX64][A32]") {
auto validate_d_m_n = [](u32 inst) -> bool { auto validate_d_m_n = [](u32 inst) -> bool {
return Bits<16, 19>(inst) != 15 && return Bits<16, 19>(inst) != 15 &&
Bits<8, 11>(inst) != 15 && Bits<8, 11>(inst) != 15 &&
@ -970,7 +862,7 @@ TEST_CASE("Fuzz ARM multiply instructions", "[JitX64]") {
} }
} }
TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel]") { TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel][A32]") {
const auto is_valid = [](u32 instr) -> bool { const auto is_valid = [](u32 instr) -> bool {
// R15 as Rd, Rn, or Rm is UNPREDICTABLE // R15 as Rd, Rn, or Rm is UNPREDICTABLE
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111; return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111;
@ -1093,7 +985,7 @@ TEST_CASE("Fuzz ARM parallel instructions", "[JitX64][parallel]") {
} }
} }
TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64]") { TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64][A32]") {
auto validate_d_m_n = [](u32 inst) -> bool { auto validate_d_m_n = [](u32 inst) -> bool {
return Bits<16, 19>(inst) != 15 && return Bits<16, 19>(inst) != 15 &&
Bits<8, 11>(inst) != 15 && Bits<8, 11>(inst) != 15 &&
@ -1116,10 +1008,11 @@ TEST_CASE("Fuzz ARM sum of absolute differences", "[JitX64]") {
} }
} }
TEST_CASE( "SMUAD", "[JitX64]" ) { TEST_CASE( "SMUAD", "[JitX64][A32]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ArmTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xE700F211; // smuad r0, r1, r2 test_env.code_mem.fill({});
test_env.code_mem[0] = 0xE700F211; // smuad r0, r1, r2
jit.Regs() = { jit.Regs() = {
0, // Rd 0, // Rd
@ -1132,7 +1025,7 @@ TEST_CASE( "SMUAD", "[JitX64]" ) {
}; };
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit_num_ticks = 6; test_env.ticks_left = 6;
jit.Run(); jit.Run();
REQUIRE(jit.Regs()[0] == 0x80000000); REQUIRE(jit.Regs()[0] == 0x80000000);
@ -1141,7 +1034,7 @@ TEST_CASE( "SMUAD", "[JitX64]" ) {
REQUIRE(jit.Cpsr() == 0x080001d0); REQUIRE(jit.Cpsr() == 0x080001d0);
} }
TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") { TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp][A32]") {
const auto is_valid = [](u32 instr) -> bool { const auto is_valid = [](u32 instr) -> bool {
auto regs = (instr & 0x100) ? (Bits<0, 7>(instr) >> 1) : Bits<0, 7>(instr); auto regs = (instr & 0x100) ? (Bits<0, 7>(instr) >> 1) : Bits<0, 7>(instr);
auto base = Bits<12, 15>(instr); auto base = Bits<12, 15>(instr);
@ -1165,7 +1058,7 @@ TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") {
}); });
} }
TEST_CASE("Test ARM misc instructions", "[JitX64]") { TEST_CASE("Test ARM misc instructions", "[JitX64][A32]") {
const auto is_clz_valid = [](u32 instr) -> bool { const auto is_clz_valid = [](u32 instr) -> bool {
// R15 as Rd, or Rm is UNPREDICTABLE // R15 as Rd, or Rm is UNPREDICTABLE
return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111; return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111;
@ -1180,7 +1073,7 @@ TEST_CASE("Test ARM misc instructions", "[JitX64]") {
} }
} }
TEST_CASE("Test ARM MSR instructions", "[JitX64]") { TEST_CASE("Test ARM MSR instructions", "[JitX64][A32]") {
const auto is_msr_valid = [](u32 instr) -> bool { const auto is_msr_valid = [](u32 instr) -> bool {
return Bits<18, 19>(instr) != 0; return Bits<18, 19>(instr) != 0;
}; };
@ -1212,7 +1105,7 @@ TEST_CASE("Test ARM MSR instructions", "[JitX64]") {
} }
} }
TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64]") { TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64][A32]") {
auto is_valid = [](u32 inst) -> bool { auto is_valid = [](u32 inst) -> bool {
// R15 as Rd, Rn, or Rm is UNPREDICTABLE // R15 as Rd, Rn, or Rm is UNPREDICTABLE
return Bits<16, 19>(inst) != 0b1111 && return Bits<16, 19>(inst) != 0b1111 &&
@ -1234,7 +1127,7 @@ TEST_CASE("Fuzz ARM saturated add/sub instructions", "[JitX64]") {
} }
} }
TEST_CASE("Fuzz ARM saturation instructions", "[JitX64]") { TEST_CASE("Fuzz ARM saturation instructions", "[JitX64][A32]") {
auto is_valid = [](u32 inst) -> bool { auto is_valid = [](u32 inst) -> bool {
// R15 as Rd or Rn is UNPREDICTABLE // R15 as Rd or Rn is UNPREDICTABLE
return Bits<12, 15>(inst) != 0b1111 && return Bits<12, 15>(inst) != 0b1111 &&
@ -1253,7 +1146,7 @@ TEST_CASE("Fuzz ARM saturation instructions", "[JitX64]") {
}); });
} }
TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") { TEST_CASE("Fuzz ARM packing instructions", "[JitX64][A32]") {
auto is_pkh_valid = [](u32 inst) -> bool { auto is_pkh_valid = [](u32 inst) -> bool {
// R15 as Rd, Rn, or Rm is UNPREDICTABLE // R15 as Rd, Rn, or Rm is UNPREDICTABLE
return Bits<16, 19>(inst) != 0b1111 && return Bits<16, 19>(inst) != 0b1111 &&
@ -1273,18 +1166,19 @@ TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") {
} }
} }
TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") { TEST_CASE("arm: Test InvalidateCacheRange", "[arm][A32]") {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ArmTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xe3a00005; // mov r0, #5 test_env.code_mem.fill({});
code_mem[1] = 0xe3a0100D; // mov r1, #13 test_env.code_mem[0] = 0xe3a00005; // mov r0, #5
code_mem[2] = 0xe0812000; // add r2, r1, r0 test_env.code_mem[1] = 0xe3a0100D; // mov r1, #13
code_mem[3] = 0xeafffffe; // b +#0 (infinite loop) test_env.code_mem[2] = 0xe0812000; // add r2, r1, r0
test_env.code_mem[3] = 0xeafffffe; // b +#0 (infinite loop)
jit.Regs() = {}; jit.Regs() = {};
jit.SetCpsr(0x000001d0); // User-mode jit.SetCpsr(0x000001d0); // User-mode
jit_num_ticks = 4; test_env.ticks_left = 4;
jit.Run(); jit.Run();
REQUIRE(jit.Regs()[0] == 5); REQUIRE(jit.Regs()[0] == 5);
@ -1294,13 +1188,13 @@ TEST_CASE("arm: Test InvalidateCacheRange", "[arm]") {
REQUIRE(jit.Cpsr() == 0x000001d0); REQUIRE(jit.Cpsr() == 0x000001d0);
// Change the code // Change the code
code_mem[1] = 0xe3a01007; // mov r1, #7 test_env.code_mem[1] = 0xe3a01007; // mov r1, #7
jit.InvalidateCacheRange(/*start_memory_location = */ 4, /* length_in_bytes = */ 4); jit.InvalidateCacheRange(/*start_memory_location = */ 4, /* length_in_bytes = */ 4);
// Reset position of PC // Reset position of PC
jit.Regs()[15] = 0; jit.Regs()[15] = 0;
jit_num_ticks = 4; test_env.ticks_left = 4;
jit.Run(); jit.Run();
REQUIRE(jit.Regs()[0] == 5); REQUIRE(jit.Regs()[0] == 5);

View file

@ -26,133 +26,17 @@
#include "frontend/ir/basic_block.h" #include "frontend/ir/basic_block.h"
#include "ir_opt/passes.h" #include "ir_opt/passes.h"
#include "rand_int.h" #include "rand_int.h"
#include "testenv.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "A32/skyeye_interpreter/skyeye_common/armstate.h" #include "A32/skyeye_interpreter/skyeye_common/armstate.h"
struct WriteRecord { static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
size_t size; Dynarmic::A32::UserConfig user_config;
u32 address; user_config.callbacks = testenv;
u64 data; return user_config;
};
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);
} }
static u64 jit_num_ticks = 0; using WriteRecords = std::map<u32, u8>;
static std::array<u16, 3000> code_mem{};
static std::vector<WriteRecord> write_records;
static u64 GetTicksRemaining();
static void AddTicks(u64 ticks);
static bool IsReadOnlyMemory(u32 vaddr);
static u8 MemoryRead8(u32 vaddr);
static u16 MemoryRead16(u32 vaddr);
static u32 MemoryRead32(u32 vaddr);
static u64 MemoryRead64(u32 vaddr);
static void MemoryWrite8(u32 vaddr, u8 value);
static void MemoryWrite16(u32 vaddr, u16 value);
static void MemoryWrite32(u32 vaddr, u32 value);
static void MemoryWrite64(u32 vaddr, u64 value);
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
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) {
return vaddr < code_mem.size();
}
static u8 MemoryRead8(u32 vaddr) {
return static_cast<u8>(vaddr);
}
static u16 MemoryRead16(u32 vaddr) {
return static_cast<u16>(vaddr);
}
static u32 MemoryRead32(u32 vaddr) {
if (vaddr < code_mem.size() * sizeof(u16)) {
size_t index = vaddr / sizeof(u16);
if (index + 1 >= code_mem.size())
return code_mem[index];
return code_mem[index] | (code_mem[index+1] << 16);
}
return vaddr;
}
static u64 MemoryRead64(u32 vaddr) {
return vaddr;
}
static u32 MemoryReadCode(u32 vaddr) {
if (vaddr < code_mem.size() * sizeof(u16)) {
size_t index = vaddr / sizeof(u16);
if (index + 1 >= code_mem.size())
return code_mem[index];
return code_mem[index] | (code_mem[index + 1] << 16);
}
return 0xE7FEE7FE; // b +#0, b +#0
}
static void MemoryWrite8(u32 vaddr, u8 value){
write_records.push_back({8, vaddr, value});
}
static void MemoryWrite16(u32 vaddr, u16 value){
write_records.push_back({16, vaddr, value});
}
static void MemoryWrite32(u32 vaddr, u32 value){
write_records.push_back({32, vaddr, value});
}
static void MemoryWrite64(u32 vaddr, u64 value){
write_records.push_back({64, vaddr, value});
}
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
ARMul_State interp_state{USER32MODE};
interp_state.user_callbacks = GetUserCallbacks();
interp_state.NumInstrsToExecute = 1;
interp_state.Reg = jit->Regs();
interp_state.Cpsr = jit->Cpsr();
interp_state.Reg[15] = pc;
InterpreterClearCache();
InterpreterMainLoop(&interp_state);
bool T = Dynarmic::Common::Bit<5>(interp_state.Cpsr);
interp_state.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
jit->Regs() = interp_state.Reg;
jit->SetCpsr(interp_state.Cpsr);
}
static void Fail() {
FAIL();
}
static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
Dynarmic::A32::UserCallbacks user_callbacks{};
user_callbacks.InterpreterFallback = &InterpreterFallback;
user_callbacks.CallSVC = (void (*)(u32)) &Fail;
user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory;
user_callbacks.memory.Read8 = &MemoryRead8;
user_callbacks.memory.Read16 = &MemoryRead16;
user_callbacks.memory.Read32 = &MemoryRead32;
user_callbacks.memory.Read64 = &MemoryRead64;
user_callbacks.memory.ReadCode = &MemoryReadCode;
user_callbacks.memory.Write8 = &MemoryWrite8;
user_callbacks.memory.Write16 = &MemoryWrite16;
user_callbacks.memory.Write32 = &MemoryWrite32;
user_callbacks.memory.Write64 = &MemoryWrite64;
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
user_callbacks.AddTicks = &AddTicks;
return user_callbacks;
}
struct ThumbInstGen final { struct ThumbInstGen final {
public: public:
@ -193,7 +77,7 @@ private:
std::function<bool(u16)> is_valid; std::function<bool(u16)> is_valid;
}; };
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) { static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, WriteRecords& interp_write_records, WriteRecords& 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();
@ -202,7 +86,7 @@ static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Ji
&& interp_write_records == jit_write_records; && interp_write_records == jit_write_records;
} }
static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::Jit& jit, const std::array<u32, 16>& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) { static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State& interp, Dynarmic::A32::Jit& jit, const std::array<u32, 16>& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
interp.instruction_cache.clear(); interp.instruction_cache.clear();
InterpreterClearCache(); InterpreterClearCache();
jit.ClearCache(); jit.ClearCache();
@ -215,20 +99,20 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
jit.Regs() = initial_regs; jit.Regs() = initial_regs;
// Run interpreter // Run interpreter
write_records.clear(); test_env.modified_memory.clear();
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count); interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
InterpreterMainLoop(&interp); InterpreterMainLoop(&interp);
auto interp_write_records = write_records; auto interp_write_records = test_env.modified_memory;
{ {
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr); bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC; interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
} }
// Run jit // Run jit
write_records.clear(); test_env.modified_memory.clear();
jit_num_ticks = instructions_to_execute_count; test_env.ticks_left = instructions_to_execute_count;
jit.Run(); jit.Run();
auto jit_write_records = write_records; auto jit_write_records = test_env.modified_memory;
// Compare // Compare
if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) { if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) {
@ -236,7 +120,7 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
printf("\nInstruction Listing: \n"); printf("\nInstruction Listing: \n");
for (size_t i = 0; i < instruction_count; i++) { for (size_t i = 0; i < instruction_count; i++) {
printf("%04x %s\n", code_mem[i], Dynarmic::A32::DisassembleThumb16(code_mem[i]).c_str()); printf("%04x %s\n", test_env.code_mem[i], Dynarmic::A32::DisassembleThumb16(test_env.code_mem[i]).c_str());
} }
printf("\nInitial Register Listing: \n"); printf("\nInitial Register Listing: \n");
@ -253,12 +137,12 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
printf("\nInterp Write Records:\n"); printf("\nInterp Write Records:\n");
for (auto& record : interp_write_records) { for (auto& record : interp_write_records) {
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data); printf("[%08x] = %02x\n", record.first, record.second);
} }
printf("\nJIT Write Records:\n"); printf("\nJIT Write Records:\n");
for (auto& record : jit_write_records) { for (auto& record : jit_write_records) {
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data); printf("[%08x] = %02x\n", record.first, record.second);
} }
Dynarmic::A32::PSR cpsr; Dynarmic::A32::PSR cpsr;
@ -267,10 +151,10 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
size_t num_insts = 0; size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) { while (num_insts < instructions_to_execute_count) {
Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, Dynarmic::A32::FPSCR{}}; Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, Dynarmic::A32::FPSCR{}};
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, &MemoryReadCode); Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); });
Dynarmic::Optimization::A32GetSetElimination(ir_block); Dynarmic::Optimization::A32GetSetElimination(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, GetUserCallbacks().memory); Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, &test_env);
Dynarmic::Optimization::ConstantPropagation(ir_block); Dynarmic::Optimization::ConstantPropagation(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block); Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::VerificationPass(ir_block); Dynarmic::Optimization::VerificationPass(ir_block);
@ -287,22 +171,24 @@ static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::J
} }
void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) { void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
ThumbTestEnv test_env;
// Prepare memory // Prepare memory
code_mem.fill(0xE7FE); // b +#0 test_env.code_mem.fill(0xE7FE); // b +#0
// Prepare test subjects // Prepare test subjects
ARMul_State interp{USER32MODE}; ARMul_State interp{USER32MODE};
interp.user_callbacks = GetUserCallbacks(); interp.user_callbacks = &test_env;
Dynarmic::A32::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
for (size_t run_number = 0; run_number < run_count; run_number++) { for (size_t run_number = 0; run_number < run_count; run_number++) {
std::array<u32, 16> initial_regs; std::array<u32, 16> initial_regs;
std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); }); std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
initial_regs[15] = 0; initial_regs[15] = 0;
std::generate_n(code_mem.begin(), instruction_count, instruction_generator); std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
RunInstance(run_number, interp, jit, initial_regs, instruction_count, instructions_to_execute_count); RunInstance(run_number, test_env, interp, jit, initial_regs, instruction_count, instructions_to_execute_count);
} }
} }
@ -388,13 +274,15 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
} }
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") { TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
ThumbTestEnv test_env;
// Prepare memory // Prepare memory
code_mem.fill(0xE7FE); // b +#0 test_env.code_mem.fill(0xE7FE); // b +#0
// Prepare test subjects // Prepare test subjects
ARMul_State interp{USER32MODE}; ARMul_State interp{USER32MODE};
interp.user_callbacks = GetUserCallbacks(); interp.user_callbacks = &test_env;
Dynarmic::A32::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
std::array<u32, 16> initial_regs { std::array<u32, 16> initial_regs {
0xe90ecd70, 0xe90ecd70,
@ -415,11 +303,11 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
0x00000000, 0x00000000,
}; };
code_mem[0] = 0x40B8; // lsls r0, r7, #0 test_env.code_mem[0] = 0x40B8; // lsls r0, r7, #0
code_mem[1] = 0x01CA; // lsls r2, r1, #7 test_env.code_mem[1] = 0x01CA; // lsls r2, r1, #7
code_mem[2] = 0x83A1; // strh r1, [r4, #28] test_env.code_mem[2] = 0x83A1; // strh r1, [r4, #28]
code_mem[3] = 0x708A; // strb r2, [r1, #2] test_env.code_mem[3] = 0x708A; // strb r2, [r1, #2]
code_mem[4] = 0xBCC4; // pop {r2, r6, r7} test_env.code_mem[4] = 0xBCC4; // pop {r2, r6, r7}
RunInstance(1, interp, jit, initial_regs, 5, 5); RunInstance(1, test_env, interp, jit, initial_regs, 5, 5);
} }

View file

@ -803,7 +803,7 @@ enum {
static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) { static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) {
unsigned int inst_size = 4; unsigned int inst_size = 4;
unsigned int inst = (*cpu->user_callbacks.memory.ReadCode)(phys_addr & 0xFFFFFFFC); unsigned int inst = cpu->user_callbacks->MemoryReadCode(phys_addr & 0xFFFFFFFC);
// If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM instruction
if (cpu->TFlag) { if (cpu->TFlag) {
@ -3506,7 +3506,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) { if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
swi_inst* const inst_cream = (swi_inst*)inst_base->component; swi_inst* const inst_cream = (swi_inst*)inst_base->component;
// SVC::CallSVC(inst_cream->num & 0xFFFF); // SVC::CallSVC(inst_cream->num & 0xFFFF);
(*cpu->user_callbacks.CallSVC)(inst_cream->num & 0xFFFF); cpu->user_callbacks->CallSVC(inst_cream->num & 0xFFFF);
} }
cpu->Reg[15] += cpu->GetInstructionSize(); cpu->Reg[15] += cpu->GetInstructionSize();

View file

@ -204,14 +204,14 @@ u8 ARMul_State::ReadMemory8(u32 address) const
{ {
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); // CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
return (*user_callbacks.memory.Read8)(address); return user_callbacks->MemoryRead8(address);
} }
u16 ARMul_State::ReadMemory16(u32 address) const u16 ARMul_State::ReadMemory16(u32 address) const
{ {
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); // CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
u16 data = (*user_callbacks.memory.Read16)(address); u16 data = user_callbacks->MemoryRead16(address);
if (InBigEndianMode()) if (InBigEndianMode())
data = Common::swap16(data); data = Common::swap16(data);
@ -223,7 +223,7 @@ u32 ARMul_State::ReadMemory32(u32 address) const
{ {
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); // CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
u32 data = (*user_callbacks.memory.Read32)(address); u32 data = user_callbacks->MemoryRead32(address);
if (InBigEndianMode()) if (InBigEndianMode())
data = Common::swap32(data); data = Common::swap32(data);
@ -235,7 +235,7 @@ u64 ARMul_State::ReadMemory64(u32 address) const
{ {
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read); // CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
u64 data = (*user_callbacks.memory.Read64)(address); u64 data = user_callbacks->MemoryRead64(address);
if (InBigEndianMode()) if (InBigEndianMode())
data = Common::swap64(data); data = Common::swap64(data);
@ -247,7 +247,7 @@ void ARMul_State::WriteMemory8(u32 address, u8 data)
{ {
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write); // CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
(*user_callbacks.memory.Write8)(address, data); user_callbacks->MemoryWrite8(address, data);
} }
void ARMul_State::WriteMemory16(u32 address, u16 data) void ARMul_State::WriteMemory16(u32 address, u16 data)
@ -257,7 +257,7 @@ void ARMul_State::WriteMemory16(u32 address, u16 data)
if (InBigEndianMode()) if (InBigEndianMode())
data = Common::swap16(data); data = Common::swap16(data);
(*user_callbacks.memory.Write16)(address, data); user_callbacks->MemoryWrite16(address, data);
} }
void ARMul_State::WriteMemory32(u32 address, u32 data) void ARMul_State::WriteMemory32(u32 address, u32 data)
@ -267,7 +267,7 @@ void ARMul_State::WriteMemory32(u32 address, u32 data)
if (InBigEndianMode()) if (InBigEndianMode())
data = Common::swap32(data); data = Common::swap32(data);
(*user_callbacks.memory.Write32)(address, data); user_callbacks->MemoryWrite32(address, data);
} }
void ARMul_State::WriteMemory64(u32 address, u64 data) void ARMul_State::WriteMemory64(u32 address, u64 data)
@ -277,7 +277,7 @@ void ARMul_State::WriteMemory64(u32 address, u64 data)
if (InBigEndianMode()) if (InBigEndianMode())
data = Common::swap64(data); data = Common::swap64(data);
(*user_callbacks.memory.Write64)(address, data); user_callbacks->MemoryWrite64(address, data);
} }

View file

@ -20,7 +20,7 @@
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
#include <dynarmic/A32/callbacks.h> #include <dynarmic/A32/config.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "A32/skyeye_interpreter/skyeye_common/arm_regformat.h" #include "A32/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::A32::UserCallbacks user_callbacks; Dynarmic::A32::UserCallbacks* user_callbacks;
}; };

View file

@ -11,77 +11,27 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "A32/skyeye_interpreter/skyeye_common/armstate.h" #include "A32/skyeye_interpreter/skyeye_common/armstate.h"
#include "testenv.h"
static u64 jit_num_ticks = 0; static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
static std::array<u16, 1024> code_mem{}; Dynarmic::A32::UserConfig user_config;
user_config.callbacks = testenv;
static u64 GetTicksRemaining(); return user_config;
static void AddTicks(u64 ticks);
static u32 MemoryRead32(u32 vaddr);
static u32 MemoryReadCode(u32 vaddr);
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*);
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) {
return vaddr;
}
static u32 MemoryReadCode(u32 vaddr) {
if (vaddr < code_mem.size() * sizeof(u16)) {
size_t index = vaddr / sizeof(u16);
return code_mem[index] | (code_mem[index+1] << 16);
}
return 0xE7FEE7FE; //b +#0, b +#0
}
static void InterpreterFallback(u32 pc, Dynarmic::A32::Jit* jit, void*) {
ARMul_State interp_state{USER32MODE};
interp_state.user_callbacks = GetUserCallbacks();
interp_state.NumInstrsToExecute = 1;
interp_state.Reg = jit->Regs();
interp_state.Cpsr = jit->Cpsr();
interp_state.Reg[15] = pc;
InterpreterClearCache();
InterpreterMainLoop(&interp_state);
jit->Regs() = interp_state.Reg;
jit->SetCpsr(interp_state.Cpsr);
}
static Dynarmic::A32::UserCallbacks GetUserCallbacks() {
Dynarmic::A32::UserCallbacks user_callbacks{};
user_callbacks.memory.Read32 = &MemoryRead32;
user_callbacks.memory.ReadCode = &MemoryReadCode;
user_callbacks.InterpreterFallback = &InterpreterFallback;
user_callbacks.GetTicksRemaining = &GetTicksRemaining;
user_callbacks.AddTicks = &AddTicks;
return user_callbacks;
} }
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) { TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0x0088; // lsls r0, r1, #2 test_env.code_mem.fill({});
code_mem[1] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0x0088; // lsls r0, r1, #2
test_env.code_mem[1] = 0xE7FE; // b +#0
jit.Regs()[0] = 1; jit.Regs()[0] = 1;
jit.Regs()[1] = 2; jit.Regs()[1] = 2;
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[0] == 8 ); REQUIRE( jit.Regs()[0] == 8 );
@ -91,17 +41,18 @@ 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::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0x07C8; // lsls r0, r1, #31 test_env.code_mem.fill({});
code_mem[1] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0x07C8; // lsls r0, r1, #31
test_env.code_mem[1] = 0xE7FE; // b +#0
jit.Regs()[0] = 1; jit.Regs()[0] = 1;
jit.Regs()[1] = 0xFFFFFFFF; jit.Regs()[1] = 0xFFFFFFFF;
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[0] == 0x80000000 ); REQUIRE( jit.Regs()[0] == 0x80000000 );
@ -111,16 +62,17 @@ TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
} }
TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) { TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xBADC; // revsh r4, r3 test_env.code_mem.fill({});
code_mem[1] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0xBADC; // revsh r4, r3
test_env.code_mem[1] = 0xE7FE; // b +#0
jit.Regs()[3] = 0x12345678; jit.Regs()[3] = 0x12345678;
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[3] == 0x12345678 ); REQUIRE( jit.Regs()[3] == 0x12345678 );
@ -130,33 +82,35 @@ TEST_CASE( "thumb: revsh r4, r3", "[thumb]" ) {
} }
TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) { TEST_CASE( "thumb: ldr r3, [r3, #28]", "[thumb]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0x69DB; // ldr r3, [r3, #28] test_env.code_mem.fill({});
code_mem[1] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0x69DB; // ldr r3, [r3, #28]
test_env.code_mem[1] = 0xE7FE; // b +#0
jit.Regs()[3] = 0x12345678; jit.Regs()[3] = 0x12345678;
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[3] == 0x12345694 ); REQUIRE( jit.Regs()[3] == 0x97969594 ); // Memory location 0x12345694
REQUIRE( jit.Regs()[15] == 2 ); REQUIRE( jit.Regs()[15] == 2 );
REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode REQUIRE( jit.Cpsr() == 0x00000030 ); // Thumb, User-mode
} }
TEST_CASE( "thumb: blx +#67712", "[thumb]" ) { TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xF010; code_mem[1] = 0xEC3E; // blx +#67712 test_env.code_mem.fill({});
code_mem[2] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0xF010; test_env.code_mem[1] = 0xEC3E; // blx +#67712
test_env.code_mem[2] = 0xE7FE; // b +#0
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[14] == (0x4 | 1) ); REQUIRE( jit.Regs()[14] == (0x4 | 1) );
@ -165,15 +119,16 @@ TEST_CASE( "thumb: blx +#67712", "[thumb]" ) {
} }
TEST_CASE( "thumb: bl +#234584", "[thumb]" ) { TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xF039; code_mem[1] = 0xFA2A; // bl +#234584 test_env.code_mem.fill({});
code_mem[2] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0xF039; test_env.code_mem[1] = 0xFA2A; // bl +#234584
test_env.code_mem[2] = 0xE7FE; // b +#0
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[14] == (0x4 | 1) ); REQUIRE( jit.Regs()[14] == (0x4 | 1) );
@ -182,15 +137,16 @@ TEST_CASE( "thumb: bl +#234584", "[thumb]" ) {
} }
TEST_CASE( "thumb: bl -#42", "[thumb]" ) { TEST_CASE( "thumb: bl -#42", "[thumb]" ) {
Dynarmic::A32::Jit jit{GetUserCallbacks()}; ThumbTestEnv test_env;
code_mem.fill({}); Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
code_mem[0] = 0xF7FF; code_mem[1] = 0xFFE9; // bl -#42 test_env.code_mem.fill({});
code_mem[2] = 0xE7FE; // b +#0 test_env.code_mem[0] = 0xF7FF; test_env.code_mem[1] = 0xFFE9; // bl -#42
test_env.code_mem[2] = 0xE7FE; // b +#0
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_num_ticks = 1; test_env.ticks_left = 1;
jit.Run(); jit.Run();
REQUIRE( jit.Regs()[14] == (0x4 | 1) ); REQUIRE( jit.Regs()[14] == (0x4 | 1) );

93
tests/A32/testenv.h Normal file
View file

@ -0,0 +1,93 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 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 <array>
#include <cinttypes>
#include <map>
#include <dynarmic/A32/a32.h>
#include "common/assert.h"
#include "common/common_types.h"
template <typename InstructionType, u32 infinite_loop>
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
public:
u64 ticks_left = 0;
bool code_mem_modified_by_guest = false;
std::array<InstructionType, 2048> code_mem{};
std::map<u32, u8> modified_memory;
std::uint32_t MemoryReadCode(u32 vaddr) override {
const size_t index = vaddr / sizeof(InstructionType);
if (index < code_mem.size()) {
u32 value;
std::memcpy(&value, &code_mem[index], sizeof(u32));
return value;
}
return infinite_loop; // B .
}
std::uint8_t MemoryRead8(u32 vaddr) override {
if (vaddr < sizeof(InstructionType) * code_mem.size()) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
}
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
return iter->second;
}
return static_cast<u8>(vaddr);
}
std::uint16_t MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
void MemoryWrite8(u32 vaddr, std::uint8_t value) override {
if (vaddr < code_mem.size() * sizeof(u32)) {
code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value;
}
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
}
void InterpreterFallback(u32 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback(%08x, %zu)", pc, num_instructions); }
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC(%u)", swi); }
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised(%08x)", pc); }
void AddTicks(std::uint64_t ticks) override {
if (ticks > ticks_left) {
ticks_left = 0;
return;
}
ticks_left -= ticks;
}
std::uint64_t GetTicksRemaining() override {
return ticks_left;
}
};
using ArmTestEnv = A32TestEnv<u32, 0xEAFFFFFE>;
using ThumbTestEnv = A32TestEnv<u16, 0xE7FEE7FE>;

View file

@ -24,6 +24,7 @@ add_executable(dynarmic_tests
A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp
A32/test_arm_disassembler.cpp A32/test_arm_disassembler.cpp
A32/test_thumb_instructions.cpp A32/test_thumb_instructions.cpp
A32/testenv.h
A64/a64.cpp A64/a64.cpp
A64/inst_gen.cpp A64/inst_gen.cpp
A64/inst_gen.h A64/inst_gen.h