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 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondEq, valReg, 0); 35 loadWordDisp(cUnit, r6SELF, offsetof(Thread, cardTable), 36 regCardBase); 37 opRegRegImm(cUnit, kOpLsr, regCardNo, tgtAddrReg, GC_CARD_SHIFT); 38 storeBaseIndexed(cUnit, regCardBase, regCardNo, regCardBase, 0, 39 kUnsignedByte); 40 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 41 target->defMask = ENCODE_ALL; 42 branchOver->generic.target = (LIR *)target; 43 dvmCompilerFreeTemp(cUnit, regCardBase); 44 dvmCompilerFreeTemp(cUnit, regCardNo); 45 } 46 47 static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct, 48 int srcSize, int tgtSize) 49 { 50 /* 51 * Don't optimize the register usage since it calls out to template 52 * functions 53 */ 54 RegLocation rlSrc; 55 RegLocation rlDest; 56 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 57 if (srcSize == 1) { 58 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 59 loadValueDirectFixed(cUnit, rlSrc, r0); 60 } else { 61 rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 62 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1); 63 } 64 LOAD_FUNC_ADDR(cUnit, r2, (int)funct); 65 opReg(cUnit, kOpBlx, r2); 66 dvmCompilerClobberCallRegs(cUnit); 67 if (tgtSize == 1) { 68 RegLocation rlResult; 69 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 70 rlResult = dvmCompilerGetReturn(cUnit); 71 storeValue(cUnit, rlDest, rlResult); 72 } else { 73 RegLocation rlResult; 74 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 75 rlResult = dvmCompilerGetReturnWide(cUnit); 76 storeValueWide(cUnit, rlDest, rlResult); 77 } 78 return false; 79 } 80 81 static bool genArithOpFloatPortable(CompilationUnit *cUnit, MIR *mir, 82 RegLocation rlDest, RegLocation rlSrc1, 83 RegLocation rlSrc2) 84 { 85 RegLocation rlResult; 86 void* funct; 87 88 switch (mir->dalvikInsn.opcode) { 89 case OP_ADD_FLOAT_2ADDR: 90 case OP_ADD_FLOAT: 91 funct = (void*) __aeabi_fadd; 92 break; 93 case OP_SUB_FLOAT_2ADDR: 94 case OP_SUB_FLOAT: 95 funct = (void*) __aeabi_fsub; 96 break; 97 case OP_DIV_FLOAT_2ADDR: 98 case OP_DIV_FLOAT: 99 funct = (void*) __aeabi_fdiv; 100 break; 101 case OP_MUL_FLOAT_2ADDR: 102 case OP_MUL_FLOAT: 103 funct = (void*) __aeabi_fmul; 104 break; 105 case OP_REM_FLOAT_2ADDR: 106 case OP_REM_FLOAT: 107 funct = (void*) fmodf; 108 break; 109 case OP_NEG_FLOAT: { 110 genNegFloat(cUnit, rlDest, rlSrc1); 111 return false; 112 } 113 default: 114 return true; 115 } 116 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 117 loadValueDirectFixed(cUnit, rlSrc1, r0); 118 loadValueDirectFixed(cUnit, rlSrc2, r1); 119 LOAD_FUNC_ADDR(cUnit, r2, (int)funct); 120 opReg(cUnit, kOpBlx, r2); 121 dvmCompilerClobberCallRegs(cUnit); 122 rlResult = dvmCompilerGetReturn(cUnit); 123 storeValue(cUnit, rlDest, rlResult); 124 return false; 125 } 126 127 static bool genArithOpDoublePortable(CompilationUnit *cUnit, MIR *mir, 128 RegLocation rlDest, RegLocation rlSrc1, 129 RegLocation rlSrc2) 130 { 131 RegLocation rlResult; 132 void* funct; 133 134 switch (mir->dalvikInsn.opcode) { 135 case OP_ADD_DOUBLE_2ADDR: 136 case OP_ADD_DOUBLE: 137 funct = (void*) __aeabi_dadd; 138 break; 139 case OP_SUB_DOUBLE_2ADDR: 140 case OP_SUB_DOUBLE: 141 funct = (void*) __aeabi_dsub; 142 break; 143 case OP_DIV_DOUBLE_2ADDR: 144 case OP_DIV_DOUBLE: 145 funct = (void*) __aeabi_ddiv; 146 break; 147 case OP_MUL_DOUBLE_2ADDR: 148 case OP_MUL_DOUBLE: 149 funct = (void*) __aeabi_dmul; 150 break; 151 case OP_REM_DOUBLE_2ADDR: 152 case OP_REM_DOUBLE: 153 funct = (void*) (double (*)(double, double)) fmod; 154 break; 155 case OP_NEG_DOUBLE: { 156 genNegDouble(cUnit, rlDest, rlSrc1); 157 return false; 158 } 159 default: 160 return true; 161 } 162 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 163 LOAD_FUNC_ADDR(cUnit, r14lr, (int)funct); 164 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); 165 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3); 166 opReg(cUnit, kOpBlx, r14lr); 167 dvmCompilerClobberCallRegs(cUnit); 168 rlResult = dvmCompilerGetReturnWide(cUnit); 169 storeValueWide(cUnit, rlDest, rlResult); 170 #if defined(WITH_SELF_VERIFICATION) 171 cUnit->usesLinkRegister = true; 172 #endif 173 return false; 174 } 175 176 static bool genConversionPortable(CompilationUnit *cUnit, MIR *mir) 177 { 178 Opcode opcode = mir->dalvikInsn.opcode; 179 180 switch (opcode) { 181 case OP_INT_TO_FLOAT: 182 return genConversionCall(cUnit, mir, (void*)__aeabi_i2f, 1, 1); 183 case OP_FLOAT_TO_INT: 184 return genConversionCall(cUnit, mir, (void*)__aeabi_f2iz, 1, 1); 185 case OP_DOUBLE_TO_FLOAT: 186 return genConversionCall(cUnit, mir, (void*)__aeabi_d2f, 2, 1); 187 case OP_FLOAT_TO_DOUBLE: 188 return genConversionCall(cUnit, mir, (void*)__aeabi_f2d, 1, 2); 189 case OP_INT_TO_DOUBLE: 190 return genConversionCall(cUnit, mir, (void*)__aeabi_i2d, 1, 2); 191 case OP_DOUBLE_TO_INT: 192 return genConversionCall(cUnit, mir, (void*)__aeabi_d2iz, 2, 1); 193 case OP_FLOAT_TO_LONG: 194 return genConversionCall(cUnit, mir, (void*)dvmJitf2l, 1, 2); 195 case OP_LONG_TO_FLOAT: 196 return genConversionCall(cUnit, mir, (void*)__aeabi_l2f, 2, 1); 197 case OP_DOUBLE_TO_LONG: 198 return genConversionCall(cUnit, mir, (void*)dvmJitd2l, 2, 2); 199 case OP_LONG_TO_DOUBLE: 200 return genConversionCall(cUnit, mir, (void*)__aeabi_l2d, 2, 2); 201 default: 202 return true; 203 } 204 return false; 205 } 206 207 #if defined(WITH_SELF_VERIFICATION) 208 static void selfVerificationBranchInsert(LIR *currentLIR, ArmOpcode opcode, 209 int dest, int src1) 210 { 211 ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 212 insn->opcode = opcode; 213 insn->operands[0] = dest; 214 insn->operands[1] = src1; 215 setupResourceMasks(insn); 216 dvmCompilerInsertLIRBefore(currentLIR, (LIR *) insn); 217 } 218 219 /* 220 * Example where r14 (LR) is preserved around a heap access under 221 * self-verification mode in Thumb2: 222 * 223 * D/dalvikvm( 1538): 0x59414c5e (0026): ldr r14, [r15pc, #220] <-hoisted 224 * D/dalvikvm( 1538): 0x59414c62 (002a): mla r4, r0, r8, r4 225 * D/dalvikvm( 1538): 0x59414c66 (002e): adds r3, r4, r3 226 * D/dalvikvm( 1538): 0x59414c6a (0032): push <r5, r14> ---+ 227 * D/dalvikvm( 1538): 0x59414c6c (0034): blx_1 0x5940f494 | 228 * D/dalvikvm( 1538): 0x59414c6e (0036): blx_2 see above <-MEM_OP_DECODE 229 * D/dalvikvm( 1538): 0x59414c70 (0038): ldr r10, [r9, #0] | 230 * D/dalvikvm( 1538): 0x59414c74 (003c): pop <r5, r14> ---+ 231 * D/dalvikvm( 1538): 0x59414c78 (0040): mov r11, r10 232 * D/dalvikvm( 1538): 0x59414c7a (0042): asr r12, r11, #31 233 * D/dalvikvm( 1538): 0x59414c7e (0046): movs r0, r2 234 * D/dalvikvm( 1538): 0x59414c80 (0048): movs r1, r3 235 * D/dalvikvm( 1538): 0x59414c82 (004a): str r2, [r5, #16] 236 * D/dalvikvm( 1538): 0x59414c84 (004c): mov r2, r11 237 * D/dalvikvm( 1538): 0x59414c86 (004e): str r3, [r5, #20] 238 * D/dalvikvm( 1538): 0x59414c88 (0050): mov r3, r12 239 * D/dalvikvm( 1538): 0x59414c8a (0052): str r11, [r5, #24] 240 * D/dalvikvm( 1538): 0x59414c8e (0056): str r12, [r5, #28] 241 * D/dalvikvm( 1538): 0x59414c92 (005a): blx r14 <-use of LR 242 * 243 */ 244 static void selfVerificationBranchInsertPass(CompilationUnit *cUnit) 245 { 246 ArmLIR *thisLIR; 247 TemplateOpcode opcode = TEMPLATE_MEM_OP_DECODE; 248 249 for (thisLIR = (ArmLIR *) cUnit->firstLIRInsn; 250 thisLIR != (ArmLIR *) cUnit->lastLIRInsn; 251 thisLIR = NEXT_LIR(thisLIR)) { 252 if (!thisLIR->flags.isNop && thisLIR->flags.insertWrapper) { 253 /* 254 * Push r5(FP) and r14(LR) onto stack. We need to make sure that 255 * SP is 8-byte aligned, and we use r5 as a temp to restore LR 256 * for Thumb-only target since LR cannot be directly accessed in 257 * Thumb mode. Another reason to choose r5 here is it is the Dalvik 258 * frame pointer and cannot be the target of the emulated heap 259 * load. 260 */ 261 if (cUnit->usesLinkRegister) { 262 genSelfVerificationPreBranch(cUnit, thisLIR); 263 } 264 265 /* Branch to mem op decode template */ 266 selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx1, 267 (int) gDvmJit.codeCache + templateEntryOffsets[opcode], 268 (int) gDvmJit.codeCache + templateEntryOffsets[opcode]); 269 selfVerificationBranchInsert((LIR *) thisLIR, kThumbBlx2, 270 (int) gDvmJit.codeCache + templateEntryOffsets[opcode], 271 (int) gDvmJit.codeCache + templateEntryOffsets[opcode]); 272 273 /* Restore LR */ 274 if (cUnit->usesLinkRegister) { 275 genSelfVerificationPostBranch(cUnit, thisLIR); 276 } 277 } 278 } 279 } 280 #endif 281 282 /* Generate conditional branch instructions */ 283 static ArmLIR *genConditionalBranch(CompilationUnit *cUnit, 284 ArmConditionCode cond, 285 ArmLIR *target) 286 { 287 ArmLIR *branch = opCondBranch(cUnit, cond); 288 branch->generic.target = (LIR *) target; 289 return branch; 290 } 291 292 /* Generate a unconditional branch to go to the interpreter */ 293 static inline ArmLIR *genTrap(CompilationUnit *cUnit, int dOffset, 294 ArmLIR *pcrLabel) 295 { 296 ArmLIR *branch = opNone(cUnit, kOpUncondBr); 297 return genCheckCommon(cUnit, dOffset, branch, pcrLabel); 298 } 299 300 /* Load a wide field from an object instance */ 301 static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) 302 { 303 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 304 RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 305 RegLocation rlResult; 306 rlObj = loadValue(cUnit, rlObj, kCoreReg); 307 int regPtr = dvmCompilerAllocTemp(cUnit); 308 309 assert(rlDest.wide); 310 311 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 312 NULL);/* null object? */ 313 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); 314 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 315 316 HEAP_ACCESS_SHADOW(true); 317 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); 318 HEAP_ACCESS_SHADOW(false); 319 320 dvmCompilerFreeTemp(cUnit, regPtr); 321 storeValueWide(cUnit, rlDest, rlResult); 322 } 323 324 /* Store a wide field to an object instance */ 325 static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) 326 { 327 RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 328 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 2); 329 rlObj = loadValue(cUnit, rlObj, kCoreReg); 330 int regPtr; 331 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); 332 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 333 NULL);/* null object? */ 334 regPtr = dvmCompilerAllocTemp(cUnit); 335 opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset); 336 337 HEAP_ACCESS_SHADOW(true); 338 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg); 339 HEAP_ACCESS_SHADOW(false); 340 341 dvmCompilerFreeTemp(cUnit, regPtr); 342 } 343 344 /* 345 * Load a field from an object instance 346 * 347 */ 348 static void genIGet(CompilationUnit *cUnit, MIR *mir, OpSize size, 349 int fieldOffset, bool isVolatile) 350 { 351 RegLocation rlResult; 352 RegisterClass regClass = dvmCompilerRegClassBySize(size); 353 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 354 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 355 rlObj = loadValue(cUnit, rlObj, kCoreReg); 356 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true); 357 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 358 NULL);/* null object? */ 359 360 HEAP_ACCESS_SHADOW(true); 361 loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg, 362 size, rlObj.sRegLow); 363 HEAP_ACCESS_SHADOW(false); 364 if (isVolatile) { 365 dvmCompilerGenMemBarrier(cUnit, kSY); 366 } 367 368 storeValue(cUnit, rlDest, rlResult); 369 } 370 371 /* 372 * Store a field to an object instance 373 * 374 */ 375 static void genIPut(CompilationUnit *cUnit, MIR *mir, OpSize size, 376 int fieldOffset, bool isObject, bool isVolatile) 377 { 378 RegisterClass regClass = dvmCompilerRegClassBySize(size); 379 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 380 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 1); 381 rlObj = loadValue(cUnit, rlObj, kCoreReg); 382 rlSrc = loadValue(cUnit, rlSrc, regClass); 383 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, 384 NULL);/* null object? */ 385 386 if (isVolatile) { 387 dvmCompilerGenMemBarrier(cUnit, kST); 388 } 389 HEAP_ACCESS_SHADOW(true); 390 storeBaseDisp(cUnit, rlObj.lowReg, fieldOffset, rlSrc.lowReg, size); 391 HEAP_ACCESS_SHADOW(false); 392 if (isVolatile) { 393 dvmCompilerGenMemBarrier(cUnit, kSY); 394 } 395 if (isObject) { 396 /* NOTE: marking card based on object head */ 397 markCard(cUnit, rlSrc.lowReg, rlObj.lowReg); 398 } 399 } 400 401 402 /* 403 * Generate array load 404 */ 405 static void genArrayGet(CompilationUnit *cUnit, MIR *mir, OpSize size, 406 RegLocation rlArray, RegLocation rlIndex, 407 RegLocation rlDest, int scale) 408 { 409 RegisterClass regClass = dvmCompilerRegClassBySize(size); 410 int lenOffset = OFFSETOF_MEMBER(ArrayObject, length); 411 int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents); 412 RegLocation rlResult; 413 rlArray = loadValue(cUnit, rlArray, kCoreReg); 414 rlIndex = loadValue(cUnit, rlIndex, kCoreReg); 415 int regPtr; 416 417 /* null object? */ 418 ArmLIR * pcrLabel = NULL; 419 420 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { 421 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, 422 rlArray.lowReg, mir->offset, NULL); 423 } 424 425 regPtr = dvmCompilerAllocTemp(cUnit); 426 427 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { 428 int regLen = dvmCompilerAllocTemp(cUnit); 429 /* Get len */ 430 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); 431 /* regPtr -> array data */ 432 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); 433 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, 434 pcrLabel); 435 dvmCompilerFreeTemp(cUnit, regLen); 436 } else { 437 /* regPtr -> array data */ 438 opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset); 439 } 440 if ((size == kLong) || (size == kDouble)) { 441 if (scale) { 442 int rNewIndex = dvmCompilerAllocTemp(cUnit); 443 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); 444 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); 445 dvmCompilerFreeTemp(cUnit, rNewIndex); 446 } else { 447 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); 448 } 449 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true); 450 451 HEAP_ACCESS_SHADOW(true); 452 loadPair(cUnit, regPtr, rlResult.lowReg, rlResult.highReg); 453 HEAP_ACCESS_SHADOW(false); 454 455 dvmCompilerFreeTemp(cUnit, regPtr); 456 storeValueWide(cUnit, rlDest, rlResult); 457 } else { 458 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, regClass, true); 459 460 HEAP_ACCESS_SHADOW(true); 461 loadBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlResult.lowReg, 462 scale, size); 463 HEAP_ACCESS_SHADOW(false); 464 465 dvmCompilerFreeTemp(cUnit, regPtr); 466 storeValue(cUnit, rlDest, rlResult); 467 } 468 } 469 470 /* 471 * Generate array store 472 * 473 */ 474 static void genArrayPut(CompilationUnit *cUnit, MIR *mir, OpSize size, 475 RegLocation rlArray, RegLocation rlIndex, 476 RegLocation rlSrc, int scale) 477 { 478 RegisterClass regClass = dvmCompilerRegClassBySize(size); 479 int lenOffset = OFFSETOF_MEMBER(ArrayObject, length); 480 int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents); 481 482 int regPtr; 483 rlArray = loadValue(cUnit, rlArray, kCoreReg); 484 rlIndex = loadValue(cUnit, rlIndex, kCoreReg); 485 486 if (dvmCompilerIsTemp(cUnit, rlArray.lowReg)) { 487 dvmCompilerClobber(cUnit, rlArray.lowReg); 488 regPtr = rlArray.lowReg; 489 } else { 490 regPtr = dvmCompilerAllocTemp(cUnit); 491 genRegCopy(cUnit, regPtr, rlArray.lowReg); 492 } 493 494 /* null object? */ 495 ArmLIR * pcrLabel = NULL; 496 497 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { 498 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, 499 mir->offset, NULL); 500 } 501 502 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { 503 int regLen = dvmCompilerAllocTemp(cUnit); 504 //NOTE: max live temps(4) here. 505 /* Get len */ 506 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen); 507 /* regPtr -> array data */ 508 opRegImm(cUnit, kOpAdd, regPtr, dataOffset); 509 genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset, 510 pcrLabel); 511 dvmCompilerFreeTemp(cUnit, regLen); 512 } else { 513 /* regPtr -> array data */ 514 opRegImm(cUnit, kOpAdd, regPtr, dataOffset); 515 } 516 /* at this point, regPtr points to array, 2 live temps */ 517 if ((size == kLong) || (size == kDouble)) { 518 //TODO: need specific wide routine that can handle fp regs 519 if (scale) { 520 int rNewIndex = dvmCompilerAllocTemp(cUnit); 521 opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale); 522 opRegReg(cUnit, kOpAdd, regPtr, rNewIndex); 523 dvmCompilerFreeTemp(cUnit, rNewIndex); 524 } else { 525 opRegReg(cUnit, kOpAdd, regPtr, rlIndex.lowReg); 526 } 527 rlSrc = loadValueWide(cUnit, rlSrc, regClass); 528 529 HEAP_ACCESS_SHADOW(true); 530 storePair(cUnit, regPtr, rlSrc.lowReg, rlSrc.highReg); 531 HEAP_ACCESS_SHADOW(false); 532 533 dvmCompilerFreeTemp(cUnit, regPtr); 534 } else { 535 rlSrc = loadValue(cUnit, rlSrc, regClass); 536 537 HEAP_ACCESS_SHADOW(true); 538 storeBaseIndexed(cUnit, regPtr, rlIndex.lowReg, rlSrc.lowReg, 539 scale, size); 540 HEAP_ACCESS_SHADOW(false); 541 } 542 } 543 544 /* 545 * Generate array object store 546 * Must use explicit register allocation here because of 547 * call-out to dvmCanPutArrayElement 548 */ 549 static void genArrayObjectPut(CompilationUnit *cUnit, MIR *mir, 550 RegLocation rlArray, RegLocation rlIndex, 551 RegLocation rlSrc, int scale) 552 { 553 int lenOffset = OFFSETOF_MEMBER(ArrayObject, length); 554 int dataOffset = OFFSETOF_MEMBER(ArrayObject, contents); 555 556 dvmCompilerFlushAllRegs(cUnit); 557 558 int regLen = r0; 559 int regPtr = r4PC; /* Preserved across call */ 560 int regArray = r1; 561 int regIndex = r7; /* Preserved across call */ 562 563 loadValueDirectFixed(cUnit, rlArray, regArray); 564 loadValueDirectFixed(cUnit, rlIndex, regIndex); 565 566 /* null object? */ 567 ArmLIR * pcrLabel = NULL; 568 569 if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { 570 pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, regArray, 571 mir->offset, NULL); 572 } 573 574 if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { 575 /* Get len */ 576 loadWordDisp(cUnit, regArray, lenOffset, regLen); 577 /* regPtr -> array data */ 578 opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset); 579 genBoundsCheck(cUnit, regIndex, regLen, mir->offset, 580 pcrLabel); 581 } else { 582 /* regPtr -> array data */ 583 opRegRegImm(cUnit, kOpAdd, regPtr, regArray, dataOffset); 584 } 585 586 /* Get object to store */ 587 loadValueDirectFixed(cUnit, rlSrc, r0); 588 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmCanPutArrayElement); 589 590 /* Are we storing null? If so, avoid check */ 591 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondEq, r0, 0); 592 593 /* Make sure the types are compatible */ 594 loadWordDisp(cUnit, regArray, offsetof(Object, clazz), r1); 595 loadWordDisp(cUnit, r0, offsetof(Object, clazz), r0); 596 opReg(cUnit, kOpBlx, r2); 597 dvmCompilerClobberCallRegs(cUnit); 598 599 /* 600 * Using fixed registers here, and counting on r4 and r7 being 601 * preserved across the above call. Tell the register allocation 602 * utilities about the regs we are using directly 603 */ 604 dvmCompilerLockTemp(cUnit, regPtr); // r4PC 605 dvmCompilerLockTemp(cUnit, regIndex); // r7 606 dvmCompilerLockTemp(cUnit, r0); 607 dvmCompilerLockTemp(cUnit, r1); 608 609 /* Bad? - roll back and re-execute if so */ 610 genRegImmCheck(cUnit, kArmCondEq, r0, 0, mir->offset, pcrLabel); 611 612 /* Resume here - must reload element & array, regPtr & index preserved */ 613 loadValueDirectFixed(cUnit, rlSrc, r0); 614 loadValueDirectFixed(cUnit, rlArray, r1); 615 616 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 617 target->defMask = ENCODE_ALL; 618 branchOver->generic.target = (LIR *) target; 619 620 HEAP_ACCESS_SHADOW(true); 621 storeBaseIndexed(cUnit, regPtr, regIndex, r0, 622 scale, kWord); 623 HEAP_ACCESS_SHADOW(false); 624 625 dvmCompilerFreeTemp(cUnit, regPtr); 626 dvmCompilerFreeTemp(cUnit, regIndex); 627 628 /* NOTE: marking card here based on object head */ 629 markCard(cUnit, r0, r1); 630 } 631 632 static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, 633 RegLocation rlDest, RegLocation rlSrc1, 634 RegLocation rlShift) 635 { 636 /* 637 * Don't mess with the regsiters here as there is a particular calling 638 * convention to the out-of-line handler. 639 */ 640 RegLocation rlResult; 641 642 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); 643 loadValueDirect(cUnit, rlShift, r2); 644 switch( mir->dalvikInsn.opcode) { 645 case OP_SHL_LONG: 646 case OP_SHL_LONG_2ADDR: 647 genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG); 648 break; 649 case OP_SHR_LONG: 650 case OP_SHR_LONG_2ADDR: 651 genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG); 652 break; 653 case OP_USHR_LONG: 654 case OP_USHR_LONG_2ADDR: 655 genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG); 656 break; 657 default: 658 return true; 659 } 660 rlResult = dvmCompilerGetReturnWide(cUnit); 661 storeValueWide(cUnit, rlDest, rlResult); 662 return false; 663 } 664 665 static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir, 666 RegLocation rlDest, RegLocation rlSrc1, 667 RegLocation rlSrc2) 668 { 669 RegLocation rlResult; 670 OpKind firstOp = kOpBkpt; 671 OpKind secondOp = kOpBkpt; 672 bool callOut = false; 673 bool checkZero = false; 674 void *callTgt; 675 int retReg = r0; 676 677 switch (mir->dalvikInsn.opcode) { 678 case OP_NOT_LONG: 679 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); 680 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 681 opRegReg(cUnit, kOpMvn, rlResult.lowReg, rlSrc2.lowReg); 682 opRegReg(cUnit, kOpMvn, rlResult.highReg, rlSrc2.highReg); 683 storeValueWide(cUnit, rlDest, rlResult); 684 return false; 685 break; 686 case OP_ADD_LONG: 687 case OP_ADD_LONG_2ADDR: 688 firstOp = kOpAdd; 689 secondOp = kOpAdc; 690 break; 691 case OP_SUB_LONG: 692 case OP_SUB_LONG_2ADDR: 693 firstOp = kOpSub; 694 secondOp = kOpSbc; 695 break; 696 case OP_MUL_LONG: 697 case OP_MUL_LONG_2ADDR: 698 genMulLong(cUnit, rlDest, rlSrc1, rlSrc2); 699 return false; 700 case OP_DIV_LONG: 701 case OP_DIV_LONG_2ADDR: 702 callOut = true; 703 retReg = r0; 704 checkZero = true; 705 callTgt = (void*)__aeabi_ldivmod; 706 break; 707 /* NOTE - result is in r2/r3 instead of r0/r1 */ 708 case OP_REM_LONG: 709 case OP_REM_LONG_2ADDR: 710 callOut = true; 711 callTgt = (void*)__aeabi_ldivmod; 712 retReg = r2; 713 checkZero = true; 714 break; 715 case OP_AND_LONG_2ADDR: 716 case OP_AND_LONG: 717 firstOp = kOpAnd; 718 secondOp = kOpAnd; 719 break; 720 case OP_OR_LONG: 721 case OP_OR_LONG_2ADDR: 722 firstOp = kOpOr; 723 secondOp = kOpOr; 724 break; 725 case OP_XOR_LONG: 726 case OP_XOR_LONG_2ADDR: 727 firstOp = kOpXor; 728 secondOp = kOpXor; 729 break; 730 case OP_NEG_LONG: { 731 //TUNING: can improve this using Thumb2 code 732 int tReg = dvmCompilerAllocTemp(cUnit); 733 rlSrc2 = loadValueWide(cUnit, rlSrc2, kCoreReg); 734 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 735 loadConstantNoClobber(cUnit, tReg, 0); 736 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, 737 tReg, rlSrc2.lowReg); 738 opRegReg(cUnit, kOpSbc, tReg, rlSrc2.highReg); 739 genRegCopy(cUnit, rlResult.highReg, tReg); 740 storeValueWide(cUnit, rlDest, rlResult); 741 return false; 742 } 743 default: 744 ALOGE("Invalid long arith op"); 745 dvmCompilerAbort(cUnit); 746 } 747 if (!callOut) { 748 genLong3Addr(cUnit, mir, firstOp, secondOp, rlDest, rlSrc1, rlSrc2); 749 } else { 750 // Adjust return regs in to handle case of rem returning r2/r3 751 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 752 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3); 753 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1); 754 LOAD_FUNC_ADDR(cUnit, r14lr, (int) callTgt); 755 if (checkZero) { 756 int tReg = r12; // Using fixed registers during call sequence 757 opRegRegReg(cUnit, kOpOr, tReg, r2, r3); 758 genRegImmCheck(cUnit, kArmCondEq, tReg, 0, mir->offset, NULL); 759 } 760 opReg(cUnit, kOpBlx, r14lr); 761 dvmCompilerClobberCallRegs(cUnit); 762 if (retReg == r0) 763 rlResult = dvmCompilerGetReturnWide(cUnit); 764 else 765 rlResult = dvmCompilerGetReturnWideAlt(cUnit); 766 storeValueWide(cUnit, rlDest, rlResult); 767 #if defined(WITH_SELF_VERIFICATION) 768 cUnit->usesLinkRegister = true; 769 #endif 770 } 771 return false; 772 } 773 774 static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir, 775 RegLocation rlDest, RegLocation rlSrc1, 776 RegLocation rlSrc2) 777 { 778 OpKind op = kOpBkpt; 779 bool callOut = false; 780 bool checkZero = false; 781 bool unary = false; 782 int retReg = r0; 783 int (*callTgt)(int, int); 784 RegLocation rlResult; 785 bool shiftOp = false; 786 787 switch (mir->dalvikInsn.opcode) { 788 case OP_NEG_INT: 789 op = kOpNeg; 790 unary = true; 791 break; 792 case OP_NOT_INT: 793 op = kOpMvn; 794 unary = true; 795 break; 796 case OP_ADD_INT: 797 case OP_ADD_INT_2ADDR: 798 op = kOpAdd; 799 break; 800 case OP_SUB_INT: 801 case OP_SUB_INT_2ADDR: 802 op = kOpSub; 803 break; 804 case OP_MUL_INT: 805 case OP_MUL_INT_2ADDR: 806 op = kOpMul; 807 break; 808 case OP_DIV_INT: 809 case OP_DIV_INT_2ADDR: 810 callOut = true; 811 checkZero = true; 812 callTgt = __aeabi_idiv; 813 retReg = r0; 814 break; 815 /* NOTE: returns in r1 */ 816 case OP_REM_INT: 817 case OP_REM_INT_2ADDR: 818 callOut = true; 819 checkZero = true; 820 callTgt = __aeabi_idivmod; 821 retReg = r1; 822 break; 823 case OP_AND_INT: 824 case OP_AND_INT_2ADDR: 825 op = kOpAnd; 826 break; 827 case OP_OR_INT: 828 case OP_OR_INT_2ADDR: 829 op = kOpOr; 830 break; 831 case OP_XOR_INT: 832 case OP_XOR_INT_2ADDR: 833 op = kOpXor; 834 break; 835 case OP_SHL_INT: 836 case OP_SHL_INT_2ADDR: 837 shiftOp = true; 838 op = kOpLsl; 839 break; 840 case OP_SHR_INT: 841 case OP_SHR_INT_2ADDR: 842 shiftOp = true; 843 op = kOpAsr; 844 break; 845 case OP_USHR_INT: 846 case OP_USHR_INT_2ADDR: 847 shiftOp = true; 848 op = kOpLsr; 849 break; 850 default: 851 ALOGE("Invalid word arith op: %#x(%d)", 852 mir->dalvikInsn.opcode, mir->dalvikInsn.opcode); 853 dvmCompilerAbort(cUnit); 854 } 855 if (!callOut) { 856 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); 857 if (unary) { 858 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 859 opRegReg(cUnit, op, rlResult.lowReg, 860 rlSrc1.lowReg); 861 } else { 862 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); 863 if (shiftOp) { 864 int tReg = dvmCompilerAllocTemp(cUnit); 865 opRegRegImm(cUnit, kOpAnd, tReg, rlSrc2.lowReg, 31); 866 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 867 opRegRegReg(cUnit, op, rlResult.lowReg, 868 rlSrc1.lowReg, tReg); 869 dvmCompilerFreeTemp(cUnit, tReg); 870 } else { 871 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 872 opRegRegReg(cUnit, op, rlResult.lowReg, 873 rlSrc1.lowReg, rlSrc2.lowReg); 874 } 875 } 876 storeValue(cUnit, rlDest, rlResult); 877 } else { 878 RegLocation rlResult; 879 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 880 loadValueDirectFixed(cUnit, rlSrc2, r1); 881 LOAD_FUNC_ADDR(cUnit, r2, (int) callTgt); 882 loadValueDirectFixed(cUnit, rlSrc1, r0); 883 if (checkZero) { 884 genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL); 885 } 886 opReg(cUnit, kOpBlx, r2); 887 dvmCompilerClobberCallRegs(cUnit); 888 if (retReg == r0) 889 rlResult = dvmCompilerGetReturn(cUnit); 890 else 891 rlResult = dvmCompilerGetReturnAlt(cUnit); 892 storeValue(cUnit, rlDest, rlResult); 893 } 894 return false; 895 } 896 897 static bool genArithOp(CompilationUnit *cUnit, MIR *mir) 898 { 899 Opcode opcode = mir->dalvikInsn.opcode; 900 RegLocation rlDest; 901 RegLocation rlSrc1; 902 RegLocation rlSrc2; 903 /* Deduce sizes of operands */ 904 if (mir->ssaRep->numUses == 2) { 905 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0); 906 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1); 907 } else if (mir->ssaRep->numUses == 3) { 908 rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 909 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2); 910 } else { 911 rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 912 rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3); 913 assert(mir->ssaRep->numUses == 4); 914 } 915 if (mir->ssaRep->numDefs == 1) { 916 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 917 } else { 918 assert(mir->ssaRep->numDefs == 2); 919 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 920 } 921 922 if ((opcode >= OP_ADD_LONG_2ADDR) && (opcode <= OP_XOR_LONG_2ADDR)) { 923 return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 924 } 925 if ((opcode >= OP_ADD_LONG) && (opcode <= OP_XOR_LONG)) { 926 return genArithOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 927 } 928 if ((opcode >= OP_SHL_LONG_2ADDR) && (opcode <= OP_USHR_LONG_2ADDR)) { 929 return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 930 } 931 if ((opcode >= OP_SHL_LONG) && (opcode <= OP_USHR_LONG)) { 932 return genShiftOpLong(cUnit,mir, rlDest, rlSrc1, rlSrc2); 933 } 934 if ((opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_USHR_INT_2ADDR)) { 935 return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2); 936 } 937 if ((opcode >= OP_ADD_INT) && (opcode <= OP_USHR_INT)) { 938 return genArithOpInt(cUnit,mir, rlDest, rlSrc1, rlSrc2); 939 } 940 if ((opcode >= OP_ADD_FLOAT_2ADDR) && (opcode <= OP_REM_FLOAT_2ADDR)) { 941 return genArithOpFloat(cUnit,mir, rlDest, rlSrc1, rlSrc2); 942 } 943 if ((opcode >= OP_ADD_FLOAT) && (opcode <= OP_REM_FLOAT)) { 944 return genArithOpFloat(cUnit, mir, rlDest, rlSrc1, rlSrc2); 945 } 946 if ((opcode >= OP_ADD_DOUBLE_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) { 947 return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2); 948 } 949 if ((opcode >= OP_ADD_DOUBLE) && (opcode <= OP_REM_DOUBLE)) { 950 return genArithOpDouble(cUnit,mir, rlDest, rlSrc1, rlSrc2); 951 } 952 return true; 953 } 954 955 /* Generate unconditional branch instructions */ 956 static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target) 957 { 958 ArmLIR *branch = opNone(cUnit, kOpUncondBr); 959 branch->generic.target = (LIR *) target; 960 return branch; 961 } 962 963 /* Perform the actual operation for OP_RETURN_* */ 964 static void genReturnCommon(CompilationUnit *cUnit, MIR *mir) 965 { 966 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 967 TEMPLATE_RETURN_PROF : TEMPLATE_RETURN); 968 #if defined(WITH_JIT_TUNING) 969 gDvmJit.returnOp++; 970 #endif 971 int dPC = (int) (cUnit->method->insns + mir->offset); 972 /* Insert branch, but defer setting of target */ 973 ArmLIR *branch = genUnconditionalBranch(cUnit, NULL); 974 /* Set up the place holder to reconstruct this Dalvik PC */ 975 ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 976 pcrLabel->opcode = kArmPseudoPCReconstructionCell; 977 pcrLabel->operands[0] = dPC; 978 pcrLabel->operands[1] = mir->offset; 979 /* Insert the place holder to the growable list */ 980 dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel); 981 /* Branch to the PC reconstruction code */ 982 branch->generic.target = (LIR *) pcrLabel; 983 } 984 985 static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir, 986 DecodedInstruction *dInsn, 987 ArmLIR **pcrLabel) 988 { 989 unsigned int i; 990 unsigned int regMask = 0; 991 RegLocation rlArg; 992 int numDone = 0; 993 994 /* 995 * Load arguments to r0..r4. Note that these registers may contain 996 * live values, so we clobber them immediately after loading to prevent 997 * them from being used as sources for subsequent loads. 998 */ 999 dvmCompilerLockAllTemps(cUnit); 1000 for (i = 0; i < dInsn->vA; i++) { 1001 regMask |= 1 << i; 1002 rlArg = dvmCompilerGetSrc(cUnit, mir, numDone++); 1003 loadValueDirectFixed(cUnit, rlArg, i); 1004 } 1005 if (regMask) { 1006 /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ 1007 opRegRegImm(cUnit, kOpSub, r7, r5FP, 1008 sizeof(StackSaveArea) + (dInsn->vA << 2)); 1009 /* generate null check */ 1010 if (pcrLabel) { 1011 *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0, 1012 mir->offset, NULL); 1013 } 1014 storeMultiple(cUnit, r7, regMask); 1015 } 1016 } 1017 1018 static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir, 1019 DecodedInstruction *dInsn, 1020 ArmLIR **pcrLabel) 1021 { 1022 int srcOffset = dInsn->vC << 2; 1023 int numArgs = dInsn->vA; 1024 int regMask; 1025 1026 /* 1027 * Note: here, all promoted registers will have been flushed 1028 * back to the Dalvik base locations, so register usage restrictins 1029 * are lifted. All parms loaded from original Dalvik register 1030 * region - even though some might conceivably have valid copies 1031 * cached in a preserved register. 1032 */ 1033 dvmCompilerLockAllTemps(cUnit); 1034 1035 /* 1036 * r4PC : &r5FP[vC] 1037 * r7: &newFP[0] 1038 */ 1039 opRegRegImm(cUnit, kOpAdd, r4PC, r5FP, srcOffset); 1040 /* load [r0 .. min(numArgs,4)] */ 1041 regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1; 1042 /* 1043 * Protect the loadMultiple instruction from being reordered with other 1044 * Dalvik stack accesses. 1045 */ 1046 if (numArgs != 0) loadMultiple(cUnit, r4PC, regMask); 1047 1048 opRegRegImm(cUnit, kOpSub, r7, r5FP, 1049 sizeof(StackSaveArea) + (numArgs << 2)); 1050 /* generate null check */ 1051 if (pcrLabel) { 1052 *pcrLabel = genNullCheck(cUnit, dvmCompilerSSASrc(mir, 0), r0, 1053 mir->offset, NULL); 1054 } 1055 1056 /* 1057 * Handle remaining 4n arguments: 1058 * store previously loaded 4 values and load the next 4 values 1059 */ 1060 if (numArgs >= 8) { 1061 ArmLIR *loopLabel = NULL; 1062 /* 1063 * r0 contains "this" and it will be used later, so push it to the stack 1064 * first. Pushing r5FP is just for stack alignment purposes. 1065 */ 1066 opImm(cUnit, kOpPush, (1 << r0 | 1 << r5FP)); 1067 /* No need to generate the loop structure if numArgs <= 11 */ 1068 if (numArgs > 11) { 1069 loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2); 1070 loopLabel = newLIR0(cUnit, kArmPseudoTargetLabel); 1071 loopLabel->defMask = ENCODE_ALL; 1072 } 1073 storeMultiple(cUnit, r7, regMask); 1074 /* 1075 * Protect the loadMultiple instruction from being reordered with other 1076 * Dalvik stack accesses. 1077 */ 1078 loadMultiple(cUnit, r4PC, regMask); 1079 /* No need to generate the loop structure if numArgs <= 11 */ 1080 if (numArgs > 11) { 1081 opRegImm(cUnit, kOpSub, r5FP, 4); 1082 genConditionalBranch(cUnit, kArmCondNe, loopLabel); 1083 } 1084 } 1085 1086 /* Save the last batch of loaded values */ 1087 if (numArgs != 0) storeMultiple(cUnit, r7, regMask); 1088 1089 /* Generate the loop epilogue - don't use r0 */ 1090 if ((numArgs > 4) && (numArgs % 4)) { 1091 regMask = ((1 << (numArgs & 0x3)) - 1) << 1; 1092 /* 1093 * Protect the loadMultiple instruction from being reordered with other 1094 * Dalvik stack accesses. 1095 */ 1096 loadMultiple(cUnit, r4PC, regMask); 1097 } 1098 if (numArgs >= 8) 1099 opImm(cUnit, kOpPop, (1 << r0 | 1 << r5FP)); 1100 1101 /* Save the modulo 4 arguments */ 1102 if ((numArgs > 4) && (numArgs % 4)) { 1103 storeMultiple(cUnit, r7, regMask); 1104 } 1105 } 1106 1107 /* 1108 * Generate code to setup the call stack then jump to the chaining cell if it 1109 * is not a native method. 1110 */ 1111 static void genInvokeSingletonCommon(CompilationUnit *cUnit, MIR *mir, 1112 BasicBlock *bb, ArmLIR *labelList, 1113 ArmLIR *pcrLabel, 1114 const Method *calleeMethod) 1115 { 1116 /* 1117 * Note: all Dalvik register state should be flushed to 1118 * memory by the point, so register usage restrictions no 1119 * longer apply. All temp & preserved registers may be used. 1120 */ 1121 dvmCompilerLockAllTemps(cUnit); 1122 ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; 1123 1124 /* r1 = &retChainingCell */ 1125 ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0); 1126 1127 /* r4PC = dalvikCallsite */ 1128 loadConstant(cUnit, r4PC, 1129 (int) (cUnit->method->insns + mir->offset)); 1130 addrRetChain->generic.target = (LIR *) retChainingCell; 1131 1132 /* r7 = calleeMethod->registersSize */ 1133 loadConstant(cUnit, r7, calleeMethod->registersSize); 1134 /* 1135 * r0 = calleeMethod (loaded upon calling genInvokeSingletonCommon) 1136 * r1 = &ChainingCell 1137 * r2 = calleeMethod->outsSize (to be loaded later for Java callees) 1138 * r4PC = callsiteDPC 1139 * r7 = calleeMethod->registersSize 1140 */ 1141 if (dvmIsNativeMethod(calleeMethod)) { 1142 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 1143 TEMPLATE_INVOKE_METHOD_NATIVE_PROF : 1144 TEMPLATE_INVOKE_METHOD_NATIVE); 1145 #if defined(WITH_JIT_TUNING) 1146 gDvmJit.invokeNative++; 1147 #endif 1148 } else { 1149 /* For Java callees, set up r2 to be calleeMethod->outsSize */ 1150 loadConstant(cUnit, r2, calleeMethod->outsSize); 1151 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 1152 TEMPLATE_INVOKE_METHOD_CHAIN_PROF : 1153 TEMPLATE_INVOKE_METHOD_CHAIN); 1154 #if defined(WITH_JIT_TUNING) 1155 gDvmJit.invokeMonomorphic++; 1156 #endif 1157 /* Branch to the chaining cell */ 1158 genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); 1159 } 1160 /* Handle exceptions using the interpreter */ 1161 genTrap(cUnit, mir->offset, pcrLabel); 1162 } 1163 1164 /* 1165 * Generate code to check the validity of a predicted chain and take actions 1166 * based on the result. 1167 * 1168 * 0x426a99aa : ldr r4, [pc, #72] --> r4 <- dalvikPC of this invoke 1169 * 0x426a99ac : add r1, pc, #32 --> r1 <- &retChainingCell 1170 * 0x426a99ae : add r2, pc, #40 --> r2 <- &predictedChainingCell 1171 * 0x426a99b0 : blx_1 0x426a918c --+ TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN 1172 * 0x426a99b2 : blx_2 see above --+ 1173 * 0x426a99b4 : b 0x426a99d8 --> off to the predicted chain 1174 * 0x426a99b6 : b 0x426a99c8 --> punt to the interpreter 1175 * 0x426a99b8 : ldr r0, [r7, #44] --> r0 <- this->class->vtable[methodIdx] 1176 * 0x426a99ba : cmp r1, #0 --> compare r1 (rechain count) against 0 1177 * 0x426a99bc : bgt 0x426a99c2 --> >=0? don't rechain 1178 * 0x426a99be : ldr r7, [pc, #off]--+ dvmJitToPatchPredictedChain 1179 * 0x426a99c0 : blx r7 --+ 1180 * 0x426a99c2 : add r1, pc, #12 --> r1 <- &retChainingCell 1181 * 0x426a99c4 : blx_1 0x426a9098 --+ TEMPLATE_INVOKE_METHOD_NO_OPT 1182 * 0x426a99c6 : blx_2 see above --+ 1183 */ 1184 static void genInvokeVirtualCommon(CompilationUnit *cUnit, MIR *mir, 1185 int methodIndex, 1186 ArmLIR *retChainingCell, 1187 ArmLIR *predChainingCell, 1188 ArmLIR *pcrLabel) 1189 { 1190 /* 1191 * Note: all Dalvik register state should be flushed to 1192 * memory by the point, so register usage restrictions no 1193 * longer apply. Lock temps to prevent them from being 1194 * allocated by utility routines. 1195 */ 1196 dvmCompilerLockAllTemps(cUnit); 1197 1198 /* 1199 * For verbose printing, store the method pointer in operands[1] first as 1200 * operands[0] will be clobbered in dvmCompilerMIR2LIR. 1201 */ 1202 predChainingCell->operands[1] = (int) mir->meta.callsiteInfo->method; 1203 1204 /* "this" is already left in r0 by genProcessArgs* */ 1205 1206 /* r4PC = dalvikCallsite */ 1207 loadConstant(cUnit, r4PC, 1208 (int) (cUnit->method->insns + mir->offset)); 1209 1210 /* r1 = &retChainingCell */ 1211 ArmLIR *addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0); 1212 addrRetChain->generic.target = (LIR *) retChainingCell; 1213 1214 /* r2 = &predictedChainingCell */ 1215 ArmLIR *predictedChainingCell = opRegRegImm(cUnit, kOpAdd, r2, r15pc, 0); 1216 predictedChainingCell->generic.target = (LIR *) predChainingCell; 1217 1218 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 1219 TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF : 1220 TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN); 1221 1222 /* return through lr - jump to the chaining cell */ 1223 genUnconditionalBranch(cUnit, predChainingCell); 1224 1225 /* 1226 * null-check on "this" may have been eliminated, but we still need a PC- 1227 * reconstruction label for stack overflow bailout. 1228 */ 1229 if (pcrLabel == NULL) { 1230 int dPC = (int) (cUnit->method->insns + mir->offset); 1231 pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 1232 pcrLabel->opcode = kArmPseudoPCReconstructionCell; 1233 pcrLabel->operands[0] = dPC; 1234 pcrLabel->operands[1] = mir->offset; 1235 /* Insert the place holder to the growable list */ 1236 dvmInsertGrowableList(&cUnit->pcReconstructionList, 1237 (intptr_t) pcrLabel); 1238 } 1239 1240 /* return through lr+2 - punt to the interpreter */ 1241 genUnconditionalBranch(cUnit, pcrLabel); 1242 1243 /* 1244 * return through lr+4 - fully resolve the callee method. 1245 * r1 <- count 1246 * r2 <- &predictedChainCell 1247 * r3 <- this->class 1248 * r4 <- dPC 1249 * r7 <- this->class->vtable 1250 */ 1251 1252 /* r0 <- calleeMethod */ 1253 loadWordDisp(cUnit, r7, methodIndex * 4, r0); 1254 1255 /* Check if rechain limit is reached */ 1256 ArmLIR *bypassRechaining = genCmpImmBranch(cUnit, kArmCondGt, r1, 0); 1257 1258 LOAD_FUNC_ADDR(cUnit, r7, (int) dvmJitToPatchPredictedChain); 1259 1260 genRegCopy(cUnit, r1, r6SELF); 1261 1262 /* 1263 * r0 = calleeMethod 1264 * r2 = &predictedChainingCell 1265 * r3 = class 1266 * 1267 * &returnChainingCell has been loaded into r1 but is not needed 1268 * when patching the chaining cell and will be clobbered upon 1269 * returning so it will be reconstructed again. 1270 */ 1271 opReg(cUnit, kOpBlx, r7); 1272 1273 /* r1 = &retChainingCell */ 1274 addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0); 1275 addrRetChain->generic.target = (LIR *) retChainingCell; 1276 1277 bypassRechaining->generic.target = (LIR *) addrRetChain; 1278 /* 1279 * r0 = calleeMethod, 1280 * r1 = &ChainingCell, 1281 * r4PC = callsiteDPC, 1282 */ 1283 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 1284 TEMPLATE_INVOKE_METHOD_NO_OPT_PROF : 1285 TEMPLATE_INVOKE_METHOD_NO_OPT); 1286 #if defined(WITH_JIT_TUNING) 1287 gDvmJit.invokePolymorphic++; 1288 #endif 1289 /* Handle exceptions using the interpreter */ 1290 genTrap(cUnit, mir->offset, pcrLabel); 1291 } 1292 1293 /* "this" pointer is already in r0 */ 1294 static void genInvokeVirtualWholeMethod(CompilationUnit *cUnit, 1295 MIR *mir, 1296 void *calleeAddr, 1297 ArmLIR *retChainingCell) 1298 { 1299 CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo; 1300 dvmCompilerLockAllTemps(cUnit); 1301 1302 loadClassPointer(cUnit, r1, (int) callsiteInfo); 1303 1304 loadWordDisp(cUnit, r0, offsetof(Object, clazz), r2); 1305 /* Branch to the slow path if classes are not equal */ 1306 opRegReg(cUnit, kOpCmp, r1, r2); 1307 /* 1308 * Set the misPredBranchOver target so that it will be generated when the 1309 * code for the non-optimized invoke is generated. 1310 */ 1311 ArmLIR *classCheck = opCondBranch(cUnit, kArmCondNe); 1312 1313 /* r0 = the Dalvik PC of the callsite */ 1314 loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); 1315 1316 newLIR2(cUnit, kThumbBl1, (int) calleeAddr, (int) calleeAddr); 1317 newLIR2(cUnit, kThumbBl2, (int) calleeAddr, (int) calleeAddr); 1318 genUnconditionalBranch(cUnit, retChainingCell); 1319 1320 /* Target of slow path */ 1321 ArmLIR *slowPathLabel = newLIR0(cUnit, kArmPseudoTargetLabel); 1322 1323 slowPathLabel->defMask = ENCODE_ALL; 1324 classCheck->generic.target = (LIR *) slowPathLabel; 1325 1326 // FIXME 1327 cUnit->printMe = true; 1328 } 1329 1330 static void genInvokeSingletonWholeMethod(CompilationUnit *cUnit, 1331 MIR *mir, 1332 void *calleeAddr, 1333 ArmLIR *retChainingCell) 1334 { 1335 /* r0 = the Dalvik PC of the callsite */ 1336 loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); 1337 1338 newLIR2(cUnit, kThumbBl1, (int) calleeAddr, (int) calleeAddr); 1339 newLIR2(cUnit, kThumbBl2, (int) calleeAddr, (int) calleeAddr); 1340 genUnconditionalBranch(cUnit, retChainingCell); 1341 1342 // FIXME 1343 cUnit->printMe = true; 1344 } 1345 1346 /* Geneate a branch to go back to the interpreter */ 1347 static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset) 1348 { 1349 /* r0 = dalvik pc */ 1350 dvmCompilerFlushAllRegs(cUnit); 1351 loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset)); 1352 loadWordDisp(cUnit, r6SELF, offsetof(Thread, 1353 jitToInterpEntries.dvmJitToInterpPunt), r1); 1354 opReg(cUnit, kOpBlx, r1); 1355 } 1356 1357 /* 1358 * Attempt to single step one instruction using the interpreter and return 1359 * to the compiled code for the next Dalvik instruction 1360 */ 1361 static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir) 1362 { 1363 int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode); 1364 int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn; 1365 1366 // Single stepping is considered loop mode breaker 1367 if (cUnit->jitMode == kJitLoop) { 1368 cUnit->quitLoopMode = true; 1369 return; 1370 } 1371 1372 //If already optimized out, just ignore 1373 if (mir->dalvikInsn.opcode == OP_NOP) 1374 return; 1375 1376 //Ugly, but necessary. Flush all Dalvik regs so Interp can find them 1377 dvmCompilerFlushAllRegs(cUnit); 1378 1379 if ((mir->next == NULL) || (flags & flagsToCheck)) { 1380 genPuntToInterp(cUnit, mir->offset); 1381 return; 1382 } 1383 int entryAddr = offsetof(Thread, 1384 jitToInterpEntries.dvmJitToInterpSingleStep); 1385 loadWordDisp(cUnit, r6SELF, entryAddr, r2); 1386 /* r0 = dalvik pc */ 1387 loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); 1388 /* r1 = dalvik pc of following instruction */ 1389 loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset)); 1390 opReg(cUnit, kOpBlx, r2); 1391 } 1392 1393 #if defined(_ARMV5TE) || defined(_ARMV5TE_VFP) 1394 /* 1395 * To prevent a thread in a monitor wait from blocking the Jit from 1396 * resetting the code cache, heavyweight monitor lock will not 1397 * be allowed to return to an existing translation. Instead, we will 1398 * handle them by branching to a handler, which will in turn call the 1399 * runtime lock routine and then branch directly back to the 1400 * interpreter main loop. Given the high cost of the heavyweight 1401 * lock operation, this additional cost should be slight (especially when 1402 * considering that we expect the vast majority of lock operations to 1403 * use the fast-path thin lock bypass). 1404 */ 1405 static void genMonitorPortable(CompilationUnit *cUnit, MIR *mir) 1406 { 1407 bool isEnter = (mir->dalvikInsn.opcode == OP_MONITOR_ENTER); 1408 genExportPC(cUnit, mir); 1409 dvmCompilerFlushAllRegs(cUnit); /* Send everything to home location */ 1410 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1411 loadValueDirectFixed(cUnit, rlSrc, r1); 1412 genRegCopy(cUnit, r0, r6SELF); 1413 genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL); 1414 if (isEnter) { 1415 /* Get dPC of next insn */ 1416 loadConstant(cUnit, r4PC, (int)(cUnit->method->insns + mir->offset + 1417 dexGetWidthFromOpcode(OP_MONITOR_ENTER))); 1418 genDispatchToHandler(cUnit, TEMPLATE_MONITOR_ENTER); 1419 } else { 1420 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmUnlockObject); 1421 /* Do the call */ 1422 opReg(cUnit, kOpBlx, r2); 1423 /* Did we throw? */ 1424 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0); 1425 loadConstant(cUnit, r0, 1426 (int) (cUnit->method->insns + mir->offset + 1427 dexGetWidthFromOpcode(OP_MONITOR_EXIT))); 1428 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 1429 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 1430 target->defMask = ENCODE_ALL; 1431 branchOver->generic.target = (LIR *) target; 1432 dvmCompilerClobberCallRegs(cUnit); 1433 } 1434 } 1435 #endif 1436 1437 /* 1438 * Fetch *self->info.breakFlags. If the breakFlags are non-zero, 1439 * punt to the interpreter. 1440 */ 1441 static void genSuspendPoll(CompilationUnit *cUnit, MIR *mir) 1442 { 1443 int rTemp = dvmCompilerAllocTemp(cUnit); 1444 ArmLIR *ld; 1445 ld = loadBaseDisp(cUnit, NULL, r6SELF, 1446 offsetof(Thread, interpBreak.ctl.breakFlags), 1447 rTemp, kUnsignedByte, INVALID_SREG); 1448 setMemRefType(ld, true /* isLoad */, kMustNotAlias); 1449 genRegImmCheck(cUnit, kArmCondNe, rTemp, 0, mir->offset, NULL); 1450 } 1451 1452 /* 1453 * The following are the first-level codegen routines that analyze the format 1454 * of each bytecode then either dispatch special purpose codegen routines 1455 * or produce corresponding Thumb instructions directly. 1456 */ 1457 1458 static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir, 1459 BasicBlock *bb, ArmLIR *labelList) 1460 { 1461 /* backward branch? */ 1462 bool backwardBranch = (bb->taken->startOffset <= mir->offset); 1463 1464 if (backwardBranch && 1465 (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) { 1466 genSuspendPoll(cUnit, mir); 1467 } 1468 1469 int numPredecessors = dvmCountSetBits(bb->taken->predecessors); 1470 /* 1471 * Things could be hoisted out of the taken block into the predecessor, so 1472 * make sure it is dominated by the predecessor. 1473 */ 1474 if (numPredecessors == 1 && bb->taken->visited == false && 1475 bb->taken->blockType == kDalvikByteCode) { 1476 cUnit->nextCodegenBlock = bb->taken; 1477 } else { 1478 /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */ 1479 genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); 1480 } 1481 return false; 1482 } 1483 1484 static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir) 1485 { 1486 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 1487 if ((dalvikOpcode >= OP_UNUSED_3E) && (dalvikOpcode <= OP_UNUSED_43)) { 1488 ALOGE("Codegen: got unused opcode %#x",dalvikOpcode); 1489 return true; 1490 } 1491 switch (dalvikOpcode) { 1492 case OP_RETURN_VOID_BARRIER: 1493 dvmCompilerGenMemBarrier(cUnit, kST); 1494 // Intentional fallthrough 1495 case OP_RETURN_VOID: 1496 genReturnCommon(cUnit,mir); 1497 break; 1498 case OP_UNUSED_73: 1499 case OP_UNUSED_79: 1500 case OP_UNUSED_7A: 1501 case OP_UNUSED_FF: 1502 ALOGE("Codegen: got unused opcode %#x",dalvikOpcode); 1503 return true; 1504 case OP_NOP: 1505 break; 1506 default: 1507 return true; 1508 } 1509 return false; 1510 } 1511 1512 static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir) 1513 { 1514 RegLocation rlDest; 1515 RegLocation rlResult; 1516 if (mir->ssaRep->numDefs == 2) { 1517 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1518 } else { 1519 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1520 } 1521 1522 switch (mir->dalvikInsn.opcode) { 1523 case OP_CONST: 1524 case OP_CONST_4: { 1525 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1526 loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB); 1527 storeValue(cUnit, rlDest, rlResult); 1528 break; 1529 } 1530 case OP_CONST_WIDE_32: { 1531 //TUNING: single routine to load constant pair for support doubles 1532 //TUNING: load 0/-1 separately to avoid load dependency 1533 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1534 loadConstantNoClobber(cUnit, rlResult.lowReg, mir->dalvikInsn.vB); 1535 opRegRegImm(cUnit, kOpAsr, rlResult.highReg, 1536 rlResult.lowReg, 31); 1537 storeValueWide(cUnit, rlDest, rlResult); 1538 break; 1539 } 1540 default: 1541 return true; 1542 } 1543 return false; 1544 } 1545 1546 static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir) 1547 { 1548 RegLocation rlDest; 1549 RegLocation rlResult; 1550 if (mir->ssaRep->numDefs == 2) { 1551 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1552 } else { 1553 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1554 } 1555 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1556 1557 switch (mir->dalvikInsn.opcode) { 1558 case OP_CONST_HIGH16: { 1559 loadConstantNoClobber(cUnit, rlResult.lowReg, 1560 mir->dalvikInsn.vB << 16); 1561 storeValue(cUnit, rlDest, rlResult); 1562 break; 1563 } 1564 case OP_CONST_WIDE_HIGH16: { 1565 loadConstantValueWide(cUnit, rlResult.lowReg, rlResult.highReg, 1566 0, mir->dalvikInsn.vB << 16); 1567 storeValueWide(cUnit, rlDest, rlResult); 1568 break; 1569 } 1570 default: 1571 return true; 1572 } 1573 return false; 1574 } 1575 1576 static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir) 1577 { 1578 /* For OP_THROW_VERIFICATION_ERROR */ 1579 genInterpSingleStep(cUnit, mir); 1580 return false; 1581 } 1582 1583 static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) 1584 { 1585 RegLocation rlResult; 1586 RegLocation rlDest; 1587 RegLocation rlSrc; 1588 1589 switch (mir->dalvikInsn.opcode) { 1590 case OP_CONST_STRING_JUMBO: 1591 case OP_CONST_STRING: { 1592 void *strPtr = (void*) 1593 (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]); 1594 1595 if (strPtr == NULL) { 1596 BAIL_LOOP_COMPILATION(); 1597 ALOGE("Unexpected null string"); 1598 dvmAbort(); 1599 } 1600 1601 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1602 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1603 loadConstantNoClobber(cUnit, rlResult.lowReg, (int) strPtr ); 1604 storeValue(cUnit, rlDest, rlResult); 1605 break; 1606 } 1607 case OP_CONST_CLASS: { 1608 void *classPtr = (void*) 1609 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); 1610 1611 if (classPtr == NULL) { 1612 BAIL_LOOP_COMPILATION(); 1613 ALOGE("Unexpected null class"); 1614 dvmAbort(); 1615 } 1616 1617 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1618 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1619 loadConstantNoClobber(cUnit, rlResult.lowReg, (int) classPtr ); 1620 storeValue(cUnit, rlDest, rlResult); 1621 break; 1622 } 1623 case OP_SGET: 1624 case OP_SGET_VOLATILE: 1625 case OP_SGET_OBJECT: 1626 case OP_SGET_OBJECT_VOLATILE: 1627 case OP_SGET_BOOLEAN: 1628 case OP_SGET_CHAR: 1629 case OP_SGET_BYTE: 1630 case OP_SGET_SHORT: { 1631 int valOffset = OFFSETOF_MEMBER(StaticField, value); 1632 int tReg = dvmCompilerAllocTemp(cUnit); 1633 bool isVolatile; 1634 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1635 mir->meta.calleeMethod : cUnit->method; 1636 void *fieldPtr = (void*) 1637 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1638 1639 if (fieldPtr == NULL) { 1640 BAIL_LOOP_COMPILATION(); 1641 ALOGE("Unexpected null static field"); 1642 dvmAbort(); 1643 } 1644 1645 /* 1646 * On SMP systems, Dalvik opcodes found to be referencing 1647 * volatile fields are rewritten to their _VOLATILE variant. 1648 * However, this does not happen on non-SMP systems. The JIT 1649 * still needs to know about volatility to avoid unsafe 1650 * optimizations so we determine volatility based on either 1651 * the opcode or the field access flags. 1652 */ 1653 #if ANDROID_SMP != 0 1654 Opcode opcode = mir->dalvikInsn.opcode; 1655 isVolatile = (opcode == OP_SGET_VOLATILE) || 1656 (opcode == OP_SGET_OBJECT_VOLATILE); 1657 assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr)); 1658 #else 1659 isVolatile = dvmIsVolatileField((Field *) fieldPtr); 1660 #endif 1661 1662 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1663 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1664 loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); 1665 1666 if (isVolatile) { 1667 dvmCompilerGenMemBarrier(cUnit, kSY); 1668 } 1669 HEAP_ACCESS_SHADOW(true); 1670 loadWordDisp(cUnit, tReg, 0, rlResult.lowReg); 1671 HEAP_ACCESS_SHADOW(false); 1672 1673 storeValue(cUnit, rlDest, rlResult); 1674 break; 1675 } 1676 case OP_SGET_WIDE: { 1677 int valOffset = OFFSETOF_MEMBER(StaticField, value); 1678 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1679 mir->meta.calleeMethod : cUnit->method; 1680 void *fieldPtr = (void*) 1681 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1682 1683 if (fieldPtr == NULL) { 1684 BAIL_LOOP_COMPILATION(); 1685 ALOGE("Unexpected null static field"); 1686 dvmAbort(); 1687 } 1688 1689 int tReg = dvmCompilerAllocTemp(cUnit); 1690 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1691 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 1692 loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); 1693 1694 HEAP_ACCESS_SHADOW(true); 1695 loadPair(cUnit, tReg, rlResult.lowReg, rlResult.highReg); 1696 HEAP_ACCESS_SHADOW(false); 1697 1698 storeValueWide(cUnit, rlDest, rlResult); 1699 break; 1700 } 1701 case OP_SPUT: 1702 case OP_SPUT_VOLATILE: 1703 case OP_SPUT_OBJECT: 1704 case OP_SPUT_OBJECT_VOLATILE: 1705 case OP_SPUT_BOOLEAN: 1706 case OP_SPUT_CHAR: 1707 case OP_SPUT_BYTE: 1708 case OP_SPUT_SHORT: { 1709 int valOffset = OFFSETOF_MEMBER(StaticField, value); 1710 int tReg = dvmCompilerAllocTemp(cUnit); 1711 int objHead; 1712 bool isVolatile; 1713 bool isSputObject; 1714 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1715 mir->meta.calleeMethod : cUnit->method; 1716 void *fieldPtr = (void*) 1717 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1718 Opcode opcode = mir->dalvikInsn.opcode; 1719 1720 if (fieldPtr == NULL) { 1721 BAIL_LOOP_COMPILATION(); 1722 ALOGE("Unexpected null static field"); 1723 dvmAbort(); 1724 } 1725 1726 #if ANDROID_SMP != 0 1727 isVolatile = (opcode == OP_SPUT_VOLATILE) || 1728 (opcode == OP_SPUT_OBJECT_VOLATILE); 1729 assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr)); 1730 #else 1731 isVolatile = dvmIsVolatileField((Field *) fieldPtr); 1732 #endif 1733 1734 isSputObject = (opcode == OP_SPUT_OBJECT) || 1735 (opcode == OP_SPUT_OBJECT_VOLATILE); 1736 1737 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1738 rlSrc = loadValue(cUnit, rlSrc, kAnyReg); 1739 loadConstant(cUnit, tReg, (int) fieldPtr); 1740 if (isSputObject) { 1741 objHead = dvmCompilerAllocTemp(cUnit); 1742 loadWordDisp(cUnit, tReg, OFFSETOF_MEMBER(Field, clazz), objHead); 1743 } 1744 if (isVolatile) { 1745 dvmCompilerGenMemBarrier(cUnit, kST); 1746 } 1747 HEAP_ACCESS_SHADOW(true); 1748 storeWordDisp(cUnit, tReg, valOffset ,rlSrc.lowReg); 1749 dvmCompilerFreeTemp(cUnit, tReg); 1750 HEAP_ACCESS_SHADOW(false); 1751 if (isVolatile) { 1752 dvmCompilerGenMemBarrier(cUnit, kSY); 1753 } 1754 if (isSputObject) { 1755 /* NOTE: marking card based sfield->clazz */ 1756 markCard(cUnit, rlSrc.lowReg, objHead); 1757 dvmCompilerFreeTemp(cUnit, objHead); 1758 } 1759 1760 break; 1761 } 1762 case OP_SPUT_WIDE: { 1763 int tReg = dvmCompilerAllocTemp(cUnit); 1764 int valOffset = OFFSETOF_MEMBER(StaticField, value); 1765 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 1766 mir->meta.calleeMethod : cUnit->method; 1767 void *fieldPtr = (void*) 1768 (method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); 1769 1770 if (fieldPtr == NULL) { 1771 BAIL_LOOP_COMPILATION(); 1772 ALOGE("Unexpected null static field"); 1773 dvmAbort(); 1774 } 1775 1776 rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 1777 rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg); 1778 loadConstant(cUnit, tReg, (int) fieldPtr + valOffset); 1779 1780 HEAP_ACCESS_SHADOW(true); 1781 storePair(cUnit, tReg, rlSrc.lowReg, rlSrc.highReg); 1782 HEAP_ACCESS_SHADOW(false); 1783 break; 1784 } 1785 case OP_NEW_INSTANCE: { 1786 /* 1787 * Obey the calling convention and don't mess with the register 1788 * usage. 1789 */ 1790 ClassObject *classPtr = (ClassObject *) 1791 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); 1792 1793 if (classPtr == NULL) { 1794 BAIL_LOOP_COMPILATION(); 1795 ALOGE("Unexpected null class"); 1796 dvmAbort(); 1797 } 1798 1799 /* 1800 * If it is going to throw, it should not make to the trace to begin 1801 * with. However, Alloc might throw, so we need to genExportPC() 1802 */ 1803 assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0); 1804 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 1805 genExportPC(cUnit, mir); 1806 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmAllocObject); 1807 loadConstant(cUnit, r0, (int) classPtr); 1808 loadConstant(cUnit, r1, ALLOC_DONT_TRACK); 1809 opReg(cUnit, kOpBlx, r2); 1810 dvmCompilerClobberCallRegs(cUnit); 1811 /* generate a branch over if allocation is successful */ 1812 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0); 1813 /* 1814 * OOM exception needs to be thrown here and cannot re-execute 1815 */ 1816 loadConstant(cUnit, r0, 1817 (int) (cUnit->method->insns + mir->offset)); 1818 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 1819 /* noreturn */ 1820 1821 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 1822 target->defMask = ENCODE_ALL; 1823 branchOver->generic.target = (LIR *) target; 1824 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1825 rlResult = dvmCompilerGetReturn(cUnit); 1826 storeValue(cUnit, rlDest, rlResult); 1827 break; 1828 } 1829 case OP_CHECK_CAST: { 1830 /* 1831 * Obey the calling convention and don't mess with the register 1832 * usage. 1833 */ 1834 ClassObject *classPtr = 1835 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); 1836 /* 1837 * Note: It is possible that classPtr is NULL at this point, 1838 * even though this instruction has been successfully interpreted. 1839 * If the previous interpretation had a null source, the 1840 * interpreter would not have bothered to resolve the clazz. 1841 * Bail out to the interpreter in this case, and log it 1842 * so that we can tell if it happens frequently. 1843 */ 1844 if (classPtr == NULL) { 1845 BAIL_LOOP_COMPILATION(); 1846 LOGVV("null clazz in OP_CHECK_CAST, single-stepping"); 1847 genInterpSingleStep(cUnit, mir); 1848 return false; 1849 } 1850 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 1851 loadConstant(cUnit, r1, (int) classPtr ); 1852 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1853 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 1854 /* Null? */ 1855 ArmLIR *branch1 = genCmpImmBranch(cUnit, kArmCondEq, 1856 rlSrc.lowReg, 0); 1857 /* 1858 * rlSrc.lowReg now contains object->clazz. Note that 1859 * it could have been allocated r0, but we're okay so long 1860 * as we don't do anything desctructive until r0 is loaded 1861 * with clazz. 1862 */ 1863 /* r0 now contains object->clazz */ 1864 loadWordDisp(cUnit, rlSrc.lowReg, offsetof(Object, clazz), r0); 1865 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInstanceofNonTrivial); 1866 opRegReg(cUnit, kOpCmp, r0, r1); 1867 ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq); 1868 opReg(cUnit, kOpBlx, r2); 1869 dvmCompilerClobberCallRegs(cUnit); 1870 /* 1871 * If null, check cast failed - punt to the interpreter. Because 1872 * interpreter will be the one throwing, we don't need to 1873 * genExportPC() here. 1874 */ 1875 genZeroCheck(cUnit, r0, mir->offset, NULL); 1876 /* check cast passed - branch target here */ 1877 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 1878 target->defMask = ENCODE_ALL; 1879 branch1->generic.target = (LIR *)target; 1880 branch2->generic.target = (LIR *)target; 1881 break; 1882 } 1883 case OP_SGET_WIDE_VOLATILE: 1884 case OP_SPUT_WIDE_VOLATILE: 1885 genInterpSingleStep(cUnit, mir); 1886 break; 1887 default: 1888 return true; 1889 } 1890 return false; 1891 } 1892 1893 /* 1894 * A typical example of inlined getter/setter from a monomorphic callsite: 1895 * 1896 * D/dalvikvm( 289): -------- dalvik offset: 0x0000 @ invoke-static (I) 1897 * D/dalvikvm( 289): -------- dalvik offset: 0x0000 @ sget-object (C) v0, ... 1898 * D/dalvikvm( 289): 0x4427fc22 (0002): ldr r0, [pc, #56] 1899 * D/dalvikvm( 289): 0x4427fc24 (0004): ldr r1, [r0, #0] 1900 * D/dalvikvm( 289): 0x4427fc26 (0006): str r1, [r5, #0] 1901 * D/dalvikvm( 289): 0x4427fc28 (0008): .align4 1902 * D/dalvikvm( 289): L0x0003: 1903 * D/dalvikvm( 289): -------- dalvik offset: 0x0003 @ move-result-object (I) v0 1904 * 1905 * Note the invoke-static and move-result-object with the (I) notation are 1906 * turned into no-op. 1907 */ 1908 static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir) 1909 { 1910 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 1911 RegLocation rlResult; 1912 switch (dalvikOpcode) { 1913 case OP_MOVE_EXCEPTION: { 1914 int exOffset = offsetof(Thread, exception); 1915 int resetReg = dvmCompilerAllocTemp(cUnit); 1916 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1917 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 1918 loadWordDisp(cUnit, r6SELF, exOffset, rlResult.lowReg); 1919 loadConstant(cUnit, resetReg, 0); 1920 storeWordDisp(cUnit, r6SELF, exOffset, resetReg); 1921 storeValue(cUnit, rlDest, rlResult); 1922 break; 1923 } 1924 case OP_MOVE_RESULT: 1925 case OP_MOVE_RESULT_OBJECT: { 1926 /* An inlined move result is effectively no-op */ 1927 if (mir->OptimizationFlags & MIR_INLINED) 1928 break; 1929 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1930 RegLocation rlSrc = LOC_DALVIK_RETURN_VAL; 1931 rlSrc.fp = rlDest.fp; 1932 storeValue(cUnit, rlDest, rlSrc); 1933 break; 1934 } 1935 case OP_MOVE_RESULT_WIDE: { 1936 /* An inlined move result is effectively no-op */ 1937 if (mir->OptimizationFlags & MIR_INLINED) 1938 break; 1939 RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1940 RegLocation rlSrc = LOC_DALVIK_RETURN_VAL_WIDE; 1941 rlSrc.fp = rlDest.fp; 1942 storeValueWide(cUnit, rlDest, rlSrc); 1943 break; 1944 } 1945 case OP_RETURN_WIDE: { 1946 RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 1947 RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE; 1948 rlDest.fp = rlSrc.fp; 1949 storeValueWide(cUnit, rlDest, rlSrc); 1950 genReturnCommon(cUnit,mir); 1951 break; 1952 } 1953 case OP_RETURN: 1954 case OP_RETURN_OBJECT: { 1955 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1956 RegLocation rlDest = LOC_DALVIK_RETURN_VAL; 1957 rlDest.fp = rlSrc.fp; 1958 storeValue(cUnit, rlDest, rlSrc); 1959 genReturnCommon(cUnit, mir); 1960 break; 1961 } 1962 case OP_MONITOR_EXIT: 1963 case OP_MONITOR_ENTER: 1964 genMonitor(cUnit, mir); 1965 break; 1966 case OP_THROW: 1967 genInterpSingleStep(cUnit, mir); 1968 break; 1969 default: 1970 return true; 1971 } 1972 return false; 1973 } 1974 1975 static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir) 1976 { 1977 Opcode opcode = mir->dalvikInsn.opcode; 1978 RegLocation rlDest; 1979 RegLocation rlSrc; 1980 RegLocation rlResult; 1981 1982 if ( (opcode >= OP_ADD_INT_2ADDR) && (opcode <= OP_REM_DOUBLE_2ADDR)) { 1983 return genArithOp( cUnit, mir ); 1984 } 1985 1986 if (mir->ssaRep->numUses == 2) 1987 rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 1988 else 1989 rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 1990 if (mir->ssaRep->numDefs == 2) 1991 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 1992 else 1993 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 1994 1995 switch (opcode) { 1996 case OP_DOUBLE_TO_INT: 1997 case OP_INT_TO_FLOAT: 1998 case OP_FLOAT_TO_INT: 1999 case OP_DOUBLE_TO_FLOAT: 2000 case OP_FLOAT_TO_DOUBLE: 2001 case OP_INT_TO_DOUBLE: 2002 case OP_FLOAT_TO_LONG: 2003 case OP_LONG_TO_FLOAT: 2004 case OP_DOUBLE_TO_LONG: 2005 case OP_LONG_TO_DOUBLE: 2006 return genConversion(cUnit, mir); 2007 case OP_NEG_INT: 2008 case OP_NOT_INT: 2009 return genArithOpInt(cUnit, mir, rlDest, rlSrc, rlSrc); 2010 case OP_NEG_LONG: 2011 case OP_NOT_LONG: 2012 return genArithOpLong(cUnit, mir, rlDest, rlSrc, rlSrc); 2013 case OP_NEG_FLOAT: 2014 return genArithOpFloat(cUnit, mir, rlDest, rlSrc, rlSrc); 2015 case OP_NEG_DOUBLE: 2016 return genArithOpDouble(cUnit, mir, rlDest, rlSrc, rlSrc); 2017 case OP_MOVE_WIDE: 2018 storeValueWide(cUnit, rlDest, rlSrc); 2019 break; 2020 case OP_INT_TO_LONG: 2021 rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc); 2022 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2023 //TUNING: shouldn't loadValueDirect already check for phys reg? 2024 if (rlSrc.location == kLocPhysReg) { 2025 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); 2026 } else { 2027 loadValueDirect(cUnit, rlSrc, rlResult.lowReg); 2028 } 2029 opRegRegImm(cUnit, kOpAsr, rlResult.highReg, 2030 rlResult.lowReg, 31); 2031 storeValueWide(cUnit, rlDest, rlResult); 2032 break; 2033 case OP_LONG_TO_INT: 2034 rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc); 2035 rlSrc = dvmCompilerWideToNarrow(cUnit, rlSrc); 2036 // Intentional fallthrough 2037 case OP_MOVE: 2038 case OP_MOVE_OBJECT: 2039 storeValue(cUnit, rlDest, rlSrc); 2040 break; 2041 case OP_INT_TO_BYTE: 2042 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2043 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2044 opRegReg(cUnit, kOp2Byte, rlResult.lowReg, rlSrc.lowReg); 2045 storeValue(cUnit, rlDest, rlResult); 2046 break; 2047 case OP_INT_TO_SHORT: 2048 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2049 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2050 opRegReg(cUnit, kOp2Short, rlResult.lowReg, rlSrc.lowReg); 2051 storeValue(cUnit, rlDest, rlResult); 2052 break; 2053 case OP_INT_TO_CHAR: 2054 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2055 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2056 opRegReg(cUnit, kOp2Char, rlResult.lowReg, rlSrc.lowReg); 2057 storeValue(cUnit, rlDest, rlResult); 2058 break; 2059 case OP_ARRAY_LENGTH: { 2060 int lenOffset = OFFSETOF_MEMBER(ArrayObject, length); 2061 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2062 genNullCheck(cUnit, rlSrc.sRegLow, rlSrc.lowReg, 2063 mir->offset, NULL); 2064 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2065 loadWordDisp(cUnit, rlSrc.lowReg, lenOffset, 2066 rlResult.lowReg); 2067 storeValue(cUnit, rlDest, rlResult); 2068 break; 2069 } 2070 default: 2071 return true; 2072 } 2073 return false; 2074 } 2075 2076 static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir) 2077 { 2078 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2079 RegLocation rlDest; 2080 RegLocation rlResult; 2081 int BBBB = mir->dalvikInsn.vB; 2082 if (dalvikOpcode == OP_CONST_WIDE_16) { 2083 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 2084 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2085 loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB); 2086 //TUNING: do high separately to avoid load dependency 2087 opRegRegImm(cUnit, kOpAsr, rlResult.highReg, rlResult.lowReg, 31); 2088 storeValueWide(cUnit, rlDest, rlResult); 2089 } else if (dalvikOpcode == OP_CONST_16) { 2090 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2091 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true); 2092 loadConstantNoClobber(cUnit, rlResult.lowReg, BBBB); 2093 storeValue(cUnit, rlDest, rlResult); 2094 } else 2095 return true; 2096 return false; 2097 } 2098 2099 /* Compare agaist zero */ 2100 static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, 2101 ArmLIR *labelList) 2102 { 2103 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2104 ArmConditionCode cond; 2105 /* backward branch? */ 2106 bool backwardBranch = (bb->taken->startOffset <= mir->offset); 2107 2108 if (backwardBranch && 2109 (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) { 2110 genSuspendPoll(cUnit, mir); 2111 } 2112 2113 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2114 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2115 2116 switch (dalvikOpcode) { 2117 case OP_IF_EQZ: 2118 cond = kArmCondEq; 2119 break; 2120 case OP_IF_NEZ: 2121 cond = kArmCondNe; 2122 break; 2123 case OP_IF_LTZ: 2124 cond = kArmCondLt; 2125 break; 2126 case OP_IF_GEZ: 2127 cond = kArmCondGe; 2128 break; 2129 case OP_IF_GTZ: 2130 cond = kArmCondGt; 2131 break; 2132 case OP_IF_LEZ: 2133 cond = kArmCondLe; 2134 break; 2135 default: 2136 cond = (ArmConditionCode)0; 2137 ALOGE("Unexpected opcode (%d) for Fmt21t", dalvikOpcode); 2138 dvmCompilerAbort(cUnit); 2139 } 2140 ArmLIR* branch = genCmpImmBranch(cUnit, cond, rlSrc.lowReg, 0); 2141 branch->generic.target = (LIR*)&labelList[bb->taken->id]; 2142 /* This mostly likely will be optimized away in a later phase */ 2143 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); 2144 return false; 2145 } 2146 2147 static bool isPowerOfTwo(int x) 2148 { 2149 return (x & (x - 1)) == 0; 2150 } 2151 2152 // Returns true if no more than two bits are set in 'x'. 2153 static bool isPopCountLE2(unsigned int x) 2154 { 2155 x &= x - 1; 2156 return (x & (x - 1)) == 0; 2157 } 2158 2159 // Returns the index of the lowest set bit in 'x'. 2160 static int lowestSetBit(unsigned int x) { 2161 int bit_posn = 0; 2162 while ((x & 0xf) == 0) { 2163 bit_posn += 4; 2164 x >>= 4; 2165 } 2166 while ((x & 1) == 0) { 2167 bit_posn++; 2168 x >>= 1; 2169 } 2170 return bit_posn; 2171 } 2172 2173 // Returns true if it added instructions to 'cUnit' to divide 'rlSrc' by 'lit' 2174 // and store the result in 'rlDest'. 2175 static bool handleEasyDivide(CompilationUnit *cUnit, Opcode dalvikOpcode, 2176 RegLocation rlSrc, RegLocation rlDest, int lit) 2177 { 2178 if (lit < 2 || !isPowerOfTwo(lit)) { 2179 return false; 2180 } 2181 int k = lowestSetBit(lit); 2182 if (k >= 30) { 2183 // Avoid special cases. 2184 return false; 2185 } 2186 bool div = (dalvikOpcode == OP_DIV_INT_LIT8 || dalvikOpcode == OP_DIV_INT_LIT16); 2187 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2188 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2189 if (div) { 2190 int tReg = dvmCompilerAllocTemp(cUnit); 2191 if (lit == 2) { 2192 // Division by 2 is by far the most common division by constant. 2193 opRegRegImm(cUnit, kOpLsr, tReg, rlSrc.lowReg, 32 - k); 2194 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); 2195 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); 2196 } else { 2197 opRegRegImm(cUnit, kOpAsr, tReg, rlSrc.lowReg, 31); 2198 opRegRegImm(cUnit, kOpLsr, tReg, tReg, 32 - k); 2199 opRegRegReg(cUnit, kOpAdd, tReg, tReg, rlSrc.lowReg); 2200 opRegRegImm(cUnit, kOpAsr, rlResult.lowReg, tReg, k); 2201 } 2202 } else { 2203 int cReg = dvmCompilerAllocTemp(cUnit); 2204 loadConstant(cUnit, cReg, lit - 1); 2205 int tReg1 = dvmCompilerAllocTemp(cUnit); 2206 int tReg2 = dvmCompilerAllocTemp(cUnit); 2207 if (lit == 2) { 2208 opRegRegImm(cUnit, kOpLsr, tReg1, rlSrc.lowReg, 32 - k); 2209 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); 2210 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg); 2211 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); 2212 } else { 2213 opRegRegImm(cUnit, kOpAsr, tReg1, rlSrc.lowReg, 31); 2214 opRegRegImm(cUnit, kOpLsr, tReg1, tReg1, 32 - k); 2215 opRegRegReg(cUnit, kOpAdd, tReg2, tReg1, rlSrc.lowReg); 2216 opRegRegReg(cUnit, kOpAnd, tReg2, tReg2, cReg); 2217 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg2, tReg1); 2218 } 2219 } 2220 storeValue(cUnit, rlDest, rlResult); 2221 return true; 2222 } 2223 2224 // Returns true if it added instructions to 'cUnit' to multiply 'rlSrc' by 'lit' 2225 // and store the result in 'rlDest'. 2226 static bool handleEasyMultiply(CompilationUnit *cUnit, 2227 RegLocation rlSrc, RegLocation rlDest, int lit) 2228 { 2229 // Can we simplify this multiplication? 2230 bool powerOfTwo = false; 2231 bool popCountLE2 = false; 2232 bool powerOfTwoMinusOne = false; 2233 if (lit < 2) { 2234 // Avoid special cases. 2235 return false; 2236 } else if (isPowerOfTwo(lit)) { 2237 powerOfTwo = true; 2238 } else if (isPopCountLE2(lit)) { 2239 popCountLE2 = true; 2240 } else if (isPowerOfTwo(lit + 1)) { 2241 powerOfTwoMinusOne = true; 2242 } else { 2243 return false; 2244 } 2245 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2246 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2247 if (powerOfTwo) { 2248 // Shift. 2249 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlSrc.lowReg, 2250 lowestSetBit(lit)); 2251 } else if (popCountLE2) { 2252 // Shift and add and shift. 2253 int firstBit = lowestSetBit(lit); 2254 int secondBit = lowestSetBit(lit ^ (1 << firstBit)); 2255 genMultiplyByTwoBitMultiplier(cUnit, rlSrc, rlResult, lit, 2256 firstBit, secondBit); 2257 } else { 2258 // Reverse subtract: (src << (shift + 1)) - src. 2259 assert(powerOfTwoMinusOne); 2260 genMultiplyByShiftAndReverseSubtract(cUnit, rlSrc, rlResult, lowestSetBit(lit + 1)); 2261 } 2262 storeValue(cUnit, rlDest, rlResult); 2263 return true; 2264 } 2265 2266 static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir) 2267 { 2268 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2269 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2270 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2271 RegLocation rlResult; 2272 int lit = mir->dalvikInsn.vC; 2273 OpKind op = (OpKind)0; /* Make gcc happy */ 2274 int shiftOp = false; 2275 bool isDiv = false; 2276 2277 switch (dalvikOpcode) { 2278 case OP_RSUB_INT_LIT8: 2279 case OP_RSUB_INT: { 2280 int tReg; 2281 //TUNING: add support for use of Arm rsub op 2282 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2283 tReg = dvmCompilerAllocTemp(cUnit); 2284 loadConstant(cUnit, tReg, lit); 2285 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2286 opRegRegReg(cUnit, kOpSub, rlResult.lowReg, 2287 tReg, rlSrc.lowReg); 2288 storeValue(cUnit, rlDest, rlResult); 2289 return false; 2290 break; 2291 } 2292 2293 case OP_ADD_INT_LIT8: 2294 case OP_ADD_INT_LIT16: 2295 op = kOpAdd; 2296 break; 2297 case OP_MUL_INT_LIT8: 2298 case OP_MUL_INT_LIT16: { 2299 if (handleEasyMultiply(cUnit, rlSrc, rlDest, lit)) { 2300 return false; 2301 } 2302 op = kOpMul; 2303 break; 2304 } 2305 case OP_AND_INT_LIT8: 2306 case OP_AND_INT_LIT16: 2307 op = kOpAnd; 2308 break; 2309 case OP_OR_INT_LIT8: 2310 case OP_OR_INT_LIT16: 2311 op = kOpOr; 2312 break; 2313 case OP_XOR_INT_LIT8: 2314 case OP_XOR_INT_LIT16: 2315 op = kOpXor; 2316 break; 2317 case OP_SHL_INT_LIT8: 2318 lit &= 31; 2319 shiftOp = true; 2320 op = kOpLsl; 2321 break; 2322 case OP_SHR_INT_LIT8: 2323 lit &= 31; 2324 shiftOp = true; 2325 op = kOpAsr; 2326 break; 2327 case OP_USHR_INT_LIT8: 2328 lit &= 31; 2329 shiftOp = true; 2330 op = kOpLsr; 2331 break; 2332 2333 case OP_DIV_INT_LIT8: 2334 case OP_DIV_INT_LIT16: 2335 case OP_REM_INT_LIT8: 2336 case OP_REM_INT_LIT16: 2337 if (lit == 0) { 2338 /* Let the interpreter deal with div by 0 */ 2339 genInterpSingleStep(cUnit, mir); 2340 return false; 2341 } 2342 if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) { 2343 return false; 2344 } 2345 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2346 loadValueDirectFixed(cUnit, rlSrc, r0); 2347 dvmCompilerClobber(cUnit, r0); 2348 if ((dalvikOpcode == OP_DIV_INT_LIT8) || 2349 (dalvikOpcode == OP_DIV_INT_LIT16)) { 2350 LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idiv); 2351 isDiv = true; 2352 } else { 2353 LOAD_FUNC_ADDR(cUnit, r2, (int)__aeabi_idivmod); 2354 isDiv = false; 2355 } 2356 loadConstant(cUnit, r1, lit); 2357 opReg(cUnit, kOpBlx, r2); 2358 dvmCompilerClobberCallRegs(cUnit); 2359 if (isDiv) 2360 rlResult = dvmCompilerGetReturn(cUnit); 2361 else 2362 rlResult = dvmCompilerGetReturnAlt(cUnit); 2363 storeValue(cUnit, rlDest, rlResult); 2364 return false; 2365 break; 2366 default: 2367 return true; 2368 } 2369 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 2370 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 2371 // Avoid shifts by literal 0 - no support in Thumb. Change to copy 2372 if (shiftOp && (lit == 0)) { 2373 genRegCopy(cUnit, rlResult.lowReg, rlSrc.lowReg); 2374 } else { 2375 opRegRegImm(cUnit, op, rlResult.lowReg, rlSrc.lowReg, lit); 2376 } 2377 storeValue(cUnit, rlDest, rlResult); 2378 return false; 2379 } 2380 2381 static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir) 2382 { 2383 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2384 int fieldOffset = -1; 2385 bool isVolatile = false; 2386 switch (dalvikOpcode) { 2387 /* 2388 * Wide volatiles currently handled via single step. 2389 * Add them here if generating in-line code. 2390 * case OP_IGET_WIDE_VOLATILE: 2391 * case OP_IPUT_WIDE_VOLATILE: 2392 */ 2393 case OP_IGET_VOLATILE: 2394 case OP_IGET_OBJECT_VOLATILE: 2395 case OP_IPUT_VOLATILE: 2396 case OP_IPUT_OBJECT_VOLATILE: 2397 #if ANDROID_SMP != 0 2398 isVolatile = true; 2399 // NOTE: intentional fallthrough 2400 #endif 2401 case OP_IGET: 2402 case OP_IGET_WIDE: 2403 case OP_IGET_OBJECT: 2404 case OP_IGET_BOOLEAN: 2405 case OP_IGET_BYTE: 2406 case OP_IGET_CHAR: 2407 case OP_IGET_SHORT: 2408 case OP_IPUT: 2409 case OP_IPUT_WIDE: 2410 case OP_IPUT_OBJECT: 2411 case OP_IPUT_BOOLEAN: 2412 case OP_IPUT_BYTE: 2413 case OP_IPUT_CHAR: 2414 case OP_IPUT_SHORT: { 2415 const Method *method = (mir->OptimizationFlags & MIR_CALLEE) ? 2416 mir->meta.calleeMethod : cUnit->method; 2417 Field *fieldPtr = 2418 method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC]; 2419 2420 if (fieldPtr == NULL) { 2421 BAIL_LOOP_COMPILATION(); 2422 ALOGE("Unexpected null instance field"); 2423 dvmAbort(); 2424 } 2425 2426 #if ANDROID_SMP != 0 2427 assert(isVolatile == dvmIsVolatileField((Field *) fieldPtr)); 2428 #else 2429 isVolatile = dvmIsVolatileField((Field *) fieldPtr); 2430 #endif 2431 fieldOffset = ((InstField *)fieldPtr)->byteOffset; 2432 break; 2433 } 2434 default: 2435 break; 2436 } 2437 2438 switch (dalvikOpcode) { 2439 case OP_NEW_ARRAY: { 2440 // Generates a call - use explicit registers 2441 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2442 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2443 RegLocation rlResult; 2444 void *classPtr = (void*) 2445 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); 2446 2447 if (classPtr == NULL) { 2448 BAIL_LOOP_COMPILATION(); 2449 ALOGE("Unexpected null class"); 2450 dvmAbort(); 2451 } 2452 2453 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2454 genExportPC(cUnit, mir); 2455 loadValueDirectFixed(cUnit, rlSrc, r1); /* Len */ 2456 loadConstant(cUnit, r0, (int) classPtr ); 2457 LOAD_FUNC_ADDR(cUnit, r3, (int)dvmAllocArrayByClass); 2458 /* 2459 * "len < 0": bail to the interpreter to re-execute the 2460 * instruction 2461 */ 2462 genRegImmCheck(cUnit, kArmCondMi, r1, 0, mir->offset, NULL); 2463 loadConstant(cUnit, r2, ALLOC_DONT_TRACK); 2464 opReg(cUnit, kOpBlx, r3); 2465 dvmCompilerClobberCallRegs(cUnit); 2466 /* generate a branch over if allocation is successful */ 2467 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0); 2468 /* 2469 * OOM exception needs to be thrown here and cannot re-execute 2470 */ 2471 loadConstant(cUnit, r0, 2472 (int) (cUnit->method->insns + mir->offset)); 2473 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 2474 /* noreturn */ 2475 2476 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2477 target->defMask = ENCODE_ALL; 2478 branchOver->generic.target = (LIR *) target; 2479 rlResult = dvmCompilerGetReturn(cUnit); 2480 storeValue(cUnit, rlDest, rlResult); 2481 break; 2482 } 2483 case OP_INSTANCE_OF: { 2484 // May generate a call - use explicit registers 2485 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2486 RegLocation rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2487 RegLocation rlResult; 2488 ClassObject *classPtr = 2489 (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); 2490 /* 2491 * Note: It is possible that classPtr is NULL at this point, 2492 * even though this instruction has been successfully interpreted. 2493 * If the previous interpretation had a null source, the 2494 * interpreter would not have bothered to resolve the clazz. 2495 * Bail out to the interpreter in this case, and log it 2496 * so that we can tell if it happens frequently. 2497 */ 2498 if (classPtr == NULL) { 2499 BAIL_LOOP_COMPILATION(); 2500 ALOGD("null clazz in OP_INSTANCE_OF, single-stepping"); 2501 genInterpSingleStep(cUnit, mir); 2502 break; 2503 } 2504 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2505 loadValueDirectFixed(cUnit, rlSrc, r0); /* Ref */ 2506 loadConstant(cUnit, r2, (int) classPtr ); 2507 /* When taken r0 has NULL which can be used for store directly */ 2508 ArmLIR *branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0); 2509 /* r1 now contains object->clazz */ 2510 loadWordDisp(cUnit, r0, offsetof(Object, clazz), r1); 2511 /* r1 now contains object->clazz */ 2512 LOAD_FUNC_ADDR(cUnit, r3, (int)dvmInstanceofNonTrivial); 2513 loadConstant(cUnit, r0, 1); /* Assume true */ 2514 opRegReg(cUnit, kOpCmp, r1, r2); 2515 ArmLIR *branch2 = opCondBranch(cUnit, kArmCondEq); 2516 genRegCopy(cUnit, r0, r1); 2517 genRegCopy(cUnit, r1, r2); 2518 opReg(cUnit, kOpBlx, r3); 2519 dvmCompilerClobberCallRegs(cUnit); 2520 /* branch target here */ 2521 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2522 target->defMask = ENCODE_ALL; 2523 rlResult = dvmCompilerGetReturn(cUnit); 2524 storeValue(cUnit, rlDest, rlResult); 2525 branch1->generic.target = (LIR *)target; 2526 branch2->generic.target = (LIR *)target; 2527 break; 2528 } 2529 case OP_IGET_WIDE: 2530 genIGetWide(cUnit, mir, fieldOffset); 2531 break; 2532 case OP_IGET_VOLATILE: 2533 case OP_IGET_OBJECT_VOLATILE: 2534 case OP_IGET: 2535 case OP_IGET_OBJECT: 2536 case OP_IGET_BOOLEAN: 2537 case OP_IGET_BYTE: 2538 case OP_IGET_CHAR: 2539 case OP_IGET_SHORT: 2540 genIGet(cUnit, mir, kWord, fieldOffset, isVolatile); 2541 break; 2542 case OP_IPUT_WIDE: 2543 genIPutWide(cUnit, mir, fieldOffset); 2544 break; 2545 case OP_IPUT_VOLATILE: 2546 case OP_IPUT: 2547 case OP_IPUT_BOOLEAN: 2548 case OP_IPUT_BYTE: 2549 case OP_IPUT_CHAR: 2550 case OP_IPUT_SHORT: 2551 genIPut(cUnit, mir, kWord, fieldOffset, false, isVolatile); 2552 break; 2553 case OP_IPUT_OBJECT_VOLATILE: 2554 case OP_IPUT_OBJECT: 2555 genIPut(cUnit, mir, kWord, fieldOffset, true, isVolatile); 2556 break; 2557 case OP_IGET_WIDE_VOLATILE: 2558 case OP_IPUT_WIDE_VOLATILE: 2559 genInterpSingleStep(cUnit, mir); 2560 break; 2561 default: 2562 return true; 2563 } 2564 return false; 2565 } 2566 2567 static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir) 2568 { 2569 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2570 int fieldOffset = mir->dalvikInsn.vC; 2571 switch (dalvikOpcode) { 2572 case OP_IGET_QUICK: 2573 case OP_IGET_OBJECT_QUICK: 2574 genIGet(cUnit, mir, kWord, fieldOffset, false); 2575 break; 2576 case OP_IPUT_QUICK: 2577 genIPut(cUnit, mir, kWord, fieldOffset, false, false); 2578 break; 2579 case OP_IPUT_OBJECT_QUICK: 2580 genIPut(cUnit, mir, kWord, fieldOffset, true, false); 2581 break; 2582 case OP_IGET_WIDE_QUICK: 2583 genIGetWide(cUnit, mir, fieldOffset); 2584 break; 2585 case OP_IPUT_WIDE_QUICK: 2586 genIPutWide(cUnit, mir, fieldOffset); 2587 break; 2588 default: 2589 return true; 2590 } 2591 return false; 2592 2593 } 2594 2595 /* Compare agaist zero */ 2596 static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, 2597 ArmLIR *labelList) 2598 { 2599 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2600 ArmConditionCode cond; 2601 /* backward branch? */ 2602 bool backwardBranch = (bb->taken->startOffset <= mir->offset); 2603 2604 if (backwardBranch && 2605 (gDvmJit.genSuspendPoll || cUnit->jitMode == kJitLoop)) { 2606 genSuspendPoll(cUnit, mir); 2607 } 2608 2609 RegLocation rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0); 2610 RegLocation rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1); 2611 2612 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg); 2613 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg); 2614 2615 opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg); 2616 2617 switch (dalvikOpcode) { 2618 case OP_IF_EQ: 2619 cond = kArmCondEq; 2620 break; 2621 case OP_IF_NE: 2622 cond = kArmCondNe; 2623 break; 2624 case OP_IF_LT: 2625 cond = kArmCondLt; 2626 break; 2627 case OP_IF_GE: 2628 cond = kArmCondGe; 2629 break; 2630 case OP_IF_GT: 2631 cond = kArmCondGt; 2632 break; 2633 case OP_IF_LE: 2634 cond = kArmCondLe; 2635 break; 2636 default: 2637 cond = (ArmConditionCode)0; 2638 ALOGE("Unexpected opcode (%d) for Fmt22t", dalvikOpcode); 2639 dvmCompilerAbort(cUnit); 2640 } 2641 genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); 2642 /* This mostly likely will be optimized away in a later phase */ 2643 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); 2644 return false; 2645 } 2646 2647 static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir) 2648 { 2649 Opcode opcode = mir->dalvikInsn.opcode; 2650 2651 switch (opcode) { 2652 case OP_MOVE_16: 2653 case OP_MOVE_OBJECT_16: 2654 case OP_MOVE_FROM16: 2655 case OP_MOVE_OBJECT_FROM16: { 2656 storeValue(cUnit, dvmCompilerGetDest(cUnit, mir, 0), 2657 dvmCompilerGetSrc(cUnit, mir, 0)); 2658 break; 2659 } 2660 case OP_MOVE_WIDE_16: 2661 case OP_MOVE_WIDE_FROM16: { 2662 storeValueWide(cUnit, dvmCompilerGetDestWide(cUnit, mir, 0, 1), 2663 dvmCompilerGetSrcWide(cUnit, mir, 0, 1)); 2664 break; 2665 } 2666 default: 2667 return true; 2668 } 2669 return false; 2670 } 2671 2672 static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir) 2673 { 2674 Opcode opcode = mir->dalvikInsn.opcode; 2675 RegLocation rlSrc1; 2676 RegLocation rlSrc2; 2677 RegLocation rlDest; 2678 2679 if ( (opcode >= OP_ADD_INT) && (opcode <= OP_REM_DOUBLE)) { 2680 return genArithOp( cUnit, mir ); 2681 } 2682 2683 /* APUTs have 3 sources and no targets */ 2684 if (mir->ssaRep->numDefs == 0) { 2685 if (mir->ssaRep->numUses == 3) { 2686 rlDest = dvmCompilerGetSrc(cUnit, mir, 0); 2687 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 1); 2688 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 2); 2689 } else { 2690 assert(mir->ssaRep->numUses == 4); 2691 rlDest = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 2692 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 2); 2693 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 3); 2694 } 2695 } else { 2696 /* Two sources and 1 dest. Deduce the operand sizes */ 2697 if (mir->ssaRep->numUses == 4) { 2698 rlSrc1 = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 2699 rlSrc2 = dvmCompilerGetSrcWide(cUnit, mir, 2, 3); 2700 } else { 2701 assert(mir->ssaRep->numUses == 2); 2702 rlSrc1 = dvmCompilerGetSrc(cUnit, mir, 0); 2703 rlSrc2 = dvmCompilerGetSrc(cUnit, mir, 1); 2704 } 2705 if (mir->ssaRep->numDefs == 2) { 2706 rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 2707 } else { 2708 assert(mir->ssaRep->numDefs == 1); 2709 rlDest = dvmCompilerGetDest(cUnit, mir, 0); 2710 } 2711 } 2712 2713 2714 switch (opcode) { 2715 case OP_CMPL_FLOAT: 2716 case OP_CMPG_FLOAT: 2717 case OP_CMPL_DOUBLE: 2718 case OP_CMPG_DOUBLE: 2719 return genCmpFP(cUnit, mir, rlDest, rlSrc1, rlSrc2); 2720 case OP_CMP_LONG: 2721 genCmpLong(cUnit, mir, rlDest, rlSrc1, rlSrc2); 2722 break; 2723 case OP_AGET_WIDE: 2724 genArrayGet(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3); 2725 break; 2726 case OP_AGET: 2727 case OP_AGET_OBJECT: 2728 genArrayGet(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2); 2729 break; 2730 case OP_AGET_BOOLEAN: 2731 genArrayGet(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0); 2732 break; 2733 case OP_AGET_BYTE: 2734 genArrayGet(cUnit, mir, kSignedByte, rlSrc1, rlSrc2, rlDest, 0); 2735 break; 2736 case OP_AGET_CHAR: 2737 genArrayGet(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1); 2738 break; 2739 case OP_AGET_SHORT: 2740 genArrayGet(cUnit, mir, kSignedHalf, rlSrc1, rlSrc2, rlDest, 1); 2741 break; 2742 case OP_APUT_WIDE: 2743 genArrayPut(cUnit, mir, kLong, rlSrc1, rlSrc2, rlDest, 3); 2744 break; 2745 case OP_APUT: 2746 genArrayPut(cUnit, mir, kWord, rlSrc1, rlSrc2, rlDest, 2); 2747 break; 2748 case OP_APUT_OBJECT: 2749 genArrayObjectPut(cUnit, mir, rlSrc1, rlSrc2, rlDest, 2); 2750 break; 2751 case OP_APUT_SHORT: 2752 case OP_APUT_CHAR: 2753 genArrayPut(cUnit, mir, kUnsignedHalf, rlSrc1, rlSrc2, rlDest, 1); 2754 break; 2755 case OP_APUT_BYTE: 2756 case OP_APUT_BOOLEAN: 2757 genArrayPut(cUnit, mir, kUnsignedByte, rlSrc1, rlSrc2, rlDest, 0); 2758 break; 2759 default: 2760 return true; 2761 } 2762 return false; 2763 } 2764 2765 /* 2766 * Find the matching case. 2767 * 2768 * return values: 2769 * r0 (low 32-bit): pc of the chaining cell corresponding to the resolved case, 2770 * including default which is placed at MIN(size, MAX_CHAINED_SWITCH_CASES). 2771 * r1 (high 32-bit): the branch offset of the matching case (only for indexes 2772 * above MAX_CHAINED_SWITCH_CASES). 2773 * 2774 * Instructions around the call are: 2775 * 2776 * mov r2, pc 2777 * blx &findPackedSwitchIndex 2778 * mov pc, r0 2779 * .align4 2780 * chaining cell for case 0 [12 bytes] 2781 * chaining cell for case 1 [12 bytes] 2782 * : 2783 * chaining cell for case MIN(size, MAX_CHAINED_SWITCH_CASES)-1 [12 bytes] 2784 * chaining cell for case default [8 bytes] 2785 * noChain exit 2786 */ 2787 static u8 findPackedSwitchIndex(const u2* switchData, int testVal, uintptr_t pc) 2788 { 2789 int size; 2790 int firstKey; 2791 const int *entries; 2792 int index; 2793 int jumpIndex; 2794 uintptr_t caseDPCOffset = 0; 2795 /* In Thumb mode pc is 4 ahead of the "mov r2, pc" instruction */ 2796 uintptr_t chainingPC = (pc + 4) & ~3; 2797 2798 /* 2799 * Packed switch data format: 2800 * ushort ident = 0x0100 magic value 2801 * ushort size number of entries in the table 2802 * int first_key first (and lowest) switch case value 2803 * int targets[size] branch targets, relative to switch opcode 2804 * 2805 * Total size is (4+size*2) 16-bit code units. 2806 */ 2807 size = switchData[1]; 2808 assert(size > 0); 2809 2810 firstKey = switchData[2]; 2811 firstKey |= switchData[3] << 16; 2812 2813 2814 /* The entries are guaranteed to be aligned on a 32-bit boundary; 2815 * we can treat them as a native int array. 2816 */ 2817 entries = (const int*) &switchData[4]; 2818 assert(((u4)entries & 0x3) == 0); 2819 2820 index = testVal - firstKey; 2821 2822 /* Jump to the default cell */ 2823 if (index < 0 || index >= size) { 2824 jumpIndex = MIN(size, MAX_CHAINED_SWITCH_CASES); 2825 /* Jump to the non-chaining exit point */ 2826 } else if (index >= MAX_CHAINED_SWITCH_CASES) { 2827 jumpIndex = MAX_CHAINED_SWITCH_CASES + 1; 2828 caseDPCOffset = entries[index]; 2829 /* Jump to the inline chaining cell */ 2830 } else { 2831 jumpIndex = index; 2832 } 2833 2834 chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE; 2835 return (((u8) caseDPCOffset) << 32) | (u8) chainingPC; 2836 } 2837 2838 /* See comments for findPackedSwitchIndex */ 2839 static u8 findSparseSwitchIndex(const u2* switchData, int testVal, uintptr_t pc) 2840 { 2841 int size; 2842 const int *keys; 2843 const int *entries; 2844 uintptr_t chainingPC = (pc + 4) & ~3; 2845 int i; 2846 2847 /* 2848 * Sparse switch data format: 2849 * ushort ident = 0x0200 magic value 2850 * ushort size number of entries in the table; > 0 2851 * int keys[size] keys, sorted low-to-high; 32-bit aligned 2852 * int targets[size] branch targets, relative to switch opcode 2853 * 2854 * Total size is (2+size*4) 16-bit code units. 2855 */ 2856 2857 size = switchData[1]; 2858 assert(size > 0); 2859 2860 /* The keys are guaranteed to be aligned on a 32-bit boundary; 2861 * we can treat them as a native int array. 2862 */ 2863 keys = (const int*) &switchData[2]; 2864 assert(((u4)keys & 0x3) == 0); 2865 2866 /* The entries are guaranteed to be aligned on a 32-bit boundary; 2867 * we can treat them as a native int array. 2868 */ 2869 entries = keys + size; 2870 assert(((u4)entries & 0x3) == 0); 2871 2872 /* 2873 * Run through the list of keys, which are guaranteed to 2874 * be sorted low-to-high. 2875 * 2876 * Most tables have 3-4 entries. Few have more than 10. A binary 2877 * search here is probably not useful. 2878 */ 2879 for (i = 0; i < size; i++) { 2880 int k = keys[i]; 2881 if (k == testVal) { 2882 /* MAX_CHAINED_SWITCH_CASES + 1 is the start of the overflow case */ 2883 int jumpIndex = (i < MAX_CHAINED_SWITCH_CASES) ? 2884 i : MAX_CHAINED_SWITCH_CASES + 1; 2885 chainingPC += jumpIndex * CHAIN_CELL_NORMAL_SIZE; 2886 return (((u8) entries[i]) << 32) | (u8) chainingPC; 2887 } else if (k > testVal) { 2888 break; 2889 } 2890 } 2891 return chainingPC + MIN(size, MAX_CHAINED_SWITCH_CASES) * 2892 CHAIN_CELL_NORMAL_SIZE; 2893 } 2894 2895 static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir) 2896 { 2897 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 2898 switch (dalvikOpcode) { 2899 case OP_FILL_ARRAY_DATA: { 2900 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2901 // Making a call - use explicit registers 2902 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2903 genExportPC(cUnit, mir); 2904 loadValueDirectFixed(cUnit, rlSrc, r0); 2905 LOAD_FUNC_ADDR(cUnit, r2, (int)dvmInterpHandleFillArrayData); 2906 loadConstant(cUnit, r1, 2907 (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB)); 2908 opReg(cUnit, kOpBlx, r2); 2909 dvmCompilerClobberCallRegs(cUnit); 2910 /* generate a branch over if successful */ 2911 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0); 2912 loadConstant(cUnit, r0, 2913 (int) (cUnit->method->insns + mir->offset)); 2914 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 2915 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2916 target->defMask = ENCODE_ALL; 2917 branchOver->generic.target = (LIR *) target; 2918 break; 2919 } 2920 /* 2921 * Compute the goto target of up to 2922 * MIN(switchSize, MAX_CHAINED_SWITCH_CASES) + 1 chaining cells. 2923 * See the comment before findPackedSwitchIndex for the code layout. 2924 */ 2925 case OP_PACKED_SWITCH: 2926 case OP_SPARSE_SWITCH: { 2927 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 2928 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 2929 loadValueDirectFixed(cUnit, rlSrc, r1); 2930 dvmCompilerLockAllTemps(cUnit); 2931 if (dalvikOpcode == OP_PACKED_SWITCH) { 2932 LOAD_FUNC_ADDR(cUnit, r4PC, (int)findPackedSwitchIndex); 2933 } else { 2934 LOAD_FUNC_ADDR(cUnit, r4PC, (int)findSparseSwitchIndex); 2935 } 2936 /* r0 <- Addr of the switch data */ 2937 loadConstant(cUnit, r0, 2938 (int) (cUnit->method->insns + mir->offset + mir->dalvikInsn.vB)); 2939 /* r2 <- pc of the instruction following the blx */ 2940 opRegReg(cUnit, kOpMov, r2, r15pc); 2941 opReg(cUnit, kOpBlx, r4PC); 2942 dvmCompilerClobberCallRegs(cUnit); 2943 /* pc <- computed goto target */ 2944 opRegReg(cUnit, kOpMov, r15pc, r0); 2945 break; 2946 } 2947 default: 2948 return true; 2949 } 2950 return false; 2951 } 2952 2953 /* 2954 * See the example of predicted inlining listed before the 2955 * genValidationForPredictedInline function. The function here takes care the 2956 * branch over at 0x4858de78 and the misprediction target at 0x4858de7a. 2957 */ 2958 static void genLandingPadForMispredictedCallee(CompilationUnit *cUnit, MIR *mir, 2959 BasicBlock *bb, 2960 ArmLIR *labelList) 2961 { 2962 BasicBlock *fallThrough = bb->fallThrough; 2963 2964 /* Bypass the move-result block if there is one */ 2965 if (fallThrough->firstMIRInsn) { 2966 assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED); 2967 fallThrough = fallThrough->fallThrough; 2968 } 2969 /* Generate a branch over if the predicted inlining is correct */ 2970 genUnconditionalBranch(cUnit, &labelList[fallThrough->id]); 2971 2972 /* Reset the register state */ 2973 dvmCompilerResetRegPool(cUnit); 2974 dvmCompilerClobberAllRegs(cUnit); 2975 dvmCompilerResetNullCheck(cUnit); 2976 2977 /* Target for the slow invoke path */ 2978 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 2979 target->defMask = ENCODE_ALL; 2980 /* Hook up the target to the verification branch */ 2981 mir->meta.callsiteInfo->misPredBranchOver->target = (LIR *) target; 2982 } 2983 2984 static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, 2985 BasicBlock *bb, ArmLIR *labelList) 2986 { 2987 ArmLIR *retChainingCell = NULL; 2988 ArmLIR *pcrLabel = NULL; 2989 2990 /* An invoke with the MIR_INLINED is effectively a no-op */ 2991 if (mir->OptimizationFlags & MIR_INLINED) 2992 return false; 2993 2994 if (bb->fallThrough != NULL) 2995 retChainingCell = &labelList[bb->fallThrough->id]; 2996 2997 DecodedInstruction *dInsn = &mir->dalvikInsn; 2998 switch (mir->dalvikInsn.opcode) { 2999 /* 3000 * calleeMethod = this->clazz->vtable[ 3001 * method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex 3002 * ] 3003 */ 3004 case OP_INVOKE_VIRTUAL: 3005 case OP_INVOKE_VIRTUAL_RANGE: { 3006 ArmLIR *predChainingCell = &labelList[bb->taken->id]; 3007 int methodIndex = 3008 cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]-> 3009 methodIndex; 3010 3011 /* 3012 * If the invoke has non-null misPredBranchOver, we need to generate 3013 * the non-inlined version of the invoke here to handle the 3014 * mispredicted case. 3015 */ 3016 if (mir->meta.callsiteInfo->misPredBranchOver) { 3017 genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList); 3018 } 3019 3020 if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL) 3021 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3022 else 3023 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3024 3025 genInvokeVirtualCommon(cUnit, mir, methodIndex, 3026 retChainingCell, 3027 predChainingCell, 3028 pcrLabel); 3029 break; 3030 } 3031 /* 3032 * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex 3033 * ->pResMethods[BBBB]->methodIndex] 3034 */ 3035 case OP_INVOKE_SUPER: 3036 case OP_INVOKE_SUPER_RANGE: { 3037 /* Grab the method ptr directly from what the interpreter sees */ 3038 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3039 assert(calleeMethod == cUnit->method->clazz->super->vtable[ 3040 cUnit->method->clazz->pDvmDex-> 3041 pResMethods[dInsn->vB]->methodIndex]); 3042 3043 if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER) 3044 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3045 else 3046 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3047 3048 if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) { 3049 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3050 void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns); 3051 assert(calleeAddr); 3052 genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr, 3053 retChainingCell); 3054 } else { 3055 /* r0 = calleeMethod */ 3056 loadConstant(cUnit, r0, (int) calleeMethod); 3057 3058 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 3059 calleeMethod); 3060 } 3061 break; 3062 } 3063 /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ 3064 case OP_INVOKE_DIRECT: 3065 case OP_INVOKE_DIRECT_RANGE: { 3066 /* Grab the method ptr directly from what the interpreter sees */ 3067 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3068 assert(calleeMethod == 3069 cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]); 3070 3071 if (mir->dalvikInsn.opcode == OP_INVOKE_DIRECT) 3072 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3073 else 3074 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3075 3076 /* r0 = calleeMethod */ 3077 loadConstant(cUnit, r0, (int) calleeMethod); 3078 3079 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 3080 calleeMethod); 3081 break; 3082 } 3083 /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ 3084 case OP_INVOKE_STATIC: 3085 case OP_INVOKE_STATIC_RANGE: { 3086 /* Grab the method ptr directly from what the interpreter sees */ 3087 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3088 assert(calleeMethod == 3089 cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]); 3090 3091 if (mir->dalvikInsn.opcode == OP_INVOKE_STATIC) 3092 genProcessArgsNoRange(cUnit, mir, dInsn, 3093 NULL /* no null check */); 3094 else 3095 genProcessArgsRange(cUnit, mir, dInsn, 3096 NULL /* no null check */); 3097 3098 if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) { 3099 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3100 void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns); 3101 assert(calleeAddr); 3102 genInvokeSingletonWholeMethod(cUnit, mir, calleeAddr, 3103 retChainingCell); 3104 } else { 3105 /* r0 = calleeMethod */ 3106 loadConstant(cUnit, r0, (int) calleeMethod); 3107 3108 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 3109 calleeMethod); 3110 } 3111 break; 3112 } 3113 /* 3114 * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz, 3115 * BBBB, method, method->clazz->pDvmDex) 3116 * 3117 * The following is an example of generated code for 3118 * "invoke-interface v0" 3119 * 3120 * -------- dalvik offset: 0x0008 @ invoke-interface v0 3121 * 0x47357e36 : ldr r0, [r5, #0] --+ 3122 * 0x47357e38 : sub r7,r5,#24 | 3123 * 0x47357e3c : cmp r0, #0 | genProcessArgsNoRange 3124 * 0x47357e3e : beq 0x47357e82 | 3125 * 0x47357e40 : stmia r7, <r0> --+ 3126 * 0x47357e42 : ldr r4, [pc, #120] --> r4 <- dalvikPC of this invoke 3127 * 0x47357e44 : add r1, pc, #64 --> r1 <- &retChainingCell 3128 * 0x47357e46 : add r2, pc, #72 --> r2 <- &predictedChainingCell 3129 * 0x47357e48 : blx_1 0x47348190 --+ TEMPLATE_INVOKE_METHOD_ 3130 * 0x47357e4a : blx_2 see above --+ PREDICTED_CHAIN 3131 * 0x47357e4c : b 0x47357e90 --> off to the predicted chain 3132 * 0x47357e4e : b 0x47357e82 --> punt to the interpreter 3133 * 0x47357e50 : mov r8, r1 --+ 3134 * 0x47357e52 : mov r9, r2 | 3135 * 0x47357e54 : ldr r2, [pc, #96] | 3136 * 0x47357e56 : mov r10, r3 | 3137 * 0x47357e58 : movs r0, r3 | dvmFindInterfaceMethodInCache 3138 * 0x47357e5a : ldr r3, [pc, #88] | 3139 * 0x47357e5c : ldr r7, [pc, #80] | 3140 * 0x47357e5e : mov r1, #1452 | 3141 * 0x47357e62 : blx r7 --+ 3142 * 0x47357e64 : cmp r0, #0 --> calleeMethod == NULL? 3143 * 0x47357e66 : bne 0x47357e6e --> branch over the throw if !r0 3144 * 0x47357e68 : ldr r0, [pc, #80] --> load Dalvik PC of the invoke 3145 * 0x47357e6a : blx_1 0x47348494 --+ TEMPLATE_THROW_EXCEPTION_ 3146 * 0x47357e6c : blx_2 see above --+ COMMON 3147 * 0x47357e6e : mov r1, r8 --> r1 <- &retChainingCell 3148 * 0x47357e70 : cmp r1, #0 --> compare against 0 3149 * 0x47357e72 : bgt 0x47357e7c --> >=0? don't rechain 3150 * 0x47357e74 : ldr r7, [pc, #off] --+ 3151 * 0x47357e76 : mov r2, r9 | dvmJitToPatchPredictedChain 3152 * 0x47357e78 : mov r3, r10 | 3153 * 0x47357e7a : blx r7 --+ 3154 * 0x47357e7c : add r1, pc, #8 --> r1 <- &retChainingCell 3155 * 0x47357e7e : blx_1 0x4734809c --+ TEMPLATE_INVOKE_METHOD_NO_OPT 3156 * 0x47357e80 : blx_2 see above --+ 3157 * -------- reconstruct dalvik PC : 0x425719dc @ +0x0008 3158 * 0x47357e82 : ldr r0, [pc, #56] 3159 * Exception_Handling: 3160 * 0x47357e84 : ldr r1, [r6, #92] 3161 * 0x47357e86 : blx r1 3162 * 0x47357e88 : .align4 3163 * -------- chaining cell (hot): 0x000b 3164 * 0x47357e88 : ldr r0, [r6, #104] 3165 * 0x47357e8a : blx r0 3166 * 0x47357e8c : data 0x19e2(6626) 3167 * 0x47357e8e : data 0x4257(16983) 3168 * 0x47357e90 : .align4 3169 * -------- chaining cell (predicted) 3170 * 0x47357e90 : data 0xe7fe(59390) --> will be patched into bx 3171 * 0x47357e92 : data 0x0000(0) 3172 * 0x47357e94 : data 0x0000(0) --> class 3173 * 0x47357e96 : data 0x0000(0) 3174 * 0x47357e98 : data 0x0000(0) --> method 3175 * 0x47357e9a : data 0x0000(0) 3176 * 0x47357e9c : data 0x0000(0) --> rechain count 3177 * 0x47357e9e : data 0x0000(0) 3178 * -------- end of chaining cells (0x006c) 3179 * 0x47357eb0 : .word (0xad03e369) 3180 * 0x47357eb4 : .word (0x28a90) 3181 * 0x47357eb8 : .word (0x41a63394) 3182 * 0x47357ebc : .word (0x425719dc) 3183 */ 3184 case OP_INVOKE_INTERFACE: 3185 case OP_INVOKE_INTERFACE_RANGE: { 3186 ArmLIR *predChainingCell = &labelList[bb->taken->id]; 3187 3188 /* 3189 * If the invoke has non-null misPredBranchOver, we need to generate 3190 * the non-inlined version of the invoke here to handle the 3191 * mispredicted case. 3192 */ 3193 if (mir->meta.callsiteInfo->misPredBranchOver) { 3194 genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList); 3195 } 3196 3197 if (mir->dalvikInsn.opcode == OP_INVOKE_INTERFACE) 3198 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3199 else 3200 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3201 3202 /* "this" is already left in r0 by genProcessArgs* */ 3203 3204 /* r4PC = dalvikCallsite */ 3205 loadConstant(cUnit, r4PC, 3206 (int) (cUnit->method->insns + mir->offset)); 3207 3208 /* r1 = &retChainingCell */ 3209 ArmLIR *addrRetChain = 3210 opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0); 3211 addrRetChain->generic.target = (LIR *) retChainingCell; 3212 3213 /* r2 = &predictedChainingCell */ 3214 ArmLIR *predictedChainingCell = 3215 opRegRegImm(cUnit, kOpAdd, r2, r15pc, 0); 3216 predictedChainingCell->generic.target = (LIR *) predChainingCell; 3217 3218 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 3219 TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN_PROF : 3220 TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN); 3221 3222 /* return through lr - jump to the chaining cell */ 3223 genUnconditionalBranch(cUnit, predChainingCell); 3224 3225 /* 3226 * null-check on "this" may have been eliminated, but we still need 3227 * a PC-reconstruction label for stack overflow bailout. 3228 */ 3229 if (pcrLabel == NULL) { 3230 int dPC = (int) (cUnit->method->insns + mir->offset); 3231 pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 3232 pcrLabel->opcode = kArmPseudoPCReconstructionCell; 3233 pcrLabel->operands[0] = dPC; 3234 pcrLabel->operands[1] = mir->offset; 3235 /* Insert the place holder to the growable list */ 3236 dvmInsertGrowableList(&cUnit->pcReconstructionList, 3237 (intptr_t) pcrLabel); 3238 } 3239 3240 /* return through lr+2 - punt to the interpreter */ 3241 genUnconditionalBranch(cUnit, pcrLabel); 3242 3243 /* 3244 * return through lr+4 - fully resolve the callee method. 3245 * r1 <- count 3246 * r2 <- &predictedChainCell 3247 * r3 <- this->class 3248 * r4 <- dPC 3249 * r7 <- this->class->vtable 3250 */ 3251 3252 /* Save count, &predictedChainCell, and class to high regs first */ 3253 genRegCopy(cUnit, r8, r1); 3254 genRegCopy(cUnit, r9, r2); 3255 genRegCopy(cUnit, r10, r3); 3256 3257 /* r0 now contains this->clazz */ 3258 genRegCopy(cUnit, r0, r3); 3259 3260 /* r1 = BBBB */ 3261 loadConstant(cUnit, r1, dInsn->vB); 3262 3263 /* r2 = method (caller) */ 3264 loadConstant(cUnit, r2, (int) cUnit->method); 3265 3266 /* r3 = pDvmDex */ 3267 loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex); 3268 3269 LOAD_FUNC_ADDR(cUnit, r7, 3270 (intptr_t) dvmFindInterfaceMethodInCache); 3271 opReg(cUnit, kOpBlx, r7); 3272 /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */ 3273 3274 dvmCompilerClobberCallRegs(cUnit); 3275 /* generate a branch over if the interface method is resolved */ 3276 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0); 3277 /* 3278 * calleeMethod == NULL -> throw 3279 */ 3280 loadConstant(cUnit, r0, 3281 (int) (cUnit->method->insns + mir->offset)); 3282 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 3283 /* noreturn */ 3284 3285 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 3286 target->defMask = ENCODE_ALL; 3287 branchOver->generic.target = (LIR *) target; 3288 3289 genRegCopy(cUnit, r1, r8); 3290 3291 /* Check if rechain limit is reached */ 3292 ArmLIR *bypassRechaining = genCmpImmBranch(cUnit, kArmCondGt, 3293 r1, 0); 3294 3295 LOAD_FUNC_ADDR(cUnit, r7, (int) dvmJitToPatchPredictedChain); 3296 3297 genRegCopy(cUnit, r1, r6SELF); 3298 genRegCopy(cUnit, r2, r9); 3299 genRegCopy(cUnit, r3, r10); 3300 3301 /* 3302 * r0 = calleeMethod 3303 * r2 = &predictedChainingCell 3304 * r3 = class 3305 * 3306 * &returnChainingCell has been loaded into r1 but is not needed 3307 * when patching the chaining cell and will be clobbered upon 3308 * returning so it will be reconstructed again. 3309 */ 3310 opReg(cUnit, kOpBlx, r7); 3311 3312 /* r1 = &retChainingCell */ 3313 addrRetChain = opRegRegImm(cUnit, kOpAdd, r1, r15pc, 0); 3314 addrRetChain->generic.target = (LIR *) retChainingCell; 3315 3316 bypassRechaining->generic.target = (LIR *) addrRetChain; 3317 3318 /* 3319 * r0 = this, r1 = calleeMethod, 3320 * r1 = &ChainingCell, 3321 * r4PC = callsiteDPC, 3322 */ 3323 genDispatchToHandler(cUnit, gDvmJit.methodTraceSupport ? 3324 TEMPLATE_INVOKE_METHOD_NO_OPT_PROF : 3325 TEMPLATE_INVOKE_METHOD_NO_OPT); 3326 #if defined(WITH_JIT_TUNING) 3327 gDvmJit.invokePolymorphic++; 3328 #endif 3329 /* Handle exceptions using the interpreter */ 3330 genTrap(cUnit, mir->offset, pcrLabel); 3331 break; 3332 } 3333 case OP_INVOKE_OBJECT_INIT_RANGE: 3334 case OP_FILLED_NEW_ARRAY: 3335 case OP_FILLED_NEW_ARRAY_RANGE: { 3336 /* Just let the interpreter deal with these */ 3337 genInterpSingleStep(cUnit, mir); 3338 break; 3339 } 3340 default: 3341 return true; 3342 } 3343 return false; 3344 } 3345 3346 static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir, 3347 BasicBlock *bb, ArmLIR *labelList) 3348 { 3349 ArmLIR *pcrLabel = NULL; 3350 3351 /* An invoke with the MIR_INLINED is effectively a no-op */ 3352 if (mir->OptimizationFlags & MIR_INLINED) 3353 return false; 3354 3355 DecodedInstruction *dInsn = &mir->dalvikInsn; 3356 switch (mir->dalvikInsn.opcode) { 3357 /* calleeMethod = this->clazz->vtable[BBBB] */ 3358 case OP_INVOKE_VIRTUAL_QUICK_RANGE: 3359 case OP_INVOKE_VIRTUAL_QUICK: { 3360 int methodIndex = dInsn->vB; 3361 ArmLIR *retChainingCell = &labelList[bb->fallThrough->id]; 3362 ArmLIR *predChainingCell = &labelList[bb->taken->id]; 3363 3364 /* 3365 * If the invoke has non-null misPredBranchOver, we need to generate 3366 * the non-inlined version of the invoke here to handle the 3367 * mispredicted case. 3368 */ 3369 if (mir->meta.callsiteInfo->misPredBranchOver) { 3370 genLandingPadForMispredictedCallee(cUnit, mir, bb, labelList); 3371 } 3372 3373 if (mir->dalvikInsn.opcode == OP_INVOKE_VIRTUAL_QUICK) 3374 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3375 else 3376 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3377 3378 3379 if (mir->OptimizationFlags & MIR_INVOKE_METHOD_JIT) { 3380 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3381 void *calleeAddr = dvmJitGetMethodAddr(calleeMethod->insns); 3382 assert(calleeAddr); 3383 genInvokeVirtualWholeMethod(cUnit, mir, calleeAddr, 3384 retChainingCell); 3385 } 3386 3387 genInvokeVirtualCommon(cUnit, mir, methodIndex, 3388 retChainingCell, 3389 predChainingCell, 3390 pcrLabel); 3391 break; 3392 } 3393 /* calleeMethod = method->clazz->super->vtable[BBBB] */ 3394 case OP_INVOKE_SUPER_QUICK: 3395 case OP_INVOKE_SUPER_QUICK_RANGE: { 3396 /* Grab the method ptr directly from what the interpreter sees */ 3397 const Method *calleeMethod = mir->meta.callsiteInfo->method; 3398 assert(calleeMethod == 3399 cUnit->method->clazz->super->vtable[dInsn->vB]); 3400 3401 if (mir->dalvikInsn.opcode == OP_INVOKE_SUPER_QUICK) 3402 genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); 3403 else 3404 genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); 3405 3406 /* r0 = calleeMethod */ 3407 loadConstant(cUnit, r0, (int) calleeMethod); 3408 3409 genInvokeSingletonCommon(cUnit, mir, bb, labelList, pcrLabel, 3410 calleeMethod); 3411 break; 3412 } 3413 default: 3414 return true; 3415 } 3416 return false; 3417 } 3418 3419 /* 3420 * This operation is complex enough that we'll do it partly inline 3421 * and partly with a handler. NOTE: the handler uses hardcoded 3422 * values for string object offsets and must be revisitied if the 3423 * layout changes. 3424 */ 3425 static bool genInlinedCompareTo(CompilationUnit *cUnit, MIR *mir) 3426 { 3427 #if defined(USE_GLOBAL_STRING_DEFS) 3428 return handleExecuteInlineC(cUnit, mir); 3429 #else 3430 ArmLIR *rollback; 3431 RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0); 3432 RegLocation rlComp = dvmCompilerGetSrc(cUnit, mir, 1); 3433 3434 loadValueDirectFixed(cUnit, rlThis, r0); 3435 loadValueDirectFixed(cUnit, rlComp, r1); 3436 /* Test objects for NULL */ 3437 rollback = genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL); 3438 genNullCheck(cUnit, rlComp.sRegLow, r1, mir->offset, rollback); 3439 /* 3440 * TUNING: we could check for object pointer equality before invoking 3441 * handler. Unclear whether the gain would be worth the added code size 3442 * expansion. 3443 */ 3444 genDispatchToHandler(cUnit, TEMPLATE_STRING_COMPARETO); 3445 storeValue(cUnit, inlinedTarget(cUnit, mir, false), 3446 dvmCompilerGetReturn(cUnit)); 3447 return false; 3448 #endif 3449 } 3450 3451 static bool genInlinedFastIndexOf(CompilationUnit *cUnit, MIR *mir) 3452 { 3453 #if defined(USE_GLOBAL_STRING_DEFS) 3454 return handleExecuteInlineC(cUnit, mir); 3455 #else 3456 RegLocation rlThis = dvmCompilerGetSrc(cUnit, mir, 0); 3457 RegLocation rlChar = dvmCompilerGetSrc(cUnit, mir, 1); 3458 3459 loadValueDirectFixed(cUnit, rlThis, r0); 3460 loadValueDirectFixed(cUnit, rlChar, r1); 3461 RegLocation rlStart = dvmCompilerGetSrc(cUnit, mir, 2); 3462 loadValueDirectFixed(cUnit, rlStart, r2); 3463 /* Test objects for NULL */ 3464 genNullCheck(cUnit, rlThis.sRegLow, r0, mir->offset, NULL); 3465 genDispatchToHandler(cUnit, TEMPLATE_STRING_INDEXOF); 3466 storeValue(cUnit, inlinedTarget(cUnit, mir, false), 3467 dvmCompilerGetReturn(cUnit)); 3468 return false; 3469 #endif 3470 } 3471 3472 // Generates an inlined String.isEmpty or String.length. 3473 static bool genInlinedStringIsEmptyOrLength(CompilationUnit *cUnit, MIR *mir, 3474 bool isEmpty) 3475 { 3476 // dst = src.length(); 3477 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 3478 RegLocation rlDest = inlinedTarget(cUnit, mir, false); 3479 rlObj = loadValue(cUnit, rlObj, kCoreReg); 3480 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 3481 genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset, NULL); 3482 loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, 3483 rlResult.lowReg); 3484 if (isEmpty) { 3485 // dst = (dst == 0); 3486 int tReg = dvmCompilerAllocTemp(cUnit); 3487 opRegReg(cUnit, kOpNeg, tReg, rlResult.lowReg); 3488 opRegRegReg(cUnit, kOpAdc, rlResult.lowReg, rlResult.lowReg, tReg); 3489 } 3490 storeValue(cUnit, rlDest, rlResult); 3491 return false; 3492 } 3493 3494 static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir) 3495 { 3496 return genInlinedStringIsEmptyOrLength(cUnit, mir, false); 3497 } 3498 3499 static bool genInlinedStringIsEmpty(CompilationUnit *cUnit, MIR *mir) 3500 { 3501 return genInlinedStringIsEmptyOrLength(cUnit, mir, true); 3502 } 3503 3504 static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir) 3505 { 3506 int contents = OFFSETOF_MEMBER(ArrayObject, contents); 3507 RegLocation rlObj = dvmCompilerGetSrc(cUnit, mir, 0); 3508 RegLocation rlIdx = dvmCompilerGetSrc(cUnit, mir, 1); 3509 RegLocation rlDest = inlinedTarget(cUnit, mir, false); 3510 RegLocation rlResult; 3511 rlObj = loadValue(cUnit, rlObj, kCoreReg); 3512 rlIdx = loadValue(cUnit, rlIdx, kCoreReg); 3513 int regMax = dvmCompilerAllocTemp(cUnit); 3514 int regOff = dvmCompilerAllocTemp(cUnit); 3515 int regPtr = dvmCompilerAllocTemp(cUnit); 3516 ArmLIR *pcrLabel = genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, 3517 mir->offset, NULL); 3518 loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_count, regMax); 3519 loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_offset, regOff); 3520 loadWordDisp(cUnit, rlObj.lowReg, gDvm.offJavaLangString_value, regPtr); 3521 genBoundsCheck(cUnit, rlIdx.lowReg, regMax, mir->offset, pcrLabel); 3522 dvmCompilerFreeTemp(cUnit, regMax); 3523 opRegImm(cUnit, kOpAdd, regPtr, contents); 3524 opRegReg(cUnit, kOpAdd, regOff, rlIdx.lowReg); 3525 rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 3526 loadBaseIndexed(cUnit, regPtr, regOff, rlResult.lowReg, 1, kUnsignedHalf); 3527 storeValue(cUnit, rlDest, rlResult); 3528 return false; 3529 } 3530 3531 static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir) 3532 { 3533 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 3534 rlSrc = loadValue(cUnit, rlSrc, kCoreReg); 3535 RegLocation rlDest = inlinedTarget(cUnit, mir, false); 3536 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 3537 int signReg = dvmCompilerAllocTemp(cUnit); 3538 /* 3539 * abs(x) = y<=x>>31, (x+y)^y. 3540 * Thumb2's IT block also yields 3 instructions, but imposes 3541 * scheduling constraints. 3542 */ 3543 opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.lowReg, 31); 3544 opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg); 3545 opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg); 3546 storeValue(cUnit, rlDest, rlResult); 3547 return false; 3548 } 3549 3550 static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir) 3551 { 3552 RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 3553 RegLocation rlDest = inlinedTargetWide(cUnit, mir, false); 3554 rlSrc = loadValueWide(cUnit, rlSrc, kCoreReg); 3555 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 3556 int signReg = dvmCompilerAllocTemp(cUnit); 3557 /* 3558 * abs(x) = y<=x>>31, (x+y)^y. 3559 * Thumb2 IT block allows slightly shorter sequence, 3560 * but introduces a scheduling barrier. Stick with this 3561 * mechanism for now. 3562 */ 3563 opRegRegImm(cUnit, kOpAsr, signReg, rlSrc.highReg, 31); 3564 opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, signReg); 3565 opRegRegReg(cUnit, kOpAdc, rlResult.highReg, rlSrc.highReg, signReg); 3566 opRegReg(cUnit, kOpXor, rlResult.lowReg, signReg); 3567 opRegReg(cUnit, kOpXor, rlResult.highReg, signReg); 3568 storeValueWide(cUnit, rlDest, rlResult); 3569 return false; 3570 } 3571 3572 static bool genInlinedIntFloatConversion(CompilationUnit *cUnit, MIR *mir) 3573 { 3574 // Just move from source to destination... 3575 RegLocation rlSrc = dvmCompilerGetSrc(cUnit, mir, 0); 3576 RegLocation rlDest = inlinedTarget(cUnit, mir, false); 3577 storeValue(cUnit, rlDest, rlSrc); 3578 return false; 3579 } 3580 3581 static bool genInlinedLongDoubleConversion(CompilationUnit *cUnit, MIR *mir) 3582 { 3583 // Just move from source to destination... 3584 RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1); 3585 RegLocation rlDest = inlinedTargetWide(cUnit, mir, false); 3586 storeValueWide(cUnit, rlDest, rlSrc); 3587 return false; 3588 } 3589 3590 /* 3591 * JITs a call to a C function. 3592 * TODO: use this for faster native method invocation for simple native 3593 * methods (http://b/3069458). 3594 */ 3595 static bool handleExecuteInlineC(CompilationUnit *cUnit, MIR *mir) 3596 { 3597 DecodedInstruction *dInsn = &mir->dalvikInsn; 3598 int operation = dInsn->vB; 3599 unsigned int i; 3600 const InlineOperation* inLineTable = dvmGetInlineOpsTable(); 3601 uintptr_t fn = (int) inLineTable[operation].func; 3602 if (fn == 0) { 3603 dvmCompilerAbort(cUnit); 3604 } 3605 dvmCompilerFlushAllRegs(cUnit); /* Everything to home location */ 3606 dvmCompilerClobberCallRegs(cUnit); 3607 dvmCompilerClobber(cUnit, r4PC); 3608 dvmCompilerClobber(cUnit, r7); 3609 int offset = offsetof(Thread, interpSave.retval); 3610 opRegRegImm(cUnit, kOpAdd, r4PC, r6SELF, offset); 3611 opImm(cUnit, kOpPush, (1<<r4PC) | (1<<r7)); 3612 LOAD_FUNC_ADDR(cUnit, r4PC, fn); 3613 genExportPC(cUnit, mir); 3614 for (i=0; i < dInsn->vA; i++) { 3615 loadValueDirect(cUnit, dvmCompilerGetSrc(cUnit, mir, i), i); 3616 } 3617 opReg(cUnit, kOpBlx, r4PC); 3618 opRegImm(cUnit, kOpAdd, r13sp, 8); 3619 /* NULL? */ 3620 ArmLIR *branchOver = genCmpImmBranch(cUnit, kArmCondNe, r0, 0); 3621 loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); 3622 genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON); 3623 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 3624 target->defMask = ENCODE_ALL; 3625 branchOver->generic.target = (LIR *) target; 3626 return false; 3627 } 3628 3629 /* 3630 * NOTE: Handles both range and non-range versions (arguments 3631 * have already been normalized by this point). 3632 */ 3633 static bool handleExecuteInline(CompilationUnit *cUnit, MIR *mir) 3634 { 3635 DecodedInstruction *dInsn = &mir->dalvikInsn; 3636 assert(dInsn->opcode == OP_EXECUTE_INLINE_RANGE || 3637 dInsn->opcode == OP_EXECUTE_INLINE); 3638 switch (dInsn->vB) { 3639 case INLINE_EMPTYINLINEMETHOD: 3640 return false; /* Nop */ 3641 3642 /* These ones we potentially JIT inline. */ 3643 3644 case INLINE_STRING_CHARAT: 3645 return genInlinedStringCharAt(cUnit, mir); 3646 case INLINE_STRING_LENGTH: 3647 return genInlinedStringLength(cUnit, mir); 3648 case INLINE_STRING_IS_EMPTY: 3649 return genInlinedStringIsEmpty(cUnit, mir); 3650 case INLINE_STRING_COMPARETO: 3651 return genInlinedCompareTo(cUnit, mir); 3652 case INLINE_STRING_FASTINDEXOF_II: 3653 return genInlinedFastIndexOf(cUnit, mir); 3654 3655 case INLINE_MATH_ABS_INT: 3656 case INLINE_STRICT_MATH_ABS_INT: 3657 return genInlinedAbsInt(cUnit, mir); 3658 case INLINE_MATH_ABS_LONG: 3659 case INLINE_STRICT_MATH_ABS_LONG: 3660 return genInlinedAbsLong(cUnit, mir); 3661 case INLINE_MATH_MIN_INT: 3662 case INLINE_STRICT_MATH_MIN_INT: 3663 return genInlinedMinMaxInt(cUnit, mir, true); 3664 case INLINE_MATH_MAX_INT: 3665 case INLINE_STRICT_MATH_MAX_INT: 3666 return genInlinedMinMaxInt(cUnit, mir, false); 3667 case INLINE_MATH_SQRT: 3668 case INLINE_STRICT_MATH_SQRT: 3669 return genInlineSqrt(cUnit, mir); 3670 case INLINE_MATH_ABS_FLOAT: 3671 case INLINE_STRICT_MATH_ABS_FLOAT: 3672 return genInlinedAbsFloat(cUnit, mir); 3673 case INLINE_MATH_ABS_DOUBLE: 3674 case INLINE_STRICT_MATH_ABS_DOUBLE: 3675 return genInlinedAbsDouble(cUnit, mir); 3676 3677 case INLINE_FLOAT_TO_RAW_INT_BITS: 3678 case INLINE_INT_BITS_TO_FLOAT: 3679 return genInlinedIntFloatConversion(cUnit, mir); 3680 case INLINE_DOUBLE_TO_RAW_LONG_BITS: 3681 case INLINE_LONG_BITS_TO_DOUBLE: 3682 return genInlinedLongDoubleConversion(cUnit, mir); 3683 3684 /* 3685 * These ones we just JIT a call to a C function for. 3686 * TODO: special-case these in the other "invoke" call paths. 3687 */ 3688 case INLINE_STRING_EQUALS: 3689 case INLINE_MATH_COS: 3690 case INLINE_MATH_SIN: 3691 case INLINE_FLOAT_TO_INT_BITS: 3692 case INLINE_DOUBLE_TO_LONG_BITS: 3693 return handleExecuteInlineC(cUnit, mir); 3694 } 3695 dvmCompilerAbort(cUnit); 3696 return false; // Not reachable; keeps compiler happy. 3697 } 3698 3699 static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir) 3700 { 3701 //TUNING: We're using core regs here - not optimal when target is a double 3702 RegLocation rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1); 3703 RegLocation rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kCoreReg, true); 3704 loadConstantNoClobber(cUnit, rlResult.lowReg, 3705 mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL); 3706 loadConstantNoClobber(cUnit, rlResult.highReg, 3707 (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL); 3708 storeValueWide(cUnit, rlDest, rlResult); 3709 return false; 3710 } 3711 3712 /* 3713 * The following are special processing routines that handle transfer of 3714 * controls between compiled code and the interpreter. Certain VM states like 3715 * Dalvik PC and special-purpose registers are reconstructed here. 3716 */ 3717 3718 /* 3719 * Insert a 3720 * b .+4 3721 * nop 3722 * pair at the beginning of a chaining cell. This serves as the 3723 * switch branch that selects between reverting to the interpreter or 3724 * not. Once the cell is chained to a translation, the cell will 3725 * contain a 32-bit branch. Subsequent chain/unchain operations will 3726 * then only alter that first 16-bits - the "b .+4" for unchaining, 3727 * and the restoration of the first half of the 32-bit branch for 3728 * rechaining. 3729 */ 3730 static void insertChainingSwitch(CompilationUnit *cUnit) 3731 { 3732 ArmLIR *branch = newLIR0(cUnit, kThumbBUncond); 3733 newLIR2(cUnit, kThumbOrr, r0, r0); 3734 ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel); 3735 target->defMask = ENCODE_ALL; 3736 branch->generic.target = (LIR *) target; 3737 } 3738 3739 /* Chaining cell for code that may need warmup. */ 3740 static void handleNormalChainingCell(CompilationUnit *cUnit, 3741 unsigned int offset) 3742 { 3743 /* 3744 * Use raw instruction constructors to guarantee that the generated 3745 * instructions fit the predefined cell size. 3746 */ 3747 insertChainingSwitch(cUnit); 3748 newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF, 3749 offsetof(Thread, 3750 jitToInterpEntries.dvmJitToInterpNormal) >> 2); 3751 newLIR1(cUnit, kThumbBlxR, r0); 3752 addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset)); 3753 } 3754 3755 /* 3756 * Chaining cell for instructions that immediately following already translated 3757 * code. 3758 */ 3759 static void handleHotChainingCell(CompilationUnit *cUnit, 3760 unsigned int offset) 3761 { 3762 /* 3763 * Use raw instruction constructors to guarantee that the generated 3764 * instructions fit the predefined cell size. 3765 */ 3766 insertChainingSwitch(cUnit); 3767 newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF, 3768 offsetof(Thread, 3769 jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2); 3770 newLIR1(cUnit, kThumbBlxR, r0); 3771 addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset)); 3772 } 3773 3774 /* Chaining cell for branches that branch back into the same basic block */ 3775 static void handleBackwardBranchChainingCell(CompilationUnit *cUnit, 3776 unsigned int offset) 3777 { 3778 /* 3779 * Use raw instruction constructors to guarantee that the generated 3780 * instructions fit the predefined cell size. 3781 */ 3782 insertChainingSwitch(cUnit); 3783 #if defined(WITH_SELF_VERIFICATION) 3784 newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF, 3785 offsetof(Thread, 3786 jitToInterpEntries.dvmJitToInterpBackwardBranch) >> 2); 3787 #else 3788 newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF, 3789 offsetof(Thread, jitToInterpEntries.dvmJitToInterpNormal) >> 2); 3790 #endif 3791 newLIR1(cUnit, kThumbBlxR, r0); 3792 addWordData(cUnit, NULL, (int) (cUnit->method->insns + offset)); 3793 } 3794 3795 /* Chaining cell for monomorphic method invocations. */ 3796 static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit, 3797 const Method *callee) 3798 { 3799 /* 3800 * Use raw instruction constructors to guarantee that the generated 3801 * instructions fit the predefined cell size. 3802 */ 3803 insertChainingSwitch(cUnit); 3804 newLIR3(cUnit, kThumbLdrRRI5, r0, r6SELF, 3805 offsetof(Thread, 3806 jitToInterpEntries.dvmJitToInterpTraceSelect) >> 2); 3807 newLIR1(cUnit, kThumbBlxR, r0); 3808 addWordData(cUnit, NULL, (int) (callee->insns)); 3809 } 3810 3811 /* Chaining cell for monomorphic method invocations. */ 3812 static void handleInvokePredictedChainingCell(CompilationUnit *cUnit) 3813 { 3814 3815 /* Should not be executed in the initial state */ 3816 addWordData(cUnit, NULL, PREDICTED_CHAIN_BX_PAIR_INIT); 3817 /* To be filled: class */ 3818 addWordData(cUnit, NULL, PREDICTED_CHAIN_CLAZZ_INIT); 3819 /* To be filled: method */ 3820 addWordData(cUnit, NULL, PREDICTED_CHAIN_METHOD_INIT); 3821 /* 3822 * Rechain count. The initial value of 0 here will trigger chaining upon 3823 * the first invocation of this callsite. 3824 */ 3825 addWordData(cUnit, NULL, PREDICTED_CHAIN_COUNTER_INIT); 3826 } 3827 3828 /* Load the Dalvik PC into r0 and jump to the specified target */ 3829 static void handlePCReconstruction(CompilationUnit *cUnit, 3830 ArmLIR *targetLabel) 3831 { 3832 ArmLIR **pcrLabel = 3833 (ArmLIR **) cUnit->pcReconstructionList.elemList; 3834 int numElems = cUnit->pcReconstructionList.numUsed; 3835 int i; 3836 3837 /* 3838 * We should never reach here through fall-through code, so insert 3839 * a bomb to signal troubles immediately. 3840 */ 3841 if (numElems) { 3842 newLIR0(cUnit, kThumbUndefined); 3843 } 3844 3845 for (i = 0; i < numElems; i++) { 3846 dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]); 3847 /* r0 = dalvik PC */ 3848 loadConstant(cUnit, r0, pcrLabel[i]->operands[0]); 3849 genUnconditionalBranch(cUnit, targetLabel); 3850 } 3851 } 3852 3853 static const char *extendedMIROpNames[kMirOpLast - kMirOpFirst] = { 3854 "kMirOpPhi", 3855 "kMirOpNullNRangeUpCheck", 3856 "kMirOpNullNRangeDownCheck", 3857 "kMirOpLowerBound", 3858 "kMirOpPunt", 3859 "kMirOpCheckInlinePrediction", 3860 }; 3861 3862 /* 3863 * vA = arrayReg; 3864 * vB = idxReg; 3865 * vC = endConditionReg; 3866 * arg[0] = maxC 3867 * arg[1] = minC 3868 * arg[2] = loopBranchConditionCode 3869 */ 3870 static void genHoistedChecksForCountUpLoop(CompilationUnit *cUnit, MIR *mir) 3871 { 3872 /* 3873 * NOTE: these synthesized blocks don't have ssa names assigned 3874 * for Dalvik registers. However, because they dominate the following 3875 * blocks we can simply use the Dalvik name w/ subscript 0 as the 3876 * ssa name. 3877 */ 3878 DecodedInstruction *dInsn = &mir->dalvikInsn; 3879 const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length); 3880 const int maxC = dInsn->arg[0]; 3881 int regLength; 3882 RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA]; 3883 RegLocation rlIdxEnd = cUnit->regLocation[mir->dalvikInsn.vC]; 3884 3885 /* regArray <- arrayRef */ 3886 rlArray = loadValue(cUnit, rlArray, kCoreReg); 3887 rlIdxEnd = loadValue(cUnit, rlIdxEnd, kCoreReg); 3888 genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0, 3889 (ArmLIR *) cUnit->loopAnalysis->branchToPCR); 3890 3891 /* regLength <- len(arrayRef) */ 3892 regLength = dvmCompilerAllocTemp(cUnit); 3893 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength); 3894 3895 int delta = maxC; 3896 /* 3897 * If the loop end condition is ">=" instead of ">", then the largest value 3898 * of the index is "endCondition - 1". 3899 */ 3900 if (dInsn->arg[2] == OP_IF_GE) { 3901 delta--; 3902 } 3903 3904 if (delta) { 3905 int tReg = dvmCompilerAllocTemp(cUnit); 3906 opRegRegImm(cUnit, kOpAdd, tReg, rlIdxEnd.lowReg, delta); 3907 rlIdxEnd.lowReg = tReg; 3908 dvmCompilerFreeTemp(cUnit, tReg); 3909 } 3910 /* Punt if "regIdxEnd < len(Array)" is false */ 3911 genRegRegCheck(cUnit, kArmCondGe, rlIdxEnd.lowReg, regLength, 0, 3912 (ArmLIR *) cUnit->loopAnalysis->branchToPCR); 3913 } 3914 3915 /* 3916 * vA = arrayReg; 3917 * vB = idxReg; 3918 * vC = endConditionReg; 3919 * arg[0] = maxC 3920 * arg[1] = minC 3921 * arg[2] = loopBranchConditionCode 3922 */ 3923 static void genHoistedChecksForCountDownLoop(CompilationUnit *cUnit, MIR *mir) 3924 { 3925 DecodedInstruction *dInsn = &mir->dalvikInsn; 3926 const int lenOffset = OFFSETOF_MEMBER(ArrayObject, length); 3927 const int regLength = dvmCompilerAllocTemp(cUnit); 3928 const int maxC = dInsn->arg[0]; 3929 RegLocation rlArray = cUnit->regLocation[mir->dalvikInsn.vA]; 3930 RegLocation rlIdxInit = cUnit->regLocation[mir->dalvikInsn.vB]; 3931 3932 /* regArray <- arrayRef */ 3933 rlArray = loadValue(cUnit, rlArray, kCoreReg); 3934 rlIdxInit = loadValue(cUnit, rlIdxInit, kCoreReg); 3935 genRegImmCheck(cUnit, kArmCondEq, rlArray.lowReg, 0, 0, 3936 (ArmLIR *) cUnit->loopAnalysis->branchToPCR); 3937 3938 /* regLength <- len(arrayRef) */ 3939 loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLength); 3940 3941 if (maxC) { 3942 int tReg = dvmCompilerAllocTemp(cUnit); 3943 opRegRegImm(cUnit, kOpAdd, tReg, rlIdxInit.lowReg, maxC); 3944 rlIdxInit.lowReg = tReg; 3945 dvmCompilerFreeTemp(cUnit, tReg); 3946 } 3947 3948 /* Punt if "regIdxInit < len(Array)" is false */ 3949 genRegRegCheck(cUnit, kArmCondGe, rlIdxInit.lowReg, regLength, 0, 3950 (ArmLIR *) cUnit->loopAnalysis->branchToPCR); 3951 } 3952 3953 /* 3954 * vA = idxReg; 3955 * vB = minC; 3956 */ 3957 static void genHoistedLowerBoundCheck(CompilationUnit *cUnit, MIR *mir) 3958 { 3959 DecodedInstruction *dInsn = &mir->dalvikInsn; 3960 const int minC = dInsn->vB; 3961 RegLocation rlIdx = cUnit->regLocation[mir->dalvikInsn.vA]; 3962 3963 /* regIdx <- initial index value */ 3964 rlIdx = loadValue(cUnit, rlIdx, kCoreReg); 3965 3966 /* Punt if "regIdxInit + minC >= 0" is false */ 3967 genRegImmCheck(cUnit, kArmCondLt, rlIdx.lowReg, -minC, 0, 3968 (ArmLIR *) cUnit->loopAnalysis->branchToPCR); 3969 } 3970 3971 /* 3972 * vC = this 3973 * 3974 * A predicted inlining target looks like the following, where instructions 3975 * between 0x4858de66 and 0x4858de72 are checking if the predicted class 3976 * matches "this", and the verificaion code is generated by this routine. 3977 * 3978 * (C) means the instruction is inlined from the callee, and (PI) means the 3979 * instruction is the predicted inlined invoke, whose corresponding 3980 * instructions are still generated to handle the mispredicted case. 3981 * 3982 * D/dalvikvm( 86): -------- kMirOpCheckInlinePrediction 3983 * D/dalvikvm( 86): 0x4858de66 (0002): ldr r0, [r5, #68] 3984 * D/dalvikvm( 86): 0x4858de68 (0004): ldr r1, [pc, #140] 3985 * D/dalvikvm( 86): 0x4858de6a (0006): cmp r0, #0 3986 * D/dalvikvm( 86): 0x4858de6c (0008): beq 0x4858deb2 3987 * D/dalvikvm( 86): 0x4858de6e (000a): ldr r2, [r0, #0] 3988 * D/dalvikvm( 86): 0x4858de70 (000c): cmp r1, r2 3989 * D/dalvikvm( 86): 0x4858de72 (000e): bne 0x4858de7a 3990 * D/dalvikvm( 86): -------- dalvik offset: 0x004c @ +iget-object-quick (C) 3991 * v4, v17, (#8) 3992 * D/dalvikvm( 86): 0x4858de74 (0010): ldr r3, [r0, #8] 3993 * D/dalvikvm( 86): 0x4858de76 (0012): str r3, [r5, #16] 3994 * D/dalvikvm( 86): -------- dalvik offset: 0x004c @ 3995 * +invoke-virtual-quick/range (PI) v17..v17 3996 * D/dalvikvm( 86): 0x4858de78 (0014): b 0x4858debc 3997 * D/dalvikvm( 86): 0x4858de7a (0016): add r4,r5,#68 3998 * D/dalvikvm( 86): -------- BARRIER 3999 * D/dalvikvm( 86): 0x4858de7e (001a): ldmia r4, <r0> 4000 * D/dalvikvm( 86): -------- BARRIER 4001 * D/dalvikvm( 86): 0x4858de80 (001c): sub r7,r5,#24 4002 * D/dalvikvm( 86): 0x4858de84 (0020): cmp r0, #0 4003 * D/dalvikvm( 86): 0x4858de86 (0022): beq 0x4858deb6 4004 * D/dalvikvm( 86): -------- BARRIER 4005 * D/dalvikvm( 86): 0x4858de88 (0024): stmia r7, <r0> 4006 * D/dalvikvm( 86): -------- BARRIER 4007 * D/dalvikvm( 86): 0x4858de8a (0026): ldr r4, [pc, #104] 4008 * D/dalvikvm( 86): 0x4858de8c (0028): add r1, pc, #28 4009 * D/dalvikvm( 86): 0x4858de8e (002a): add r2, pc, #56 4010 * D/dalvikvm( 86): 0x4858de90 (002c): blx_1 0x48589198 4011 * D/dalvikvm( 86): 0x4858de92 (002e): blx_2 see above 4012 * D/dalvikvm( 86): 0x4858de94 (0030): b 0x4858dec8 4013 * D/dalvikvm( 86): 0x4858de96 (0032): b 0x4858deb6 4014 * D/dalvikvm( 86): 0x4858de98 (0034): ldr r0, [r7, #72] 4015 * D/dalvikvm( 86): 0x4858de9a (0036): cmp r1, #0 4016 * D/dalvikvm( 86): 0x4858de9c (0038): bgt 0x4858dea4 4017 * D/dalvikvm( 86): 0x4858de9e (003a): ldr r7, [r6, #116] 4018 * D/dalvikvm( 86): 0x4858dea0 (003c): movs r1, r6 4019 * D/dalvikvm( 86): 0x4858dea2 (003e): blx r7 4020 * D/dalvikvm( 86): 0x4858dea4 (0040): add r1, pc, #4 4021 * D/dalvikvm( 86): 0x4858dea6 (0042): blx_1 0x485890a0 4022 * D/dalvikvm( 86): 0x4858dea8 (0044): blx_2 see above 4023 * D/dalvikvm( 86): 0x4858deaa (0046): b 0x4858deb6 4024 * D/dalvikvm( 86): 0x4858deac (0048): .align4 4025 * D/dalvikvm( 86): L0x004f: 4026 * D/dalvikvm( 86): -------- dalvik offset: 0x004f @ move-result-object (PI) 4027 * v4, (#0), (#0) 4028 * D/dalvikvm( 86): 0x4858deac (0048): ldr r4, [r6, #8] 4029 * D/dalvikvm( 86): 0x4858deae (004a): str r4, [r5, #16] 4030 * D/dalvikvm( 86): 0x4858deb0 (004c): b 0x4858debc 4031 * D/dalvikvm( 86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c 4032 * D/dalvikvm( 86): 0x4858deb2 (004e): ldr r0, [pc, #64] 4033 * D/dalvikvm( 86): 0x4858deb4 (0050): b 0x4858deb8 4034 * D/dalvikvm( 86): -------- reconstruct dalvik PC : 0x42beefcc @ +0x004c 4035 * D/dalvikvm( 86): 0x4858deb6 (0052): ldr r0, [pc, #60] 4036 * D/dalvikvm( 86): Exception_Handling: 4037 * D/dalvikvm( 86): 0x4858deb8 (0054): ldr r1, [r6, #100] 4038 * D/dalvikvm( 86): 0x4858deba (0056): blx r1 4039 * D/dalvikvm( 86): 0x4858debc (0058): .align4 4040 * D/dalvikvm( 86): -------- chaining cell (hot): 0x0050 4041 * D/dalvikvm( 86): 0x4858debc (0058): b 0x4858dec0 4042 * D/dalvikvm( 86): 0x4858debe (005a): orrs r0, r0 4043 * D/dalvikvm( 86): 0x4858dec0 (005c): ldr r0, [r6, #112] 4044 * D/dalvikvm( 86): 0x4858dec2 (005e): blx r0 4045 * D/dalvikvm( 86): 0x4858dec4 (0060): data 0xefd4(61396) 4046 * D/dalvikvm( 86): 0x4858dec6 (0062): data 0x42be(17086) 4047 * D/dalvikvm( 86): 0x4858dec8 (0064): .align4 4048 * D/dalvikvm( 86): -------- chaining cell (predicted) 4049 * D/dalvikvm( 86): 0x4858dec8 (0064): data 0xe7fe(59390) 4050 * D/dalvikvm( 86): 0x4858deca (0066): data 0x0000(0) 4051 * D/dalvikvm( 86): 0x4858decc (0068): data 0x0000(0) 4052 * D/dalvikvm( 86): 0x4858dece (006a): data 0x0000(0) 4053 * : 4054 */ 4055 static void genValidationForPredictedInline(CompilationUnit *cUnit, MIR *mir) 4056 { 4057 CallsiteInfo *callsiteInfo = mir->meta.callsiteInfo; 4058 RegLocation rlThis = cUnit->regLocation[mir->dalvikInsn.vC]; 4059 4060 rlThis = loadValue(cUnit, rlThis, kCoreReg); 4061 int regPredictedClass = dvmCompilerAllocTemp(cUnit); 4062 loadClassPointer(cUnit, regPredictedClass, (int) callsiteInfo); 4063 genNullCheck(cUnit, rlThis.sRegLow, rlThis.lowReg, mir->offset, 4064 NULL);/* null object? */ 4065 int regActualClass = dvmCompilerAllocTemp(cUnit); 4066 loadWordDisp(cUnit, rlThis.lowReg, offsetof(Object, clazz), regActualClass); 4067 opRegReg(cUnit, kOpCmp, regPredictedClass, regActualClass); 4068 /* 4069 * Set the misPredBranchOver target so that it will be generated when the 4070 * code for the non-optimized invoke is generated. 4071 */ 4072 callsiteInfo->misPredBranchOver = (LIR *) opCondBranch(cUnit, kArmCondNe); 4073 } 4074 4075 /* Extended MIR instructions like PHI */ 4076 static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir) 4077 { 4078 int opOffset = mir->dalvikInsn.opcode - kMirOpFirst; 4079 char *msg = (char *)dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1, 4080 false); 4081 strcpy(msg, extendedMIROpNames[opOffset]); 4082 newLIR1(cUnit, kArmPseudoExtended, (int) msg); 4083 4084 switch ((ExtendedMIROpcode)mir->dalvikInsn.opcode) { 4085 case kMirOpPhi: { 4086 char *ssaString = dvmCompilerGetSSAString(cUnit, mir->ssaRep); 4087 newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString); 4088 break; 4089 } 4090 case kMirOpNullNRangeUpCheck: { 4091 genHoistedChecksForCountUpLoop(cUnit, mir); 4092 break; 4093 } 4094 case kMirOpNullNRangeDownCheck: { 4095 genHoistedChecksForCountDownLoop(cUnit, mir); 4096 break; 4097 } 4098 case kMirOpLowerBound: { 4099 genHoistedLowerBoundCheck(cUnit, mir); 4100 break; 4101 } 4102 case kMirOpPunt: { 4103 genUnconditionalBranch(cUnit, 4104 (ArmLIR *) cUnit->loopAnalysis->branchToPCR); 4105 break; 4106 } 4107 case kMirOpCheckInlinePrediction: { 4108 genValidationForPredictedInline(cUnit, mir); 4109 break; 4110 } 4111 default: 4112 break; 4113 } 4114 } 4115 4116 /* 4117 * Create a PC-reconstruction cell for the starting offset of this trace. 4118 * Since the PCR cell is placed near the end of the compiled code which is 4119 * usually out of range for a conditional branch, we put two branches (one 4120 * branch over to the loop body and one layover branch to the actual PCR) at the 4121 * end of the entry block. 4122 */ 4123 static void setupLoopEntryBlock(CompilationUnit *cUnit, BasicBlock *entry, 4124 ArmLIR *bodyLabel) 4125 { 4126 /* Set up the place holder to reconstruct this Dalvik PC */ 4127 ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 4128 pcrLabel->opcode = kArmPseudoPCReconstructionCell; 4129 pcrLabel->operands[0] = 4130 (int) (cUnit->method->insns + entry->startOffset); 4131 pcrLabel->operands[1] = entry->startOffset; 4132 /* Insert the place holder to the growable list */ 4133 dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel); 4134 4135 /* 4136 * Next, create two branches - one branch over to the loop body and the 4137 * other branch to the PCR cell to punt. 4138 */ 4139 ArmLIR *branchToBody = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 4140 branchToBody->opcode = kThumbBUncond; 4141 branchToBody->generic.target = (LIR *) bodyLabel; 4142 setupResourceMasks(branchToBody); 4143 cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody; 4144 4145 ArmLIR *branchToPCR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true); 4146 branchToPCR->opcode = kThumbBUncond; 4147 branchToPCR->generic.target = (LIR *) pcrLabel; 4148 setupResourceMasks(branchToPCR); 4149 cUnit->loopAnalysis->branchToPCR = (LIR *) branchToPCR; 4150 } 4151 4152 #if defined(WITH_SELF_VERIFICATION) 4153 static bool selfVerificationPuntOps(MIR *mir) 4154 { 4155 DecodedInstruction *decInsn = &mir->dalvikInsn; 4156 4157 /* 4158 * All opcodes that can throw exceptions and use the 4159 * TEMPLATE_THROW_EXCEPTION_COMMON template should be excluded in the trace 4160 * under self-verification mode. 4161 */ 4162 switch (decInsn->opcode) { 4163 case OP_MONITOR_ENTER: 4164 case OP_MONITOR_EXIT: 4165 case OP_NEW_INSTANCE: 4166 case OP_NEW_ARRAY: 4167 case OP_CHECK_CAST: 4168 case OP_MOVE_EXCEPTION: 4169 case OP_FILL_ARRAY_DATA: 4170 case OP_EXECUTE_INLINE: 4171 case OP_EXECUTE_INLINE_RANGE: 4172 return true; 4173 default: 4174 return false; 4175 } 4176 } 4177 #endif 4178 4179 void dvmCompilerMIR2LIR(CompilationUnit *cUnit) 4180 { 4181 /* Used to hold the labels of each block */ 4182 ArmLIR *labelList = 4183 (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true); 4184 ArmLIR *headLIR = NULL; 4185 GrowableList chainingListByType[kChainingCellGap]; 4186 int i; 4187 4188 /* 4189 * Initialize various types chaining lists. 4190 */ 4191 for (i = 0; i < kChainingCellGap; i++) { 4192 dvmInitGrowableList(&chainingListByType[i], 2); 4193 } 4194 4195 /* Clear the visited flag for each block */ 4196 dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag, 4197 kAllNodes, false /* isIterative */); 4198 4199 GrowableListIterator iterator; 4200 dvmGrowableListIteratorInit(&cUnit->blockList, &iterator); 4201 4202 /* Traces start with a profiling entry point. Generate it here */ 4203 cUnit->profileCodeSize = genTraceProfileEntry(cUnit); 4204 4205 /* Handle the content in each basic block */ 4206 for (i = 0; ; i++) { 4207 MIR *mir; 4208 BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator); 4209 if (bb == NULL) break; 4210 if (bb->visited == true) continue; 4211 4212 labelList[i].operands[0] = bb->startOffset; 4213 4214 if (bb->blockType >= kChainingCellGap) { 4215 if (bb->isFallThroughFromInvoke == true) { 4216 /* Align this block first since it is a return chaining cell */ 4217 newLIR0(cUnit, kArmPseudoPseudoAlign4); 4218 } 4219 /* 4220 * Append the label pseudo LIR first. Chaining cells will be handled 4221 * separately afterwards. 4222 */ 4223 dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]); 4224 } 4225 4226 if (bb->blockType == kEntryBlock) { 4227 labelList[i].opcode = kArmPseudoEntryBlock; 4228 if (bb->firstMIRInsn == NULL) { 4229 continue; 4230 } else { 4231 setupLoopEntryBlock(cUnit, bb, 4232 &labelList[bb->fallThrough->id]); 4233 } 4234 } else if (bb->blockType == kExitBlock) { 4235 labelList[i].opcode = kArmPseudoExitBlock; 4236 goto gen_fallthrough; 4237 } else if (bb->blockType == kDalvikByteCode) { 4238 if (bb->hidden == true) continue; 4239 labelList[i].opcode = kArmPseudoNormalBlockLabel; 4240 /* Reset the register state */ 4241 dvmCompilerResetRegPool(cUnit); 4242 dvmCompilerClobberAllRegs(cUnit); 4243 dvmCompilerResetNullCheck(cUnit); 4244 } else { 4245 switch (bb->blockType) { 4246 case kChainingCellNormal: 4247 labelList[i].opcode = kArmPseudoChainingCellNormal; 4248 /* handle the codegen later */ 4249 dvmInsertGrowableList( 4250 &chainingListByType[kChainingCellNormal], i); 4251 break; 4252 case kChainingCellInvokeSingleton: 4253 labelList[i].opcode = 4254 kArmPseudoChainingCellInvokeSingleton; 4255 labelList[i].operands[0] = 4256 (int) bb->containingMethod; 4257 /* handle the codegen later */ 4258 dvmInsertGrowableList( 4259 &chainingListByType[kChainingCellInvokeSingleton], i); 4260 break; 4261 case kChainingCellInvokePredicted: 4262 labelList[i].opcode = 4263 kArmPseudoChainingCellInvokePredicted; 4264 /* 4265 * Move the cached method pointer from operand 1 to 0. 4266 * Operand 0 was clobbered earlier in this routine to store 4267 * the block starting offset, which is not applicable to 4268 * predicted chaining cell. 4269 */ 4270 labelList[i].operands[0] = labelList[i].operands[1]; 4271 /* handle the codegen later */ 4272 dvmInsertGrowableList( 4273 &chainingListByType[kChainingCellInvokePredicted], i); 4274 break; 4275 case kChainingCellHot: 4276 labelList[i].opcode = 4277 kArmPseudoChainingCellHot; 4278 /* handle the codegen later */ 4279 dvmInsertGrowableList( 4280 &chainingListByType[kChainingCellHot], i); 4281 break; 4282 case kPCReconstruction: 4283 /* Make sure exception handling block is next */ 4284 labelList[i].opcode = 4285 kArmPseudoPCReconstructionBlockLabel; 4286 handlePCReconstruction(cUnit, 4287 &labelList[cUnit->puntBlock->id]); 4288 break; 4289 case kExceptionHandling: 4290 labelList[i].opcode = kArmPseudoEHBlockLabel; 4291 if (cUnit->pcReconstructionList.numUsed) { 4292 loadWordDisp(cUnit, r6SELF, offsetof(Thread, 4293 jitToInterpEntries.dvmJitToInterpPunt), 4294 r1); 4295 opReg(cUnit, kOpBlx, r1); 4296 } 4297 break; 4298 case kChainingCellBackwardBranch: 4299 labelList[i].opcode = 4300 kArmPseudoChainingCellBackwardBranch; 4301 /* handle the codegen later */ 4302 dvmInsertGrowableList( 4303 &chainingListByType[kChainingCellBackwardBranch], 4304 i); 4305 break; 4306 default: 4307 break; 4308 } 4309 continue; 4310 } 4311 4312 /* 4313 * Try to build a longer optimization unit. Currently if the previous 4314 * block ends with a goto, we continue adding instructions and don't 4315 * reset the register allocation pool. 4316 */ 4317 for (BasicBlock *nextBB = bb; nextBB != NULL; nextBB = cUnit->nextCodegenBlock) { 4318 bb = nextBB; 4319 bb->visited = true; 4320 cUnit->nextCodegenBlock = NULL; 4321 4322 for (mir = bb->firstMIRInsn; mir; mir = mir->next) { 4323 4324 dvmCompilerResetRegPool(cUnit); 4325 if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) { 4326 dvmCompilerClobberAllRegs(cUnit); 4327 } 4328 4329 if (gDvmJit.disableOpt & (1 << kSuppressLoads)) { 4330 dvmCompilerResetDefTracking(cUnit); 4331 } 4332 4333 if ((int)mir->dalvikInsn.opcode >= (int)kMirOpFirst) { 4334 handleExtendedMIR(cUnit, mir); 4335 continue; 4336 } 4337 4338 Opcode dalvikOpcode = mir->dalvikInsn.opcode; 4339 InstructionFormat dalvikFormat = 4340 dexGetFormatFromOpcode(dalvikOpcode); 4341 const char *note; 4342 if (mir->OptimizationFlags & MIR_INLINED) { 4343 note = " (I)"; 4344 } else if (mir->OptimizationFlags & MIR_INLINED_PRED) { 4345 note = " (PI)"; 4346 } else if (mir->OptimizationFlags & MIR_CALLEE) { 4347 note = " (C)"; 4348 } else { 4349 note = NULL; 4350 } 4351 4352 ArmLIR *boundaryLIR; 4353 4354 /* 4355 * Don't generate the boundary LIR unless we are debugging this 4356 * trace or we need a scheduling barrier. 4357 */ 4358 if (headLIR == NULL || cUnit->printMe == true) { 4359 boundaryLIR = 4360 newLIR2(cUnit, kArmPseudoDalvikByteCodeBoundary, 4361 mir->offset, 4362 (int) dvmCompilerGetDalvikDisassembly( 4363 &mir->dalvikInsn, note)); 4364 /* Remember the first LIR for this block */ 4365 if (headLIR == NULL) { 4366 headLIR = boundaryLIR; 4367 /* Set the first boundaryLIR as a scheduling barrier */ 4368 headLIR->defMask = ENCODE_ALL; 4369 } 4370 } 4371 4372 /* 4373 * Don't generate the SSA annotation unless verbose mode is on 4374 */ 4375 if (cUnit->printMe && mir->ssaRep) { 4376 char *ssaString = dvmCompilerGetSSAString(cUnit, 4377 mir->ssaRep); 4378 newLIR1(cUnit, kArmPseudoSSARep, (int) ssaString); 4379 } 4380 4381 bool notHandled; 4382 /* 4383 * Debugging: screen the opcode first to see if it is in the 4384 * do[-not]-compile list 4385 */ 4386 bool singleStepMe = SINGLE_STEP_OP(dalvikOpcode); 4387 #if defined(WITH_SELF_VERIFICATION) 4388 if (singleStepMe == false) { 4389 singleStepMe = selfVerificationPuntOps(mir); 4390 } 4391 #endif 4392 if (singleStepMe || cUnit->allSingleStep) { 4393 notHandled = false; 4394 genInterpSingleStep(cUnit, mir); 4395 } else { 4396 opcodeCoverage[dalvikOpcode]++; 4397 switch (dalvikFormat) { 4398 case kFmt10t: 4399 case kFmt20t: 4400 case kFmt30t: 4401 notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit, 4402 mir, bb, labelList); 4403 break; 4404 case kFmt10x: 4405 notHandled = handleFmt10x(cUnit, mir); 4406 break; 4407 case kFmt11n: 4408 case kFmt31i: 4409 notHandled = handleFmt11n_Fmt31i(cUnit, mir); 4410 break; 4411 case kFmt11x: 4412 notHandled = handleFmt11x(cUnit, mir); 4413 break; 4414 case kFmt12x: 4415 notHandled = handleFmt12x(cUnit, mir); 4416 break; 4417 case kFmt20bc: 4418 notHandled = handleFmt20bc(cUnit, mir); 4419 break; 4420 case kFmt21c: 4421 case kFmt31c: 4422 notHandled = handleFmt21c_Fmt31c(cUnit, mir); 4423 break; 4424 case kFmt21h: 4425 notHandled = handleFmt21h(cUnit, mir); 4426 break; 4427 case kFmt21s: 4428 notHandled = handleFmt21s(cUnit, mir); 4429 break; 4430 case kFmt21t: 4431 notHandled = handleFmt21t(cUnit, mir, bb, 4432 labelList); 4433 break; 4434 case kFmt22b: 4435 case kFmt22s: 4436 notHandled = handleFmt22b_Fmt22s(cUnit, mir); 4437 break; 4438 case kFmt22c: 4439 notHandled = handleFmt22c(cUnit, mir); 4440 break; 4441 case kFmt22cs: 4442 notHandled = handleFmt22cs(cUnit, mir); 4443 break; 4444 case kFmt22t: 4445 notHandled = handleFmt22t(cUnit, mir, bb, 4446 labelList); 4447 break; 4448 case kFmt22x: 4449 case kFmt32x: 4450 notHandled = handleFmt22x_Fmt32x(cUnit, mir); 4451 break; 4452 case kFmt23x: 4453 notHandled = handleFmt23x(cUnit, mir); 4454 break; 4455 case kFmt31t: 4456 notHandled = handleFmt31t(cUnit, mir); 4457 break; 4458 case kFmt3rc: 4459 case kFmt35c: 4460 notHandled = handleFmt35c_3rc(cUnit, mir, bb, 4461 labelList); 4462 break; 4463 case kFmt3rms: 4464 case kFmt35ms: 4465 notHandled = handleFmt35ms_3rms(cUnit, mir, bb, 4466 labelList); 4467 break; 4468 case kFmt35mi: 4469 case kFmt3rmi: 4470 notHandled = handleExecuteInline(cUnit, mir); 4471 break; 4472 case kFmt51l: 4473 notHandled = handleFmt51l(cUnit, mir); 4474 break; 4475 default: 4476 notHandled = true; 4477 break; 4478 } 4479 } 4480 if (notHandled) { 4481 ALOGE("%#06x: Opcode %#x (%s) / Fmt %d not handled", 4482 mir->offset, 4483 dalvikOpcode, dexGetOpcodeName(dalvikOpcode), 4484 dalvikFormat); 4485 dvmCompilerAbort(cUnit); 4486 break; 4487 } 4488 } 4489 } 4490 4491 if (bb->blockType == kEntryBlock) { 4492 dvmCompilerAppendLIR(cUnit, 4493 (LIR *) cUnit->loopAnalysis->branchToBody); 4494 dvmCompilerAppendLIR(cUnit, 4495 (LIR *) cUnit->loopAnalysis->branchToPCR); 4496 } 4497 4498 if (headLIR) { 4499 /* 4500 * Eliminate redundant loads/stores and delay stores into later 4501 * slots 4502 */ 4503 dvmCompilerApplyLocalOptimizations(cUnit, (LIR *) headLIR, 4504 cUnit->lastLIRInsn); 4505 /* Reset headLIR which is also the optimization boundary */ 4506 headLIR = NULL; 4507 } 4508 4509 gen_fallthrough: 4510 /* 4511 * Check if the block is terminated due to trace length constraint - 4512 * insert an unconditional branch to the chaining cell. 4513 */ 4514 if (bb->needFallThroughBranch) { 4515 genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); 4516 } 4517 } 4518 4519 /* Handle the chaining cells in predefined order */ 4520 for (i = 0; i < kChainingCellGap; i++) { 4521 size_t j; 4522 int *blockIdList = (int *) chainingListByType[i].elemList; 4523 4524 cUnit->numChainingCells[i] = chainingListByType[i].numUsed; 4525 4526 /* No chaining cells of this type */ 4527 if (cUnit->numChainingCells[i] == 0) 4528 continue; 4529 4530 /* Record the first LIR for a new type of chaining cell */ 4531 cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]]; 4532 4533 for (j = 0; j < chainingListByType[i].numUsed; j++) { 4534 int blockId = blockIdList[j]; 4535 BasicBlock *chainingBlock = 4536 (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 4537 blockId); 4538 4539 /* Align this chaining cell first */ 4540 newLIR0(cUnit, kArmPseudoPseudoAlign4); 4541 4542 /* Insert the pseudo chaining instruction */ 4543 dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]); 4544 4545 4546 switch (chainingBlock->blockType) { 4547 case kChainingCellNormal: 4548 handleNormalChainingCell(cUnit, chainingBlock->startOffset); 4549 break; 4550 case kChainingCellInvokeSingleton: 4551 handleInvokeSingletonChainingCell(cUnit, 4552 chainingBlock->containingMethod); 4553 break; 4554 case kChainingCellInvokePredicted: 4555 handleInvokePredictedChainingCell(cUnit); 4556 break; 4557 case kChainingCellHot: 4558 handleHotChainingCell(cUnit, chainingBlock->startOffset); 4559 break; 4560 case kChainingCellBackwardBranch: 4561 handleBackwardBranchChainingCell(cUnit, 4562 chainingBlock->startOffset); 4563 break; 4564 default: 4565 ALOGE("Bad blocktype %d", chainingBlock->blockType); 4566 dvmCompilerAbort(cUnit); 4567 } 4568 } 4569 } 4570 4571 /* Mark the bottom of chaining cells */ 4572 cUnit->chainingCellBottom = (LIR *) newLIR0(cUnit, kArmChainingCellBottom); 4573 4574 /* 4575 * Generate the branch to the dvmJitToInterpNoChain entry point at the end 4576 * of all chaining cells for the overflow cases. 4577 */ 4578 if (cUnit->switchOverflowPad) { 4579 loadConstant(cUnit, r0, (int) cUnit->switchOverflowPad); 4580 loadWordDisp(cUnit, r6SELF, offsetof(Thread, 4581 jitToInterpEntries.dvmJitToInterpNoChain), r2); 4582 opRegReg(cUnit, kOpAdd, r1, r1); 4583 opRegRegReg(cUnit, kOpAdd, r4PC, r0, r1); 4584 #if defined(WITH_JIT_TUNING) 4585 loadConstant(cUnit, r0, kSwitchOverflow); 4586 #endif 4587 opReg(cUnit, kOpBlx, r2); 4588 } 4589 4590 dvmCompilerApplyGlobalOptimizations(cUnit); 4591 4592 #if defined(WITH_SELF_VERIFICATION) 4593 selfVerificationBranchInsertPass(cUnit); 4594 #endif 4595 } 4596 4597 /* 4598 * Accept the work and start compiling. Returns true if compilation 4599 * is attempted. 4600 */ 4601 bool dvmCompilerDoWork(CompilerWorkOrder *work) 4602 { 4603 JitTraceDescription *desc; 4604 bool isCompile; 4605 bool success = true; 4606 4607 if (gDvmJit.codeCacheFull) { 4608 return false; 4609 } 4610 4611 switch (work->kind) { 4612 case kWorkOrderTrace: 4613 isCompile = true; 4614 /* Start compilation with maximally allowed trace length */ 4615 desc = (JitTraceDescription *)work->info; 4616 success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result, 4617 work->bailPtr, 0 /* no hints */); 4618 break; 4619 case kWorkOrderTraceDebug: { 4620 bool oldPrintMe = gDvmJit.printMe; 4621 gDvmJit.printMe = true; 4622 isCompile = true; 4623 /* Start compilation with maximally allowed trace length */ 4624 desc = (JitTraceDescription *)work->info; 4625 success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result, 4626 work->bailPtr, 0 /* no hints */); 4627 gDvmJit.printMe = oldPrintMe; 4628 break; 4629 } 4630 case kWorkOrderProfileMode: 4631 dvmJitChangeProfileMode((TraceProfilingModes)(int)work->info); 4632 isCompile = false; 4633 break; 4634 default: 4635 isCompile = false; 4636 ALOGE("Jit: unknown work order type"); 4637 assert(0); // Bail if debug build, discard otherwise 4638 } 4639 if (!success) 4640 work->result.codeAddress = NULL; 4641 return isCompile; 4642 } 4643 4644 /* Architectural-specific debugging helpers go here */ 4645 void dvmCompilerArchDump(void) 4646 { 4647 /* Print compiled opcode in this VM instance */ 4648 int i, start, streak; 4649 char buf[1024]; 4650 4651 streak = i = 0; 4652 buf[0] = 0; 4653 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) { 4654 i++; 4655 } 4656 if (i == kNumPackedOpcodes) { 4657 return; 4658 } 4659 for (start = i++, streak = 1; i < kNumPackedOpcodes; i++) { 4660 if (opcodeCoverage[i]) { 4661 streak++; 4662 } else { 4663 if (streak == 1) { 4664 sprintf(buf+strlen(buf), "%x,", start); 4665 } else { 4666 sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1); 4667 } 4668 streak = 0; 4669 while (opcodeCoverage[i] == 0 && i < kNumPackedOpcodes) { 4670 i++; 4671 } 4672 if (i < kNumPackedOpcodes) { 4673 streak = 1; 4674 start = i; 4675 } 4676 } 4677 } 4678 if (streak) { 4679 if (streak == 1) { 4680 sprintf(buf+strlen(buf), "%x", start); 4681 } else { 4682 sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1); 4683 } 4684 } 4685 if (strlen(buf)) { 4686 ALOGD("dalvik.vm.jit.op = %s", buf); 4687 } 4688 } 4689 4690 /* Common initialization routine for an architecture family */ 4691 bool dvmCompilerArchInit() 4692 { 4693 int i; 4694 4695 for (i = 0; i < kArmLast; i++) { 4696 if (EncodingMap[i].opcode != i) { 4697 ALOGE("Encoding order for %s is wrong: expecting %d, seeing %d", 4698 EncodingMap[i].name, i, EncodingMap[i].opcode); 4699 dvmAbort(); // OK to dvmAbort - build error 4700 } 4701 } 4702 4703 return dvmCompilerArchVariantInit(); 4704 } 4705 4706 void *dvmCompilerGetInterpretTemplate() 4707 { 4708 return (void*) ((int)gDvmJit.codeCache + 4709 templateEntryOffsets[TEMPLATE_INTERPRET]); 4710 } 4711 4712 JitInstructionSetType dvmCompilerGetInterpretTemplateSet() 4713 { 4714 return DALVIK_JIT_ARM; 4715 } 4716 4717 /* Needed by the Assembler */ 4718 void dvmCompilerSetupResourceMasks(ArmLIR *lir) 4719 { 4720 setupResourceMasks(lir); 4721 } 4722 4723 /* Needed by the ld/st optmizatons */ 4724 ArmLIR* dvmCompilerRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc) 4725 { 4726 return genRegCopyNoInsert(cUnit, rDest, rSrc); 4727 } 4728 4729 /* Needed by the register allocator */ 4730 ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc) 4731 { 4732 return genRegCopy(cUnit, rDest, rSrc); 4733 } 4734 4735 /* Needed by the register allocator */ 4736 void dvmCompilerRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi, 4737 int srcLo, int srcHi) 4738 { 4739 genRegCopyWide(cUnit, destLo, destHi, srcLo, srcHi); 4740 } 4741 4742 void dvmCompilerFlushRegImpl(CompilationUnit *cUnit, int rBase, 4743 int displacement, int rSrc, OpSize size) 4744 { 4745 storeBaseDisp(cUnit, rBase, displacement, rSrc, size); 4746 } 4747 4748 void dvmCompilerFlushRegWideImpl(CompilationUnit *cUnit, int rBase, 4749 int displacement, int rSrcLo, int rSrcHi) 4750 { 4751 storeBaseDispWide(cUnit, rBase, displacement, rSrcLo, rSrcHi); 4752 } 4753