From 0a45af15c62ae75905af543c21cce03132f2b728 Mon Sep 17 00:00:00 2001 From: jimmyk Date: Fri, 17 Apr 2020 14:22:33 -0400 Subject: [PATCH] New iterative implementation of findCycle Original recursive findCycle implementation renamed to findCycleDEPRECATED and put behind the TR_UseOldFindCycle environment variable. New iterative implementation of findCycle is now used by default. The new implementation no longer has the stack overflow problem that the original recursive findCycle implementation had when compiling very large methods. Closes: #5220 Signed-off-by: jimmyk --- compiler/optimizer/Structure.cpp | 142 +++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 7 deletions(-) diff --git a/compiler/optimizer/Structure.cpp b/compiler/optimizer/Structure.cpp index c190cc8a08..51a4009682 100644 --- a/compiler/optimizer/Structure.cpp +++ b/compiler/optimizer/Structure.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corp. and others + * Copyright (c) 2000, 2020 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -1166,7 +1166,7 @@ void TR_RegionStructure::addGlobalRegisterCandidateToExits(TR_RegisterCandidate } } -static bool findCycle(TR_StructureSubGraphNode *node, TR_BitVector ®ionNodes, TR_BitVector &nodesSeenOnPath, TR_BitVector &nodesCleared, int32_t entryNode) +static bool findCycleDEPRECATED(TR_StructureSubGraphNode *node, TR_BitVector ®ionNodes, TR_BitVector &nodesSeenOnPath, TR_BitVector &nodesCleared, int32_t entryNode) { if (nodesSeenOnPath.get(node->getNumber())) return true; // An internal cycle found @@ -1180,7 +1180,7 @@ static bool findCycle(TR_StructureSubGraphNode *node, TR_BitVector ®ionNodes, TR_ASSERT((*edge)->getTo()->asStructureSubGraphNode(),"Expecting a CFG node which can be downcast to StructureSubGraphNode"); TR_StructureSubGraphNode *succ = toStructureSubGraphNode((*edge)->getTo()); if (succ->getNumber() != entryNode && regionNodes.get(succ->getNumber()) && - findCycle(succ,regionNodes,nodesSeenOnPath,nodesCleared,entryNode)) + findCycleDEPRECATED(succ,regionNodes,nodesSeenOnPath,nodesCleared,entryNode)) return true; } for (auto edge = node->getExceptionSuccessors().begin(); edge != node->getExceptionSuccessors().end(); ++edge) @@ -1188,7 +1188,7 @@ static bool findCycle(TR_StructureSubGraphNode *node, TR_BitVector ®ionNodes, TR_ASSERT((*edge)->getTo()->asStructureSubGraphNode(),"Expecting a CFG node which can be downcast to StructureSubGraphNode"); TR_StructureSubGraphNode *succ = toStructureSubGraphNode((*edge)->getTo()); if (/* succ->getNumber() != entryNode && */ regionNodes.get(succ->getNumber()) && - findCycle(succ,regionNodes,nodesSeenOnPath,nodesCleared,entryNode)) + findCycleDEPRECATED(succ,regionNodes,nodesSeenOnPath,nodesCleared,entryNode)) return true; } @@ -1197,18 +1197,146 @@ static bool findCycle(TR_StructureSubGraphNode *node, TR_BitVector ®ionNodes, return false; } + /* + * findCycle is a depth first traversal of the graph in search for a cycle. + * Returns true if a cycle is found, otherwise returns false. + */ +static bool findCycle(TR::Compilation *comp, TR_StructureSubGraphNode *origin, TR_BitVector ®ionNodes, int32_t entryNode) + { + TR::StackMemoryRegion memRegion(*(comp->trMemory())); + + int32_t numNodes = comp->getFlowGraph()->getNextNodeNumber(); + + /* + * First element of the pair is the subgraph node being looked at. + * Second element of the pair is a bool that indicates if the node's children have already been processed. + */ + TR::deque, TR::Region&> nodeStack(0, memRegion); + + /* + * nodesDiscovered is a bit vector that tracks the path of subgraph nodes from origin to the node being looked at. + * If a node is set to Discovered and its next child to examine is also set to Discovered, a cycle has been found. + * Processed flag supersedes Discovered flag. If the node is marked as Processed, it counts as being in the Processed state. + */ + TR_BitVector nodesDiscovered(numNodes, memRegion); + + /* + * nodesProcessed is a bit vector that tracks which subgraph nodes have been confirmed to not be part of a cycle. + * If a node is set to Processed, both that node and its children have already been looked at and a cycle was not found. + */ + TR_BitVector nodesProcessed(numNodes, memRegion); + + /* + * Two pairs for each node are added to nodeStack. + * The first one pushed has childrenProcessed set to true and the second one has it set to false. + * Note that this is a stack so the pair where childrenProcessed is false is pushed second but handled first. + * When the node where childrenProcessed is false is being handled, that node's children will be added to nodeStack. + * After all the children have been handled and set to Processed the node on the top of the stack will have childrenProcessed + * set to true which sets the node to being Processed. + */ + nodeStack.push_front(std::make_pair(origin,true)); + nodeStack.push_front(std::make_pair(origin,false)); + + while (!nodeStack.empty()) + { + TR_StructureSubGraphNode *node = nodeStack.front().first; + int32_t nodeNum = node->getNumber(); + bool childrenProcessed = nodeStack.front().second; + nodeStack.pop_front(); + + /* + * If all children are set to processed and a cycle was not found, the current node is also not part of a cycle + * and should be set to processed as well. + */ + if (childrenProcessed) + { + nodesProcessed.set(nodeNum); + continue; + } + + /* + * If the current node is set to processed than it is already known to NOT be part of a cycle. + */ + if (nodesProcessed.get(nodeNum)) + { + continue; + } + + /* + * If the current node is set to Discovered then that means it was seen before as part of the path between origin and the current + * node. This means a cycle has been found. findCycle can end immediately and return true. + * + * If the current node is not set to Discovered, set it to Discovered to indicate it is now part of the path back to origin. + */ + if (nodesDiscovered.get(nodeNum)) + { + return true; + } + else + { + nodesDiscovered.set(nodeNum); + } + + /* + * Iterate over the children nodes and add them to nodeStack. + * Similar to before, two pairs for each node are added to nodeStack. + * The first one pushed has childrenProcessed set to true and the second one has it set to false. + * This is a stack so the pair where childrenProcessed is false is pushed second but handled first. + */ + for (auto edge = node->getExceptionSuccessors().begin(); edge != node->getExceptionSuccessors().end(); ++edge) + { + TR_StructureSubGraphNode *succ = toStructureSubGraphNode((*edge)->getTo()); + int32_t succNumber = succ->getNumber(); + + if (regionNodes.get(succNumber)) + { + nodeStack.push_front(std::make_pair(succ, true)); + nodeStack.push_front(std::make_pair(succ, false)); + } + } + + for (auto edge = node->getSuccessors().begin(); edge != node->getSuccessors().end(); ++edge) + { + TR_StructureSubGraphNode *succ = toStructureSubGraphNode((*edge)->getTo()); + int32_t succNumber = succ->getNumber(); + + if (succNumber != entryNode && regionNodes.get(succNumber)) + { + nodeStack.push_front(std::make_pair(succ, true)); + nodeStack.push_front(std::make_pair(succ, false)); + } + } + } + + return false; + } + + void TR_RegionStructure::checkForInternalCycles() { TR::StackMemoryRegion stackMemoryRegion(*trMemory()); int32_t numNodes = comp()->getFlowGraph()->getNextNodeNumber(); - TR_BitVector nodesSeenOnPath(numNodes, stackMemoryRegion); - TR_BitVector nodesCleared(numNodes, stackMemoryRegion); TR_BitVector regionNodes(numNodes, stackMemoryRegion); for (auto itr = _subNodes.begin(), end = _subNodes.end(); itr != end; ++itr) regionNodes.set((*itr)->getNumber()); - setContainsInternalCycles(findCycle(getEntry(), regionNodes, nodesSeenOnPath, nodesCleared, getNumber())); + /* + * findCycleDEPRECATED is the old recursive implentation while findCycle is the new iterative implementation. + * findCycleDEPRECATED is still available via the TR_UseOldFindCycle envvar but will be removed in a future release. + */ + static bool useOldFindCycle = (feGetEnv("TR_UseOldFindCycle") != NULL); + + if (!useOldFindCycle) + { + setContainsInternalCycles(findCycle(comp(), getEntry(), regionNodes, getNumber())); + } + else + { + TR_BitVector nodesSeenOnPath(numNodes, stackMemoryRegion); + TR_BitVector nodesCleared(numNodes, stackMemoryRegion); + setContainsInternalCycles(findCycleDEPRECATED(getEntry(), regionNodes, nodesSeenOnPath, nodesCleared, getNumber())); + } } bool TR_RegionStructure::hasExceptionOutEdges()