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