Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/LoongArch64/LoongArch64Asm.cpp
3188 views
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 "Common/Log.h"
19
#include "Core/CoreTiming.h"
20
#include "Core/MemMap.h"
21
#include "Core/MIPS/LoongArch64/LoongArch64Jit.h"
22
#include "Core/MIPS/LoongArch64/LoongArch64RegCache.h"
23
#include "Core/MIPS/JitCommon/JitCommon.h"
24
#include "Core/MIPS/JitCommon/JitState.h"
25
#include "Core/Core.h"
26
27
namespace MIPSComp {
28
29
using namespace LoongArch64Gen;
30
using namespace LoongArch64JitConstants;
31
32
static const bool enableDebug = false;
33
static const bool enableDisasm = false;
34
35
static void ShowPC(u32 downcount, void *membase, void *jitbase) {
36
static int count = 0;
37
if (currentMIPS) {
38
ERROR_LOG(Log::JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);
39
} else {
40
ERROR_LOG(Log::JIT, "Universe corrupt?");
41
}
42
count++;
43
}
44
45
void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
46
// This will be used as a writable scratch area, always 32-bit accessible.
47
const u8 *start = AlignCodePage();
48
if (DebugProfilerEnabled()) {
49
ProtectMemoryPages(start, GetMemoryProtectPageSize(), MEM_PROT_READ | MEM_PROT_WRITE);
50
hooks_.profilerPC = (uint32_t *)GetWritableCodePtr();
51
*hooks_.profilerPC = 0;
52
hooks_.profilerStatus = (IRProfilerStatus *)GetWritableCodePtr() + 1;
53
*hooks_.profilerStatus = IRProfilerStatus::NOT_RUNNING;
54
SetCodePointer(GetCodePtr() + sizeof(uint32_t) * 2, GetWritableCodePtr() + sizeof(uint32_t) * 2);
55
}
56
57
const u8 *disasmStart = AlignCodePage();
58
BeginWrite(GetMemoryProtectPageSize());
59
if (jo.useStaticAlloc) {
60
saveStaticRegisters_ = AlignCode16();
61
ST_W(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
62
regs_.EmitSaveStaticRegisters();
63
RET();
64
65
loadStaticRegisters_ = AlignCode16();
66
regs_.EmitLoadStaticRegisters();
67
LD_W(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
68
RET();
69
} else {
70
saveStaticRegisters_ = nullptr;
71
loadStaticRegisters_ = nullptr;
72
}
73
74
applyRoundingMode_ = AlignCode16();
75
{
76
// LoongArch64 does not have any flush to zero capability, so leaving it off
77
LD_WU(SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
78
// We have to do this because otherwise, FMUL will output inaccurate results,
79
// which cause some game going into infinite loop, for example PATAPON.
80
ANDI(SCRATCH2, SCRATCH2, 3);
81
SLLI_D(SCRATCH2, SCRATCH2, 8);
82
83
// We can skip if the rounding mode is nearest (0) and flush is not set.
84
// (as restoreRoundingMode cleared it out anyway)
85
FixupBranch skip = BEQZ(SCRATCH2);
86
87
// MIPS Rounding Mode: LoongArch64
88
// 0: Round nearest 0 RNE
89
// 1: Round to zero 1 RZ
90
// 2: Round up (ceil) 2 RP
91
// 3: Round down (floor) 3 RM
92
MOVGR2FCSR(FCSR3, SCRATCH2);
93
94
SetJumpTarget(skip);
95
RET();
96
}
97
98
hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();
99
100
// Start by saving some regs on the stack. There are 11 GPs and 8 FPs we want.
101
// Note: we leave R_SP as, well, SP, so it doesn't need to be saved.
102
static constexpr LoongArch64Reg regs_to_save[]{ R_RA, R22, R23, R24, R25, R26, R27, R28, R29, R30, R31 };
103
// TODO: Maybe we shouldn't regalloc all of these? Is it worth it?
104
static constexpr LoongArch64Reg regs_to_save_fp[]{ F24, F25, F26, F27, F28, F29, F30, F31 };
105
int saveSize = (64 / 8) * (int)(ARRAY_SIZE(regs_to_save) + ARRAY_SIZE(regs_to_save_fp));
106
if (saveSize & 0xF)
107
saveSize += 8;
108
_assert_msg_((saveSize & 0xF) == 0, "Stack must be kept aligned");
109
int saveOffset = 0;
110
ADDI_D(R_SP, R_SP, -saveSize);
111
for (LoongArch64Reg r : regs_to_save) {
112
ST_D(r, R_SP, saveOffset);
113
saveOffset += 64 / 8;
114
}
115
for (LoongArch64Reg r : regs_to_save_fp) {
116
FST_D(r, R_SP, saveOffset);
117
saveOffset += 64 / 8;
118
}
119
_assert_(saveOffset <= saveSize);
120
121
// Fixed registers, these are always kept when in Jit context.
122
LI(MEMBASEREG, Memory::base);
123
LI(CTXREG, mipsState);
124
LI(JITBASEREG, GetBasePtr() - MIPS_EMUHACK_OPCODE);
125
126
LoadStaticRegisters();
127
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
128
MovFromPC(SCRATCH1);
129
WriteDebugPC(SCRATCH1);
130
outerLoopPCInSCRATCH1_ = GetCodePtr();
131
MovToPC(SCRATCH1);
132
outerLoop_ = GetCodePtr();
133
// Advance can change the downcount (or thread), so must save/restore around it.
134
SaveStaticRegisters();
135
RestoreRoundingMode(true);
136
WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE);
137
QuickCallFunction(&CoreTiming::Advance, R20);
138
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
139
ApplyRoundingMode(true);
140
LoadStaticRegisters();
141
142
dispatcherCheckCoreState_ = GetCodePtr();
143
LI(SCRATCH1, &coreState);
144
LD_W(SCRATCH1, SCRATCH1, 0);
145
FixupBranch badCoreState = BNEZ(SCRATCH1);
146
147
// We just checked coreState, so go to advance if downcount is negative.
148
BLT(DOWNCOUNTREG, R_ZERO, outerLoop_);
149
FixupBranch skipToRealDispatch = B();
150
151
dispatcherPCInSCRATCH1_ = GetCodePtr();
152
MovToPC(SCRATCH1);
153
154
hooks_.dispatcher = GetCodePtr();
155
FixupBranch bail = BLT(DOWNCOUNTREG, R_ZERO);
156
SetJumpTarget(skipToRealDispatch);
157
158
dispatcherNoCheck_ = GetCodePtr();
159
160
// Debug
161
if (enableDebug) {
162
MOVE(R4, DOWNCOUNTREG);
163
MOVE(R5, MEMBASEREG);
164
MOVE(R6, JITBASEREG);
165
QuickCallFunction(&ShowPC, R20);
166
}
167
168
LD_WU(SCRATCH1, CTXREG, offsetof(MIPSState, pc));
169
WriteDebugPC(SCRATCH1);
170
#ifdef MASKED_PSP_MEMORY
171
LI(SCRATCH2, 0x3FFFFFFF);
172
AND(SCRATCH1, SCRATCH1, SCRATCH2);
173
#endif
174
ADD_D(SCRATCH1, SCRATCH1, MEMBASEREG);
175
hooks_.dispatchFetch = GetCodePtr();
176
LD_WU(SCRATCH1, SCRATCH1, 0);
177
SRLI_D(SCRATCH2, SCRATCH1, 24);
178
// We're in other words comparing to the top 8 bits of MIPS_EMUHACK_OPCODE by subtracting.
179
ADDI_D(SCRATCH2, SCRATCH2, -(MIPS_EMUHACK_OPCODE >> 24));
180
FixupBranch needsCompile = BNEZ(SCRATCH2);
181
// No need to mask, JITBASEREG has already accounted for the upper bits.
182
ADD_D(SCRATCH1, JITBASEREG, SCRATCH1);
183
JR(SCRATCH1);
184
SetJumpTarget(needsCompile);
185
186
// No block found, let's jit. We don't need to save static regs, they're all callee saved.
187
RestoreRoundingMode(true);
188
WriteDebugProfilerStatus(IRProfilerStatus::COMPILING);
189
QuickCallFunction(&MIPSComp::JitAt, R20);
190
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
191
ApplyRoundingMode(true);
192
193
// Try again, the block index should be set now.
194
B(dispatcherNoCheck_);
195
196
SetJumpTarget(bail);
197
198
LI(SCRATCH1, &coreState);
199
LD_W(SCRATCH1, SCRATCH1, 0);
200
BEQZ(SCRATCH1, outerLoop_);
201
202
const uint8_t *quitLoop = GetCodePtr();
203
SetJumpTarget(badCoreState);
204
205
WriteDebugProfilerStatus(IRProfilerStatus::NOT_RUNNING);
206
SaveStaticRegisters();
207
RestoreRoundingMode(true);
208
209
saveOffset = 0;
210
for (LoongArch64Reg r : regs_to_save) {
211
LD_D(r, R_SP, saveOffset);
212
saveOffset += 64 / 8;
213
}
214
for (LoongArch64Reg r : regs_to_save_fp) {
215
FLD_D(r, R_SP, saveOffset);
216
saveOffset += 64 / 8;
217
}
218
ADDI_D(R_SP, R_SP, saveSize);
219
220
RET();
221
222
hooks_.crashHandler = GetCodePtr();
223
LI(SCRATCH1, &coreState);
224
LI(SCRATCH2, CORE_RUNTIME_ERROR);
225
ST_W(SCRATCH2, SCRATCH1, 0);
226
B(quitLoop);
227
228
// Leave this at the end, add more stuff above.
229
if (enableDisasm) {
230
#if PPSSPP_ARCH(LOONGARCH64)
231
std::vector<std::string> lines = DisassembleLA64(start, GetCodePtr() - start);
232
for (auto s : lines) {
233
INFO_LOG(Log::JIT, "%s", s.c_str());
234
}
235
#endif
236
}
237
238
// Let's spare the pre-generated code from unprotect-reprotect.
239
AlignCodePage();
240
jitStartOffset_ = (int)(GetCodePtr() - start);
241
// Don't forget to zap the instruction cache! This must stay at the end of this function.
242
FlushIcache();
243
EndWrite();
244
}
245
246
} // namespace MIPSComp
247
248