Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Copyright (C) 2009 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 #include "Dalvik.h"
     18 #include "CompilerInternals.h"
     19 
     20 static ArenaMemBlock *arenaHead, *currentArena;
     21 static int numArenaBlocks;
     22 
     23 /* Allocate the initial memory block for arena-based allocation */
     24 bool dvmCompilerHeapInit(void)
     25 {
     26     assert(arenaHead == NULL);
     27     arenaHead =
     28         (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE);
     29     if (arenaHead == NULL) {
     30         ALOGE("No memory left to create compiler heap memory");
     31         return false;
     32     }
     33     arenaHead->blockSize = ARENA_DEFAULT_SIZE;
     34     currentArena = arenaHead;
     35     currentArena->bytesAllocated = 0;
     36     currentArena->next = NULL;
     37     numArenaBlocks = 1;
     38 
     39     return true;
     40 }
     41 
     42 /* Arena-based malloc for compilation tasks */
     43 void * dvmCompilerNew(size_t size, bool zero)
     44 {
     45     size = (size + 3) & ~3;
     46 retry:
     47     /* Normal case - space is available in the current page */
     48     if (size + currentArena->bytesAllocated <= currentArena->blockSize) {
     49         void *ptr;
     50         ptr = &currentArena->ptr[currentArena->bytesAllocated];
     51         currentArena->bytesAllocated += size;
     52         if (zero) {
     53             memset(ptr, 0, size);
     54         }
     55         return ptr;
     56     } else {
     57         /*
     58          * See if there are previously allocated arena blocks before the last
     59          * reset
     60          */
     61         if (currentArena->next) {
     62             currentArena = currentArena->next;
     63             goto retry;
     64         }
     65 
     66         size_t blockSize = (size < ARENA_DEFAULT_SIZE) ?
     67                           ARENA_DEFAULT_SIZE : size;
     68         /* Time to allocate a new arena */
     69         ArenaMemBlock *newArena = (ArenaMemBlock *)
     70             malloc(sizeof(ArenaMemBlock) + blockSize);
     71         if (newArena == NULL) {
     72             ALOGE("Arena allocation failure");
     73             dvmAbort();
     74         }
     75         newArena->blockSize = blockSize;
     76         newArena->bytesAllocated = 0;
     77         newArena->next = NULL;
     78         currentArena->next = newArena;
     79         currentArena = newArena;
     80         numArenaBlocks++;
     81         if (numArenaBlocks > 10)
     82             ALOGI("Total arena pages for JIT: %d", numArenaBlocks);
     83         goto retry;
     84     }
     85     /* Should not reach here */
     86     dvmAbort();
     87 }
     88 
     89 /* Reclaim all the arena blocks allocated so far */
     90 void dvmCompilerArenaReset(void)
     91 {
     92     ArenaMemBlock *block;
     93 
     94     for (block = arenaHead; block; block = block->next) {
     95         block->bytesAllocated = 0;
     96     }
     97     currentArena = arenaHead;
     98 }
     99 
    100 /* Growable List initialization */
    101 void dvmInitGrowableList(GrowableList *gList, size_t initLength)
    102 {
    103     gList->numAllocated = initLength;
    104     gList->numUsed = 0;
    105     gList->elemList = (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * initLength,
    106                                                   true);
    107 }
    108 
    109 /* Expand the capacity of a growable list */
    110 static void expandGrowableList(GrowableList *gList)
    111 {
    112     int newLength = gList->numAllocated;
    113     if (newLength < 128) {
    114         newLength <<= 1;
    115     } else {
    116         newLength += 128;
    117     }
    118     intptr_t *newArray =
    119         (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * newLength, true);
    120     memcpy(newArray, gList->elemList, sizeof(intptr_t) * gList->numAllocated);
    121     gList->numAllocated = newLength;
    122     gList->elemList = newArray;
    123 }
    124 
    125 /* Insert a new element into the growable list */
    126 void dvmInsertGrowableList(GrowableList *gList, intptr_t elem)
    127 {
    128     assert(gList->numAllocated != 0);
    129     if (gList->numUsed == gList->numAllocated) {
    130         expandGrowableList(gList);
    131     }
    132     gList->elemList[gList->numUsed++] = elem;
    133 }
    134 
    135 void dvmGrowableListIteratorInit(GrowableList *gList,
    136                                  GrowableListIterator *iterator)
    137 {
    138     iterator->list = gList;
    139     iterator->idx = 0;
    140     iterator->size = gList->numUsed;
    141 }
    142 
    143 intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator)
    144 {
    145     assert(iterator->size == iterator->list->numUsed);
    146     if (iterator->idx == iterator->size) return 0;
    147     return iterator->list->elemList[iterator->idx++];
    148 }
    149 
    150 intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx)
    151 {
    152     assert(idx < gList->numUsed);
    153     return gList->elemList[idx];
    154 }
    155 
    156 /* Debug Utility - dump a compilation unit */
    157 void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit)
    158 {
    159     BasicBlock *bb;
    160     const char *blockTypeNames[] = {
    161         "Normal Chaining Cell",
    162         "Hot Chaining Cell",
    163         "Singleton Chaining Cell",
    164         "Predicted Chaining Cell",
    165         "Backward Branch",
    166         "Chaining Cell Gap",
    167         "N/A",
    168         "Entry Block",
    169         "Code Block",
    170         "Exit Block",
    171         "PC Reconstruction",
    172         "Exception Handling",
    173     };
    174 
    175     ALOGD("Compiling %s %s", cUnit->method->clazz->descriptor,
    176          cUnit->method->name);
    177     ALOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method));
    178     ALOGD("%d blocks in total", cUnit->numBlocks);
    179     GrowableListIterator iterator;
    180 
    181     dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
    182 
    183     while (true) {
    184         bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
    185         if (bb == NULL) break;
    186         ALOGD("Block %d (%s) (insn %04x - %04x%s)",
    187              bb->id,
    188              blockTypeNames[bb->blockType],
    189              bb->startOffset,
    190              bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset,
    191              bb->lastMIRInsn ? "" : " empty");
    192         if (bb->taken) {
    193             ALOGD("  Taken branch: block %d (%04x)",
    194                  bb->taken->id, bb->taken->startOffset);
    195         }
    196         if (bb->fallThrough) {
    197             ALOGD("  Fallthrough : block %d (%04x)",
    198                  bb->fallThrough->id, bb->fallThrough->startOffset);
    199         }
    200     }
    201 }
    202 
    203 /*
    204  * dvmHashForeach callback.
    205  */
    206 static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats)
    207 {
    208     CompilerMethodStats *methodStats =
    209         (CompilerMethodStats *) compilerMethodStats;
    210     CompilerMethodStats *totalStats =
    211         (CompilerMethodStats *) totalMethodStats;
    212 
    213     totalStats->dalvikSize += methodStats->dalvikSize;
    214     totalStats->compiledDalvikSize += methodStats->compiledDalvikSize;
    215     totalStats->nativeSize += methodStats->nativeSize;
    216 
    217     /* Enable the following when fine-tuning the JIT performance */
    218 #if 0
    219     int limit = (methodStats->dalvikSize >> 2) * 3;
    220 
    221     /* If over 3/4 of the Dalvik code is compiled, print something */
    222     if (methodStats->compiledDalvikSize >= limit) {
    223         ALOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)",
    224              methodStats->method->clazz->descriptor,
    225              methodStats->method->name,
    226              methodStats->compiledDalvikSize,
    227              methodStats->dalvikSize,
    228              methodStats->nativeSize);
    229     }
    230 #endif
    231     return 0;
    232 }
    233 
    234 /*
    235  * Dump the current stats of the compiler, including number of bytes used in
    236  * the code cache, arena size, and work queue length, and various JIT stats.
    237  */
    238 void dvmCompilerDumpStats(void)
    239 {
    240     CompilerMethodStats totalMethodStats;
    241 
    242     memset(&totalMethodStats, 0, sizeof(CompilerMethodStats));
    243     ALOGD("%d compilations using %d + %d bytes",
    244          gDvmJit.numCompilations,
    245          gDvmJit.templateSize,
    246          gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
    247     ALOGD("Compiler arena uses %d blocks (%d bytes each)",
    248          numArenaBlocks, ARENA_DEFAULT_SIZE);
    249     ALOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength,
    250          gDvmJit.compilerMaxQueued);
    251     dvmJitStats();
    252     dvmCompilerArchDump();
    253     if (gDvmJit.methodStatsTable) {
    254         dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats,
    255                        &totalMethodStats);
    256         ALOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)",
    257              totalMethodStats.compiledDalvikSize,
    258              totalMethodStats.dalvikSize,
    259              totalMethodStats.nativeSize);
    260     }
    261 }
    262 
    263 /*
    264  * Allocate a bit vector with enough space to hold at least the specified
    265  * number of bits.
    266  *
    267  * NOTE: this is the sister implementation of dvmAllocBitVector. In this version
    268  * memory is allocated from the compiler arena.
    269  */
    270 BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable)
    271 {
    272     BitVector* bv;
    273     unsigned int count;
    274 
    275     assert(sizeof(bv->storage[0]) == 4);        /* assuming 32-bit units */
    276 
    277     bv = (BitVector*) dvmCompilerNew(sizeof(BitVector), false);
    278 
    279     count = (startBits + 31) >> 5;
    280 
    281     bv->storageSize = count;
    282     bv->expandable = expandable;
    283     bv->storage = (u4*) dvmCompilerNew(count * sizeof(u4), true);
    284     return bv;
    285 }
    286 
    287 /*
    288  * Mark the specified bit as "set".
    289  *
    290  * Returns "false" if the bit is outside the range of the vector and we're
    291  * not allowed to expand.
    292  *
    293  * NOTE: this is the sister implementation of dvmSetBit. In this version
    294  * memory is allocated from the compiler arena.
    295  */
    296 bool dvmCompilerSetBit(BitVector *pBits, unsigned int num)
    297 {
    298     if (num >= pBits->storageSize * sizeof(u4) * 8) {
    299         if (!pBits->expandable)
    300             dvmAbort();
    301 
    302         /* Round up to word boundaries for "num+1" bits */
    303         unsigned int newSize = (num + 1 + 31) >> 5;
    304         assert(newSize > pBits->storageSize);
    305         u4 *newStorage = (u4*)dvmCompilerNew(newSize * sizeof(u4), false);
    306         memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4));
    307         memset(&newStorage[pBits->storageSize], 0,
    308                (newSize - pBits->storageSize) * sizeof(u4));
    309         pBits->storage = newStorage;
    310         pBits->storageSize = newSize;
    311     }
    312 
    313     pBits->storage[num >> 5] |= 1 << (num & 0x1f);
    314     return true;
    315 }
    316 
    317 /*
    318  * Mark the specified bit as "unset".
    319  *
    320  * Returns "false" if the bit is outside the range of the vector and we're
    321  * not allowed to expand.
    322  *
    323  * NOTE: this is the sister implementation of dvmClearBit. In this version
    324  * memory is allocated from the compiler arena.
    325  */
    326 bool dvmCompilerClearBit(BitVector *pBits, unsigned int num)
    327 {
    328     if (num >= pBits->storageSize * sizeof(u4) * 8) {
    329         ALOGE("Trying to clear a bit that is not set in the vector yet!");
    330         dvmAbort();
    331     }
    332 
    333     pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
    334     return true;
    335 }
    336 
    337 /*
    338  * If set is true, mark all bits as 1. Otherwise mark all bits as 0.
    339  */
    340 void dvmCompilerMarkAllBits(BitVector *pBits, bool set)
    341 {
    342     int value = set ? -1 : 0;
    343     memset(pBits->storage, value, pBits->storageSize * (int)sizeof(u4));
    344 }
    345 
    346 void dvmDebugBitVector(char *msg, const BitVector *bv, int length)
    347 {
    348     int i;
    349 
    350     ALOGE("%s", msg);
    351     for (i = 0; i < length; i++) {
    352         if (dvmIsBitSet(bv, i)) {
    353             ALOGE("    Bit %d is set", i);
    354         }
    355     }
    356 }
    357 
    358 void dvmCompilerAbort(CompilationUnit *cUnit)
    359 {
    360     ALOGE("Jit: aborting trace compilation, reverting to interpreter");
    361     /* Force a traceback in debug builds */
    362     assert(0);
    363     /*
    364      * Abort translation and force to interpret-only for this trace
    365      * Matching setjmp in compiler thread work loop in Compiler.c.
    366      */
    367     longjmp(*cUnit->bailPtr, 1);
    368 }
    369 
    370 void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
    371                            const BitVector *bv, int length)
    372 {
    373     int i;
    374 
    375     ALOGE("%s", msg);
    376     for (i = 0; i < length; i++) {
    377         if (dvmIsBitSet(bv, i)) {
    378             BasicBlock *bb =
    379                 (BasicBlock *) dvmGrowableListGetElement(blocks, i);
    380             char blockName[BLOCK_NAME_LEN];
    381             dvmGetBlockName(bb, blockName);
    382             ALOGE("Bit %d / %s is set", i, blockName);
    383         }
    384     }
    385 }
    386 
    387 void dvmGetBlockName(BasicBlock *bb, char *name)
    388 {
    389     switch (bb->blockType) {
    390         case kEntryBlock:
    391             snprintf(name, BLOCK_NAME_LEN, "entry");
    392             break;
    393         case kExitBlock:
    394             snprintf(name, BLOCK_NAME_LEN, "exit");
    395             break;
    396         case kDalvikByteCode:
    397             snprintf(name, BLOCK_NAME_LEN, "block%04x", bb->startOffset);
    398             break;
    399         case kChainingCellNormal:
    400             snprintf(name, BLOCK_NAME_LEN, "chain%04x", bb->startOffset);
    401             break;
    402         case kExceptionHandling:
    403             snprintf(name, BLOCK_NAME_LEN, "exception%04x", bb->startOffset);
    404             break;
    405         default:
    406             snprintf(name, BLOCK_NAME_LEN, "??");
    407             break;
    408     }
    409 }
    410