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/x86/X64IRCompALU.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 "ppsspp_config.h"
19
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
20
21
#include "Common/CPUDetect.h"
22
#include "Core/MemMap.h"
23
#include "Core/MIPS/x86/X64IRJit.h"
24
#include "Core/MIPS/x86/X64IRRegCache.h"
25
26
// This file contains compilation for integer / arithmetic / logic related instructions.
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 Gen;
39
using namespace X64IRJitConstants;
40
41
void X64JitBackend::CompIR_Arith(IRInst inst) {
42
CONDITIONAL_DISABLE;
43
44
bool allowPtrMath = inst.constant <= 0x7FFFFFFF;
45
#ifdef MASKED_PSP_MEMORY
46
// Since we modify it, we can't safely.
47
allowPtrMath = false;
48
#endif
49
50
switch (inst.op) {
51
case IROp::Add:
52
regs_.Map(inst);
53
if (inst.src1 == inst.src2) {
54
LEA(32, regs_.RX(inst.dest), MScaled(regs_.RX(inst.src1), 2, 0));
55
} else if (inst.dest == inst.src2) {
56
ADD(32, regs_.R(inst.dest), regs_.R(inst.src1));
57
} else if (inst.dest == inst.src1) {
58
ADD(32, regs_.R(inst.dest), regs_.R(inst.src2));
59
} else {
60
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
61
ADD(32, regs_.R(inst.dest), regs_.R(inst.src2));
62
}
63
break;
64
65
case IROp::Sub:
66
regs_.Map(inst);
67
if (inst.src1 == inst.src2) {
68
regs_.SetGPRImm(inst.dest, 0);
69
} else if (inst.dest == inst.src2) {
70
NEG(32, regs_.R(inst.src2));
71
ADD(32, regs_.R(inst.dest), regs_.R(inst.src1));
72
} else if (inst.dest == inst.src1) {
73
SUB(32, regs_.R(inst.dest), regs_.R(inst.src2));
74
} else {
75
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
76
SUB(32, regs_.R(inst.dest), regs_.R(inst.src2));
77
}
78
break;
79
80
case IROp::AddConst:
81
if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {
82
regs_.MarkGPRAsPointerDirty(inst.dest);
83
LEA(PTRBITS, regs_.RXPtr(inst.dest), MDisp(regs_.RXPtr(inst.dest), inst.constant));
84
} else {
85
regs_.Map(inst);
86
LEA(32, regs_.RX(inst.dest), MDisp(regs_.RX(inst.src1), inst.constant));
87
}
88
break;
89
90
case IROp::SubConst:
91
if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {
92
regs_.MarkGPRAsPointerDirty(inst.dest);
93
LEA(PTRBITS, regs_.RXPtr(inst.dest), MDisp(regs_.RXPtr(inst.dest), -(int)inst.constant));
94
} else {
95
regs_.Map(inst);
96
LEA(32, regs_.RX(inst.dest), MDisp(regs_.RX(inst.src1), -(int)inst.constant));
97
}
98
break;
99
100
case IROp::Neg:
101
regs_.Map(inst);
102
if (inst.dest != inst.src1) {
103
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
104
}
105
NEG(32, regs_.R(inst.dest));
106
break;
107
108
default:
109
INVALIDOP;
110
break;
111
}
112
}
113
114
void X64JitBackend::CompIR_Assign(IRInst inst) {
115
CONDITIONAL_DISABLE;
116
117
switch (inst.op) {
118
case IROp::Mov:
119
if (inst.dest != inst.src1) {
120
regs_.Map(inst);
121
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
122
}
123
break;
124
125
case IROp::Ext8to32:
126
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::LOW_SUBREG);
127
MOVSX(32, 8, regs_.RX(inst.dest), regs_.R(inst.src1));
128
break;
129
130
case IROp::Ext16to32:
131
regs_.Map(inst);
132
MOVSX(32, 16, regs_.RX(inst.dest), regs_.R(inst.src1));
133
break;
134
135
default:
136
INVALIDOP;
137
break;
138
}
139
}
140
141
void X64JitBackend::CompIR_Bits(IRInst inst) {
142
CONDITIONAL_DISABLE;
143
144
switch (inst.op) {
145
case IROp::BSwap32:
146
regs_.Map(inst);
147
if (inst.src1 != inst.dest) {
148
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
149
}
150
BSWAP(32, regs_.RX(inst.dest));
151
break;
152
153
case IROp::ReverseBits:
154
regs_.Map(inst);
155
if (inst.src1 != inst.dest) {
156
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
157
}
158
159
// Swap even/odd bits (in bits: 0123 -> 1032.)
160
LEA(32, SCRATCH1, MScaled(regs_.RX(inst.dest), 2, 0));
161
SHR(32, regs_.R(inst.dest), Imm8(1));
162
XOR(32, regs_.R(inst.dest), R(SCRATCH1));
163
AND(32, regs_.R(inst.dest), Imm32(0x55555555));
164
XOR(32, regs_.R(inst.dest), R(SCRATCH1));
165
166
// Swap pairs of bits (in bits: 10325476 -> 32107654.)
167
LEA(32, SCRATCH1, MScaled(regs_.RX(inst.dest), 4, 0));
168
SHR(32, regs_.R(inst.dest), Imm8(2));
169
XOR(32, regs_.R(inst.dest), R(SCRATCH1));
170
AND(32, regs_.R(inst.dest), Imm32(0x33333333));
171
XOR(32, regs_.R(inst.dest), R(SCRATCH1));
172
173
// Swap nibbles (in nibbles: ABCD -> BADC.)
174
MOV(32, R(SCRATCH1), regs_.R(inst.dest));
175
SHL(32, R(SCRATCH1), Imm8(4));
176
SHR(32, regs_.R(inst.dest), Imm8(4));
177
XOR(32, regs_.R(inst.dest), R(SCRATCH1));
178
AND(32, regs_.R(inst.dest), Imm32(0x0F0F0F0F));
179
XOR(32, regs_.R(inst.dest), R(SCRATCH1));
180
181
// Finally, swap the bytes to drop everything into place (nibbles: BADCFEHG -> HGFEDCBA.)
182
BSWAP(32, regs_.RX(inst.dest));
183
break;
184
185
case IROp::BSwap16:
186
regs_.Map(inst);
187
if (cpu_info.bBMI2) {
188
// Rotate to put it into the correct register, then swap.
189
if (inst.dest != inst.src1)
190
RORX(32, regs_.RX(inst.dest), regs_.R(inst.src1), 16);
191
else
192
ROR(32, regs_.R(inst.dest), Imm8(16));
193
BSWAP(32, regs_.RX(inst.dest));
194
} else {
195
if (inst.dest != inst.src1)
196
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
197
BSWAP(32, regs_.RX(inst.dest));
198
ROR(32, regs_.R(inst.dest), Imm8(16));
199
}
200
break;
201
202
case IROp::Clz:
203
regs_.Map(inst);
204
if (cpu_info.bLZCNT) {
205
LZCNT(32, regs_.RX(inst.dest), regs_.R(inst.src1));
206
} else {
207
BSR(32, regs_.RX(inst.dest), regs_.R(inst.src1));
208
FixupBranch notFound = J_CC(CC_Z);
209
210
// Since one of these bits must be set, and none outside, this subtracts from 31.
211
XOR(32, regs_.R(inst.dest), Imm8(31));
212
FixupBranch skip = J();
213
214
SetJumpTarget(notFound);
215
MOV(32, regs_.R(inst.dest), Imm32(32));
216
217
SetJumpTarget(skip);
218
}
219
break;
220
221
default:
222
INVALIDOP;
223
break;
224
}
225
}
226
227
void X64JitBackend::CompIR_Compare(IRInst inst) {
228
CONDITIONAL_DISABLE;
229
230
auto setCC = [&](const OpArg &arg, CCFlags cc) {
231
// If it's carry, we can take advantage of ADC to avoid subregisters.
232
if (cc == CC_C && inst.dest != inst.src1 && inst.dest != inst.src2) {
233
XOR(32, regs_.R(inst.dest), regs_.R(inst.dest));
234
CMP(32, regs_.R(inst.src1), arg);
235
ADC(32, regs_.R(inst.dest), Imm8(0));
236
} else if (regs_.HasLowSubregister(regs_.RX(inst.dest)) && inst.dest != inst.src1 && inst.dest != inst.src2) {
237
XOR(32, regs_.R(inst.dest), regs_.R(inst.dest));
238
CMP(32, regs_.R(inst.src1), arg);
239
SETcc(cc, regs_.R(inst.dest));
240
} else {
241
CMP(32, regs_.R(inst.src1), arg);
242
SETcc(cc, R(SCRATCH1));
243
MOVZX(32, 8, regs_.RX(inst.dest), R(SCRATCH1));
244
}
245
};
246
247
switch (inst.op) {
248
case IROp::Slt:
249
regs_.Map(inst);
250
setCC(regs_.R(inst.src2), CC_L);
251
break;
252
253
case IROp::SltConst:
254
if (inst.constant == 0) {
255
// Basically, getting the sign bit. Let's shift instead.
256
regs_.Map(inst);
257
if (inst.dest != inst.src1)
258
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
259
SHR(32, regs_.R(inst.dest), Imm8(31));
260
} else {
261
regs_.Map(inst);
262
setCC(Imm32(inst.constant), CC_L);
263
}
264
break;
265
266
case IROp::SltU:
267
if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {
268
// This is kinda common, same as != 0. Avoid flushing src1.
269
regs_.SpillLockGPR(inst.src2, inst.dest);
270
regs_.MapGPR(inst.src2);
271
regs_.MapGPR(inst.dest, MIPSMap::NOINIT);
272
if (inst.dest != inst.src2 && regs_.HasLowSubregister(regs_.RX(inst.dest))) {
273
XOR(32, regs_.R(inst.dest), regs_.R(inst.dest));
274
TEST(32, regs_.R(inst.src2), regs_.R(inst.src2));
275
SETcc(CC_NE, regs_.R(inst.dest));
276
} else {
277
CMP(32, regs_.R(inst.src2), Imm8(0));
278
SETcc(CC_NE, R(SCRATCH1));
279
MOVZX(32, 8, regs_.RX(inst.dest), R(SCRATCH1));
280
}
281
} else {
282
regs_.Map(inst);
283
setCC(regs_.R(inst.src2), CC_B);
284
}
285
break;
286
287
case IROp::SltUConst:
288
if (inst.constant == 0) {
289
regs_.SetGPRImm(inst.dest, 0);
290
} else {
291
regs_.Map(inst);
292
setCC(Imm32(inst.constant), CC_B);
293
}
294
break;
295
296
default:
297
INVALIDOP;
298
break;
299
}
300
}
301
302
void X64JitBackend::CompIR_CondAssign(IRInst inst) {
303
CONDITIONAL_DISABLE;
304
305
switch (inst.op) {
306
case IROp::MovZ:
307
if (inst.dest != inst.src2) {
308
regs_.Map(inst);
309
TEST(32, regs_.R(inst.src1), regs_.R(inst.src1));
310
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src2), CC_Z);
311
}
312
break;
313
314
case IROp::MovNZ:
315
if (inst.dest != inst.src2) {
316
regs_.Map(inst);
317
TEST(32, regs_.R(inst.src1), regs_.R(inst.src1));
318
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src2), CC_NZ);
319
}
320
break;
321
322
case IROp::Max:
323
regs_.Map(inst);
324
if (inst.src1 == inst.src2) {
325
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
326
} else if (inst.dest == inst.src1) {
327
CMP(32, regs_.R(inst.src1), regs_.R(inst.src2));
328
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src2), CC_L);
329
} else if (inst.dest == inst.src2) {
330
CMP(32, regs_.R(inst.src1), regs_.R(inst.src2));
331
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src1), CC_G);
332
} else {
333
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
334
CMP(32, regs_.R(inst.dest), regs_.R(inst.src2));
335
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src2), CC_L);
336
}
337
break;
338
339
case IROp::Min:
340
regs_.Map(inst);
341
if (inst.src1 == inst.src2) {
342
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
343
} else if (inst.dest == inst.src1) {
344
CMP(32, regs_.R(inst.src1), regs_.R(inst.src2));
345
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src2), CC_G);
346
} else if (inst.dest == inst.src2) {
347
CMP(32, regs_.R(inst.src1), regs_.R(inst.src2));
348
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src1), CC_L);
349
} else {
350
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
351
CMP(32, regs_.R(inst.dest), regs_.R(inst.src2));
352
CMOVcc(32, regs_.RX(inst.dest), regs_.R(inst.src2), CC_G);
353
}
354
break;
355
356
default:
357
INVALIDOP;
358
break;
359
}
360
}
361
362
void X64JitBackend::CompIR_Div(IRInst inst) {
363
CONDITIONAL_DISABLE;
364
365
switch (inst.op) {
366
case IROp::Div:
367
#if PPSSPP_ARCH(AMD64)
368
// We need EDX specifically, so force a spill (before spill locks happen.)
369
regs_.MapGPR2(IRREG_LO, MIPSMap::NOINIT | X64Map::HIGH_DATA);
370
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT | X64Map::HIGH_DATA } });
371
#else // PPSSPP_ARCH(X86)
372
// Force a spill, it's HI in this path.
373
regs_.MapGPR(IRREG_HI, MIPSMap::NOINIT | X64Map::HIGH_DATA);
374
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT | X64Map::HIGH_DATA } });
375
#endif
376
{
377
TEST(32, regs_.R(inst.src2), regs_.R(inst.src2));
378
FixupBranch divideByZero = J_CC(CC_E, false);
379
380
// Sign extension sets HI to -1 for us on x64.
381
MOV(PTRBITS, regs_.R(IRREG_LO), Imm32(0x80000000));
382
#if PPSSPP_ARCH(X86)
383
MOV(PTRBITS, regs_.R(IRREG_HI), Imm32(-1));
384
#endif
385
CMP(32, regs_.R(inst.src1), regs_.R(IRREG_LO));
386
FixupBranch numeratorNotOverflow = J_CC(CC_NE, false);
387
CMP(32, regs_.R(inst.src2), Imm32(-1));
388
FixupBranch denominatorOverflow = J_CC(CC_E, false);
389
390
SetJumpTarget(numeratorNotOverflow);
391
392
// It's finally time to actually divide.
393
MOV(32, R(EAX), regs_.R(inst.src1));
394
CDQ();
395
IDIV(32, regs_.R(inst.src2));
396
#if PPSSPP_ARCH(AMD64)
397
// EDX == RX(IRREG_LO). Put the remainder in the upper bits, done.
398
SHL(64, R(EDX), Imm8(32));
399
OR(64, R(EDX), R(EAX));
400
#else // PPSSPP_ARCH(X86)
401
// EDX is already good (HI), just move EAX into place.
402
MOV(32, regs_.R(IRREG_LO), R(EAX));
403
#endif
404
FixupBranch done = J(false);
405
406
SetJumpTarget(divideByZero);
407
X64Reg loReg = SCRATCH1;
408
#if PPSSPP_ARCH(X86)
409
if (regs_.HasLowSubregister(regs_.RX(IRREG_LO)))
410
loReg = regs_.RX(IRREG_LO);
411
#endif
412
// Set to -1 if numerator positive using SF.
413
XOR(32, R(loReg), R(loReg));
414
TEST(32, regs_.R(inst.src1), regs_.R(inst.src1));
415
SETcc(CC_NS, R(loReg));
416
NEG(32, R(loReg));
417
// If it was negative, OR in 1 (so we get -1 or 1.)
418
OR(32, R(loReg), Imm8(1));
419
420
#if PPSSPP_ARCH(AMD64)
421
// Move the numerator into the high bits.
422
MOV(32, regs_.R(IRREG_LO), regs_.R(inst.src1));
423
SHL(64, regs_.R(IRREG_LO), Imm8(32));
424
OR(64, regs_.R(IRREG_LO), R(loReg));
425
#else // PPSSPP_ARCH(X86)
426
// If we didn't have a subreg, move into place.
427
if (loReg != regs_.RX(IRREG_LO))
428
MOV(32, regs_.R(IRREG_LO), R(loReg));
429
MOV(32, regs_.R(IRREG_HI), regs_.R(inst.src1));
430
#endif
431
432
SetJumpTarget(denominatorOverflow);
433
SetJumpTarget(done);
434
}
435
break;
436
437
case IROp::DivU:
438
#if PPSSPP_ARCH(AMD64)
439
// We need EDX specifically, so force a spill (before spill locks happen.)
440
regs_.MapGPR2(IRREG_LO, MIPSMap::NOINIT | X64Map::HIGH_DATA);
441
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT | X64Map::HIGH_DATA } });
442
#else // PPSSPP_ARCH(X86)
443
// Force a spill, it's HI in this path.
444
regs_.MapGPR(IRREG_HI, MIPSMap::NOINIT | X64Map::HIGH_DATA);
445
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT | X64Map::HIGH_DATA } });
446
#endif
447
{
448
TEST(32, regs_.R(inst.src2), regs_.R(inst.src2));
449
FixupBranch divideByZero = J_CC(CC_E, false);
450
451
MOV(32, R(EAX), regs_.R(inst.src1));
452
XOR(32, R(EDX), R(EDX));
453
DIV(32, regs_.R(inst.src2));
454
#if PPSSPP_ARCH(AMD64)
455
// EDX == RX(IRREG_LO). Put the remainder in the upper bits, done.
456
SHL(64, R(EDX), Imm8(32));
457
OR(64, R(EDX), R(EAX));
458
#else // PPSSPP_ARCH(X86)
459
// EDX is already good (HI), just move EAX into place.
460
MOV(32, regs_.R(IRREG_LO), R(EAX));
461
#endif
462
FixupBranch done = J(false);
463
464
SetJumpTarget(divideByZero);
465
// First, set LO to 0xFFFF if numerator was <= that value.
466
MOV(32, regs_.R(IRREG_LO), Imm32(0xFFFF));
467
XOR(32, R(SCRATCH1), R(SCRATCH1));
468
CMP(32, regs_.R(IRREG_LO), regs_.R(inst.src1));
469
// If 0xFFFF was less, CF was set - SBB will subtract 1 from 0, netting -1.
470
SBB(32, R(SCRATCH1), Imm8(0));
471
OR(32, regs_.R(IRREG_LO), R(SCRATCH1));
472
473
#if PPSSPP_ARCH(AMD64)
474
// Move the numerator into the high bits.
475
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
476
SHL(64, R(SCRATCH1), Imm8(32));
477
OR(64, regs_.R(IRREG_LO), R(SCRATCH1));
478
#else // PPSSPP_ARCH(X86)
479
MOV(32, regs_.R(IRREG_HI), regs_.R(inst.src1));
480
#endif
481
482
SetJumpTarget(done);
483
}
484
break;
485
486
default:
487
INVALIDOP;
488
break;
489
}
490
}
491
492
void X64JitBackend::CompIR_HiLo(IRInst inst) {
493
CONDITIONAL_DISABLE;
494
495
switch (inst.op) {
496
case IROp::MtLo:
497
#if PPSSPP_ARCH(AMD64)
498
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
499
// First, clear the bits we're replacing.
500
SHR(64, regs_.R(IRREG_LO), Imm8(32));
501
SHL(64, regs_.R(IRREG_LO), Imm8(32));
502
// Now clear the high bits and merge.
503
MOVZX(64, 32, regs_.RX(inst.src1), regs_.R(inst.src1));
504
OR(64, regs_.R(IRREG_LO), regs_.R(inst.src1));
505
#else // PPSSPP_ARCH(X86)
506
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY } });
507
MOV(32, regs_.R(IRREG_LO), regs_.R(inst.src1));
508
#endif
509
break;
510
511
case IROp::MtHi:
512
#if PPSSPP_ARCH(AMD64)
513
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
514
// First, clear the bits we're replacing.
515
MOVZX(64, 32, regs_.RX(IRREG_LO), regs_.R(IRREG_LO));
516
// Then move the new bits into place.
517
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
518
SHL(64, R(SCRATCH1), Imm8(32));
519
OR(64, regs_.R(IRREG_LO), R(SCRATCH1));
520
#else // PPSSPP_ARCH(X86)
521
regs_.MapWithExtra(inst, { { 'G', IRREG_HI, 1, MIPSMap::DIRTY } });
522
MOV(32, regs_.R(IRREG_HI), regs_.R(inst.src1));
523
#endif
524
break;
525
526
case IROp::MfLo:
527
#if PPSSPP_ARCH(AMD64)
528
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::INIT } });
529
MOV(32, regs_.R(inst.dest), regs_.R(IRREG_LO));
530
#else // PPSSPP_ARCH(X86)
531
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::INIT } });
532
MOV(32, regs_.R(inst.dest), regs_.R(IRREG_LO));
533
#endif
534
break;
535
536
case IROp::MfHi:
537
#if PPSSPP_ARCH(AMD64)
538
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::INIT } });
539
MOV(64, regs_.R(inst.dest), regs_.R(IRREG_LO));
540
SHR(64, regs_.R(inst.dest), Imm8(32));
541
#else // PPSSPP_ARCH(X86)
542
regs_.MapWithExtra(inst, { { 'G', IRREG_HI, 1, MIPSMap::INIT } });
543
MOV(32, regs_.R(inst.dest), regs_.R(IRREG_HI));
544
#endif
545
break;
546
547
default:
548
INVALIDOP;
549
break;
550
}
551
}
552
553
void X64JitBackend::CompIR_Logic(IRInst inst) {
554
CONDITIONAL_DISABLE;
555
556
switch (inst.op) {
557
case IROp::And:
558
regs_.Map(inst);
559
if (inst.dest == inst.src1) {
560
AND(32, regs_.R(inst.dest), regs_.R(inst.src2));
561
} else if (inst.dest == inst.src2) {
562
AND(32, regs_.R(inst.dest), regs_.R(inst.src1));
563
} else {
564
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
565
AND(32, regs_.R(inst.dest), regs_.R(inst.src2));
566
}
567
break;
568
569
case IROp::Or:
570
regs_.Map(inst);
571
if (inst.dest == inst.src1) {
572
OR(32, regs_.R(inst.dest), regs_.R(inst.src2));
573
} else if (inst.dest == inst.src2) {
574
OR(32, regs_.R(inst.dest), regs_.R(inst.src1));
575
} else {
576
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
577
OR(32, regs_.R(inst.dest), regs_.R(inst.src2));
578
}
579
break;
580
581
case IROp::Xor:
582
regs_.Map(inst);
583
if (inst.dest == inst.src1) {
584
XOR(32, regs_.R(inst.dest), regs_.R(inst.src2));
585
} else if (inst.dest == inst.src2) {
586
XOR(32, regs_.R(inst.dest), regs_.R(inst.src1));
587
} else {
588
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
589
XOR(32, regs_.R(inst.dest), regs_.R(inst.src2));
590
}
591
break;
592
593
case IROp::AndConst:
594
regs_.Map(inst);
595
if (inst.dest != inst.src1)
596
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
597
AND(32, regs_.R(inst.dest), SImmAuto((s32)inst.constant));
598
break;
599
600
case IROp::OrConst:
601
regs_.Map(inst);
602
if (inst.dest != inst.src1)
603
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
604
OR(32, regs_.R(inst.dest), SImmAuto((s32)inst.constant));
605
break;
606
607
case IROp::XorConst:
608
regs_.Map(inst);
609
if (inst.dest != inst.src1)
610
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
611
XOR(32, regs_.R(inst.dest), SImmAuto((s32)inst.constant));
612
break;
613
614
case IROp::Not:
615
regs_.Map(inst);
616
if (inst.dest != inst.src1)
617
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
618
NOT(32, regs_.R(inst.dest));
619
break;
620
621
default:
622
INVALIDOP;
623
break;
624
}
625
}
626
627
void X64JitBackend::CompIR_Mult(IRInst inst) {
628
CONDITIONAL_DISABLE;
629
630
switch (inst.op) {
631
case IROp::Mult:
632
#if PPSSPP_ARCH(AMD64)
633
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });
634
MOVSX(64, 32, regs_.RX(IRREG_LO), regs_.R(inst.src1));
635
MOVSX(64, 32, regs_.RX(inst.src2), regs_.R(inst.src2));
636
IMUL(64, regs_.RX(IRREG_LO), regs_.R(inst.src2));
637
#else // PPSSPP_ARCH(X86)
638
// Force a spill (before spill locks.)
639
regs_.MapGPR(IRREG_HI, MIPSMap::NOINIT | X64Map::HIGH_DATA);
640
// We keep it here so it stays locked.
641
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT | X64Map::HIGH_DATA } });
642
MOV(32, R(EAX), regs_.R(inst.src1));
643
IMUL(32, regs_.R(inst.src2));
644
MOV(32, regs_.R(IRREG_LO), R(EAX));
645
// IRREG_HI was mapped to EDX.
646
#endif
647
break;
648
649
case IROp::MultU:
650
#if PPSSPP_ARCH(AMD64)
651
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });
652
MOVZX(64, 32, regs_.RX(IRREG_LO), regs_.R(inst.src1));
653
MOVZX(64, 32, regs_.RX(inst.src2), regs_.R(inst.src2));
654
IMUL(64, regs_.RX(IRREG_LO), regs_.R(inst.src2));
655
#else // PPSSPP_ARCH(X86)
656
// Force a spill (before spill locks.)
657
regs_.MapGPR(IRREG_HI, MIPSMap::NOINIT | X64Map::HIGH_DATA);
658
// We keep it here so it stays locked.
659
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT | X64Map::HIGH_DATA } });
660
MOV(32, R(EAX), regs_.R(inst.src1));
661
MUL(32, regs_.R(inst.src2));
662
MOV(32, regs_.R(IRREG_LO), R(EAX));
663
// IRREG_HI was mapped to EDX.
664
#endif
665
break;
666
667
case IROp::Madd:
668
#if PPSSPP_ARCH(AMD64)
669
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
670
MOVSX(64, 32, SCRATCH1, regs_.R(inst.src1));
671
MOVSX(64, 32, regs_.RX(inst.src2), regs_.R(inst.src2));
672
IMUL(64, SCRATCH1, regs_.R(inst.src2));
673
ADD(64, regs_.R(IRREG_LO), R(SCRATCH1));
674
#else // PPSSPP_ARCH(X86)
675
// For ones that modify LO/HI, we can't have anything else in EDX.
676
regs_.ReserveAndLockXGPR(EDX);
677
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } });
678
MOV(32, R(EAX), regs_.R(inst.src1));
679
IMUL(32, regs_.R(inst.src2));
680
ADD(32, regs_.R(IRREG_LO), R(EAX));
681
ADC(32, regs_.R(IRREG_HI), R(EDX));
682
#endif
683
break;
684
685
case IROp::MaddU:
686
#if PPSSPP_ARCH(AMD64)
687
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
688
MOVZX(64, 32, SCRATCH1, regs_.R(inst.src1));
689
MOVZX(64, 32, regs_.RX(inst.src2), regs_.R(inst.src2));
690
IMUL(64, SCRATCH1, regs_.R(inst.src2));
691
ADD(64, regs_.R(IRREG_LO), R(SCRATCH1));
692
#else // PPSSPP_ARCH(X86)
693
// For ones that modify LO/HI, we can't have anything else in EDX.
694
regs_.ReserveAndLockXGPR(EDX);
695
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } });
696
MOV(32, R(EAX), regs_.R(inst.src1));
697
MUL(32, regs_.R(inst.src2));
698
ADD(32, regs_.R(IRREG_LO), R(EAX));
699
ADC(32, regs_.R(IRREG_HI), R(EDX));
700
#endif
701
break;
702
703
case IROp::Msub:
704
#if PPSSPP_ARCH(AMD64)
705
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
706
MOVSX(64, 32, SCRATCH1, regs_.R(inst.src1));
707
MOVSX(64, 32, regs_.RX(inst.src2), regs_.R(inst.src2));
708
IMUL(64, SCRATCH1, regs_.R(inst.src2));
709
SUB(64, regs_.R(IRREG_LO), R(SCRATCH1));
710
#else // PPSSPP_ARCH(X86)
711
// For ones that modify LO/HI, we can't have anything else in EDX.
712
regs_.ReserveAndLockXGPR(EDX);
713
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } });
714
MOV(32, R(EAX), regs_.R(inst.src1));
715
IMUL(32, regs_.R(inst.src2));
716
SUB(32, regs_.R(IRREG_LO), R(EAX));
717
SBB(32, regs_.R(IRREG_HI), R(EDX));
718
#endif
719
break;
720
721
case IROp::MsubU:
722
#if PPSSPP_ARCH(AMD64)
723
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
724
MOVZX(64, 32, SCRATCH1, regs_.R(inst.src1));
725
MOVZX(64, 32, regs_.RX(inst.src2), regs_.R(inst.src2));
726
IMUL(64, SCRATCH1, regs_.R(inst.src2));
727
SUB(64, regs_.R(IRREG_LO), R(SCRATCH1));
728
#else // PPSSPP_ARCH(X86)
729
// For ones that modify LO/HI, we can't have anything else in EDX.
730
regs_.ReserveAndLockXGPR(EDX);
731
regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } });
732
MOV(32, R(EAX), regs_.R(inst.src1));
733
MUL(32, regs_.R(inst.src2));
734
SUB(32, regs_.R(IRREG_LO), R(EAX));
735
SBB(32, regs_.R(IRREG_HI), R(EDX));
736
#endif
737
break;
738
739
default:
740
INVALIDOP;
741
break;
742
}
743
}
744
745
void X64JitBackend::CompIR_Shift(IRInst inst) {
746
CONDITIONAL_DISABLE;
747
748
switch (inst.op) {
749
case IROp::Shl:
750
if (cpu_info.bBMI2) {
751
regs_.Map(inst);
752
SHLX(32, regs_.RX(inst.dest), regs_.R(inst.src1), regs_.RX(inst.src2));
753
} else {
754
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
755
if (inst.dest == inst.src1) {
756
SHL(32, regs_.R(inst.dest), regs_.R(inst.src2));
757
} else if (inst.dest == inst.src2) {
758
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
759
SHL(32, R(SCRATCH1), regs_.R(inst.src2));
760
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
761
} else {
762
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
763
SHL(32, regs_.R(inst.dest), regs_.R(inst.src2));
764
}
765
}
766
break;
767
768
case IROp::Shr:
769
if (cpu_info.bBMI2) {
770
regs_.Map(inst);
771
SHRX(32, regs_.RX(inst.dest), regs_.R(inst.src1), regs_.RX(inst.src2));
772
} else {
773
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
774
if (inst.dest == inst.src1) {
775
SHR(32, regs_.R(inst.dest), regs_.R(inst.src2));
776
} else if (inst.dest == inst.src2) {
777
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
778
SHR(32, R(SCRATCH1), regs_.R(inst.src2));
779
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
780
} else {
781
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
782
SHR(32, regs_.R(inst.dest), regs_.R(inst.src2));
783
}
784
}
785
break;
786
787
case IROp::Sar:
788
if (cpu_info.bBMI2) {
789
regs_.Map(inst);
790
SARX(32, regs_.RX(inst.dest), regs_.R(inst.src1), regs_.RX(inst.src2));
791
} else {
792
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
793
if (inst.dest == inst.src1) {
794
SAR(32, regs_.R(inst.dest), regs_.R(inst.src2));
795
} else if (inst.dest == inst.src2) {
796
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
797
SAR(32, R(SCRATCH1), regs_.R(inst.src2));
798
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
799
} else {
800
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
801
SAR(32, regs_.R(inst.dest), regs_.R(inst.src2));
802
}
803
}
804
break;
805
806
case IROp::Ror:
807
regs_.MapWithFlags(inst, X64Map::NONE, X64Map::NONE, X64Map::SHIFT);
808
if (inst.dest == inst.src1) {
809
ROR(32, regs_.R(inst.dest), regs_.R(inst.src2));
810
} else if (inst.dest == inst.src2) {
811
MOV(32, R(SCRATCH1), regs_.R(inst.src1));
812
ROR(32, R(SCRATCH1), regs_.R(inst.src2));
813
MOV(32, regs_.R(inst.dest), R(SCRATCH1));
814
} else {
815
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
816
ROR(32, regs_.R(inst.dest), regs_.R(inst.src2));
817
}
818
break;
819
820
case IROp::ShlImm:
821
// Shouldn't happen, but let's be safe of any passes that modify the ops.
822
if (inst.src2 >= 32) {
823
regs_.SetGPRImm(inst.dest, 0);
824
} else if (inst.src2 == 0) {
825
if (inst.dest != inst.src1) {
826
regs_.Map(inst);
827
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
828
}
829
} else if (inst.src2 <= 3) {
830
regs_.Map(inst);
831
LEA(32, regs_.RX(inst.dest), MScaled(regs_.RX(inst.src1), 1 << inst.src2, 0));
832
} else {
833
regs_.Map(inst);
834
if (inst.dest != inst.src1)
835
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
836
SHL(32, regs_.R(inst.dest), Imm8(inst.src2));
837
}
838
break;
839
840
case IROp::ShrImm:
841
// Shouldn't happen, but let's be safe of any passes that modify the ops.
842
if (inst.src2 >= 32) {
843
regs_.SetGPRImm(inst.dest, 0);
844
} else if (inst.src2 == 0) {
845
if (inst.dest != inst.src1) {
846
regs_.Map(inst);
847
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
848
}
849
} else {
850
regs_.Map(inst);
851
if (inst.dest != inst.src1)
852
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
853
SHR(32, regs_.R(inst.dest), Imm8(inst.src2));
854
}
855
break;
856
857
case IROp::SarImm:
858
// Shouldn't happen, but let's be safe of any passes that modify the ops.
859
if (inst.src2 >= 32) {
860
regs_.Map(inst);
861
if (inst.dest != inst.src1)
862
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
863
SAR(32, regs_.R(inst.dest), Imm8(31));
864
} else if (inst.src2 == 0) {
865
if (inst.dest != inst.src1) {
866
regs_.Map(inst);
867
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
868
}
869
} else {
870
regs_.Map(inst);
871
if (inst.dest != inst.src1)
872
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
873
SAR(32, regs_.R(inst.dest), Imm8(inst.src2));
874
}
875
break;
876
877
case IROp::RorImm:
878
if (inst.src2 == 0) {
879
if (inst.dest != inst.src1) {
880
regs_.Map(inst);
881
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
882
}
883
} else if (cpu_info.bBMI2) {
884
regs_.Map(inst);
885
RORX(32, regs_.RX(inst.dest), regs_.R(inst.src1), inst.src2 & 31);
886
} else {
887
regs_.Map(inst);
888
if (inst.dest != inst.src1)
889
MOV(32, regs_.R(inst.dest), regs_.R(inst.src1));
890
ROR(32, regs_.R(inst.dest), Imm8(inst.src2 & 31));
891
}
892
break;
893
894
default:
895
INVALIDOP;
896
break;
897
}
898
}
899
900
} // namespace MIPSComp
901
902
#endif
903
904