Skip to content

Commit b9014de

Browse files
authored
Merge pull request diffblue#2381 from polgreen/depth_limited_search
Depth limited search
2 parents 7c767ab + 904132d commit b9014de

File tree

4 files changed

+186
-0
lines changed

4 files changed

+186
-0
lines changed

src/analyses/call_graph_helpers.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,29 @@ std::set<irep_idt> get_reaching_functions(
7070
{
7171
return get_connected_functions(graph, function, false);
7272
}
73+
74+
std::set<irep_idt> get_functions_reachable_within_n_steps(
75+
const call_grapht::directed_grapht &graph,
76+
const std::set<irep_idt> &start_functions,
77+
std::size_t &n)
78+
{
79+
std::vector<std::size_t> start_indices;
80+
std::set<irep_idt> result;
81+
82+
for(const auto &func : start_functions)
83+
start_indices.push_back(*(graph.get_node_index(func)));
84+
85+
for(const auto &index : graph.depth_limited_search(start_indices, n))
86+
result.insert(graph[index].function);
87+
88+
return result;
89+
}
90+
91+
std::set<irep_idt> get_functions_reachable_within_n_steps(
92+
const call_grapht::directed_grapht &graph,
93+
const irep_idt &start_function,
94+
std::size_t &n)
95+
{
96+
std::set<irep_idt> start_functions({ start_function });
97+
return get_functions_reachable_within_n_steps(graph, start_functions, n);
98+
}

src/analyses/call_graph_helpers.h

+24
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,28 @@ std::set<irep_idt> get_reachable_functions(
4949
std::set<irep_idt> get_reaching_functions(
5050
const call_grapht::directed_grapht &graph, const irep_idt &function);
5151

52+
/// Get either callers or callees reachable from a given
53+
/// list of functions within N steps
54+
/// \param graph: call graph
55+
/// \param start_functions: set of start functions
56+
/// \param n: number of steps to consider
57+
/// \return set of functions that can be reached from the start function
58+
/// including the start function
59+
std::set<irep_idt> get_functions_reachable_within_n_steps(
60+
const call_grapht::directed_grapht &graph,
61+
const std::set<irep_idt> &start_functions,
62+
std::size_t &n);
63+
64+
/// Get either callers or callees reachable from a given
65+
/// list of functions within N steps
66+
/// \param graph: call graph
67+
/// \param start_function: single start function
68+
/// \param n: number of steps to consider
69+
/// \return set of functions that can be reached from the start function
70+
/// including the start function
71+
std::set<irep_idt> get_functions_reachable_within_n_steps(
72+
const call_grapht::directed_grapht &graph,
73+
const irep_idt &start_function,
74+
std::size_t &n);
75+
5276
#endif

src/util/graph.h

+94
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,18 @@ class grapht
255255
void disconnect_unreachable(node_indext src);
256256
void disconnect_unreachable(const std::vector<node_indext> &src);
257257

258+
std::vector<node_indext> depth_limited_search(
259+
const node_indext &src,
260+
std::size_t &limit,
261+
bool forwards) const;
262+
263+
std::vector<typename N::node_indext>
264+
depth_limited_search(typename N::node_indext src, std::size_t limit) const;
265+
266+
std::vector<typename N::node_indext> depth_limited_search(
267+
std::vector<typename N::node_indext> &src,
268+
std::size_t limit) const;
269+
258270
void make_chordal();
259271

260272
// return value: number of connected subgraphs
@@ -278,6 +290,11 @@ class grapht
278290
std::function<void(const node_indext &)> f) const;
279291

280292
protected:
293+
std::vector<typename N::node_indext> depth_limited_search(
294+
std::vector<typename N::node_indext> &src,
295+
std::size_t limit,
296+
std::vector<bool> &visited) const;
297+
281298
class tarjant
282299
{
283300
public:
@@ -584,6 +601,83 @@ std::vector<typename N::node_indext> grapht<N>::get_reachable(
584601
return result;
585602
}
586603

604+
/// Run recursive depth-limited search on the graph, starting
605+
/// from multiple source nodes, to find the nodes reachable within n steps.
606+
/// This function initialises the search.
607+
/// \param src The node to start the search from.
608+
/// \param limit limit on steps
609+
/// \return a vector of reachable node indices
610+
template <class N>
611+
std::vector<typename N::node_indext> grapht<N>::depth_limited_search(
612+
const typename N::node_indext src,
613+
std::size_t limit) const
614+
{
615+
std::vector<node_indext> start_vector(1, src);
616+
return depth_limited_search(start_vector, limit);
617+
}
618+
619+
/// Run recursive depth-limited search on the graph, starting
620+
/// from multiple source nodes, to find the nodes reachable within n steps.
621+
/// This function initialises the search.
622+
/// \param src The nodes to start the search from.
623+
/// \param limit limit on steps
624+
/// \return a vector of reachable node indices
625+
template <class N>
626+
std::vector<typename N::node_indext> grapht<N>::depth_limited_search(
627+
std::vector<typename N::node_indext> &src,
628+
std::size_t limit) const
629+
{
630+
std::vector<bool> visited(nodes.size(), false);
631+
632+
for(const auto &node : src)
633+
{
634+
PRECONDITION(node < nodes.size());
635+
visited[node] = true;
636+
}
637+
638+
return depth_limited_search(src, limit, visited);
639+
}
640+
641+
/// Run recursive depth-limited search on the graph, starting
642+
// from multiple source nodes, to find the nodes reachable within n steps
643+
/// \param src The nodes to start the search from.
644+
/// \param limit limit on steps
645+
/// \param visited vector of booleans indicating whether a node has been visited
646+
/// \return a vector of reachable node indices
647+
template <class N>
648+
std::vector<typename N::node_indext> grapht<N>::depth_limited_search(
649+
std::vector<typename N::node_indext> &src,
650+
std::size_t limit,
651+
std::vector<bool> &visited) const
652+
{
653+
if(limit == 0)
654+
return src;
655+
656+
std::vector<node_indext> next_ring;
657+
658+
for(const auto &n : src)
659+
{
660+
for(const auto &o : nodes[n].out)
661+
{
662+
if(!visited[o.first])
663+
{
664+
next_ring.push_back(o.first);
665+
visited[o.first] = true;
666+
}
667+
}
668+
}
669+
670+
if(next_ring.empty())
671+
return src;
672+
673+
limit--;
674+
675+
for(const auto &succ : depth_limited_search(next_ring, limit, visited))
676+
src.push_back(succ);
677+
678+
return src;
679+
}
680+
587681
template<class N>
588682
std::size_t grapht<N>::connected_subgraphs(
589683
std::vector<node_indext> &subgraph_nr)

unit/analyses/call_graph.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,48 @@ SCENARIO("call_graph",
223223
REQUIRE(exported.has_edge(nodes_by_name["B"], nodes_by_name["D"]));
224224
}
225225

226+
THEN("We expect {A,B} to be reachable from {A} in 1 step")
227+
{
228+
irep_idt function_name = "A";
229+
std::size_t depth = 1;
230+
std::set<irep_idt> reachable = get_functions_reachable_within_n_steps(
231+
exported, function_name, depth);
232+
REQUIRE(reachable.size() == 2);
233+
REQUIRE(reachable.count("A"));
234+
REQUIRE(reachable.count("B"));
235+
}
236+
THEN("We expect {A,B,C,D} to be reachable from {A} in 2 and 3 steps")
237+
{
238+
irep_idt function_name = "A";
239+
std::size_t depth = 2;
240+
std::set<irep_idt> reachable = get_functions_reachable_within_n_steps(
241+
exported, function_name, depth);
242+
REQUIRE(reachable.size() == 4);
243+
REQUIRE(reachable.count("A"));
244+
REQUIRE(reachable.count("B"));
245+
REQUIRE(reachable.count("C"));
246+
REQUIRE(reachable.count("D"));
247+
248+
depth = 3;
249+
reachable = get_functions_reachable_within_n_steps(
250+
exported, function_name, depth);
251+
REQUIRE(reachable.size() == 4);
252+
REQUIRE(reachable.count("A"));
253+
REQUIRE(reachable.count("B"));
254+
REQUIRE(reachable.count("C"));
255+
REQUIRE(reachable.count("D"));
256+
}
257+
258+
THEN("We expect only {A} to be reachable from {A} in 0 steps")
259+
{
260+
irep_idt function_name = "A";
261+
std::size_t depth = 0;
262+
std::set<irep_idt> reachable = get_functions_reachable_within_n_steps(
263+
exported, function_name, depth);
264+
REQUIRE(reachable.size() == 1);
265+
REQUIRE(reachable.count("A"));
266+
}
267+
226268
THEN("We expect A to have successors {A, B}")
227269
{
228270
std::set<irep_idt> successors = get_callees(exported, "A");

0 commit comments

Comments
 (0)