Skip to content

Commit b04cbf3

Browse files
Merge pull request #7551 from remi-delmas-3000/contracts-codegen-loop-assigns
CONTRACTS: refactor DFCC code for loop contracts
2 parents aec6269 + fb35a17 commit b04cbf3

20 files changed

+1406
-704
lines changed

src/ansi-c/library/cprover_contracts.c

+59-73
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ typedef struct
7272
/// \brief Set of freeable pointers derived from the contract (indexed mode)
7373
__CPROVER_contracts_obj_set_t contract_frees;
7474
/// \brief Set of freeable pointers derived from the contract (append mode)
75-
__CPROVER_contracts_obj_set_t contract_frees_replacement;
75+
__CPROVER_contracts_obj_set_t contract_frees_append;
7676
/// \brief Set of objects allocated by the function under analysis
7777
/// (indexed mode)
7878
__CPROVER_contracts_obj_set_t allocated;
@@ -83,12 +83,9 @@ typedef struct
8383
/// (indexed mode)
8484
__CPROVER_contracts_obj_set_ptr_t linked_is_fresh;
8585
/// \brief Object set recording the is_fresh allocations in post conditions
86-
/// (replacement mode only)
8786
__CPROVER_contracts_obj_set_ptr_t linked_allocated;
8887
/// \brief Object set recording the deallocations (used by was_freed)
8988
__CPROVER_contracts_obj_set_ptr_t linked_deallocated;
90-
/// \brief True iff this write set is used for contract replacement
91-
__CPROVER_bool replacement;
9289
/// \brief True iff the write set checks requires clauses in an assumption ctx
9390
__CPROVER_bool assume_requires_ctx;
9491
/// \brief True iff the write set checks requires clauses in an assertion ctx
@@ -97,6 +94,10 @@ typedef struct
9794
__CPROVER_bool assume_ensures_ctx;
9895
/// \brief True iff this write set checks ensures clauses in an assertion ctx
9996
__CPROVER_bool assert_ensures_ctx;
97+
/// \brief True iff dynamic allocation is allowed (default: true)
98+
__CPROVER_bool allow_allocate;
99+
/// \brief True iff dynamic deallocation is allowed (default: true)
100+
__CPROVER_bool allow_deallocate;
100101
} __CPROVER_contracts_write_set_t;
101102

102103
/// \brief Type of pointers to \ref __CPROVER_contracts_write_set_t.
@@ -388,7 +389,6 @@ __CPROVER_HIDE:;
388389
/// \param[inout] set Pointer to the object to initialise
389390
/// \param[in] contract_assigns_size Max size of the assigns clause
390391
/// \param[in] contract_frees_size Max size of the frees clause
391-
/// \param[in] replacement True iff this write set is used to replace a contract
392392
/// \param[in] assume_requires_ctx True iff this write set is used to check side
393393
/// effects in a requires clause in contract checking mode
394394
/// \param[in] assert_requires_ctx True iff this write set is used to check side
@@ -397,15 +397,20 @@ __CPROVER_HIDE:;
397397
/// side effects in an ensures clause in contract replacement mode
398398
/// \param[in] assert_ensures_ctx True iff this write set is used to check for
399399
/// side effects in an ensures clause in contract checking mode
400+
/// \param[in] allow_allocate True iff the context gobally allows dynamic
401+
/// allocation.
402+
/// \param[in] allow_deallocate True iff the context gobally allows dynamic
403+
/// deallocation.
400404
void __CPROVER_contracts_write_set_create(
401405
__CPROVER_contracts_write_set_ptr_t set,
402406
__CPROVER_size_t contract_assigns_size,
403407
__CPROVER_size_t contract_frees_size,
404-
__CPROVER_bool replacement,
405408
__CPROVER_bool assume_requires_ctx,
406409
__CPROVER_bool assert_requires_ctx,
407410
__CPROVER_bool assume_ensures_ctx,
408-
__CPROVER_bool assert_ensures_ctx)
411+
__CPROVER_bool assert_ensures_ctx,
412+
__CPROVER_bool allow_allocate,
413+
__CPROVER_bool allow_deallocate)
409414
{
410415
__CPROVER_HIDE:;
411416
#ifdef DFCC_DEBUG
@@ -417,16 +422,8 @@ __CPROVER_HIDE:;
417422
&(set->contract_assigns), contract_assigns_size);
418423
__CPROVER_contracts_obj_set_create_indexed_by_object_id(
419424
&(set->contract_frees));
420-
set->replacement = replacement;
421-
if(replacement)
422-
{
423-
__CPROVER_contracts_obj_set_create_append(
424-
&(set->contract_frees_replacement), contract_frees_size);
425-
}
426-
else
427-
{
428-
set->contract_frees_replacement.elems = 0;
429-
}
425+
__CPROVER_contracts_obj_set_create_append(
426+
&(set->contract_frees_append), contract_frees_size);
430427
__CPROVER_contracts_obj_set_create_indexed_by_object_id(&(set->allocated));
431428
__CPROVER_contracts_obj_set_create_indexed_by_object_id(&(set->deallocated));
432429
set->linked_is_fresh = 0;
@@ -436,6 +433,8 @@ __CPROVER_HIDE:;
436433
set->assert_requires_ctx = assert_requires_ctx;
437434
set->assume_ensures_ctx = assume_ensures_ctx;
438435
set->assert_ensures_ctx = assert_ensures_ctx;
436+
set->allow_allocate = allow_allocate;
437+
set->allow_deallocate = allow_deallocate;
439438
}
440439

441440
/// \brief Releases resources used by \p set.
@@ -454,20 +453,16 @@ __CPROVER_HIDE:;
454453
__CPROVER_rw_ok(&(set->contract_frees.elems), 0),
455454
"contract_frees writable");
456455
__CPROVER_assert(
457-
(set->replacement == 0) ||
458-
__CPROVER_rw_ok(&(set->contract_frees_replacement.elems), 0),
459-
"contract_frees_replacement writable");
456+
__CPROVER_rw_ok(&(set->contract_frees_append.elems), 0),
457+
"contract_frees_append writable");
460458
__CPROVER_assert(
461459
__CPROVER_rw_ok(&(set->allocated.elems), 0), "allocated writable");
462460
__CPROVER_assert(
463461
__CPROVER_rw_ok(&(set->deallocated.elems), 0), "deallocated writable");
464462
#endif
465463
__CPROVER_deallocate(set->contract_assigns.elems);
466464
__CPROVER_deallocate(set->contract_frees.elems);
467-
if(set->replacement != 0)
468-
{
469-
__CPROVER_deallocate(set->contract_frees_replacement.elems);
470-
}
465+
__CPROVER_deallocate(set->contract_frees_append.elems);
471466
__CPROVER_deallocate(set->allocated.elems);
472467
__CPROVER_deallocate(set->deallocated.elems);
473468
// do not free set->linked_is_fresh->elems or set->deallocated_linked->elems
@@ -585,29 +580,44 @@ __CPROVER_HIDE:;
585580

586581
// append pointer if available
587582
#ifdef DFCC_DEBUG
588-
if(set->replacement)
589-
__CPROVER_contracts_obj_set_append(&(set->contract_frees_replacement), ptr);
583+
__CPROVER_contracts_obj_set_append(&(set->contract_frees_append), ptr);
590584
#else
591-
if(set->replacement)
592-
{
593-
set->contract_frees_replacement.nof_elems =
594-
set->contract_frees_replacement.watermark;
595-
set->contract_frees_replacement
596-
.elems[set->contract_frees_replacement.watermark] = ptr;
597-
set->contract_frees_replacement.watermark += 1;
598-
set->contract_frees_replacement.is_empty = 0;
599-
}
585+
set->contract_frees_append.nof_elems = set->contract_frees_append.watermark;
586+
set->contract_frees_append.elems[set->contract_frees_append.watermark] = ptr;
587+
set->contract_frees_append.watermark += 1;
588+
set->contract_frees_append.is_empty = 0;
600589
#endif
601590
}
602591

603-
/// \brief Adds the pointer \p ptr to \p set->allocated.
592+
/// \brief Adds the dynamically allocated pointer \p ptr to \p set->allocated.
604593
/// \param[inout] set The set to update
605-
/// \param[in] ptr Pointer to an object declared using a `DECL x` or
606-
/// `x = __CPROVER_allocate(...)` GOTO instruction.
594+
/// \param[in] ptr Pointer to a dynamic object `x = __CPROVER_allocate(...)`.
607595
void __CPROVER_contracts_write_set_add_allocated(
608596
__CPROVER_contracts_write_set_ptr_t set,
609597
void *ptr)
610598
{
599+
__CPROVER_HIDE:;
600+
__CPROVER_assert(set->allow_allocate, "dynamic allocation is allowed");
601+
#if DFCC_DEBUG
602+
// call inlined below
603+
__CPROVER_contracts_obj_set_add(&(set->allocated), ptr);
604+
#else
605+
__CPROVER_size_t object_id = __CPROVER_POINTER_OBJECT(ptr);
606+
set->allocated.nof_elems = (set->allocated.elems[object_id] != 0)
607+
? set->allocated.nof_elems
608+
: set->allocated.nof_elems + 1;
609+
set->allocated.elems[object_id] = ptr;
610+
set->allocated.is_empty = 0;
611+
#endif
612+
}
613+
614+
/// \brief Adds the pointer \p ptr to \p set->allocated.
615+
/// \param[inout] set The set to update
616+
/// \param[in] ptr Pointer to an object declared using `DECL x`.
617+
void __CPROVER_contracts_write_set_add_decl(
618+
__CPROVER_contracts_write_set_ptr_t set,
619+
void *ptr)
620+
{
611621
__CPROVER_HIDE:;
612622
#if DFCC_DEBUG
613623
// call inlined below
@@ -659,10 +669,6 @@ void __CPROVER_contracts_write_set_record_deallocated(
659669
void *ptr)
660670
{
661671
__CPROVER_HIDE:;
662-
#ifdef DFCC_DEBUG
663-
__CPROVER_assert(set->replacement == 0, "!replacement");
664-
#endif
665-
666672
#if DFCC_DEBUG
667673
// we record the deallocation to be able to evaluate was_freed post conditions
668674
__CPROVER_contracts_obj_set_add(&(set->deallocated), ptr);
@@ -735,7 +741,6 @@ __CPROVER_bool __CPROVER_contracts_write_set_check_assignment(
735741
// manually inlined below
736742
{
737743
__CPROVER_HIDE:;
738-
__CPROVER_assert(set->replacement == 0, "!replacement");
739744
__CPROVER_assert(
740745
((ptr == 0) | __CPROVER_rw_ok(ptr, size)),
741746
"ptr NULL or writable up to size");
@@ -904,16 +909,13 @@ __CPROVER_HIDE:;
904909
/// \param[in] set Write set to check the deallocation against
905910
/// \param[in] ptr Deallocated pointer to check set to check the deallocation
906911
/// against
907-
/// \return True iff \p ptr is contained in \p set->contract_frees or
908-
/// \p set->allocated.
912+
/// \return True iff deallocation is allowed and \p ptr is contained in
913+
/// \p set->contract_frees or \p set->allocated.
909914
__CPROVER_bool __CPROVER_contracts_write_set_check_deallocate(
910915
__CPROVER_contracts_write_set_ptr_t set,
911916
void *ptr)
912917
{
913918
__CPROVER_HIDE:;
914-
#ifdef DFCC_DEBUG
915-
__CPROVER_assert(set->replacement == 0, "!replacement");
916-
#endif
917919
__CPROVER_size_t object_id = __CPROVER_POINTER_OBJECT(ptr);
918920

919921
#ifdef DFCC_DEBUG
@@ -924,16 +926,15 @@ __CPROVER_HIDE:;
924926
set->allocated.indexed_by_object_id,
925927
"set->allocated is indexed by object id");
926928
#endif
927-
return (ptr == 0) | (set->contract_frees.elems[object_id] == ptr) |
928-
(set->allocated.elems[object_id] == ptr);
929+
return (set->allow_deallocate) &
930+
((ptr == 0) | (set->contract_frees.elems[object_id] == ptr) |
931+
(set->allocated.elems[object_id] == ptr));
929932
}
930933

931934
/// \brief Checks the inclusion of the \p candidate->contract_assigns elements
932935
/// in \p reference->contract_assigns or \p reference->allocated.
933936
///
934-
/// \pre \p reference must not be in replacement mode.
935-
/// \pre \p candidate must be in replacement mode and \p candidate->allocated
936-
/// must be empty.
937+
/// \pre \p candidate->allocated must be empty.
937938
///
938939
/// \param[in] reference Reference write set from a caller
939940
/// \param[in] candidate Candidate write set from a contract being replaced
@@ -944,11 +945,6 @@ __CPROVER_bool __CPROVER_contracts_write_set_check_assigns_clause_inclusion(
944945
__CPROVER_contracts_write_set_ptr_t candidate)
945946
{
946947
__CPROVER_HIDE:;
947-
#ifdef DFCC_DEBUG
948-
__CPROVER_assert(
949-
reference->replacement == 0, "reference set in !replacement");
950-
__CPROVER_assert(candidate->replacement != 0, "candidate set in replacement");
951-
#endif
952948
__CPROVER_bool incl = 1;
953949
__CPROVER_contracts_car_t *current = candidate->contract_assigns.elems;
954950
__CPROVER_size_t idx = candidate->contract_assigns.max_elems;
@@ -969,9 +965,7 @@ __CPROVER_HIDE:;
969965
/// \brief Checks the inclusion of the \p candidate->contract_frees elements
970966
/// in \p reference->contract_frees or \p reference->allocated.
971967
///
972-
/// \pre \p reference must not be in replacement mode.
973-
/// \pre \p candidate must be in replacement mode and \p candidate->allocated
974-
/// must be empty.
968+
/// \pre \p candidate->allocated must be empty.
975969
///
976970
/// \param[in] reference Reference write set from a caller
977971
/// \param[in] candidate Candidate write set from a contract being replaced
@@ -983,9 +977,6 @@ __CPROVER_bool __CPROVER_contracts_write_set_check_frees_clause_inclusion(
983977
{
984978
__CPROVER_HIDE:;
985979
#ifdef DFCC_DEBUG
986-
__CPROVER_assert(
987-
reference->replacement == 0, "reference set in !replacement");
988-
__CPROVER_assert(candidate->replacement != 0, "candidate set in replacement");
989980
__CPROVER_assert(
990981
reference->contract_frees.indexed_by_object_id,
991982
"reference->contract_frees is indexed by object id");
@@ -994,8 +985,8 @@ __CPROVER_HIDE:;
994985
"reference->allocated is indexed by object id");
995986
#endif
996987
__CPROVER_bool all_incl = 1;
997-
void **current = candidate->contract_frees_replacement.elems;
998-
__CPROVER_size_t idx = candidate->contract_frees_replacement.max_elems;
988+
void **current = candidate->contract_frees_append.elems;
989+
__CPROVER_size_t idx = candidate->contract_frees_append.max_elems;
999990

1000991
SET_CHECK_FREES_CLAUSE_INCLUSION_LOOP:
1001992
while(idx != 0)
@@ -1030,13 +1021,8 @@ void __CPROVER_contracts_write_set_deallocate_freeable(
10301021
__CPROVER_contracts_write_set_ptr_t target)
10311022
{
10321023
__CPROVER_HIDE:;
1033-
#ifdef DFCC_DEBUG
1034-
__CPROVER_assert(set->replacement == 1, "set is in replacement");
1035-
__CPROVER_assert(
1036-
(target == 0) | (target->replacement == 0), "target is in !replacement");
1037-
#endif
1038-
void **current = set->contract_frees_replacement.elems;
1039-
__CPROVER_size_t idx = set->contract_frees_replacement.max_elems;
1024+
void **current = set->contract_frees_append.elems;
1025+
__CPROVER_size_t idx = set->contract_frees_append.max_elems;
10401026
SET_DEALLOCATE_FREEABLE_LOOP:
10411027
while(idx != 0)
10421028
{

src/goto-instrument/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SRC = accelerate/accelerate.cpp \
2626
contracts/dynamic-frames/dfcc_lift_memory_predicates.cpp \
2727
contracts/dynamic-frames/dfcc_instrument.cpp \
2828
contracts/dynamic-frames/dfcc_spec_functions.cpp \
29+
contracts/dynamic-frames/dfcc_contract_clauses_codegen.cpp \
2930
contracts/dynamic-frames/dfcc_contract_functions.cpp \
3031
contracts/dynamic-frames/dfcc_wrapper_program.cpp \
3132
contracts/dynamic-frames/dfcc_contract_handler.cpp \

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,14 @@ parse_function_contract_pair(const irep_idt &cli_flag)
9292
else if(split.size() == 2)
9393
{
9494
auto function_name = split[0];
95-
if(function_name.size() == 0)
95+
if(function_name.empty())
9696
{
9797
throw invalid_function_contract_pair_exceptiont{
9898
"couldn't find function name before '/' in '" + cli_flag_str + "'",
9999
correct_format_message};
100100
}
101101
auto contract_name = split[1];
102-
if(contract_name.size() == 0)
102+
if(contract_name.empty())
103103
{
104104
throw invalid_function_contract_pair_exceptiont{
105105
"couldn't find contract name after '/' in '" + cli_flag_str + "'",
@@ -191,14 +191,21 @@ dfcct::dfcct(
191191
instrument(goto_model, message_handler, utils, library),
192192
memory_predicates(goto_model, utils, library, instrument, message_handler),
193193
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),
194200
contract_handler(
195201
goto_model,
196202
message_handler,
197203
utils,
198204
library,
199205
instrument,
200206
memory_predicates,
201-
spec_functions),
207+
spec_functions,
208+
contract_clauses_codegen),
202209
swap_and_wrap(
203210
goto_model,
204211
message_handler,
@@ -483,6 +490,8 @@ void dfcct::instrument_other_functions()
483490

484491
goto_model.goto_functions.update();
485492

493+
// TODO specialise the library functions for the max size of
494+
// loop and function contracts
486495
if(to_check.has_value())
487496
{
488497
log.status() << "Specializing cprover_contracts functions for assigns "

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Author: Remi Delmas, [email protected]
3333
#include <util/irep.h>
3434
#include <util/message.h>
3535

36+
#include "dfcc_contract_clauses_codegen.h"
3637
#include "dfcc_contract_handler.h"
3738
#include "dfcc_instrument.h"
3839
#include "dfcc_library.h"
@@ -208,13 +209,15 @@ class dfcct
208209
message_handlert &message_handler;
209210
messaget log;
210211

211-
// hold the global state of the transformation (caches etc.)
212+
// Singletons that hold the global state of the program transformation
213+
// (caches etc.)
212214
dfcc_utilst utils;
213215
dfcc_libraryt library;
214216
namespacet ns;
215217
dfcc_instrumentt instrument;
216218
dfcc_lift_memory_predicatest memory_predicates;
217219
dfcc_spec_functionst spec_functions;
220+
dfcc_contract_clauses_codegent contract_clauses_codegen;
218221
dfcc_contract_handlert contract_handler;
219222
dfcc_swap_and_wrapt swap_and_wrap;
220223

0 commit comments

Comments
 (0)