TR_DominatorVerifier::TR_DominatorVerifier(TR_Dominators &findDominators) : _compilation(findDominators.comp()) { TR::StackMemoryRegion stackMemoryRegion(*trMemory()); _dominators = &findDominators; TR::CFG *cfg = comp()->getFlowGraph(); _visitCount = comp()->incVisitCount(); _numBlocks = cfg->getNumberOfNodes()+1; if (debug("traceVER")) { dumpOptDetails(comp(), "Printing out the TreeTops from DominatorVerifier\n"); TR::TreeTop *currentTree = comp()->getStartTree(); while (!(currentTree == NULL)) { comp()->getDebug()->print(comp()->getOutFile(), currentTree); currentTree = currentTree->getNextTreeTop(); } dumpOptDetails(comp(), "Printing out the CFG from DominatorVerifier\n"); if (cfg != NULL) comp()->getDebug()->print(comp()->getOutFile(), cfg); } TR_DominatorsChk expensiveAlgorithm(comp()); expensiveAlgorithmCorrect = isExpensiveAlgorithmCorrect(expensiveAlgorithm); if (expensiveAlgorithmCorrect) { if (debug("traceVER")) dumpOptDetails(comp(), "Dominators computed by the expensive algorithm are correct\n"); } else { if (debug("traceVER")) dumpOptDetails(comp(), "Dominators computed by the expensive algorithm are NOT correct\n"); TR_ASSERT(0, "Dominators computed by the expensive algorithm are NOT correct\n"); } bothImplementationsConsistent = areBothImplementationsConsistent(expensiveAlgorithm, findDominators); if (bothImplementationsConsistent) { if (debug("traceVER")) dumpOptDetails(comp(), "Dominators computed by the two implementations are consistent\n"); } else { if (debug("traceVER")) dumpOptDetails(comp(), "Dominators computed by the two implementations are NOT consistent\n"); TR_ASSERT(0, "Dominators computed by the two implementations are NOT consistent\n"); } }
void TR_ReachabilityAnalysis::perform(TR_BitVector *result) { TR::CFG *cfg = comp()->getFlowGraph(); int32_t numBlockIndexes = cfg->getNextNodeNumber(); int32_t numBlocks = cfg->getNumberOfNodes(); _blocks = cfg->createArrayOfBlocks(); blocknum_t *stack = (blocknum_t*)comp()->trMemory()->allocateStackMemory(numBlockIndexes * sizeof(stack[0])); blocknum_t *depthMap = (blocknum_t*)comp()->trMemory()->allocateStackMemory(numBlockIndexes * sizeof(depthMap[0])); memset(depthMap, 0, numBlockIndexes * sizeof(depthMap[0])); bool trace = comp()->getOption(TR_TraceReachability); if (trace) traceMsg(comp(), "BEGIN REACHABILITY: %d blocks\n", numBlocks); for (TR::Block *block = comp()->getStartBlock(); block; block = block->getNextBlock()) { blocknum_t blockNum = block->getNumber(); if (trace) traceMsg(comp(), "Visit block_%d\n", blockNum); if (depthMap[blockNum] == 0) traverse(blockNum, 0, stack, depthMap, result); else traceMsg(comp(), " depth is already %d; skip\n", depthMap[blockNum]); } if (comp()->getOption(TR_TraceReachability)) { traceMsg(comp(), "END REACHABILITY. Result:\n"); result->print(comp(), comp()->getOutFile()); traceMsg(comp(), "\n"); } }
int32_t TR_CatchBlockRemover::perform() { TR::CFG *cfg = comp()->getFlowGraph(); if (cfg == NULL) { if (trace()) traceMsg(comp(), "Can't do Catch Block Removal, no CFG\n"); return 0; } if (trace()) traceMsg(comp(), "Starting Catch Block Removal\n"); bool thereMayBeRemovableCatchBlocks = false; { TR::StackMemoryRegion stackMemoryRegion(*trMemory()); TR::Block *block; ListIterator<TR::CFGEdge> edgeIterator; // Go through all blocks that have exception successors and see if any of them // are not reached. Mark each of these edges with a visit count so they can // be identified later. // vcount_t visitCount = comp()->incOrResetVisitCount(); TR::CFGNode *cfgNode; for (cfgNode = cfg->getFirstNode(); cfgNode; cfgNode = cfgNode->getNext()) { if (cfgNode->getExceptionSuccessors().empty()) continue; block = toBlock(cfgNode); uint32_t reachedExceptions = 0; TR::TreeTop *treeTop; for (treeTop = block->getEntry(); treeTop != block->getExit(); treeTop = treeTop->getNextTreeTop()) { reachedExceptions |= treeTop->getNode()->exceptionsRaised(); if (treeTop->getNode()->getOpCodeValue() == TR::monexitfence) // for live monitor metadata reachedExceptions |= TR::Block::CanCatchMonitorExit; } if (reachedExceptions & TR::Block::CanCatchUserThrows) continue; for (auto edge = block->getExceptionSuccessors().begin(); edge != block->getExceptionSuccessors().end();) { TR::CFGEdge * current = *(edge++); TR::Block *catchBlock = toBlock(current->getTo()); if (catchBlock->isOSRCodeBlock() || catchBlock->isOSRCatchBlock()) continue; if (!reachedExceptions && performTransformation(comp(), "%sRemove redundant exception edge from block_%d at [%p] to catch block_%d at [%p]\n", optDetailString(), block->getNumber(), block, catchBlock->getNumber(), catchBlock)) { cfg->removeEdge(block, catchBlock); thereMayBeRemovableCatchBlocks = true; } else { if (!catchBlock->canCatchExceptions(reachedExceptions)) { current->setVisitCount(visitCount); thereMayBeRemovableCatchBlocks = true; } } } } bool edgesRemoved = false; // Now look to see if there are any catch blocks for which all exception // predecessors have the visit count set. If so, the block is unreachable and // can be removed. // If only some of the exception predecessors are marked, these edges are // left in place to identify the try/catch structure properly. // while (thereMayBeRemovableCatchBlocks) { thereMayBeRemovableCatchBlocks = false; for (cfgNode = cfg->getFirstNode(); cfgNode; cfgNode = cfgNode->getNext()) { if (cfgNode->getExceptionPredecessors().empty()) continue; auto edgeIt = cfgNode->getExceptionPredecessors().begin(); for (; edgeIt != cfgNode->getExceptionPredecessors().end(); ++edgeIt) { if ((*edgeIt)->getVisitCount() != visitCount) break; } if (edgeIt == cfgNode->getExceptionPredecessors().end() && performTransformation(comp(), "%sRemove redundant catch block_%d at [%p]\n", optDetailString(), cfgNode->getNumber(), cfgNode)) { while (!cfgNode->getExceptionPredecessors().empty()) { cfg->removeEdge(cfgNode->getExceptionPredecessors().front()); } edgesRemoved = true; thereMayBeRemovableCatchBlocks = true; } } } // Any transformations invalidate use/def and value number information // if (edgesRemoved) { optimizer()->setUseDefInfo(NULL); optimizer()->setValueNumberInfo(NULL); requestOpt(OMR::treeSimplification, true); } } // scope of the stack memory region if (trace()) traceMsg(comp(), "\nEnding Catch Block Removal\n"); return 1; // actual cost }
// This function splits a single succeesor block following an guard and is used to // do the following transform // block - cold1 block - cold1 // \ / => | | // nextBlock nextBlock nextBlock' (called tailSplitBlock below) // | \ / // ... ... void TR_VirtualGuardHeadMerger::tailSplitBlock(TR::Block * block, TR::Block * cold1) { TR::CFG *cfg = comp()->getFlowGraph(); cfg->setStructure(NULL); TR_BlockCloner cloner(cfg); TR::Block *tailSplitBlock = cloner.cloneBlocks(block->getNextBlock(), block->getNextBlock()); tailSplitBlock->setFrequency(cold1->getFrequency()); if (cold1->isCold()) tailSplitBlock->setIsCold(); // physically put the block after cold1 since we want cold1 to fall through tailSplitBlock->getExit()->join(cold1->getExit()->getNextTreeTop()); cold1->getExit()->join(tailSplitBlock->getEntry()); // remove cold1's goto TR::TransformUtil::removeTree(comp(), cold1->getExit()->getPrevRealTreeTop()); // copy the exception edges for (auto e = block->getNextBlock()->getExceptionSuccessors().begin(); e != block->getNextBlock()->getExceptionSuccessors().end(); ++e) cfg->addExceptionEdge(tailSplitBlock, (*e)->getTo()); cfg->addEdge(cold1, tailSplitBlock); // lastly fix up the exit of tailSplitBlock TR::Node *tailSplitEnd = tailSplitBlock->getExit()->getPrevRealTreeTop()->getNode(); if (tailSplitEnd->getOpCode().isGoto()) { tailSplitEnd->setBranchDestination(block->getNextBlock()->getLastRealTreeTop()->getNode()->getBranchDestination()); cfg->addEdge(tailSplitBlock, block->getNextBlock()->getSuccessors().front()->getTo()); } else if (tailSplitEnd->getOpCode().isBranch()) { TR::Block *gotoBlock = TR::Block::createEmptyBlock(tailSplitEnd, comp(), cold1->getFrequency()); if (cold1->isCold()) gotoBlock->setIsCold(true); gotoBlock->getExit()->join(tailSplitBlock->getExit()->getNextTreeTop()); tailSplitBlock->getExit()->join(gotoBlock->getEntry()); cfg->addNode(gotoBlock); gotoBlock->append(TR::TreeTop::create(comp(), TR::Node::create(tailSplitEnd, TR::Goto, 0, block->getNextBlock()->getExit()->getNextTreeTop()))); cfg->addEdge(tailSplitBlock, gotoBlock); cfg->addEdge(tailSplitBlock, tailSplitBlock->getLastRealTreeTop()->getNode()->getBranchDestination()->getEnclosingBlock()); cfg->addEdge(gotoBlock, block->getNextBlock()->getNextBlock()); } else if ( !tailSplitEnd->getOpCode().isReturn() && !tailSplitEnd->getOpCode().isJumpWithMultipleTargets() && tailSplitEnd->getOpCodeValue() != TR::athrow && !(tailSplitEnd->getNumChildren() >= 1 && tailSplitEnd->getFirstChild()->getOpCodeValue() == TR::athrow) ) { tailSplitBlock->append(TR::TreeTop::create(comp(), TR::Node::create(tailSplitEnd, TR::Goto, 0, block->getNextBlock()->getExit()->getNextTreeTop()))); cfg->addEdge(tailSplitBlock, block->getNextBlock()->getNextBlock()); } else { for (auto e = block->getNextBlock()->getSuccessors().begin(); e != block->getNextBlock()->getSuccessors().end(); ++e) cfg->addEdge(tailSplitBlock, (*e)->getTo()); } cfg->removeEdge(cold1, block->getNextBlock()); optimizer()->setUseDefInfo(NULL); optimizer()->setValueNumberInfo(NULL); }
// This opt tries to reduce merge backs from cold code that are the result of inliner // gnerated nopable virtual guards // It looks for one basic pattern // // guard1 -> cold1 // BBEND // BBSTART // guard2 -> cold2 // if guard1 is the guard for a method which calls the method guard2 protects or cold1 is // a predecessor of cold2 (a situation commonly greated by virtual guard tail splitter) we // can transform the guards as follows when guard1 and guard2 a // guard1 -> cold1 // BBEND // BBSTART // guard2 -> cold1 // This is safe because there are no trees between the guards and calling the caller will // result in the call to the callee if we need to patch guard2. cold2 and its mergebacks // can then be eliminated // // In addition this opt will try to move guard2 up from the end of a block to the // start of the block. We can do this if guard2 is an HCR guard and there is no GC point // between BBSTART and guard2 since HCR is a stop-the-world event. // // Finally, there is a simple tail splitting step run before the analysis of a guard if we // detect that the taken side of the guard merges back in the next block - this happens // for some empty methods and is common for Object.<init> at the top of constructors. int32_t TR_VirtualGuardHeadMerger::perform() { static char *disableVGHeadMergerTailSplitting = feGetEnv("TR_DisableVGHeadMergerTailSplitting"); TR::CFG *cfg = comp()->getFlowGraph(); // Cache the loads for the outer guard's cold path TR_BitVector coldPathLoads(comp()->trMemory()->currentStackRegion()); TR_BitVector privArgSymRefs(comp()->trMemory()->currentStackRegion()); bool evaluatedColdPathLoads = false; for (TR::Block *block = optimizer()->getMethodSymbol()->getFirstTreeTop()->getNode()->getBlock(); block; block = block->getNextBlock()) { TR::Node *guard1 = block->getLastRealTreeTop()->getNode(); if (isMergeableGuard(guard1)) { if (trace()) traceMsg(comp(), "Found mergeable guard in block_%d\n", block->getNumber()); TR::Block *cold1 = guard1->getBranchDestination()->getEnclosingBlock(); // check for an immediate merge back from the cold block and // tail split one block if we can - we only handle splitting a block // ending in a fallthrough, a branch or a goto for now for simplicity if (!disableVGHeadMergerTailSplitting && (cold1->getSuccessors().size() == 1) && cold1->hasSuccessor(block->getNextBlock()) && cold1->getLastRealTreeTop()->getNode()->getOpCode().isGoto()) { // TODO handle moving code earlier in the block down below the guard // tail split if ((block->getNextBlock()->getSuccessors().size() == 1) || ((block->getNextBlock()->getSuccessors().size() == 2) && block->getNextBlock()->getLastRealTreeTop()->getNode()->getOpCode().isBranch()) && performTransformation(comp(), "%sCloning block_%d and placing clone after block_%d to reduce HCR guard nops\n", OPT_DETAILS, block->getNextBlock()->getNumber(), cold1->getNumber())) tailSplitBlock(block, cold1); } // guard motion is fairly complex but what we want to achieve around guard1 is a sequence // of relocated privarg blocks, followed by a sequence of runtime patchable guards going to // guard1's cold block, followed by a sequence of stop-the-world guards going to guard1's // cold block // // The following code is to setup the various insert points based on the following diagrams // of basic blocks: // // start: setup: end result after moving runtime guard' // | | +-------+ <-- privargIns // | | <-- privargIns | // +-------+ <-- runtimeIns +-------+ // | | | | Guard'| // | | V +-------+ <-- runtimeIns // +-------+ +-------+ | // | Guard | | Guard | V // +-------+ +-------+ <-- HCRIns +-------+ // | ===> | ===> | Guard | // V V +-------+ <-- HCRIns // +-------+ +-------+ | // | | | | V // | | | | +-------+ // // Note we always split the block - this may create an empty block but preserves the incoming // control flow we leave the rest to block extension to fix later block = block->split(block->getLastRealTreeTop(), cfg, true, false); TR::Block *privargIns = block->getPrevBlock(); TR::Block *runtimeIns = block->getPrevBlock(); TR::Block *HCRIns = block; // New outer guard so cold paths must be evaluated evaluatedColdPathLoads = false; // scan for candidate guards to merge with guard1 identified above for (TR::Block *nextBlock = block->getNextBlock(); nextBlock; nextBlock = nextBlock->getNextBlock()) { if (!(nextBlock->getPredecessors().size() == 1) || !nextBlock->hasPredecessor(block)) { break; } TR::TreeTop *guard2Tree = NULL; if (isMergeableGuard(nextBlock->getFirstRealTreeTop()->getNode())) { guard2Tree = nextBlock->getFirstRealTreeTop(); } else if (isMergeableGuard(nextBlock->getLastRealTreeTop()->getNode())) { guard2Tree = nextBlock->getLastRealTreeTop(); } else break; TR::Node *guard2 = guard2Tree->getNode(); TR::Block *guard2Block = nextBlock; // It is not possible to shift an OSR guard unless the destination is already an OSR point // as the necessary OSR state will not be available if (guard2->isOSRGuard() && !guard1->isOSRGuard()) break; TR::Block *insertPoint = isStopTheWorldGuard(guard2) ? HCRIns : runtimeIns; if (!safeToMoveGuard(insertPoint, guard2Tree, guard1->getBranchDestination(), privArgSymRefs)) break; // now we figure out if we can redirect guard2 to guard1's cold block // ie can we do the head merge TR::Block *cold2 = guard2->getBranchDestination()->getEnclosingBlock(); if (guard1->getInlinedSiteIndex() == guard2->getInlinedSiteIndex()) { if (trace()) traceMsg(comp(), " Guard1 [%p] is guarding the same call as Guard2 [%p] - proceeding with guard merging\n", guard1, guard2); } else if (guard2->getInlinedSiteIndex() > -1 && guard1->getInlinedSiteIndex() == TR::comp()->getInlinedCallSite(guard2->getInlinedSiteIndex())._byteCodeInfo.getCallerIndex()) { if (trace()) traceMsg(comp(), " Guard1 [%p] is the caller of Guard2 [%p] - proceeding with guard merging\n", guard1, guard2); } else if ((cold1->getSuccessors().size() == 1) && cold1->hasSuccessor(cold2)) { if (trace()) traceMsg(comp(), " Guard1 cold destination block_%d has guard2 cold destination block_%d as its only successor - proceeding with guard merging\n", cold1->getNumber(), cold2->getNumber()); } else { if (trace()) traceMsg(comp(), " Cold1 block_%d and cold2 block_%d of guard2 [%p] in unknown relationship - abandon the merge attempt\n", cold1->getNumber(), cold2->getNumber(), guard2); break; } // Runtime guards will shift their privargs, so it is necessary to check such a move is safe // This is possible if a privarg temp was recycled for the inner call site, with a prior use as an // argument for the outer call site. As the privargs for the inner call site must be evaluated before // both guards, this would result in the recycled temp holding the incorrect value if the guard is ever // taken. if (!isStopTheWorldGuard(guard2)) { if (!evaluatedColdPathLoads) { collectColdPathLoads(cold1, coldPathLoads); evaluatedColdPathLoads = true; } if (coldPathLoads.intersects(privArgSymRefs)) { if (trace()) traceMsg(comp(), " Recycled temp live in cold1 block_%d and used as privarg before guard2 [%p] - stop guard merging", cold1->getNumber(), guard2); break; } } if (!performTransformation(comp(), "%sRedirecting %s guard [%p] in block_%d to parent guard cold block_%d\n", OPT_DETAILS, isStopTheWorldGuard(guard2) ? "stop the world" : "runtime", guard2, guard2Block->getNumber(), cold1->getNumber())) continue; if (guard2->getBranchDestination() != guard1->getBranchDestination()) guard2Block->changeBranchDestination(guard1->getBranchDestination(), cfg); if (guard2Tree != guard2Block->getFirstRealTreeTop()) { cfg->setStructure(NULL); // We should leave code ahead of an HCR guard in place because: // 1, it might have side effect to runtime guards after it, moving it up might cause us to falsely merge // the subsequent runtime guards // 2, it might contain live monitor, moving it up above a guard can affect the monitor's live range if (!isStopTheWorldGuard(guard2)) { // the block created above guard2 contains only privarg treetops or monitor stores if // guard2 is a runtime-patchable guard and is safe to merge. We need to move the priv // args up to the runtime insert point and leave the monitor stores in place // It's safe to do so because there is no data dependency between the monitor store and // the priv arg store, because the priv arg store does not load the value from the temp // holding the monitored object // Split priv arg stores from monitor stores // Monitor store is generated for the caller of the method guard2 protects, so should appear before // priv arg stores for the method guard2 protects TR::Block *privargBlock = guard2Block; guard2Block = splitRuntimeGuardBlock(comp(), guard2Block, cfg); if (privargBlock != guard2Block) { if (trace()) traceMsg(comp(), " Moving privarg block_%d after block_%d\n", privargBlock->getNumber(), privargIns->getNumber()); moveBlockAfterDest(cfg, privargBlock, privargIns); if (HCRIns == privargIns) HCRIns = privargBlock; if (runtimeIns == privargIns) runtimeIns = privargBlock; privargIns = privargBlock; // refresh the insertPoint since it could be stale after the above updates insertPoint = runtimeIns; } } guard2Block = guard2Block->split(guard2Tree, cfg, true, false); if (trace()) traceMsg(comp(), " Created new block_%d to hold guard [%p] from block_%d\n", guard2Block->getNumber(), guard2, guard2Block->getNumber()); } if (insertPoint != guard2Block->getPrevBlock()) { TR::DebugCounter::incStaticDebugCounter(comp(), TR::DebugCounter::debugCounterName(comp(), "headMerger/%s_%s/(%s)", isStopTheWorldGuard(guard1) ? "stop the world" : "runtime", isStopTheWorldGuard(guard2) ? "stop the world" : "runtime", comp()->signature())); cfg->setStructure(NULL); block = nextBlock = guard2Block->getPrevBlock(); if (trace()) traceMsg(comp(), " Moving guard2 block block_%d after block_%d\n", guard2Block->getNumber(), insertPoint->getNumber()); moveBlockAfterDest(cfg, guard2Block, insertPoint); if (HCRIns == insertPoint) HCRIns = guard2Block; if (runtimeIns == insertPoint) runtimeIns = guard2Block; } else { block = guard2Block; } guard1 = guard2; } } } return 1; }
TR_Latestness::TR_Latestness(TR::Compilation *comp, TR::Optimizer *optimizer, TR_Structure *rootStructure, bool trace) : TR_BackwardIntersectionBitVectorAnalysis(comp, comp->getFlowGraph(), optimizer, trace) { _delayedness = new (comp->allocator()) TR_Delayedness(comp, optimizer, rootStructure, trace); _supportedNodesAsArray = _delayedness->_supportedNodesAsArray; if (trace) traceMsg(comp, "Starting Latestness\n"); TR::CFG *cfg = comp->getFlowGraph(); _numberOfNodes = cfg->getNextNodeNumber(); TR_ASSERT(_numberOfNodes > 0, "Latestness, node numbers not assigned"); _numberOfBits = getNumberOfBits(); _inSetInfo = (ContainerType **)trMemory()->allocateStackMemory(_numberOfNodes*sizeof(ContainerType *)); for (int32_t i=0;i<_numberOfNodes;i++) allocateContainer(_inSetInfo+i); // Allocate temp bit vectors from block info, since it is local to this analysis ContainerType *intersection, *negation; allocateBlockInfoContainer(&intersection); allocateBlockInfoContainer(&negation); TR::CFGNode *nextNode; for (nextNode = cfg->getFirstNode(); nextNode; nextNode = nextNode->getNext()) { TR_BlockStructure *blockStructure = (toBlock(nextNode))->getStructureOf(); if ((blockStructure == NULL) || (blockStructure->getBlock()->getSuccessors().empty() && blockStructure->getBlock()->getExceptionSuccessors().empty())) continue; /////analyzeTreeTopsInBlockStructure(blockStructure); /////analysisInfo->_containsExceptionTreeTop = _containsExceptionTreeTop; initializeInfo(intersection); for (auto succ = nextNode->getSuccessors().begin(); succ != nextNode->getSuccessors().end(); ++succ) { TR::CFGNode *succBlock = (*succ)->getTo(); compose(intersection, _delayedness->_inSetInfo[succBlock->getNumber()]); } /////if (getAnalysisInfo(blockStructure)->_containsExceptionTreeTop) { for (auto succ = nextNode->getExceptionSuccessors().begin(); succ != nextNode->getExceptionSuccessors().end(); ++succ) { TR::CFGNode *succBlock = (*succ)->getTo(); compose(intersection, _delayedness->_inSetInfo[succBlock->getNumber()]); } } negation->setAll(_numberOfBits); *negation -= *intersection; copyFromInto(negation, _inSetInfo[blockStructure->getNumber()]); *(_inSetInfo[blockStructure->getNumber()]) |= *(_delayedness->_earliestness->_globalAnticipatability->_localAnticipatability.getDownwardExposedAnalysisInfo(blockStructure->getBlock()->getNumber())); *(_inSetInfo[blockStructure->getNumber()]) &= *(_delayedness->_inSetInfo[blockStructure->getNumber()]); if (trace) { traceMsg(comp, "\nIn Set of Block : %d\n", blockStructure->getNumber()); _inSetInfo[blockStructure->getNumber()]->print(comp); } } if (trace) traceMsg(comp, "\nEnding Latestness\n"); // Null out info that will not be used by callers _delayedness->_inSetInfo = NULL; _blockAnalysisInfo = NULL; }
TR_LocalAnalysisInfo::TR_LocalAnalysisInfo(TR::Compilation *c, bool t) : _compilation(c), _trace(t), _trMemory(c->trMemory()) { _numNodes = -1; #if 0 // somehow stops PRE from happening // We are going to increment visit count for every tree so can reach max // for big methods quickly. Perhaps can improve containsCall() in the future. comp()->resetVisitCounts(0); #endif if (comp()->getVisitCount() > HIGH_VISIT_COUNT) { _compilation->resetVisitCounts(1); dumpOptDetails(comp(), "\nResetting visit counts for this method before LocalAnalysisInfo\n"); } TR::CFG *cfg = comp()->getFlowGraph(); _numBlocks = cfg->getNextNodeNumber(); TR_ASSERT(_numBlocks > 0, "Local analysis, node numbers not assigned"); // Allocate information on the stack. It is the responsibility of the user // of this class to determine the life of the information by using jitStackMark // and jitStackRelease. // //_blocksInfo = (TR::Block **) trMemory()->allocateStackMemory(_numBlocks*sizeof(TR::Block *)); //memset(_blocksInfo, 0, _numBlocks*sizeof(TR::Block *)); TR::TreeTop *currentTree = comp()->getStartTree(); // Only do this if not done before; typically this would be done in the // first call to this method through LocalTransparency and would NOT // need to be re-done by LocalAnticipatability. // if (_numNodes < 0) { _optimizer = comp()->getOptimizer(); int32_t numBuckets; int32_t numNodes = comp()->getNodeCount(); if (numNodes < 10) numBuckets = 1; else if (numNodes < 100) numBuckets = 7; else if (numNodes < 500) numBuckets = 31; else if (numNodes < 3000) numBuckets = 127; else if (numNodes < 6000) numBuckets = 511; else numBuckets = 1023; // Allocate hash table for matching expressions // HashTable hashTable(numBuckets, comp()); _hashTable = &hashTable; // Null checks are handled differently as the criterion for // commoning a null check is different than that used for // other nodes; for a null check, the null check reference is // important (and not the actual indirect access itself) // _numNullChecks = 0; while (currentTree) { if (currentTree->getNode()->getOpCodeValue() == TR::NULLCHK) //////if (currentTree->getNode()->getOpCode().isNullCheck()) _numNullChecks++; currentTree = currentTree->getNextTreeTop(); } if (_numNullChecks == 0) _nullCheckNodesAsArray = NULL; else { _nullCheckNodesAsArray = (TR::Node**)trMemory()->allocateStackMemory(_numNullChecks*sizeof(TR::Node*)); memset(_nullCheckNodesAsArray, 0, _numNullChecks*sizeof(TR::Node*)); } currentTree = comp()->getStartTree(); int32_t symRefCount = comp()->getSymRefCount(); _checkSymbolReferences = new (trStackMemory()) TR_BitVector(symRefCount, trMemory(), stackAlloc); _numNodes = 1; _numNullChecks = 0; // This loop counts all the nodes that are going to take part in PRE. // This is a computation intensive loop as we check if the node that // is syntactically equivalent to a given node has been seen before // and if so we use the local index of the original node (that // is syntactically equivalent to the given node). Could be improved // in complexity with value numbering at some stage. // _visitCount = comp()->incVisitCount(); while (currentTree) { TR::Node *firstNodeInTree = currentTree->getNode(); TR::ILOpCode *opCode = &firstNodeInTree->getOpCode(); if (((firstNodeInTree->getOpCodeValue() == TR::treetop) || (comp()->useAnchors() && firstNodeInTree->getOpCode().isAnchor())) && (firstNodeInTree->getFirstChild()->getOpCode().isStore())) { firstNodeInTree->setLocalIndex(-1); if (comp()->useAnchors() && firstNodeInTree->getOpCode().isAnchor()) firstNodeInTree->getSecondChild()->setLocalIndex(-1); firstNodeInTree = firstNodeInTree->getFirstChild(); opCode = &firstNodeInTree->getOpCode(); } // This call finds nodes with opcodes that are supported by PRE // in this subtree; this accounts for all opcodes other than stores/checks // which are handled later on below // bool firstNodeInTreeHasCallsInStoreLhs = false; countSupportedNodes(firstNodeInTree, NULL, firstNodeInTreeHasCallsInStoreLhs); if ((opCode->isStore() && !firstNodeInTree->getSymbolReference()->getSymbol()->isAutoOrParm()) || opCode->isCheck()) { int32_t oldExpressionOnRhs = hasOldExpressionOnRhs(firstNodeInTree); // // Return value 0 denotes that the node contains some sub-expression // that cannot participate in PRE; e.g. a call or a new // // Return value -1 denotes that the node can participate in PRE // but did not match with any existing expression seen so far // // Any other return value (should be positive always) denotes that // the node can participate in PRE and has been matched with a seen // expression having local index == return value // if (oldExpressionOnRhs == -1) { if (trace()) { traceMsg(comp(), "\nExpression #%d is : \n", _numNodes); comp()->getDebug()->print(comp()->getOutFile(), firstNodeInTree, 6, true); } firstNodeInTree->setLocalIndex(_numNodes++); } else firstNodeInTree->setLocalIndex(oldExpressionOnRhs); if (opCode->isCheck() && (firstNodeInTree->getFirstChild()->getOpCode().isStore() && !firstNodeInTree->getFirstChild()->getSymbolReference()->getSymbol()->isAutoOrParm())) { int oldExpressionOnRhs = hasOldExpressionOnRhs(firstNodeInTree->getFirstChild()); if (oldExpressionOnRhs == -1) { if (trace()) { traceMsg(comp(), "\nExpression #%d is : \n", _numNodes); comp()->getDebug()->print(comp()->getOutFile(), firstNodeInTree->getFirstChild(), 6, true); } firstNodeInTree->getFirstChild()->setLocalIndex(_numNodes++); } else firstNodeInTree->getFirstChild()->setLocalIndex(oldExpressionOnRhs); } } else firstNodeInTree->setLocalIndex(-1); currentTree = currentTree->getNextTreeTop(); } } _supportedNodesAsArray = (TR::Node**)trMemory()->allocateStackMemory(_numNodes*sizeof(TR::Node*)); memset(_supportedNodesAsArray, 0, _numNodes*sizeof(TR::Node*)); _checkExpressions = new (trStackMemory()) TR_BitVector(_numNodes, trMemory(), stackAlloc); //_checkExpressions.init(_numNodes, trMemory(), stackAlloc); // This loop goes through the trees and collects the nodes // that would take part in PRE. Each node has its local index set to // the bit position that it occupies in the bit vector analyses. // currentTree = comp()->getStartTree(); _visitCount = comp()->incVisitCount(); while (currentTree) { TR::Node *firstNodeInTree = currentTree->getNode(); TR::ILOpCode *opCode = &firstNodeInTree->getOpCode(); if (((firstNodeInTree->getOpCodeValue() == TR::treetop) || (comp()->useAnchors() && firstNodeInTree->getOpCode().isAnchor())) && (firstNodeInTree->getFirstChild()->getOpCode().isStore())) { firstNodeInTree = firstNodeInTree->getFirstChild(); opCode = &firstNodeInTree->getOpCode(); } collectSupportedNodes(firstNodeInTree, NULL); if ((opCode->isStore() && !firstNodeInTree->getSymbolReference()->getSymbol()->isAutoOrParm()) || opCode->isCheck()) { if (opCode->isCheck()) { _checkSymbolReferences->set(firstNodeInTree->getSymbolReference()->getReferenceNumber()); _checkExpressions->set(firstNodeInTree->getLocalIndex()); } if (!_supportedNodesAsArray[firstNodeInTree->getLocalIndex()]) _supportedNodesAsArray[firstNodeInTree->getLocalIndex()] = firstNodeInTree; if (opCode->isCheck() && firstNodeInTree->getFirstChild()->getOpCode().isStore() && !firstNodeInTree->getFirstChild()->getSymbolReference()->getSymbol()->isAutoOrParm() && !_supportedNodesAsArray[firstNodeInTree->getFirstChild()->getLocalIndex()]) _supportedNodesAsArray[firstNodeInTree->getFirstChild()->getLocalIndex()] = firstNodeInTree->getFirstChild(); } currentTree = currentTree->getNextTreeTop(); } //initialize(toBlock(cfg->getStart())); }