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/Arm64CompALU.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 <algorithm>
22
23
#include "Common/BitSet.h"
24
#include "Common/CPUDetect.h"
25
#include "Common/Data/Convert/SmallDataConvert.h"
26
#include "Core/MIPS/MIPS.h"
27
#include "Core/MIPS/MIPSAnalyst.h"
28
#include "Core/MIPS/MIPSCodeUtils.h"
29
#include "Core/MIPS/ARM64/Arm64Jit.h"
30
#include "Core/MIPS/ARM64/Arm64RegCache.h"
31
32
using namespace MIPSAnalyst;
33
34
#define _RS MIPS_GET_RS(op)
35
#define _RT MIPS_GET_RT(op)
36
#define _RD MIPS_GET_RD(op)
37
#define _FS MIPS_GET_FS(op)
38
#define _FT MIPS_GET_FT(op)
39
#define _FD MIPS_GET_FD(op)
40
#define _SA MIPS_GET_SA(op)
41
#define _POS ((op>> 6) & 0x1F)
42
#define _SIZE ((op>>11) & 0x1F)
43
#define _IMM16 (signed short)(op & 0xFFFF)
44
#define _IMM26 (op & 0x03FFFFFF)
45
46
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
47
// Currently known non working ones should have DISABLE.
48
49
//#define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
50
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
51
#define DISABLE { Comp_Generic(op); return; }
52
53
namespace MIPSComp {
54
using namespace Arm64Gen;
55
using namespace Arm64JitConstants;
56
57
static u32 EvalOr(u32 a, u32 b) { return a | b; }
58
static u32 EvalEor(u32 a, u32 b) { return a ^ b; }
59
static u32 EvalAnd(u32 a, u32 b) { return a & b; }
60
static u32 EvalAdd(u32 a, u32 b) { return a + b; }
61
static u32 EvalSub(u32 a, u32 b) { return a - b; }
62
63
void Arm64Jit::CompImmLogic(MIPSGPReg rs, MIPSGPReg rt, u32 uimm, void (ARM64XEmitter::*arith)(ARM64Reg dst, ARM64Reg src, ARM64Reg src2), bool (ARM64XEmitter::*tryArithI2R)(ARM64Reg dst, ARM64Reg src, u64 val), u32 (*eval)(u32 a, u32 b)) {
64
if (gpr.IsImm(rs)) {
65
gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm));
66
} else {
67
gpr.MapDirtyIn(rt, rs);
68
if (!(this->*tryArithI2R)(gpr.R(rt), gpr.R(rs), uimm)) {
69
gpr.SetRegImm(SCRATCH1, uimm);
70
(this->*arith)(gpr.R(rt), gpr.R(rs), SCRATCH1);
71
}
72
}
73
}
74
75
void Arm64Jit::Comp_IType(MIPSOpcode op) {
76
CONDITIONAL_DISABLE(ALU_IMM);
77
u32 uimm = op & 0xFFFF;
78
s32 simm = SignExtend16ToS32(op);
79
u32 suimm = SignExtend16ToU32(op);
80
81
MIPSGPReg rt = _RT;
82
MIPSGPReg rs = _RS;
83
84
// noop, won't write to ZERO.
85
if (rt == 0)
86
return;
87
88
switch (op >> 26) {
89
case 8: // same as addiu?
90
case 9: // R(rt) = R(rs) + simm; break; //addiu
91
// Special-case for small adjustments of pointerified registers. Commonly for SP but happens for others.
92
if (rs == rt && gpr.IsMappedAsPointer(rs) && IsImmArithmetic(simm < 0 ? -simm : simm, nullptr, nullptr)) {
93
ARM64Reg r32 = gpr.RPtr(rs);
94
gpr.MarkDirty(r32);
95
ARM64Reg r = EncodeRegTo64(r32);
96
ADDI2R(r, r, simm);
97
} else {
98
if (simm >= 0) {
99
CompImmLogic(rs, rt, simm, &ARM64XEmitter::ADD, &ARM64XEmitter::TryADDI2R, &EvalAdd);
100
} else if (simm < 0) {
101
CompImmLogic(rs, rt, -simm, &ARM64XEmitter::SUB, &ARM64XEmitter::TrySUBI2R, &EvalSub);
102
}
103
}
104
break;
105
106
case 12: CompImmLogic(rs, rt, uimm, &ARM64XEmitter::AND, &ARM64XEmitter::TryANDI2R, &EvalAnd); break;
107
case 13: CompImmLogic(rs, rt, uimm, &ARM64XEmitter::ORR, &ARM64XEmitter::TryORRI2R, &EvalOr); break;
108
case 14: CompImmLogic(rs, rt, uimm, &ARM64XEmitter::EOR, &ARM64XEmitter::TryEORI2R, &EvalEor); break;
109
110
case 10: // R(rt) = (s32)R(rs) < simm; break; //slti
111
if (gpr.IsImm(rs)) {
112
gpr.SetImm(rt, (s32)gpr.GetImm(rs) < simm ? 1 : 0);
113
break;
114
} else if (simm == 0) {
115
gpr.MapDirtyIn(rt, rs);
116
// Grab the sign bit (< 0) as 1/0. Slightly faster than a shift.
117
UBFX(gpr.R(rt), gpr.R(rs), 31, 1);
118
break;
119
}
120
gpr.MapDirtyIn(rt, rs);
121
if (!TryCMPI2R(gpr.R(rs), (u32)simm)) {
122
gpr.SetRegImm(SCRATCH1, simm);
123
CMP(gpr.R(rs), SCRATCH1);
124
}
125
CSET(gpr.R(rt), CC_LT);
126
break;
127
128
case 11: // R(rt) = R(rs) < suimm; break; //sltiu
129
if (gpr.IsImm(rs)) {
130
gpr.SetImm(rt, gpr.GetImm(rs) < suimm ? 1 : 0);
131
break;
132
}
133
gpr.MapDirtyIn(rt, rs);
134
if (!TryCMPI2R(gpr.R(rs), suimm)) {
135
gpr.SetRegImm(SCRATCH1, suimm);
136
CMP(gpr.R(rs), SCRATCH1);
137
}
138
CSET(gpr.R(rt), CC_LO);
139
break;
140
141
case 15: // R(rt) = uimm << 16; //lui
142
gpr.SetImm(rt, uimm << 16);
143
break;
144
145
default:
146
Comp_Generic(op);
147
break;
148
}
149
}
150
151
void Arm64Jit::Comp_RType2(MIPSOpcode op) {
152
CONDITIONAL_DISABLE(ALU_BIT);
153
154
MIPSGPReg rs = _RS;
155
MIPSGPReg rd = _RD;
156
157
// Don't change $zr.
158
if (rd == 0)
159
return;
160
161
switch (op & 63) {
162
case 22: //clz
163
if (gpr.IsImm(rs)) {
164
u32 value = gpr.GetImm(rs);
165
int x = 31;
166
int count = 0;
167
while (x >= 0 && !(value & (1 << x))) {
168
count++;
169
x--;
170
}
171
gpr.SetImm(rd, count);
172
break;
173
}
174
gpr.MapDirtyIn(rd, rs);
175
CLZ(gpr.R(rd), gpr.R(rs));
176
break;
177
case 23: //clo
178
if (gpr.IsImm(rs)) {
179
u32 value = gpr.GetImm(rs);
180
int x = 31;
181
int count = 0;
182
while (x >= 0 && (value & (1 << x))) {
183
count++;
184
x--;
185
}
186
gpr.SetImm(rd, count);
187
break;
188
}
189
gpr.MapDirtyIn(rd, rs);
190
MVN(gpr.R(rd), gpr.R(rs));
191
CLZ(gpr.R(rd), gpr.R(rd));
192
break;
193
default:
194
DISABLE;
195
}
196
}
197
198
void Arm64Jit::CompType3(MIPSGPReg rd, MIPSGPReg rs, MIPSGPReg rt, void (ARM64XEmitter::*arith)(ARM64Reg dst, ARM64Reg rm, ARM64Reg rn), bool (ARM64XEmitter::*tryArithI2R)(ARM64Reg dst, ARM64Reg rm, u64 val), u32(*eval)(u32 a, u32 b), bool symmetric) {
199
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
200
gpr.SetImm(rd, (*eval)(gpr.GetImm(rs), gpr.GetImm(rt)));
201
return;
202
}
203
204
// Optimize anything against zero.
205
if (gpr.IsImm(rs) && gpr.GetImm(rs) == 0) {
206
gpr.MapDirtyIn(rd, rt);
207
(this->*arith)(gpr.R(rd), WZR, gpr.R(rt));
208
return;
209
}
210
if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0) {
211
gpr.MapDirtyIn(rd, rs);
212
(this->*arith)(gpr.R(rd), gpr.R(rs), WZR);
213
return;
214
}
215
216
if (gpr.IsImm(rt) || (gpr.IsImm(rs) && symmetric)) {
217
MIPSGPReg lhs = gpr.IsImm(rs) ? rt : rs;
218
MIPSGPReg rhs = gpr.IsImm(rs) ? rs : rt;
219
u32 rhsImm = gpr.GetImm(rhs);
220
gpr.MapDirtyIn(rd, lhs);
221
if ((this->*tryArithI2R)(gpr.R(rd), gpr.R(lhs), rhsImm)) {
222
return;
223
}
224
// If rd is rhs, we may have lost it in the MapDirtyIn(). lhs was kept.
225
// This means the rhsImm value was never flushed to rhs, and would be garbage.
226
if (rd == rhs) {
227
// Luckily, it was just an imm.
228
gpr.SetImm(rhs, rhsImm);
229
}
230
}
231
232
// Can't do the RSB optimization on ARM64 - no RSB!
233
234
// Generic solution. If it's an imm, better to flush at this point.
235
gpr.MapDirtyInIn(rd, rs, rt);
236
(this->*arith)(gpr.R(rd), gpr.R(rs), gpr.R(rt));
237
}
238
239
void Arm64Jit::Comp_RType3(MIPSOpcode op) {
240
CONDITIONAL_DISABLE(ALU);
241
242
MIPSGPReg rt = _RT;
243
MIPSGPReg rs = _RS;
244
MIPSGPReg rd = _RD;
245
246
// noop, won't write to ZERO.
247
if (rd == 0)
248
return;
249
250
switch (op & 63) {
251
case 10: //if (!R(rt)) R(rd) = R(rs); break; //movz
252
gpr.MapDirtyInIn(rd, rt, rs, false);
253
CMP(gpr.R(rt), 0);
254
CSEL(gpr.R(rd), gpr.R(rs), gpr.R(rd), CC_EQ);
255
break;
256
case 11:// if (R(rt)) R(rd) = R(rs); break; //movn
257
gpr.MapDirtyInIn(rd, rt, rs, false);
258
CMP(gpr.R(rt), 0);
259
CSEL(gpr.R(rd), gpr.R(rs), gpr.R(rd), CC_NEQ);
260
break;
261
262
case 32: //R(rd) = R(rs) + R(rt); break; //add
263
case 33: //R(rd) = R(rs) + R(rt); break; //addu
264
if (gpr.IsImm(rs) && gpr.GetImm(rs) == 0 && !gpr.IsImm(rt)) {
265
// Special case: actually a mov, avoid arithmetic.
266
gpr.MapDirtyIn(rd, rt);
267
MOV(gpr.R(rd), gpr.R(rt));
268
} else if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0 && !gpr.IsImm(rs)) {
269
gpr.MapDirtyIn(rd, rs);
270
MOV(gpr.R(rd), gpr.R(rs));
271
} else {
272
CompType3(rd, rs, rt, &ARM64XEmitter::ADD, &ARM64XEmitter::TryADDI2R, &EvalAdd, true);
273
}
274
break;
275
276
case 34: //R(rd) = R(rs) - R(rt); break; //sub
277
case 35: //R(rd) = R(rs) - R(rt); break; //subu
278
CompType3(rd, rs, rt, &ARM64XEmitter::SUB, &ARM64XEmitter::TrySUBI2R, &EvalSub, false);
279
break;
280
281
case 36: //R(rd) = R(rs) & R(rt); break; //and
282
CompType3(rd, rs, rt, &ARM64XEmitter::AND, &ARM64XEmitter::TryANDI2R, &EvalAnd, true);
283
break;
284
case 37: //R(rd) = R(rs) | R(rt); break; //or
285
CompType3(rd, rs, rt, &ARM64XEmitter::ORR, &ARM64XEmitter::TryORRI2R, &EvalOr, true);
286
break;
287
case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor
288
CompType3(rd, rs, rt, &ARM64XEmitter::EOR, &ARM64XEmitter::TryEORI2R, &EvalEor, true);
289
break;
290
291
case 39: // R(rd) = ~(R(rs) | R(rt)); break; //nor
292
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
293
gpr.SetImm(rd, ~(gpr.GetImm(rs) | gpr.GetImm(rt)));
294
} else if (gpr.IsImm(rs) || gpr.IsImm(rt)) {
295
MIPSGPReg lhs = gpr.IsImm(rs) ? rt : rs;
296
MIPSGPReg rhs = gpr.IsImm(rs) ? rs : rt;
297
u32 rhsImm = gpr.GetImm(rhs);
298
if (rhsImm == 0) {
299
gpr.MapDirtyIn(rd, lhs);
300
MVN(gpr.R(rd), gpr.R(lhs));
301
} else {
302
// Ignored, just for IsImmLogical.
303
unsigned int n, imm_s, imm_r;
304
if (IsImmLogical(rhsImm, 32, &n, &imm_s, &imm_r)) {
305
// Great, we can avoid flushing a reg.
306
gpr.MapDirtyIn(rd, lhs);
307
ORRI2R(gpr.R(rd), gpr.R(lhs), rhsImm);
308
} else {
309
gpr.MapDirtyInIn(rd, rs, rt);
310
ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt));
311
}
312
MVN(gpr.R(rd), gpr.R(rd));
313
}
314
} else {
315
gpr.MapDirtyInIn(rd, rs, rt);
316
ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt));
317
MVN(gpr.R(rd), gpr.R(rd));
318
}
319
break;
320
321
case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt
322
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
323
gpr.SetImm(rd, (s32)gpr.GetImm(rs) < (s32)gpr.GetImm(rt));
324
} else {
325
gpr.MapDirtyInIn(rd, rs, rt);
326
CMP(gpr.R(rs), gpr.R(rt));
327
CSET(gpr.R(rd), CC_LT);
328
}
329
break;
330
331
case 43: //R(rd) = R(rs) < R(rt); break; //sltu
332
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
333
gpr.SetImm(rd, gpr.GetImm(rs) < gpr.GetImm(rt));
334
} else {
335
gpr.MapDirtyInIn(rd, rs, rt);
336
CMP(gpr.R(rs), gpr.R(rt));
337
CSET(gpr.R(rd), CC_LO);
338
}
339
break;
340
341
case 44: //R(rd) = max(R(rs), R(rt); break; //max
342
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
343
gpr.SetImm(rd, std::max(gpr.GetImm(rs), gpr.GetImm(rt)));
344
break;
345
}
346
gpr.MapDirtyInIn(rd, rs, rt);
347
CMP(gpr.R(rs), gpr.R(rt));
348
CSEL(gpr.R(rd), gpr.R(rs), gpr.R(rt), CC_GE);
349
break;
350
351
case 45: //R(rd) = min(R(rs), R(rt)); break; //min
352
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
353
gpr.SetImm(rd, std::min(gpr.GetImm(rs), gpr.GetImm(rt)));
354
break;
355
}
356
gpr.MapDirtyInIn(rd, rs, rt);
357
CMP(gpr.R(rs), gpr.R(rt));
358
CSEL(gpr.R(rd), gpr.R(rs), gpr.R(rt), CC_LE);
359
break;
360
361
default:
362
Comp_Generic(op);
363
break;
364
}
365
}
366
367
void Arm64Jit::CompShiftImm(MIPSOpcode op, Arm64Gen::ShiftType shiftType, int sa) {
368
MIPSGPReg rd = _RD;
369
MIPSGPReg rt = _RT;
370
if (gpr.IsImm(rt)) {
371
switch (shiftType) {
372
case ST_LSL:
373
gpr.SetImm(rd, gpr.GetImm(rt) << sa);
374
break;
375
case ST_LSR:
376
gpr.SetImm(rd, gpr.GetImm(rt) >> sa);
377
break;
378
case ST_ASR:
379
gpr.SetImm(rd, (int)gpr.GetImm(rt) >> sa);
380
break;
381
case ST_ROR:
382
gpr.SetImm(rd, (gpr.GetImm(rt) >> sa) | (gpr.GetImm(rt) << (32 - sa)));
383
break;
384
default:
385
DISABLE;
386
}
387
} else {
388
gpr.MapDirtyIn(rd, rt);
389
MOV(gpr.R(rd), gpr.R(rt), ArithOption(gpr.R(rd), shiftType, sa));
390
}
391
}
392
393
void Arm64Jit::CompShiftVar(MIPSOpcode op, Arm64Gen::ShiftType shiftType) {
394
MIPSGPReg rd = _RD;
395
MIPSGPReg rt = _RT;
396
MIPSGPReg rs = _RS;
397
if (gpr.IsImm(rs)) {
398
int sa = gpr.GetImm(rs) & 0x1F;
399
CompShiftImm(op, shiftType, sa);
400
return;
401
}
402
gpr.MapDirtyInIn(rd, rs, rt);
403
switch (shiftType) {
404
case ST_LSL: LSLV(gpr.R(rd), gpr.R(rt), gpr.R(rs)); break;
405
case ST_LSR: LSRV(gpr.R(rd), gpr.R(rt), gpr.R(rs)); break;
406
case ST_ASR: ASRV(gpr.R(rd), gpr.R(rt), gpr.R(rs)); break;
407
case ST_ROR: RORV(gpr.R(rd), gpr.R(rt), gpr.R(rs)); break;
408
}
409
}
410
411
void Arm64Jit::Comp_ShiftType(MIPSOpcode op) {
412
CONDITIONAL_DISABLE(ALU);
413
MIPSGPReg rs = _RS;
414
MIPSGPReg rd = _RD;
415
int fd = _FD;
416
int sa = _SA;
417
418
// noop, won't write to ZERO.
419
if (rd == 0)
420
return;
421
422
// WARNING : ROTR
423
switch (op & 0x3f) {
424
case 0: CompShiftImm(op, ST_LSL, sa); break; //sll
425
case 2: CompShiftImm(op, rs == 1 ? ST_ROR : ST_LSR, sa); break; //srl
426
case 3: CompShiftImm(op, ST_ASR, sa); break; //sra
427
case 4: CompShiftVar(op, ST_LSL); break; //sllv
428
case 6: CompShiftVar(op, fd == 1 ? ST_ROR : ST_LSR); break; //srlv
429
case 7: CompShiftVar(op, ST_ASR); break; //srav
430
default:
431
DISABLE;
432
break;
433
}
434
}
435
436
void Arm64Jit::Comp_Special3(MIPSOpcode op) {
437
CONDITIONAL_DISABLE(ALU_BIT);
438
MIPSGPReg rs = _RS;
439
MIPSGPReg rt = _RT;
440
441
int pos = _POS;
442
int size = _SIZE + 1;
443
u32 mask = 0xFFFFFFFFUL >> (32 - size);
444
445
// Don't change $zr.
446
if (rt == 0)
447
return;
448
449
switch (op & 0x3f) {
450
case 0x0: //ext
451
if (gpr.IsImm(rs)) {
452
gpr.SetImm(rt, (gpr.GetImm(rs) >> pos) & mask);
453
return;
454
}
455
456
gpr.MapDirtyIn(rt, rs);
457
UBFX(gpr.R(rt), gpr.R(rs), pos, size);
458
break;
459
460
case 0x4: //ins
461
{
462
u32 sourcemask = mask >> pos;
463
u32 destmask = ~(sourcemask << pos);
464
if (gpr.IsImm(rs)) {
465
u32 inserted = (gpr.GetImm(rs) & sourcemask) << pos;
466
if (gpr.IsImm(rt)) {
467
gpr.SetImm(rt, (gpr.GetImm(rt) & destmask) | inserted);
468
return;
469
}
470
471
// It might be nice to avoid flushing rs, but it's a little slower and
472
// usually more instructions. Not worth it.
473
gpr.MapDirtyIn(rt, rs, false);
474
BFI(gpr.R(rt), gpr.R(rs), pos, size - pos);
475
} else {
476
gpr.MapDirtyIn(rt, rs, false);
477
BFI(gpr.R(rt), gpr.R(rs), pos, size - pos);
478
}
479
}
480
break;
481
}
482
}
483
484
void Arm64Jit::Comp_Allegrex(MIPSOpcode op) {
485
CONDITIONAL_DISABLE(ALU_BIT);
486
MIPSGPReg rt = _RT;
487
MIPSGPReg rd = _RD;
488
// Don't change $zr.
489
if (rd == 0)
490
return;
491
492
switch ((op >> 6) & 31) {
493
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
494
if (gpr.IsImm(rt)) {
495
gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt)));
496
return;
497
}
498
gpr.MapDirtyIn(rd, rt);
499
SXTB(gpr.R(rd), gpr.R(rt));
500
break;
501
502
case 24: // seh
503
if (gpr.IsImm(rt)) {
504
gpr.SetImm(rd, SignExtend16ToU32(gpr.GetImm(rt)));
505
return;
506
}
507
gpr.MapDirtyIn(rd, rt);
508
SXTH(gpr.R(rd), gpr.R(rt));
509
break;
510
511
case 20: //bitrev
512
if (gpr.IsImm(rt)) {
513
// http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
514
u32 v = gpr.GetImm(rt);
515
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // odd<->even
516
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); // pair<->pair
517
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); // nibb<->nibb
518
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); // byte<->byte
519
v = (v >> 16) | (v << 16); // hword<->hword
520
gpr.SetImm(rd, v);
521
return;
522
}
523
524
gpr.MapDirtyIn(rd, rt);
525
RBIT(gpr.R(rd), gpr.R(rt));
526
break;
527
528
default:
529
Comp_Generic(op);
530
return;
531
}
532
}
533
534
void Arm64Jit::Comp_Allegrex2(MIPSOpcode op) {
535
CONDITIONAL_DISABLE(ALU_BIT);
536
MIPSGPReg rt = _RT;
537
MIPSGPReg rd = _RD;
538
// Don't change $zr.
539
if (rd == 0)
540
return;
541
542
switch (op & 0x3ff) {
543
case 0xA0: //wsbh
544
if (gpr.IsImm(rt)) {
545
gpr.SetImm(rd, ((gpr.GetImm(rt) & 0xFF00FF00) >> 8) | ((gpr.GetImm(rt) & 0x00FF00FF) << 8));
546
} else {
547
gpr.MapDirtyIn(rd, rt);
548
REV16(gpr.R(rd), gpr.R(rt));
549
}
550
break;
551
case 0xE0: //wsbw
552
if (gpr.IsImm(rt)) {
553
gpr.SetImm(rd, swap32(gpr.GetImm(rt)));
554
} else {
555
gpr.MapDirtyIn(rd, rt);
556
REV32(gpr.R(rd), gpr.R(rt));
557
}
558
break;
559
default:
560
Comp_Generic(op);
561
break;
562
}
563
}
564
565
void Arm64Jit::Comp_MulDivType(MIPSOpcode op) {
566
CONDITIONAL_DISABLE(MULDIV);
567
MIPSGPReg rt = _RT;
568
MIPSGPReg rs = _RS;
569
MIPSGPReg rd = _RD;
570
571
// Note that in all cases below, LO is actually mapped to HI:LO.
572
// That is, the host reg is 64 bits and has HI at the top.
573
// HI is not mappable.
574
575
switch (op & 63) {
576
case 16: // R(rd) = HI; //mfhi
577
// LO and HI are in the same reg.
578
if (gpr.IsImm(MIPS_REG_LO)) {
579
gpr.SetImm(rd, gpr.GetImm(MIPS_REG_LO) >> 32);
580
break;
581
}
582
gpr.MapDirtyIn(rd, MIPS_REG_LO);
583
UBFX(EncodeRegTo64(gpr.R(rd)), EncodeRegTo64(gpr.R(MIPS_REG_LO)), 32, 32);
584
break;
585
586
case 17: // HI = R(rs); //mthi
587
if (gpr.IsImm(rs) && gpr.IsImm(MIPS_REG_LO)) {
588
gpr.SetImm(MIPS_REG_LO, (gpr.GetImm(rs) << 32) | (gpr.GetImm(MIPS_REG_LO) & 0xFFFFFFFFULL));
589
break;
590
}
591
gpr.MapDirtyIn(MIPS_REG_LO, rs, false);
592
BFI(EncodeRegTo64(gpr.R(MIPS_REG_LO)), EncodeRegTo64(gpr.R(rs)), 32, 32);
593
break;
594
595
case 18: // R(rd) = LO; break; //mflo
596
if (gpr.IsImm(MIPS_REG_LO)) {
597
gpr.SetImm(rd, gpr.GetImm(MIPS_REG_LO) & 0xFFFFFFFFULL);
598
break;
599
}
600
gpr.MapDirtyIn(rd, MIPS_REG_LO);
601
MOV(gpr.R(rd), gpr.R(MIPS_REG_LO));
602
break;
603
604
case 19: // LO = R(rs); break; //mtlo
605
if (gpr.IsImm(rs) && gpr.IsImm(MIPS_REG_LO)) {
606
gpr.SetImm(MIPS_REG_LO, gpr.GetImm(rs) | (gpr.GetImm(MIPS_REG_LO) & ~0xFFFFFFFFULL));
607
break;
608
}
609
gpr.MapDirtyIn(MIPS_REG_LO, rs, false);
610
BFI(EncodeRegTo64(gpr.R(MIPS_REG_LO)), EncodeRegTo64(gpr.R(rs)), 0, 32);
611
break;
612
613
case 24: //mult (the most popular one). lo,hi = signed mul (rs * rt)
614
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
615
s64 result = (s64)(s32)gpr.GetImm(rs) * (s64)(s32)gpr.GetImm(rt);
616
gpr.SetImm(MIPS_REG_LO, (u64)result);
617
break;
618
}
619
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt);
620
SMULL(EncodeRegTo64(gpr.R(MIPS_REG_LO)), gpr.R(rs), gpr.R(rt));
621
break;
622
623
case 25: //multu (2nd) lo,hi = unsigned mul (rs * rt)
624
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
625
u64 resultBits = (u64)gpr.GetImm(rs) * (u64)gpr.GetImm(rt);
626
gpr.SetImm(MIPS_REG_LO, resultBits);
627
break;
628
}
629
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt);
630
// In case of pointerification, let's use UMULL.
631
UMULL(EncodeRegTo64(gpr.R(MIPS_REG_LO)), gpr.R(rs), gpr.R(rt));
632
break;
633
634
case 26: //div
635
{
636
// TODO: Does this handle INT_MAX, 0, etc. correctly?
637
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt);
638
SDIV(gpr.R(MIPS_REG_LO), gpr.R(rs), gpr.R(rt));
639
MSUB(SCRATCH1, gpr.R(rt), gpr.R(MIPS_REG_LO), gpr.R(rs));
640
641
CMPI2R(gpr.R(rt), 0);
642
FixupBranch skipZero = B(CC_NEQ);
643
// HI set properly already, we just need to set LO.
644
MOVI2R(gpr.R(MIPS_REG_LO), -1);
645
CMPI2R(gpr.R(rs), 0);
646
FixupBranch moreThan16Bit = B(CC_GE);
647
MOVI2R(gpr.R(MIPS_REG_LO), 1);
648
SetJumpTarget(moreThan16Bit);
649
SetJumpTarget(skipZero);
650
651
BFI(EncodeRegTo64(gpr.R(MIPS_REG_LO)), SCRATCH1_64, 32, 32);
652
break;
653
}
654
655
case 27: //divu
656
// Do we have a known power-of-two denominator? Yes, this happens.
657
if (gpr.IsImm(rt) && (gpr.GetImm(rt) & (gpr.GetImm(rt) - 1)) == 0 && gpr.GetImm(rt) != 0) {
658
u32 denominator = gpr.GetImm(rt);
659
gpr.MapDirtyIn(MIPS_REG_LO, rs);
660
// Remainder is just an AND, neat.
661
ANDI2R(SCRATCH1, gpr.R(rs), denominator - 1, SCRATCH1);
662
int shift = 0;
663
while (denominator != 0) {
664
++shift;
665
denominator >>= 1;
666
}
667
// The shift value is one too much for the divide by the same value.
668
if (shift > 1) {
669
LSR(gpr.R(MIPS_REG_LO), gpr.R(rs), shift - 1);
670
} else {
671
MOV(gpr.R(MIPS_REG_LO), gpr.R(rs));
672
}
673
BFI(EncodeRegTo64(gpr.R(MIPS_REG_LO)), SCRATCH1_64, 32, 32);
674
} else {
675
// TODO: Does this handle INT_MAX, 0, etc. correctly?
676
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt);
677
UDIV(gpr.R(MIPS_REG_LO), gpr.R(rs), gpr.R(rt));
678
MSUB(SCRATCH1, gpr.R(rt), gpr.R(MIPS_REG_LO), gpr.R(rs));
679
680
CMPI2R(gpr.R(rt), 0);
681
FixupBranch skipZero = B(CC_NEQ);
682
// HI set properly, we just need to set LO.
683
MOVI2R(SCRATCH2, 0xFFFF);
684
MOVI2R(gpr.R(MIPS_REG_LO), -1);
685
CMP(gpr.R(rs), SCRATCH2);
686
FixupBranch moreThan16Bit = B(CC_HI);
687
MOV(gpr.R(MIPS_REG_LO), SCRATCH2);
688
SetJumpTarget(moreThan16Bit);
689
SetJumpTarget(skipZero);
690
691
BFI(EncodeRegTo64(gpr.R(MIPS_REG_LO)), SCRATCH1_64, 32, 32);
692
}
693
break;
694
695
case 28: //madd
696
{
697
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt, false);
698
ARM64Reg lo64 = EncodeRegTo64(gpr.R(MIPS_REG_LO));
699
SMADDL(lo64, gpr.R(rs), gpr.R(rt), lo64); // Operands are backwards in the emitter!
700
}
701
break;
702
703
case 29: //maddu
704
{
705
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt, false);
706
ARM64Reg lo64 = EncodeRegTo64(gpr.R(MIPS_REG_LO));
707
UMADDL(lo64, gpr.R(rs), gpr.R(rt), lo64); // Operands are backwards in the emitter!
708
}
709
break;
710
711
case 46: // msub
712
{
713
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt, false);
714
ARM64Reg lo64 = EncodeRegTo64(gpr.R(MIPS_REG_LO));
715
SMSUBL(lo64, gpr.R(rs), gpr.R(rt), lo64); // Operands are backwards in the emitter!
716
}
717
break;
718
719
case 47: // msubu
720
{
721
gpr.MapDirtyInIn(MIPS_REG_LO, rs, rt, false);
722
ARM64Reg lo64 = EncodeRegTo64(gpr.R(MIPS_REG_LO));
723
UMSUBL(lo64, gpr.R(rs), gpr.R(rt), lo64); // Operands are backwards in the emitter!
724
break;
725
}
726
727
default:
728
DISABLE;
729
}
730
}
731
732
}
733
734
#endif // PPSSPP_ARCH(ARM64)
735
736