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