Skip to content

Commit 7b2a569

Browse files
committed
SSA trace: don't concretise steps that will subsequently be dropped
Previously the SSA trace did all concretisation (taking the solver's output and turning it into concrete expressions to be output in the result trace) before sorting the steps by time and determining what belongs in the final trace. This was generally harmless, but could result in much wasted time and potentially memory exhaustion when concretising very large arrays and strings. Therefore, we now perform the time-order sort and figure out which steps are to be kept first, *then* concretise them.
1 parent f3630f0 commit 7b2a569

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+
}
271+
272+
time_map[current_time].push_back(it);
273+
}
283274

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);
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");
288279

289-
if(SSA_step.ssa_lhs.is_not_nil())
290-
goto_trace_step.lhs_object_value=prop_conv.get(SSA_step.ssa_lhs);
280+
// Now build the GOTO trace, ordered by time, then by SSA trace order.
291281

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-
}
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();
307306
}
308-
}
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;
309325

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;
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);
330+
}
331+
332+
if(SSA_step.ssa_lhs.is_not_nil())
333+
goto_trace_step.lhs_object_value = prop_conv.get(SSA_step.ssa_lhs);
315334

316-
goto_trace_step.cond_value=
317-
prop_conv.l_get(SSA_step.cond_literal).is_true();
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+
}
353+
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)