@@ -54,11 +54,49 @@ void reachability_slicert::fixedpoint_to_assertions(
5454 const is_threadedt &is_threaded,
5555 slicing_criteriont &criterion)
5656{
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.
5860
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+ }
62100}
63101
64102// / Perform forwards depth-first search of the control-flow graph of the
@@ -71,11 +109,71 @@ void reachability_slicert::fixedpoint_from_assertions(
71109 const is_threadedt &is_threaded,
72110 slicing_criteriont &criterion)
73111{
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 ();
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+ 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+ }
79177}
80178
81179// / This function removes all instructions that have the flag
0 commit comments