@@ -44,6 +44,15 @@ reachability_slicert::get_sources(
44
44
return sources;
45
45
}
46
46
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
+
47
56
// / Perform backwards depth-first search of the control-flow graph of the
48
57
// / goto program, starting from the nodes corresponding to the criterion and
49
58
// / the instructions that might be executed concurrently. Set reaches_assertion
@@ -54,11 +63,50 @@ void reachability_slicert::fixedpoint_to_assertions(
54
63
const is_threadedt &is_threaded,
55
64
slicing_criteriont &criterion)
56
65
{
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 ();
58
76
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
+ }
62
110
}
63
111
64
112
// / Perform forwards depth-first search of the control-flow graph of the
@@ -71,11 +119,66 @@ void reachability_slicert::fixedpoint_from_assertions(
71
119
const is_threadedt &is_threaded,
72
120
slicing_criteriont &criterion)
73
121
{
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 );
75
126
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
+ }
79
182
}
80
183
81
184
// / This function removes all instructions that have the flag
0 commit comments