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