OSDN Git Service

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