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