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