A32: Introduce PreCodeTranslationHook

This commit is contained in:
MerryMage 2021-05-22 12:13:16 +01:00
parent 714216fd0e
commit 51b155df92
8 changed files with 59 additions and 21 deletions

View file

@ -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);

View file

@ -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);

View file

@ -14,6 +14,7 @@ class Block;
namespace Dynarmic::A32 {
class LocationDescriptor;
struct TranslateCallbacks;
using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>;
@ -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.

View file

@ -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<TranslatorVisitor>(arm_instruction)) {
should_continue = vfp_decoder->get().call(visitor, arm_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(arm_instruction)) {

View file

@ -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

View file

@ -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<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) {
u32 first_part = memory_read_code(arm_pc & 0xFFFFFFFC);
std::tuple<u32, ThumbInstSize> 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<u32, ThumbInstSize> 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<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {

View file

@ -10,6 +10,7 @@
#include <cstdint>
#include <memory>
#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;

View file

@ -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<Th
size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) {
A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, A32::FPSCR{}};
IR::Block ir_block = A32::Translate(descriptor, [&test_env](u32 vaddr) { return test_env.MemoryReadCode(vaddr); }, {});
IR::Block ir_block = A32::Translate(descriptor, &test_env, {});
Optimization::A32GetSetElimination(ir_block);
Optimization::DeadCodeElimination(ir_block);
Optimization::A32ConstantMemoryReads(ir_block, &test_env);