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 <memory>
8
9 #include <boost/icl/interval_set.hpp>
10 #include <fmt/format.h>
11
12 #include <dynarmic/A32/a32.h>
13 #include <dynarmic/A32/context.h>
14
15 #include "backend/A64/a32_emit_a64.h"
16 #include "backend/A64/a32_jitstate.h"
17 #include "backend/A64/block_of_code.h"
18 #include "backend/A64/callback.h"
19 #include "backend/A64/devirtualize.h"
20 #include "backend/A64/jitstate_info.h"
21 #include "common/assert.h"
22 #include "common/common_types.h"
23 #include "common/llvm_disassemble.h"
24 #include "common/scope_exit.h"
25 #include "frontend/A32/translate/translate.h"
26 #include "frontend/ir/basic_block.h"
27 #include "frontend/ir/location_descriptor.h"
28 #include "ir_opt/passes.h"
29
30 namespace Dynarmic::A32 {
31
32 using namespace BackendA64;
33
GenRunCodeCallbacks(const A32::UserConfig & config,CodePtr (* LookupBlock)(void * lookup_block_arg),void * arg)34 static RunCodeCallbacks GenRunCodeCallbacks(const A32::UserConfig& config, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg) {
35 return RunCodeCallbacks{
36 std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
37 std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::AddTicks>(config.callbacks)),
38 std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(config.callbacks)),
39 reinterpret_cast<u64>(config.fastmem_pointer),
40 };
41 }
42
43 struct Jit::Impl {
ImplDynarmic::A32::Jit::Impl44 Impl(Jit* jit, A32::UserConfig config)
45 : block_of_code(GenRunCodeCallbacks(config, &GetCurrentBlockThunk, this), JitStateInfo{jit_state})
46 , emitter(block_of_code, config, jit)
47 , config(std::move(config))
48 , jit_interface(jit)
49 {}
50
51 A32JitState jit_state;
52 BlockOfCode block_of_code;
53 A32EmitA64 emitter;
54
55 const A32::UserConfig config;
56
57 // Requests made during execution to invalidate the cache are queued up here.
58 size_t invalid_cache_generation = 0;
59 boost::icl::interval_set<u32> invalid_cache_ranges;
60 bool invalidate_entire_cache = false;
61
ExecuteDynarmic::A32::Jit::Impl62 void Execute() {
63 const CodePtr current_codeptr = [this]{
64 // RSB optimization
65 const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
66 if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
67 jit_state.rsb_ptr = new_rsb_ptr;
68 return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
69 }
70
71 return GetCurrentBlock();
72 }();
73
74 block_of_code.RunCode(&jit_state, current_codeptr);
75 }
76
StepDynarmic::A32::Jit::Impl77 void Step() {
78 block_of_code.StepCode(&jit_state, GetCurrentSingleStep());
79 }
80
DisassembleDynarmic::A32::Jit::Impl81 std::string Disassemble(const IR::LocationDescriptor& descriptor) {
82 auto block = GetBasicBlock(descriptor);
83 std::string result = fmt::format("address: {}\nsize: {} bytes\n", block.entrypoint, block.size);
84 #ifdef DYNARMIC_USE_LLVM
85 for (const u32* pos = reinterpret_cast<const u32*>(block.entrypoint);
86 reinterpret_cast<const u8*>(pos) < reinterpret_cast<const u8*>(block.entrypoint) + block.size; pos += 1) {
87 fmt::print("0x{:02x} 0x{:02x} ", reinterpret_cast<u64>(pos), *pos);
88 fmt::print("{}", Common::DisassembleAArch64(*pos, reinterpret_cast<u64>(pos)));
89 result += Common::DisassembleAArch64(*pos, reinterpret_cast<u64>(pos));
90 }
91 #endif
92 return result;
93 }
94
PerformCacheInvalidationDynarmic::A32::Jit::Impl95 void PerformCacheInvalidation() {
96 if (invalidate_entire_cache) {
97 jit_state.ResetRSB();
98 block_of_code.ClearCache();
99 emitter.ClearCache();
100
101 invalid_cache_ranges.clear();
102 invalidate_entire_cache = false;
103 invalid_cache_generation++;
104 return;
105 }
106
107 if (invalid_cache_ranges.empty()) {
108 return;
109 }
110
111 jit_state.ResetRSB();
112 emitter.InvalidateCacheRanges(invalid_cache_ranges);
113 invalid_cache_ranges.clear();
114 invalid_cache_generation++;
115 }
116
RequestCacheInvalidationDynarmic::A32::Jit::Impl117 void RequestCacheInvalidation() {
118 if (jit_interface->is_executing) {
119 jit_state.halt_requested = true;
120 return;
121 }
122
123 PerformCacheInvalidation();
124 }
125
126 private:
127 Jit* jit_interface;
128
GetCurrentBlockThunkDynarmic::A32::Jit::Impl129 static CodePtr GetCurrentBlockThunk(void* this_voidptr) {
130 Jit::Impl& this_ = *static_cast<Jit::Impl*>(this_voidptr);
131 return this_.GetCurrentBlock();
132 }
133
GetCurrentLocationDynarmic::A32::Jit::Impl134 IR::LocationDescriptor GetCurrentLocation() const {
135 return IR::LocationDescriptor{jit_state.GetUniqueHash()};
136 }
137
GetCurrentBlockDynarmic::A32::Jit::Impl138 CodePtr GetCurrentBlock() {
139 return GetBasicBlock(GetCurrentLocation()).entrypoint;
140 }
141
GetCurrentSingleStepDynarmic::A32::Jit::Impl142 CodePtr GetCurrentSingleStep() {
143 return GetBasicBlock(A32::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true)).entrypoint;
144 }
145
GetBasicBlockDynarmic::A32::Jit::Impl146 A32EmitA64::BlockDescriptor GetBasicBlock(IR::LocationDescriptor descriptor) {
147 auto block = emitter.GetBasicBlock(descriptor);
148 if (block)
149 return *block;
150
151 constexpr size_t MINIMUM_REMAINING_CODESIZE = 1 * 1024 * 1024;
152 if (block_of_code.SpaceRemaining() < MINIMUM_REMAINING_CODESIZE) {
153 invalidate_entire_cache = true;
154 PerformCacheInvalidation();
155 }
156
157 IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return config.callbacks->MemoryReadCode(vaddr); }, {config.define_unpredictable_behaviour, config.hook_hint_instructions});
158 if (config.enable_optimizations) {
159 Optimization::A32GetSetElimination(ir_block);
160 Optimization::DeadCodeElimination(ir_block);
161 Optimization::A32ConstantMemoryReads(ir_block, config.callbacks);
162 Optimization::ConstantPropagation(ir_block);
163 Optimization::DeadCodeElimination(ir_block);
164 Optimization::A32MergeInterpretBlocksPass(ir_block, config.callbacks);
165 }
166 Optimization::VerificationPass(ir_block);
167 return emitter.Emit(ir_block);
168 }
169 };
170
Jit(UserConfig config)171 Jit::Jit(UserConfig config) : impl(std::make_unique<Impl>(this, std::move(config))) {}
172
173 Jit::~Jit() = default;
174
Run()175 void Jit::Run() {
176 ASSERT(!is_executing);
177 is_executing = true;
178 SCOPE_EXIT { this->is_executing = false; };
179
180 impl->jit_state.halt_requested = false;
181
182 impl->Execute();
183
184 impl->PerformCacheInvalidation();
185 }
186
Step()187 void Jit::Step() {
188 ASSERT(!is_executing);
189 is_executing = true;
190 SCOPE_EXIT { this->is_executing = false; };
191
192 impl->jit_state.halt_requested = true;
193
194 impl->Step();
195
196 impl->PerformCacheInvalidation();
197 }
198
ClearCache()199 void Jit::ClearCache() {
200 impl->invalidate_entire_cache = true;
201 impl->RequestCacheInvalidation();
202 }
203
InvalidateCacheRange(std::uint32_t start_address,std::size_t length)204 void Jit::InvalidateCacheRange(std::uint32_t start_address, std::size_t length) {
205 impl->invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
206 impl->RequestCacheInvalidation();
207 }
208
Reset()209 void Jit::Reset() {
210 ASSERT(!is_executing);
211 impl->jit_state = {};
212 }
213
HaltExecution()214 void Jit::HaltExecution() {
215 impl->jit_state.halt_requested = true;
216 }
217
Regs()218 std::array<u32, 16>& Jit::Regs() {
219 return impl->jit_state.Reg;
220 }
Regs() const221 const std::array<u32, 16>& Jit::Regs() const {
222 return impl->jit_state.Reg;
223 }
224
ExtRegs()225 std::array<u32, 64>& Jit::ExtRegs() {
226 return impl->jit_state.ExtReg;
227 }
228
ExtRegs() const229 const std::array<u32, 64>& Jit::ExtRegs() const {
230 return impl->jit_state.ExtReg;
231 }
232
Cpsr() const233 u32 Jit::Cpsr() const {
234 return impl->jit_state.Cpsr();
235 }
236
SetCpsr(u32 value)237 void Jit::SetCpsr(u32 value) {
238 return impl->jit_state.SetCpsr(value);
239 }
240
Fpscr() const241 u32 Jit::Fpscr() const {
242 return impl->jit_state.Fpscr();
243 }
244
SetFpscr(u32 value)245 void Jit::SetFpscr(u32 value) {
246 return impl->jit_state.SetFpscr(value);
247 }
248
SaveContext() const249 Context Jit::SaveContext() const {
250 Context ctx;
251 SaveContext(ctx);
252 return ctx;
253 }
254
255 struct Context::Impl {
256 A32JitState jit_state;
257 size_t invalid_cache_generation;
258 };
259
Context()260 Context::Context() : impl(std::make_unique<Context::Impl>()) { impl->jit_state.ResetRSB(); }
261 Context::~Context() = default;
Context(const Context & ctx)262 Context::Context(const Context& ctx) : impl(std::make_unique<Context::Impl>(*ctx.impl)) {}
Context(Context && ctx)263 Context::Context(Context&& ctx) noexcept : impl(std::move(ctx.impl)) {}
operator =(const Context & ctx)264 Context& Context::operator=(const Context& ctx) {
265 *impl = *ctx.impl;
266 return *this;
267 }
operator =(Context && ctx)268 Context& Context::operator=(Context&& ctx) noexcept {
269 impl = std::move(ctx.impl);
270 return *this;
271 }
272
Regs()273 std::array<std::uint32_t, 16>& Context::Regs() {
274 return impl->jit_state.Reg;
275 }
Regs() const276 const std::array<std::uint32_t, 16>& Context::Regs() const {
277 return impl->jit_state.Reg;
278 }
ExtRegs()279 std::array<std::uint32_t, 64>& Context::ExtRegs() {
280 return impl->jit_state.ExtReg;
281 }
ExtRegs() const282 const std::array<std::uint32_t, 64>& Context::ExtRegs() const {
283 return impl->jit_state.ExtReg;
284 }
285
Cpsr() const286 std::uint32_t Context::Cpsr() const {
287 return impl->jit_state.Cpsr();
288 }
SetCpsr(std::uint32_t value)289 void Context::SetCpsr(std::uint32_t value) {
290 impl->jit_state.SetCpsr(value);
291 }
292
Fpscr() const293 std::uint32_t Context::Fpscr() const {
294 return impl->jit_state.Fpscr();
295 }
SetFpscr(std::uint32_t value)296 void Context::SetFpscr(std::uint32_t value) {
297 return impl->jit_state.SetFpscr(value);
298 }
299
SaveContext(Context & ctx) const300 void Jit::SaveContext(Context& ctx) const {
301 ctx.impl->jit_state.TransferJitState(impl->jit_state, false);
302 ctx.impl->invalid_cache_generation = impl->invalid_cache_generation;
303 }
304
LoadContext(const Context & ctx)305 void Jit::LoadContext(const Context& ctx) {
306 bool reset_rsb = ctx.impl->invalid_cache_generation != impl->invalid_cache_generation;
307 impl->jit_state.TransferJitState(ctx.impl->jit_state, reset_rsb);
308 }
309
Disassemble() const310 std::string Jit::Disassemble() const {
311 std::string result;
312 #ifdef DYNARMIC_USE_LLVM
313 for (const u32* pos = reinterpret_cast<const u32*>(impl->block_of_code.GetCodeBegin());
314 reinterpret_cast<const u8*>(pos) < reinterpret_cast<const u8*>(impl->block_of_code.GetCodePtr()); pos += 1) {
315 fmt::print("0x{:02x} 0x{:02x} ", reinterpret_cast<u64>(pos), *pos);
316 fmt::print("{}", Common::DisassembleAArch64(*pos, reinterpret_cast<u64>(pos)));
317 result += Common::DisassembleAArch64(*pos, reinterpret_cast<u64>(pos));
318 }
319 #endif
320 return result;
321 }
322
323 } // namespace Dynarmic::A32
324