OSDN Git Service

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