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