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