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/ARM/ArmCompALU.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(ARM)
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/ARM/ArmJit.h"
30
#include "Core/MIPS/ARM/ArmRegCache.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
{
55
using namespace ArmGen;
56
using namespace ArmJitConstants;
57
58
static u32 EvalOr(u32 a, u32 b) { return a | b; }
59
static u32 EvalEor(u32 a, u32 b) { return a ^ b; }
60
static u32 EvalAnd(u32 a, u32 b) { return a & b; }
61
static u32 EvalAdd(u32 a, u32 b) { return a + b; }
62
static u32 EvalSub(u32 a, u32 b) { return a - b; }
63
64
void ArmJit::CompImmLogic(MIPSGPReg rs, MIPSGPReg rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), bool (ARMXEmitter::*tryArithI2R)(ARMReg dst, ARMReg src, u32 val), u32 (*eval)(u32 a, u32 b))
65
{
66
if (gpr.IsImm(rs)) {
67
gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm));
68
} else {
69
gpr.MapDirtyIn(rt, rs);
70
if (!(this->*tryArithI2R)(gpr.R(rt), gpr.R(rs), uimm)) {
71
gpr.SetRegImm(SCRATCHREG1, uimm);
72
(this->*arith)(gpr.R(rt), gpr.R(rs), SCRATCHREG1);
73
}
74
}
75
}
76
77
void ArmJit::Comp_IType(MIPSOpcode op)
78
{
79
CONDITIONAL_DISABLE(ALU_IMM);
80
u32 uimm = op & 0xFFFF;
81
s32 simm = SignExtend16ToS32(op);
82
u32 suimm = SignExtend16ToU32(op);
83
84
MIPSGPReg rt = _RT;
85
MIPSGPReg rs = _RS;
86
87
// noop, won't write to ZERO.
88
if (rt == 0)
89
return;
90
91
switch (op >> 26)
92
{
93
case 8: // same as addiu?
94
case 9: // R(rt) = R(rs) + simm; break; //addiu
95
CompImmLogic(rs, rt, simm, &ARMXEmitter::ADD, &ARMXEmitter::TryADDI2R, &EvalAdd);
96
break;
97
98
case 12: CompImmLogic(rs, rt, uimm, &ARMXEmitter::AND, &ARMXEmitter::TryANDI2R, &EvalAnd); break;
99
case 13: CompImmLogic(rs, rt, uimm, &ARMXEmitter::ORR, &ARMXEmitter::TryORI2R, &EvalOr); break;
100
case 14: CompImmLogic(rs, rt, uimm, &ARMXEmitter::EOR, &ARMXEmitter::TryEORI2R, &EvalEor); break;
101
102
case 10: // R(rt) = (s32)R(rs) < simm; break; //slti
103
{
104
if (gpr.IsImm(rs)) {
105
gpr.SetImm(rt, (s32)gpr.GetImm(rs) < simm ? 1 : 0);
106
break;
107
} else if (simm == 0) {
108
gpr.MapDirtyIn(rt, rs);
109
// Shift to get the sign bit only (for < 0.)
110
LSR(gpr.R(rt), gpr.R(rs), 31);
111
break;
112
}
113
gpr.MapDirtyIn(rt, rs);
114
if (!TryCMPI2R(gpr.R(rs), simm)) {
115
gpr.SetRegImm(SCRATCHREG1, simm);
116
CMP(gpr.R(rs), SCRATCHREG1);
117
}
118
SetCC(CC_LT);
119
MOVI2R(gpr.R(rt), 1);
120
SetCC(CC_GE);
121
MOVI2R(gpr.R(rt), 0);
122
SetCC(CC_AL);
123
}
124
break;
125
126
case 11: // R(rt) = R(rs) < suimm; break; //sltiu
127
{
128
if (gpr.IsImm(rs)) {
129
gpr.SetImm(rt, gpr.GetImm(rs) < suimm ? 1 : 0);
130
break;
131
}
132
gpr.MapDirtyIn(rt, rs);
133
if (!TryCMPI2R(gpr.R(rs), suimm)) {
134
gpr.SetRegImm(SCRATCHREG1, suimm);
135
CMP(gpr.R(rs), SCRATCHREG1);
136
}
137
SetCC(CC_LO);
138
MOVI2R(gpr.R(rt), 1);
139
SetCC(CC_HS);
140
MOVI2R(gpr.R(rt), 0);
141
SetCC(CC_AL);
142
}
143
break;
144
145
case 15: // R(rt) = uimm << 16; //lui
146
gpr.SetImm(rt, uimm << 16);
147
break;
148
149
default:
150
Comp_Generic(op);
151
break;
152
}
153
}
154
155
void ArmJit::Comp_RType2(MIPSOpcode op)
156
{
157
CONDITIONAL_DISABLE(ALU_BIT);
158
MIPSGPReg rs = _RS;
159
MIPSGPReg rd = _RD;
160
161
// Don't change $zr.
162
if (rd == 0)
163
return;
164
165
switch (op & 63)
166
{
167
case 22: //clz
168
if (gpr.IsImm(rs)) {
169
u32 value = gpr.GetImm(rs);
170
int x = 31;
171
int count = 0;
172
while (x >= 0 && !(value & (1 << x))) {
173
count++;
174
x--;
175
}
176
gpr.SetImm(rd, count);
177
break;
178
}
179
gpr.MapDirtyIn(rd, rs);
180
CLZ(gpr.R(rd), gpr.R(rs));
181
break;
182
case 23: //clo
183
if (gpr.IsImm(rs)) {
184
u32 value = gpr.GetImm(rs);
185
int x = 31;
186
int count = 0;
187
while (x >= 0 && (value & (1 << x))) {
188
count++;
189
x--;
190
}
191
gpr.SetImm(rd, count);
192
break;
193
}
194
gpr.MapDirtyIn(rd, rs);
195
MVN(SCRATCHREG1, gpr.R(rs));
196
CLZ(gpr.R(rd), SCRATCHREG1);
197
break;
198
default:
199
DISABLE;
200
}
201
}
202
203
void ArmJit::CompType3(MIPSGPReg rd, MIPSGPReg rs, MIPSGPReg rt, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg rm, Operand2 rn), bool (ARMXEmitter::*tryArithI2R)(ARMReg dst, ARMReg rm, u32 val), u32 (*eval)(u32 a, u32 b), bool symmetric)
204
{
205
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
206
gpr.SetImm(rd, (*eval)(gpr.GetImm(rs), gpr.GetImm(rt)));
207
return;
208
}
209
210
if (gpr.IsImm(rt) || (gpr.IsImm(rs) && symmetric)) {
211
MIPSGPReg lhs = gpr.IsImm(rs) ? rt : rs;
212
MIPSGPReg rhs = gpr.IsImm(rs) ? rs : rt;
213
u32 rhsImm = gpr.GetImm(rhs);
214
gpr.MapDirtyIn(rd, lhs);
215
if ((this->*tryArithI2R)(gpr.R(rd), gpr.R(lhs), rhsImm)) {
216
return;
217
}
218
// If rd is rhs, we may have lost it in the MapDirtyIn(). lhs was kept.
219
// This means the rhsImm value was never flushed to rhs, and would be garbage.
220
if (rd == rhs) {
221
// Luckily, it was just an imm.
222
gpr.SetImm(rhs, rhsImm);
223
}
224
} else if (gpr.IsImm(rs) && !symmetric) {
225
Operand2 op2;
226
// For SUB, we can use RSB as a reverse operation.
227
if (eval == &EvalSub && TryMakeOperand2(gpr.GetImm(rs), op2)) {
228
gpr.MapDirtyIn(rd, rt);
229
RSB(gpr.R(rd), gpr.R(rt), op2);
230
return;
231
}
232
}
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 ArmJit::Comp_RType3(MIPSOpcode op)
240
{
241
CONDITIONAL_DISABLE(ALU);
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
{
252
case 10: //if (!R(rt)) R(rd) = R(rs); break; //movz
253
if (rd == rs || (gpr.IsImm(rd) && gpr.IsImm(rs) && gpr.GetImm(rd) == gpr.GetImm(rs)))
254
break;
255
if (!gpr.IsImm(rt)) {
256
Operand2 op2;
257
// Avoid flushing the imm if possible.
258
if (gpr.IsImm(rs) && TryMakeOperand2(gpr.GetImm(rs), op2)) {
259
gpr.MapDirtyIn(rd, rt, false);
260
} else {
261
gpr.MapDirtyInIn(rd, rt, rs, false);
262
op2 = gpr.R(rs);
263
}
264
CMP(gpr.R(rt), Operand2(0));
265
SetCC(CC_EQ);
266
MOV(gpr.R(rd), op2);
267
SetCC(CC_AL);
268
} else if (gpr.GetImm(rt) == 0) {
269
// Yes, this actually happens.
270
if (gpr.IsImm(rs)) {
271
gpr.SetImm(rd, gpr.GetImm(rs));
272
} else {
273
gpr.MapDirtyIn(rd, rs);
274
MOV(gpr.R(rd), gpr.R(rs));
275
}
276
}
277
break;
278
case 11:// if (R(rt)) R(rd) = R(rs); break; //movn
279
if (rd == rs || (gpr.IsImm(rd) && gpr.IsImm(rs) && gpr.GetImm(rd) == gpr.GetImm(rs)))
280
break;
281
if (!gpr.IsImm(rt)) {
282
Operand2 op2;
283
// Avoid flushing the imm if possible.
284
if (gpr.IsImm(rs) && TryMakeOperand2(gpr.GetImm(rs), op2)) {
285
gpr.MapDirtyIn(rd, rt, false);
286
} else {
287
gpr.MapDirtyInIn(rd, rt, rs, false);
288
op2 = gpr.R(rs);
289
}
290
CMP(gpr.R(rt), Operand2(0));
291
SetCC(CC_NEQ);
292
MOV(gpr.R(rd), op2);
293
SetCC(CC_AL);
294
} else if (gpr.GetImm(rt) != 0) {
295
// Yes, this actually happens.
296
if (gpr.IsImm(rs)) {
297
gpr.SetImm(rd, gpr.GetImm(rs));
298
} else {
299
gpr.MapDirtyIn(rd, rs);
300
MOV(gpr.R(rd), gpr.R(rs));
301
}
302
}
303
break;
304
305
case 32: //R(rd) = R(rs) + R(rt); break; //add
306
case 33: //R(rd) = R(rs) + R(rt); break; //addu
307
// We optimize out 0 as an operand2 ADD.
308
CompType3(rd, rs, rt, &ARMXEmitter::ADD, &ARMXEmitter::TryADDI2R, &EvalAdd, true);
309
break;
310
311
case 34: //R(rd) = R(rs) - R(rt); break; //sub
312
case 35: //R(rd) = R(rs) - R(rt); break; //subu
313
CompType3(rd, rs, rt, &ARMXEmitter::SUB, &ARMXEmitter::TrySUBI2R, &EvalSub, false);
314
break;
315
case 36: //R(rd) = R(rs) & R(rt); break; //and
316
CompType3(rd, rs, rt, &ARMXEmitter::AND, &ARMXEmitter::TryANDI2R, &EvalAnd, true);
317
break;
318
case 37: //R(rd) = R(rs) | R(rt); break; //or
319
CompType3(rd, rs, rt, &ARMXEmitter::ORR, &ARMXEmitter::TryORI2R, &EvalOr, true);
320
break;
321
case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor
322
CompType3(rd, rs, rt, &ARMXEmitter::EOR, &ARMXEmitter::TryEORI2R, &EvalEor, true);
323
break;
324
325
case 39: // R(rd) = ~(R(rs) | R(rt)); break; //nor
326
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
327
gpr.SetImm(rd, ~(gpr.GetImm(rs) | gpr.GetImm(rt)));
328
} else if (gpr.IsImm(rs) || gpr.IsImm(rt)) {
329
MIPSGPReg lhs = gpr.IsImm(rs) ? rt : rs;
330
MIPSGPReg rhs = gpr.IsImm(rs) ? rs : rt;
331
u32 rhsImm = gpr.GetImm(rhs);
332
Operand2 op2;
333
if (TryMakeOperand2(rhsImm, op2)) {
334
gpr.MapDirtyIn(rd, lhs);
335
} else {
336
gpr.MapDirtyInIn(rd, rs, rt);
337
op2 = gpr.R(rhs);
338
}
339
if (rhsImm == 0) {
340
MVN(gpr.R(rd), gpr.R(lhs));
341
} else {
342
ORR(gpr.R(rd), gpr.R(lhs), op2);
343
MVN(gpr.R(rd), gpr.R(rd));
344
}
345
} else {
346
gpr.MapDirtyInIn(rd, rs, rt);
347
ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt));
348
MVN(gpr.R(rd), gpr.R(rd));
349
}
350
break;
351
352
case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt
353
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
354
gpr.SetImm(rd, (s32)gpr.GetImm(rs) < (s32)gpr.GetImm(rt));
355
} else {
356
CCFlags caseOne = CC_LT;
357
CCFlags caseZero = CC_GE;
358
Operand2 op2;
359
bool negated;
360
if (gpr.IsImm(rs) && TryMakeOperand2_AllowNegation(gpr.GetImm(rs), op2, &negated)) {
361
gpr.MapDirtyIn(rd, rt);
362
if (!negated)
363
CMP(gpr.R(rt), op2);
364
else
365
CMN(gpr.R(rt), op2);
366
367
// Swap the condition since we swapped the arguments.
368
caseOne = CC_GT;
369
caseZero = CC_LE;
370
} else if (gpr.IsImm(rt) && TryMakeOperand2_AllowNegation(gpr.GetImm(rt), op2, &negated)) {
371
gpr.MapDirtyIn(rd, rs);
372
if (!negated)
373
CMP(gpr.R(rs), op2);
374
else
375
CMN(gpr.R(rs), op2);
376
} else {
377
gpr.MapDirtyInIn(rd, rs, rt);
378
CMP(gpr.R(rs), gpr.R(rt));
379
}
380
381
SetCC(caseOne);
382
MOVI2R(gpr.R(rd), 1);
383
SetCC(caseZero);
384
MOVI2R(gpr.R(rd), 0);
385
SetCC(CC_AL);
386
}
387
break;
388
389
case 43: //R(rd) = R(rs) < R(rt); break; //sltu
390
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
391
gpr.SetImm(rd, gpr.GetImm(rs) < gpr.GetImm(rt));
392
} else {
393
CCFlags caseOne = CC_LO;
394
CCFlags caseZero = CC_HS;
395
Operand2 op2;
396
bool negated;
397
if (gpr.IsImm(rs) && TryMakeOperand2_AllowNegation(gpr.GetImm(rs), op2, &negated)) {
398
gpr.MapDirtyIn(rd, rt);
399
if (!negated)
400
CMP(gpr.R(rt), op2);
401
else
402
CMN(gpr.R(rt), op2);
403
404
// Swap the condition since we swapped the arguments.
405
caseOne = CC_HI;
406
caseZero = CC_LS;
407
} else if (gpr.IsImm(rt) && TryMakeOperand2_AllowNegation(gpr.GetImm(rt), op2, &negated)) {
408
gpr.MapDirtyIn(rd, rs);
409
if (!negated)
410
CMP(gpr.R(rs), op2);
411
else
412
CMN(gpr.R(rs), op2);
413
} else {
414
gpr.MapDirtyInIn(rd, rs, rt);
415
CMP(gpr.R(rs), gpr.R(rt));
416
}
417
418
SetCC(caseOne);
419
MOVI2R(gpr.R(rd), 1);
420
SetCC(caseZero);
421
MOVI2R(gpr.R(rd), 0);
422
SetCC(CC_AL);
423
}
424
break;
425
426
case 44: //R(rd) = max(R(rs), R(rt); break; //max
427
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
428
gpr.SetImm(rd, std::max(gpr.GetImm(rs), gpr.GetImm(rt)));
429
break;
430
}
431
gpr.MapDirtyInIn(rd, rs, rt);
432
CMP(gpr.R(rs), gpr.R(rt));
433
SetCC(CC_GT);
434
if (rd != rs)
435
MOV(gpr.R(rd), gpr.R(rs));
436
SetCC(CC_LE);
437
if (rd != rt)
438
MOV(gpr.R(rd), gpr.R(rt));
439
SetCC(CC_AL);
440
break;
441
442
case 45: //R(rd) = min(R(rs), R(rt)); break; //min
443
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
444
gpr.SetImm(rd, std::min(gpr.GetImm(rs), gpr.GetImm(rt)));
445
break;
446
}
447
gpr.MapDirtyInIn(rd, rs, rt);
448
CMP(gpr.R(rs), gpr.R(rt));
449
SetCC(CC_LT);
450
if (rd != rs)
451
MOV(gpr.R(rd), gpr.R(rs));
452
SetCC(CC_GE);
453
if (rd != rt)
454
MOV(gpr.R(rd), gpr.R(rt));
455
SetCC(CC_AL);
456
break;
457
458
default:
459
Comp_Generic(op);
460
break;
461
}
462
}
463
464
void ArmJit::CompShiftImm(MIPSOpcode op, ArmGen::ShiftType shiftType, int sa)
465
{
466
MIPSGPReg rd = _RD;
467
MIPSGPReg rt = _RT;
468
469
if (gpr.IsImm(rt)) {
470
switch (shiftType) {
471
case ST_LSL:
472
gpr.SetImm(rd, gpr.GetImm(rt) << sa);
473
break;
474
case ST_LSR:
475
gpr.SetImm(rd, gpr.GetImm(rt) >> sa);
476
break;
477
case ST_ASR:
478
gpr.SetImm(rd, (int)gpr.GetImm(rt) >> sa);
479
break;
480
case ST_ROR:
481
gpr.SetImm(rd, (gpr.GetImm(rt) >> sa) | (gpr.GetImm(rt) << (32 - sa)));
482
break;
483
default:
484
DISABLE;
485
}
486
} else {
487
gpr.MapDirtyIn(rd, rt);
488
MOV(gpr.R(rd), Operand2(gpr.R(rt), shiftType, sa));
489
}
490
}
491
492
void ArmJit::CompShiftVar(MIPSOpcode op, ArmGen::ShiftType shiftType)
493
{
494
MIPSGPReg rd = _RD;
495
MIPSGPReg rt = _RT;
496
MIPSGPReg rs = _RS;
497
if (gpr.IsImm(rs)) {
498
int sa = gpr.GetImm(rs) & 0x1F;
499
CompShiftImm(op, shiftType, sa);
500
return;
501
}
502
gpr.MapDirtyInIn(rd, rs, rt);
503
AND(SCRATCHREG1, gpr.R(rs), Operand2(0x1F));
504
MOV(gpr.R(rd), Operand2(gpr.R(rt), shiftType, SCRATCHREG1));
505
}
506
507
void ArmJit::Comp_ShiftType(MIPSOpcode op)
508
{
509
CONDITIONAL_DISABLE(ALU);
510
MIPSGPReg rs = _RS;
511
MIPSGPReg rd = _RD;
512
int fd = _FD;
513
int sa = _SA;
514
515
// noop, won't write to ZERO.
516
if (rd == 0)
517
return;
518
519
// WARNING : ROTR
520
switch (op & 0x3f) {
521
case 0: CompShiftImm(op, ST_LSL, sa); break; //sll
522
case 2: CompShiftImm(op, rs == 1 ? ST_ROR : ST_LSR, sa); break; //srl
523
case 3: CompShiftImm(op, ST_ASR, sa); break; //sra
524
case 4: CompShiftVar(op, ST_LSL); break; //sllv
525
case 6: CompShiftVar(op, fd == 1 ? ST_ROR : ST_LSR); break; //srlv
526
case 7: CompShiftVar(op, ST_ASR); break; //srav
527
528
default:
529
Comp_Generic(op);
530
break;
531
}
532
}
533
534
void ArmJit::Comp_Special3(MIPSOpcode op)
535
{
536
CONDITIONAL_DISABLE(ALU_BIT);
537
538
MIPSGPReg rs = _RS;
539
MIPSGPReg rt = _RT;
540
541
int pos = _POS;
542
int size = _SIZE + 1;
543
u32 mask = 0xFFFFFFFFUL >> (32 - size);
544
545
// Don't change $zr.
546
if (rt == 0)
547
return;
548
549
switch (op & 0x3f) {
550
case 0x0: //ext
551
if (gpr.IsImm(rs)) {
552
gpr.SetImm(rt, (gpr.GetImm(rs) >> pos) & mask);
553
return;
554
}
555
556
gpr.MapDirtyIn(rt, rs);
557
#if PPSSPP_ARCH(ARMV7)
558
UBFX(gpr.R(rt), gpr.R(rs), pos, size);
559
#else
560
MOV(gpr.R(rt), Operand2(gpr.R(rs), ST_LSR, pos));
561
ANDI2R(gpr.R(rt), gpr.R(rt), mask, SCRATCHREG1);
562
#endif
563
break;
564
565
case 0x4: //ins
566
{
567
u32 sourcemask = mask >> pos;
568
u32 destmask = ~(sourcemask << pos);
569
if (gpr.IsImm(rs)) {
570
u32 inserted = (gpr.GetImm(rs) & sourcemask) << pos;
571
if (gpr.IsImm(rt)) {
572
gpr.SetImm(rt, (gpr.GetImm(rt) & destmask) | inserted);
573
return;
574
}
575
576
gpr.MapReg(rt, MAP_DIRTY);
577
ANDI2R(gpr.R(rt), gpr.R(rt), destmask, SCRATCHREG1);
578
if (inserted != 0) {
579
ORI2R(gpr.R(rt), gpr.R(rt), inserted, SCRATCHREG1);
580
}
581
} else {
582
gpr.MapDirtyIn(rt, rs, false);
583
#if PPSSPP_ARCH(ARMV7)
584
BFI(gpr.R(rt), gpr.R(rs), pos, size - pos);
585
#else
586
ANDI2R(SCRATCHREG1, gpr.R(rs), sourcemask, SCRATCHREG2);
587
ANDI2R(gpr.R(rt), gpr.R(rt), destmask, SCRATCHREG2);
588
ORR(gpr.R(rt), gpr.R(rt), Operand2(SCRATCHREG1, ST_LSL, pos));
589
#endif
590
}
591
}
592
break;
593
}
594
}
595
596
void ArmJit::Comp_Allegrex(MIPSOpcode op)
597
{
598
CONDITIONAL_DISABLE(ALU_BIT);
599
MIPSGPReg rt = _RT;
600
MIPSGPReg rd = _RD;
601
// Don't change $zr.
602
if (rd == 0)
603
return;
604
605
switch ((op >> 6) & 31) {
606
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
607
if (gpr.IsImm(rt)) {
608
gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt)));
609
return;
610
}
611
gpr.MapDirtyIn(rd, rt);
612
SXTB(gpr.R(rd), gpr.R(rt));
613
break;
614
615
case 24: // seh
616
if (gpr.IsImm(rt)) {
617
gpr.SetImm(rd, SignExtend16ToU32(gpr.GetImm(rt)));
618
return;
619
}
620
gpr.MapDirtyIn(rd, rt);
621
SXTH(gpr.R(rd), gpr.R(rt));
622
break;
623
624
case 20: //bitrev
625
if (gpr.IsImm(rt)) {
626
// http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
627
u32 v = gpr.GetImm(rt);
628
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // odd<->even
629
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); // pair<->pair
630
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); // nibb<->nibb
631
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); // byte<->byte
632
v = ( v >> 16 ) | ( v << 16); // hword<->hword
633
gpr.SetImm(rd, v);
634
return;
635
}
636
637
#if PPSSPP_ARCH(ARMV7)
638
gpr.MapDirtyIn(rd, rt);
639
RBIT(gpr.R(rd), gpr.R(rt));
640
#else
641
Comp_Generic(op);
642
#endif
643
break;
644
default:
645
Comp_Generic(op);
646
return;
647
}
648
}
649
650
void ArmJit::Comp_Allegrex2(MIPSOpcode op)
651
{
652
CONDITIONAL_DISABLE(ALU_BIT);
653
MIPSGPReg rt = _RT;
654
MIPSGPReg rd = _RD;
655
// Don't change $zr.
656
if (rd == 0)
657
return;
658
659
switch (op & 0x3ff) {
660
case 0xA0: //wsbh
661
if (gpr.IsImm(rt)) {
662
gpr.SetImm(rd, ((gpr.GetImm(rt) & 0xFF00FF00) >> 8) | ((gpr.GetImm(rt) & 0x00FF00FF) << 8));
663
} else {
664
gpr.MapDirtyIn(rd, rt);
665
REV16(gpr.R(rd), gpr.R(rt));
666
}
667
break;
668
case 0xE0: //wsbw
669
if (gpr.IsImm(rt)) {
670
gpr.SetImm(rd, swap32(gpr.GetImm(rt)));
671
} else {
672
gpr.MapDirtyIn(rd, rt);
673
REV(gpr.R(rd), gpr.R(rt));
674
}
675
break;
676
default:
677
Comp_Generic(op);
678
break;
679
}
680
}
681
682
void ArmJit::Comp_MulDivType(MIPSOpcode op)
683
{
684
CONDITIONAL_DISABLE(MULDIV);
685
MIPSGPReg rt = _RT;
686
MIPSGPReg rs = _RS;
687
MIPSGPReg rd = _RD;
688
689
switch (op & 63) {
690
case 16: // R(rd) = HI; //mfhi
691
if (rd != MIPS_REG_ZERO) {
692
if (gpr.IsImm(MIPS_REG_HI)) {
693
gpr.SetImm(rd, gpr.GetImm(MIPS_REG_HI));
694
break;
695
}
696
gpr.MapDirtyIn(rd, MIPS_REG_HI);
697
MOV(gpr.R(rd), gpr.R(MIPS_REG_HI));
698
}
699
break;
700
701
case 17: // HI = R(rs); //mthi
702
if (gpr.IsImm(rs)) {
703
gpr.SetImm(MIPS_REG_HI, gpr.GetImm(rs));
704
break;
705
}
706
gpr.MapDirtyIn(MIPS_REG_HI, rs);
707
MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));
708
break;
709
710
case 18: // R(rd) = LO; break; //mflo
711
if (rd != MIPS_REG_ZERO) {
712
if (gpr.IsImm(MIPS_REG_LO)) {
713
gpr.SetImm(rd, gpr.GetImm(MIPS_REG_LO));
714
break;
715
}
716
gpr.MapDirtyIn(rd, MIPS_REG_LO);
717
MOV(gpr.R(rd), gpr.R(MIPS_REG_LO));
718
}
719
break;
720
721
case 19: // LO = R(rs); break; //mtlo
722
if (gpr.IsImm(rs)) {
723
gpr.SetImm(MIPS_REG_LO, gpr.GetImm(rs));
724
break;
725
}
726
gpr.MapDirtyIn(MIPS_REG_LO, rs);
727
MOV(gpr.R(MIPS_REG_LO), gpr.R(rs));
728
break;
729
730
case 24: //mult (the most popular one). lo,hi = signed mul (rs * rt)
731
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
732
s64 result = (s64)(s32)gpr.GetImm(rs) * (s64)(s32)gpr.GetImm(rt);
733
u64 resultBits = (u64)result;
734
gpr.SetImm(MIPS_REG_LO, (u32)(resultBits >> 0));
735
gpr.SetImm(MIPS_REG_HI, (u32)(resultBits >> 32));
736
break;
737
}
738
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);
739
SMULL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));
740
break;
741
742
case 25: //multu (2nd) lo,hi = unsigned mul (rs * rt)
743
if (gpr.IsImm(rs) && gpr.IsImm(rt)) {
744
u64 resultBits = (u64)gpr.GetImm(rs) * (u64)gpr.GetImm(rt);
745
gpr.SetImm(MIPS_REG_LO, (u32)(resultBits >> 0));
746
gpr.SetImm(MIPS_REG_HI, (u32)(resultBits >> 32));
747
break;
748
}
749
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);
750
UMULL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));
751
break;
752
753
case 26: //div
754
if (cpu_info.bIDIVa) {
755
// TODO: Does this handle INT_MAX, 0, etc. correctly?
756
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);
757
758
CMPI2R(gpr.R(rt), 0, SCRATCHREG1);
759
FixupBranch skipZero = B_CC(CC_NEQ);
760
MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));
761
MOVI2R(gpr.R(MIPS_REG_LO), -1);
762
CMPI2R(gpr.R(rs), 0, SCRATCHREG1);
763
SetCC(CC_LT);
764
MOVI2R(gpr.R(MIPS_REG_LO), 1);
765
SetCC(CC_AL);
766
FixupBranch skipDiv = B();
767
768
SetJumpTarget(skipZero);
769
SDIV(gpr.R(MIPS_REG_LO), gpr.R(rs), gpr.R(rt));
770
SetJumpTarget(skipDiv);
771
MUL(SCRATCHREG1, gpr.R(rt), gpr.R(MIPS_REG_LO));
772
SUB(gpr.R(MIPS_REG_HI), gpr.R(rs), Operand2(SCRATCHREG1));
773
} else {
774
DISABLE;
775
}
776
break;
777
778
case 27: //divu
779
// Do we have a known power-of-two denominator? Yes, this happens.
780
if (gpr.IsImm(rt) && (gpr.GetImm(rt) & (gpr.GetImm(rt) - 1)) == 0 && gpr.GetImm(rt) != 0) {
781
u32 denominator = gpr.GetImm(rt);
782
gpr.MapDirtyDirtyIn(MIPS_REG_LO, MIPS_REG_HI, rs);
783
// Remainder is just an AND, neat.
784
ANDI2R(gpr.R(MIPS_REG_HI), gpr.R(rs), denominator - 1, SCRATCHREG1);
785
int shift = 0;
786
while (denominator != 0) {
787
++shift;
788
denominator >>= 1;
789
}
790
// The shift value is one too much for the divide by the same value.
791
if (shift > 1) {
792
LSR(gpr.R(MIPS_REG_LO), gpr.R(rs), shift - 1);
793
} else {
794
MOV(gpr.R(MIPS_REG_LO), gpr.R(rs));
795
}
796
} else if (cpu_info.bIDIVa) {
797
// TODO: Does this handle INT_MAX, 0, etc. correctly?
798
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);
799
800
CMPI2R(gpr.R(rt), 0, SCRATCHREG1);
801
FixupBranch skipZero = B_CC(CC_NEQ);
802
MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));
803
MOVI2R(SCRATCHREG1, 0xFFFF);
804
CMP(gpr.R(rs), SCRATCHREG1);
805
MOVI2R(gpr.R(MIPS_REG_LO), -1);
806
SetCC(CC_LS);
807
MOV(gpr.R(MIPS_REG_LO), SCRATCHREG1);
808
SetCC(CC_AL);
809
FixupBranch skipDiv = B();
810
811
SetJumpTarget(skipZero);
812
UDIV(gpr.R(MIPS_REG_LO), gpr.R(rs), gpr.R(rt));
813
SetJumpTarget(skipDiv);
814
MUL(SCRATCHREG1, gpr.R(rt), gpr.R(MIPS_REG_LO));
815
SUB(gpr.R(MIPS_REG_HI), gpr.R(rs), Operand2(SCRATCHREG1));
816
} else {
817
// If rt is 0, we either caught it above, or it's not an imm.
818
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);
819
MOV(SCRATCHREG1, gpr.R(rt));
820
// We start at rs for the remainder and subtract out.
821
MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));
822
823
CMP(gpr.R(rt), 0);
824
FixupBranch skipper = B_CC(CC_EQ);
825
826
// Double SCRATCHREG1 until it would be (but isn't) bigger than the numerator.
827
CMP(SCRATCHREG1, Operand2(gpr.R(rs), ST_LSR, 1));
828
const u8 *doubleLoop = GetCodePtr();
829
SetCC(CC_LS);
830
MOV(SCRATCHREG1, Operand2(SCRATCHREG1, ST_LSL, 1));
831
SetCC(CC_AL);
832
CMP(SCRATCHREG1, Operand2(gpr.R(rs), ST_LSR, 1));
833
B_CC(CC_LS, doubleLoop);
834
835
MOV(gpr.R(MIPS_REG_LO), 0);
836
837
// Subtract and halve SCRATCHREG1 (doubling and adding the result) until it's below the denominator.
838
const u8 *subLoop = GetCodePtr();
839
CMP(gpr.R(MIPS_REG_HI), SCRATCHREG1);
840
SetCC(CC_HS);
841
SUB(gpr.R(MIPS_REG_HI), gpr.R(MIPS_REG_HI), SCRATCHREG1);
842
SetCC(CC_AL);
843
// Carry will be set if we subtracted.
844
ADC(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO));
845
MOV(SCRATCHREG1, Operand2(SCRATCHREG1, ST_LSR, 1));
846
CMP(SCRATCHREG1, gpr.R(rt));
847
B_CC(CC_HS, subLoop);
848
849
// We didn't change rt. If it was 0, then clear HI and LO.
850
FixupBranch zeroSkip = B();
851
SetJumpTarget(skipper);
852
MOVI2R(SCRATCHREG1, 0xFFFF);
853
CMP(gpr.R(rs), SCRATCHREG1);
854
MOVI2R(gpr.R(MIPS_REG_LO), -1);
855
SetCC(CC_LS);
856
MOV(gpr.R(MIPS_REG_LO), SCRATCHREG1);
857
SetCC(CC_AL);
858
SetJumpTarget(zeroSkip);
859
}
860
break;
861
862
case 28: //madd
863
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);
864
SMLAL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));
865
break;
866
867
case 29: //maddu
868
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);
869
UMLAL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));
870
break;
871
872
case 46: // msub
873
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);
874
SMULL(SCRATCHREG1, SCRATCHREG2, gpr.R(rs), gpr.R(rt));
875
SUBS(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO), SCRATCHREG1);
876
SBC(gpr.R(MIPS_REG_HI), gpr.R(MIPS_REG_HI), SCRATCHREG2);
877
break;
878
879
case 47: // msubu
880
gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);
881
UMULL(SCRATCHREG1, SCRATCHREG2, gpr.R(rs), gpr.R(rt));
882
SUBS(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO), SCRATCHREG1);
883
SBC(gpr.R(MIPS_REG_HI), gpr.R(MIPS_REG_HI), SCRATCHREG2);
884
break;
885
886
default:
887
DISABLE;
888
}
889
}
890
891
}
892
893
#endif // PPSSPP_ARCH(ARM)
894
895