/* * 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" #include "Dataflow.h" #include "Loop.h" #define DEBUG_LOOP(X) /* * Given the current simple natural loops, the phi node placement can be * determined in the following fashion: * entry (B0) * +---v v * | loop body (B1) * | v * | loop back (B2) * +---+ v * exit (B3) * * 1) Add live-ins of B1 to B0 as defs * 2) The intersect of defs(B0)/defs(B1) and defs(B2)/def(B0) are the variables * that need PHI nodes in B1. */ static void handlePhiPlacement(CompilationUnit *cUnit) { BasicBlock *entry = cUnit->blockList[0]; BasicBlock *loopBody = cUnit->blockList[1]; BasicBlock *loopBranch = cUnit->blockList[2]; dvmCopyBitVector(entry->dataFlowInfo->defV, loopBody->dataFlowInfo->liveInV); BitVector *phiV = dvmCompilerAllocBitVector(cUnit->method->registersSize, false); dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV, loopBody->dataFlowInfo->defV); dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV, loopBranch->dataFlowInfo->defV); /* Insert the PHI MIRs */ int i; for (i = 0; i < cUnit->method->registersSize; i++) { if (!dvmIsBitSet(phiV, i)) { continue; } MIR *phi = dvmCompilerNew(sizeof(MIR), true); phi->dalvikInsn.opCode = kMirOpPhi; phi->dalvikInsn.vA = i; dvmCompilerPrependMIR(loopBody, phi); } } static void fillPhiNodeContents(CompilationUnit *cUnit) { BasicBlock *entry = cUnit->blockList[0]; BasicBlock *loopBody = cUnit->blockList[1]; BasicBlock *loopBranch = cUnit->blockList[2]; MIR *mir; for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) { if (mir->dalvikInsn.opCode != kMirOpPhi) break; int dalvikReg = mir->dalvikInsn.vA; mir->ssaRep->numUses = 2; mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * 2, false); mir->ssaRep->uses[0] = DECODE_REG(entry->dataFlowInfo->dalvikToSSAMap[dalvikReg]); mir->ssaRep->uses[1] = DECODE_REG(loopBranch->dataFlowInfo->dalvikToSSAMap[dalvikReg]); } } #if 0 /* Debugging routines */ static void dumpConstants(CompilationUnit *cUnit) { int i; for (i = 0; i < cUnit->numSSARegs; i++) { if (dvmIsBitSet(cUnit->isConstantV, i)) { int subNReg = dvmConvertSSARegToDalvik(cUnit, i); LOGE("s%d(v%d_%d) has %d", i, DECODE_REG(subNReg), DECODE_SUB(subNReg), cUnit->constantValues[i]); } } } static void dumpIVList(CompilationUnit *cUnit) { unsigned int i; GrowableList *ivList = cUnit->loopAnalysis->ivList; int *ssaToDalvikMap = (int *) cUnit->ssaToDalvikMap->elemList; for (i = 0; i < ivList->numUsed; i++) { InductionVariableInfo *ivInfo = ivList->elemList[i]; /* Basic IV */ if (ivInfo->ssaReg == ivInfo->basicSSAReg) { LOGE("BIV %d: s%d(v%d) + %d", i, ivInfo->ssaReg, ssaToDalvikMap[ivInfo->ssaReg] & 0xffff, ivInfo->inc); /* Dependent IV */ } else { LOGE("DIV %d: s%d(v%d) = %d * s%d(v%d) + %d", i, ivInfo->ssaReg, ssaToDalvikMap[ivInfo->ssaReg] & 0xffff, ivInfo->m, ivInfo->basicSSAReg, ssaToDalvikMap[ivInfo->basicSSAReg] & 0xffff, ivInfo->c); } } } static void dumpHoistedChecks(CompilationUnit *cUnit) { LoopAnalysis *loopAnalysis = cUnit->loopAnalysis; unsigned int i; for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) { ArrayAccessInfo *arrayAccessInfo = GET_ELEM_N(loopAnalysis->arrayAccessInfo, ArrayAccessInfo*, i); int arrayReg = DECODE_REG( dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg)); int idxReg = DECODE_REG( dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg)); LOGE("Array access %d", i); LOGE(" arrayReg %d", arrayReg); LOGE(" idxReg %d", idxReg); LOGE(" endReg %d", loopAnalysis->endConditionReg); LOGE(" maxC %d", arrayAccessInfo->maxC); LOGE(" minC %d", arrayAccessInfo->minC); LOGE(" opcode %d", loopAnalysis->loopBranchOpcode); } } #endif /* * A loop is considered optimizable if: * 1) It has one basic induction variable * 2) The loop back branch compares the BIV with a constant * 3) If it is a count-up loop, the condition is GE/GT, or LE/LT/LEZ/LTZ for * a count-down loop. * * Return false if the loop is not optimizable. */ static bool isLoopOptimizable(CompilationUnit *cUnit) { unsigned int i; BasicBlock *loopBranch = cUnit->blockList[2]; LoopAnalysis *loopAnalysis = cUnit->loopAnalysis; if (loopAnalysis->numBasicIV != 1) return false; for (i = 0; i < loopAnalysis->ivList->numUsed; i++) { InductionVariableInfo *ivInfo; ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i); /* Count up or down loop? */ if (ivInfo->ssaReg == ivInfo->basicSSAReg) { /* Infinite loop */ if (ivInfo->inc == 0) { return false; } loopAnalysis->isCountUpLoop = ivInfo->inc > 0; break; } } MIR *branch = loopBranch->lastMIRInsn; OpCode opCode = branch->dalvikInsn.opCode; /* * If the instruction is not accessing the IV as the first operand, return * false. */ if (branch->ssaRep->numUses == 0 || branch->ssaRep->numDefs != 0) { return false; } /* * If the first operand of the comparison is not the basic induction * variable, return false. */ if (branch->ssaRep->uses[0] != loopAnalysis->ssaBIV) { return false; } if (loopAnalysis->isCountUpLoop) { /* * If the condition op is not > or >=, this is not an optimization * candidate. */ if (opCode != OP_IF_GT && opCode != OP_IF_GE) { return false; } /* * If the comparison is not between the BIV and a loop invariant, * return false. endReg is loop invariant if one of the following is * true: * - It is not defined in the loop (ie DECODE_SUB returns 0) * - It is reloaded with a constant */ int endReg = dvmConvertSSARegToDalvik(cUnit, branch->ssaRep->uses[1]); if (DECODE_SUB(endReg) != 0 && !dvmIsBitSet(cUnit->isConstantV, branch->ssaRep->uses[1])) { return false; } loopAnalysis->endConditionReg = DECODE_REG(endReg); } else { /* * If the condition op is not < or <=, this is not an optimization * candidate. */ if (opCode == OP_IF_LT || opCode == OP_IF_LE) { /* * If the comparison is not between the BIV and a loop invariant, * return false. */ int endReg = dvmConvertSSARegToDalvik(cUnit, branch->ssaRep->uses[1]); if (DECODE_SUB(endReg) != 0) { return false; } loopAnalysis->endConditionReg = DECODE_REG(endReg); } else if (opCode != OP_IF_LTZ && opCode != OP_IF_LEZ) { return false; } } loopAnalysis->loopBranchOpcode = opCode; return true; } /* * Record the upper and lower bound information for range checks for each * induction variable. If array A is accessed by index "i+5", the upper and * lower bound will be len(A)-5 and -5, respectively. */ static void updateRangeCheckInfo(CompilationUnit *cUnit, int arrayReg, int idxReg) { InductionVariableInfo *ivInfo; LoopAnalysis *loopAnalysis = cUnit->loopAnalysis; unsigned int i, j; for (i = 0; i < loopAnalysis->ivList->numUsed; i++) { ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i); if (ivInfo->ssaReg == idxReg) { ArrayAccessInfo *arrayAccessInfo = NULL; for (j = 0; j < loopAnalysis->arrayAccessInfo->numUsed; j++) { ArrayAccessInfo *existingArrayAccessInfo = GET_ELEM_N(loopAnalysis->arrayAccessInfo, ArrayAccessInfo*, j); if (existingArrayAccessInfo->arrayReg == arrayReg) { if (ivInfo->c > existingArrayAccessInfo->maxC) { existingArrayAccessInfo->maxC = ivInfo->c; } if (ivInfo->c < existingArrayAccessInfo->minC) { existingArrayAccessInfo->minC = ivInfo->c; } arrayAccessInfo = existingArrayAccessInfo; break; } } if (arrayAccessInfo == NULL) { arrayAccessInfo = dvmCompilerNew(sizeof(ArrayAccessInfo), false); arrayAccessInfo->ivReg = ivInfo->basicSSAReg; arrayAccessInfo->arrayReg = arrayReg; arrayAccessInfo->maxC = (ivInfo->c > 0) ? ivInfo->c : 0; arrayAccessInfo->minC = (ivInfo->c < 0) ? ivInfo->c : 0; dvmInsertGrowableList(loopAnalysis->arrayAccessInfo, arrayAccessInfo); } break; } } } /* Returns true if the loop body cannot throw any exceptions */ static bool doLoopBodyCodeMotion(CompilationUnit *cUnit) { BasicBlock *loopBody = cUnit->blockList[1]; MIR *mir; bool loopBodyCanThrow = false; for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) { DecodedInstruction *dInsn = &mir->dalvikInsn; int dfAttributes = dvmCompilerDataFlowAttributes[mir->dalvikInsn.opCode]; /* Skip extended MIR instructions */ if (dInsn->opCode > 255) continue; int instrFlags = dexGetInstrFlags(gDvm.instrFlags, dInsn->opCode); /* Instruction is clean */ if ((instrFlags & kInstrCanThrow) == 0) continue; /* * Currently we can only optimize away null and range checks. Punt on * instructions that can throw due to other exceptions. */ if (!(dfAttributes & DF_HAS_NR_CHECKS)) { loopBodyCanThrow = true; continue; } /* * This comparison is redundant now, but we will have more than one * group of flags to check soon. */ if (dfAttributes & DF_HAS_NR_CHECKS) { /* * Check if the null check is applied on a loop invariant register? * If the register's SSA id is less than the number of Dalvik * registers, then it is loop invariant. */ int refIdx; switch (dfAttributes & DF_HAS_NR_CHECKS) { case DF_NULL_N_RANGE_CHECK_0: refIdx = 0; break; case DF_NULL_N_RANGE_CHECK_1: refIdx = 1; break; case DF_NULL_N_RANGE_CHECK_2: refIdx = 2; break; default: refIdx = 0; LOGE("Jit: bad case in doLoopBodyCodeMotion"); dvmCompilerAbort(cUnit); } int useIdx = refIdx + 1; int subNRegArray = dvmConvertSSARegToDalvik(cUnit, mir->ssaRep->uses[refIdx]); int arraySub = DECODE_SUB(subNRegArray); /* * If the register is never updated in the loop (ie subscript == 0), * it is an optimization candidate. */ if (arraySub != 0) { loopBodyCanThrow = true; continue; } /* * Then check if the range check can be hoisted out of the loop if * it is basic or dependent induction variable. */ if (dvmIsBitSet(cUnit->loopAnalysis->isIndVarV, mir->ssaRep->uses[useIdx])) { mir->OptimizationFlags |= MIR_IGNORE_RANGE_CHECK | MIR_IGNORE_NULL_CHECK; updateRangeCheckInfo(cUnit, mir->ssaRep->uses[refIdx], mir->ssaRep->uses[useIdx]); } } } return !loopBodyCanThrow; } static void genHoistedChecks(CompilationUnit *cUnit) { unsigned int i; BasicBlock *entry = cUnit->blockList[0]; LoopAnalysis *loopAnalysis = cUnit->loopAnalysis; int globalMaxC = 0; int globalMinC = 0; /* Should be loop invariant */ int idxReg = 0; for (i = 0; i < loopAnalysis->arrayAccessInfo->numUsed; i++) { ArrayAccessInfo *arrayAccessInfo = GET_ELEM_N(loopAnalysis->arrayAccessInfo, ArrayAccessInfo*, i); int arrayReg = DECODE_REG( dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->arrayReg)); idxReg = DECODE_REG( dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg)); MIR *rangeCheckMIR = dvmCompilerNew(sizeof(MIR), true); rangeCheckMIR->dalvikInsn.opCode = (loopAnalysis->isCountUpLoop) ? kMirOpNullNRangeUpCheck : kMirOpNullNRangeDownCheck; rangeCheckMIR->dalvikInsn.vA = arrayReg; rangeCheckMIR->dalvikInsn.vB = idxReg; rangeCheckMIR->dalvikInsn.vC = loopAnalysis->endConditionReg; rangeCheckMIR->dalvikInsn.arg[0] = arrayAccessInfo->maxC; rangeCheckMIR->dalvikInsn.arg[1] = arrayAccessInfo->minC; rangeCheckMIR->dalvikInsn.arg[2] = loopAnalysis->loopBranchOpcode; dvmCompilerAppendMIR(entry, rangeCheckMIR); if (arrayAccessInfo->maxC > globalMaxC) { globalMaxC = arrayAccessInfo->maxC; } if (arrayAccessInfo->minC < globalMinC) { globalMinC = arrayAccessInfo->minC; } } if (loopAnalysis->arrayAccessInfo->numUsed != 0) { if (loopAnalysis->isCountUpLoop) { MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true); boundCheckMIR->dalvikInsn.opCode = kMirOpLowerBound; boundCheckMIR->dalvikInsn.vA = idxReg; boundCheckMIR->dalvikInsn.vB = globalMinC; dvmCompilerAppendMIR(entry, boundCheckMIR); } else { if (loopAnalysis->loopBranchOpcode == OP_IF_LT || loopAnalysis->loopBranchOpcode == OP_IF_LE) { MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true); boundCheckMIR->dalvikInsn.opCode = kMirOpLowerBound; boundCheckMIR->dalvikInsn.vA = loopAnalysis->endConditionReg; boundCheckMIR->dalvikInsn.vB = globalMinC; /* * If the end condition is ">" in the source, the check in the * Dalvik bytecode is OP_IF_LE. In this case add 1 back to the * constant field to reflect the fact that the smallest index * value is "endValue + constant + 1". */ if (loopAnalysis->loopBranchOpcode == OP_IF_LE) { boundCheckMIR->dalvikInsn.vB++; } dvmCompilerAppendMIR(entry, boundCheckMIR); } else if (loopAnalysis->loopBranchOpcode == OP_IF_LTZ) { /* Array index will fall below 0 */ if (globalMinC < 0) { MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true); boundCheckMIR->dalvikInsn.opCode = kMirOpPunt; dvmCompilerAppendMIR(entry, boundCheckMIR); } } else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) { /* Array index will fall below 0 */ if (globalMinC < -1) { MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true); boundCheckMIR->dalvikInsn.opCode = kMirOpPunt; dvmCompilerAppendMIR(entry, boundCheckMIR); } } else { LOGE("Jit: bad case in genHoistedChecks"); dvmCompilerAbort(cUnit); } } } } /* * Main entry point to do loop optimization. * Return false if sanity checks for loop formation/optimization failed. */ bool dvmCompilerLoopOpt(CompilationUnit *cUnit) { LoopAnalysis *loopAnalysis = dvmCompilerNew(sizeof(LoopAnalysis), true); assert(cUnit->blockList[0]->blockType == kTraceEntryBlock); assert(cUnit->blockList[2]->blockType == kDalvikByteCode); assert(cUnit->blockList[3]->blockType == kTraceExitBlock); cUnit->loopAnalysis = loopAnalysis; /* * Find live-in variables to the loop body so that we can fake their * definitions in the entry block. */ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLiveIn); /* Insert phi nodes to the loop body */ handlePhiPlacement(cUnit); dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion); fillPhiNodeContents(cUnit); /* Constant propagation */ cUnit->isConstantV = dvmAllocBitVector(cUnit->numSSARegs, false); cUnit->constantValues = dvmCompilerNew(sizeof(int) * cUnit->numSSARegs, true); dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoConstantPropagation); DEBUG_LOOP(dumpConstants(cUnit);) /* Find induction variables - basic and dependent */ loopAnalysis->ivList = dvmCompilerNew(sizeof(GrowableList), true); dvmInitGrowableList(loopAnalysis->ivList, 4); loopAnalysis->isIndVarV = dvmAllocBitVector(cUnit->numSSARegs, false); dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindInductionVariables); DEBUG_LOOP(dumpIVList(cUnit);) /* If the loop turns out to be non-optimizable, return early */ if (!isLoopOptimizable(cUnit)) return false; loopAnalysis->arrayAccessInfo = dvmCompilerNew(sizeof(GrowableList), true); dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4); loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit); DEBUG_LOOP(dumpHoistedChecks(cUnit);) /* * Convert the array access information into extended MIR code in the loop * header. */ genHoistedChecks(cUnit); return true; }