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