Skip to content

Commit 2400d10

Browse files
authored
Merge pull request #5171 from xbauch/feature/function-harness-param-equal
Allow function pointer arguments to be equal
2 parents 4682d07 + 49b86dc commit 2400d10

File tree

8 files changed

+282
-4
lines changed

8 files changed

+282
-4
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef struct st1
5+
{
6+
struct st *next;
7+
int data;
8+
} st1_t;
9+
10+
typedef struct st2
11+
{
12+
struct st *next;
13+
int data;
14+
} st2_t;
15+
16+
void func(st1_t *p, st1_t *q, st2_t *r, st2_t *s, st2_t *t)
17+
{
18+
assert(p != NULL);
19+
assert(p->next != NULL);
20+
21+
assert(p == q);
22+
23+
assert((void *)p != (void *)r);
24+
25+
assert(r == s);
26+
assert(r == t);
27+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --treat-pointers-equal 'p,q;r,s,t' --treat-pointers-equal-maybe
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[func.assertion.\d+\] line \d+ assertion p == q: FAILURE$
7+
^\[func.assertion.\d+\] line \d+ assertion \(void \*\)p != \(void \*\)r: SUCCESS$
8+
^\[func.assertion.\d+\] line \d+ assertion r == s: FAILURE$
9+
^\[func.assertion.\d+\] line \d+ assertion r == t: FAILURE$
10+
VERIFICATION FAILED
11+
--
12+
^warning: ignoring
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef struct st1
5+
{
6+
struct st *next;
7+
int data;
8+
} st1_t;
9+
10+
typedef struct st2
11+
{
12+
struct st *next;
13+
int data;
14+
} st2_t;
15+
16+
void func(st1_t *p, st1_t *q, st2_t *r, st2_t *s, st2_t *t)
17+
{
18+
assert(p != NULL);
19+
assert(p->next != NULL);
20+
21+
assert(p == q);
22+
23+
assert((void *)p != (void *)r);
24+
25+
assert(r == s);
26+
assert(r == t);
27+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --treat-pointers-equal 'p,q;r,s,t'
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
VERIFICATION SUCCESSFUL
7+
--
8+
^warning: ignoring

src/goto-harness/function_call_harness_generator.cpp

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ struct function_call_harness_generatort::implt
4848
std::set<irep_idt> function_parameters_to_treat_as_arrays;
4949
std::set<irep_idt> function_arguments_to_treat_as_arrays;
5050

51+
std::vector<std::set<irep_idt>> function_parameters_to_treat_equal;
52+
std::vector<std::set<irep_idt>> function_arguments_to_treat_equal;
53+
5154
std::set<irep_idt> function_parameters_to_treat_as_cstrings;
5255
std::set<irep_idt> function_arguments_to_treat_as_cstrings;
5356

@@ -112,6 +115,21 @@ void function_call_harness_generatort::handle_option(
112115
p_impl->function_parameters_to_treat_as_arrays.insert(
113116
values.begin(), values.end());
114117
}
118+
else if(option == FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT)
119+
{
120+
for(auto const &value : values)
121+
{
122+
for(auto const &param_cluster : split_string(value, ';'))
123+
{
124+
std::set<irep_idt> equal_param_set;
125+
for(auto const &param_id : split_string(param_cluster, ','))
126+
{
127+
equal_param_set.insert(param_id);
128+
}
129+
p_impl->function_parameters_to_treat_equal.push_back(equal_param_set);
130+
}
131+
}
132+
}
115133
else if(option == FUNCTION_HARNESS_GENERATOR_ASSOCIATED_ARRAY_SIZE_OPT)
116134
{
117135
for(auto const &array_size_pair : values)
@@ -151,6 +169,10 @@ void function_call_harness_generatort::handle_option(
151169
p_impl->function_parameters_to_treat_as_cstrings.insert(
152170
values.begin(), values.end());
153171
}
172+
else if(option == FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_MAYBE_OPT)
173+
{
174+
p_impl->recursive_initialization_config.arguments_may_be_equal = true;
175+
}
154176
else
155177
{
156178
throw invalid_command_line_argument_exceptiont{
@@ -183,6 +205,8 @@ void function_call_harness_generatort::implt::generate(
183205
recursive_initialization_config.mode = function_to_call.mode;
184206
recursive_initialization_config.pointers_to_treat_as_arrays =
185207
function_arguments_to_treat_as_arrays;
208+
recursive_initialization_config.pointers_to_treat_equal =
209+
function_arguments_to_treat_equal;
186210
recursive_initialization_config.array_name_to_associated_array_size_variable =
187211
function_argument_to_associated_array_size;
188212
for(const auto &pair : function_argument_to_associated_array_size)
@@ -199,9 +223,9 @@ void function_call_harness_generatort::implt::generate(
199223
call_function(arguments, function_body);
200224
for(const auto &global_pointer : global_pointers)
201225
{
202-
function_body.add(code_function_callt{
203-
recursive_initialization->get_free_function(), {global_pointer}});
226+
recursive_initialization->free_if_possible(global_pointer, function_body);
204227
}
228+
recursive_initialization->free_cluster_origins(function_body);
205229
add_harness_function_to_goto_model(std::move(function_body));
206230
}
207231

@@ -238,7 +262,7 @@ void function_call_harness_generatort::implt::generate_initialisation_code_for(
238262
{
239263
recursive_initialization->initialize(
240264
lhs, from_integer(0, signed_int_type()), block);
241-
if(lhs.type().id() == ID_pointer)
265+
if(recursive_initialization->needs_freeing(lhs))
242266
global_pointers.insert(to_symbol_expr(lhs));
243267
}
244268

@@ -258,6 +282,49 @@ void function_call_harness_generatort::validate_options(
258282
"--" COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT
259283
" --" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT};
260284
}
285+
286+
auto function_to_call = goto_model.symbol_table.lookup_ref(p_impl->function);
287+
auto ftype = to_code_type(function_to_call.type);
288+
for(auto const &equal_cluster : p_impl->function_parameters_to_treat_equal)
289+
{
290+
for(auto const &pointer_id : equal_cluster)
291+
{
292+
std::string decorated_pointer_id =
293+
id2string(p_impl->function) + "::" + id2string(pointer_id);
294+
bool is_a_param = false;
295+
typet common_type = empty_typet{};
296+
for(auto const &formal_param : ftype.parameters())
297+
{
298+
if(formal_param.get_identifier() == decorated_pointer_id)
299+
{
300+
is_a_param = true;
301+
if(formal_param.type().id() != ID_pointer)
302+
{
303+
throw invalid_command_line_argument_exceptiont{
304+
id2string(pointer_id) + " is not a pointer parameter",
305+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT};
306+
}
307+
if(common_type.id() != ID_empty)
308+
{
309+
if(common_type != formal_param.type())
310+
{
311+
throw invalid_command_line_argument_exceptiont{
312+
"pointer arguments of conflicting type",
313+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT};
314+
}
315+
}
316+
else
317+
common_type = formal_param.type();
318+
}
319+
}
320+
if(!is_a_param)
321+
{
322+
throw invalid_command_line_argument_exceptiont{
323+
id2string(pointer_id) + " is not a parameter",
324+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT};
325+
}
326+
}
327+
}
261328
}
262329

263330
const symbolt &
@@ -365,6 +432,22 @@ function_call_harness_generatort::implt::declare_arguments(
365432
{argument_name, associated_array_size_argument->second});
366433
}
367434
}
435+
436+
// translate the parameter to argument also in the equality clusters
437+
for(auto const &equal_cluster : function_parameters_to_treat_equal)
438+
{
439+
std::set<irep_idt> cluster_argument_names;
440+
for(auto const &parameter_name : equal_cluster)
441+
{
442+
INVARIANT(
443+
parameter_name_to_argument_name.count(parameter_name) != 0,
444+
id2string(parameter_name) + " is not a parameter");
445+
cluster_argument_names.insert(
446+
parameter_name_to_argument_name[parameter_name]);
447+
}
448+
function_arguments_to_treat_equal.push_back(cluster_argument_names);
449+
}
450+
368451
allocate_objects.declare_created_symbols(function_body);
369452
return arguments;
370453
}
@@ -377,7 +460,7 @@ void function_call_harness_generatort::implt::call_function(
377460
for(auto const &argument : arguments)
378461
{
379462
generate_initialisation_code_for(function_body, argument);
380-
if(argument.type().id() == ID_pointer)
463+
if(recursive_initialization->needs_freeing(argument))
381464
global_pointers.insert(to_symbol_expr(argument));
382465
}
383466
code_function_callt function_call{function_to_call.symbol_expr(),

src/goto-harness/function_harness_generator_options.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,24 @@ Author: Diffblue Ltd.
1515
#define FUNCTION_HARNESS_GENERATOR_NONDET_GLOBALS_OPT "nondet-globals"
1616
#define FUNCTION_HARNESS_GENERATOR_TREAT_POINTER_AS_ARRAY_OPT \
1717
"treat-pointer-as-array"
18+
#define FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT \
19+
"treat-pointers-equal"
1820
#define FUNCTION_HARNESS_GENERATOR_ASSOCIATED_ARRAY_SIZE_OPT \
1921
"associated-array-size"
2022
#define FUNCTION_HARNESS_GENERATOR_TREAT_POINTER_AS_CSTRING \
2123
"treat-pointer-as-cstring"
24+
#define FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_MAYBE_OPT \
25+
"treat-pointers-equal-maybe"
2226

2327
// clang-format off
2428
#define FUNCTION_HARNESS_GENERATOR_OPTIONS \
2529
"(" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT "):" \
2630
"(" FUNCTION_HARNESS_GENERATOR_NONDET_GLOBALS_OPT ")" \
2731
"(" FUNCTION_HARNESS_GENERATOR_TREAT_POINTER_AS_ARRAY_OPT "):" \
32+
"(" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT "):" \
2833
"(" FUNCTION_HARNESS_GENERATOR_ASSOCIATED_ARRAY_SIZE_OPT "):" \
2934
"(" FUNCTION_HARNESS_GENERATOR_TREAT_POINTER_AS_CSTRING "):" \
35+
"(" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_MAYBE_OPT ")" \
3036
// FUNCTION_HARNESS_GENERATOR_OPTIONS
3137

3238
// clang-format on
@@ -43,6 +49,11 @@ Author: Diffblue Ltd.
4349
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTER_AS_ARRAY_OPT \
4450
" p treat the function parameter with the name `p' as\n" \
4551
" an array\n" \
52+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT \
53+
" p,q,r[;s,t] treat the function parameters `q,r' equal\n" \
54+
" to parameter `p'; `s` to `t` and so on\n" \
55+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_MAYBE_OPT \
56+
" function parameters equality is non-deterministic\n" \
4657
"--" FUNCTION_HARNESS_GENERATOR_ASSOCIATED_ARRAY_SIZE_OPT \
4758
" array_name:size_name\n" \
4859
" set the parameter <size_name> to the size" \

src/goto-harness/recursive_initialization.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,54 @@ recursive_initializationt::recursive_initializationt(
9393
initialization_config.min_null_tree_depth,
9494
signed_int_type())))
9595
{
96+
common_arguments_origins.resize(
97+
this->initialization_config.pointers_to_treat_equal.size());
9698
}
9799

98100
void recursive_initializationt::initialize(
99101
const exprt &lhs,
100102
const exprt &depth,
101103
code_blockt &body)
102104
{
105+
// special handling for the case that pointer arguments should be treated
106+
// equal: if the equality is enforced (rather than the pointers may be equal),
107+
// then we don't even build the constructor functions
108+
if(lhs.id() == ID_symbol)
109+
{
110+
const auto maybe_cluster_index =
111+
find_equal_cluster(to_symbol_expr(lhs).get_identifier());
112+
if(maybe_cluster_index.has_value())
113+
{
114+
if(common_arguments_origins[*maybe_cluster_index].has_value())
115+
{
116+
const auto set_equal_case =
117+
code_assignt{lhs, *common_arguments_origins[*maybe_cluster_index]};
118+
if(initialization_config.arguments_may_be_equal)
119+
{
120+
const irep_idt &fun_name = build_constructor(lhs);
121+
const symbolt &fun_symbol =
122+
goto_model.symbol_table.lookup_ref(fun_name);
123+
const auto proper_init_case = code_function_callt{
124+
fun_symbol.symbol_expr(), {depth, address_of_exprt{lhs}}};
125+
126+
body.add(code_ifthenelset{
127+
side_effect_expr_nondett{bool_typet{}, source_locationt{}},
128+
set_equal_case,
129+
proper_init_case});
130+
}
131+
else
132+
{
133+
body.add(set_equal_case);
134+
}
135+
return;
136+
}
137+
else
138+
{
139+
common_arguments_origins[*maybe_cluster_index] = lhs;
140+
}
141+
}
142+
}
143+
103144
const irep_idt &fun_name = build_constructor(lhs);
104145
const symbolt &fun_symbol = goto_model.symbol_table.lookup_ref(fun_name);
105146

@@ -280,6 +321,19 @@ bool recursive_initializationt::should_be_treated_as_array(
280321
initialization_config.pointers_to_treat_as_arrays.end();
281322
}
282323

324+
optionalt<recursive_initializationt::equal_cluster_idt>
325+
recursive_initializationt::find_equal_cluster(const irep_idt &name) const
326+
{
327+
for(equal_cluster_idt index = 0;
328+
index != initialization_config.pointers_to_treat_equal.size();
329+
++index)
330+
{
331+
if(initialization_config.pointers_to_treat_equal[index].count(name) != 0)
332+
return index;
333+
}
334+
return {};
335+
}
336+
283337
bool recursive_initializationt::is_array_size_parameter(
284338
const irep_idt &cmdline_arg) const
285339
{
@@ -763,3 +817,49 @@ code_blockt recursive_initializationt::build_dynamic_array_constructor(
763817

764818
return body;
765819
}
820+
821+
bool recursive_initializationt::needs_freeing(const exprt &expr) const
822+
{
823+
return expr.type().id() == ID_pointer && expr.type().id() != ID_code;
824+
}
825+
826+
void recursive_initializationt::free_if_possible(
827+
const exprt &expr,
828+
code_blockt &body)
829+
{
830+
PRECONDITION(expr.id() == ID_symbol);
831+
const auto expr_id = to_symbol_expr(expr).get_identifier();
832+
const auto maybe_cluster_index = find_equal_cluster(expr_id);
833+
const auto call_free = code_function_callt{get_free_function(), {expr}};
834+
if(!maybe_cluster_index.has_value())
835+
{
836+
// not in any equality cluster -> just free
837+
body.add(call_free);
838+
return;
839+
}
840+
841+
if(
842+
to_symbol_expr(*common_arguments_origins[*maybe_cluster_index])
843+
.get_identifier() != expr_id &&
844+
initialization_config.arguments_may_be_equal)
845+
{
846+
// in equality cluster but not common origin -> free if not equal to origin
847+
const auto condition =
848+
notequal_exprt{expr, *common_arguments_origins[*maybe_cluster_index]};
849+
body.add(code_ifthenelset{condition, call_free});
850+
}
851+
else
852+
{
853+
// expr is common origin, leave freeing until the rest of the cluster is
854+
// freed
855+
return;
856+
}
857+
}
858+
859+
void recursive_initializationt::free_cluster_origins(code_blockt &body)
860+
{
861+
for(auto const &origin : common_arguments_origins)
862+
{
863+
body.add(code_function_callt{get_free_function(), {*origin}});
864+
}
865+
}

0 commit comments

Comments
 (0)