Skip to content

Commit e15f48b

Browse files
committed
goto-symex: apply_condition should not change L2 index
Updating the L2 index would result in additional phi assignments, which can result in more costly verification. One such example is that the use of `i != size` instead of `i < size` in loop conditions resulted increased verification time when we would expect not-equal to be cheaper than less-than.
1 parent 2e6200a commit e15f48b

File tree

10 files changed

+98
-35
lines changed

10 files changed

+98
-35
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
int main()
2+
{
3+
int s;
4+
5+
int i = 0;
6+
if(i != s)
7+
++i;
8+
else
9+
__CPROVER_assert(s == 0, "constant propagate");
10+
11+
int s2 = s;
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE paths-lifo-expected-failure
2+
main.c
3+
--program-only
4+
s2!0@1#2 == s!0@1#1
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
ASSERT
9+
s2!0@1#[3-9]
10+
--
11+
The branch condition should yield constant propagation on s without introducing
12+
fresh assignments to s.

regression/validate-trace-xml-schema/check.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
['havoc_slice', 'functional_slice_bytes.desc'],
4545
['havoc_slice', 'functional_slice_typed.desc'],
4646
['saturating_arithmetric', 'output-formula.desc'],
47+
['apply_condition2', 'test.desc'],
4748
# these test for invalid command line handling
4849
['bad_option', 'test_multiple.desc'],
4950
['bad_option', 'test.desc'],

src/goto-symex/goto_state.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,11 @@ void goto_statet::apply_condition(
107107
const ssa_exprt l1_lhs = remove_level_2(ssa_lhs);
108108
const irep_idt &l1_identifier = l1_lhs.get_identifier();
109109

110-
level2.increase_generation(
111-
l1_identifier, l1_lhs, previous_state.get_l2_name_provider());
112-
113-
const auto propagation_entry = propagation.find(l1_identifier);
110+
const auto propagation_entry = branch_propagation.find(l1_identifier);
114111
if(!propagation_entry.has_value())
115-
propagation.insert(l1_identifier, rhs);
112+
branch_propagation.insert(l1_identifier, rhs);
116113
else if(propagation_entry->get() != rhs)
117-
propagation.replace(l1_identifier, rhs);
114+
branch_propagation.replace(l1_identifier, rhs);
118115

119116
value_set.assign(l1_lhs, rhs, ns, true, false);
120117
}

src/goto-symex/goto_state.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ class goto_statet
6868
// "constants" can include symbols, but only in the context of an address-of
6969
// op (i.e. &x can be propagated), and an address-taken thing should only be
7070
// L1.
71-
sharing_mapt<irep_idt, exprt> propagation;
71+
//
72+
// Entries in branch_propagation are local to the current branch and will
73+
// never be merged back in phi nodes.
74+
sharing_mapt<irep_idt, exprt> propagation, branch_propagation;
7275

7376
void output_propagation_map(std::ostream &);
7477

src/goto-symex/goto_symex.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,19 @@ goto_symext::try_evaluate_constant_string(
388388
return {};
389389
}
390390

391-
const auto s_pointer_opt =
392-
state.propagation.find(to_symbol_expr(content).get_identifier());
391+
const irep_idt &content_id = to_symbol_expr(content).get_identifier();
393392

394-
if(!s_pointer_opt)
395-
{
396-
return {};
397-
}
393+
const auto s_pointer_opt = state.propagation.find(content_id);
394+
395+
if(s_pointer_opt)
396+
return try_get_string_data_array(s_pointer_opt->get(), ns);
397+
398+
const auto b_entry = state.branch_propagation.find(content_id);
399+
400+
if(b_entry)
401+
return try_get_string_data_array(b_entry->get(), ns);
398402

399-
return try_get_string_data_array(s_pointer_opt->get(), ns);
403+
return {};
400404
}
401405

402406
std::optional<std::reference_wrapper<const constant_exprt>>
@@ -407,16 +411,25 @@ goto_symext::try_evaluate_constant(const statet &state, const exprt &expr)
407411
return {};
408412
}
409413

410-
const auto constant_expr_opt =
411-
state.propagation.find(to_symbol_expr(expr).get_identifier());
414+
const irep_idt &expr_id = to_symbol_expr(expr).get_identifier();
412415

413-
if(!constant_expr_opt || !constant_expr_opt->get().is_constant())
416+
const auto constant_expr_opt = state.propagation.find(expr_id);
417+
418+
if(constant_expr_opt && constant_expr_opt->get().is_constant())
414419
{
415-
return {};
420+
return std::optional<std::reference_wrapper<const constant_exprt>>(
421+
to_constant_expr(constant_expr_opt->get()));
422+
}
423+
424+
const auto b_entry = state.branch_propagation.find(expr_id);
425+
426+
if(b_entry && b_entry->get().id() == ID_constant)
427+
{
428+
return std::optional<std::reference_wrapper<const constant_exprt>>(
429+
to_constant_expr(b_entry->get()));
416430
}
417431

418-
return std::optional<std::reference_wrapper<const constant_exprt>>(
419-
to_constant_expr(constant_expr_opt->get()));
432+
return {};
420433
}
421434

422435
void goto_symext::constant_propagate_empty_string(

src/goto-symex/goto_symex_state.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ renamedt<ssa_exprt, L2> goto_symex_statet::assignment(
116116
"pointer handling for concurrency is unsound");
117117

118118
// Update constant propagation map -- the RHS is L2
119+
branch_propagation.erase_if_exists(l1_identifier);
119120
if(!is_shared && record_value && goto_symex_can_forward_propagatet(ns)(rhs))
120121
{
121122
const auto propagation_entry = propagation.find(l1_identifier);
@@ -205,14 +206,19 @@ goto_symex_statet::rename(exprt expr, const namespacet &ns)
205206
{
206207
// We also consider propagation if we go up to L2.
207208
// L1 identifiers are used for propagation!
208-
auto p_it = propagation.find(ssa.get_identifier());
209+
const auto &l1_identifier = ssa.get_identifier();
210+
auto p_it = propagation.find(l1_identifier);
209211

210212
if(p_it.has_value())
211213
{
212214
return renamedt<exprt, level>(*p_it); // already L2
213215
}
214216
else
215217
{
218+
auto b_entry = branch_propagation.find(l1_identifier);
219+
if(b_entry.has_value())
220+
return renamedt<exprt, level>(*b_entry);
221+
216222
if(level == L2)
217223
ssa = set_indices<L2>(std::move(ssa), ns).get();
218224
return renamedt<exprt, level>(std::move(ssa));
@@ -448,7 +454,7 @@ bool goto_symex_statet::l2_thread_read_encoding(
448454
// written this object within the atomic section. We must actually do this,
449455
// because goto_state::apply_condition may have placed the latest value in
450456
// the propagation map without recording an assignment.
451-
auto p_it = propagation.find(ssa_l1.get_identifier());
457+
auto p_it = branch_propagation.find(ssa_l1.get_identifier());
452458
const exprt l2_true_case =
453459
p_it.has_value() ? *p_it : set_indices<L2>(ssa_l1, ns).get();
454460

src/goto-symex/symex_dead.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ static void remove_l1_object_rec(
4141
state.value_set.values.erase_if_exists(l1_identifier);
4242
}
4343
state.propagation.erase_if_exists(l1_identifier);
44+
state.branch_propagation.erase_if_exists(l1_identifier);
4445
// Remove from the local L2 renaming map; this means any reads from the dead
4546
// identifier will use generation 0 (e.g. x!N@M#0, where N and M are
4647
// positive integers), which is never defined by any write, and will be

src/goto-symex/symex_goto.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,13 @@ static void merge_names(
637637
if(p_it.has_value())
638638
goto_state_rhs = *p_it;
639639
else
640-
to_ssa_expr(goto_state_rhs).set_level_2(goto_count);
640+
{
641+
auto b_entry = goto_state.branch_propagation.find(l1_identifier);
642+
if(b_entry.has_value())
643+
goto_state_rhs = *b_entry;
644+
else
645+
to_ssa_expr(goto_state_rhs).set_level_2(goto_count);
646+
}
641647
}
642648

643649
{
@@ -646,7 +652,13 @@ static void merge_names(
646652
if(p_it.has_value())
647653
dest_state_rhs = *p_it;
648654
else
649-
to_ssa_expr(dest_state_rhs).set_level_2(dest_count);
655+
{
656+
auto b_entry = dest_state.branch_propagation.find(l1_identifier);
657+
if(b_entry.has_value())
658+
dest_state_rhs = *b_entry;
659+
else
660+
to_ssa_expr(dest_state_rhs).set_level_2(dest_count);
661+
}
650662
}
651663

652664
exprt rhs;
@@ -763,6 +775,12 @@ void goto_symext::phi_function(
763775
goto_count,
764776
dest_count);
765777
}
778+
779+
sharing_mapt<irep_idt, exprt>::delta_viewt bp_delta_view =
780+
dest_state.branch_propagation.get_delta_view(
781+
goto_state.branch_propagation, false);
782+
for(const auto &delta_item : bp_delta_view)
783+
dest_state.branch_propagation.erase(delta_item.k);
766784
}
767785

768786
void goto_symext::loop_bound_exceeded(

unit/goto-symex/apply_condition.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ SCENARIO(
6161

6262
THEN("b should be in the constant propagator with value 'true'")
6363
{
64-
auto it = goto_state.propagation.find(
64+
auto it = goto_state.branch_propagation.find(
6565
to_ssa_expr(renamed_b).get_l1_object_identifier());
6666
REQUIRE(it);
6767
REQUIRE(it->get() == true_exprt{});
@@ -75,7 +75,7 @@ SCENARIO(
7575

7676
THEN("b should be in the constant propagator with value 'false'")
7777
{
78-
auto it = goto_state.propagation.find(
78+
auto it = goto_state.branch_propagation.find(
7979
to_ssa_expr(renamed_b).get_l1_object_identifier());
8080
REQUIRE(it);
8181
REQUIRE(it->get() == false_exprt{});
@@ -89,7 +89,7 @@ SCENARIO(
8989

9090
THEN("b should be in the constant propagator with value 'true'")
9191
{
92-
auto it = goto_state.propagation.find(
92+
auto it = goto_state.branch_propagation.find(
9393
to_ssa_expr(renamed_b).get_l1_object_identifier());
9494
REQUIRE(it);
9595
REQUIRE(it->get() == true_exprt{});
@@ -103,7 +103,7 @@ SCENARIO(
103103

104104
THEN("b should be in the constant propagator with value 'false'")
105105
{
106-
auto it = goto_state.propagation.find(
106+
auto it = goto_state.branch_propagation.find(
107107
to_ssa_expr(renamed_b).get_l1_object_identifier());
108108
REQUIRE(it);
109109
REQUIRE(it->get() == false_exprt{});
@@ -117,7 +117,7 @@ SCENARIO(
117117

118118
THEN("b should be in the constant propagator with value 'false'")
119119
{
120-
auto it = goto_state.propagation.find(
120+
auto it = goto_state.branch_propagation.find(
121121
to_ssa_expr(renamed_b).get_l1_object_identifier());
122122
REQUIRE(it);
123123
REQUIRE(it->get() == false_exprt{});
@@ -131,7 +131,7 @@ SCENARIO(
131131

132132
THEN("b should be in the constant propagator with value 'true'")
133133
{
134-
auto it = goto_state.propagation.find(
134+
auto it = goto_state.branch_propagation.find(
135135
to_ssa_expr(renamed_b).get_l1_object_identifier());
136136
REQUIRE(it);
137137
REQUIRE(it->get() == true_exprt{});
@@ -145,7 +145,7 @@ SCENARIO(
145145

146146
THEN("b should be in the constant propagator with value 'false'")
147147
{
148-
auto it = goto_state.propagation.find(
148+
auto it = goto_state.branch_propagation.find(
149149
to_ssa_expr(renamed_b).get_l1_object_identifier());
150150
REQUIRE(it);
151151
REQUIRE(it->get() == false_exprt{});
@@ -159,7 +159,7 @@ SCENARIO(
159159

160160
THEN("b should be in the constant propagator with value 'true'")
161161
{
162-
auto it = goto_state.propagation.find(
162+
auto it = goto_state.branch_propagation.find(
163163
to_ssa_expr(renamed_b).get_l1_object_identifier());
164164
REQUIRE(it);
165165
REQUIRE(it->get() == true_exprt{});
@@ -173,7 +173,7 @@ SCENARIO(
173173

174174
THEN("b should be in the constant propagator with value 'true'")
175175
{
176-
auto it = goto_state.propagation.find(
176+
auto it = goto_state.branch_propagation.find(
177177
to_ssa_expr(renamed_b).get_l1_object_identifier());
178178
REQUIRE(it);
179179
REQUIRE(it->get() == true_exprt{});
@@ -187,7 +187,7 @@ SCENARIO(
187187

188188
THEN("b should be in the constant propagator with value 'false'")
189189
{
190-
auto it = goto_state.propagation.find(
190+
auto it = goto_state.branch_propagation.find(
191191
to_ssa_expr(renamed_b).get_l1_object_identifier());
192192
REQUIRE(it);
193193
REQUIRE(it->get() == false_exprt{});
@@ -201,7 +201,7 @@ SCENARIO(
201201

202202
THEN("b should be in the constant propagator with value 'true'")
203203
{
204-
auto it = goto_state.propagation.find(
204+
auto it = goto_state.branch_propagation.find(
205205
to_ssa_expr(renamed_b).get_l1_object_identifier());
206206
REQUIRE(it);
207207
REQUIRE(it->get() == true_exprt{});

0 commit comments

Comments
 (0)