/* * Iterate through all successor blocks and propagate up the live-in sets. * The calculated result is used for phi-node pruning - where we only need to * insert a phi node if the variable is live-in to the block. */ static bool computeBlockLiveIns(CompilationUnit *cUnit, BasicBlock *bb) { BitVector *tempDalvikRegisterV = cUnit->tempDalvikRegisterV; if (bb->dataFlowInfo == NULL) return false; dvmCopyBitVector(tempDalvikRegisterV, bb->dataFlowInfo->liveInV); if (bb->taken && bb->taken->dataFlowInfo) computeSuccLiveIn(tempDalvikRegisterV, bb->taken->dataFlowInfo->liveInV, bb->dataFlowInfo->defV); if (bb->fallThrough && bb->fallThrough->dataFlowInfo) computeSuccLiveIn(tempDalvikRegisterV, bb->fallThrough->dataFlowInfo->liveInV, bb->dataFlowInfo->defV); if (bb->successorBlockList.blockListType != kNotUsed) { GrowableListIterator iterator; dvmGrowableListIteratorInit(&bb->successorBlockList.blocks, &iterator); while (true) { SuccessorBlockInfo *successorBlockInfo = (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator); if (successorBlockInfo == NULL) break; BasicBlock *succBB = successorBlockInfo->block; if (succBB->dataFlowInfo) { computeSuccLiveIn(tempDalvikRegisterV, succBB->dataFlowInfo->liveInV, bb->dataFlowInfo->defV); } } } if (dvmCompareBitVectors(tempDalvikRegisterV, bb->dataFlowInfo->liveInV)) { dvmCopyBitVector(bb->dataFlowInfo->liveInV, tempDalvikRegisterV); return true; } return false; }
/* Worker function to compute each block's dominators */ static bool computeBlockDominators(CompilationUnit *cUnit, BasicBlock *bb) { GrowableList *blockList = &cUnit->blockList; int numTotalBlocks = blockList->numUsed; BitVector *tempBlockV = cUnit->tempBlockV; BitVectorIterator bvIterator; /* * The dominator of the entry block has been preset to itself and we need * to skip the calculation here. */ if (bb == cUnit->entryBlock) return false; dvmSetInitialBits(tempBlockV, numTotalBlocks); /* Iterate through the predecessors */ dvmBitVectorIteratorInit(bb->predecessors, &bvIterator); while (true) { int predIdx = dvmBitVectorIteratorNext(&bvIterator); if (predIdx == -1) break; BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement( blockList, predIdx); /* tempBlockV = tempBlockV ^ dominators */ dvmIntersectBitVectors(tempBlockV, tempBlockV, predBB->dominators); } dvmSetBit(tempBlockV, bb->id); if (dvmCompareBitVectors(tempBlockV, bb->dominators)) { dvmCopyBitVector(bb->dominators, tempBlockV); return true; } return false; }
/* Worker function to compute the idom */ static bool computeImmediateDominator(CompilationUnit *cUnit, BasicBlock *bb) { GrowableList *blockList = &cUnit->blockList; BitVector *tempBlockV = cUnit->tempBlockV; BitVectorIterator bvIterator; BasicBlock *iDom; if (bb == cUnit->entryBlock) return false; dvmCopyBitVector(tempBlockV, bb->dominators); dvmClearBit(tempBlockV, bb->id); dvmBitVectorIteratorInit(tempBlockV, &bvIterator); /* Should not see any dead block */ assert(dvmCountSetBits(tempBlockV) != 0); if (dvmCountSetBits(tempBlockV) == 1) { iDom = (BasicBlock *) dvmGrowableListGetElement( blockList, dvmBitVectorIteratorNext(&bvIterator)); bb->iDom = iDom; } else { int iDomIdx = dvmBitVectorIteratorNext(&bvIterator); assert(iDomIdx != -1); while (true) { int nextDom = dvmBitVectorIteratorNext(&bvIterator); if (nextDom == -1) break; BasicBlock *nextDomBB = (BasicBlock *) dvmGrowableListGetElement(blockList, nextDom); /* iDom dominates nextDom - set new iDom */ if (dvmIsBitSet(nextDomBB->dominators, iDomIdx)) { iDomIdx = nextDom; } } iDom = (BasicBlock *) dvmGrowableListGetElement(blockList, iDomIdx); /* Set the immediate dominator block for bb */ bb->iDom = iDom; } /* Add bb to the iDominated set of the immediate dominator block */ dvmCompilerSetBit(iDom->iDominated, bb->id); return true; }
/* * Compute the "liveness" of every register at all GC points. */ bool dvmComputeLiveness(VerifierData* vdata) { const InsnFlags* insnFlags = vdata->insnFlags; InstructionWidth* backwardWidth; VfyBasicBlock* startGuess = NULL; BitVector* workBits = NULL; bool result = false; bool verbose = false; //= dvmWantVerboseVerification(vdata->method); if (verbose) { const Method* meth = vdata->method; ALOGI("Computing liveness for %s.%s:%s", meth->clazz->descriptor, meth->name, meth->shorty); } assert(vdata->registerLines != NULL); backwardWidth = createBackwardWidthTable(vdata); if (backwardWidth == NULL) goto bail; /* * Allocate space for intra-block work set. Does not include space * for method result "registers", which aren't visible to the GC. * (They would be made live by move-result and then die on the * instruction immediately before it.) */ workBits = dvmAllocBitVector(vdata->insnRegCount, false); if (workBits == NULL) goto bail; /* * We continue until all blocks have been visited, and no block * requires further attention ("visited" is set and "changed" is * clear). * * TODO: consider creating a "dense" array of basic blocks to make * the walking faster. */ for (int iter = 0;;) { VfyBasicBlock* workBlock = NULL; if (iter++ > 100000) { LOG_VFY_METH(vdata->method, "oh dear"); dvmAbort(); } /* * If a block is marked "changed", we stop and handle it. If it * just hasn't been visited yet, we remember it but keep searching * for one that has been changed. * * The thought here is that this is more likely to let us work * from end to start, which reduces the amount of re-evaluation * required (both by using "changed" as a work list, and by picking * un-visited blocks from the tail end of the method). */ if (startGuess != NULL) { assert(startGuess->changed); workBlock = startGuess; } else { for (u4 idx = 0; idx < vdata->insnsSize; idx++) { VfyBasicBlock* block = vdata->basicBlocks[idx]; if (block == NULL) continue; if (block->changed) { workBlock = block; break; } else if (!block->visited) { workBlock = block; } } } if (workBlock == NULL) { /* all done */ break; } assert(workBlock->changed || !workBlock->visited); startGuess = NULL; /* * Load work bits. These represent the liveness of registers * after the last instruction in the block has finished executing. */ assert(workBlock->liveRegs != NULL); dvmCopyBitVector(workBits, workBlock->liveRegs); if (verbose) { ALOGI("Loaded work bits from last=0x%04x", workBlock->lastAddr); dumpLiveState(vdata, 0xfffd, workBlock->liveRegs); dumpLiveState(vdata, 0xffff, workBits); } /* * Process a single basic block. * * If this instruction is a GC point, we want to save the result * in the RegisterLine. * * We don't break basic blocks on every GC point -- in particular, * instructions that might throw but have no "try" block don't * end a basic block -- so there could be more than one GC point * in a given basic block. * * We could change this, but it turns out to be not all that useful. * At first glance it appears that we could share the liveness bit * vector between the basic block struct and the register line, * but the basic block needs to reflect the state *after* the * instruction has finished, while the GC points need to describe * the state before the instruction starts. */ u4 curIdx = workBlock->lastAddr; while (true) { if (!processInstruction(vdata, curIdx, workBits)) goto bail; if (verbose) { dumpLiveState(vdata, curIdx + 0x8000, workBits); } if (dvmInsnIsGcPoint(insnFlags, curIdx)) { BitVector* lineBits = vdata->registerLines[curIdx].liveRegs; if (lineBits == NULL) { lineBits = vdata->registerLines[curIdx].liveRegs = dvmAllocBitVector(vdata->insnRegCount, false); } dvmCopyBitVector(lineBits, workBits); } if (curIdx == workBlock->firstAddr) break; assert(curIdx >= backwardWidth[curIdx]); curIdx -= backwardWidth[curIdx]; } workBlock->visited = true; workBlock->changed = false; if (verbose) { dumpLiveState(vdata, curIdx, workBits); } /* * Merge changes to all predecessors. If the new bits don't match * the old bits, set the "changed" flag. */ PointerSet* preds = workBlock->predecessors; size_t numPreds = dvmPointerSetGetCount(preds); unsigned int predIdx; for (predIdx = 0; predIdx < numPreds; predIdx++) { VfyBasicBlock* pred = (VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx); pred->changed = dvmCheckMergeBitVectors(pred->liveRegs, workBits); if (verbose) { ALOGI("merging cur=%04x into pred last=%04x (ch=%d)", curIdx, pred->lastAddr, pred->changed); dumpLiveState(vdata, 0xfffa, pred->liveRegs); dumpLiveState(vdata, 0xfffb, workBits); } /* * We want to set the "changed" flag on unvisited predecessors * as a way of guiding the verifier through basic blocks in * a reasonable order. We can't count on variable liveness * changing, so we force "changed" to true even if it hasn't. */ if (!pred->visited) pred->changed = true; /* * Keep track of one of the changed blocks so we can start * there instead of having to scan through the list. */ if (pred->changed) startGuess = pred; } } #ifndef NDEBUG /* * Sanity check: verify that all GC point register lines have a * liveness bit vector allocated. Also, we're not expecting non-GC * points to have them. */ u4 checkIdx; for (checkIdx = 0; checkIdx < vdata->insnsSize; ) { if (dvmInsnIsGcPoint(insnFlags, checkIdx)) { if (vdata->registerLines[checkIdx].liveRegs == NULL) { LOG_VFY_METH(vdata->method, "GLITCH: no liveRegs for GC point 0x%04x", checkIdx); dvmAbort(); } } else if (vdata->registerLines[checkIdx].liveRegs != NULL) { LOG_VFY_METH(vdata->method, "GLITCH: liveRegs for non-GC point 0x%04x", checkIdx); dvmAbort(); } u4 insnWidth = dvmInsnGetWidth(insnFlags, checkIdx); checkIdx += insnWidth; } #endif /* * Factor in the debug info, if any. */ if (!markDebugLocals(vdata)) goto bail; result = true; bail: free(backwardWidth); dvmFreeBitVector(workBits); return result; }
/* Insert phi nodes to for each variable to the dominance frontiers */ static void insertPhiNodes(CompilationUnit *cUnit) { int dalvikReg; const GrowableList *blockList = &cUnit->blockList; BitVector *phiBlocks = dvmCompilerAllocBitVector(cUnit->numBlocks, false); BitVector *tmpBlocks = dvmCompilerAllocBitVector(cUnit->numBlocks, false); BitVector *inputBlocks = dvmCompilerAllocBitVector(cUnit->numBlocks, false); cUnit->tempDalvikRegisterV = dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false); dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockLiveIns, kPostOrderDFSTraversal, true /* isIterative */); /* Iterate through each Dalvik register */ for (dalvikReg = 0; dalvikReg < cUnit->numDalvikRegisters; dalvikReg++) { bool change; BitVectorIterator iterator; dvmCopyBitVector(inputBlocks, cUnit->defBlockMatrix[dalvikReg]); dvmClearAllBits(phiBlocks); /* Calculate the phi blocks for each Dalvik register */ do { change = false; dvmClearAllBits(tmpBlocks); dvmBitVectorIteratorInit(inputBlocks, &iterator); while (true) { int idx = dvmBitVectorIteratorNext(&iterator); if (idx == -1) break; BasicBlock *defBB = (BasicBlock *) dvmGrowableListGetElement(blockList, idx); /* Merge the dominance frontier to tmpBlocks */ dvmUnifyBitVectors(tmpBlocks, tmpBlocks, defBB->domFrontier); } if (dvmCompareBitVectors(phiBlocks, tmpBlocks)) { change = true; dvmCopyBitVector(phiBlocks, tmpBlocks); /* * Iterate through the original blocks plus the new ones in * the dominance frontier. */ dvmCopyBitVector(inputBlocks, phiBlocks); dvmUnifyBitVectors(inputBlocks, inputBlocks, cUnit->defBlockMatrix[dalvikReg]); } } while (change); /* * Insert a phi node for dalvikReg in the phiBlocks if the Dalvik * register is in the live-in set. */ dvmBitVectorIteratorInit(phiBlocks, &iterator); while (true) { int idx = dvmBitVectorIteratorNext(&iterator); if (idx == -1) break; BasicBlock *phiBB = (BasicBlock *) dvmGrowableListGetElement(blockList, idx); /* Variable will be clobbered before being used - no need for phi */ if (!dvmIsBitSet(phiBB->dataFlowInfo->liveInV, dalvikReg)) continue; MIR *phi = (MIR *) dvmCompilerNew(sizeof(MIR), true); phi->dalvikInsn.opcode = (Opcode) kMirOpPhi; phi->dalvikInsn.vA = dalvikReg; phi->offset = phiBB->startOffset; dvmCompilerPrependMIR(phiBB, phi); } } }