Skip to content

Commit 0872016

Browse files
committed
Add apply_loop_contracts in dfcc
1 parent b04cbf3 commit 0872016

25 files changed

+1997
-35
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
foo();
6+
}
7+
8+
int foo()
9+
{
10+
int r1, s1 = 1;
11+
__CPROVER_assume(r1 >= 0);
12+
while(r1 > 0)
13+
__CPROVER_loop_invariant(r1 >= 0 && s1 == 1) __CPROVER_decreases(r1)
14+
{
15+
s1 = 1;
16+
r1--;
17+
}
18+
assert(r1 == 0);
19+
20+
int r2, s2 = 1;
21+
__CPROVER_assume(r2 >= 0);
22+
while(r2 > 0)
23+
__CPROVER_assigns(r2, s2) __CPROVER_loop_invariant(r2 >= 0 && s2 == 1)
24+
__CPROVER_decreases(r2)
25+
{
26+
s2 = 1;
27+
r2--;
28+
}
29+
assert(r2 == 0);
30+
return 0;
31+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
CORE
2+
main.c
3+
--dfcc main
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.\d+\] .* Check decreases clause on loop iteration: SUCCESS$
9+
^\[main.assertion.\d+\] .* assertion r1 == 0: SUCCESS$
10+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
11+
^\[main.assigns.\d+\] .* Check that s2 is assignable: SUCCESS$
12+
^\[main.assigns.\d+\] .* Check that r2 is assignable: SUCCESS$
13+
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$
14+
^\[main.\d+\] .* Check decreases clause on loop iteration: SUCCESS$
15+
^\[main.assertion.\d+\] .* assertion r2 == 0: SUCCESS$
16+
^VERIFICATION SUCCESSFUL$
17+
--
18+
--
19+
This test checks that adding assigns clause is optional
20+
and that if a proof does not require it, then adding an
21+
appropriate assigns clause does not lead to any
22+
unexpected behavior.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
#define SIZE 8
5+
6+
struct blob
7+
{
8+
char *data;
9+
};
10+
11+
void main()
12+
{
13+
foo();
14+
}
15+
16+
void foo()
17+
{
18+
struct blob *b = malloc(sizeof(struct blob));
19+
b->data = malloc(SIZE);
20+
21+
b->data[5] = 0;
22+
for(unsigned i = 0; i < SIZE; i++)
23+
// clang-format off
24+
__CPROVER_assigns(i, __CPROVER_object_whole(b->data))
25+
__CPROVER_loop_invariant(i <= SIZE)
26+
// clang-format on
27+
{
28+
b->data[i] = 1;
29+
}
30+
31+
assert(b->data[5] == 0);
32+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CORE
2+
main.c
3+
--dfcc main
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
8+
^\[main.assigns.\d+\] .* Check that b->data\[(.*)i\] is assignable: SUCCESS$
9+
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$
10+
^\[main.assertion.\d+\] .* assertion b->data\[5\] == 0: FAILURE$
11+
^VERIFICATION FAILED$
12+
--
13+
--
14+
This test (taken from #6021) shows the need for assigns clauses on loops.
15+
The alias analysis in this case returns `unknown`,
16+
and so we must manually annotate an assigns clause on the loop.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdlib.h>
2+
3+
#define SIZE 8
4+
5+
void main()
6+
{
7+
foo();
8+
}
9+
10+
void foo()
11+
{
12+
int *b = malloc(SIZE * sizeof(int));
13+
for(unsigned i = 0; i < SIZE; i++)
14+
// clang-format off
15+
__CPROVER_loop_invariant(i <= SIZE)
16+
// clang-format on
17+
{
18+
b[i] = 1;
19+
}
20+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--dfcc main
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.\d+\] .* Check loop invariant before entry: SUCCESS$
7+
^\[main.\d+\] .* Check that loop invariant is preserved: SUCCESS$
8+
^\[main.assigns.\d+\] .* Check that i is assignable: SUCCESS$
9+
^\[main.assigns.\d+\] .* Check that b\[(.*)i\] is assignable: SUCCESS$
10+
^VERIFICATION SUCCESSFUL$
11+
--
12+
--
13+
This test checks assigns clauses (i, __CPROVER_POINTER_OBJECT(b)) is inferred,
14+
and widened correctly.

src/goto-instrument/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ SRC = accelerate/accelerate.cpp \
1818
call_sequences.cpp \
1919
contracts/contracts.cpp \
2020
contracts/dynamic-frames/dfcc_utils.cpp \
21+
contracts/dynamic-frames/dfcc_loop_utils.cpp \
22+
contracts/dynamic-frames/dfcc_loop_instrument.cpp \
2123
contracts/dynamic-frames/dfcc_library.cpp \
2224
contracts/dynamic-frames/dfcc_is_fresh.cpp \
2325
contracts/dynamic-frames/dfcc_pointer_in_range.cpp \

src/goto-instrument/contracts/cfg_info.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Date: June 2022
2626

2727
#include <analyses/dirty.h>
2828
#include <analyses/locals.h>
29+
#include <goto-instrument/contracts/dynamic-frames/dfcc_loop_utils.h>
2930
#include <goto-instrument/havoc_utils.h>
3031
#include <goto-instrument/loop_utils.h>
3132

@@ -152,6 +153,26 @@ class loop_cfg_infot : public cfg_infot
152153
}
153154
}
154155

156+
loop_cfg_infot(
157+
goto_functiont &_goto_function,
158+
const std::vector<std::size_t> nesting_ids)
159+
: is_dirty(_goto_function)
160+
{
161+
for(auto it = _goto_function.body.instructions.begin();
162+
it != _goto_function.body.instructions.end();
163+
it++)
164+
{
165+
if(
166+
it->is_decl() && !get_loop_instr_type(it).empty() &&
167+
std::find(
168+
nesting_ids.begin(), nesting_ids.end(), get_loop_nesting_id(it)) !=
169+
nesting_ids.end())
170+
{
171+
locals.insert(it->decl_symbol().get_identifier());
172+
}
173+
}
174+
}
175+
155176
/// Returns true iff `ident` is a loop local.
156177
bool is_local(const irep_idt &ident) const override
157178
{
@@ -201,7 +222,7 @@ class goto_program_cfg_infot : public cfg_infot
201222
public:
202223
explicit goto_program_cfg_infot(const goto_programt &goto_program)
203224
{
204-
// collect symbols declared in the insruction sequence as locals
225+
// collect symbols declared in the instruction sequence as locals
205226
goto_program.get_decl_identifiers(locals);
206227

207228
// collect dirty locals

src/goto-instrument/contracts/contracts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ void code_contractst::check_apply_loop_contracts(
220220

221221
// If the set contains pairs (i, a[i]),
222222
// we widen them to (i, __CPROVER_POINTER_OBJECT(a))
223-
widen_assigns(to_havoc, ns);
223+
widen_assigns(to_havoc, ns, false);
224224

225225
log.debug() << "No loop assigns clause provided. Inferred targets: {";
226226
// Add inferred targets to the loop assigns clause.

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ Author: Remi Delmas, [email protected]
3838
#include <ansi-c/c_object_factory_parameters.h>
3939
#include <ansi-c/cprover_library.h>
4040
#include <goto-instrument/contracts/cfg_info.h>
41-
#include <goto-instrument/contracts/instrument_spec_assigns.h>
4241
#include <goto-instrument/contracts/utils.h>
4342
#include <goto-instrument/nondet_static.h>
4443
#include <langapi/language.h>
@@ -188,15 +187,9 @@ dfcct::dfcct(
188187
utils(goto_model, message_handler),
189188
library(goto_model, utils, message_handler),
190189
ns(goto_model.symbol_table),
191-
instrument(goto_model, message_handler, utils, library),
192190
memory_predicates(goto_model, utils, library, instrument, message_handler),
193191
spec_functions(goto_model, message_handler, utils, library, instrument),
194-
contract_clauses_codegen(
195-
goto_model,
196-
message_handler,
197-
utils,
198-
library,
199-
spec_functions),
192+
contract_clauses_codegen(goto_model, message_handler, utils, library),
200193
contract_handler(
201194
goto_model,
202195
message_handler,
@@ -214,6 +207,13 @@ dfcct::dfcct(
214207
instrument,
215208
spec_functions,
216209
contract_handler),
210+
loop_instrument(
211+
goto_model,
212+
message_handler,
213+
library,
214+
spec_functions,
215+
contract_clauses_codegen),
216+
instrument(goto_model, message_handler, utils, loop_instrument, library),
217217
max_assigns_clause_size(0)
218218
{
219219
transform_goto_model();
@@ -377,7 +377,7 @@ void dfcct::wrap_checked_function()
377377
if(other_symbols.find(wrapper_id) != other_symbols.end())
378378
other_symbols.erase(wrapper_id);
379379

380-
// upate max contract size
380+
// update max contract size
381381
const std::size_t assigns_clause_size =
382382
contract_handler.get_assigns_clause_size(contract_id);
383383
if(assigns_clause_size > max_assigns_clause_size)
@@ -492,8 +492,13 @@ void dfcct::instrument_other_functions()
492492

493493
// TODO specialise the library functions for the max size of
494494
// loop and function contracts
495-
if(to_check.has_value())
495+
if(to_check.has_value() || loop_instrument.max_nof_assigns != 0)
496496
{
497+
max_assigns_clause_size =
498+
loop_instrument.max_nof_assigns > max_assigns_clause_size
499+
? loop_instrument.max_nof_assigns
500+
: max_assigns_clause_size;
501+
497502
log.status() << "Specializing cprover_contracts functions for assigns "
498503
"clauses of at most "
499504
<< max_assigns_clause_size << " targets" << messaget::eom;

src/goto-instrument/contracts/dynamic-frames/dfcc.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Author: Remi Delmas, [email protected]
3838
#include "dfcc_instrument.h"
3939
#include "dfcc_library.h"
4040
#include "dfcc_lift_memory_predicates.h"
41+
#include "dfcc_loop_instrument.h"
4142
#include "dfcc_spec_functions.h"
4243
#include "dfcc_swap_and_wrap.h"
4344
#include "dfcc_utils.h"
@@ -214,12 +215,13 @@ class dfcct
214215
dfcc_utilst utils;
215216
dfcc_libraryt library;
216217
namespacet ns;
217-
dfcc_instrumentt instrument;
218218
dfcc_lift_memory_predicatest memory_predicates;
219219
dfcc_spec_functionst spec_functions;
220220
dfcc_contract_clauses_codegent contract_clauses_codegen;
221221
dfcc_contract_handlert contract_handler;
222222
dfcc_swap_and_wrapt swap_and_wrap;
223+
dfcc_loop_instrumentt loop_instrument;
224+
dfcc_instrumentt instrument;
223225

224226
/// Tracks the maximum number of targets in any assigns clause handled in the
225227
/// transformation (used to specialize/unwind loops in DFCC library functions)

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,12 @@ dfcc_contract_clauses_codegent::dfcc_contract_clauses_codegent(
3030
goto_modelt &goto_model,
3131
message_handlert &message_handler,
3232
dfcc_utilst &utils,
33-
dfcc_libraryt &library,
34-
dfcc_spec_functionst &spec_functions)
33+
dfcc_libraryt &library)
3534
: goto_model(goto_model),
3635
message_handler(message_handler),
3736
log(message_handler),
3837
utils(utils),
3938
library(library),
40-
spec_functions(spec_functions),
4139
ns(goto_model.symbol_table)
4240
{
4341
}
@@ -63,6 +61,7 @@ void dfcc_contract_clauses_codegent::gen_spec_assigns_instructions(
6361

6462
// inline resulting program and check for loops
6563
inline_and_check_warnings(dest);
64+
dest.update();
6665
PRECONDITION_WITH_DIAGNOSTICS(
6766
utils.has_no_loops(dest),
6867
"loops in assigns clause specification functions must be unwound before "

src/goto-instrument/contracts/dynamic-frames/dfcc_contract_clauses_codegen.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ class goto_modelt;
2929
class message_handlert;
3030
class dfcc_libraryt;
3131
class dfcc_utilst;
32-
class dfcc_spec_functionst;
3332
class code_with_contract_typet;
3433
class conditional_target_group_exprt;
3534

@@ -40,14 +39,11 @@ class dfcc_contract_clauses_codegent
4039
/// \param message_handler used debug/warning/error messages
4140
/// \param utils utility class for dynamic frames
4241
/// \param library the contracts instrumentation library
43-
/// \param spec_functions provides translation methods for assignable set
44-
/// or freeable set specification functions.
4542
dfcc_contract_clauses_codegent(
4643
goto_modelt &goto_model,
4744
message_handlert &message_handler,
4845
dfcc_utilst &utils,
49-
dfcc_libraryt &library,
50-
dfcc_spec_functionst &spec_functions);
46+
dfcc_libraryt &library);
5147

5248
/// \brief Generates instructions encoding the \p assigns_clause targets and
5349
/// adds them to \p dest.
@@ -85,7 +81,6 @@ class dfcc_contract_clauses_codegent
8581
messaget log;
8682
dfcc_utilst &utils;
8783
dfcc_libraryt &library;
88-
dfcc_spec_functionst &spec_functions;
8984
namespacet ns;
9085

9186
/// Generates GOTO instructions to build the representation of the given

0 commit comments

Comments
 (0)