Skip to content

Commit f56ce2e

Browse files
authored
Merge pull request diffblue#3522 from romainbrenguier/optimize/phi_function_loop
Optimize loop of phi_function
2 parents 4f473cf + bbec003 commit f56ce2e

File tree

1 file changed

+171
-115
lines changed

1 file changed

+171
-115
lines changed

src/goto-symex/symex_goto.cpp

Lines changed: 171 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ Author: Daniel Kroening, [email protected]
1717
#include <util/expr_util.h>
1818
#include <util/invariant.h>
1919
#include <util/pointer_offset_size.h>
20-
#include <util/range.h>
2120
#include <util/std_expr.h>
2221

2322
#include <analyses/dirty.h>
23+
#include <util/simplify_expr.h>
2424

2525
void goto_symext::symex_goto(statet &state)
2626
{
@@ -353,138 +353,194 @@ void goto_symext::merge_value_sets(
353353
dest.value_set.make_union(src.value_set);
354354
}
355355

356-
void goto_symext::phi_function(
357-
const statet::goto_statet &goto_state,
358-
statet &dest_state)
356+
/// Applies f to `(k, ssa, i, j)` if the first map maps k to (ssa, i) and
357+
/// the second to (ssa', j). If the first map has an entry for k but not the
358+
/// second one then j is 0, and when the first map has no entry for k then i = 0
359+
static void for_each2(
360+
const std::map<irep_idt, std::pair<ssa_exprt, unsigned>> &first_map,
361+
const std::map<irep_idt, std::pair<ssa_exprt, unsigned>> &second_map,
362+
const std::function<void(const ssa_exprt &, unsigned, unsigned)> &f)
359363
{
360-
auto ssa_of_current_name =
361-
[&](const std::pair<irep_idt, std::pair<ssa_exprt, unsigned>> &pair) {
362-
return pair.second.first;
363-
};
364-
365-
auto dest_state_names_range =
366-
make_range(dest_state.level2.current_names)
367-
.filter(
368-
[&](const std::pair<irep_idt, std::pair<ssa_exprt, unsigned>> &pair) {
369-
// We ignore the identifiers that are already in goto_state names
370-
return goto_state.level2_current_names.count(pair.first) == 0;
371-
})
372-
.map<const ssa_exprt>(ssa_of_current_name);
373-
374-
// go over all variables to see what changed
375-
auto all_current_names_range = make_range(goto_state.level2_current_names)
376-
.map<const ssa_exprt>(ssa_of_current_name)
377-
.concat(dest_state_names_range);
378-
379-
guardt diff_guard;
380-
if(!all_current_names_range.empty())
364+
auto second_it = second_map.begin();
365+
for(const auto &first_pair : first_map)
381366
{
382-
diff_guard=goto_state.guard;
383-
384-
// this gets the diff between the guards
385-
diff_guard-=dest_state.guard;
367+
while(second_it != second_map.end() && second_it->first < first_pair.first)
368+
{
369+
f(second_it->second.first, 0, second_it->second.second);
370+
++second_it;
371+
}
372+
const ssa_exprt &ssa = first_pair.second.first;
373+
const unsigned count = first_pair.second.second;
374+
if(second_it != second_map.end() && second_it->first == first_pair.first)
375+
{
376+
f(ssa, count, second_it->second.second);
377+
++second_it;
378+
}
379+
else
380+
f(ssa, count, 0);
386381
}
387-
388-
for(const auto &ssa : all_current_names_range)
382+
while(second_it != second_map.end())
389383
{
390-
const irep_idt l1_identifier = ssa.get_identifier();
391-
const irep_idt &obj_identifier = ssa.get_object_name();
384+
f(second_it->second.first, 0, second_it->second.second);
385+
++second_it;
386+
}
387+
}
392388

393-
if(obj_identifier==guard_identifier)
394-
continue; // just a guard, don't bother
389+
/// Helper function for \c phi_function which merges the names of an identifier
390+
/// for two different states.
391+
/// \param goto_state: first state
392+
/// \param[in, out] dest_state: second state
393+
/// \param ns: namespace
394+
/// \param diff_guard: difference between the guards of the two states
395+
/// \param guard_identifier: prefix used for goto symex guards
396+
/// \param[out] log: logger for debug messages
397+
/// \param do_simplify: should the right-hand-side of the assignment that is
398+
/// added to the target be simplified
399+
/// \param[out] target: equation that will receive the resulting assignment
400+
/// \param ssa: SSA expression to merge
401+
/// \param goto_count: current level 2 count in \p goto_state of
402+
/// \p l1_identifier
403+
/// \param dest_count: level 2 count in \p dest_state of \p l1_identifier
404+
static void merge_names(
405+
const goto_symext::statet::goto_statet &goto_state,
406+
goto_symext::statet &dest_state,
407+
const namespacet &ns,
408+
const guardt &diff_guard,
409+
const irep_idt &guard_identifier,
410+
messaget &log,
411+
const bool do_simplify,
412+
symex_target_equationt &target,
413+
const ssa_exprt &ssa,
414+
const unsigned goto_count,
415+
const unsigned dest_count)
416+
{
417+
const irep_idt l1_identifier = ssa.get_identifier();
418+
const irep_idt &obj_identifier = ssa.get_object_name();
395419

396-
if(goto_state.level2_current_count(l1_identifier)==
397-
dest_state.level2.current_count(l1_identifier))
398-
continue; // not at all changed
420+
if(obj_identifier == guard_identifier)
421+
return; // just a guard, don't bother
399422

400-
// changed!
423+
if(goto_count == dest_count)
424+
return; // not at all changed
401425

402-
// shared variables are renamed on every access anyway, we don't need to
403-
// merge anything
404-
const symbolt &symbol=ns.lookup(obj_identifier);
426+
// changed!
405427

406-
// shared?
407-
if(
408-
dest_state.atomic_section_id == 0 && dest_state.threads.size() >= 2 &&
409-
(symbol.is_shared() || (dest_state.dirty)(symbol.name)))
410-
continue; // no phi nodes for shared stuff
411-
412-
// don't merge (thread-)locals across different threads, which
413-
// may have been introduced by symex_start_thread (and will
414-
// only later be removed from level2.current_names by pop_frame
415-
// once the thread is executed)
416-
if(
417-
!ssa.get_level_0().empty() &&
418-
ssa.get_level_0() != std::to_string(dest_state.source.thread_nr))
419-
continue;
428+
// shared variables are renamed on every access anyway, we don't need to
429+
// merge anything
430+
const symbolt &symbol = ns.lookup(obj_identifier);
420431

421-
exprt goto_state_rhs = ssa, dest_state_rhs = ssa;
432+
// shared?
433+
if(
434+
dest_state.atomic_section_id == 0 && dest_state.threads.size() >= 2 &&
435+
(symbol.is_shared() || (dest_state.dirty)(symbol.name)))
436+
return; // no phi nodes for shared stuff
437+
438+
// don't merge (thread-)locals across different threads, which
439+
// may have been introduced by symex_start_thread (and will
440+
// only later be removed from level2.current_names by pop_frame
441+
// once the thread is executed)
442+
const irep_idt level_0 = ssa.get_level_0();
443+
if(!level_0.empty() && level_0 != std::to_string(dest_state.source.thread_nr))
444+
return;
422445

423-
{
424-
const auto p_it = goto_state.propagation.find(l1_identifier);
446+
exprt goto_state_rhs = ssa, dest_state_rhs = ssa;
425447

426-
if(p_it != goto_state.propagation.end())
427-
goto_state_rhs=p_it->second;
428-
else
429-
to_ssa_expr(goto_state_rhs).set_level_2(
430-
goto_state.level2_current_count(l1_identifier));
431-
}
448+
{
449+
const auto p_it = goto_state.propagation.find(l1_identifier);
432450

433-
{
434-
const auto p_it = dest_state.propagation.find(l1_identifier);
451+
if(p_it != goto_state.propagation.end())
452+
goto_state_rhs = p_it->second;
453+
else
454+
to_ssa_expr(goto_state_rhs).set_level_2(goto_count);
455+
}
435456

436-
if(p_it != dest_state.propagation.end())
437-
dest_state_rhs=p_it->second;
438-
else
439-
to_ssa_expr(dest_state_rhs).set_level_2(
440-
dest_state.level2.current_count(l1_identifier));
441-
}
457+
{
458+
const auto p_it = dest_state.propagation.find(l1_identifier);
442459

443-
exprt rhs;
444-
445-
// Don't add a conditional to the assignment when:
446-
// 1. Either guard is false, so we can't follow that branch.
447-
// 2. Either identifier is of generation zero, and so hasn't been
448-
// initialized and therefor an invalid target.
449-
if(dest_state.guard.is_false())
450-
rhs=goto_state_rhs;
451-
else if(goto_state.guard.is_false())
452-
rhs=dest_state_rhs;
453-
else if(goto_state.level2_current_count(l1_identifier) == 0)
454-
{
455-
rhs = dest_state_rhs;
456-
}
457-
else if(dest_state.level2.current_count(l1_identifier) == 0)
458-
{
459-
rhs = goto_state_rhs;
460-
}
460+
if(p_it != dest_state.propagation.end())
461+
dest_state_rhs = p_it->second;
461462
else
462-
{
463-
rhs=if_exprt(diff_guard.as_expr(), goto_state_rhs, dest_state_rhs);
464-
do_simplify(rhs);
465-
}
463+
to_ssa_expr(dest_state_rhs).set_level_2(dest_count);
464+
}
466465

467-
ssa_exprt new_lhs = ssa;
468-
const bool record_events=dest_state.record_events;
469-
dest_state.record_events=false;
470-
dest_state.assignment(new_lhs, rhs, ns, true, true);
471-
dest_state.record_events=record_events;
472-
473-
log.conditional_output(
474-
log.debug(),
475-
[this, &new_lhs](messaget::mstreamt &mstream) {
476-
mstream << "Assignment to " << new_lhs.get_identifier()
477-
<< " [" << pointer_offset_bits(new_lhs.type(), ns).value_or(0) << " bits]"
478-
<< messaget::eom;
479-
});
480-
481-
target.assignment(
482-
true_exprt(),
483-
new_lhs, new_lhs, new_lhs.get_original_expr(),
484-
rhs,
485-
dest_state.source,
486-
symex_targett::assignment_typet::PHI);
466+
exprt rhs;
467+
468+
// Don't add a conditional to the assignment when:
469+
// 1. Either guard is false, so we can't follow that branch.
470+
// 2. Either identifier is of generation zero, and so hasn't been
471+
// initialized and therefor an invalid target.
472+
if(dest_state.guard.is_false())
473+
rhs = goto_state_rhs;
474+
else if(goto_state.guard.is_false())
475+
rhs = dest_state_rhs;
476+
else if(goto_count == 0)
477+
{
478+
rhs = dest_state_rhs;
487479
}
480+
else if(dest_count == 0)
481+
{
482+
rhs = goto_state_rhs;
483+
}
484+
else
485+
{
486+
rhs = if_exprt(diff_guard.as_expr(), goto_state_rhs, dest_state_rhs);
487+
if(do_simplify)
488+
simplify(rhs, ns);
489+
}
490+
491+
ssa_exprt new_lhs = ssa;
492+
const bool record_events = dest_state.record_events;
493+
dest_state.record_events = false;
494+
dest_state.assignment(new_lhs, rhs, ns, true, true);
495+
dest_state.record_events = record_events;
496+
497+
log.conditional_output(
498+
log.debug(), [ns, &new_lhs](messaget::mstreamt &mstream) {
499+
mstream << "Assignment to " << new_lhs.get_identifier() << " ["
500+
<< pointer_offset_bits(new_lhs.type(), ns).value_or(0) << " bits]"
501+
<< messaget::eom;
502+
});
503+
504+
target.assignment(
505+
true_exprt(),
506+
new_lhs,
507+
new_lhs,
508+
new_lhs.get_original_expr(),
509+
rhs,
510+
dest_state.source,
511+
symex_targett::assignment_typet::PHI);
512+
}
513+
514+
void goto_symext::phi_function(
515+
const statet::goto_statet &goto_state,
516+
statet &dest_state)
517+
{
518+
if(
519+
goto_state.level2_current_names.empty() &&
520+
dest_state.level2.current_names.empty())
521+
return;
522+
523+
guardt diff_guard = goto_state.guard;
524+
// this gets the diff between the guards
525+
diff_guard -= dest_state.guard;
526+
527+
for_each2(
528+
goto_state.level2_current_names,
529+
dest_state.level2.current_names,
530+
[&](const ssa_exprt &ssa, unsigned goto_count, unsigned dest_count) {
531+
merge_names(
532+
goto_state,
533+
dest_state,
534+
ns,
535+
diff_guard,
536+
guard_identifier,
537+
log,
538+
symex_config.simplify_opt,
539+
target,
540+
ssa,
541+
goto_count,
542+
dest_count);
543+
});
488544
}
489545

490546
void goto_symext::loop_bound_exceeded(

0 commit comments

Comments
 (0)