From d877777c502addeb499f843b81f503b491ae20cc Mon Sep 17 00:00:00 2001 From: Merry Date: Tue, 12 Jul 2022 16:15:48 +0100 Subject: [PATCH] backend/arm64: Initial framework --- .github/workflows/aarch64.yml | 1 + src/dynarmic/CMakeLists.txt | 17 ++ .../backend/arm64/a32_address_space.cpp | 67 +++++ .../backend/arm64/a32_address_space.h | 53 ++++ src/dynarmic/backend/arm64/a32_core.h | 24 ++ src/dynarmic/backend/arm64/a32_interface.cpp | 269 ++++++++++++++++++ src/dynarmic/backend/arm64/a32_jitstate.cpp | 73 +++++ src/dynarmic/backend/arm64/a32_jitstate.h | 44 +++ 8 files changed, 548 insertions(+) create mode 100644 src/dynarmic/backend/arm64/a32_address_space.cpp create mode 100644 src/dynarmic/backend/arm64/a32_address_space.h create mode 100644 src/dynarmic/backend/arm64/a32_core.h create mode 100644 src/dynarmic/backend/arm64/a32_interface.cpp create mode 100644 src/dynarmic/backend/arm64/a32_jitstate.cpp create mode 100644 src/dynarmic/backend/arm64/a32_jitstate.h diff --git a/.github/workflows/aarch64.yml b/.github/workflows/aarch64.yml index 8c0f8f6b..29b036a3 100644 --- a/.github/workflows/aarch64.yml +++ b/.github/workflows/aarch64.yml @@ -45,6 +45,7 @@ jobs: -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DDYNARMIC_TESTS_USE_UNICORN=0 -DDYNARMIC_USE_LLVM=0 + -DDYNARMIC_FRONTENDS=A32 -G Ninja - name: Build diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 82d73f60..e6a0ee3c 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -365,6 +365,23 @@ if (ARCHITECTURE STREQUAL "x86_64") else() target_sources(dynarmic PRIVATE backend/x64/exception_handler_generic.cpp) endif() +elseif(ARCHITECTURE STREQUAL "arm64") + target_link_libraries(dynarmic PRIVATE $) + + if ("A32" IN_LIST DYNARMIC_FRONTENDS) + target_sources(dynarmic PRIVATE + backend/arm64/a32_address_space.cpp + backend/arm64/a32_address_space.h + backend/arm64/a32_core.h + backend/arm64/a32_interface.cpp + backend/arm64/a32_jitstate.cpp + backend/arm64/a32_jitstate.h + ) + endif() + + if ("A64" IN_LIST DYNARMIC_FRONTENDS) + message(FATAL_ERROR "TODO: Unimplemented frontend for this host architecture") + endif() else() message(FATAL_ERROR "Unsupported architecture") endif() diff --git a/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/backend/arm64/a32_address_space.cpp new file mode 100644 index 00000000..da8e91f7 --- /dev/null +++ b/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -0,0 +1,67 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/arm64/a32_address_space.h" + +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/frontend/A32/translate/a32_translate.h" +#include "dynarmic/ir/opt/passes.h" + +namespace Dynarmic::Backend::Arm64 { + +A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf) + : conf(conf) + , mem(conf.code_cache_size) + , code(mem.ptr()) { + EmitPrelude(); +} + +IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const { + IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); + + Optimization::PolyfillPass(ir_block, {}); + if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { + Optimization::A32GetSetElimination(ir_block, {.convert_nzc_to_nz = true}); + Optimization::DeadCodeElimination(ir_block); + } + if (conf.HasOptimization(OptimizationFlag::ConstProp)) { + Optimization::A32ConstantMemoryReads(ir_block, conf.callbacks); + Optimization::ConstantPropagation(ir_block); + Optimization::DeadCodeElimination(ir_block); + } + Optimization::VerificationPass(ir_block); + + return ir_block; +} + +void* A32AddressSpace::Get(IR::LocationDescriptor descriptor) { + if (const auto iter = block_entries.find(descriptor.Value()); iter != block_entries.end()) { + return iter->second; + } + return nullptr; +} + +void* A32AddressSpace::GetOrEmit(IR::LocationDescriptor descriptor) { + if (void* block_entry = Get(descriptor)) { + return block_entry; + } + + IR::Block ir_block = GenerateIR(descriptor); + void* block_entry = Emit(std::move(ir_block)); + block_entries.insert_or_assign(descriptor.Value(), block_entry); + return block_entry; +} + +void A32AddressSpace::EmitPrelude() { + prelude_info.run_code = code.ptr(); + + prelude_info.end_of_prelude = code.ptr(); +} + +void* A32AddressSpace::Emit(IR::Block) { + ASSERT_FALSE("Unimplemented"); +} + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/a32_address_space.h b/src/dynarmic/backend/arm64/a32_address_space.h new file mode 100644 index 00000000..bb9e5461 --- /dev/null +++ b/src/dynarmic/backend/arm64/a32_address_space.h @@ -0,0 +1,53 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include +#include +#include + +#include "dynarmic/interface/A32/config.h" +#include "dynarmic/interface/halt_reason.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::Arm64 { + +struct A32JitState; + +class A32AddressSpace final { +public: + explicit A32AddressSpace(const A32::UserConfig& conf); + + IR::Block GenerateIR(IR::LocationDescriptor) const; + + void* Get(IR::LocationDescriptor descriptor); + + void* GetOrEmit(IR::LocationDescriptor descriptor); + +private: + friend class A32Core; + + void EmitPrelude(); + + void* Emit(IR::Block ir_block); + + const A32::UserConfig conf; + + oaknut::CodeBlock mem; + oaknut::CodeGenerator code; + + tsl::robin_map block_entries; + + struct PreludeInfo { + u32* end_of_prelude; + + using RunCodeFuncType = HaltReason (*)(void* entry_point, A32JitState* context, volatile u32* halt_reason); + RunCodeFuncType run_code; + } prelude_info; +}; + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/a32_core.h b/src/dynarmic/backend/arm64/a32_core.h new file mode 100644 index 00000000..10ec3d7b --- /dev/null +++ b/src/dynarmic/backend/arm64/a32_core.h @@ -0,0 +1,24 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include "dynarmic/backend/arm64/a32_address_space.h" +#include "dynarmic/backend/arm64/a32_jitstate.h" + +namespace Dynarmic::Backend::Arm64 { + +class A32Core final { +public: + explicit A32Core(const A32::UserConfig&) {} + + HaltReason Run(A32AddressSpace& process, A32JitState& thread_ctx, volatile u32* halt_reason) { + const auto location_descriptor = thread_ctx.GetLocationDescriptor(); + const auto entry_point = process.GetOrEmit(location_descriptor); + return process.prelude_info.run_code(entry_point, &thread_ctx, halt_reason); + } +}; + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/a32_interface.cpp b/src/dynarmic/backend/arm64/a32_interface.cpp new file mode 100644 index 00000000..943eb9ba --- /dev/null +++ b/src/dynarmic/backend/arm64/a32_interface.cpp @@ -0,0 +1,269 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2021 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include +#include + +#include +#include +#include +#include + +#include "dynarmic/backend/arm64/a32_address_space.h" +#include "dynarmic/backend/arm64/a32_core.h" +#include "dynarmic/backend/arm64/a32_jitstate.h" +#include "dynarmic/common/atomic.h" +#include "dynarmic/interface/A32/a32.h" +#include "dynarmic/interface/A32/context.h" + +namespace Dynarmic::A32 { + +using namespace Backend::Arm64; + +struct Context::Impl { + A32JitState state; +}; + +Context::Context() + : impl(std::make_unique()) {} + +Context::~Context() = default; + +Context::Context(const Context& ctx) + : impl(std::make_unique(*ctx.impl)) {} + +Context::Context(Context&& ctx) noexcept + : impl(std::move(ctx.impl)) {} + +Context& Context::operator=(const Context& ctx) { + *impl = *ctx.impl; + return *this; +} + +Context& Context::operator=(Context&& ctx) noexcept { + impl = std::move(ctx.impl); + return *this; +} + +struct Jit::Impl final { + Impl(Jit* jit_interface, A32::UserConfig conf) + : jit_interface(jit_interface) + , conf(conf) + , current_address_space(conf) + , core(conf) {} + + HaltReason Run() { + ASSERT(!jit_interface->is_executing); + jit_interface->is_executing = true; + SCOPE_EXIT { + jit_interface->is_executing = false; + }; + + HaltReason hr = core.Run(current_address_space, current_state, &halt_reason); + + RequestCacheInvalidation(); + + return hr; + } + + HaltReason Step() { + ASSERT(!jit_interface->is_executing); + jit_interface->is_executing = true; + SCOPE_EXIT { + jit_interface->is_executing = false; + }; + + ASSERT_FALSE("Unimplemented"); + + RequestCacheInvalidation(); + + return HaltReason{}; + } + + void ClearCache() { + std::unique_lock lock{invalidation_mutex}; + invalidate_entire_cache = true; + HaltExecution(HaltReason::CacheInvalidation); + } + + void InvalidateCacheRange(std::uint32_t start_address, std::size_t length) { + std::unique_lock lock{invalidation_mutex}; + invalid_cache_ranges.add(boost::icl::discrete_interval::closed(start_address, static_cast(start_address + length - 1))); + HaltExecution(HaltReason::CacheInvalidation); + } + + void Reset() { + current_state = {}; + } + + void HaltExecution(HaltReason hr) { + Atomic::Or(&halt_reason, ~static_cast(hr)); + } + + void ClearHalt(HaltReason hr) { + Atomic::And(&halt_reason, ~static_cast(hr)); + } + + std::array& Regs() { + return current_state.regs; + } + + const std::array& Regs() const { + return current_state.regs; + } + + std::array& ExtRegs() { + return current_state.ext_regs; + } + + const std::array& ExtRegs() const { + return current_state.ext_regs; + } + + std::uint32_t Cpsr() const { + return current_state.Cpsr(); + } + + void SetCpsr(std::uint32_t value) { + current_state.SetCpsr(value); + } + + std::uint32_t Fpscr() const { + return current_state.Fpscr(); + } + + void SetFpscr(std::uint32_t value) { + current_state.SetFpscr(value); + } + + Context SaveContext() const { + Context ctx; + ctx.impl->state = current_state; + return ctx; + } + + void SaveContext(Context& ctx) const { + ctx.impl->state = current_state; + } + + void LoadContext(const Context& ctx) { + current_state = ctx.impl->state; + } + + void ClearExclusiveState() { + current_state.exclusive_state = false; + } + + void DumpDisassembly() const { + ASSERT_FALSE("Unimplemented"); + } + +private: + void RequestCacheInvalidation() { + ASSERT_FALSE("Unimplemented"); + + invalidate_entire_cache = false; + invalid_cache_ranges.clear(); + } + + Jit* jit_interface; + A32::UserConfig conf; + A32JitState current_state{}; + A32AddressSpace current_address_space; + A32Core core; + + volatile u32 halt_reason = 0; + + std::mutex invalidation_mutex; + boost::icl::interval_set invalid_cache_ranges; + bool invalidate_entire_cache = false; +}; + +Jit::Jit(UserConfig conf) + : impl(std::make_unique(this, conf)) {} + +Jit::~Jit() = default; + +HaltReason Jit::Run() { + return impl->Run(); +} + +HaltReason Jit::Step() { + return impl->Step(); +} + +void Jit::ClearCache() { + impl->ClearCache(); +} + +void Jit::InvalidateCacheRange(std::uint32_t start_address, std::size_t length) { + impl->InvalidateCacheRange(start_address, length); +} + +void Jit::Reset() { + impl->Reset(); +} + +void Jit::HaltExecution(HaltReason hr) { + impl->HaltExecution(hr); +} + +void Jit::ClearHalt(HaltReason hr) { + impl->ClearHalt(hr); +} + +std::array& Jit::Regs() { + return impl->Regs(); +} + +const std::array& Jit::Regs() const { + return impl->Regs(); +} + +std::array& Jit::ExtRegs() { + return impl->ExtRegs(); +} + +const std::array& Jit::ExtRegs() const { + return impl->ExtRegs(); +} + +std::uint32_t Jit::Cpsr() const { + return impl->Cpsr(); +} + +void Jit::SetCpsr(std::uint32_t value) { + impl->SetCpsr(value); +} + +std::uint32_t Jit::Fpscr() const { + return impl->Fpscr(); +} + +void Jit::SetFpscr(std::uint32_t value) { + impl->SetFpscr(value); +} + +Context Jit::SaveContext() const { + return impl->SaveContext(); +} + +void Jit::SaveContext(Context& ctx) const { + impl->SaveContext(ctx); +} + +void Jit::LoadContext(const Context& ctx) { + impl->LoadContext(ctx); +} + +void Jit::ClearExclusiveState() { + impl->ClearExclusiveState(); +} + +void Jit::DumpDisassembly() const { + impl->DumpDisassembly(); +} + +} // namespace Dynarmic::A32 diff --git a/src/dynarmic/backend/arm64/a32_jitstate.cpp b/src/dynarmic/backend/arm64/a32_jitstate.cpp new file mode 100644 index 00000000..27fd4e89 --- /dev/null +++ b/src/dynarmic/backend/arm64/a32_jitstate.cpp @@ -0,0 +1,73 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2016 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/arm64/a32_jitstate.h" + +#include +#include + +namespace Dynarmic::Backend::Arm64 { + +u32 A32JitState::Cpsr() const { + u32 cpsr = 0; + + // NZCV flags + cpsr |= cpsr_nzcv; + // Q flag + cpsr |= cpsr_q; + // GE flags + cpsr |= mcl::bit::get_bit<31>(cpsr_ge) ? 1 << 19 : 0; + cpsr |= mcl::bit::get_bit<23>(cpsr_ge) ? 1 << 18 : 0; + cpsr |= mcl::bit::get_bit<15>(cpsr_ge) ? 1 << 17 : 0; + cpsr |= mcl::bit::get_bit<7>(cpsr_ge) ? 1 << 16 : 0; + // E flag, T flag + cpsr |= mcl::bit::get_bit<1>(upper_location_descriptor) ? 1 << 9 : 0; + cpsr |= mcl::bit::get_bit<0>(upper_location_descriptor) ? 1 << 5 : 0; + // IT state + cpsr |= static_cast(upper_location_descriptor & 0b11111100'00000000); + cpsr |= static_cast(upper_location_descriptor & 0b00000011'00000000) << 17; + // Other flags + cpsr |= cpsr_jaifm; + + return cpsr; +} + +void A32JitState::SetCpsr(u32 cpsr) { + // NZCV flags + cpsr_nzcv = cpsr & 0xF0000000; + // Q flag + cpsr_q = cpsr & (1 << 27); + // GE flags + cpsr_ge = 0; + cpsr_ge |= mcl::bit::get_bit<19>(cpsr) ? 0xFF000000 : 0; + cpsr_ge |= mcl::bit::get_bit<18>(cpsr) ? 0x00FF0000 : 0; + cpsr_ge |= mcl::bit::get_bit<17>(cpsr) ? 0x0000FF00 : 0; + cpsr_ge |= mcl::bit::get_bit<16>(cpsr) ? 0x000000FF : 0; + + upper_location_descriptor &= 0xFFFF0000; + // E flag, T flag + upper_location_descriptor |= mcl::bit::get_bit<9>(cpsr) ? 2 : 0; + upper_location_descriptor |= mcl::bit::get_bit<5>(cpsr) ? 1 : 0; + // IT state + upper_location_descriptor |= (cpsr >> 0) & 0b11111100'00000000; + upper_location_descriptor |= (cpsr >> 17) & 0b00000011'00000000; + + // Other flags + cpsr_jaifm = cpsr & 0x010001DF; +} + +constexpr u32 FPCR_MASK = A32::LocationDescriptor::FPSCR_MODE_MASK; +constexpr u32 FPSR_MASK = 0xF800009F; + +u32 A32JitState::Fpscr() const { + return (upper_location_descriptor & 0xffff0000) | fpsr; +} + +void A32JitState::SetFpscr(u32 fpscr) { + fpsr = fpscr & FPSR_MASK; + upper_location_descriptor = (upper_location_descriptor & 0x0000ffff) | (fpscr & FPCR_MASK); +} + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/a32_jitstate.h b/src/dynarmic/backend/arm64/a32_jitstate.h new file mode 100644 index 00000000..687eda47 --- /dev/null +++ b/src/dynarmic/backend/arm64/a32_jitstate.h @@ -0,0 +1,44 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2021 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +#include + +#include "dynarmic/frontend/A32/a32_location_descriptor.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::Arm64 { + +struct A32JitState { + std::array regs{}; + + u32 upper_location_descriptor; + + alignas(16) std::array ext_regs{}; + + u32 cpsr_nzcv = 0; + u32 cpsr_ge = 0; + u32 cpsr_jaifm = 0; + u32 cpsr_q = 0; + + u32 fpsr = 0; + + u32 exclusive_state = 0; + + u32 Cpsr() const; + void SetCpsr(u32 cpsr); + + u32 Fpscr() const; + void SetFpscr(u32 fpscr); + + IR::LocationDescriptor GetLocationDescriptor() const { + return IR::LocationDescriptor{regs[15] | (static_cast(upper_location_descriptor) << 32)}; + } +}; + +} // namespace Dynarmic::Backend::Arm64