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/ARM64/Arm64Asm.cpp
Views: 1401
1
// Copyright (c) 2015- 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
#if PPSSPP_ARCH(ARM64)
21
22
#include "Common/Log.h"
23
#include "Common/MemoryUtil.h"
24
#include "Common/CPUDetect.h"
25
#include "Common/Arm64Emitter.h"
26
#include "Core/MemMap.h"
27
#include "Core/MIPS/MIPS.h"
28
#include "Core/System.h"
29
#include "Core/CoreTiming.h"
30
#include "Core/MIPS/ARM64/Arm64Jit.h"
31
#include "Core/MIPS/JitCommon/JitCommon.h"
32
33
using namespace Arm64Gen;
34
35
//static int temp32; // unused?
36
37
static const bool enableDebug = false;
38
static const bool enableDisasm = false;
39
40
//static bool enableStatistics = false; //unused?
41
42
43
// ARM64 calling conventions
44
// Standard: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
45
// Apple: https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
46
47
// Summary:
48
// ===========
49
// SP ("x31") is not a GPR so irrelevant.
50
// x0-x7: 8 parameter/result registers
51
// x8: "Indirect result location register" (points to struct return values? I think we can map this)
52
// x9-x15: 7 temporary registers (no need to save)
53
// x16: temporary register/procedure call scratch register 1
54
// x17: temporary register/procedure call scratch register 2
55
// x18: unavailable (reserved for use by the OS or linker or whatever - iOS, for example, uses it)
56
// x19-x28: 10 callee-saved registers
57
// x29: the frame pointer register
58
// x30: link register for procedure calls
59
60
// So: Scratch registers: x16, x17
61
// Mappable registers in priority order:
62
// x19, x20, x21, x22, x23, (x24, x25, x26, x27, x28), x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x0, x1,
63
// That's a whole lot of registers so we might be able to statically allocate a bunch of common MIPS registers.
64
// We should put statically allocated registers in the 7 callee-save regs that are left over after the system regs (x19-x25), so we don't have to bother with
65
// saving them when we call out of the JIT. We will perform regular dynamic register allocation in the rest (x0-x15)
66
67
// STATIC ALLOCATION ARM64 (these are all callee-save registers):
68
// x23 : Down counter
69
// x24 : PC save on JR with non-nice delay slot (to be eliminated later?)
70
// x25 : MSR/MRS temporary (to be eliminated later)
71
// x26 : JIT base reg
72
// x27 : MIPS state (Could eliminate by placing the MIPS state right at the memory base)
73
// x28 : Memory base pointer.
74
75
extern volatile CoreState coreState;
76
77
void ShowPC(u32 downcount, void *membase, void *jitbase) {
78
static int count = 0;
79
if (currentMIPS) {
80
ERROR_LOG(Log::JIT, "ShowPC : %08x Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);
81
} else {
82
ERROR_LOG(Log::JIT, "Universe corrupt?");
83
}
84
//if (count > 2000)
85
// exit(0);
86
count++;
87
}
88
89
void DisassembleArm(const u8 *data, int size);
90
91
// PLAN: no more block numbers - crazy opcodes just contain offset within
92
// dynarec buffer
93
// At this offset - 4, there is an int specifying the block number.
94
95
namespace MIPSComp {
96
97
using namespace Arm64JitConstants;
98
99
void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
100
BeginWrite(GetMemoryProtectPageSize());
101
const u8 *start = AlignCodePage();
102
103
if (jo.useStaticAlloc) {
104
saveStaticRegisters = AlignCode16();
105
STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
106
gpr.EmitSaveStaticRegisters();
107
RET();
108
109
loadStaticRegisters = AlignCode16();
110
gpr.EmitLoadStaticRegisters();
111
LDR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
112
RET();
113
114
start = saveStaticRegisters;
115
} else {
116
saveStaticRegisters = nullptr;
117
loadStaticRegisters = nullptr;
118
}
119
120
restoreRoundingMode = AlignCode16(); {
121
MRS(SCRATCH2_64, FIELD_FPCR);
122
// We are not in flush-to-zero mode outside the JIT, so let's turn it off.
123
uint32_t mask = ~(4 << 22);
124
// Assume we're always in round-to-nearest mode beforehand.
125
mask &= ~(3 << 22);
126
ANDI2R(SCRATCH2, SCRATCH2, mask);
127
_MSR(FIELD_FPCR, SCRATCH2_64);
128
RET();
129
}
130
131
applyRoundingMode = AlignCode16(); {
132
LDR(INDEX_UNSIGNED, SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
133
TSTI2R(SCRATCH2, 1 << 24);
134
ANDI2R(SCRATCH2, SCRATCH2, 3);
135
FixupBranch skip1 = B(CC_EQ);
136
ADDI2R(SCRATCH2, SCRATCH2, 4);
137
SetJumpTarget(skip1);
138
139
// We can skip if the rounding mode is nearest (0) and flush is not set.
140
// (as restoreRoundingMode cleared it out anyway)
141
CMPI2R(SCRATCH2, 0);
142
FixupBranch skip = B(CC_EQ);
143
144
// MIPS Rounding Mode: ARM Rounding Mode
145
// 0: Round nearest 0
146
// 1: Round to zero 3
147
// 2: Round up (ceil) 1
148
// 3: Round down (floor) 2
149
ANDI2R(SCRATCH1, SCRATCH2, 3);
150
CMPI2R(SCRATCH1, 1);
151
152
FixupBranch skipadd = B(CC_NEQ);
153
ADDI2R(SCRATCH2, SCRATCH2, 2);
154
SetJumpTarget(skipadd);
155
FixupBranch skipsub = B(CC_LE);
156
SUBI2R(SCRATCH2, SCRATCH2, 1);
157
SetJumpTarget(skipsub);
158
159
// Actually change the system FPCR register
160
MRS(SCRATCH1_64, FIELD_FPCR);
161
// Clear both flush-to-zero and rounding before re-setting them.
162
ANDI2R(SCRATCH1, SCRATCH1, ~((4 | 3) << 22));
163
ORR(SCRATCH1, SCRATCH1, SCRATCH2, ArithOption(SCRATCH2, ST_LSL, 22));
164
_MSR(FIELD_FPCR, SCRATCH1_64);
165
166
SetJumpTarget(skip);
167
RET();
168
}
169
170
updateRoundingMode = AlignCode16(); {
171
LDR(INDEX_UNSIGNED, SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
172
173
// Set SCRATCH2 to FZ:RM (FZ is bit 24, and RM are lowest 2 bits.)
174
TSTI2R(SCRATCH2, 1 << 24);
175
ANDI2R(SCRATCH2, SCRATCH2, 3);
176
FixupBranch skip = B(CC_EQ);
177
ADDI2R(SCRATCH2, SCRATCH2, 4);
178
SetJumpTarget(skip);
179
180
// Let's update js.currentRoundingFunc with the right convertS0ToSCRATCH1 func.
181
MOVP2R(SCRATCH1_64, convertS0ToSCRATCH1);
182
LSL(SCRATCH2, SCRATCH2, 3);
183
LDR(SCRATCH2_64, SCRATCH1_64, SCRATCH2);
184
MOVP2R(SCRATCH1_64, &js.currentRoundingFunc);
185
STR(INDEX_UNSIGNED, SCRATCH2_64, SCRATCH1_64, 0);
186
RET();
187
}
188
189
enterDispatcher = AlignCode16();
190
191
uint32_t regs_to_save = Arm64Gen::ALL_CALLEE_SAVED;
192
uint32_t regs_to_save_fp = Arm64Gen::ALL_CALLEE_SAVED_FP;
193
fp.ABI_PushRegisters(regs_to_save, regs_to_save_fp);
194
195
// Fixed registers, these are always kept when in Jit context.
196
MOVP2R(MEMBASEREG, Memory::base);
197
MOVP2R(CTXREG, mips_);
198
MOVP2R(JITBASEREG, GetBasePtr());
199
200
LoadStaticRegisters();
201
MovFromPC(SCRATCH1);
202
outerLoopPCInSCRATCH1 = GetCodePtr();
203
MovToPC(SCRATCH1);
204
outerLoop = GetCodePtr();
205
SaveStaticRegisters(); // Advance can change the downcount, so must save/restore
206
RestoreRoundingMode(true);
207
QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance);
208
ApplyRoundingMode(true);
209
LoadStaticRegisters();
210
FixupBranch skipToCoreStateCheck = B(); //skip the downcount check
211
212
dispatcherCheckCoreState = GetCodePtr();
213
214
// The result of slice decrementation should be in flags if somebody jumped here
215
// IMPORTANT - We jump on negative, not carry!!!
216
FixupBranch bailCoreState = B(CC_MI);
217
218
SetJumpTarget(skipToCoreStateCheck);
219
220
MOVP2R(SCRATCH1_64, &coreState);
221
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
222
CMP(SCRATCH1, 0);
223
FixupBranch badCoreState = B(CC_NEQ);
224
FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time
225
226
dispatcherPCInSCRATCH1 = GetCodePtr();
227
// TODO: Do we always need to write PC to RAM here?
228
MovToPC(SCRATCH1);
229
230
// At this point : flags = EQ. Fine for the next check, no need to jump over it.
231
dispatcher = GetCodePtr();
232
233
// The result of slice decrementation should be in flags if somebody jumped here
234
// IMPORTANT - We jump on negative, not carry!!!
235
FixupBranch bail = B(CC_MI);
236
237
SetJumpTarget(skipToRealDispatch2);
238
239
dispatcherNoCheck = GetCodePtr();
240
241
// Debug
242
if (enableDebug) {
243
MOV(W0, DOWNCOUNTREG);
244
MOV(X1, MEMBASEREG);
245
MOV(X2, JITBASEREG);
246
QuickCallFunction(SCRATCH1_64, (void *)&ShowPC);
247
}
248
249
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc));
250
#ifdef MASKED_PSP_MEMORY
251
ANDI2R(SCRATCH1, SCRATCH1, 0x3FFFFFFF);
252
#endif
253
dispatcherFetch = GetCodePtr();
254
LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64);
255
LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8)
256
ANDI2R(SCRATCH1, SCRATCH1, 0x00FFFFFF);
257
CMP(SCRATCH2, MIPS_EMUHACK_OPCODE >> 24);
258
FixupBranch skipJump = B(CC_NEQ);
259
ADD(SCRATCH1_64, JITBASEREG, SCRATCH1_64);
260
BR(SCRATCH1_64);
261
SetJumpTarget(skipJump);
262
263
// No block found, let's jit. I don't think we actually need to save static regs that are in callee-save regs here but whatever.
264
// Also, rounding mode gotta be irrelevant here..
265
SaveStaticRegisters();
266
RestoreRoundingMode(true);
267
QuickCallFunction(SCRATCH1_64, (void *)&MIPSComp::JitAt);
268
ApplyRoundingMode(true);
269
LoadStaticRegisters();
270
271
B(dispatcherNoCheck); // no point in special casing this
272
273
SetJumpTarget(bail);
274
SetJumpTarget(bailCoreState);
275
276
MOVP2R(SCRATCH1_64, &coreState);
277
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
278
CMP(SCRATCH1, 0);
279
B(CC_EQ, outerLoop);
280
281
const uint8_t *quitLoop = GetCodePtr();
282
SetJumpTarget(badCoreState);
283
284
SaveStaticRegisters();
285
RestoreRoundingMode(true);
286
287
fp.ABI_PopRegisters(regs_to_save, regs_to_save_fp);
288
289
RET();
290
291
crashHandler = GetCodePtr();
292
MOVP2R(SCRATCH1_64, &coreState);
293
MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR);
294
STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);
295
B(quitLoop);
296
297
// Generate some integer conversion funcs.
298
// MIPS order!
299
static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M };
300
for (size_t i = 0; i < ARRAY_SIZE(roundModes); ++i) {
301
convertS0ToSCRATCH1[i] = AlignCode16();
302
303
fp.FCMP(S0, S0); // Detect NaN
304
fp.FCVTS(S0, S0, roundModes[i]);
305
FixupBranch skip = B(CC_VC);
306
MOVI2R(SCRATCH2, 0x7FFFFFFF);
307
fp.FMOV(S0, SCRATCH2);
308
SetJumpTarget(skip);
309
310
RET();
311
}
312
313
// Leave this at the end, add more stuff above.
314
if (enableDisasm) {
315
std::vector<std::string> lines = DisassembleArm64(start, (int)(GetCodePtr() - start));
316
for (auto s : lines) {
317
INFO_LOG(Log::JIT, "%s", s.c_str());
318
}
319
}
320
321
// Let's spare the pre-generated code from unprotect-reprotect.
322
AlignCodePage();
323
jitStartOffset = (int)(GetCodePtr() - start);
324
// Don't forget to zap the instruction cache! This must stay at the end of this function.
325
FlushIcache();
326
EndWrite();
327
}
328
329
} // namespace MIPSComp
330
331
#endif // PPSSPP_ARCH(ARM64)
332
333