From 64fdb9b0150296802ae2a2468e2217fc8e0c2266 Mon Sep 17 00:00:00 2001 From: Polgreen Date: Thu, 21 Jun 2018 11:48:21 +0200 Subject: [PATCH 1/3] depht limited search for grapht This performs a depth limited depth-first search on a grapht --- src/util/graph.h | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/util/graph.h b/src/util/graph.h index 8a83a0905a1..a79cf2f4316 100644 --- a/src/util/graph.h +++ b/src/util/graph.h @@ -255,6 +255,18 @@ class grapht void disconnect_unreachable(node_indext src); void disconnect_unreachable(const std::vector &src); + std::vector depth_limited_search( + const node_indext &src, + std::size_t &limit, + bool forwards) const; + + std::vector + depth_limited_search(typename N::node_indext src, std::size_t limit) const; + + std::vector depth_limited_search( + std::vector &src, + std::size_t limit) const; + void make_chordal(); // return value: number of connected subgraphs @@ -278,6 +290,11 @@ class grapht std::function f) const; protected: + std::vector depth_limited_search( + std::vector &src, + std::size_t limit, + std::vector &visited) const; + class tarjant { public: @@ -584,6 +601,83 @@ std::vector grapht::get_reachable( return result; } +/// Run recursive depth-limited search on the graph, starting +/// from multiple source nodes, to find the nodes reachable within n steps. +/// This function initialises the search. +/// \param src The node to start the search from. +/// \param limit limit on steps +/// \return a vector of reachable node indices +template +std::vector grapht::depth_limited_search( + const typename N::node_indext src, + std::size_t limit) const +{ + std::vector start_vector(1, src); + return depth_limited_search(start_vector, limit); +} + +/// Run recursive depth-limited search on the graph, starting +/// from multiple source nodes, to find the nodes reachable within n steps. +/// This function initialises the search. +/// \param src The nodes to start the search from. +/// \param limit limit on steps +/// \return a vector of reachable node indices +template +std::vector grapht::depth_limited_search( + std::vector &src, + std::size_t limit) const +{ + std::vector visited(nodes.size(), false); + + for(const auto &node : src) + { + PRECONDITION(node < nodes.size()); + visited[node] = true; + } + + return depth_limited_search(src, limit, visited); +} + +/// Run recursive depth-limited search on the graph, starting +// from multiple source nodes, to find the nodes reachable within n steps +/// \param src The nodes to start the search from. +/// \param limit limit on steps +/// \param visited vector of booleans indicating whether a node has been visited +/// \return a vector of reachable node indices +template +std::vector grapht::depth_limited_search( + std::vector &src, + std::size_t limit, + std::vector &visited) const +{ + if(limit == 0) + return src; + + std::vector next_ring; + + for(const auto &n : src) + { + for(const auto &o : nodes[n].out) + { + if(!visited[o.first]) + { + next_ring.push_back(o.first); + visited[o.first] = true; + } + } + } + + if(next_ring.empty()) + return src; + + limit--; + + for(const auto &succ : depth_limited_search(next_ring, limit, visited)) + src.push_back(succ); + + return src; +} + template std::size_t grapht::connected_subgraphs( std::vector &subgraph_nr) From 2c76d0d7c0cbaf4089084cdfcd092d9ffd1839e2 Mon Sep 17 00:00:00 2001 From: Polgreen Date: Thu, 21 Jun 2018 11:51:31 +0200 Subject: [PATCH 2/3] call graph helper interface to depth limited search Gets all functions reachable within n steps, by calling depth limited search --- src/analyses/call_graph_helpers.cpp | 26 ++++++++++++++++++++++++++ src/analyses/call_graph_helpers.h | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/analyses/call_graph_helpers.cpp b/src/analyses/call_graph_helpers.cpp index 116ef12680e..449de47c6cc 100644 --- a/src/analyses/call_graph_helpers.cpp +++ b/src/analyses/call_graph_helpers.cpp @@ -70,3 +70,29 @@ std::set get_reaching_functions( { return get_connected_functions(graph, function, false); } + +std::set get_functions_reachable_within_n_steps( + const call_grapht::directed_grapht &graph, + const std::set &start_functions, + std::size_t &n) +{ + std::vector start_indices; + std::set result; + + for(const auto &func : start_functions) + start_indices.push_back(*(graph.get_node_index(func))); + + for(const auto &index : graph.depth_limited_search(start_indices, n)) + result.insert(graph[index].function); + + return result; +} + +std::set get_functions_reachable_within_n_steps( + const call_grapht::directed_grapht &graph, + const irep_idt &start_function, + std::size_t &n) +{ + std::set start_functions({ start_function }); + return get_functions_reachable_within_n_steps(graph, start_functions, n); +} diff --git a/src/analyses/call_graph_helpers.h b/src/analyses/call_graph_helpers.h index 170785a9f89..faedd44f15c 100644 --- a/src/analyses/call_graph_helpers.h +++ b/src/analyses/call_graph_helpers.h @@ -49,4 +49,28 @@ std::set get_reachable_functions( std::set get_reaching_functions( const call_grapht::directed_grapht &graph, const irep_idt &function); +/// Get either callers or callees reachable from a given +/// list of functions within N steps +/// \param graph: call graph +/// \param start_functions: set of start functions +/// \param n: number of steps to consider +/// \return set of functions that can be reached from the start function +/// including the start function +std::set get_functions_reachable_within_n_steps( + const call_grapht::directed_grapht &graph, + const std::set &start_functions, + std::size_t &n); + +/// Get either callers or callees reachable from a given +/// list of functions within N steps +/// \param graph: call graph +/// \param start_function: single start function +/// \param n: number of steps to consider +/// \return set of functions that can be reached from the start function +/// including the start function +std::set get_functions_reachable_within_n_steps( + const call_grapht::directed_grapht &graph, + const irep_idt &start_function, + std::size_t &n); + #endif From 904132d56772dccd5c76cf3f07e0f57aa028cdc8 Mon Sep 17 00:00:00 2001 From: Polgreen Date: Thu, 21 Jun 2018 11:52:19 +0200 Subject: [PATCH 3/3] unit tests for depth limited search on call graph --- unit/analyses/call_graph.cpp | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/unit/analyses/call_graph.cpp b/unit/analyses/call_graph.cpp index c6e626af8b5..d14127e7af3 100644 --- a/unit/analyses/call_graph.cpp +++ b/unit/analyses/call_graph.cpp @@ -223,6 +223,48 @@ SCENARIO("call_graph", REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["D"])); } + THEN("We expect {A,B} to be reachable from {A} in 1 step") + { + irep_idt function_name = "A"; + std::size_t depth = 1; + std::set reachable = get_functions_reachable_within_n_steps( + exported, function_name, depth); + REQUIRE(reachable.size() == 2); + REQUIRE(reachable.count("A")); + REQUIRE(reachable.count("B")); + } + THEN("We expect {A,B,C,D} to be reachable from {A} in 2 and 3 steps") + { + irep_idt function_name = "A"; + std::size_t depth = 2; + std::set reachable = get_functions_reachable_within_n_steps( + exported, function_name, depth); + REQUIRE(reachable.size() == 4); + REQUIRE(reachable.count("A")); + REQUIRE(reachable.count("B")); + REQUIRE(reachable.count("C")); + REQUIRE(reachable.count("D")); + + depth = 3; + reachable = get_functions_reachable_within_n_steps( + exported, function_name, depth); + REQUIRE(reachable.size() == 4); + REQUIRE(reachable.count("A")); + REQUIRE(reachable.count("B")); + REQUIRE(reachable.count("C")); + REQUIRE(reachable.count("D")); + } + + THEN("We expect only {A} to be reachable from {A} in 0 steps") + { + irep_idt function_name = "A"; + std::size_t depth = 0; + std::set reachable = get_functions_reachable_within_n_steps( + exported, function_name, depth); + REQUIRE(reachable.size() == 1); + REQUIRE(reachable.count("A")); + } + THEN("We expect A to have successors {A, B}") { std::set successors = get_callees(exported, "A");