Skip to content

Commit

Permalink
Rewrite recursive cfg traversal to non-recursive
Browse files Browse the repository at this point in the history
  • Loading branch information
dberlin committed Feb 7, 2019
1 parent 5fb7c9f commit 09c7bbe
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 30 deletions.
3 changes: 2 additions & 1 deletion include/retdec/llvmir2hll/graphs/cfg/cfg_traversal.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ class CFGTraversal: private retdec::utils::NonCopyable {
private:
bool performTraversalImpl(ShPtr<CFG::Node> startNode,
CFG::stmt_iterator startStmtIter);
bool visitSingleNode(CFG::stmt_iterator startStmtIter,
CFG::stmt_iterator endStmtIter);
bool performReverseTraversalImpl(ShPtr<CFG::Node> startNode,
CFG::stmt_reverse_iterator startStmtRIter);
bool traverseNodeSuccessors(ShPtr<CFG::Node> node);
bool traverseNodePredecessors(ShPtr<CFG::Node> node);
};

Expand Down
114 changes: 85 additions & 29 deletions src/llvmir2hll/graphs/cfg/cfg_traversal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* @copyright (c) 2017 Avast Software, licensed under the MIT license
*/

#include <stack>
#include <tuple>
#include "retdec/llvmir2hll/graphs/cfg/cfg_traversal.h"
#include "retdec/llvmir2hll/ir/empty_stmt.h"
#include "retdec/llvmir2hll/support/debug.h"
Expand Down Expand Up @@ -80,13 +82,24 @@ bool CFGTraversal::performTraversalFromSuccessors(ShPtr<Statement> stmt) {
ASSERT_MSG(stmtInNode.first,
"the statement `" << stmt << "` is not in the CFG");

auto retVal = getEndRetVal();
if (stmtInNode.second != stmtInNode.first->stmt_end()) {
// It has a successor in the same node, so start traversing from the
// successor.
return performTraversalImpl(stmtInNode.first, ++stmtInNode.second);
} else {
auto node = stmtInNode.first;
// It is the last statement in the node, so traverse all node successors.
// For each outgoing edge...
for (auto i = node->succ_begin(), e = node->succ_end(); i != e; ++i) {
ShPtr<CFG::Node> dstNode((*i)->getDst());
retVal = combineRetVals(retVal, performTraversalImpl(node, node->stmt_end()));
if (stopTraversal) {
break;
}
}
}
// It is the last statement in the node, so traverse all node successors.
return traverseNodeSuccessors(stmtInNode.first);
return retVal;
}

/**
Expand Down Expand Up @@ -163,12 +176,78 @@ bool CFGTraversal::getCurrRetVal() const {
* @param[in] node Node to be traversed.
* @param[in] stmtIter Iterator to a statement in @a node to be checked.
*
* If @a stmtIter equals @c node->stmt_end(), the function traverses all
* successors of @a node.
* This function traverses the entire CFG rooted at @a node, starting with the statement
* in @a stmtIter.
*/
bool CFGTraversal::performTraversalImpl(ShPtr<CFG::Node> node,
CFG::stmt_iterator stmtIter) {
while (stmtIter != node->stmt_end()) {
// The stack nodes consist of the current CFG node, a statement iterator storing the current
// place in the node's statements, and an iterator storing the current place in the node's
// successor list.
// Statement place is only needed because we may be called with a statement iterator
// that doesn't point to the beginning.
using NodeType = std::tuple<ShPtr<CFG::Node>, CFG::stmt_iterator, CFG::succ_iterator>;
std::stack<NodeType> visitStack;
visitStack.push({node, stmtIter, node->succ_begin()});

// This logic is a bit complicated because it's normally part of a depth first iterator.
auto retVal = getEndRetVal();
do {
// Note that we directly mutate the successor iterators that are on the
// stack, because we can't pop it until we are done.
auto node = std::get<0>(visitStack.top());
auto stmtPlace = std::get<1>(visitStack.top());
auto &succPlace = std::get<2>(visitStack.top());


// First visitSingleNode will visit all remaining statements in the node.
retVal = combineRetVals(retVal, visitSingleNode(stmtPlace,
node->stmt_end()));
if (stopTraversal) {
break;
}
// Now push the next successor on the stack to make it get
// visited. We have to track whether we push an element because
// they may have all been visited already.
bool pushedElement = false;
while (succPlace != node->succ_end()) {
auto succNode((*succPlace)->getDst());
// This mutates the actual in-stack succ iterator
++succPlace;
// Push this node on the stack to be visited if it
// hasn't been visited and isn't empty.
if (succNode->stmt_begin() != succNode->stmt_end() &&
!hasItem(checkedStmts, *(succNode->stmt_begin()))) {
pushedElement = true;
visitStack.push({succNode, succNode->stmt_begin(),
succNode->succ_begin()});
}
}
// If we pushed an element, we are done with this iteration,
// continue so we go process that node.
if (pushedElement) {
continue;
}
// Otherwise, we need to pop up a level because we are done
// completely with the stack node.
visitStack.pop();

} while (!visitStack.empty());
return retVal;
}

/**
* @brief Visit a single node during our traversal, and all the statements in it.
*
* @param[in] stmtIter Iterator to a statement in node to be checked.
* @param[in] endStmt Iterator to last statement in node to be checked.
*
*/

bool CFGTraversal::visitSingleNode(CFG::stmt_iterator stmtIter,
CFG::stmt_iterator endStmt)
{
while (stmtIter != endStmt) {
// We're not at the end of the node, so check the statement under
// stmtIter.

Expand All @@ -184,9 +263,7 @@ bool CFGTraversal::performTraversalImpl(ShPtr<CFG::Node> node,

++stmtIter;
}

// We have reached the end of the node, so check node's successors.
return traverseNodeSuccessors(node);
return getEndRetVal();
}

/**
Expand Down Expand Up @@ -223,27 +300,6 @@ bool CFGTraversal::performReverseTraversalImpl(ShPtr<CFG::Node> node,
return traverseNodePredecessors(node);
}

/**
* @brief Traverses all the successors of @a node.
*
* @return Result of the traversal, just like performTraversal().
*
* This function is meant to be called within functions traversing a CFG.
*/
bool CFGTraversal::traverseNodeSuccessors(ShPtr<CFG::Node> node) {
bool retVal = getEndRetVal();
// For each outgoing edge...
for (auto i = node->succ_begin(), e = node->succ_end(); i != e; ++i) {
ShPtr<CFG::Node> dstNode((*i)->getDst());
retVal = combineRetVals(retVal, performTraversalImpl(dstNode,
dstNode->stmt_begin()));
if (stopTraversal) {
break;
}
}
return retVal;
}

/**
* @brief Traverses all the predecessors of @a node.
*
Expand Down

0 comments on commit 09c7bbe

Please sign in to comment.