CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/JitCommon/JitCommon.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
#include <cstdlib>
20
#include <mutex>
21
22
#include "ext/disarm.h"
23
#include "ext/riscv-disas.h"
24
#include "ext/udis86/udis86.h"
25
26
#include "Common/LogReporting.h"
27
#include "Common/StringUtils.h"
28
#include "Common/Serialize/Serializer.h"
29
#include "Common/Serialize/SerializeFuncs.h"
30
31
#include "Core/Util/DisArm64.h"
32
#include "Core/Config.h"
33
34
#include "Core/MIPS/IR/IRJit.h"
35
#include "Core/MIPS/JitCommon/JitCommon.h"
36
#include "Core/MIPS/JitCommon/JitState.h"
37
#include "Core/MIPS/MIPSCodeUtils.h"
38
#include "Core/MIPS/MIPSTables.h"
39
40
#if PPSSPP_ARCH(ARM)
41
#include "../ARM/ArmJit.h"
42
#elif PPSSPP_ARCH(ARM64)
43
#include "../ARM64/Arm64Jit.h"
44
#include "../ARM64/Arm64IRJit.h"
45
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
46
#include "../x86/Jit.h"
47
#include "../x86/X64IRJit.h"
48
#elif PPSSPP_ARCH(MIPS)
49
#include "../MIPS/MipsJit.h"
50
#elif PPSSPP_ARCH(RISCV64)
51
#include "../RiscV/RiscVJit.h"
52
#else
53
#include "../fake/FakeJit.h"
54
#endif
55
56
namespace MIPSComp {
57
JitInterface *jit;
58
std::recursive_mutex jitLock;
59
60
void JitAt() {
61
// TODO: We could probably check for a bad pc here, and fire an exception. Could spare us from some crashes.
62
// Although, we just tried to load from this address to check for a JIT block, and if we're here, that succeeded..
63
jit->Compile(currentMIPS->pc);
64
}
65
66
void DoDummyJitState(PointerWrap &p) {
67
// This is here so the savestate matches between jit and non-jit.
68
auto s = p.Section("Jit", 1, 2);
69
if (!s)
70
return;
71
72
bool dummy = false;
73
Do(p, dummy);
74
if (s >= 2) {
75
dummy = true;
76
Do(p, dummy);
77
}
78
}
79
80
BranchInfo::BranchInfo(u32 pc, MIPSOpcode o, MIPSOpcode delayO, bool al, bool l)
81
: compilerPC(pc), op(o), delaySlotOp(delayO), likely(l), andLink(al) {
82
delaySlotInfo = MIPSGetInfo(delaySlotOp).value;
83
delaySlotIsBranch = (delaySlotInfo & (IS_JUMP | IS_CONDBRANCH)) != 0;
84
}
85
86
u32 ResolveNotTakenTarget(const BranchInfo &branchInfo) {
87
u32 notTakenTarget = branchInfo.compilerPC + 8;
88
if ((branchInfo.delaySlotInfo & (IS_JUMP | IS_CONDBRANCH)) != 0) {
89
// If a branch has a j/jr/jal/jalr as a delay slot, that is run if the branch is not taken.
90
// TODO: Technically, in the likely case, we should somehow suppress andLink on this exit.
91
bool isJump = (branchInfo.delaySlotInfo & IS_JUMP) != 0;
92
// If the delay slot is a branch, likely skips it.
93
if (isJump || !branchInfo.likely)
94
notTakenTarget -= 4;
95
96
// For a branch (not a jump), it actually should try the delay slot and take its target potentially.
97
// This is similar to the VFPU case and has not been seen, so just report it.
98
if (!isJump && SignExtend16ToU32(branchInfo.delaySlotOp) != SignExtend16ToU32(branchInfo.op) - 1)
99
ERROR_LOG_REPORT(Log::JIT, "Branch in branch delay slot at %08x with different target", branchInfo.compilerPC);
100
if (isJump && branchInfo.likely && (branchInfo.delaySlotInfo & (OUT_RA | OUT_RD)) != 0)
101
ERROR_LOG_REPORT(Log::JIT, "Jump in likely branch delay slot with link at %08x", branchInfo.compilerPC);
102
}
103
return notTakenTarget;
104
}
105
106
JitInterface *CreateNativeJit(MIPSState *mipsState, bool useIR) {
107
#if PPSSPP_ARCH(ARM)
108
return new MIPSComp::ArmJit(mipsState);
109
#elif PPSSPP_ARCH(ARM64)
110
if (useIR)
111
return new MIPSComp::Arm64IRJit(mipsState);
112
return new MIPSComp::Arm64Jit(mipsState);
113
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
114
if (useIR)
115
return new MIPSComp::X64IRJit(mipsState);
116
return new MIPSComp::Jit(mipsState);
117
#elif PPSSPP_ARCH(MIPS)
118
return new MIPSComp::MipsJit(mipsState);
119
#elif PPSSPP_ARCH(RISCV64)
120
return new MIPSComp::RiscVJit(mipsState);
121
#else
122
return new MIPSComp::FakeJit(mipsState);
123
#endif
124
}
125
126
}
127
#if PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__)
128
#define DISASM_ALL 1
129
#endif
130
131
#if PPSSPP_ARCH(ARM) || defined(DISASM_ALL)
132
// We compile this for x86 as well because it may be useful when developing the ARM JIT on a PC.
133
std::vector<std::string> DisassembleArm2(const u8 *data, int size) {
134
std::vector<std::string> lines;
135
136
char temp[256];
137
int bkpt_count = 0;
138
lines.reserve(size / 4);
139
for (int i = 0; i < size; i += 4) {
140
const u32 *codePtr = (const u32 *)(data + i);
141
u32 inst = codePtr[0];
142
u32 next = (i < size - 4) ? codePtr[1] : 0;
143
// MAGIC SPECIAL CASE for MOVW/MOVT readability!
144
if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) {
145
u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF);
146
u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF);
147
int reg0 = (inst & 0x0000F000) >> 12;
148
int reg1 = (next & 0x0000F000) >> 12;
149
if (reg0 == reg1) {
150
snprintf(temp, sizeof(temp), "MOV32 %s, %04x%04x", ArmRegName(reg0), hi, low);
151
lines.push_back(temp);
152
i += 4;
153
continue;
154
}
155
}
156
ArmDis((u32)(intptr_t)codePtr, inst, temp, sizeof(temp), false);
157
std::string buf = temp;
158
if (buf == "BKPT 1") {
159
bkpt_count++;
160
} else {
161
if (bkpt_count) {
162
lines.push_back(StringFromFormat("BKPT 1 (x%d)", bkpt_count));
163
bkpt_count = 0;
164
}
165
lines.push_back(buf);
166
}
167
}
168
if (bkpt_count) {
169
lines.push_back(StringFromFormat("BKPT 1 (x%d)", bkpt_count));
170
}
171
return lines;
172
}
173
#endif
174
175
std::string AddAddress(const std::string &buf, uint64_t addr) {
176
char buf2[16];
177
snprintf(buf2, sizeof(buf2), "%04x%08x", (uint32_t)(addr >> 32), (uint32_t)(addr & 0xFFFFFFFF));
178
return std::string(buf2) + " " + buf;
179
}
180
181
#if PPSSPP_ARCH(ARM64) || defined(DISASM_ALL)
182
183
static bool Arm64SymbolCallback(char *buffer, int bufsize, uint8_t *address) {
184
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
185
if (MIPSComp::jit) {
186
std::string name;
187
if (MIPSComp::jit->DescribeCodePtr(address, name)) {
188
truncate_cpy(buffer, bufsize, name.c_str());
189
return true;
190
}
191
}
192
return false;
193
}
194
195
std::vector<std::string> DisassembleArm64(const u8 *data, int size) {
196
std::vector<std::string> lines;
197
198
char temp[256];
199
int bkpt_count = 0;
200
lines.reserve(size / 4);
201
for (int i = 0; i < size; i += 4) {
202
const u32 *codePtr = (const u32 *)(data + i);
203
uint64_t addr = (intptr_t)codePtr;
204
u32 inst = codePtr[0];
205
u32 next = (i < size - 4) ? codePtr[1] : 0;
206
// MAGIC SPECIAL CASE for MOVZ+MOVK readability!
207
if (((inst >> 21) & 0x3FF) == 0x294 && ((next >> 21) & 0x3FF) == 0x395) {
208
u32 low = (inst >> 5) & 0xFFFF;
209
u32 hi = (next >> 5) & 0xFFFF;
210
int reg0 = inst & 0x1F;
211
int reg1 = next & 0x1F;
212
char r = (inst >> 31) ? 'x' : 'w';
213
if (reg0 == reg1) {
214
snprintf(temp, sizeof(temp), "movi32 %c%d, %04x%04x", r, reg0, hi, low);
215
lines.push_back(AddAddress(temp, addr));
216
i += 4;
217
continue;
218
}
219
}
220
Arm64Dis((intptr_t)codePtr, inst, temp, sizeof(temp), false, Arm64SymbolCallback);
221
std::string buf = temp;
222
if (buf == "BKPT 1") {
223
bkpt_count++;
224
} else {
225
if (bkpt_count) {
226
lines.push_back(StringFromFormat("BKPT 1 (x%d)", bkpt_count));
227
bkpt_count = 0;
228
}
229
if (true) {
230
buf = AddAddress(buf, addr);
231
}
232
lines.push_back(buf);
233
}
234
}
235
if (bkpt_count) {
236
lines.push_back(StringFromFormat("BKPT 1 (x%d)", bkpt_count));
237
}
238
return lines;
239
}
240
#endif
241
242
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
243
244
const char *ppsspp_resolver(struct ud*,
245
uint64_t addr,
246
int64_t *offset) {
247
// For some reason these don't seem to trigger..
248
if (addr >= (uint64_t)(&currentMIPS->r[0]) && addr < (uint64_t)&currentMIPS->r[32]) {
249
*offset = addr - (uint64_t)(&currentMIPS->r[0]);
250
return "mips.r";
251
} else if (addr >= (uint64_t)(&currentMIPS->v[0]) && addr < (uint64_t)&currentMIPS->v[128]) {
252
*offset = addr - (uint64_t)(&currentMIPS->v[0]);
253
return "mips.v";
254
} else if (addr == (uint64_t)(&currentMIPS->downcount)) {
255
return "mips.downcount";
256
} else if (addr == (uint64_t)(&currentMIPS->fpcond)) {
257
return "mips.fpcond";
258
} else if (addr == (uint64_t)(&currentMIPS->temp)) {
259
return "mips.temp";
260
} else if (addr == (uint64_t)(&currentMIPS->pc)) {
261
return "mips.pc";
262
} else if (addr == (uint64_t)(&currentMIPS->hi)) {
263
return "mips.hi";
264
} else if (addr == (uint64_t)(&currentMIPS->lo)) {
265
return "mips.lo";
266
} else if (addr == (uint64_t)(&currentMIPS->fcr31)) {
267
return "mips.fcr31";
268
} else if (addr >= (uint64_t)(&currentMIPS->vfpuCtrl[0]) && addr < (uint64_t)(&currentMIPS->vfpuCtrl[16])) {
269
return "mips.vfpuCtrl";
270
}
271
272
// But these do.
273
274
// UGLY HACK because the API is terrible
275
static char buf[128];
276
std::string str;
277
278
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
279
if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr((u8 *)(uintptr_t)addr, str)) {
280
*offset = 0;
281
truncate_cpy(buf, sizeof(buf), str.c_str());
282
return buf;
283
}
284
return NULL;
285
}
286
287
std::vector<std::string> DisassembleX86(const u8 *data, int size) {
288
std::vector<std::string> lines;
289
ud_t ud_obj;
290
ud_init(&ud_obj);
291
ud_set_mode(&ud_obj, sizeof(void*) * 8);
292
ud_set_pc(&ud_obj, (intptr_t)data);
293
ud_set_vendor(&ud_obj, UD_VENDOR_ANY);
294
ud_set_syntax(&ud_obj, UD_SYN_INTEL);
295
ud_set_sym_resolver(&ud_obj, &ppsspp_resolver);
296
297
ud_set_input_buffer(&ud_obj, data, size);
298
299
int int3_count = 0;
300
while (ud_disassemble(&ud_obj) != 0) {
301
const char *buf = ud_insn_asm(&ud_obj);
302
if (!buf) {
303
lines.push_back("[bad]");
304
continue;
305
}
306
std::string str = buf;
307
if (str == "int3") {
308
int3_count++;
309
} else {
310
if (int3_count) {
311
lines.push_back(StringFromFormat("int3 (x%d)", int3_count));
312
int3_count = 0;
313
}
314
lines.push_back(str);
315
}
316
}
317
if (int3_count) {
318
lines.push_back(StringFromFormat("int3 (x%d)", int3_count));
319
}
320
return lines;
321
}
322
323
#endif
324
325
#if PPSSPP_ARCH(RISCV64) || defined(DISASM_ALL)
326
std::vector<std::string> DisassembleRV64(const u8 *data, int size) {
327
std::vector<std::string> lines;
328
329
int invalid_count = 0;
330
auto invalid_flush = [&]() {
331
if (invalid_count != 0) {
332
lines.push_back(StringFromFormat("(%d invalid bytes)", invalid_count));
333
invalid_count = 0;
334
}
335
};
336
337
char temp[512];
338
rv_inst inst;
339
size_t len;
340
for (int i = 0; i < size; ) {
341
riscv_inst_fetch(data + i, &inst, &len);
342
if (len == 0) {
343
// Force align in case we're somehow unaligned.
344
len = 2 - ((uintptr_t)data & 1);
345
invalid_count += (int)len;
346
i += (int)len;
347
continue;
348
}
349
350
invalid_flush();
351
riscv_disasm_inst(temp, sizeof(temp), rv64, (uintptr_t)data + i, inst);
352
lines.push_back(ReplaceAll(temp, "\t", " "));
353
354
i += (int)len;
355
}
356
357
invalid_flush();
358
return lines;
359
}
360
#endif
361
362