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