diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 9b5cb40d..c7a999a0 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -395,6 +395,7 @@ elseif(ARCHITECTURE STREQUAL "arm64") backend/arm64/emit_arm64_vector_saturation.cpp backend/arm64/emit_context.h backend/arm64/exclusive_monitor.cpp + backend/arm64/fastmem.h backend/arm64/fpsr_manager.cpp backend/arm64/fpsr_manager.h backend/arm64/reg_alloc.cpp diff --git a/src/dynarmic/backend/arm64/address_space.cpp b/src/dynarmic/backend/arm64/address_space.cpp index ce6729bb..2047df79 100644 --- a/src/dynarmic/backend/arm64/address_space.cpp +++ b/src/dynarmic/backend/arm64/address_space.cpp @@ -19,7 +19,13 @@ namespace Dynarmic::Backend::Arm64 { AddressSpace::AddressSpace(size_t code_cache_size) : code_cache_size(code_cache_size) , mem(code_cache_size) - , code(mem.ptr()) {} + , code(mem.ptr()) + , fastmem_manager(exception_handler) { + exception_handler.Register(mem, code_cache_size); + exception_handler.SetFastmemCallback([this](u64 host_pc) { + return FastmemCallback(host_pc); + }); +} AddressSpace::~AddressSpace() = default; @@ -91,7 +97,7 @@ EmittedBlockInfo AddressSpace::Emit(IR::Block block) { mem.unprotect(); - EmittedBlockInfo block_info = EmitArm64(code, std::move(block), GetEmitConfig()); + EmittedBlockInfo block_info = EmitArm64(code, std::move(block), GetEmitConfig(), fastmem_manager); block_infos.insert_or_assign(block.Location().Value(), block_info); block_entries.insert_or_assign(block.Location().Value(), block_info.entry_point); @@ -274,4 +280,39 @@ void AddressSpace::RelinkForDescriptor(IR::LocationDescriptor target_descriptor, } } +FakeCall AddressSpace::FastmemCallback(u64 host_pc) { + { + const auto host_ptr = mcl::bit_cast(host_pc); + + const auto location_descriptor = ReverseGet(host_ptr); + if (!location_descriptor) { + goto fail; + } + + const auto block_iter = block_infos.find(location_descriptor->Value()); + if (block_iter == block_infos.end()) { + goto fail; + } + + const auto block_entry_point = block_iter->second.entry_point; + const auto iter = block_iter->second.fastmem_patch_info.find(host_ptr - block_entry_point); + if (iter == block_iter->second.fastmem_patch_info.end()) { + goto fail; + } + + if (iter->second.recompile) { + const auto marker = iter->second.marker; + fastmem_manager.MarkDoNotFastmem(marker); + InvalidateBasicBlocks({std::get<0>(marker)}); + } + + return iter->second.fc; + } + +fail: + fmt::print("dynarmic: Segfault happened within JITted code at host_pc = {:016x}\n", host_pc); + fmt::print("Segfault wasn't at a fastmem patch location!\n"); + ASSERT_FALSE("segfault"); +} + } // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/address_space.h b/src/dynarmic/backend/arm64/address_space.h index 49b43889..91773dcf 100644 --- a/src/dynarmic/backend/arm64/address_space.h +++ b/src/dynarmic/backend/arm64/address_space.h @@ -15,6 +15,7 @@ #include #include "dynarmic/backend/arm64/emit_arm64.h" +#include "dynarmic/backend/arm64/fastmem.h" #include "dynarmic/interface/halt_reason.h" #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/location_descriptor.h" @@ -47,6 +48,8 @@ protected: void Link(IR::LocationDescriptor block_descriptor, EmittedBlockInfo& block); void RelinkForDescriptor(IR::LocationDescriptor target_descriptor, CodePtr target_ptr); + FakeCall FastmemCallback(u64 host_pc); + const size_t code_cache_size; oaknut::CodeBlock mem; oaknut::CodeGenerator code; @@ -56,6 +59,9 @@ protected: tsl::robin_map block_infos; tsl::robin_map> block_references; + ExceptionHandler exception_handler; + FastmemManager fastmem_manager; + struct PreludeInfo { u32* end_of_prelude; diff --git a/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/backend/arm64/emit_arm64.cpp index 3ac68ac7..39e0f73a 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -175,12 +175,12 @@ static void EmitAddCycles(oaknut::CodeGenerator& code, EmitContext& ctx, size_t } } -EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& conf) { +EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& conf, FastmemManager& fastmem_manager) { EmittedBlockInfo ebi; FpsrManager fpsr_manager{code, conf.state_fpsr_offset}; RegAlloc reg_alloc{code, fpsr_manager, GPR_ORDER, FPR_ORDER}; - EmitContext ctx{block, reg_alloc, conf, ebi, fpsr_manager, {}}; + EmitContext ctx{block, reg_alloc, conf, ebi, fpsr_manager, fastmem_manager, {}}; ebi.entry_point = code.ptr(); diff --git a/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/backend/arm64/emit_arm64.h index c93aa61e..391193aa 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.h +++ b/src/dynarmic/backend/arm64/emit_arm64.h @@ -13,6 +13,7 @@ #include #include +#include "dynarmic/backend/arm64/fastmem.h" #include "dynarmic/interface/A32/coprocessor.h" #include "dynarmic/interface/optimization_flags.h" #include "dynarmic/ir/location_descriptor.h" @@ -99,6 +100,7 @@ struct EmittedBlockInfo { size_t size; std::vector relocations; tsl::robin_map> block_relocations; + tsl::robin_map fastmem_patch_info; }; struct EmitConfig { @@ -149,7 +151,7 @@ struct EmitConfig { std::array, 16> coprocessors{}; }; -EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf); +EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf, FastmemManager& fastmem_manager); template void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); diff --git a/src/dynarmic/backend/arm64/emit_context.h b/src/dynarmic/backend/arm64/emit_context.h index 0f55a630..6bfc6f3c 100644 --- a/src/dynarmic/backend/arm64/emit_context.h +++ b/src/dynarmic/backend/arm64/emit_context.h @@ -23,6 +23,7 @@ class Block; namespace Dynarmic::Backend::Arm64 { struct EmitConfig; +class FastmemManager; class FpsrManager; using SharedLabel = std::shared_ptr; @@ -37,6 +38,7 @@ struct EmitContext { const EmitConfig& conf; EmittedBlockInfo& ebi; FpsrManager& fpsr; + FastmemManager& fastmem; std::vector> deferred_emits; diff --git a/src/dynarmic/backend/arm64/fastmem.h b/src/dynarmic/backend/arm64/fastmem.h new file mode 100644 index 00000000..fd6d91f7 --- /dev/null +++ b/src/dynarmic/backend/arm64/fastmem.h @@ -0,0 +1,52 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include +#include + +#include +#include +#include + +#include "dynarmic/backend/exception_handler.h" +#include "dynarmic/ir/location_descriptor.h" + +namespace Dynarmic::Backend::Arm64 { + +using DoNotFastmemMarker = std::tuple; + +struct DoNotFastmemMarkerHash { + size_t operator()(const DoNotFastmemMarker& value) const { + return mcl::hash::xmrx(std::get<0>(value).Value() ^ static_cast(std::get<1>(value))); + } +}; + +struct FastmemPatchInfo { + DoNotFastmemMarker marker; + FakeCall fc; + bool recompile; +}; + +class FastmemManager { +public: + explicit FastmemManager(ExceptionHandler& eh) + : exception_handler(eh) {} + + bool ShouldFastmem(DoNotFastmemMarker marker) const { + return exception_handler.SupportsFastmem() && do_not_fastmem.count(marker) == 0; + } + + void MarkDoNotFastmem(DoNotFastmemMarker marker) { + do_not_fastmem.emplace(marker); + } + +private: + ExceptionHandler& exception_handler; + tsl::robin_set do_not_fastmem; +}; + +} // namespace Dynarmic::Backend::Arm64