Optimize.cpp revision ab35b50311951feea3782151dd5422ee944685c2
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            LOGE("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 */
104void 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 */
120void 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 */
146static 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 */
237rewrite_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 */
274rewrite_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 */
357void 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 */
377static 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 */
397static 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 */
416static 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 */
437ClassObject* 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        LOGI("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        LOGW("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 */
516InstField* 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        LOGI("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 */
589StaticField* 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        LOGI("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 */
677static 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        LOGI("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 */
717static 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        LOGI("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
741static 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 */
754Method* 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            LOGW("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            LOGW("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_LOGI() {
866            char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype);
867            LOGI("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 */
888static 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    //LOGI("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 */
933static 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 */
982Method* 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            LOGI("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            LOGW("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 */
1050static 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            LOGI("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            //LOGI("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 */
1101static 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            //LOGI("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 */
1144static 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 */
1183static void rewriteReturnVoid(Method* method, u2* insns)
1184{
1185    assert((insns[0] & 0xff) == OP_RETURN_VOID);
1186    updateOpcode(method, insns, OP_RETURN_VOID_BARRIER);
1187}
1188