Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite recursive cfg traversal to non-recursive #495

Merged
merged 1 commit into from
Mar 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 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,11 @@ class CFGTraversal: private retdec::utils::NonCopyable {
private:
bool performTraversalImpl(ShPtr<CFG::Node> startNode,
CFG::stmt_iterator startStmtIter);
std::pair<bool,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
183 changes: 151 additions & 32 deletions src/llvmir2hll/graphs/cfg/cfg_traversal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
* @copyright (c) 2017 Avast Software, licensed under the MIT license
s3rvac marked this conversation as resolved.
Show resolved Hide resolved
*/

#include <stack>
#include <tuple>
s3rvac marked this conversation as resolved.
Show resolved Hide resolved
#include <unordered_set>

#include "retdec/llvmir2hll/graphs/cfg/cfg_traversal.h"
#include "retdec/llvmir2hll/ir/empty_stmt.h"
#include "retdec/llvmir2hll/support/debug.h"
Expand All @@ -14,6 +18,97 @@ using retdec::utils::hasItem;
namespace retdec {
namespace llvmir2hll {

namespace {

template <class Graph>
class df_iterator : public std::iterator<std::forward_iterator_tag,
ShPtr<typename Graph::Node>>
{
using Node = ShPtr<typename Graph::Node>;
using super = std::iterator<std::forward_iterator_tag, Node>;
// 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 that
// doesn't point to the beginning.
using StackNodeType = std::tuple<Node, typename Graph::stmt_iterator, typename Graph::succ_iterator>;
using ReturnNodeType = std::pair<Node, typename Graph::stmt_iterator>;
std::stack<StackNodeType> visitStack;
std::unordered_set<Node> visitedNodes;
private:
inline df_iterator(Node node, typename Graph::stmt_iterator stmtIter) {
visitedNodes.insert(node);
visitStack.emplace(node, stmtIter, node->succ_begin());
}
inline df_iterator(Node node) : df_iterator(node, node->stmt_begin()) {
}
inline df_iterator() = default; // End is when stack is empty
inline void moveToNextNode() {
// Note that we directly mutate the successor iterators that are
// on the stack, because we can't pop it until we are done.
do {
auto node = std::get<0>(visitStack.top());
auto &succPlace = std::get<2>(visitStack.top());
// Now push the next successor on the stack to make it
// get visited.
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.
if (visitedNodes.insert(succNode).second) {
visitStack.emplace(succNode, succNode->stmt_begin(),
succNode->succ_begin());
return;
}
}
// Otherwise, we need to pop up a level because we are done
// completely with the stack node.
visitStack.pop();
} while (!visitStack.empty());
}
public:
using pointer = typename super::pointer;

// Provide static begin and end methods as our public "constructors"
static df_iterator<Graph> begin(const Node &N) { return df_iterator(N); }
static df_iterator<Graph> begin(const Node &N,
typename Graph::stmt_iterator stmtIter) {
return df_iterator(N, stmtIter);
}

static df_iterator<Graph> end(const Node &N) { return df_iterator(); }

bool operator==(const df_iterator &x) const {
return visitStack == x.visitStack;
}
bool operator!=(const df_iterator &x) const { return !(*this == x); }

ReturnNodeType operator*() const { auto &topNode = visitStack.top();
return std::make_pair(std::get<0>(topNode), std::get<1>(topNode)); }

df_iterator &operator++() { // Preincrement
moveToNextNode();
return *this;
}

// Skips all children of the current node. Note that this may cause the
// iterator to be at end when it is done.
df_iterator &skipChildren() {
visitStack.pop();
return *this;
}

df_iterator operator++(int) { // Postincrement
df_iterator tmp = *this;
++*this;
return tmp;
}
};
} // anonymous namespace

/**
* @brief Constructs a new traverser.
*
Expand Down Expand Up @@ -75,18 +170,28 @@ bool CFGTraversal::performTraversalFromSuccessors(ShPtr<Statement> stmt) {

// Initialization.
stopTraversal = false;

CFG::StmtInNode stmtInNode(cfg->getNodeForStmt(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,30 +268,65 @@ 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()) {
// Walk the CFG rooted at node in depth first order
auto retVal = getEndRetVal();
for (auto I = df_iterator<CFG>::begin(node, stmtIter), E = df_iterator<CFG>::end(node); I != E; ++I) {
auto nodePair = *I;
auto node = nodePair.first;
auto stmtPlace = nodePair.second;
auto visitState = visitSingleNode(stmtPlace, node->stmt_end());

s3rvac marked this conversation as resolved.
Show resolved Hide resolved
retVal = combineRetVals(retVal, visitState.first);
if (stopTraversal) {
break;
}
// If we got asked to skip children, do it
if (visitState.second) {
// This may cause us to be at the end of the iterator
I.skipChildren();
if (I == E) {
break;
}
}
}

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.
*
* @return Result of the visit, and whether to skip children of the node.
*/

s3rvac marked this conversation as resolved.
Show resolved Hide resolved
std::pair<bool, 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.

if (hasItem(checkedStmts, *stmtIter)) {
return getEndRetVal();
return std::make_pair(getEndRetVal(), false);
}
checkedStmts.insert(*stmtIter);

bool shouldContinue = visitStmt(*stmtIter);
if (!shouldContinue || stopTraversal) {
return getCurrRetVal();
return std::make_pair(getCurrRetVal(), true);
}

++stmtIter;
}

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

/**
Expand Down Expand Up @@ -223,27 +363,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