1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* This file contains codegen for the Mips ISA */ 18 19 #include "codegen_mips.h" 20 21 #include "base/logging.h" 22 #include "dex/mir_graph.h" 23 #include "dex/quick/mir_to_lir-inl.h" 24 #include "dex/reg_storage_eq.h" 25 #include "entrypoints/quick/quick_entrypoints.h" 26 #include "mips_lir.h" 27 #include "mirror/array-inl.h" 28 29 namespace art { 30 31 /* 32 * Compare two 64-bit values 33 * x = y return 0 34 * x < y return -1 35 * x > y return 1 36 * 37 * Mips32 implementation 38 * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 39 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 40 * subu res, t0, t1 # res = -1:1:0 for [ < > = ] 41 * bnez res, finish 42 * sltu t0, x.lo, y.lo 43 * sgtu r1, x.lo, y.lo 44 * subu res, t0, t1 45 * finish: 46 * 47 * Mips64 implementation 48 * slt temp, x, y; # (x < y) ? 1:0 49 * slt res, y, x; # (x > y) ? 1:0 50 * subu res, res, temp; # res = -1:1:0 for [ < > = ] 51 * 52 */ 53 void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { 54 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 55 rl_src2 = LoadValueWide(rl_src2, kCoreReg); 56 if (cu_->target64) { 57 RegStorage temp = AllocTempWide(); 58 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 59 NewLIR3(kMipsSlt, temp.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 60 NewLIR3(kMipsSlt, rl_result.reg.GetReg(), rl_src2.reg.GetReg(), rl_src1.reg.GetReg()); 61 NewLIR3(kMipsSubu, rl_result.reg.GetReg(), rl_result.reg.GetReg(), temp.GetReg()); 62 FreeTemp(temp); 63 StoreValue(rl_dest, rl_result); 64 } else { 65 RegStorage t0 = AllocTemp(); 66 RegStorage t1 = AllocTemp(); 67 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 68 NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg()); 69 NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg()); 70 NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); 71 LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, nullptr); 72 NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg()); 73 NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg()); 74 NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); 75 FreeTemp(t0); 76 FreeTemp(t1); 77 LIR* target = NewLIR0(kPseudoTargetLabel); 78 branch->target = target; 79 StoreValue(rl_dest, rl_result); 80 } 81 } 82 83 LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) { 84 LIR* branch; 85 MipsOpCode slt_op; 86 MipsOpCode br_op; 87 bool cmp_zero = false; 88 bool swapped = false; 89 switch (cond) { 90 case kCondEq: 91 br_op = kMipsBeq; 92 cmp_zero = true; 93 break; 94 case kCondNe: 95 br_op = kMipsBne; 96 cmp_zero = true; 97 break; 98 case kCondUlt: 99 slt_op = kMipsSltu; 100 br_op = kMipsBnez; 101 break; 102 case kCondUge: 103 slt_op = kMipsSltu; 104 br_op = kMipsBeqz; 105 break; 106 case kCondGe: 107 slt_op = kMipsSlt; 108 br_op = kMipsBeqz; 109 break; 110 case kCondGt: 111 slt_op = kMipsSlt; 112 br_op = kMipsBnez; 113 swapped = true; 114 break; 115 case kCondLe: 116 slt_op = kMipsSlt; 117 br_op = kMipsBeqz; 118 swapped = true; 119 break; 120 case kCondLt: 121 slt_op = kMipsSlt; 122 br_op = kMipsBnez; 123 break; 124 case kCondHi: // Gtu 125 slt_op = kMipsSltu; 126 br_op = kMipsBnez; 127 swapped = true; 128 break; 129 default: 130 LOG(FATAL) << "No support for ConditionCode: " << cond; 131 return nullptr; 132 } 133 if (cmp_zero) { 134 branch = NewLIR2(br_op, src1.GetReg(), src2.GetReg()); 135 } else { 136 RegStorage t_reg = AllocTemp(); 137 if (swapped) { 138 NewLIR3(slt_op, t_reg.GetReg(), src2.GetReg(), src1.GetReg()); 139 } else { 140 NewLIR3(slt_op, t_reg.GetReg(), src1.GetReg(), src2.GetReg()); 141 } 142 branch = NewLIR1(br_op, t_reg.GetReg()); 143 FreeTemp(t_reg); 144 } 145 branch->target = target; 146 return branch; 147 } 148 149 LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) { 150 LIR* branch; 151 if (check_value != 0) { 152 // TUNING: handle s16 & kCondLt/Mi case using slti. 153 RegStorage t_reg = AllocTemp(); 154 LoadConstant(t_reg, check_value); 155 branch = OpCmpBranch(cond, reg, t_reg, target); 156 FreeTemp(t_reg); 157 return branch; 158 } 159 MipsOpCode opc; 160 switch (cond) { 161 case kCondEq: opc = kMipsBeqz; break; 162 case kCondGe: opc = kMipsBgez; break; 163 case kCondGt: opc = kMipsBgtz; break; 164 case kCondLe: opc = kMipsBlez; break; 165 // case KCondMi: 166 case kCondLt: opc = kMipsBltz; break; 167 case kCondNe: opc = kMipsBnez; break; 168 default: 169 // Tuning: use slti when applicable 170 RegStorage t_reg = AllocTemp(); 171 LoadConstant(t_reg, check_value); 172 branch = OpCmpBranch(cond, reg, t_reg, target); 173 FreeTemp(t_reg); 174 return branch; 175 } 176 branch = NewLIR1(opc, reg.GetReg()); 177 branch->target = target; 178 return branch; 179 } 180 181 LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { 182 LIR* res; 183 MipsOpCode opcode; 184 185 if (!cu_->target64) { 186 // If src or dest is a pair, we'll be using low reg. 187 if (r_dest.IsPair()) { 188 r_dest = r_dest.GetLow(); 189 } 190 if (r_src.IsPair()) { 191 r_src = r_src.GetLow(); 192 } 193 } else { 194 DCHECK(!r_dest.IsPair() && !r_src.IsPair()); 195 } 196 197 if (r_dest.IsFloat() || r_src.IsFloat()) 198 return OpFpRegCopy(r_dest, r_src); 199 if (cu_->target64) { 200 // TODO: Check that r_src and r_dest are both 32 or both 64 bits length on Mips64. 201 if (r_dest.Is64Bit() || r_src.Is64Bit()) { 202 opcode = kMipsMove; 203 } else { 204 opcode = kMipsSll; 205 } 206 } else { 207 opcode = kMipsMove; 208 } 209 res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg()); 210 if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { 211 res->flags.is_nop = true; 212 } 213 return res; 214 } 215 216 void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { 217 if (r_dest != r_src) { 218 LIR *res = OpRegCopyNoInsert(r_dest, r_src); 219 AppendLIR(res); 220 } 221 } 222 223 void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { 224 if (cu_->target64) { 225 OpRegCopy(r_dest, r_src); 226 return; 227 } 228 if (r_dest != r_src) { 229 bool dest_fp = r_dest.IsFloat(); 230 bool src_fp = r_src.IsFloat(); 231 if (dest_fp) { 232 if (src_fp) { 233 // Here if both src and dest are fp registers. OpRegCopy will choose the right copy 234 // (solo or pair). 235 OpRegCopy(r_dest, r_src); 236 } else { 237 // note the operands are swapped for the mtc1 and mthc1 instr. 238 // Here if dest is fp reg and src is core reg. 239 if (fpuIs32Bit_) { 240 NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetLowReg()); 241 NewLIR2(kMipsMtc1, r_src.GetHighReg(), r_dest.GetHighReg()); 242 } else { 243 r_dest = Fp64ToSolo32(r_dest); 244 NewLIR2(kMipsMtc1, r_src.GetLowReg(), r_dest.GetReg()); 245 NewLIR2(kMipsMthc1, r_src.GetHighReg(), r_dest.GetReg()); 246 } 247 } 248 } else { 249 if (src_fp) { 250 // Here if dest is core reg and src is fp reg. 251 if (fpuIs32Bit_) { 252 NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg()); 253 NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg()); 254 } else { 255 r_src = Fp64ToSolo32(r_src); 256 NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetReg()); 257 NewLIR2(kMipsMfhc1, r_dest.GetHighReg(), r_src.GetReg()); 258 } 259 } else { 260 // Here if both src and dest are core registers. 261 // Handle overlap 262 if (r_src.GetHighReg() != r_dest.GetLowReg()) { 263 OpRegCopy(r_dest.GetLow(), r_src.GetLow()); 264 OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); 265 } else if (r_src.GetLowReg() != r_dest.GetHighReg()) { 266 OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); 267 OpRegCopy(r_dest.GetLow(), r_src.GetLow()); 268 } else { 269 RegStorage r_tmp = AllocTemp(); 270 OpRegCopy(r_tmp, r_src.GetHigh()); 271 OpRegCopy(r_dest.GetLow(), r_src.GetLow()); 272 OpRegCopy(r_dest.GetHigh(), r_tmp); 273 FreeTemp(r_tmp); 274 } 275 } 276 } 277 } 278 } 279 280 void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, 281 int32_t true_val, int32_t false_val, RegStorage rs_dest, 282 RegisterClass dest_reg_class) { 283 UNUSED(dest_reg_class); 284 // Implement as a branch-over. 285 // TODO: Conditional move? 286 LoadConstant(rs_dest, true_val); 287 LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, nullptr); 288 LoadConstant(rs_dest, false_val); 289 LIR* target_label = NewLIR0(kPseudoTargetLabel); 290 ne_branchover->target = target_label; 291 } 292 293 void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { 294 UNUSED(bb, mir); 295 UNIMPLEMENTED(FATAL) << "Need codegen for select"; 296 } 297 298 void MipsMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { 299 UNUSED(bb, mir); 300 UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; 301 } 302 303 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2, 304 bool is_div) { 305 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 306 307 if (isaIsR6_) { 308 NewLIR3(is_div ? kMipsR6Div : kMipsR6Mod, rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg()); 309 } else { 310 NewLIR2(kMipsR2Div, reg1.GetReg(), reg2.GetReg()); 311 NewLIR1(is_div ? kMipsR2Mflo : kMipsR2Mfhi, rl_result.reg.GetReg()); 312 } 313 return rl_result; 314 } 315 316 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) { 317 RegStorage t_reg = AllocTemp(); 318 // lit is guarantee to be a 16-bit constant 319 if (IsUint<16>(lit)) { 320 NewLIR3(kMipsOri, t_reg.GetReg(), rZERO, lit); 321 } else { 322 // Addiu will sign extend the entire width (32 or 64) of the register. 323 NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit); 324 } 325 RegLocation rl_result = GenDivRem(rl_dest, reg1, t_reg, is_div); 326 FreeTemp(t_reg); 327 return rl_result; 328 } 329 330 RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, 331 bool is_div, int flags) { 332 UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags); 333 LOG(FATAL) << "Unexpected use of GenDivRem for Mips"; 334 UNREACHABLE(); 335 } 336 337 RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, 338 bool is_div) { 339 UNUSED(rl_dest, rl_src1, lit, is_div); 340 LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips"; 341 UNREACHABLE(); 342 } 343 344 bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { 345 UNUSED(info, is_long, is_object); 346 return false; 347 } 348 349 bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) { 350 UNUSED(info); 351 // TODO: add Mips implementation. 352 return false; 353 } 354 355 bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) { 356 UNUSED(info); 357 // TODO: add Mips implementation. 358 return false; 359 } 360 361 bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) { 362 UNUSED(info); 363 return false; 364 } 365 366 bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { 367 if (size != kSignedByte) { 368 // MIPS supports only aligned access. Defer unaligned access to JNI implementation. 369 return false; 370 } 371 RegLocation rl_src_address = info->args[0]; // Long address. 372 if (!cu_->target64) { 373 rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1]. 374 } 375 RegLocation rl_dest = InlineTarget(info); 376 RegLocation rl_address; 377 if (cu_->target64) { 378 rl_address = LoadValueWide(rl_src_address, kCoreReg); 379 } else { 380 rl_address = LoadValue(rl_src_address, kCoreReg); 381 } 382 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 383 DCHECK(size == kSignedByte); 384 LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); 385 StoreValue(rl_dest, rl_result); 386 return true; 387 } 388 389 bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { 390 if (size != kSignedByte) { 391 // MIPS supports only aligned access. Defer unaligned access to JNI implementation. 392 return false; 393 } 394 RegLocation rl_src_address = info->args[0]; // Long address. 395 if (!cu_->target64) { 396 rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1]. 397 } 398 RegLocation rl_src_value = info->args[2]; // [size] value. 399 RegLocation rl_address; 400 if (cu_->target64) { 401 rl_address = LoadValueWide(rl_src_address, kCoreReg); 402 } else { 403 rl_address = LoadValue(rl_src_address, kCoreReg); 404 } 405 DCHECK(size == kSignedByte); 406 RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); 407 StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); 408 return true; 409 } 410 411 void MipsMir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) { 412 UNUSED(reg, target); 413 LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips"; 414 UNREACHABLE(); 415 } 416 417 LIR* MipsMir2Lir::OpVldm(RegStorage r_base, int count) { 418 UNUSED(r_base, count); 419 LOG(FATAL) << "Unexpected use of OpVldm for Mips"; 420 UNREACHABLE(); 421 } 422 423 LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) { 424 UNUSED(r_base, count); 425 LOG(FATAL) << "Unexpected use of OpVstm for Mips"; 426 UNREACHABLE(); 427 } 428 429 void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, 430 int first_bit, int second_bit) { 431 UNUSED(lit); 432 RegStorage t_reg = AllocTemp(); 433 OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit); 434 OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg); 435 FreeTemp(t_reg); 436 if (first_bit != 0) { 437 OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit); 438 } 439 } 440 441 void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) { 442 if (cu_->target64) { 443 GenDivZeroCheck(reg); 444 } else { 445 DCHECK(reg.IsPair()); // TODO: support k64BitSolo. 446 RegStorage t_reg = AllocTemp(); 447 OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); 448 GenDivZeroCheck(t_reg); 449 FreeTemp(t_reg); 450 } 451 } 452 453 // Test suspend flag, return target of taken suspend branch. 454 LIR* MipsMir2Lir::OpTestSuspend(LIR* target) { 455 OpRegImm(kOpSub, TargetPtrReg(kSuspend), 1); 456 return OpCmpImmBranch((target == nullptr) ? kCondEq : kCondNe, TargetPtrReg(kSuspend), 0, target); 457 } 458 459 // Decrement register and branch on condition. 460 LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) { 461 OpRegImm(kOpSub, reg, 1); 462 return OpCmpImmBranch(c_code, reg, 0, target); 463 } 464 465 bool MipsMir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, 466 RegLocation rl_src, RegLocation rl_dest, int lit) { 467 UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit); 468 LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips"; 469 UNREACHABLE(); 470 } 471 472 bool MipsMir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) { 473 UNUSED(rl_src, rl_dest, lit); 474 LOG(FATAL) << "Unexpected use of easyMultiply in Mips"; 475 UNREACHABLE(); 476 } 477 478 LIR* MipsMir2Lir::OpIT(ConditionCode cond, const char* guide) { 479 UNUSED(cond, guide); 480 LOG(FATAL) << "Unexpected use of OpIT in Mips"; 481 UNREACHABLE(); 482 } 483 484 void MipsMir2Lir::OpEndIT(LIR* it) { 485 UNUSED(it); 486 LOG(FATAL) << "Unexpected use of OpEndIT in Mips"; 487 } 488 489 void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { 490 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 491 rl_src2 = LoadValueWide(rl_src2, kCoreReg); 492 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 493 /* 494 * [v1 v0] = [a1 a0] + [a3 a2]; 495 * addu v0,a2,a0 496 * addu t1,a3,a1 497 * sltu v1,v0,a2 498 * addu v1,v1,t1 499 */ 500 501 OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src2.reg.GetLow(), rl_src1.reg.GetLow()); 502 RegStorage t_reg = AllocTemp(); 503 OpRegRegReg(kOpAdd, t_reg, rl_src2.reg.GetHigh(), rl_src1.reg.GetHigh()); 504 NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(), 505 rl_src2.reg.GetLowReg()); 506 OpRegRegReg(kOpAdd, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); 507 FreeTemp(t_reg); 508 StoreValueWide(rl_dest, rl_result); 509 } 510 511 void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { 512 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 513 rl_src2 = LoadValueWide(rl_src2, kCoreReg); 514 RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); 515 /* 516 * [v1 v0] = [a1 a0] - [a3 a2]; 517 * sltu t1,a0,a2 518 * subu v0,a0,a2 519 * subu v1,a1,a3 520 * subu v1,v1,t1 521 */ 522 523 RegStorage t_reg = AllocTemp(); 524 NewLIR3(kMipsSltu, t_reg.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg()); 525 OpRegRegReg(kOpSub, rl_result.reg.GetLow(), rl_src1.reg.GetLow(), rl_src2.reg.GetLow()); 526 OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_src1.reg.GetHigh(), rl_src2.reg.GetHigh()); 527 OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); 528 FreeTemp(t_reg); 529 StoreValueWide(rl_dest, rl_result); 530 } 531 532 void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, 533 RegLocation rl_src2, int flags) { 534 if (cu_->target64) { 535 switch (opcode) { 536 case Instruction::NOT_LONG: 537 GenNotLong(rl_dest, rl_src2); 538 return; 539 case Instruction::ADD_LONG: 540 case Instruction::ADD_LONG_2ADDR: 541 GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2); 542 return; 543 case Instruction::SUB_LONG: 544 case Instruction::SUB_LONG_2ADDR: 545 GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2); 546 return; 547 case Instruction::MUL_LONG: 548 case Instruction::MUL_LONG_2ADDR: 549 GenMulLong(rl_dest, rl_src1, rl_src2); 550 return; 551 case Instruction::DIV_LONG: 552 case Instruction::DIV_LONG_2ADDR: 553 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags); 554 return; 555 case Instruction::REM_LONG: 556 case Instruction::REM_LONG_2ADDR: 557 GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags); 558 return; 559 case Instruction::AND_LONG: 560 case Instruction::AND_LONG_2ADDR: 561 GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2); 562 return; 563 case Instruction::OR_LONG: 564 case Instruction::OR_LONG_2ADDR: 565 GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2); 566 return; 567 case Instruction::XOR_LONG: 568 case Instruction::XOR_LONG_2ADDR: 569 GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2); 570 return; 571 case Instruction::NEG_LONG: 572 GenNegLong(rl_dest, rl_src2); 573 return; 574 575 default: 576 LOG(FATAL) << "Invalid long arith op"; 577 return; 578 } 579 } else { 580 switch (opcode) { 581 case Instruction::ADD_LONG: 582 case Instruction::ADD_LONG_2ADDR: 583 GenAddLong(rl_dest, rl_src1, rl_src2); 584 return; 585 case Instruction::SUB_LONG: 586 case Instruction::SUB_LONG_2ADDR: 587 GenSubLong(rl_dest, rl_src1, rl_src2); 588 return; 589 case Instruction::NEG_LONG: 590 GenNegLong(rl_dest, rl_src2); 591 return; 592 default: 593 break; 594 } 595 // Fallback for all other ops. 596 Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); 597 } 598 } 599 600 void MipsMir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1, 601 RegLocation rl_src2) { 602 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 603 rl_src2 = LoadValueWide(rl_src2, kCoreReg); 604 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 605 OpRegRegReg(op, rl_result.reg, rl_src1.reg, rl_src2.reg); 606 StoreValueWide(rl_dest, rl_result); 607 } 608 609 void MipsMir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) { 610 rl_src = LoadValueWide(rl_src, kCoreReg); 611 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 612 OpRegReg(kOpMvn, rl_result.reg, rl_src.reg); 613 StoreValueWide(rl_dest, rl_result); 614 } 615 616 void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { 617 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 618 rl_src2 = LoadValueWide(rl_src2, kCoreReg); 619 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 620 NewLIR3(kMips64Dmul, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); 621 StoreValueWide(rl_dest, rl_result); 622 } 623 624 void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, 625 RegLocation rl_src2, bool is_div, int flags) { 626 UNUSED(opcode); 627 // TODO: Implement easy div/rem? 628 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 629 rl_src2 = LoadValueWide(rl_src2, kCoreReg); 630 if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) { 631 GenDivZeroCheckWide(rl_src2.reg); 632 } 633 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 634 NewLIR3(is_div ? kMips64Ddiv : kMips64Dmod, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), 635 rl_src2.reg.GetReg()); 636 StoreValueWide(rl_dest, rl_result); 637 } 638 639 void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { 640 rl_src = LoadValueWide(rl_src, kCoreReg); 641 RegLocation rl_result; 642 643 if (cu_->target64) { 644 rl_result = EvalLocWide(rl_dest, kCoreReg, true); 645 OpRegReg(kOpNeg, rl_result.reg, rl_src.reg); 646 StoreValueWide(rl_dest, rl_result); 647 } else { 648 rl_result = EvalLoc(rl_dest, kCoreReg, true); 649 // [v1 v0] = -[a1 a0] 650 // negu v0,a0 651 // negu v1,a1 652 // sltu t1,r_zero 653 // subu v1,v1,t1 654 OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow()); 655 OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh()); 656 RegStorage t_reg = AllocTemp(); 657 NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg()); 658 OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); 659 FreeTemp(t_reg); 660 StoreValueWide(rl_dest, rl_result); 661 } 662 } 663 664 /* 665 * Generate array load 666 */ 667 void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, 668 RegLocation rl_index, RegLocation rl_dest, int scale) { 669 RegisterClass reg_class = RegClassBySize(size); 670 int len_offset = mirror::Array::LengthOffset().Int32Value(); 671 int data_offset; 672 RegLocation rl_result; 673 rl_array = LoadValue(rl_array, kRefReg); 674 rl_index = LoadValue(rl_index, kCoreReg); 675 676 // FIXME: need to add support for rl_index.is_const. 677 678 if (size == k64 || size == kDouble) { 679 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); 680 } else { 681 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); 682 } 683 684 // Null object? 685 GenNullCheck(rl_array.reg, opt_flags); 686 687 RegStorage reg_ptr = (cu_->target64) ? AllocTempRef() : AllocTemp(); 688 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); 689 RegStorage reg_len; 690 if (needs_range_check) { 691 reg_len = AllocTemp(); 692 // Get len. 693 Load32Disp(rl_array.reg, len_offset, reg_len); 694 MarkPossibleNullPointerException(opt_flags); 695 } else { 696 ForceImplicitNullCheck(rl_array.reg, opt_flags, false); 697 } 698 // reg_ptr -> array data. 699 OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); 700 FreeTemp(rl_array.reg); 701 if ((size == k64) || (size == kDouble)) { 702 if (scale) { 703 RegStorage r_new_index = AllocTemp(); 704 OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); 705 OpRegReg(kOpAdd, reg_ptr, r_new_index); 706 FreeTemp(r_new_index); 707 } else { 708 OpRegReg(kOpAdd, reg_ptr, rl_index.reg); 709 } 710 FreeTemp(rl_index.reg); 711 rl_result = EvalLoc(rl_dest, reg_class, true); 712 713 if (needs_range_check) { 714 GenArrayBoundsCheck(rl_index.reg, reg_len); 715 FreeTemp(reg_len); 716 } 717 LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile); 718 719 FreeTemp(reg_ptr); 720 StoreValueWide(rl_dest, rl_result); 721 } else { 722 rl_result = EvalLoc(rl_dest, reg_class, true); 723 724 if (needs_range_check) { 725 GenArrayBoundsCheck(rl_index.reg, reg_len); 726 FreeTemp(reg_len); 727 } 728 729 if (cu_->target64) { 730 if (rl_result.ref) { 731 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), As32BitReg(rl_result.reg), scale, 732 kReference); 733 } else { 734 LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); 735 } 736 } else { 737 LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); 738 } 739 740 FreeTemp(reg_ptr); 741 StoreValue(rl_dest, rl_result); 742 } 743 } 744 745 /* 746 * Generate array store 747 * 748 */ 749 void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, 750 RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) { 751 RegisterClass reg_class = RegClassBySize(size); 752 int len_offset = mirror::Array::LengthOffset().Int32Value(); 753 int data_offset; 754 755 if (size == k64 || size == kDouble) { 756 data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); 757 } else { 758 data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); 759 } 760 761 rl_array = LoadValue(rl_array, kRefReg); 762 rl_index = LoadValue(rl_index, kCoreReg); 763 764 // FIXME: need to add support for rl_index.is_const. 765 766 RegStorage reg_ptr; 767 bool allocated_reg_ptr_temp = false; 768 if (IsTemp(rl_array.reg) && !card_mark) { 769 Clobber(rl_array.reg); 770 reg_ptr = rl_array.reg; 771 } else { 772 reg_ptr = AllocTemp(); 773 OpRegCopy(reg_ptr, rl_array.reg); 774 allocated_reg_ptr_temp = true; 775 } 776 777 // Null object? 778 GenNullCheck(rl_array.reg, opt_flags); 779 780 bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); 781 RegStorage reg_len; 782 if (needs_range_check) { 783 reg_len = AllocTemp(); 784 // NOTE: max live temps(4) here. 785 // Get len. 786 Load32Disp(rl_array.reg, len_offset, reg_len); 787 MarkPossibleNullPointerException(opt_flags); 788 } else { 789 ForceImplicitNullCheck(rl_array.reg, opt_flags, false); 790 } 791 // reg_ptr -> array data. 792 OpRegImm(kOpAdd, reg_ptr, data_offset); 793 // At this point, reg_ptr points to array, 2 live temps. 794 if ((size == k64) || (size == kDouble)) { 795 // TUNING: specific wide routine that can handle fp regs. 796 if (scale) { 797 RegStorage r_new_index = AllocTemp(); 798 OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); 799 OpRegReg(kOpAdd, reg_ptr, r_new_index); 800 FreeTemp(r_new_index); 801 } else { 802 OpRegReg(kOpAdd, reg_ptr, rl_index.reg); 803 } 804 rl_src = LoadValueWide(rl_src, reg_class); 805 806 if (needs_range_check) { 807 GenArrayBoundsCheck(rl_index.reg, reg_len); 808 FreeTemp(reg_len); 809 } 810 811 StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile); 812 } else { 813 rl_src = LoadValue(rl_src, reg_class); 814 if (needs_range_check) { 815 GenArrayBoundsCheck(rl_index.reg, reg_len); 816 FreeTemp(reg_len); 817 } 818 StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); 819 } 820 if (allocated_reg_ptr_temp) { 821 FreeTemp(reg_ptr); 822 } 823 if (card_mark) { 824 MarkGCCard(opt_flags, rl_src.reg, rl_array.reg); 825 } 826 } 827 828 void MipsMir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, 829 RegLocation rl_shift) { 830 if (!cu_->target64) { 831 Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); 832 return; 833 } 834 OpKind op = kOpBkpt; 835 switch (opcode) { 836 case Instruction::SHL_LONG: 837 case Instruction::SHL_LONG_2ADDR: 838 op = kOpLsl; 839 break; 840 case Instruction::SHR_LONG: 841 case Instruction::SHR_LONG_2ADDR: 842 op = kOpAsr; 843 break; 844 case Instruction::USHR_LONG: 845 case Instruction::USHR_LONG_2ADDR: 846 op = kOpLsr; 847 break; 848 default: 849 LOG(FATAL) << "Unexpected case: " << opcode; 850 } 851 rl_shift = LoadValue(rl_shift, kCoreReg); 852 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 853 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 854 OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg)); 855 StoreValueWide(rl_dest, rl_result); 856 } 857 858 void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, 859 RegLocation rl_src1, RegLocation rl_shift, int flags) { 860 UNUSED(flags); 861 if (!cu_->target64) { 862 // Default implementation is just to ignore the constant case. 863 GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); 864 return; 865 } 866 OpKind op = kOpBkpt; 867 // Per spec, we only care about low 6 bits of shift amount. 868 int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; 869 rl_src1 = LoadValueWide(rl_src1, kCoreReg); 870 if (shift_amount == 0) { 871 StoreValueWide(rl_dest, rl_src1); 872 return; 873 } 874 875 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 876 switch (opcode) { 877 case Instruction::SHL_LONG: 878 case Instruction::SHL_LONG_2ADDR: 879 op = kOpLsl; 880 break; 881 case Instruction::SHR_LONG: 882 case Instruction::SHR_LONG_2ADDR: 883 op = kOpAsr; 884 break; 885 case Instruction::USHR_LONG: 886 case Instruction::USHR_LONG_2ADDR: 887 op = kOpLsr; 888 break; 889 default: 890 LOG(FATAL) << "Unexpected case"; 891 } 892 OpRegRegImm(op, rl_result.reg, rl_src1.reg, shift_amount); 893 StoreValueWide(rl_dest, rl_result); 894 } 895 896 void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, 897 RegLocation rl_src1, RegLocation rl_src2, int flags) { 898 // Default - bail to non-const handler. 899 GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); 900 } 901 902 void MipsMir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { 903 if (!cu_->target64) { 904 Mir2Lir::GenIntToLong(rl_dest, rl_src); 905 return; 906 } 907 rl_src = LoadValue(rl_src, kCoreReg); 908 RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); 909 NewLIR3(kMipsSll, rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0); 910 StoreValueWide(rl_dest, rl_result); 911 } 912 913 void MipsMir2Lir::GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, 914 RegLocation rl_src, RegisterClass reg_class) { 915 FlushAllRegs(); // Send everything to home location. 916 CallRuntimeHelperRegLocation(trampoline, rl_src, false); 917 if (rl_dest.wide) { 918 RegLocation rl_result; 919 rl_result = GetReturnWide(reg_class); 920 StoreValueWide(rl_dest, rl_result); 921 } else { 922 RegLocation rl_result; 923 rl_result = GetReturn(reg_class); 924 StoreValue(rl_dest, rl_result); 925 } 926 } 927 928 } // namespace art 929