diff options
| -rw-r--r-- | src/compiler/Compiler.h | 1 | ||||
| -rw-r--r-- | src/compiler/CompilerIR.h | 12 | ||||
| -rw-r--r-- | src/compiler/Dataflow.cc | 108 | ||||
| -rw-r--r-- | src/compiler/Dataflow.h | 10 | ||||
| -rw-r--r-- | src/compiler/Frontend.cc | 21 | ||||
| -rw-r--r-- | src/compiler/IntermediateRep.cc | 1 | ||||
| -rw-r--r-- | src/compiler/SSATransformation.cc | 15 | ||||
| -rw-r--r-- | src/compiler/codegen/CodegenUtil.cc | 17 | ||||
| -rw-r--r-- | src/compiler/codegen/MethodBitcode.cc | 15 | ||||
| -rw-r--r-- | src/compiler/codegen/MethodCodegenDriver.cc | 13 | ||||
| -rw-r--r-- | src/compiler/codegen/arm/Thumb2/Gen.cc | 5 | ||||
| -rw-r--r-- | src/safe_map.h | 6 |
12 files changed, 174 insertions, 50 deletions
diff --git a/src/compiler/Compiler.h b/src/compiler/Compiler.h index 9ac6016fd3..4a7683b10b 100644 --- a/src/compiler/Compiler.h +++ b/src/compiler/Compiler.h @@ -140,6 +140,7 @@ enum debugControlVector { kDebugShowMemoryUsage, kDebugShowNops, kDebugCountOpcodes, + kDebugDumpCheckStats, #if defined(ART_USE_QUICK_COMPILER) kDebugDumpBitcodeFile, kDebugVerifyBitcode, diff --git a/src/compiler/CompilerIR.h b/src/compiler/CompilerIR.h index 02c7621f77..1cbf2b5f19 100644 --- a/src/compiler/CompilerIR.h +++ b/src/compiler/CompilerIR.h @@ -160,6 +160,7 @@ enum BBType { kExitBlock, kExceptionHandling, kCatchEntry, + kDead, }; /* Utility macros to traverse the LIR list */ @@ -239,6 +240,13 @@ struct CallsiteInfo { LIR* misPredBranchOver; }; +struct Checkstats { + int nullChecks; + int nullChecksEliminated; + int rangeChecks; + int rangeChecksEliminated; +}; + struct MIR { DecodedInstruction dalvikInsn; unsigned int width; @@ -272,7 +280,6 @@ struct BasicBlock { bool visited; bool hidden; bool catchEntry; - bool fallThroughTarget; // Reached via fallthrough #if defined(ART_USE_QUICK_COMPILER) bool hasReturn; #endif @@ -412,6 +419,7 @@ struct CompilationUnit { currentArena(NULL), numArenaBlocks(0), mstats(NULL), + checkstats(NULL), #if defined(ART_USE_QUICK_COMPILER) genBitcode(false), context(NULL), @@ -571,6 +579,7 @@ struct CompilationUnit { u4 insnsSize; bool disableDataflow; // Skip dataflow analysis if possible SafeMap<unsigned int, BasicBlock*> blockMap; // findBlock lookup cache + SafeMap<unsigned int, unsigned int> blockIdMap; // Block collapse lookup cache SafeMap<unsigned int, LIR*> boundaryMap; // boundary lookup cache int defCount; // Used to estimate number of SSA names @@ -582,6 +591,7 @@ struct CompilationUnit { ArenaMemBlock* currentArena; int numArenaBlocks; Memstats* mstats; + Checkstats* checkstats; #if defined(ART_USE_QUICK_COMPILER) bool genBitcode; llvm::LLVMContext* context; diff --git a/src/compiler/Dataflow.cc b/src/compiler/Dataflow.cc index 896764969a..a5cdbdeaaa 100644 --- a/src/compiler/Dataflow.cc +++ b/src/compiler/Dataflow.cc @@ -1945,6 +1945,90 @@ bool nullCheckEliminationInit(struct CompilationUnit* cUnit, return true; } +/* Collect stats on number of checks removed */ +bool countChecks( struct CompilationUnit* cUnit, struct BasicBlock* bb) +{ + if (bb->dataFlowInfo == NULL) return false; + for (MIR* mir = bb->firstMIRInsn; mir; mir = mir->next) { + if (mir->ssaRep == NULL) { + continue; + } + int dfAttributes = oatDataFlowAttributes[mir->dalvikInsn.opcode]; + if (dfAttributes & DF_HAS_NULL_CHKS) { + cUnit->checkstats->nullChecks++; + if (mir->optimizationFlags & MIR_IGNORE_NULL_CHECK) { + cUnit->checkstats->nullChecksEliminated++; + } + } + if (dfAttributes & DF_HAS_RANGE_CHKS) { + cUnit->checkstats->rangeChecks++; + if (mir->optimizationFlags & MIR_IGNORE_RANGE_CHECK) { + cUnit->checkstats->rangeChecksEliminated++; + } + } + } + return false; +} + +/* Combine any basic blocks terminated by instructions that we now know can't throw */ +bool combineBlocks(struct CompilationUnit* cUnit, struct BasicBlock* bb) +{ + // Loop here to allow combining a sequence of blocks + while (true) { + // Check termination conditions + if ((bb->firstMIRInsn == NULL) + || (bb->dataFlowInfo == NULL) + || (bb->blockType == kExceptionHandling) + || (bb->blockType == kExitBlock) + || (bb->blockType == kDead) + || ((bb->taken == NULL) || (bb->taken->blockType != kExceptionHandling)) + || (bb->successorBlockList.blockListType != kNotUsed) + || ((int)bb->lastMIRInsn->dalvikInsn.opcode != kMirOpCheck)) { + break; + } + + // Test the kMirOpCheck instruction + MIR* mir = bb->lastMIRInsn; + // Grab the attributes from the paired opcode + MIR* throwInsn = mir->meta.throwInsn; + int dfAttributes = oatDataFlowAttributes[throwInsn->dalvikInsn.opcode]; + // Only null checks, and null checks eliminated? + if (((dfAttributes & DF_HAS_NULL_CHKS) == 0) || ((dfAttributes & DF_HAS_RANGE_CHKS) != 0) + || !(throwInsn->optimizationFlags & MIR_IGNORE_NULL_CHECK)) { + break; + } + // OK - got one. Combine + BasicBlock* bbNext = bb->fallThrough; + DCHECK(!bbNext->catchEntry); + DCHECK_EQ(bbNext->predecessors->numUsed, 1U); + MIR* tMir = bb->lastMIRInsn->prev; + // Overwrite the kOpCheck insn with the paired opcode + DCHECK_EQ(bbNext->firstMIRInsn, throwInsn); + *bb->lastMIRInsn = *throwInsn; + bb->lastMIRInsn->prev = tMir; + // Use the successor info from the next block + bb->successorBlockList = bbNext->successorBlockList; + // Use the ending block linkage from the next block + bb->fallThrough = bbNext->fallThrough; + bb->taken->blockType = kDead; // Kill the unused exception block + bb->taken = bbNext->taken; + // Include the rest of the instructions + bb->lastMIRInsn = bbNext->lastMIRInsn; + + /* + * NOTE: we aren't updating all dataflow info here. Should either make sure this pass + * happens after uses of iDominated, domFrontier or update the dataflow info here. + */ + + // Kill bbNext and remap now-dead id to parent + bbNext->blockType = kDead; + cUnit->blockIdMap.Overwrite(bbNext->id, bb->id); + + // Now, loop back and see if we can keep going + } + return false; +} + /* Eliminate unnecessary null checks for a basic block. */ bool eliminateNullChecks( struct CompilationUnit* cUnit, struct BasicBlock* bb) { @@ -2049,7 +2133,7 @@ bool eliminateNullChecks( struct CompilationUnit* cUnit, struct BasicBlock* bb) } // Already nullchecked? - if (dfAttributes & DF_HAS_NULL_CHKS) { + if ((dfAttributes & DF_HAS_NULL_CHKS) && !(mir->optimizationFlags & MIR_IGNORE_NULL_CHECK)) { int srcIdx; if (dfAttributes & DF_NULL_CHK_1) { srcIdx = 1; @@ -2091,6 +2175,28 @@ void oatMethodNullCheckElimination(CompilationUnit *cUnit) } } +void oatMethodBasicBlockCombine(CompilationUnit* cUnit) +{ + oatDataFlowAnalysisDispatcher(cUnit, combineBlocks, kPreOrderDFSTraversal, false); +} + +void oatDumpCheckStats(CompilationUnit *cUnit) +{ + Checkstats* stats = (Checkstats*)oatNew(cUnit, sizeof(Checkstats), true, kAllocDFInfo); + cUnit->checkstats = stats; + oatDataFlowAnalysisDispatcher(cUnit, countChecks, kAllNodes, false /* isIterative */); + if (stats->nullChecks > 0) { + LOG(INFO) << "Null Checks: " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " + << stats->nullChecksEliminated << " of " << stats->nullChecks << " -> " + << ((float)stats->nullChecksEliminated/(float)stats->nullChecks) * 100.0 << "%"; + } + if (stats->rangeChecks > 0) { + LOG(INFO) << "Range Checks: " << PrettyMethod(cUnit->method_idx, *cUnit->dex_file) << " " + << stats->rangeChecksEliminated << " of " << stats->rangeChecks << " -> " + << ((float)stats->rangeChecksEliminated/(float)stats->rangeChecks) * 100.0 << "%"; + } +} + void oatMethodBasicBlockOptimization(CompilationUnit *cUnit) { if (!(cUnit->disableOpt & (1 << kBBOpt))) { diff --git a/src/compiler/Dataflow.h b/src/compiler/Dataflow.h index 9ac54d59e5..8263f335d7 100644 --- a/src/compiler/Dataflow.h +++ b/src/compiler/Dataflow.h @@ -100,10 +100,12 @@ enum DataFlowAttributePos { DF_NULL_CHK_2 | \ DF_NULL_CHK_OUT0) -#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \ - DF_RANGE_CHK_1 | \ +#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_1 | \ DF_RANGE_CHK_2) +#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \ + DF_HAS_RANGE_CHKS) + #define DF_A_IS_REG (DF_UA | DF_DA) #define DF_B_IS_REG (DF_UB) #define DF_C_IS_REG (DF_UC) @@ -161,6 +163,10 @@ void oatMethodUseCount(CompilationUnit*); void oatMethodNullCheckElimination(CompilationUnit*); +void oatDumpCheckStats(CompilationUnit*); + +void oatMethodBasicBlockCombine(CompilationUnit*); + void oatMethodBasicBlockOptimization(CompilationUnit*); } // namespace art diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc index 8c403c599e..4aa0ac89e9 100644 --- a/src/compiler/Frontend.cc +++ b/src/compiler/Frontend.cc @@ -80,6 +80,7 @@ static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes //(1 << kDebugShowMemoryUsage) | //(1 << kDebugShowNops) | //(1 << kDebugCountOpcodes) | + //(1 << kDebugDumpCheckStats) | #if defined(ART_USE_QUICK_COMPILER) //(1 << kDebugDumpBitcodeFile) | //(1 << kDebugVerifyBitcode) | @@ -315,6 +316,7 @@ void oatDumpCFG(CompilationUnit* cUnit, const char* dirPrefix) BasicBlock *bb = (BasicBlock *) oatGrowableListGetElement(blockList, blockIdx); if (bb == NULL) break; + if (bb->blockType == kDead) continue; if (bb->blockType == kEntryBlock) { fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id); } else if (bb->blockType == kExitBlock) { @@ -1049,18 +1051,16 @@ CompiledMethod* oatCompileMethod(Compiler& compiler, } } -#if defined(ART_USE_QUICK_COMPILER) - if (cUnit->genBitcode) { - // Bitcode generation requires full dataflow analysis, no qdMode - cUnit->qdMode = false; - } -#endif - if (cUnit->qdMode) { +#if !defined(ART_USE_QUICK_COMPILER) + // Bitcode generation requires full dataflow analysis cUnit->disableDataflow = true; +#endif // Disable optimization which require dataflow/ssa cUnit->disableOpt |= +#if !defined(ART_USE_QUICK_COMPILER) (1 << kNullCheckElimination) | +#endif (1 << kBBOpt) | (1 << kPromoteRegs); if (cUnit->printMe) { @@ -1104,9 +1104,16 @@ CompiledMethod* oatCompileMethod(Compiler& compiler, /* Perform null check elimination */ oatMethodNullCheckElimination(cUnit.get()); + /* Combine basic blocks where possible */ + oatMethodBasicBlockCombine(cUnit.get()); + /* Do some basic block optimizations */ oatMethodBasicBlockOptimization(cUnit.get()); + if (cUnit->enableDebug & (1 << kDebugDumpCheckStats)) { + oatDumpCheckStats(cUnit.get()); + } + oatInitializeRegAlloc(cUnit.get()); // Needs to happen after SSA naming /* Allocate Registers using simple local allocation scheme */ diff --git a/src/compiler/IntermediateRep.cc b/src/compiler/IntermediateRep.cc index 237f5e0469..031d3d379c 100644 --- a/src/compiler/IntermediateRep.cc +++ b/src/compiler/IntermediateRep.cc @@ -77,6 +77,7 @@ BasicBlock* oatNewBB(CompilationUnit* cUnit, BBType blockType, int blockId) oatInitGrowableList(cUnit, bb->predecessors, (blockType == kExitBlock) ? 2048 : 2, kListPredecessors); + cUnit->blockIdMap.Put(blockId, blockId); return bb; } diff --git a/src/compiler/SSATransformation.cc b/src/compiler/SSATransformation.cc index 10957b2517..f625261b78 100644 --- a/src/compiler/SSATransformation.cc +++ b/src/compiler/SSATransformation.cc @@ -57,18 +57,6 @@ BasicBlock* nextUnvisitedSuccessor(BasicBlock* bb) void markPreOrder(CompilationUnit* cUnit, BasicBlock* block) { block->visited = true; - // Can this block be reached only via previous block fallthrough? - if ((block->blockType == kDalvikByteCode) && - (block->predecessors->numUsed == 1)) { - DCHECK_GE(cUnit->dfsOrder.numUsed, 1U); - int prevIdx = cUnit->dfsOrder.numUsed - 1; - int prevId = cUnit->dfsOrder.elemList[prevIdx]; - BasicBlock* predBB = (BasicBlock*)block->predecessors->elemList[0]; - if (predBB->id == prevId) { - block->fallThroughTarget = true; - } - } - /* Enqueue the preOrder block id */ oatInsertGrowableList(cUnit, &cUnit->dfsOrder, block->id); } @@ -107,9 +95,6 @@ void recursiveRecordDFSOrders(CompilationUnit* cUnit, BasicBlock* block) int prevIdx = cUnit->dfsOrder.numUsed - 1; int prevId = cUnit->dfsOrder.elemList[prevIdx]; BasicBlock* predBB = (BasicBlock*)block->predecessors->elemList[0]; - if (predBB->id == prevId) { - block->fallThroughTarget = true; - } } /* Enqueue the preOrder block id */ diff --git a/src/compiler/codegen/CodegenUtil.cc b/src/compiler/codegen/CodegenUtil.cc index 3aea76a4cc..bdc2c8b207 100644 --- a/src/compiler/codegen/CodegenUtil.cc +++ b/src/compiler/codegen/CodegenUtil.cc @@ -998,5 +998,20 @@ void dumpPackedSwitchTable(const u2* table) } } +/* + * Set up special LIR to mark a Dalvik byte-code instruction start and + * record it in the boundaryMap. NOTE: in cases such as kMirOpCheck in + * which we split a single Dalvik instruction, only the first MIR op + * associated with a Dalvik PC should be entered into the map. + */ +LIR* markBoundary(CompilationUnit* cUnit, int offset, const char* instStr) +{ + LIR* res = newLIR1(cUnit, kPseudoDalvikByteCodeBoundary, (intptr_t) instStr); + if (cUnit->boundaryMap.find(offset) == cUnit->boundaryMap.end()) { + cUnit->boundaryMap.Put(offset, res); + } + return res; +} -} // namespace art +} + // namespace art diff --git a/src/compiler/codegen/MethodBitcode.cc b/src/compiler/codegen/MethodBitcode.cc index cfe6e6a517..8d62750a49 100644 --- a/src/compiler/codegen/MethodBitcode.cc +++ b/src/compiler/codegen/MethodBitcode.cc @@ -1668,8 +1668,11 @@ void convertExtendedMIR(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir, DCHECK_EQ(rlDest.fp, loc.fp); DCHECK_EQ(rlDest.core, loc.core); DCHECK_EQ(rlDest.ref, loc.ref); + SafeMap<unsigned int, unsigned int>::iterator it; + it = cUnit->blockIdMap.find(incoming[i]); + DCHECK(it != cUnit->blockIdMap.end()); phi->addIncoming(getLLVMValue(cUnit, loc.origSReg), - getLLVMBlock(cUnit, incoming[i])); + getLLVMBlock(cUnit, it->second)); } defineValue(cUnit, phi, rlDest.origSReg); break; @@ -1752,6 +1755,7 @@ void setMethodInfo(CompilationUnit* cUnit) /* Handle the content in each basic block */ bool methodBlockBitcodeConversion(CompilationUnit* cUnit, BasicBlock* bb) { + if (bb->blockType == kDead) return false; llvm::BasicBlock* llvmBB = getLLVMBlock(cUnit, bb->id); cUnit->irb->SetInsertPoint(llvmBB); setDexOffset(cUnit, bb->startOffset); @@ -1964,7 +1968,7 @@ bool createFunction(CompilationUnit* cUnit) { bool createLLVMBasicBlock(CompilationUnit* cUnit, BasicBlock* bb) { // Skip the exit block - if (bb->blockType == kExitBlock) { + if ((bb->blockType == kDead) ||(bb->blockType == kExitBlock)) { cUnit->idToBlockMap.Put(bb->id, NULL); } else { int offset = bb->startOffset; @@ -2924,11 +2928,8 @@ bool methodBitcodeBlockCodeGen(CompilationUnit* cUnit, llvm::BasicBlock* bb) cUnit->liveSReg = INVALID_SREG; #endif - LIR* boundaryLIR; - const char* instStr = "boundary"; - boundaryLIR = newLIR1(cUnit, kPseudoDalvikByteCodeBoundary, - (intptr_t) instStr); - cUnit->boundaryMap.Overwrite(cUnit->currentDalvikOffset, boundaryLIR); + // TODO: use llvm opcode name here instead of "boundary" if verbose + LIR* boundaryLIR = markBoundary(cUnit, cUnit->currentDalvikOffset, "boundary"); /* Remember the first LIR for thisl block*/ if (headLIR == NULL) { diff --git a/src/compiler/codegen/MethodCodegenDriver.cc b/src/compiler/codegen/MethodCodegenDriver.cc index b4e14ce52a..e76834d7c6 100644 --- a/src/compiler/codegen/MethodCodegenDriver.cc +++ b/src/compiler/codegen/MethodCodegenDriver.cc @@ -881,14 +881,7 @@ bool methodBlockCodeGen(CompilationUnit* cUnit, BasicBlock* bb) oatResetRegPool(cUnit); oatResetDefTracking(cUnit); - /* - * If control reached us from our immediate predecessor via - * fallthrough and we have no other incoming arcs we can - * reuse existing liveness. Otherwise, reset. - */ - if (!bb->fallThroughTarget || bb->predecessors->numUsed != 1) { - oatClobberAllRegs(cUnit); - } + oatClobberAllRegs(cUnit); LIR* headLIR = NULL; @@ -922,9 +915,7 @@ bool methodBlockCodeGen(CompilationUnit* cUnit, BasicBlock* bb) /* Mark the beginning of a Dalvik instruction for line tracking */ char* instStr = cUnit->printMe ? oatGetDalvikDisassembly(cUnit, mir->dalvikInsn, "") : NULL; - boundaryLIR = newLIR1(cUnit, kPseudoDalvikByteCodeBoundary, - (intptr_t) instStr); - cUnit->boundaryMap.Overwrite(mir->offset, boundaryLIR); + boundaryLIR = markBoundary(cUnit, mir->offset, instStr); /* Remember the first LIR for this block */ if (headLIR == NULL) { headLIR = boundaryLIR; diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc index d2532b7a47..925bbf2151 100644 --- a/src/compiler/codegen/arm/Thumb2/Gen.cc +++ b/src/compiler/codegen/arm/Thumb2/Gen.cc @@ -127,13 +127,10 @@ MIR* getNextMir(CompilationUnit* cUnit, BasicBlock** pBb, MIR* mir) /* Used for the "printMe" listing */ void genPrintLabel(CompilationUnit *cUnit, MIR* mir) { - LIR* boundaryLIR; /* Mark the beginning of a Dalvik instruction for line tracking */ char* instStr = cUnit->printMe ? oatGetDalvikDisassembly(cUnit, mir->dalvikInsn, "") : NULL; - boundaryLIR = newLIR1(cUnit, kPseudoDalvikByteCodeBoundary, - (intptr_t) instStr); - cUnit->boundaryMap.Put(mir->offset, boundaryLIR); + markBoundary(cUnit, mir->offset, instStr); /* Don't generate the SSA annotation unless verbose mode is on */ if (cUnit->printMe && mir->ssaRep) { char* ssaString = oatGetSSAString(cUnit, mir->ssaRep); diff --git a/src/safe_map.h b/src/safe_map.h index 62a0415945..4001aaf31c 100644 --- a/src/safe_map.h +++ b/src/safe_map.h @@ -75,7 +75,11 @@ class SafeMap { // of this container is a pointer, any overwritten pointer will be lost and if this container // was the owner, you have a leak. void Overwrite(const K& k, const V& v) { - map_.insert(std::make_pair(k, v)); + std::pair<iterator, bool> result = map_.insert(std::make_pair(k, v)); + if (!result.second) { + // Already there - update the value for the existing key + result.first->second = v; + } } bool Equals(const Self& rhs) const { |