Skip to content

Commit 90712ff

Browse files
author
Owen
committed
Evaluate some pointer comparisons as true or false
Use the value-set to evaluate pointer comparisons as true or false where possible
1 parent 7247b7c commit 90712ff

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

src/goto-symex/renaming_level.h

Lines changed: 32 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/simplify_expr.h>
2022
#include <util/ssa_expr.h>
2123

@@ -87,20 +89,50 @@ class renamedt
8789
(void)::simplify(value, ns);
8890
}
8991

92+
typedef std::function<optionalt<renamedt<exprt, level>>(const exprt &)>
93+
expr_mutator_functiont;
94+
95+
/// This permits replacing subexpressions of the renamed value, so long as
96+
/// each replacement is consistent with our current renaming level (for
97+
/// example, replacing subexpressions of L2 expressions with ones which are
98+
/// themselves L2 renamed).
99+
/// The passed function will be called with each expression node in preorder
100+
/// (i.e. parent expressions before children), and should return an empty
101+
/// optional to make no change or a renamed expression to make a change.
102+
void selectively_mutate(expr_mutator_functiont get_mutated_expr)
103+
{
104+
for(
105+
auto it = value.depth_begin(), itend = value.depth_end();
106+
it != itend;
107+
++it)
108+
{
109+
auto replacement = get_mutated_expr(*it);
110+
if(replacement)
111+
it.mutate() = replacement->get();
112+
}
113+
}
114+
90115
private:
91116
underlyingt value;
92117

93118
friend struct symex_level0t;
94119
friend struct symex_level1t;
95120
friend struct symex_level2t;
96121
friend class goto_symex_statet;
122+
template <levelt make_renamed_level>
123+
friend renamedt<exprt, make_renamed_level> make_renamed(constant_exprt constant);
97124

98125
/// Only the friend classes can create renamedt objects
99126
explicit renamedt(underlyingt value) : value(std::move(value))
100127
{
101128
}
102129
};
103130

131+
template <levelt level>
132+
renamedt<exprt, level> make_renamed(constant_exprt constant) {
133+
return renamedt<exprt, level>(std::move(constant));
134+
}
135+
104136
/// Functor to set the level 0 renaming of SSA expressions.
105137
/// Level 0 corresponds to threads.
106138
/// The renaming is built for one particular interleaving.

src/goto-symex/symex_goto.cpp

Lines changed: 170 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

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

2223
#include <util/simplify_expr.h>
24+
#include <pointer-analysis/value_set_dereference.h>
2325

2426
void goto_symext::apply_goto_condition(
2527
goto_symex_statet &current_state,
@@ -65,6 +67,172 @@ void goto_symext::apply_goto_condition(
6567
}
6668
}
6769

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

75243
renamedt<exprt, L2> renamed_guard = state.rename(std::move(new_guard), ns);
244+
renamed_guard = try_evaluate_pointer_comparisons(
245+
std::move(renamed_guard), state.value_set, language_mode, ns);
76246
if(symex_config.simplify_opt)
77247
renamed_guard.simplify(ns);
78248
new_guard = renamed_guard.get();

0 commit comments

Comments
 (0)