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