1 /* This file is part of the dynarmic project. 2 * Copyright (c) 2016 MerryMage 3 * This software may be used and distributed according to the terms of the GNU 4 * General Public License version 2 or any later version. 5 */ 6 7 #pragma once 8 9 #include <array> 10 #include <optional> 11 #include <string> 12 #include <type_traits> 13 #include <unordered_map> 14 #include <unordered_set> 15 #include <vector> 16 17 #include "backend/A64/reg_alloc.h" 18 #include "backend/A64/emitter/a64_emitter.h" 19 #include "common/bit_util.h" 20 #include "common/fp/rounding_mode.h" 21 #include "frontend/ir/location_descriptor.h" 22 #include "frontend/ir/terminal.h" 23 24 namespace Dynarmic::IR { 25 class Block; 26 class Inst; 27 } // namespace Dynarmic::IR 28 29 namespace Dynarmic::BackendA64 { 30 31 class BlockOfCode; 32 33 using namespace Arm64Gen; 34 35 using A64FullVectorWidth = std::integral_constant<size_t, 128>; 36 37 // Array alias that always sizes itself according to the given type T 38 // relative to the size of a vector register. e.g. T = u32 would result 39 // in a std::array<u32, 4>. 40 template <typename T> 41 using VectorArray = std::array<T, A64FullVectorWidth::value / Common::BitSize<T>()>; 42 43 struct EmitContext { 44 EmitContext(RegAlloc& reg_alloc, IR::Block& block); 45 46 void EraseInstruction(IR::Inst* inst); 47 48 virtual FP::RoundingMode FPSCR_RMode() const = 0; 49 virtual u32 FPCR() const = 0; 50 virtual bool FPSCR_FTZ() const = 0; 51 virtual bool FPSCR_DN() const = 0; AccurateNaNEmitContext52 virtual bool AccurateNaN() const { return true; } 53 54 RegAlloc& reg_alloc; 55 IR::Block& block; 56 }; 57 58 class EmitA64 { 59 public: 60 struct BlockDescriptor { 61 CodePtr entrypoint; // Entrypoint of emitted code 62 size_t size; // Length in bytes of emitted code 63 }; 64 65 EmitA64(BlockOfCode& code); 66 virtual ~EmitA64(); 67 68 /// Looks up an emitted host block in the cache. 69 std::optional<BlockDescriptor> GetBasicBlock(IR::LocationDescriptor descriptor) const; 70 71 /// Empties the entire cache. 72 virtual void ClearCache(); 73 74 /// Invalidates a selection of basic blocks. 75 void InvalidateBasicBlocks(const std::unordered_set<IR::LocationDescriptor>& locations); 76 77 protected: 78 // Microinstruction emitters 79 #define OPCODE(name, type, ...) void Emit##name(EmitContext& ctx, IR::Inst* inst); 80 #define A32OPC(...) 81 #define A64OPC(...) 82 #include "backend/A64/opcodes.inc" 83 #undef OPCODE 84 #undef A32OPC 85 #undef A64OPC 86 87 // Helpers 88 virtual std::string LocationDescriptorToFriendlyName(const IR::LocationDescriptor&) const = 0; 89 void EmitAddCycles(size_t cycles); 90 FixupBranch EmitCond(IR::Cond cond); 91 BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, size_t size); 92 void PushRSBHelper(Arm64Gen::ARM64Reg loc_desc_reg, Arm64Gen::ARM64Reg index_reg, IR::LocationDescriptor target); 93 94 // Terminal instruction emitters 95 void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); 96 virtual void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 97 virtual void EmitTerminalImpl(IR::Term::ReturnToDispatch terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 98 virtual void EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 99 virtual void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 100 virtual void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 101 virtual void EmitTerminalImpl(IR::Term::FastDispatchHint terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 102 virtual void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 103 virtual void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 104 virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; 105 106 // Patching 107 struct PatchInformation { 108 std::vector<CodePtr> jg; 109 std::vector<CodePtr> jmp; 110 std::vector<CodePtr> mov_x0; 111 }; 112 void Patch(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr); 113 virtual void Unpatch(const IR::LocationDescriptor& target_desc); 114 virtual void EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr = nullptr) = 0; 115 virtual void EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr = nullptr) = 0; 116 virtual void EmitPatchMovX0(CodePtr target_code_ptr = nullptr) = 0; 117 118 // State 119 BlockOfCode& code; 120 std::unordered_map<IR::LocationDescriptor, BlockDescriptor> block_descriptors; 121 std::unordered_map<IR::LocationDescriptor, PatchInformation> patch_information; 122 }; 123 124 } // namespace Dynarmic::BackendX64 125