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/Arm64CompFPU.cpp
Views: 1401
1
// Copyright (c) 2012- 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
#if PPSSPP_ARCH(ARM64)
20
21
#include "Core/Config.h"
22
#include "Core/MemMap.h"
23
#include "Core/MIPS/MIPS.h"
24
#include "Core/MIPS/MIPSCodeUtils.h"
25
#include "Core/MIPS/MIPSTables.h"
26
27
#include "Core/MIPS/ARM64/Arm64Jit.h"
28
#include "Core/MIPS/ARM64/Arm64RegCache.h"
29
#include "Common/CPUDetect.h"
30
31
#define _RS MIPS_GET_RS(op)
32
#define _RT MIPS_GET_RT(op)
33
#define _RD MIPS_GET_RD(op)
34
#define _FS MIPS_GET_FS(op)
35
#define _FT MIPS_GET_FT(op)
36
#define _FD MIPS_GET_FD(op)
37
#define _SA MIPS_GET_SA(op)
38
#define _POS ((op>> 6) & 0x1F)
39
#define _SIZE ((op>>11) & 0x1F)
40
#define _IMM16 (signed short)(op & 0xFFFF)
41
#define _IMM26 (op & 0x03FFFFFF)
42
43
44
// FPCR interesting bits:
45
// 24: FZ (flush-to-zero)
46
// 23:22: RMode (0 = nearest, 1 = +inf, 2 = -inf, 3 = zero)
47
// not much else is interesting for us, but should be preserved.
48
// To access: MRS Xt, FPCR ; MSR FPCR, Xt
49
50
51
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
52
// Currently known non working ones should have DISABLE.
53
54
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
55
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
56
#define DISABLE { Comp_Generic(op); return; }
57
58
namespace MIPSComp {
59
using namespace Arm64Gen;
60
using namespace Arm64JitConstants;
61
62
void Arm64Jit::Comp_FPU3op(MIPSOpcode op) {
63
CONDITIONAL_DISABLE(FPU);
64
65
int ft = _FT;
66
int fs = _FS;
67
int fd = _FD;
68
69
fpr.MapDirtyInIn(fd, fs, ft);
70
switch (op & 0x3f) {
71
case 0: fp.FADD(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) + F(ft); //add
72
case 1: fp.FSUB(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) - F(ft); //sub
73
case 2: fp.FMUL(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) * F(ft); //mul
74
case 3: fp.FDIV(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) / F(ft); //div
75
default:
76
DISABLE;
77
return;
78
}
79
}
80
81
void Arm64Jit::Comp_FPULS(MIPSOpcode op)
82
{
83
CONDITIONAL_DISABLE(LSU_FPU);
84
CheckMemoryBreakpoint();
85
86
s32 offset = SignExtend16ToS32(op & 0xFFFF);
87
int ft = _FT;
88
MIPSGPReg rs = _RS;
89
// u32 addr = R(rs) + offset;
90
std::vector<FixupBranch> skips;
91
switch (op >> 26) {
92
case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1
93
if (!gpr.IsImm(rs) && jo.cachePointers && g_Config.bFastMemory && (offset & 3) == 0 && offset <= 16380 && offset >= 0) {
94
gpr.MapRegAsPointer(rs);
95
fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY);
96
fp.LDR(32, INDEX_UNSIGNED, fpr.R(ft), gpr.RPtr(rs), offset);
97
break;
98
}
99
100
fpr.SpillLock(ft);
101
fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY);
102
if (gpr.IsImm(rs)) {
103
#ifdef MASKED_PSP_MEMORY
104
u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF;
105
#else
106
u32 addr = offset + gpr.GetImm(rs);
107
#endif
108
gpr.SetRegImm(SCRATCH1, addr);
109
} else {
110
gpr.MapReg(rs);
111
if (g_Config.bFastMemory) {
112
SetScratch1ToEffectiveAddress(rs, offset);
113
} else {
114
skips = SetScratch1ForSafeAddress(rs, offset, SCRATCH2);
115
}
116
}
117
fp.LDR(32, fpr.R(ft), SCRATCH1_64, ArithOption(MEMBASEREG));
118
for (auto skip : skips) {
119
SetJumpTarget(skip);
120
}
121
fpr.ReleaseSpillLocksAndDiscardTemps();
122
break;
123
124
case 57: //Memory::Write_U32(FI(ft), addr); break; //swc1
125
if (!gpr.IsImm(rs) && jo.cachePointers && g_Config.bFastMemory && (offset & 3) == 0 && offset <= 16380 && offset >= 0) {
126
gpr.MapRegAsPointer(rs);
127
fpr.MapReg(ft, 0);
128
fp.STR(32, INDEX_UNSIGNED, fpr.R(ft), gpr.RPtr(rs), offset);
129
break;
130
}
131
132
fpr.MapReg(ft);
133
if (gpr.IsImm(rs)) {
134
#ifdef MASKED_PSP_MEMORY
135
u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF;
136
#else
137
u32 addr = offset + gpr.GetImm(rs);
138
#endif
139
gpr.SetRegImm(SCRATCH1, addr);
140
} else {
141
gpr.MapReg(rs);
142
if (g_Config.bFastMemory) {
143
SetScratch1ToEffectiveAddress(rs, offset);
144
} else {
145
skips = SetScratch1ForSafeAddress(rs, offset, SCRATCH2);
146
}
147
}
148
fp.STR(32, fpr.R(ft), SCRATCH1_64, ArithOption(MEMBASEREG));
149
for (auto skip : skips) {
150
SetJumpTarget(skip);
151
}
152
break;
153
154
default:
155
Comp_Generic(op);
156
return;
157
}
158
}
159
160
void Arm64Jit::Comp_FPUComp(MIPSOpcode op) {
161
CONDITIONAL_DISABLE(FPU_COMP);
162
163
int opc = op & 0xF;
164
if (opc >= 8) opc -= 8; // alias
165
if (opc == 0) { // f, sf (signalling false)
166
gpr.SetImm(MIPS_REG_FPCOND, 0);
167
return;
168
}
169
170
int fs = _FS;
171
int ft = _FT;
172
gpr.MapReg(MIPS_REG_FPCOND, MAP_DIRTY | MAP_NOINIT);
173
fpr.MapInIn(fs, ft);
174
fp.FCMP(fpr.R(fs), fpr.R(ft));
175
176
switch (opc) {
177
case 1: // un, ngle (unordered)
178
CSET(gpr.R(MIPS_REG_FPCOND), CC_VS);
179
break;
180
case 2: // eq, seq (equal, ordered)
181
CSET(gpr.R(MIPS_REG_FPCOND), CC_EQ);
182
break;
183
case 3: // ueq, ngl (equal, unordered)
184
CSET(gpr.R(MIPS_REG_FPCOND), CC_EQ);
185
// If ordered, use the above result. If unordered, use ZR+1 (being 1.)
186
CSINC(gpr.R(MIPS_REG_FPCOND), gpr.R(MIPS_REG_FPCOND), WZR, CC_VC);
187
return;
188
case 4: // olt, lt (less than, ordered)
189
CSET(gpr.R(MIPS_REG_FPCOND), CC_LO);
190
break;
191
case 5: // ult, nge (less than, unordered)
192
CSET(gpr.R(MIPS_REG_FPCOND), CC_LT);
193
break;
194
case 6: // ole, le (less equal, ordered)
195
CSET(gpr.R(MIPS_REG_FPCOND), CC_LS);
196
break;
197
case 7: // ule, ngt (less equal, unordered)
198
CSET(gpr.R(MIPS_REG_FPCOND), CC_LE);
199
break;
200
default:
201
Comp_Generic(op);
202
return;
203
}
204
}
205
206
void Arm64Jit::Comp_FPU2op(MIPSOpcode op) {
207
CONDITIONAL_DISABLE(FPU);
208
int fs = _FS;
209
int fd = _FD;
210
211
switch (op & 0x3f) {
212
case 4: //F(fd) = sqrtf(F(fs)); break; //sqrt
213
fpr.MapDirtyIn(fd, fs);
214
fp.FSQRT(fpr.R(fd), fpr.R(fs));
215
break;
216
case 5: //F(fd) = fabsf(F(fs)); break; //abs
217
fpr.MapDirtyIn(fd, fs);
218
fp.FABS(fpr.R(fd), fpr.R(fs));
219
break;
220
case 6: //F(fd) = F(fs); break; //mov
221
fpr.MapDirtyIn(fd, fs);
222
fp.FMOV(fpr.R(fd), fpr.R(fs));
223
break;
224
case 7: //F(fd) = -F(fs); break; //neg
225
fpr.MapDirtyIn(fd, fs);
226
fp.FNEG(fpr.R(fd), fpr.R(fs));
227
break;
228
229
case 12: //FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
230
{
231
fpr.MapDirtyIn(fd, fs);
232
fp.FCMP(fpr.R(fs), fpr.R(fs)); // Detect NaN
233
fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_N); // to nearest, ties to even
234
FixupBranch skip = B(CC_VC);
235
MOVI2R(SCRATCH1, 0x7FFFFFFF);
236
fp.FMOV(fpr.R(fd), SCRATCH1);
237
SetJumpTarget(skip);
238
break;
239
}
240
241
case 13: //FsI(fd) = Rto0(F(fs))); break; //trunc.w.s
242
{
243
fpr.MapDirtyIn(fd, fs);
244
fp.FCMP(fpr.R(fs), fpr.R(fs));
245
fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_Z);
246
FixupBranch skip = B(CC_VC);
247
MOVI2R(SCRATCH1, 0x7FFFFFFF);
248
fp.FMOV(fpr.R(fd), SCRATCH1);
249
SetJumpTarget(skip);
250
break;
251
}
252
253
case 14://FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
254
{
255
fpr.MapDirtyIn(fd, fs);
256
fp.FCMP(fpr.R(fs), fpr.R(fs));
257
fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_P); // towards +inf
258
FixupBranch skip = B(CC_VC);
259
MOVI2R(SCRATCH1, 0x7FFFFFFF);
260
fp.FMOV(fpr.R(fd), SCRATCH1);
261
SetJumpTarget(skip);
262
break;
263
}
264
case 15: //FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
265
{
266
fpr.MapDirtyIn(fd, fs);
267
fp.FCMP(fpr.R(fs), fpr.R(fs));
268
fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_M); // towards -inf
269
FixupBranch skip = B(CC_VC);
270
MOVI2R(SCRATCH1, 0x7FFFFFFF);
271
fp.FMOV(fpr.R(fd), SCRATCH1);
272
SetJumpTarget(skip);
273
break;
274
}
275
276
case 32: //F(fd) = (float)FsI(fs); break; //cvt.s.w
277
fpr.MapDirtyIn(fd, fs);
278
fp.SCVTF(fpr.R(fd), fpr.R(fs));
279
break;
280
281
case 36: //FsI(fd) = (int) F(fs); break; //cvt.w.s
282
fpr.MapDirtyIn(fd, fs);
283
if (js.hasSetRounding) {
284
// We're just going to defer to our cached func. Here's the arg.
285
fp.FMOV(S0, fpr.R(fs));
286
287
MOVP2R(SCRATCH1_64, &js.currentRoundingFunc);
288
LDR(INDEX_UNSIGNED, SCRATCH1_64, SCRATCH1_64, 0);
289
290
BLR(SCRATCH1_64);
291
292
fp.FMOV(fpr.R(fd), S0);
293
} else {
294
fp.FCMP(fpr.R(fs), fpr.R(fs));
295
fp.FCVTS(fpr.R(fd), fpr.R(fs), ROUND_N);
296
FixupBranch skip_nan = B(CC_VC);
297
MOVI2R(SCRATCH1, 0x7FFFFFFF);
298
fp.FMOV(fpr.R(fd), SCRATCH1);
299
SetJumpTarget(skip_nan);
300
}
301
break;
302
303
default:
304
DISABLE;
305
}
306
}
307
308
void Arm64Jit::Comp_mxc1(MIPSOpcode op)
309
{
310
CONDITIONAL_DISABLE(FPU_XFER);
311
312
int fs = _FS;
313
MIPSGPReg rt = _RT;
314
315
switch ((op >> 21) & 0x1f) {
316
case 0: // R(rt) = FI(fs); break; //mfc1
317
if (rt == MIPS_REG_ZERO) {
318
return;
319
}
320
gpr.MapReg(rt, MAP_DIRTY | MAP_NOINIT);
321
if (fpr.IsMapped(fs)) {
322
fp.FMOV(gpr.R(rt), fpr.R(fs));
323
} else {
324
LDR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, fpr.GetMipsRegOffset(fs));
325
}
326
return;
327
328
case 2: //cfc1
329
if (rt == MIPS_REG_ZERO) {
330
return;
331
}
332
if (fs == 31) {
333
if (gpr.IsImm(MIPS_REG_FPCOND)) {
334
gpr.MapReg(rt, MAP_DIRTY | MAP_NOINIT);
335
LDR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
336
if (gpr.GetImm(MIPS_REG_FPCOND) & 1) {
337
ORRI2R(gpr.R(rt), gpr.R(rt), 0x1 << 23, SCRATCH2);
338
} else {
339
ANDI2R(gpr.R(rt), gpr.R(rt), ~(0x1 << 23), SCRATCH2);
340
}
341
} else {
342
gpr.MapDirtyIn(rt, MIPS_REG_FPCOND);
343
LDR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
344
BFI(gpr.R(rt), gpr.R(MIPS_REG_FPCOND), 23, 1);
345
}
346
} else if (fs == 0) {
347
gpr.SetImm(rt, MIPSState::FCR0_VALUE);
348
} else {
349
// Unsupported regs are always 0.
350
gpr.SetImm(rt, 0);
351
}
352
return;
353
354
case 4: //FI(fs) = R(rt); break; //mtc1
355
if (gpr.IsImm(rt)) {
356
// This can't be run on LO/HI.
357
uint32_t ival = (uint32_t)gpr.GetImm(rt);
358
float floatval;
359
memcpy(&floatval, &ival, sizeof(floatval));
360
uint8_t imm8;
361
// If zero, just zero it.
362
fpr.MapReg(fs, MAP_NOINIT | MAP_DIRTY);
363
if (ival == 0) {
364
fp.FMOV(fpr.R(fs), WZR); // This is supposedly special cased in hardware to be fast.
365
} else if (FPImm8FromFloat(floatval, &imm8)) {
366
fp.FMOV(fpr.R(fs), imm8);
367
} else {
368
// Materialize the register and do a cross move.
369
gpr.MapReg(rt);
370
fp.FMOV(fpr.R(fs), gpr.R(rt));
371
}
372
} else {
373
gpr.MapReg(rt);
374
fpr.MapReg(fs, MAP_NOINIT | MAP_DIRTY);
375
fp.FMOV(fpr.R(fs), gpr.R(rt));
376
}
377
return;
378
379
case 6: //ctc1
380
if (fs == 31) {
381
// Must clear before setting, since ApplyRoundingMode() assumes it was cleared.
382
RestoreRoundingMode();
383
bool wasImm = gpr.IsImm(rt);
384
u32 immVal = -1;
385
if (wasImm) {
386
immVal = gpr.GetImm(rt);
387
gpr.SetImm(MIPS_REG_FPCOND, (immVal >> 23) & 1);
388
gpr.MapReg(rt);
389
} else {
390
gpr.MapDirtyIn(MIPS_REG_FPCOND, rt);
391
}
392
393
// Update MIPS state
394
// TODO: Technically, should mask by 0x0181FFFF. Maybe just put all of FCR31 in the reg?
395
STR(INDEX_UNSIGNED, gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
396
if (!wasImm) {
397
UBFX(gpr.R(MIPS_REG_FPCOND), gpr.R(rt), 23, 1);
398
// TODO: We do have the fcr31 value in a register here, could use that in UpdateRoundingMode to avoid reloading it.
399
UpdateRoundingMode();
400
} else {
401
UpdateRoundingMode(immVal);
402
}
403
ApplyRoundingMode();
404
} else {
405
Comp_Generic(op);
406
}
407
return;
408
default:
409
DISABLE;
410
break;
411
}
412
}
413
414
} // namespace MIPSComp
415
416
#endif // PPSSPP_ARCH(ARM64)
417
418