OSDN Git Service

Merge change I49b05da2
[android-x86/dalvik.git] / vm / analysis / ReduceConstants.c
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