Skip to content

Commit 9c516d7

Browse files
Owensmowton
Owen
authored andcommitted
Evaluate some pointer comparisons as true or false
Use the value-set to evaluate pointer comparisons as true or false where possible
1 parent a989cca commit 9c516d7

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

src/goto-symex/renaming_level.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ Author: Romain Brenguier, [email protected]
1515
#include <map>
1616
#include <unordered_set>
1717

18+
#include <util/expr_iterator.h>
1819
#include <util/irep.h>
20+
#include <util/range.h>
1921
#include <util/sharing_map.h>
2022
#include <util/simplify_expr.h>
2123
#include <util/ssa_expr.h>
@@ -87,6 +89,9 @@ class renamedt : private underlyingt
8789
(void)::simplify(value(), ns);
8890
}
8991

92+
using mutator_functiont =
93+
std::function<optionalt<renamedt>(const renamedt &)>;
94+
9095
private:
9196
underlyingt &value()
9297
{
@@ -98,12 +103,51 @@ class renamedt : private underlyingt
98103
friend struct symex_level2t;
99104
friend class goto_symex_statet;
100105

106+
template <levelt make_renamed_level>
107+
friend renamedt<exprt, make_renamed_level>
108+
make_renamed(constant_exprt constant);
109+
110+
template <levelt selectively_mutate_level>
111+
friend void selectively_mutate(
112+
renamedt<exprt, selectively_mutate_level> &renamed,
113+
typename renamedt<exprt, selectively_mutate_level>::mutator_functiont
114+
get_mutated_expr);
115+
101116
/// Only the friend classes can create renamedt objects
102117
explicit renamedt(underlyingt value) : underlyingt(std::move(value))
103118
{
104119
}
105120
};
106121

122+
template <levelt level>
123+
renamedt<exprt, level> make_renamed(constant_exprt constant)
124+
{
125+
return renamedt<exprt, level>(std::move(constant));
126+
}
127+
128+
/// This permits replacing subexpressions of the renamed value, so long as
129+
/// each replacement is consistent with our current renaming level (for
130+
/// example, replacing subexpressions of L2 expressions with ones which are
131+
/// themselves L2 renamed).
132+
/// The passed function will be called with each expression node in preorder
133+
/// (i.e. parent expressions before children), and should return an empty
134+
/// optional to make no change or a renamed expression to make a change.
135+
template <levelt level>
136+
void selectively_mutate(
137+
renamedt<exprt, level> &renamed,
138+
typename renamedt<exprt, level>::mutator_functiont get_mutated_expr)
139+
{
140+
for(auto it = renamed.depth_begin(), itend = renamed.depth_end(); it != itend;
141+
++it)
142+
{
143+
optionalt<renamedt<exprt, level>> replacement =
144+
get_mutated_expr(static_cast<const renamedt<exprt, level> &>(*it));
145+
146+
if(replacement)
147+
it.mutate() = std::move(replacement->value());
148+
}
149+
}
150+
107151
/// Functor to set the level 0 renaming of SSA expressions.
108152
/// Level 0 corresponds to threads.
109153
/// The renaming is built for one particular interleaving.

src/goto-symex/symex_goto.cpp

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Author: Daniel Kroening, [email protected]
1010
/// Symbolic Execution
1111

1212
#include "goto_symex.h"
13+
#include "goto_symex_is_constant.h"
1314

1415
#include <algorithm>
1516

@@ -19,6 +20,7 @@ Author: Daniel Kroening, [email protected]
1920
#include <util/pointer_offset_size.h>
2021
#include <util/std_expr.h>
2122

23+
#include <pointer-analysis/value_set_dereference.h>
2224
#include <util/simplify_expr.h>
2325

2426
void goto_symext::apply_goto_condition(
@@ -65,6 +67,161 @@ void goto_symext::apply_goto_condition(
6567
}
6668
}
6769

70+
/// Try to evaluate a simple pointer comparison.
71+
/// \param operation: ID_equal or ID_not_equal
72+
/// \param symbol_expr: The symbol expression in the condition
73+
/// \param other_operand: The other expression in the condition - must pass
74+
/// goto_symex_is_constant, and since it is pointer-typed it must therefore
75+
/// be an address of expression, a typecast of an address of expression or a
76+
/// null pointer
77+
/// \param value_set: The value-set for looking up what the symbol can point to
78+
/// \param language_mode: The language mode
79+
/// \param ns: A namespace
80+
/// \return If we were able to evaluate the condition as true or false then we
81+
/// return that, otherwise we return an empty optionalt
82+
static optionalt<renamedt<exprt, L2>> try_evaluate_pointer_comparison(
83+
irep_idt operation,
84+
const symbol_exprt &symbol_expr,
85+
const exprt &other_operand,
86+
const value_sett &value_set,
87+
const irep_idt language_mode,
88+
const namespacet &ns)
89+
{
90+
const constant_exprt *constant_expr =
91+
expr_try_dynamic_cast<constant_exprt>(other_operand);
92+
93+
INVARIANT(
94+
skip_typecast(other_operand).id() == ID_address_of ||
95+
(constant_expr && constant_expr->get_value() == ID_NULL),
96+
"An expression that passes goto_symex_is_constant and has "
97+
"pointer-type must be an address of expression (possibly with some "
98+
"typecasts) or a null pointer");
99+
100+
const ssa_exprt *ssa_symbol_expr =
101+
expr_try_dynamic_cast<ssa_exprt>(symbol_expr);
102+
103+
value_setst::valuest value_set_elements;
104+
value_set.get_value_set(
105+
ssa_symbol_expr->get_l1_object(), value_set_elements, ns);
106+
107+
bool constant_found = false;
108+
109+
for(const auto &value_set_element : value_set_elements)
110+
{
111+
if(
112+
value_set_element.id() == ID_unknown ||
113+
value_set_element.id() == ID_invalid)
114+
{
115+
// If ID_unknown or ID_invalid are in the value-set then we can't
116+
// conclude anything about it
117+
return {};
118+
}
119+
120+
if(!constant_found)
121+
{
122+
if(value_set_dereferencet::should_ignore_value(
123+
value_set_element, false, language_mode))
124+
{
125+
continue;
126+
}
127+
128+
value_set_dereferencet::valuet value =
129+
value_set_dereferencet::build_reference_to(
130+
value_set_element, symbol_expr, ns);
131+
132+
if(value.pointer == other_operand)
133+
{
134+
constant_found = true;
135+
// We can't break because we have to make sure we find any instances of
136+
// ID_unknown or ID_invalid
137+
}
138+
}
139+
}
140+
141+
if(!constant_found)
142+
{
143+
// The symbol cannot possibly have the value \p other_operand because it
144+
// isn't in the symbol's value-set
145+
return operation == ID_equal ? make_renamed<L2>(false_exprt{})
146+
: make_renamed<L2>(true_exprt{});
147+
}
148+
else if(value_set_elements.size() == 1)
149+
{
150+
// The symbol must have the value \p other_operand because it is the only
151+
// thing in the symbol's value-set
152+
return operation == ID_equal ? make_renamed<L2>(true_exprt{})
153+
: make_renamed<L2>(false_exprt{});
154+
}
155+
else
156+
{
157+
return {};
158+
}
159+
}
160+
161+
/// Check if we have a simple pointer comparison, and if so try to evaluate it.
162+
/// \param renamed_expr: The L2-renamed expression to check
163+
/// \param value_set: The value-set for looking up what the symbol can point to
164+
/// \param language_mode: The language mode
165+
/// \param ns: A namespace
166+
/// \return If we were able to evaluate the condition as true or false then we
167+
/// return that, otherwise we return an empty optionalt
168+
static optionalt<renamedt<exprt, L2>> try_evaluate_pointer_comparison(
169+
const renamedt<exprt, L2> &renamed_expr,
170+
const value_sett &value_set,
171+
const irep_idt language_mode,
172+
const namespacet &ns)
173+
{
174+
const exprt &expr = renamed_expr.get();
175+
176+
if(expr.id() != ID_equal && expr.id() != ID_notequal)
177+
return {};
178+
179+
if(!can_cast_type<pointer_typet>(expr.op0().type()))
180+
return {};
181+
182+
exprt lhs = expr.op0(), rhs = expr.op1();
183+
if(can_cast_expr<symbol_exprt>(rhs))
184+
std::swap(lhs, rhs);
185+
186+
const symbol_exprt *symbol_expr_lhs =
187+
expr_try_dynamic_cast<symbol_exprt>(lhs);
188+
189+
if(!symbol_expr_lhs)
190+
return {};
191+
192+
if(!goto_symex_is_constantt()(rhs))
193+
return {};
194+
195+
return try_evaluate_pointer_comparison(
196+
expr.id(), *symbol_expr_lhs, rhs, value_set, language_mode, ns);
197+
}
198+
199+
/// Try to evaluate pointer comparisons where they can be trivially determined
200+
/// using the value-set. This is optional as all it does is allow symex to
201+
/// resolve some comparisons itself and therefore create a simpler formula for
202+
/// the SAT solver.
203+
/// \param [in,out] condition: An L2-renamed expression with boolean type
204+
/// \param value_set: The value-set for determining what pointer-typed symbols
205+
/// might possibly point to
206+
/// \param language_mode: The language mode
207+
/// \param ns: A namespace
208+
/// \return The possibly modified condition
209+
static renamedt<exprt, L2> try_evaluate_pointer_comparisons(
210+
renamedt<exprt, L2> condition,
211+
const value_sett &value_set,
212+
const irep_idt language_mode,
213+
const namespacet &ns)
214+
{
215+
selectively_mutate(
216+
condition,
217+
[&value_set, &language_mode, &ns](const renamedt<exprt, L2> &expr) {
218+
return try_evaluate_pointer_comparison(
219+
expr, value_set, language_mode, ns);
220+
});
221+
222+
return condition;
223+
}
224+
68225
void goto_symext::symex_goto(statet &state)
69226
{
70227
const goto_programt::instructiont &instruction=*state.source.pc;
@@ -73,6 +230,8 @@ void goto_symext::symex_goto(statet &state)
73230
clean_expr(new_guard, state, false);
74231

75232
renamedt<exprt, L2> renamed_guard = state.rename(std::move(new_guard), ns);
233+
renamed_guard = try_evaluate_pointer_comparisons(
234+
std::move(renamed_guard), state.value_set, language_mode, ns);
76235
if(symex_config.simplify_opt)
77236
renamed_guard.simplify(ns);
78237
new_guard = renamed_guard.get();

0 commit comments

Comments
 (0)