Skip to content

Commit b1e951c

Browse files
committed
Introduce value-set supported simplifier for goto-symex
Move `try_evaluate_pointer_comparison` to a simplifier that can eventually support more cases than just equalities in GOTO conditions. The initial change does not alter behaviour (except that previously `try_evaluate_pointer_comparison` was even used when simplification was disabled). A side-effect is that we can also clean up renamedt.
1 parent 3c915eb commit b1e951c

16 files changed

+273
-292
lines changed

src/goto-symex/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ SRC = auto_objects.cpp \
1818
shadow_memory_util.cpp \
1919
show_program.cpp \
2020
show_vcc.cpp \
21+
simplify_expr_with_value_set.cpp \
2122
slice.cpp \
2223
solver_hardness.cpp \
2324
ssa_step.cpp \

src/goto-symex/field_sensitivity.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ exprt field_sensitivityt::apply(
160160
// place the entire index expression, not just the array operand, in an
161161
// SSA expression
162162
ssa_exprt tmp = to_ssa_expr(index.array());
163-
auto l2_index = state.rename(index.index(), ns);
163+
auto l2_index = state.rename(index.index(), ns).get();
164164
if(should_simplify)
165-
l2_index.simplify(ns);
165+
simplify(l2_index, ns);
166166
bool was_l2 = !tmp.get_level_2().empty();
167167
exprt l2_size =
168168
state.rename(to_array_type(index.array().type()).size(), ns).get();
@@ -181,14 +181,14 @@ exprt field_sensitivityt::apply(
181181
numeric_cast_v<mp_integer>(to_constant_expr(l2_size)) <=
182182
max_field_sensitivity_array_size)
183183
{
184-
if(l2_index.get().is_constant())
184+
if(l2_index.is_constant())
185185
{
186186
// place the entire index expression, not just the array operand,
187187
// in an SSA expression
188188
ssa_exprt ssa_array = to_ssa_expr(index.array());
189189
ssa_array.remove_level_2();
190190
index.array() = ssa_array.get_original_expr();
191-
index.index() = l2_index.get();
191+
index.index() = l2_index;
192192
tmp.set_expression(index);
193193
if(was_l2)
194194
{

src/goto-symex/goto_symex.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -876,20 +876,4 @@ void symex_transition(
876876
goto_programt::const_targett to,
877877
bool is_backwards_goto);
878878

879-
/// Try to evaluate pointer comparisons where they can be trivially determined
880-
/// using the value-set. This is optional as all it does is allow symex to
881-
/// resolve some comparisons itself and therefore create a simpler formula for
882-
/// the SAT solver.
883-
/// \param [in,out] condition: An L2-renamed expression with boolean type
884-
/// \param value_set: The value-set for determining what pointer-typed symbols
885-
/// might possibly point to
886-
/// \param language_mode: The language mode
887-
/// \param ns: A namespace
888-
/// \return The possibly modified condition
889-
renamedt<exprt, L2> try_evaluate_pointer_comparisons(
890-
renamedt<exprt, L2> condition,
891-
const value_sett &value_set,
892-
const irep_idt &language_mode,
893-
const namespacet &ns);
894-
895879
#endif // CPROVER_GOTO_SYMEX_GOTO_SYMEX_H

src/goto-symex/goto_symex_state.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Author: Daniel Kroening, [email protected]
1818
#include <util/exception_utils.h>
1919
#include <util/expr_util.h>
2020
#include <util/invariant.h>
21+
#include <util/simplify_expr.h>
2122
#include <util/std_expr.h>
2223

2324
#include <analyses/dirty.h>

src/goto-symex/renamed.h

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ Author: Romain Brenguier, [email protected]
1111
#ifndef CPROVER_GOTO_SYMEX_RENAMED_H
1212
#define CPROVER_GOTO_SYMEX_RENAMED_H
1313

14-
#include <util/expr_iterator.h>
15-
#include <util/simplify_expr.h>
14+
#include <util/simplify_expr_class.h>
15+
1616
#include <util/std_expr.h>
1717

1818
class ssa_exprt;
@@ -42,14 +42,11 @@ class renamedt : private underlyingt
4242
return static_cast<const underlyingt &>(*this);
4343
}
4444

45-
void simplify(const namespacet &ns)
45+
void simplify(simplify_exprt &simplifier)
4646
{
47-
(void)::simplify(value(), ns);
47+
simplifier.simplify(value());
4848
}
4949

50-
using mutator_functiont =
51-
std::function<std::optional<renamedt>(const renamedt &)>;
52-
5350
private:
5451
underlyingt &value()
5552
{
@@ -62,49 +59,10 @@ class renamedt : private underlyingt
6259
friend struct symex_level2t;
6360
friend class goto_symex_statet;
6461

65-
template <levelt make_renamed_level>
66-
friend renamedt<exprt, make_renamed_level>
67-
make_renamed(constant_exprt constant);
68-
69-
template <levelt selectively_mutate_level>
70-
friend void selectively_mutate(
71-
renamedt<exprt, selectively_mutate_level> &renamed,
72-
typename renamedt<exprt, selectively_mutate_level>::mutator_functiont
73-
get_mutated_expr);
74-
7562
/// Only the friend classes can create renamedt objects
7663
explicit renamedt(underlyingt value) : underlyingt(std::move(value))
7764
{
7865
}
7966
};
8067

81-
template <levelt level>
82-
renamedt<exprt, level> make_renamed(constant_exprt constant)
83-
{
84-
return renamedt<exprt, level>(std::move(constant));
85-
}
86-
87-
/// This permits replacing subexpressions of the renamed value, so long as
88-
/// each replacement is consistent with our current renaming level (for
89-
/// example, replacing subexpressions of L2 expressions with ones which are
90-
/// themselves L2 renamed).
91-
/// The passed function will be called with each expression node in preorder
92-
/// (i.e. parent expressions before children), and should return an empty
93-
/// optional to make no change or a renamed expression to make a change.
94-
template <levelt level>
95-
void selectively_mutate(
96-
renamedt<exprt, level> &renamed,
97-
typename renamedt<exprt, level>::mutator_functiont get_mutated_expr)
98-
{
99-
for(auto it = renamed.depth_begin(), itend = renamed.depth_end(); it != itend;
100-
++it)
101-
{
102-
std::optional<renamedt<exprt, level>> replacement =
103-
get_mutated_expr(static_cast<const renamedt<exprt, level> &>(*it));
104-
105-
if(replacement)
106-
it.mutate() = std::move(replacement->value());
107-
}
108-
}
109-
11068
#endif // CPROVER_GOTO_SYMEX_RENAMED_H

src/goto-symex/shadow_memory_util.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Author: Peter Schrammel
2020
#include <util/invariant.h>
2121
#include <util/namespace.h>
2222
#include <util/pointer_offset_size.h>
23+
#include <util/simplify_expr.h>
2324
#include <util/ssa_expr.h>
2425
#include <util/std_expr.h>
2526
#include <util/string_constant.h>
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*******************************************************************\
2+
3+
Module: Variant of simplify_exprt that uses a value_sett
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#include "simplify_expr_with_value_set.h"
10+
11+
#include <util/expr_util.h>
12+
#include <util/pointer_expr.h>
13+
#include <util/simplify_expr.h>
14+
#include <util/ssa_expr.h>
15+
16+
#include <pointer-analysis/add_failed_symbols.h>
17+
#include <pointer-analysis/value_set.h>
18+
#include <pointer-analysis/value_set_dereference.h>
19+
20+
#include "goto_symex_can_forward_propagate.h"
21+
22+
/// Try to evaluate a simple pointer comparison.
23+
/// \param operation: ID_equal or ID_not_equal
24+
/// \param symbol_expr: The symbol expression in the condition
25+
/// \param other_operand: The other expression in the condition; we only support
26+
/// an address of expression, a typecast of an address of expression or a
27+
/// null pointer, and return an empty std::optional in all other cases
28+
/// \param value_set: The value-set for looking up what the symbol can point to
29+
/// \param language_mode: The language mode
30+
/// \param ns: A namespace
31+
/// \return If we were able to evaluate the condition as true or false then we
32+
/// return that, otherwise we return an empty std::optional
33+
static std::optional<exprt> try_evaluate_pointer_comparison(
34+
const irep_idt &operation,
35+
const symbol_exprt &symbol_expr,
36+
const exprt &other_operand,
37+
const value_sett &value_set,
38+
const irep_idt language_mode,
39+
const namespacet &ns)
40+
{
41+
const constant_exprt *constant_expr =
42+
expr_try_dynamic_cast<constant_exprt>(other_operand);
43+
44+
if(
45+
skip_typecast(other_operand).id() != ID_address_of &&
46+
(!constant_expr || !constant_expr->is_null_pointer()))
47+
{
48+
return {};
49+
}
50+
51+
const ssa_exprt *ssa_symbol_expr =
52+
expr_try_dynamic_cast<ssa_exprt>(symbol_expr);
53+
54+
ssa_exprt l1_expr{*ssa_symbol_expr};
55+
l1_expr.remove_level_2();
56+
const std::vector<exprt> value_set_elements =
57+
value_set.get_value_set(l1_expr, ns);
58+
59+
bool constant_found = false;
60+
61+
for(const auto &value_set_element : value_set_elements)
62+
{
63+
if(
64+
value_set_element.id() == ID_unknown ||
65+
value_set_element.id() == ID_invalid ||
66+
is_failed_symbol(
67+
to_object_descriptor_expr(value_set_element).root_object()) ||
68+
to_object_descriptor_expr(value_set_element).offset().id() == ID_unknown)
69+
{
70+
// We can't conclude anything about the value-set
71+
return {};
72+
}
73+
74+
if(!constant_found)
75+
{
76+
if(value_set_dereferencet::should_ignore_value(
77+
value_set_element, false, language_mode))
78+
{
79+
continue;
80+
}
81+
82+
value_set_dereferencet::valuet value =
83+
value_set_dereferencet::build_reference_to(
84+
value_set_element, symbol_expr, ns);
85+
86+
// use the simplifier to test equality as we need to skip over typecasts
87+
// and cannot rely on canonical representations, which would permit a
88+
// simple syntactic equality test
89+
exprt test_equal = simplify_expr(
90+
equal_exprt{
91+
typecast_exprt::conditional_cast(value.pointer, other_operand.type()),
92+
other_operand},
93+
ns);
94+
if(test_equal.is_true())
95+
{
96+
constant_found = true;
97+
// We can't break because we have to make sure we find any instances of
98+
// ID_unknown or ID_invalid
99+
}
100+
else if(!test_equal.is_false())
101+
{
102+
// We can't conclude anything about the value-set
103+
return {};
104+
}
105+
}
106+
}
107+
108+
if(!constant_found)
109+
{
110+
// The symbol cannot possibly have the value \p other_operand because it
111+
// isn't in the symbol's value-set
112+
return operation == ID_equal ? static_cast<exprt>(false_exprt{})
113+
: static_cast<exprt>(true_exprt{});
114+
}
115+
else if(value_set_elements.size() == 1)
116+
{
117+
// The symbol must have the value \p other_operand because it is the only
118+
// thing in the symbol's value-set
119+
return operation == ID_equal ? static_cast<exprt>(true_exprt{})
120+
: static_cast<exprt>(false_exprt{});
121+
}
122+
else
123+
{
124+
return {};
125+
}
126+
}
127+
128+
simplify_exprt::resultt<> simplify_expr_with_value_sett::simplify_inequality(
129+
const binary_relation_exprt &expr)
130+
{
131+
if(expr.id() != ID_equal && expr.id() != ID_notequal)
132+
return simplify_exprt::simplify_inequality(expr);
133+
134+
if(!can_cast_type<pointer_typet>(to_binary_expr(expr).op0().type()))
135+
return simplify_exprt::simplify_inequality(expr);
136+
137+
exprt lhs = to_binary_expr(expr).op0(), rhs = to_binary_expr(expr).op1();
138+
if(can_cast_expr<symbol_exprt>(rhs))
139+
std::swap(lhs, rhs);
140+
141+
const symbol_exprt *symbol_expr_lhs =
142+
expr_try_dynamic_cast<symbol_exprt>(lhs);
143+
144+
if(!symbol_expr_lhs)
145+
return simplify_exprt::simplify_inequality(expr);
146+
147+
if(!goto_symex_can_forward_propagatet(ns)(rhs))
148+
return simplify_exprt::simplify_inequality(expr);
149+
150+
auto maybe_constant = try_evaluate_pointer_comparison(
151+
expr.id(), *symbol_expr_lhs, rhs, value_set, language_mode, ns);
152+
if(maybe_constant.has_value())
153+
return changed(*maybe_constant);
154+
else
155+
return unchanged(expr);
156+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*******************************************************************\
2+
3+
Module: Variant of simplify_exprt that uses a value_sett
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_GOTO_SYMEX_SIMPLIFY_EXPR_WITH_VALUE_SET_H
10+
#define CPROVER_GOTO_SYMEX_SIMPLIFY_EXPR_WITH_VALUE_SET_H
11+
12+
#include <util/simplify_expr_class.h>
13+
14+
class value_sett;
15+
16+
class simplify_expr_with_value_sett : public simplify_exprt
17+
{
18+
public:
19+
simplify_expr_with_value_sett(
20+
const value_sett &_vs,
21+
const namespacet &_ns,
22+
const irep_idt &_mode)
23+
: simplify_exprt(_ns), value_set(_vs), language_mode(_mode)
24+
{
25+
}
26+
27+
[[nodiscard]] resultt<>
28+
simplify_inequality(const binary_relation_exprt &) override;
29+
30+
protected:
31+
const value_sett &value_set;
32+
const irep_idt language_mode;
33+
};
34+
35+
#endif // CPROVER_GOTO_SYMEX_SIMPLIFY_EXPR_WITH_VALUE_SET_H

src/goto-symex/symex_assign.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Author: Daniel Kroening, [email protected]
1515
#include <util/expr_util.h>
1616
#include <util/pointer_expr.h>
1717
#include <util/range.h>
18+
#include <util/simplify_expr.h>
1819

1920
#include "expr_skeleton.h"
2021
#include "goto_symex_state.h"

src/goto-symex/symex_builtin_functions.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,10 @@ void goto_symext::symex_output(
460460
{
461461
renamedt<exprt, L2> l2_arg = state.rename(code.operands()[i], ns);
462462
if(symex_config.simplify_opt)
463-
l2_arg.simplify(ns);
463+
{
464+
simplify_exprt simp{ns};
465+
l2_arg.simplify(simp);
466+
}
464467
args.emplace_back(l2_arg);
465468
}
466469

0 commit comments

Comments
 (0)