@@ -54,11 +54,49 @@ void reachability_slicert::fixedpoint_to_assertions(
54
54
const is_threadedt &is_threaded,
55
55
slicing_criteriont &criterion)
56
56
{
57
- std::vector<cfgt::node_indext> src = get_sources (is_threaded, criterion);
57
+ // Stack entries are pairs of node indices and a boolean flag that indicates
58
+ // whether the function's callsite is known, in which case it has already
59
+ // been placed on the stack and function heads are a no-op.
58
60
59
- std::vector<cfgt::node_indext> reachable = cfg.get_reachable (src, false );
60
- for (const auto index : reachable)
61
- cfg[index ].reaches_assertion = true ;
61
+ std::vector<std::pair<cfgt::node_indext, bool >> stack;
62
+ std::vector<cfgt::node_indext> sources = get_sources (is_threaded, criterion);
63
+ for (const auto source : sources)
64
+ stack.emplace_back (source, false );
65
+
66
+ while (!stack.empty ())
67
+ {
68
+ auto index = stack.back ().first ;
69
+ auto callsite_is_known = stack.back ().second ;
70
+ stack.pop_back ();
71
+
72
+ auto &node = cfg[index ];
73
+ if (node.reaches_assertion )
74
+ continue ;
75
+ node.reaches_assertion = true ;
76
+
77
+ for (const auto &edge : node.in )
78
+ {
79
+ const auto &pred_node = cfg[edge.first ];
80
+
81
+ if (pred_node.PC ->is_end_function ())
82
+ {
83
+ stack.emplace_back (edge.first , true );
84
+ stack.emplace_back (
85
+ cfg.entry_map [std::prev (node.PC )], callsite_is_known);
86
+ }
87
+ else if (pred_node.PC ->is_function_call ())
88
+ {
89
+ // Skip this predecessor, unless this is a bodyless function, or we
90
+ // don't know who our callee was:
91
+ if (!callsite_is_known || pred_node.PC == std::prev (node.PC ))
92
+ stack.emplace_back (edge.first , callsite_is_known);
93
+ }
94
+ else
95
+ {
96
+ stack.emplace_back (edge.first , callsite_is_known);
97
+ }
98
+ }
99
+ }
62
100
}
63
101
64
102
// / Perform forwards depth-first search of the control-flow graph of the
@@ -71,11 +109,71 @@ void reachability_slicert::fixedpoint_from_assertions(
71
109
const is_threadedt &is_threaded,
72
110
slicing_criteriont &criterion)
73
111
{
74
- std::vector<cfgt::node_indext> src = get_sources (is_threaded, criterion);
112
+ // Stack entries are pairs of node indices and a boolean flag that indicates
113
+ // whether the function's callsite is known, in which case it has already
114
+ // been placed on the stack and return sites are a no-op.
115
+
116
+ std::vector<std::pair<cfgt::node_indext, bool >> stack;
117
+ std::vector<cfgt::node_indext> sources = get_sources (is_threaded, criterion);
118
+ for (const auto source : sources)
119
+ stack.emplace_back (source, false );
120
+
121
+ while (!stack.empty ())
122
+ {
123
+ auto index = stack.back ().first ;
124
+ auto callsite_is_known = stack.back ().second ;
125
+ stack.pop_back ();
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
+ auto &node = cfg[index ];
128
+ if (node.reachable_from_assertion )
129
+ continue ;
130
+ node.reachable_from_assertion = true ;
131
+
132
+ if (node.PC ->is_function_call ())
133
+ {
134
+ // Queue the instruction's natural successor (function head, or next
135
+ // instruction if the function is bodyless)
136
+ INVARIANT (node.out .size () == 1 , " Call sites should have one successor" );
137
+ auto successor_index = node.out .begin ()->first ;
138
+
139
+ // If the function has a body, mark the function head, but note that we
140
+ // have already taken care of the return site.
141
+ const auto &callee_head_node = cfg[successor_index];
142
+ auto callee_it = callee_head_node.PC ;
143
+ if (callee_it != std::next (node.PC ))
144
+ {
145
+ stack.emplace_back (successor_index, true );
146
+
147
+ // Check if it can return, and if so mark the callsite's successor:
148
+ while (!callee_it->is_end_function ())
149
+ ++callee_it;
150
+
151
+ if (cfg[cfg.entry_map [callee_it]].out .size () != 0 )
152
+ {
153
+ stack.emplace_back (
154
+ cfg.entry_map [std::next (node.PC )], callsite_is_known);
155
+ }
156
+ }
157
+ else
158
+ {
159
+ // Bodyless function -- mark the successor instruction only.
160
+ stack.emplace_back (successor_index, callsite_is_known);
161
+ }
162
+ }
163
+ else if (node.PC ->is_end_function ())
164
+ {
165
+ if (!callsite_is_known)
166
+ {
167
+ for (const auto &edge : node.out )
168
+ stack.emplace_back (edge.first , false );
169
+ }
170
+ }
171
+ else
172
+ {
173
+ for (const auto &edge : node.out )
174
+ stack.emplace_back (edge.first , callsite_is_known);
175
+ }
176
+ }
79
177
}
80
178
81
179
// / This function removes all instructions that have the flag
0 commit comments