Skip to content

Commit b42ac11

Browse files
committed
Library functions: mark them as compiled
When adding library functions to the goto model we no longer need their function bodies as values in the symbol table. Marking them as "compiled" will make sure we don't re-convert them in any subsequent run (e.g., running cbmc after goto-instrument). Doing so would undo any application of "drop-unused-functions." This was most notable with contracts/dynamic frames, where we ended up reporting thousands of properties as "SUCCESS" in library functions that were never actually called. For some contracts examples this now reduces the number of properties reported from over a thousand to tens of properties, and in other cases this made apparent that we were spuriously reporting "SUCCESS" when we never actually invoked those functions in the first place.
1 parent b87d38a commit b42ac11

File tree

9 files changed

+73
-46
lines changed

9 files changed

+73
-46
lines changed

regression/contracts-dfcc/chain.sh

+3
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,8 @@ elif echo $args_inst | grep -q -- "--dump-c" ; then
5858

5959
rm "${name}${dfcc_suffix}-mod.c"
6060
fi
61+
if ! echo "${args_cbmc}" | grep -q -- --function ; then
62+
$goto_instrument --drop-unused-functions "${name}${dfcc_suffix}-mod.gb" "${name}${dfcc_suffix}-mod.gb"
63+
fi
6164
$goto_instrument --show-goto-functions "${name}${dfcc_suffix}-mod.gb"
6265
$cbmc "${name}${dfcc_suffix}-mod.gb" ${args_cbmc}

regression/contracts/assigns_enforce_03/main.c

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
void f2(int *, int *, int *);
2+
void f3(int *, int *, int *);
3+
14
void f1(int *x1, int *y1, int *z1) __CPROVER_assigns(*x1, *y1, *z1)
25
{
36
f2(x1, y1, z1);
@@ -22,6 +25,8 @@ int main()
2225
int q = 2;
2326
int r = 3;
2427
f1(&p, &q, &r);
28+
f2(&p, &q, &r);
29+
f3(&p, &q, &r);
2530

2631
return 0;
2732
}

regression/contracts/assigns_enforce_03/test.desc

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
CORE
22
main.c
33
--enforce-contract f1 --enforce-contract f2 --enforce-contract f3
4-
^\[f1.assigns.\d+\] line 1 Check that \*x1 is valid: SUCCESS$
5-
^\[f1.assigns.\d+\] line 1 Check that \*y1 is valid: SUCCESS$
6-
^\[f1.assigns.\d+\] line 1 Check that \*z1 is valid: SUCCESS$
7-
^\[f2.assigns.\d+\] line 6 Check that \*x2 is valid: SUCCESS$
8-
^\[f2.assigns.\d+\] line 6 Check that \*y2 is valid: SUCCESS$
9-
^\[f2.assigns.\d+\] line 6 Check that \*z2 is valid: SUCCESS$
10-
^\[f3.assigns.\d+\] line 11 Check that \*x3 is valid: SUCCESS$
11-
^\[f3.assigns.\d+\] line 11 Check that \*y3 is valid: SUCCESS$
12-
^\[f3.assigns.\d+\] line 12 Check that \*z3 is valid: SUCCESS$
13-
^\[f3.assigns.\d+\] line 14 Check that \*x3 is assignable: SUCCESS$
14-
^\[f3.assigns.\d+\] line 15 Check that \*y3 is assignable: SUCCESS$
15-
^\[f3.assigns.\d+\] line 16 Check that \*z3 is assignable: SUCCESS$
4+
^\[f1.assigns.\d+\] line 4 Check that \*x1 is valid: SUCCESS$
5+
^\[f1.assigns.\d+\] line 4 Check that \*y1 is valid: SUCCESS$
6+
^\[f1.assigns.\d+\] line 4 Check that \*z1 is valid: SUCCESS$
7+
^\[f2.assigns.\d+\] line 9 Check that \*x2 is valid: SUCCESS$
8+
^\[f2.assigns.\d+\] line 9 Check that \*y2 is valid: SUCCESS$
9+
^\[f2.assigns.\d+\] line 9 Check that \*z2 is valid: SUCCESS$
10+
^\[f3.assigns.\d+\] line 14 Check that \*x3 is valid: SUCCESS$
11+
^\[f3.assigns.\d+\] line 14 Check that \*y3 is valid: SUCCESS$
12+
^\[f3.assigns.\d+\] line 15 Check that \*z3 is valid: SUCCESS$
13+
^\[f3.assigns.\d+\] line 17 Check that \*x3 is assignable: SUCCESS$
14+
^\[f3.assigns.\d+\] line 18 Check that \*y3 is assignable: SUCCESS$
15+
^\[f3.assigns.\d+\] line 19 Check that \*z3 is assignable: SUCCESS$
1616
^VERIFICATION SUCCESSFUL$
1717
^EXIT=0$
1818
^SIGNAL=0$

regression/contracts/chain.sh

+3
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,8 @@ elif echo $args_inst | grep -q -- "--dump-c" ; then
4141

4242
rm "${name}-mod.c"
4343
fi
44+
if ! echo "${args_cbmc}" | grep -q -- --function ; then
45+
$goto_instrument --drop-unused-functions "${name}-mod.gb" "${name}-mod.gb"
46+
fi
4447
$goto_instrument --show-goto-functions "${name}-mod.gb"
4548
$cbmc "${name}-mod.gb" ${args_cbmc}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
CORE
2+
main.c
3+
--apply-loop-contracts
4+
^\[ackermann\.\d+\] line 21 Check loop invariant before entry: SUCCESS$
5+
^\[ackermann\.\d+\] line 21 Check that loop invariant is preserved: SUCCESS$
6+
^\[ackermann\.\d+\] line 21 Check decreases clause on loop iteration: SUCCESS$
7+
^\[ackermann.assigns.\d+\] line 29 Check that m is assignable: SUCCESS$
8+
^\[ackermann.assigns.\d+\] line 30 Check that n is assignable: SUCCESS$
9+
^\[ackermann.assigns.\d+\] line 35 Check that m is assignable: SUCCESS$
10+
^\[ackermann.overflow.\d+] line 39 arithmetic overflow on signed \+ in n \+ 1: FAILURE$
11+
^\*\* 1 of \d+ failed
12+
^VERIFICATION FAILED$
13+
^EXIT=10$
14+
^SIGNAL=0$
15+
--
16+
--
17+
It tests whether we can prove (only partially) the termination of the Ackermann
18+
function using a multidimensional decreases clause.
19+
20+
Note that this particular implementation of the Ackermann function contains
21+
both a while-loop and recursion. Therefore, to fully prove the termination of
22+
the Ackermann function, we must prove both
23+
(i) the termination of the while-loop and
24+
(ii) the termination of the recursion.
25+
Because CBMC does not support termination proofs of recursions (yet), we cannot
26+
prove the latter, but the former. Hence, the termination proof in the code is
27+
only "partial."
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,18 @@
11
CORE
22
main.c
3-
--apply-loop-contracts --replace-call-with-contract ackermann
3+
--replace-call-with-contract ackermann
44
^\[ackermann.precondition\.\d+\] line \d+ Check requires clause of ackermann in main: SUCCESS$
5-
^\[ackermann.precondition\.\d+\] line \d+ Check requires clause of ackermann in ackermann: SUCCESS$
6-
^\[ackermann\.\d+\] line 21 Check loop invariant before entry: SUCCESS$
7-
^\[ackermann\.\d+\] line 21 Check that loop invariant is preserved: SUCCESS$
8-
^\[ackermann\.\d+\] line 21 Check decreases clause on loop iteration: SUCCESS$
9-
^\[ackermann.assigns.\d+\] line 29 Check that m is assignable: SUCCESS$
10-
^\[ackermann.assigns.\d+\] line 30 Check that n is assignable: SUCCESS$
11-
^\[ackermann.assigns.\d+\] line 34 Check that n is assignable: SUCCESS$
12-
^\[ackermann.assigns.\d+\] line 35 Check that m is assignable: SUCCESS$
135
^VERIFICATION SUCCESSFUL$
146
^EXIT=0$
157
^SIGNAL=0$
168
--
179
--
18-
It tests whether we can prove (only partially) the termination of the Ackermann
19-
function using a multidimensional decreases clause.
20-
21-
Note that this particular implementation of the Ackermann function contains
22-
both a while-loop and recursion. Therefore, to fully prove the termination of
23-
the Ackermann function, we must prove both
24-
(i) the termination of the while-loop and
25-
(ii) the termination of the recursion.
26-
Because CBMC does not support termination proofs of recursions (yet), we cannot
27-
prove the latter, but the former. Hence, the termination proof in the code is
28-
only "partial."
29-
30-
Furthermore, the Ackermann function has a function contract that the result
10+
The Ackermann function has a function contract that the result
3111
is always non-negative. This post-condition is necessary for establishing
3212
the loop invariant. However, in this test, we do not enforce the function
3313
contract. Instead, we assume that the function contract is correct and use it
34-
(i.e. replace a recursive call of the Ackermann function with its contract).
14+
(i.e. replace a recursive call of the Ackermann function with its contract).
3515

3616
We cannot verify/enforce the function contract of the Ackermann function, since
3717
CBMC does not support function contracts for recursively defined functions.
38-
As of now, CBMC only supports function contracts for non-recursive functions.
18+
As of now, CBMC only supports function contracts for non-recursive functions.

src/ansi-c/goto-conversion/link_to_library.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,36 @@ add_one_function(
4141
// convert to CFG
4242
if(
4343
library_model.symbol_table.symbols.find(missing_function) !=
44-
library_model.symbol_table.symbols.end())
44+
library_model.symbol_table.symbols.end() &&
45+
library_model.symbol_table.lookup_ref(missing_function).value.is_not_nil())
4546
{
4647
goto_convert(
4748
missing_function,
4849
library_model.symbol_table,
4950
library_model.goto_functions,
5051
message_handler);
52+
// this function is now included in goto_functions, no need to re-convert
53+
// should the goto binary be reloaded
54+
library_model.symbol_table.get_writeable_ref(missing_function)
55+
.set_compiled();
5156
}
5257
// We might need a function that's outside our own library, but brought in via
5358
// some header file included by the library. Those functions already exist in
5459
// goto_model.symbol_table, but haven't been converted just yet.
5560
else if(
5661
goto_model.symbol_table.symbols.find(missing_function) !=
57-
goto_model.symbol_table.symbols.end())
62+
goto_model.symbol_table.symbols.end() &&
63+
goto_model.symbol_table.lookup_ref(missing_function).value.is_not_nil() &&
64+
!goto_model.symbol_table.lookup_ref(missing_function).is_compiled())
5865
{
5966
goto_convert(
6067
missing_function,
6168
goto_model.symbol_table,
6269
library_model.goto_functions,
6370
message_handler);
71+
// this function is now included in goto_functions, no need to re-convert
72+
// should the goto binary be reloaded
73+
goto_model.symbol_table.get_writeable_ref(missing_function).set_compiled();
6474
}
6575

6676
// check whether additional initialization may be required

src/goto-instrument/contracts/dynamic-frames/dfcc_library.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ void dfcc_libraryt::load(std::set<irep_idt> &to_instrument)
326326
{
327327
goto_convert(
328328
id, goto_model.symbol_table, goto_model.goto_functions, message_handler);
329+
goto_model.symbol_table.get_writeable_ref(id).set_compiled();
329330
}
330331

331332
// check that all symbols have a goto_implementation

src/goto-synthesizer/cegis_verifier.cpp

+6-8
Original file line numberDiff line numberDiff line change
@@ -546,12 +546,8 @@ cext cegis_verifiert::build_cex(
546546

547547
void cegis_verifiert::restore_functions()
548548
{
549-
for(const auto &fun_entry : goto_model.goto_functions.function_map)
550-
{
551-
irep_idt fun_name = fun_entry.first;
552-
goto_model.goto_functions.function_map[fun_name].body.swap(
553-
original_functions[fun_name]);
554-
}
549+
for(auto &[fun_name, orig_fun_body] : original_functions)
550+
goto_model.goto_functions.function_map[fun_name].body.swap(orig_fun_body);
555551
}
556552

557553
std::optional<cext> cegis_verifiert::verify()
@@ -569,10 +565,12 @@ std::optional<cext> cegis_verifiert::verify()
569565
// 3. construct the formatted counterexample from the violated property and
570566
// its trace.
571567

572-
// Store the original functions. We will restore them after the verification.
568+
// Store the original functions when they have a body (library functions might
569+
// not yet have one). We will restore them after the verification.
573570
for(const auto &fun_entry : goto_model.goto_functions.function_map)
574571
{
575-
original_functions[fun_entry.first].copy_from(fun_entry.second.body);
572+
if(fun_entry.second.body_available())
573+
original_functions[fun_entry.first].copy_from(fun_entry.second.body);
576574
}
577575

578576
// Annotate the candidates to the goto_model for checking.

0 commit comments

Comments
 (0)