Skip to content

Commit 92dc298

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 365871d commit 92dc298

File tree

11 files changed

+106
-30
lines changed

11 files changed

+106
-30
lines changed
+12
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+
}
+12
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

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
['Quantifiers-simplify', 'simplify_not_forall.desc'],
3939
['array-cell-sensitivity15', 'test.desc'],
4040
['saturating_arithmetric', 'output-formula.desc'],
41+
['apply_condition2', 'test.desc'],
4142
# these test for invalid command line handling
4243
['bad_option', 'test_multiple.desc'],
4344
['bad_option', 'test.desc'],

src/goto-symex/goto_state.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ void goto_statet::apply_condition(
106106
const ssa_exprt l1_lhs = remove_level_2(ssa_lhs);
107107
const irep_idt &l1_identifier = l1_lhs.get_identifier();
108108

109+
#if 1
110+
const auto propagation_entry = branch_propagation.find(l1_identifier);
111+
if(!propagation_entry.has_value())
112+
branch_propagation.insert(l1_identifier, rhs);
113+
else if(propagation_entry->get() != rhs)
114+
branch_propagation.replace(l1_identifier, rhs);
115+
#else
109116
level2.increase_generation(
110117
l1_identifier, l1_lhs, previous_state.get_l2_name_provider());
111118
@@ -114,6 +121,7 @@ void goto_statet::apply_condition(
114121
propagation.insert(l1_identifier, rhs);
115122
else if(propagation_entry->get() != rhs)
116123
propagation.replace(l1_identifier, rhs);
124+
#endif
117125

118126
value_set.assign(l1_lhs, rhs, ns, true, false);
119127
}

src/goto-symex/goto_state.h

+4-1
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

+26-13
Original file line numberDiff line numberDiff line change
@@ -351,15 +351,19 @@ goto_symext::try_evaluate_constant_string(
351351
return {};
352352
}
353353

354-
const auto s_pointer_opt =
355-
state.propagation.find(to_symbol_expr(content).get_identifier());
354+
const irep_idt &content_id = to_symbol_expr(content).get_identifier();
356355

357-
if(!s_pointer_opt)
358-
{
359-
return {};
360-
}
356+
const auto s_pointer_opt = state.propagation.find(content_id);
357+
358+
if(s_pointer_opt)
359+
return try_get_string_data_array(s_pointer_opt->get(), ns);
360+
361+
const auto b_entry = state.branch_propagation.find(content_id);
362+
363+
if(b_entry)
364+
return try_get_string_data_array(b_entry->get(), ns);
361365

362-
return try_get_string_data_array(s_pointer_opt->get(), ns);
366+
return {};
363367
}
364368

365369
optionalt<std::reference_wrapper<const constant_exprt>>
@@ -370,16 +374,25 @@ goto_symext::try_evaluate_constant(const statet &state, const exprt &expr)
370374
return {};
371375
}
372376

373-
const auto constant_expr_opt =
374-
state.propagation.find(to_symbol_expr(expr).get_identifier());
377+
const irep_idt &expr_id = to_symbol_expr(expr).get_identifier();
375378

376-
if(!constant_expr_opt || constant_expr_opt->get().id() != ID_constant)
379+
const auto constant_expr_opt = state.propagation.find(expr_id);
380+
381+
if(constant_expr_opt && constant_expr_opt->get().id() == ID_constant)
377382
{
378-
return {};
383+
return optionalt<std::reference_wrapper<const constant_exprt>>(
384+
to_constant_expr(constant_expr_opt->get()));
385+
}
386+
387+
const auto b_entry = state.branch_propagation.find(expr_id);
388+
389+
if(b_entry && b_entry->get().id() == ID_constant)
390+
{
391+
return optionalt<std::reference_wrapper<const constant_exprt>>(
392+
to_constant_expr(b_entry->get()));
379393
}
380394

381-
return optionalt<std::reference_wrapper<const constant_exprt>>(
382-
to_constant_expr(constant_expr_opt->get()));
395+
return {};
383396
}
384397

385398
void goto_symext::constant_propagate_empty_string(

src/goto-symex/goto_symex_state.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ renamedt<ssa_exprt, L2> goto_symex_statet::assignment(
110110
"pointer handling for concurrency is unsound");
111111

112112
// Update constant propagation map -- the RHS is L2
113+
branch_propagation.erase_if_exists(l1_identifier);
113114
if(!is_shared && record_value && goto_symex_is_constantt()(rhs))
114115
{
115116
const auto propagation_entry = propagation.find(l1_identifier);
@@ -212,14 +213,19 @@ goto_symex_statet::rename(exprt expr, const namespacet &ns)
212213
{
213214
// We also consider propagation if we go up to L2.
214215
// L1 identifiers are used for propagation!
215-
auto p_it = propagation.find(ssa.get_identifier());
216+
const auto &l1_identifier = ssa.get_identifier();
217+
auto p_it = propagation.find(l1_identifier);
216218

217219
if(p_it.has_value())
218220
{
219221
return renamedt<exprt, level>(*p_it); // already L2
220222
}
221223
else
222224
{
225+
auto b_entry = branch_propagation.find(l1_identifier);
226+
if(b_entry.has_value())
227+
return renamedt<exprt, level>(*b_entry);
228+
223229
if(level == L2)
224230
ssa = set_indices<L2>(std::move(ssa), ns).get();
225231
return renamedt<exprt, level>(std::move(ssa));
@@ -429,7 +435,7 @@ bool goto_symex_statet::l2_thread_read_encoding(
429435
// written this object within the atomic section. We must actually do this,
430436
// because goto_state::apply_condition may have placed the latest value in
431437
// the propagation map without recording an assignment.
432-
auto p_it = propagation.find(ssa_l1.get_identifier());
438+
auto p_it = branch_propagation.find(ssa_l1.get_identifier());
433439
const exprt l2_true_case =
434440
p_it.has_value() ? *p_it : set_indices<L2>(ssa_l1, ns).get();
435441

src/goto-symex/symex_assign.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,9 @@ void symex_assignt::assign_non_struct_symbol(
218218
// Erase the composite symbol from our working state. Note that we need to
219219
// have it in the propagation table and the value set while doing the field
220220
// assignments, thus we cannot skip putting it in there above.
221-
state.propagation.erase_if_exists(l1_lhs.get_identifier());
221+
const irep_idt &l1_identifier = l1_lhs.get_identifier();
222+
state.propagation.erase_if_exists(l1_identifier);
223+
state.branch_propagation.erase_if_exists(l1_identifier);
222224
state.value_set.erase_symbol(l1_lhs, ns);
223225
}
224226
}

src/goto-symex/symex_dead.cpp

+1
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

+20-2
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,13 @@ static void merge_names(
795795
if(p_it.has_value())
796796
goto_state_rhs = *p_it;
797797
else
798-
to_ssa_expr(goto_state_rhs).set_level_2(goto_count);
798+
{
799+
auto b_entry = goto_state.branch_propagation.find(l1_identifier);
800+
if(b_entry.has_value())
801+
goto_state_rhs = *b_entry;
802+
else
803+
to_ssa_expr(goto_state_rhs).set_level_2(goto_count);
804+
}
799805
}
800806

801807
{
@@ -804,7 +810,13 @@ static void merge_names(
804810
if(p_it.has_value())
805811
dest_state_rhs = *p_it;
806812
else
807-
to_ssa_expr(dest_state_rhs).set_level_2(dest_count);
813+
{
814+
auto b_entry = dest_state.branch_propagation.find(l1_identifier);
815+
if(b_entry.has_value())
816+
dest_state_rhs = *b_entry;
817+
else
818+
to_ssa_expr(dest_state_rhs).set_level_2(dest_count);
819+
}
808820
}
809821

810822
exprt rhs;
@@ -917,6 +929,12 @@ void goto_symext::phi_function(
917929
goto_count,
918930
dest_count);
919931
}
932+
933+
sharing_mapt<irep_idt, exprt>::delta_viewt bp_delta_view =
934+
dest_state.branch_propagation.get_delta_view(
935+
goto_state.branch_propagation, false);
936+
for(const auto &delta_item : bp_delta_view)
937+
dest_state.branch_propagation.erase(delta_item.k);
920938
}
921939

922940
void goto_symext::loop_bound_exceeded(

unit/goto-symex/apply_condition.cpp

+11-11
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ SCENARIO(
6262

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

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

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

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

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

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

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

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

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

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

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

0 commit comments

Comments
 (0)