From 09c7bbe05039d1259e2fae6166b73d85a2d912d4 Mon Sep 17 00:00:00 2001 From: Daniel Berlin Date: Tue, 5 Feb 2019 17:32:01 -0800 Subject: [PATCH] Rewrite recursive cfg traversal to non-recursive --- .../llvmir2hll/graphs/cfg/cfg_traversal.h | 3 +- src/llvmir2hll/graphs/cfg/cfg_traversal.cpp | 114 +++++++++++++----- 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/include/retdec/llvmir2hll/graphs/cfg/cfg_traversal.h b/include/retdec/llvmir2hll/graphs/cfg/cfg_traversal.h index 4905a90ce7..aa5b8b42a4 100644 --- a/include/retdec/llvmir2hll/graphs/cfg/cfg_traversal.h +++ b/include/retdec/llvmir2hll/graphs/cfg/cfg_traversal.h @@ -85,9 +85,10 @@ class CFGTraversal: private retdec::utils::NonCopyable { private: bool performTraversalImpl(ShPtr startNode, CFG::stmt_iterator startStmtIter); + bool visitSingleNode(CFG::stmt_iterator startStmtIter, + CFG::stmt_iterator endStmtIter); bool performReverseTraversalImpl(ShPtr startNode, CFG::stmt_reverse_iterator startStmtRIter); - bool traverseNodeSuccessors(ShPtr node); bool traverseNodePredecessors(ShPtr node); }; diff --git a/src/llvmir2hll/graphs/cfg/cfg_traversal.cpp b/src/llvmir2hll/graphs/cfg/cfg_traversal.cpp index 1422d94878..93e28927fd 100644 --- a/src/llvmir2hll/graphs/cfg/cfg_traversal.cpp +++ b/src/llvmir2hll/graphs/cfg/cfg_traversal.cpp @@ -4,6 +4,8 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ +#include +#include #include "retdec/llvmir2hll/graphs/cfg/cfg_traversal.h" #include "retdec/llvmir2hll/ir/empty_stmt.h" #include "retdec/llvmir2hll/support/debug.h" @@ -80,13 +82,24 @@ bool CFGTraversal::performTraversalFromSuccessors(ShPtr 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 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; } /** @@ -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 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, CFG::stmt_iterator, CFG::succ_iterator>; + std::stack 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. @@ -184,9 +263,7 @@ bool CFGTraversal::performTraversalImpl(ShPtr node, ++stmtIter; } - - // We have reached the end of the node, so check node's successors. - return traverseNodeSuccessors(node); + return getEndRetVal(); } /** @@ -223,27 +300,6 @@ bool CFGTraversal::performReverseTraversalImpl(ShPtr 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 node) { - bool retVal = getEndRetVal(); - // For each outgoing edge... - for (auto i = node->succ_begin(), e = node->succ_end(); i != e; ++i) { - ShPtr dstNode((*i)->getDst()); - retVal = combineRetVals(retVal, performTraversalImpl(dstNode, - dstNode->stmt_begin())); - if (stopTraversal) { - break; - } - } - return retVal; -} - /** * @brief Traverses all the predecessors of @a node. *