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 #include <unordered_map>
8 #include <unordered_set>
9 
10 #include "backend/A64/block_of_code.h"
11 #include "backend/A64/emit_a64.h"
12 #include "backend/A64/hostloc.h"
13 #include "backend/A64/perf_map.h"
14 #include "common/assert.h"
15 #include "common/bit_util.h"
16 #include "common/common_types.h"
17 #include "common/scope_exit.h"
18 #include "common/variant_util.h"
19 #include "frontend/ir/basic_block.h"
20 #include "frontend/ir/microinstruction.h"
21 #include "frontend/ir/opcodes.h"
22 
23 // TODO: Have ARM flags in host flags and not have them use up GPR registers unless necessary.
24 // TODO: Actually implement that proper instruction selector you've always wanted to sweetheart.
25 
26 namespace Dynarmic::BackendA64 {
27 
EmitContext(RegAlloc & reg_alloc,IR::Block & block)28 EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block)
29     : reg_alloc(reg_alloc), block(block) {}
30 
EraseInstruction(IR::Inst * inst)31 void EmitContext::EraseInstruction(IR::Inst* inst) {
32     block.Instructions().erase(inst);
33     inst->ClearArgs();
34 }
35 
EmitA64(BlockOfCode & code)36 EmitA64::EmitA64(BlockOfCode& code)
37     : code(code) {}
38 
39 EmitA64::~EmitA64() = default;
40 
GetBasicBlock(IR::LocationDescriptor descriptor) const41 std::optional<typename EmitA64::BlockDescriptor> EmitA64::GetBasicBlock(IR::LocationDescriptor descriptor) const {
42     auto iter = block_descriptors.find(descriptor);
43     if (iter == block_descriptors.end())
44         return std::nullopt;
45     return iter->second;
46 }
47 
EmitVoid(EmitContext &,IR::Inst *)48 void EmitA64::EmitVoid(EmitContext&, IR::Inst*) {
49 }
50 
EmitBreakpoint(EmitContext &,IR::Inst *)51 void EmitA64::EmitBreakpoint(EmitContext&, IR::Inst*) {
52     code.BRK(0);
53 }
54 
EmitIdentity(EmitContext & ctx,IR::Inst * inst)55 void EmitA64::EmitIdentity(EmitContext& ctx, IR::Inst* inst) {
56     auto args = ctx.reg_alloc.GetArgumentInfo(inst);
57     if (!args[0].IsImmediate()) {
58         ctx.reg_alloc.DefineValue(inst, args[0]);
59     }
60 }
61 
PushRSBHelper(ARM64Reg loc_desc_reg,ARM64Reg index_reg,IR::LocationDescriptor target)62 void EmitA64::PushRSBHelper(ARM64Reg loc_desc_reg, ARM64Reg index_reg, IR::LocationDescriptor target) {
63     auto iter = block_descriptors.find(target);
64     CodePtr target_code_ptr = iter != block_descriptors.end()
65                             ? iter->second.entrypoint
66                             : code.GetReturnFromRunCodeAddress();
67 
68     code.LDR(INDEX_UNSIGNED, DecodeReg(index_reg), X28, code.GetJitStateInfo().offsetof_rsb_ptr);
69 
70     code.MOVI2R(loc_desc_reg, target.Value());
71 
72     patch_information[target].mov_x0.emplace_back(code.GetCodePtr());
73     EmitPatchMovX0(target_code_ptr);
74 
75     code.ADD(code.ABI_SCRATCH1, X28, DecodeReg(index_reg), ArithOption{index_reg, ST_LSL, 3});
76     code.STR(INDEX_UNSIGNED, loc_desc_reg, code.ABI_SCRATCH1, code.GetJitStateInfo().offsetof_rsb_location_descriptors);
77     code.STR(INDEX_UNSIGNED, X0, code.ABI_SCRATCH1, code.GetJitStateInfo().offsetof_rsb_codeptrs);
78 
79     code.ADDI2R(DecodeReg(index_reg), DecodeReg(index_reg), 1);
80     code.ANDI2R(DecodeReg(index_reg), DecodeReg(index_reg), code.GetJitStateInfo().rsb_ptr_mask, code.ABI_SCRATCH1);
81     code.STR(INDEX_UNSIGNED, DecodeReg(index_reg), X28, code.GetJitStateInfo().offsetof_rsb_ptr);
82 }
83 
EmitPushRSB(EmitContext & ctx,IR::Inst * inst)84 void EmitA64::EmitPushRSB(EmitContext& ctx, IR::Inst* inst) {
85     auto args = ctx.reg_alloc.GetArgumentInfo(inst);
86     ASSERT(args[0].IsImmediate());
87     u64 unique_hash_of_target = args[0].GetImmediateU64();
88 
89     ctx.reg_alloc.ScratchGpr({HostLoc::X0});
90     Arm64Gen::ARM64Reg loc_desc_reg = ctx.reg_alloc.ScratchGpr();
91     Arm64Gen::ARM64Reg index_reg = ctx.reg_alloc.ScratchGpr();
92 
93     PushRSBHelper(loc_desc_reg, index_reg, IR::LocationDescriptor{unique_hash_of_target});
94 }
95 
EmitGetCarryFromOp(EmitContext &,IR::Inst *)96 void EmitA64::EmitGetCarryFromOp(EmitContext&, IR::Inst*) {
97     ASSERT_FALSE("should never happen");
98 }
99 
EmitGetOverflowFromOp(EmitContext &,IR::Inst *)100 void EmitA64::EmitGetOverflowFromOp(EmitContext&, IR::Inst*) {
101     ASSERT_FALSE("should never happen");
102 }
103 
EmitGetGEFromOp(EmitContext &,IR::Inst *)104 void EmitA64::EmitGetGEFromOp(EmitContext&, IR::Inst*) {
105     ASSERT_FALSE("should never happen");
106 }
107 
EmitGetUpperFromOp(EmitContext &,IR::Inst *)108 void EmitA64::EmitGetUpperFromOp(EmitContext&, IR::Inst*) {
109     ASSERT_FALSE("should never happen");
110 }
111 
EmitGetLowerFromOp(EmitContext &,IR::Inst *)112 void EmitA64::EmitGetLowerFromOp(EmitContext&, IR::Inst*) {
113     ASSERT_FALSE("should never happen");
114 }
115 
EmitGetNZCVFromOp(EmitContext & ctx,IR::Inst * inst)116 void EmitA64::EmitGetNZCVFromOp(EmitContext& ctx, IR::Inst* inst) {
117     auto args = ctx.reg_alloc.GetArgumentInfo(inst);
118 
119     Arm64Gen::ARM64Reg nzcv = ctx.reg_alloc.ScratchGpr();
120     Arm64Gen::ARM64Reg value = ctx.reg_alloc.UseGpr(args[0]);
121     code.CMP(value, ZR);
122     code.MRS(nzcv, FIELD_NZCV);
123     ctx.reg_alloc.DefineValue(inst, nzcv);
124 }
125 
EmitNZCVFromPackedFlags(EmitContext & ctx,IR::Inst * inst)126 void EmitA64::EmitNZCVFromPackedFlags(EmitContext& ctx, IR::Inst* inst) {
127     auto args = ctx.reg_alloc.GetArgumentInfo(inst);
128 
129     if (args[0].IsImmediate()) {
130         Arm64Gen::ARM64Reg nzcv = DecodeReg(ctx.reg_alloc.ScratchGpr());
131         u32 value = 0;
132         value |= Common::Bit<31>(args[0].GetImmediateU32()) ? (1 << 15) : 0;
133         value |= Common::Bit<30>(args[0].GetImmediateU32()) ? (1 << 14) : 0;
134         value |= Common::Bit<29>(args[0].GetImmediateU32()) ? (1 << 8) : 0;
135         value |= Common::Bit<28>(args[0].GetImmediateU32()) ? (1 << 0) : 0;
136         code.MOVI2R(nzcv, value);
137         ctx.reg_alloc.DefineValue(inst, nzcv);
138     } else {
139         Arm64Gen::ARM64Reg nzcv = DecodeReg(ctx.reg_alloc.UseScratchGpr(args[0]));
140         Arm64Gen::ARM64Reg scratch = DecodeReg(ctx.reg_alloc.ScratchGpr());
141         // TODO: Optimize
142         code.LSR(nzcv, nzcv, 28);
143         code.MOVI2R(scratch, 0b00010000'10000001);
144         code.MUL(nzcv, nzcv, scratch);
145         code.ANDI2R(nzcv, nzcv, 1, scratch);
146         ctx.reg_alloc.DefineValue(inst, nzcv);
147     }
148 }
149 
EmitAddCycles(size_t cycles)150 void EmitA64::EmitAddCycles(size_t cycles) {
151     ASSERT(cycles < std::numeric_limits<u32>::max());
152     code.SUBI2R(X26, X26, static_cast<u32>(cycles));
153 }
154 
EmitCond(IR::Cond cond)155 FixupBranch EmitA64::EmitCond(IR::Cond cond) {
156     FixupBranch label;
157 
158     const Arm64Gen::ARM64Reg cpsr = code.ABI_SCRATCH1;
159     code.LDR(INDEX_UNSIGNED, DecodeReg(cpsr), X28, code.GetJitStateInfo().offsetof_cpsr_nzcv);
160     code._MSR(FIELD_NZCV, cpsr);
161 
162     switch (cond) {
163     case IR::Cond::EQ: //z
164         label = code.B(CC_EQ);
165         break;
166     case IR::Cond::NE: //!z
167         label = code.B(CC_NEQ);
168         break;
169     case IR::Cond::CS: //c
170         label = code.B(CC_CS);
171         break;
172     case IR::Cond::CC: //!c
173         label = code.B(CC_CC);
174         break;
175     case IR::Cond::MI: //n
176         label = code.B(CC_MI);
177         break;
178     case IR::Cond::PL: //!n
179         label = code.B(CC_PL);
180         break;
181     case IR::Cond::VS: //v
182         label = code.B(CC_VS);
183         break;
184     case IR::Cond::VC: //!v
185         label = code.B(CC_VC);
186         break;
187     case IR::Cond::HI:  //c & !z
188         label = code.B(CC_HI);
189         break;
190     case IR::Cond::LS:  //!c | z
191         label = code.B(CC_LS);
192         break;
193     case IR::Cond::GE:  // n == v
194         label = code.B(CC_GE);
195         break;
196     case IR::Cond::LT:  // n != v
197         label = code.B(CC_LT);
198         break;
199     case IR::Cond::GT:  // !z & (n == v)
200         label = code.B(CC_GT);
201         break;
202     case IR::Cond::LE:  // z | (n != v)
203         label = code.B(CC_LE);
204         break;
205     default:
206         ASSERT_MSG(false, "Unknown cond {}", static_cast<size_t>(cond));
207         break;
208     }
209 
210     return label;
211 }
212 
RegisterBlock(const IR::LocationDescriptor & descriptor,CodePtr entrypoint,size_t size)213 EmitA64::BlockDescriptor EmitA64::RegisterBlock(const IR::LocationDescriptor& descriptor, CodePtr entrypoint, size_t size) {
214     PerfMapRegister(entrypoint, code.GetCodePtr(), LocationDescriptorToFriendlyName(descriptor));
215     Patch(descriptor, entrypoint);
216     BlockDescriptor block_desc{entrypoint, size};
217 
218     block_descriptors.emplace(descriptor.Value(), block_desc);
219     return block_desc;
220 }
221 
EmitTerminal(IR::Terminal terminal,IR::LocationDescriptor initial_location,bool is_single_step)222 void EmitA64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
223     Common::VisitVariant<void>(terminal, [this, initial_location, is_single_step](auto x) {
224         using T = std::decay_t<decltype(x)>;
225         if constexpr (!std::is_same_v<T, IR::Term::Invalid>) {
226             this->EmitTerminalImpl(x, initial_location, is_single_step);
227         } else {
228             ASSERT_MSG(false, "Invalid terminal");
229         }
230     });
231 }
232 
Patch(const IR::LocationDescriptor & desc,CodePtr bb)233 void EmitA64::Patch(const IR::LocationDescriptor& desc, CodePtr bb) {
234     const CodePtr save_code_ptr = code.GetCodePtr();
235     const PatchInformation& patch_info = patch_information[desc];
236 
237     for (CodePtr location : patch_info.jg) {
238         code.SetCodePtr(location);
239         EmitPatchJg(desc, bb);
240         code.FlushIcache();
241     }
242 
243     for (CodePtr location : patch_info.jmp) {
244         code.SetCodePtr(location);
245         EmitPatchJmp(desc, bb);
246         code.FlushIcache();
247     }
248 
249     for (CodePtr location : patch_info.mov_x0) {
250         code.SetCodePtr(location);
251         EmitPatchMovX0(bb);
252         code.FlushIcache();
253     }
254 
255     code.SetCodePtr(save_code_ptr);
256 }
257 
Unpatch(const IR::LocationDescriptor & desc)258 void EmitA64::Unpatch(const IR::LocationDescriptor& desc) {
259     Patch(desc, nullptr);
260 }
261 
ClearCache()262 void EmitA64::ClearCache() {
263     block_descriptors.clear();
264     patch_information.clear();
265 
266     PerfMapClear();
267 }
268 
InvalidateBasicBlocks(const std::unordered_set<IR::LocationDescriptor> & locations)269 void EmitA64::InvalidateBasicBlocks(const std::unordered_set<IR::LocationDescriptor>& locations) {
270     code.EnableWriting();
271     SCOPE_EXIT { code.DisableWriting(); };
272 
273     for (const auto &descriptor : locations) {
274         auto it = block_descriptors.find(descriptor);
275         if (it == block_descriptors.end()) {
276             continue;
277         }
278 
279         if (patch_information.count(descriptor)) {
280             Unpatch(descriptor);
281         }
282         block_descriptors.erase(it);
283     }
284 }
285 
286 } // namespace Dynarmic::BackendA64
287