Skip to content

Commit a69e95d

Browse files
committed
Track and kill let-bound variables when their statement is executed
This is done using the owner class symex_live_let_variablest, which is passed down by utility functions to those such as symex_assign that are responsible for executing a whole instruction. When the instruction execution is complete (i.e. all VCCs attributable to it, and which can refer to the let-bound variable, are emitted) the instance is allowed to die, and it in turn kills the now-unneeded binders.
1 parent a1d4433 commit a69e95d

10 files changed

+150
-44
lines changed

src/goto-symex/goto_symex.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Author: Daniel Kroening, [email protected]
1818
#include <goto-programs/abstract_goto_model.h>
1919

2020
#include "path_storage.h"
21+
#include "symex_live_let_variables.h"
2122

2223
class byte_extract_exprt;
2324
class typet;
@@ -273,11 +274,12 @@ class goto_symext
273274
/// \param expr: The expression to clean up
274275
/// \param state
275276
/// \param write
276-
void clean_expr(exprt &expr, statet &state, bool write);
277+
NODISCARD symex_live_let_variablest
278+
clean_expr(exprt &expr, statet &state, bool write);
277279

278280
void trigger_auto_object(const exprt &, statet &);
279281
void initialize_auto_object(const exprt &, statet &);
280-
void process_array_expr(statet &, exprt &);
282+
NODISCARD symex_live_let_variablest process_array_expr(statet &, exprt &);
281283
exprt make_auto_object(const typet &, statet &);
282284
virtual void dereference(exprt &, statet &, bool write);
283285

@@ -496,7 +498,7 @@ class goto_symext
496498

497499
typedef symex_targett::assignment_typet assignment_typet;
498500

499-
void lift_lets(statet &, exprt &, assignment_typet);
501+
NODISCARD symex_live_let_variablest lift_lets(statet &, exprt &expr);
500502
void lift_let(statet &state, const let_exprt &let_expr);
501503

502504
void symex_assign_rec(
@@ -586,7 +588,7 @@ class goto_symext
586588
/// \param state: Symbolic execution state for current instruction
587589
/// \param lhs: The expression to assign to
588590
/// \param code: The `new` expression
589-
virtual void
591+
NODISCARD virtual symex_live_let_variablest
590592
symex_cpp_new(statet &state, const exprt &lhs, const side_effect_exprt &code);
591593
/// Symbolically execute a FUNCTION_CALL instruction for a function whose
592594
/// name starts with CPROVER_FKT_PREFIX
@@ -674,6 +676,8 @@ class goto_symext
674676
{
675677
target.validate(ns, vm);
676678
}
679+
680+
friend class symex_live_let_variablest;
677681
};
678682

679683
/// Transition to the next instruction, which increments the internal program

src/goto-symex/symex_assign.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ void goto_symext::symex_assign(statet &state, const code_assignt &code)
3333
DATA_INVARIANT(
3434
lhs.type() == rhs.type(), "assignments must be type consistent");
3535

36-
clean_expr(lhs, state, true);
37-
clean_expr(rhs, state, false);
36+
// These let-variables containers kill the bound variables when they are
37+
// destroyed (i.e. at the end of symex_assign)
38+
auto live_let_variables = clean_expr(lhs, state, true);
39+
live_let_variables.add(clean_expr(rhs, state, false));
3840

3941
if(rhs.id()==ID_side_effect)
4042
{
@@ -44,7 +46,7 @@ void goto_symext::symex_assign(statet &state, const code_assignt &code)
4446
if(
4547
statement == ID_cpp_new || statement == ID_cpp_new_array ||
4648
statement == ID_java_new_array_data)
47-
symex_cpp_new(state, lhs, side_effect_expr);
49+
live_let_variables.add(symex_cpp_new(state, lhs, side_effect_expr));
4850
else if(statement==ID_allocate)
4951
symex_allocate(state, lhs, side_effect_expr);
5052
else if(statement==ID_printf)

src/goto-symex/symex_builtin_functions.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ void goto_symext::symex_output(
378378
/// \param state: Symex state
379379
/// \param lhs: left-hand side of assignment
380380
/// \param code: right-hand side containing side effect
381-
void goto_symext::symex_cpp_new(
381+
symex_live_let_variablest goto_symext::symex_cpp_new(
382382
statet &state,
383383
const exprt &lhs,
384384
const side_effect_exprt &code)
@@ -412,10 +412,11 @@ void goto_symext::symex_cpp_new(
412412
else
413413
INVARIANT_WITH_IREP(false, "Unexpected side effect expression", code);
414414

415+
symex_live_let_variablest live_let_variables(*this, state);
415416
if(do_array)
416417
{
417418
exprt size_arg = static_cast<const exprt &>(code.find(ID_size));
418-
clean_expr(size_arg, state, false);
419+
live_let_variables.add(clean_expr(size_arg, state, false));
419420
symbol.type = array_typet(pointer_type.subtype(), size_arg);
420421
}
421422
else
@@ -438,6 +439,8 @@ void goto_symext::symex_cpp_new(
438439
rhs.copy_to_operands(symbol.symbol_expr());
439440

440441
symex_assign(state, code_assignt(lhs, rhs));
442+
443+
return live_let_variables;
441444
}
442445

443446
void goto_symext::symex_cpp_delete(

src/goto-symex/symex_clean_expr.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,20 @@ process_array_expr(exprt &expr, bool do_simplify, const namespacet &ns)
120120
}
121121
}
122122

123-
void goto_symext::process_array_expr(statet &state, exprt &expr)
123+
symex_live_let_variablest
124+
goto_symext::process_array_expr(statet &state, exprt &expr)
124125
{
125126
symex_dereference_statet symex_dereference_state(state, ns);
126127

127128
value_set_dereferencet dereference(
128129
ns, state.symbol_table, symex_dereference_state, language_mode, false);
129130

130131
expr = dereference.dereference(expr);
131-
lift_lets(state, expr);
132+
auto live_let_variables = lift_lets(state, expr);
132133

133134
::process_array_expr(expr, symex_config.simplify_opt, ns);
135+
136+
return live_let_variables;
134137
}
135138

136139
/// Rewrite index/member expressions in byte_extract to offset
@@ -174,7 +177,10 @@ replace_nondet(exprt &expr, symex_nondet_generatort &build_symex_nondet)
174177
void goto_symext::lift_let(statet &state, const let_exprt &let_expr)
175178
{
176179
exprt let_value = let_expr.value();
177-
clean_expr(let_value, state, false);
180+
auto nested_let_variables = clean_expr(let_value, state, false);
181+
INVARIANT(
182+
nested_let_variables.empty(),
183+
"lift_let should have nested lets removed first");
178184
let_value = state.rename(std::move(let_value), ns).get();
179185
do_simplify(let_value);
180186

@@ -190,38 +196,43 @@ void goto_symext::lift_let(statet &state, const let_exprt &let_expr)
190196

191197
symex_live_let_variablest goto_symext::lift_lets(statet &state, exprt &rhs)
192198
{
199+
symex_live_let_variablest lifted_let_variables(*this, state);
200+
193201
for(auto it = rhs.depth_begin(), itend = rhs.depth_end(); it != itend;)
194202
{
195203
if(it->id() == ID_let)
196204
{
197205
// Visit post-order, so more-local definitions are made before usage:
198206
exprt &replaced_expr = it.mutate();
199207
let_exprt &replaced_let = to_let_expr(replaced_expr);
200-
lift_lets(state, replaced_let.value());
201-
lift_lets(state, replaced_let.where());
208+
lifted_let_variables.add(lift_lets(state, replaced_let.value()));
209+
lifted_let_variables.add(lift_lets(state, replaced_let.where()));
202210

203211
lift_let(state, replaced_let);
212+
lifted_let_variables.add(replaced_let.symbol());
204213
replaced_expr = replaced_let.where();
205214

206215
it.next_sibling_or_parent();
207216
}
208217
else
209218
++it;
210219
}
220+
221+
return lifted_let_variables;
211222
}
212223

213-
void goto_symext::clean_expr(
214-
exprt &expr,
215-
statet &state,
216-
const bool write)
224+
symex_live_let_variablest
225+
goto_symext::clean_expr(exprt &expr, statet &state, const bool write)
217226
{
218227
replace_nondet(expr, path_storage.build_symex_nondet);
219228
dereference(expr, state, write);
220-
lift_lets(state, expr);
229+
auto live_let_variables = lift_lets(state, expr);
221230

222231
// make sure all remaining byte extract operations use the root
223232
// object to avoid nesting of with/update and byte_update when on
224233
// lhs
225234
if(write)
226235
adjust_byte_extract_rec(expr, ns);
236+
237+
return live_let_variables;
227238
}

src/goto-symex/symex_function_call.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ void goto_symext::parameter_assignments(
132132
assignment_type =
133133
symex_targett::assignment_typet::VISIBLE_ACTUAL_PARAMETER;
134134

135-
clean_expr(lhs, state, true);
136-
clean_expr(rhs, state, false);
135+
auto lhs_let_variables = clean_expr(lhs, state, true);
136+
auto rhs_let_variables = clean_expr(rhs, state, false);
137137

138138
exprt::operandst lhs_conditions;
139139
symex_assign_rec(
@@ -200,13 +200,14 @@ void goto_symext::symex_function_call_symbol(
200200
{
201201
code_function_callt code = original_code;
202202

203+
symex_live_let_variablest live_let_variables(*this, state);
203204
if(code.lhs().is_not_nil())
204-
clean_expr(code.lhs(), state, true);
205+
live_let_variables.add(clean_expr(code.lhs(), state, true));
205206

206-
clean_expr(code.function(), state, false);
207+
live_let_variables.add(clean_expr(code.function(), state, false));
207208

208209
Forall_expr(it, code.arguments())
209-
clean_expr(*it, state, false);
210+
live_let_variables.add(clean_expr(*it, state, false));
210211

211212
target.location(state.guard.as_expr(), state.source);
212213

src/goto-symex/symex_goto.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ void goto_symext::symex_goto(statet &state)
7070
const goto_programt::instructiont &instruction=*state.source.pc;
7171

7272
exprt new_guard = instruction.get_condition();
73-
clean_expr(new_guard, state, false);
73+
auto live_let_variables = clean_expr(new_guard, state, false);
7474

7575
renamedt<exprt, L2> renamed_guard = state.rename(std::move(new_guard), ns);
7676
if(symex_config.simplify_opt)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*******************************************************************\
2+
3+
Module: Container for live let-bound variables used by goto-symex
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Container for live let-bound variables used by goto-symex
11+
12+
#include "symex_live_let_variables.h"
13+
#include "goto_symex.h"
14+
15+
symex_live_let_variablest::~symex_live_let_variablest()
16+
{
17+
for(const auto &symbol_expr : live_let_variables)
18+
symex.symex_dead(state, symbol_expr);
19+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*******************************************************************\
2+
3+
Module: Container for live let-bound variables used by goto-symex
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Container for live let-bound variables used by goto-symex
11+
12+
#ifndef CPROVER_GOTO_SYMEX_SYMEX_LIVE_LET_VARIABLES_H
13+
#define CPROVER_GOTO_SYMEX_SYMEX_LIVE_LET_VARIABLES_H
14+
15+
#include <util/nodiscard.h>
16+
#include <util/ssa_expr.h>
17+
18+
#include <vector>
19+
20+
class goto_symext;
21+
class goto_symex_statet;
22+
23+
/// Owner class for let-bound variables that will be killed when this instance
24+
/// is destroyed. Returned by clean_expr and related functions that execute let
25+
/// or other local definitions, and then allowed to die when all VCCs that may
26+
/// refer to the bound variable have been created.
27+
class symex_live_let_variablest
28+
{
29+
std::vector<symbol_exprt> live_let_variables;
30+
goto_symext &symex;
31+
goto_symex_statet &state;
32+
33+
public:
34+
symex_live_let_variablest(goto_symext &symex, goto_symex_statet &state)
35+
: symex(symex), state(state)
36+
{
37+
}
38+
symex_live_let_variablest(const symex_live_let_variablest &) = delete;
39+
symex_live_let_variablest(symex_live_let_variablest &&) = default;
40+
const symex_live_let_variablest &
41+
operator=(const symex_live_let_variablest &) = delete;
42+
symex_live_let_variablest &operator=(symex_live_let_variablest &&) = delete;
43+
44+
~symex_live_let_variablest();
45+
46+
void add(const symbol_exprt &symbol_expr)
47+
{
48+
live_let_variables.push_back(symbol_expr);
49+
}
50+
51+
void add(symex_live_let_variablest &&other)
52+
{
53+
live_let_variables.insert(
54+
live_let_variables.end(),
55+
other.live_let_variables.begin(),
56+
other.live_let_variables.end());
57+
other.live_let_variables.clear();
58+
}
59+
60+
bool empty() const
61+
{
62+
return live_let_variables.empty();
63+
}
64+
};
65+
66+
#endif

src/goto-symex/symex_main.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void goto_symext::symex_assert(
8282
statet &state)
8383
{
8484
exprt condition = instruction.get_condition();
85-
clean_expr(condition, state, false);
85+
auto live_let_variables = clean_expr(condition, state, false);
8686

8787
// First, push negations in and perhaps convert existential quantifiers into
8888
// universals:
@@ -127,7 +127,7 @@ void goto_symext::symex_assume(statet &state, const exprt &cond)
127127
{
128128
exprt simplified_cond=cond;
129129

130-
clean_expr(simplified_cond, state, false);
130+
auto live_let_variables = clean_expr(simplified_cond, state, false);
131131
simplified_cond = state.rename(std::move(simplified_cond), ns).get();
132132
do_simplify(simplified_cond);
133133

0 commit comments

Comments
 (0)