Skip to content

Commit 54f3731

Browse files
authored
Merge pull request diffblue#2610 from smowton/smowton/fix/ssa-trace-unreachable-values
SSA trace: don't concretise steps that will subsequently be dropped
2 parents 6397f62 + 501546a commit 54f3731

File tree

5 files changed

+164
-128
lines changed

5 files changed

+164
-128
lines changed

src/cbmc/all_properties.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ void bmc_all_propertiest::goal_covered(const cover_goalst::goalt &)
4141
if(solver.l_get(cond).is_false())
4242
{
4343
g.second.status=goalt::statust::FAILURE;
44-
symex_target_equationt::SSA_stepst::iterator next=c;
45-
next++; // include the assertion
46-
build_goto_trace(bmc.equation, next, solver, bmc.ns,
47-
g.second.goto_trace);
44+
build_goto_trace(bmc.equation, c, solver, bmc.ns, g.second.goto_trace);
4845
break;
4946
}
5047
}

src/cbmc/bmc_cover.cpp

+9-15
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ class bmc_covert:
144144
bmct &bmc;
145145
};
146146

147+
static bool is_failed_assumption_step(
148+
symex_target_equationt::SSA_stepst::const_iterator step,
149+
const prop_convt &prop_conv)
150+
{
151+
return step->is_assume() && prop_conv.l_get(step->cond_literal).is_false();
152+
}
153+
147154
void bmc_covert::satisfying_assignment()
148155
{
149156
tests.push_back(testt());
@@ -173,21 +180,8 @@ void bmc_covert::satisfying_assignment()
173180
}
174181
}
175182

176-
build_goto_trace(bmc.equation, bmc.equation.SSA_steps.end(),
177-
solver, bmc.ns, test.goto_trace);
178-
179-
goto_tracet &goto_trace=test.goto_trace;
180-
181-
// Now delete anything after first failed assumption
182-
for(goto_tracet::stepst::iterator
183-
s_it1=goto_trace.steps.begin();
184-
s_it1!=goto_trace.steps.end();
185-
s_it1++)
186-
if(s_it1->is_assume() && !s_it1->cond_value)
187-
{
188-
goto_trace.steps.erase(++s_it1, goto_trace.steps.end());
189-
break;
190-
}
183+
build_goto_trace(
184+
bmc.equation, is_failed_assumption_step, solver, bmc.ns, test.goto_trace);
191185
}
192186

193187
bool bmc_covert::operator()()

src/cbmc/fault_localization.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,8 @@ void fault_localizationt::goal_covered(
336336
if(solver.l_get(cond).is_false())
337337
{
338338
goal_pair.second.status=goalt::statust::FAILURE;
339-
symex_target_equationt::SSA_stepst::iterator next=inst;
340-
next++; // include the assertion
341339
build_goto_trace(
342-
bmc.equation, next, solver, bmc.ns, goal_pair.second.goto_trace);
340+
bmc.equation, inst, solver, bmc.ns, goal_pair.second.goto_trace);
343341

344342
// localize faults
345343
run(goal_pair.first);

src/goto-symex/build_goto_trace.cpp

+139-104
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ void update_internal_field(
164164

165165
void build_goto_trace(
166166
const symex_target_equationt &target,
167-
symex_target_equationt::SSA_stepst::const_iterator end_step,
167+
ssa_step_predicatet is_last_step_to_keep,
168168
const prop_convt &prop_conv,
169169
const namespacet &ns,
170170
goto_tracet &goto_trace)
@@ -173,21 +173,28 @@ void build_goto_trace(
173173
// Furthermore, read-events need to occur before write
174174
// events with the same clock.
175175

176-
typedef std::map<mp_integer, goto_tracet::stepst> time_mapt;
176+
typedef symex_target_equationt::SSA_stepst::const_iterator ssa_step_iteratort;
177+
typedef std::map<mp_integer, std::vector<ssa_step_iteratort>> time_mapt;
177178
time_mapt time_map;
178179

179180
mp_integer current_time=0;
180181

181-
const goto_trace_stept *end_ptr=nullptr;
182-
bool end_step_seen=false;
182+
ssa_step_iteratort last_step_to_keep = target.SSA_steps.end();
183+
bool last_step_was_kept = false;
183184

184-
for(symex_target_equationt::SSA_stepst::const_iterator
185-
it=target.SSA_steps.begin();
186-
it!=target.SSA_steps.end();
185+
// First sort the SSA steps by time, in the process dropping steps
186+
// we definitely don't want to retain in the final trace:
187+
188+
for(ssa_step_iteratort it = target.SSA_steps.begin();
189+
it != target.SSA_steps.end();
187190
it++)
188191
{
189-
if(it==end_step)
190-
end_step_seen=true;
192+
if(
193+
last_step_to_keep == target.SSA_steps.end() &&
194+
is_last_step_to_keep(it, prop_conv))
195+
{
196+
last_step_to_keep = it;
197+
}
191198

192199
const symex_target_equationt::SSA_stept &SSA_step=*it;
193200

@@ -227,12 +234,21 @@ void build_goto_trace(
227234

228235
if(time_before<0)
229236
{
230-
time_mapt::iterator entry=
231-
time_map.insert(std::make_pair(
232-
current_time,
233-
goto_tracet::stepst())).first;
234-
entry->second.splice(entry->second.end(), time_map[time_before]);
235-
time_map.erase(time_before);
237+
time_mapt::const_iterator time_before_steps_it =
238+
time_map.find(time_before);
239+
240+
if(time_before_steps_it != time_map.end())
241+
{
242+
std::vector<ssa_step_iteratort> &current_time_steps =
243+
time_map[current_time];
244+
245+
current_time_steps.insert(
246+
current_time_steps.end(),
247+
time_before_steps_it->second.begin(),
248+
time_before_steps_it->second.end());
249+
250+
time_map.erase(time_before_steps_it);
251+
}
236252
}
237253

238254
continue;
@@ -248,97 +264,128 @@ void build_goto_trace(
248264
continue;
249265
}
250266

251-
goto_tracet::stepst &steps=time_map[current_time];
252-
steps.push_back(goto_trace_stept());
253-
goto_trace_stept &goto_trace_step=steps.back();
254-
if(!end_step_seen)
255-
end_ptr=&goto_trace_step;
256-
257-
goto_trace_step.thread_nr=SSA_step.source.thread_nr;
258-
goto_trace_step.pc=SSA_step.source.pc;
259-
goto_trace_step.comment=SSA_step.comment;
260-
if(SSA_step.ssa_lhs.is_not_nil())
261-
goto_trace_step.lhs_object=
262-
ssa_exprt(SSA_step.ssa_lhs.get_original_expr());
263-
else
264-
goto_trace_step.lhs_object.make_nil();
265-
goto_trace_step.type=SSA_step.type;
266-
goto_trace_step.hidden=SSA_step.hidden;
267-
goto_trace_step.format_string=SSA_step.format_string;
268-
goto_trace_step.io_id=SSA_step.io_id;
269-
goto_trace_step.formatted=SSA_step.formatted;
270-
goto_trace_step.identifier=SSA_step.identifier;
271-
272-
// update internal field for specific variables in the counterexample
273-
update_internal_field(SSA_step, goto_trace_step, ns);
274-
275-
goto_trace_step.assignment_type=
276-
(it->is_assignment()&&
277-
(SSA_step.assignment_type==
278-
symex_targett::assignment_typet::VISIBLE_ACTUAL_PARAMETER ||
279-
SSA_step.assignment_type==
280-
symex_targett::assignment_typet::HIDDEN_ACTUAL_PARAMETER))?
281-
goto_trace_stept::assignment_typet::ACTUAL_PARAMETER:
282-
goto_trace_stept::assignment_typet::STATE;
267+
if(it == last_step_to_keep)
268+
{
269+
last_step_was_kept = true;
270+
}
283271

284-
if(SSA_step.original_full_lhs.is_not_nil())
285-
goto_trace_step.full_lhs=
286-
build_full_lhs_rec(
287-
prop_conv, ns, SSA_step.original_full_lhs, SSA_step.ssa_full_lhs);
272+
time_map[current_time].push_back(it);
273+
}
288274

289-
if(SSA_step.ssa_lhs.is_not_nil())
290-
goto_trace_step.lhs_object_value=prop_conv.get(SSA_step.ssa_lhs);
275+
INVARIANT(
276+
last_step_to_keep == target.SSA_steps.end() || last_step_was_kept,
277+
"last step in SSA trace to keep must not be filtered out as a sync "
278+
"instruction, not-taken branch, PHI node, or similar");
291279

292-
if(SSA_step.ssa_full_lhs.is_not_nil())
293-
{
294-
goto_trace_step.full_lhs_value=prop_conv.get(SSA_step.ssa_full_lhs);
295-
simplify(goto_trace_step.full_lhs_value, ns);
296-
}
280+
// Now build the GOTO trace, ordered by time, then by SSA trace order.
281+
282+
// produce the step numbers
283+
unsigned step_nr = 0;
297284

298-
for(const auto &j : SSA_step.converted_io_args)
285+
for(const auto &time_and_ssa_steps : time_map)
286+
{
287+
for(const auto ssa_step_it : time_and_ssa_steps.second)
299288
{
300-
if(j.is_constant() ||
301-
j.id()==ID_string_constant)
302-
goto_trace_step.io_args.push_back(j);
289+
const auto &SSA_step = *ssa_step_it;
290+
goto_trace.steps.push_back(goto_trace_stept());
291+
goto_trace_stept &goto_trace_step = goto_trace.steps.back();
292+
293+
goto_trace_step.step_nr = ++step_nr;
294+
295+
goto_trace_step.thread_nr = SSA_step.source.thread_nr;
296+
goto_trace_step.pc = SSA_step.source.pc;
297+
goto_trace_step.comment = SSA_step.comment;
298+
if(SSA_step.ssa_lhs.is_not_nil())
299+
{
300+
goto_trace_step.lhs_object =
301+
ssa_exprt(SSA_step.ssa_lhs.get_original_expr());
302+
}
303303
else
304304
{
305-
exprt tmp=prop_conv.get(j);
306-
goto_trace_step.io_args.push_back(tmp);
305+
goto_trace_step.lhs_object.make_nil();
306+
}
307+
goto_trace_step.type = SSA_step.type;
308+
goto_trace_step.hidden = SSA_step.hidden;
309+
goto_trace_step.format_string = SSA_step.format_string;
310+
goto_trace_step.io_id = SSA_step.io_id;
311+
goto_trace_step.formatted = SSA_step.formatted;
312+
goto_trace_step.identifier = SSA_step.identifier;
313+
314+
// update internal field for specific variables in the counterexample
315+
update_internal_field(SSA_step, goto_trace_step, ns);
316+
317+
goto_trace_step.assignment_type =
318+
(SSA_step.is_assignment() &&
319+
(SSA_step.assignment_type ==
320+
symex_targett::assignment_typet::VISIBLE_ACTUAL_PARAMETER ||
321+
SSA_step.assignment_type ==
322+
symex_targett::assignment_typet::HIDDEN_ACTUAL_PARAMETER))
323+
? goto_trace_stept::assignment_typet::ACTUAL_PARAMETER
324+
: goto_trace_stept::assignment_typet::STATE;
325+
326+
if(SSA_step.original_full_lhs.is_not_nil())
327+
{
328+
goto_trace_step.full_lhs = build_full_lhs_rec(
329+
prop_conv, ns, SSA_step.original_full_lhs, SSA_step.ssa_full_lhs);
307330
}
308-
}
309331

310-
if(SSA_step.is_assert() ||
311-
SSA_step.is_assume() ||
312-
SSA_step.is_goto())
313-
{
314-
goto_trace_step.cond_expr=SSA_step.cond_expr;
332+
if(SSA_step.ssa_lhs.is_not_nil())
333+
goto_trace_step.lhs_object_value = prop_conv.get(SSA_step.ssa_lhs);
334+
335+
if(SSA_step.ssa_full_lhs.is_not_nil())
336+
{
337+
goto_trace_step.full_lhs_value = prop_conv.get(SSA_step.ssa_full_lhs);
338+
simplify(goto_trace_step.full_lhs_value, ns);
339+
}
340+
341+
for(const auto &j : SSA_step.converted_io_args)
342+
{
343+
if(j.is_constant() || j.id() == ID_string_constant)
344+
{
345+
goto_trace_step.io_args.push_back(j);
346+
}
347+
else
348+
{
349+
exprt tmp = prop_conv.get(j);
350+
goto_trace_step.io_args.push_back(tmp);
351+
}
352+
}
315353

316-
goto_trace_step.cond_value=
317-
prop_conv.l_get(SSA_step.cond_literal).is_true();
354+
if(SSA_step.is_assert() || SSA_step.is_assume() || SSA_step.is_goto())
355+
{
356+
goto_trace_step.cond_expr = SSA_step.cond_expr;
357+
358+
goto_trace_step.cond_value =
359+
prop_conv.l_get(SSA_step.cond_literal).is_true();
360+
}
361+
362+
if(ssa_step_it == last_step_to_keep)
363+
return;
318364
}
319365
}
366+
}
320367

321-
// Now assemble into a single goto_trace.
322-
// This exploits sorted-ness of the map.
323-
for(auto &t_it : time_map)
324-
goto_trace.steps.splice(goto_trace.steps.end(), t_it.second);
325-
326-
// cut off the trace at the desired end
327-
for(goto_tracet::stepst::iterator
328-
s_it1=goto_trace.steps.begin();
329-
s_it1!=goto_trace.steps.end();
330-
++s_it1)
331-
if(end_step_seen && end_ptr==&(*s_it1))
332-
{
333-
goto_trace.trim_after(s_it1);
334-
break;
335-
}
368+
void build_goto_trace(
369+
const symex_target_equationt &target,
370+
symex_target_equationt::SSA_stepst::const_iterator last_step_to_keep,
371+
const prop_convt &prop_conv,
372+
const namespacet &ns,
373+
goto_tracet &goto_trace)
374+
{
375+
const auto is_last_step_to_keep = [last_step_to_keep](
376+
symex_target_equationt::SSA_stepst::const_iterator it, const prop_convt &) {
377+
return last_step_to_keep == it;
378+
};
336379

337-
// produce the step numbers
338-
unsigned step_nr=0;
380+
return build_goto_trace(
381+
target, is_last_step_to_keep, prop_conv, ns, goto_trace);
382+
}
339383

340-
for(auto &s_it : goto_trace.steps)
341-
s_it.step_nr=++step_nr;
384+
static bool is_failed_assertion_step(
385+
symex_target_equationt::SSA_stepst::const_iterator step,
386+
const prop_convt &prop_conv)
387+
{
388+
return step->is_assert() && prop_conv.l_get(step->cond_literal).is_false();
342389
}
343390

344391
void build_goto_trace(
@@ -347,17 +394,5 @@ void build_goto_trace(
347394
const namespacet &ns,
348395
goto_tracet &goto_trace)
349396
{
350-
build_goto_trace(
351-
target, target.SSA_steps.end(), prop_conv, ns, goto_trace);
352-
353-
// Now delete anything after first failed assertion
354-
for(goto_tracet::stepst::iterator
355-
s_it1=goto_trace.steps.begin();
356-
s_it1!=goto_trace.steps.end();
357-
s_it1++)
358-
if(s_it1->is_assert() && !s_it1->cond_value)
359-
{
360-
goto_trace.trim_after(s_it1);
361-
break;
362-
}
397+
build_goto_trace(target, is_failed_assertion_step, prop_conv, ns, goto_trace);
363398
}

src/goto-symex/build_goto_trace.h

+14-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,22 @@ void build_goto_trace(
2424
const namespacet &ns,
2525
goto_tracet &goto_trace);
2626

27-
// builds a trace that stops after given step
27+
// builds a trace that stops after the given step
2828
void build_goto_trace(
2929
const symex_target_equationt &target,
30-
symex_target_equationt::SSA_stepst::const_iterator stop,
30+
symex_target_equationt::SSA_stepst::const_iterator last_step_to_keep,
31+
const prop_convt &prop_conv,
32+
const namespacet &ns,
33+
goto_tracet &goto_trace);
34+
35+
typedef std::function<
36+
bool(symex_target_equationt::SSA_stepst::const_iterator, const prop_convt &)>
37+
ssa_step_predicatet;
38+
39+
// builds a trace that stops after the step matching a given condition
40+
void build_goto_trace(
41+
const symex_target_equationt &target,
42+
ssa_step_predicatet stop_after_predicate,
3143
const prop_convt &prop_conv,
3244
const namespacet &ns,
3345
goto_tracet &goto_trace);

0 commit comments

Comments
 (0)