1 /* 2 * Copyright (C) 2008 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 * Perform some simple bytecode optimizations, chiefly "quickening" of 19 * opcodes. 20 */ 21 #include "Dalvik.h" 22 #include "libdex/InstrUtils.h" 23 #include "Optimize.h" 24 25 #include <zlib.h> 26 27 #include <stdlib.h> 28 29 /* 30 * Virtual/direct calls to "method" are replaced with an execute-inline 31 * instruction with index "idx". 32 */ 33 struct InlineSub { 34 Method* method; 35 int inlineIdx; 36 }; 37 38 39 /* fwd */ 40 static void optimizeMethod(Method* method, bool essentialOnly); 41 static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc, 42 Opcode volatileOpc); 43 static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc); 44 static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc); 45 static bool rewriteInvokeObjectInit(Method* method, u2* insns); 46 static bool rewriteExecuteInline(Method* method, u2* insns, 47 MethodType methodType); 48 static bool rewriteExecuteInlineRange(Method* method, u2* insns, 49 MethodType methodType); 50 static void rewriteReturnVoid(Method* method, u2* insns); 51 static bool needsReturnBarrier(Method* method); 52 53 54 /* 55 * Create a table of inline substitutions. Sets gDvm.inlineSubs. 56 * 57 * TODO: this is currently just a linear array. We will want to put this 58 * into a hash table as the list size increases. 59 */ 60 bool dvmCreateInlineSubsTable() 61 { 62 const InlineOperation* ops = dvmGetInlineOpsTable(); 63 const int count = dvmGetInlineOpsTableLength(); 64 InlineSub* table; 65 int i, tableIndex; 66 67 assert(gDvm.inlineSubs == NULL); 68 69 /* 70 * One slot per entry, plus an end-of-list marker. 71 */ 72 table = (InlineSub*) calloc(count + 1, sizeof(InlineSub)); 73 74 tableIndex = 0; 75 for (i = 0; i < count; i++) { 76 Method* method = dvmFindInlinableMethod(ops[i].classDescriptor, 77 ops[i].methodName, ops[i].methodSignature); 78 if (method == NULL) { 79 /* 80 * Not expected. We only use this for key methods in core 81 * classes, so we should always be able to find them. 82 */ 83 ALOGE("Unable to find method for inlining: %s.%s:%s", 84 ops[i].classDescriptor, ops[i].methodName, 85 ops[i].methodSignature); 86 free(table); 87 return false; 88 } 89 90 table[tableIndex].method = method; 91 table[tableIndex].inlineIdx = i; 92 tableIndex++; 93 } 94 95 /* mark end of table */ 96 table[tableIndex].method = NULL; 97 98 gDvm.inlineSubs = table; 99 return true; 100 } 101 102 /* 103 * Release inline sub data structure. 104 */ 105 void dvmFreeInlineSubsTable() 106 { 107 free(gDvm.inlineSubs); 108 gDvm.inlineSubs = NULL; 109 } 110 111 112 /* 113 * Optimize the specified class. 114 * 115 * If "essentialOnly" is true, we only do essential optimizations. For 116 * example, accesses to volatile 64-bit fields must be replaced with 117 * "-wide-volatile" instructions or the program could behave incorrectly. 118 * (Skipping non-essential optimizations makes us a little bit faster, and 119 * more importantly avoids dirtying DEX pages.) 120 */ 121 void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly) 122 { 123 int i; 124 125 for (i = 0; i < clazz->directMethodCount; i++) { 126 optimizeMethod(&clazz->directMethods[i], essentialOnly); 127 } 128 for (i = 0; i < clazz->virtualMethodCount; i++) { 129 optimizeMethod(&clazz->virtualMethods[i], essentialOnly); 130 } 131 } 132 133 /* 134 * Optimize instructions in a method. 135 * 136 * This does a single pass through the code, examining each instruction. 137 * 138 * This is not expected to fail if the class was successfully verified. 139 * The only significant failure modes on unverified code occur when an 140 * "essential" update fails, but we can't generally identify those: if we 141 * can't look up a field, we can't know if the field access was supposed 142 * to be handled as volatile. 143 * 144 * Instead, we give it our best effort, and hope for the best. For 100% 145 * reliability, only optimize a class after verification succeeds. 146 */ 147 static void optimizeMethod(Method* method, bool essentialOnly) 148 { 149 bool needRetBar, forSmp; 150 u4 insnsSize; 151 u2* insns; 152 153 if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method)) 154 return; 155 156 forSmp = gDvm.dexOptForSmp; 157 needRetBar = needsReturnBarrier(method); 158 159 insns = (u2*) method->insns; 160 assert(insns != NULL); 161 insnsSize = dvmGetMethodInsnsSize(method); 162 163 while (insnsSize > 0) { 164 Opcode opc, quickOpc, volatileOpc; 165 size_t width; 166 bool matched = true; 167 168 opc = dexOpcodeFromCodeUnit(*insns); 169 width = dexGetWidthFromInstruction(insns); 170 volatileOpc = OP_NOP; 171 172 /* 173 * Each instruction may have: 174 * - "volatile" replacement 175 * - may be essential or essential-on-SMP 176 * - correctness replacement 177 * - may be essential or essential-on-SMP 178 * - performance replacement 179 * - always non-essential 180 * 181 * Replacements are considered in the order shown, and the first 182 * match is applied. For example, iget-wide will convert to 183 * iget-wide-volatile rather than iget-wide-quick if the target 184 * field is volatile. 185 */ 186 187 /* 188 * essential substitutions: 189 * {iget,iput,sget,sput}-wide --> {op}-wide-volatile 190 * invoke-direct[/range] --> invoke-object-init/range 191 * 192 * essential-on-SMP substitutions: 193 * {iget,iput,sget,sput}-* --> {op}-volatile 194 * return-void --> return-void-barrier 195 * 196 * non-essential substitutions: 197 * {iget,iput}-* --> {op}-quick 198 * 199 * TODO: might be time to merge this with the other two switches 200 */ 201 switch (opc) { 202 case OP_IGET: 203 case OP_IGET_BOOLEAN: 204 case OP_IGET_BYTE: 205 case OP_IGET_CHAR: 206 case OP_IGET_SHORT: 207 quickOpc = OP_IGET_QUICK; 208 if (forSmp) 209 volatileOpc = OP_IGET_VOLATILE; 210 goto rewrite_inst_field; 211 case OP_IGET_WIDE: 212 quickOpc = OP_IGET_WIDE_QUICK; 213 volatileOpc = OP_IGET_WIDE_VOLATILE; 214 goto rewrite_inst_field; 215 case OP_IGET_OBJECT: 216 quickOpc = OP_IGET_OBJECT_QUICK; 217 if (forSmp) 218 volatileOpc = OP_IGET_OBJECT_VOLATILE; 219 goto rewrite_inst_field; 220 case OP_IPUT: 221 case OP_IPUT_BOOLEAN: 222 case OP_IPUT_BYTE: 223 case OP_IPUT_CHAR: 224 case OP_IPUT_SHORT: 225 quickOpc = OP_IPUT_QUICK; 226 if (forSmp) 227 volatileOpc = OP_IPUT_VOLATILE; 228 goto rewrite_inst_field; 229 case OP_IPUT_WIDE: 230 quickOpc = OP_IPUT_WIDE_QUICK; 231 volatileOpc = OP_IPUT_WIDE_VOLATILE; 232 goto rewrite_inst_field; 233 case OP_IPUT_OBJECT: 234 quickOpc = OP_IPUT_OBJECT_QUICK; 235 if (forSmp) 236 volatileOpc = OP_IPUT_OBJECT_VOLATILE; 237 /* fall through */ 238 rewrite_inst_field: 239 if (essentialOnly) 240 quickOpc = OP_NOP; /* if essential-only, no "-quick" sub */ 241 if (quickOpc != OP_NOP || volatileOpc != OP_NOP) 242 rewriteInstField(method, insns, quickOpc, volatileOpc); 243 break; 244 245 case OP_SGET: 246 case OP_SGET_BOOLEAN: 247 case OP_SGET_BYTE: 248 case OP_SGET_CHAR: 249 case OP_SGET_SHORT: 250 if (forSmp) 251 volatileOpc = OP_SGET_VOLATILE; 252 goto rewrite_static_field; 253 case OP_SGET_WIDE: 254 volatileOpc = OP_SGET_WIDE_VOLATILE; 255 goto rewrite_static_field; 256 case OP_SGET_OBJECT: 257 if (forSmp) 258 volatileOpc = OP_SGET_OBJECT_VOLATILE; 259 goto rewrite_static_field; 260 case OP_SPUT: 261 case OP_SPUT_BOOLEAN: 262 case OP_SPUT_BYTE: 263 case OP_SPUT_CHAR: 264 case OP_SPUT_SHORT: 265 if (forSmp) 266 volatileOpc = OP_SPUT_VOLATILE; 267 goto rewrite_static_field; 268 case OP_SPUT_WIDE: 269 volatileOpc = OP_SPUT_WIDE_VOLATILE; 270 goto rewrite_static_field; 271 case OP_SPUT_OBJECT: 272 if (forSmp) 273 volatileOpc = OP_SPUT_OBJECT_VOLATILE; 274 /* fall through */ 275 rewrite_static_field: 276 if (volatileOpc != OP_NOP) 277 rewriteStaticField(method, insns, volatileOpc); 278 break; 279 280 case OP_INVOKE_DIRECT: 281 case OP_INVOKE_DIRECT_RANGE: 282 if (!rewriteInvokeObjectInit(method, insns)) { 283 /* may want to try execute-inline, below */ 284 matched = false; 285 } 286 break; 287 case OP_RETURN_VOID: 288 if (needRetBar) 289 rewriteReturnVoid(method, insns); 290 break; 291 default: 292 matched = false; 293 break; 294 } 295 296 297 /* 298 * non-essential substitutions: 299 * invoke-{virtual,direct,static}[/range] --> execute-inline 300 * invoke-{virtual,super}[/range] --> invoke-*-quick 301 */ 302 if (!matched && !essentialOnly) { 303 switch (opc) { 304 case OP_INVOKE_VIRTUAL: 305 if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) { 306 rewriteVirtualInvoke(method, insns, 307 OP_INVOKE_VIRTUAL_QUICK); 308 } 309 break; 310 case OP_INVOKE_VIRTUAL_RANGE: 311 if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) { 312 rewriteVirtualInvoke(method, insns, 313 OP_INVOKE_VIRTUAL_QUICK_RANGE); 314 } 315 break; 316 case OP_INVOKE_SUPER: 317 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK); 318 break; 319 case OP_INVOKE_SUPER_RANGE: 320 rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE); 321 break; 322 case OP_INVOKE_DIRECT: 323 rewriteExecuteInline(method, insns, METHOD_DIRECT); 324 break; 325 case OP_INVOKE_DIRECT_RANGE: 326 rewriteExecuteInlineRange(method, insns, METHOD_DIRECT); 327 break; 328 case OP_INVOKE_STATIC: 329 rewriteExecuteInline(method, insns, METHOD_STATIC); 330 break; 331 case OP_INVOKE_STATIC_RANGE: 332 rewriteExecuteInlineRange(method, insns, METHOD_STATIC); 333 break; 334 default: 335 /* nothing to do for this instruction */ 336 ; 337 } 338 } 339 340 assert(width > 0); 341 assert(width <= insnsSize); 342 assert(width == dexGetWidthFromInstruction(insns)); 343 344 insns += width; 345 insnsSize -= width; 346 } 347 348 assert(insnsSize == 0); 349 } 350 351 /* 352 * Update a 16-bit code unit in "meth". The way in which the DEX data was 353 * loaded determines how we go about the write. 354 * 355 * This will be operating on post-byte-swap DEX data, so values will 356 * be in host order. 357 */ 358 void dvmUpdateCodeUnit(const Method* meth, u2* ptr, u2 newVal) 359 { 360 DvmDex* pDvmDex = meth->clazz->pDvmDex; 361 362 if (!pDvmDex->isMappedReadOnly) { 363 /* in-memory DEX (dexopt or byte[]), alter the output directly */ 364 *ptr = newVal; 365 } else { 366 /* memory-mapped file, toggle the page read/write status */ 367 dvmDexChangeDex2(pDvmDex, ptr, newVal); 368 } 369 } 370 371 /* 372 * Update an instruction's opcode. 373 * 374 * If "opcode" is an 8-bit op, we just replace that portion. If it's a 375 * 16-bit op, we convert the opcode from "packed" form (e.g. 0x0108) to 376 * bytecode form (e.g. 0x08ff). 377 */ 378 static inline void updateOpcode(const Method* meth, u2* ptr, u2 opcode) 379 { 380 if (opcode >= 256) { 381 /* opcode low byte becomes high byte, low byte becomes 0xff */ 382 assert((ptr[0] & 0xff) == 0xff); 383 dvmUpdateCodeUnit(meth, ptr, (opcode << 8) | 0x00ff); 384 } else { 385 /* 8-bit op, just replace the low byte */ 386 assert((ptr[0] & 0xff) != 0xff); 387 dvmUpdateCodeUnit(meth, ptr, (ptr[0] & 0xff00) | opcode); 388 } 389 } 390 391 /* 392 * If "referrer" and "resClass" don't come from the same DEX file, and 393 * the DEX we're working on is not destined for the bootstrap class path, 394 * tweak the class loader so package-access checks work correctly. 395 * 396 * Only do this if we're doing pre-verification or optimization. 397 */ 398 static void tweakLoader(ClassObject* referrer, ClassObject* resClass) 399 { 400 if (!gDvm.optimizing) 401 return; 402 assert(referrer->classLoader == NULL); 403 assert(resClass->classLoader == NULL); 404 405 if (!gDvm.optimizingBootstrapClass) { 406 /* class loader for an array class comes from element type */ 407 if (dvmIsArrayClass(resClass)) 408 resClass = resClass->elementClass; 409 if (referrer->pDvmDex != resClass->pDvmDex) 410 resClass->classLoader = (Object*) 0xdead3333; 411 } 412 } 413 414 /* 415 * Undo the effects of tweakLoader. 416 */ 417 static void untweakLoader(ClassObject* referrer, ClassObject* resClass) 418 { 419 if (!gDvm.optimizing || gDvm.optimizingBootstrapClass) 420 return; 421 422 if (dvmIsArrayClass(resClass)) 423 resClass = resClass->elementClass; 424 resClass->classLoader = NULL; 425 } 426 427 428 /* 429 * Alternate version of dvmResolveClass for use with verification and 430 * optimization. Performs access checks on every resolve, and refuses 431 * to acknowledge the existence of classes defined in more than one DEX 432 * file. 433 * 434 * Exceptions caused by failures are cleared before returning. 435 * 436 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. 437 */ 438 ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, 439 VerifyError* pFailure) 440 { 441 DvmDex* pDvmDex = referrer->pDvmDex; 442 ClassObject* resClass; 443 444 /* 445 * Check the table first. If not there, do the lookup by name. 446 */ 447 resClass = dvmDexGetResolvedClass(pDvmDex, classIdx); 448 if (resClass == NULL) { 449 const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, classIdx); 450 if (className[0] != '\0' && className[1] == '\0') { 451 /* primitive type */ 452 resClass = dvmFindPrimitiveClass(className[0]); 453 } else { 454 resClass = dvmFindClassNoInit(className, referrer->classLoader); 455 } 456 if (resClass == NULL) { 457 /* not found, exception should be raised */ 458 ALOGV("DexOpt: class %d (%s) not found", 459 classIdx, 460 dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); 461 if (pFailure != NULL) { 462 /* dig through the wrappers to find the original failure */ 463 Object* excep = dvmGetException(dvmThreadSelf()); 464 while (true) { 465 Object* cause = dvmGetExceptionCause(excep); 466 if (cause == NULL) 467 break; 468 excep = cause; 469 } 470 if (strcmp(excep->clazz->descriptor, 471 "Ljava/lang/IncompatibleClassChangeError;") == 0) 472 { 473 *pFailure = VERIFY_ERROR_CLASS_CHANGE; 474 } else { 475 *pFailure = VERIFY_ERROR_NO_CLASS; 476 } 477 } 478 dvmClearOptException(dvmThreadSelf()); 479 return NULL; 480 } 481 482 /* 483 * Add it to the resolved table so we're faster on the next lookup. 484 */ 485 dvmDexSetResolvedClass(pDvmDex, classIdx, resClass); 486 } 487 488 /* multiple definitions? */ 489 if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) { 490 ALOGI("DexOpt: not resolving ambiguous class '%s'", 491 resClass->descriptor); 492 if (pFailure != NULL) 493 *pFailure = VERIFY_ERROR_NO_CLASS; 494 return NULL; 495 } 496 497 /* access allowed? */ 498 tweakLoader(referrer, resClass); 499 bool allowed = dvmCheckClassAccess(referrer, resClass); 500 untweakLoader(referrer, resClass); 501 if (!allowed) { 502 ALOGW("DexOpt: resolve class illegal access: %s -> %s", 503 referrer->descriptor, resClass->descriptor); 504 if (pFailure != NULL) 505 *pFailure = VERIFY_ERROR_ACCESS_CLASS; 506 return NULL; 507 } 508 509 return resClass; 510 } 511 512 /* 513 * Alternate version of dvmResolveInstField(). 514 * 515 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. 516 */ 517 InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx, 518 VerifyError* pFailure) 519 { 520 DvmDex* pDvmDex = referrer->pDvmDex; 521 InstField* resField; 522 523 resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx); 524 if (resField == NULL) { 525 const DexFieldId* pFieldId; 526 ClassObject* resClass; 527 528 pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); 529 530 /* 531 * Find the field's class. 532 */ 533 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); 534 if (resClass == NULL) { 535 //dvmClearOptException(dvmThreadSelf()); 536 assert(!dvmCheckException(dvmThreadSelf())); 537 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } 538 return NULL; 539 } 540 541 resField = (InstField*)dvmFindFieldHier(resClass, 542 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), 543 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); 544 if (resField == NULL) { 545 ALOGD("DexOpt: couldn't find field %s.%s", 546 resClass->descriptor, 547 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); 548 if (pFailure != NULL) 549 *pFailure = VERIFY_ERROR_NO_FIELD; 550 return NULL; 551 } 552 if (dvmIsStaticField(resField)) { 553 ALOGD("DexOpt: wanted instance, got static for field %s.%s", 554 resClass->descriptor, 555 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); 556 if (pFailure != NULL) 557 *pFailure = VERIFY_ERROR_CLASS_CHANGE; 558 return NULL; 559 } 560 561 /* 562 * Add it to the resolved table so we're faster on the next lookup. 563 */ 564 dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField); 565 } 566 567 /* access allowed? */ 568 tweakLoader(referrer, resField->clazz); 569 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); 570 untweakLoader(referrer, resField->clazz); 571 if (!allowed) { 572 ALOGI("DexOpt: access denied from %s to field %s.%s", 573 referrer->descriptor, resField->clazz->descriptor, 574 resField->name); 575 if (pFailure != NULL) 576 *pFailure = VERIFY_ERROR_ACCESS_FIELD; 577 return NULL; 578 } 579 580 return resField; 581 } 582 583 /* 584 * Alternate version of dvmResolveStaticField(). 585 * 586 * Does not force initialization of the resolved field's class. 587 * 588 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. 589 */ 590 StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, 591 VerifyError* pFailure) 592 { 593 DvmDex* pDvmDex = referrer->pDvmDex; 594 StaticField* resField; 595 596 resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx); 597 if (resField == NULL) { 598 const DexFieldId* pFieldId; 599 ClassObject* resClass; 600 601 pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); 602 603 /* 604 * Find the field's class. 605 */ 606 resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); 607 if (resClass == NULL) { 608 //dvmClearOptException(dvmThreadSelf()); 609 assert(!dvmCheckException(dvmThreadSelf())); 610 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } 611 return NULL; 612 } 613 614 const char* fieldName = 615 dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx); 616 617 resField = (StaticField*)dvmFindFieldHier(resClass, fieldName, 618 dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); 619 if (resField == NULL) { 620 ALOGD("DexOpt: couldn't find static field %s.%s", 621 resClass->descriptor, fieldName); 622 if (pFailure != NULL) 623 *pFailure = VERIFY_ERROR_NO_FIELD; 624 return NULL; 625 } 626 if (!dvmIsStaticField(resField)) { 627 ALOGD("DexOpt: wanted static, got instance for field %s.%s", 628 resClass->descriptor, fieldName); 629 if (pFailure != NULL) 630 *pFailure = VERIFY_ERROR_CLASS_CHANGE; 631 return NULL; 632 } 633 634 /* 635 * Add it to the resolved table so we're faster on the next lookup. 636 * 637 * We can only do this if we're in "dexopt", because the presence 638 * of a valid value in the resolution table implies that the class 639 * containing the static field has been initialized. 640 */ 641 if (gDvm.optimizing) 642 dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); 643 } 644 645 /* access allowed? */ 646 tweakLoader(referrer, resField->clazz); 647 bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); 648 untweakLoader(referrer, resField->clazz); 649 if (!allowed) { 650 ALOGI("DexOpt: access denied from %s to field %s.%s", 651 referrer->descriptor, resField->clazz->descriptor, 652 resField->name); 653 if (pFailure != NULL) 654 *pFailure = VERIFY_ERROR_ACCESS_FIELD; 655 return NULL; 656 } 657 658 return resField; 659 } 660 661 662 /* 663 * Rewrite an iget/iput instruction if appropriate. These all have the form: 664 * op vA, vB, field@CCCC 665 * 666 * Where vA holds the value, vB holds the object reference, and CCCC is 667 * the field reference constant pool offset. For a non-volatile field, 668 * we want to replace the opcode with "quickOpc" and replace CCCC with 669 * the byte offset from the start of the object. For a volatile field, 670 * we just want to replace the opcode with "volatileOpc". 671 * 672 * If "volatileOpc" is OP_NOP we don't check to see if it's a volatile 673 * field. If "quickOpc" is OP_NOP, and this is a non-volatile field, 674 * we don't do anything. 675 * 676 * "method" is the referring method. 677 */ 678 static void rewriteInstField(Method* method, u2* insns, Opcode quickOpc, 679 Opcode volatileOpc) 680 { 681 ClassObject* clazz = method->clazz; 682 u2 fieldIdx = insns[1]; 683 InstField* instField; 684 685 instField = dvmOptResolveInstField(clazz, fieldIdx, NULL); 686 if (instField == NULL) { 687 ALOGI("DexOpt: unable to optimize instance field ref " 688 "0x%04x at 0x%02x in %s.%s", 689 fieldIdx, (int) (insns - method->insns), clazz->descriptor, 690 method->name); 691 return; 692 } 693 694 if (volatileOpc != OP_NOP && dvmIsVolatileField(instField)) { 695 updateOpcode(method, insns, volatileOpc); 696 ALOGV("DexOpt: rewrote ifield access %s.%s --> volatile", 697 instField->clazz->descriptor, instField->name); 698 } else if (quickOpc != OP_NOP && instField->byteOffset < 65536) { 699 updateOpcode(method, insns, quickOpc); 700 dvmUpdateCodeUnit(method, insns+1, (u2) instField->byteOffset); 701 ALOGV("DexOpt: rewrote ifield access %s.%s --> %d", 702 instField->clazz->descriptor, instField->name, 703 instField->byteOffset); 704 } else { 705 ALOGV("DexOpt: no rewrite of ifield access %s.%s", 706 instField->clazz->descriptor, instField->name); 707 } 708 709 return; 710 } 711 712 /* 713 * Rewrite a static field access instruction if appropriate. If 714 * the target field is volatile, we replace the opcode with "volatileOpc". 715 * 716 * "method" is the referring method. 717 */ 718 static void rewriteStaticField0(Method* method, u2* insns, Opcode volatileOpc, 719 u4 fieldIdx) 720 { 721 ClassObject* clazz = method->clazz; 722 StaticField* staticField; 723 724 assert(volatileOpc != OP_NOP); 725 726 staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL); 727 if (staticField == NULL) { 728 ALOGI("DexOpt: unable to optimize static field ref " 729 "0x%04x at 0x%02x in %s.%s", 730 fieldIdx, (int) (insns - method->insns), clazz->descriptor, 731 method->name); 732 return; 733 } 734 735 if (dvmIsVolatileField(staticField)) { 736 updateOpcode(method, insns, volatileOpc); 737 ALOGV("DexOpt: rewrote sfield access %s.%s --> volatile", 738 staticField->clazz->descriptor, staticField->name); 739 } 740 } 741 742 static void rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc) 743 { 744 u2 fieldIdx = insns[1]; 745 rewriteStaticField0(method, insns, volatileOpc, fieldIdx); 746 } 747 748 /* 749 * Alternate version of dvmResolveMethod(). 750 * 751 * Doesn't throw exceptions, and checks access on every lookup. 752 * 753 * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. 754 */ 755 Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, 756 MethodType methodType, VerifyError* pFailure) 757 { 758 DvmDex* pDvmDex = referrer->pDvmDex; 759 Method* resMethod; 760 761 assert(methodType == METHOD_DIRECT || 762 methodType == METHOD_VIRTUAL || 763 methodType == METHOD_STATIC); 764 765 LOGVV("--- resolving method %u (referrer=%s)", methodIdx, 766 referrer->descriptor); 767 768 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); 769 if (resMethod == NULL) { 770 const DexMethodId* pMethodId; 771 ClassObject* resClass; 772 773 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); 774 775 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure); 776 if (resClass == NULL) { 777 /* 778 * Can't find the class that the method is a part of, or don't 779 * have permission to access the class. 780 */ 781 ALOGV("DexOpt: can't find called method's class (?.%s)", 782 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); 783 if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } 784 return NULL; 785 } 786 if (dvmIsInterfaceClass(resClass)) { 787 /* method is part of an interface; this is wrong method for that */ 788 ALOGW("DexOpt: method is in an interface"); 789 if (pFailure != NULL) 790 *pFailure = VERIFY_ERROR_GENERIC; 791 return NULL; 792 } 793 794 /* 795 * We need to chase up the class hierarchy to find methods defined 796 * in super-classes. (We only want to check the current class 797 * if we're looking for a constructor.) 798 */ 799 DexProto proto; 800 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); 801 802 if (methodType == METHOD_DIRECT) { 803 resMethod = dvmFindDirectMethod(resClass, 804 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); 805 } else { 806 /* METHOD_STATIC or METHOD_VIRTUAL */ 807 resMethod = dvmFindMethodHier(resClass, 808 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); 809 } 810 811 if (resMethod == NULL) { 812 ALOGV("DexOpt: couldn't find method '%s'", 813 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); 814 if (pFailure != NULL) 815 *pFailure = VERIFY_ERROR_NO_METHOD; 816 return NULL; 817 } 818 if (methodType == METHOD_STATIC) { 819 if (!dvmIsStaticMethod(resMethod)) { 820 ALOGD("DexOpt: wanted static, got instance for method %s.%s", 821 resClass->descriptor, resMethod->name); 822 if (pFailure != NULL) 823 *pFailure = VERIFY_ERROR_CLASS_CHANGE; 824 return NULL; 825 } 826 } else if (methodType == METHOD_VIRTUAL) { 827 if (dvmIsStaticMethod(resMethod)) { 828 ALOGD("DexOpt: wanted instance, got static for method %s.%s", 829 resClass->descriptor, resMethod->name); 830 if (pFailure != NULL) 831 *pFailure = VERIFY_ERROR_CLASS_CHANGE; 832 return NULL; 833 } 834 } 835 836 /* see if this is a pure-abstract method */ 837 if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { 838 ALOGW("DexOpt: pure-abstract method '%s' in %s", 839 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), 840 resClass->descriptor); 841 if (pFailure != NULL) 842 *pFailure = VERIFY_ERROR_GENERIC; 843 return NULL; 844 } 845 846 /* 847 * Add it to the resolved table so we're faster on the next lookup. 848 * 849 * We can only do this for static methods if we're not in "dexopt", 850 * because the presence of a valid value in the resolution table 851 * implies that the class containing the static field has been 852 * initialized. 853 */ 854 if (methodType != METHOD_STATIC || gDvm.optimizing) 855 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); 856 } 857 858 LOGVV("--- found method %d (%s.%s)", 859 methodIdx, resMethod->clazz->descriptor, resMethod->name); 860 861 /* access allowed? */ 862 tweakLoader(referrer, resMethod->clazz); 863 bool allowed = dvmCheckMethodAccess(referrer, resMethod); 864 untweakLoader(referrer, resMethod->clazz); 865 if (!allowed) { 866 IF_ALOGI() { 867 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); 868 ALOGI("DexOpt: illegal method access (call %s.%s %s from %s)", 869 resMethod->clazz->descriptor, resMethod->name, desc, 870 referrer->descriptor); 871 free(desc); 872 } 873 if (pFailure != NULL) 874 *pFailure = VERIFY_ERROR_ACCESS_METHOD; 875 return NULL; 876 } 877 878 return resMethod; 879 } 880 881 /* 882 * Rewrite invoke-virtual, invoke-virtual/range, invoke-super, and 883 * invoke-super/range if appropriate. These all have the form: 884 * op vAA, meth@BBBB, reg stuff @CCCC 885 * 886 * We want to replace the method constant pool index BBBB with the 887 * vtable index. 888 */ 889 static void rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc) 890 { 891 ClassObject* clazz = method->clazz; 892 Method* baseMethod; 893 u2 methodIdx = insns[1]; 894 895 baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL); 896 if (baseMethod == NULL) { 897 ALOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s", 898 methodIdx, 899 (int) (insns - method->insns), clazz->descriptor, 900 method->name); 901 return; 902 } 903 904 assert((insns[0] & 0xff) == OP_INVOKE_VIRTUAL || 905 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE || 906 (insns[0] & 0xff) == OP_INVOKE_SUPER || 907 (insns[0] & 0xff) == OP_INVOKE_SUPER_RANGE); 908 909 /* 910 * Note: Method->methodIndex is a u2 and is range checked during the 911 * initial load. 912 */ 913 updateOpcode(method, insns, newOpc); 914 dvmUpdateCodeUnit(method, insns+1, baseMethod->methodIndex); 915 916 //ALOGI("DexOpt: rewrote call to %s.%s --> %s.%s", 917 // method->clazz->descriptor, method->name, 918 // baseMethod->clazz->descriptor, baseMethod->name); 919 920 return; 921 } 922 923 /* 924 * Rewrite invoke-direct[/range] if the target is Object.<init>. 925 * 926 * This is useful as an optimization, because otherwise every object 927 * instantiation will cause us to call a method that does nothing. 928 * It also allows us to inexpensively mark objects as finalizable at the 929 * correct time. 930 * 931 * TODO: verifier should ensure Object.<init> contains only return-void, 932 * and issue a warning if not. 933 */ 934 static bool rewriteInvokeObjectInit(Method* method, u2* insns) 935 { 936 ClassObject* clazz = method->clazz; 937 Method* calledMethod; 938 u2 methodIdx = insns[1]; 939 940 calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL); 941 if (calledMethod == NULL) { 942 ALOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s", 943 methodIdx, (int) (insns - method->insns), 944 clazz->descriptor, method->name); 945 return false; 946 } 947 948 if (calledMethod->clazz == gDvm.classJavaLangObject && 949 dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0) 950 { 951 /* 952 * Replace the instruction. If the debugger is attached, the 953 * interpreter will forward execution to the invoke-direct/range 954 * handler. If this was an invoke-direct/range instruction we can 955 * just replace the opcode, but if it was an invoke-direct we 956 * have to set the argument count (high 8 bits of first code unit) 957 * to 1. 958 */ 959 u1 origOp = insns[0] & 0xff; 960 if (origOp == OP_INVOKE_DIRECT) { 961 dvmUpdateCodeUnit(method, insns, 962 OP_INVOKE_OBJECT_INIT_RANGE | 0x100); 963 } else { 964 assert(origOp == OP_INVOKE_DIRECT_RANGE); 965 assert((insns[0] >> 8) == 1); 966 updateOpcode(method, insns, OP_INVOKE_OBJECT_INIT_RANGE); 967 } 968 969 LOGVV("DexOpt: replaced Object.<init> in %s.%s", 970 method->clazz->descriptor, method->name); 971 } 972 973 return true; 974 } 975 976 /* 977 * Resolve an interface method reference. 978 * 979 * No method access check here -- interface methods are always public. 980 * 981 * Returns NULL if the method was not found. Does not throw an exception. 982 */ 983 Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx) 984 { 985 DvmDex* pDvmDex = referrer->pDvmDex; 986 Method* resMethod; 987 988 LOGVV("--- resolving interface method %d (referrer=%s)", 989 methodIdx, referrer->descriptor); 990 991 resMethod = dvmDexGetResolvedMethod(pDvmDex, methodIdx); 992 if (resMethod == NULL) { 993 const DexMethodId* pMethodId; 994 ClassObject* resClass; 995 996 pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); 997 998 resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL); 999 if (resClass == NULL) { 1000 /* can't find the class that the method is a part of */ 1001 dvmClearOptException(dvmThreadSelf()); 1002 return NULL; 1003 } 1004 if (!dvmIsInterfaceClass(resClass)) { 1005 /* whoops */ 1006 ALOGI("Interface method not part of interface class"); 1007 return NULL; 1008 } 1009 1010 const char* methodName = 1011 dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx); 1012 DexProto proto; 1013 dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); 1014 1015 LOGVV("+++ looking for '%s' '%s' in resClass='%s'", 1016 methodName, methodSig, resClass->descriptor); 1017 resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto); 1018 if (resMethod == NULL) { 1019 return NULL; 1020 } 1021 1022 /* we're expecting this to be abstract */ 1023 if (!dvmIsAbstractMethod(resMethod)) { 1024 char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); 1025 ALOGW("Found non-abstract interface method %s.%s %s", 1026 resMethod->clazz->descriptor, resMethod->name, desc); 1027 free(desc); 1028 return NULL; 1029 } 1030 1031 /* 1032 * Add it to the resolved table so we're faster on the next lookup. 1033 */ 1034 dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); 1035 } 1036 1037 LOGVV("--- found interface method %d (%s.%s)", 1038 methodIdx, resMethod->clazz->descriptor, resMethod->name); 1039 1040 /* interface methods are always public; no need to check access */ 1041 1042 return resMethod; 1043 } 1044 1045 /* 1046 * Replace invoke-virtual, invoke-direct, or invoke-static with an 1047 * execute-inline operation if appropriate. 1048 * 1049 * Returns "true" if we replace it. 1050 */ 1051 static bool rewriteExecuteInline(Method* method, u2* insns, 1052 MethodType methodType) 1053 { 1054 const InlineSub* inlineSubs = gDvm.inlineSubs; 1055 ClassObject* clazz = method->clazz; 1056 Method* calledMethod; 1057 u2 methodIdx = insns[1]; 1058 1059 //return false; 1060 1061 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); 1062 if (calledMethod == NULL) { 1063 ALOGV("+++ DexOpt inline: can't find %d", methodIdx); 1064 return false; 1065 } 1066 1067 while (inlineSubs->method != NULL) { 1068 /* 1069 if (extra) { 1070 ALOGI("comparing %p vs %p %s.%s %s", 1071 inlineSubs->method, calledMethod, 1072 inlineSubs->method->clazz->descriptor, 1073 inlineSubs->method->name, 1074 inlineSubs->method->signature); 1075 } 1076 */ 1077 if (inlineSubs->method == calledMethod) { 1078 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT || 1079 (insns[0] & 0xff) == OP_INVOKE_STATIC || 1080 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL); 1081 updateOpcode(method, insns, OP_EXECUTE_INLINE); 1082 dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx); 1083 1084 //ALOGI("DexOpt: execute-inline %s.%s --> %s.%s", 1085 // method->clazz->descriptor, method->name, 1086 // calledMethod->clazz->descriptor, calledMethod->name); 1087 return true; 1088 } 1089 1090 inlineSubs++; 1091 } 1092 1093 return false; 1094 } 1095 1096 /* 1097 * Replace invoke-virtual/range, invoke-direct/range, or invoke-static/range 1098 * with an execute-inline operation if appropriate. 1099 * 1100 * Returns "true" if we replace it. 1101 */ 1102 static bool rewriteExecuteInlineRange(Method* method, u2* insns, 1103 MethodType methodType) 1104 { 1105 const InlineSub* inlineSubs = gDvm.inlineSubs; 1106 ClassObject* clazz = method->clazz; 1107 Method* calledMethod; 1108 u2 methodIdx = insns[1]; 1109 1110 calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); 1111 if (calledMethod == NULL) { 1112 ALOGV("+++ DexOpt inline/range: can't find %d", methodIdx); 1113 return false; 1114 } 1115 1116 while (inlineSubs->method != NULL) { 1117 if (inlineSubs->method == calledMethod) { 1118 assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE || 1119 (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE || 1120 (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE); 1121 updateOpcode(method, insns, OP_EXECUTE_INLINE_RANGE); 1122 dvmUpdateCodeUnit(method, insns+1, (u2) inlineSubs->inlineIdx); 1123 1124 //ALOGI("DexOpt: execute-inline/range %s.%s --> %s.%s", 1125 // method->clazz->descriptor, method->name, 1126 // calledMethod->clazz->descriptor, calledMethod->name); 1127 return true; 1128 } 1129 1130 inlineSubs++; 1131 } 1132 1133 return false; 1134 } 1135 1136 /* 1137 * Returns "true" if the return-void instructions in this method should 1138 * be converted to return-void-barrier. 1139 * 1140 * This is needed to satisfy a Java Memory Model requirement regarding 1141 * the construction of objects with final fields. (This does not apply 1142 * to <clinit> or static fields, since appropriate barriers are guaranteed 1143 * by the class initialization process.) 1144 */ 1145 static bool needsReturnBarrier(Method* method) 1146 { 1147 if (!gDvm.dexOptForSmp) 1148 return false; 1149 if (strcmp(method->name, "<init>") != 0) 1150 return false; 1151 1152 /* 1153 * Check to see if the class is finalizable. The loader sets a flag 1154 * if the class or one of its superclasses overrides finalize(). 1155 */ 1156 const ClassObject* clazz = method->clazz; 1157 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) 1158 return true; 1159 1160 /* 1161 * Check to see if the class has any final fields. If not, we don't 1162 * need to generate a barrier instruction. 1163 * 1164 * In theory, we only need to do this if the method actually modifies 1165 * a final field. In practice, non-constructor methods are allowed 1166 * to modify final fields, and there are 3rd-party tools that rely on 1167 * this behavior. (The compiler does not allow it, but the VM does.) 1168 * 1169 * If we alter the verifier to restrict final-field updates to 1170 * constructors, we can tighten this up as well. 1171 */ 1172 int idx = clazz->ifieldCount; 1173 while (--idx >= 0) { 1174 if (dvmIsFinalField(&clazz->ifields[idx])) 1175 return true; 1176 } 1177 1178 return false; 1179 } 1180 1181 /* 1182 * Convert a return-void to a return-void-barrier. 1183 */ 1184 static void rewriteReturnVoid(Method* method, u2* insns) 1185 { 1186 assert((insns[0] & 0xff) == OP_RETURN_VOID); 1187 updateOpcode(method, insns, OP_RETURN_VOID_BARRIER); 1188 } 1189