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