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/Arm64IRAsm.cpp
Views: 1401
1
// Copyright (c) 2023- 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
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
20
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
21
22
#include "Common/Log.h"
23
#include "Core/CoreTiming.h"
24
#include "Core/MemMap.h"
25
#include "Core/MIPS/ARM64/Arm64IRJit.h"
26
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
27
#include "Core/MIPS/JitCommon/JitCommon.h"
28
#include "Core/MIPS/JitCommon/JitState.h"
29
#include "Core/System.h"
30
31
namespace MIPSComp {
32
33
using namespace Arm64Gen;
34
using namespace Arm64IRJitConstants;
35
36
static const bool enableDebug = false;
37
static const bool enableDisasm = false;
38
39
static void ShowPC(void *membase, void *jitbase) {
40
static int count = 0;
41
if (currentMIPS) {
42
u32 downcount = currentMIPS->downcount;
43
ERROR_LOG(Log::JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);
44
} else {
45
ERROR_LOG(Log::JIT, "Universe corrupt?");
46
}
47
//if (count > 2000)
48
// exit(0);
49
count++;
50
}
51
52
void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
53
// This will be used as a writable scratch area, always 32-bit accessible.
54
const u8 *start = AlignCodePage();
55
if (DebugProfilerEnabled()) {
56
ProtectMemoryPages(start, GetMemoryProtectPageSize(), MEM_PROT_READ | MEM_PROT_WRITE);
57
hooks_.profilerPC = (uint32_t *)GetWritableCodePtr();
58
Write32(0);
59
hooks_.profilerStatus = (IRProfilerStatus *)GetWritableCodePtr();
60
Write32(0);
61
}
62
63
const u8 *disasmStart = AlignCodePage();
64
BeginWrite(GetMemoryProtectPageSize());
65
66
if (jo.useStaticAlloc) {
67
saveStaticRegisters_ = AlignCode16();
68
STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
69
regs_.EmitSaveStaticRegisters();
70
RET();
71
72
loadStaticRegisters_ = AlignCode16();
73
regs_.EmitLoadStaticRegisters();
74
LDR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
75
RET();
76
} else {
77
saveStaticRegisters_ = nullptr;
78
loadStaticRegisters_ = nullptr;
79
}
80
81
restoreRoundingMode_ = AlignCode16();
82
{
83
MRS(SCRATCH2_64, FIELD_FPCR);
84
// We are not in flush-to-zero mode outside the JIT, so let's turn it off.
85
uint32_t mask = ~(4 << 22);
86
// Assume we're always in round-to-nearest mode beforehand.
87
mask &= ~(3 << 22);
88
ANDI2R(SCRATCH2, SCRATCH2, mask);
89
_MSR(FIELD_FPCR, SCRATCH2_64);
90
RET();
91
}
92
93
applyRoundingMode_ = AlignCode16();
94
{
95
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, fcr31));
96
ANDI2R(SCRATCH2, SCRATCH1, 3);
97
FixupBranch skip1 = TBZ(SCRATCH1, 24);
98
ADDI2R(SCRATCH2, SCRATCH2, 4);
99
SetJumpTarget(skip1);
100
101
// We can skip if the rounding mode is nearest (0) and flush is not set.
102
// (as restoreRoundingMode cleared it out anyway)
103
FixupBranch skip = CBZ(SCRATCH2);
104
105
// MIPS Rounding Mode: ARM Rounding Mode
106
// 0: Round nearest 0
107
// 1: Round to zero 3
108
// 2: Round up (ceil) 1
109
// 3: Round down (floor) 2
110
ANDI2R(SCRATCH1, SCRATCH2, 3);
111
CMPI2R(SCRATCH1, 1);
112
113
FixupBranch skipadd = B(CC_NEQ);
114
ADDI2R(SCRATCH2, SCRATCH2, 2);
115
SetJumpTarget(skipadd);
116
FixupBranch skipsub = B(CC_LE);
117
SUBI2R(SCRATCH2, SCRATCH2, 1);
118
SetJumpTarget(skipsub);
119
120
// Actually change the system FPCR register
121
MRS(SCRATCH1_64, FIELD_FPCR);
122
// Clear both flush-to-zero and rounding before re-setting them.
123
ANDI2R(SCRATCH1, SCRATCH1, ~((4 | 3) << 22));
124
ORR(SCRATCH1, SCRATCH1, SCRATCH2, ArithOption(SCRATCH2, ST_LSL, 22));
125
_MSR(FIELD_FPCR, SCRATCH1_64);
126
127
SetJumpTarget(skip);
128
RET();
129
}
130
131
updateRoundingMode_ = AlignCode16();
132
{
133
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, fcr31));
134
135
// Set SCRATCH2 to FZ:RM (FZ is bit 24, and RM are lowest 2 bits.)
136
ANDI2R(SCRATCH2, SCRATCH1, 3);
137
FixupBranch skip = TBZ(SCRATCH1, 24);
138
ADDI2R(SCRATCH2, SCRATCH2, 4);
139
SetJumpTarget(skip);
140
141
// Update currentRoundingFunc_ with the right convertS0ToSCRATCH1_ func.
142
MOVP2R(SCRATCH1_64, convertS0ToSCRATCH1_);
143
LSL(SCRATCH2, SCRATCH2, 3);
144
LDR(SCRATCH2_64, SCRATCH1_64, SCRATCH2);
145
MOVP2R(SCRATCH1_64, &currentRoundingFunc_);
146
STR(INDEX_UNSIGNED, SCRATCH2_64, SCRATCH1_64, 0);
147
RET();
148
}
149
150
hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();
151
152
uint32_t regs_to_save = Arm64Gen::ALL_CALLEE_SAVED;
153
uint32_t regs_to_save_fp = Arm64Gen::ALL_CALLEE_SAVED_FP;
154
fp_.ABI_PushRegisters(regs_to_save, regs_to_save_fp);
155
156
// Fixed registers, these are always kept when in Jit context.
157
MOVP2R(MEMBASEREG, Memory::base);
158
MOVP2R(CTXREG, mipsState);
159
// Pre-subtract this to save time later.
160
MOVI2R(JITBASEREG, (intptr_t)GetBasePtr() - MIPS_EMUHACK_OPCODE);
161
162
LoadStaticRegisters();
163
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
164
MovFromPC(SCRATCH1);
165
WriteDebugPC(SCRATCH1);
166
outerLoopPCInSCRATCH1_ = GetCodePtr();
167
MovToPC(SCRATCH1);
168
outerLoop_ = GetCodePtr();
169
SaveStaticRegisters(); // Advance can change the downcount, so must save/restore
170
RestoreRoundingMode(true);
171
WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE);
172
QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance);
173
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
174
ApplyRoundingMode(true);
175
LoadStaticRegisters();
176
177
dispatcherCheckCoreState_ = GetCodePtr();
178
179
MOVP2R(SCRATCH1_64, &coreState);
180
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
181
FixupBranch badCoreState = CBNZ(SCRATCH1);
182
183
// Check downcount.
184
TBNZ(DOWNCOUNTREG, 31, outerLoop_);
185
FixupBranch skipToRealDispatch = B();
186
187
dispatcherPCInSCRATCH1_ = GetCodePtr();
188
MovToPC(SCRATCH1);
189
190
hooks_.dispatcher = GetCodePtr();
191
192
FixupBranch bail = TBNZ(DOWNCOUNTREG, 31);
193
SetJumpTarget(skipToRealDispatch);
194
195
dispatcherNoCheck_ = GetCodePtr();
196
197
// Debug
198
if (enableDebug) {
199
MOV(W0, DOWNCOUNTREG);
200
MOV(X1, MEMBASEREG);
201
MOV(X2, JITBASEREG);
202
QuickCallFunction(SCRATCH1_64, &ShowPC);
203
}
204
205
MovFromPC(SCRATCH1);
206
WriteDebugPC(SCRATCH1);
207
#ifdef MASKED_PSP_MEMORY
208
ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK);
209
#endif
210
hooks_.dispatchFetch = GetCodePtr();
211
LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64);
212
LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8)
213
// We don't mask SCRATCH1 as that's already baked into JITBASEREG.
214
CMP(SCRATCH2, MIPS_EMUHACK_OPCODE >> 24);
215
FixupBranch skipJump = B(CC_NEQ);
216
ADD(SCRATCH1_64, JITBASEREG, SCRATCH1_64);
217
BR(SCRATCH1_64);
218
SetJumpTarget(skipJump);
219
220
// No block found, let's jit. We don't need to save static regs, they're all callee saved.
221
RestoreRoundingMode(true);
222
WriteDebugProfilerStatus(IRProfilerStatus::COMPILING);
223
QuickCallFunction(SCRATCH1_64, &MIPSComp::JitAt);
224
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
225
ApplyRoundingMode(true);
226
227
// Let's just dispatch again, we'll enter the block since we know it's there.
228
B(dispatcherNoCheck_);
229
230
SetJumpTarget(bail);
231
232
MOVP2R(SCRATCH1_64, &coreState);
233
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
234
CBZ(SCRATCH1, outerLoop_);
235
236
const uint8_t *quitLoop = GetCodePtr();
237
SetJumpTarget(badCoreState);
238
239
WriteDebugProfilerStatus(IRProfilerStatus::NOT_RUNNING);
240
SaveStaticRegisters();
241
RestoreRoundingMode(true);
242
243
fp_.ABI_PopRegisters(regs_to_save, regs_to_save_fp);
244
245
RET();
246
247
hooks_.crashHandler = GetCodePtr();
248
MOVP2R(SCRATCH1_64, &coreState);
249
MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR);
250
STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);
251
B(quitLoop);
252
253
// Generate some integer conversion funcs.
254
// MIPS order!
255
static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M };
256
for (size_t i = 0; i < ARRAY_SIZE(roundModes); ++i) {
257
convertS0ToSCRATCH1_[i] = AlignCode16();
258
259
// Invert 0x80000000 -> 0x7FFFFFFF for the NAN result.
260
fp_.MVNI(32, EncodeRegToDouble(SCRATCHF2), 0x80, 24);
261
fp_.FCMP(S0, S0); // Detect NaN
262
fp_.FCVTS(S0, S0, roundModes[i]);
263
fp_.FCSEL(S0, S0, SCRATCHF2, CC_VC);
264
265
RET();
266
}
267
268
// Leave this at the end, add more stuff above.
269
if (enableDisasm) {
270
std::vector<std::string> lines = DisassembleArm64(disasmStart, (int)(GetCodePtr() - disasmStart));
271
for (auto s : lines) {
272
INFO_LOG(Log::JIT, "%s", s.c_str());
273
}
274
}
275
276
// Let's spare the pre-generated code from unprotect-reprotect.
277
AlignCodePage();
278
jitStartOffset_ = (int)(GetCodePtr() - start);
279
// Don't forget to zap the instruction cache! This must stay at the end of this function.
280
FlushIcache();
281
EndWrite();
282
283
// Update our current cached rounding mode func, too.
284
UpdateFCR31(mipsState);
285
}
286
287
} // namespace MIPSComp
288
289
#endif
290
291