1 /* 2 * Copyright (C) 2009 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 /* 18 * This file contains codegen and support common to all supported 19 * ARM variants. It is included by: 20 * 21 * Codegen-$(TARGET_ARCH_VARIANT).c 22 * 23 * which combines this common code with specific support found in the 24 * applicable directory below this one. 25 */ 26 27 /* 28 * Mark garbage collection card. Skip if the value we're storing is null. 29 */ 30 static void markCard(CompilationUnit *cUnit, int valReg, int tgtAddrReg) 31 { 32 int regCardBase = dvmCompilerAllocTemp(cUnit); 33 int regCardNo = dvmCompilerAllocTemp(cUnit); 34 opRegImm(cUnit, kOpCmp, valReg, 0); /* storing null? */ 35 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq); 36 loadWordDisp(cUnit, rGLUE, offsetof(InterpState, cardTable), 37 regCardBase); 38 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT); 39 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0, 40 kUnsignedByte); 41 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 42 target->defMask = ENCODE_ALL; 43 branchOver->generic.target = (LIR *)target; 44 dvmCompilerFreeTemp(cUnit, regCardBase); 45 dvmCompilerFreeTemp(cUnit, regCardNo); 46 } 47 48 static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct, 49 int srcSize, int tgtSize) 50 { 51 /* 52 * Don't optimize the register usage since it calls out to template 53 * functions 54 */ 55 RegLocation rlSrc; 56 RegLocation rlDest; 57 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 58 if (srcSize == 1) { 59 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 60 loadValueDirectFixed(cUnit, rlSrc, r0); 61 } else { 62 rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 63 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1); 64 } 65 LOAD_FUNC_ADDR(cUnit, r2, (int)funct); 66 opReg(cUnit, kOpBlx, r2); 67 dvmCompilerClobberCallRegs(cUnit); 68 if (tgtSize == 1) { 69 RegLocation rlResult; 70 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 71 rlResult = dvmCompilerGetReturn(cUnit); 72 storeValue(cUnit, rlDest, rlResult); 73 } else { 74 RegLocation rlResult; 75 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 76 rlResult = dvmCompilerGetReturnWide(cUnit); 77 storeValueWide(cUnit, rlDest, rlResult); 78 } 79 return false; 80 } 81 82 static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir, 83 RegLocation rlDest, RegLocation rlSrc1, 84 RegLocation rlSrc2) 85 { 86 RegLocation rlResult; 87 void* funct; 88 89 switch (mir->dalvikInsn.opCode) { 90 case OP_ADD_FLOAT_2ADDR: 91 case OP_ADD_FLOAT: 92 funct = (void*) __aeabi_fadd; 93 break; 94 case OP_SUB_FLOAT_2ADDR: 95 case OP_SUB_FLOAT: 96 funct = (void*) __aeabi_fsub; 97 break; 98 case OP_DIV_FLOAT_2ADDR: 99 case OP_DIV_FLOAT: 100 funct = (void*) __aeabi_fdiv; 101 break; 102 case OP_MUL_FLOAT_2ADDR: 103 case OP_MUL_FLOAT: 104 funct = (void*) __aeabi_fmul; 105 break; 106 case OP_REM_FLOAT_2ADDR: 107 case OP_REM_FLOAT: 108 funct = (void*) fmodf; 109 break; 110 case OP_NEG_FLOAT: { 111 genNegFloat(cUnit, rlDest, rlSrc1); 112 return false; 113 } 114 default: 115 return true; 116 } 117 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 118 loadValueDirectFixed(cUnit, rlSrc1, r0); 119 loadValueDirectFixed(cUnit, rlSrc2, r1); 120 LOAD_FUNC_ADDR(cUnit, r2, (int)funct); 121 opReg(cUnit, kOpBlx, r2); 122 dvmCompilerClobberCallRegs(cUnit); 123 rlResult = dvmCompilerGetReturn(cUnit); 124 storeValue(cUnit, rlDest, rlResult); 125 return false; 126 } 127 128 static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir, 129 RegLocation rlDest, RegLocation rlSrc1, 130 RegLocation rlSrc2) 131 { 132 RegLocation rlResult; 133 void* funct; 134 135 switch (mir->dalvikInsn.opCode) { 136 case OP_ADD_DOUBLE_2ADDR: 137 case OP_ADD_DOUBLE: 138 funct = (void*) __aeabi_dadd; 139 break; 140 case OP_SUB_DOUBLE_2ADDR: 141 case OP_SUB_DOUBLE: 142 funct = (void*) __aeabi_dsub; 143 break; 144 case OP_DIV_DOUBLE_2ADDR: 145 case OP_DIV_DOUBLE: 146 funct = (void*) __aeabi_ddiv; 147 break; 148 case OP_MUL_DOUBLE_2ADDR: 149 case OP_MUL_DOUBLE: 150 funct = (void*) __aeabi_dmul; 151 break; 152 case OP_REM_DOUBLE_2ADDR: 153 case OP_REM_DOUBLE: 154 funct = (void*) fmod; 155 break; 156 case OP_NEG_DOUBLE: { 157 genNegDouble(cUnit, rlDest, rlSrc1); 158 return false; 159 } 160 default: 161 return true; 162 } 163 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 164 LOAD_FUNC_ADDR(cUnit, rlr, (int)funct); 165 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); 166 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3); 167 opReg(cUnit, kOpBlx, rlr); 168 dvmCompilerClobberCallRegs(cUnit); 169 rlResult = dvmCompilerGetReturnWide(cUnit); 170 storeValueWide(cUnit, rlDest, rlResult); 171 return false; 172 } 173 174 static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir) 175 { 176 OpCode opCode = mir->dalvikInsn.opCode; 177 178 switch (opCode) { 179 case OP_INT_TO_FLOAT: 180 return genConversionCall(cUnit, mir, (void*)__aeabi_i2f, 1, 1); 181 case OP_FLOAT_TO_INT: 182 return genConversionCall(cUnit, mir, (void*)__aeabi_f2iz, 1, 1); 183 case OP_DOUBLE_TO_FLOAT: 184 return genConversionCall(cUnit, mir, (void*)__aeabi_d2f, 2, 1); 185 case OP_FLOAT_TO_DOUBLE: 186 return genConversionCall(cUnit, mir, (void*)__aeabi_f2d, 1, 2); 187 case OP_INT_TO_DOUBLE: 188 return genConversionCall(cUnit, mir, (void*)__aeabi_i2d, 1, 2); 189 case OP_DOUBLE_TO_INT: 190 return genConversionCall(cUnit, mir, (void*)__aeabi_d2iz, 2, 1); 191 case OP_FLOAT_TO_LONG: 192 return genConversionCall(cUnit, mir, (void*)dvmJitf2l, 1, 2); 193 case OP_LONG_TO_FLOAT: 194 return genConversionCall(cUnit, mir, (void*)__aeabi_l2f, 2, 1); 195 case OP_DOUBLE_TO_LONG: 196 return genConversionCall(cUnit, mir, (void*)dvmJitd2l, 2, 2); 197 case OP_LONG_TO_DOUBLE: 198 return genConversionCall(cUnit, mir, (void*)__aeabi_l2d, 2, 2); 199 default: 200 return true; 201 } 202 return false; 203 } 204 205 #if defined(WITH_SELF_VERIFICATION) 206 static void selfVerificationBranchInsert(LIR *currentLIR, ArmOpCode opCode, 207 int dest, int src1) 208 { 209 ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true); 210 insn->opCode = opCode; 211 insn->operands[0] = dest; 212 insn->operands[1] = src1; 213 setupResourceMasks(insn); 214 dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn); 215 } 216 217 static void selfVerificationBranchInsertPass(CompilationUnit *cUnit) 218 { 219 ArmLIR *thisLIR; 220 TemplateOpCode opCode = TEMPLATE_MEM_OP_DECODE; 221 222 for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn; 223 thisLIR != (ArmLIR *) cUnit->lastLIRInsn; 224 thisLIR = NEXT_LIR(thisLIR)) { 225 if (thisLIR->branchInsertSV) { 226 /* Branch to mem op decode template */ 227 selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1, 228 (int) gDvmJit.codeCache + templateEntryOffsets[opCode], 229 (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); 230 selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2, 231 (int) gDvmJit.codeCache + templateEntryOffsets[opCode], 232 (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); 233 } 234 } 235 } 236 #endif 237 238 /* Generate conditional branch instructions */ 239 static ArmLIR *genConditionalBranch(CompilationUnit *cUnit, 240 ArmConditionCode cond, 241 ArmLIR *target) 242 { 243 ArmLIR *branch = opCondBranch(cUnit, cond); 244 branch->generic.target = (LIR *) target; 245 return branch; 246 } 247 248 /* Generate a unconditional branch to go to the interpreter */ 249 static inline ArmLIR *genTrap(CompilationUnit *cUnit, int dOffset, 250 ArmLIR *pcrLabel) 251 { 252 ArmLIR *branch = opNone(cUnit, kOpUncondBr); 253 return genCheckCommon(cUnit, dOffset, branch, pcrLabel); 254 } 255 256 /* Load a wide field from an object instance */ 257 static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) 258 { 259 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 260 RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 261 RegLocation rlResult; 262 rlObj = loadValue(cUnit, rlObj, kCoreReg); 263 int regPtr = dvmCompilerAllocTemp(cUnit); 264 265 assert(rlDest.wide); 266 267 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 268 NULL);/* null object? */ 269 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); 270 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 271 272 HEAP_ACCESS_SHADOW(true); 273 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); 274 HEAP_ACCESS_SHADOW(false); 275 276 dvmCompilerFreeTemp(cUnit, regPtr); 277 storeValueWide(cUnit, rlDest, rlResult); 278 } 279 280 /* Store a wide field to an object instance */ 281 static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) 282 { 283 RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 284 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2); 285 rlObj = loadValue(cUnit, rlObj, kCoreReg); 286 int regPtr; 287 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); 288 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 289 NULL);/* null object? */ 290 regPtr = dvmCompilerAllocTemp(cUnit); 291 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); 292 293 HEAP_ACCESS_SHADOW(true); 294 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg); 295 HEAP_ACCESS_SHADOW(false); 296 297 dvmCompilerFreeTemp(cUnit, regPtr); 298 } 299 300 /* 301 * Load a field from an object instance 302 * 303 */ 304 static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size, 305 int fieldOffset, bool isVolatile) 306 { 307 RegLocation rlResult; 308 RegisterClass regClass = dvmCompilerRegClassBySize(size); 309 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 310 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 311 rlObj = loadValue(cUnit, rlObj, kCoreReg); 312 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true); 313 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 314 NULL);/* null object? */ 315 316 HEAP_ACCESS_SHADOW(true); 317 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg, 318 size, rlObj.sRegLow); 319 HEAP_ACCESS_SHADOW(false); 320 if (isVolatile) { 321 dvmCompilerGenMemBarrier(cUnit); 322 } 323 324 storeValue(cUnit, rlDest, rlResult); 325 } 326 327 /* 328 * Store a field to an object instance 329 * 330 */ 331 static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size, 332 int fieldOffset, bool isObject, bool isVolatile) 333 { 334 RegisterClass regClass = dvmCompilerRegClassBySize(size); 335 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 336 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 1); 337 rlObj = loadValue(cUnit, rlObj, kCoreReg); 338 rlSrc = loadValue(cUnit, rlSrc, regClass); 339 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 340 NULL);/* null object? */ 341 342 if (isVolatile) { 343 dvmCompilerGenMemBarrier(cUnit); 344 } 345 HEAP_ACCESS_SHADOW(true); 346 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size); 347 HEAP_ACCESS_SHADOW(false); 348 if (isObject) { 349 /* NOTE: marking card based on object head */ 350 markCard(cUnit, rlSrc.lowReg, rlObj.lowReg); 351 } 352 } 353 354 355 /* 356 * Generate array load 357 */ 358 static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size, 359 RegLocation rlArray, RegLocation rlIndex, 360 RegLocation rlDest, int scale) 361 { 362 RegisterClass regClass = dvmCompilerRegClassBySize(size); 363 int lenOffset = offsetof(ArrayObject, length); 364 int dataOffset = offsetof(ArrayObject, contents); 365 RegLocation rlResult; 366 rlArray = loadValue(cUnit, rlArray, kCoreReg); 367 rlIndex = loadValue(cUnit, rlIndex, kCoreReg); 368 int regPtr; 369 370 /* null object? */ 371 ArmLIR * pcrLabel = NULL; 372 373 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { 374 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, 375 rlArray.lowReg, mir->offset, NULL); 376 } 377 378 regPtr = dvmCompilerAllocTemp(cUnit); 379 380 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { 381 int regLen = dvmCompilerAllocTemp(cUnit); 382 /* Get len */ 383 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); 384 /* regPtr -> array data */ 385 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); 386 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, 387 pcrLabel); 388 dvmCompilerFreeTemp(cUnit, regLen); 389 } else { 390 /* regPtr -> array data */ 391 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); 392 } 393 if ((size == kLong) || (size == kDouble)) { 394 if (scale) { 395 int rNewIndex = dvmCompilerAllocTemp(cUnit); 396 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); 397 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); 398 dvmCompilerFreeTemp(cUnit, rNewIndex); 399 } else { 400 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); 401 } 402 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true); 403 404 HEAP_ACCESS_SHADOW(true); 405 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); 406 HEAP_ACCESS_SHADOW(false); 407 408 dvmCompilerFreeTemp(cUnit, regPtr); 409 storeValueWide(cUnit, rlDest, rlResult); 410 } else { 411 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true); 412 413 HEAP_ACCESS_SHADOW(true); 414 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg, 415 scale, size); 416 HEAP_ACCESS_SHADOW(false); 417 418 dvmCompilerFreeTemp(cUnit, regPtr); 419 storeValue(cUnit, rlDest, rlResult); 420 } 421 } 422 423 /* 424 * Generate array store 425 * 426 */ 427 static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size, 428 RegLocation rlArray, RegLocation rlIndex, 429 RegLocation rlSrc, int scale) 430 { 431 RegisterClass regClass = dvmCompilerRegClassBySize(size); 432 int lenOffset = offsetof(ArrayObject, length); 433 int dataOffset = offsetof(ArrayObject, contents); 434 435 int regPtr; 436 rlArray = loadValue(cUnit, rlArray, kCoreReg); 437 rlIndex = loadValue(cUnit, rlIndex, kCoreReg); 438 439 if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) { 440 dvmCompilerClobber(cUnit, rlArray.lowReg); 441 regPtr = rlArray.lowReg; 442 } else { 443 regPtr = dvmCompilerAllocTemp(cUnit); 444 genRegCopy(cUnit, regPtr, rlArray.lowReg); 445 } 446 447 /* null object? */ 448 ArmLIR * pcrLabel = NULL; 449 450 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { 451 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, 452 mir->offset, NULL); 453 } 454 455 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { 456 int regLen = dvmCompilerAllocTemp(cUnit); 457 //NOTE: max live temps(4) here. 458 /* Get len */ 459 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); 460 /* regPtr -> array data */ 461 opRegImm(cUnit, kOpAdd, regPtr, dataOffset); 462 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, 463 pcrLabel); 464 dvmCompilerFreeTemp(cUnit, regLen); 465 } else { 466 /* regPtr -> array data */ 467 opRegImm(cUnit, kOpAdd, regPtr, dataOffset); 468 } 469 /* at this point, regPtr points to array, 2 live temps */ 470 if ((size == kLong) || (size == kDouble)) { 471 //TODO: need specific wide routine that can handle fp regs 472 if (scale) { 473 int rNewIndex = dvmCompilerAllocTemp(cUnit); 474 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); 475 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); 476 dvmCompilerFreeTemp(cUnit, rNewIndex); 477 } else { 478 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); 479 } 480 rlSrc = loadValueWide(cUnit, rlSrc, regClass); 481 482 HEAP_ACCESS_SHADOW(true); 483 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg); 484 HEAP_ACCESS_SHADOW(false); 485 486 dvmCompilerFreeTemp(cUnit, regPtr); 487 } else { 488 rlSrc = loadValue(cUnit, rlSrc, regClass); 489 490 HEAP_ACCESS_SHADOW(true); 491 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg, 492 scale, size); 493 HEAP_ACCESS_SHADOW(false); 494 } 495 } 496 497 /* 498 * Generate array object store 499 * Must use explicit register allocation here because of 500 * call-out to dvmCanPutArrayElement 501 */ 502 static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir, 503 RegLocation rlArray, RegLocation rlIndex, 504 RegLocation rlSrc, int scale) 505 { 506 int lenOffset = offsetof(ArrayObject, length); 507 int dataOffset = offsetof(ArrayObject, contents); 508 509 dvmCompilerFlushAllRegs(cUnit); 510 511 int regLen = r0; 512 int regPtr = r4PC; /* Preserved across call */ 513 int regArray = r1; 514 int regIndex = r7; /* Preserved across call */ 515 516 loadValueDirectFixed(cUnit, rlArray, regArray); 517 loadValueDirectFixed(cUnit, rlIndex, regIndex); 518 519 /* null object? */ 520 ArmLIR * pcrLabel = NULL; 521 522 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { 523 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray, 524 mir->offset, NULL); 525 } 526 527 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { 528 /* Get len */ 529 loadWordDisp(cUnit, regArray, lenOffset, regLen); 530 /* regPtr -> array data */ 531 opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset); 532 genBoundsCheck(cUnit, regIndex, regLen, mir->offset, 533 pcrLabel); 534 } else { 535 /* regPtr -> array data */ 536 opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset); 537 } 538 539 /* Get object to store */ 540 loadValueDirectFixed(cUnit, rlSrc, r0); 541 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmCanPutArrayElement); 542 543 /* Are we storing null? If so, avoid check */ 544 opRegImm(cUnit, kOpCmp, r0, 0); 545 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondEq); 546 547 /* Make sure the types are compatible */ 548 loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r1); 549 loadWordDisp(cUnit, r0, offsetof(Object, clazz), r0); 550 opReg(cUnit, kOpBlx, r2); 551 dvmCompilerClobberCallRegs(cUnit); 552 553 /* 554 * Using fixed registers here, and counting on r4 and r7 being 555 * preserved across the above call. Tell the register allocation 556 * utilities about the regs we are using directly 557 */ 558 dvmCompilerLockTemp(cUnit, regPtr); // r4PC 559 dvmCompilerLockTemp(cUnit, regIndex); // r7 560 dvmCompilerLockTemp(cUnit, r0); 561 dvmCompilerLockTemp(cUnit, r1); 562 563 /* Bad? - roll back and re-execute if so */ 564 genRegImmCheck(cUnit, kArmCondEq, r0, 0, mir->offset, pcrLabel); 565 566 /* Resume here - must reload element & array, regPtr & index preserved */ 567 loadValueDirectFixed(cUnit, rlSrc, r0); 568 loadValueDirectFixed(cUnit, rlArray, r1); 569 570 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 571 target->defMask = ENCODE_ALL; 572 branchOver->generic.target = (LIR *) target; 573 574 HEAP_ACCESS_SHADOW(true); 575 storeBaseIndexed(cUnit, regPtr, regIndex, r0, 576 scale, kWord); 577 HEAP_ACCESS_SHADOW(false); 578 579 dvmCompilerFreeTemp(cUnit, regPtr); 580 dvmCompilerFreeTemp(cUnit, regIndex); 581 582 /* NOTE: marking card here based on object head */ 583 markCard(cUnit, r0, r1); 584 } 585 586 static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, 587 RegLocation rlDest, RegLocation rlSrc1, 588 RegLocation rlShift) 589 { 590 /* 591 * Don't mess with the regsiters here as there is a particular calling 592 * convention to the out-of-line handler. 593 */ 594 RegLocation rlResult; 595 596 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); 597 loadValueDirect(cUnit, rlShift, r2); 598 switch( mir->dalvikInsn.opCode) { 599 case OP_SHL_LONG: 600 case OP_SHL_LONG_2ADDR: 601 genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG); 602 break; 603 case OP_SHR_LONG: 604 case OP_SHR_LONG_2ADDR: 605 genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG); 606 break; 607 case OP_USHR_LONG: 608 case OP_USHR_LONG_2ADDR: 609 genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG); 610 break; 611 default: 612 return true; 613 } 614 rlResult = dvmCompilerGetReturnWide(cUnit); 615 storeValueWide(cUnit, rlDest, rlResult); 616 return false; 617 } 618 619 static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir, 620 RegLocation rlDest, RegLocation rlSrc1, 621 RegLocation rlSrc2) 622 { 623 RegLocation rlResult; 624 OpKind firstOp = kOpBkpt; 625 OpKind secondOp = kOpBkpt; 626 bool callOut = false; 627 void *callTgt; 628 int retReg = r0; 629 630 switch (mir->dalvikInsn.opCode) { 631 case OP_NOT_LONG: 632 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); 633 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 634 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg); 635 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg); 636 storeValueWide(cUnit, rlDest, rlResult); 637 return false; 638 break; 639 case OP_ADD_LONG: 640 case OP_ADD_LONG_2ADDR: 641 firstOp = kOpAdd; 642 secondOp = kOpAdc; 643 break; 644 case OP_SUB_LONG: 645 case OP_SUB_LONG_2ADDR: 646 firstOp = kOpSub; 647 secondOp = kOpSbc; 648 break; 649 case OP_MUL_LONG: 650 case OP_MUL_LONG_2ADDR: 651 genMulLong(cUnit, rlDest, rlSrc1, rlSrc2); 652 return false; 653 case OP_DIV_LONG: 654 case OP_DIV_LONG_2ADDR: 655 callOut = true; 656 retReg = r0; 657 callTgt = (void*)__aeabi_ldivmod; 658 break; 659 /* NOTE - result is in r2/r3 instead of r0/r1 */ 660 case OP_REM_LONG: 661 case OP_REM_LONG_2ADDR: 662 callOut = true; 663 callTgt = (void*)__aeabi_ldivmod; 664 retReg = r2; 665 break; 666 case OP_AND_LONG_2ADDR: 667 case OP_AND_LONG: 668 firstOp = kOpAnd; 669 secondOp = kOpAnd; 670 break; 671 case OP_OR_LONG: 672 case OP_OR_LONG_2ADDR: 673 firstOp = kOpOr; 674 secondOp = kOpOr; 675 break; 676 case OP_XOR_LONG: 677 case OP_XOR_LONG_2ADDR: 678 firstOp = kOpXor; 679 secondOp = kOpXor; 680 break; 681 case OP_NEG_LONG: { 682 //TUNING: can improve this using Thumb2 code 683 int tReg = dvmCompilerAllocTemp(cUnit); 684 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); 685 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 686 loadConstantNoClobber(cUnit, tReg, 0); 687 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, 688 tReg, rlSrc2.lowReg); 689 opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg); 690 genRegCopy(cUnit, rlResult.highReg, tReg); 691 storeValueWide(cUnit, rlDest, rlResult); 692 return false; 693 } 694 default: 695 LOGE("Invalid long arith op"); 696 dvmCompilerAbort(cUnit); 697 } 698 if (!callOut) { 699 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2); 700 } else { 701 // Adjust return regs in to handle case of rem returning r2/r3 702 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 703 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); 704 LOAD_FUNC_ADDR(cUnit, rlr, (int) callTgt); 705 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3); 706 opReg(cUnit, kOpBlx, rlr); 707 dvmCompilerClobberCallRegs(cUnit); 708 if (retReg == r0) 709 rlResult = dvmCompilerGetReturnWide(cUnit); 710 else 711 rlResult = dvmCompilerGetReturnWideAlt(cUnit); 712 storeValueWide(cUnit, rlDest, rlResult); 713 } 714 return false; 715 } 716 717 static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir, 718 RegLocation rlDest, RegLocation rlSrc1, 719 RegLocation rlSrc2) 720 { 721 OpKind op = kOpBkpt; 722 bool callOut = false; 723 bool checkZero = false; 724 bool unary = false; 725 int retReg = r0; 726 void *callTgt; 727 RegLocation rlResult; 728 bool shiftOp = false; 729 730 switch (mir->dalvikInsn.opCode) { 731 case OP_NEG_INT: 732 op = kOpNeg; 733 unary = true; 734 break; 735 case OP_NOT_INT: 736 op = kOpMvn; 737 unary = true; 738 break; 739 case OP_ADD_INT: 740 case OP_ADD_INT_2ADDR: 741 op = kOpAdd; 742 break; 743 case OP_SUB_INT: 744 case OP_SUB_INT_2ADDR: 745 op = kOpSub; 746 break; 747 case OP_MUL_INT: 748 case OP_MUL_INT_2ADDR: 749 op = kOpMul; 750 break; 751 case OP_DIV_INT: 752 case OP_DIV_INT_2ADDR: 753 callOut = true; 754 checkZero = true; 755 callTgt = __aeabi_idiv; 756 retReg = r0; 757 break; 758 /* NOTE: returns in r1 */ 759 case OP_REM_INT: 760 case OP_REM_INT_2ADDR: 761 callOut = true; 762 checkZero = true; 763 callTgt = __aeabi_idivmod; 764 retReg = r1; 765 break; 766 case OP_AND_INT: 767 case OP_AND_INT_2ADDR: 768 op = kOpAnd; 769 break; 770 case OP_OR_INT: 771 case OP_OR_INT_2ADDR: 772 op = kOpOr; 773 break; 774 case OP_XOR_INT: 775 case OP_XOR_INT_2ADDR: 776 op = kOpXor; 777 break; 778 case OP_SHL_INT: 779 case OP_SHL_INT_2ADDR: 780 shiftOp = true; 781 op = kOpLsl; 782 break; 783 case OP_SHR_INT: 784 case OP_SHR_INT_2ADDR: 785 shiftOp = true; 786 op = kOpAsr; 787 break; 788 case OP_USHR_INT: 789 case OP_USHR_INT_2ADDR: 790 shiftOp = true; 791 op = kOpLsr; 792 break; 793 default: 794 LOGE("Invalid word arith op: 0x%x(%d)", 795 mir->dalvikInsn.opCode, mir->dalvikInsn.opCode); 796 dvmCompilerAbort(cUnit); 797 } 798 if (!callOut) { 799 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); 800 if (unary) { 801 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 802 opRegReg(cUnit, op, rlResult.lowReg, 803 rlSrc1.lowReg); 804 } else { 805 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); 806 if (shiftOp) { 807 int tReg = dvmCompilerAllocTemp(cUnit); 808 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31); 809 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 810 opRegRegReg(cUnit, op, rlResult.lowReg, 811 rlSrc1.lowReg, tReg); 812 dvmCompilerFreeTemp(cUnit, tReg); 813 } else { 814 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 815 opRegRegReg(cUnit, op, rlResult.lowReg, 816 rlSrc1.lowReg, rlSrc2.lowReg); 817 } 818 } 819 storeValue(cUnit, rlDest, rlResult); 820 } else { 821 RegLocation rlResult; 822 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 823 loadValueDirectFixed(cUnit, rlSrc2, r1); 824 LOAD_FUNC_ADDR(cUnit, r2, (int) callTgt); 825 loadValueDirectFixed(cUnit, rlSrc1, r0); 826 if (checkZero) { 827 genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL); 828 } 829 opReg(cUnit, kOpBlx, r2); 830 dvmCompilerClobberCallRegs(cUnit); 831 if (retReg == r0) 832 rlResult = dvmCompilerGetReturn(cUnit); 833 else 834 rlResult = dvmCompilerGetReturnAlt(cUnit); 835 storeValue(cUnit, rlDest, rlResult); 836 } 837 return false; 838 } 839 840 static bool genArithOp(CompilationUnit *cUnit, MIR *mir) 841 { 842 OpCode opCode = mir->dalvikInsn.opCode; 843 RegLocation rlDest; 844 RegLocation rlSrc1; 845 RegLocation rlSrc2; 846 /* Deduce sizes of operands */ 847 if (mir->ssaRep->numUses == 2) { 848 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0); 849 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1); 850 } else if (mir->ssaRep->numUses == 3) { 851 rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 852 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2); 853 } else { 854 rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 855 rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3); 856 assert(mir->ssaRep->numUses == 4); 857 } 858 if (mir->ssaRep->numDefs == 1) { 859 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 860 } else { 861 assert(mir->ssaRep->numDefs == 2); 862 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 863 } 864 865 if ((opCode >= OP_ADD_LONG_2ADDR) && (opCode <= OP_XOR_LONG_2ADDR)) { 866 return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 867 } 868 if ((opCode >= OP_ADD_LONG) && (opCode <= OP_XOR_LONG)) { 869 return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 870 } 871 if ((opCode >= OP_SHL_LONG_2ADDR) && (opCode <= OP_USHR_LONG_2ADDR)) { 872 return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 873 } 874 if ((opCode >= OP_SHL_LONG) && (opCode <= OP_USHR_LONG)) { 875 return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 876 } 877 if ((opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_USHR_INT_2ADDR)) { 878 return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2); 879 } 880 if ((opCode >= OP_ADD_INT) && (opCode <= OP_USHR_INT)) { 881 return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2); 882 } 883 if ((opCode >= OP_ADD_FLOAT_2ADDR) && (opCode <= OP_REM_FLOAT_2ADDR)) { 884 return genArithOpFloat(cUnit,mir, rlDest, rlSrc1, rlSrc2); 885 } 886 if ((opCode >= OP_ADD_FLOAT) && (opCode <= OP_REM_FLOAT)) { 887 return genArithOpFloat(cUnit, mir, rlDest, rlSrc1, rlSrc2); 888 } 889 if ((opCode >= OP_ADD_DOUBLE_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { 890 return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2); 891 } 892 if ((opCode >= OP_ADD_DOUBLE) && (opCode <= OP_REM_DOUBLE)) { 893 return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2); 894 } 895 return true; 896 } 897 898 /* Generate unconditional branch instructions */ 899 static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target) 900 { 901 ArmLIR *branch = opNone(cUnit, kOpUncondBr); 902 branch->generic.target = (LIR *) target; 903 return branch; 904 } 905 906 /* Perform the actual operation for OP_RETURN_* */ 907 static void genReturnCommon(CompilationUnit *cUnit, MIR *mir) 908 { 909 genDispatchToHandler(cUnit, TEMPLATE_RETURN); 910 #if defined(WITH_JIT_TUNING) 911 gDvmJit.returnOp++; 912 #endif 913 int dPC = (int) (cUnit->method->insns + mir->offset); 914 /* Insert branch, but defer setting of target */ 915 ArmLIR *branch = genUnconditionalBranch(cUnit, NULL); 916 /* Set up the place holder to reconstruct this Dalvik PC */ 917 ArmLIR *pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); 918 pcrLabel->opCode = kArmPseudoPCReconstructionCell; 919 pcrLabel->operands[0] = dPC; 920 pcrLabel->operands[1] = mir->offset; 921 /* Insert the place holder to the growable list */ 922 dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); 923 /* Branch to the PC reconstruction code */ 924 branch->generic.target = (LIR *) pcrLabel; 925 } 926 927 static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir, 928 DecodedInstruction *dInsn, 929 ArmLIR **pcrLabel) 930 { 931 unsigned int i; 932 unsigned int regMask = 0; 933 RegLocation rlArg; 934 int numDone = 0; 935 936 /* 937 * Load arguments to r0..r4. Note that these registers may contain 938 * live values, so we clobber them immediately after loading to prevent 939 * them from being used as sources for subsequent loads. 940 */ 941 dvmCompilerLockAllTemps(cUnit); 942 for (i = 0; i < dInsn->vA; i++) { 943 regMask |= 1 << i; 944 rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++); 945 loadValueDirectFixed(cUnit, rlArg, i); 946 } 947 if (regMask) { 948 /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ 949 opRegRegImm(cUnit, kOpSub, r7, rFP, 950 sizeof(StackSaveArea) + (dInsn->vA << 2)); 951 /* generate null check */ 952 if (pcrLabel) { 953 *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0, 954 mir->offset, NULL); 955 } 956 storeMultiple(cUnit, r7, regMask); 957 } 958 } 959 960 static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir, 961 DecodedInstruction *dInsn, 962 ArmLIR **pcrLabel) 963 { 964 int srcOffset = dInsn->vC << 2; 965 int numArgs = dInsn->vA; 966 int regMask; 967 968 /* 969 * Note: here, all promoted registers will have been flushed 970 * back to the Dalvik base locations, so register usage restrictins 971 * are lifted. All parms loaded from original Dalvik register 972 * region - even though some might conceivably have valid copies 973 * cached in a preserved register. 974 */ 975 dvmCompilerLockAllTemps(cUnit); 976 977 /* 978 * r4PC : &rFP[vC] 979 * r7: &newFP[0] 980 */ 981 opRegRegImm(cUnit, kOpAdd, r4PC, rFP, srcOffset); 982 /* load [r0 .. min(numArgs,4)] */ 983 regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1; 984 /* 985 * Protect the loadMultiple instruction from being reordered with other 986 * Dalvik stack accesses. 987 */ 988 loadMultiple(cUnit, r4PC, regMask); 989 990 opRegRegImm(cUnit, kOpSub, r7, rFP, 991 sizeof(StackSaveArea) + (numArgs << 2)); 992 /* generate null check */ 993 if (pcrLabel) { 994 *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0, 995 mir->offset, NULL); 996 } 997 998 /* 999 * Handle remaining 4n arguments: 1000 * store previously loaded 4 values and load the next 4 values 1001 */ 1002 if (numArgs >= 8) { 1003 ArmLIR *loopLabel = NULL; 1004 /* 1005 * r0 contains "this" and it will be used later, so push it to the stack 1006 * first. Pushing r5 (rFP) is just for stack alignment purposes. 1007 */ 1008 opImm(cUnit, kOpPush, (1 << r0 | 1 << rFP)); 1009 /* No need to generate the loop structure if numArgs <= 11 */ 1010 if (numArgs > 11) { 1011 loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2); 1012 loopLabel = newLIR0(cUnit, kArmPseudoTargetLabel); 1013 loopLabel->defMask = ENCODE_ALL; 1014 } 1015 storeMultiple(cUnit, r7, regMask); 1016 /* 1017 * Protect the loadMultiple instruction from being reordered with other 1018 * Dalvik stack accesses. 1019 */ 1020 loadMultiple(cUnit, r4PC, regMask); 1021 /* No need to generate the loop structure if numArgs <= 11 */ 1022 if (numArgs > 11) { 1023 opRegImm(cUnit, kOpSub, rFP, 4); 1024 genConditionalBranch(cUnit, kArmCondNe, loopLabel); 1025 } 1026 } 1027 1028 /* Save the last batch of loaded values */ 1029 storeMultiple(cUnit, r7, regMask); 1030 1031 /* Generate the loop epilogue - don't use r0 */ 1032 if ((numArgs > 4) && (numArgs % 4)) { 1033 regMask = ((1 << (numArgs & 0x3)) - 1) << 1; 1034 /* 1035 * Protect the loadMultiple instruction from being reordered with other 1036 * Dalvik stack accesses. 1037 */ 1038 loadMultiple(cUnit, r4PC, regMask); 1039 } 1040 if (numArgs >= 8) 1041 opImm(cUnit, kOpPop, (1 << r0 | 1 << rFP)); 1042 1043 /* Save the modulo 4 arguments */ 1044 if ((numArgs > 4) && (numArgs % 4)) { 1045 storeMultiple(cUnit, r7, regMask); 1046 } 1047 } 1048 1049 /* 1050 * Generate code to setup the call stack then jump to the chaining cell if it 1051 * is not a native method. 1052 */ 1053 static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir, 1054 BasicBlock *bb, ArmLIR *labelList, 1055 ArmLIR *pcrLabel, 1056 const Method *calleeMethod) 1057 { 1058 /* 1059 * Note: all Dalvik register state should be flushed to 1060 * memory by the point, so register usage restrictions no 1061 * longer apply. All temp & preserved registers may be used. 1062 */ 1063 dvmCompilerLockAllTemps(cUnit); 1064 ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; 1065 1066 /* r1 = &retChainingCell */ 1067 dvmCompilerLockTemp(cUnit, r1); 1068 ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0); 1069 /* r4PC = dalvikCallsite */ 1070 loadConstant(cUnit, r4PC, 1071 (int) (cUnit->method->insns + mir->offset)); 1072 addrRetChain->generic.target = (LIR *) retChainingCell; 1073 /* 1074 * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon) 1075 * r1 = &ChainingCell 1076 * r4PC = callsiteDPC 1077 */ 1078 if (dvmIsNativeMethod(calleeMethod)) { 1079 genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NATIVE); 1080 #if defined(WITH_JIT_TUNING) 1081 gDvmJit.invokeNative++; 1082 #endif 1083 } else { 1084 genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_CHAIN); 1085 #if defined(WITH_JIT_TUNING) 1086 gDvmJit.invokeMonomorphic++; 1087 #endif 1088 /* Branch to the chaining cell */ 1089 genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); 1090 } 1091 /* Handle exceptions using the interpreter */ 1092 genTrap(cUnit, mir->offset, pcrLabel); 1093 } 1094 1095 /* 1096 * Generate code to check the validity of a predicted chain and take actions 1097 * based on the result. 1098 * 1099 * 0x426a99aa : ldr r4, [pc, #72] --> r4 <- dalvikPC of this invoke 1100 * 0x426a99ac : add r1, pc, #32 --> r1 <- &retChainingCell 1101 * 0x426a99ae : add r2, pc, #40 --> r2 <- &predictedChainingCell 1102 * 0x426a99b0 : blx_1 0x426a918c --+ TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN 1103 * 0x426a99b2 : blx_2 see above --+ 1104 * 0x426a99b4 : b 0x426a99d8 --> off to the predicted chain 1105 * 0x426a99b6 : b 0x426a99c8 --> punt to the interpreter 1106 * 0x426a99b8 : ldr r0, [r7, #44] --> r0 <- this->class->vtable[methodIdx] 1107 * 0x426a99ba : cmp r1, #0 --> compare r1 (rechain count) against 0 1108 * 0x426a99bc : bgt 0x426a99c2 --> >=0? don't rechain 1109 * 0x426a99be : ldr r7, [r6, #96] --+ dvmJitToPatchPredictedChain 1110 * 0x426a99c0 : blx r7 --+ 1111 * 0x426a99c2 : add r1, pc, #12 --> r1 <- &retChainingCell 1112 * 0x426a99c4 : blx_1 0x426a9098 --+ TEMPLATE_INVOKE_METHOD_NO_OPT 1113 * 0x426a99c6 : blx_2 see above --+ 1114 */ 1115 static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir, 1116 int methodIndex, 1117 ArmLIR *retChainingCell, 1118 ArmLIR *predChainingCell, 1119 ArmLIR *pcrLabel) 1120 { 1121 /* 1122 * Note: all Dalvik register state should be flushed to 1123 * memory by the point, so register usage restrictions no 1124 * longer apply. Lock temps to prevent them from being 1125 * allocated by utility routines. 1126 */ 1127 dvmCompilerLockAllTemps(cUnit); 1128 1129 /* "this" is already left in r0 by genProcessArgs* */ 1130 1131 /* r4PC = dalvikCallsite */ 1132 loadConstant(cUnit, r4PC, 1133 (int) (cUnit->method->insns + mir->offset)); 1134 1135 /* r1 = &retChainingCell */ 1136 ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0); 1137 addrRetChain->generic.target = (LIR *) retChainingCell; 1138 1139 /* r2 = &predictedChainingCell */ 1140 ArmLIR *predictedChainingCell = opRegRegImm(cUnit, kOpAdd, r2, rpc, 0); 1141 predictedChainingCell->generic.target = (LIR *) predChainingCell; 1142 1143 genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN); 1144 1145 /* return through lr - jump to the chaining cell */ 1146 genUnconditionalBranch(cUnit, predChainingCell); 1147 1148 /* 1149 * null-check on "this" may have been eliminated, but we still need a PC- 1150 * reconstruction label for stack overflow bailout. 1151 */ 1152 if (pcrLabel == NULL) { 1153 int dPC = (int) (cUnit->method->insns + mir->offset); 1154 pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); 1155 pcrLabel->opCode = kArmPseudoPCReconstructionCell; 1156 pcrLabel->operands[0] = dPC; 1157 pcrLabel->operands[1] = mir->offset; 1158 /* Insert the place holder to the growable list */ 1159 dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); 1160 } 1161 1162 /* return through lr+2 - punt to the interpreter */ 1163 genUnconditionalBranch(cUnit, pcrLabel); 1164 1165 /* 1166 * return through lr+4 - fully resolve the callee method. 1167 * r1 <- count 1168 * r2 <- &predictedChainCell 1169 * r3 <- this->class 1170 * r4 <- dPC 1171 * r7 <- this->class->vtable 1172 */ 1173 1174 /* r0 <- calleeMethod */ 1175 loadWordDisp(cUnit, r7, methodIndex * 4, r0); 1176 1177 /* Check if rechain limit is reached */ 1178 opRegImm(cUnit, kOpCmp, r1, 0); 1179 1180 ArmLIR *bypassRechaining = opCondBranch(cUnit, kArmCondGt); 1181 1182 loadWordDisp(cUnit, rGLUE, offsetof(InterpState, 1183 jitToInterpEntries.dvmJitToPatchPredictedChain), r7); 1184 1185 genRegCopy(cUnit, r1, rGLUE); 1186 1187 /* 1188 * r0 = calleeMethod 1189 * r2 = &predictedChainingCell 1190 * r3 = class 1191 * 1192 * &returnChainingCell has been loaded into r1 but is not needed 1193 * when patching the chaining cell and will be clobbered upon 1194 * returning so it will be reconstructed again. 1195 */ 1196 opReg(cUnit, kOpBlx, r7); 1197 1198 /* r1 = &retChainingCell */ 1199 addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0); 1200 addrRetChain->generic.target = (LIR *) retChainingCell; 1201 1202 bypassRechaining->generic.target = (LIR *) addrRetChain; 1203 /* 1204 * r0 = calleeMethod, 1205 * r1 = &ChainingCell, 1206 * r4PC = callsiteDPC, 1207 */ 1208 genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); 1209 #if defined(WITH_JIT_TUNING) 1210 gDvmJit.invokePolymorphic++; 1211 #endif 1212 /* Handle exceptions using the interpreter */ 1213 genTrap(cUnit, mir->offset, pcrLabel); 1214 } 1215 1216 /* Geneate a branch to go back to the interpreter */ 1217 static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset) 1218 { 1219 /* r0 = dalvik pc */ 1220 dvmCompilerFlushAllRegs(cUnit); 1221 loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset)); 1222 loadWordDisp(cUnit, r0, offsetof(Object, clazz), r3); 1223 loadWordDisp(cUnit, rGLUE, offsetof(InterpState, 1224 jitToInterpEntries.dvmJitToInterpPunt), r1); 1225 opReg(cUnit, kOpBlx, r1); 1226 } 1227 1228 /* 1229 * Attempt to single step one instruction using the interpreter and return 1230 * to the compiled code for the next Dalvik instruction 1231 */ 1232 static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir) 1233 { 1234 int flags = dexGetInstrFlags(gDvm.instrFlags, mir->dalvikInsn.opCode); 1235 int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn | 1236 kInstrCanThrow; 1237 1238 //If already optimized out, just ignore 1239 if (mir->dalvikInsn.opCode == OP_NOP) 1240 return; 1241 1242 //Ugly, but necessary. Flush all Dalvik regs so Interp can find them 1243 dvmCompilerFlushAllRegs(cUnit); 1244 1245 if ((mir->next == NULL) || (flags & flagsToCheck)) { 1246 genPuntToInterp(cUnit, mir->offset); 1247 return; 1248 } 1249 int entryAddr = offsetof(InterpState, 1250 jitToInterpEntries.dvmJitToInterpSingleStep); 1251 loadWordDisp(cUnit, rGLUE, entryAddr, r2); 1252 /* r0 = dalvik pc */ 1253 loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); 1254 /* r1 = dalvik pc of following instruction */ 1255 loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset)); 1256 opReg(cUnit, kOpBlx, r2); 1257 } 1258 1259 #if defined(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING) || \ 1260 defined(_ARMV5TE) || defined(_ARMV5TE_VFP) 1261 /* 1262 * To prevent a thread in a monitor wait from blocking the Jit from 1263 * resetting the code cache, heavyweight monitor lock will not 1264 * be allowed to return to an existing translation. Instead, we will 1265 * handle them by branching to a handler, which will in turn call the 1266 * runtime lock routine and then branch directly back to the 1267 * interpreter main loop. Given the high cost of the heavyweight 1268 * lock operation, this additional cost should be slight (especially when 1269 * considering that we expect the vast majority of lock operations to 1270 * use the fast-path thin lock bypass). 1271 */ 1272 static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir) 1273 { 1274 bool isEnter = (mir->dalvikInsn.opCode == OP_MONITOR_ENTER); 1275 genExportPC(cUnit, mir); 1276 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 1277 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1278 loadValueDirectFixed(cUnit, rlSrc, r1); 1279 loadWordDisp(cUnit, rGLUE, offsetof(InterpState, self), r0); 1280 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL); 1281 if (isEnter) { 1282 /* Get dPC of next insn */ 1283 loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset + 1284 dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_ENTER))); 1285 #if defined(WITH_DEADLOCK_PREDICTION) 1286 genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER_DEBUG); 1287 #else 1288 genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER); 1289 #endif 1290 } else { 1291 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmUnlockObject); 1292 /* Do the call */ 1293 opReg(cUnit, kOpBlx, r2); 1294 opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */ 1295 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe); 1296 loadConstant(cUnit, r0, 1297 (int) (cUnit->method->insns + mir->offset + 1298 dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_EXIT))); 1299 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 1300 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 1301 target->defMask = ENCODE_ALL; 1302 branchOver->generic.target = (LIR *) target; 1303 dvmCompilerClobberCallRegs(cUnit); 1304 } 1305 } 1306 #endif 1307 1308 /* 1309 * The following are the first-level codegen routines that analyze the format 1310 * of each bytecode then either dispatch special purpose codegen routines 1311 * or produce corresponding Thumb instructions directly. 1312 */ 1313 1314 static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir, 1315 BasicBlock *bb, ArmLIR *labelList) 1316 { 1317 /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */ 1318 genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); 1319 return false; 1320 } 1321 1322 static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir) 1323 { 1324 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 1325 if ((dalvikOpCode >= OP_UNUSED_3E) && (dalvikOpCode <= OP_UNUSED_43)) { 1326 LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode); 1327 return true; 1328 } 1329 switch (dalvikOpCode) { 1330 case OP_RETURN_VOID: 1331 genReturnCommon(cUnit,mir); 1332 break; 1333 case OP_UNUSED_73: 1334 case OP_UNUSED_79: 1335 case OP_UNUSED_7A: 1336 case OP_UNUSED_F1: 1337 case OP_UNUSED_FF: 1338 LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode); 1339 return true; 1340 case OP_NOP: 1341 break; 1342 default: 1343 return true; 1344 } 1345 return false; 1346 } 1347 1348 static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir) 1349 { 1350 RegLocation rlDest; 1351 RegLocation rlResult; 1352 if (mir->ssaRep->numDefs == 2) { 1353 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1354 } else { 1355 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1356 } 1357 1358 switch (mir->dalvikInsn.opCode) { 1359 case OP_CONST: 1360 case OP_CONST_4: { 1361 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1362 loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB); 1363 storeValue(cUnit, rlDest, rlResult); 1364 break; 1365 } 1366 case OP_CONST_WIDE_32: { 1367 //TUNING: single routine to load constant pair for support doubles 1368 //TUNING: load 0/-1 separately to avoid load dependency 1369 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1370 loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB); 1371 opRegRegImm(cUnit, kOpAsr, rlResult.highReg, 1372 rlResult.lowReg, 31); 1373 storeValueWide(cUnit, rlDest, rlResult); 1374 break; 1375 } 1376 default: 1377 return true; 1378 } 1379 return false; 1380 } 1381 1382 static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir) 1383 { 1384 RegLocation rlDest; 1385 RegLocation rlResult; 1386 if (mir->ssaRep->numDefs == 2) { 1387 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1388 } else { 1389 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1390 } 1391 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1392 1393 switch (mir->dalvikInsn.opCode) { 1394 case OP_CONST_HIGH16: { 1395 loadConstantNoClobber(cUnit, rlResult.lowReg, 1396 mir->dalvikInsn.vB << 16); 1397 storeValue(cUnit, rlDest, rlResult); 1398 break; 1399 } 1400 case OP_CONST_WIDE_HIGH16: { 1401 loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg, 1402 0, mir->dalvikInsn.vB << 16); 1403 storeValueWide(cUnit, rlDest, rlResult); 1404 break; 1405 } 1406 default: 1407 return true; 1408 } 1409 return false; 1410 } 1411 1412 static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir) 1413 { 1414 /* For OP_THROW_VERIFICATION_ERROR */ 1415 genInterpSingleStep(cUnit, mir); 1416 return false; 1417 } 1418 1419 static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) 1420 { 1421 RegLocation rlResult; 1422 RegLocation rlDest; 1423 RegLocation rlSrc; 1424 1425 switch (mir->dalvikInsn.opCode) { 1426 case OP_CONST_STRING_JUMBO: 1427 case OP_CONST_STRING: { 1428 void *strPtr = (void*) 1429 (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]); 1430 1431 if (strPtr == NULL) { 1432 LOGE("Unexpected null string"); 1433 dvmAbort(); 1434 } 1435 1436 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1437 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1438 loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr ); 1439 storeValue(cUnit, rlDest, rlResult); 1440 break; 1441 } 1442 case OP_CONST_CLASS: { 1443 void *classPtr = (void*) 1444 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); 1445 1446 if (classPtr == NULL) { 1447 LOGE("Unexpected null class"); 1448 dvmAbort(); 1449 } 1450 1451 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1452 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1453 loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr ); 1454 storeValue(cUnit, rlDest, rlResult); 1455 break; 1456 } 1457 case OP_SGET_VOLATILE: 1458 case OP_SGET_OBJECT_VOLATILE: 1459 case OP_SGET_OBJECT: 1460 case OP_SGET_BOOLEAN: 1461 case OP_SGET_CHAR: 1462 case OP_SGET_BYTE: 1463 case OP_SGET_SHORT: 1464 case OP_SGET: { 1465 int valOffset = offsetof(StaticField, value); 1466 int tReg = dvmCompilerAllocTemp(cUnit); 1467 bool isVolatile; 1468 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1469 mir->meta.calleeMethod : cUnit->method; 1470 void *fieldPtr = (void*) 1471 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1472 1473 if (fieldPtr == NULL) { 1474 LOGE("Unexpected null static field"); 1475 dvmAbort(); 1476 } 1477 1478 isVolatile = (mir->dalvikInsn.opCode == OP_SGET_VOLATILE) || 1479 (mir->dalvikInsn.opCode == OP_SGET_OBJECT_VOLATILE) || 1480 dvmIsVolatileField(fieldPtr); 1481 1482 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1483 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1484 loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); 1485 1486 if (isVolatile) { 1487 dvmCompilerGenMemBarrier(cUnit); 1488 } 1489 HEAP_ACCESS_SHADOW(true); 1490 loadWordDisp(cUnit, tReg, 0, rlResult.lowReg); 1491 HEAP_ACCESS_SHADOW(false); 1492 1493 storeValue(cUnit, rlDest, rlResult); 1494 break; 1495 } 1496 case OP_SGET_WIDE: { 1497 int valOffset = offsetof(StaticField, value); 1498 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1499 mir->meta.calleeMethod : cUnit->method; 1500 void *fieldPtr = (void*) 1501 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1502 1503 if (fieldPtr == NULL) { 1504 LOGE("Unexpected null static field"); 1505 dvmAbort(); 1506 } 1507 1508 int tReg = dvmCompilerAllocTemp(cUnit); 1509 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1510 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1511 loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); 1512 1513 HEAP_ACCESS_SHADOW(true); 1514 loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg); 1515 HEAP_ACCESS_SHADOW(false); 1516 1517 storeValueWide(cUnit, rlDest, rlResult); 1518 break; 1519 } 1520 case OP_SPUT_OBJECT: 1521 case OP_SPUT_OBJECT_VOLATILE: 1522 case OP_SPUT_VOLATILE: 1523 case OP_SPUT_BOOLEAN: 1524 case OP_SPUT_CHAR: 1525 case OP_SPUT_BYTE: 1526 case OP_SPUT_SHORT: 1527 case OP_SPUT: { 1528 int valOffset = offsetof(StaticField, value); 1529 int tReg = dvmCompilerAllocTemp(cUnit); 1530 int objHead; 1531 bool isVolatile; 1532 bool isSputObject; 1533 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1534 mir->meta.calleeMethod : cUnit->method; 1535 void *fieldPtr = (void*) 1536 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1537 1538 isVolatile = (mir->dalvikInsn.opCode == OP_SPUT_VOLATILE) || 1539 (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE) || 1540 dvmIsVolatileField(fieldPtr); 1541 1542 isSputObject = (mir->dalvikInsn.opCode == OP_SPUT_OBJECT) || 1543 (mir->dalvikInsn.opCode == OP_SPUT_OBJECT_VOLATILE); 1544 1545 if (fieldPtr == NULL) { 1546 LOGE("Unexpected null static field"); 1547 dvmAbort(); 1548 } 1549 1550 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1551 rlSrc = loadValue(cUnit, rlSrc, kAnyReg); 1552 loadConstant(cUnit, tReg, (int) fieldPtr); 1553 if (isSputObject) { 1554 objHead = dvmCompilerAllocTemp(cUnit); 1555 loadWordDisp(cUnit, tReg, offsetof(Field, clazz), objHead); 1556 } 1557 HEAP_ACCESS_SHADOW(true); 1558 storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg); 1559 dvmCompilerFreeTemp(cUnit, tReg); 1560 HEAP_ACCESS_SHADOW(false); 1561 if (isVolatile) { 1562 dvmCompilerGenMemBarrier(cUnit); 1563 } 1564 if (isSputObject) { 1565 /* NOTE: marking card based sfield->clazz */ 1566 markCard(cUnit, rlSrc.lowReg, objHead); 1567 dvmCompilerFreeTemp(cUnit, objHead); 1568 } 1569 1570 break; 1571 } 1572 case OP_SPUT_WIDE: { 1573 int tReg = dvmCompilerAllocTemp(cUnit); 1574 int valOffset = offsetof(StaticField, value); 1575 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1576 mir->meta.calleeMethod : cUnit->method; 1577 void *fieldPtr = (void*) 1578 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1579 1580 if (fieldPtr == NULL) { 1581 LOGE("Unexpected null static field"); 1582 dvmAbort(); 1583 } 1584 1585 rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 1586 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); 1587 loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); 1588 1589 HEAP_ACCESS_SHADOW(true); 1590 storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg); 1591 HEAP_ACCESS_SHADOW(false); 1592 break; 1593 } 1594 case OP_NEW_INSTANCE: { 1595 /* 1596 * Obey the calling convention and don't mess with the register 1597 * usage. 1598 */ 1599 ClassObject *classPtr = (void*) 1600 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); 1601 1602 if (classPtr == NULL) { 1603 LOGE("Unexpected null class"); 1604 dvmAbort(); 1605 } 1606 1607 /* 1608 * If it is going to throw, it should not make to the trace to begin 1609 * with. However, Alloc might throw, so we need to genExportPC() 1610 */ 1611 assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0); 1612 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 1613 genExportPC(cUnit, mir); 1614 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmAllocObject); 1615 loadConstant(cUnit, r0, (int) classPtr); 1616 loadConstant(cUnit, r1, ALLOC_DONT_TRACK); 1617 opReg(cUnit, kOpBlx, r2); 1618 dvmCompilerClobberCallRegs(cUnit); 1619 /* generate a branch over if allocation is successful */ 1620 opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */ 1621 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe); 1622 /* 1623 * OOM exception needs to be thrown here and cannot re-execute 1624 */ 1625 loadConstant(cUnit, r0, 1626 (int) (cUnit->method->insns + mir->offset)); 1627 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 1628 /* noreturn */ 1629 1630 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 1631 target->defMask = ENCODE_ALL; 1632 branchOver->generic.target = (LIR *) target; 1633 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1634 rlResult = dvmCompilerGetReturn(cUnit); 1635 storeValue(cUnit, rlDest, rlResult); 1636 break; 1637 } 1638 case OP_CHECK_CAST: { 1639 /* 1640 * Obey the calling convention and don't mess with the register 1641 * usage. 1642 */ 1643 ClassObject *classPtr = 1644 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); 1645 /* 1646 * Note: It is possible that classPtr is NULL at this point, 1647 * even though this instruction has been successfully interpreted. 1648 * If the previous interpretation had a null source, the 1649 * interpreter would not have bothered to resolve the clazz. 1650 * Bail out to the interpreter in this case, and log it 1651 * so that we can tell if it happens frequently. 1652 */ 1653 if (classPtr == NULL) { 1654 LOGVV("null clazz in OP_CHECK_CAST, single-stepping"); 1655 genInterpSingleStep(cUnit, mir); 1656 return false; 1657 } 1658 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 1659 loadConstant(cUnit, r1, (int) classPtr ); 1660 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1661 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1662 opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0); /* Null? */ 1663 ArmLIR *branch1 = opCondBranch(cUnit, kArmCondEq); 1664 /* 1665 * rlSrc.lowReg now contains object->clazz. Note that 1666 * it could have been allocated r0, but we're okay so long 1667 * as we don't do anything desctructive until r0 is loaded 1668 * with clazz. 1669 */ 1670 /* r0 now contains object->clazz */ 1671 loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r0); 1672 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInstanceofNonTrivial); 1673 opRegReg(cUnit, kOpCmp, r0, r1); 1674 ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq); 1675 opReg(cUnit, kOpBlx, r2); 1676 dvmCompilerClobberCallRegs(cUnit); 1677 /* 1678 * If null, check cast failed - punt to the interpreter. Because 1679 * interpreter will be the one throwing, we don't need to 1680 * genExportPC() here. 1681 */ 1682 genZeroCheck(cUnit, r0, mir->offset, NULL); 1683 /* check cast passed - branch target here */ 1684 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 1685 target->defMask = ENCODE_ALL; 1686 branch1->generic.target = (LIR *)target; 1687 branch2->generic.target = (LIR *)target; 1688 break; 1689 } 1690 case OP_SGET_WIDE_VOLATILE: 1691 case OP_SPUT_WIDE_VOLATILE: 1692 genInterpSingleStep(cUnit, mir); 1693 break; 1694 default: 1695 return true; 1696 } 1697 return false; 1698 } 1699 1700 /* 1701 * A typical example of inlined getter/setter from a monomorphic callsite: 1702 * 1703 * D/dalvikvm( 289): -------- dalvik offset: 0x0000 @ invoke-static (I) 1704 * D/dalvikvm( 289): -------- dalvik offset: 0x0000 @ sget-object (C) v0, ... 1705 * D/dalvikvm( 289): 0x4427fc22 (0002): ldr r0, [pc, #56] 1706 * D/dalvikvm( 289): 0x4427fc24 (0004): ldr r1, [r0, #0] 1707 * D/dalvikvm( 289): 0x4427fc26 (0006): str r1, [r5, #0] 1708 * D/dalvikvm( 289): 0x4427fc28 (0008): .align4 1709 * D/dalvikvm( 289): L0x0003: 1710 * D/dalvikvm( 289): -------- dalvik offset: 0x0003 @ move-result-object (I) v0 1711 * 1712 * Note the invoke-static and move-result-object with the (I) notation are 1713 * turned into no-op. 1714 */ 1715 static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir) 1716 { 1717 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 1718 RegLocation rlResult; 1719 switch (dalvikOpCode) { 1720 case OP_MOVE_EXCEPTION: { 1721 int offset = offsetof(InterpState, self); 1722 int exOffset = offsetof(Thread, exception); 1723 int selfReg = dvmCompilerAllocTemp(cUnit); 1724 int resetReg = dvmCompilerAllocTemp(cUnit); 1725 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1726 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1727 loadWordDisp(cUnit, rGLUE, offset, selfReg); 1728 loadConstant(cUnit, resetReg, 0); 1729 loadWordDisp(cUnit, selfReg, exOffset, rlResult.lowReg); 1730 storeWordDisp(cUnit, selfReg, exOffset, resetReg); 1731 storeValue(cUnit, rlDest, rlResult); 1732 break; 1733 } 1734 case OP_MOVE_RESULT: 1735 case OP_MOVE_RESULT_OBJECT: { 1736 /* An inlined move result is effectively no-op */ 1737 if (mir->OptimizationFlags & MIR_INLINED) 1738 break; 1739 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1740 RegLocation rlSrc = LOC_DALVIK_RETURN_VAL; 1741 rlSrc.fp = rlDest.fp; 1742 storeValue(cUnit, rlDest, rlSrc); 1743 break; 1744 } 1745 case OP_MOVE_RESULT_WIDE: { 1746 /* An inlined move result is effectively no-op */ 1747 if (mir->OptimizationFlags & MIR_INLINED) 1748 break; 1749 RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1750 RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE; 1751 rlSrc.fp = rlDest.fp; 1752 storeValueWide(cUnit, rlDest, rlSrc); 1753 break; 1754 } 1755 case OP_RETURN_WIDE: { 1756 RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 1757 RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE; 1758 rlDest.fp = rlSrc.fp; 1759 storeValueWide(cUnit, rlDest, rlSrc); 1760 genReturnCommon(cUnit,mir); 1761 break; 1762 } 1763 case OP_RETURN: 1764 case OP_RETURN_OBJECT: { 1765 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1766 RegLocation rlDest = LOC_DALVIK_RETURN_VAL; 1767 rlDest.fp = rlSrc.fp; 1768 storeValue(cUnit, rlDest, rlSrc); 1769 genReturnCommon(cUnit,mir); 1770 break; 1771 } 1772 case OP_MONITOR_EXIT: 1773 case OP_MONITOR_ENTER: 1774 #if defined(WITH_DEADLOCK_PREDICTION) || defined(WITH_MONITOR_TRACKING) 1775 genMonitorPortable(cUnit, mir); 1776 #else 1777 genMonitor(cUnit, mir); 1778 #endif 1779 break; 1780 case OP_THROW: { 1781 genInterpSingleStep(cUnit, mir); 1782 break; 1783 } 1784 default: 1785 return true; 1786 } 1787 return false; 1788 } 1789 1790 static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir) 1791 { 1792 OpCode opCode = mir->dalvikInsn.opCode; 1793 RegLocation rlDest; 1794 RegLocation rlSrc; 1795 RegLocation rlResult; 1796 1797 if ( (opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { 1798 return genArithOp( cUnit, mir ); 1799 } 1800 1801 if (mir->ssaRep->numUses == 2) 1802 rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 1803 else 1804 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1805 if (mir->ssaRep->numDefs == 2) 1806 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1807 else 1808 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1809 1810 switch (opCode) { 1811 case OP_DOUBLE_TO_INT: 1812 case OP_INT_TO_FLOAT: 1813 case OP_FLOAT_TO_INT: 1814 case OP_DOUBLE_TO_FLOAT: 1815 case OP_FLOAT_TO_DOUBLE: 1816 case OP_INT_TO_DOUBLE: 1817 case OP_FLOAT_TO_LONG: 1818 case OP_LONG_TO_FLOAT: 1819 case OP_DOUBLE_TO_LONG: 1820 case OP_LONG_TO_DOUBLE: 1821 return genConversion(cUnit, mir); 1822 case OP_NEG_INT: 1823 case OP_NOT_INT: 1824 return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc); 1825 case OP_NEG_LONG: 1826 case OP_NOT_LONG: 1827 return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc); 1828 case OP_NEG_FLOAT: 1829 return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc); 1830 case OP_NEG_DOUBLE: 1831 return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc); 1832 case OP_MOVE_WIDE: 1833 storeValueWide(cUnit, rlDest, rlSrc); 1834 break; 1835 case OP_INT_TO_LONG: 1836 rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc); 1837 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1838 //TUNING: shouldn't loadValueDirect already check for phys reg? 1839 if (rlSrc.location == kLocPhysReg) { 1840 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); 1841 } else { 1842 loadValueDirect(cUnit, rlSrc, rlResult.lowReg); 1843 } 1844 opRegRegImm(cUnit, kOpAsr, rlResult.highReg, 1845 rlResult.lowReg, 31); 1846 storeValueWide(cUnit, rlDest, rlResult); 1847 break; 1848 case OP_LONG_TO_INT: 1849 rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc); 1850 rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc); 1851 // Intentional fallthrough 1852 case OP_MOVE: 1853 case OP_MOVE_OBJECT: 1854 storeValue(cUnit, rlDest, rlSrc); 1855 break; 1856 case OP_INT_TO_BYTE: 1857 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1858 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1859 opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg); 1860 storeValue(cUnit, rlDest, rlResult); 1861 break; 1862 case OP_INT_TO_SHORT: 1863 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1864 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1865 opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg); 1866 storeValue(cUnit, rlDest, rlResult); 1867 break; 1868 case OP_INT_TO_CHAR: 1869 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1870 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1871 opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg); 1872 storeValue(cUnit, rlDest, rlResult); 1873 break; 1874 case OP_ARRAY_LENGTH: { 1875 int lenOffset = offsetof(ArrayObject, length); 1876 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1877 genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg, 1878 mir->offset, NULL); 1879 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1880 loadWordDisp(cUnit, rlSrc.lowReg, lenOffset, 1881 rlResult.lowReg); 1882 storeValue(cUnit, rlDest, rlResult); 1883 break; 1884 } 1885 default: 1886 return true; 1887 } 1888 return false; 1889 } 1890 1891 static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir) 1892 { 1893 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 1894 RegLocation rlDest; 1895 RegLocation rlResult; 1896 int BBBB = mir->dalvikInsn.vB; 1897 if (dalvikOpCode == OP_CONST_WIDE_16) { 1898 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1899 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1900 loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB); 1901 //TUNING: do high separately to avoid load dependency 1902 opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31); 1903 storeValueWide(cUnit, rlDest, rlResult); 1904 } else if (dalvikOpCode == OP_CONST_16) { 1905 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1906 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1907 loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB); 1908 storeValue(cUnit, rlDest, rlResult); 1909 } else 1910 return true; 1911 return false; 1912 } 1913 1914 /* Compare agaist zero */ 1915 static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, 1916 ArmLIR *labelList) 1917 { 1918 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 1919 ArmConditionCode cond; 1920 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1921 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1922 opRegImm(cUnit, kOpCmp, rlSrc.lowReg, 0); 1923 1924 //TUNING: break this out to allow use of Thumb2 CB[N]Z 1925 switch (dalvikOpCode) { 1926 case OP_IF_EQZ: 1927 cond = kArmCondEq; 1928 break; 1929 case OP_IF_NEZ: 1930 cond = kArmCondNe; 1931 break; 1932 case OP_IF_LTZ: 1933 cond = kArmCondLt; 1934 break; 1935 case OP_IF_GEZ: 1936 cond = kArmCondGe; 1937 break; 1938 case OP_IF_GTZ: 1939 cond = kArmCondGt; 1940 break; 1941 case OP_IF_LEZ: 1942 cond = kArmCondLe; 1943 break; 1944 default: 1945 cond = 0; 1946 LOGE("Unexpected opcode (%d) for Fmt21t\n", dalvikOpCode); 1947 dvmCompilerAbort(cUnit); 1948 } 1949 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); 1950 /* This mostly likely will be optimized away in a later phase */ 1951 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); 1952 return false; 1953 } 1954 1955 static bool isPowerOfTwo(int x) 1956 { 1957 return (x & (x - 1)) == 0; 1958 } 1959 1960 // Returns true if no more than two bits are set in 'x'. 1961 static bool isPopCountLE2(unsigned int x) 1962 { 1963 x &= x - 1; 1964 return (x & (x - 1)) == 0; 1965 } 1966 1967 // Returns the index of the lowest set bit in 'x'. 1968 static int lowestSetBit(unsigned int x) { 1969 int bit_posn = 0; 1970 while ((x & 0xf) == 0) { 1971 bit_posn += 4; 1972 x >>= 4; 1973 } 1974 while ((x & 1) == 0) { 1975 bit_posn++; 1976 x >>= 1; 1977 } 1978 return bit_posn; 1979 } 1980 1981 // Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit' 1982 // and store the result in 'rlDest'. 1983 static bool handleEasyDivide(CompilationUnit *cUnit, OpCode dalvikOpCode, 1984 RegLocation rlSrc, RegLocation rlDest, int lit) 1985 { 1986 if (lit < 2 || !isPowerOfTwo(lit)) { 1987 return false; 1988 } 1989 int k = lowestSetBit(lit); 1990 if (k >= 30) { 1991 // Avoid special cases. 1992 return false; 1993 } 1994 bool div = (dalvikOpCode == OP_DIV_INT_LIT8 || dalvikOpCode == OP_DIV_INT_LIT16); 1995 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1996 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1997 if (div) { 1998 int tReg = dvmCompilerAllocTemp(cUnit); 1999 if (lit == 2) { 2000 // Division by 2 is by far the most common division by constant. 2001 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k); 2002 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); 2003 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); 2004 } else { 2005 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31); 2006 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k); 2007 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); 2008 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); 2009 } 2010 } else { 2011 int cReg = dvmCompilerAllocTemp(cUnit); 2012 loadConstant(cUnit, cReg, lit - 1); 2013 int tReg1 = dvmCompilerAllocTemp(cUnit); 2014 int tReg2 = dvmCompilerAllocTemp(cUnit); 2015 if (lit == 2) { 2016 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k); 2017 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); 2018 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg); 2019 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); 2020 } else { 2021 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31); 2022 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k); 2023 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); 2024 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg); 2025 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); 2026 } 2027 } 2028 storeValue(cUnit, rlDest, rlResult); 2029 return true; 2030 } 2031 2032 // Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit' 2033 // and store the result in 'rlDest'. 2034 static bool handleEasyMultiply(CompilationUnit *cUnit, 2035 RegLocation rlSrc, RegLocation rlDest, int lit) 2036 { 2037 // Can we simplify this multiplication? 2038 bool powerOfTwo = false; 2039 bool popCountLE2 = false; 2040 bool powerOfTwoMinusOne = false; 2041 if (lit < 2) { 2042 // Avoid special cases. 2043 return false; 2044 } else if (isPowerOfTwo(lit)) { 2045 powerOfTwo = true; 2046 } else if (isPopCountLE2(lit)) { 2047 popCountLE2 = true; 2048 } else if (isPowerOfTwo(lit + 1)) { 2049 powerOfTwoMinusOne = true; 2050 } else { 2051 return false; 2052 } 2053 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2054 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2055 if (powerOfTwo) { 2056 // Shift. 2057 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg, 2058 lowestSetBit(lit)); 2059 } else if (popCountLE2) { 2060 // Shift and add and shift. 2061 int firstBit = lowestSetBit(lit); 2062 int secondBit = lowestSetBit(lit ^ (1 << firstBit)); 2063 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit, 2064 firstBit, secondBit); 2065 } else { 2066 // Reverse subtract: (src << (shift + 1)) - src. 2067 assert(powerOfTwoMinusOne); 2068 // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1) 2069 int tReg = dvmCompilerAllocTemp(cUnit); 2070 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1)); 2071 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg); 2072 } 2073 storeValue(cUnit, rlDest, rlResult); 2074 return true; 2075 } 2076 2077 static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir) 2078 { 2079 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 2080 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2081 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2082 RegLocation rlResult; 2083 int lit = mir->dalvikInsn.vC; 2084 OpKind op = 0; /* Make gcc happy */ 2085 int shiftOp = false; 2086 bool isDiv = false; 2087 2088 switch (dalvikOpCode) { 2089 case OP_RSUB_INT_LIT8: 2090 case OP_RSUB_INT: { 2091 int tReg; 2092 //TUNING: add support for use of Arm rsub op 2093 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2094 tReg = dvmCompilerAllocTemp(cUnit); 2095 loadConstant(cUnit, tReg, lit); 2096 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2097 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, 2098 tReg, rlSrc.lowReg); 2099 storeValue(cUnit, rlDest, rlResult); 2100 return false; 2101 break; 2102 } 2103 2104 case OP_ADD_INT_LIT8: 2105 case OP_ADD_INT_LIT16: 2106 op = kOpAdd; 2107 break; 2108 case OP_MUL_INT_LIT8: 2109 case OP_MUL_INT_LIT16: { 2110 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) { 2111 return false; 2112 } 2113 op = kOpMul; 2114 break; 2115 } 2116 case OP_AND_INT_LIT8: 2117 case OP_AND_INT_LIT16: 2118 op = kOpAnd; 2119 break; 2120 case OP_OR_INT_LIT8: 2121 case OP_OR_INT_LIT16: 2122 op = kOpOr; 2123 break; 2124 case OP_XOR_INT_LIT8: 2125 case OP_XOR_INT_LIT16: 2126 op = kOpXor; 2127 break; 2128 case OP_SHL_INT_LIT8: 2129 lit &= 31; 2130 shiftOp = true; 2131 op = kOpLsl; 2132 break; 2133 case OP_SHR_INT_LIT8: 2134 lit &= 31; 2135 shiftOp = true; 2136 op = kOpAsr; 2137 break; 2138 case OP_USHR_INT_LIT8: 2139 lit &= 31; 2140 shiftOp = true; 2141 op = kOpLsr; 2142 break; 2143 2144 case OP_DIV_INT_LIT8: 2145 case OP_DIV_INT_LIT16: 2146 case OP_REM_INT_LIT8: 2147 case OP_REM_INT_LIT16: 2148 if (lit == 0) { 2149 /* Let the interpreter deal with div by 0 */ 2150 genInterpSingleStep(cUnit, mir); 2151 return false; 2152 } 2153 if (handleEasyDivide(cUnit, dalvikOpCode, rlSrc, rlDest, lit)) { 2154 return false; 2155 } 2156 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2157 loadValueDirectFixed(cUnit, rlSrc, r0); 2158 dvmCompilerClobber(cUnit, r0); 2159 if ((dalvikOpCode == OP_DIV_INT_LIT8) || 2160 (dalvikOpCode == OP_DIV_INT_LIT16)) { 2161 LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idiv); 2162 isDiv = true; 2163 } else { 2164 LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idivmod); 2165 isDiv = false; 2166 } 2167 loadConstant(cUnit, r1, lit); 2168 opReg(cUnit, kOpBlx, r2); 2169 dvmCompilerClobberCallRegs(cUnit); 2170 if (isDiv) 2171 rlResult = dvmCompilerGetReturn(cUnit); 2172 else 2173 rlResult = dvmCompilerGetReturnAlt(cUnit); 2174 storeValue(cUnit, rlDest, rlResult); 2175 return false; 2176 break; 2177 default: 2178 return true; 2179 } 2180 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2181 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2182 // Avoid shifts by literal 0 - no support in Thumb. Change to copy 2183 if (shiftOp && (lit == 0)) { 2184 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); 2185 } else { 2186 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit); 2187 } 2188 storeValue(cUnit, rlDest, rlResult); 2189 return false; 2190 } 2191 2192 static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir) 2193 { 2194 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 2195 int fieldOffset = -1; 2196 bool isVolatile = false; 2197 switch (dalvikOpCode) { 2198 /* 2199 * Wide volatiles currently handled via single step. 2200 * Add them here if generating in-line code. 2201 * case OP_IGET_WIDE_VOLATILE: 2202 * case OP_IPUT_WIDE_VOLATILE: 2203 */ 2204 case OP_IGET: 2205 case OP_IGET_VOLATILE: 2206 case OP_IGET_WIDE: 2207 case OP_IGET_OBJECT: 2208 case OP_IGET_OBJECT_VOLATILE: 2209 case OP_IGET_BOOLEAN: 2210 case OP_IGET_BYTE: 2211 case OP_IGET_CHAR: 2212 case OP_IGET_SHORT: 2213 case OP_IPUT: 2214 case OP_IPUT_VOLATILE: 2215 case OP_IPUT_WIDE: 2216 case OP_IPUT_OBJECT: 2217 case OP_IPUT_OBJECT_VOLATILE: 2218 case OP_IPUT_BOOLEAN: 2219 case OP_IPUT_BYTE: 2220 case OP_IPUT_CHAR: 2221 case OP_IPUT_SHORT: { 2222 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 2223 mir->meta.calleeMethod : cUnit->method; 2224 Field *fieldPtr = 2225 method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC]; 2226 2227 if (fieldPtr == NULL) { 2228 LOGE("Unexpected null instance field"); 2229 dvmAbort(); 2230 } 2231 isVolatile = dvmIsVolatileField(fieldPtr); 2232 fieldOffset = ((InstField *)fieldPtr)->byteOffset; 2233 break; 2234 } 2235 default: 2236 break; 2237 } 2238 2239 switch (dalvikOpCode) { 2240 case OP_NEW_ARRAY: { 2241 // Generates a call - use explicit registers 2242 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2243 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2244 RegLocation rlResult; 2245 void *classPtr = (void*) 2246 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); 2247 2248 if (classPtr == NULL) { 2249 LOGE("Unexpected null class"); 2250 dvmAbort(); 2251 } 2252 2253 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2254 genExportPC(cUnit, mir); 2255 loadValueDirectFixed(cUnit, rlSrc, r1); /* Len */ 2256 loadConstant(cUnit, r0, (int) classPtr ); 2257 LOAD_FUNC_ADDR(cUnit, r3, (int)dvmAllocArrayByClass); 2258 /* 2259 * "len < 0": bail to the interpreter to re-execute the 2260 * instruction 2261 */ 2262 genRegImmCheck(cUnit, kArmCondMi, r1, 0, mir->offset, NULL); 2263 loadConstant(cUnit, r2, ALLOC_DONT_TRACK); 2264 opReg(cUnit, kOpBlx, r3); 2265 dvmCompilerClobberCallRegs(cUnit); 2266 /* generate a branch over if allocation is successful */ 2267 opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */ 2268 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe); 2269 /* 2270 * OOM exception needs to be thrown here and cannot re-execute 2271 */ 2272 loadConstant(cUnit, r0, 2273 (int) (cUnit->method->insns + mir->offset)); 2274 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 2275 /* noreturn */ 2276 2277 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2278 target->defMask = ENCODE_ALL; 2279 branchOver->generic.target = (LIR *) target; 2280 rlResult = dvmCompilerGetReturn(cUnit); 2281 storeValue(cUnit, rlDest, rlResult); 2282 break; 2283 } 2284 case OP_INSTANCE_OF: { 2285 // May generate a call - use explicit registers 2286 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2287 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2288 RegLocation rlResult; 2289 ClassObject *classPtr = 2290 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); 2291 /* 2292 * Note: It is possible that classPtr is NULL at this point, 2293 * even though this instruction has been successfully interpreted. 2294 * If the previous interpretation had a null source, the 2295 * interpreter would not have bothered to resolve the clazz. 2296 * Bail out to the interpreter in this case, and log it 2297 * so that we can tell if it happens frequently. 2298 */ 2299 if (classPtr == NULL) { 2300 LOGD("null clazz in OP_INSTANCE_OF, single-stepping"); 2301 genInterpSingleStep(cUnit, mir); 2302 break; 2303 } 2304 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2305 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */ 2306 loadConstant(cUnit, r2, (int) classPtr ); 2307 //TUNING: compare to 0 primative to allow use of CB[N]Z 2308 opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */ 2309 /* When taken r0 has NULL which can be used for store directly */ 2310 ArmLIR *branch1 = opCondBranch(cUnit, kArmCondEq); 2311 /* r1 now contains object->clazz */ 2312 loadWordDisp(cUnit, r0, offsetof(Object, clazz), r1); 2313 /* r1 now contains object->clazz */ 2314 LOAD_FUNC_ADDR(cUnit, r3, (int)dvmInstanceofNonTrivial); 2315 loadConstant(cUnit, r0, 1); /* Assume true */ 2316 opRegReg(cUnit, kOpCmp, r1, r2); 2317 ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq); 2318 genRegCopy(cUnit, r0, r1); 2319 genRegCopy(cUnit, r1, r2); 2320 opReg(cUnit, kOpBlx, r3); 2321 dvmCompilerClobberCallRegs(cUnit); 2322 /* branch target here */ 2323 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2324 target->defMask = ENCODE_ALL; 2325 rlResult = dvmCompilerGetReturn(cUnit); 2326 storeValue(cUnit, rlDest, rlResult); 2327 branch1->generic.target = (LIR *)target; 2328 branch2->generic.target = (LIR *)target; 2329 break; 2330 } 2331 case OP_IGET_WIDE: 2332 genIGetWide(cUnit, mir, fieldOffset); 2333 break; 2334 case OP_IGET_VOLATILE: 2335 case OP_IGET_OBJECT_VOLATILE: 2336 isVolatile = true; 2337 // NOTE: intentional fallthrough 2338 case OP_IGET: 2339 case OP_IGET_OBJECT: 2340 case OP_IGET_BOOLEAN: 2341 case OP_IGET_BYTE: 2342 case OP_IGET_CHAR: 2343 case OP_IGET_SHORT: 2344 genIGet(cUnit, mir, kWord, fieldOffset, isVolatile); 2345 break; 2346 case OP_IPUT_WIDE: 2347 genIPutWide(cUnit, mir, fieldOffset); 2348 break; 2349 case OP_IPUT: 2350 case OP_IPUT_SHORT: 2351 case OP_IPUT_CHAR: 2352 case OP_IPUT_BYTE: 2353 case OP_IPUT_BOOLEAN: 2354 genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile); 2355 break; 2356 case OP_IPUT_VOLATILE: 2357 case OP_IPUT_OBJECT_VOLATILE: 2358 isVolatile = true; 2359 // NOTE: intentional fallthrough 2360 case OP_IPUT_OBJECT: 2361 genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile); 2362 break; 2363 case OP_IGET_WIDE_VOLATILE: 2364 case OP_IPUT_WIDE_VOLATILE: 2365 genInterpSingleStep(cUnit, mir); 2366 break; 2367 default: 2368 return true; 2369 } 2370 return false; 2371 } 2372 2373 static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir) 2374 { 2375 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 2376 int fieldOffset = mir->dalvikInsn.vC; 2377 switch (dalvikOpCode) { 2378 case OP_IGET_QUICK: 2379 case OP_IGET_OBJECT_QUICK: 2380 genIGet(cUnit, mir, kWord, fieldOffset, false); 2381 break; 2382 case OP_IPUT_QUICK: 2383 genIPut(cUnit, mir, kWord, fieldOffset, false, false); 2384 break; 2385 case OP_IPUT_OBJECT_QUICK: 2386 genIPut(cUnit, mir, kWord, fieldOffset, true, false); 2387 break; 2388 case OP_IGET_WIDE_QUICK: 2389 genIGetWide(cUnit, mir, fieldOffset); 2390 break; 2391 case OP_IPUT_WIDE_QUICK: 2392 genIPutWide(cUnit, mir, fieldOffset); 2393 break; 2394 default: 2395 return true; 2396 } 2397 return false; 2398 2399 } 2400 2401 /* Compare agaist zero */ 2402 static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, 2403 ArmLIR *labelList) 2404 { 2405 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 2406 ArmConditionCode cond; 2407 RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0); 2408 RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1); 2409 2410 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); 2411 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); 2412 opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg); 2413 2414 switch (dalvikOpCode) { 2415 case OP_IF_EQ: 2416 cond = kArmCondEq; 2417 break; 2418 case OP_IF_NE: 2419 cond = kArmCondNe; 2420 break; 2421 case OP_IF_LT: 2422 cond = kArmCondLt; 2423 break; 2424 case OP_IF_GE: 2425 cond = kArmCondGe; 2426 break; 2427 case OP_IF_GT: 2428 cond = kArmCondGt; 2429 break; 2430 case OP_IF_LE: 2431 cond = kArmCondLe; 2432 break; 2433 default: 2434 cond = 0; 2435 LOGE("Unexpected opcode (%d) for Fmt22t\n", dalvikOpCode); 2436 dvmCompilerAbort(cUnit); 2437 } 2438 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); 2439 /* This mostly likely will be optimized away in a later phase */ 2440 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); 2441 return false; 2442 } 2443 2444 static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir) 2445 { 2446 OpCode opCode = mir->dalvikInsn.opCode; 2447 2448 switch (opCode) { 2449 case OP_MOVE_16: 2450 case OP_MOVE_OBJECT_16: 2451 case OP_MOVE_FROM16: 2452 case OP_MOVE_OBJECT_FROM16: { 2453 storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0), 2454 dvmCompilerGetSrc(cUnit, mir, 0)); 2455 break; 2456 } 2457 case OP_MOVE_WIDE_16: 2458 case OP_MOVE_WIDE_FROM16: { 2459 storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1), 2460 dvmCompilerGetSrcWide(cUnit, mir, 0, 1)); 2461 break; 2462 } 2463 default: 2464 return true; 2465 } 2466 return false; 2467 } 2468 2469 static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir) 2470 { 2471 OpCode opCode = mir->dalvikInsn.opCode; 2472 RegLocation rlSrc1; 2473 RegLocation rlSrc2; 2474 RegLocation rlDest; 2475 2476 if ( (opCode >= OP_ADD_INT) && (opCode <= OP_REM_DOUBLE)) { 2477 return genArithOp( cUnit, mir ); 2478 } 2479 2480 /* APUTs have 3 sources and no targets */ 2481 if (mir->ssaRep->numDefs == 0) { 2482 if (mir->ssaRep->numUses == 3) { 2483 rlDest = dvmCompilerGetSrc(cUnit, mir, 0); 2484 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1); 2485 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2); 2486 } else { 2487 assert(mir->ssaRep->numUses == 4); 2488 rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 2489 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2); 2490 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3); 2491 } 2492 } else { 2493 /* Two sources and 1 dest. Deduce the operand sizes */ 2494 if (mir->ssaRep->numUses == 4) { 2495 rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 2496 rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3); 2497 } else { 2498 assert(mir->ssaRep->numUses == 2); 2499 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0); 2500 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1); 2501 } 2502 if (mir->ssaRep->numDefs == 2) { 2503 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 2504 } else { 2505 assert(mir->ssaRep->numDefs == 1); 2506 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2507 } 2508 } 2509 2510 2511 switch (opCode) { 2512 case OP_CMPL_FLOAT: 2513 case OP_CMPG_FLOAT: 2514 case OP_CMPL_DOUBLE: 2515 case OP_CMPG_DOUBLE: 2516 return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2); 2517 case OP_CMP_LONG: 2518 genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2); 2519 break; 2520 case OP_AGET_WIDE: 2521 genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3); 2522 break; 2523 case OP_AGET: 2524 case OP_AGET_OBJECT: 2525 genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2); 2526 break; 2527 case OP_AGET_BOOLEAN: 2528 genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0); 2529 break; 2530 case OP_AGET_BYTE: 2531 genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0); 2532 break; 2533 case OP_AGET_CHAR: 2534 genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1); 2535 break; 2536 case OP_AGET_SHORT: 2537 genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1); 2538 break; 2539 case OP_APUT_WIDE: 2540 genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3); 2541 break; 2542 case OP_APUT: 2543 genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2); 2544 break; 2545 case OP_APUT_OBJECT: 2546 genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2); 2547 break; 2548 case OP_APUT_SHORT: 2549 case OP_APUT_CHAR: 2550 genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1); 2551 break; 2552 case OP_APUT_BYTE: 2553 case OP_APUT_BOOLEAN: 2554 genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0); 2555 break; 2556 default: 2557 return true; 2558 } 2559 return false; 2560 } 2561 2562 /* 2563 * Find the matching case. 2564 * 2565 * return values: 2566 * r0 (low 32-bit): pc of the chaining cell corresponding to the resolved case, 2567 * including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES). 2568 * r1 (high 32-bit): the branch offset of the matching case (only for indexes 2569 * above MAX_CHAINED_SWITCH_CASES). 2570 * 2571 * Instructions around the call are: 2572 * 2573 * mov r2, pc 2574 * blx &findPackedSwitchIndex 2575 * mov pc, r0 2576 * .align4 2577 * chaining cell for case 0 [12 bytes] 2578 * chaining cell for case 1 [12 bytes] 2579 * : 2580 * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [12 bytes] 2581 * chaining cell for case default [8 bytes] 2582 * noChain exit 2583 */ 2584 static s8 findPackedSwitchIndex(const u2* switchData, int testVal, int pc) 2585 { 2586 int size; 2587 int firstKey; 2588 const int *entries; 2589 int index; 2590 int jumpIndex; 2591 int caseDPCOffset = 0; 2592 /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */ 2593 int chainingPC = (pc + 4) & ~3; 2594 2595 /* 2596 * Packed switch data format: 2597 * ushort ident = 0x0100 magic value 2598 * ushort size number of entries in the table 2599 * int first_key first (and lowest) switch case value 2600 * int targets[size] branch targets, relative to switch opcode 2601 * 2602 * Total size is (4+size*2) 16-bit code units. 2603 */ 2604 size = switchData[1]; 2605 assert(size > 0); 2606 2607 firstKey = switchData[2]; 2608 firstKey |= switchData[3] << 16; 2609 2610 2611 /* The entries are guaranteed to be aligned on a 32-bit boundary; 2612 * we can treat them as a native int array. 2613 */ 2614 entries = (const int*) &switchData[4]; 2615 assert(((u4)entries & 0x3) == 0); 2616 2617 index = testVal - firstKey; 2618 2619 /* Jump to the default cell */ 2620 if (index < 0 || index >= size) { 2621 jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES); 2622 /* Jump to the non-chaining exit point */ 2623 } else if (index >= MAX_CHAINED_SWITCH_CASES) { 2624 jumpIndex = MAX_CHAINED_SWITCH_CASES + 1; 2625 caseDPCOffset = entries[index]; 2626 /* Jump to the inline chaining cell */ 2627 } else { 2628 jumpIndex = index; 2629 } 2630 2631 chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE; 2632 return (((s8) caseDPCOffset) << 32) | (u8) chainingPC; 2633 } 2634 2635 /* See comments for findPackedSwitchIndex */ 2636 static s8 findSparseSwitchIndex(const u2* switchData, int testVal, int pc) 2637 { 2638 int size; 2639 const int *keys; 2640 const int *entries; 2641 int chainingPC = (pc + 4) & ~3; 2642 int i; 2643 2644 /* 2645 * Sparse switch data format: 2646 * ushort ident = 0x0200 magic value 2647 * ushort size number of entries in the table; > 0 2648 * int keys[size] keys, sorted low-to-high; 32-bit aligned 2649 * int targets[size] branch targets, relative to switch opcode 2650 * 2651 * Total size is (2+size*4) 16-bit code units. 2652 */ 2653 2654 size = switchData[1]; 2655 assert(size > 0); 2656 2657 /* The keys are guaranteed to be aligned on a 32-bit boundary; 2658 * we can treat them as a native int array. 2659 */ 2660 keys = (const int*) &switchData[2]; 2661 assert(((u4)keys & 0x3) == 0); 2662 2663 /* The entries are guaranteed to be aligned on a 32-bit boundary; 2664 * we can treat them as a native int array. 2665 */ 2666 entries = keys + size; 2667 assert(((u4)entries & 0x3) == 0); 2668 2669 /* 2670 * Run through the list of keys, which are guaranteed to 2671 * be sorted low-to-high. 2672 * 2673 * Most tables have 3-4 entries. Few have more than 10. A binary 2674 * search here is probably not useful. 2675 */ 2676 for (i = 0; i < size; i++) { 2677 int k = keys[i]; 2678 if (k == testVal) { 2679 /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */ 2680 int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ? 2681 i : MAX_CHAINED_SWITCH_CASES + 1; 2682 chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE; 2683 return (((s8) entries[i]) << 32) | (u8) chainingPC; 2684 } else if (k > testVal) { 2685 break; 2686 } 2687 } 2688 return chainingPC + MIN(size, MAX_CHAINED_SWITCH_CASES) * 2689 CHAIN_CELL_NORMAL_SIZE; 2690 } 2691 2692 static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir) 2693 { 2694 OpCode dalvikOpCode = mir->dalvikInsn.opCode; 2695 switch (dalvikOpCode) { 2696 case OP_FILL_ARRAY_DATA: { 2697 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2698 // Making a call - use explicit registers 2699 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2700 genExportPC(cUnit, mir); 2701 loadValueDirectFixed(cUnit, rlSrc, r0); 2702 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInterpHandleFillArrayData); 2703 loadConstant(cUnit, r1, 2704 (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB)); 2705 opReg(cUnit, kOpBlx, r2); 2706 dvmCompilerClobberCallRegs(cUnit); 2707 /* generate a branch over if successful */ 2708 opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */ 2709 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe); 2710 loadConstant(cUnit, r0, 2711 (int) (cUnit->method->insns + mir->offset)); 2712 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 2713 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2714 target->defMask = ENCODE_ALL; 2715 branchOver->generic.target = (LIR *) target; 2716 break; 2717 } 2718 /* 2719 * Compute the goto target of up to 2720 * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells. 2721 * See the comment before findPackedSwitchIndex for the code layout. 2722 */ 2723 case OP_PACKED_SWITCH: 2724 case OP_SPARSE_SWITCH: { 2725 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2726 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2727 loadValueDirectFixed(cUnit, rlSrc, r1); 2728 dvmCompilerLockAllTemps(cUnit); 2729 if (dalvikOpCode == OP_PACKED_SWITCH) { 2730 LOAD_FUNC_ADDR(cUnit, r4PC, (int)findPackedSwitchIndex); 2731 } else { 2732 LOAD_FUNC_ADDR(cUnit, r4PC, (int)findSparseSwitchIndex); 2733 } 2734 /* r0 <- Addr of the switch data */ 2735 loadConstant(cUnit, r0, 2736 (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB)); 2737 /* r2 <- pc of the instruction following the blx */ 2738 opRegReg(cUnit, kOpMov, r2, rpc); 2739 opReg(cUnit, kOpBlx, r4PC); 2740 dvmCompilerClobberCallRegs(cUnit); 2741 /* pc <- computed goto target */ 2742 opRegReg(cUnit, kOpMov, rpc, r0); 2743 break; 2744 } 2745 default: 2746 return true; 2747 } 2748 return false; 2749 } 2750 2751 /* 2752 * See the example of predicted inlining listed before the 2753 * genValidationForPredictedInline function. The function here takes care the 2754 * branch over at 0x4858de78 and the misprediction target at 0x4858de7a. 2755 */ 2756 static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir, 2757 BasicBlock *bb, 2758 ArmLIR *labelList) 2759 { 2760 BasicBlock *fallThrough = bb->fallThrough; 2761 2762 /* Bypass the move-result block if there is one */ 2763 if (fallThrough->firstMIRInsn) { 2764 assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED); 2765 fallThrough = fallThrough->fallThrough; 2766 } 2767 /* Generate a branch over if the predicted inlining is correct */ 2768 genUnconditionalBranch(cUnit, &labelList[fallThrough->id]); 2769 2770 /* Reset the register state */ 2771 dvmCompilerResetRegPool(cUnit); 2772 dvmCompilerClobberAllRegs(cUnit); 2773 dvmCompilerResetNullCheck(cUnit); 2774 2775 /* Target for the slow invoke path */ 2776 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2777 target->defMask = ENCODE_ALL; 2778 /* Hook up the target to the verification branch */ 2779 mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target; 2780 } 2781 2782 static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, 2783 ArmLIR *labelList) 2784 { 2785 ArmLIR *retChainingCell = NULL; 2786 ArmLIR *pcrLabel = NULL; 2787 2788 /* An invoke with the MIR_INLINED is effectively a no-op */ 2789 if (mir->OptimizationFlags & MIR_INLINED) 2790 return false; 2791 2792 if (bb->fallThrough != NULL) 2793 retChainingCell = &labelList[bb->fallThrough->id]; 2794 2795 DecodedInstruction *dInsn = &mir->dalvikInsn; 2796 switch (mir->dalvikInsn.opCode) { 2797 /* 2798 * calleeMethod = this->clazz->vtable[ 2799 * method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex 2800 * ] 2801 */ 2802 case OP_INVOKE_VIRTUAL: 2803 case OP_INVOKE_VIRTUAL_RANGE: { 2804 ArmLIR *predChainingCell = &labelList[bb->taken->id]; 2805 int methodIndex = 2806 cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]-> 2807 methodIndex; 2808 2809 /* 2810 * If the invoke has non-null misPredBranchOver, we need to generate 2811 * the non-inlined version of the invoke here to handle the 2812 * mispredicted case. 2813 */ 2814 if (mir->meta.callsiteInfo->misPredBranchOver) { 2815 genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList); 2816 } 2817 2818 if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL) 2819 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 2820 else 2821 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 2822 2823 genInvokeVirtualCommon(cUnit, mir, methodIndex, 2824 retChainingCell, 2825 predChainingCell, 2826 pcrLabel); 2827 break; 2828 } 2829 /* 2830 * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex 2831 * ->pResMethods[BBBB]->methodIndex] 2832 */ 2833 case OP_INVOKE_SUPER: 2834 case OP_INVOKE_SUPER_RANGE: { 2835 /* Grab the method ptr directly from what the interpreter sees */ 2836 const Method *calleeMethod = mir->meta.callsiteInfo->method; 2837 assert(calleeMethod == cUnit->method->clazz->super->vtable[ 2838 cUnit->method->clazz->pDvmDex-> 2839 pResMethods[dInsn->vB]->methodIndex]); 2840 2841 if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER) 2842 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 2843 else 2844 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 2845 2846 /* r0 = calleeMethod */ 2847 loadConstant(cUnit, r0, (int) calleeMethod); 2848 2849 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 2850 calleeMethod); 2851 break; 2852 } 2853 /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ 2854 case OP_INVOKE_DIRECT: 2855 case OP_INVOKE_DIRECT_RANGE: { 2856 /* Grab the method ptr directly from what the interpreter sees */ 2857 const Method *calleeMethod = mir->meta.callsiteInfo->method; 2858 assert(calleeMethod == 2859 cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]); 2860 2861 if (mir->dalvikInsn.opCode == OP_INVOKE_DIRECT) 2862 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 2863 else 2864 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 2865 2866 /* r0 = calleeMethod */ 2867 loadConstant(cUnit, r0, (int) calleeMethod); 2868 2869 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 2870 calleeMethod); 2871 break; 2872 } 2873 /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ 2874 case OP_INVOKE_STATIC: 2875 case OP_INVOKE_STATIC_RANGE: { 2876 /* Grab the method ptr directly from what the interpreter sees */ 2877 const Method *calleeMethod = mir->meta.callsiteInfo->method; 2878 assert(calleeMethod == 2879 cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]); 2880 2881 if (mir->dalvikInsn.opCode == OP_INVOKE_STATIC) 2882 genProcessArgsNoRange(cUnit, mir, dInsn, 2883 NULL /* no null check */); 2884 else 2885 genProcessArgsRange(cUnit, mir, dInsn, 2886 NULL /* no null check */); 2887 2888 /* r0 = calleeMethod */ 2889 loadConstant(cUnit, r0, (int) calleeMethod); 2890 2891 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 2892 calleeMethod); 2893 break; 2894 } 2895 /* 2896 * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz, 2897 * BBBB, method, method->clazz->pDvmDex) 2898 * 2899 * The following is an example of generated code for 2900 * "invoke-interface v0" 2901 * 2902 * -------- dalvik offset: 0x0008 @ invoke-interface v0 2903 * 0x47357e36 : ldr r0, [r5, #0] --+ 2904 * 0x47357e38 : sub r7,r5,#24 | 2905 * 0x47357e3c : cmp r0, #0 | genProcessArgsNoRange 2906 * 0x47357e3e : beq 0x47357e82 | 2907 * 0x47357e40 : stmia r7, <r0> --+ 2908 * 0x47357e42 : ldr r4, [pc, #120] --> r4 <- dalvikPC of this invoke 2909 * 0x47357e44 : add r1, pc, #64 --> r1 <- &retChainingCell 2910 * 0x47357e46 : add r2, pc, #72 --> r2 <- &predictedChainingCell 2911 * 0x47357e48 : blx_1 0x47348190 --+ TEMPLATE_INVOKE_METHOD_ 2912 * 0x47357e4a : blx_2 see above --+ PREDICTED_CHAIN 2913 * 0x47357e4c : b 0x47357e90 --> off to the predicted chain 2914 * 0x47357e4e : b 0x47357e82 --> punt to the interpreter 2915 * 0x47357e50 : mov r8, r1 --+ 2916 * 0x47357e52 : mov r9, r2 | 2917 * 0x47357e54 : ldr r2, [pc, #96] | 2918 * 0x47357e56 : mov r10, r3 | 2919 * 0x47357e58 : movs r0, r3 | dvmFindInterfaceMethodInCache 2920 * 0x47357e5a : ldr r3, [pc, #88] | 2921 * 0x47357e5c : ldr r7, [pc, #80] | 2922 * 0x47357e5e : mov r1, #1452 | 2923 * 0x47357e62 : blx r7 --+ 2924 * 0x47357e64 : cmp r0, #0 --> calleeMethod == NULL? 2925 * 0x47357e66 : bne 0x47357e6e --> branch over the throw if !r0 2926 * 0x47357e68 : ldr r0, [pc, #80] --> load Dalvik PC of the invoke 2927 * 0x47357e6a : blx_1 0x47348494 --+ TEMPLATE_THROW_EXCEPTION_ 2928 * 0x47357e6c : blx_2 see above --+ COMMON 2929 * 0x47357e6e : mov r1, r8 --> r1 <- &retChainingCell 2930 * 0x47357e70 : cmp r1, #0 --> compare against 0 2931 * 0x47357e72 : bgt 0x47357e7c --> >=0? don't rechain 2932 * 0x47357e74 : ldr r7, [r6, #108] --+ 2933 * 0x47357e76 : mov r2, r9 | dvmJitToPatchPredictedChain 2934 * 0x47357e78 : mov r3, r10 | 2935 * 0x47357e7a : blx r7 --+ 2936 * 0x47357e7c : add r1, pc, #8 --> r1 <- &retChainingCell 2937 * 0x47357e7e : blx_1 0x4734809c --+ TEMPLATE_INVOKE_METHOD_NO_OPT 2938 * 0x47357e80 : blx_2 see above --+ 2939 * -------- reconstruct dalvik PC : 0x425719dc @ +0x0008 2940 * 0x47357e82 : ldr r0, [pc, #56] 2941 * Exception_Handling: 2942 * 0x47357e84 : ldr r1, [r6, #92] 2943 * 0x47357e86 : blx r1 2944 * 0x47357e88 : .align4 2945 * -------- chaining cell (hot): 0x000b 2946 * 0x47357e88 : ldr r0, [r6, #104] 2947 * 0x47357e8a : blx r0 2948 * 0x47357e8c : data 0x19e2(6626) 2949 * 0x47357e8e : data 0x4257(16983) 2950 * 0x47357e90 : .align4 2951 * -------- chaining cell (predicted) 2952 * 0x47357e90 : data 0xe7fe(59390) --> will be patched into bx 2953 * 0x47357e92 : data 0x0000(0) 2954 * 0x47357e94 : data 0x0000(0) --> class 2955 * 0x47357e96 : data 0x0000(0) 2956 * 0x47357e98 : data 0x0000(0) --> method 2957 * 0x47357e9a : data 0x0000(0) 2958 * 0x47357e9c : data 0x0000(0) --> rechain count 2959 * 0x47357e9e : data 0x0000(0) 2960 * -------- end of chaining cells (0x006c) 2961 * 0x47357eb0 : .word (0xad03e369) 2962 * 0x47357eb4 : .word (0x28a90) 2963 * 0x47357eb8 : .word (0x41a63394) 2964 * 0x47357ebc : .word (0x425719dc) 2965 */ 2966 case OP_INVOKE_INTERFACE: 2967 case OP_INVOKE_INTERFACE_RANGE: { 2968 ArmLIR *predChainingCell = &labelList[bb->taken->id]; 2969 2970 /* 2971 * If the invoke has non-null misPredBranchOver, we need to generate 2972 * the non-inlined version of the invoke here to handle the 2973 * mispredicted case. 2974 */ 2975 if (mir->meta.callsiteInfo->misPredBranchOver) { 2976 genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList); 2977 } 2978 2979 if (mir->dalvikInsn.opCode == OP_INVOKE_INTERFACE) 2980 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 2981 else 2982 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 2983 2984 /* "this" is already left in r0 by genProcessArgs* */ 2985 2986 /* r4PC = dalvikCallsite */ 2987 loadConstant(cUnit, r4PC, 2988 (int) (cUnit->method->insns + mir->offset)); 2989 2990 /* r1 = &retChainingCell */ 2991 ArmLIR *addrRetChain = 2992 opRegRegImm(cUnit, kOpAdd, r1, rpc, 0); 2993 addrRetChain->generic.target = (LIR *) retChainingCell; 2994 2995 /* r2 = &predictedChainingCell */ 2996 ArmLIR *predictedChainingCell = 2997 opRegRegImm(cUnit, kOpAdd, r2, rpc, 0); 2998 predictedChainingCell->generic.target = (LIR *) predChainingCell; 2999 3000 genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN); 3001 3002 /* return through lr - jump to the chaining cell */ 3003 genUnconditionalBranch(cUnit, predChainingCell); 3004 3005 /* 3006 * null-check on "this" may have been eliminated, but we still need 3007 * a PC-reconstruction label for stack overflow bailout. 3008 */ 3009 if (pcrLabel == NULL) { 3010 int dPC = (int) (cUnit->method->insns + mir->offset); 3011 pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true); 3012 pcrLabel->opCode = kArmPseudoPCReconstructionCell; 3013 pcrLabel->operands[0] = dPC; 3014 pcrLabel->operands[1] = mir->offset; 3015 /* Insert the place holder to the growable list */ 3016 dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); 3017 } 3018 3019 /* return through lr+2 - punt to the interpreter */ 3020 genUnconditionalBranch(cUnit, pcrLabel); 3021 3022 /* 3023 * return through lr+4 - fully resolve the callee method. 3024 * r1 <- count 3025 * r2 <- &predictedChainCell 3026 * r3 <- this->class 3027 * r4 <- dPC 3028 * r7 <- this->class->vtable 3029 */ 3030 3031 /* Save count, &predictedChainCell, and class to high regs first */ 3032 genRegCopy(cUnit, r8, r1); 3033 genRegCopy(cUnit, r9, r2); 3034 genRegCopy(cUnit, r10, r3); 3035 3036 /* r0 now contains this->clazz */ 3037 genRegCopy(cUnit, r0, r3); 3038 3039 /* r1 = BBBB */ 3040 loadConstant(cUnit, r1, dInsn->vB); 3041 3042 /* r2 = method (caller) */ 3043 loadConstant(cUnit, r2, (int) cUnit->method); 3044 3045 /* r3 = pDvmDex */ 3046 loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex); 3047 3048 LOAD_FUNC_ADDR(cUnit, r7, 3049 (intptr_t) dvmFindInterfaceMethodInCache); 3050 opReg(cUnit, kOpBlx, r7); 3051 /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */ 3052 3053 dvmCompilerClobberCallRegs(cUnit); 3054 /* generate a branch over if the interface method is resolved */ 3055 opRegImm(cUnit, kOpCmp, r0, 0); /* NULL? */ 3056 ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe); 3057 /* 3058 * calleeMethod == NULL -> throw 3059 */ 3060 loadConstant(cUnit, r0, 3061 (int) (cUnit->method->insns + mir->offset)); 3062 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 3063 /* noreturn */ 3064 3065 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 3066 target->defMask = ENCODE_ALL; 3067 branchOver->generic.target = (LIR *) target; 3068 3069 genRegCopy(cUnit, r1, r8); 3070 3071 /* Check if rechain limit is reached */ 3072 opRegImm(cUnit, kOpCmp, r1, 0); 3073 3074 ArmLIR *bypassRechaining = opCondBranch(cUnit, kArmCondGt); 3075 3076 loadWordDisp(cUnit, rGLUE, offsetof(InterpState, 3077 jitToInterpEntries.dvmJitToPatchPredictedChain), r7); 3078 3079 genRegCopy(cUnit, r1, rGLUE); 3080 genRegCopy(cUnit, r2, r9); 3081 genRegCopy(cUnit, r3, r10); 3082 3083 /* 3084 * r0 = calleeMethod 3085 * r2 = &predictedChainingCell 3086 * r3 = class 3087 * 3088 * &returnChainingCell has been loaded into r1 but is not needed 3089 * when patching the chaining cell and will be clobbered upon 3090 * returning so it will be reconstructed again. 3091 */ 3092 opReg(cUnit, kOpBlx, r7); 3093 3094 /* r1 = &retChainingCell */ 3095 addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, rpc, 0); 3096 addrRetChain->generic.target = (LIR *) retChainingCell; 3097 3098 bypassRechaining->generic.target = (LIR *) addrRetChain; 3099 3100 /* 3101 * r0 = this, r1 = calleeMethod, 3102 * r1 = &ChainingCell, 3103 * r4PC = callsiteDPC, 3104 */ 3105 genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); 3106 #if defined(WITH_JIT_TUNING) 3107 gDvmJit.invokePolymorphic++; 3108 #endif 3109 /* Handle exceptions using the interpreter */ 3110 genTrap(cUnit, mir->offset, pcrLabel); 3111 break; 3112 } 3113 /* NOP */ 3114 case OP_INVOKE_DIRECT_EMPTY: { 3115 return false; 3116 } 3117 case OP_FILLED_NEW_ARRAY: 3118 case OP_FILLED_NEW_ARRAY_RANGE: { 3119 /* Just let the interpreter deal with these */ 3120 genInterpSingleStep(cUnit, mir); 3121 break; 3122 } 3123 default: 3124 return true; 3125 } 3126 return false; 3127 } 3128 3129 static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir, 3130 BasicBlock *bb, ArmLIR *labelList) 3131 { 3132 ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; 3133 ArmLIR *predChainingCell = &labelList[bb->taken->id]; 3134 ArmLIR *pcrLabel = NULL; 3135 3136 /* An invoke with the MIR_INLINED is effectively a no-op */ 3137 if (mir->OptimizationFlags & MIR_INLINED) 3138 return false; 3139 3140 DecodedInstruction *dInsn = &mir->dalvikInsn; 3141 switch (mir->dalvikInsn.opCode) { 3142 /* calleeMethod = this->clazz->vtable[BBBB] */ 3143 case OP_INVOKE_VIRTUAL_QUICK_RANGE: 3144 case OP_INVOKE_VIRTUAL_QUICK: { 3145 int methodIndex = dInsn->vB; 3146 3147 /* 3148 * If the invoke has non-null misPredBranchOver, we need to generate 3149 * the non-inlined version of the invoke here to handle the 3150 * mispredicted case. 3151 */ 3152 if (mir->meta.callsiteInfo->misPredBranchOver) { 3153 genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList); 3154 } 3155 3156 if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL_QUICK) 3157 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3158 else 3159 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3160 3161 genInvokeVirtualCommon(cUnit, mir, methodIndex, 3162 retChainingCell, 3163 predChainingCell, 3164 pcrLabel); 3165 break; 3166 } 3167 /* calleeMethod = method->clazz->super->vtable[BBBB] */ 3168 case OP_INVOKE_SUPER_QUICK: 3169 case OP_INVOKE_SUPER_QUICK_RANGE: { 3170 /* Grab the method ptr directly from what the interpreter sees */ 3171 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3172 assert(calleeMethod == 3173 cUnit->method->clazz->super->vtable[dInsn->vB]); 3174 3175 if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER_QUICK) 3176 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3177 else 3178 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3179 3180 /* r0 = calleeMethod */ 3181 loadConstant(cUnit, r0, (int) calleeMethod); 3182 3183 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 3184 calleeMethod); 3185 break; 3186 } 3187 default: 3188 return true; 3189 } 3190 return false; 3191 } 3192 3193 /* 3194 * This operation is complex enough that we'll do it partly inline 3195 * and partly with a handler. NOTE: the handler uses hardcoded 3196 * values for string object offsets and must be revisitied if the 3197 * layout changes. 3198 */ 3199 static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir) 3200 { 3201 #if defined(USE_GLOBAL_STRING_DEFS) 3202 return false; 3203 #else 3204 ArmLIR *rollback; 3205 RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0); 3206 RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1); 3207 3208 loadValueDirectFixed(cUnit, rlThis, r0); 3209 loadValueDirectFixed(cUnit, rlComp, r1); 3210 /* Test objects for NULL */ 3211 rollback = genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL); 3212 genNullCheck(cUnit, rlComp.sRegLow, r1, mir->offset, rollback); 3213 /* 3214 * TUNING: we could check for object pointer equality before invoking 3215 * handler. Unclear whether the gain would be worth the added code size 3216 * expansion. 3217 */ 3218 genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO); 3219 storeValue(cUnit, inlinedTarget(cUnit, mir, false), 3220 dvmCompilerGetReturn(cUnit)); 3221 return true; 3222 #endif 3223 } 3224 3225 static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir) 3226 { 3227 #if defined(USE_GLOBAL_STRING_DEFS) 3228 return false; 3229 #else 3230 RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0); 3231 RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1); 3232 3233 loadValueDirectFixed(cUnit, rlThis, r0); 3234 loadValueDirectFixed(cUnit, rlChar, r1); 3235 RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2); 3236 loadValueDirectFixed(cUnit, rlStart, r2); 3237 /* Test objects for NULL */ 3238 genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL); 3239 genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF); 3240 storeValue(cUnit, inlinedTarget(cUnit, mir, false), 3241 dvmCompilerGetReturn(cUnit)); 3242 return true; 3243 #endif 3244 } 3245 3246 // Generates an inlined String.isEmpty or String.length. 3247 static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir, 3248 bool isEmpty) 3249 { 3250 // dst = src.length(); 3251 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 3252 RegLocation rlDest = inlinedTarget(cUnit, mir, false); 3253 rlObj = loadValue(cUnit, rlObj, kCoreReg); 3254 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 3255 genNullCheck(