diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index e6a0ee3c..0970640c 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -376,6 +376,8 @@ elseif(ARCHITECTURE STREQUAL "arm64") backend/arm64/a32_interface.cpp backend/arm64/a32_jitstate.cpp backend/arm64/a32_jitstate.h + backend/arm64/emit_arm64.cpp + backend/arm64/emit_arm64.h ) endif() diff --git a/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/backend/arm64/a32_address_space.cpp index da8e91f7..4e5a8e74 100644 --- a/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -5,6 +5,7 @@ #include "dynarmic/backend/arm64/a32_address_space.h" +#include "dynarmic/backend/arm64/emit_arm64.h" #include "dynarmic/frontend/A32/a32_location_descriptor.h" #include "dynarmic/frontend/A32/translate/a32_translate.h" #include "dynarmic/ir/opt/passes.h" @@ -49,19 +50,85 @@ void* A32AddressSpace::GetOrEmit(IR::LocationDescriptor descriptor) { } 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; + const EmittedBlockInfo block_info = Emit(std::move(ir_block)); + + block_infos.insert_or_assign(descriptor.Value(), block_info); + block_entries.insert_or_assign(descriptor.Value(), block_info.entry_point); + return block_info.entry_point; +} + +void A32AddressSpace::ClearCache() { + block_entries.clear(); + block_infos.clear(); + code.set_ptr(prelude_info.end_of_prelude); } void A32AddressSpace::EmitPrelude() { + using namespace oaknut; + using namespace oaknut::util; + + mem.unprotect(); + prelude_info.run_code = code.ptr(); + // TODO: Minimize this. + code.STR(X30, SP, PRE_INDEXED, -16); + for (int i = 0; i < 30; i += 2) { + code.STP(XReg{i}, XReg{i + 1}, SP, PRE_INDEXED, -16); + } + for (int i = 0; i < 32; i += 2) { + code.STP(QReg{i}, QReg{i + 1}, SP, PRE_INDEXED, -32); + } + code.BR(X0); + + prelude_info.return_from_run_code = code.ptr(); + for (int i = 30; i >= 0; i -= 2) { + code.LDP(QReg{i}, QReg{i + 1}, SP, POST_INDEXED, 32); + } + for (int i = 28; i >= 0; i -= 2) { + code.LDP(XReg{i}, XReg{i + 1}, SP, POST_INDEXED, 16); + } + code.LDR(X30, SP, POST_INDEXED, 16); + code.RET(); + + mem.protect(); prelude_info.end_of_prelude = code.ptr(); } -void* A32AddressSpace::Emit(IR::Block) { - ASSERT_FALSE("Unimplemented"); +size_t A32AddressSpace::GetRemainingSize() { + return conf.code_cache_size - (reinterpret_cast(code.ptr()) - reinterpret_cast(mem.ptr())); +} + +EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) { + if (GetRemainingSize() < 1024 * 1024) { + ClearCache(); + } + + mem.unprotect(); + + EmittedBlockInfo block_info = EmitArm64(code, std::move(block)); + Link(block_info); + + mem.protect(); + + return block_info; +} + +void A32AddressSpace::Link(EmittedBlockInfo& block_info) { + using namespace oaknut; + using namespace oaknut::util; + + for (auto [ptr_offset, target] : block_info.relocations) { + CodeGenerator c{reinterpret_cast(reinterpret_cast(block_info.entry_point) + ptr_offset)}; + + switch (target) { + case LinkTarget::ReturnFromRunCode: + c.B(prelude_info.return_from_run_code); + break; + default: + ASSERT_FALSE("Invalid relocation target"); + } + } } } // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/a32_address_space.h b/src/dynarmic/backend/arm64/a32_address_space.h index bb9e5461..c3484e79 100644 --- a/src/dynarmic/backend/arm64/a32_address_space.h +++ b/src/dynarmic/backend/arm64/a32_address_space.h @@ -5,10 +5,12 @@ #pragma once +#include #include #include #include +#include "dynarmic/backend/arm64/emit_arm64.h" #include "dynarmic/interface/A32/config.h" #include "dynarmic/interface/halt_reason.h" #include "dynarmic/ir/basic_block.h" @@ -28,12 +30,16 @@ public: void* GetOrEmit(IR::LocationDescriptor descriptor); + void ClearCache(); + private: friend class A32Core; void EmitPrelude(); - void* Emit(IR::Block ir_block); + size_t GetRemainingSize(); + EmittedBlockInfo Emit(IR::Block ir_block); + void Link(EmittedBlockInfo& block); const A32::UserConfig conf; @@ -41,12 +47,14 @@ private: oaknut::CodeGenerator code; tsl::robin_map block_entries; + tsl::robin_map block_infos; struct PreludeInfo { u32* end_of_prelude; using RunCodeFuncType = HaltReason (*)(void* entry_point, A32JitState* context, volatile u32* halt_reason); RunCodeFuncType run_code; + void* return_from_run_code; } prelude_info; }; diff --git a/src/dynarmic/backend/arm64/a32_interface.cpp b/src/dynarmic/backend/arm64/a32_interface.cpp index 943eb9ba..26844d58 100644 --- a/src/dynarmic/backend/arm64/a32_interface.cpp +++ b/src/dynarmic/backend/arm64/a32_interface.cpp @@ -162,7 +162,7 @@ struct Jit::Impl final { private: void RequestCacheInvalidation() { - ASSERT_FALSE("Unimplemented"); + // ASSERT_FALSE("Unimplemented"); invalidate_entire_cache = false; invalid_cache_ranges.clear(); diff --git a/src/dynarmic/backend/arm64/abi.cpp b/src/dynarmic/backend/arm64/abi.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/dynarmic/backend/arm64/abi.h b/src/dynarmic/backend/arm64/abi.h new file mode 100644 index 00000000..5c12d722 --- /dev/null +++ b/src/dynarmic/backend/arm64/abi.h @@ -0,0 +1,16 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +#include + +namespace Dynarmic::Backend::Arm64 { + +constexpr u32 ABI_ALL_ + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/backend/arm64/emit_arm64.cpp new file mode 100644 index 00000000..ef404f34 --- /dev/null +++ b/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -0,0 +1,36 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/arm64/emit_arm64.h" + +#include + +#include "dynarmic/backend/arm64/a32_jitstate.h" +#include "dynarmic/ir/basic_block.h" + +namespace Dynarmic::Backend::Arm64 { + +using namespace oaknut::util; + +EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block) { + (void)block; + + EmittedBlockInfo ebi; + ebi.entry_point = code.ptr(); + + code.MOV(W0, 8); + code.STR(W0, X1, offsetof(A32JitState, regs) + 0 * sizeof(u32)); + code.MOV(W0, 2); + code.STR(W0, X1, offsetof(A32JitState, regs) + 1 * sizeof(u32)); + code.STR(W0, X1, offsetof(A32JitState, regs) + 15 * sizeof(u32)); + + ebi.relocations[code.ptr() - reinterpret_cast(ebi.entry_point)] = LinkTarget::ReturnFromRunCode; + code.NOP(); + + ebi.size = reinterpret_cast(code.ptr()) - reinterpret_cast(ebi.entry_point); + return ebi; +} + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/backend/arm64/emit_arm64.h new file mode 100644 index 00000000..1855432d --- /dev/null +++ b/src/dynarmic/backend/arm64/emit_arm64.h @@ -0,0 +1,36 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include +#include + +namespace oaknut { +struct PointerCodeGeneratorPolicy; +template +class BasicCodeGenerator; +using CodeGenerator = BasicCodeGenerator; +} // namespace oaknut + +namespace Dynarmic::IR { +class Block; +} // namespace Dynarmic::IR + +namespace Dynarmic::Backend::Arm64 { + +enum class LinkTarget { + ReturnFromRunCode, +}; + +struct EmittedBlockInfo { + void* entry_point; + size_t size; + tsl::robin_map relocations; +}; + +EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block); + +} // namespace Dynarmic::Backend::Arm64