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/RiscVCompSystem.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/Profiler/Profiler.h"
19
#include "Core/Core.h"
20
#include "Core/HLE/HLE.h"
21
#include "Core/HLE/ReplaceTables.h"
22
#include "Core/MemMap.h"
23
#include "Core/MIPS/RiscV/RiscVJit.h"
24
#include "Core/MIPS/RiscV/RiscVRegCache.h"
25
26
// This file contains compilation for basic PC/downcount accounting, syscalls, debug funcs, etc.
27
//
28
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
29
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
30
31
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
32
#define CONDITIONAL_DISABLE {}
33
#define DISABLE { CompIR_Generic(inst); return; }
34
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
35
36
namespace MIPSComp {
37
38
using namespace RiscVGen;
39
using namespace RiscVJitConstants;
40
41
void RiscVJitBackend::CompIR_Basic(IRInst inst) {
42
CONDITIONAL_DISABLE;
43
44
switch (inst.op) {
45
case IROp::SetConst:
46
// Sign extend all constants. We get 0xFFFFFFFF sometimes, and it's more work to truncate.
47
// The register only holds 32 bits in the end anyway.
48
regs_.SetGPRImm(inst.dest, (int32_t)inst.constant);
49
break;
50
51
case IROp::SetConstF:
52
regs_.Map(inst);
53
if (inst.constant == 0)
54
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO);
55
else
56
QuickFLI(32, regs_.F(inst.dest), inst.constant, SCRATCH1);
57
break;
58
59
case IROp::Downcount:
60
if (inst.constant <= 2048) {
61
ADDI(DOWNCOUNTREG, DOWNCOUNTREG, -(s32)inst.constant);
62
} else {
63
LI(SCRATCH1, inst.constant, SCRATCH2);
64
SUB(DOWNCOUNTREG, DOWNCOUNTREG, SCRATCH1);
65
}
66
break;
67
68
case IROp::SetPC:
69
regs_.Map(inst);
70
MovToPC(regs_.R(inst.src1));
71
break;
72
73
case IROp::SetPCConst:
74
LI(SCRATCH1, inst.constant, SCRATCH2);
75
MovToPC(SCRATCH1);
76
break;
77
78
default:
79
INVALIDOP;
80
break;
81
}
82
}
83
84
void RiscVJitBackend::CompIR_Transfer(IRInst inst) {
85
CONDITIONAL_DISABLE;
86
87
switch (inst.op) {
88
case IROp::SetCtrlVFPU:
89
regs_.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, inst.constant);
90
break;
91
92
case IROp::SetCtrlVFPUReg:
93
regs_.Map(inst);
94
MV(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.R(inst.src1));
95
regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, regs_.IsNormalized32(inst.src1));
96
break;
97
98
case IROp::SetCtrlVFPUFReg:
99
regs_.Map(inst);
100
FMV(FMv::X, FMv::W, regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1));
101
regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, true);
102
break;
103
104
case IROp::FpCondFromReg:
105
regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });
106
MV(regs_.R(IRREG_FPCOND), regs_.R(inst.src1));
107
break;
108
109
case IROp::FpCondToReg:
110
regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } });
111
MV(regs_.R(inst.dest), regs_.R(IRREG_FPCOND));
112
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_FPCOND));
113
break;
114
115
case IROp::FpCtrlFromReg:
116
regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });
117
LI(SCRATCH1, 0x0181FFFF);
118
AND(SCRATCH1, regs_.R(inst.src1), SCRATCH1);
119
// Extract the new fpcond value.
120
if (cpu_info.RiscV_Zbs) {
121
BEXTI(regs_.R(IRREG_FPCOND), SCRATCH1, 23);
122
} else {
123
SRLI(regs_.R(IRREG_FPCOND), SCRATCH1, 23);
124
ANDI(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), 1);
125
}
126
SW(SCRATCH1, CTXREG, IRREG_FCR31 * 4);
127
regs_.MarkGPRDirty(IRREG_FPCOND, true);
128
break;
129
130
case IROp::FpCtrlToReg:
131
regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } });
132
// Load fcr31 and clear the fpcond bit.
133
LW(SCRATCH1, CTXREG, IRREG_FCR31 * 4);
134
if (cpu_info.RiscV_Zbs) {
135
BCLRI(SCRATCH1, SCRATCH1, 23);
136
} else {
137
LI(SCRATCH2, ~(1 << 23));
138
AND(SCRATCH1, SCRATCH1, SCRATCH2);
139
}
140
141
// Now get the correct fpcond bit.
142
ANDI(SCRATCH2, regs_.R(IRREG_FPCOND), 1);
143
SLLI(SCRATCH2, SCRATCH2, 23);
144
OR(regs_.R(inst.dest), SCRATCH1, SCRATCH2);
145
146
// Also update mips->fcr31 while we're here.
147
SW(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4);
148
regs_.MarkGPRDirty(inst.dest, true);
149
break;
150
151
case IROp::VfpuCtrlToReg:
152
regs_.Map(inst);
153
MV(regs_.R(inst.dest), regs_.R(IRREG_VFPU_CTRL_BASE + inst.src1));
154
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1));
155
break;
156
157
case IROp::FMovFromGPR:
158
if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {
159
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
160
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO);
161
} else {
162
regs_.Map(inst);
163
FMV(FMv::W, FMv::X, regs_.F(inst.dest), regs_.R(inst.src1));
164
}
165
break;
166
167
case IROp::FMovToGPR:
168
regs_.Map(inst);
169
FMV(FMv::X, FMv::W, regs_.R(inst.dest), regs_.F(inst.src1));
170
regs_.MarkGPRDirty(inst.dest, true);
171
break;
172
173
default:
174
INVALIDOP;
175
break;
176
}
177
}
178
179
void RiscVJitBackend::CompIR_System(IRInst inst) {
180
CONDITIONAL_DISABLE;
181
182
switch (inst.op) {
183
case IROp::Syscall:
184
FlushAll();
185
SaveStaticRegisters();
186
187
WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);
188
#ifdef USE_PROFILER
189
// When profiling, we can't skip CallSyscall, since it times syscalls.
190
LI(X10, (int32_t)inst.constant);
191
QuickCallFunction(&CallSyscall, SCRATCH2);
192
#else
193
// Skip the CallSyscall where possible.
194
{
195
MIPSOpcode op(inst.constant);
196
void *quickFunc = GetQuickSyscallFunc(op);
197
if (quickFunc) {
198
LI(X10, (uintptr_t)GetSyscallFuncPointer(op));
199
QuickCallFunction((const u8 *)quickFunc, SCRATCH2);
200
} else {
201
LI(X10, (int32_t)inst.constant);
202
QuickCallFunction(&CallSyscall, SCRATCH2);
203
}
204
}
205
#endif
206
207
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
208
LoadStaticRegisters();
209
// This is always followed by an ExitToPC, where we check coreState.
210
break;
211
212
case IROp::CallReplacement:
213
FlushAll();
214
SaveStaticRegisters();
215
WriteDebugProfilerStatus(IRProfilerStatus::REPLACEMENT);
216
QuickCallFunction(GetReplacementFunc(inst.constant)->replaceFunc, SCRATCH2);
217
WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);
218
LoadStaticRegisters();
219
220
regs_.Map(inst);
221
SRAIW(regs_.R(inst.dest), X10, 31);
222
223
// Absolute value trick: if neg, abs(x) == (x ^ -1) + 1.
224
XOR(X10, X10, regs_.R(inst.dest));
225
SUBW(X10, X10, regs_.R(inst.dest));
226
SUB(DOWNCOUNTREG, DOWNCOUNTREG, X10);
227
break;
228
229
case IROp::Break:
230
FlushAll();
231
// This doesn't naturally have restore/apply around it.
232
RestoreRoundingMode(true);
233
SaveStaticRegisters();
234
MovFromPC(X10);
235
QuickCallFunction(&Core_Break, SCRATCH2);
236
LoadStaticRegisters();
237
ApplyRoundingMode(true);
238
MovFromPC(SCRATCH1);
239
ADDI(SCRATCH1, SCRATCH1, 4);
240
QuickJ(R_RA, dispatcherPCInSCRATCH1_);
241
break;
242
243
default:
244
INVALIDOP;
245
break;
246
}
247
}
248
249
void RiscVJitBackend::CompIR_Breakpoint(IRInst inst) {
250
CONDITIONAL_DISABLE;
251
252
switch (inst.op) {
253
case IROp::Breakpoint:
254
case IROp::MemoryCheck:
255
CompIR_Generic(inst);
256
break;
257
258
default:
259
INVALIDOP;
260
break;
261
}
262
}
263
264
void RiscVJitBackend::CompIR_ValidateAddress(IRInst inst) {
265
CONDITIONAL_DISABLE;
266
267
switch (inst.op) {
268
case IROp::ValidateAddress8:
269
case IROp::ValidateAddress16:
270
case IROp::ValidateAddress32:
271
case IROp::ValidateAddress128:
272
CompIR_Generic(inst);
273
break;
274
275
default:
276
INVALIDOP;
277
break;
278
}
279
}
280
281
} // namespace MIPSComp
282
283