Skip to content

Commit 0930b0d

Browse files
committed
Constant propagator: add callback to filter tracked values
A user may supply a predicate, in which case the constant propagator will only track symbols that pass the predicate. For example, one out-of-tree user uses this to restrict propagation to globals.
1 parent 6e554d9 commit 0930b0d

File tree

4 files changed

+168
-14
lines changed

4 files changed

+168
-14
lines changed

src/analyses/constant_propagator.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ void constant_propagator_domaint::assign_rec(
3131
valuest &values,
3232
const exprt &lhs,
3333
const exprt &rhs,
34-
const namespacet &ns)
34+
const namespacet &ns,
35+
const constant_propagator_ait *cp)
3536
{
3637
if(lhs.id()!=ID_symbol)
3738
return;
3839

40+
if(cp && !cp->should_track_value(lhs, ns))
41+
return;
42+
3943
const symbol_exprt &s=to_symbol_expr(lhs);
4044

4145
exprt tmp=rhs;
@@ -89,11 +93,11 @@ void constant_propagator_domaint::transform(
8993
const code_assignt &assignment=to_code_assign(from->code);
9094
const exprt &lhs=assignment.lhs();
9195
const exprt &rhs=assignment.rhs();
92-
assign_rec(values, lhs, rhs, ns);
96+
assign_rec(values, lhs, rhs, ns, cp);
9397
}
9498
else if(from->is_assume())
9599
{
96-
two_way_propagate_rec(from->guard, ns);
100+
two_way_propagate_rec(from->guard, ns, cp);
97101
}
98102
else if(from->is_goto())
99103
{
@@ -110,7 +114,7 @@ void constant_propagator_domaint::transform(
110114
values.set_to_bottom();
111115
else
112116
{
113-
two_way_propagate_rec(g, ns);
117+
two_way_propagate_rec(g, ns, cp);
114118
// If two way propagate is enabled then it may be possible to detect
115119
// that the branch condition is infeasible and thus the domain should
116120
// be set to bottom. Without it the domain will only be set to bottom
@@ -175,7 +179,7 @@ void constant_propagator_domaint::transform(
175179
break;
176180

177181
const symbol_exprt parameter_expr(p_it->get_identifier(), arg.type());
178-
assign_rec(values, parameter_expr, arg, ns);
182+
assign_rec(values, parameter_expr, arg, ns, cp);
179183

180184
++p_it;
181185
}
@@ -220,7 +224,8 @@ void constant_propagator_domaint::transform(
220224
/// handles equalities and conjunctions containing equalities
221225
bool constant_propagator_domaint::two_way_propagate_rec(
222226
const exprt &expr,
223-
const namespacet &ns)
227+
const namespacet &ns,
228+
const constant_propagator_ait *cp)
224229
{
225230
#ifdef DEBUG
226231
std::cout << "two_way_propagate_rec: " << format(expr) << '\n';
@@ -238,7 +243,7 @@ bool constant_propagator_domaint::two_way_propagate_rec(
238243
change = false;
239244

240245
forall_operands(it, expr)
241-
if(two_way_propagate_rec(*it, ns))
246+
if(two_way_propagate_rec(*it, ns, cp))
242247
change=true;
243248
}
244249
while(change);

src/analyses/constant_propagator.h

+39-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Author: Peter Schrammel
1818
#include "ai.h"
1919
#include "dirty.h"
2020

21+
class constant_propagator_ait;
22+
2123
class constant_propagator_domaint:public ai_domain_baset
2224
{
2325
public:
@@ -143,12 +145,15 @@ class constant_propagator_domaint:public ai_domain_baset
143145
protected:
144146
void assign_rec(
145147
valuest &values,
146-
const exprt &lhs, const exprt &rhs,
147-
const namespacet &ns);
148+
const exprt &lhs,
149+
const exprt &rhs,
150+
const namespacet &ns,
151+
const constant_propagator_ait *cp);
148152

149153
bool two_way_propagate_rec(
150154
const exprt &expr,
151-
const namespacet &ns);
155+
const namespacet &ns,
156+
const constant_propagator_ait *cp);
152157

153158
bool partial_evaluate_with_all_rounding_modes(
154159
exprt &expr,
@@ -160,13 +165,35 @@ class constant_propagator_domaint:public ai_domain_baset
160165
class constant_propagator_ait:public ait<constant_propagator_domaint>
161166
{
162167
public:
163-
explicit constant_propagator_ait(const goto_functionst &goto_functions):
164-
dirty(goto_functions)
168+
typedef std::function<bool(const exprt &, const namespacet &)>
169+
should_track_valuet;
170+
171+
static bool track_all_values(const exprt &, const namespacet &)
172+
{
173+
return true;
174+
}
175+
176+
explicit constant_propagator_ait(
177+
const goto_functionst &goto_functions,
178+
should_track_valuet should_track_value = track_all_values):
179+
dirty(goto_functions),
180+
should_track_value(should_track_value)
181+
{
182+
}
183+
184+
explicit constant_propagator_ait(
185+
const goto_functiont &goto_function,
186+
should_track_valuet should_track_value = track_all_values):
187+
dirty(goto_function),
188+
should_track_value(should_track_value)
165189
{
166190
}
167191

168192
constant_propagator_ait(
169-
goto_modelt &goto_model):dirty(goto_model.goto_functions)
193+
goto_modelt &goto_model,
194+
should_track_valuet should_track_value = track_all_values):
195+
dirty(goto_model.goto_functions),
196+
should_track_value(should_track_value)
170197
{
171198
const namespacet ns(goto_model.symbol_table);
172199
operator()(goto_model.goto_functions, ns);
@@ -175,7 +202,10 @@ class constant_propagator_ait:public ait<constant_propagator_domaint>
175202

176203
constant_propagator_ait(
177204
goto_functionst::goto_functiont &goto_function,
178-
const namespacet &ns):dirty(goto_function)
205+
const namespacet &ns,
206+
should_track_valuet should_track_value = track_all_values):
207+
dirty(goto_function),
208+
should_track_value(should_track_value)
179209
{
180210
operator()(goto_function, ns);
181211
replace(goto_function, ns);
@@ -197,6 +227,8 @@ class constant_propagator_ait:public ait<constant_propagator_domaint>
197227
void replace_types_rec(
198228
const replace_symbolt &replace_const,
199229
exprt &expr);
230+
231+
std::function<bool(const exprt &, const namespacet &)> should_track_value;
200232
};
201233

202234
#endif // CPROVER_ANALYSES_CONSTANT_PROPAGATOR_H

unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ SRC = unit_tests.cpp \
99
SRC += unit_tests.cpp \
1010
analyses/ai/ai_simplify_lhs.cpp \
1111
analyses/call_graph.cpp \
12+
analyses/constant_propagator.cpp \
1213
analyses/disconnect_unreachable_nodes_in_graph.cpp \
1314
analyses/does_remove_const/does_expr_lose_const.cpp \
1415
analyses/does_remove_const/does_type_preserve_const_correctness.cpp \

unit/analyses/constant_propagator.cpp

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*******************************************************************\
2+
3+
Module: Unit test for constant propagation
4+
5+
Author: Diffblue Ltd
6+
7+
\*******************************************************************/
8+
9+
#include <testing-utils/catch.hpp>
10+
11+
#include <analyses/constant_propagator.h>
12+
13+
#include <ansi-c/ansi_c_language.h>
14+
15+
#include <goto-programs/goto_convert_functions.h>
16+
17+
#include <util/message.h>
18+
19+
#include <iostream>
20+
21+
static bool starts_with_x(const exprt &e, const namespacet &)
22+
{
23+
if(e.id() != ID_symbol)
24+
return false;
25+
return has_prefix(id2string(to_symbol_expr(e).get_identifier()), "x");
26+
}
27+
28+
SCENARIO("constant_propagator", "[core][analyses][constant_propagator]")
29+
{
30+
GIVEN("A simple GOTO program")
31+
{
32+
stream_message_handlert log(std::cout);
33+
34+
goto_modelt goto_model;
35+
namespacet ns(goto_model.symbol_table);
36+
37+
// Create the program:
38+
// int x = 1;
39+
// int y = 2;
40+
41+
symbolt local_x;
42+
symbolt local_y;
43+
local_x.name = "x";
44+
local_x.type = integer_typet();
45+
local_x.mode = ID_C;
46+
local_y.name = "y";
47+
local_y.type = integer_typet();
48+
local_y.mode = ID_C;
49+
50+
code_blockt code;
51+
code.copy_to_operands(code_declt(local_x.symbol_expr()));
52+
code.copy_to_operands(code_declt(local_y.symbol_expr()));
53+
code.copy_to_operands(
54+
code_assignt(
55+
local_x.symbol_expr(), constant_exprt("1", integer_typet())));
56+
code.copy_to_operands(
57+
code_assignt(
58+
local_y.symbol_expr(), constant_exprt("2", integer_typet())));
59+
60+
symbolt main_function_symbol;
61+
main_function_symbol.name = "main";
62+
main_function_symbol.type = code_typet();
63+
main_function_symbol.value = code;
64+
main_function_symbol.mode = ID_C;
65+
66+
goto_model.symbol_table.add(local_x);
67+
goto_model.symbol_table.add(local_y);
68+
goto_model.symbol_table.add(main_function_symbol);
69+
70+
goto_convert(goto_model, log);
71+
72+
const goto_functiont &main_function = goto_model.get_goto_function("main");
73+
74+
// Find the instruction after "y = 2;"
75+
goto_programt::const_targett test_instruction =
76+
main_function.body.instructions.begin();
77+
while(
78+
test_instruction != main_function.body.instructions.end() &&
79+
(!test_instruction->is_assign() ||
80+
to_code_assign(test_instruction->code).lhs() != local_y.symbol_expr()))
81+
{
82+
++test_instruction;
83+
}
84+
85+
REQUIRE(test_instruction != main_function.body.instructions.end());
86+
++test_instruction;
87+
88+
WHEN("We apply conventional constant propagation")
89+
{
90+
constant_propagator_ait constant_propagator(main_function);
91+
constant_propagator(main_function, ns);
92+
93+
THEN("The propagator should discover values for both 'x' and 'y'")
94+
{
95+
const auto &final_domain = constant_propagator[test_instruction];
96+
97+
REQUIRE(final_domain.values.is_constant(local_x.symbol_expr()));
98+
REQUIRE(final_domain.values.is_constant(local_y.symbol_expr()));
99+
}
100+
}
101+
102+
WHEN("We apply constant propagation for symbols beginning with 'x'")
103+
{
104+
constant_propagator_ait constant_propagator(main_function, starts_with_x);
105+
constant_propagator(main_function, ns);
106+
107+
THEN("The propagator should discover a value for 'x' but not 'y'")
108+
{
109+
const auto &final_domain = constant_propagator[test_instruction];
110+
111+
REQUIRE(final_domain.values.is_constant(local_x.symbol_expr()));
112+
REQUIRE(!final_domain.values.is_constant(local_y.symbol_expr()));
113+
}
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)