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