/*
* 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;
}
}