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/IR/IRCompALU.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 <algorithm>
19
20
#include "Core/MIPS/MIPS.h"
21
#include "Core/MIPS/MIPSCodeUtils.h"
22
#include "Core/MIPS/IR/IRFrontend.h"
23
#include "Common/CPUDetect.h"
24
25
#define _RS MIPS_GET_RS(op)
26
#define _RT MIPS_GET_RT(op)
27
#define _RD MIPS_GET_RD(op)
28
#define _FS MIPS_GET_FS(op)
29
#define _FT MIPS_GET_FT(op)
30
#define _FD MIPS_GET_FD(op)
31
#define _SA MIPS_GET_SA(op)
32
#define _POS ((op>> 6) & 0x1F)
33
#define _SIZE ((op>>11) & 0x1F)
34
#define _IMM16 (signed short)(op & 0xFFFF)
35
#define _IMM26 (op & 0x03FFFFFF)
36
37
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
38
// Currently known non working ones should have DISABLE.
39
40
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
41
#define CONDITIONAL_DISABLE(flag) if (opts.disableFlags & (uint32_t)JitDisable::flag) { Comp_Generic(op); return; }
42
#define DISABLE { Comp_Generic(op); return; }
43
#define INVALIDOP { Comp_Generic(op); return; }
44
45
namespace MIPSComp {
46
47
void IRFrontend::Comp_IType(MIPSOpcode op) {
48
CONDITIONAL_DISABLE(ALU_IMM);
49
50
u32 uimm = (u16)_IMM16;
51
s32 simm = SignExtend16ToS32(op);
52
u32 suimm = SignExtend16ToU32(op);
53
54
MIPSGPReg rt = _RT;
55
MIPSGPReg rs = _RS;
56
57
// noop, won't write to ZERO.
58
if (rt == MIPS_REG_ZERO)
59
return;
60
61
switch (op >> 26) {
62
case 8: // same as addiu?
63
case 9: // R(rt) = R(rs) + simm; break; //addiu
64
ir.Write(IROp::AddConst, rt, rs, ir.AddConstant(simm));
65
break;
66
67
case 12: ir.Write(IROp::AndConst, rt, rs, ir.AddConstant(uimm)); break;
68
case 13: ir.Write(IROp::OrConst, rt, rs, ir.AddConstant(uimm)); break;
69
case 14: ir.Write(IROp::XorConst, rt, rs, ir.AddConstant(uimm)); break;
70
71
case 10: // R(rt) = (s32)R(rs) < simm; break; //slti
72
ir.Write(IROp::SltConst, rt, rs, ir.AddConstant(simm));
73
break;
74
75
case 11: // R(rt) = R(rs) < suimm; break; //sltiu
76
ir.Write(IROp::SltUConst, rt, rs, ir.AddConstant(suimm));
77
break;
78
79
case 15: // R(rt) = uimm << 16; //lui
80
ir.WriteSetConstant(rt, uimm << 16);
81
break;
82
83
default:
84
INVALIDOP;
85
break;
86
}
87
}
88
89
void IRFrontend::Comp_RType2(MIPSOpcode op) {
90
CONDITIONAL_DISABLE(ALU_BIT);
91
92
MIPSGPReg rs = _RS;
93
MIPSGPReg rd = _RD;
94
95
// Don't change $zr.
96
if (rd == MIPS_REG_ZERO)
97
return;
98
99
switch (op & 63) {
100
case 22: //clz
101
ir.Write(IROp::Clz, rd, rs);
102
break;
103
case 23: //clo
104
ir.Write(IROp::Not, IRTEMP_0, rs);
105
ir.Write(IROp::Clz, rd, IRTEMP_0);
106
break;
107
default:
108
INVALIDOP;
109
break;
110
}
111
}
112
113
void IRFrontend::Comp_RType3(MIPSOpcode op) {
114
CONDITIONAL_DISABLE(ALU);
115
116
MIPSGPReg rt = _RT;
117
MIPSGPReg rs = _RS;
118
MIPSGPReg rd = _RD;
119
120
// noop, won't write to ZERO.
121
if (rd == MIPS_REG_ZERO)
122
return;
123
124
switch (op & 63) {
125
case 10: //if (!R(rt)) R(rd) = R(rs); break; //movz
126
ir.Write(IROp::MovZ, rd, rt, rs);
127
break;
128
case 11:// if (R(rt)) R(rd) = R(rs); break; //movn
129
ir.Write(IROp::MovNZ, rd, rt, rs);
130
break;
131
132
case 32: //R(rd) = R(rs) + R(rt); break; //add
133
case 33: //R(rd) = R(rs) + R(rt); break; //addu
134
ir.Write(IROp::Add, rd, rs, rt);
135
break;
136
137
case 34: //R(rd) = R(rs) - R(rt); break; //sub
138
case 35: //R(rd) = R(rs) - R(rt); break; //subu
139
ir.Write(IROp::Sub, rd, rs, rt);
140
break;
141
142
case 36: //R(rd) = R(rs) & R(rt); break; //and
143
ir.Write(IROp::And, rd, rs, rt);
144
break;
145
case 37: //R(rd) = R(rs) | R(rt); break; //or
146
ir.Write(IROp::Or, rd, rs, rt);
147
break;
148
case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor
149
ir.Write(IROp::Xor, rd, rs, rt);
150
break;
151
152
case 39: // R(rd) = ~(R(rs) | R(rt)); break; //nor
153
if (rs == 0) {
154
ir.Write(IROp::Not, rd, rt);
155
} else if (rt == 0) {
156
ir.Write(IROp::Not, rd, rs);
157
} else {
158
ir.Write(IROp::Or, IRTEMP_0, rs, rt);
159
ir.Write(IROp::Not, rd, IRTEMP_0);
160
}
161
break;
162
163
case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt
164
ir.Write(IROp::Slt, rd, rs, rt);
165
break;
166
167
case 43: //R(rd) = R(rs) < R(rt); break; //sltu
168
ir.Write(IROp::SltU, rd, rs, rt);
169
break;
170
171
case 44: //R(rd) = max(R(rs), R(rt); break; //max
172
ir.Write(IROp::Max, rd, rs, rt);
173
break;
174
175
case 45: //R(rd) = min(R(rs), R(rt)); break; //min
176
ir.Write(IROp::Min, rd, rs, rt);
177
break;
178
179
default:
180
INVALIDOP;
181
break;
182
}
183
}
184
185
void IRFrontend::CompShiftImm(MIPSOpcode op, IROp shiftOpImm, int sa) {
186
MIPSGPReg rd = _RD;
187
MIPSGPReg rt = _RT;
188
ir.Write(shiftOpImm, rd, rt, sa);
189
}
190
191
void IRFrontend::CompShiftVar(MIPSOpcode op, IROp shiftOp) {
192
MIPSGPReg rd = _RD;
193
MIPSGPReg rt = _RT;
194
MIPSGPReg rs = _RS;
195
196
if (opts.optimizeForInterpreter) {
197
// The interpreter already masks where needed, don't need to generate extra ops.
198
ir.Write(shiftOp, rd, rt, rs);
199
} else {
200
ir.Write(IROp::AndConst, IRTEMP_0, rs, ir.AddConstant(31));
201
ir.Write(shiftOp, rd, rt, IRTEMP_0);
202
}
203
}
204
205
void IRFrontend::Comp_ShiftType(MIPSOpcode op) {
206
CONDITIONAL_DISABLE(ALU);
207
MIPSGPReg rs = _RS;
208
MIPSGPReg rd = _RD;
209
int sa = _SA;
210
211
// noop, won't write to ZERO.
212
if (rd == MIPS_REG_ZERO)
213
return;
214
215
// WARNING: srl/rotr and srlv/rotrv share encodings (differentiated using unused bits.)
216
switch (op & 0x3f) {
217
case 0: CompShiftImm(op, IROp::ShlImm, sa); break; //sll
218
case 2: CompShiftImm(op, (rs == 1 ? IROp::RorImm : IROp::ShrImm), sa); break; //srl
219
case 3: CompShiftImm(op, IROp::SarImm, sa); break; //sra
220
case 4: CompShiftVar(op, IROp::Shl); break; //sllv
221
case 6: CompShiftVar(op, (sa == 1 ? IROp::Ror : IROp::Shr)); break; //srlv
222
case 7: CompShiftVar(op, IROp::Sar); break; //srav
223
224
default:
225
INVALIDOP;
226
break;
227
}
228
}
229
230
void IRFrontend::Comp_Special3(MIPSOpcode op) {
231
CONDITIONAL_DISABLE(ALU_BIT);
232
MIPSGPReg rs = _RS;
233
MIPSGPReg rt = _RT;
234
235
int pos = _POS;
236
int size = _SIZE + 1;
237
u32 mask = 0xFFFFFFFFUL >> (32 - size);
238
239
// Don't change $zr.
240
if (rt == MIPS_REG_ZERO)
241
return;
242
243
switch (op & 0x3f) {
244
case 0x0: // ext
245
if (pos != 0) {
246
ir.Write(IROp::ShrImm, rt, rs, pos);
247
ir.Write(IROp::AndConst, rt, rt, ir.AddConstant(mask));
248
} else {
249
ir.Write(IROp::AndConst, rt, rs, ir.AddConstant(mask));
250
}
251
break;
252
253
case 0x4: //ins
254
{
255
// TODO: Might be good to support natively in the interpreter. Though, would have to
256
// abuse a register as a constant
257
u32 sourcemask = mask >> pos;
258
u32 destmask = ~(sourcemask << pos);
259
260
if (size != 32) {
261
// Need to use the sourcemask.
262
ir.Write(IROp::AndConst, IRTEMP_0, rs, ir.AddConstant(sourcemask));
263
if (pos != 0) {
264
ir.Write(IROp::ShlImm, IRTEMP_0, IRTEMP_0, pos);
265
}
266
} else {
267
// If the shl takes care of the sourcemask, don't need to and.
268
if (pos != 0) {
269
ir.Write(IROp::ShlImm, IRTEMP_0, rs, pos);
270
} else {
271
ir.Write(IROp::Mov, IRTEMP_0, rs);
272
}
273
}
274
ir.Write(IROp::AndConst, rt, rt, ir.AddConstant(destmask));
275
ir.Write(IROp::Or, rt, rt, IRTEMP_0);
276
}
277
break;
278
279
default:
280
INVALIDOP;
281
break;
282
}
283
}
284
285
286
void IRFrontend::Comp_Allegrex(MIPSOpcode op) {
287
CONDITIONAL_DISABLE(ALU_BIT);
288
MIPSGPReg rt = _RT;
289
MIPSGPReg rd = _RD;
290
291
// Don't change $zr.
292
if (rd == MIPS_REG_ZERO)
293
return;
294
295
switch ((op >> 6) & 31) {
296
case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));
297
ir.Write(IROp::Ext8to32, rd, rt);
298
break;
299
300
case 24: // seh
301
ir.Write(IROp::Ext16to32, rd, rt);
302
break;
303
304
case 20: // bitrev
305
ir.Write(IROp::ReverseBits, rd, rt);
306
break;
307
308
default:
309
INVALIDOP;
310
return;
311
}
312
}
313
314
void IRFrontend::Comp_Allegrex2(MIPSOpcode op) {
315
CONDITIONAL_DISABLE(ALU_BIT);
316
MIPSGPReg rt = _RT;
317
MIPSGPReg rd = _RD;
318
319
// Don't change $zr.
320
if (rd == MIPS_REG_ZERO)
321
return;
322
323
switch (op & 0x3ff) {
324
case 0xA0: //wsbh
325
ir.Write(IROp::BSwap16, rd, rt);
326
break;
327
case 0xE0: //wsbw
328
ir.Write(IROp::BSwap32, rd, rt);
329
break;
330
default:
331
INVALIDOP;
332
break;
333
}
334
}
335
336
void IRFrontend::Comp_MulDivType(MIPSOpcode op) {
337
CONDITIONAL_DISABLE(MULDIV);
338
MIPSGPReg rt = _RT;
339
MIPSGPReg rs = _RS;
340
MIPSGPReg rd = _RD;
341
342
switch (op & 63) {
343
case 16: // R(rd) = HI; //mfhi
344
if (rd != MIPS_REG_ZERO) {
345
ir.Write(IROp::MfHi, rd);
346
}
347
break;
348
349
case 17: // HI = R(rs); //mthi
350
ir.Write(IROp::MtHi, 0, rs);
351
break;
352
353
case 18: // R(rd) = LO; break; //mflo
354
if (rd != MIPS_REG_ZERO) {
355
ir.Write(IROp::MfLo, rd);
356
}
357
break;
358
359
case 19: // LO = R(rs); break; //mtlo
360
ir.Write(IROp::MtLo, 0, rs);
361
break;
362
363
case 24: //mult (the most popular one). lo,hi = signed mul (rs * rt)
364
ir.Write(IROp::Mult, 0, rs, rt);
365
break;
366
367
case 25: //multu (2nd) lo,hi = unsigned mul (rs * rt)
368
ir.Write(IROp::MultU, 0, rs, rt);
369
break;
370
371
case 26: //div
372
ir.Write(IROp::Div, 0, rs, rt);
373
break;
374
375
case 27: //divu
376
ir.Write(IROp::DivU, 0, rs, rt);
377
break;
378
379
case 28: //madd
380
ir.Write(IROp::Madd, 0, rs, rt);
381
break;
382
383
case 29: //maddu
384
ir.Write(IROp::MaddU, 0, rs, rt);
385
break;
386
387
case 46: // msub
388
ir.Write(IROp::Msub, 0, rs, rt);
389
break;
390
391
case 47: // msubu
392
ir.Write(IROp::MsubU, 0, rs, rt);
393
break;
394
395
default:
396
INVALIDOP;
397
}
398
}
399
400
}
401
402