1 // Copyright 2013 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "Core/PowerPC/JitInterface.h"
6
7 #include <algorithm>
8 #include <cinttypes>
9 #include <cstdio>
10 #include <string>
11 #include <unordered_set>
12
13 #ifdef _WIN32
14 #include <windows.h>
15 #else
16 #include "Common/PerformanceCounter.h"
17 #endif
18
19 #include "Common/ChunkFile.h"
20 #include "Common/CommonTypes.h"
21 #include "Common/File.h"
22 #include "Common/MsgHandler.h"
23
24 #include "Core/Core.h"
25 #include "Core/PowerPC/CPUCoreBase.h"
26 #include "Core/PowerPC/CachedInterpreter/CachedInterpreter.h"
27 #include "Core/PowerPC/JitCommon/JitBase.h"
28 #include "Core/PowerPC/MMU.h"
29 #include "Core/PowerPC/PPCSymbolDB.h"
30 #include "Core/PowerPC/PowerPC.h"
31 #include "Core/PowerPC/Profiler.h"
32
33 #if _M_X86
34 #include "Core/PowerPC/Jit64/Jit.h"
35 #endif
36
37 #if _M_ARM_64
38 #include "Core/PowerPC/JitArm64/Jit.h"
39 #endif
40
41 namespace JitInterface
42 {
43 static JitBase* g_jit = nullptr;
SetJit(JitBase * jit)44 void SetJit(JitBase* jit)
45 {
46 g_jit = jit;
47 }
DoState(PointerWrap & p)48 void DoState(PointerWrap& p)
49 {
50 if (g_jit && p.GetMode() == PointerWrap::MODE_READ)
51 g_jit->ClearCache();
52 }
InitJitCore(PowerPC::CPUCore core)53 CPUCoreBase* InitJitCore(PowerPC::CPUCore core)
54 {
55 switch (core)
56 {
57 #if _M_X86
58 case PowerPC::CPUCore::JIT64:
59 g_jit = new Jit64();
60 break;
61 #endif
62 #if _M_ARM_64
63 case PowerPC::CPUCore::JITARM64:
64 g_jit = new JitArm64();
65 break;
66 #endif
67 case PowerPC::CPUCore::CachedInterpreter:
68 g_jit = new CachedInterpreter();
69 break;
70
71 default:
72 PanicAlertT("The selected CPU emulation core (%d) is not available. "
73 "Please select a different CPU emulation core in the settings.",
74 static_cast<int>(core));
75 g_jit = nullptr;
76 return nullptr;
77 }
78 g_jit->Init();
79 return g_jit;
80 }
81
GetCore()82 CPUCoreBase* GetCore()
83 {
84 return g_jit;
85 }
86
SetProfilingState(ProfilingState state)87 void SetProfilingState(ProfilingState state)
88 {
89 if (!g_jit)
90 return;
91
92 g_jit->jo.profile_blocks = state == ProfilingState::Enabled;
93 }
94
WriteProfileResults(const std::string & filename)95 void WriteProfileResults(const std::string& filename)
96 {
97 Profiler::ProfileStats prof_stats;
98 GetProfileResults(&prof_stats);
99
100 File::IOFile f(filename, "w");
101 if (!f)
102 {
103 PanicAlert("Failed to open %s", filename.c_str());
104 return;
105 }
106 fprintf(f.GetHandle(), "origAddr\tblkName\trunCount\tcost\ttimeCost\tpercent\ttimePercent\tOvAlli"
107 "nBlkTime(ms)\tblkCodeSize\n");
108 for (auto& stat : prof_stats.block_stats)
109 {
110 std::string name = g_symbolDB.GetDescription(stat.addr);
111 double percent = 100.0 * (double)stat.cost / (double)prof_stats.cost_sum;
112 double timePercent = 100.0 * (double)stat.tick_counter / (double)prof_stats.timecost_sum;
113 fprintf(f.GetHandle(),
114 "%08x\t%s\t%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 "\t%.2f\t%.2f\t%.2f\t%i\n", stat.addr,
115 name.c_str(), stat.run_count, stat.cost, stat.tick_counter, percent, timePercent,
116 (double)stat.tick_counter * 1000.0 / (double)prof_stats.countsPerSec, stat.block_size);
117 }
118 }
119
GetProfileResults(Profiler::ProfileStats * prof_stats)120 void GetProfileResults(Profiler::ProfileStats* prof_stats)
121 {
122 // Can't really do this with no g_jit core available
123 if (!g_jit)
124 return;
125
126 prof_stats->cost_sum = 0;
127 prof_stats->timecost_sum = 0;
128 prof_stats->block_stats.clear();
129
130 Core::State old_state = Core::GetState();
131 if (old_state == Core::State::Running)
132 Core::SetState(Core::State::Paused);
133
134 QueryPerformanceFrequency((LARGE_INTEGER*)&prof_stats->countsPerSec);
135 g_jit->GetBlockCache()->RunOnBlocks([&prof_stats](const JitBlock& block) {
136 const auto& data = block.profile_data;
137 u64 cost = data.downcountCounter;
138 u64 timecost = data.ticCounter;
139 // Todo: tweak.
140 if (data.runCount >= 1)
141 prof_stats->block_stats.emplace_back(block.effectiveAddress, cost, timecost, data.runCount,
142 block.codeSize);
143 prof_stats->cost_sum += cost;
144 prof_stats->timecost_sum += timecost;
145 });
146
147 sort(prof_stats->block_stats.begin(), prof_stats->block_stats.end());
148 if (old_state == Core::State::Running)
149 Core::SetState(Core::State::Running);
150 }
151
GetHostCode(u32 * address,const u8 ** code,u32 * code_size)152 int GetHostCode(u32* address, const u8** code, u32* code_size)
153 {
154 if (!g_jit)
155 {
156 *code_size = 0;
157 return 1;
158 }
159
160 JitBlock* block = g_jit->GetBlockCache()->GetBlockFromStartAddress(*address, MSR.Hex);
161 if (!block)
162 {
163 for (int i = 0; i < 500; i++)
164 {
165 block = g_jit->GetBlockCache()->GetBlockFromStartAddress(*address - 4 * i, MSR.Hex);
166 if (block)
167 break;
168 }
169
170 if (block)
171 {
172 if (!(block->effectiveAddress <= *address &&
173 block->originalSize + block->effectiveAddress >= *address))
174 block = nullptr;
175 }
176
177 // Do not merge this "if" with the above - block_num changes inside it.
178 if (!block)
179 {
180 *code_size = 0;
181 return 2;
182 }
183 }
184
185 *code = block->checkedEntry;
186 *code_size = block->codeSize;
187 *address = block->effectiveAddress;
188 return 0;
189 }
190
HandleFault(uintptr_t access_address,SContext * ctx)191 bool HandleFault(uintptr_t access_address, SContext* ctx)
192 {
193 // Prevent nullptr dereference on a crash with no JIT present
194 if (!g_jit)
195 {
196 return false;
197 }
198
199 return g_jit->HandleFault(access_address, ctx);
200 }
201
HandleStackFault()202 bool HandleStackFault()
203 {
204 if (!g_jit)
205 {
206 return false;
207 }
208
209 return g_jit->HandleStackFault();
210 }
211
ClearCache()212 void ClearCache()
213 {
214 if (g_jit)
215 g_jit->ClearCache();
216 }
ClearSafe()217 void ClearSafe()
218 {
219 if (g_jit)
220 g_jit->GetBlockCache()->Clear();
221 }
222
InvalidateICache(u32 address,u32 size,bool forced)223 void InvalidateICache(u32 address, u32 size, bool forced)
224 {
225 if (g_jit)
226 g_jit->GetBlockCache()->InvalidateICache(address, size, forced);
227 }
228
CompileExceptionCheck(ExceptionType type)229 void CompileExceptionCheck(ExceptionType type)
230 {
231 if (!g_jit)
232 return;
233
234 std::unordered_set<u32>* exception_addresses = nullptr;
235
236 switch (type)
237 {
238 case ExceptionType::FIFOWrite:
239 exception_addresses = &g_jit->js.fifoWriteAddresses;
240 break;
241 case ExceptionType::PairedQuantize:
242 exception_addresses = &g_jit->js.pairedQuantizeAddresses;
243 break;
244 case ExceptionType::SpeculativeConstants:
245 exception_addresses = &g_jit->js.noSpeculativeConstantsAddresses;
246 break;
247 }
248
249 if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end()))
250 {
251 if (type == ExceptionType::FIFOWrite)
252 {
253 // Check in case the code has been replaced since: do we need to do this?
254 const OpType optype = PPCTables::GetOpInfo(PowerPC::HostRead_U32(PC))->type;
255 if (optype != OpType::Store && optype != OpType::StoreFP && optype != OpType::StorePS)
256 return;
257 }
258 exception_addresses->insert(PC);
259
260 // Invalidate the JIT block so that it gets recompiled with the external exception check
261 // included.
262 g_jit->GetBlockCache()->InvalidateICache(PC, 4, true);
263 }
264 }
265
Shutdown()266 void Shutdown()
267 {
268 if (g_jit)
269 {
270 g_jit->Shutdown();
271 delete g_jit;
272 g_jit = nullptr;
273 }
274 }
275 } // namespace JitInterface
276