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