From: Dan Bornstein Date: Wed, 8 Sep 2010 22:50:00 +0000 (-0700) Subject: Remove the functionality in ReduceConstants.[ch]. X-Git-Tag: android-x86-4.0-r1~164^2~18^2~48 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=d394371b;p=android-x86%2Fdalvik.git Remove the functionality in ReduceConstants.[ch]. It was a good experiment to try, but it was never made production-ready, and it doesn't look like it would be a net win at this point. We metaphorically pour out a beer in its honor. Change-Id: I7f6ac95f5b7c963df0a3015ed33595fa1a928636 --- diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c index 714a0f86c..636408afb 100644 --- a/dexdump/DexDump.c +++ b/dexdump/DexDump.c @@ -482,12 +482,6 @@ void dumpOptDirectory(const DexFile* pDexFile) case kDexChunkRegisterMaps: verboseStr = "register maps"; break; - case kDexChunkReducingIndexMap: - verboseStr = "'reducing' index map"; - break; - case kDexChunkExpandingIndexMap: - verboseStr = "'expanding' index map"; - break; default: verboseStr = "(unknown chunk type)"; break; diff --git a/libdex/DexFile.h b/libdex/DexFile.h index 279650f88..06dc8646f 100644 --- a/libdex/DexFile.h +++ b/libdex/DexFile.h @@ -167,9 +167,6 @@ enum { kDexChunkClassLookup = 0x434c4b50, /* CLKP */ kDexChunkRegisterMaps = 0x524d4150, /* RMAP */ - kDexChunkReducingIndexMap = 0x5249584d, /* RIXM */ - kDexChunkExpandingIndexMap = 0x4549584d, /* EIXM */ - kDexChunkEnd = 0x41454e44, /* AEND */ }; @@ -444,30 +441,6 @@ typedef struct DexClassLookup { } DexClassLookup; /* - * Map constant pool indices from one form to another. Some or all of these - * may be NULL. - * - * The map values are 16-bit unsigned values. If the values we map to - * require a larger range, we omit the mapping for that category (which - * requires that the lookup code recognize that the data will not be - * there for all DEX files in all categories.) - */ -typedef struct DexIndexMap { - const u2* classMap; /* map, either expanding or reducing */ - u4 classFullCount; /* same as typeIdsSize */ - u4 classReducedCount; /* post-reduction count */ - const u2* methodMap; - u4 methodFullCount; - u4 methodReducedCount; - const u2* fieldMap; - u4 fieldFullCount; - u4 fieldReducedCount; - const u2* stringMap; - u4 stringFullCount; - u4 stringReducedCount; -} DexIndexMap; - -/* * Header added by DEX optimization pass. Values are always written in * local byte and structure padding. The first field (magic + version) * is guaranteed to be present and directly readable for all expected @@ -523,7 +496,6 @@ typedef struct DexFile { * included in the file. */ const DexClassLookup* pClassLookup; - DexIndexMap indexMap; const void* pRegisterMapPool; // RegisterMapClassPool /* points to start of DEX file data */ diff --git a/libdex/DexOptData.c b/libdex/DexOptData.c index 995b65529..10a4b5d29 100644 --- a/libdex/DexOptData.c +++ b/libdex/DexOptData.c @@ -45,130 +45,6 @@ u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader) return (u4) adler32(adler, start, end - start); } -/* - * Parse out an index map entry, advancing "*pData" and reducing "*pSize". - */ -static bool parseIndexMapEntry(const u1** pData, u4* pSize, bool expanding, - u4* pFullCount, u4* pReducedCount, const u2** pMap) -{ - const u4* wordPtr = (const u4*) *pData; - u4 size = *pSize; - u4 mapCount; - - if (expanding) { - if (size < 4) - return false; - mapCount = *pReducedCount = *wordPtr++; - *pFullCount = (u4) -1; - size -= sizeof(u4); - } else { - if (size < 8) - return false; - mapCount = *pFullCount = *wordPtr++; - *pReducedCount = *wordPtr++; - size -= sizeof(u4) * 2; - } - - u4 mapSize = mapCount * sizeof(u2); - - if (size < mapSize) - return false; - *pMap = (const u2*) wordPtr; - size -= mapSize; - - /* advance the pointer */ - const u1* ptr = (const u1*) wordPtr; - ptr += (mapSize + 3) & ~0x3; - - /* update pass-by-reference values */ - *pData = (const u1*) ptr; - *pSize = size; - - return true; -} - -/* - * Set up some pointers into the mapped data. - * - * See analysis/ReduceConstants.c for the data layout description. - */ -static bool parseIndexMap(DexFile* pDexFile, const u1* data, u4 size, - bool expanding) -{ - if (!parseIndexMapEntry(&data, &size, expanding, - &pDexFile->indexMap.classFullCount, - &pDexFile->indexMap.classReducedCount, - &pDexFile->indexMap.classMap)) - { - return false; - } - - if (!parseIndexMapEntry(&data, &size, expanding, - &pDexFile->indexMap.methodFullCount, - &pDexFile->indexMap.methodReducedCount, - &pDexFile->indexMap.methodMap)) - { - return false; - } - - if (!parseIndexMapEntry(&data, &size, expanding, - &pDexFile->indexMap.fieldFullCount, - &pDexFile->indexMap.fieldReducedCount, - &pDexFile->indexMap.fieldMap)) - { - return false; - } - - if (!parseIndexMapEntry(&data, &size, expanding, - &pDexFile->indexMap.stringFullCount, - &pDexFile->indexMap.stringReducedCount, - &pDexFile->indexMap.stringMap)) - { - return false; - } - - if (expanding) { - /* - * The map includes the "reduced" counts; pull the original counts - * out of the DexFile so that code has a consistent source. - */ - assert(pDexFile->indexMap.classFullCount == (u4) -1); - assert(pDexFile->indexMap.methodFullCount == (u4) -1); - assert(pDexFile->indexMap.fieldFullCount == (u4) -1); - assert(pDexFile->indexMap.stringFullCount == (u4) -1); - -#if 0 // TODO: not available yet -- do later or just skip this - pDexFile->indexMap.classFullCount = - pDexFile->pHeader->typeIdsSize; - pDexFile->indexMap.methodFullCount = - pDexFile->pHeader->methodIdsSize; - pDexFile->indexMap.fieldFullCount = - pDexFile->pHeader->fieldIdsSize; - pDexFile->indexMap.stringFullCount = - pDexFile->pHeader->stringIdsSize; -#endif - } - - LOGI("Class : %u %u %u\n", - pDexFile->indexMap.classFullCount, - pDexFile->indexMap.classReducedCount, - pDexFile->indexMap.classMap[0]); - LOGI("Method: %u %u %u\n", - pDexFile->indexMap.methodFullCount, - pDexFile->indexMap.methodReducedCount, - pDexFile->indexMap.methodMap[0]); - LOGI("Field : %u %u %u\n", - pDexFile->indexMap.fieldFullCount, - pDexFile->indexMap.fieldReducedCount, - pDexFile->indexMap.fieldMap[0]); - LOGI("String: %u %u %u\n", - pDexFile->indexMap.stringFullCount, - pDexFile->indexMap.stringReducedCount, - pDexFile->indexMap.stringMap[0]); - - return true; -} - /* (documented in header file) */ bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile) { @@ -176,7 +52,6 @@ bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile) const void* pOptEnd = data + length; const u4* pOpt = pOptStart; u4 optLength = (const u1*) pOptEnd - (const u1*) pOptStart; - u4 indexMapType = 0; /* * Make sure the opt data start is in range and aligned. This may @@ -233,22 +108,6 @@ bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile) case kDexChunkClassLookup: pDexFile->pClassLookup = (const DexClassLookup*) pOptData; break; - case kDexChunkReducingIndexMap: - LOGI("+++ found reducing index map, size=%u\n", size); - if (!parseIndexMap(pDexFile, pOptData, size, false)) { - LOGE("Failed parsing reducing index map\n"); - return false; - } - indexMapType = *pOpt; - break; - case kDexChunkExpandingIndexMap: - LOGI("+++ found expanding index map, size=%u\n", size); - if (!parseIndexMap(pDexFile, pOptData, size, true)) { - LOGE("Failed parsing expanding index map\n"); - return false; - } - indexMapType = *pOpt; - break; case kDexChunkRegisterMaps: LOGV("+++ found register maps, size=%u\n", size); pDexFile->pRegisterMapPool = pOptData; @@ -265,19 +124,5 @@ bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile) pOpt = pNextOpt; } -#if 0 // TODO: propagate expected map type from the VM through the API - /* - * If we're configured to expect an index map, and we don't find one, - * reject this DEX so we'll regenerate it. Also, if we found an - * "expanding" map but we're not configured to use it, we have to fail - * because the constants aren't usable without translation. - */ - if (indexMapType != expectedIndexMapType) { - LOGW("Incompatible index map configuration: found 0x%04x, need %d\n", - indexMapType, DVM_REDUCE_CONSTANTS); - return false; - } -#endif - return true; } diff --git a/vm/Dalvik.h b/vm/Dalvik.h index 6418bf2c7..5779b08b7 100644 --- a/vm/Dalvik.h +++ b/vm/Dalvik.h @@ -30,7 +30,6 @@ #include "libdex/DexFile.h" #include "libdex/DexProto.h" #include "libdex/ZipArchive.h" -#include "analysis/ReduceConstants.h" #include "DvmDex.h" #include "RawDexFile.h" #include "Sync.h" diff --git a/vm/Dvm.mk b/vm/Dvm.mk index d5f5e27d8..85fc59806 100644 --- a/vm/Dvm.mk +++ b/vm/Dvm.mk @@ -32,9 +32,6 @@ LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter # Optional features. These may impact the size or performance of the VM. # -# 0=full cache, 1/2=reduced, 3=no cache -LOCAL_CFLAGS += -DDVM_RESOLVER_CACHE=0 - ifeq ($(WITH_DEADLOCK_PREDICTION),true) LOCAL_CFLAGS += -DWITH_DEADLOCK_PREDICTION WITH_MONITOR_TRACKING := true @@ -142,7 +139,6 @@ LOCAL_SRC_FILES := \ analysis/DexPrepare.c \ analysis/DexVerify.c \ analysis/Optimize.c \ - analysis/ReduceConstants.c \ analysis/RegisterMap.c \ analysis/VerifySubs.c \ interp/Interp.c.arm \ diff --git a/vm/DvmDex.c b/vm/DvmDex.c index 02dd758a4..94422b328 100644 --- a/vm/DvmDex.c +++ b/vm/DvmDex.c @@ -57,20 +57,6 @@ static DvmDex* allocateAuxStructures(DexFile* pDexFile) methodCount = pHeader->methodIdsSize; fieldCount = pHeader->fieldIdsSize; -#if (DVM_RESOLVER_CACHE == DVM_RC_REDUCING) || \ - (DVM_RESOLVER_CACHE == DVM_RC_EXPANDING) - if (pDexFile->indexMap.stringReducedCount > 0) - stringCount = pDexFile->indexMap.stringReducedCount; - if (pDexFile->indexMap.classReducedCount > 0) - classCount = pDexFile->indexMap.classReducedCount; - if (pDexFile->indexMap.methodReducedCount > 0) - methodCount = pDexFile->indexMap.methodReducedCount; - if (pDexFile->indexMap.fieldReducedCount > 0) - fieldCount = pDexFile->indexMap.fieldReducedCount; -#elif (DVM_RESOLVER_CACHE == DVM_RC_NO_CACHE) - stringCount = classCount = methodCount = fieldCount = 0; -#endif - pDvmDex->pResStrings = (struct StringObject**) calloc(stringCount, sizeof(struct StringObject*)); diff --git a/vm/DvmDex.h b/vm/DvmDex.h index 803be1f00..f36940b9a 100644 --- a/vm/DvmDex.h +++ b/vm/DvmDex.h @@ -100,9 +100,6 @@ bool dvmDexChangeDex1(DvmDex* pDvmDex, u1* addr, u1 newVal); bool dvmDexChangeDex2(DvmDex* pDvmDex, u2* addr, u2 newVal); -#if DVM_RESOLVER_CACHE == DVM_RC_DISABLED -/* 1:1 mapping */ - /* * Return the requested item if it has been resolved, or NULL if it hasn't. */ @@ -160,179 +157,4 @@ INLINE void dvmDexSetResolvedField(DvmDex* pDvmDex, u4 fieldIdx, pDvmDex->pResFields[fieldIdx] = field; } -#elif DVM_RESOLVER_CACHE == DVM_RC_REDUCING -/* reduce request to fit in a less-than-full-size cache table */ - -/* - * Return the requested item if it has been resolved, or NULL if it hasn't. - * - * If we have a mapping table defined for this category, but there's no - * entry for this index, we always return NULL. Otherwise, we return the - * entry. (To regain some performance we may want to assume that the - * table exists when compiled in this mode -- avoids a null check but - * prevents us from switching back and forth without rebuilding the VM.) - * - * We could save an integer compare here by ensuring that map[kNoIndexMapping] - * always evalutes to NULL (e.g. set kNoIndexMapping = 0). - */ -INLINE struct StringObject* dvmDexGetResolvedString(const DvmDex* pDvmDex, - u4 stringIdx) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - - assert(stringIdx < pDvmDex->pHeader->stringIdsSize); - if (pIndexMap->stringReducedCount > 0) { - stringIdx = pIndexMap->stringMap[stringIdx]; - if (stringIdx == kNoIndexMapping) - return NULL; - } - return pDvmDex->pResStrings[stringIdx]; -} -INLINE struct ClassObject* dvmDexGetResolvedClass(const DvmDex* pDvmDex, - u4 classIdx) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - - assert(classIdx < pDvmDex->pHeader->typeIdsSize); - if (pIndexMap->classReducedCount > 0) { - classIdx = pIndexMap->classMap[classIdx]; - if (classIdx == kNoIndexMapping) - return NULL; - } - return pDvmDex->pResClasses[classIdx]; -} -INLINE struct Method* dvmDexGetResolvedMethod(const DvmDex* pDvmDex, - u4 methodIdx) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - - assert(methodIdx < pDvmDex->pHeader->methodIdsSize); - if (pIndexMap->methodReducedCount > 0) { - methodIdx = pIndexMap->methodMap[methodIdx]; - if (methodIdx == kNoIndexMapping) - return NULL; - } - return pDvmDex->pResMethods[methodIdx]; -} -INLINE struct Field* dvmDexGetResolvedField(const DvmDex* pDvmDex, - u4 fieldIdx) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - - assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize); - if (pIndexMap->fieldReducedCount > 0) { - fieldIdx = pIndexMap->fieldMap[fieldIdx]; - if (fieldIdx == kNoIndexMapping) - return NULL; - } - return pDvmDex->pResFields[fieldIdx]; -} - -/* - * Update the resolved item table. Resolution always produces the same - * result, so we're not worried about atomicity here. - */ -INLINE void dvmDexSetResolvedString(DvmDex* pDvmDex, u4 stringIdx, - struct StringObject* str) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - u4 newIdx; - - assert(stringIdx < pDvmDex->pHeader->stringIdsSize); - if (pIndexMap->stringReducedCount > 0) { - newIdx = pIndexMap->stringMap[stringIdx]; - if (newIdx != kNoIndexMapping) - pDvmDex->pResStrings[newIdx] = str; - } -} -INLINE void dvmDexSetResolvedClass(DvmDex* pDvmDex, u4 classIdx, - struct ClassObject* clazz) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - u4 newIdx; - - assert(classIdx < pDvmDex->pHeader->typeIdsSize); - if (pIndexMap->classReducedCount > 0) { - newIdx = pIndexMap->classMap[classIdx]; - if (newIdx != kNoIndexMapping) - pDvmDex->pResClasses[newIdx] = clazz; - } -} -INLINE void dvmDexSetResolvedMethod(DvmDex* pDvmDex, u4 methodIdx, - struct Method* method) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - u4 newIdx; - - assert(methodIdx < pDvmDex->pHeader->methodIdsSize); - if (pIndexMap->methodReducedCount > 0) { - newIdx = pIndexMap->methodMap[methodIdx]; - if (newIdx != kNoIndexMapping) - pDvmDex->pResMethods[newIdx] = method; - } -} -INLINE void dvmDexSetResolvedField(DvmDex* pDvmDex, u4 fieldIdx, - struct Field* field) -{ - const DexIndexMap* pIndexMap = &pDvmDex->pDexFile->indexMap; - u4 newIdx; - - assert(fieldIdx < pDvmDex->pHeader->fieldIdsSize); - if (pIndexMap->fieldReducedCount > 0) { - newIdx = pIndexMap->fieldMap[fieldIdx]; - if (newIdx != kNoIndexMapping) - pDvmDex->pResFields[newIdx] = field; - } -} - -#elif DVM_RESOLVER_CACHE == DVM_RC_EXPANDING - -#error "not implemented" /* TODO */ - -#elif DVM_RESOLVER_CACHE == DVM_RC_NO_CACHE - -/* - * There's no cache, so we always return NULL. - */ -INLINE struct StringObject* dvmDexGetResolvedString(const DvmDex* pDvmDex, - u4 stringIdx) -{ - return NULL; -} -INLINE struct ClassObject* dvmDexGetResolvedClass(const DvmDex* pDvmDex, - u4 classIdx) -{ - return NULL; -} -INLINE struct Method* dvmDexGetResolvedMethod(const DvmDex* pDvmDex, - u4 methodIdx) -{ - return NULL; -} -INLINE struct Field* dvmDexGetResolvedField(const DvmDex* pDvmDex, - u4 fieldIdx) -{ - return NULL; -} - -/* - * Update the resolved item table. There is no table, so do nothing. - */ -INLINE void dvmDexSetResolvedString(DvmDex* pDvmDex, u4 stringIdx, - struct StringObject* str) -{} -INLINE void dvmDexSetResolvedClass(DvmDex* pDvmDex, u4 classIdx, - struct ClassObject* clazz) -{} -INLINE void dvmDexSetResolvedMethod(DvmDex* pDvmDex, u4 methodIdx, - struct Method* method) -{} -INLINE void dvmDexSetResolvedField(DvmDex* pDvmDex, u4 fieldIdx, - struct Field* field) -{} - -#else -#error "huh?" -#endif /*DVM_RESOLVER_CACHE==N*/ - #endif /*_DALVIK_DVMDEX*/ diff --git a/vm/Init.c b/vm/Init.c index 81a385ae0..f5a8b3c96 100644 --- a/vm/Init.c +++ b/vm/Init.c @@ -181,13 +181,6 @@ static void dvmUsage(const char* progName) #ifdef PROFILE_FIELD_ACCESS " profile_field_access" #endif -#if DVM_RESOLVER_CACHE == DVM_RC_REDUCING - " resolver_cache_reducing" -#elif DVM_RESOLVER_CACHE == DVM_RC_EXPANDING - " resolver_cache_expanding" -#elif DVM_RESOLVER_CACHE == DVM_RC_NO_CACHE - " resolver_cache_disabled" -#endif #if defined(WITH_JIT) " jit" #endif diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c index e43042f24..11fd7be74 100644 --- a/vm/analysis/DexPrepare.c +++ b/vm/analysis/DexPrepare.c @@ -52,7 +52,7 @@ static void verifyAndOptimizeClass(DexFile* pDexFile, ClassObject* clazz, static void updateChecksum(u1* addr, int len, DexHeader* pHeader); static int writeDependencies(int fd, u4 modWhen, u4 crc); static bool writeOptData(int fd, const DexClassLookup* pClassLookup,\ - const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder); + const RegisterMapBuilder* pRegMapBuilder); static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum); @@ -475,7 +475,6 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) { DexClassLookup* pClassLookup = NULL; - IndexMapSet* pIndexMapSet = NULL; RegisterMapBuilder* pRegMapBuilder = NULL; u4 headerFlags = 0; @@ -544,15 +543,6 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, success = false; } else { /* - * If configured to do so, scan the instructions, looking - * for ways to reduce the size of the resolved-constant table. - * This is done post-optimization, across the instructions - * in all methods in all classes (even the ones that failed - * to load). - */ - pIndexMapSet = dvmRewriteConstants(pDvmDex); - - /* * If configured to do so, generate register map output * for all verified classes. The register maps were * generated during verification, and will now be serialized. @@ -635,7 +625,7 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, /* * Append any optimized pre-computed data structures. */ - if (!writeOptData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) { + if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) { LOGW("Failed writing opt data\n"); goto bail; } @@ -680,7 +670,6 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, //dvmRegisterMapDumpStats(); bail: - dvmFreeIndexMapSet(pIndexMapSet); dvmFreeRegisterMapBuilder(pRegMapBuilder); free(pClassLookup); return result; @@ -1365,7 +1354,7 @@ static bool writeChunk(int fd, u4 type, const void* data, size_t size) * so it can be used directly when the file is mapped for reading. */ static bool writeOptData(int fd, const DexClassLookup* pClassLookup, - const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder) + const RegisterMapBuilder* pRegMapBuilder) { /* pre-computed class lookup hash table */ if (!writeChunk(fd, (u4) kDexChunkClassLookup, @@ -1374,15 +1363,6 @@ static bool writeOptData(int fd, const DexClassLookup* pClassLookup, return false; } - /* remapped constants (optional) */ - if (pIndexMapSet != NULL) { - if (!writeChunk(fd, pIndexMapSet->chunkType, - pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen)) - { - return false; - } - } - /* register maps (optional) */ if (pRegMapBuilder != NULL) { if (!writeChunk(fd, (u4) kDexChunkRegisterMaps, diff --git a/vm/analysis/ReduceConstants.c b/vm/analysis/ReduceConstants.c deleted file mode 100644 index 69657e3e4..000000000 --- a/vm/analysis/ReduceConstants.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Compress the range of "constant pool" indexes in instructions and - * annotations to lower runtime RAM footprint. - * - * NOTE: this is an incomplete experimental feature. Do not try to use it. - */ -#include "Dalvik.h" -#include "libdex/InstrUtils.h" -#include "libdex/OptInvocation.h" -#include "libdex/DexClass.h" - -/* -Overview - -When a class, method, field, or string constant is referred to from -Dalvik bytecode, the reference takes the form of an integer index value. -This value indexes into an array of type_id_item, method_id_item, -field_id_item, or string_id_item in the DEX file. The first three -themselves contain (directly or indirectly) indexes to strings that the -resolver uses to convert the instruction stream index into a pointer to -the appropriate object or struct. - -For example, an invoke-virtual instruction needs to specify which method -is to be invoked. The method constant indexes into the method_id_item -array, each entry of which has indexes that specify the defining class -(type_id_item), method name (string_id_item), and method prototype -(proto_id_item). The type_id_item just holds an index to a string_id_item, -which holds the file offset to the string with the class name. The VM -finds the class by name, then searches through the class' table of virtual -methods to find one with a matching name and prototype. - -This process is fairly expensive, so after the first time it completes -successfully, the VM records that the method index resolved to a specific -Method struct. On subsequent execution, the VM just pulls the Method ptr -out of the resolved-methods array. A similar approach is used with -the indexes for classes, fields, and string constants. - -The problem with this approach is that we need to have a "resolved" entry -for every possible class, method, field, and string constant in every -DEX file, even if some of those aren't used from code. The DEX string -constant table has entries for method prototypes and class names that are -never used by the code, and "public static final" fields often turn into -immediate constants. The resolution table entries are only 4 bytes each, -but there are roughly 200,000 of them in the bootstrap classes alone. - -DEX optimization removes many index references by replacing virtual method -indexes with vtable offsets and instance field indexes with byte offsets. -In the earlier example, the method would be resolved at "dexopt" time, and -the instruction rewritten as invoke-virtual-quick with the vtable offset. - -(There are comparatively few classes compared to other constant pool -entries, and a much higher percentage (typically 60-70%) are used. The -biggest gains come from the string pool.) - -Using the resolved-entity tables provides a substantial performance -improvement, but results in applications allocating 1MB+ of tables that -are 70% unused. The used and unused entries are freely intermixed, -preventing effective sharing with the zygote process, and resulting in -large numbers of private/dirty pages on the native heap as the tables -populate on first use. - -The trick is to reduce the memory usage without decreasing performance. -Using smaller resolved-entity tables can actually give us a speed boost, -because we'll have a smaller "live" set of pages and make more effective -use of the data cache. - - -The approach we're going to use is to determine the set of indexes that -could potentially be resolved, generate a mapping from the minimal set to -the full set, and append the mapping to the DEX file. This is done at -"dexopt" time, because we need to keep the changes in shared/read-only -pages or we'll lose the benefits of doing the work. - -There are two ways to create and use the new mapping: - - (1) Write the entire full->minimal mapping to the ".odex" file. On every - instruction that uses an index, use the mapping to determine the - "compressed" constant value, and then use that to index into the - resolved-entity tables on the heap. The instruction stream is unchanged, - and the resolver can easily tell if a given index is cacheable. - - (2) Write the inverse miminal->full mapping to the ".odex" file, and - rewrite the constants in the instruction stream. The interpreter is - unchanged, and the resolver code uses the mapping to find the original - data in the DEX. - -Approach #1 is easier and safer to implement, but it requires a table -lookup every time we execute an instruction that includes a constant -pool reference. This causes an unacceptable performance hit, chiefly -because we're hitting semi-random memory pages and hosing the data cache. -This is mitigated somewhat by DEX optimizations that replace the constant -with a vtable index or field byte offset. Approach #1 also requires -a larger map table, increasing the size of the DEX on disk. One nice -property of approach #1 is that most of the DEX file is unmodified, -so use of the mapping is a runtime decision. - -Approach #2 is preferred for performance reasons. - - -The class/method/field/string resolver code has to handle indices from -three sources: interpreted instructions, annotations, and exception -"catch" lists. Sometimes these occur indirectly, e.g. we need to resolve -the declaring class associated with fields and methods when the latter -two are themselves resolved. Parsing and rewriting instructions is fairly -straightforward, but annotations use a complex format with variable-width -index values. - -We can safely rewrite index values in annotations if we guarantee that the -new value is smaller than the original. This implies a two-pass approach: -the first determines the set of indexes actually used, the second does the -rewrite. Doing the rewrite in a single pass would be much harder. - -Instances of the "original" indices will still be found in the file; if -we try to be all-inclusive we will include some stuff that doesn't need -to be there (e.g. we don't generally need to cache the class name string -index result, since once we have the class resolved we don't need to look -it up by name through the resolver again). There is some potential for -performance improvement by caching more than we strictly need, but we can -afford to give up a little performance during class loading if it allows -us to regain some memory. - -For safety and debugging, it's useful to distinguish the "compressed" -constants in some way, e.g. setting the high bit when we rewrite them. -In practice we don't have any free bits: indexes are usually 16-bit -values, and we have more than 32,767 string constants in at least one of -our core DEX files. Also, this does not work with constants embedded in -annotations, because of the variable-width encoding. - -We should be safe if we can establish a clear distinction between sources -of "original" and "compressed" indices. If the values get crossed up we -can end up with elusive bugs. The easiest approach is to declare that -only indices pulled from certain locations (the instruction stream and/or -annotations) are compressed. This prevents us from adding indices in -arbitrary locations to the compressed set, but should allow a reasonably -robust implementation. - - -Further implementation thoughts: - - - We don't have to do annotations in the first pass. At heart the - resolved entity cache is a performance optimization, not necessary for - correctness, and we're not making annotation performance a priority - at this stage. - - The most important "fast path" is instruction processing. Everything - else can do additional work without having a measurable impact. - However... - - We need to keep an eye on uncached resolves to ensure that we haven't - introduced noticeable performance losses. In particular, the use of - runtime annotations with string constants may suffer if we don't include - annotation rewriting in the solution. - - We can have separate resolver functions for "original" and "compressed" - indices. This way we don't have to add a flag argument to the resolver - functions (which would require passing an additional parameter in from - the interpreter). - - The VM spec has some specific things to say about string constant - equality and interning. Index compression should have no effect on - that; we just change how long it takes to find the interned string in - certain circumstances. The impact can be mitigated somewhat by - improving the performance of the interned string table code. - - This can make e.g. method resolution slower. The method_id_item has - an index to a method name string, and we will no longer cache the - result of resolving that string. This impacts resolution of any method - with the same name as a previously-resolved method. - - We may need to tweak the tools, particularly "dexdump", to show the - translated values. - - We can use 16-bit values in the mapping table, since we should have - fewer than 2^16 remapped entries. If we overflow we can skip the remap - for that table or for the entire DEX file. The resolver will need to - check for the existence of the table to determine whether or not entries - must be remapped. The cost of the extra check is acceptable for - approach #2, since it's only at resolve time, but may be undesirable - for approach #1. -*/ -/* -Output Formats - -There are two possible output formats, from which we choose based on how -we plan to take advantage of the remapped constants. At most one of these -will appear in the DEX. - -NOTE: if EIXM appears in the DEX, the VM *must* be configured with -DVM_RESOLVER_CACHE=DVM_RC_EXPANDING (2). Otherwise the constants we -pull from the instruction stream will be wrong and we will fail quickly. - -For approach #1: map from original indices to the reduced set. - - This includes the four "mapToNew" tables. - - Format (RIXM): - u4 classCount // #of entries in classMap[]; == typeIdsSize - u4 reducedClassCount // #of entries in remapped table (for alloc) - u2 classMap[] - u4 methodCount - u4 reducedMethodCount - u2 methodMap[] - u4 fieldCount - u4 reducedFieldCount - u2 fieldMap[] - u4 stringCount - u4 reducedStringCount - u2 stringMap[] - -For approach #2: map from the reduced set back to the originals. - - This includes the four "mapToOld" tables. - - Format (EIXM): - u4 classCount // #of entries in classMap[]; post-reduction - u2 classMap[] - u4 methodCount - u2 methodMap[] - u4 fieldCount - u2 fieldMap[] - u4 stringCount - u2 stringMap[] - -The arrays are padded so that the "count" values are always aligned on -32-bit boundaries. All multi-byte values are in native host order. -*/ - - -/* - * Gather results from the post-optimization instruction scan. - */ -typedef struct ScanResults { - /* output */ - BitVector* usedClasses; - BitVector* usedMethods; - BitVector* usedFields; - BitVector* usedStrings; -} ScanResults; - -/* prototype for the for-all-methods function */ -typedef void (AllMethodsFunc)(DexFile* pDexFile, const char* classDescriptor, - DexMethod* pDexMethod, void* arg); - - -/* - * Free scan results. - */ -static void freeScanResults(ScanResults* pResults) -{ - if (pResults == NULL) - return; - - dvmFreeBitVector(pResults->usedClasses); - dvmFreeBitVector(pResults->usedMethods); - dvmFreeBitVector(pResults->usedFields); - dvmFreeBitVector(pResults->usedStrings); - free(pResults); -} - -/* - * Allocate storage for the results of the instruction scan. - */ -static ScanResults* allocScanResults(const DexFile* pDexFile) -{ - ScanResults* pResults; - const DexHeader* pHeader = pDexFile->pHeader; - - pResults = (ScanResults*) calloc(1, sizeof(ScanResults)); - if (pResults == NULL) - return NULL; - - pResults->usedClasses = dvmAllocBitVector(pHeader->typeIdsSize, false); - pResults->usedMethods = dvmAllocBitVector(pHeader->methodIdsSize, false); - pResults->usedFields = dvmAllocBitVector(pHeader->fieldIdsSize, false); - pResults->usedStrings = dvmAllocBitVector(pHeader->stringIdsSize, false); - - if (pResults->usedClasses == NULL || - pResults->usedMethods == NULL || - pResults->usedFields == NULL || - pResults->usedStrings == NULL) - { - freeScanResults(pResults); - return NULL; - } - - return pResults; -} - -/* - * Call "func(method, arg)" on all methods in the specified class. - * - * Pass in a pointer to the class_data_item, positioned at the start of - * the field data (i.e. just past the class data header). - * - * "classDescriptor" is for debug messages. - */ -static void forAllMethodsInClass(DexFile* pDexFile, const u1** ppEncodedData, - const DexClassDataHeader* pHeader, const char* classDescriptor, - AllMethodsFunc func, void* arg) -{ - int i; - - /* - * Consume field data. - */ - if (pHeader->staticFieldsSize != 0) { - int count = (int) pHeader->staticFieldsSize; - u4 lastIndex = 0; - DexField field; - for (i = 0; i < count; i++) { - dexReadClassDataField(ppEncodedData, &field, &lastIndex); - } - } - if (pHeader->instanceFieldsSize != 0) { - int count = (int) pHeader->instanceFieldsSize; - u4 lastIndex = 0; - DexField field; - for (i = 0; i < count; i++) { - dexReadClassDataField(ppEncodedData, &field, &lastIndex); - } - } - - /* - * Run through all methods. - */ - if (pHeader->directMethodsSize != 0) { - int count = (int) pHeader->directMethodsSize; - u4 lastIndex = 0; - DexMethod method; - - for (i = 0; i < count; i++) { - dexReadClassDataMethod(ppEncodedData, &method, &lastIndex); - (func)(pDexFile, classDescriptor, &method, arg); - } - } - if (pHeader->virtualMethodsSize != 0) { - int count = (int) pHeader->virtualMethodsSize; - u4 lastIndex = 0; - DexMethod method; - - for (i = 0; i < count; i++) { - dexReadClassDataMethod(ppEncodedData, &method, &lastIndex); - (func)(pDexFile, classDescriptor, &method, arg); - } - } -} - -/* - * Call "func(method, arg)" on all methods in all classes in DexFile. - */ -static void forAllMethods(DexFile* pDexFile, AllMethodsFunc func, void* arg) -{ - u4 count = pDexFile->pHeader->classDefsSize; - u4 idx; - - for (idx = 0; idx < count; idx++) { - const DexClassDef* pClassDef; - DexClassDataHeader header; - const u1* pEncodedData; - - pClassDef = dexGetClassDef(pDexFile, idx); - pEncodedData = dexGetClassData(pDexFile, pClassDef); - - const char* classDescriptor; - classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); - - if (pEncodedData != NULL) { - dexReadClassDataHeader(&pEncodedData, &header); - - forAllMethodsInClass(pDexFile, &pEncodedData, &header, - classDescriptor, func, arg); - } else { - //printf("%s: no class data\n", classDescriptor); - /* no class data, e.g. "marker interface" */ - } - } -} - -/* - * Mark a class ID as referenced. - */ -static void markClass(const u2* ptr, ScanResults* pResults) -{ - u2 classIdx = *ptr; - if (!dvmSetBit(pResults->usedClasses, classIdx)) { - LOGE("Unable to mark class %d as in-use\n", classIdx); - } -} - -/* - * Mark a method ID as referenced. - */ -static void markMethod(const u2* ptr, ScanResults* pResults) -{ - u2 methodIdx = *ptr; - if (!dvmSetBit(pResults->usedMethods, methodIdx)) { - LOGE("Unable to mark method %d as in-use\n", methodIdx); - } -} - -/* - * Mark a field ID as referenced. - */ -static void markField(const u2* ptr, ScanResults* pResults) -{ - u2 fieldIdx = *ptr; - if (!dvmSetBit(pResults->usedFields, fieldIdx)) { - LOGE("Unable to mark field %d as in-use\n", fieldIdx); - } -} - -/* - * Mark a string constant as referenced. - */ -static void markString(const u2* ptr, ScanResults* pResults) -{ - u2 stringIdx = *ptr; - if (!dvmSetBit(pResults->usedStrings, stringIdx)) { - LOGE("Unable to mark string %d as in-use\n", stringIdx); - } -} - -/* - * Mark a "jumbo" string constant as referenced. - */ -static void markJumboString(u2* ptr, ScanResults* pResults) -{ - u4 stringIdx; - - /* it's in native byte order, but might not be 32-bit aligned */ - memcpy(&stringIdx, ptr, sizeof(u4)); - if (!dvmSetBit(pResults->usedStrings, stringIdx)) { - LOGE("Unable to mark string %d as in-use\n", stringIdx); - } -} - -/* - * Remap a value in the instruction stream. - */ -static inline void updateValue(u2* ptr, const IndexMapSet* pIndexMapSet, - int whichMap) -{ - const IndexMap* pMap = &pIndexMapSet->map[whichMap]; - if (pMap != NULL) { - u2 newIdx = pMap->mapToNew[*ptr]; - assert(newIdx != kNoIndexMapping); - *ptr = newIdx; - } -} -static void updateClass(u2* ptr, const IndexMapSet* pIndexMapSet) -{ - updateValue(ptr, pIndexMapSet, kMapClasses); -} -static void updateMethod(u2* ptr, const IndexMapSet* pIndexMapSet) -{ - updateValue(ptr, pIndexMapSet, kMapMethods); -} -static void updateField(u2* ptr, const IndexMapSet* pIndexMapSet) -{ - updateValue(ptr, pIndexMapSet, kMapFields); -} -static void updateString(u2* ptr, const IndexMapSet* pIndexMapSet) -{ - updateValue(ptr, pIndexMapSet, kMapStrings); -} -static void updateJumboString(u2* ptr, const IndexMapSet* pIndexMapSet) -{ - u4 stringIdx; - u4 newIdx; - - /* it's in native byte order, but might not be 32-bit aligned */ - memcpy(&stringIdx, ptr, sizeof(stringIdx)); - - /* get new value */ - newIdx = pIndexMapSet->map[kMapStrings].mapToNew[*ptr]; - assert(newIdx != kNoIndexMapping); - - /* copy it out */ - memcpy(ptr, &newIdx, sizeof(newIdx)); -} - -/* - * Run through an instructions stream, marking constants as we see them. - * - * If "pResults" is non-NULL, we populate "pResults" with what we find, - * making no changes to the instruction stream. - * - * If "pIndexMapSet" is non-NULL, we rewrite the constants in the - * instruction stream. - */ -static void markUsedConstantsFromInsns(u2* insns, u4 insnsSize, - ScanResults* pResults, const IndexMapSet* pIndexMapSet) -{ - //printf(" %p %u units\n", insns, insnsSize); - - while (insnsSize > 0) { - int width; - u2* pConst = insns + 1; - - switch (*insns & 0xff) { - case OP_IGET: - case OP_IGET_WIDE: - case OP_IGET_OBJECT: - case OP_IGET_BOOLEAN: - case OP_IGET_BYTE: - case OP_IGET_CHAR: - case OP_IGET_SHORT: - case OP_IPUT: - case OP_IPUT_WIDE: - case OP_IPUT_OBJECT: - case OP_IPUT_BOOLEAN: - case OP_IPUT_BYTE: - case OP_IPUT_CHAR: - case OP_IPUT_SHORT: - case OP_SGET: - case OP_SGET_WIDE: - case OP_SGET_OBJECT: - case OP_SGET_BOOLEAN: - case OP_SGET_BYTE: - case OP_SGET_CHAR: - case OP_SGET_SHORT: - case OP_SPUT: - case OP_SPUT_WIDE: - case OP_SPUT_OBJECT: - case OP_SPUT_BOOLEAN: - case OP_SPUT_BYTE: - case OP_SPUT_CHAR: - case OP_SPUT_SHORT: - /* instanceop vA, vB, field@CCCC */ - /* staticop vAA, field@BBBB */ - if (pResults != NULL) - markField(pConst, pResults); - else - updateField(pConst, pIndexMapSet); - break; - - case OP_CONST_STRING: - /* const-string vAA, string@BBBB */ - if (pResults != NULL) - markString(pConst, pResults); - else - updateString(pConst, pIndexMapSet); - break; - - case OP_CONST_STRING_JUMBO: - /* const-string/jumbo vAA, string@BBBBBBBB */ - if (pResults != NULL) - markJumboString(pConst, pResults); - else - updateJumboString(pConst, pIndexMapSet); - break; - - case OP_CONST_CLASS: - case OP_CHECK_CAST: - case OP_NEW_INSTANCE: - case OP_FILLED_NEW_ARRAY_RANGE: - case OP_INSTANCE_OF: - case OP_NEW_ARRAY: - case OP_FILLED_NEW_ARRAY: - /* const-class vAA, type@BBBB */ - /* check-cast vAA, type@BBBB */ - /* new-instance vAA, type@BBBB */ - /* filled-new-array/range {vCCCC .. vNNNN}, type@BBBB */ - /* instance-of vA, vB, type@CCCC */ - /* new-array vA, vB, type@CCCC */ - /* filled-new-array {vD, vE, vF, vG, vA}, type@CCCC */ - if (pResults != NULL) - markClass(pConst, pResults); - else - updateClass(pConst, pIndexMapSet); - break; - - case OP_INVOKE_VIRTUAL: - case OP_INVOKE_SUPER: - case OP_INVOKE_DIRECT: - case OP_INVOKE_STATIC: - case OP_INVOKE_INTERFACE: - case OP_INVOKE_VIRTUAL_RANGE: - case OP_INVOKE_SUPER_RANGE: - case OP_INVOKE_DIRECT_RANGE: - case OP_INVOKE_STATIC_RANGE: - case OP_INVOKE_INTERFACE_RANGE: - /* invoke-kind {vD, vE, vF, vG, vA}, meth@CCCC */ - /* invoke-kind/range {vCCCC .. vNNNN}, meth@BBBB */ - if (pResults != NULL) - markMethod(pConst, pResults); - else - updateMethod(pConst, pIndexMapSet); - break; - - default: - // ignore this instruction - ; - } - - width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns); - assert(width > 0 && width <= (int)insnsSize); - - insns += width; - insnsSize -= width; - } -} - -/* - * This is an AllMethodsFunc implementation. - * - * Run through the instructions in this method, setting bits in the "pResults" - * struct as we locate constants. - */ -static void markUsedConstants(DexFile* pDexFile, const char* classDescriptor, - DexMethod* pDexMethod, void* arg) -{ - ScanResults* pResults = (ScanResults*) arg; - const DexCode* pDexCode = dexGetCode(pDexFile, pDexMethod); - - if (false) { - const DexMethodId* pMethodId; - const char* methodName; - pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); - methodName = dexStringById(pDexFile, pMethodId->nameIdx); - printf(" %s.%s\n", classDescriptor, methodName); - } - - if (pDexCode != NULL) { - u2* insns = (u2*) pDexCode->insns; - u4 insnsSize = pDexCode->insnsSize; - markUsedConstantsFromInsns(insns, insnsSize, pResults, NULL); - } else { - //printf(" (no code)\n"); - } -} - -/* - * This is an AllMethodsFunc implementation. - * - * Run through the instructions in this method, altering the constants used. - */ -#if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING -static void updateUsedConstants(DexFile* pDexFile, const char* classDescriptor, - DexMethod* pDexMethod, void* arg) -{ - const IndexMapSet* pIndexMapSet = (const IndexMapSet*) arg; - const DexCode* pDexCode = dexGetCode(pDexFile, pDexMethod); - - if (false) { - const DexMethodId* pMethodId; - const char* methodName; - pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); - methodName = dexStringById(pDexFile, pMethodId->nameIdx); - printf(" %s.%s\n", classDescriptor, methodName); - } - - if (pDexCode != NULL) { - u2* insns = (u2*) pDexCode->insns; - u4 insnsSize = pDexCode->insnsSize; - markUsedConstantsFromInsns(insns, insnsSize, NULL, pIndexMapSet); - } else { - //printf(" (no code)\n"); - } -} -#endif - -/* - * Count up the bits and show a count. - */ -static void showBitCount(const char* label, int setCount, int maxCount) -{ - printf("%s: %d of %d (%.1f%% unused)\n", label, setCount, maxCount, - ((maxCount - setCount) * 100.0f) / maxCount); -} - -/* - * Print some debug info. - */ -static void summarizeResults(DvmDex* pDvmDex, ScanResults* pResults) -{ - DexFile* pDexFile = pDvmDex->pDexFile; -#if 0 - int i; - - for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->typeIdsSize; i++) { - const DexTypeId* pDexTypeId; - const char* classDescr; - - pDexTypeId = dexGetTypeId(pDexFile, i); - classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx); - - if (dvmIsBitSet(pResults->usedStrings, i)) - printf("used : %04x '%s'\n", i, classDescr); - else - printf("unused: %04x '%s'\n", i, classDescr); - } -#endif -#if 0 - for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->methodIdsSize; i++) { - const DexMethodId* pDexMethodId; - const DexTypeId* pDexTypeId; - const char* classDescr; - const char* methodName; - - pDexMethodId = dexGetMethodId(pDexFile, i); - methodName = dexStringById(pDexFile, pDexMethodId->nameIdx); - - pDexTypeId = dexGetTypeId(pDexFile, pDexMethodId->classIdx); - classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx); - if (dvmIsBitSet(pResults->usedMethods, i)) - printf("used : %s.%s\n", classDescr, methodName); - else - printf("unused: %s.%s\n", classDescr, methodName); - } -#endif -#if 0 - for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->fieldIdsSize; i++) { - const DexFieldId* pDexFieldId; - const DexTypeId* pDexTypeId; - const char* classDescr; - const char* fieldName; - - pDexFieldId = dexGetFieldId(pDexFile, i); - fieldName = dexStringById(pDexFile, pDexFieldId->nameIdx); - - pDexTypeId = dexGetTypeId(pDexFile, pDexFieldId->classIdx); - classDescr = dexStringById(pDexFile, pDexTypeId->descriptorIdx); - if (dvmIsBitSet(pResults->usedFields, i)) - printf("used : %s.%s\n", classDescr, fieldName); - else - printf("unused: %s.%s\n", classDescr, fieldName); - } -#endif -#if 0 - for (i = 0; i < (int) pDvmDex->pDexFile->pHeader->stringIdsSize; i++) { - const char* str; - - str = dexStringById(pDexFile, i); - - if (dvmIsBitSet(pResults->usedStrings, i)) - printf("used : %04x '%s'\n", i, str); - else - printf("unused: %04x '%s'\n", i, str); - } -#endif - - int totalMax, totalSet; - int setCount; - - totalMax = totalSet = 0; - - setCount = dvmCountSetBits(pResults->usedClasses); - showBitCount("classes", setCount, pDexFile->pHeader->typeIdsSize); - totalSet += setCount; - totalMax += pDexFile->pHeader->typeIdsSize; - - setCount = dvmCountSetBits(pResults->usedMethods); - showBitCount("methods", setCount, pDexFile->pHeader->methodIdsSize); - totalSet += setCount; - totalMax += pDexFile->pHeader->methodIdsSize; - - setCount = dvmCountSetBits(pResults->usedFields); - showBitCount("fields", setCount, pDexFile->pHeader->fieldIdsSize); - totalSet += setCount; - totalMax += pDexFile->pHeader->fieldIdsSize; - - setCount = dvmCountSetBits(pResults->usedStrings); - showBitCount("strings", setCount, pDexFile->pHeader->stringIdsSize); - totalSet += setCount; - totalMax += pDexFile->pHeader->stringIdsSize; - - printf("TOTAL %d of %d (%.1f%% unused -- %.1fK)\n", totalSet, totalMax, - ((totalMax - totalSet) * 100.0f) / totalMax, - (totalMax - totalSet) / 256.0f); -} - -/* - * Fill out an index map set entry. - * - * If we can't fit the map into our base type, we don't create the map. - * - * Returns "false" if allocation fails. - */ -static bool constructIndexMap(int totalCount, const BitVector* pBits, - IndexMap* pMap) -{ - const int kMaxIndex = 65534; // 65535, a/k/a -1, is special - int setCount; - - setCount = dvmCountSetBits(pBits); - if (setCount < 0 || setCount > kMaxIndex) - return true; - - u2* mapToOld = (u2*) malloc(setCount * sizeof(u2)); - u2* mapToNew = (u2*) malloc(totalCount * sizeof(u2)); - if (mapToOld == NULL || mapToNew == NULL) { - free(mapToOld); - free(mapToNew); - return false; - } - - /* fill in both arrays */ - int entry, idx = 0; - for (entry = 0; entry < totalCount; entry++) { - if (dvmIsBitSet(pBits, entry)) { - mapToNew[entry] = idx; - mapToOld[idx] = entry; - idx++; - } else { - mapToNew[entry] = kNoIndexMapping; - } - } - - if (idx != setCount) { - LOGE("GLITCH: idx=%d setCount=%d\n", idx, setCount); - dvmAbort(); - } - - /* success */ - pMap->mapToOld = mapToOld; - pMap->mapToNew = mapToNew; - pMap->origCount = totalCount; - pMap->newCount = setCount; - - return true; -} - -/* - * Construct a "reducing" chunk, with maps that convert the constants in - * instructions to their reduced value for the cache lookup. - */ -static bool constructReducingDataChunk(IndexMapSet* pIndexMapSet) -{ - int chunkLen = 0; - int i; - - pIndexMapSet->chunkType = kDexChunkReducingIndexMap; - - /* - * Compute space requirements and allocate storage. - */ - for (i = 0; i < kNumIndexMaps; i++) { - /* space for the "original" count */ - chunkLen += sizeof(u4); - - /* space for the "reduced" count */ - chunkLen += sizeof(u4); - - /* add data length, round up to 32-bit boundary */ - chunkLen += pIndexMapSet->map[i].origCount * sizeof(u2); - chunkLen = (chunkLen + 3) & ~3; - } - - pIndexMapSet->chunkDataLen = chunkLen; - pIndexMapSet->chunkData = (u1*) calloc(1, chunkLen); - if (pIndexMapSet->chunkData == NULL) - return false; - - /* - * Copy the data in. - */ - u1* ptr = pIndexMapSet->chunkData; - for (i = 0; i < kNumIndexMaps; i++) { - u4* wordPtr = (u4*) ptr; - int dataLen = pIndexMapSet->map[i].origCount * sizeof(u2); - - *wordPtr++ = pIndexMapSet->map[i].origCount; - *wordPtr++ = pIndexMapSet->map[i].newCount; - if (dataLen != 0) - memcpy(wordPtr, pIndexMapSet->map[i].mapToNew, dataLen); - - /* advance pointer, maintaining 32-bit alignment */ - ptr = ((u1*) wordPtr) + dataLen; - ptr = (u1*) (((int) ptr + 3) & ~3); - } - - if (ptr - (u1*) pIndexMapSet->chunkData != chunkLen) { - LOGE("GLITCH: expected len=%d, actual=%d\n", - chunkLen, ptr - (u1*) pIndexMapSet->chunkData); - dvmAbort(); - } - - return true; -} - -/* - * Construct an "expanding" chunk, with maps that convert instructions - * with reduced constants back to their full original values. - */ -#if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING -static bool constructExpandingDataChunk(IndexMapSet* pIndexMapSet) -{ - int chunkLen = 0; - int i; - - pIndexMapSet->chunkType = kDexChunkExpandingIndexMap; - - /* - * Compute space requirements and allocate storage. - */ - for (i = 0; i < kNumIndexMaps; i++) { - /* space for the length word */ - chunkLen += sizeof(u4); - - /* add data length, round up to 32-bit boundary */ - chunkLen += pIndexMapSet->map[i].newCount * sizeof(u2); - chunkLen = (chunkLen + 3) & ~3; - } - - pIndexMapSet->chunkDataLen = chunkLen; - pIndexMapSet->chunkData = (u1*) calloc(1, chunkLen); - if (pIndexMapSet->chunkData == NULL) - return false; - - /* - * Copy the data in. - */ - u1* ptr = pIndexMapSet->chunkData; - for (i = 0; i < kNumIndexMaps; i++) { - u4* wordPtr = (u4*) ptr; - int dataLen = pIndexMapSet->map[i].newCount * sizeof(u2); - - *wordPtr++ = pIndexMapSet->map[i].newCount; - if (dataLen != 0) - memcpy(wordPtr, pIndexMapSet->map[i].mapToOld, dataLen); - - /* advance pointer, maintaining 32-bit alignment */ - ptr = ((u1*) wordPtr) + dataLen; - ptr = (u1*) (((int) ptr + 3) & ~3); - } - - if (ptr - (u1*) pIndexMapSet->chunkData != chunkLen) { - LOGE("GLITCH: expected len=%d, actual=%d\n", - chunkLen, ptr - (u1*) pIndexMapSet->chunkData); - dvmAbort(); - } - - return true; -} -#endif - -/* - * Construct the "chunk" of data that will be appended to the optimized DEX - * file. - */ -static bool constructDataChunk(IndexMapSet* pIndexMapSet) -{ - assert(sizeof(pIndexMapSet->map[0].mapToOld[0]) == sizeof(u2)); - assert(sizeof(pIndexMapSet->map[0].mapToNew[0]) == sizeof(u2)); - -#if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING - return constructExpandingDataChunk(pIndexMapSet); -#else - return constructReducingDataChunk(pIndexMapSet); -#endif -} - -/* - * Allocate storage to hold the maps. - */ -static IndexMapSet* createIndexMapSet(const DexFile* pDexFile, - ScanResults* pResults) -{ - IndexMapSet* pIndexMapSet; - bool okay = true; - - pIndexMapSet = calloc(1, sizeof(*pIndexMapSet)); - if (pIndexMapSet == NULL) - return NULL; - - okay = okay && constructIndexMap(pDexFile->pHeader->typeIdsSize, - pResults->usedClasses, &pIndexMapSet->map[kMapClasses]); - okay = okay && constructIndexMap(pDexFile->pHeader->methodIdsSize, - pResults->usedMethods, &pIndexMapSet->map[kMapMethods]); - okay = okay && constructIndexMap(pDexFile->pHeader->fieldIdsSize, - pResults->usedFields, &pIndexMapSet->map[kMapFields]); - okay = okay && constructIndexMap(pDexFile->pHeader->stringIdsSize, - pResults->usedStrings, &pIndexMapSet->map[kMapStrings]); - - LOGVV("Constr: %d %d %d %d\n", - pIndexMapSet->map[kMapClasses].mapToOld[0], - pIndexMapSet->map[kMapMethods].mapToOld[0], - pIndexMapSet->map[kMapFields].mapToOld[0], - pIndexMapSet->map[kMapStrings].mapToOld[0]); - - okay = okay && constructDataChunk(pIndexMapSet); - - if (!okay) { - dvmFreeIndexMapSet(pIndexMapSet); - return NULL; - } - - return pIndexMapSet; -} - -/* - * Free map storage. - * - * "pIndexMapSet" may be incomplete. - */ -void dvmFreeIndexMapSet(IndexMapSet* pIndexMapSet) -{ - int i; - - if (pIndexMapSet == NULL) - return; - - for (i = 0; i < kNumIndexMaps; i++) { - free(pIndexMapSet->map[i].mapToOld); - free(pIndexMapSet->map[i].mapToNew); - } - free(pIndexMapSet->chunkData); - free(pIndexMapSet); -} - -/* - * Rewrite constant indexes to reduce heap requirements. - */ -IndexMapSet* dvmRewriteConstants(DvmDex* pDvmDex) -{ -#if (DVM_RESOLVER_CACHE != DVM_RC_REDUCING) && \ - (DVM_RESOLVER_CACHE != DVM_RC_EXPANDING) - /* nothing to do */ - return NULL; -#endif - - /* - * We're looking for instructions that use "constant pool" entries for - * classes, methods, fields, and strings. Many field and method entries - * are optimized away, and many string constants are never accessed from - * code or annotations. - */ - ScanResults* pResults = allocScanResults(pDvmDex->pDexFile); - forAllMethods(pDvmDex->pDexFile, markUsedConstants, pResults); - - summarizeResults(pDvmDex, pResults); - - /* - * Allocate and populate the index maps. - */ - IndexMapSet* pIndexMapSet = createIndexMapSet(pDvmDex->pDexFile, pResults); -#if DVM_RESOLVER_CACHE == DVM_RC_EXPANDING - if (pIndexMapSet != NULL) { - /* - * Rewrite the constants to use the reduced set. - */ - forAllMethods(pDvmDex->pDexFile, updateUsedConstants, pIndexMapSet); - } -#endif - - freeScanResults(pResults); - - return pIndexMapSet; -} diff --git a/vm/analysis/ReduceConstants.h b/vm/analysis/ReduceConstants.h deleted file mode 100644 index 342e12531..000000000 --- a/vm/analysis/ReduceConstants.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * DEX constant-reduction declarations. - */ -#ifndef _DALVIK_REDUCECONSTANTS -#define _DALVIK_REDUCECONSTANTS - -#define DVM_RC_DISABLED 0 /* no reduction, 1:1 map */ -#define DVM_RC_REDUCING 1 /* normal constants, reduced lookup table */ -#define DVM_RC_EXPANDING 2 /* reduced constants, expanded on resolve */ -#define DVM_RC_NO_CACHE 3 /* disable the cache (reduce to zero) */ - -enum { - kMapClasses = 0, - kMapMethods = 1, - kMapFields = 2, - kMapStrings = 3, - - kNumIndexMaps -}; - -struct DvmDex; - -#define kNoIndexMapping ((u2) -1) - -/* - * Map indices back to the original. - */ -typedef struct IndexMap { - int origCount; /* original size; describes range of entries in map */ - int newCount; /* reduced size */ - u2* mapToNew; /* sparse map, from "orig" to "new" */ - u2* mapToOld; /* dense map, from "new" back to "orig" */ -} IndexMap; -typedef struct IndexMapSet { - /* maps for the different sections */ - IndexMap map[kNumIndexMaps]; - - /* data stream that gets appended to the optimized DEX file */ - u4 chunkType; - int chunkDataLen; - u1* chunkData; -} IndexMapSet; - -/* - * Constant pool compaction. - * - * The caller is responsible for freeing the returned structure by - * calling dvmFreeIndexMap(). - */ -IndexMapSet* dvmRewriteConstants(struct DvmDex* pDvmDex); - -/* free an index map set */ -void dvmFreeIndexMapSet(IndexMapSet* indexMapSet); - -#endif /*_DALVIK_REDUCECONSTANTS*/