| /* |
| * Copyright (C) 2009 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. |
| */ |
| |
| #include "Dalvik.h" |
| #include "CompilerInternals.h" |
| |
| static ArenaMemBlock *arenaHead, *currentArena; |
| static int numArenaBlocks; |
| |
| /* Allocate the initial memory block for arena-based allocation */ |
| bool dvmCompilerHeapInit(void) |
| { |
| assert(arenaHead == NULL); |
| arenaHead = |
| (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE); |
| if (arenaHead == NULL) { |
| ALOGE("No memory left to create compiler heap memory"); |
| return false; |
| } |
| arenaHead->blockSize = ARENA_DEFAULT_SIZE; |
| currentArena = arenaHead; |
| currentArena->bytesAllocated = 0; |
| currentArena->next = NULL; |
| numArenaBlocks = 1; |
| |
| return true; |
| } |
| |
| /* Arena-based malloc for compilation tasks */ |
| void * dvmCompilerNew(size_t size, bool zero) |
| { |
| size = (size + 3) & ~3; |
| retry: |
| /* Normal case - space is available in the current page */ |
| if (size + currentArena->bytesAllocated <= currentArena->blockSize) { |
| void *ptr; |
| ptr = ¤tArena->ptr[currentArena->bytesAllocated]; |
| currentArena->bytesAllocated += size; |
| if (zero) { |
| memset(ptr, 0, size); |
| } |
| return ptr; |
| } else { |
| /* |
| * See if there are previously allocated arena blocks before the last |
| * reset |
| */ |
| if (currentArena->next) { |
| currentArena = currentArena->next; |
| goto retry; |
| } |
| |
| size_t blockSize = (size < ARENA_DEFAULT_SIZE) ? |
| ARENA_DEFAULT_SIZE : size; |
| /* Time to allocate a new arena */ |
| ArenaMemBlock *newArena = (ArenaMemBlock *) |
| malloc(sizeof(ArenaMemBlock) + blockSize); |
| if (newArena == NULL) { |
| ALOGE("Arena allocation failure"); |
| dvmAbort(); |
| } |
| newArena->blockSize = blockSize; |
| newArena->bytesAllocated = 0; |
| newArena->next = NULL; |
| currentArena->next = newArena; |
| currentArena = newArena; |
| numArenaBlocks++; |
| if (numArenaBlocks > 10) |
| ALOGI("Total arena pages for JIT: %d", numArenaBlocks); |
| goto retry; |
| } |
| /* Should not reach here */ |
| dvmAbort(); |
| } |
| |
| /* Reclaim all the arena blocks allocated so far */ |
| void dvmCompilerArenaReset(void) |
| { |
| ArenaMemBlock *block; |
| |
| for (block = arenaHead; block; block = block->next) { |
| block->bytesAllocated = 0; |
| } |
| currentArena = arenaHead; |
| } |
| |
| /* Growable List initialization */ |
| void dvmInitGrowableList(GrowableList *gList, size_t initLength) |
| { |
| gList->numAllocated = initLength; |
| gList->numUsed = 0; |
| gList->elemList = (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * initLength, |
| true); |
| } |
| |
| /* Expand the capacity of a growable list */ |
| static void expandGrowableList(GrowableList *gList) |
| { |
| int newLength = gList->numAllocated; |
| if (newLength < 128) { |
| newLength <<= 1; |
| } else { |
| newLength += 128; |
| } |
| intptr_t *newArray = |
| (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * newLength, true); |
| memcpy(newArray, gList->elemList, sizeof(intptr_t) * gList->numAllocated); |
| gList->numAllocated = newLength; |
| gList->elemList = newArray; |
| } |
| |
| /* Insert a new element into the growable list */ |
| void dvmInsertGrowableList(GrowableList *gList, intptr_t elem) |
| { |
| assert(gList->numAllocated != 0); |
| if (gList->numUsed == gList->numAllocated) { |
| expandGrowableList(gList); |
| } |
| gList->elemList[gList->numUsed++] = elem; |
| } |
| |
| void dvmGrowableListIteratorInit(GrowableList *gList, |
| GrowableListIterator *iterator) |
| { |
| iterator->list = gList; |
| iterator->idx = 0; |
| iterator->size = gList->numUsed; |
| } |
| |
| intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator) |
| { |
| assert(iterator->size == iterator->list->numUsed); |
| if (iterator->idx == iterator->size) return 0; |
| return iterator->list->elemList[iterator->idx++]; |
| } |
| |
| intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx) |
| { |
| assert(idx < gList->numUsed); |
| return gList->elemList[idx]; |
| } |
| |
| /* Debug Utility - dump a compilation unit */ |
| void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit) |
| { |
| BasicBlock *bb; |
| const char *blockTypeNames[] = { |
| "Normal Chaining Cell", |
| "Hot Chaining Cell", |
| "Singleton Chaining Cell", |
| "Predicted Chaining Cell", |
| "Backward Branch", |
| "Chaining Cell Gap", |
| "N/A", |
| "Entry Block", |
| "Code Block", |
| "Exit Block", |
| "PC Reconstruction", |
| "Exception Handling", |
| }; |
| |
| ALOGD("Compiling %s %s", cUnit->method->clazz->descriptor, |
| cUnit->method->name); |
| ALOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method)); |
| ALOGD("%d blocks in total", cUnit->numBlocks); |
| GrowableListIterator iterator; |
| |
| dvmGrowableListIteratorInit(&cUnit->blockList, &iterator); |
| |
| while (true) { |
| bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator); |
| if (bb == NULL) break; |
| ALOGD("Block %d (%s) (insn %04x - %04x%s)", |
| bb->id, |
| blockTypeNames[bb->blockType], |
| bb->startOffset, |
| bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset, |
| bb->lastMIRInsn ? "" : " empty"); |
| if (bb->taken) { |
| ALOGD(" Taken branch: block %d (%04x)", |
| bb->taken->id, bb->taken->startOffset); |
| } |
| if (bb->fallThrough) { |
| ALOGD(" Fallthrough : block %d (%04x)", |
| bb->fallThrough->id, bb->fallThrough->startOffset); |
| } |
| } |
| } |
| |
| /* |
| * dvmHashForeach callback. |
| */ |
| static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats) |
| { |
| CompilerMethodStats *methodStats = |
| (CompilerMethodStats *) compilerMethodStats; |
| CompilerMethodStats *totalStats = |
| (CompilerMethodStats *) totalMethodStats; |
| |
| totalStats->dalvikSize += methodStats->dalvikSize; |
| totalStats->compiledDalvikSize += methodStats->compiledDalvikSize; |
| totalStats->nativeSize += methodStats->nativeSize; |
| |
| /* Enable the following when fine-tuning the JIT performance */ |
| #if 0 |
| int limit = (methodStats->dalvikSize >> 2) * 3; |
| |
| /* If over 3/4 of the Dalvik code is compiled, print something */ |
| if (methodStats->compiledDalvikSize >= limit) { |
| ALOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)", |
| methodStats->method->clazz->descriptor, |
| methodStats->method->name, |
| methodStats->compiledDalvikSize, |
| methodStats->dalvikSize, |
| methodStats->nativeSize); |
| } |
| #endif |
| return 0; |
| } |
| |
| /* |
| * Dump the current stats of the compiler, including number of bytes used in |
| * the code cache, arena size, and work queue length, and various JIT stats. |
| */ |
| void dvmCompilerDumpStats(void) |
| { |
| CompilerMethodStats totalMethodStats; |
| |
| memset(&totalMethodStats, 0, sizeof(CompilerMethodStats)); |
| ALOGD("%d compilations using %d + %d bytes", |
| gDvmJit.numCompilations, |
| gDvmJit.templateSize, |
| gDvmJit.codeCacheByteUsed - gDvmJit.templateSize); |
| ALOGD("Compiler arena uses %d blocks (%d bytes each)", |
| numArenaBlocks, ARENA_DEFAULT_SIZE); |
| ALOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength, |
| gDvmJit.compilerMaxQueued); |
| dvmJitStats(); |
| dvmCompilerArchDump(); |
| if (gDvmJit.methodStatsTable) { |
| dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats, |
| &totalMethodStats); |
| ALOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)", |
| totalMethodStats.compiledDalvikSize, |
| totalMethodStats.dalvikSize, |
| totalMethodStats.nativeSize); |
| } |
| } |
| |
| /* |
| * Allocate a bit vector with enough space to hold at least the specified |
| * number of bits. |
| * |
| * NOTE: this is the sister implementation of dvmAllocBitVector. In this version |
| * memory is allocated from the compiler arena. |
| */ |
| BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable) |
| { |
| BitVector* bv; |
| unsigned int count; |
| |
| assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */ |
| |
| bv = (BitVector*) dvmCompilerNew(sizeof(BitVector), false); |
| |
| count = (startBits + 31) >> 5; |
| |
| bv->storageSize = count; |
| bv->expandable = expandable; |
| bv->storage = (u4*) dvmCompilerNew(count * sizeof(u4), true); |
| return bv; |
| } |
| |
| /* |
| * Mark the specified bit as "set". |
| * |
| * Returns "false" if the bit is outside the range of the vector and we're |
| * not allowed to expand. |
| * |
| * NOTE: this is the sister implementation of dvmSetBit. In this version |
| * memory is allocated from the compiler arena. |
| */ |
| bool dvmCompilerSetBit(BitVector *pBits, unsigned int num) |
| { |
| if (num >= pBits->storageSize * sizeof(u4) * 8) { |
| if (!pBits->expandable) |
| dvmAbort(); |
| |
| /* Round up to word boundaries for "num+1" bits */ |
| unsigned int newSize = (num + 1 + 31) >> 5; |
| assert(newSize > pBits->storageSize); |
| u4 *newStorage = (u4*)dvmCompilerNew(newSize * sizeof(u4), false); |
| memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4)); |
| memset(&newStorage[pBits->storageSize], 0, |
| (newSize - pBits->storageSize) * sizeof(u4)); |
| pBits->storage = newStorage; |
| pBits->storageSize = newSize; |
| } |
| |
| pBits->storage[num >> 5] |= 1 << (num & 0x1f); |
| return true; |
| } |
| |
| /* |
| * Mark the specified bit as "unset". |
| * |
| * Returns "false" if the bit is outside the range of the vector and we're |
| * not allowed to expand. |
| * |
| * NOTE: this is the sister implementation of dvmClearBit. In this version |
| * memory is allocated from the compiler arena. |
| */ |
| bool dvmCompilerClearBit(BitVector *pBits, unsigned int num) |
| { |
| if (num >= pBits->storageSize * sizeof(u4) * 8) { |
| ALOGE("Trying to clear a bit that is not set in the vector yet!"); |
| dvmAbort(); |
| } |
| |
| pBits->storage[num >> 5] &= ~(1 << (num & 0x1f)); |
| return true; |
| } |
| |
| /* |
| * If set is true, mark all bits as 1. Otherwise mark all bits as 0. |
| */ |
| void dvmCompilerMarkAllBits(BitVector *pBits, bool set) |
| { |
| int value = set ? -1 : 0; |
| memset(pBits->storage, value, pBits->storageSize * (int)sizeof(u4)); |
| } |
| |
| void dvmDebugBitVector(char *msg, const BitVector *bv, int length) |
| { |
| int i; |
| |
| ALOGE("%s", msg); |
| for (i = 0; i < length; i++) { |
| if (dvmIsBitSet(bv, i)) { |
| ALOGE(" Bit %d is set", i); |
| } |
| } |
| } |
| |
| void dvmCompilerAbort(CompilationUnit *cUnit) |
| { |
| ALOGE("Jit: aborting trace compilation, reverting to interpreter"); |
| /* Force a traceback in debug builds */ |
| assert(0); |
| /* |
| * Abort translation and force to interpret-only for this trace |
| * Matching setjmp in compiler thread work loop in Compiler.c. |
| */ |
| longjmp(*cUnit->bailPtr, 1); |
| } |
| |
| void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg, |
| const BitVector *bv, int length) |
| { |
| int i; |
| |
| ALOGE("%s", msg); |
| for (i = 0; i < length; i++) { |
| if (dvmIsBitSet(bv, i)) { |
| BasicBlock *bb = |
| (BasicBlock *) dvmGrowableListGetElement(blocks, i); |
| char blockName[BLOCK_NAME_LEN]; |
| dvmGetBlockName(bb, blockName); |
| ALOGE("Bit %d / %s is set", i, blockName); |
| } |
| } |
| } |
| |
| void dvmGetBlockName(BasicBlock *bb, char *name) |
| { |
| switch (bb->blockType) { |
| case kEntryBlock: |
| snprintf(name, BLOCK_NAME_LEN, "entry"); |
| break; |
| case kExitBlock: |
| snprintf(name, BLOCK_NAME_LEN, "exit"); |
| break; |
| case kDalvikByteCode: |
| snprintf(name, BLOCK_NAME_LEN, "block%04x", bb->startOffset); |
| break; |
| case kChainingCellNormal: |
| snprintf(name, BLOCK_NAME_LEN, "chain%04x", bb->startOffset); |
| break; |
| case kExceptionHandling: |
| snprintf(name, BLOCK_NAME_LEN, "exception%04x", bb->startOffset); |
| break; |
| default: |
| snprintf(name, BLOCK_NAME_LEN, "??"); |
| break; |
| } |
| } |
| |
| void dvmCompilerCacheFlush(uintptr_t start, uintptr_t end) { |
| __builtin___clear_cache(reinterpret_cast<void*>(start), reinterpret_cast<void*>(end)); |
| } |