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