15
15
#include < memory>
16
16
17
17
#include < util/exception_utils.h>
18
+ #include < util/expr_iterator.h>
18
19
#include < util/expr_util.h>
19
20
#include < util/make_unique.h>
20
21
#include < util/mathematical_expr.h>
@@ -124,6 +125,7 @@ void goto_symext::vcc(
124
125
125
126
void goto_symext::symex_assume (statet &state, const exprt &cond)
126
127
{
128
+
127
129
exprt simplified_cond=cond;
128
130
129
131
clean_expr (simplified_cond, state, false );
@@ -135,6 +137,13 @@ void goto_symext::symex_assume(statet &state, const exprt &cond)
135
137
bool apply_condition_successful =
136
138
state.apply_condition (simplified_cond, state, ns);
137
139
140
+ if (!apply_condition_successful)
141
+ {
142
+ // try to update the value sets using information contained in the guard
143
+ try_filter_value_sets (
144
+ state, cond, state.value_set , &state.value_set , nullptr , ns);
145
+ }
146
+
138
147
symex_assume_l2 (state, simplified_cond);
139
148
}
140
149
@@ -544,3 +553,158 @@ void goto_symext::symex_step(
544
553
UNREACHABLE;
545
554
}
546
555
}
556
+
557
+ // / Check if an expression only contains one unique symbol (possibly repeated
558
+ // / multiple times)
559
+ // / \param expr: The expression to examine
560
+ // / \return If only one unique symbol occurs in \p expr then return it;
561
+ // / otherwise return an empty optionalt
562
+ static optionalt<symbol_exprt>
563
+ find_unique_pointer_typed_symbol (const exprt &expr)
564
+ {
565
+ optionalt<symbol_exprt> return_value;
566
+ for (auto it = expr.depth_cbegin (); it != expr.depth_cend (); ++it)
567
+ {
568
+ const symbol_exprt *symbol_expr = expr_try_dynamic_cast<symbol_exprt>(*it);
569
+ if (symbol_expr && can_cast_type<pointer_typet>(symbol_expr->type ()))
570
+ {
571
+ // If we already have a potential return value, check if it is the same
572
+ // symbol, and return an empty optionalt if not
573
+ if (return_value && *symbol_expr != *return_value)
574
+ {
575
+ return {};
576
+ }
577
+ return_value = *symbol_expr;
578
+ }
579
+ }
580
+
581
+ // Either expr contains no pointer-typed symbols or it contains one unique
582
+ // pointer-typed symbol, possibly repeated multiple times
583
+ return return_value;
584
+ }
585
+
586
+ // / This is a simplified version of value_set_dereferencet::build_reference_to.
587
+ // / It ignores the ID_dynamic_object case (which doesn't occur in goto-symex)
588
+ // / and gives up for integer addresses and non-trivial symbols
589
+ // / \param value_set_element: An element of a value-set
590
+ // / \param type: the type of the expression that might point to
591
+ // / \p value_set_element
592
+ // / \return An expression for the value of the pointer indicated by \p
593
+ // / value_set_element if it is easy to determine, or an empty optionalt
594
+ // / otherwise
595
+ static optionalt<exprt>
596
+ value_set_element_to_expr (exprt value_set_element, pointer_typet type)
597
+ {
598
+ const object_descriptor_exprt *object_descriptor =
599
+ expr_try_dynamic_cast<object_descriptor_exprt>(value_set_element);
600
+ if (!object_descriptor)
601
+ {
602
+ return {};
603
+ }
604
+
605
+ const exprt &root_object = object_descriptor->root_object ();
606
+ const exprt &object = object_descriptor->object ();
607
+
608
+ if (root_object.id () == ID_null_object)
609
+ {
610
+ return null_pointer_exprt{type};
611
+ }
612
+ else if (root_object.id () == ID_integer_address)
613
+ {
614
+ return {};
615
+ }
616
+ else
617
+ {
618
+ // We should do something like
619
+ // value_set_dereference::dereference_type_compare, which deals with
620
+ // arrays having types containing void
621
+ if (object_descriptor->offset ().is_zero () && object.type () == type.subtype ())
622
+ {
623
+ return address_of_exprt (object);
624
+ }
625
+ else
626
+ {
627
+ return {};
628
+ }
629
+ }
630
+ }
631
+
632
+ void goto_symext::try_filter_value_sets (
633
+ goto_symex_statet &state,
634
+ exprt condition,
635
+ const value_sett &original_value_set,
636
+ value_sett *jump_taken_value_set,
637
+ value_sett *jump_not_taken_value_set,
638
+ const namespacet &ns)
639
+ {
640
+ condition = state.rename <L1>(std::move (condition), ns).get ();
641
+
642
+ optionalt<symbol_exprt> symbol_expr =
643
+ find_unique_pointer_typed_symbol (condition);
644
+
645
+ if (!symbol_expr)
646
+ {
647
+ return ;
648
+ }
649
+
650
+ const pointer_typet &symbol_type = to_pointer_type (symbol_expr->type ());
651
+
652
+ value_setst::valuest value_set_elements;
653
+ original_value_set.get_value_set (*symbol_expr, value_set_elements, ns);
654
+
655
+ // Try evaluating the condition with the symbol replaced by a pointer to each
656
+ // one of its possible values in turn. If that leads to a true for some
657
+ // value_set_element then we can delete it from the value set that will be
658
+ // used if the condition is false, and vice versa.
659
+ for (const auto &value_set_element : value_set_elements)
660
+ {
661
+ optionalt<exprt> possible_value =
662
+ value_set_element_to_expr (value_set_element, symbol_type);
663
+
664
+ if (!possible_value)
665
+ {
666
+ continue ;
667
+ }
668
+
669
+ exprt modified_condition (condition);
670
+
671
+ address_of_aware_replace_symbolt replace_symbol{};
672
+ replace_symbol.insert (*symbol_expr, *possible_value);
673
+ replace_symbol (modified_condition);
674
+
675
+ // This do_simplify() is needed for the following reason: if condition is
676
+ // `*p == a` and we replace `p` with `&a` then we get `*&a == a`. Suppose
677
+ // our constant propagation knows that `a` is `. Without this call to
678
+ // do_simplify(), state.rename() turns this into `*&a == 1` (because
679
+ // rename() doesn't do constant propagation inside addresses), which
680
+ // do_simplify() turns into `a == 1`, which cannot be evaluated as true
681
+ // without another round of constant propagation.
682
+ // It would be sufficient to replace this call to do_simplify() with
683
+ // something that just replaces `*&x` with `x` whenever it finds it.
684
+ do_simplify (modified_condition);
685
+
686
+ const bool record_events = state.record_events ;
687
+ state.record_events = false ;
688
+ modified_condition = state.rename (std::move (modified_condition), ns).get ();
689
+ state.record_events = record_events;
690
+
691
+ do_simplify (modified_condition);
692
+
693
+ if (jump_taken_value_set && modified_condition.is_false ())
694
+ {
695
+ value_sett::entryt *entry = const_cast <value_sett::entryt *>(
696
+ jump_taken_value_set->get_entry_for_symbol (
697
+ symbol_expr->get_identifier (), symbol_type, " " , ns));
698
+ jump_taken_value_set->erase_value_from_entry (
699
+ *entry, value_set_element);
700
+ }
701
+ else if (jump_not_taken_value_set && modified_condition.is_true ())
702
+ {
703
+ value_sett::entryt *entry = const_cast <value_sett::entryt *>(
704
+ jump_not_taken_value_set->get_entry_for_symbol (
705
+ symbol_expr->get_identifier (), symbol_type, " " , ns));
706
+ jump_not_taken_value_set->erase_value_from_entry (
707
+ *entry, value_set_element);
708
+ }
709
+ }
710
+ }
0 commit comments