blob: f5b478c1b31995494fd7323e838c0ab6935934a6 [file] [log] [blame]
/*
* Copyright (C) 2011 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"
namespace art {
#ifdef WITH_MEMSTATS
struct Memstats {
u4 allocStats[kNumAllocKinds];
int listSizes[kNumListKinds];
int listWasted[kNumListKinds];
int listGrows[kNumListKinds];
int listMaxElems[kNumListKinds];
int bitMapSizes[kNumBitMapKinds];
int bitMapWasted[kNumBitMapKinds];
int bitMapGrows[kNumBitMapKinds];
};
const char* allocNames[kNumAllocKinds] = {
"Misc ",
"BasicBlock ",
"LIR ",
"MIR ",
"DataFlow ",
"GrowList ",
"GrowBitMap ",
"Dalvik2SSA ",
"DebugInfo ",
"Successor ",
"RegAlloc ",
"Data ",
"Preds ",
};
const char* listNames[kNumListKinds] = {
"Misc ",
"blockList ",
"SSAtoDalvik ",
"dfsOrder ",
"dfsPostOrder ",
"domPostOrderTraversal ",
"throwLaunchPads ",
"suspendLaunchPads ",
"switchTables ",
"fillArrayData ",
"SuccessorBlocks ",
"Predecessors ",
};
const char* bitMapNames[kNumBitMapKinds] = {
"Misc ",
"Use ",
"Def ",
"LiveIn ",
"BlockMatrix ",
"Dominators ",
"IDominated ",
"DomFrontier ",
"Phi ",
"TmpBlocks ",
"InputBlocks ",
"RegisterV ",
"TempSSARegisterV ",
"Null Check ",
"TmpBlockV ",
"Predecessors ",
};
#endif
#define kArenaBitVectorGrowth 4 /* increase by 4 u4s when limit hit */
/* Allocate the initial memory block for arena-based allocation */
bool oatHeapInit(CompilationUnit* cUnit)
{
DCHECK(cUnit->arenaHead == NULL);
cUnit->arenaHead =
(ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE);
if (cUnit->arenaHead == NULL) {
LOG(FATAL) << "No memory left to create compiler heap memory";
}
cUnit->arenaHead->blockSize = ARENA_DEFAULT_SIZE;
cUnit->currentArena = cUnit->arenaHead;
cUnit->currentArena->bytesAllocated = 0;
cUnit->currentArena->next = NULL;
cUnit->numArenaBlocks = 1;
#ifdef WITH_MEMSTATS
cUnit->mstats = (Memstats*) oatNew(cUnit, sizeof(Memstats), true,
kAllocDebugInfo);
#endif
return true;
}
/* Arena-based malloc for compilation tasks */
void* oatNew(CompilationUnit* cUnit, size_t size, bool zero, oatAllocKind kind)
{
size = (size + 3) & ~3;
#ifdef WITH_MEMSTATS
if (cUnit->mstats != NULL) {
cUnit->mstats->allocStats[kind] += size;
}
#endif
retry:
/* Normal case - space is available in the current page */
if (size + cUnit->currentArena->bytesAllocated <=
cUnit->currentArena->blockSize) {
void *ptr;
ptr = &cUnit->currentArena->ptr[cUnit->currentArena->bytesAllocated];
cUnit->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 (cUnit->currentArena->next) {
cUnit->currentArena = cUnit->currentArena->next;
cUnit->currentArena->bytesAllocated = 0;
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) {
LOG(FATAL) << "Arena allocation failure";
}
newArena->blockSize = blockSize;
newArena->bytesAllocated = 0;
newArena->next = NULL;
cUnit->currentArena->next = newArena;
cUnit->currentArena = newArena;
cUnit->numArenaBlocks++;
if (cUnit->numArenaBlocks > 20000) {
LOG(INFO) << "Total arena pages: " << cUnit->numArenaBlocks;
}
goto retry;
}
}
/* Reclaim all the arena blocks allocated so far */
void oatArenaReset(CompilationUnit* cUnit)
{
ArenaMemBlock* head = cUnit->arenaHead;
while (head != NULL) {
ArenaMemBlock* p = head;
head = head->next;
free(p);
}
cUnit->arenaHead = NULL;
cUnit->currentArena = NULL;
}
/* Growable List initialization */
void oatInitGrowableList(CompilationUnit* cUnit, GrowableList* gList,
size_t initLength, oatListKind kind)
{
gList->numAllocated = initLength;
gList->numUsed = 0;
gList->elemList = (intptr_t *) oatNew(cUnit, sizeof(intptr_t) * initLength,
true, kAllocGrowableList);
#ifdef WITH_MEMSTATS
cUnit->mstats->listSizes[kind] += sizeof(intptr_t) * initLength;
gList->kind = kind;
if ((int)initLength > cUnit->mstats->listMaxElems[kind]) {
cUnit->mstats->listMaxElems[kind] = initLength;
}
#endif
}
/* Expand the capacity of a growable list */
void expandGrowableList(CompilationUnit* cUnit, GrowableList* gList)
{
int newLength = gList->numAllocated;
if (newLength < 128) {
newLength <<= 1;
} else {
newLength += 128;
}
intptr_t *newArray =
(intptr_t *) oatNew(cUnit, sizeof(intptr_t) * newLength, true,
kAllocGrowableList);
memcpy(newArray, gList->elemList, sizeof(intptr_t) * gList->numAllocated);
#ifdef WITH_MEMSTATS
cUnit->mstats->listSizes[gList->kind] += sizeof(intptr_t) * newLength;
cUnit->mstats->listWasted[gList->kind] +=
sizeof(intptr_t) * gList->numAllocated;
cUnit->mstats->listGrows[gList->kind]++;
if (newLength > cUnit->mstats->listMaxElems[gList->kind]) {
cUnit->mstats->listMaxElems[gList->kind] = newLength;
}
#endif
gList->numAllocated = newLength;
gList->elemList = newArray;
}
/* Insert a new element into the growable list */
void oatInsertGrowableList(CompilationUnit* cUnit, GrowableList* gList,
intptr_t elem)
{
DCHECK_NE(gList->numAllocated, 0U);
if (gList->numUsed == gList->numAllocated) {
expandGrowableList(cUnit, gList);
}
gList->elemList[gList->numUsed++] = elem;
}
/* Delete an element from a growable list. Element must be present */
void oatDeleteGrowableList(GrowableList* gList, intptr_t elem)
{
bool found = false;
for (unsigned int i = 0; i < gList->numUsed; i++) {
if (!found && gList->elemList[i] == elem) {
found = true;
}
if (found) {
gList->elemList[i] = gList->elemList[i+1];
}
}
DCHECK_EQ(found, true);
gList->numUsed--;
}
void oatGrowableListIteratorInit(GrowableList* gList,
GrowableListIterator* iterator)
{
iterator->list = gList;
iterator->idx = 0;
iterator->size = gList->numUsed;
}
intptr_t oatGrowableListIteratorNext(GrowableListIterator* iterator)
{
DCHECK_EQ(iterator->size, iterator->list->numUsed);
if (iterator->idx == iterator->size) return 0;
return iterator->list->elemList[iterator->idx++];
}
intptr_t oatGrowableListGetElement(const GrowableList* gList, size_t idx)
{
DCHECK_LT(idx, gList->numUsed);
return gList->elemList[idx];
}
#ifdef WITH_MEMSTATS
/* Dump memory usage stats */
void oatDumpMemStats(CompilationUnit* cUnit)
{
u4 total = 0;
for (int i = 0; i < kNumAllocKinds; i++) {
total += cUnit->mstats->allocStats[i];
}
if (total > (10 * 1024 * 1024)) {
LOG(INFO) << "MEMUSAGE: " << total << " : "
<< PrettyMethod(cUnit->method_idx, *cUnit->dex_file);
LOG(INFO) << "insnsSize: " << cUnit->insnsSize;
if (cUnit->disableDataflow) {
LOG(INFO) << " ** Dataflow disabled ** ";
}
LOG(INFO) << "===== Overall allocations";
for (int i = 0; i < kNumAllocKinds; i++) {
LOG(INFO) << allocNames[i] << std::setw(10) <<
cUnit->mstats->allocStats[i];
}
LOG(INFO) << "===== GrowableList allocations";
for (int i = 0; i < kNumListKinds; i++) {
LOG(INFO) << listNames[i]
<< " S:" << cUnit->mstats->listSizes[i]
<< ", W:" << cUnit->mstats->listWasted[i]
<< ", G:" << cUnit->mstats->listGrows[i]
<< ", E:" << cUnit->mstats->listMaxElems[i];
}
LOG(INFO) << "===== GrowableBitMap allocations";
for (int i = 0; i < kNumBitMapKinds; i++) {
LOG(INFO) << bitMapNames[i]
<< " S:" << cUnit->mstats->bitMapSizes[i]
<< ", W:" << cUnit->mstats->bitMapWasted[i]
<< ", G:" << cUnit->mstats->bitMapGrows[i];
}
}
}
#endif
/* Debug Utility - dump a compilation unit */
void oatDumpCompilationUnit(CompilationUnit* cUnit)
{
BasicBlock* bb;
const char* blockTypeNames[] = {
"Entry Block",
"Code Block",
"Exit Block",
"Exception Handling",
"Catch Block"
};
LOG(INFO) << "Compiling " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file);
LOG(INFO) << cUnit->insns << " insns";
LOG(INFO) << cUnit->numBlocks << " blocks in total";
GrowableListIterator iterator;
oatGrowableListIteratorInit(&cUnit->blockList, &iterator);
while (true) {
bb = (BasicBlock *) oatGrowableListIteratorNext(&iterator);
if (bb == NULL) break;
LOG(INFO) << StringPrintf("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) {
LOG(INFO) << " Taken branch: block " << bb->taken->id <<
"(0x" << std::hex << bb->taken->startOffset << ")";
}
if (bb->fallThrough) {
LOG(INFO) << " Fallthrough : block " << bb->fallThrough->id <<
" (0x" << std::hex << bb->fallThrough->startOffset << ")";
}
}
}
static uint32_t checkMasks[32] = {
0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
0x40000000, 0x80000000 };
/*
* Allocate a bit vector with enough space to hold at least the specified
* number of bits.
*
* NOTE: memory is allocated from the compiler arena.
*/
ArenaBitVector* oatAllocBitVector(CompilationUnit* cUnit,
unsigned int startBits, bool expandable,
oatBitMapKind kind)
{
ArenaBitVector* bv;
unsigned int count;
DCHECK_EQ(sizeof(bv->storage[0]), 4U); /* assuming 32-bit units */
bv = (ArenaBitVector*) oatNew(cUnit, sizeof(ArenaBitVector), false,
kAllocGrowableBitMap);
count = (startBits + 31) >> 5;
bv->storageSize = count;
bv->expandable = expandable;
bv->storage = (u4*) oatNew(cUnit, count * sizeof(u4), true,
kAllocGrowableBitMap);
#ifdef WITH_MEMSTATS
bv->kind = kind;
cUnit->mstats->bitMapSizes[kind] += count * sizeof(u4);
#endif
return bv;
}
/*
* Determine whether or not the specified bit is set.
*/
bool oatIsBitSet(const ArenaBitVector* pBits, unsigned int num)
{
DCHECK_LT(num, pBits->storageSize * sizeof(u4) * 8);
unsigned int val = pBits->storage[num >> 5] & checkMasks[num & 0x1f];
return (val != 0);
}
/*
* Mark all bits bit as "clear".
*/
void oatClearAllBits(ArenaBitVector* pBits)
{
unsigned int count = pBits->storageSize;
memset(pBits->storage, 0, count * sizeof(u4));
}
/*
* 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: memory is allocated from the compiler arena.
*/
bool oatSetBit(CompilationUnit* cUnit, ArenaBitVector* pBits, unsigned int num)
{
if (num >= pBits->storageSize * sizeof(u4) * 8) {
if (!pBits->expandable) {
LOG(FATAL) << "Can't expand";
}
/* Round up to word boundaries for "num+1" bits */
unsigned int newSize = (num + 1 + 31) >> 5;
DCHECK_GT(newSize, pBits->storageSize);
u4 *newStorage = (u4*)oatNew(cUnit, newSize * sizeof(u4), false,
kAllocGrowableBitMap);
memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4));
memset(&newStorage[pBits->storageSize], 0,
(newSize - pBits->storageSize) * sizeof(u4));
#ifdef WITH_MEMSTATS
cUnit->mstats->bitMapWasted[pBits->kind] +=
pBits->storageSize * sizeof(u4);
cUnit->mstats->bitMapSizes[pBits->kind] += newSize * sizeof(u4);
cUnit->mstats->bitMapGrows[pBits->kind]++;
#endif
pBits->storage = newStorage;
pBits->storageSize = newSize;
}
pBits->storage[num >> 5] |= checkMasks[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: memory is allocated from the compiler arena.
*/
bool oatClearBit(ArenaBitVector* pBits, unsigned int num)
{
if (num >= pBits->storageSize * sizeof(u4) * 8) {
LOG(FATAL) << "Attempt to clear a bit not set in the vector yet";;
}
pBits->storage[num >> 5] &= ~checkMasks[num & 0x1f];
return true;
}
/*
* If set is true, mark all bits as 1. Otherwise mark all bits as 0.
*/
void oatMarkAllBits(ArenaBitVector* pBits, bool set)
{
int value = set ? -1 : 0;
memset(pBits->storage, value, pBits->storageSize * (int)sizeof(u4));
}
void oatDebugBitVector(char* msg, const ArenaBitVector* bv, int length)
{
int i;
LOG(INFO) << msg;
for (i = 0; i < length; i++) {
if (oatIsBitSet(bv, i)) {
LOG(INFO) << " Bit " << i << " is set";
}
}
}
void oatAbort(CompilationUnit* cUnit)
{
LOG(FATAL) << "Compiler aborting";
}
void oatDumpBlockBitVector(const GrowableList* blocks, char* msg,
const ArenaBitVector* bv, int length)
{
int i;
LOG(INFO) << msg;
for (i = 0; i < length; i++) {
if (oatIsBitSet(bv, i)) {
BasicBlock *bb =
(BasicBlock *) oatGrowableListGetElement(blocks, i);
char blockName[BLOCK_NAME_LEN];
oatGetBlockName(bb, blockName);
LOG(INFO) << "Bit " << i << " / " << blockName << " is set";
}
}
}
/* Initialize the iterator structure */
void oatBitVectorIteratorInit(ArenaBitVector* pBits,
ArenaBitVectorIterator* iterator)
{
iterator->pBits = pBits;
iterator->bitSize = pBits->storageSize * sizeof(u4) * 8;
iterator->idx = 0;
}
/*
* If the vector sizes don't match, log an error and abort.
*/
void checkSizes(const ArenaBitVector* bv1, const ArenaBitVector* bv2)
{
if (bv1->storageSize != bv2->storageSize) {
LOG(FATAL) << "Mismatched vector sizes (" << bv1->storageSize <<
", " << bv2->storageSize << ")";
}
}
/*
* Copy a whole vector to the other. Only do that when the both vectors have
* the same size.
*/
void oatCopyBitVector(ArenaBitVector* dest, const ArenaBitVector* src)
{
/* if dest is expandable and < src, we could expand dest to match */
checkSizes(dest, src);
memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
}
/*
* Intersect two bit vectors and store the result to the dest vector.
*/
bool oatIntersectBitVectors(ArenaBitVector* dest, const ArenaBitVector* src1,
const ArenaBitVector* src2)
{
DCHECK(src1 != NULL);
DCHECK(src2 != NULL);
if (dest->storageSize != src1->storageSize ||
dest->storageSize != src2->storageSize ||
dest->expandable != src1->expandable ||
dest->expandable != src2->expandable)
return false;
unsigned int idx;
for (idx = 0; idx < dest->storageSize; idx++) {
dest->storage[idx] = src1->storage[idx] & src2->storage[idx];
}
return true;
}
/*
* Unify two bit vectors and store the result to the dest vector.
*/
bool oatUnifyBitVectors(ArenaBitVector* dest, const ArenaBitVector* src1,
const ArenaBitVector* src2)
{
DCHECK(src1 != NULL);
DCHECK(src2 != NULL);
if (dest->storageSize != src1->storageSize ||
dest->storageSize != src2->storageSize ||
dest->expandable != src1->expandable ||
dest->expandable != src2->expandable)
return false;
unsigned int idx;
for (idx = 0; idx < dest->storageSize; idx++) {
dest->storage[idx] = src1->storage[idx] | src2->storage[idx];
}
return true;
}
/*
* Return true if any bits collide. Vectors must be same size.
*/
bool oatTestBitVectors(const ArenaBitVector* src1,
const ArenaBitVector* src2)
{
DCHECK_EQ(src1->storageSize, src2->storageSize);
for (uint32_t idx = 0; idx < src1->storageSize; idx++) {
if (src1->storage[idx] & src2->storage[idx]) return true;
}
return false;
}
/*
* Compare two bit vectors and return true if difference is seen.
*/
bool oatCompareBitVectors(const ArenaBitVector* src1,
const ArenaBitVector* src2)
{
if (src1->storageSize != src2->storageSize ||
src1->expandable != src2->expandable)
return true;
unsigned int idx;
for (idx = 0; idx < src1->storageSize; idx++) {
if (src1->storage[idx] != src2->storage[idx]) return true;
}
return false;
}
/*
* Count the number of bits that are set.
*/
int oatCountSetBits(const ArenaBitVector* pBits)
{
unsigned int word;
unsigned int count = 0;
for (word = 0; word < pBits->storageSize; word++) {
u4 val = pBits->storage[word];
if (val != 0) {
if (val == 0xffffffff) {
count += 32;
} else {
/* count the number of '1' bits */
while (val != 0) {
val &= val - 1;
count++;
}
}
}
}
return count;
}
/* Return the next position set to 1. -1 means end-of-element reached */
int oatBitVectorIteratorNext(ArenaBitVectorIterator* iterator)
{
ArenaBitVector* pBits = iterator->pBits;
u4 bitIndex = iterator->idx;
u4 bitSize = iterator->bitSize;
DCHECK_EQ(bitSize, pBits->storageSize * sizeof(u4) * 8);
if (bitIndex >= bitSize) return -1;
u4 wordIndex = bitIndex >> 5;
u4 endWordIndex = bitSize >> 5;
u4* storage = pBits->storage;
u4 word = storage[wordIndex++];
// Mask out any bits in the first word we've already considered
word &= ~((1 << (bitIndex & 0x1f))-1);
for (; wordIndex <= endWordIndex;) {
u4 bitPos = bitIndex & 0x1f;
if (word == 0) {
bitIndex += (32 - bitPos);
word = storage[wordIndex++];
continue;
}
for (; bitPos < 32; bitPos++) {
if (word & (1 << bitPos)) {
iterator->idx = bitIndex + 1;
return bitIndex;
}
bitIndex++;
}
word = storage[wordIndex++];
}
iterator->idx = iterator->bitSize;
return -1;
}
/*
* Mark specified number of bits as "set". Cannot set all bits like ClearAll
* since there might be unused bits - setting those to one will confuse the
* iterator.
*/
void oatSetInitialBits(ArenaBitVector* pBits, unsigned int numBits)
{
unsigned int idx;
DCHECK_LE(((numBits + 31) >> 5), pBits->storageSize);
for (idx = 0; idx < (numBits >> 5); idx++) {
pBits->storage[idx] = -1;
}
unsigned int remNumBits = numBits & 0x1f;
if (remNumBits) {
pBits->storage[idx] = (1 << remNumBits) - 1;
}
}
void oatGetBlockName(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 kExceptionHandling:
snprintf(name, BLOCK_NAME_LEN, "exception%04x", bb->startOffset);
break;
default:
snprintf(name, BLOCK_NAME_LEN, "??");
break;
}
}
const char* oatGetShortyFromTargetIdx(CompilationUnit *cUnit, int targetIdx)
{
const DexFile::MethodId& methodId = cUnit->dex_file->GetMethodId(targetIdx);
return cUnit->dex_file->GetShorty(methodId.proto_idx_);
}
} // namespace art