diff --git a/include/dynarmic/A64/a64.h b/include/dynarmic/A64/a64.h new file mode 100644 index 00000000..afe70d8c --- /dev/null +++ b/include/dynarmic/A64/a64.h @@ -0,0 +1,98 @@ +/* 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 +#include +#include + +#include + +namespace Dynarmic { +namespace A64 { + +struct Context; + +class Jit final { +public: + explicit Jit(UserConfig conf); + ~Jit(); + + /** + * Runs the emulated CPU. + * Cannot be recursively called. + */ + void Run(); + + /** + * Clears the code cache of all compiled code. + * Can be called at any time. Halts execution if called within a callback. + */ + void ClearCache(); + + /** + * Invalidate the code cache at a range of addresses. + * @param start_address The starting address of the range to invalidate. + * @param length The length (in bytes) of the range to invalidate. + */ + void InvalidateCacheRange(std::uint64_t start_address, std::size_t length); + + /** + * Reset CPU state to state at startup. Does not clear code cache. + * Cannot be called from a callback. + */ + void Reset(); + + /** + * Stops execution in Jit::Run. + * Can only be called from a callback. + */ + void HaltExecution(); + + /// Read Stack Pointer + std::uint64_t GetSP() const; + /// Modify Stack Pointer + void SetSP(std::uint64_t value); + + /// Read Program Counter + std::uint64_t GetPC() const; + /// Modify Program Counter + void SetPC(std::uint64_t value); + + /// Read general-purpose register. + std::uint64_t GetRegister(std::size_t index) const; + /// Modify general-purpose register. + void SetRegister(size_t index, std::uint64_t value); + + struct Vector { + std::uint64_t low; + std::uint64_t high; + }; + + /// Read floating point and SIMD register. + Vector GetVector(std::size_t index) const; + /// Modify floating point and SIMD register. + void SetVector(std::size_t index, Vector value); + + /// View FPCR. + std::uint32_t GetFpcr() const; + /// Modify FPCR. + void SetFpcr(std::uint32_t value); + + /** + * Returns true if Jit::Run was called but hasn't returned yet. + * i.e.: We're in a callback. + */ + bool IsExecuting() const; + +private: + struct Impl; + std::unique_ptr impl; +}; + +} // namespace A64 +} // namespace Dynarmic diff --git a/include/dynarmic/A64/config.h b/include/dynarmic/A64/config.h new file mode 100644 index 00000000..fb7728d8 --- /dev/null +++ b/include/dynarmic/A64/config.h @@ -0,0 +1,66 @@ +/* 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 +#include +#include +#include + +namespace Dynarmic { +namespace A64 { + +using VAddr = std::uint64_t; + +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. + 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; + + // 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; + + // Determines whether AddTicks and GetTicksRemaining are called. + // If false, execution will continue until soon after Jit::HaltExecution is called. + // bool enable_ticks = true; // TODO +}; + +} // namespace A64 +} // namespace Dynarmic diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb64ca41..606f3a9c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,10 +89,17 @@ if (ARCHITECTURE_x86_64) backend_x64/a32_interface.cpp backend_x64/a32_jitstate.cpp backend_x64/a32_jitstate.h + backend_x64/a64_emit_x64.cpp + backend_x64/a64_emit_x64.h + backend_x64/a64_interface.cpp + backend_x64/a64_jitstate.cpp + backend_x64/a64_jitstate.h backend_x64/abi.cpp backend_x64/abi.h backend_x64/block_of_code.cpp backend_x64/block_of_code.h + backend_x64/callback.cpp + backend_x64/callback.h backend_x64/constant_pool.cpp backend_x64/constant_pool.h backend_x64/emit_x64.cpp diff --git a/src/backend_x64/a32_emit_x64.cpp b/src/backend_x64/a32_emit_x64.cpp index d3dfd0ac..037899d0 100644 --- a/src/backend_x64/a32_emit_x64.cpp +++ b/src/backend_x64/a32_emit_x64.cpp @@ -101,9 +101,11 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { case IR::Opcode::A32##name: \ A32EmitX64::EmitA32##name(ctx, inst); \ break; +#define A64OPC(...) #include "frontend/ir/opcodes.inc" #undef OPCODE #undef A32OPC +#undef A64OPC default: ASSERT_MSG(false, "Invalid opcode %zu", static_cast(inst->GetOpcode())); diff --git a/src/backend_x64/a32_interface.cpp b/src/backend_x64/a32_interface.cpp index bf1f3e76..5e127e7d 100644 --- a/src/backend_x64/a32_interface.cpp +++ b/src/backend_x64/a32_interface.cpp @@ -17,6 +17,7 @@ #include "backend_x64/a32_emit_x64.h" #include "backend_x64/a32_jitstate.h" #include "backend_x64/block_of_code.h" +#include "backend_x64/callback.h" #include "backend_x64/jitstate_info.h" #include "common/assert.h" #include "common/common_types.h" @@ -33,12 +34,11 @@ namespace A32 { using namespace BackendX64; -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{ - LookupBlock, - arg, - cb.AddTicks, - cb.GetTicksRemaining + std::make_unique(LookupBlock, reinterpret_cast(arg)), + std::make_unique(cb.AddTicks), + std::make_unique(cb.GetTicksRemaining), }; } diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp new file mode 100644 index 00000000..37a3002f --- /dev/null +++ b/src/backend_x64/a64_emit_x64.cpp @@ -0,0 +1,211 @@ +/* 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. + */ + +#include +#include + +#include "backend_x64/a64_emit_x64.h" +#include "backend_x64/a64_jitstate.h" +#include "backend_x64/abi.h" +#include "backend_x64/block_of_code.h" +#include "backend_x64/emit_x64.h" +#include "common/address_range.h" +#include "common/assert.h" +#include "common/bit_util.h" +#include "common/common_types.h" +#include "common/variant_util.h" +#include "frontend/A64/location_descriptor.h" +#include "frontend/A64/types.h" +#include "frontend/ir/basic_block.h" +#include "frontend/ir/microinstruction.h" +#include "frontend/ir/opcodes.h" + +// TODO: Have ARM flags in host flags and not have them use up GPR registers unless necessary. +// TODO: Actually implement that proper instruction selector you've always wanted to sweetheart. + +namespace Dynarmic { +namespace BackendX64 { + +using namespace Xbyak::util; + +A64EmitContext::A64EmitContext(RegAlloc& reg_alloc, IR::Block& block) + : EmitContext(reg_alloc, block) {} + +A64::LocationDescriptor A64EmitContext::Location() const { + return A64::LocationDescriptor{block.Location()}; +} + +bool A64EmitContext::FPSCR_RoundTowardsZero() const { + return Location().FPCR().RMode() != A64::FPCR::RoundingMode::TowardsZero; +} + +bool A64EmitContext::FPSCR_FTZ() const { + return Location().FPCR().FZ(); +} + +bool A64EmitContext::FPSCR_DN() const { + return Location().FPCR().DN(); +} + +A64EmitX64::A64EmitX64(BlockOfCode* code, A64::UserConfig conf) + : EmitX64(code), conf(conf) +{ + code->PreludeComplete(); +} + +A64EmitX64::~A64EmitX64() {} + +A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { + code->align(); + const u8* const entrypoint = code->getCurr(); + + // Start emitting. + EmitCondPrelude(block); + + RegAlloc reg_alloc{code, A64JitState::SpillCount, SpillToOpArg}; + A64EmitContext ctx{reg_alloc, block}; + + for (auto iter = block.begin(); iter != block.end(); ++iter) { + IR::Inst* inst = &*iter; + + // Call the relevant Emit* member function. + switch (inst->GetOpcode()) { + +#define OPCODE(name, type, ...) \ + case IR::Opcode::name: \ + A64EmitX64::Emit##name(ctx, inst); \ + break; +#define A32OPC(...) +#define A64OPC(name, type, ...) \ + case IR::Opcode::A64##name: \ + A64EmitX64::EmitA64##name(ctx, inst); \ + break; +#include "frontend/ir/opcodes.inc" +#undef OPCODE +#undef A32OPC +#undef A64OPC + + default: + ASSERT_MSG(false, "Invalid opcode %zu", static_cast(inst->GetOpcode())); + break; + } + + reg_alloc.EndOfAllocScope(); + } + + reg_alloc.AssertNoMoreUses(); + + EmitAddCycles(block.CycleCount()); + EmitX64::EmitTerminal(block.GetTerminal(), block.Location()); + code->int3(); + + const A64::LocationDescriptor descriptor{block.Location()}; + Patch(descriptor, entrypoint); + + const size_t size = static_cast(code->getCurr() - entrypoint); + const A64::LocationDescriptor end_location{block.EndLocation()}; + const auto range = boost::icl::discrete_interval::closed(descriptor.PC(), end_location.PC() - 1); + A64EmitX64::BlockDescriptor block_desc{entrypoint, size, block.Location(), range}; + block_descriptors.emplace(descriptor.UniqueHash(), block_desc); + block_ranges.add(std::make_pair(range, std::set{descriptor})); + + return block_desc; +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) { + code->mov(code->ABI_PARAM1.cvt32(), A64::LocationDescriptor{terminal.next}.PC()); + //code->mov(code->ABI_PARAM2, reinterpret_cast(jit_interface)); + //code->mov(code->ABI_PARAM3, reinterpret_cast(cb.user_arg)); + //code->mov(MJitStateReg(A64::Reg::PC), code->ABI_PARAM1.cvt32()); + code->SwitchMxcsrOnExit(); + //code->CallFunction(cb.InterpreterFallback); + code->ReturnFromRunCode(true); // TODO: Check cycles +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::ReturnToDispatch, IR::LocationDescriptor) { + code->ReturnFromRunCode(); +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor) { + code->cmp(qword[r15 + offsetof(A64JitState, cycles_remaining)], 0); + + patch_information[terminal.next].jg.emplace_back(code->getCurr()); + if (auto next_bb = GetBasicBlock(terminal.next)) { + EmitPatchJg(terminal.next, next_bb->entrypoint); + } else { + EmitPatchJg(terminal.next); + } + Xbyak::Label dest; + code->jmp(dest, Xbyak::CodeGenerator::T_NEAR); + + code->SwitchToFarCode(); + code->align(16); + code->L(dest); + //code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{terminal.next}.PC()); + PushRSBHelper(rax, rbx, terminal.next); + code->ForceReturnFromRunCode(); + code->SwitchToNearCode(); +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor) { + patch_information[terminal.next].jmp.emplace_back(code->getCurr()); + if (auto next_bb = GetBasicBlock(terminal.next)) { + EmitPatchJmp(terminal.next, next_bb->entrypoint); + } else { + EmitPatchJmp(terminal.next); + } +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::PopRSBHint, IR::LocationDescriptor) { + ASSERT(false); +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) { + Xbyak::Label pass = EmitCond(terminal.if_); + EmitTerminal(terminal.else_, initial_location); + code->L(pass); + EmitTerminal(terminal.then_, initial_location); +} + +void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) { + code->cmp(code->byte[r15 + offsetof(A64JitState, halt_requested)], u8(0)); + code->jne(code->GetForceReturnFromRunCodeAddress()); + EmitTerminal(terminal.else_, initial_location); +} + +void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& /*target_desc*/, CodePtr target_code_ptr) { + const CodePtr patch_location = code->getCurr(); + if (target_code_ptr) { + code->jg(target_code_ptr); + } else { + //code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{target_desc}.PC()); + code->jg(code->GetReturnFromRunCodeAddress()); + } + code->EnsurePatchLocationSize(patch_location, 14); +} + +void A64EmitX64::EmitPatchJmp(const IR::LocationDescriptor& /*target_desc*/, CodePtr target_code_ptr) { + const CodePtr patch_location = code->getCurr(); + if (target_code_ptr) { + code->jmp(target_code_ptr); + } else { + //code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{target_desc}.PC()); + code->jmp(code->GetReturnFromRunCodeAddress()); + } + code->EnsurePatchLocationSize(patch_location, 13); +} + +void A64EmitX64::EmitPatchMovRcx(CodePtr target_code_ptr) { + if (!target_code_ptr) { + target_code_ptr = code->GetReturnFromRunCodeAddress(); + } + const CodePtr patch_location = code->getCurr(); + code->mov(code->rcx, reinterpret_cast(target_code_ptr)); + code->EnsurePatchLocationSize(patch_location, 10); +} + +} // namespace BackendX64 +} // namespace Dynarmic diff --git a/src/backend_x64/a64_emit_x64.h b/src/backend_x64/a64_emit_x64.h new file mode 100644 index 00000000..adaa2f79 --- /dev/null +++ b/src/backend_x64/a64_emit_x64.h @@ -0,0 +1,78 @@ +/* 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 +#include + +#include +#include +#include + +#include + +#include "backend_x64/a64_jitstate.h" +#include "backend_x64/emit_x64.h" +#include "common/address_range.h" +#include "dynarmic/A64/a64.h" +#include "dynarmic/A64/config.h" +#include "frontend/A64/location_descriptor.h" +#include "frontend/ir/terminal.h" + +namespace Dynarmic { +namespace BackendX64 { + +class RegAlloc; + +struct A64EmitContext final : public EmitContext { + A64EmitContext(RegAlloc& reg_alloc, IR::Block& block); + A64::LocationDescriptor Location() const; + bool FPSCR_RoundTowardsZero() const override; + bool FPSCR_FTZ() const override; + bool FPSCR_DN() const override; +}; + +class A64EmitX64 final : public EmitX64 { +public: + A64EmitX64(BlockOfCode* code, A64::UserConfig conf); + ~A64EmitX64(); + + /** + * Emit host machine code for a basic block with intermediate representation `ir`. + * @note ir is modified. + */ + BlockDescriptor Emit(IR::Block& ir); + +protected: + const A64::UserConfig conf; + + // Microinstruction emitters +#define OPCODE(...) +#define A32OPC(...) +#define A64OPC(name, type, ...) void EmitA64##name(A64EmitContext& ctx, IR::Inst* inst); +#include "frontend/ir/opcodes.inc" +#undef OPCODE +#undef A32OPC +#undef A64OPC + + // Terminal instruction emitters + void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override; + + // Patching + void EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr = nullptr) override; + void EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr = nullptr) override; + void EmitPatchMovRcx(CodePtr target_code_ptr = nullptr) override; +}; + +} // namespace BackendX64 +} // namespace Dynarmic diff --git a/src/backend_x64/a64_interface.cpp b/src/backend_x64/a64_interface.cpp new file mode 100644 index 00000000..a70bb398 --- /dev/null +++ b/src/backend_x64/a64_interface.cpp @@ -0,0 +1,263 @@ +/* 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. + */ + +#include + +#include + +#include "backend_x64/a64_emit_x64.h" +#include "backend_x64/a64_jitstate.h" +#include "backend_x64/block_of_code.h" +#include "backend_x64/jitstate_info.h" +#include "common/assert.h" +#include "common/scope_exit.h" +#include "dynarmic/A64/a64.h" +#include "frontend/A64/translate/translate.h" +#include "frontend/ir/basic_block.h" +#include "ir_opt/passes.h" + +namespace Dynarmic { +namespace A64 { + +using namespace BackendX64; + +template +static Ret Thunk(A64::UserCallbacks* cb, Args... args) { + return (cb->*fn)(std::forward(args)...); +} + +static RunCodeCallbacks GenRunCodeCallbacks(A64::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg) { + return RunCodeCallbacks{ + std::make_unique(LookupBlock, reinterpret_cast(arg)), + std::make_unique(&Thunk<&A64::UserCallbacks::AddTicks, void, u64>, reinterpret_cast(cb)), + std::make_unique(&Thunk<&A64::UserCallbacks::GetTicksRemaining, u64>, reinterpret_cast(cb)), + }; +} + +struct Jit::Impl final { +public: + explicit Impl(UserConfig conf) + : conf(conf) + , block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this), JitStateInfo{jit_state}) + , emitter(&block_of_code, conf) + {} + + ~Impl() = default; + + void Run() { + ASSERT(!is_executing); + is_executing = true; + SCOPE_EXIT({ this->is_executing = false; }); + jit_state.halt_requested = false; + + block_of_code.RunCode(&jit_state); + + PerformRequestedCacheInvalidation(); + } + + void ClearCache() { + invalidate_entire_cache = true; + RequestCacheInvalidation(); + } + + void InvalidateCacheRange(u64 start_address, size_t length) { + const auto end_address = static_cast(start_address + length - 1); + const auto range = boost::icl::discrete_interval::closed(start_address, end_address); + invalid_cache_ranges.add(range); + RequestCacheInvalidation(); + } + + void Reset() { + ASSERT(!is_executing); + jit_state = {}; + } + + void HaltExecution() { + jit_state.halt_requested = true; + } + + u64 GetSP() const { + return jit_state.sp; + } + + void SetSP(u64 value) { + jit_state.sp = value; + } + + u64 GetPC() const { + return jit_state.pc; + } + + void SetPC(u64 value) { + jit_state.pc = value; + } + + u64 GetRegister(size_t index) const { + if (index == 31) + return GetSP(); + return jit_state.reg.at(index); + } + + void SetRegister(size_t index, u64 value) { + if (index == 31) + return SetSP(value); + jit_state.reg.at(index) = value; + } + + Vector GetVector(size_t index) const { + return {jit_state.vec.at(index * 2), jit_state.vec.at(index * 2 + 1)}; + } + + void SetVector(size_t index, Vector value) { + jit_state.vec.at(index * 2) = value.low; + jit_state.vec.at(index * 2 + 1) = value.high; + } + + u32 GetFpcr() const { + return jit_state.GetFpcr(); + } + + void SetFpcr(u32 value) { + jit_state.SetFpcr(value); + } + + bool IsExecuting() const { + return is_executing; + } + +private: + static CodePtr GetCurrentBlockThunk(void* thisptr) { + Jit::Impl* this_ = reinterpret_cast(thisptr); + return this_->GetCurrentBlock(); + } + + CodePtr GetCurrentBlock() { + IR::LocationDescriptor current_location{jit_state.GetUniqueHash()}; + + if (auto block = emitter.GetBasicBlock(current_location)) + return block->entrypoint; + + constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024; + if (block_of_code.SpaceRemaining() < MINIMUM_REMAINING_CODESIZE) { + // Immediately evacuate cache + invalidate_entire_cache = true; + PerformRequestedCacheInvalidation(); + } + + // JIT Compile + IR::Block ir_block = A64::Translate(A64::LocationDescriptor{current_location}, [this](u64 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }); + Optimization::DeadCodeElimination(ir_block); + Optimization::VerificationPass(ir_block); + return emitter.Emit(ir_block).entrypoint; + } + + void RequestCacheInvalidation() { + if (is_executing) { + jit_state.halt_requested = true; + return; + } + + PerformRequestedCacheInvalidation(); + } + + void PerformRequestedCacheInvalidation() { + if (!invalidate_entire_cache && invalid_cache_ranges.empty()) { + return; + } + + jit_state.ResetRSB(); + if (invalidate_entire_cache) { + block_of_code.ClearCache(); + emitter.ClearCache(); + } else { + emitter.InvalidateCacheRanges(invalid_cache_ranges); + } + invalid_cache_ranges.clear(); + invalidate_entire_cache = false; + } + + bool is_executing = false; + + UserConfig conf; + A64JitState jit_state; + BlockOfCode block_of_code; + A64EmitX64 emitter; + + bool invalidate_entire_cache = false; + boost::icl::interval_set invalid_cache_ranges; +}; + +Jit::Jit(UserConfig conf) + : impl(std::make_unique(conf)) {} + +Jit::~Jit() = default; + +void Jit::Run() { + impl->Run(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(u64 start_address, size_t length) { + impl->InvalidateCacheRange(start_address, length); +} + +void Jit::Reset() { + impl->Reset(); +} + +void Jit::HaltExecution() { + impl->HaltExecution(); +} + +u64 Jit::GetSP() const { + return impl->GetSP(); +} + +void Jit::SetSP(u64 value) { + impl->SetSP(value); +} + +u64 Jit::GetPC() const { + return impl->GetPC(); +} + +void Jit::SetPC(u64 value) { + impl->SetPC(value); +} + +u64 Jit::GetRegister(size_t index) const { + return impl->GetRegister(index); +} + +void Jit::SetRegister(size_t index, u64 value) { + impl->SetRegister(index, value); +} + +Jit::Vector Jit::GetVector(size_t index) const { + return impl->GetVector(index); +} + +void Jit::SetVector(size_t index, Vector value) { + impl->SetVector(index, value); +} + +u32 Jit::GetFpcr() const { + return impl->GetFpcr(); +} + +void Jit::SetFpcr(u32 value) { + impl->SetFpcr(value); +} + +bool Jit::IsExecuting() const { + return impl->IsExecuting(); +} + +} // namespace A64 +} // namespace Dynarmic diff --git a/src/backend_x64/a64_jitstate.cpp b/src/backend_x64/a64_jitstate.cpp new file mode 100644 index 00000000..bf87840f --- /dev/null +++ b/src/backend_x64/a64_jitstate.cpp @@ -0,0 +1,20 @@ +/* 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. + */ + +#include "backend_x64/a64_jitstate.h" +#include "frontend/A64/location_descriptor.h" + +namespace Dynarmic { +namespace BackendX64 { + +u64 A64JitState::GetUniqueHash() const { + u64 fpcr_u64 = static_cast(fpcr & A64::LocationDescriptor::FPCR_MASK) << 37; + u64 pc_u64 = pc & A64::LocationDescriptor::PC_MASK; + return pc_u64 | fpcr_u64; +} + +} // namespace BackendX64 +} // namespace Dynarmic diff --git a/src/backend_x64/a64_jitstate.h b/src/backend_x64/a64_jitstate.h new file mode 100644 index 00000000..fe830b23 --- /dev/null +++ b/src/backend_x64/a64_jitstate.h @@ -0,0 +1,79 @@ +/* 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 + +#include + +#include "common/common_types.h" + +namespace Dynarmic { +namespace BackendX64 { + +class BlockOfCode; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4324) // Structure was padded due to alignment specifier +#endif + +struct A64JitState { + using ProgramCounterType = u64; + + A64JitState() { ResetRSB(); } + + std::array reg{}; + u64 sp; + u64 pc; + + u32 CPSR_nzcv = 0; + u32 FPSCR_nzcv = 0; + + alignas(16) std::array vec{}; // Extension registers. + + static constexpr size_t SpillCount = 64; + std::array spill{}; // Spill. + static Xbyak::Address GetSpillLocationFromIndex(size_t i) { + using namespace Xbyak::util; + return qword[r15 + offsetof(A64JitState, spill) + i * sizeof(u64)]; + } + + // For internal use (See: BlockOfCode::RunCode) + u32 guest_MXCSR = 0x00001f80; + u32 save_host_MXCSR = 0; + s64 cycles_to_run = 0; + s64 cycles_remaining = 0; + bool halt_requested = false; + + static constexpr size_t RSBSize = 8; // MUST be a power of 2. + static constexpr size_t RSBPtrMask = RSBSize - 1; + u32 rsb_ptr = 0; + std::array rsb_location_descriptors; + std::array rsb_codeptrs; + void ResetRSB() { + rsb_location_descriptors.fill(0xFFFFFFFFFFFFFFFFull); + rsb_codeptrs.fill(0); + } + + u32 FPSCR_IDC = 0; + u32 FPSCR_UFC = 0; + u32 fpcr = 0; + u32 GetFpcr() const { return fpcr; } + void SetFpcr(u32 new_fpcr) { fpcr = new_fpcr; } + + u64 GetUniqueHash() const; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +using CodePtr = const void*; + +} // namespace BackendX64 +} // namespace Dynarmic diff --git a/src/backend_x64/block_of_code.cpp b/src/backend_x64/block_of_code.cpp index 9ef19adf..2c130b14 100644 --- a/src/backend_x64/block_of_code.cpp +++ b/src/backend_x64/block_of_code.cpp @@ -36,7 +36,7 @@ constexpr size_t FAR_CODE_OFFSET = 100 * 1024 * 1024; BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi) : Xbyak::CodeGenerator(TOTAL_CODE_SIZE) - , cb(cb) + , cb(std::move(cb)) , jsi(jsi) , constant_pool(this, 256) { @@ -112,7 +112,7 @@ void BlockOfCode::GenRunCode() { mov(r15, ABI_PARAM1); mov(r14, ABI_PARAM2); // save temporarily in non-volatile register - CallFunction(cb.GetTicksRemaining); + cb.GetTicksRemaining->EmitCall(this); mov(qword[r15 + jsi.offsetof_cycles_to_run], ABI_RETURN); mov(qword[r15 + jsi.offsetof_cycles_remaining], ABI_RETURN); @@ -130,15 +130,14 @@ void BlockOfCode::GenRunCode() { mov(r15, ABI_PARAM1); - CallFunction(cb.GetTicksRemaining); + cb.GetTicksRemaining->EmitCall(this); mov(qword[r15 + jsi.offsetof_cycles_to_run], ABI_RETURN); mov(qword[r15 + jsi.offsetof_cycles_remaining], ABI_RETURN); L(enter_mxcsr_then_loop); SwitchMxcsrOnEntry(); L(loop); - mov(ABI_PARAM1, u64(cb.lookup_block_arg)); - CallFunction(cb.LookupBlock); + cb.LookupBlock->EmitCall(this); jmp(ABI_RETURN); @@ -153,9 +152,10 @@ void BlockOfCode::GenRunCode() { SwitchMxcsrOnExit(); } - mov(ABI_PARAM1, qword[r15 + jsi.offsetof_cycles_to_run]); - sub(ABI_PARAM1, qword[r15 + jsi.offsetof_cycles_remaining]); - CallFunction(cb.AddTicks); + cb.AddTicks->EmitCall(this, [this](Xbyak::Reg64 param1) { + mov(param1, qword[r15 + jsi.offsetof_cycles_to_run]); + sub(param1, qword[r15 + jsi.offsetof_cycles_remaining]); + }); ABI_PopCalleeSaveRegistersAndAdjustStack(this); ret(); diff --git a/src/backend_x64/block_of_code.h b/src/backend_x64/block_of_code.h index 7793bde1..a7853ad7 100644 --- a/src/backend_x64/block_of_code.h +++ b/src/backend_x64/block_of_code.h @@ -12,6 +12,7 @@ #include #include +#include "backend_x64/callback.h" #include "backend_x64/constant_pool.h" #include "backend_x64/jitstate_info.h" #include "common/common_types.h" @@ -23,11 +24,9 @@ namespace BackendX64 { using CodePtr = const void*; struct RunCodeCallbacks { - CodePtr (*LookupBlock)(void* lookup_block_arg); - void* lookup_block_arg; - - void (*AddTicks)(std::uint64_t ticks); - std::uint64_t (*GetTicksRemaining)(); + std::unique_ptr LookupBlock; + std::unique_ptr AddTicks; + std::unique_ptr GetTicksRemaining; }; class BlockOfCode final : public Xbyak::CodeGenerator { diff --git a/src/backend_x64/callback.cpp b/src/backend_x64/callback.cpp new file mode 100644 index 00000000..cb6a2979 --- /dev/null +++ b/src/backend_x64/callback.cpp @@ -0,0 +1,58 @@ +/* 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. + */ + +#include "backend_x64/block_of_code.h" +#include "backend_x64/callback.h" + +namespace Dynarmic { +namespace BackendX64 { + +void SimpleCallback::EmitCall(BlockOfCode* code, std::function l) { + l(); + code->CallFunction(fn); +} + +void SimpleCallback::EmitCall(BlockOfCode* code, std::function l) { + l(code->ABI_PARAM1); + code->CallFunction(fn); +} + +void SimpleCallback::EmitCall(BlockOfCode* code, std::function l) { + l(code->ABI_PARAM1, code->ABI_PARAM2); + code->CallFunction(fn); +} + +void SimpleCallback::EmitCall(BlockOfCode* code, std::function l) { + l(code->ABI_PARAM1, code->ABI_PARAM2, code->ABI_PARAM3); + code->CallFunction(fn); +} + +void ArgCallback::EmitCall(BlockOfCode* code, std::function l) { + code->mov(code->ABI_PARAM1, arg); + l(); + code->CallFunction(fn); +} + +void ArgCallback::EmitCall(BlockOfCode* code, std::function l) { + code->mov(code->ABI_PARAM1, arg); + l(code->ABI_PARAM2); + code->CallFunction(fn); +} + +void ArgCallback::EmitCall(BlockOfCode* code, std::function l) { + code->mov(code->ABI_PARAM1, arg); + l(code->ABI_PARAM2, code->ABI_PARAM3); + code->CallFunction(fn); +} + +void ArgCallback::EmitCall(BlockOfCode* code, std::function l) { + code->mov(code->ABI_PARAM1, arg); + l(code->ABI_PARAM2, code->ABI_PARAM3, code->ABI_PARAM4); + code->CallFunction(fn); +} + +} // namespace BackendX64 +} // namespace Dynarmic diff --git a/src/backend_x64/callback.h b/src/backend_x64/callback.h new file mode 100644 index 00000000..1014586b --- /dev/null +++ b/src/backend_x64/callback.h @@ -0,0 +1,64 @@ +/* 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 + +#include + +#include "common/common_types.h" + +namespace Dynarmic { +namespace BackendX64 { + +class BlockOfCode; + +class Callback { +public: + virtual ~Callback() = default; + + virtual void EmitCall(BlockOfCode* code, std::function fn = []{}) = 0; + virtual void EmitCall(BlockOfCode* code, std::function fn) = 0; + virtual void EmitCall(BlockOfCode* code, std::function fn) = 0; + virtual void EmitCall(BlockOfCode* code, std::function fn) = 0; +}; + +class SimpleCallback final : public Callback { +public: + template + SimpleCallback(Function fn) : fn(reinterpret_cast(fn)) {} + + ~SimpleCallback() = default; + + void EmitCall(BlockOfCode* code, std::function l = []{}) override; + void EmitCall(BlockOfCode* code, std::function l) override; + void EmitCall(BlockOfCode* code, std::function l) override; + void EmitCall(BlockOfCode* code, std::function l) override; + +private: + void (*fn)(); +}; + +class ArgCallback final : public Callback { +public: + template + ArgCallback(Function fn, u64 arg) : fn(reinterpret_cast(fn)), arg(arg) {} + + ~ArgCallback() = default; + + void EmitCall(BlockOfCode* code, std::function l = []{}) override; + void EmitCall(BlockOfCode* code, std::function l) override; + void EmitCall(BlockOfCode* code, std::function l) override; + void EmitCall(BlockOfCode* code, std::function l) override; + +private: + void (*fn)(); + u64 arg; +}; + +} // namespace BackendX64 +} // namespace Dynarmic diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index a374be9f..6c68579f 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -2668,5 +2668,7 @@ void EmitX64::InvalidateCacheRanges(const boost::icl::interval_set; +template class Dynarmic::BackendX64::EmitX64; diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index 732ff30f..541d3e15 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -72,9 +72,11 @@ protected: // Microinstruction emitters #define OPCODE(name, type, ...) void Emit##name(EmitContext& ctx, IR::Inst* inst); #define A32OPC(...) +#define A64OPC(...) #include "frontend/ir/opcodes.inc" #undef OPCODE #undef A32OPC +#undef A64OPC // Helpers void EmitAddCycles(size_t cycles); diff --git a/src/frontend/A64/ir_emitter.cpp b/src/frontend/A64/ir_emitter.cpp new file mode 100644 index 00000000..48868e25 --- /dev/null +++ b/src/frontend/A64/ir_emitter.cpp @@ -0,0 +1,42 @@ +/* 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. + */ + +#include "common/assert.h" +#include "frontend/A64/ir_emitter.h" +#include "frontend/ir/opcodes.h" + +namespace Dynarmic { +namespace A64 { + +using Opcode = IR::Opcode; + +u64 IREmitter::PC() { + return current_location.PC(); +} + +u64 IREmitter::AlignPC(size_t alignment) { + u64 pc = PC(); + return static_cast(pc - pc % alignment); +} + +IR::U32 IREmitter::GetW(Reg reg) { + return Inst(Opcode::A64GetW, IR::Value(reg)); +} + +IR::U64 IREmitter::GetX(Reg reg) { + return Inst(Opcode::A64GetX, IR::Value(reg)); +} + +void IREmitter::SetW(const Reg reg, const IR::U32& value) { + Inst(Opcode::A64SetW, IR::Value(reg), value); +} + +void IREmitter::SetX(const Reg reg, const IR::U64& value) { + Inst(Opcode::A64SetX, IR::Value(reg), value); +} + +} // namespace IR +} // namespace Dynarmic diff --git a/src/frontend/A64/ir_emitter.h b/src/frontend/A64/ir_emitter.h new file mode 100644 index 00000000..9ce8fb8e --- /dev/null +++ b/src/frontend/A64/ir_emitter.h @@ -0,0 +1,41 @@ +/* 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 + +#include "common/common_types.h" +#include "frontend/A64/location_descriptor.h" +#include "frontend/A64/types.h" +#include "frontend/ir/ir_emitter.h" +#include "frontend/ir/value.h" + +namespace Dynarmic { +namespace A64 { + +/** + * Convenience class to construct a basic block of the intermediate representation. + * `block` is the resulting block. + * The user of this class updates `current_location` as appropriate. + */ +class IREmitter : public IR::IREmitter { +public: + explicit IREmitter(LocationDescriptor descriptor) : IR::IREmitter(descriptor), current_location(descriptor) {} + + LocationDescriptor current_location; + + u64 PC(); + u64 AlignPC(size_t alignment); + + IR::U32 GetW(Reg source_reg); + IR::U64 GetX(Reg source_reg); + void SetW(Reg dest_reg, const IR::U32& value); + void SetX(Reg dest_reg, const IR::U64& value); +}; + +} // namespace IR +} // namespace Dynarmic diff --git a/src/frontend/A64/translate/translate.h b/src/frontend/A64/translate/translate.h index 9e8fc710..ba16e7ff 100644 --- a/src/frontend/A64/translate/translate.h +++ b/src/frontend/A64/translate/translate.h @@ -5,6 +5,8 @@ */ #pragma once +#include + #include "common/common_types.h" namespace Dynarmic { @@ -17,7 +19,7 @@ namespace A64 { class LocationDescriptor; -using MemoryReadCodeFuncType = u32 (*)(u64 vaddr); +using MemoryReadCodeFuncType = std::function; /** * This function translates instructions in memory into our intermediate representation. diff --git a/tests/arm/fuzz_arm.cpp b/tests/A32/fuzz_arm.cpp similarity index 99% rename from tests/arm/fuzz_arm.cpp rename to tests/A32/fuzz_arm.cpp index 6dd9201d..d1fd1eba 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/A32/fuzz_arm.cpp @@ -28,8 +28,8 @@ #include "frontend/ir/location_descriptor.h" #include "ir_opt/passes.h" #include "rand_int.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" #ifdef __unix__ #include diff --git a/tests/arm/fuzz_thumb.cpp b/tests/A32/fuzz_thumb.cpp similarity index 99% rename from tests/arm/fuzz_thumb.cpp rename to tests/A32/fuzz_thumb.cpp index a2478fc4..46c7eb70 100644 --- a/tests/arm/fuzz_thumb.cpp +++ b/tests/A32/fuzz_thumb.cpp @@ -26,8 +26,8 @@ #include "frontend/ir/basic_block.h" #include "ir_opt/passes.h" #include "rand_int.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" struct WriteRecord { size_t size; diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp similarity index 99% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp index f69c9d43..f563e1fe 100644 --- a/tests/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp +++ b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp @@ -2,8 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "skyeye_interpreter/dyncom/arm_dyncom_dec.h" -#include "skyeye_interpreter/skyeye_common/armsupp.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h" +#include "A32/skyeye_interpreter/skyeye_common/armsupp.h" const InstructionSetEncodingItem arm_instruction[] = { { "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }}, diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_dec.h b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h similarity index 100% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_dec.h rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp similarity index 99% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp index 7b2b0662..69757ed4 100644 --- a/tests/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp +++ b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp @@ -18,15 +18,15 @@ //#include "core/memory.h" //#include "core/hle/svc.h" -//#include "skyeye_interpreter/disassembler/arm_disasm.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_dec.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_thumb.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_trans.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_run.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" -#include "skyeye_interpreter/skyeye_common/armsupp.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp.h" +//#include "A32/skyeye_interpreter/disassembler/arm_disasm.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_run.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/armsupp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h" //#include "core/gdbstub/gdbstub.h" @@ -4233,7 +4233,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { } #define VFP_INTERPRETER_IMPL - #include "skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp" + #include "A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp" #undef VFP_INTERPRETER_IMPL END: diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h similarity index 100% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_run.h b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_run.h similarity index 96% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_run.h rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_run.h index 9c8b11bd..165f6265 100644 --- a/tests/skyeye_interpreter/dyncom/arm_dyncom_run.h +++ b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_run.h @@ -18,7 +18,7 @@ #pragma once -#include "skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" /** * Checks if the PC is being read, and if so, word-aligns it. diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp similarity index 99% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp index 001390bf..695276b6 100644 --- a/tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp +++ b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp @@ -7,8 +7,8 @@ // We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding // ARM instruction, and using the existing ARM simulator. -#include "skyeye_interpreter/dyncom/arm_dyncom_thumb.h" -#include "skyeye_interpreter/skyeye_common/armsupp.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h" +#include "A32/skyeye_interpreter/skyeye_common/armsupp.h" // Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field, // with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.h b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h similarity index 100% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.h rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp similarity index 99% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp index feaf8b3b..e6e346f7 100644 --- a/tests/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp +++ b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp @@ -3,11 +3,11 @@ #include "common/assert.h" #include "common/common_types.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_trans.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" -#include "skyeye_interpreter/skyeye_common/armsupp.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/armsupp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h" #define LOG_INFO(...) do{}while(0) #define LOG_TRACE(...) do{}while(0) @@ -1971,7 +1971,7 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(yield)(unsigned int inst, int index) // Floating point VFPv3 instructions #define VFP_INTERPRETER_TRANS -#include "skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp" #undef VFP_INTERPRETER_TRANS const transop_fp_t arm_instruction_trans[] = { diff --git a/tests/skyeye_interpreter/dyncom/arm_dyncom_trans.h b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h similarity index 99% rename from tests/skyeye_interpreter/dyncom/arm_dyncom_trans.h rename to tests/A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h index a9f17b91..ce95d90b 100644 --- a/tests/skyeye_interpreter/dyncom/arm_dyncom_trans.h +++ b/tests/A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h @@ -478,7 +478,7 @@ struct pkh_inst { // Floating point VFPv3 structures #define VFP_INTERPRETER_STRUCT -#include "skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp" #undef VFP_INTERPRETER_STRUCT typedef void (*get_addr_fp_t)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr); diff --git a/tests/skyeye_interpreter/skyeye_common/arm_regformat.h b/tests/A32/skyeye_interpreter/skyeye_common/arm_regformat.h similarity index 100% rename from tests/skyeye_interpreter/skyeye_common/arm_regformat.h rename to tests/A32/skyeye_interpreter/skyeye_common/arm_regformat.h diff --git a/tests/skyeye_interpreter/skyeye_common/armstate.cpp b/tests/A32/skyeye_interpreter/skyeye_common/armstate.cpp similarity index 99% rename from tests/skyeye_interpreter/skyeye_common/armstate.cpp rename to tests/A32/skyeye_interpreter/skyeye_common/armstate.cpp index d57d1b9e..0365ff81 100644 --- a/tests/skyeye_interpreter/skyeye_common/armstate.cpp +++ b/tests/A32/skyeye_interpreter/skyeye_common/armstate.cpp @@ -8,8 +8,8 @@ #include #include "common/assert.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h" namespace Common { inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);} diff --git a/tests/skyeye_interpreter/skyeye_common/armstate.h b/tests/A32/skyeye_interpreter/skyeye_common/armstate.h similarity index 99% rename from tests/skyeye_interpreter/skyeye_common/armstate.h rename to tests/A32/skyeye_interpreter/skyeye_common/armstate.h index 46d56754..ef5d0c5e 100644 --- a/tests/skyeye_interpreter/skyeye_common/armstate.h +++ b/tests/A32/skyeye_interpreter/skyeye_common/armstate.h @@ -23,7 +23,7 @@ #include #include "common/common_types.h" -#include "skyeye_interpreter/skyeye_common/arm_regformat.h" +#include "A32/skyeye_interpreter/skyeye_common/arm_regformat.h" // Signal levels enum { diff --git a/tests/skyeye_interpreter/skyeye_common/armsupp.cpp b/tests/A32/skyeye_interpreter/skyeye_common/armsupp.cpp similarity index 96% rename from tests/skyeye_interpreter/skyeye_common/armsupp.cpp rename to tests/A32/skyeye_interpreter/skyeye_common/armsupp.cpp index 2ceaf53b..d268a89a 100644 --- a/tests/skyeye_interpreter/skyeye_common/armsupp.cpp +++ b/tests/A32/skyeye_interpreter/skyeye_common/armsupp.cpp @@ -17,9 +17,9 @@ //#include "common/logging/log.h" -#include "skyeye_interpreter/skyeye_common/arm_regformat.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" -#include "skyeye_interpreter/skyeye_common/armsupp.h" +#include "A32/skyeye_interpreter/skyeye_common/arm_regformat.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/armsupp.h" // Unsigned sum of absolute difference u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right) diff --git a/tests/skyeye_interpreter/skyeye_common/armsupp.h b/tests/A32/skyeye_interpreter/skyeye_common/armsupp.h similarity index 100% rename from tests/skyeye_interpreter/skyeye_common/armsupp.h rename to tests/A32/skyeye_interpreter/skyeye_common/armsupp.h diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h b/tests/A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h similarity index 100% rename from tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/vfp.cpp b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp.cpp similarity index 96% rename from tests/skyeye_interpreter/skyeye_common/vfp/vfp.cpp rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp.cpp index a6b68d1b..ff594afb 100644 --- a/tests/skyeye_interpreter/skyeye_common/vfp/vfp.cpp +++ b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp.cpp @@ -35,9 +35,9 @@ #include "common/common_types.h" //#include "common/logging/log.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" -#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h" #define LOG_INFO(...) do{}while(0) #define LOG_TRACE(...) do{}while(0) diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/vfp.h b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp.h similarity index 94% rename from tests/skyeye_interpreter/skyeye_common/vfp/vfp.h rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp.h index 6be286c9..550dc6ff 100644 --- a/tests/skyeye_interpreter/skyeye_common/vfp/vfp.h +++ b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp.h @@ -20,7 +20,7 @@ #pragma once -#include "skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */ +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */ #define LOG_INFO(...) do{}while(0) #define LOG_TRACE(...) do{}while(0) diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h similarity index 99% rename from tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h index 2b295c58..df7e9d59 100644 --- a/tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h +++ b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h @@ -34,8 +34,8 @@ #include #include "common/common_types.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" -#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" #define LOG_INFO(...) do{}while(0) #define LOG_TRACE(...) do{}while(0) diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp similarity index 99% rename from tests/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp index 6c0fd68b..aeffac80 100644 --- a/tests/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp +++ b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp @@ -63,9 +63,9 @@ #include //#include "common/logging/log.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" -#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" #define LOG_INFO(...) do{}while(0) #define LOG_TRACE(...) do{}while(0) diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp similarity index 100% rename from tests/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp diff --git a/tests/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp similarity index 99% rename from tests/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp rename to tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp index 52de1e04..40124219 100644 --- a/tests/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp +++ b/tests/A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp @@ -69,9 +69,9 @@ #include "common/common_types.h" //#include "common/logging/log.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" -#include "skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" -#include "skyeye_interpreter/skyeye_common/vfp/vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h" +#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h" #define LOG_INFO(...) do{}while(0) #define LOG_TRACE(...) do{}while(0) diff --git a/tests/arm/test_arm_disassembler.cpp b/tests/A32/test_arm_disassembler.cpp similarity index 100% rename from tests/arm/test_arm_disassembler.cpp rename to tests/A32/test_arm_disassembler.cpp diff --git a/tests/arm/test_thumb_instructions.cpp b/tests/A32/test_thumb_instructions.cpp similarity index 97% rename from tests/arm/test_thumb_instructions.cpp rename to tests/A32/test_thumb_instructions.cpp index ee5674f6..df3bbfb7 100644 --- a/tests/arm/test_thumb_instructions.cpp +++ b/tests/A32/test_thumb_instructions.cpp @@ -9,8 +9,8 @@ #include #include "common/common_types.h" -#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" -#include "skyeye_interpreter/skyeye_common/armstate.h" +#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" +#include "A32/skyeye_interpreter/skyeye_common/armstate.h" static u64 jit_num_ticks = 0; static std::array code_mem{}; diff --git a/tests/arm/vfp_vadd_f32.inc b/tests/A32/vfp_vadd_f32.inc similarity index 100% rename from tests/arm/vfp_vadd_f32.inc rename to tests/A32/vfp_vadd_f32.inc diff --git a/tests/arm/vfp_vsub_f32.inc b/tests/A32/vfp_vsub_f32.inc similarity index 100% rename from tests/arm/vfp_vsub_f32.inc rename to tests/A32/vfp_vsub_f32.inc diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp new file mode 100644 index 00000000..2f5dfd23 --- /dev/null +++ b/tests/A64/a64.cpp @@ -0,0 +1,58 @@ +/* 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. + */ + +#include + +#include + +#include + +#include "common/assert.h" +#include "common/common_types.h" + +class TestEnv final : public Dynarmic::A64::UserCallbacks { + u64 ticks_left = 0; + std::array code_mem{}; + + std::uint32_t MemoryReadCode(u64 vaddr) override { + if (vaddr < code_mem.size() * sizeof(u32)) { + size_t index = vaddr / sizeof(u32); + return code_mem[index]; + } + ASSERT_MSG(false, "MemoryReadCode(%llx)", vaddr); + } + + std::uint8_t MemoryRead8(u64 vaddr) override { ASSERT_MSG(false, "MemoryRead8(%llx)", vaddr); } + std::uint16_t MemoryRead16(u64 vaddr) override { ASSERT_MSG(false, "MemoryRead16(%llx)", vaddr); } + std::uint32_t MemoryRead32(u64 vaddr) override { ASSERT_MSG(false, "MemoryRead32(%llx)", vaddr); } + std::uint64_t MemoryRead64(u64 vaddr) override { ASSERT_MSG(false, "MemoryRead64(%llx)", vaddr); } + + void MemoryWrite8(u64 vaddr, std::uint8_t value) override { ASSERT_MSG(false, "MemoryWrite8(%llx, %hhi)", vaddr, value); } + void MemoryWrite16(u64 vaddr, std::uint16_t value) override { ASSERT_MSG(false, "MemoryWrite16(%llx, %hi)", vaddr, value); } + void MemoryWrite32(u64 vaddr, std::uint32_t value) override { ASSERT_MSG(false, "MemoryWrite32(%llx, %i)", vaddr, value); } + void MemoryWrite64(u64 vaddr, std::uint64_t value) override { ASSERT_MSG(false, "MemoryWrite64(%llx, %lli)", vaddr, value); } + + void InterpreterFallback(u64 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback(%llx, %zu)", pc, num_instructions); } + + void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC(%u)", swi); } + + 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; + } + +}; + +TEST_CASE("A64", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7e779b0d..3f47eed2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,31 +1,32 @@ add_executable(dynarmic_tests - arm/fuzz_arm.cpp - arm/fuzz_thumb.cpp - arm/test_arm_disassembler.cpp - arm/test_thumb_instructions.cpp + A32/fuzz_arm.cpp + A32/fuzz_thumb.cpp + A32/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp + A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h + A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp + A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h + A32/skyeye_interpreter/dyncom/arm_dyncom_run.h + A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp + A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h + A32/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp + A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h + A32/skyeye_interpreter/skyeye_common/arm_regformat.h + A32/skyeye_interpreter/skyeye_common/armstate.cpp + A32/skyeye_interpreter/skyeye_common/armstate.h + A32/skyeye_interpreter/skyeye_common/armsupp.cpp + A32/skyeye_interpreter/skyeye_common/armsupp.h + A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h + A32/skyeye_interpreter/skyeye_common/vfp/vfp.cpp + A32/skyeye_interpreter/skyeye_common/vfp/vfp.h + A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h + A32/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp + A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp + A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp + A32/test_arm_disassembler.cpp + A32/test_thumb_instructions.cpp + A64/a64.cpp main.cpp rand_int.h - skyeye_interpreter/dyncom/arm_dyncom_dec.cpp - skyeye_interpreter/dyncom/arm_dyncom_dec.h - skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp - skyeye_interpreter/dyncom/arm_dyncom_interpreter.h - skyeye_interpreter/dyncom/arm_dyncom_run.h - skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp - skyeye_interpreter/dyncom/arm_dyncom_thumb.h - skyeye_interpreter/dyncom/arm_dyncom_trans.cpp - skyeye_interpreter/dyncom/arm_dyncom_trans.h - skyeye_interpreter/skyeye_common/arm_regformat.h - skyeye_interpreter/skyeye_common/armstate.cpp - skyeye_interpreter/skyeye_common/armstate.h - skyeye_interpreter/skyeye_common/armsupp.cpp - skyeye_interpreter/skyeye_common/armsupp.h - skyeye_interpreter/skyeye_common/vfp/asm_vfp.h - skyeye_interpreter/skyeye_common/vfp/vfp.cpp - skyeye_interpreter/skyeye_common/vfp/vfp.h - skyeye_interpreter/skyeye_common/vfp/vfp_helper.h - skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp - skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp - skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp ) include(CreateDirectoryGroups)