diff --git a/src/dynarmic/backend/x64/a32_interface.cpp b/src/dynarmic/backend/x64/a32_interface.cpp index 3e9eb4cc..e917a356 100644 --- a/src/dynarmic/backend/x64/a32_interface.cpp +++ b/src/dynarmic/backend/x64/a32_interface.cpp @@ -161,7 +161,7 @@ private: PerformCacheInvalidation(); } - IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); + IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { Optimization::A32GetSetElimination(ir_block); Optimization::DeadCodeElimination(ir_block); diff --git a/src/dynarmic/frontend/A32/translate/translate.cpp b/src/dynarmic/frontend/A32/translate/translate.cpp index 932a4422..5e7971a8 100644 --- a/src/dynarmic/frontend/A32/translate/translate.cpp +++ b/src/dynarmic/frontend/A32/translate/translate.cpp @@ -3,17 +3,18 @@ * SPDX-License-Identifier: 0BSD */ -#include "dynarmic/frontend/A32/location_descriptor.h" #include "dynarmic/frontend/A32/translate/translate.h" + +#include "dynarmic/frontend/A32/location_descriptor.h" #include "dynarmic/ir/basic_block.h" namespace Dynarmic::A32 { -IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options); -IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options); +IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options); +IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options); -IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { - return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(descriptor, memory_read_code, options); +IR::Block Translate(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) { + return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(descriptor, tcb, options); } bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction); diff --git a/src/dynarmic/frontend/A32/translate/translate.h b/src/dynarmic/frontend/A32/translate/translate.h index 34d15a58..f0502735 100644 --- a/src/dynarmic/frontend/A32/translate/translate.h +++ b/src/dynarmic/frontend/A32/translate/translate.h @@ -14,6 +14,7 @@ class Block; namespace Dynarmic::A32 { class LocationDescriptor; +struct TranslateCallbacks; using MemoryReadCodeFuncType = std::function; @@ -34,11 +35,11 @@ struct TranslationOptions { /** * This function translates instructions in memory into our intermediate representation. * @param descriptor The starting location of the basic block. Includes information like PC, Thumb state, &c. - * @param memory_read_code The function we should use to read emulated memory. + * @param tcb The callbacks we should use to read emulated memory. * @param options Configures how certain instructions are translated. * @return A translated basic block in the intermediate representation. */ -IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options); +IR::Block Translate(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options); /** * This function translates a single provided instruction into our intermediate representation. diff --git a/src/dynarmic/frontend/A32/translate/translate_arm.cpp b/src/dynarmic/frontend/A32/translate/translate_arm.cpp index a2e796d1..8e90771e 100644 --- a/src/dynarmic/frontend/A32/translate/translate_arm.cpp +++ b/src/dynarmic/frontend/A32/translate/translate_arm.cpp @@ -11,13 +11,14 @@ #include "dynarmic/frontend/A32/translate/conditional_state.h" #include "dynarmic/frontend/A32/translate/impl/translate.h" #include "dynarmic/frontend/A32/translate/translate.h" +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/frontend/A32/types.h" #include "dynarmic/interface/A32/config.h" #include "dynarmic/ir/basic_block.h" namespace Dynarmic::A32 { -IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { +IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) { const bool single_step = descriptor.SingleStepping(); IR::Block block{descriptor}; @@ -26,9 +27,11 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType mem bool should_continue = true; do { const u32 arm_pc = visitor.ir.current_location.PC(); - const u32 arm_instruction = memory_read_code(arm_pc); + const u32 arm_instruction = tcb->MemoryReadCode(arm_pc); visitor.current_instruction_size = 4; + tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir); + if (const auto vfp_decoder = DecodeVFP(arm_instruction)) { should_continue = vfp_decoder->get().call(visitor, arm_instruction); } else if (const auto asimd_decoder = DecodeASIMD(arm_instruction)) { diff --git a/src/dynarmic/frontend/A32/translate/translate_callbacks.h b/src/dynarmic/frontend/A32/translate/translate_callbacks.h new file mode 100644 index 00000000..612cedaf --- /dev/null +++ b/src/dynarmic/frontend/A32/translate/translate_callbacks.h @@ -0,0 +1,25 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2021 MerryMage + * SPDX-License-Identifier: 0BSD + */ +#pragma once + +#include "dynarmic/common/common_types.h" + +namespace Dynarmic::A32 { + +using VAddr = u32; + +class IREmitter; + +struct TranslateCallbacks { + // All reads through this callback are 4-byte aligned. + // Memory must be interpreted as little endian. + virtual std::uint32_t MemoryReadCode(VAddr vaddr) = 0; + + // Thus function is called before the instruction at pc is interpreted. + // IR code can be emitted by the callee prior to translation of the instruction. + virtual void PreCodeTranslationHook(bool is_thumb, VAddr pc, A32::IREmitter& ir) = 0; +}; + +} // namespace Dynarmic::A32 diff --git a/src/dynarmic/frontend/A32/translate/translate_thumb.cpp b/src/dynarmic/frontend/A32/translate/translate_thumb.cpp index 124275a8..99482340 100644 --- a/src/dynarmic/frontend/A32/translate/translate_thumb.cpp +++ b/src/dynarmic/frontend/A32/translate/translate_thumb.cpp @@ -16,6 +16,7 @@ #include "dynarmic/frontend/A32/translate/conditional_state.h" #include "dynarmic/frontend/A32/translate/impl/translate.h" #include "dynarmic/frontend/A32/translate/translate.h" +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/frontend/imm.h" #include "dynarmic/interface/A32/config.h" @@ -40,8 +41,8 @@ bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) { return false; } -std::tuple ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) { - u32 first_part = memory_read_code(arm_pc & 0xFFFFFFFC); +std::tuple ReadThumbInstruction(u32 arm_pc, TranslateCallbacks* tcb) { + u32 first_part = tcb->MemoryReadCode(arm_pc & 0xFFFFFFFC); if ((arm_pc & 0x2) != 0) { first_part >>= 16; } @@ -55,7 +56,7 @@ std::tuple ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFu // 32-bit thumb instruction // These always start with 0b11101, 0b11110 or 0b11111. - u32 second_part = memory_read_code((arm_pc + 2) & 0xFFFFFFFC); + u32 second_part = tcb->MemoryReadCode((arm_pc + 2) & 0xFFFFFFFC); if (((arm_pc + 2) & 0x2) != 0) { second_part >>= 16; } @@ -84,7 +85,7 @@ bool MaybeVFPOrASIMDInstruction(u32 thumb_instruction) { } // local namespace -IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { +IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb, const TranslationOptions& options) { const bool single_step = descriptor.SingleStepping(); IR::Block block{descriptor}; @@ -93,10 +94,12 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m bool should_continue = true; do { const u32 arm_pc = visitor.ir.current_location.PC(); - const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code); + const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, tcb); const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16; visitor.current_instruction_size = is_thumb_16 ? 2 : 4; + tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir); + if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) { if (is_thumb_16) { if (const auto decoder = DecodeThumb16(static_cast(thumb_instruction))) { diff --git a/src/dynarmic/interface/A32/config.h b/src/dynarmic/interface/A32/config.h index 33bdb0c3..43a49c9d 100644 --- a/src/dynarmic/interface/A32/config.h +++ b/src/dynarmic/interface/A32/config.h @@ -10,6 +10,7 @@ #include #include +#include "dynarmic/frontend/A32/translate/translate_callbacks.h" #include "dynarmic/interface/A32/arch_version.h" #include "dynarmic/interface/optimization_flags.h" @@ -53,12 +54,16 @@ enum class Exception { }; /// These function pointers may be inserted into compiled code. -struct UserCallbacks { +struct UserCallbacks : public TranslateCallbacks { 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); } + std::uint32_t MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); } + + // Thus function is called before the instruction at pc is interpreted. + // IR code can be emitted by the callee prior to translation of the instruction. + void PreCodeTranslationHook(bool /*is_thumb*/, VAddr /*pc*/, A32::IREmitter& /*ir*/) override {} // Reads through these callbacks may not be aligned. // Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT. @@ -83,7 +88,7 @@ struct UserCallbacks { // 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; } + virtual bool IsReadOnlyMemory(VAddr /*vaddr*/) { return false; } /// The interpreter must execute exactly num_instructions starting from PC. virtual void InterpreterFallback(VAddr pc, size_t num_instructions) = 0; diff --git a/tests/A32/fuzz_thumb.cpp b/tests/A32/fuzz_thumb.cpp index 9ac46fd1..e40e175f 100644 --- a/tests/A32/fuzz_thumb.cpp +++ b/tests/A32/fuzz_thumb.cpp @@ -19,10 +19,10 @@ #include "./testenv.h" #include "dynarmic/common/bit_util.h" #include "dynarmic/common/common_types.h" -#include "dynarmic/frontend/A32/disassembler/disassembler.h" #include "dynarmic/frontend/A32/FPSCR.h" -#include "dynarmic/frontend/A32/location_descriptor.h" #include "dynarmic/frontend/A32/PSR.h" +#include "dynarmic/frontend/A32/disassembler/disassembler.h" +#include "dynarmic/frontend/A32/location_descriptor.h" #include "dynarmic/frontend/A32/translate/translate.h" #include "dynarmic/interface/A32/a32.h" #include "dynarmic/ir/basic_block.h" @@ -177,7 +177,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn