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/RiscV/RiscVAsm.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 "Common/Log.h"
19
#include "Core/CoreTiming.h"
20
#include "Core/MemMap.h"
21
#include "Core/MIPS/RiscV/RiscVJit.h"
22
#include "Core/MIPS/RiscV/RiscVRegCache.h"
23
#include "Core/MIPS/JitCommon/JitCommon.h"
24
#include "Core/MIPS/JitCommon/JitState.h"
25
#include "Core/System.h"
26
27
namespace MIPSComp {
28
29
using namespace RiscVGen;
30
using namespace RiscVJitConstants;
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
//if (count > 2000)
43
// exit(0);
44
count++;
45
}
46
47
void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) {
48
// This will be used as a writable scratch area, always 32-bit accessible.
49
const u8 *start = AlignCodePage();
50
if (DebugProfilerEnabled()) {
51
ProtectMemoryPages(start, GetMemoryProtectPageSize(), MEM_PROT_READ | MEM_PROT_WRITE);
52
hooks_.profilerPC = (uint32_t *)GetWritableCodePtr();
53
*hooks_.profilerPC = 0;
54
hooks_.profilerStatus = (IRProfilerStatus *)GetWritableCodePtr() + 1;
55
*hooks_.profilerStatus = IRProfilerStatus::NOT_RUNNING;
56
SetCodePointer(GetCodePtr() + sizeof(uint32_t) * 2, GetWritableCodePtr() + sizeof(uint32_t) * 2);
57
}
58
59
const u8 *disasmStart = AlignCodePage();
60
BeginWrite(GetMemoryProtectPageSize());
61
62
if (jo.useStaticAlloc) {
63
saveStaticRegisters_ = AlignCode16();
64
SW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
65
regs_.EmitSaveStaticRegisters();
66
RET();
67
68
loadStaticRegisters_ = AlignCode16();
69
regs_.EmitLoadStaticRegisters();
70
LW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
71
RET();
72
} else {
73
saveStaticRegisters_ = nullptr;
74
loadStaticRegisters_ = nullptr;
75
}
76
77
applyRoundingMode_ = AlignCode16();
78
{
79
// Not sure if RISC-V has any flush to zero capability? Leaving it off for now...
80
LWU(SCRATCH2, CTXREG, offsetof(MIPSState, fcr31));
81
82
// We can skip if the rounding mode is nearest (0) and flush is not set.
83
// (as restoreRoundingMode cleared it out anyway)
84
FixupBranch skip = BEQ(SCRATCH2, R_ZERO);
85
86
// MIPS Rounding Mode: RISC-V
87
// 0: Round nearest 0
88
// 1: Round to zero 1
89
// 2: Round up (ceil) 3
90
// 3: Round down (floor) 2
91
if (cpu_info.RiscV_Zbs) {
92
BEXTI(SCRATCH1, SCRATCH2, 1);
93
} else {
94
ANDI(SCRATCH1, SCRATCH2, 2);
95
SRLI(SCRATCH1, SCRATCH1, 1);
96
}
97
// Swap the lowest bit by the second bit.
98
XOR(SCRATCH2, SCRATCH2, SCRATCH1);
99
100
FSRM(SCRATCH2);
101
102
SetJumpTarget(skip);
103
RET();
104
}
105
106
hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();
107
108
// Start by saving some regs on the stack. There are 12 GPs and 12 FPs we want.
109
// Note: we leave R_SP as, well, SP, so it doesn't need to be saved.
110
_assert_msg_(cpu_info.Mode64bit, "RiscVAsm currently assumes RV64, not RV32 or RV128");
111
static constexpr RiscVReg regs_to_save[]{ R_RA, X8, X9, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27 };
112
// TODO: Maybe we shouldn't regalloc all of these? Is it worth it?
113
static constexpr RiscVReg regs_to_save_fp[]{ F8, F9, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27 };
114
int saveSize = (XLEN / 8) * (int)(ARRAY_SIZE(regs_to_save) + ARRAY_SIZE(regs_to_save_fp));
115
if (saveSize & 0xF)
116
saveSize += 8;
117
_assert_msg_((saveSize & 0xF) == 0, "Stack must be kept aligned");
118
int saveOffset = 0;
119
ADDI(R_SP, R_SP, -saveSize);
120
for (RiscVReg r : regs_to_save) {
121
SD(r, R_SP, saveOffset);
122
saveOffset += XLEN / 8;
123
}
124
for (RiscVReg r : regs_to_save_fp) {
125
FS(64, r, R_SP, saveOffset);
126
saveOffset += XLEN / 8;
127
}
128
_assert_(saveOffset <= saveSize);
129
130
// Fixed registers, these are always kept when in Jit context.
131
LI(MEMBASEREG, Memory::base, SCRATCH1);
132
LI(CTXREG, mipsState, SCRATCH1);
133
LI(JITBASEREG, GetBasePtr() - MIPS_EMUHACK_OPCODE, SCRATCH1);
134
135
LoadStaticRegisters();
136
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
137
MovFromPC(SCRATCH1);
138
WriteDebugPC(SCRATCH1);
139
outerLoopPCInSCRATCH1_ = GetCodePtr();
140
MovToPC(SCRATCH1);
141
outerLoop_ = GetCodePtr();
142
// Advance can change the downcount (or thread), so must save/restore around it.
143
SaveStaticRegisters();
144
RestoreRoundingMode(true);
145
WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE);
146
QuickCallFunction(&CoreTiming::Advance, X7);
147
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
148
ApplyRoundingMode(true);
149
LoadStaticRegisters();
150
151
dispatcherCheckCoreState_ = GetCodePtr();
152
LI(SCRATCH1, &coreState, SCRATCH2);
153
LW(SCRATCH1, SCRATCH1, 0);
154
FixupBranch badCoreState = BNE(SCRATCH1, R_ZERO);
155
156
// We just checked coreState, so go to advance if downcount is negative.
157
BLT(DOWNCOUNTREG, R_ZERO, outerLoop_);
158
FixupBranch skipToRealDispatch = J();
159
160
dispatcherPCInSCRATCH1_ = GetCodePtr();
161
MovToPC(SCRATCH1);
162
163
hooks_.dispatcher = GetCodePtr();
164
FixupBranch bail = BLT(DOWNCOUNTREG, R_ZERO);
165
SetJumpTarget(skipToRealDispatch);
166
167
dispatcherNoCheck_ = GetCodePtr();
168
169
// Debug
170
if (enableDebug) {
171
MV(X10, DOWNCOUNTREG);
172
MV(X11, MEMBASEREG);
173
MV(X12, JITBASEREG);
174
QuickCallFunction(&ShowPC, X7);
175
}
176
177
LWU(SCRATCH1, CTXREG, offsetof(MIPSState, pc));
178
WriteDebugPC(SCRATCH1);
179
#ifdef MASKED_PSP_MEMORY
180
LI(SCRATCH2, 0x3FFFFFFF);
181
AND(SCRATCH1, SCRATCH1, SCRATCH2);
182
#endif
183
ADD(SCRATCH1, SCRATCH1, MEMBASEREG);
184
hooks_.dispatchFetch = GetCodePtr();
185
LWU(SCRATCH1, SCRATCH1, 0);
186
SRLI(SCRATCH2, SCRATCH1, 24);
187
// We're in other words comparing to the top 8 bits of MIPS_EMUHACK_OPCODE by subtracting.
188
ADDI(SCRATCH2, SCRATCH2, -(MIPS_EMUHACK_OPCODE >> 24));
189
FixupBranch needsCompile = BNE(SCRATCH2, R_ZERO);
190
// No need to mask, JITBASEREG has already accounted for the upper bits.
191
ADD(SCRATCH1, JITBASEREG, SCRATCH1);
192
JR(SCRATCH1);
193
SetJumpTarget(needsCompile);
194
195
// No block found, let's jit. We don't need to save static regs, they're all callee saved.
196
RestoreRoundingMode(true);
197
WriteDebugProfilerStatus(IRProfilerStatus::COMPILING);
198
QuickCallFunction(&MIPSComp::JitAt, X7);
199
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
200
ApplyRoundingMode(true);
201
202
// Try again, the block index should be set now.
203
J(dispatcherNoCheck_);
204
205
SetJumpTarget(bail);
206
207
LI(SCRATCH1, &coreState, SCRATCH2);
208
LW(SCRATCH1, SCRATCH1, 0);
209
BEQ(SCRATCH1, R_ZERO, outerLoop_);
210
211
const uint8_t *quitLoop = GetCodePtr();
212
SetJumpTarget(badCoreState);
213
214
WriteDebugProfilerStatus(IRProfilerStatus::NOT_RUNNING);
215
SaveStaticRegisters();
216
RestoreRoundingMode(true);
217
218
_assert_msg_(cpu_info.Mode64bit, "RiscVAsm currently assumes RV64, not RV32 or RV128");
219
saveOffset = 0;
220
for (RiscVReg r : regs_to_save) {
221
LD(r, R_SP, saveOffset);
222
saveOffset += XLEN / 8;
223
}
224
for (RiscVReg r : regs_to_save_fp) {
225
FL(64, r, R_SP, saveOffset);
226
saveOffset += XLEN / 8;
227
}
228
ADDI(R_SP, R_SP, saveSize);
229
230
RET();
231
232
hooks_.crashHandler = GetCodePtr();
233
LI(SCRATCH1, &coreState, SCRATCH2);
234
LI(SCRATCH2, CORE_RUNTIME_ERROR);
235
SW(SCRATCH2, SCRATCH1, 0);
236
J(quitLoop);
237
238
// Leave this at the end, add more stuff above.
239
if (enableDisasm) {
240
#if PPSSPP_ARCH(RISCV64)
241
std::vector<std::string> lines = DisassembleRV64(start, GetCodePtr() - start);
242
for (auto s : lines) {
243
INFO_LOG(Log::JIT, "%s", s.c_str());
244
}
245
#endif
246
}
247
248
// Let's spare the pre-generated code from unprotect-reprotect.
249
AlignCodePage();
250
jitStartOffset_ = (int)(GetCodePtr() - start);
251
// Don't forget to zap the instruction cache! This must stay at the end of this function.
252
FlushIcache();
253
EndWrite();
254
}
255
256
} // namespace MIPSComp
257
258