Skip to content

Commit 4d1f9ca

Browse files
committed
Symex: rename LHS rvalue components and simplify before symex_assign_rec
This was already happening in most cases (e.g. symex_dereference would apply field-sensitivity before symex_assign_rec was entered), but the case of a statically non-constant but dynamically constant array index or byte-extract offset would only be handled *after* symex_assign_rec, leading to an asymmetry in which operations were combined into the ssa_exprt and which ones accumulated in the expr_skeletont. With this change the LHS expression is maximally renamed and simplified before symex_assign_rec is entered, which means that any field-sensitive symbols on the LHS are fully constructed as early as possible, and expr_skeletont only accumulates those member, index and byte-extract operations which *cannot* be associated with an ssa_exprt. This means there is no need to undo with-operations or try to simplify byte-extract operations in goto_symext::assign_from_non_struct_symbol, as this has been taken care of already, and the l2_full_lhs can be constructed from the expression skeleton trivially.
1 parent e5e026d commit 4d1f9ca

File tree

3 files changed

+71
-42
lines changed

3 files changed

+71
-42
lines changed

src/goto-symex/goto_symex.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ void goto_symext::symex_assign(statet &state, const code_assignt &code)
4141
<< messaget::eom;
4242
});
4343

44+
// rvalues present within the lhs (for example, "some_array[this_rvalue]" or
45+
// "byte_extract <type> from an_lvalue offset this_rvalue") can affect whether
46+
// we use field-sensitive symbols or not, so L2-rename them up front:
47+
lhs = state.l2_rename_rvalues(lhs, ns);
48+
do_simplify(lhs);
49+
lhs = state.field_sensitivity.apply(ns, state, std::move(lhs), true);
50+
4451
if(rhs.id() == ID_side_effect)
4552
{
4653
const side_effect_exprt &side_effect_expr = to_side_effect_expr(rhs);

src/goto-symex/symex_assign.cpp

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -362,16 +362,7 @@ void symex_assignt::assign_non_struct_symbol(
362362
ns)
363363
.get();
364364

365-
// Note the following two calls are specifically required for
366-
// field-sensitivity. For example, with-expressions, which may have just been
367-
// introduced by assign_struct_member, are transformed into member
368-
// expressions on the LHS. If we add an option to disable field-sensitivity
369-
// in the future these should be omitted.
370-
assignmentt assignment = rewrite_with_to_field_symbols<use_update()>(
371-
state,
372-
shift_indexed_access_to_lhs<use_update()>(
373-
state, {lhs, full_lhs, std::move(l2_rhs)}, ns, symex_config.simplify_opt),
374-
ns);
365+
assignmentt assignment{lhs, full_lhs, l2_rhs};
375366

376367
if(symex_config.simplify_opt)
377368
assignment.rhs = simplify_expr(std::move(assignment.rhs), ns);
@@ -387,8 +378,15 @@ void symex_assignt::assign_non_struct_symbol(
387378
.get();
388379

389380
state.record_events.push(false);
390-
const exprt l2_full_lhs =
391-
state.rename(assignment.original_lhs_skeleton.apply(l2_lhs), ns).get();
381+
// Note any other symbols mentioned in the skeleton are rvalues -- for example
382+
// array indices -- and were renamed to L2 at the start of
383+
// goto_symext::symex_assign.
384+
const exprt l2_full_lhs = assignment.original_lhs_skeleton.apply(l2_lhs);
385+
if(symex_config.run_validation_checks)
386+
{
387+
INVARIANT(
388+
!check_renaming(l2_full_lhs), "l2_full_lhs should be renamed to L2");
389+
}
392390
state.record_events.pop();
393391

394392
auto current_assignment_type =
@@ -552,23 +550,14 @@ void symex_assignt::assign_if(
552550
exprt::operandst &guard)
553551
{
554552
// we have (c?a:b)=e;
555-
exprt renamed_guard = state.rename(lhs.cond(), ns).get();
556-
if(symex_config.simplify_opt)
557-
renamed_guard = simplify_expr(std::move(renamed_guard), ns);
558553

559-
if(!renamed_guard.is_false())
560-
{
561-
guard.push_back(renamed_guard);
562-
assign_rec(lhs.true_case(), full_lhs, rhs, guard);
563-
guard.pop_back();
564-
}
554+
guard.push_back(lhs.cond());
555+
assign_rec(lhs.true_case(), full_lhs, rhs, guard);
556+
guard.pop_back();
565557

566-
if(!renamed_guard.is_true())
567-
{
568-
guard.push_back(not_exprt(renamed_guard));
569-
assign_rec(lhs.false_case(), full_lhs, rhs, guard);
570-
guard.pop_back();
571-
}
558+
guard.push_back(not_exprt(lhs.cond()));
559+
assign_rec(lhs.false_case(), full_lhs, rhs, guard);
560+
guard.pop_back();
572561
}
573562

574563
void symex_assignt::assign_byte_extract(

unit/goto-symex/symex_assign.cpp

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -219,33 +219,66 @@ SCENARIO(
219219
symex_config,
220220
target_equation}
221221
.assign_symbol(struct1_ssa, skeleton, rhs, guard);
222-
THEN("An equation is added to the target")
222+
THEN("Two equations are added to the target")
223223
{
224-
REQUIRE(target_equation.SSA_steps.size() == 1);
225-
SSA_stept step = target_equation.SSA_steps.back();
226-
THEN("LHS is `struct1!0#2..field1`")
224+
REQUIRE(target_equation.SSA_steps.size() == 2);
225+
SSA_stept assign_step = target_equation.SSA_steps.front();
226+
SSA_stept expand_step = target_equation.SSA_steps.back();
227+
THEN("Assign step LHS is `struct1!0#1`")
227228
{
228-
REQUIRE(step.ssa_lhs.get_identifier() == "struct1!0#1..field1");
229+
REQUIRE(assign_step.ssa_lhs.get_identifier() == "struct1!0#1");
229230
}
230-
THEN("Original full LHS is `struct1.field1`")
231+
THEN("Assign step original full LHS is `struct1.field1`")
231232
{
232233
REQUIRE(
233-
step.original_full_lhs ==
234+
assign_step.original_full_lhs ==
234235
member_exprt{struct1_sym, "field1", int_type});
235236
}
236-
THEN("SSA full LHS is `struct1!0#1..field1`")
237+
THEN("Assign step SSA full LHS is `struct1!0#1`")
237238
{
238239
const auto as_symbol =
239-
expr_try_dynamic_cast<symbol_exprt>(step.ssa_lhs);
240+
expr_try_dynamic_cast<symbol_exprt>(assign_step.ssa_lhs);
240241
REQUIRE(as_symbol);
241-
REQUIRE(as_symbol->get_identifier() == "struct1!0#1..field1");
242+
REQUIRE(as_symbol->get_identifier() == "struct1!0#1");
243+
}
244+
THEN("Assign step RHS is { struct1!0#0..field1 } with field1 = 234")
245+
{
246+
ssa_exprt struct1_v0_field1{
247+
member_exprt{struct1_sym, "field1", int_type}};
248+
struct1_v0_field1.set_level_0(0);
249+
struct1_v0_field1.set_level_2(0);
250+
struct_exprt struct_expr(struct_type);
251+
struct_expr.add_to_operands(struct1_v0_field1);
252+
with_exprt struct1_v0_with_field_set = rhs;
253+
struct1_v0_with_field_set.old() = struct_expr;
254+
REQUIRE(assign_step.ssa_rhs == struct1_v0_with_field_set);
242255
}
243-
THEN("RHS is 234")
256+
THEN("Expand step LHS is `struct1!0#2..field1`")
244257
{
245-
const auto as_constant =
246-
expr_try_dynamic_cast<constant_exprt>(step.ssa_rhs);
247-
REQUIRE(as_constant);
248-
REQUIRE(numeric_cast_v<mp_integer>(*as_constant) == 234);
258+
REQUIRE(
259+
expand_step.ssa_lhs.get_identifier() == "struct1!0#2..field1");
260+
}
261+
THEN("Expand step original full LHS is `struct1.field1`")
262+
{
263+
REQUIRE(
264+
expand_step.original_full_lhs ==
265+
member_exprt{struct1_sym, "field1", int_type});
266+
}
267+
THEN("Expand step SSA full LHS is `struct1!0#2..field1`")
268+
{
269+
const auto as_symbol =
270+
expr_try_dynamic_cast<symbol_exprt>(expand_step.ssa_lhs);
271+
REQUIRE(as_symbol);
272+
REQUIRE(as_symbol->get_identifier() == "struct1!0#2..field1");
273+
}
274+
THEN("Expand step RHS is struct1!0#1.field1")
275+
{
276+
ssa_exprt struct1_v1{struct1_sym};
277+
struct1_v1.set_level_0(0);
278+
struct1_v1.set_level_2(1);
279+
REQUIRE(
280+
expand_step.ssa_rhs ==
281+
member_exprt{struct1_v1, "field1", int_type});
249282
}
250283
}
251284
}

0 commit comments

Comments
 (0)