@@ -44,6 +44,15 @@ reachability_slicert::get_sources(
4444 return sources;
4545}
4646
47+ static bool is_same_target (
48+ goto_programt::const_targett it1,
49+ goto_programt::const_targett it2)
50+ {
51+ // Avoid comparing iterators belonging to different functions, and therefore
52+ // different std::lists.
53+ return it1->function == it2->function && it1 == it2;
54+ }
55+
4756// / Perform backwards depth-first search of the control-flow graph of the
4857// / goto program, starting from the nodes corresponding to the criterion and
4958// / the instructions that might be executed concurrently. Set reaches_assertion
@@ -54,11 +63,50 @@ void reachability_slicert::fixedpoint_to_assertions(
5463 const is_threadedt &is_threaded,
5564 slicing_criteriont &criterion)
5665{
57- std::vector<cfgt::node_indext> src = get_sources (is_threaded, criterion);
66+ std::vector<search_stack_entryt> stack;
67+ std::vector<cfgt::node_indext> sources = get_sources (is_threaded, criterion);
68+ for (const auto source : sources)
69+ stack.emplace_back (source, false );
70+
71+ while (!stack.empty ())
72+ {
73+ auto &node = cfg[stack.back ().node_index ];
74+ const auto caller_is_known = stack.back ().caller_is_known ;
75+ stack.pop_back ();
5876
59- std::vector<cfgt::node_indext> reachable = cfg.get_reachable (src, false );
60- for (const auto index : reachable)
61- cfg[index].reaches_assertion = true ;
77+ if (node.reaches_assertion )
78+ continue ;
79+ node.reaches_assertion = true ;
80+
81+ for (const auto &edge : node.in )
82+ {
83+ const auto &pred_node = cfg[edge.first ];
84+
85+ if (pred_node.PC ->is_end_function ())
86+ {
87+ // This is an end-of-function -> successor-of-callsite edge.
88+ // Queue both the caller and the end of the callee.
89+ INVARIANT (
90+ std::prev (node.PC )->is_function_call (),
91+ " all function return edges should point to the successor of a "
92+ " FUNCTION_CALL instruction" );
93+ stack.emplace_back (edge.first , true );
94+ stack.emplace_back (
95+ cfg.entry_map [std::prev (node.PC )], caller_is_known);
96+ }
97+ else if (pred_node.PC ->is_function_call ())
98+ {
99+ // Skip this predecessor, unless this is a bodyless function, or we
100+ // don't know who our callee was:
101+ if (!caller_is_known || is_same_target (std::next (pred_node.PC ), node.PC ))
102+ stack.emplace_back (edge.first , caller_is_known);
103+ }
104+ else
105+ {
106+ stack.emplace_back (edge.first , caller_is_known);
107+ }
108+ }
109+ }
62110}
63111
64112// / Perform forwards depth-first search of the control-flow graph of the
@@ -71,11 +119,66 @@ void reachability_slicert::fixedpoint_from_assertions(
71119 const is_threadedt &is_threaded,
72120 slicing_criteriont &criterion)
73121{
74- std::vector<cfgt::node_indext> src = get_sources (is_threaded, criterion);
122+ std::vector<search_stack_entryt> stack;
123+ std::vector<cfgt::node_indext> sources = get_sources (is_threaded, criterion);
124+ for (const auto source : sources)
125+ stack.emplace_back (source, false );
75126
76- const std::vector<cfgt::node_indext> reachable = cfg.get_reachable (src, true );
77- for (const auto index : reachable)
78- cfg[index].reachable_from_assertion = true ;
127+ while (!stack.empty ())
128+ {
129+ auto &node = cfg[stack.back ().node_index ];
130+ const auto caller_is_known = stack.back ().caller_is_known ;
131+ stack.pop_back ();
132+
133+ if (node.reachable_from_assertion )
134+ continue ;
135+ node.reachable_from_assertion = true ;
136+
137+ if (node.PC ->is_function_call ())
138+ {
139+ // Queue the instruction's natural successor (function head, or next
140+ // instruction if the function is bodyless)
141+ INVARIANT (node.out .size () == 1 , " Call sites should have one successor" );
142+ const auto successor_index = node.out .begin ()->first ;
143+
144+ // If the function has a body, mark the function head, but note that we
145+ // have already taken care of the return site.
146+ const auto &callee_head_node = cfg[successor_index];
147+ auto callee_it = callee_head_node.PC ;
148+ if (!is_same_target (callee_it, std::next (node.PC )))
149+ {
150+ stack.emplace_back (successor_index, true );
151+
152+ // Check if it can return, and if so mark the callsite's successor:
153+ while (!callee_it->is_end_function ())
154+ ++callee_it;
155+
156+ if (!cfg[cfg.entry_map [callee_it]].out .empty ())
157+ {
158+ stack.emplace_back (
159+ cfg.entry_map [std::next (node.PC )], caller_is_known);
160+ }
161+ }
162+ else
163+ {
164+ // Bodyless function -- mark the successor instruction only.
165+ stack.emplace_back (successor_index, caller_is_known);
166+ }
167+ }
168+ else if (node.PC ->is_end_function () && caller_is_known)
169+ {
170+ // Special case -- the callsite successor was already queued when entering
171+ // this function, more precisely than we can see from the function return
172+ // edges (which lead to all possible callers), so nothing to do here.
173+ }
174+ else
175+ {
176+ // General case, including end-of-function where we don't know our caller.
177+ // Queue all successors.
178+ for (const auto &edge : node.out )
179+ stack.emplace_back (edge.first , caller_is_known);
180+ }
181+ }
79182}
80183
81184// / This function removes all instructions that have the flag
0 commit comments