The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 |
Carl Shapiro | de75089 | 2010-06-08 16:37:12 -0700 | [diff] [blame] | 192 | |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 | */ |
Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 647 | #if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 648 | static void updateUsedConstants(DexFile* pDexFile, const char* classDescriptor, |
| 649 | DexMethod* pDexMethod, void* arg) |
| 650 | { |
| 651 | const IndexMapSet* pIndexMapSet = (const IndexMapSet*) arg; |
| 652 | const DexCode* pDexCode = dexGetCode(pDexFile, pDexMethod); |
| 653 | |
| 654 | if (false) { |
| 655 | const DexMethodId* pMethodId; |
| 656 | const char* methodName; |
| 657 | pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); |
| 658 | methodName = dexStringById(pDexFile, pMethodId->nameIdx); |
| 659 | printf(" %s.%s\n", classDescriptor, methodName); |
| 660 | } |
| 661 | |
| 662 | if (pDexCode != NULL) { |
| 663 | u2* insns = (u2*) pDexCode->insns; |
| 664 | u4 insnsSize = pDexCode->insnsSize; |
| 665 | markUsedConstantsFromInsns(insns, insnsSize, NULL, pIndexMapSet); |
| 666 | } else { |
| 667 | //printf(" (no code)\n"); |
| 668 | } |
| 669 | } |
Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 670 | #endif |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 671 | |
| 672 | /* |
| 673 | * Count up the bits and show a count. |
| 674 | */ |
| 675 | static void showBitCount(const char* label, int setCount, int maxCount) |
| 676 | { |
| 677 | printf("%s: %d of %d (%.1f%% unused)\n", label, setCount, maxCount, |
| 678 | ((maxCount - setCount) * 100.0f) / maxCount); |
| 679 | } |
| 680 | |
| 681 | /* |
| 682 | * Print some debug info. |
| 683 | */ |
| 684 | static void summarizeResults(DvmDex* pDvmDex, ScanResults* pResults) |
| 685 | { |
| 686 | DexFile* pDexFile = pDvmDex->pDexFile; |
Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 687 | #if 0 |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 688 | int i; |
| 689 | |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 690 | for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->typeIdsSize; i++) { |
| 691 | const DexTypeId* pDexTypeId; |
| 692 | const char* classDescr; |
| 693 | |
| 694 | pDexTypeId = dexGetTypeId(pDexFile, i); |
| 695 | classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx); |
| 696 | |
| 697 | if (dvmIsBitSet(pResults->usedStrings, i)) |
| 698 | printf("used : %04x '%s'\n", i, classDescr); |
| 699 | else |
| 700 | printf("unused: %04x '%s'\n", i, classDescr); |
| 701 | } |
| 702 | #endif |
| 703 | #if 0 |
| 704 | for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->methodIdsSize; i++) { |
| 705 | const DexMethodId* pDexMethodId; |
| 706 | const DexTypeId* pDexTypeId; |
| 707 | const char* classDescr; |
| 708 | const char* methodName; |
| 709 | |
| 710 | pDexMethodId = dexGetMethodId(pDexFile, i); |
| 711 | methodName = dexStringById(pDexFile, pDexMethodId->nameIdx); |
| 712 | |
| 713 | pDexTypeId = dexGetTypeId(pDexFile, pDexMethodId->classIdx); |
| 714 | classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx); |
| 715 | if (dvmIsBitSet(pResults->usedMethods, i)) |
| 716 | printf("used : %s.%s\n", classDescr, methodName); |
| 717 | else |
| 718 | printf("unused: %s.%s\n", classDescr, methodName); |
| 719 | } |
| 720 | #endif |
| 721 | #if 0 |
| 722 | for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->fieldIdsSize; i++) { |
| 723 | const DexFieldId* pDexFieldId; |
| 724 | const DexTypeId* pDexTypeId; |
| 725 | const char* classDescr; |
| 726 | const char* fieldName; |
| 727 | |
| 728 | pDexFieldId = dexGetFieldId(pDexFile, i); |
| 729 | fieldName = dexStringById(pDexFile, pDexFieldId->nameIdx); |
| 730 | |
| 731 | pDexTypeId = dexGetTypeId(pDexFile, pDexFieldId->classIdx); |
| 732 | classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx); |
| 733 | if (dvmIsBitSet(pResults->usedFields, i)) |
| 734 | printf("used : %s.%s\n", classDescr, fieldName); |
| 735 | else |
| 736 | printf("unused: %s.%s\n", classDescr, fieldName); |
| 737 | } |
| 738 | #endif |
| 739 | #if 0 |
| 740 | for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->stringIdsSize; i++) { |
| 741 | const char* str; |
| 742 | |
| 743 | str = dexStringById(pDexFile, i); |
| 744 | |
| 745 | if (dvmIsBitSet(pResults->usedStrings, i)) |
| 746 | printf("used : %04x '%s'\n", i, str); |
| 747 | else |
| 748 | printf("unused: %04x '%s'\n", i, str); |
| 749 | } |
| 750 | #endif |
| 751 | |
| 752 | int totalMax, totalSet; |
| 753 | int setCount; |
| 754 | |
| 755 | totalMax = totalSet = 0; |
| 756 | |
| 757 | setCount = dvmCountSetBits(pResults->usedClasses); |
| 758 | showBitCount("classes", setCount, pDexFile->pHeader->typeIdsSize); |
| 759 | totalSet += setCount; |
| 760 | totalMax += pDexFile->pHeader->typeIdsSize; |
| 761 | |
| 762 | setCount = dvmCountSetBits(pResults->usedMethods); |
| 763 | showBitCount("methods", setCount, pDexFile->pHeader->methodIdsSize); |
| 764 | totalSet += setCount; |
| 765 | totalMax += pDexFile->pHeader->methodIdsSize; |
| 766 | |
| 767 | setCount = dvmCountSetBits(pResults->usedFields); |
| 768 | showBitCount("fields", setCount, pDexFile->pHeader->fieldIdsSize); |
| 769 | totalSet += setCount; |
| 770 | totalMax += pDexFile->pHeader->fieldIdsSize; |
| 771 | |
| 772 | setCount = dvmCountSetBits(pResults->usedStrings); |
| 773 | showBitCount("strings", setCount, pDexFile->pHeader->stringIdsSize); |
| 774 | totalSet += setCount; |
| 775 | totalMax += pDexFile->pHeader->stringIdsSize; |
| 776 | |
| 777 | printf("TOTAL %d of %d (%.1f%% unused -- %.1fK)\n", totalSet, totalMax, |
| 778 | ((totalMax - totalSet) * 100.0f) / totalMax, |
| 779 | (totalMax - totalSet) / 256.0f); |
| 780 | } |
| 781 | |
| 782 | /* |
| 783 | * Fill out an index map set entry. |
| 784 | * |
| 785 | * If we can't fit the map into our base type, we don't create the map. |
| 786 | * |
| 787 | * Returns "false" if allocation fails. |
| 788 | */ |
| 789 | static bool constructIndexMap(int totalCount, const BitVector* pBits, |
| 790 | IndexMap* pMap) |
| 791 | { |
| 792 | const int kMaxIndex = 65534; // 65535, a/k/a -1, is special |
| 793 | int setCount; |
| 794 | |
| 795 | setCount = dvmCountSetBits(pBits); |
| 796 | if (setCount < 0 || setCount > kMaxIndex) |
| 797 | return true; |
| 798 | |
| 799 | u2* mapToOld = (u2*) malloc(setCount * sizeof(u2)); |
| 800 | u2* mapToNew = (u2*) malloc(totalCount * sizeof(u2)); |
| 801 | if (mapToOld == NULL || mapToNew == NULL) { |
| 802 | free(mapToOld); |
| 803 | free(mapToNew); |
| 804 | return false; |
| 805 | } |
| 806 | |
| 807 | /* fill in both arrays */ |
| 808 | int entry, idx = 0; |
| 809 | for (entry = 0; entry < totalCount; entry++) { |
| 810 | if (dvmIsBitSet(pBits, entry)) { |
| 811 | mapToNew[entry] = idx; |
| 812 | mapToOld[idx] = entry; |
| 813 | idx++; |
| 814 | } else { |
| 815 | mapToNew[entry] = kNoIndexMapping; |
| 816 | } |
| 817 | } |
| 818 | |
| 819 | if (idx != setCount) { |
| 820 | LOGE("GLITCH: idx=%d setCount=%d\n", idx, setCount); |
| 821 | dvmAbort(); |
| 822 | } |
| 823 | |
| 824 | /* success */ |
| 825 | pMap->mapToOld = mapToOld; |
| 826 | pMap->mapToNew = mapToNew; |
| 827 | pMap->origCount = totalCount; |
| 828 | pMap->newCount = setCount; |
| 829 | |
| 830 | return true; |
| 831 | } |
| 832 | |
| 833 | /* |
| 834 | * Construct a "reducing" chunk, with maps that convert the constants in |
| 835 | * instructions to their reduced value for the cache lookup. |
| 836 | */ |
| 837 | static bool constructReducingDataChunk(IndexMapSet* pIndexMapSet) |
| 838 | { |
| 839 | int chunkLen = 0; |
| 840 | int i; |
| 841 | |
| 842 | pIndexMapSet->chunkType = kDexChunkReducingIndexMap; |
| 843 | |
| 844 | /* |
| 845 | * Compute space requirements and allocate storage. |
| 846 | */ |
| 847 | for (i = 0; i < kNumIndexMaps; i++) { |
| 848 | /* space for the "original" count */ |
| 849 | chunkLen += sizeof(u4); |
| 850 | |
| 851 | /* space for the "reduced" count */ |
| 852 | chunkLen += sizeof(u4); |
| 853 | |
| 854 | /* add data length, round up to 32-bit boundary */ |
| 855 | chunkLen += pIndexMapSet->map[i].origCount * sizeof(u2); |
| 856 | chunkLen = (chunkLen + 3) & ~3; |
| 857 | } |
| 858 | |
| 859 | pIndexMapSet->chunkDataLen = chunkLen; |
| 860 | pIndexMapSet->chunkData = (u1*) calloc(1, chunkLen); |
| 861 | if (pIndexMapSet->chunkData == NULL) |
| 862 | return false; |
| 863 | |
| 864 | /* |
| 865 | * Copy the data in. |
| 866 | */ |
| 867 | u1* ptr = pIndexMapSet->chunkData; |
| 868 | for (i = 0; i < kNumIndexMaps; i++) { |
| 869 | u4* wordPtr = (u4*) ptr; |
| 870 | int dataLen = pIndexMapSet->map[i].origCount * sizeof(u2); |
| 871 | |
| 872 | *wordPtr++ = pIndexMapSet->map[i].origCount; |
| 873 | *wordPtr++ = pIndexMapSet->map[i].newCount; |
| 874 | if (dataLen != 0) |
| 875 | memcpy(wordPtr, pIndexMapSet->map[i].mapToNew, dataLen); |
| 876 | |
| 877 | /* advance pointer, maintaining 32-bit alignment */ |
| 878 | ptr = ((u1*) wordPtr) + dataLen; |
| 879 | ptr = (u1*) (((int) ptr + 3) & ~3); |
| 880 | } |
| 881 | |
| 882 | if (ptr - (u1*) pIndexMapSet->chunkData != chunkLen) { |
| 883 | LOGE("GLITCH: expected len=%d, actual=%d\n", |
| 884 | chunkLen, ptr - (u1*) pIndexMapSet->chunkData); |
| 885 | dvmAbort(); |
| 886 | } |
| 887 | |
| 888 | return true; |
| 889 | } |
| 890 | |
| 891 | /* |
| 892 | * Construct an "expanding" chunk, with maps that convert instructions |
| 893 | * with reduced constants back to their full original values. |
| 894 | */ |
Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 895 | #if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 896 | static bool constructExpandingDataChunk(IndexMapSet* pIndexMapSet) |
| 897 | { |
| 898 | int chunkLen = 0; |
| 899 | int i; |
| 900 | |
| 901 | pIndexMapSet->chunkType = kDexChunkExpandingIndexMap; |
| 902 | |
| 903 | /* |
| 904 | * Compute space requirements and allocate storage. |
| 905 | */ |
| 906 | for (i = 0; i < kNumIndexMaps; i++) { |
| 907 | /* space for the length word */ |
| 908 | chunkLen += sizeof(u4); |
| 909 | |
| 910 | /* add data length, round up to 32-bit boundary */ |
| 911 | chunkLen += pIndexMapSet->map[i].newCount * sizeof(u2); |
| 912 | chunkLen = (chunkLen + 3) & ~3; |
| 913 | } |
| 914 | |
| 915 | pIndexMapSet->chunkDataLen = chunkLen; |
| 916 | pIndexMapSet->chunkData = (u1*) calloc(1, chunkLen); |
| 917 | if (pIndexMapSet->chunkData == NULL) |
| 918 | return false; |
| 919 | |
| 920 | /* |
| 921 | * Copy the data in. |
| 922 | */ |
| 923 | u1* ptr = pIndexMapSet->chunkData; |
| 924 | for (i = 0; i < kNumIndexMaps; i++) { |
| 925 | u4* wordPtr = (u4*) ptr; |
| 926 | int dataLen = pIndexMapSet->map[i].newCount * sizeof(u2); |
| 927 | |
| 928 | *wordPtr++ = pIndexMapSet->map[i].newCount; |
| 929 | if (dataLen != 0) |
| 930 | memcpy(wordPtr, pIndexMapSet->map[i].mapToOld, dataLen); |
| 931 | |
| 932 | /* advance pointer, maintaining 32-bit alignment */ |
| 933 | ptr = ((u1*) wordPtr) + dataLen; |
| 934 | ptr = (u1*) (((int) ptr + 3) & ~3); |
| 935 | } |
| 936 | |
| 937 | if (ptr - (u1*) pIndexMapSet->chunkData != chunkLen) { |
| 938 | LOGE("GLITCH: expected len=%d, actual=%d\n", |
| 939 | chunkLen, ptr - (u1*) pIndexMapSet->chunkData); |
| 940 | dvmAbort(); |
| 941 | } |
| 942 | |
| 943 | return true; |
| 944 | } |
Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 945 | #endif |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 946 | |
| 947 | /* |
| 948 | * Construct the "chunk" of data that will be appended to the optimized DEX |
| 949 | * file. |
| 950 | */ |
| 951 | static bool constructDataChunk(IndexMapSet* pIndexMapSet) |
| 952 | { |
| 953 | assert(sizeof(pIndexMapSet->map[0].mapToOld[0]) == sizeof(u2)); |
| 954 | assert(sizeof(pIndexMapSet->map[0].mapToNew[0]) == sizeof(u2)); |
| 955 | |
| 956 | #if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING |
| 957 | return constructExpandingDataChunk(pIndexMapSet); |
| 958 | #else |
| 959 | return constructReducingDataChunk(pIndexMapSet); |
| 960 | #endif |
| 961 | } |
| 962 | |
| 963 | /* |
| 964 | * Allocate storage to hold the maps. |
| 965 | */ |
| 966 | static IndexMapSet* createIndexMapSet(const DexFile* pDexFile, |
| 967 | ScanResults* pResults) |
| 968 | { |
| 969 | IndexMapSet* pIndexMapSet; |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 970 | bool okay = true; |
| 971 | |
| 972 | pIndexMapSet = calloc(1, sizeof(*pIndexMapSet)); |
| 973 | if (pIndexMapSet == NULL) |
| 974 | return NULL; |
| 975 | |
| 976 | okay = okay && constructIndexMap(pDexFile->pHeader->typeIdsSize, |
| 977 | pResults->usedClasses, &pIndexMapSet->map[kMapClasses]); |
| 978 | okay = okay && constructIndexMap(pDexFile->pHeader->methodIdsSize, |
| 979 | pResults->usedMethods, &pIndexMapSet->map[kMapMethods]); |
| 980 | okay = okay && constructIndexMap(pDexFile->pHeader->fieldIdsSize, |
| 981 | pResults->usedFields, &pIndexMapSet->map[kMapFields]); |
| 982 | okay = okay && constructIndexMap(pDexFile->pHeader->stringIdsSize, |
| 983 | pResults->usedStrings, &pIndexMapSet->map[kMapStrings]); |
| 984 | |
| 985 | LOGVV("Constr: %d %d %d %d\n", |
| 986 | pIndexMapSet->map[kMapClasses].mapToOld[0], |
| 987 | pIndexMapSet->map[kMapMethods].mapToOld[0], |
| 988 | pIndexMapSet->map[kMapFields].mapToOld[0], |
| 989 | pIndexMapSet->map[kMapStrings].mapToOld[0]); |
| 990 | |
| 991 | okay = okay && constructDataChunk(pIndexMapSet); |
| 992 | |
| 993 | if (!okay) { |
| 994 | dvmFreeIndexMapSet(pIndexMapSet); |
| 995 | return NULL; |
| 996 | } |
| 997 | |
| 998 | return pIndexMapSet; |
| 999 | } |
| 1000 | |
| 1001 | /* |
| 1002 | * Free map storage. |
| 1003 | * |
| 1004 | * "pIndexMapSet" may be incomplete. |
| 1005 | */ |
| 1006 | void dvmFreeIndexMapSet(IndexMapSet* pIndexMapSet) |
| 1007 | { |
| 1008 | int i; |
| 1009 | |
| 1010 | if (pIndexMapSet == NULL) |
| 1011 | return; |
| 1012 | |
| 1013 | for (i = 0; i < kNumIndexMaps; i++) { |
| 1014 | free(pIndexMapSet->map[i].mapToOld); |
| 1015 | free(pIndexMapSet->map[i].mapToNew); |
| 1016 | } |
| 1017 | free(pIndexMapSet->chunkData); |
| 1018 | free(pIndexMapSet); |
| 1019 | } |
| 1020 | |
| 1021 | /* |
| 1022 | * Rewrite constant indexes to reduce heap requirements. |
| 1023 | */ |
| 1024 | IndexMapSet* dvmRewriteConstants(DvmDex* pDvmDex) |
| 1025 | { |
| 1026 | #if (DVM_RESOLVER_CACHE != DVM_RC_REDUCING) && \ |
| 1027 | (DVM_RESOLVER_CACHE != DVM_RC_EXPANDING) |
| 1028 | /* nothing to do */ |
| 1029 | return NULL; |
| 1030 | #endif |
| 1031 | |
| 1032 | /* |
| 1033 | * We're looking for instructions that use "constant pool" entries for |
| 1034 | * classes, methods, fields, and strings. Many field and method entries |
| 1035 | * are optimized away, and many string constants are never accessed from |
| 1036 | * code or annotations. |
| 1037 | */ |
| 1038 | ScanResults* pResults = allocScanResults(pDvmDex->pDexFile); |
| 1039 | forAllMethods(pDvmDex->pDexFile, markUsedConstants, pResults); |
| 1040 | |
| 1041 | summarizeResults(pDvmDex, pResults); |
| 1042 | |
| 1043 | /* |
| 1044 | * Allocate and populate the index maps. |
| 1045 | */ |
| 1046 | IndexMapSet* pIndexMapSet = createIndexMapSet(pDvmDex->pDexFile, pResults); |
| 1047 | #if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING |
| 1048 | if (pIndexMapSet != NULL) { |
| 1049 | /* |
| 1050 | * Rewrite the constants to use the reduced set. |
| 1051 | */ |
| 1052 | forAllMethods(pDvmDex->pDexFile, updateUsedConstants, pIndexMapSet); |
| 1053 | } |
| 1054 | #endif |
| 1055 | |
| 1056 | freeScanResults(pResults); |
| 1057 | |
| 1058 | return pIndexMapSet; |
| 1059 | } |