Home | History | Annotate | Download | only in analysis
      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  * Compress the range of "constant pool" indexes in instructions and
     19  * annotations to lower runtime RAM footprint.
     20  *
     21  * NOTE: this is an incomplete experimental feature.  Do not try to use it.
     22  */
     23 #include "Dalvik.h"
     24 #include "libdex/InstrUtils.h"
     25 #include "libdex/OptInvocation.h"
     26 #include "libdex/DexClass.h"
     27 
     28 /*
     29 Overview
     30 
     31 When a class, method, field, or string constant is referred to from
     32 Dalvik bytecode, the reference takes the form of an integer index value.
     33 This value indexes into an array of type_id_item, method_id_item,
     34 field_id_item, or string_id_item in the DEX file.  The first three
     35 themselves contain (directly or indirectly) indexes to strings that the
     36 resolver uses to convert the instruction stream index into a pointer to
     37 the appropriate object or struct.
     38 
     39 For example, an invoke-virtual instruction needs to specify which method
     40 is to be invoked.  The method constant indexes into the method_id_item
     41 array, each entry of which has indexes that specify the defining class
     42 (type_id_item), method name (string_id_item), and method prototype
     43 (proto_id_item).  The type_id_item just holds an index to a string_id_item,
     44 which holds the file offset to the string with the class name.  The VM
     45 finds the class by name, then searches through the class' table of virtual
     46 methods to find one with a matching name and prototype.
     47 
     48 This process is fairly expensive, so after the first time it completes
     49 successfully, the VM records that the method index resolved to a specific
     50 Method struct.  On subsequent execution, the VM just pulls the Method ptr
     51 out of the resolved-methods array.  A similar approach is used with
     52 the indexes for classes, fields, and string constants.
     53 
     54 The problem with this approach is that we need to have a "resolved" entry
     55 for every possible class, method, field, and string constant in every
     56 DEX file, even if some of those aren't used from code.  The DEX string
     57 constant table has entries for method prototypes and class names that are
     58 never used by the code, and "public static final" fields often turn into
     59 immediate constants.  The resolution table entries are only 4 bytes each,
     60 but there are roughly 200,000 of them in the bootstrap classes alone.
     61 
     62 DEX optimization removes many index references by replacing virtual method
     63 indexes with vtable offsets and instance field indexes with byte offsets.
     64 In the earlier example, the method would be resolved at "dexopt" time, and
     65 the instruction rewritten as invoke-virtual-quick with the vtable offset.
     66 
     67 (There are comparatively few classes compared to other constant pool
     68 entries, and a much higher percentage (typically 60-70%) are used.  The
     69 biggest gains come from the string pool.)
     70 
     71 Using the resolved-entity tables provides a substantial performance
     72 improvement, but results in applications allocating 1MB+ of tables that
     73 are 70% unused.  The used and unused entries are freely intermixed,
     74 preventing effective sharing with the zygote process, and resulting in
     75 large numbers of private/dirty pages on the native heap as the tables
     76 populate on first use.
     77 
     78 The trick is to reduce the memory usage without decreasing performance.
     79 Using smaller resolved-entity tables can actually give us a speed boost,
     80 because we'll have a smaller "live" set of pages and make more effective
     81 use of the data cache.
     82 
     83 
     84 The approach we're going to use is to determine the set of indexes that
     85 could potentially be resolved, generate a mapping from the minimal set to
     86 the full set, and append the mapping to the DEX file.  This is done at
     87 "dexopt" time, because we need to keep the changes in shared/read-only
     88 pages or we'll lose the benefits of doing the work.
     89 
     90 There are two ways to create and use the new mapping:
     91 
     92  (1) Write the entire full->minimal mapping to the ".odex" file.  On every
     93  instruction that uses an index, use the mapping to determine the
     94  "compressed" constant value, and then use that to index into the
     95  resolved-entity tables on the heap.  The instruction stream is unchanged,
     96  and the resolver can easily tell if a given index is cacheable.
     97 
     98  (2) Write the inverse miminal->full mapping to the ".odex" file, and
     99  rewrite the constants in the instruction stream.  The interpreter is
    100  unchanged, and the resolver code uses the mapping to find the original
    101  data in the DEX.
    102 
    103 Approach #1 is easier and safer to implement, but it requires a table
    104 lookup every time we execute an instruction that includes a constant
    105 pool reference.  This causes an unacceptable performance hit, chiefly
    106 because we're hitting semi-random memory pages and hosing the data cache.
    107 This is mitigated somewhat by DEX optimizations that replace the constant
    108 with a vtable index or field byte offset.  Approach #1 also requires
    109 a larger map table, increasing the size of the DEX on disk.  One nice
    110 property of approach #1 is that most of the DEX file is unmodified,
    111 so use of the mapping is a runtime decision.
    112 
    113 Approach #2 is preferred for performance reasons.
    114 
    115 
    116 The class/method/field/string resolver code has to handle indices from
    117 three sources: interpreted instructions, annotations, and exception
    118 "catch" lists.  Sometimes these occur indirectly, e.g. we need to resolve
    119 the declaring class associated with fields and methods when the latter
    120 two are themselves resolved.  Parsing and rewriting instructions is fairly
    121 straightforward, but annotations use a complex format with variable-width
    122 index values.
    123 
    124 We can safely rewrite index values in annotations if we guarantee that the
    125 new value is smaller than the original.  This implies a two-pass approach:
    126 the first determines the set of indexes actually used, the second does the
    127 rewrite.  Doing the rewrite in a single pass would be much harder.
    128 
    129 Instances of the "original" indices will still be found in the file; if
    130 we try to be all-inclusive we will include some stuff that doesn't need
    131 to be there (e.g. we don't generally need to cache the class name string
    132 index result, since once we have the class resolved we don't need to look
    133 it up by name through the resolver again).  There is some potential for
    134 performance improvement by caching more than we strictly need, but we can
    135 afford to give up a little performance during class loading if it allows
    136 us to regain some memory.
    137 
    138 For safety and debugging, it's useful to distinguish the "compressed"
    139 constants in some way, e.g. setting the high bit when we rewrite them.
    140 In practice we don't have any free bits: indexes are usually 16-bit
    141 values, and we have more than 32,767 string constants in at least one of
    142 our core DEX files.  Also, this does not work with constants embedded in
    143 annotations, because of the variable-width encoding.
    144 
    145 We should be safe if we can establish a clear distinction between sources
    146 of "original" and "compressed" indices.  If the values get crossed up we
    147 can end up with elusive bugs.  The easiest approach is to declare that
    148 only indices pulled from certain locations (the instruction stream and/or
    149 annotations) are compressed.  This prevents us from adding indices in
    150 arbitrary locations to the compressed set, but should allow a reasonably
    151 robust implementation.
    152 
    153 
    154 Further implementation thoughts:
    155 
    156  - We don't have to do annotations in the first pass.  At heart the
    157    resolved entity cache is a performance optimization, not necessary for
    158    correctness, and we're not making annotation performance a priority
    159    at this stage.
    160  - The most important "fast path" is instruction processing.  Everything
    161    else can do additional work without having a measurable impact.
    162    However...
    163  - We need to keep an eye on uncached resolves to ensure that we haven't
    164    introduced noticeable performance losses.  In particular, the use of
    165    runtime annotations with string constants may suffer if we don't include
    166    annotation rewriting in the solution.
    167  - We can have separate resolver functions for "original" and "compressed"
    168    indices.  This way we don't have to add a flag argument to the resolver
    169    functions (which would require passing an additional parameter in from
    170    the interpreter).
    171  - The VM spec has some specific things to say about string constant
    172    equality and interning.  Index compression should have no effect on
    173    that; we just change how long it takes to find the interned string in
    174    certain circumstances.  The impact can be mitigated somewhat by
    175    improving the performance of the interned string table code.
    176  - This can make e.g. method resolution slower.  The method_id_item has
    177    an index to a method name string, and we will no longer cache the
    178    result of resolving that string.  This impacts resolution of any method
    179    with the same name as a previously-resolved method.
    180  - We may need to tweak the tools, particularly "dexdump", to show the
    181    translated values.
    182  - We can use 16-bit values in the mapping table, since we should have
    183    fewer than 2^16 remapped entries.  If we overflow we can skip the remap
    184    for that table or for the entire DEX file.  The resolver will need to
    185    check for the existence of the table to determine whether or not entries
    186    must be remapped.  The cost of the extra check is acceptable for
    187    approach #2, since it's only at resolve time, but may be undesirable
    188    for approach #1.
    189 */
    190 /*
    191 Output Formats
    192 
    193 There are two possible output formats, from which we choose based on how
    194 we plan to take advantage of the remapped constants.  At most one of these
    195 will appear in the DEX.
    196 
    197 NOTE: if EIXM appears in the DEX, the VM *must* be configured with
    198 DVM_RESOLVER_CACHE=DVM_RC_EXPANDING (2).  Otherwise the constants we
    199 pull from the instruction stream will be wrong and we will fail quickly.
    200 
    201 For approach #1: map from original indices to the reduced set.
    202 
    203   This includes the four "mapToNew" tables.
    204 
    205   Format (RIXM):
    206    u4 classCount            // #of entries in classMap[]; == typeIdsSize
    207    u4 reducedClassCount     // #of entries in remapped table (for alloc)
    208    u2 classMap[]
    209    u4 methodCount
    210    u4 reducedMethodCount
    211    u2 methodMap[]
    212    u4 fieldCount
    213    u4 reducedFieldCount
    214    u2 fieldMap[]
    215    u4 stringCount
    216    u4 reducedStringCount
    217    u2 stringMap[]
    218 
    219 For approach #2: map from the reduced set back to the originals.
    220 
    221   This includes the four "mapToOld" tables.
    222 
    223   Format (EIXM):
    224    u4 classCount            // #of entries in classMap[]; post-reduction
    225    u2 classMap[]
    226    u4 methodCount
    227    u2 methodMap[]
    228    u4 fieldCount
    229    u2 fieldMap[]
    230    u4 stringCount
    231    u2 stringMap[]
    232 
    233 The arrays are padded so that the "count" values are always aligned on
    234 32-bit boundaries.  All multi-byte values are in native host order.
    235 */
    236 
    237 
    238 /*
    239  * Gather results from the post-optimization instruction scan.
    240  */
    241 typedef struct ScanResults {
    242     /* output */
    243     BitVector*  usedClasses;
    244     BitVector*  usedMethods;
    245     BitVector*  usedFields;
    246     BitVector*  usedStrings;
    247 } ScanResults;
    248 
    249 /* prototype for the for-all-methods function */
    250 typedef void (AllMethodsFunc)(DexFile* pDexFile, const char* classDescriptor,
    251     DexMethod* pDexMethod, void* arg);
    252 
    253 
    254 /*
    255  * Free scan results.
    256  */
    257 static void freeScanResults(ScanResults* pResults)
    258 {
    259     if (pResults == NULL)
    260         return;
    261 
    262     dvmFreeBitVector(pResults->usedClasses);
    263     dvmFreeBitVector(pResults->usedMethods);
    264     dvmFreeBitVector(pResults->usedFields);
    265     dvmFreeBitVector(pResults->usedStrings);
    266     free(pResults);
    267 }
    268 
    269 /*
    270  * Allocate storage for the results of the instruction scan.
    271  */
    272 static ScanResults* allocScanResults(const DexFile* pDexFile)
    273 {
    274     ScanResults* pResults;
    275     const DexHeader* pHeader = pDexFile->pHeader;
    276 
    277     pResults = (ScanResults*) calloc(1, sizeof(ScanResults));
    278     if (pResults == NULL)
    279         return NULL;
    280 
    281     pResults->usedClasses = dvmAllocBitVector(pHeader->typeIdsSize, false);
    282     pResults->usedMethods = dvmAllocBitVector(pHeader->methodIdsSize, false);
    283     pResults->usedFields = dvmAllocBitVector(pHeader->fieldIdsSize, false);
    284     pResults->usedStrings = dvmAllocBitVector(pHeader->stringIdsSize, false);
    285 
    286     if (pResults->usedClasses == NULL ||
    287         pResults->usedMethods == NULL ||
    288         pResults->usedFields == NULL ||
    289         pResults->usedStrings == NULL)
    290     {
    291         freeScanResults(pResults);
    292         return NULL;
    293     }
    294 
    295     return pResults;
    296 }
    297 
    298 /*
    299  * Call "func(method, arg)" on all methods in the specified class.
    300  *
    301  * Pass in a pointer to the class_data_item, positioned at the start of
    302  * the field data (i.e. just past the class data header).
    303  *
    304  * "classDescriptor" is for debug messages.
    305  */
    306 static void forAllMethodsInClass(DexFile* pDexFile, const u1** ppEncodedData,
    307     const DexClassDataHeader* pHeader, const char* classDescriptor,
    308     AllMethodsFunc func, void* arg)
    309 {
    310     int i;
    311 
    312     /*
    313      * Consume field data.
    314      */
    315     if (pHeader->staticFieldsSize != 0) {
    316         int count = (int) pHeader->staticFieldsSize;
    317         u4 lastIndex = 0;
    318         DexField field;
    319         for (i = 0; i < count; i++) {
    320             dexReadClassDataField(ppEncodedData, &field, &lastIndex);
    321         }
    322     }
    323     if (pHeader->instanceFieldsSize != 0) {
    324         int count = (int) pHeader->instanceFieldsSize;
    325         u4 lastIndex = 0;
    326         DexField field;
    327         for (i = 0; i < count; i++) {
    328             dexReadClassDataField(ppEncodedData, &field, &lastIndex);
    329         }
    330     }
    331 
    332     /*
    333      * Run through all methods.
    334      */
    335     if (pHeader->directMethodsSize != 0) {
    336         int count = (int) pHeader->directMethodsSize;
    337         u4 lastIndex = 0;
    338         DexMethod method;
    339 
    340         for (i = 0; i < count; i++) {
    341             dexReadClassDataMethod(ppEncodedData, &method, &lastIndex);
    342             (func)(pDexFile, classDescriptor, &method, arg);
    343         }
    344     }
    345     if (pHeader->virtualMethodsSize != 0) {
    346         int count = (int) pHeader->virtualMethodsSize;
    347         u4 lastIndex = 0;
    348         DexMethod method;
    349 
    350         for (i = 0; i < count; i++) {
    351             dexReadClassDataMethod(ppEncodedData, &method, &lastIndex);
    352             (func)(pDexFile, classDescriptor, &method, arg);
    353         }
    354     }
    355 }
    356 
    357 /*
    358  * Call "func(method, arg)" on all methods in all classes in DexFile.
    359  */
    360 static void forAllMethods(DexFile* pDexFile, AllMethodsFunc func, void* arg)
    361 {
    362     u4 count = pDexFile->pHeader->classDefsSize;
    363     u4 idx;
    364 
    365     for (idx = 0; idx < count; idx++) {
    366         const DexClassDef* pClassDef;
    367         DexClassDataHeader header;
    368         const u1* pEncodedData;
    369 
    370         pClassDef = dexGetClassDef(pDexFile, idx);
    371         pEncodedData = dexGetClassData(pDexFile, pClassDef);
    372 
    373         const char* classDescriptor;
    374         classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
    375 
    376         if (pEncodedData != NULL) {
    377             dexReadClassDataHeader(&pEncodedData, &header);
    378 
    379             forAllMethodsInClass(pDexFile, &pEncodedData, &header,
    380                 classDescriptor, func, arg);
    381         } else {
    382             //printf("%s: no class data\n", classDescriptor);
    383             /* no class data, e.g. "marker interface" */
    384         }
    385     }
    386 }
    387 
    388 /*
    389  * Mark a class ID as referenced.
    390  */
    391 static void markClass(const u2* ptr, ScanResults* pResults)
    392 {
    393     u2 classIdx = *ptr;
    394     if (!dvmSetBit(pResults->usedClasses, classIdx)) {
    395         LOGE("Unable to mark class %d as in-use\n", classIdx);
    396     }
    397 }
    398 
    399 /*
    400  * Mark a method ID as referenced.
    401  */
    402 static void markMethod(const u2* ptr, ScanResults* pResults)
    403 {
    404     u2 methodIdx = *ptr;
    405     if (!dvmSetBit(pResults->usedMethods, methodIdx)) {
    406         LOGE("Unable to mark method %d as in-use\n", methodIdx);
    407     }
    408 }
    409 
    410 /*
    411  * Mark a field ID as referenced.
    412  */
    413 static void markField(const u2* ptr, ScanResults* pResults)
    414 {
    415     u2 fieldIdx = *ptr;
    416     if (!dvmSetBit(pResults->usedFields, fieldIdx)) {
    417         LOGE("Unable to mark field %d as in-use\n", fieldIdx);
    418     }
    419 }
    420 
    421 /*
    422  * Mark a string constant as referenced.
    423  */
    424 static void markString(const u2* ptr, ScanResults* pResults)
    425 {
    426     u2 stringIdx = *ptr;
    427     if (!dvmSetBit(pResults->usedStrings, stringIdx)) {
    428         LOGE("Unable to mark string %d as in-use\n", stringIdx);
    429     }
    430 }
    431 
    432 /*
    433  * Mark a "jumbo" string constant as referenced.
    434  */
    435 static void markJumboString(u2* ptr, ScanResults* pResults)
    436 {
    437     u4 stringIdx;
    438 
    439     /* it's in native byte order, but might not be 32-bit aligned */
    440     memcpy(&stringIdx, ptr, sizeof(u4));
    441     if (!dvmSetBit(pResults->usedStrings, stringIdx)) {
    442         LOGE("Unable to mark string %d as in-use\n", stringIdx);
    443     }
    444 }
    445 
    446 /*
    447  * Remap a value in the instruction stream.
    448  */
    449 static inline void updateValue(u2* ptr, const IndexMapSet* pIndexMapSet,
    450     int whichMap)
    451 {
    452     const IndexMap* pMap = &pIndexMapSet->map[whichMap];
    453     if (pMap != NULL) {
    454         u2 newIdx = pMap->mapToNew[*ptr];
    455         assert(newIdx != kNoIndexMapping);
    456         *ptr = newIdx;
    457     }
    458 }
    459 static void updateClass(u2* ptr, const IndexMapSet* pIndexMapSet)
    460 {
    461     updateValue(ptr, pIndexMapSet, kMapClasses);
    462 }
    463 static void updateMethod(u2* ptr, const IndexMapSet* pIndexMapSet)
    464 {
    465     updateValue(ptr, pIndexMapSet, kMapMethods);
    466 }
    467 static void updateField(u2* ptr, const IndexMapSet* pIndexMapSet)
    468 {
    469     updateValue(ptr, pIndexMapSet, kMapFields);
    470 }
    471 static void updateString(u2* ptr, const IndexMapSet* pIndexMapSet)
    472 {
    473     updateValue(ptr, pIndexMapSet, kMapStrings);
    474 }
    475 static void updateJumboString(u2* ptr, const IndexMapSet* pIndexMapSet)
    476 {
    477     u4 stringIdx;
    478     u4 newIdx;
    479 
    480     /* it's in native byte order, but might not be 32-bit aligned */
    481     memcpy(&stringIdx, ptr, sizeof(stringIdx));
    482 
    483     /* get new value */
    484     newIdx = pIndexMapSet->map[kMapStrings].mapToNew[*ptr];
    485     assert(newIdx != kNoIndexMapping);
    486 
    487     /* copy it out */
    488     memcpy(ptr, &newIdx, sizeof(newIdx));
    489 }
    490 
    491 /*
    492  * Run through an instructions stream, marking constants as we see them.
    493  *
    494  * If "pResults" is non-NULL, we populate "pResults" with what we find,
    495  * making no changes to the instruction stream.
    496  *
    497  * If "pIndexMapSet" is non-NULL, we rewrite the constants in the
    498  * instruction stream.
    499  */
    500 static void markUsedConstantsFromInsns(u2* insns, u4 insnsSize,
    501     ScanResults* pResults, const IndexMapSet* pIndexMapSet)
    502 {
    503     //printf(" %p %u units\n", insns, insnsSize);
    504 
    505     while (insnsSize > 0) {
    506         int width;
    507         u2* pConst = insns + 1;
    508 
    509         switch (*insns & 0xff) {
    510         case OP_IGET:
    511         case OP_IGET_WIDE:
    512         case OP_IGET_OBJECT:
    513         case OP_IGET_BOOLEAN:
    514         case OP_IGET_BYTE:
    515         case OP_IGET_CHAR:
    516         case OP_IGET_SHORT:
    517         case OP_IPUT:
    518         case OP_IPUT_WIDE:
    519         case OP_IPUT_OBJECT:
    520         case OP_IPUT_BOOLEAN:
    521         case OP_IPUT_BYTE:
    522         case OP_IPUT_CHAR:
    523         case OP_IPUT_SHORT:
    524         case OP_SGET:
    525         case OP_SGET_WIDE:
    526         case OP_SGET_OBJECT:
    527         case OP_SGET_BOOLEAN:
    528         case OP_SGET_BYTE:
    529         case OP_SGET_CHAR:
    530         case OP_SGET_SHORT:
    531         case OP_SPUT:
    532         case OP_SPUT_WIDE:
    533         case OP_SPUT_OBJECT:
    534         case OP_SPUT_BOOLEAN:
    535         case OP_SPUT_BYTE:
    536         case OP_SPUT_CHAR:
    537         case OP_SPUT_SHORT:
    538             /* instanceop vA, vB, field@CCCC */
    539             /* staticop vAA, field@BBBB */
    540             if (pResults != NULL)
    541                 markField(pConst, pResults);
    542             else
    543                 updateField(pConst, pIndexMapSet);
    544             break;
    545 
    546         case OP_CONST_STRING:
    547             /* const-string vAA, string@BBBB */
    548             if (pResults != NULL)
    549                 markString(pConst, pResults);
    550             else
    551                 updateString(pConst, pIndexMapSet);
    552             break;
    553 
    554         case OP_CONST_STRING_JUMBO:
    555             /* const-string/jumbo vAA, string@BBBBBBBB */
    556             if (pResults != NULL)
    557                 markJumboString(pConst, pResults);
    558             else
    559                 updateJumboString(pConst, pIndexMapSet);
    560             break;
    561 
    562         case OP_CONST_CLASS:
    563         case OP_CHECK_CAST:
    564         case OP_NEW_INSTANCE:
    565         case OP_FILLED_NEW_ARRAY_RANGE:
    566         case OP_INSTANCE_OF:
    567         case OP_NEW_ARRAY:
    568         case OP_FILLED_NEW_ARRAY:
    569             /* const-class vAA, type@BBBB */
    570             /* check-cast vAA, type@BBBB */
    571             /* new-instance vAA, type@BBBB */
    572             /* filled-new-array/range {vCCCC .. vNNNN}, type@BBBB */
    573             /* instance-of vA, vB, type@CCCC */
    574             /* new-array vA, vB, type@CCCC */
    575             /* filled-new-array {vD, vE, vF, vG, vA}, type@CCCC */
    576             if (pResults != NULL)
    577                 markClass(pConst, pResults);
    578             else
    579                 updateClass(pConst, pIndexMapSet);
    580             break;
    581 
    582         case OP_INVOKE_VIRTUAL:
    583         case OP_INVOKE_SUPER:
    584         case OP_INVOKE_DIRECT:
    585         case OP_INVOKE_STATIC:
    586         case OP_INVOKE_INTERFACE:
    587         case OP_INVOKE_VIRTUAL_RANGE:
    588         case OP_INVOKE_SUPER_RANGE:
    589         case OP_INVOKE_DIRECT_RANGE:
    590         case OP_INVOKE_STATIC_RANGE:
    591         case OP_INVOKE_INTERFACE_RANGE:
    592             /* invoke-kind {vD, vE, vF, vG, vA}, meth@CCCC */
    593             /* invoke-kind/range {vCCCC .. vNNNN}, meth@BBBB */
    594             if (pResults != NULL)
    595                 markMethod(pConst, pResults);
    596             else
    597                 updateMethod(pConst, pIndexMapSet);
    598             break;
    599 
    600         default:
    601             // ignore this instruction
    602             ;
    603         }
    604 
    605         width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
    606         assert(width > 0 && width <= (int)insnsSize);
    607 
    608         insns += width;
    609         insnsSize -= width;
    610     }
    611 }
    612 
    613 /*
    614  * This is an AllMethodsFunc implementation.
    615  *
    616  * Run through the instructions in this method, setting bits in the "pResults"
    617  * struct as we locate constants.
    618  */
    619 static void markUsedConstants(DexFile* pDexFile, const char* classDescriptor,
    620     DexMethod* pDexMethod, void* arg)
    621 {
    622     ScanResults* pResults = (ScanResults*) arg;
    623     const DexCode* pDexCode = dexGetCode(pDexFile, pDexMethod);
    624 
    625     if (false) {
    626         const DexMethodId* pMethodId;
    627         const char* methodName;
    628         pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
    629         methodName = dexStringById(pDexFile, pMethodId->nameIdx);
    630         printf(" %s.%s\n", classDescriptor, methodName);
    631     }
    632 
    633     if (pDexCode != NULL) {
    634         u2* insns = (u2*) pDexCode->insns;
    635         u4 insnsSize = pDexCode->insnsSize;
    636         markUsedConstantsFromInsns(insns, insnsSize, pResults, NULL);
    637     } else {
    638         //printf(" (no code)\n");
    639     }
    640 }
    641 
    642 /*
    643  * This is an AllMethodsFunc implementation.
    644  *
    645  * Run through the instructions in this method, altering the constants used.
    646  */
    647 static void updateUsedConstants(DexFile* pDexFile, const char* classDescriptor,
    648     DexMethod* pDexMethod, void* arg)
    649 {
    650     const IndexMapSet* pIndexMapSet = (const IndexMapSet*) arg;
    651     const DexCode* pDexCode = dexGetCode(pDexFile, pDexMethod);
    652 
    653     if (false) {
    654         const DexMethodId* pMethodId;
    655         const char* methodName;
    656         pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
    657         methodName = dexStringById(pDexFile, pMethodId->nameIdx);
    658         printf(" %s.%s\n", classDescriptor, methodName);
    659     }
    660 
    661     if (pDexCode != NULL) {
    662         u2* insns = (u2*) pDexCode->insns;
    663         u4 insnsSize = pDexCode->insnsSize;
    664         markUsedConstantsFromInsns(insns, insnsSize, NULL, pIndexMapSet);
    665     } else {
    666         //printf(" (no code)\n");
    667     }
    668 }
    669 
    670 /*
    671  * Count up the bits and show a count.
    672  */
    673 static void showBitCount(const char* label, int setCount, int maxCount)
    674 {
    675     printf("%s: %d of %d (%.1f%% unused)\n", label, setCount, maxCount,
    676         ((maxCount - setCount) * 100.0f) / maxCount);
    677 }
    678 
    679 /*
    680  * Print some debug info.
    681  */
    682 static void summarizeResults(DvmDex* pDvmDex, ScanResults* pResults)
    683 {
    684     DexFile* pDexFile = pDvmDex->pDexFile;
    685     int i;
    686 
    687 #if 0
    688     for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->typeIdsSize; i++) {
    689         const DexTypeId* pDexTypeId;
    690         const char* classDescr;
    691 
    692         pDexTypeId = dexGetTypeId(pDexFile, i);
    693         classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx);
    694 
    695         if (dvmIsBitSet(pResults->usedStrings, i))
    696             printf("used  : %04x '%s'\n", i, classDescr);
    697         else
    698             printf("unused: %04x '%s'\n", i, classDescr);
    699     }
    700 #endif
    701 #if 0
    702     for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->methodIdsSize; i++) {
    703         const DexMethodId* pDexMethodId;
    704         const DexTypeId* pDexTypeId;
    705         const char* classDescr;
    706         const char* methodName;
    707 
    708         pDexMethodId = dexGetMethodId(pDexFile, i);
    709         methodName = dexStringById(pDexFile, pDexMethodId->nameIdx);
    710 
    711         pDexTypeId = dexGetTypeId(pDexFile, pDexMethodId->classIdx);
    712         classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx);
    713         if (dvmIsBitSet(pResults->usedMethods, i))
    714             printf("used  : %s.%s\n", classDescr, methodName);
    715         else
    716             printf("unused: %s.%s\n", classDescr, methodName);
    717     }
    718 #endif
    719 #if 0
    720     for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->fieldIdsSize; i++) {
    721         const DexFieldId* pDexFieldId;
    722         const DexTypeId* pDexTypeId;
    723         const char* classDescr;
    724         const char* fieldName;
    725 
    726         pDexFieldId = dexGetFieldId(pDexFile, i);
    727         fieldName = dexStringById(pDexFile, pDexFieldId->nameIdx);
    728 
    729         pDexTypeId = dexGetTypeId(pDexFile, pDexFieldId->classIdx);
    730         classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx);
    731         if (dvmIsBitSet(pResults->usedFields, i))
    732             printf("used  : %s.%s\n", classDescr, fieldName);
    733         else
    734             printf("unused: %s.%s\n", classDescr, fieldName);
    735     }
    736 #endif
    737 #if 0
    738     for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->stringIdsSize; i++) {
    739         const char* str;
    740 
    741         str = dexStringById(pDexFile, i);
    742 
    743         if (dvmIsBitSet(pResults->usedStrings, i))
    744             printf("used  : %04x '%s'\n", i, str);
    745         else
    746             printf("unused: %04x '%s'\n", i, str);
    747     }
    748 #endif
    749 
    750     int totalMax, totalSet;
    751     int setCount;
    752 
    753     totalMax = totalSet = 0;
    754 
    755     setCount = dvmCountSetBits(pResults->usedClasses);
    756     showBitCount("classes", setCount, pDexFile->pHeader->typeIdsSize);
    757     totalSet += setCount;
    758     totalMax += pDexFile->pHeader->typeIdsSize;
    759 
    760     setCount = dvmCountSetBits(pResults->usedMethods);
    761     showBitCount("methods", setCount, pDexFile->pHeader->methodIdsSize);
    762     totalSet += setCount;
    763     totalMax += pDexFile->pHeader->methodIdsSize;
    764 
    765     setCount = dvmCountSetBits(pResults->usedFields);
    766     showBitCount("fields",  setCount, pDexFile->pHeader->fieldIdsSize);
    767     totalSet += setCount;
    768     totalMax += pDexFile->pHeader->fieldIdsSize;
    769 
    770     setCount = dvmCountSetBits(pResults->usedStrings);
    771     showBitCount("strings", setCount, pDexFile->pHeader->stringIdsSize);
    772     totalSet += setCount;
    773     totalMax += pDexFile->pHeader->stringIdsSize;
    774 
    775     printf("TOTAL %d of %d (%.1f%% unused -- %.1fK)\n", totalSet, totalMax,
    776         ((totalMax - totalSet) * 100.0f) / totalMax,
    777         (totalMax - totalSet) / 256.0f);
    778 }
    779 
    780 /*
    781  * Fill out an index map set entry.
    782  *
    783  * If we can't fit the map into our base type, we don't create the map.
    784  *
    785  * Returns "false" if allocation fails.
    786  */
    787 static bool constructIndexMap(int totalCount, const BitVector* pBits,
    788     IndexMap* pMap)
    789 {
    790     const int kMaxIndex = 65534;        // 65535, a/k/a -1, is special
    791     int setCount;
    792 
    793     setCount = dvmCountSetBits(pBits);
    794     if (setCount < 0 || setCount > kMaxIndex)
    795         return true;
    796 
    797     u2* mapToOld = (u2*) malloc(setCount * sizeof(u2));
    798     u2* mapToNew = (u2*) malloc(totalCount * sizeof(u2));
    799     if (mapToOld == NULL || mapToNew == NULL) {
    800         free(mapToOld);
    801         free(mapToNew);
    802         return false;
    803     }
    804 
    805     /* fill in both arrays */
    806     int entry, idx = 0;
    807     for (entry = 0; entry < totalCount; entry++) {
    808         if (dvmIsBitSet(pBits, entry)) {
    809             mapToNew[entry] = idx;
    810             mapToOld[idx] = entry;
    811             idx++;
    812         } else {
    813             mapToNew[entry] = kNoIndexMapping;
    814         }
    815     }
    816 
    817     if (idx != setCount) {
    818         LOGE("GLITCH: idx=%d setCount=%d\n", idx, setCount);
    819         dvmAbort();
    820     }
    821 
    822     /* success */
    823     pMap->mapToOld = mapToOld;
    824     pMap->mapToNew = mapToNew;
    825     pMap->origCount = totalCount;
    826     pMap->newCount = setCount;
    827 
    828     return true;
    829 }
    830 
    831 /*
    832  * Construct a "reducing" chunk, with maps that convert the constants in
    833  * instructions to their reduced value for the cache lookup.
    834  */
    835 static bool constructReducingDataChunk(IndexMapSet* pIndexMapSet)
    836 {
    837     int chunkLen = 0;
    838     int i;
    839 
    840     pIndexMapSet->chunkType = kDexChunkReducingIndexMap;
    841 
    842     /*
    843      * Compute space requirements and allocate storage.
    844      */
    845     for (i = 0; i < kNumIndexMaps; i++) {
    846         /* space for the "original" count */
    847         chunkLen += sizeof(u4);
    848 
    849         /* space for the "reduced" count */
    850         chunkLen += sizeof(u4);
    851 
    852         /* add data length, round up to 32-bit boundary */
    853         chunkLen += pIndexMapSet->map[i].origCount * sizeof(u2);
    854         chunkLen = (chunkLen + 3) & ~3;
    855     }
    856 
    857     pIndexMapSet->chunkDataLen = chunkLen;
    858     pIndexMapSet->chunkData = (u1*) calloc(1, chunkLen);
    859     if (pIndexMapSet->chunkData == NULL)
    860         return false;
    861 
    862     /*
    863      * Copy the data in.
    864      */
    865     u1* ptr = pIndexMapSet->chunkData;
    866     for (i = 0; i < kNumIndexMaps; i++) {
    867         u4* wordPtr = (u4*) ptr;
    868         int dataLen = pIndexMapSet->map[i].origCount * sizeof(u2);
    869 
    870         *wordPtr++ = pIndexMapSet->map[i].origCount;
    871         *wordPtr++ = pIndexMapSet->map[i].newCount;
    872         if (dataLen != 0)
    873             memcpy(wordPtr, pIndexMapSet->map[i].mapToNew, dataLen);
    874 
    875         /* advance pointer, maintaining 32-bit alignment */
    876         ptr = ((u1*) wordPtr) + dataLen;
    877         ptr = (u1*) (((int) ptr + 3) & ~3);
    878     }
    879 
    880     if (ptr - (u1*) pIndexMapSet->chunkData != chunkLen) {
    881         LOGE("GLITCH: expected len=%d, actual=%d\n",
    882             chunkLen, ptr - (u1*) pIndexMapSet->chunkData);
    883         dvmAbort();
    884     }
    885 
    886     return true;
    887 }
    888 
    889 /*
    890  * Construct an "expanding" chunk, with maps that convert instructions
    891  * with reduced constants back to their full original values.
    892  */
    893 static bool constructExpandingDataChunk(IndexMapSet* pIndexMapSet)
    894 {
    895     int chunkLen = 0;
    896     int i;
    897 
    898     pIndexMapSet->chunkType = kDexChunkExpandingIndexMap;
    899 
    900     /*
    901      * Compute space requirements and allocate storage.
    902      */
    903     for (i = 0; i < kNumIndexMaps; i++) {
    904         /* space for the length word */
    905         chunkLen += sizeof(u4);
    906 
    907         /* add data length, round up to 32-bit boundary */
    908         chunkLen += pIndexMapSet->map[i].newCount * sizeof(u2);
    909         chunkLen = (chunkLen + 3) & ~3;
    910     }
    911 
    912     pIndexMapSet->chunkDataLen = chunkLen;
    913     pIndexMapSet->chunkData = (u1*) calloc(1, chunkLen);
    914     if (pIndexMapSet->chunkData == NULL)
    915         return false;
    916 
    917     /*
    918      * Copy the data in.
    919      */
    920     u1* ptr = pIndexMapSet->chunkData;
    921     for (i = 0; i < kNumIndexMaps; i++) {
    922         u4* wordPtr = (u4*) ptr;
    923         int dataLen = pIndexMapSet->map[i].newCount * sizeof(u2);
    924 
    925         *wordPtr++ = pIndexMapSet->map[i].newCount;
    926         if (dataLen != 0)
    927             memcpy(wordPtr, pIndexMapSet->map[i].mapToOld, dataLen);
    928 
    929         /* advance pointer, maintaining 32-bit alignment */
    930         ptr = ((u1*) wordPtr) + dataLen;
    931         ptr = (u1*) (((int) ptr + 3) & ~3);
    932     }
    933 
    934     if (ptr - (u1*) pIndexMapSet->chunkData != chunkLen) {
    935         LOGE("GLITCH: expected len=%d, actual=%d\n",
    936             chunkLen, ptr - (u1*) pIndexMapSet->chunkData);
    937         dvmAbort();
    938     }
    939 
    940     return true;
    941 }
    942 
    943 /*
    944  * Construct the "chunk" of data that will be appended to the optimized DEX
    945  * file.
    946  */
    947 static bool constructDataChunk(IndexMapSet* pIndexMapSet)
    948 {
    949     assert(sizeof(pIndexMapSet->map[0].mapToOld[0]) == sizeof(u2));
    950     assert(sizeof(pIndexMapSet->map[0].mapToNew[0]) == sizeof(u2));
    951 
    952 #if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING
    953     return constructExpandingDataChunk(pIndexMapSet);
    954 #else
    955     return constructReducingDataChunk(pIndexMapSet);
    956 #endif
    957 }
    958 
    959 /*
    960  * Allocate storage to hold the maps.
    961  */
    962 static IndexMapSet* createIndexMapSet(const DexFile* pDexFile,
    963     ScanResults* pResults)
    964 {
    965     IndexMapSet* pIndexMapSet;
    966     int setCount;
    967     bool okay = true;
    968 
    969     pIndexMapSet = calloc(1, sizeof(*pIndexMapSet));
    970     if (pIndexMapSet == NULL)
    971         return NULL;
    972 
    973     okay = okay && constructIndexMap(pDexFile->pHeader->typeIdsSize,
    974             pResults->usedClasses, &pIndexMapSet->map[kMapClasses]);
    975     okay = okay && constructIndexMap(pDexFile->pHeader->methodIdsSize,
    976             pResults->usedMethods, &pIndexMapSet->map[kMapMethods]);
    977     okay = okay && constructIndexMap(pDexFile->pHeader->fieldIdsSize,
    978             pResults->usedFields, &pIndexMapSet->map[kMapFields]);
    979     okay = okay && constructIndexMap(pDexFile->pHeader->stringIdsSize,
    980             pResults->usedStrings, &pIndexMapSet->map[kMapStrings]);
    981 
    982     LOGVV("Constr: %d %d %d %d\n",
    983         pIndexMapSet->map[kMapClasses].mapToOld[0],
    984         pIndexMapSet->map[kMapMethods].mapToOld[0],
    985         pIndexMapSet->map[kMapFields].mapToOld[0],
    986         pIndexMapSet->map[kMapStrings].mapToOld[0]);
    987 
    988     okay = okay && constructDataChunk(pIndexMapSet);
    989 
    990     if (!okay) {
    991         dvmFreeIndexMapSet(pIndexMapSet);
    992         return NULL;
    993     }
    994 
    995     return pIndexMapSet;
    996 }
    997 
    998 /*
    999  * Free map storage.
   1000  *
   1001  * "pIndexMapSet" may be incomplete.
   1002  */
   1003 void dvmFreeIndexMapSet(IndexMapSet* pIndexMapSet)
   1004 {
   1005     int i;
   1006 
   1007     if (pIndexMapSet == NULL)
   1008         return;
   1009 
   1010     for (i = 0; i < kNumIndexMaps; i++) {
   1011         free(pIndexMapSet->map[i].mapToOld);
   1012         free(pIndexMapSet->map[i].mapToNew);
   1013     }
   1014     free(pIndexMapSet->chunkData);
   1015     free(pIndexMapSet);
   1016 }
   1017 
   1018 /*
   1019  * Rewrite constant indexes to reduce heap requirements.
   1020  */
   1021 IndexMapSet* dvmRewriteConstants(DvmDex* pDvmDex)
   1022 {
   1023 #if (DVM_RESOLVER_CACHE != DVM_RC_REDUCING) && \
   1024     (DVM_RESOLVER_CACHE != DVM_RC_EXPANDING)
   1025     /* nothing to do */
   1026     return NULL;
   1027 #endif
   1028 
   1029     /*
   1030      * We're looking for instructions that use "constant pool" entries for
   1031      * classes, methods, fields, and strings.  Many field and method entries
   1032      * are optimized away, and many string constants are never accessed from
   1033      * code or annotations.
   1034      */
   1035     ScanResults* pResults = allocScanResults(pDvmDex->pDexFile);
   1036     forAllMethods(pDvmDex->pDexFile, markUsedConstants, pResults);
   1037 
   1038     summarizeResults(pDvmDex, pResults);
   1039 
   1040     /*
   1041      * Allocate and populate the index maps.
   1042      */
   1043     IndexMapSet* pIndexMapSet = createIndexMapSet(pDvmDex->pDexFile, pResults);
   1044 #if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING
   1045     if (pIndexMapSet != NULL) {
   1046         /*
   1047          * Rewrite the constants to use the reduced set.
   1048          */
   1049         forAllMethods(pDvmDex->pDexFile, updateUsedConstants, pIndexMapSet);
   1050     }
   1051 #endif
   1052 
   1053     freeScanResults(pResults);
   1054 
   1055     return pIndexMapSet;
   1056 }
   1057 
   1058