diff --git a/.travis.yml b/.travis.yml index 6f1e2bc14..73653de41 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ matrix: compiler: clang env: COMPILER=clang++ - env: NAME="CPP-LINT" + before_script: git fetch origin master:master script: scripts/run_lint.sh master HEAD script: diff --git a/README.md b/README.md index aa46ece57..9a457586c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ About ===== -2LS is a verification tool for C programs. It is built upon the +2LS ("tools") is a verification tool for C programs. It is built upon the CPROVER framework ([cprover.org](http://www.cprover.org)), which supports C89, C99, most of C11 and most compiler extensions provided by gcc and Visual Studio. It allows verifying array bounds (buffer @@ -11,7 +11,6 @@ overflows), pointer safety, exceptions, user-specified assertions, and termination properties. The analysis is performed by template-based predicate synthesis and abstraction refinements techniques. -For more information see [cprover.org](http://www.cprover.org/2LS). License ======= @@ -19,3 +18,139 @@ License [build_img]: https://travis-ci.org/diffblue/2ls.svg?branch=master [travis]: https://travis-ci.org/diffblue/2ls + + +Overview +======== + + 2LS reduces program analysis problems expressed in second order logic + such as invariant or ranking function inference to synthesis problems + over templates. Hence, it reduces (an existential fragment of) 2nd + order Logic Solving to quantifier elimination in first order logic. + +The current tool has following capabilities: + +* function-modular interprocedural analysis of C code based on summaries +* summary and invariant inference using generic templates +* combined k-induction and invariant inference +* incremental bounded model checking +* function-modular termination analysis +* non-termination analysis + +Releases +======== + +Download using `git clone http://github.com/diffblue/2ls; cd 2ls; git checkout 2ls-x.y` + +* [2LS 0.7](http://github.com/diffblue/2ls/releases/tag/2ls-0.7) (08/2018) +* [2LS 0.6](http://github.com/diffblue/2ls/releases/tag/2ls-0.6) (12/2017) +* [2LS 0.5](http://github.com/diffblue/2ls/releases/tag/2ls-0.5) (01/2017) +* [2LS 0.4](http://github.com/diffblue/2ls/releases/tag/2ls-0.4) (08/2016) +* [2LS 0.3](http://svn.cprover.org/svn/deltacheck/releases/2ls-0.3) (08/2015) +* [2LS 0.2](http://svn.cprover.org/svn/deltacheck/releases/2ls-0.2) (06/2015) +* [2LS 0.1](http://svn.cprover.org/svn/deltacheck/releases/2ls-0.1) (11/2014) + +Software Verification Competition Contributions + +* [SV-COMP 2018](http://github.com/diffblue/2ls/releases/tag/2ls-0.6) (12/2017) +* [SV-COMP 2017](http://github.com/diffblue/2ls/releases/tag/2ls-0.5-sv-comp-2017) (01/2017) +* [SV-COMP 2016](http://svn.cprover.org/svn/deltacheck/releases/2ls-0.3-sv-comp-2016) (11/2015) [Follow these instructions](http://www.cprover.org/2LS/2ls-sv-comp-2016.pdf) + +Installation +============ + +`cd 2ls; ./install.sh` + + Run `src/2ls/2ls` + +Command line options +==================== + +The default abstract domain are intervals. If no options are given a context-insensitive interprocedural analysis is performed. For context-sensitivity, add --context-sensitive. + +Other analyses include: + +* BMC: --inline --havoc --unwind n +* Incremental BMC: --incremental-bmc +* Incremental k-induction: --havoc --k-induction +* Incremental k-induction and k-invariants (kIkI): --k-induction +* Intraprocedural abstract interpretation with property checks: --inline +* Necessary preconditions: --preconditions +* Sufficient preconditions: --preconditions --sufficient + +Currently the following abstract domains are available: + +* Intervals (default): --intervals +* Zones: --zones +* Octagons --octagons +* Equalities/disequalities: --equalities +* The abstract domain consisting of the Top element: --havoc + +Since release 0.6: + +* Heap abstract domain: --heap + +Since release 0.7: + +* Heap abstract domain with intervals or zones: --heap-[intervals|zones] +* Heap abstract domain with intervals or zones and loop paths: --heap-[intervals|zones] --sympath + +Interprocedural Termination Analysis +==================================== + +Is supported by release 0.1 and >=0.3. + +* Universal termination: --termination +* Context-sensitive universal termination: --termination --context-sensitive +* Sufficient preconditions for termination --termination --context-sensitive --preconditions + +Since release 0.6: + +* Nontermination analysis: --non-termination + +Features in development +======================= + +* ACDL solver (ATVA'17 [Lifting CDCL to Template-Based Abstract Domains for Program Verification](https://doi.org/10.1007/978-3-319-68167-2_2)) +* array domain +* disjunctive domains +* custom template specifications +* modular refinement +* template refinement +* thread-modular analysis + +Publications +============ + +* FMCAD'18 Template-Based Verification of Heap-Manipulating Programs +* TACAS'18 [2LS: Memory Safety and Non-Termination](https://link.springer.com/chapter/10.1007%2F978-3-319-89963-3_24) +* TOPLAS 40(1) 2018 [Bit-Precise Procedure-Modular Termination Analysis](https://dl.acm.org/citation.cfm?doid=3121136) +* ATVA'17 [Compositional Refutation Techniques](https://link.springer.com/chapter/10.1007%2F978-3-319-68167-2_12) +* ATVA'17 [Lifting CDCL to Template-Based Abstract Domains for Program Verification](https://doi.org/10.1007/978-3-319-68167-2_2) +* TACAS'16 [2LS for Program Analysis](http://dl.acm.org/citation.cfm?id=2945506) +* SAS'15 [Safety Verification and Refutation by k-Invariants and k-Induction](http://link.springer.com/chapter/10.1007%2F978-3-662-48288-9_9) +* ASE'15 [Synthesising Interprocedural Bit-Precise Termination Proofs](http://dl.acm.org/citation.cfm?id=2916211) [Experimental log](http://www.cs.ox.ac.uk/people/peter.schrammel/2ls/ase15-experimental_results_log.txt) [Additional material](http://www.cs.ox.ac.uk/people/peter.schrammel/2ls/ase15-additional-material.tgz) [Website](http://www.cprover.org/termination/modular) + +Contributors +============ + +* Björn Wachter +* Cristina David +* Daniel Kroening +* Hongyi Chen +* Madhukar Kumar +* Martin Brain +* Martin Hruska +* Peter Schrammel +* Rajdeep Mukherjee +* Samuel Bücheli +* Saurabh Joshi +* Stefan Marticek +* Viktor Malik + +Contact +======= + +[Peter Schrammel](http://www.schrammel.it) + + diff --git a/install.sh b/install.sh index c72d8988e..ecff84bae 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/bin/bash CBMC_REPO=https://github.com/peterschrammel/cbmc -CBMC_VERSION=d95e7da28018fd315b04a1201d5b7cfe8195cbc6 +CBMC_VERSION=2ls-prerequisites-0.7 if [ "$1" != "" ] then @@ -12,7 +12,16 @@ git clone $CBMC_REPO cd cbmc CBMC=`pwd` git checkout $CBMC_VERSION -make -C src minisat2-download +if grep '^MINISAT2' src/config.inc > /dev/null +then + make -C src minisat2-download > /dev/null +elif grep '^GLUCOSE' src/config.inc +then + make -C src glucose-download +else + echo "SAT solver not supported" + exit 1 +fi if [ "$COMPILER" != "" ] then make -C src CXX=$COMPILER diff --git a/regression/Makefile b/regression/Makefile index 22aec995c..7143b0ad8 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -1,7 +1,15 @@ -DIRS = termination kiki preconditions interprocedural invariants +DIRS = nontermination \ + termination \ + kiki \ + preconditions \ + interprocedural \ + invariants \ + heap \ + heap-data \ + memsafety test: - $(foreach var,$(DIRS), make -C $(var) test;) + $(foreach var,$(DIRS), make -C $(var) test || exit 1;) clean: $(foreach var,$(DIRS), make -C $(var) clean;) diff --git a/regression/graphml/Makefile b/regression/graphml/Makefile index bfa217de8..7a53bfe32 100644 --- a/regression/graphml/Makefile +++ b/regression/graphml/Makefile @@ -1,6 +1,6 @@ #FLAGS = --k-induction --competition-mode --32 FLAGS = --unwind 11 --no-unwinding-assertions -#2LS = ../../../src/summarizer/2ls +#2LS = ../../../src/summarizer/2ls 2LS = $W/svncprover/branches/peter-incremental-unwinding/src/cbmc/cbmc CPACHECKER = $W/svncpachecker ULTIMATE = $W/UltimateAutomizer diff --git a/regression/graphml/loop23/main.c b/regression/graphml/loop23/main.c index ac7fc9378..5b2e53f86 100644 --- a/regression/graphml/loop23/main.c +++ b/regression/graphml/loop23/main.c @@ -6,10 +6,10 @@ int main() int y; if(x) { return x;} - for(x=0;x<2;x++) + for(x=0;x<2;x++) { if(x==y) { return x;} - + } __VERIFIER_assert(0); return 0; diff --git a/regression/graphml/loop29/main.c b/regression/graphml/loop29/main.c index ec555a910..da0ea8cbe 100644 --- a/regression/graphml/loop29/main.c +++ b/regression/graphml/loop29/main.c @@ -7,6 +7,6 @@ void main() { int y; if(-3>y || y>-1) return; x += y; - } - __VERIFIER_assert(x==0 || x==-2); -} + } + __VERIFIER_assert(x==0 || x==-2); +} diff --git a/regression/graphml/malloc1/main.c b/regression/graphml/malloc1/main.c index 30f3fc751..36eda4d94 100644 --- a/regression/graphml/malloc1/main.c +++ b/regression/graphml/malloc1/main.c @@ -1029,7 +1029,7 @@ int ssl3_connect(SSL *s ) { blastFlag = 0; - s->state = 12292; + s->state = 12292; while (1) { if (s->state == 12292) { goto switch_1_12292; @@ -1047,15 +1047,15 @@ int ssl3_connect(SSL *s ) switch_1_4368: ; if (blastFlag == 0) { blastFlag = 1; - } + } s->state = 4384; goto switch_1_break; switch_1_4384: ; if (blastFlag == 1) { blastFlag = 2; goto ERROR; - } - + } + goto end; switch_1_default: goto end; diff --git a/regression/heap-data/Makefile b/regression/heap-data/Makefile new file mode 100644 index 000000000..34cd734b9 --- /dev/null +++ b/regression/heap-data/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 + +test: + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/heap-data/calendar/main.c b/regression/heap-data/calendar/main.c new file mode 100644 index 000000000..2ee9630e5 --- /dev/null +++ b/regression/heap-data/calendar/main.c @@ -0,0 +1,40 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +#define APPEND(l,i) {i->next=l; l=i;} + +typedef struct node { + struct node *next; + int event1; + int event2; +} Node; + +int main() { + Node *l = NULL; + + while (__VERIFIER_nondet_int()) { + int ev1 = __VERIFIER_nondet_int(); + int ev2 = __VERIFIER_nondet_int(); + if (ev1 < 0 || ev1 > 3 || ev2 < 0 || ev2 > 3) + continue; + + if (((ev1 == 0) && (ev2 == 2)) || ((ev1 == 1) && (ev2 == 3)) || ((ev1 == 0) && (ev2 == 3))) + continue; + + Node *p = malloc(sizeof(*p)); + p->event1 = ev1; + p->event2 = ev2; + APPEND(l,p) + } + + Node *i = l; + + while (i != NULL) { + if (((i->event1 == 1) && (i->event2 == 3)) || ((i->event1 == 0) && (i->event2 == 2))) + __VERIFIER_error(); + i = i->next; + } +} + diff --git a/regression/heap-data/calendar/test.desc b/regression/heap-data/calendar/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/calendar/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/cart/main.c b/regression/heap-data/cart/main.c new file mode 100644 index 000000000..3144e411e --- /dev/null +++ b/regression/heap-data/cart/main.c @@ -0,0 +1,46 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +#define APPEND(l,i) {i->next=l; l=i;} + +typedef struct node { + struct node *next; + int stock; + int order; +} Node; + +int main() { + Node *l = NULL; + + while (__VERIFIER_nondet_int()) { + int stock = __VERIFIER_nondet_int(); + if (stock < 0) + continue; + + Node *p = malloc(sizeof(*p)); + p->stock = stock; + p->order = 0; + APPEND(l,p) + } + + Node *i = l; + while (i != NULL) { + int order = __VERIFIER_nondet_int(); + if (order < 0 || i->stock < order) + continue; + i->order = order; + i->stock = i->stock; + i = i->next; + } + + + i = l; + while (i != NULL) { + if (i->order > i->stock) + __VERIFIER_error(); + i = i->next; + } +} + diff --git a/regression/heap-data/cart/test.desc b/regression/heap-data/cart/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/cart/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/hash_fun/main.c b/regression/heap-data/hash_fun/main.c new file mode 100644 index 000000000..85448ca32 --- /dev/null +++ b/regression/heap-data/hash_fun/main.c @@ -0,0 +1,42 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +#define INTERVAL_SIZE 100 + +struct node { + int hash; + struct node *next; +}; + +int hash_fun(); + +void append_to_list(struct node **list, int hash) { + struct node *node = malloc(sizeof(*node)); + node->next = *list; + node->hash = hash; + *list = node; +} + +int main() { + struct node *list = NULL; + + int base = __VERIFIER_nondet_int(); + + while (__VERIFIER_nondet_int()) { + if (base >= 0 && base <= 1000000) { + base = base + 0; + int hash = hash_fun(); + + if (hash > base && hash < base + INTERVAL_SIZE) + append_to_list(&list, hash); + } + } + + while (list) { + if (!(list->hash >= base && list->hash < base + INTERVAL_SIZE)) + __VERIFIER_error(); + list = list->next; + } +} diff --git a/regression/heap-data/hash_fun/test.desc b/regression/heap-data/hash_fun/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/hash_fun/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/min_max/main.c b/regression/heap-data/min_max/main.c new file mode 100644 index 000000000..76b129535 --- /dev/null +++ b/regression/heap-data/min_max/main.c @@ -0,0 +1,40 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include +#include + +#define APPEND(l,i) {i->next=l; l=i;} + +typedef struct node { + struct node *next; + int val; +} Node; + +int main() { + Node *l = NULL; + int min = INT_MAX, max = -INT_MAX; + + while (__VERIFIER_nondet_int()) { + Node *p = malloc(sizeof(*p)); + p->val = __VERIFIER_nondet_int(); + APPEND(l, p) + + if (min > p->val) { + min = p->val; + } + if (max < p->val) { + max = p->val; + } + + } + + Node *i = l; + while (i != NULL) { + if (i->val < min) + __VERIFIER_error(); + if (i->val > max) + __VERIFIER_error(); + i = i->next; + } +} diff --git a/regression/heap-data/min_max/test.desc b/regression/heap-data/min_max/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/min_max/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/packet_filter/main.c b/regression/heap-data/packet_filter/main.c new file mode 100644 index 000000000..b2a16f4b4 --- /dev/null +++ b/regression/heap-data/packet_filter/main.c @@ -0,0 +1,81 @@ +extern unsigned __VERIFIER_nondet_uint(); +extern int __VERIFIER_nondet_int(); +extern char *__VERIFIER_nondet_charp(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +#define LOW 0 +#define HIGH 1 + +typedef struct packet { + unsigned size; + unsigned prio; + char *payload; +} Packet; + +typedef struct packet_list_node { + struct packet packet; + struct packet_list_node *next; +} *Node; + +struct packet_queue { + struct packet_list_node *front; +}; + + +Packet receive() { + Packet packet; + packet.size = __VERIFIER_nondet_uint(); + packet.prio = __VERIFIER_nondet_int() ? LOW : HIGH; + packet.payload = __VERIFIER_nondet_charp(); + return packet; +} + +extern void send(struct packet p); + +void append_to_queue(Packet p, Node *q) { + Node node = malloc(sizeof(*node)); + node->packet = p; + node->next = *q; + *q = node; +} + +void process_prio_queue(Node q) { + for (Node node = q; node != NULL; node = node->next) { + if (!(node->packet.prio == HIGH || node->packet.size < 500)) + __VERIFIER_error(); + send(node->packet); + } +} + +void process_normal_queue(Node q) { + for (Node node = q; node != NULL; node = node->next) { + if (!(node->packet.prio == LOW && node->packet.size >= 500)) + __VERIFIER_error(); + send(node->packet); + } +} + +int main() { + Node prio_queue = NULL; + Node normal_queue = NULL; + + while (__VERIFIER_nondet_int()) { + Packet new_packet = receive(); + if (new_packet.size > 0) { + if (new_packet.prio == HIGH) { + append_to_queue(new_packet, &prio_queue); + } else if (new_packet.size < 500) { + append_to_queue(new_packet, &prio_queue); + } else { + append_to_queue(new_packet, &normal_queue); + } + } + } + + process_prio_queue(prio_queue); + process_normal_queue(normal_queue); + + return 0; +} diff --git a/regression/heap-data/packet_filter/test.desc b/regression/heap-data/packet_filter/test.desc new file mode 100644 index 000000000..2ed70fde0 --- /dev/null +++ b/regression/heap-data/packet_filter/test.desc @@ -0,0 +1,6 @@ +THOROUGH +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/process_queue/main.c b/regression/heap-data/process_queue/main.c new file mode 100644 index 000000000..8108ecfac --- /dev/null +++ b/regression/heap-data/process_queue/main.c @@ -0,0 +1,69 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +#define MAX_PROC 1000 + +struct process_node { + int process_id; + int time_to_wait; + + struct process_node *next; +}; + +extern void run_process(int id); + +void append_to_queue(struct process_node *n, struct process_node **q) { + n->next = *q; + *q = n; +} + +struct process_node *choose_next(struct process_node **q) { + struct process_node *node = *q; + struct process_node *prev = NULL; + struct process_node *result = NULL; + while (node != NULL) { + if (node->time_to_wait == 1) { + result = node; + if (prev == NULL) + *q = node->next; + else + prev->next = node->next; + } else { + node->time_to_wait--; + } + prev = node; + node = node->next; + } + return result; +} + +void check_queue(struct process_node *q) { + for (struct process_node *n = q; n != NULL; n = n->next) + if (!(n->time_to_wait >= 1)) + __VERIFIER_error(); +} + + +int main() { + struct process_node *queue = NULL; + int next_time = 1; + + while (__VERIFIER_nondet_int()) { + if (next_time < MAX_PROC && __VERIFIER_nondet_int()) { + int new_id = __VERIFIER_nondet_int(); + + struct process_node *new_process = malloc(sizeof(*new_process)); + new_process->process_id = __VERIFIER_nondet_int(); + new_process->time_to_wait = next_time++; + append_to_queue(new_process, &queue); + } else if (next_time > 1){ + struct process_node *p = choose_next(&queue); + next_time--; + run_process(p->process_id); + } + + check_queue(queue); + } +} diff --git a/regression/heap-data/process_queue/test.desc b/regression/heap-data/process_queue/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/process_queue/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/quick_sort_split/main.c b/regression/heap-data/quick_sort_split/main.c new file mode 100644 index 000000000..8b9ec9efb --- /dev/null +++ b/regression/heap-data/quick_sort_split/main.c @@ -0,0 +1,62 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +#define LOW -1 +#define HIGH 1 + +struct node { + int expected_list; + int value; + struct node *next; +}; + +void append_to_list(struct node **list, int val, int exp) { + struct node *node = malloc(sizeof(*node)); + node->next = *list; + node->value = val; + node->expected_list = exp; + *list = node; +} + +struct node *create_list() { + struct node *list = NULL; + while (__VERIFIER_nondet_int()) { + int v = __VERIFIER_nondet_int(); + if (v < 0) + append_to_list(&list, v, LOW); + else + append_to_list(&list, v, HIGH); + } + return list; +} + +int main() { + struct node *list = create_list(); + + struct node *low = NULL; + struct node *high = NULL; + + // Split list into low and high + struct node *p = list; + while (p) { + struct node *l = p->value >= 0 ? high : low; + struct node *next = p->next; + p->next = l; + l = p; + p = next; + } + + // Check that low and high contain expected elements + while (low) { + if (!(low->expected_list == LOW)) + __VERIFIER_error(); + low = low->next; + } + while (high) { + if (!(high->expected_list == HIGH)) + __VERIFIER_error(); + high = high->next; + } +} diff --git a/regression/heap-data/quick_sort_split/test.desc b/regression/heap-data/quick_sort_split/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/quick_sort_split/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/running_example/main.c b/regression/heap-data/running_example/main.c new file mode 100644 index 000000000..378fa8348 --- /dev/null +++ b/regression/heap-data/running_example/main.c @@ -0,0 +1,35 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +typedef struct node { + int val; + struct node *next; +} Node; + +int main() { + Node *p, *list = malloc(sizeof(*list)); + Node *tail = list; + list->next = NULL; + list->val = 10; + while (__VERIFIER_nondet_int()) { + int x; + if (x < 10 || x > 20) continue; + p = malloc(sizeof(*p)); + tail->next = p; + p->next = NULL; + p->val = x; + tail = p; + } + + while (1) { + for (p = list; p!= NULL; p = p->next) { + if (!(p->val <= 20 && p->val >= 10)) + __VERIFIER_error(); + if (p->val < 20) p->val++; + else p->val /= 2; + } + } +} + diff --git a/regression/heap-data/running_example/test.desc b/regression/heap-data/running_example/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/running_example/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/running_example_assume/main.c b/regression/heap-data/running_example_assume/main.c new file mode 100644 index 000000000..d64eed513 --- /dev/null +++ b/regression/heap-data/running_example_assume/main.c @@ -0,0 +1,35 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +typedef struct node { + int val; + struct node *next; +} Node; + +int main() { + Node *p, *list = malloc(sizeof(*list)); + Node *tail = list; + list->next = NULL; + list->val = 10; + while (__VERIFIER_nondet_int()) { + int x; + __CPROVER_assume(x >= 10 && x <= 20); + p = malloc(sizeof(*p)); + tail->next = p; + p->next = NULL; + p->val = x; + tail = p; + } + + while (1) { + for (p = list; p!= NULL; p = p->next) { + if (!(p->val <= 20 && p->val >= 10)) + __VERIFIER_error(); + if (p->val < 20) p->val++; + else p->val /= 2; + } + } +} + diff --git a/regression/heap-data/running_example_assume/test.desc b/regression/heap-data/running_example_assume/test.desc new file mode 100644 index 000000000..253d82f29 --- /dev/null +++ b/regression/heap-data/running_example_assume/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/shared_mem1/main.c b/regression/heap-data/shared_mem1/main.c new file mode 100644 index 000000000..2c0b16503 --- /dev/null +++ b/regression/heap-data/shared_mem1/main.c @@ -0,0 +1,49 @@ +extern int __VERIFIER_nondet_int(); +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +struct mem { + int val; +}; + +struct list_node { + int x; + struct mem *mem; + struct list_node *next; +}; + +int main() { + struct mem *m = malloc(sizeof(*m)); + m->val = 100; + + struct list_node *head = malloc(sizeof(*head)); + head->x = 1; + head->mem = m; + head->next = head; + + struct list_node *list = head; + + while (__VERIFIER_nondet_int()) { + int x = __VERIFIER_nondet_int(); + if (x > 0 && x < 10) { + struct list_node *n = malloc(sizeof(*n)); + n->x = x; + n->mem = m; + n->next = head; + list->next = n; + } + } + + list = head; + while (list) { + if (list->mem->val <= 100) + list->mem->val += list->x; + else + list->mem->val -= list->x; + list = list->next; + + if (!(m->val > 90 && m->val < 110)) + __VERIFIER_error(); + } +} diff --git a/regression/heap-data/shared_mem1/test.desc b/regression/heap-data/shared_mem1/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/shared_mem1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap-data/shared_mem2/main.c b/regression/heap-data/shared_mem2/main.c new file mode 100644 index 000000000..40032f9b3 --- /dev/null +++ b/regression/heap-data/shared_mem2/main.c @@ -0,0 +1,45 @@ +extern int __VERIFIER_nondet_int(); + +#include + +struct mem { + int val; +}; + +struct list_node { + int x; + struct mem *mem; + struct list_node *next; +}; + +int main() { + struct mem *m = malloc(sizeof(*m)); + m->val = 0; + + struct list_node *head = malloc(sizeof(*head)); + head->x = 1; + head->mem = m; + head->next = head; + + struct list_node *list = head; + + while (__VERIFIER_nondet_int()) { + int x = __VERIFIER_nondet_int(); + if (x > 0 && x < 10) { + struct list_node *n = malloc(sizeof(*n)); + n->x = x; + n->mem = m; + n->next = head; + list->next = n; + } + } + + list = head; + while (m->val < 100) { + if (list->mem->val + list->x <= 100) + list->mem->val += list->x; + list = list->next; + } + if (!(m->val == 100)) + __VERIFIER_nondet_int(); +} diff --git a/regression/heap-data/shared_mem2/test.desc b/regression/heap-data/shared_mem2/test.desc new file mode 100644 index 000000000..b48b4027a --- /dev/null +++ b/regression/heap-data/shared_mem2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-values-refine --sympath --inline +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/Makefile b/regression/heap/Makefile new file mode 100644 index 000000000..34cd734b9 --- /dev/null +++ b/regression/heap/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 + +test: + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/heap/array_unwind1/main.c b/regression/heap/array_unwind1/main.c new file mode 100644 index 000000000..dceaa92ce --- /dev/null +++ b/regression/heap/array_unwind1/main.c @@ -0,0 +1,15 @@ +#include +#include + +void main() +{ + int *a[2]; + a[0]=malloc(sizeof(int)); + for(int i=0; i<2; ++i) + { + a[i]=malloc(sizeof(int)); + *a[i]=i; + } + assert(*a[0]==0); + assert(*a[1]==2); +} diff --git a/regression/heap/array_unwind1/test.desc b/regression/heap/array_unwind1/test.desc new file mode 100644 index 000000000..fddf53028 --- /dev/null +++ b/regression/heap/array_unwind1/test.desc @@ -0,0 +1,8 @@ +KNOWNBUG +main.c +--incremental-bmc --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion \*a\[0\]==0: OK +^\[main\.assertion\.2\] assertion \*a\[1\]==2: FAILURE diff --git a/regression/heap/array_unwind2/main.c b/regression/heap/array_unwind2/main.c new file mode 100644 index 000000000..cc0435077 --- /dev/null +++ b/regression/heap/array_unwind2/main.c @@ -0,0 +1,15 @@ +#include +#include + +void main() +{ + int *a[2]; + a[0]=malloc(sizeof(int)); + for(int i=0; i<2; ++i) + { + a[i]=malloc(sizeof(int)); + *a[i]=i; + } + assert(*a[0]==1); + assert(*a[1]==1); +} diff --git a/regression/heap/array_unwind2/test.desc b/regression/heap/array_unwind2/test.desc new file mode 100644 index 000000000..63d86204c --- /dev/null +++ b/regression/heap/array_unwind2/test.desc @@ -0,0 +1,8 @@ +KNOWNBUG +main.c +--incremental-bmc --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion \*a\[0\]==1: FAILURE +^\[main\.assertion\.2\] assertion \*a\[1\]==1: OK diff --git a/regression/heap/built_from_end/main.c b/regression/heap/built_from_end/main.c new file mode 100644 index 000000000..47fe17aed --- /dev/null +++ b/regression/heap/built_from_end/main.c @@ -0,0 +1,38 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s and finally a 0 (arbitrary length); + * afterwards, go through it and check if the list does have the correct form, and in particular + * finishes by a 0. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List t; + List p = 0; + while (__VERIFIER_nondet_int()) { + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->h = 1; + t->n = p; + p = t; + } + while (p!=0) { + if (p->h != 1) { + ERROR: __VERIFIER_error(); + } + p = p->n; + } +} + diff --git a/regression/heap/built_from_end/test.desc b/regression/heap/built_from_end/test.desc new file mode 100644 index 000000000..e086f8559 --- /dev/null +++ b/regression/heap/built_from_end/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-interval --inline --no-propagation --sympath +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/dll1_simple/main.c b/regression/heap/dll1_simple/main.c new file mode 100644 index 000000000..2fc0f460f --- /dev/null +++ b/regression/heap/dll1_simple/main.c @@ -0,0 +1,107 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +extern int __VERIFIER_nondet_int(void); + +static void fail(void) { +ERROR: __VERIFIER_error(); +} + +#define ___SL_ASSERT(cond) do { \ + if (!(cond)) \ + fail(); \ + assert(cond); \ +} while (0) + +struct node { + struct node *next; + struct node *prev; +}; + +static struct node* alloc_node(void) +{ + struct node *ptr = malloc(sizeof *ptr); + + ptr->next = NULL; + ptr->prev = NULL; + return ptr; +} + +static void chain_node(struct node **ppnode) +{ + struct node *node = alloc_node(); + node->next = *ppnode; + *ppnode = node; +} + +static struct node* create_sll() +{ + struct node *list = NULL; + + do + chain_node(&list); + while (__VERIFIER_nondet_int()); + + return list; +} + +void init_back_link(struct node *list) { + while (list) { + struct node *next = list->next; + // if (!next) + // return; + + next->prev = list; + list = next; + } +} + +void reverse_dll(struct node *list) { + while (list) { + struct node *next = list->next; + list->next = list->prev; + list->prev = next; + list = next; + } +} + +void remove_fw_link(struct node *list) { + while (list) { + struct node *next = list->next; + list->next = NULL; + list = next; + } +} + +void check_seq_next(const struct node *beg, const struct node *const end) { + assert(beg != NULL); + assert(end != NULL); + + for (beg = beg->next; end != beg; beg = beg->next) + assert(beg != NULL); +} + +void check_seq_prev(const struct node *beg, const struct node *const end) { + assert(beg != NULL); + assert(end != NULL); + + for (beg = beg->prev; end != beg; beg = beg->prev) + assert(beg != NULL); +} + +void main() +{ + struct node *list = create_sll(); + + init_back_link(list); + + // reverse_dll(list); + // check_seq_prev(p1, p2); + // check_seq_next(p2, p1); + + remove_fw_link(list); + + assert(list->next == NULL); +} + diff --git a/regression/heap/dll1_simple/test.desc b/regression/heap/dll1_simple/test.desc new file mode 100644 index 000000000..92784083a --- /dev/null +++ b/regression/heap/dll1_simple/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--heap --inline --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/dll2_segments/main.c b/regression/heap/dll2_segments/main.c new file mode 100644 index 000000000..e9c81d319 --- /dev/null +++ b/regression/heap/dll2_segments/main.c @@ -0,0 +1,122 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +#include + +extern int __VERIFIER_nondet_int(void); + +static void fail(void) { +ERROR: __VERIFIER_error(); +} + +#define ___SL_ASSERT(cond) do { \ + if (!(cond)) \ + fail(); \ + assert(cond); \ +} while (0) + +struct node { + struct node *next; + struct node *prev; +}; + +static struct node* alloc_node(void) +{ + struct node *ptr = malloc(sizeof *ptr); + + ptr->next = NULL; + ptr->prev = NULL; + return ptr; +} + +static void chain_node(struct node **ppnode) +{ + struct node *node = alloc_node(); + node->next = *ppnode; + *ppnode = node; +} + +static struct node* create_sll(const struct node **pp1, const struct node **pp2) +{ + *pp2 = NULL; + + do + chain_node(pp2); + while (__VERIFIER_nondet_int()); + + *pp1 = *pp2; + + do + chain_node(pp1); + while (__VERIFIER_nondet_int()); + + struct node *list = *pp1; + + do + chain_node(&list); + while (__VERIFIER_nondet_int()); + + return list; +} + +void init_back_link(struct node *list) { + for (;;) { + struct node *next = list->next; + if (!next) + return; + + next->prev = list; + list = next; + } +} + +// void reverse_dll(struct node *list) { +// while (list) { +// struct node *next = list->next; +// list->next = list->prev; +// list->prev = next; +// list = next; +// } +// } + +void remove_fw_link(struct node *list) { + while (list) { + struct node *next = list->next; + list->next = NULL; + list = next; + } +} + +void check_seq_next(const struct node *beg, const struct node *const end) { + assert(beg != NULL); + assert(end != NULL); + + for (beg = beg->next; end != beg; beg = beg->next) + assert(beg != NULL); +} + +void check_seq_prev(const struct node *beg, const struct node *const end) { + assert(beg != NULL); + assert(end != NULL); + + for (beg = beg->prev; end != beg; beg = beg->prev) + assert(beg != NULL); +} + +void main() +{ + const struct node *p1, *p2; + + struct node *list = create_sll(&p1, &p2); + + check_seq_next(p1, p2); + + init_back_link(list); + + check_seq_next(p1, p2); + check_seq_prev(p2, p1); + + remove_fw_link(list); + + check_seq_prev(p2, p1); +} + diff --git a/regression/heap/dll2_segments/test.desc b/regression/heap/dll2_segments/test.desc new file mode 100644 index 000000000..901c52dd3 --- /dev/null +++ b/regression/heap/dll2_segments/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--heap --context-sensitive --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/dynobj_concret/main.c b/regression/heap/dynobj_concret/main.c new file mode 100644 index 000000000..d59aae01b --- /dev/null +++ b/regression/heap/dynobj_concret/main.c @@ -0,0 +1,30 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); + +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List t; + List p = NULL; + while (__VERIFIER_nondet_int()) { + t = (List) malloc(sizeof(struct node)); + t->n = p; + p = t; + } + + if (p && p->n) { + List n = p->n; + List nn = n->n; + assert(n==nn); + } +} diff --git a/regression/heap/list_false/main.c b/regression/heap/list_false/main.c new file mode 100644 index 000000000..386921ee1 --- /dev/null +++ b/regression/heap/list_false/main.c @@ -0,0 +1,53 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s, then 2s and finally + * on 3 (arbitrary length); afterwards, go through it and check + * if the the list does have the correct form, and in particular + * finishes by a 3. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->2->....->2->3 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + List t; + List p = a; + while (__VERIFIER_nondet_int()) { + p->h = 1; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + while (__VERIFIER_nondet_int()) { + p->h = 2; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + p->h = 3; + + /* Check it */ + p = a; + while (p->h == 2) + p = p->n; + while (p->h == 1) + p = p->n; + if(p->h != 3) + ERROR: __VERIFIER_error(); + + return 0; +} diff --git a/regression/heap/list_false/test.desc b/regression/heap/list_false/test.desc new file mode 100644 index 000000000..450b08c19 --- /dev/null +++ b/regression/heap/list_false/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--heap-interval --sympath --inline --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ +^\[main.assertion.1\] : UNKNOWN diff --git a/regression/heap/list_false_kind/main.c b/regression/heap/list_false_kind/main.c new file mode 100644 index 000000000..386921ee1 --- /dev/null +++ b/regression/heap/list_false_kind/main.c @@ -0,0 +1,53 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s, then 2s and finally + * on 3 (arbitrary length); afterwards, go through it and check + * if the the list does have the correct form, and in particular + * finishes by a 3. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->2->....->2->3 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + List t; + List p = a; + while (__VERIFIER_nondet_int()) { + p->h = 1; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + while (__VERIFIER_nondet_int()) { + p->h = 2; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + p->h = 3; + + /* Check it */ + p = a; + while (p->h == 2) + p = p->n; + while (p->h == 1) + p = p->n; + if(p->h != 3) + ERROR: __VERIFIER_error(); + + return 0; +} diff --git a/regression/heap/list_false_kind/test.desc b/regression/heap/list_false_kind/test.desc new file mode 100644 index 000000000..93984c1aa --- /dev/null +++ b/regression/heap/list_false_kind/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--heap-interval --sympath --inline --no-propagation --k-induction +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main.assertion.1\] : FAILURE diff --git a/regression/heap/list_iter1/main.c b/regression/heap/list_iter1/main.c new file mode 100644 index 000000000..cfb4639a9 --- /dev/null +++ b/regression/heap/list_iter1/main.c @@ -0,0 +1,25 @@ +#include +#include + +struct list +{ + int x; + struct list *n; +}; + +void main() +{ + struct list *l; + struct list *nl1=malloc(sizeof(struct list)); + nl1->x=0; + nl1->n=l; + l=nl1; + struct list *nl2=malloc(sizeof(struct list)); + nl2->x=1; + nl2->n=l; + l=nl2; + assert(l->n->x==0); + assert(l->n->x==1); + assert(l->x==2); + assert(l->x==1); +} diff --git a/regression/heap/list_iter1/test.desc b/regression/heap/list_iter1/test.desc new file mode 100644 index 000000000..6b9b3ed96 --- /dev/null +++ b/regression/heap/list_iter1/test.desc @@ -0,0 +1,10 @@ +KNOWNBUG +main.c +--incremental-bmc --independent-properties --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion l->n->x==0: OK +^\[main\.assertion\.2\] assertion l->n->x==1: FAILURE +^\[main\.assertion\.3\] assertion l->x==2: FAILURE +^\[main\.assertion\.4\] assertion l->x==1: OK diff --git a/regression/heap/list_iter2/main.c b/regression/heap/list_iter2/main.c new file mode 100644 index 000000000..d53a6b3e5 --- /dev/null +++ b/regression/heap/list_iter2/main.c @@ -0,0 +1,33 @@ +#include +#include + +struct list +{ + int x; + struct list *n; +}; + +void main() +{ + struct list *l; + struct list *nl1=malloc(sizeof(struct list)); + nl1->x=-1; + nl1->n=l; + l=nl1; + struct list *nl2=malloc(sizeof(struct list)); + nl2->x=-1; + nl2->n=l; + l=nl2; + + struct list *m=l; + for(int i=0; i<2; ++i) + { + m->x=i; + m=m->n; + } + + assert(l->n->x==0); + assert(l->n->x==1); + assert(l->x==2); + assert(l->x==1); +} diff --git a/regression/heap/list_iter2/test.desc b/regression/heap/list_iter2/test.desc new file mode 100644 index 000000000..6b9b3ed96 --- /dev/null +++ b/regression/heap/list_iter2/test.desc @@ -0,0 +1,10 @@ +KNOWNBUG +main.c +--incremental-bmc --independent-properties --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion l->n->x==0: OK +^\[main\.assertion\.2\] assertion l->n->x==1: FAILURE +^\[main\.assertion\.3\] assertion l->x==2: FAILURE +^\[main\.assertion\.4\] assertion l->x==1: OK diff --git a/regression/heap/list_iter3/main.c b/regression/heap/list_iter3/main.c new file mode 100644 index 000000000..4cc233ea3 --- /dev/null +++ b/regression/heap/list_iter3/main.c @@ -0,0 +1,33 @@ +#include +#include + +struct list +{ + int x; + struct list *n; +}; + +void main() +{ + struct list *l; + struct list *nl1=malloc(sizeof(struct list)); + nl1->x=-1; + nl1->n=l; + l=nl1; + struct list *nl2=malloc(sizeof(struct list)); + nl2->x=-1; + nl2->n=l; + l=nl2; + + struct list *m=l; + for(int i=0; m!=NULL; ++i) + { + m->x=i; + m=m->n; + } + + assert(l->n->x==0); + assert(l->n->x==1); + assert(l->x==2); + assert(l->x==1); +} diff --git a/regression/heap/list_iter3/test.desc b/regression/heap/list_iter3/test.desc new file mode 100644 index 000000000..6b9b3ed96 --- /dev/null +++ b/regression/heap/list_iter3/test.desc @@ -0,0 +1,10 @@ +KNOWNBUG +main.c +--incremental-bmc --independent-properties --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion l->n->x==0: OK +^\[main\.assertion\.2\] assertion l->n->x==1: FAILURE +^\[main\.assertion\.3\] assertion l->x==2: FAILURE +^\[main\.assertion\.4\] assertion l->x==1: OK diff --git a/regression/heap/list_iter4/main.c b/regression/heap/list_iter4/main.c new file mode 100644 index 000000000..8266624ac --- /dev/null +++ b/regression/heap/list_iter4/main.c @@ -0,0 +1,28 @@ +#include +#include + +struct list +{ + int x; + struct list *n; +}; + +void main() +{ + struct list *l; + struct list *nl1=malloc(sizeof(struct list)); + nl1->x=0; + nl1->n=l; + l=nl1; + struct list *nl2=malloc(sizeof(struct list)); + nl2->x=1; + nl2->n=l; + l=nl2; + + struct list *m=l; + for(int i=0; i<2; ++i) + { + assert(m->x==i); + assert(m->x==-1); + } +} diff --git a/regression/heap/list_iter4/test.desc b/regression/heap/list_iter4/test.desc new file mode 100644 index 000000000..39b8f9acd --- /dev/null +++ b/regression/heap/list_iter4/test.desc @@ -0,0 +1,8 @@ +KNOWNBUG +main.c +--incremental-bmc --independent-properties --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion m->x==i: OK +^\[main\.assertion\.2\] assertion m->x==-1: FAILURE diff --git a/regression/heap/list_true/main.c b/regression/heap/list_true/main.c new file mode 100644 index 000000000..f0647ca1f --- /dev/null +++ b/regression/heap/list_true/main.c @@ -0,0 +1,55 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s, then 2s and finally + * on 3 (arbitrary length); afterwards, go through it and check + * if the the list does have the correct form, and in particular + * finishes by a 3. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->2->....->2->3 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + List t; + List p = a; + while (__VERIFIER_nondet_int()) { + p->h = 1; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->n = NULL; + p->n = t; + p = p->n; + } + while (__VERIFIER_nondet_int()) { + p->h = 2; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->n = NULL; + p->n = t; + p = p->n; + } + p->h = 3; + + /* Check it */ + p = a; + while (p->h == 1) + p = p->n; + while (p->h == 2) + p = p->n; + if(p->h != 3) + ERROR: __VERIFIER_error(); + + return 0; +} diff --git a/regression/heap/list_true/test.desc b/regression/heap/list_true/test.desc new file mode 100644 index 000000000..e2ef350dc --- /dev/null +++ b/regression/heap/list_true/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--heap-interval --sympath --inline --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/list_unwind1/main.c b/regression/heap/list_unwind1/main.c new file mode 100644 index 000000000..5a27f15e4 --- /dev/null +++ b/regression/heap/list_unwind1/main.c @@ -0,0 +1,22 @@ +#include +#include + +struct list +{ + int x; + struct list *n; +}; + +void main() +{ + struct list *l; + for(int i=0; i<2; ++i) + { + struct list *nl=malloc(sizeof(struct list)); + nl->x=i; + nl->n=l; + l=nl; + } + assert(l->n->x==0); + assert(l->x==2); +} diff --git a/regression/heap/list_unwind1/test.desc b/regression/heap/list_unwind1/test.desc new file mode 100644 index 000000000..2569ab217 --- /dev/null +++ b/regression/heap/list_unwind1/test.desc @@ -0,0 +1,8 @@ +KNOWNBUG +main.c +--incremental-bmc --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion l->n->x==0: OK +^\[main\.assertion\.2\] assertion l->x==2: FAILURE diff --git a/regression/heap/list_unwind2/main.c b/regression/heap/list_unwind2/main.c new file mode 100644 index 000000000..3341fcdec --- /dev/null +++ b/regression/heap/list_unwind2/main.c @@ -0,0 +1,22 @@ +#include +#include + +struct list +{ + int x; + struct list *n; +}; + +void main() +{ + struct list *l; + for(int i=0; i<2; ++i) + { + struct list *nl=malloc(sizeof(struct list)); + nl->x=i; + nl->n=l; + l=nl; + } + assert(l->n->x==1); + assert(l->x==1); +} diff --git a/regression/heap/list_unwind2/test.desc b/regression/heap/list_unwind2/test.desc new file mode 100644 index 000000000..22c105e62 --- /dev/null +++ b/regression/heap/list_unwind2/test.desc @@ -0,0 +1,8 @@ +KNOWNBUG +main.c +--incremental-bmc --heap-interval +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main\.assertion\.1\] assertion l->n->x==1: FAILURE +^\[main\.assertion\.2\] assertion l->x==1: OK diff --git a/regression/heap/path-format.py b/regression/heap/path-format.py new file mode 100644 index 000000000..a18b0fe13 --- /dev/null +++ b/regression/heap/path-format.py @@ -0,0 +1,6 @@ +from lepl import Any, Delayed, Node, Space + +expr = Delayed() +expr += '{' / (Any() | expr[1:, Space()[:]]) / '}' > Node + +print expr.parse("{{a}{b}{{{c}}}}")[0] diff --git a/regression/heap/simple_false/main.c b/regression/heap/simple_false/main.c new file mode 100644 index 000000000..e3d4f0af8 --- /dev/null +++ b/regression/heap/simple_false/main.c @@ -0,0 +1,45 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s and finally a 0 (arbitrary length); + * afterwards, go through it and check if the list does have the correct form, and in particular + * finishes by a 0. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + List t; + List p = a; + a->h = 2; + while (__VERIFIER_nondet_int()) { + p->h = 1; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + p->h = 2; + p->n = 0; + p = a; + while (p!=0) { + if (p->h != 2) { + ERROR: __VERIFIER_error(); + } + p = p->n; + } + return 0; +} + diff --git a/regression/heap/simple_false/test.desc b/regression/heap/simple_false/test.desc new file mode 100644 index 000000000..450b08c19 --- /dev/null +++ b/regression/heap/simple_false/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--heap-interval --sympath --inline --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ +^\[main.assertion.1\] : UNKNOWN diff --git a/regression/heap/simple_false_kind/main.c b/regression/heap/simple_false_kind/main.c new file mode 100644 index 000000000..e3d4f0af8 --- /dev/null +++ b/regression/heap/simple_false_kind/main.c @@ -0,0 +1,45 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s and finally a 0 (arbitrary length); + * afterwards, go through it and check if the list does have the correct form, and in particular + * finishes by a 0. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + List t; + List p = a; + a->h = 2; + while (__VERIFIER_nondet_int()) { + p->h = 1; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + p->h = 2; + p->n = 0; + p = a; + while (p!=0) { + if (p->h != 2) { + ERROR: __VERIFIER_error(); + } + p = p->n; + } + return 0; +} + diff --git a/regression/heap/simple_false_kind/test.desc b/regression/heap/simple_false_kind/test.desc new file mode 100644 index 000000000..93984c1aa --- /dev/null +++ b/regression/heap/simple_false_kind/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--heap-interval --sympath --inline --no-propagation --k-induction +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +^\[main.assertion.1\] : FAILURE diff --git a/regression/heap/simple_true/main.c b/regression/heap/simple_true/main.c new file mode 100644 index 000000000..996941232 --- /dev/null +++ b/regression/heap/simple_true/main.c @@ -0,0 +1,44 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s and finally a 0 (arbitrary length); + * afterwards, go through it and check if the list does have the correct form, and in particular + * finishes by a 0. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + List t; + List p = a; + while (__VERIFIER_nondet_int()) { + p->h = 1; + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + p->n = t; + p = p->n; + } + p->h = 1; + p->n = 0; + p = a; + while (p!=0) { + if (p->h != 1) { + ERROR: __VERIFIER_error(); + } + p = p->n; + } + return 0; +} + diff --git a/regression/heap/simple_true/test.desc b/regression/heap/simple_true/test.desc new file mode 100644 index 000000000..405084afc --- /dev/null +++ b/regression/heap/simple_true/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--heap-interval --inline --no-propagation --sympath +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/sll_rhs_concretisation/main.c b/regression/heap/sll_rhs_concretisation/main.c new file mode 100644 index 000000000..180ee9d30 --- /dev/null +++ b/regression/heap/sll_rhs_concretisation/main.c @@ -0,0 +1,32 @@ +#include +#include + +extern int __VERIFIER_nondet_int(void); + +struct list{ + struct list* next; +}; + +int main() +{ + struct list *p,*q; + p = malloc(sizeof(struct list)); + q = malloc(sizeof(struct list)); + + p->next = malloc(sizeof(struct list)); + q->next = malloc(sizeof(struct list)); + p = q; + + while(__VERIFIER_nondet_int) { + assert(p->next != NULL); + q = p->next; + assert(q != NULL); + q->next = malloc(sizeof(struct list)); + q->next->next = q; + assert(q->next != NULL); + p = q; + assert(p == q); + } + + return 0; +} diff --git a/regression/heap/sll_rhs_concretisation/test.desc b/regression/heap/sll_rhs_concretisation/test.desc new file mode 100644 index 000000000..ed14b476a --- /dev/null +++ b/regression/heap/sll_rhs_concretisation/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap --inline --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/heap/sll_simple/main.c b/regression/heap/sll_simple/main.c new file mode 100644 index 000000000..56e995a4d --- /dev/null +++ b/regression/heap/sll_simple/main.c @@ -0,0 +1,48 @@ +#include + +struct item { + struct item *null; + struct item *next; + struct item *data; +}; + +struct item* alloc_or_die(void) +{ + struct item *pi = malloc(sizeof(*pi)); + if (!pi) + abort(); + + pi->null = NULL; + pi->data = malloc(sizeof(struct item)); +#if 1 + if (!pi->data) + abort(); +#endif + return pi; +} + +struct item* create_sll(void) +{ + struct item *sll = alloc_or_die(); + struct item *now = sll; + + // NOTE: running this on bare metal may cause the machine to swap a bit + do { + now->next = alloc_or_die(); + now->next->next = NULL; + now = now->next; + } while (__VERIFIER_nondet_int()); + + return sll; +} + +int main() +{ + struct item *sll = create_sll(); + + assert(sll); + assert(sll->next); + + return 0; +} + diff --git a/regression/heap/sll_simple/test.desc b/regression/heap/sll_simple/test.desc new file mode 100644 index 000000000..b13e412ea --- /dev/null +++ b/regression/heap/sll_simple/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap --inline --no-propagation --heap-interval +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/interprocedural/Makefile b/regression/interprocedural/Makefile index b5f2484a2..34cd734b9 100644 --- a/regression/interprocedural/Makefile +++ b/regression/interprocedural/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/interprocedural/abort1/main.c b/regression/interprocedural/abort1/main.c index 6d591ee39..84b6451b0 100644 --- a/regression/interprocedural/abort1/main.c +++ b/regression/interprocedural/abort1/main.c @@ -7,6 +7,6 @@ int foo(int x) void main() { int x; - x = foo(x); + x = foo(x); assert(x<=0); } diff --git a/regression/interprocedural/contextsensitive1/main.c b/regression/interprocedural/contextsensitive1/main.c index c1b928562..ecfa72170 100644 --- a/regression/interprocedural/contextsensitive1/main.c +++ b/regression/interprocedural/contextsensitive1/main.c @@ -1,17 +1,15 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x = 1; - int y = sign(x); - x = -x; - int z = sign(x); - assert(-1<=y && y<=1 && -1<=z && z<=1); -} - +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x = 1; + int y = sign(x); + x = -x; + int z = sign(x); + assert(-1<=y && y<=1 && -1<=z && z<=1); +} diff --git a/regression/interprocedural/contextsensitive2/main.c b/regression/interprocedural/contextsensitive2/main.c index b228a31b4..4a2ef0219 100644 --- a/regression/interprocedural/contextsensitive2/main.c +++ b/regression/interprocedural/contextsensitive2/main.c @@ -1,28 +1,25 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do1(x); + assert(y==1); + int z = do2(-x); + assert(-1<=z && z<=1); +} diff --git a/regression/interprocedural/contextsensitive3/main.c b/regression/interprocedural/contextsensitive3/main.c index deab63659..b4cd07854 100644 --- a/regression/interprocedural/contextsensitive3/main.c +++ b/regression/interprocedural/contextsensitive3/main.c @@ -1,28 +1,26 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do2(x); - assert(y==1); - x = -x; - int z = do1(x); - assert(-1<=z && z<=1); -} - +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do2(x); + assert(y==1); + x = -x; + int z = do1(x); + assert(-1<=z && z<=1); +} diff --git a/regression/interprocedural/contextsensitive4/main.c b/regression/interprocedural/contextsensitive4/main.c index 9f1348396..05af1ee45 100644 --- a/regression/interprocedural/contextsensitive4/main.c +++ b/regression/interprocedural/contextsensitive4/main.c @@ -1,29 +1,27 @@ - -int x = 1; - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - +int x = 1; + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} diff --git a/regression/interprocedural/contextsensitive5/main.c b/regression/interprocedural/contextsensitive5/main.c index fb0251140..a23d53997 100644 --- a/regression/interprocedural/contextsensitive5/main.c +++ b/regression/interprocedural/contextsensitive5/main.c @@ -1,15 +1,14 @@ -#include - -void foo(int* x) -{ - assert(x!=NULL); - *x = 0; -} - -void main() -{ - int x; - int *y = &x; - foo(y); -} - +#include + +void foo(int* x) +{ + assert(x!=NULL); + *x = 0; +} + +void main() +{ + int x; + int *y = &x; + foo(y); +} diff --git a/regression/interprocedural/contextsensitive5/test.desc b/regression/interprocedural/contextsensitive5/test.desc index 8f7a7b6f1..d30d23301 100644 --- a/regression/interprocedural/contextsensitive5/test.desc +++ b/regression/interprocedural/contextsensitive5/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --equalities --context-sensitive ^EXIT=0$ diff --git a/regression/interprocedural/contextsensitive6/main.c b/regression/interprocedural/contextsensitive6/main.c index be4631faa..b9267eb75 100644 --- a/regression/interprocedural/contextsensitive6/main.c +++ b/regression/interprocedural/contextsensitive6/main.c @@ -1,12 +1,10 @@ - -void foo(int x) -{ - assert(x!=0); -} - -void main() -{ - int x = 1; - foo(x); -} - +void foo(int x) +{ + assert(x!=0); +} + +void main() +{ + int x = 1; + foo(x); +} diff --git a/regression/interprocedural/equal1/main.c b/regression/interprocedural/equal1/main.c index e4434c38e..44bbd284d 100644 --- a/regression/interprocedural/equal1/main.c +++ b/regression/interprocedural/equal1/main.c @@ -1,5 +1,5 @@ -int foo(int x, int y) -{ +int foo(int x, int y) +{ return x; } diff --git a/regression/interprocedural/equal1/test.desc b/regression/interprocedural/equal1/test.desc index 278810d48..2912759b4 100644 --- a/regression/interprocedural/equal1/test.desc +++ b/regression/interprocedural/equal1/test.desc @@ -3,4 +3,4 @@ main.c --equalities ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/equal2/main.c b/regression/interprocedural/equal2/main.c index 5dffe2105..5280ce06f 100644 --- a/regression/interprocedural/equal2/main.c +++ b/regression/interprocedural/equal2/main.c @@ -1,5 +1,5 @@ -unsigned foo(unsigned x, unsigned y) -{ +unsigned foo(unsigned x, unsigned y) +{ __CPROVER_assume(x<10*y && y>10); return x/y; } diff --git a/regression/interprocedural/equal2/test.desc b/regression/interprocedural/equal2/test.desc index 278810d48..2912759b4 100644 --- a/regression/interprocedural/equal2/test.desc +++ b/regression/interprocedural/equal2/test.desc @@ -3,4 +3,4 @@ main.c --equalities ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/equal3/main.c b/regression/interprocedural/equal3/main.c index 6e0366b24..64deebaa1 100644 --- a/regression/interprocedural/equal3/main.c +++ b/regression/interprocedural/equal3/main.c @@ -1,5 +1,5 @@ -unsigned foo(unsigned x, unsigned y) -{ +unsigned foo(unsigned x, unsigned y) +{ if(x) return y+1; return y-1; } diff --git a/regression/interprocedural/equal3/test.desc b/regression/interprocedural/equal3/test.desc index 278810d48..2912759b4 100644 --- a/regression/interprocedural/equal3/test.desc +++ b/regression/interprocedural/equal3/test.desc @@ -3,4 +3,4 @@ main.c --equalities ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/equal4/main.c b/regression/interprocedural/equal4/main.c index 876f00c5c..62fc9aadd 100644 --- a/regression/interprocedural/equal4/main.c +++ b/regression/interprocedural/equal4/main.c @@ -3,10 +3,10 @@ int foo(int x, int y) while(x<10) { y++; - x++; + x++; y--; } - + return y; } diff --git a/regression/interprocedural/equal4/test.desc b/regression/interprocedural/equal4/test.desc index 278810d48..2912759b4 100644 --- a/regression/interprocedural/equal4/test.desc +++ b/regression/interprocedural/equal4/test.desc @@ -3,4 +3,4 @@ main.c --equalities ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/equal7/main.c b/regression/interprocedural/equal7/main.c index e44115507..305bb780d 100644 --- a/regression/interprocedural/equal7/main.c +++ b/regression/interprocedural/equal7/main.c @@ -1,5 +1,5 @@ -int foo(int x) -{ +int foo(int x) +{ __CPROVER_assume(x==0); return -x; } diff --git a/regression/interprocedural/equal7/test.desc b/regression/interprocedural/equal7/test.desc index b7d19f8c7..f94239d37 100644 --- a/regression/interprocedural/equal7/test.desc +++ b/regression/interprocedural/equal7/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/equality_through_array7/main.c b/regression/interprocedural/equality_through_array7/main.c index 2fc0c7e03..8432f2840 100644 --- a/regression/interprocedural/equality_through_array7/main.c +++ b/regression/interprocedural/equality_through_array7/main.c @@ -14,6 +14,6 @@ void main (void) { a[1] = 0; pass_through_array(); - + assert(a[0] == 0 && a[1] == 0); } diff --git a/regression/interprocedural/global1/main.c b/regression/interprocedural/global1/main.c index ecb897b62..1436e7631 100644 --- a/regression/interprocedural/global1/main.c +++ b/regression/interprocedural/global1/main.c @@ -1,21 +1,20 @@ -#include - -int g; - -int foo(int y) -{ - __CPROVER_assume(g=1); - assert(z==0); -} - +#include + +int g; + +int foo(int y) +{ + __CPROVER_assume(g=1); + assert(z==0); +} diff --git a/regression/interprocedural/global1/test.desc b/regression/interprocedural/global1/test.desc index a6987a69a..33f531990 100644 --- a/regression/interprocedural/global1/test.desc +++ b/regression/interprocedural/global1/test.desc @@ -3,4 +3,4 @@ main.c --zones ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/interprocedural/global2/main.c b/regression/interprocedural/global2/main.c index f653bce18..b41dd7bf4 100644 --- a/regression/interprocedural/global2/main.c +++ b/regression/interprocedural/global2/main.c @@ -1,22 +1,21 @@ -int g; - -void foo() -{ - g=10; -} - -int bar() -{ - return 20; -} - -void main() -{ - g = 1; - int x; - foo(); - x = bar(); - assert(g==10); - assert(x==20); -} - +int g; + +void foo() +{ + g=10; +} + +int bar() +{ + return 20; +} + +void main() +{ + g = 1; + int x; + foo(); + x = bar(); + assert(g==10); + assert(x==20); +} diff --git a/regression/interprocedural/global2/test.desc b/regression/interprocedural/global2/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/interprocedural/global2/test.desc +++ b/regression/interprocedural/global2/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/interprocedural/global3/main.c b/regression/interprocedural/global3/main.c index 965a7f015..347464275 100644 --- a/regression/interprocedural/global3/main.c +++ b/regression/interprocedural/global3/main.c @@ -1,8 +1,8 @@ int x; int z; -void foo() -{ +void foo() +{ for(x=0;x<10;x++); } diff --git a/regression/interprocedural/global3/test.desc b/regression/interprocedural/global3/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/interprocedural/global3/test.desc +++ b/regression/interprocedural/global3/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/interprocedural/global4/main.c b/regression/interprocedural/global4/main.c index 80e8b6e50..646b6a47b 100644 --- a/regression/interprocedural/global4/main.c +++ b/regression/interprocedural/global4/main.c @@ -1,6 +1,6 @@ int g = 1; -void modify_global() +void modify_global() { g = g; } @@ -11,12 +11,12 @@ void main() //g = g; - if(x) + if(x) { - modify_global(); + modify_global(); } - modify_global(); + modify_global(); assert(g==1); } diff --git a/regression/interprocedural/ite2/main.c b/regression/interprocedural/ite2/main.c index e6b24818f..fbb4eddee 100644 --- a/regression/interprocedural/ite2/main.c +++ b/regression/interprocedural/ite2/main.c @@ -1,11 +1,11 @@ -int foo(int x, int y) -{ +int foo(int x, int y) +{ int res = y; if(x) res = y+1; return res; } -int bar(int x) -{ +int bar(int x) +{ if(x) return 1; return 2; } @@ -13,7 +13,7 @@ int bar(int x) void main() { int x; - int y = 0; + int y = 0; int z = bar(x); int w = foo(z,y); diff --git a/regression/interprocedural/ite2/test.desc b/regression/interprocedural/ite2/test.desc index a6987a69a..33f531990 100644 --- a/regression/interprocedural/ite2/test.desc +++ b/regression/interprocedural/ite2/test.desc @@ -3,4 +3,4 @@ main.c --zones ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/interprocedural/ite3/main.c b/regression/interprocedural/ite3/main.c index 0bd206af3..624104931 100644 --- a/regression/interprocedural/ite3/main.c +++ b/regression/interprocedural/ite3/main.c @@ -1,14 +1,14 @@ -int foo(int x) -{ +int foo(int x) +{ int res = 0; if(x) res = 1; return res; } -int bar(int x) -{ +int bar(int x) +{ int res = 2; if(x) res = 1; - return res; + return res; } void main() diff --git a/regression/interprocedural/ite3/test.desc b/regression/interprocedural/ite3/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/interprocedural/ite3/test.desc +++ b/regression/interprocedural/ite3/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/interprocedural/ite4/main.c b/regression/interprocedural/ite4/main.c index fd030a9a5..cd12c0013 100644 --- a/regression/interprocedural/ite4/main.c +++ b/regression/interprocedural/ite4/main.c @@ -1,13 +1,13 @@ -int foo(int x) -{ +int foo(int x) +{ int res; if(x) return 0; else return 1; } -int bar(int x) -{ +int bar(int x) +{ int res; if(x) res = 0; else res = 1; diff --git a/regression/interprocedural/ite4/test.desc b/regression/interprocedural/ite4/test.desc index a6987a69a..33f531990 100644 --- a/regression/interprocedural/ite4/test.desc +++ b/regression/interprocedural/ite4/test.desc @@ -3,4 +3,4 @@ main.c --zones ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/interprocedural/partialinline1/test.desc b/regression/interprocedural/partialinline1/test.desc index 0eb76f70f..9b5e46ea4 100644 --- a/regression/interprocedural/partialinline1/test.desc +++ b/regression/interprocedural/partialinline1/test.desc @@ -3,5 +3,5 @@ main.c --inline-partial 7 ^EXIT=10$ ^SIGNAL=0$ -^** 1 of 2 failed$ +^\*\* 1 of 2 failed$ diff --git a/regression/interprocedural/pointer1/main.c b/regression/interprocedural/pointer1/main.c index 34c87bb31..da5aa119a 100644 --- a/regression/interprocedural/pointer1/main.c +++ b/regression/interprocedural/pointer1/main.c @@ -1,5 +1,5 @@ -void foo(int *x) -{ +void foo(int *x) +{ *x = 10; } diff --git a/regression/interprocedural/pointer1/test.desc b/regression/interprocedural/pointer1/test.desc index 754436e9d..505faef4e 100644 --- a/regression/interprocedural/pointer1/test.desc +++ b/regression/interprocedural/pointer1/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/pointer2/main.c b/regression/interprocedural/pointer2/main.c index 02f54b869..318e03ee5 100644 --- a/regression/interprocedural/pointer2/main.c +++ b/regression/interprocedural/pointer2/main.c @@ -1,5 +1,5 @@ -void foo(int *x) -{ +void foo(int *x) +{ x++; *x = 10; } diff --git a/regression/interprocedural/pointer2/test.desc b/regression/interprocedural/pointer2/test.desc index 754436e9d..505faef4e 100644 --- a/regression/interprocedural/pointer2/test.desc +++ b/regression/interprocedural/pointer2/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/simple1/main.c b/regression/interprocedural/simple1/main.c index 8a95214f7..0b9154bec 100644 --- a/regression/interprocedural/simple1/main.c +++ b/regression/interprocedural/simple1/main.c @@ -1,10 +1,10 @@ -int foo() -{ +int foo() +{ return 1; } -int bar() -{ - return 2; +int bar() +{ + return 2; } void main() diff --git a/regression/interprocedural/simple1/test.desc b/regression/interprocedural/simple1/test.desc index b7d19f8c7..f94239d37 100644 --- a/regression/interprocedural/simple1/test.desc +++ b/regression/interprocedural/simple1/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/simple2/main.c b/regression/interprocedural/simple2/main.c index e1f62b82d..984b32921 100644 --- a/regression/interprocedural/simple2/main.c +++ b/regression/interprocedural/simple2/main.c @@ -1,5 +1,5 @@ -int foo(int x) -{ +int foo(int x) +{ if(x) return 9; return 10; } diff --git a/regression/interprocedural/simple2/test.desc b/regression/interprocedural/simple2/test.desc index b7d19f8c7..f94239d37 100644 --- a/regression/interprocedural/simple2/test.desc +++ b/regression/interprocedural/simple2/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/interprocedural/sum1/main.c b/regression/interprocedural/sum1/main.c index df354feeb..c6f4ab383 100644 --- a/regression/interprocedural/sum1/main.c +++ b/regression/interprocedural/sum1/main.c @@ -1,27 +1,27 @@ -#include - -int max(int x, int y) -{ - if(x>y) return x; - return y; -} - -int inv(int x) -{ - __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes - return -x; -} - -void main() -{ - int x; - __CPROVER_assume(2<=x && x<=3); - - int y=inv(x); - int z=max(y,0); - - assert(y<=-2); - assert(y==-x); - assert(z>=0); - assert(z>=y); -} +#include + +int max(int x, int y) +{ + if(x>y) return x; + return y; +} + +int inv(int x) +{ + __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes + return -x; +} + +void main() +{ + int x; + __CPROVER_assume(2<=x && x<=3); + + int y=inv(x); + int z=max(y,0); + + assert(y<=-2); + assert(y==-x); + assert(z>=0); + assert(z>=y); +} diff --git a/regression/interprocedural/sum1/test.desc b/regression/interprocedural/sum1/test.desc index 618e68d44..413320cde 100644 --- a/regression/interprocedural/sum1/test.desc +++ b/regression/interprocedural/sum1/test.desc @@ -3,4 +3,4 @@ main.c --octagons ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 4 failed$ +^\*\* 0 of 4 failed$ diff --git a/regression/invariants/Makefile b/regression/invariants/Makefile index b5f2484a2..34cd734b9 100644 --- a/regression/invariants/Makefile +++ b/regression/invariants/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/invariants/array_safe5/main.c b/regression/invariants/array_safe5/main.c index 20cd3eb88..9e3671eb3 100644 --- a/regression/invariants/array_safe5/main.c +++ b/regression/invariants/array_safe5/main.c @@ -4,10 +4,10 @@ void main (void) { unsigned a[SIZE] = {0, }; //without initializtion a[i] for i>0 are not initialized in the 1st iteration, and hence we cannot do better than UINT_MAX for the upper bounds - for(unsigned i=0; i0) { diff --git a/regression/invariants/float2/main.c b/regression/invariants/float2/main.c index 235de6c53..a0c2c4a5d 100644 --- a/regression/invariants/float2/main.c +++ b/regression/invariants/float2/main.c @@ -1,6 +1,6 @@ void main() { - float x = 10.0; + float x = 10.0; while(x>0) { diff --git a/regression/invariants/float3/main.c b/regression/invariants/float3/main.c index 06b63a8f0..f1f7fdd07 100644 --- a/regression/invariants/float3/main.c +++ b/regression/invariants/float3/main.c @@ -1,6 +1,6 @@ void main() { - float x = 10.0; + float x = 10.0; while(x>0) // does not terminate { diff --git a/regression/invariants/float4/main.c b/regression/invariants/float4/main.c index a3edc4f6e..87b35329f 100644 --- a/regression/invariants/float4/main.c +++ b/regression/invariants/float4/main.c @@ -1,6 +1,6 @@ void main() { - float x = 10.0; + float x = 10.0; while(x>0.0) // does not terminate { diff --git a/regression/invariants/gotoloop1/main.c b/regression/invariants/gotoloop1/main.c index 57fde266c..06b649e04 100644 --- a/regression/invariants/gotoloop1/main.c +++ b/regression/invariants/gotoloop1/main.c @@ -2,7 +2,7 @@ void main() { int x,y; if(x<-5) goto LOOP; - + x = 0; while(x<10) { diff --git a/regression/invariants/gotoloop1/test.desc b/regression/invariants/gotoloop1/test.desc index daff41f71..dba2249f8 100644 --- a/regression/invariants/gotoloop1/test.desc +++ b/regression/invariants/gotoloop1/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c --unwind 1 ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ +-- +-- +feature deactivated diff --git a/regression/invariants/gotoloop2/main.c b/regression/invariants/gotoloop2/main.c index 7f9c3ea57..8a457218e 100644 --- a/regression/invariants/gotoloop2/main.c +++ b/regression/invariants/gotoloop2/main.c @@ -2,7 +2,7 @@ void main() { int x,y; if(x<-5) goto LOOP; - + x = 0; while(x<10) { diff --git a/regression/invariants/gotoloop2/test.desc b/regression/invariants/gotoloop2/test.desc index daff41f71..dba2249f8 100644 --- a/regression/invariants/gotoloop2/test.desc +++ b/regression/invariants/gotoloop2/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c --unwind 1 ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ +-- +-- +feature deactivated diff --git a/regression/invariants/ite1/main.c b/regression/invariants/ite1/main.c index d34a2cd40..f846c9438 100644 --- a/regression/invariants/ite1/main.c +++ b/regression/invariants/ite1/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x; - int y = 0; - - while(y==0) //need to distinguish first iteration - { - if(x>=5) y=x; - else y=5; - } - - assert(y>=5); -} +void main() +{ + int x; + int y = 0; + + while(y==0) //need to distinguish first iteration + { + if(x>=5) y=x; + else y=5; + } + + assert(y>=5); +} diff --git a/regression/invariants/loop1/main.c b/regression/invariants/loop1/main.c index f35f1c6ae..e67fdcb0f 100644 --- a/regression/invariants/loop1/main.c +++ b/regression/invariants/loop1/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; while(x<10) { diff --git a/regression/invariants/loop10/main.c b/regression/invariants/loop10/main.c index 72ee9fdf3..4c44c5306 100644 --- a/regression/invariants/loop10/main.c +++ b/regression/invariants/loop10/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; int y; while(y) diff --git a/regression/invariants/loop11/main.c b/regression/invariants/loop11/main.c index 47a26ae58..3a6b50399 100644 --- a/regression/invariants/loop11/main.c +++ b/regression/invariants/loop11/main.c @@ -2,7 +2,7 @@ void main() { int n; __CPROVER_assume(n==0); - int x = 0; + int x = 0; while(x=0); do diff --git a/regression/invariants/loop14/main.c b/regression/invariants/loop14/main.c index bb35394d4..be0199467 100644 --- a/regression/invariants/loop14/main.c +++ b/regression/invariants/loop14/main.c @@ -1,6 +1,6 @@ void main() { - int x,y,z; + int x,y,z; __CPROVER_assume(x>=0); __CPROVER_assume(-20<=z && z<=-1); diff --git a/regression/invariants/loop15/main.c b/regression/invariants/loop15/main.c index 03b3a0c55..7e336dc14 100644 --- a/regression/invariants/loop15/main.c +++ b/regression/invariants/loop15/main.c @@ -1,6 +1,6 @@ void main() { - int x,y,z; + int x,y,z; __CPROVER_assume(x>=0); __CPROVER_assume(x==y); __CPROVER_assume(-1<=z && z<=-1); diff --git a/regression/invariants/loop16/main.c b/regression/invariants/loop16/main.c index 93a2dec12..771672940 100644 --- a/regression/invariants/loop16/main.c +++ b/regression/invariants/loop16/main.c @@ -1,7 +1,7 @@ void main() { - int x = 0; + int x = 0; int y = 0; do @@ -10,7 +10,7 @@ void main() } while(x<10); - do + do { ++y; } diff --git a/regression/invariants/loop16/test.desc b/regression/invariants/loop16/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/invariants/loop16/test.desc +++ b/regression/invariants/loop16/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/invariants/loop17/main.c b/regression/invariants/loop17/main.c index 71d68b591..db66138b0 100644 --- a/regression/invariants/loop17/main.c +++ b/regression/invariants/loop17/main.c @@ -1,6 +1,6 @@ void main() { - int x; + int x; __CPROVER_assume(x>=0); do diff --git a/regression/invariants/loop18/main.c b/regression/invariants/loop18/main.c index c60b7b07d..22575a138 100644 --- a/regression/invariants/loop18/main.c +++ b/regression/invariants/loop18/main.c @@ -1,6 +1,6 @@ void main() { - int x,y,z; + int x,y,z; __CPROVER_assume(x>=0); __CPROVER_assume(x==y); __CPROVER_assume(-20<=z && z<=-1); diff --git a/regression/invariants/loop2/main.c b/regression/invariants/loop2/main.c index e67632a94..dd5d9c810 100644 --- a/regression/invariants/loop2/main.c +++ b/regression/invariants/loop2/main.c @@ -1,7 +1,7 @@ void main() { - int x = 0; + int x = 0; int y = 0; while(x<10) diff --git a/regression/invariants/loop2/test.desc b/regression/invariants/loop2/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/invariants/loop2/test.desc +++ b/regression/invariants/loop2/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/invariants/loop21/main.c b/regression/invariants/loop21/main.c index 1a80bc24f..85f265c5d 100644 --- a/regression/invariants/loop21/main.c +++ b/regression/invariants/loop21/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0, y = 100; + int x = 0, y = 100; while(x<100 && y>0) { diff --git a/regression/invariants/loop22/main.c b/regression/invariants/loop22/main.c index 3346995c0..7a13c955d 100644 --- a/regression/invariants/loop22/main.c +++ b/regression/invariants/loop22/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0, y = 100; + int x = 0, y = 100; while(x<100 && y>0) { diff --git a/regression/invariants/loop3/main.c b/regression/invariants/loop3/main.c index 21a30e139..6ddb8924c 100644 --- a/regression/invariants/loop3/main.c +++ b/regression/invariants/loop3/main.c @@ -4,13 +4,13 @@ void main() int y; x=0; y=0; - + while(x<10 && y<20) { ++x; ++y; } - + int z=x+y; assert(z>=0); diff --git a/regression/invariants/loop3/test.desc b/regression/invariants/loop3/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/invariants/loop3/test.desc +++ b/regression/invariants/loop3/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/invariants/loop4/test.desc b/regression/invariants/loop4/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/invariants/loop4/test.desc +++ b/regression/invariants/loop4/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/invariants/loop5/main.c b/regression/invariants/loop5/main.c index f301dde9d..2fd0a019f 100644 --- a/regression/invariants/loop5/main.c +++ b/regression/invariants/loop5/main.c @@ -1,6 +1,6 @@ void main() { - unsigned x = 0; + unsigned x = 0; while(x<10) ++x; diff --git a/regression/invariants/loop5/test.desc b/regression/invariants/loop5/test.desc index b7d19f8c7..f94239d37 100644 --- a/regression/invariants/loop5/test.desc +++ b/regression/invariants/loop5/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/invariants/loop6/main.c b/regression/invariants/loop6/main.c index 2c4213934..c7bd862fe 100644 --- a/regression/invariants/loop6/main.c +++ b/regression/invariants/loop6/main.c @@ -4,13 +4,13 @@ void main() { int x = 0; unsigned y = 0; - + while(x<10 && y<20) { ++x; ++y; } - + int z=x+y; assert(z<=20); } diff --git a/regression/invariants/loop6/test.desc b/regression/invariants/loop6/test.desc index d6f6483b8..1d9f49346 100644 --- a/regression/invariants/loop6/test.desc +++ b/regression/invariants/loop6/test.desc @@ -3,4 +3,4 @@ main.c --octagons ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/invariants/loop7/main.c b/regression/invariants/loop7/main.c index 3c7dea455..1707acb1d 100644 --- a/regression/invariants/loop7/main.c +++ b/regression/invariants/loop7/main.c @@ -1,6 +1,6 @@ void main() { - int x; + int x; unsigned y; x = -10; y = 10; diff --git a/regression/invariants/loop7/test.desc b/regression/invariants/loop7/test.desc index c268c1153..8fcbb550d 100644 --- a/regression/invariants/loop7/test.desc +++ b/regression/invariants/loop7/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 4 failed$ +^\*\* 0 of 4 failed$ diff --git a/regression/invariants/loop8/test.desc b/regression/invariants/loop8/test.desc index 1ec61b8c3..90e74250c 100644 --- a/regression/invariants/loop8/test.desc +++ b/regression/invariants/loop8/test.desc @@ -3,4 +3,4 @@ main.c ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 2 failed$ +^\*\* 0 of 2 failed$ diff --git a/regression/invariants/nested4/main.c b/regression/invariants/nested4/main.c index 35b9f5b5a..799dc282f 100644 --- a/regression/invariants/nested4/main.c +++ b/regression/invariants/nested4/main.c @@ -5,10 +5,10 @@ void main() int n; __CPROVER_assume(0<=n && n<=N); int cost0 = 0; -for(int i=0;i=0); int y = 0; do - { + { if(z) y = y + x; else break; x -= 3; diff --git a/regression/invariants/unwind15/main.c b/regression/invariants/unwind15/main.c index fb6137ec8..022ea24fa 100644 --- a/regression/invariants/unwind15/main.c +++ b/regression/invariants/unwind15/main.c @@ -2,7 +2,7 @@ extern int nondet(); void main() { - int x; + int x; __CPROVER_assume(x>=0); int y = 0; diff --git a/regression/invariants/unwind16/main.c b/regression/invariants/unwind16/main.c index 41f3f2dbf..5d6334051 100644 --- a/regression/invariants/unwind16/main.c +++ b/regression/invariants/unwind16/main.c @@ -1,11 +1,11 @@ void main() { - int x, z; + int x, z; __CPROVER_assume(x>=0); int y = 0; while(x>0) - { + { if(z) y = y + x; else break; x -= 3; diff --git a/regression/invariants/unwind17/main.c b/regression/invariants/unwind17/main.c index fee7987dc..b2a0505e4 100644 --- a/regression/invariants/unwind17/main.c +++ b/regression/invariants/unwind17/main.c @@ -1,16 +1,16 @@ void main() { - int x, z, w; + int x, z, w; __CPROVER_assume(x>=0); int y = 0; while(x>0) - { + { x = x; if(z) y = y + x; else break; x--; - if(w) break; + if(w) break; else x++; x -= 3; } diff --git a/regression/invariants/unwind18/main.c b/regression/invariants/unwind18/main.c index b67d02ce0..44ff548cf 100644 --- a/regression/invariants/unwind18/main.c +++ b/regression/invariants/unwind18/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; int y; while(x<10) diff --git a/regression/invariants/unwind19/main.c b/regression/invariants/unwind19/main.c index 635faa760..8fa6b264b 100644 --- a/regression/invariants/unwind19/main.c +++ b/regression/invariants/unwind19/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; int y; do diff --git a/regression/invariants/unwind2/main.c b/regression/invariants/unwind2/main.c index f8b4ebbd8..703691405 100644 --- a/regression/invariants/unwind2/main.c +++ b/regression/invariants/unwind2/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; if(x>=5) { diff --git a/regression/invariants/unwind2/test.desc b/regression/invariants/unwind2/test.desc index eb75b46d7..4219263bb 100644 --- a/regression/invariants/unwind2/test.desc +++ b/regression/invariants/unwind2/test.desc @@ -3,4 +3,4 @@ main.c --unwind 5 ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/invariants/unwind20/test.desc b/regression/invariants/unwind20/test.desc index 744e6bbe7..f3b7f654b 100644 --- a/regression/invariants/unwind20/test.desc +++ b/regression/invariants/unwind20/test.desc @@ -3,4 +3,4 @@ main.c --unwind 10 ^EXIT=10$ ^SIGNAL=0$ -^** 2 of 2 failed$ +^\*\* 2 of 2 failed$ diff --git a/regression/invariants/unwind3/main.c b/regression/invariants/unwind3/main.c index 9eb900bc9..a13c56059 100644 --- a/regression/invariants/unwind3/main.c +++ b/regression/invariants/unwind3/main.c @@ -1,6 +1,6 @@ void main() { - int x; + int x; __CPROVER_assume(5<=x && x<=100); for(int y=0;y<5;y++) x++; diff --git a/regression/invariants/unwind3/test.desc b/regression/invariants/unwind3/test.desc index eb75b46d7..4219263bb 100644 --- a/regression/invariants/unwind3/test.desc +++ b/regression/invariants/unwind3/test.desc @@ -3,4 +3,4 @@ main.c --unwind 5 ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/invariants/unwind4/main.c b/regression/invariants/unwind4/main.c index 1e6dc4576..d395ee75e 100644 --- a/regression/invariants/unwind4/main.c +++ b/regression/invariants/unwind4/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; while(x<10) ++x; diff --git a/regression/invariants/unwind4/test.desc b/regression/invariants/unwind4/test.desc index a73bcb48a..57d1fa962 100644 --- a/regression/invariants/unwind4/test.desc +++ b/regression/invariants/unwind4/test.desc @@ -3,4 +3,4 @@ main.c --unwind 20 ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/invariants/unwind5/main.c b/regression/invariants/unwind5/main.c index 6fdd141b1..5bcf04154 100644 --- a/regression/invariants/unwind5/main.c +++ b/regression/invariants/unwind5/main.c @@ -1,7 +1,7 @@ void main() { - int x = 0; - int y = 0; + int x = 0; + int y = 0; while(x<10) { diff --git a/regression/invariants/unwind5/test.desc b/regression/invariants/unwind5/test.desc index 0abefceba..d9beae8e6 100644 --- a/regression/invariants/unwind5/test.desc +++ b/regression/invariants/unwind5/test.desc @@ -3,4 +3,4 @@ main.c --unwind 10 ^EXIT=0$ ^SIGNAL=0$ -^** 0 of 1 failed$ +^\*\* 0 of 1 failed$ diff --git a/regression/invariants/unwind6/main.c b/regression/invariants/unwind6/main.c index 5742ddea5..ea0047ea3 100644 --- a/regression/invariants/unwind6/main.c +++ b/regression/invariants/unwind6/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; while(x<10) { diff --git a/regression/invariants/unwind9/main.c b/regression/invariants/unwind9/main.c index b8a4fea79..6c9b96fa1 100644 --- a/regression/invariants/unwind9/main.c +++ b/regression/invariants/unwind9/main.c @@ -10,7 +10,7 @@ void main() y++; } while(y=x); + assert(y>=x); x++; } assert(x==10); diff --git a/regression/kiki/Makefile b/regression/kiki/Makefile index b5f2484a2..34cd734b9 100644 --- a/regression/kiki/Makefile +++ b/regression/kiki/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/kiki/array1/main.c b/regression/kiki/array1/main.c index 056e454f9..76764b015 100644 --- a/regression/kiki/array1/main.c +++ b/regression/kiki/array1/main.c @@ -8,7 +8,7 @@ int main(int argc, char** argv) { int y = 2; int x; - if(1 == x) + if(1 == x) s[i] = x; else y = x; } diff --git a/regression/kiki/byte_add/main.c b/regression/kiki/byte_add/main.c index b7010d14d..b4cd4b941 100644 --- a/regression/kiki/byte_add/main.c +++ b/regression/kiki/byte_add/main.c @@ -15,7 +15,7 @@ unsigned int mp_add(unsigned int a, unsigned int b) i = i; r0 = r0; r1 = r1; } */ - + while (i < (unsigned char)2) { if (i == (unsigned char)0) { r0 = (unsigned char)0; } if (i == (unsigned char)1) { r1 = (unsigned char)0; } diff --git a/regression/kiki/elevator_13_21/main.c b/regression/kiki/elevator_13_21/main.c index 48217ef19..7b50321f6 100644 --- a/regression/kiki/elevator_13_21/main.c +++ b/regression/kiki/elevator_13_21/main.c @@ -38,7 +38,7 @@ void main() goto _L___6; } } else { - _L___6: + _L___6: if (i___0 == 1) { if (floorButtons_1) { retValue_acc = 1; @@ -47,7 +47,7 @@ void main() goto _L___5; } } else { - _L___5: + _L___5: if (i___0 == 2) { if (floorButtons_2) { retValue_acc = 1; @@ -56,7 +56,7 @@ void main() goto _L___4; } } else { - _L___4: + _L___4: if (i___0 == 3) { if (floorButtons_3) { retValue_acc = 1; @@ -65,7 +65,7 @@ void main() goto _L___3; } } else { - _L___3: + _L___3: if (i___0 == 4) { if (floorButtons_4) { retValue_acc = 1; diff --git a/regression/kiki/encoder1/main.c b/regression/kiki/encoder1/main.c index 4223e9cd8..e1695894e 100644 --- a/regression/kiki/encoder1/main.c +++ b/regression/kiki/encoder1/main.c @@ -8,33 +8,33 @@ int main () { int outputBytes = 0; int encodedBytes = 0; - int last = EOF; + int last = EOF; unsigned char count = 1; int current; - do + do { current = getchar(); inputBytes += (current == EOF) ? 0 : 1; - if ((current == last) && (count < 5 /*UCHAR_MAX - 1*/)) + if ((current == last) && (count < 5 /*UCHAR_MAX - 1*/)) { ++count; - } - else + } + else { - if (last != EOF) + if (last != EOF) { - if (count > 1) + if (count > 1) { putchar(UCHAR_MAX); ++outputBytes; putchar(count); ++outputBytes; } - if (last == UCHAR_MAX) + if (last == UCHAR_MAX) { putchar(UCHAR_MAX); ++outputBytes; } - putchar(last); + putchar(last); ++outputBytes; encodedBytes += count; } @@ -42,7 +42,7 @@ int main () { last = current; count = 1; } - } + } while (last != EOF); assert(inputBytes != encodedBytes); //should fail diff --git a/regression/kiki/induction1/main.c b/regression/kiki/induction1/main.c index 23a7e0054..0ecf6a0c3 100644 --- a/regression/kiki/induction1/main.c +++ b/regression/kiki/induction1/main.c @@ -1,6 +1,6 @@ void main() { - int x = 1; + int x = 1; while(1) { diff --git a/regression/kiki/induction2/main.c b/regression/kiki/induction2/main.c index 76f88d6eb..5114dc168 100644 --- a/regression/kiki/induction2/main.c +++ b/regression/kiki/induction2/main.c @@ -1,6 +1,6 @@ void main() { - int x = 1, y = -1, z = 1; + int x = 1, y = -1, z = 1; while(1) { diff --git a/regression/kiki/induction3/main.c b/regression/kiki/induction3/main.c index 38ef4b24f..aaa525389 100644 --- a/regression/kiki/induction3/main.c +++ b/regression/kiki/induction3/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0, y = 0, z = 0; + int x = 0, y = 0, z = 0; while(1) { diff --git a/regression/kiki/induction4/main.c b/regression/kiki/induction4/main.c index c55adf781..7a7096e3c 100644 --- a/regression/kiki/induction4/main.c +++ b/regression/kiki/induction4/main.c @@ -1,16 +1,16 @@ -#define a 2 - -extern int nondet_int(); - -int main() { - int i=0, n=3; - - int sn0 = nondet_int(); - int sn = sn0; - - while(i=10) { - x = y = z = 0; - } -// if(x>z+2) break; -// assert(x<=z+2); - } - assert(0); //this works with assertion hoisting -} +int main() { + int x,y,z; + __CPROVER_assume(x==y && y==z && -10<=x && x<0); + +// while(1) + while(x<=z+2) + { +// __CPROVER_assume(x<=z+2); + z = -y; + y = -x; + if(nondet()) x = x+1; + if(x>=10) { + x = y = z = 0; + } +// if(x>z+2) break; +// assert(x<=z+2); + } + assert(0); //this works with assertion hoisting +} diff --git a/regression/kiki/loop12/main.c b/regression/kiki/loop12/main.c index 171881bda..43464f4fb 100644 --- a/regression/kiki/loop12/main.c +++ b/regression/kiki/loop12/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; int y; while(y) diff --git a/regression/kiki/loop23/main.c b/regression/kiki/loop23/main.c index 274fe8975..944133675 100644 --- a/regression/kiki/loop23/main.c +++ b/regression/kiki/loop23/main.c @@ -5,10 +5,10 @@ int main() int y; if(x) { return x;} - for(x=0;x<2;x++) + for(x=0;x<2;x++) { if(x==y) { return x;} - + } assert(0); return 0; diff --git a/regression/kiki/loop24/main.c b/regression/kiki/loop24/main.c index 8833d199d..9266d50b7 100644 --- a/regression/kiki/loop24/main.c +++ b/regression/kiki/loop24/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x = 1, y = -1, z = 1; - - while(1) - { - if(x!=z) break; - z = y; - y = x; - x = -x; - } - assert(0); -} +void main() +{ + int x = 1, y = -1, z = 1; + + while(1) + { + if(x!=z) break; + z = y; + y = x; + x = -x; + } + assert(0); +} diff --git a/regression/kiki/loop25/main.c b/regression/kiki/loop25/main.c index 8febd85d7..e05732e45 100644 --- a/regression/kiki/loop25/main.c +++ b/regression/kiki/loop25/main.c @@ -1,12 +1,12 @@ -void main() -{ - int x = 1, y = -1, z = 1; - - while(x==z) - { - z = y; - y = x; - x = -x; - } - assert(0); -} +void main() +{ + int x = 1, y = -1, z = 1; + + while(x==z) + { + z = y; + y = x; + x = -x; + } + assert(0); +} diff --git a/regression/kiki/loop25/test.desc b/regression/kiki/loop25/test.desc index 6755e58a3..34e465e33 100644 --- a/regression/kiki/loop25/test.desc +++ b/regression/kiki/loop25/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--k-induction ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki/loop26/main.c b/regression/kiki/loop26/main.c index ae5c34f7d..f9f6ed3fd 100644 --- a/regression/kiki/loop26/main.c +++ b/regression/kiki/loop26/main.c @@ -1,15 +1,15 @@ -void main() -{ - int x = 1, y = -1, z = 1; - - while(1) - { - z = y; - y = x; - x = -x; - if(x!=z) break; - } - assert(0); -} +void main() +{ + int x = 1, y = -1, z = 1; + + while(1) + { + z = y; + y = x; + x = -x; + if(x!=z) break; + } + assert(0); +} diff --git a/regression/kiki/loop27/main.c b/regression/kiki/loop27/main.c index ca9acda4c..3c5aa9703 100644 --- a/regression/kiki/loop27/main.c +++ b/regression/kiki/loop27/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x = 1, y = -1, z = 1; - - do - { - z = y; - y = x; - x = -x; - } - while(x==z); - assert(0); -} +void main() +{ + int x = 1, y = -1, z = 1; + + do + { + z = y; + y = x; + x = -x; + } + while(x==z); + assert(0); +} diff --git a/regression/kiki/loop28/main.c b/regression/kiki/loop28/main.c index 7ae8d5301..bd0509336 100644 --- a/regression/kiki/loop28/main.c +++ b/regression/kiki/loop28/main.c @@ -1,8 +1,8 @@ -void main() { - int b = 3; - unsigned int j=0; - while (j<1 && b!=3) { - j++; - } - assert(j<1); -} +void main() { + int b = 3; + unsigned int j=0; + while (j<1 && b!=3) { + j++; + } + assert(j<1); +} diff --git a/regression/kiki/loop29/main.c b/regression/kiki/loop29/main.c index 0135d5b6d..bbde2d56a 100644 --- a/regression/kiki/loop29/main.c +++ b/regression/kiki/loop29/main.c @@ -5,6 +5,6 @@ void main() { int y; __CPROVER_assume(-3<=y && y<=-1); x += y; - } - assert(x==0 || x==-2); -} + } + assert(x==0 || x==-2); +} diff --git a/regression/kiki/loop30/main.c b/regression/kiki/loop30/main.c index c0ae436c1..e77a78ef8 100644 --- a/regression/kiki/loop30/main.c +++ b/regression/kiki/loop30/main.c @@ -4,6 +4,6 @@ void main() { { x += 1; assert(x<5); - } - while(x<10); -} + } + while(x<10); +} diff --git a/regression/kiki/loop9/main.c b/regression/kiki/loop9/main.c index 13cf22ccd..763566845 100644 --- a/regression/kiki/loop9/main.c +++ b/regression/kiki/loop9/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; int y; while(y) diff --git a/regression/kiki/malloc1/main.c b/regression/kiki/malloc1/main.c index 4f3bca21f..2b5cb01c3 100644 --- a/regression/kiki/malloc1/main.c +++ b/regression/kiki/malloc1/main.c @@ -1035,7 +1035,7 @@ int ssl3_connect(SSL *s ) { blastFlag = 0; - s->state = 12292; + s->state = 12292; while (1) { if (s->state == 12292) { goto switch_1_12292; @@ -1053,15 +1053,15 @@ int ssl3_connect(SSL *s ) switch_1_4368: ; if (blastFlag == 0) { blastFlag = 1; - } + } s->state = 4384; goto switch_1_break; switch_1_4384: ; if (blastFlag == 1) { blastFlag = 2; goto ERROR; - } - + } + goto end; switch_1_default: goto end; diff --git a/regression/kiki/malloc2/test.desc b/regression/kiki/malloc2/test.desc index f1e0e7a98..f3b3afa29 100644 --- a/regression/kiki/malloc2/test.desc +++ b/regression/kiki/malloc2/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c --inline --havoc --unwind 5 ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ +-- +-- +Needs fix for 6108ad3 diff --git a/regression/kiki/malloc3/main.c b/regression/kiki/malloc3/main.c index 2c823a1ec..33bc7c05d 100644 --- a/regression/kiki/malloc3/main.c +++ b/regression/kiki/malloc3/main.c @@ -13,7 +13,7 @@ void main() w = x; }*/ float *f; - + for(int z=0;z<1;z++) { //assert(w==5); diff --git a/regression/kiki/nested11/main.c b/regression/kiki/nested11/main.c index 790b6c4b3..d6f5d20a3 100644 --- a/regression/kiki/nested11/main.c +++ b/regression/kiki/nested11/main.c @@ -3,7 +3,7 @@ void main() int x,y; for(x=0;x<10;) { - for(y=0;y + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List t; + List p = 0; + while (__VERIFIER_nondet_int()) { + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->h = 1; + t->n = p; + p = t; + } + while (p!=0) { + t = p->n; + free(p); + p = t; + } +} + diff --git a/regression/memsafety/built_from_end/test.desc b/regression/memsafety/built_from_end/test.desc new file mode 100644 index 000000000..02a269a04 --- /dev/null +++ b/regression/memsafety/built_from_end/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-interval --inline --sympath --pointer-check --no-assertions +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/memsafety/built_from_end_false/main.c b/regression/memsafety/built_from_end_false/main.c new file mode 100644 index 000000000..153ccea0e --- /dev/null +++ b/regression/memsafety/built_from_end_false/main.c @@ -0,0 +1,37 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); +/* + * Simple example: build a list with only 1s and finally a 0 (arbitrary length); + * afterwards, go through it and check if the list does have the correct form, and in particular + * finishes by a 0. + */ +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List t; + List p = 0; + while (__VERIFIER_nondet_int()) { + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->h = 1; + t->n = p ? p : t; + p = t; + } + while (p!=0) { + t = p->n; + free(p); + p = t; + } +} + diff --git a/regression/memsafety/built_from_end_false/test.desc b/regression/memsafety/built_from_end_false/test.desc new file mode 100644 index 000000000..f93d3925c --- /dev/null +++ b/regression/memsafety/built_from_end_false/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--heap-interval --sympath --inline --pointer-check --no-assertions --k-induction +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +\[main.pointer_dereference.11\] dereference failure: deallocated dynamic object in \*p: FAILURE diff --git a/regression/memsafety/simple_false/main.c b/regression/memsafety/simple_false/main.c new file mode 100644 index 000000000..ee661872a --- /dev/null +++ b/regression/memsafety/simple_false/main.c @@ -0,0 +1,39 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); + +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + a->n = 0; + List t; + List p = a; + while (__VERIFIER_nondet_int()) { + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->n = 0; + p->n = t; + p = p->n; + } + p->n = a; + p = a; + while (p!=0) { + t = p->n; + free(p); + p = t; + } + return 0; +} + diff --git a/regression/memsafety/simple_false/test.desc b/regression/memsafety/simple_false/test.desc new file mode 100644 index 000000000..0812ec27e --- /dev/null +++ b/regression/memsafety/simple_false/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--heap-interval --sympath --inline --pointer-check --k-induction +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +\[main.pointer_dereference.23\] dereference failure: deallocated dynamic object in \*p: FAILURE diff --git a/regression/memsafety/simple_true/main.c b/regression/memsafety/simple_true/main.c new file mode 100644 index 000000000..79d7b5e74 --- /dev/null +++ b/regression/memsafety/simple_true/main.c @@ -0,0 +1,38 @@ +extern void __VERIFIER_error() __attribute__ ((__noreturn__)); + +extern int __VERIFIER_nondet_int(); + +#include + +void exit(int s) { + _EXIT: goto _EXIT; +} + +typedef struct node { + int h; + struct node *n; +} *List; + +int main() { + /* Build a list of the form 1->...->1->0 */ + List a = (List) malloc(sizeof(struct node)); + if (a == 0) exit(1); + a->n = 0; + List t; + List p = a; + while (__VERIFIER_nondet_int()) { + t = (List) malloc(sizeof(struct node)); + if (t == 0) exit(1); + t->n = 0; + p->n = t; + p = p->n; + } + p = a; + while (p!=0) { + t = p->n; + free(p); + p = t; + } + return 0; +} + diff --git a/regression/memsafety/simple_true/test.desc b/regression/memsafety/simple_true/test.desc new file mode 100644 index 000000000..02a269a04 --- /dev/null +++ b/regression/memsafety/simple_true/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--heap-interval --inline --sympath --pointer-check --no-assertions +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/nontermination/Makefile b/regression/nontermination/Makefile new file mode 100644 index 000000000..5911eab86 --- /dev/null +++ b/regression/nontermination/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --nontermination + +test: + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/nontermination/abort4/main.c b/regression/nontermination/abort4/main.c new file mode 100644 index 000000000..6872899e8 --- /dev/null +++ b/regression/nontermination/abort4/main.c @@ -0,0 +1,15 @@ +void foo() +{ + while(1); +} + +void bar() +{ +} + +int main(int argc, char** argv) +{ + foo(); + bar(); + return 0; +} diff --git a/regression/nontermination/abort4/test.desc b/regression/nontermination/abort4/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/abort4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/abort5/main.c b/regression/nontermination/abort5/main.c new file mode 100644 index 000000000..25f0cf975 --- /dev/null +++ b/regression/nontermination/abort5/main.c @@ -0,0 +1,15 @@ +void foo() +{ + while(1); +} + +void bar() +{ +} + +int main(int argc, char** argv) +{ + if(argc>=0) foo(); + bar(); + return 0; //status should be NO +} diff --git a/regression/nontermination/abort5/test.desc b/regression/nontermination/abort5/test.desc new file mode 100644 index 000000000..2d9665fb2 --- /dev/null +++ b/regression/nontermination/abort5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--context-sensitive +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/abort6/main.c b/regression/nontermination/abort6/main.c new file mode 100644 index 000000000..fba8054e7 --- /dev/null +++ b/regression/nontermination/abort6/main.c @@ -0,0 +1,15 @@ +void foo() +{ + while(1); +} + +void bar() +{ +} + +int main(int argc, char** argv) +{ + if(argc>=5) foo(); + bar(); + return 0; //status should be UNKNOWN +} diff --git a/regression/nontermination/abort6/test.desc b/regression/nontermination/abort6/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/abort6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/break_loop1/main.c b/regression/nontermination/break_loop1/main.c new file mode 100644 index 000000000..cfb20065d --- /dev/null +++ b/regression/nontermination/break_loop1/main.c @@ -0,0 +1,11 @@ +int main (void) +{ + int k = 0; + + while (k < 10) + { + if (k == 0) break; + } + + return 0; +} diff --git a/regression/nontermination/break_loop1/test.desc b/regression/nontermination/break_loop1/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/break_loop1/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/break_loop2/main.c b/regression/nontermination/break_loop2/main.c new file mode 100644 index 000000000..bf2fbb1da --- /dev/null +++ b/regression/nontermination/break_loop2/main.c @@ -0,0 +1,11 @@ +int main (void) +{ + int k = 0; + + while (k < 10) + { + if (k == 1) break; + } + + return 0; +} diff --git a/regression/nontermination/break_loop2/test.desc b/regression/nontermination/break_loop2/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/break_loop2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/break_loop3/main.c b/regression/nontermination/break_loop3/main.c new file mode 100644 index 000000000..9f51a8177 --- /dev/null +++ b/regression/nontermination/break_loop3/main.c @@ -0,0 +1,105 @@ +#include +int main(void) { + int i___0; + int d = 0; + int currentFloorID = 3; + int floorButtons_0; + int floorButtons_1; + int floorButtons_2; + int floorButtons_3; + int floorButtons_4 = 0; + int retValue_acc; + if (d == 0) { + i___0 = 0; + i___0 = currentFloorID - 1; + { + while (1) { + currentFloorID=currentFloorID; + floorButtons_4=floorButtons_4; + while_6_continue: /* CIL Label */ ; + if (i___0 >= 0) { + + } else { + goto while_6_break; + } + i___0 = currentFloorID + 1; + { + while (1) { + while_7_continue: /* CIL Label */ ; + if (i___0 < 5) { + + } else { + goto while_7_break; + } + if (i___0 == 0) { + if (floorButtons_0) { + retValue_acc = 1; + return (retValue_acc); + } else { + goto _L___6; + } + } else { + _L___6: /* CIL Label */ + if (i___0 == 1) { + if (floorButtons_1) { + retValue_acc = 1; + return (retValue_acc); + } else { + goto _L___5; + } + } else { + _L___5: /* CIL Label */ + if (i___0 == 2) { + if (floorButtons_2) { + retValue_acc = 1; + return (retValue_acc); + } else { + goto _L___4; + } + } else { + _L___4: /* CIL Label */ + if (i___0 == 3) { + if (floorButtons_3) { + retValue_acc = 1; + return (retValue_acc); + } else { + goto _L___3; + } + } else { + _L___3: /* CIL Label */ + if (i___0 == 4) { + if (floorButtons_4) { + retValue_acc = 1; + return (retValue_acc); + } else { + + } + } else { + + } + } + } + } + } + i___0 = i___0 + 1; + printf("%d %d %d %d %d %d %d %d %d\n", i___0, + d, + currentFloorID, + floorButtons_0, + floorButtons_1, + floorButtons_2, + floorButtons_3, + floorButtons_4, + retValue_acc); + } + while_7_break: /* CIL Label */ ; + } + i___0 = i___0 - 1; + } + while_6_break: /* CIL Label */ ; + } + } else { + + } + return 0; +} diff --git a/regression/nontermination/break_loop3/test.desc b/regression/nontermination/break_loop3/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/break_loop3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/continue_loop1/main.c b/regression/nontermination/continue_loop1/main.c new file mode 100644 index 000000000..993228df6 --- /dev/null +++ b/regression/nontermination/continue_loop1/main.c @@ -0,0 +1,12 @@ +int main (void) +{ + int k = 0; + + while (k < 10) + { + if (k == 0) continue; + k++; + } + + return 0; +} diff --git a/regression/nontermination/continue_loop1/test.desc b/regression/nontermination/continue_loop1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/continue_loop1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/float3/main.c b/regression/nontermination/float3/main.c new file mode 100644 index 000000000..9915bcd95 --- /dev/null +++ b/regression/nontermination/float3/main.c @@ -0,0 +1,10 @@ +void main() +{ + float x = 10.0; + + while(x>0) // does not terminate + { + x = x*0.1 + 0.1; + } + assert(x>=0.0); +} diff --git a/regression/nontermination/float3/test.desc b/regression/nontermination/float3/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/float3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/loop1/main.c b/regression/nontermination/loop1/main.c new file mode 100644 index 000000000..4db3a12ca --- /dev/null +++ b/regression/nontermination/loop1/main.c @@ -0,0 +1,14 @@ +int main(void) +{ + int i; + while(1) { + if (i++) break; + if (--i) break; + while(1) { + if (i) break; + if (i) break; + } + } + + return 0; +} diff --git a/regression/nontermination/loop1/test.desc b/regression/nontermination/loop1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/loop1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/loop_seq1/main.c b/regression/nontermination/loop_seq1/main.c new file mode 100644 index 000000000..c37b25bfa --- /dev/null +++ b/regression/nontermination/loop_seq1/main.c @@ -0,0 +1,8 @@ +int main(void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) i++; + while (j < 10) j++; + while (k < 10); + return 0; +} diff --git a/regression/nontermination/loop_seq1/test.desc b/regression/nontermination/loop_seq1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/loop_seq1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/loop_seq2/main.c b/regression/nontermination/loop_seq2/main.c new file mode 100644 index 000000000..9d7a6f994 --- /dev/null +++ b/regression/nontermination/loop_seq2/main.c @@ -0,0 +1,10 @@ +int main(void) +{ + int k = 0; + while (k < 10) if (k < 10) break; + + do { + } while (k < 10); + + return 0; +} diff --git a/regression/nontermination/loop_seq2/test.desc b/regression/nontermination/loop_seq2/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/loop_seq2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/loop_seq_break1/main.c b/regression/nontermination/loop_seq_break1/main.c new file mode 100644 index 000000000..3d8eb9612 --- /dev/null +++ b/regression/nontermination/loop_seq_break1/main.c @@ -0,0 +1,9 @@ +int main(void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) { i++; } + while (i < 15) { i++; } + while (k < 10) { if (k < 10) break; } + + return 0; +} diff --git a/regression/nontermination/loop_seq_break1/test.desc b/regression/nontermination/loop_seq_break1/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/loop_seq_break1/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/malloc1/main.c b/regression/nontermination/malloc1/main.c new file mode 100644 index 000000000..b968b3003 --- /dev/null +++ b/regression/nontermination/malloc1/main.c @@ -0,0 +1,6 @@ +int main (void) +{ + while (malloc(1)); + + return 0; +} diff --git a/regression/nontermination/malloc1/test.desc b/regression/nontermination/malloc1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/malloc1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/many_loops1/main.c b/regression/nontermination/many_loops1/main.c new file mode 100644 index 000000000..26da22f15 --- /dev/null +++ b/regression/nontermination/many_loops1/main.c @@ -0,0 +1,15 @@ +int main(void) +{ + int i = 2; + int j = 2; + + for (int k = 0; k < 10; k++); + + while (i > 1) { + if (i > 2) + i--; + if (j > 2) + j--; + } + return i; +} diff --git a/regression/nontermination/many_loops1/test.desc b/regression/nontermination/many_loops1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/many_loops1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/mem_alloc1/main.c b/regression/nontermination/mem_alloc1/main.c new file mode 100644 index 000000000..5b95dc851 --- /dev/null +++ b/regression/nontermination/mem_alloc1/main.c @@ -0,0 +1,14 @@ +int main(void) +{ + int i = 0; + while (1) { + i++; + int *a, *b; + if (malloc(110)) { + a = malloc(sizeof(malloc(10)))+1; + b = malloc(20); + } + } + + return 0; +} diff --git a/regression/nontermination/mem_alloc1/test.desc b/regression/nontermination/mem_alloc1/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/mem_alloc1/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/mto_loop1/main.c b/regression/nontermination/mto_loop1/main.c new file mode 100644 index 000000000..58e216f47 --- /dev/null +++ b/regression/nontermination/mto_loop1/main.c @@ -0,0 +1,7 @@ +int main(void) +{ + int i; + for ( int k = 0; k < 10; k++); + while ( i > 0); + return 0; +} diff --git a/regression/nontermination/mto_loop1/test.desc b/regression/nontermination/mto_loop1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/mto_loop1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops1/main.c b/regression/nontermination/nested_loops1/main.c new file mode 100644 index 000000000..5399c127d --- /dev/null +++ b/regression/nontermination/nested_loops1/main.c @@ -0,0 +1,23 @@ +int main(void) +{ + int i = 1, j = 2, k = 3; + while (i!=j && j!=k) + { + j = k - i; + k = j + i; + i = k - j; + while (i!=k && j!=k) + { + j = k - i; + k = j + i; + i = k - j; + while (i!=j && j!=k) + { + j = k - i; + k = j + i; + i = k - j; + } + + } + } +} diff --git a/regression/nontermination/nested_loops1/test.desc b/regression/nontermination/nested_loops1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops2/main.c b/regression/nontermination/nested_loops2/main.c new file mode 100644 index 000000000..97292a63d --- /dev/null +++ b/regression/nontermination/nested_loops2/main.c @@ -0,0 +1,27 @@ +#include + +int main(void) +{ //few permutations - 4th is the same 3 times unwind to prove nontermination + int i = 1, j = 2, k = 3; + int counter = 1; + while (1) + { + printf("%d %d %d\n", i, j, k); + if (counter % 2 ==0) + { + i = j + i; + j = i - j; + i = i - j; + counter--; + } + else + { + counter++; + } + //swap j and k + j = j + k; + k = j - k; + j = j - k; + //swap i and j + } +} diff --git a/regression/nontermination/nested_loops2/test.desc b/regression/nontermination/nested_loops2/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops3/main.c b/regression/nontermination/nested_loops3/main.c new file mode 100644 index 000000000..24021b6b5 --- /dev/null +++ b/regression/nontermination/nested_loops3/main.c @@ -0,0 +1,17 @@ +int main (void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) + { + while (j < 10) + { + while (k < 10) + { + i >>= 1; + j >>= 1; + } + } + } + + return 0; +} diff --git a/regression/nontermination/nested_loops3/test.desc b/regression/nontermination/nested_loops3/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops3b/main.c b/regression/nontermination/nested_loops3b/main.c new file mode 100644 index 000000000..50476ae00 --- /dev/null +++ b/regression/nontermination/nested_loops3b/main.c @@ -0,0 +1,15 @@ +int main (void) +{ + int i = 4, j = 4, k = 4; + while (j < 10) + { + while (k < 10) + { + if (!i) break; + i >>= 1; + j >>= 1; + } + } + + return 0; +} diff --git a/regression/nontermination/nested_loops3b/test.desc b/regression/nontermination/nested_loops3b/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops3b/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops4/main.c b/regression/nontermination/nested_loops4/main.c new file mode 100644 index 000000000..9aec0b597 --- /dev/null +++ b/regression/nontermination/nested_loops4/main.c @@ -0,0 +1,16 @@ +int main (void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) + { + while (j < 10) + { + while (k < 10) + { + for ( int x = 0; x < 100; x++); + } + } + } + + return 0; +} diff --git a/regression/nontermination/nested_loops4/test.desc b/regression/nontermination/nested_loops4/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/nested_loops4/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops5/main.c b/regression/nontermination/nested_loops5/main.c new file mode 100644 index 000000000..473d5578a --- /dev/null +++ b/regression/nontermination/nested_loops5/main.c @@ -0,0 +1,17 @@ +int main (void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) + { + while (j < 10) + { + while (k < 10) + { + if (k == 0) break; + } + if (j == 0) break; + } + } + + return 0; +} diff --git a/regression/nontermination/nested_loops5/test.desc b/regression/nontermination/nested_loops5/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops6/main.c b/regression/nontermination/nested_loops6/main.c new file mode 100644 index 000000000..a6c9486df --- /dev/null +++ b/regression/nontermination/nested_loops6/main.c @@ -0,0 +1,18 @@ +int main (void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) + { + while (j < 10) + { + while (k < 10) + { + if (k == 0) break; + } + if (j == 0) break; + } + if (i == 0) break; + } + + return 0; +} diff --git a/regression/nontermination/nested_loops6/test.desc b/regression/nontermination/nested_loops6/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/nested_loops6/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops7/main.c b/regression/nontermination/nested_loops7/main.c new file mode 100644 index 000000000..63bf53e75 --- /dev/null +++ b/regression/nontermination/nested_loops7/main.c @@ -0,0 +1,18 @@ +int main (void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) + { + while (j < 10) + { + while (k < 10) + { + k = j - i; + } + j++; + } + i++; + } + + return 0; +} diff --git a/regression/nontermination/nested_loops7/test.desc b/regression/nontermination/nested_loops7/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nested_loops8/main.c b/regression/nontermination/nested_loops8/main.c new file mode 100644 index 000000000..57819ffa9 --- /dev/null +++ b/regression/nontermination/nested_loops8/main.c @@ -0,0 +1,17 @@ +int main (void) +{ + int i = 0, j = 0, k = 0; + while (i < 10) + { + while (j < 10) + { + while (k < 10) + { + k++; + } + } + i++; + } + + return 0; +} diff --git a/regression/nontermination/nested_loops8/test.desc b/regression/nontermination/nested_loops8/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nested_loops8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nonterm1/main.c b/regression/nontermination/nonterm1/main.c new file mode 100644 index 000000000..6e47986c7 --- /dev/null +++ b/regression/nontermination/nonterm1/main.c @@ -0,0 +1,21 @@ +/* + * Date: 06/07/2015 + * Created by: Ton Chanh Le (chanhle@comp.nus.edu.sg) + * Adapted from Cairo_true-termination.c + */ + +typedef enum {false, true} bool; + +extern int __VERIFIER_nondet_int(void); + +int main() +{ + int x, y; + if (x > 0) { + while ((x != 0) && (y % 2 == 0)) { + x = x + 2; + y=y+2; + } + } + return 0; +} diff --git a/regression/nontermination/nonterm1/test.desc b/regression/nontermination/nonterm1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nonterm1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nontermination1/main.c b/regression/nontermination/nontermination1/main.c new file mode 100644 index 000000000..c9e776b1b --- /dev/null +++ b/regression/nontermination/nontermination1/main.c @@ -0,0 +1,15 @@ +int main (void) +{ + int i = 4, j = 4, k; + __CPROVER_assume(3<=k && k<=4); + while (j < 10) + { + do + { + i >>= 1; + j >>= 1; + } while ((k < 10)); + } + + return 0; +} diff --git a/regression/nontermination/nontermination1/test.desc b/regression/nontermination/nontermination1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nontermination1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nontermloop1/main.c b/regression/nontermination/nontermloop1/main.c new file mode 100644 index 000000000..c117788fe --- /dev/null +++ b/regression/nontermination/nontermloop1/main.c @@ -0,0 +1,9 @@ +void main() +{ + int x; + + while(1) + { + ++x; + } +} diff --git a/regression/nontermination/nontermloop1/test.desc b/regression/nontermination/nontermloop1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nontermloop1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nontermloop2/main.c b/regression/nontermination/nontermloop2/main.c new file mode 100644 index 000000000..55c33c4b9 --- /dev/null +++ b/regression/nontermination/nontermloop2/main.c @@ -0,0 +1,10 @@ +void main() +{ + int x = 1; + + while(x!=0) + { + if(x==1) x++; + else x--; + } +} diff --git a/regression/nontermination/nontermloop2/test.desc b/regression/nontermination/nontermloop2/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nontermloop2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nontermloop3/main.c b/regression/nontermination/nontermloop3/main.c new file mode 100644 index 000000000..955c8ba66 --- /dev/null +++ b/regression/nontermination/nontermloop3/main.c @@ -0,0 +1,11 @@ +void main() +{ + char x; + // __CPROVER_assume(-3<=x && x<=3); + + while(1) + { + // x = (x-1)%4; + x--; + } +} diff --git a/regression/nontermination/nontermloop3/test.desc b/regression/nontermination/nontermloop3/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nontermloop3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/nontermloop4/main.c b/regression/nontermination/nontermloop4/main.c new file mode 100644 index 000000000..537dde2a5 --- /dev/null +++ b/regression/nontermination/nontermloop4/main.c @@ -0,0 +1,6 @@ +void main() +{ + while(1) + { + } +} diff --git a/regression/nontermination/nontermloop4/test.desc b/regression/nontermination/nontermloop4/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/nontermloop4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/phase3/main.c b/regression/nontermination/phase3/main.c new file mode 100644 index 000000000..67c31f21f --- /dev/null +++ b/regression/nontermination/phase3/main.c @@ -0,0 +1,12 @@ +void main() +{ + unsigned char x = 1; + unsigned char y = 1; + + while(x>0) //does not terminate + { + if(x<100) x++; + y++; + } + assert(x==0); +} diff --git a/regression/nontermination/phase3/test.desc b/regression/nontermination/phase3/test.desc new file mode 100644 index 000000000..0c5568437 --- /dev/null +++ b/regression/nontermination/phase3/test.desc @@ -0,0 +1,9 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ +-- +-- +this can be proved by the termination checker with octagon invariants \ No newline at end of file diff --git a/regression/nontermination/phase4/main.c b/regression/nontermination/phase4/main.c new file mode 100644 index 000000000..f80f66a97 --- /dev/null +++ b/regression/nontermination/phase4/main.c @@ -0,0 +1,11 @@ +void main() +{ + unsigned char x = 1; + unsigned char y = 1; + + while(x>0) //does not terminate + { + y++; + } + assert(x==0); +} diff --git a/regression/nontermination/phase4/test.desc b/regression/nontermination/phase4/test.desc new file mode 100644 index 000000000..982a29212 --- /dev/null +++ b/regression/nontermination/phase4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--octagons +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/possible_bug1/main.c b/regression/nontermination/possible_bug1/main.c new file mode 100644 index 000000000..f8eab91e9 --- /dev/null +++ b/regression/nontermination/possible_bug1/main.c @@ -0,0 +1,12 @@ +int main(void) +{ + int i, counter = 1; + for (i=1; i< 10;) + { + if (counter % 3 == 0) i++; + counter++; + } + while (i) i=i; + + return 0; +} diff --git a/regression/nontermination/possible_bug1/test.desc b/regression/nontermination/possible_bug1/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/possible_bug1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop2/main.c b/regression/nontermination/sloop2/main.c new file mode 100644 index 000000000..3d252e48d --- /dev/null +++ b/regression/nontermination/sloop2/main.c @@ -0,0 +1,11 @@ +#include + +int main() +{ + int i = 4; + while (i > 1) { + if (i > 2) + i--; + } + return i; +} diff --git a/regression/nontermination/sloop2/test.desc b/regression/nontermination/sloop2/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/sloop2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop3/main.c b/regression/nontermination/sloop3/main.c new file mode 100644 index 000000000..88f50133f --- /dev/null +++ b/regression/nontermination/sloop3/main.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + int i = 2; + while (i > 1); + return i; +} diff --git a/regression/nontermination/sloop3/test.desc b/regression/nontermination/sloop3/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/sloop3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop4/main.c b/regression/nontermination/sloop4/main.c new file mode 100644 index 000000000..2341e3391 --- /dev/null +++ b/regression/nontermination/sloop4/main.c @@ -0,0 +1,9 @@ +#include + +int main() +{ + int i = 2; + while (i > 1) i--; + assert(i==0); + return i; +} diff --git a/regression/nontermination/sloop4/test.desc b/regression/nontermination/sloop4/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/sloop4/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop5/main.c b/regression/nontermination/sloop5/main.c new file mode 100644 index 000000000..ddcf95e49 --- /dev/null +++ b/regression/nontermination/sloop5/main.c @@ -0,0 +1,13 @@ +#include + +int main() +{ + int i = 4; + int j = 4; + while (i > 1) { + if (i > 2) + i--; + j--; + } + return i; +} diff --git a/regression/nontermination/sloop5/test.desc b/regression/nontermination/sloop5/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/sloop5/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop6/main.c b/regression/nontermination/sloop6/main.c new file mode 100644 index 000000000..0e33516b0 --- /dev/null +++ b/regression/nontermination/sloop6/main.c @@ -0,0 +1,14 @@ +#include + +int main() +{ + int i = 5; + int j = 4; + while (i > 1) { + if (i > 2) + i--; + if (j > 2) + j--; + } + return i; +} diff --git a/regression/nontermination/sloop6/test.desc b/regression/nontermination/sloop6/test.desc new file mode 100644 index 000000000..b69d21c78 --- /dev/null +++ b/regression/nontermination/sloop6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop7/main.c b/regression/nontermination/sloop7/main.c new file mode 100644 index 000000000..406a6469d --- /dev/null +++ b/regression/nontermination/sloop7/main.c @@ -0,0 +1,13 @@ +#include + +int main() +{ + int i = 4; + int j = 4; + while ( 1) { + if (i > 2) + i--; + j--; + } + return i; +} diff --git a/regression/nontermination/sloop7/test.desc b/regression/nontermination/sloop7/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/sloop7/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop8/main.c b/regression/nontermination/sloop8/main.c new file mode 100644 index 000000000..a9b1d16ab --- /dev/null +++ b/regression/nontermination/sloop8/main.c @@ -0,0 +1,10 @@ +int main(void) +{ + int x=2; + int i=1; + while(x<10) { + if(i%2==0) x++; + i++; + } + return 0; +} diff --git a/regression/nontermination/sloop8/test.desc b/regression/nontermination/sloop8/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/sloop8/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/sloop9/main.c b/regression/nontermination/sloop9/main.c new file mode 100644 index 000000000..e9e5e36f5 --- /dev/null +++ b/regression/nontermination/sloop9/main.c @@ -0,0 +1,10 @@ +int main(void) +{ + int x=9; + int i=1; + while(x<10) { + if(i%2==0) x++; + i++; + } + return 0; +} diff --git a/regression/nontermination/sloop9/test.desc b/regression/nontermination/sloop9/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/sloop9/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/nontermination/term_fail_break_loop1/main.c b/regression/nontermination/term_fail_break_loop1/main.c new file mode 100644 index 000000000..cfb20065d --- /dev/null +++ b/regression/nontermination/term_fail_break_loop1/main.c @@ -0,0 +1,11 @@ +int main (void) +{ + int k = 0; + + while (k < 10) + { + if (k == 0) break; + } + + return 0; +} diff --git a/regression/nontermination/term_fail_break_loop1/test.desc b/regression/nontermination/term_fail_break_loop1/test.desc new file mode 100644 index 000000000..a99c31502 --- /dev/null +++ b/regression/nontermination/term_fail_break_loop1/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c + +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/preconditions/Makefile b/regression/preconditions/Makefile index b5f2484a2..34cd734b9 100644 --- a/regression/preconditions/Makefile +++ b/regression/preconditions/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/preconditions/precond1/main.c b/regression/preconditions/precond1/main.c index b67310cdc..5b50bb943 100644 --- a/regression/preconditions/precond1/main.c +++ b/regression/preconditions/precond1/main.c @@ -1,5 +1,5 @@ -void foo(int x) -{ +void foo(int x) +{ int y = x; assert(y>=0); } diff --git a/regression/preconditions/precond1/test.desc b/regression/preconditions/precond1/test.desc index bb6885e71..c14aa11eb 100644 --- a/regression/preconditions/precond1/test.desc +++ b/regression/preconditions/precond1/test.desc @@ -3,4 +3,4 @@ main.c --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[foo\]: x <= 2147483647 && -((signed __CPROVER_bitvector\[33\])x) <= 0$ +^\[foo\]: x <= 2147483647 && -\(\(signed __CPROVER_bitvector\[33\]\)x\) <= 0$ diff --git a/regression/preconditions/precond2/main.c b/regression/preconditions/precond2/main.c index b67310cdc..5b50bb943 100644 --- a/regression/preconditions/precond2/main.c +++ b/regression/preconditions/precond2/main.c @@ -1,5 +1,5 @@ -void foo(int x) -{ +void foo(int x) +{ int y = x; assert(y>=0); } diff --git a/regression/preconditions/precond2/test.desc b/regression/preconditions/precond2/test.desc index dd3d5c389..372261595 100644 --- a/regression/preconditions/precond2/test.desc +++ b/regression/preconditions/precond2/test.desc @@ -3,4 +3,4 @@ main.c --preconditions --sufficient ^EXIT=5$ ^SIGNAL=0$ -^\[foo\]: !(x <= -1 && -((signed __CPROVER_bitvector\[33\])x) <= 2147483648)$ +^\[foo\]: !\(x <= -1 && -\(\(signed __CPROVER_bitvector\[33\]\)x\) <= 2147483648\)$ diff --git a/regression/preconditions/precond3/main.c b/regression/preconditions/precond3/main.c index 0045d70a5..7a25fd03b 100644 --- a/regression/preconditions/precond3/main.c +++ b/regression/preconditions/precond3/main.c @@ -1,5 +1,5 @@ -void foo(char* x) -{ +void foo(char* x) +{ assert(x!=0); *x = 0; } diff --git a/regression/preconditions/precond3/test.desc b/regression/preconditions/precond3/test.desc index 7428405d1..fc7cbc9ac 100644 --- a/regression/preconditions/precond3/test.desc +++ b/regression/preconditions/precond3/test.desc @@ -3,4 +3,4 @@ main.c --preconditions --sufficient --equalities ^EXIT=5$ ^SIGNAL=0$ -^\[foo\]: !(x == ((char \*)NULL))$ +^\[foo\]: !\(x == \(\(char \*\)NULL\)\)$ diff --git a/regression/preconditions/precond4/main.c b/regression/preconditions/precond4/main.c index e612011ba..f458914de 100644 --- a/regression/preconditions/precond4/main.c +++ b/regression/preconditions/precond4/main.c @@ -1,9 +1,9 @@ -int FirstOccurrence(int len, int a[]) -{ +int FirstOccurrence(int len, int a[]) +{ if(len<=0) return -1; int i = 0; assert(i=0) { assert(len>=3); diff --git a/regression/preconditions/precond5/test.desc b/regression/preconditions/precond5/test.desc index 2f2eca914..3c1c8d834 100644 --- a/regression/preconditions/precond5/test.desc +++ b/regression/preconditions/precond5/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c --preconditions ^EXIT=5$ ^SIGNAL=0$ ^$ +-- +-- +Needs fix for 6108ad3 diff --git a/regression/preconditions/precond6/main.c b/regression/preconditions/precond6/main.c index b67310cdc..5b50bb943 100644 --- a/regression/preconditions/precond6/main.c +++ b/regression/preconditions/precond6/main.c @@ -1,5 +1,5 @@ -void foo(int x) -{ +void foo(int x) +{ int y = x; assert(y>=0); } diff --git a/regression/preconditions/precond6/test.desc b/regression/preconditions/precond6/test.desc index bb6885e71..c14aa11eb 100644 --- a/regression/preconditions/precond6/test.desc +++ b/regression/preconditions/precond6/test.desc @@ -3,4 +3,4 @@ main.c --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[foo\]: x <= 2147483647 && -((signed __CPROVER_bitvector\[33\])x) <= 0$ +^\[foo\]: x <= 2147483647 && -\(\(signed __CPROVER_bitvector\[33\]\)x\) <= 0$ diff --git a/regression/preconditions/precond_contextsensitive1/main.c b/regression/preconditions/precond_contextsensitive1/main.c index ccf80ffd1..39cc080fa 100644 --- a/regression/preconditions/precond_contextsensitive1/main.c +++ b/regression/preconditions/precond_contextsensitive1/main.c @@ -1,15 +1,15 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x; - int y = sign(x); - assert(y==0); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x; + int y = sign(x); + assert(y==0); +} + diff --git a/regression/preconditions/precond_contextsensitive1/test.desc b/regression/preconditions/precond_contextsensitive1/test.desc index 76a98d95f..15e66e08d 100644 --- a/regression/preconditions/precond_contextsensitive1/test.desc +++ b/regression/preconditions/precond_contextsensitive1/test.desc @@ -3,4 +3,4 @@ main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[sign\]: sign#return_value#phi19 <= 0 && -((signed __CPROVER_bitvector\[33\])sign#return_value#phi19) <= 0 ==> x <= 0 && -((signed __CPROVER_bitvector\[33\])x) <= 0$ +^\[sign\]: sign#return_value#phi17 <= 0 && -\(\(signed __CPROVER_bitvector\[33\]\)sign#return_value#phi17\) <= 0 ==> x <= 0 && -\(\(signed __CPROVER_bitvector\[33\]\)x\) <= 0$ diff --git a/regression/preconditions/precond_contextsensitive2/main.c b/regression/preconditions/precond_contextsensitive2/main.c index d1713f3e3..72b6f0cf7 100644 --- a/regression/preconditions/precond_contextsensitive2/main.c +++ b/regression/preconditions/precond_contextsensitive2/main.c @@ -1,11 +1,11 @@ - -void foo(int x) -{ - assert(x!=1); -} - -int main(int argc, char** argv) -{ - foo(argc); -} - + +void foo(int x) +{ + assert(x!=1); +} + +int main(int argc, char** argv) +{ + foo(argc); +} + diff --git a/regression/preconditions/precond_contextsensitive2/test.desc b/regression/preconditions/precond_contextsensitive2/test.desc index f757b9ab1..ac8b82f52 100644 --- a/regression/preconditions/precond_contextsensitive2/test.desc +++ b/regression/preconditions/precond_contextsensitive2/test.desc @@ -3,4 +3,4 @@ main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[_start\]: argc' <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc') <= -2$ +^\[_start\]: argc' <= 268435456 && -\(\(signed __CPROVER_bitvector\[33\]\)argc'\) <= -2$ diff --git a/regression/termination/Makefile b/regression/termination/Makefile index f92c8b2be..59cfebf8f 100644 --- a/regression/termination/Makefile +++ b/regression/termination/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --termination test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -p -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/termination/abe_createBack1/main.c b/regression/termination/abe_createBack1/main.c index 8bfbfb993..5397ab702 100644 --- a/regression/termination/abe_createBack1/main.c +++ b/regression/termination/abe_createBack1/main.c @@ -59,7 +59,7 @@ void createBack(struct SDL_Surface back_surface) // SDL_UpperBlit(img, (struct SDL_Rect *)NULL, *back_surface, &pos); } //assert(-y>-(y+img.h)); - // assert(-y+(y+img.h)>0); + // assert(-y+(y+img.h)>0); assert(img.h!=0); } } diff --git a/regression/termination/abort1/main.c b/regression/termination/abort1/main.c index ab6bd07ef..3e8ad47a7 100644 --- a/regression/termination/abort1/main.c +++ b/regression/termination/abort1/main.c @@ -8,7 +8,7 @@ int foo(int x) int main(int argc, char** argv) { int x = argc; - x = foo(x); + x = foo(x); assert(x>=1); return x; } diff --git a/regression/termination/abort3/main.c b/regression/termination/abort3/main.c index ecc2f25b9..84dc211a7 100644 --- a/regression/termination/abort3/main.c +++ b/regression/termination/abort3/main.c @@ -7,7 +7,7 @@ int foo(int x) int main(int argc, char** argv) { int x = argc; - x = foo(x); + x = foo(x); assert(x>=1); return x; } diff --git a/regression/termination/abort4/test.desc b/regression/termination/abort4/test.desc index b69d21c78..a27ac3470 100644 --- a/regression/termination/abort4/test.desc +++ b/regression/termination/abort4/test.desc @@ -1,6 +1,9 @@ CORE main.c -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ +-- +-- +can be refuted with nontermination checker diff --git a/regression/termination/abort5/test.desc b/regression/termination/abort5/test.desc index 2d9665fb2..ad6260222 100644 --- a/regression/termination/abort5/test.desc +++ b/regression/termination/abort5/test.desc @@ -1,6 +1,9 @@ CORE main.c --context-sensitive -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ +-- +-- +can be refuted with nontermination checker diff --git a/regression/termination/abort6/test.desc b/regression/termination/abort6/test.desc index b69d21c78..a27ac3470 100644 --- a/regression/termination/abort6/test.desc +++ b/regression/termination/abort6/test.desc @@ -1,6 +1,9 @@ CORE main.c -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ +-- +-- +can be refuted with nontermination checker diff --git a/regression/termination/array_safe5/main.c b/regression/termination/array_safe5/main.c index 20cd3eb88..9e3671eb3 100644 --- a/regression/termination/array_safe5/main.c +++ b/regression/termination/array_safe5/main.c @@ -4,10 +4,10 @@ void main (void) { unsigned a[SIZE] = {0, }; //without initializtion a[i] for i>0 are not initialized in the 1st iteration, and hence we cannot do better than UINT_MAX for the upper bounds - for(unsigned i=0; i 0 || l_var<0 || l_var>=1073741824); // precondition for termination int i=0; - if (l_var >= 0) + if (l_var >= 0) { - while (l_var < 1073741824) + while (l_var < 1073741824) { i++; l_var = l_var << 1; @@ -21,7 +21,7 @@ void f(int l_var) int main() { int l_var = nondet_int(); - + f(l_var); return 0; diff --git a/regression/termination/cav08_1p/main.c b/regression/termination/cav08_1p/main.c index e30cb8c9d..9f0ebeb54 100644 --- a/regression/termination/cav08_1p/main.c +++ b/regression/termination/cav08_1p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -8,9 +8,9 @@ void f(int l_var) { // __CPROVER_assume(l_var> 0 || l_var<0 || l_var>=1073741824); // precondition for termination int i=0; - if (l_var >= 0) + if (l_var >= 0) { - while (l_var < 1073741824) + while (l_var < 1073741824) { i++; l_var = l_var << 1; @@ -21,7 +21,7 @@ void f(int l_var) int main() { int l_var = nondet_int(); - + f(l_var); return 0; diff --git a/regression/termination/cav08_2/main.c b/regression/termination/cav08_2/main.c index 67b296232..3c0991765 100644 --- a/regression/termination/cav08_2/main.c +++ b/regression/termination/cav08_2/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -15,7 +15,7 @@ void f(int cbSrcLength, int cbHeader, int nBlockAlignment, DWORD *pbSrc, DWORD * { __CPROVER_assume(cbSrcLength < cbHeader || nBlockAlignment > 0 && cbHeader > 0); // precondition for termination - while (cbSrcLength >= cbHeader) + while (cbSrcLength >= cbHeader) { DWORD dwHeader; UINT cbBlockLength; @@ -28,7 +28,7 @@ void f(int cbSrcLength, int cbHeader, int nBlockAlignment, DWORD *pbSrc, DWORD * int nStepIndex = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(nStepIndex) ) return 0; *pbDst++ = (BYTE)((nPredSample >> 8) + 128); */ - while (cbBlockLength--) + while (cbBlockLength--) { /* DWORD bSample = *pbSrc++; DWORD nEncSample = (bSample & (BYTE)0x0F); @@ -56,7 +56,7 @@ int main() int nBlockAlignment = nondet_int(); DWORD *pbSrc, *pbDst; int *step; - + f(cbSrcLength, cbHeader, nBlockAlignment, pbSrc, pbSrc, step); return 0; diff --git a/regression/termination/cav08_2p/main.c b/regression/termination/cav08_2p/main.c index ab173d9ae..72a0c3c9d 100644 --- a/regression/termination/cav08_2p/main.c +++ b/regression/termination/cav08_2p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -15,7 +15,7 @@ void f(int cbSrcLength, int cbHeader, int nBlockAlignment, DWORD *pbSrc, DWORD * { // __CPROVER_assume(cbSrcLength < cbHeader || nBlockAlignment > 0 && cbHeader > 0); // precondition for termination - while (cbSrcLength >= cbHeader) + while (cbSrcLength >= cbHeader) { DWORD dwHeader; UINT cbBlockLength; @@ -28,7 +28,7 @@ void f(int cbSrcLength, int cbHeader, int nBlockAlignment, DWORD *pbSrc, DWORD * int nStepIndex = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(nStepIndex) ) return 0; *pbDst++ = (BYTE)((nPredSample >> 8) + 128); */ - while (cbBlockLength--) + while (cbBlockLength--) { /* DWORD bSample = *pbSrc++; DWORD nEncSample = (bSample & (BYTE)0x0F); @@ -56,7 +56,7 @@ int main() int nBlockAlignment = nondet_int(); DWORD *pbSrc, *pbDst; int *step; - + f(cbSrcLength, cbHeader, nBlockAlignment, pbSrc, pbSrc, step); return 0; diff --git a/regression/termination/cav08_3/main.c b/regression/termination/cav08_3/main.c index fff20a220..efc7bce87 100644 --- a/regression/termination/cav08_3/main.c +++ b/regression/termination/cav08_3/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -8,7 +8,7 @@ void f(int x, int y, int z) { __CPROVER_assume(x<=0 || x+y<=0 || x+2*y+z<=0 || x+3*y+3*z<=0 || z<0 || z<=0 && y<=0); // precondition for termination - while (x>0) + while (x>0) { x = x + y; y = y + z; @@ -20,7 +20,7 @@ int main() int x = nondet_int(); int y = nondet_int(); int z = nondet_int(); - + f(x,y,z); return 0; diff --git a/regression/termination/cav08_3p/main.c b/regression/termination/cav08_3p/main.c index 3d56eff51..7a6fa2b39 100644 --- a/regression/termination/cav08_3p/main.c +++ b/regression/termination/cav08_3p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -8,7 +8,7 @@ void f(int x, int y, int z) { // __CPROVER_assume(x<=0 || x+y<=0 || x+2*y+z<=0 || x+3*y+3*z<=0 || z<0 || z<=0 && y<=0); // precondition for termination - while (x>0) + while (x>0) { x = x + y; y = y + z; @@ -20,7 +20,7 @@ int main() int x = nondet_int(); int y = nondet_int(); int z = nondet_int(); - + f(x,y,z); return 0; diff --git a/regression/termination/cav08_4/main.c b/regression/termination/cav08_4/main.c index 61fb56ac3..4e6b8ca57 100644 --- a/regression/termination/cav08_4/main.c +++ b/regression/termination/cav08_4/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -7,11 +7,11 @@ unsigned nondet_int(); void f(int x, int y, int N) { __CPROVER_assume(x>N || x+y>=0); // precondition for termination - - while (x<=N) + + while (x<=N) { int c; - if(c) + if(c) { x = 2*x + y; y = y + 1; @@ -28,7 +28,7 @@ int main() int x = nondet_int(); int y = nondet_int(); int N = nondet_int(); - + f(x,y,N); return 0; diff --git a/regression/termination/cav08_4p/main.c b/regression/termination/cav08_4p/main.c index 6016788b8..f87616cd0 100644 --- a/regression/termination/cav08_4p/main.c +++ b/regression/termination/cav08_4p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -7,11 +7,11 @@ unsigned nondet_int(); void f(int x, int y, int N) { // __CPROVER_assume(x>N || x+y>=0); // precondition for termination - - while (x<=N) + + while (x<=N) { int c; - if(c) + if(c) { x = 2*x + y; y = y + 1; @@ -28,7 +28,7 @@ int main() int x = nondet_int(); int y = nondet_int(); int N = nondet_int(); - + f(x,y,N); return 0; diff --git a/regression/termination/cav08_5/main.c b/regression/termination/cav08_5/main.c index e101bf65a..8c01cbd34 100644 --- a/regression/termination/cav08_5/main.c +++ b/regression/termination/cav08_5/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -7,8 +7,8 @@ unsigned nondet_int(); void f(int x) { __CPROVER_assume(x>5 || x<0); - - while (x>=0) + + while (x>=0) { x = -2*x + 10; } @@ -17,7 +17,7 @@ void f(int x) int main() { int x = nondet_int(); - + f(x); return 0; diff --git a/regression/termination/cav08_5p/main.c b/regression/termination/cav08_5p/main.c index 991ef5997..f96dd606d 100644 --- a/regression/termination/cav08_5p/main.c +++ b/regression/termination/cav08_5p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -7,8 +7,8 @@ unsigned nondet_int(); void f(int x) { // __CPROVER_assume(x>5 || x<0); - - while (x>=0) + + while (x>=0) { x = -2*x + 10; } @@ -17,7 +17,7 @@ void f(int x) int main() { int x = nondet_int(); - + f(x); return 0; diff --git a/regression/termination/cav08_6/main.c b/regression/termination/cav08_6/main.c index 75719e100..21436c6be 100644 --- a/regression/termination/cav08_6/main.c +++ b/regression/termination/cav08_6/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -9,10 +9,10 @@ void f(int y, int n) __CPROVER_assume(n>200 && y<9); //additional assumption given __CPROVER_assume(y>0); // precondition for termination int x = 0; - while (1) + while (1) { - if (x= 200) break; } @@ -23,7 +23,7 @@ int main() { int y = nondet_int(); int n = nondet_int(); - + f(y,n); return 0; diff --git a/regression/termination/cav08_6p/main.c b/regression/termination/cav08_6p/main.c index 9194b0fdb..54a1d9499 100644 --- a/regression/termination/cav08_6p/main.c +++ b/regression/termination/cav08_6p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -9,10 +9,10 @@ void f(int y, int n) __CPROVER_assume(n>200 && y<9); //additional assumption given // __CPROVER_assume(y>0); // precondition for termination int x = 0; - while (1) + while (1) { - if (x= 200) break; } @@ -23,7 +23,7 @@ int main() { int y = nondet_int(); int n = nondet_int(); - + f(y,n); return 0; diff --git a/regression/termination/cav08_7/main.c b/regression/termination/cav08_7/main.c index 337ee200b..eb88a2ae8 100644 --- a/regression/termination/cav08_7/main.c +++ b/regression/termination/cav08_7/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -8,12 +8,12 @@ void f(int x, int y) { __CPROVER_assume(x==y || x>0 && y>0); // precondition for termination - while (x!=y) - { - if (x>y) + while (x!=y) + { + if (x>y) { x=x-y; - } + } else { y=y-x; @@ -25,7 +25,7 @@ int main() { int x = nondet_int(); int y = nondet_int(); - + f(x,y); return 0; diff --git a/regression/termination/cav08_7p/main.c b/regression/termination/cav08_7p/main.c index 7de383b19..3579f823a 100644 --- a/regression/termination/cav08_7p/main.c +++ b/regression/termination/cav08_7p/main.c @@ -1,4 +1,4 @@ -/* +/* Example from Cook et al, CAV 2008 */ @@ -8,12 +8,12 @@ void f(int x, int y) { // __CPROVER_assume(x==y || x>0 && y>0); // precondition for termination - while (x!=y) - { - if (x>y) + while (x!=y) + { + if (x>y) { x=x-y; - } + } else { y=y-x; @@ -25,7 +25,7 @@ int main() { int x = nondet_int(); int y = nondet_int(); - + f(x,y); return 0; diff --git a/regression/termination/contextsensitive1/main.c b/regression/termination/contextsensitive1/main.c index c1b928562..80cbc563c 100644 --- a/regression/termination/contextsensitive1/main.c +++ b/regression/termination/contextsensitive1/main.c @@ -1,17 +1,17 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x = 1; - int y = sign(x); - x = -x; - int z = sign(x); - assert(-1<=y && y<=1 && -1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x = 1; + int y = sign(x); + x = -x; + int z = sign(x); + assert(-1<=y && y<=1 && -1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive2/main.c b/regression/termination/contextsensitive2/main.c index b228a31b4..bcd5eb7ed 100644 --- a/regression/termination/contextsensitive2/main.c +++ b/regression/termination/contextsensitive2/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive3/main.c b/regression/termination/contextsensitive3/main.c index deab63659..a67b572d0 100644 --- a/regression/termination/contextsensitive3/main.c +++ b/regression/termination/contextsensitive3/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do2(x); - assert(y==1); - x = -x; - int z = do1(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do2(x); + assert(y==1); + x = -x; + int z = do1(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive4/main.c b/regression/termination/contextsensitive4/main.c index 9f1348396..f90baaa2d 100644 --- a/regression/termination/contextsensitive4/main.c +++ b/regression/termination/contextsensitive4/main.c @@ -1,29 +1,29 @@ - -int x = 1; - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int x = 1; + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive5/main.c b/regression/termination/contextsensitive5/main.c index fb0251140..81be7a8ed 100644 --- a/regression/termination/contextsensitive5/main.c +++ b/regression/termination/contextsensitive5/main.c @@ -1,15 +1,15 @@ -#include - -void foo(int* x) -{ - assert(x!=NULL); - *x = 0; -} - -void main() -{ - int x; - int *y = &x; - foo(y); -} - +#include + +void foo(int* x) +{ + assert(x!=NULL); + *x = 0; +} + +void main() +{ + int x; + int *y = &x; + foo(y); +} + diff --git a/regression/termination/contextsensitive6/main.c b/regression/termination/contextsensitive6/main.c index be4631faa..ed39a132c 100644 --- a/regression/termination/contextsensitive6/main.c +++ b/regression/termination/contextsensitive6/main.c @@ -1,12 +1,12 @@ - -void foo(int x) -{ - assert(x!=0); -} - -void main() -{ - int x = 1; - foo(x); -} - + +void foo(int x) +{ + assert(x!=0); +} + +void main() +{ + int x = 1; + foo(x); +} + diff --git a/regression/termination/equal1/main.c b/regression/termination/equal1/main.c index e4434c38e..44bbd284d 100644 --- a/regression/termination/equal1/main.c +++ b/regression/termination/equal1/main.c @@ -1,5 +1,5 @@ -int foo(int x, int y) -{ +int foo(int x, int y) +{ return x; } diff --git a/regression/termination/equal2/main.c b/regression/termination/equal2/main.c index 5dffe2105..5280ce06f 100644 --- a/regression/termination/equal2/main.c +++ b/regression/termination/equal2/main.c @@ -1,5 +1,5 @@ -unsigned foo(unsigned x, unsigned y) -{ +unsigned foo(unsigned x, unsigned y) +{ __CPROVER_assume(x<10*y && y>10); return x/y; } diff --git a/regression/termination/equal3/main.c b/regression/termination/equal3/main.c index 6e0366b24..64deebaa1 100644 --- a/regression/termination/equal3/main.c +++ b/regression/termination/equal3/main.c @@ -1,5 +1,5 @@ -unsigned foo(unsigned x, unsigned y) -{ +unsigned foo(unsigned x, unsigned y) +{ if(x) return y+1; return y-1; } diff --git a/regression/termination/equal4/main.c b/regression/termination/equal4/main.c index 876f00c5c..62fc9aadd 100644 --- a/regression/termination/equal4/main.c +++ b/regression/termination/equal4/main.c @@ -3,10 +3,10 @@ int foo(int x, int y) while(x<10) { y++; - x++; + x++; y--; } - + return y; } diff --git a/regression/termination/equal6/main.c b/regression/termination/equal6/main.c index e439e7892..22e381c9d 100644 --- a/regression/termination/equal6/main.c +++ b/regression/termination/equal6/main.c @@ -4,7 +4,7 @@ void main() while(x<10) { - x++; + x++; while(y0) { diff --git a/regression/termination/float2/main.c b/regression/termination/float2/main.c index 9a5d17658..46d257f96 100644 --- a/regression/termination/float2/main.c +++ b/regression/termination/float2/main.c @@ -2,7 +2,7 @@ int main(int argc, char** argv) { - float x = 10.0; + float x = 10.0; while(x>0) { diff --git a/regression/termination/float3/main.c b/regression/termination/float3/main.c index ba27d41b6..9915bcd95 100644 --- a/regression/termination/float3/main.c +++ b/regression/termination/float3/main.c @@ -1,6 +1,6 @@ void main() { - float x = 10.0; + float x = 10.0; while(x>0) // does not terminate { diff --git a/regression/termination/float3/test.desc b/regression/termination/float3/test.desc index b69d21c78..a27ac3470 100644 --- a/regression/termination/float3/test.desc +++ b/regression/termination/float3/test.desc @@ -1,6 +1,9 @@ CORE main.c -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ +-- +-- +can be refuted with nontermination checker diff --git a/regression/termination/float4/main.c b/regression/termination/float4/main.c index 98c97c5d9..e52a531f4 100644 --- a/regression/termination/float4/main.c +++ b/regression/termination/float4/main.c @@ -1,6 +1,6 @@ void main() { - float x = 10.0; + float x = 10.0; while(x>0.0) // does not terminate { diff --git a/regression/termination/float5/main.c b/regression/termination/float5/main.c index 411a1d54e..3b93d22ed 100644 --- a/regression/termination/float5/main.c +++ b/regression/termination/float5/main.c @@ -2,7 +2,7 @@ int main(int argc, char** argv) { float x; - __CPROVER_assume(-FLT_MAX<=x && x<=FLT_MAX); + __CPROVER_assume(-FLT_MAX<=x && x<=FLT_MAX); while(x>0.0f) { x *= 0.1f; //terminates diff --git a/regression/termination/float6/main.c b/regression/termination/float6/main.c index 2625189cd..2db9b4007 100644 --- a/regression/termination/float6/main.c +++ b/regression/termination/float6/main.c @@ -5,7 +5,7 @@ int main(int argc, char** argv) while(x>=0) //doesn't terminate { x -= 0.15f; - if(0>x && x>-0.15f) + if(0>x && x>-0.15f) { x = 15.0f; } diff --git a/regression/termination/float6/test.desc b/regression/termination/float6/test.desc index 8314a397f..0d2310c63 100644 --- a/regression/termination/float6/test.desc +++ b/regression/termination/float6/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c ^EXIT=5$ ^SIGNAL=0$ ^VERIFICATION INCONCLUSIVE$ +-- +-- +result is brittle and depends on bounds on the search and the SAT solver used diff --git a/regression/termination/global1/main.c b/regression/termination/global1/main.c index ecb897b62..368fcddd7 100644 --- a/regression/termination/global1/main.c +++ b/regression/termination/global1/main.c @@ -1,21 +1,21 @@ -#include - -int g; - -int foo(int y) -{ - __CPROVER_assume(g=1); - assert(z==0); -} - +#include + +int g; + +int foo(int y) +{ + __CPROVER_assume(g=1); + assert(z==0); +} + diff --git a/regression/termination/global2/main.c b/regression/termination/global2/main.c index f653bce18..32734b1a1 100644 --- a/regression/termination/global2/main.c +++ b/regression/termination/global2/main.c @@ -1,22 +1,22 @@ -int g; - -void foo() -{ - g=10; -} - -int bar() -{ - return 20; -} - -void main() -{ - g = 1; - int x; - foo(); - x = bar(); - assert(g==10); - assert(x==20); -} - +int g; + +void foo() +{ + g=10; +} + +int bar() +{ + return 20; +} + +void main() +{ + g = 1; + int x; + foo(); + x = bar(); + assert(g==10); + assert(x==20); +} + diff --git a/regression/termination/global2/test.desc b/regression/termination/global2/test.desc index 9ebb38e34..bbaf10625 100644 --- a/regression/termination/global2/test.desc +++ b/regression/termination/global2/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c ^EXIT=0$ diff --git a/regression/termination/global3/main.c b/regression/termination/global3/main.c index 965a7f015..347464275 100644 --- a/regression/termination/global3/main.c +++ b/regression/termination/global3/main.c @@ -1,8 +1,8 @@ int x; int z; -void foo() -{ +void foo() +{ for(x=0;x<10;x++); } diff --git a/regression/termination/global4/main.c b/regression/termination/global4/main.c index 80e8b6e50..646b6a47b 100644 --- a/regression/termination/global4/main.c +++ b/regression/termination/global4/main.c @@ -1,6 +1,6 @@ int g = 1; -void modify_global() +void modify_global() { g = g; } @@ -11,12 +11,12 @@ void main() //g = g; - if(x) + if(x) { - modify_global(); + modify_global(); } - modify_global(); + modify_global(); assert(g==1); } diff --git a/regression/termination/ite1/main.c b/regression/termination/ite1/main.c index b4829d864..4f98ef44c 100644 --- a/regression/termination/ite1/main.c +++ b/regression/termination/ite1/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x; - int y = 0; - - while(y==0) - { - if(x>=5) y=x; - else y=5; - } - - assert(y>=5); -} +void main() +{ + int x; + int y = 0; + + while(y==0) + { + if(x>=5) y=x; + else y=5; + } + + assert(y>=5); +} diff --git a/regression/termination/ite2/main.c b/regression/termination/ite2/main.c index e6b24818f..fbb4eddee 100644 --- a/regression/termination/ite2/main.c +++ b/regression/termination/ite2/main.c @@ -1,11 +1,11 @@ -int foo(int x, int y) -{ +int foo(int x, int y) +{ int res = y; if(x) res = y+1; return res; } -int bar(int x) -{ +int bar(int x) +{ if(x) return 1; return 2; } @@ -13,7 +13,7 @@ int bar(int x) void main() { int x; - int y = 0; + int y = 0; int z = bar(x); int w = foo(z,y); diff --git a/regression/termination/ite3/main.c b/regression/termination/ite3/main.c index 0bd206af3..624104931 100644 --- a/regression/termination/ite3/main.c +++ b/regression/termination/ite3/main.c @@ -1,14 +1,14 @@ -int foo(int x) -{ +int foo(int x) +{ int res = 0; if(x) res = 1; return res; } -int bar(int x) -{ +int bar(int x) +{ int res = 2; if(x) res = 1; - return res; + return res; } void main() diff --git a/regression/termination/ite4/main.c b/regression/termination/ite4/main.c index fd030a9a5..cd12c0013 100644 --- a/regression/termination/ite4/main.c +++ b/regression/termination/ite4/main.c @@ -1,13 +1,13 @@ -int foo(int x) -{ +int foo(int x) +{ int res; if(x) return 0; else return 1; } -int bar(int x) -{ +int bar(int x) +{ int res; if(x) res = 0; else res = 1; diff --git a/regression/termination/locks1/main.c b/regression/termination/locks1/main.c index 99ddf6417..4f76b0c20 100644 --- a/regression/termination/locks1/main.c +++ b/regression/termination/locks1/main.c @@ -10,7 +10,7 @@ int acquire_lock(char tid) return 0; } -void release_lock() +void release_lock() { lock = 0; } @@ -20,7 +20,7 @@ void thread1() if(acquire_lock(1)) { //do_stuff1(); - exit=1; + exit=1; release_lock(); } } diff --git a/regression/termination/locks2/main.c b/regression/termination/locks2/main.c index 5d2948237..789f0d094 100644 --- a/regression/termination/locks2/main.c +++ b/regression/termination/locks2/main.c @@ -10,7 +10,7 @@ int acquire_lock(char tid) return 0; } -void release_lock() +void release_lock() { lock = 0; } @@ -20,7 +20,7 @@ void thread1() if(acquire_lock(1)) { //do_stuff1(); - exit=1; + exit=1; //release_lock(); } } diff --git a/regression/termination/loop1/main.c b/regression/termination/loop1/main.c index 6750175ea..6c0d1f808 100644 --- a/regression/termination/loop1/main.c +++ b/regression/termination/loop1/main.c @@ -1,6 +1,6 @@ void main() { - int x = 0; + int x = 0; while(x<10) { diff --git a/regression/termination/loop2/main.c b/regression/termination/loop2/main.c index e67632a94..dd5d9c810 100644 --- a/regression/termination/loop2/main.c +++ b/regression/termination/loop2/main.c @@ -1,7 +1,7 @@ void main() { - int x = 0; + int x = 0; int y = 0; while(x<10) diff --git a/regression/termination/loop3/main.c b/regression/termination/loop3/main.c index 21a30e139..6ddb8924c 100644 --- a/regression/termination/loop3/main.c +++ b/regression/termination/loop3/main.c @@ -4,13 +4,13 @@ void main() int y; x=0; y=0; - + while(x<10 && y<20) { ++x; ++y; } - + int z=x+y; assert(z>=0); diff --git a/regression/termination/loop5/main.c b/regression/termination/loop5/main.c index f301dde9d..2fd0a019f 100644 --- a/regression/termination/loop5/main.c +++ b/regression/termination/loop5/main.c @@ -1,6 +1,6 @@ void main() { - unsigned x = 0; + unsigned x = 0; while(x<10) ++x; diff --git a/regression/termination/loop6/main.c b/regression/termination/loop6/main.c index 2c4213934..c7bd862fe 100644 --- a/regression/termination/loop6/main.c +++ b/regression/termination/loop6/main.c @@ -4,13 +4,13 @@ void main() { int x = 0; unsigned y = 0; - + while(x<10 && y<20) { ++x; ++y; } - + int z=x+y; assert(z<=20); } diff --git a/regression/termination/loop7/main.c b/regression/termination/loop7/main.c index 3c7dea455..1707acb1d 100644 --- a/regression/termination/loop7/main.c +++ b/regression/termination/loop7/main.c @@ -1,6 +1,6 @@ void main() { - int x; + int x; unsigned y; x = -10; y = 10; diff --git a/regression/termination/loop9/main.c b/regression/termination/loop9/main.c index ca602a19a..201463c41 100644 --- a/regression/termination/loop9/main.c +++ b/regression/termination/loop9/main.c @@ -1,6 +1,6 @@ void main() { - int x = 10; + int x = 10; while(x>0) { diff --git a/regression/termination/loops/array_false.c b/regression/termination/loops/array_false.c index 8e391d4b1..eeb8a1f76 100644 --- a/regression/termination/loops/array_false.c +++ b/regression/termination/loops/array_false.c @@ -11,16 +11,16 @@ main() unsigned int SIZE=1; unsigned int j,k; int array[SIZE], menor; - + menor = __VERIFIER_nondet_int(); for(j=0;jmenor); + menor = array[j]; + } + + __VERIFIER_assert(array[0]>menor); } diff --git a/regression/termination/loops/array_true.c b/regression/termination/loops/array_true.c index e40b4725c..7cb2de419 100644 --- a/regression/termination/loops/array_true.c +++ b/regression/termination/loops/array_true.c @@ -11,16 +11,16 @@ main() unsigned int SIZE=1; unsigned int j,k; int array[SIZE], menor; - + menor = __VERIFIER_nondet_int(); for(j=0;j=menor); + menor = array[j]; + } + + __VERIFIER_assert(array[0]>=menor); } diff --git a/regression/termination/loops/bubble_sort_false.c b/regression/termination/loops/bubble_sort_false.c index fa4558983..603a86c21 100644 --- a/regression/termination/loops/bubble_sort_false.c +++ b/regression/termination/loops/bubble_sort_false.c @@ -24,8 +24,8 @@ extern __attribute__((__nothrow__, __noreturn__)) void abort(void) ; #line 5 "test-0180.c" extern int __VERIFIER_nondet_int(void); #line 7 "test-0180.c" -static void fail(void) -{ +static void fail(void) +{ { ERROR: assert(0); @@ -35,7 +35,7 @@ static void fail(void) #line 39 "test-0180.c" struct list_head gl_list = {& gl_list, & gl_list}; #line 41 "test-0180.c" -static void inspect(struct list_head const *head ) +static void inspect(struct list_head const *head ) { struct node const *node ; unsigned int __cil_tmp3 ; struct list_head *__cil_tmp4 ; @@ -744,7 +744,7 @@ static void inspect(struct list_head const *head ) } } #line 74 "test-0180.c" -__inline static void __list_add(struct list_head *new , struct list_head *prev , struct list_head *next ) +__inline static void __list_add(struct list_head *new , struct list_head *prev , struct list_head *next ) { unsigned int __cil_tmp4 ; unsigned int __cil_tmp5 ; unsigned int __cil_tmp6 ; @@ -772,7 +772,7 @@ __inline static void __list_add(struct list_head *new , struct list_head *prev , } } #line 84 "test-0180.c" -__inline static void __list_del(struct list_head *prev , struct list_head *next ) +__inline static void __list_del(struct list_head *prev , struct list_head *next ) { unsigned int __cil_tmp3 ; unsigned int __cil_tmp4 ; @@ -790,7 +790,7 @@ __inline static void __list_del(struct list_head *prev , struct list_head *next } } #line 90 "test-0180.c" -__inline static void list_add(struct list_head *new , struct list_head *head ) +__inline static void list_add(struct list_head *new , struct list_head *head ) { struct list_head *__cil_tmp3 ; { @@ -805,7 +805,7 @@ __inline static void list_add(struct list_head *new , struct list_head *head ) } } #line 95 "test-0180.c" -__inline static void list_move(struct list_head *list , struct list_head *head ) +__inline static void list_move(struct list_head *list , struct list_head *head ) { unsigned int __cil_tmp3 ; unsigned int __cil_tmp4 ; struct list_head *__cil_tmp5 ; @@ -831,7 +831,7 @@ __inline static void list_move(struct list_head *list , struct list_head *head ) } } #line 101 "test-0180.c" -static void gl_insert(int value ) +static void gl_insert(int value ) { struct node *node ; void *tmp ; unsigned int __cil_tmp4 ; @@ -910,7 +910,7 @@ static void gl_insert(int value ) } } #line 112 "test-0180.c" -static void gl_read(void) +static void gl_read(void) { int tmp ; int tmp___0 ; @@ -941,7 +941,7 @@ static void gl_read(void) } } #line 120 "test-0180.c" -static void gl_destroy(void) +static void gl_destroy(void) { struct list_head *next ; struct list_head *__cil_tmp2 ; unsigned int __cil_tmp3 ; @@ -1012,7 +1012,7 @@ static void gl_destroy(void) } } #line 129 "test-0180.c" -static int val_from_node(struct list_head *head ) +static int val_from_node(struct list_head *head ) { struct node *entry ; struct node *__cil_tmp3 ; unsigned int __cil_tmp4 ; @@ -1044,7 +1044,7 @@ static int val_from_node(struct list_head *head ) } } #line 134 "test-0180.c" -static _Bool gl_sort_pass(void) +static _Bool gl_sort_pass(void) { _Bool any_change ; struct list_head *pos0 ; struct list_head *pos1 ; @@ -1113,7 +1113,7 @@ static _Bool gl_sort_pass(void) } } #line 156 "test-0180.c" -static void gl_sort(void) +static void gl_sort(void) { _Bool tmp ; { @@ -1139,7 +1139,7 @@ static void gl_sort(void) } } #line 162 "test-0180.c" -int main(void) +int main(void) { struct list_head const *__cil_tmp1 ; struct list_head const *__cil_tmp2 ; diff --git a/regression/termination/loops/eureka_01_false.c b/regression/termination/loops/eureka_01_false.c index 87e5d9f0e..61c73651c 100644 --- a/regression/termination/loops/eureka_01_false.c +++ b/regression/termination/loops/eureka_01_false.c @@ -18,7 +18,7 @@ void main(){ int distance[5]; int x,y; int i,j; - + for(i = 0; i < nodecount; i++){ if(i == source){ distance[i] = 0; diff --git a/regression/termination/loops/eureka_01_true.c b/regression/termination/loops/eureka_01_true.c index 2f67ad9fe..1d519eeda 100644 --- a/regression/termination/loops/eureka_01_true.c +++ b/regression/termination/loops/eureka_01_true.c @@ -16,7 +16,7 @@ int main(){ int distance[5]; int x,y; int i,j; - + for(i = 0; i < nodecount; i++){ if(i == source){ distance[i] = 0; diff --git a/regression/termination/loops/eureka_05_true.c b/regression/termination/loops/eureka_05_true.c index 5345b95f1..ec315384c 100644 --- a/regression/termination/loops/eureka_05_true.c +++ b/regression/termination/loops/eureka_05_true.c @@ -16,7 +16,7 @@ void SelectionSort() for (lh = 0; lh < n; lh++) { rh = lh; - for (i = lh + 1; i < n; i++) + for (i = lh + 1; i < n; i++) if (array[i] < array[rh]) rh = i; temp = array[lh]; array[lh] = array[rh]; diff --git a/regression/termination/loops/insertion_sort_false.c b/regression/termination/loops/insertion_sort_false.c index e11e4d208..8efc0cb0d 100644 --- a/regression/termination/loops/insertion_sort_false.c +++ b/regression/termination/loops/insertion_sort_false.c @@ -8,8 +8,8 @@ unsigned int __VERIFIER_nondet_uint(); int main() { unsigned int SIZE=__VERIFIER_nondet_uint(); int i, j, k, key; - int v[SIZE]; - for (j=1;j=0) && (v[i]>key)) { @@ -17,9 +17,9 @@ int main() { v[i+1] = v[i]; i = i - 1; } - v[i+1] = key; - } + v[i+1] = key; + } for (k=1;k=0) && (v[i]>key)) { v[i+1] = v[i]; i = i - 1; } - v[i+1] = key; - } + v[i+1] = key; + } for (k=1;k= 0; i--) { str2[j] = str1[0]; j++; diff --git a/regression/termination/loops/invert_string_true.c b/regression/termination/loops/invert_string_true.c index cb62c80f5..14d7e1864 100644 --- a/regression/termination/loops/invert_string_true.c +++ b/regression/termination/loops/invert_string_true.c @@ -18,7 +18,7 @@ int main() { str1[max-1]= '\0'; j = 0; - + for (i = max - 1; i >= 0; i--) { str2[j] = str1[i]; j++; @@ -28,6 +28,6 @@ int main() { for (i=0; imaior) - maior = matriz[j][k]; - } - + maior = matriz[j][k]; + } + for(j=0;j=maior) - maior = matriz[j][k]; - } - - __VERIFIER_assert(matriz[0][0]<=maior); + maior = matriz[j][k]; + } + + __VERIFIER_assert(matriz[0][0]<=maior); } diff --git a/regression/termination/loops/n.c11_true.c b/regression/termination/loops/n.c11_true.c index 90df007e5..7fa8a09e6 100644 --- a/regression/termination/loops/n.c11_true.c +++ b/regression/termination/loops/n.c11_true.c @@ -15,10 +15,10 @@ int main(){ while(__VERIFIER_nondet_bool()){ - + if (len==4) len =0; - + a[len]=0; len++; @@ -26,6 +26,6 @@ int main(){ __VERIFIER_assert(len>=0 && len<5); return 1; - + } diff --git a/regression/termination/loops/n.c24_true.c b/regression/termination/loops/n.c24_true.c index 6aa4889c4..8d5fb6f16 100644 --- a/regression/termination/loops/n.c24_true.c +++ b/regression/termination/loops/n.c24_true.c @@ -23,7 +23,7 @@ int main(){ for (i = 0; i < 1000; ++i) x[i]= __VERIFIER_nondet_int(); - + for (i= 0; i < 1000; ++i){ ret = __VERIFIER_nondet_int(); @@ -32,50 +32,50 @@ int main(){ tmp_cnt = __VERIFIER_nondet_int(); if (tmp_cnt < 0) return -1; - - + + for ( offset = 0; offset < tmp_cnt; offset++ ) { ret = foo(&tel_data ) ; if ( ( ret == 0 ) || ( ret == 1 ) ) { - + return 1 ; } else if ( ret == -1 ) { - + continue ; } - + for ( j = 0; x[j] != 0; j++ ) { - + if ( x[i] == 1) { - + memmove( &x[i], &x[i + 1], (1001) - ( i + 1 ) ) ; } } - + ret = bar( x) ; - + if ( ret != -1 ) { - + continue ; } - + klen = strlen(x ) ; - + if ( klen > 20 ) { - + x[i]=0; - + } else if ( klen > 0 ) { diff --git a/regression/termination/loops/n.c40_true.c b/regression/termination/loops/n.c40_true.c index 51401c6c1..482b5ba07 100644 --- a/regression/termination/loops/n.c40_true.c +++ b/regression/termination/loops/n.c40_true.c @@ -3,16 +3,16 @@ int __VERIFIER_nondet_int(); char x[100], y[100]; int i,j,k; -void main() { +void main() { k = __VERIFIER_nondet_int(); - + i = 0; while(x[i] != 0){ y[i] = x[i]; i++; } y[i] = 0; - + if(k >= 0 && k < i) if(y[k] == 0) {ERROR: goto ERROR;} diff --git a/regression/termination/loops/nec11_false.c b/regression/termination/loops/nec11_false.c index 839657cb3..cdc6710a6 100644 --- a/regression/termination/loops/nec11_false.c +++ b/regression/termination/loops/nec11_false.c @@ -15,10 +15,10 @@ int main(){ while(c){ - + if (len==4) len =0; - + a[len]=0; len++; @@ -26,5 +26,5 @@ int main(){ __VERIFIER_assert(len==5); return 1; - + } diff --git a/regression/termination/loops/nec20_false.c b/regression/termination/loops/nec20_false.c index 2558467f4..dacfb5f99 100644 --- a/regression/termination/loops/nec20_false.c +++ b/regression/termination/loops/nec20_false.c @@ -10,7 +10,7 @@ int main(){ _Bool k=__VERIFIER_nondet_bool(); int i,n,j; int a[1025]; - + if (k){ n=0; } else { @@ -32,7 +32,7 @@ int main(){ a[b]=1; else a[b%1023] =1; - + return 1; - + } diff --git a/regression/termination/loops/nec40_true.c b/regression/termination/loops/nec40_true.c index d113517f0..13f7d086e 100644 --- a/regression/termination/loops/nec40_true.c +++ b/regression/termination/loops/nec40_true.c @@ -9,16 +9,16 @@ int __VERIFIER_nondet_int(); char x[100], y[100]; int i,j,k; -void main() { +void main() { k = __VERIFIER_nondet_int(); - + i = 0; while(x[i] != 0){ y[i] = x[i]; i++; } y[i] = 0; - + if(k >= 0 && k < i) if(y[k] != 0) {__VERIFIER_assert(0);} diff --git a/regression/termination/loops/string_false.c b/regression/termination/loops/string_false.c index ec8a43f07..5e30e6305 100644 --- a/regression/termination/loops/string_false.c +++ b/regression/termination/loops/string_false.c @@ -14,14 +14,14 @@ main() { char string_A[MAX], string_B[MAX]; int i, j, nc_A, nc_B, found=0; - - + + for(i=0; i= nc_A); - - + + i=j=0; while((inc_B-1)<= nc_A); - - + + i=j=0; while((inc_B-1); - + __VERIFIER_assert(found == 0 || found == 1); } diff --git a/regression/termination/loops/sum01_bug02_false.c b/regression/termination/loops/sum01_bug02_false.c index 1f98f5e30..bcdd3b7a2 100644 --- a/regression/termination/loops/sum01_bug02_false.c +++ b/regression/termination/loops/sum01_bug02_false.c @@ -6,10 +6,10 @@ void __VERIFIER_assert(int cond) { } #define a (2) extern unsigned int __VERIFIER_nondet_uint(); -int main() { +int main() { int i, j=10, n=__VERIFIER_nondet_uint(), sn=0; for(i=1; i<=n; i++) { - if (i=100 || z<=100); } diff --git a/regression/termination/loops/terminator_03_false.c b/regression/termination/loops/terminator_03_false.c index 6554eb44b..3d44d6e58 100644 --- a/regression/termination/loops/terminator_03_false.c +++ b/regression/termination/loops/terminator_03_false.c @@ -13,12 +13,12 @@ main() if (y>0) { - while(x<100) + while(x<100) { x=x+y; } - } - __VERIFIER_assert(y<=0 || (y<0 && x>=100)); + } + __VERIFIER_assert(y<=0 || (y<0 && x>=100)); } diff --git a/regression/termination/loops/terminator_03_true.c b/regression/termination/loops/terminator_03_true.c index fcfa5948a..5f923a55b 100644 --- a/regression/termination/loops/terminator_03_true.c +++ b/regression/termination/loops/terminator_03_true.c @@ -14,13 +14,13 @@ main() if (y>0) { - while(x<100) + while(x<100) { x=x+y; } - } - - __VERIFIER_assert(y<=0 || (y>0 && x>=100)); + } + + __VERIFIER_assert(y<=0 || (y>0 && x>=100)); } diff --git a/regression/termination/loops/trex03_false.c b/regression/termination/loops/trex03_false.c index 3d540b049..bb1c139d4 100644 --- a/regression/termination/loops/trex03_false.c +++ b/regression/termination/loops/trex03_false.c @@ -14,7 +14,7 @@ int main() unsigned int x1=__VERIFIER_nondet_uint(), x2=__VERIFIER_nondet_uint(), x3=__VERIFIER_nondet_uint(); unsigned int d1=1, d2=1, d3=1; bool c1=__VERIFIER_nondet_bool(), c2=__VERIFIER_nondet_bool(); - + while(x1>0 && x2>0 && x3>0) { if (c1) x1=x1-d1; diff --git a/regression/termination/loops/trex03_true.c b/regression/termination/loops/trex03_true.c index ebdd7c14a..62bf91091 100644 --- a/regression/termination/loops/trex03_true.c +++ b/regression/termination/loops/trex03_true.c @@ -12,7 +12,7 @@ int main() unsigned int x1=__VERIFIER_nondet_uint(), x2=__VERIFIER_nondet_uint(), x3=__VERIFIER_nondet_uint(); unsigned int d1=1, d2=1, d3=1; _Bool c1=__VERIFIER_nondet_bool(), c2=__VERIFIER_nondet_bool(); - + while(x1>0 && x2>0 && x3>0) { if (c1) x1=x1-d1; diff --git a/regression/termination/loops/trex04_true.c b/regression/termination/loops/trex04_true.c index b0e1926e8..6dee2e188 100644 --- a/regression/termination/loops/trex04_true.c +++ b/regression/termination/loops/trex04_true.c @@ -29,7 +29,7 @@ int main() if (c1) foo(); if (c2) d = d - 1; - + while(x>0) { x=x-d; diff --git a/regression/termination/loops/veris.c_OpenSER__cases1_stripFullBoth_arr_true.c b/regression/termination/loops/veris.c_OpenSER__cases1_stripFullBoth_arr_true.c index 57033331f..e27f33c65 100644 --- a/regression/termination/loops/veris.c_OpenSER__cases1_stripFullBoth_arr_true.c +++ b/regression/termination/loops/veris.c_OpenSER__cases1_stripFullBoth_arr_true.c @@ -133,11 +133,11 @@ struct sockaddr_un -static int parse_expression_list(char *str) +static int parse_expression_list(char *str) { int start=0, i=-1, j=-1; char str2[EXPRESSION_LENGTH]; - + if (!str) return -1; do { @@ -177,7 +177,7 @@ static int parse_expression_list(char *str) start = i+1; } } while (str[i] != EOS); - + return 0; } diff --git a/regression/termination/loops/verisec_OpenSER__cases1_stripFullBoth_arr_false.c b/regression/termination/loops/verisec_OpenSER__cases1_stripFullBoth_arr_false.c index c1990a839..4609139d0 100644 --- a/regression/termination/loops/verisec_OpenSER__cases1_stripFullBoth_arr_false.c +++ b/regression/termination/loops/verisec_OpenSER__cases1_stripFullBoth_arr_false.c @@ -133,11 +133,11 @@ struct sockaddr_un -static int parse_expression_list(char *str) +static int parse_expression_list(char *str) { int start=0, i=-1, j=-1; char str2[EXPRESSION_LENGTH]; - + if (!str) return -1; do { @@ -175,7 +175,7 @@ static int parse_expression_list(char *str) start = i+1; } } while (str[i] != EOS); - + return 0; } diff --git a/regression/termination/loops/vogal_false.c b/regression/termination/loops/vogal_false.c index 006a9596f..6b8cfb03c 100644 --- a/regression/termination/loops/vogal_false.c +++ b/regression/termination/loops/vogal_false.c @@ -10,14 +10,14 @@ extern char __VERIFIER_nondet_char(); main(void) { - char string_entrada[MAX], vetor_vogais[]={'a','A','e','E','i','I','o','O','u','U','\0'};; + char string_entrada[MAX], vetor_vogais[]={'a','A','e','E','i','I','o','O','u','U','\0'};; unsigned int i,j,cont, tam_string, n_caracter; for(i=0;i0) { diff --git a/regression/termination/phase2/main.c b/regression/termination/phase2/main.c index 71187b9b3..069c660a0 100644 --- a/regression/termination/phase2/main.c +++ b/regression/termination/phase2/main.c @@ -1,8 +1,8 @@ void main() { - unsigned char x = 1; - unsigned char y = 1; - unsigned char w = 0; + unsigned char x = 1; + unsigned char y = 1; + unsigned char w = 0; while(x>0) //terminates after overflow of x // -w, -y, -x { diff --git a/regression/termination/phase3/main.c b/regression/termination/phase3/main.c index 0bba05b6d..67c31f21f 100644 --- a/regression/termination/phase3/main.c +++ b/regression/termination/phase3/main.c @@ -1,7 +1,7 @@ void main() { - unsigned char x = 1; - unsigned char y = 1; + unsigned char x = 1; + unsigned char y = 1; while(x>0) //does not terminate { diff --git a/regression/termination/phase3/test.desc b/regression/termination/phase3/test.desc index 982a29212..e8087cc8b 100644 --- a/regression/termination/phase3/test.desc +++ b/regression/termination/phase3/test.desc @@ -1,6 +1,9 @@ CORE main.c --octagons -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ +-- +-- +can be refuted with nontermination checker diff --git a/regression/termination/phase4/main.c b/regression/termination/phase4/main.c index 6f3a905d8..f80f66a97 100644 --- a/regression/termination/phase4/main.c +++ b/regression/termination/phase4/main.c @@ -1,7 +1,7 @@ void main() { - unsigned char x = 1; - unsigned char y = 1; + unsigned char x = 1; + unsigned char y = 1; while(x>0) //does not terminate { diff --git a/regression/termination/phase4/test.desc b/regression/termination/phase4/test.desc index 982a29212..e8087cc8b 100644 --- a/regression/termination/phase4/test.desc +++ b/regression/termination/phase4/test.desc @@ -1,6 +1,9 @@ CORE main.c --octagons -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ +-- +-- +can be refuted with nontermination checker diff --git a/regression/termination/phase5/main.c b/regression/termination/phase5/main.c index 603f6ff90..e74f5ff72 100644 --- a/regression/termination/phase5/main.c +++ b/regression/termination/phase5/main.c @@ -1,7 +1,7 @@ void main() { - int x = 1; - int y = 1; + int x = 1; + int y = 1; while(x>0) { diff --git a/regression/termination/phase6/main.c b/regression/termination/phase6/main.c index be3c469a8..938fe91f1 100644 --- a/regression/termination/phase6/main.c +++ b/regression/termination/phase6/main.c @@ -1,7 +1,7 @@ void main() { - int x = 1; - int y = 1; + int x = 1; + int y = 1; while(x>0) { diff --git a/regression/termination/phase7/debug.c b/regression/termination/phase7/debug.c index dab17127b..4c26f0e6c 100644 --- a/regression/termination/phase7/debug.c +++ b/regression/termination/phase7/debug.c @@ -20,11 +20,11 @@ void main() { /* __CPROVER_assume(y29 == 1 + yphi21); */ /* __CPROVER_assume(uphi29 == (cond27 && guard27 ? uphi21 : u28)); */ /* __CPROVER_assume(guard29 == (cond27 && guard27 || guard28)); */ - + // assert(!(guard21 && guardls30 && guard29 && cond30 && (-(long)ylb30 <= -(long)y29 || -(long)ulb30 < -(long)uphi29) && -(long)ulb30 <= -(long)uphi29)); //assert(!(guard21 && guardls30 && guard29 && cond30 && (-ylb30 <= -y29 || -ulb30 < -uphi29) && -ulb30 <= -uphi29)); - __CPROVER_bitvector[32] c0$1$0, c0$1$1, c0$1$2, c0$0$0, c0$0$1, c0$0$2; + __CPROVER_bitvector[32] c0$1$0, c0$1$1, c0$1$2, c0$0$0, c0$0$1, c0$0$2; /* assert(c0$1$0 * 4194304 + c0$1$1 * 2147483647 + c0$1$2 * -1656643584 > c0$1$0 * 4194303 + c0$1$1 * -2147483648 + c0$1$2 * -1656643583 || */ /* c0$0$0 * 4194304 + c0$0$1 * 2147483647 + c0$0$2 * -1656643584 > c0$0$0 * 4194303 + c0$0$1 * -2147483648 + c0$0$2 * -1656643583 && c0$1$0 * 4194304 + c0$1$1 * 2147483647 + c0$1$2 * -1656643584 >= c0$1$0 * 4194303 + c0$1$1 * -2147483648 + c0$1$2 * -1656643583); */ diff --git a/regression/termination/phase7/main.c b/regression/termination/phase7/main.c index 36e0c4de6..17f57a84b 100644 --- a/regression/termination/phase7/main.c +++ b/regression/termination/phase7/main.c @@ -2,8 +2,8 @@ void main() { - int x = 1; - int y = 1; + int x = 1; + int y = 1; int u = 0; while(x>0 && u < INT_MAX) //-u, -y, x diff --git a/regression/termination/phase7/main_verify.c b/regression/termination/phase7/main_verify.c index 0fdaf7e94..57a821083 100644 --- a/regression/termination/phase7/main_verify.c +++ b/regression/termination/phase7/main_verify.c @@ -2,8 +2,8 @@ void main() { - int x;// = 1; - int y;// = 1; + int x;// = 1; + int y;// = 1; int u;// = 0; assert(verify(x, y, u) != 0); @@ -34,8 +34,8 @@ int verify(int x, int y, int u) { //if(-u0 <= -u && (-u0 != -u || -y0 <= -y)) //if(-(long)u0+(long)x0 <= -(long)u+(long)x && (-(long)u0+(long)x0 != -(long)u+(long)x || ( -(long)u0-(long)x0 <= -(long)u-(long)x && (-(long)u0-(long)x0 != -(long)u-(long)x || (long)u0+(long)x0 <= (long)u+(long)x)))) //if((long)x0 <= (long)x && ((long)x0 != (long)x || -(long)u0-(long)x0 <= -(long)u-(long)x)) - // if(-(long)u0+(long)x0 <= -(long)u+(long)x && (-(long)u0+(long)x0 != -(long)u+(long)x || - // (-(long)u0-(long)x0 <= -(long)u-(long)x && (-(long)u0-(long)x0 != -(long)u-(long)x || + // if(-(long)u0+(long)x0 <= -(long)u+(long)x && (-(long)u0+(long)x0 != -(long)u+(long)x || + // (-(long)u0-(long)x0 <= -(long)u-(long)x && (-(long)u0-(long)x0 != -(long)u-(long)x || // (long)u0+(long)x0 <= (long)u+(long)x)))) return 0; } diff --git a/regression/termination/phase8/debug.c b/regression/termination/phase8/debug.c index dab17127b..4c26f0e6c 100644 --- a/regression/termination/phase8/debug.c +++ b/regression/termination/phase8/debug.c @@ -20,11 +20,11 @@ void main() { /* __CPROVER_assume(y29 == 1 + yphi21); */ /* __CPROVER_assume(uphi29 == (cond27 && guard27 ? uphi21 : u28)); */ /* __CPROVER_assume(guard29 == (cond27 && guard27 || guard28)); */ - + // assert(!(guard21 && guardls30 && guard29 && cond30 && (-(long)ylb30 <= -(long)y29 || -(long)ulb30 < -(long)uphi29) && -(long)ulb30 <= -(long)uphi29)); //assert(!(guard21 && guardls30 && guard29 && cond30 && (-ylb30 <= -y29 || -ulb30 < -uphi29) && -ulb30 <= -uphi29)); - __CPROVER_bitvector[32] c0$1$0, c0$1$1, c0$1$2, c0$0$0, c0$0$1, c0$0$2; + __CPROVER_bitvector[32] c0$1$0, c0$1$1, c0$1$2, c0$0$0, c0$0$1, c0$0$2; /* assert(c0$1$0 * 4194304 + c0$1$1 * 2147483647 + c0$1$2 * -1656643584 > c0$1$0 * 4194303 + c0$1$1 * -2147483648 + c0$1$2 * -1656643583 || */ /* c0$0$0 * 4194304 + c0$0$1 * 2147483647 + c0$0$2 * -1656643584 > c0$0$0 * 4194303 + c0$0$1 * -2147483648 + c0$0$2 * -1656643583 && c0$1$0 * 4194304 + c0$1$1 * 2147483647 + c0$1$2 * -1656643584 >= c0$1$0 * 4194303 + c0$1$1 * -2147483648 + c0$1$2 * -1656643583); */ diff --git a/regression/termination/phase8/main.c b/regression/termination/phase8/main.c index 26b7587d8..39fac0a35 100644 --- a/regression/termination/phase8/main.c +++ b/regression/termination/phase8/main.c @@ -2,11 +2,11 @@ void main() { - int x = 1; - int y = 1; + int x = 1; + int y = 1; int u = 0; int w = 0; - + while(x>0 && w0) { diff --git a/regression/termination/pointer1/main.c b/regression/termination/pointer1/main.c index 34c87bb31..da5aa119a 100644 --- a/regression/termination/pointer1/main.c +++ b/regression/termination/pointer1/main.c @@ -1,5 +1,5 @@ -void foo(int *x) -{ +void foo(int *x) +{ *x = 10; } diff --git a/regression/termination/pointer2/main.c b/regression/termination/pointer2/main.c index 02f54b869..318e03ee5 100644 --- a/regression/termination/pointer2/main.c +++ b/regression/termination/pointer2/main.c @@ -1,5 +1,5 @@ -void foo(int *x) -{ +void foo(int *x) +{ x++; *x = 10; } diff --git a/regression/termination/pointer2/test.desc b/regression/termination/pointer2/test.desc index 9ebb38e34..96f2f993d 100644 --- a/regression/termination/pointer2/test.desc +++ b/regression/termination/pointer2/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ +-- +-- +Needs fix for 6108ad3 diff --git a/regression/termination/precond_term1/test.desc b/regression/termination/precond_term1/test.desc index bd5d69778..25aca4d27 100644 --- a/regression/termination/precond_term1/test.desc +++ b/regression/termination/precond_term1/test.desc @@ -3,4 +3,4 @@ main.c --preconditions ^EXIT=5$ ^SIGNAL=0$ -\[_start\]: !(argc' <= 1 && -((signed __CPROVER_bitvector\[33\])argc') <= -1) +\[_start\]: !\(argc' <= 1 && -\(\(signed __CPROVER_bitvector\[33\]\)argc'\) <= -1\) diff --git a/regression/termination/precond_term4/main.c b/regression/termination/precond_term4/main.c index ff0e4f92e..70fbe40c6 100644 --- a/regression/termination/precond_term4/main.c +++ b/regression/termination/precond_term4/main.c @@ -16,9 +16,9 @@ signed long int full_write(signed int fd, const void *buf, unsigned long int len void main() { - signed int fd; + signed int fd; char buf[256]; - unsigned long int len; + unsigned long int len; signed long int cc; full_write(fd,buf,len,cc); } diff --git a/regression/termination/refinement1/main.c b/regression/termination/refinement1/main.c index 8b5cf8474..da4da16df 100644 --- a/regression/termination/refinement1/main.c +++ b/regression/termination/refinement1/main.c @@ -1,7 +1,7 @@ void main() { - int x = 10; - int y = 0; + int x = 10; + int y = 0; while(x>2*y) { diff --git a/regression/termination/refinement2/main.c b/regression/termination/refinement2/main.c index ccdcfb657..f4b7f0407 100644 --- a/regression/termination/refinement2/main.c +++ b/regression/termination/refinement2/main.c @@ -1,7 +1,7 @@ void main() { - int x = 10; - int y = 0; + int x = 10; + int y = 0; while(x>y) { diff --git a/regression/termination/refinement3/main.c b/regression/termination/refinement3/main.c index 8b5cf8474..da4da16df 100644 --- a/regression/termination/refinement3/main.c +++ b/regression/termination/refinement3/main.c @@ -1,7 +1,7 @@ void main() { - int x = 10; - int y = 0; + int x = 10; + int y = 0; while(x>2*y) { diff --git a/regression/termination/runall.sh b/regression/termination/runall.sh index dd85b93a6..14e5d0498 100755 --- a/regression/termination/runall.sh +++ b/regression/termination/runall.sh @@ -12,8 +12,8 @@ do do cd $d rm -f ../$d.$o.log - for f in *.c - do + for f in *.c + do echo $d/$f "using" $o echo "FILE:" $f >> ../$d.$o.log (time (perl -e 'alarm shift @ARGV; exec @ARGV' $TIMEOUT $SUMMARIZER $CHECKS $f --$o)) &>> ../$d.$o.log diff --git a/regression/termination/running1/main.c b/regression/termination/running1/main.c index 7345364ba..e4e9fcdac 100644 --- a/regression/termination/running1/main.c +++ b/regression/termination/running1/main.c @@ -1,12 +1,12 @@ -/* +/* C version of the lapack library http://www.netlib.org/clapack/cblas/sasum.c - + run with - + ../../../src/summarizer/summarizer main.c --termination --context-sensitive - + */ int nondet_int(); @@ -22,7 +22,7 @@ int main() { int n =nondet_int(), incx=nondet_int(); - + __CPROVER_assume(incx > 0 && incx < 1000); __CPROVER_assume(n < 1000 && n > 0); diff --git a/regression/termination/running2/main.c b/regression/termination/running2/main.c index cc4656778..6ec3b7437 100644 --- a/regression/termination/running2/main.c +++ b/regression/termination/running2/main.c @@ -1,12 +1,12 @@ -/* +/* C version of the lapack library http://www.netlib.org/clapack/cblas/sasum.c - + run with - + ../../../src/summarizer/summarizer main.c --termination --context-sensitive - + */ int nondet_int(); @@ -31,8 +31,8 @@ int g(int n, int incx) int main() { int n = nondet_int(), incx = nondet_int(); - + g(n,incx); - + return 0; } diff --git a/regression/termination/running2/test.desc b/regression/termination/running2/test.desc index e1b15b6c5..3d12f7f73 100644 --- a/regression/termination/running2/test.desc +++ b/regression/termination/running2/test.desc @@ -3,4 +3,4 @@ main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -\[g\]: !(n <= 999 && -((signed __CPROVER_bitvector\[33\])n) <= -1 && incx <= 0 && -((signed __CPROVER_bitvector\[33\])incx) <= 0) +\[g\]: !\(n <= 999 && -\(\(signed __CPROVER_bitvector\[33\]\)n\) <= -1 && incx <= 0 && -\(\(signed __CPROVER_bitvector\[33\]\)incx\) <= 0\) diff --git a/regression/termination/running3/main.c b/regression/termination/running3/main.c index 5fba32973..85b6c59cb 100644 --- a/regression/termination/running3/main.c +++ b/regression/termination/running3/main.c @@ -1,32 +1,32 @@ -/* +/* C version of the lapack library http://www.netlib.org/clapack/cblas/sasum.c - + run with - + ../../../src/summarizer/summarizer main.c --termination --context-sensitive - + */ int nondet_int(); -int foo(int *sx, int n, int incx) -{ +int foo(int *sx, int n, int incx) +{ int nincx = n * incx; int stemp=0; int i; - for (i=0; incx < 0 ? i >= nincx : i <= nincx; i += incx) - { + for (i=0; incx < 0 ? i >= nincx : i <= nincx; i += incx) + { stemp += sx[i-1]; } return stemp; } -int bar(int n, int incx) +int bar(int n, int incx) { int sx[n]; - if(100 <= n && n <= 10000 && 0 < incx && incx <= 1000) + if(100 <= n && n <= 10000 && 0 < incx && incx <= 1000) { int stemp = foo(&sx, n, incx); } @@ -35,6 +35,6 @@ int bar(int n, int incx) int main() { - int n = nondet_int(), incx = nondet_int(); + int n = nondet_int(), incx = nondet_int(); bar(n,incx); } diff --git a/regression/termination/running4/main.c b/regression/termination/running4/main.c index ffb76a47c..2bd727165 100644 --- a/regression/termination/running4/main.c +++ b/regression/termination/running4/main.c @@ -1,32 +1,32 @@ -/* +/* C version of the lapack library http://www.netlib.org/clapack/cblas/sasum.c - + run with - + ../../../src/summarizer/summarizer main.c --termination --context-sensitive - + */ int nondet_int(); -int foo(int *sx, int n, int incx) -{ +int foo(int *sx, int n, int incx) +{ int nincx = n * incx; int stemp=0; int i; - for (i=0; incx < 0 ? i >= nincx : i <= nincx; i += incx) - { + for (i=0; incx < 0 ? i >= nincx : i <= nincx; i += incx) + { stemp += sx[i-1]; } return stemp; } -int bar(int n, int incx) +int bar(int n, int incx) { int sx[n]; - if(100 <= n && n <= 10000 && 0 <= incx && incx <= 1000) + if(100 <= n && n <= 10000 && 0 <= incx && incx <= 1000) { int stemp = foo(&sx, n, incx); } @@ -35,6 +35,6 @@ int bar(int n, int incx) int main() { - int n = nondet_int(), incx = nondet_int(); + int n = nondet_int(), incx = nondet_int(); bar(n,incx); } diff --git a/regression/termination/running4/test.desc b/regression/termination/running4/test.desc index 619557518..e31982902 100644 --- a/regression/termination/running4/test.desc +++ b/regression/termination/running4/test.desc @@ -1,6 +1,9 @@ -CORE +KNOWNBUG main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -\[bar\]: !(n <= 10000 && -((signed __CPROVER_bitvector\[33\])n) <= -100 && incx <= 0 && -((signed __CPROVER_bitvector\[33\])incx) <= 0) +\[bar\]: !\(n <= 10000 && -\(\(signed __CPROVER_bitvector\[33\]\)n\) <= -100 && incx <= 0 && -\(\(signed __CPROVER_bitvector\[33\]\)incx\) <= 0\) +-- +-- +result is brittle and depends on bounds on the search and the SAT solver used diff --git a/regression/termination/simple1/main.c b/regression/termination/simple1/main.c index 8a95214f7..0b9154bec 100644 --- a/regression/termination/simple1/main.c +++ b/regression/termination/simple1/main.c @@ -1,10 +1,10 @@ -int foo() -{ +int foo() +{ return 1; } -int bar() -{ - return 2; +int bar() +{ + return 2; } void main() diff --git a/regression/termination/simple2/main.c b/regression/termination/simple2/main.c index e1f62b82d..984b32921 100644 --- a/regression/termination/simple2/main.c +++ b/regression/termination/simple2/main.c @@ -1,5 +1,5 @@ -int foo(int x) -{ +int foo(int x) +{ if(x) return 9; return 10; } diff --git a/regression/termination/sum1/main.c b/regression/termination/sum1/main.c index df354feeb..c6f4ab383 100644 --- a/regression/termination/sum1/main.c +++ b/regression/termination/sum1/main.c @@ -1,27 +1,27 @@ -#include - -int max(int x, int y) -{ - if(x>y) return x; - return y; -} - -int inv(int x) -{ - __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes - return -x; -} - -void main() -{ - int x; - __CPROVER_assume(2<=x && x<=3); - - int y=inv(x); - int z=max(y,0); - - assert(y<=-2); - assert(y==-x); - assert(z>=0); - assert(z>=y); -} +#include + +int max(int x, int y) +{ + if(x>y) return x; + return y; +} + +int inv(int x) +{ + __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes + return -x; +} + +void main() +{ + int x; + __CPROVER_assume(2<=x && x<=3); + + int y=inv(x); + int z=max(y,0); + + assert(y<=-2); + assert(y==-x); + assert(z>=0); + assert(z>=y); +} diff --git a/regression/test.pl b/regression/test.pl index 62779cb12..8cd558fea 100755 --- a/regression/test.pl +++ b/regression/test.pl @@ -3,17 +3,29 @@ use subs; use strict; use warnings; +use File::Basename; + +use Cwd; + +my $has_thread_pool = eval +{ + # "http://www.cpan.org/authors/id/J/JW/JWU/Thread-Pool-Simple/Thread-Pool-Simple-0.25.tar.gz" + # Debian/Ubuntu: libthread-pool-simple-perl + require Thread::Pool::Simple; + Thread::Pool::Simple->import(); + 1; +}; # test.pl # # runs a test and check its output -sub run($$$$) { - my ($input, $cmd, $options, $output) = @_; - my $cmdline = "$cmd $options $input >$output 2>&1"; +sub run($$$$$) { + my ($name, $input, $cmd, $options, $output) = @_; + my $cmdline = "$cmd $options '$input' >'$output' 2>&1"; print LOG "Running $cmdline\n"; - system $cmdline; + system("bash", "-c", "cd '$name' ; $cmdline"); my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; @@ -24,15 +36,21 @@ ($$$$) print LOG " Core: $dumped_core\n"; if($signal_num != 0) { - $failed = 1; print "Killed by signal $signal_num"; if($dumped_core) { print " (code dumped)"; } } - system "echo EXIT=$exit_value >>$output"; - system "echo SIGNAL=$signal_num >>$output"; + open my $FH, ">>$name/$output"; + print $FH "EXIT=$exit_value\n"; + print $FH "SIGNAL=$signal_num\n"; + close $FH; + + if($signal_num == 2) { + print "\nProgram under test interrupted; stopping\n"; + exit 1; + } return $failed; } @@ -48,13 +66,34 @@ ($) return @data; } -sub test($$$$$) { - my ($name, $test, $t_level, $cmd, $ign) = @_; - my ($level, $input, $options, @results) = load($test); +sub test($$$$$$$) { + my ($name, $test, $t_level, $cmd, $ign, $dry_run, $defines) = @_; + my ($level, $input, $options, $grep_options, @results) = load("$test"); + my @keys = keys %{$defines}; + foreach my $key (@keys) { + my $value = $defines->{$key}; + $options =~ s/(\$$key$|\$$key )/$value /g; + } + if (scalar @keys) { + foreach my $word (split(/\s/, $options)) { + if ((substr($word, 0, 1) cmp '$') == 0) { + print "$name: variable \"$word\" not replaced; consider passing \"-D$word\"=..."; + } + } + } + + # If the 4th line is activate-multi-line-match we enable multi-line checks + if($grep_options ne "activate-multi-line-match") { + # No such flag, so we add it back in + unshift @results, $grep_options; + $grep_options = ""; + } + $options =~ s/$ign//g if(defined($ign)); - my $output = $input; - $output =~ s/\.(c|o|gb|xml)$/.out/; + my $descriptor = basename($test); + my $output = $descriptor; + $output =~ s/\.[^.]*$/.out/; if($output eq $input) { print("Error in test file -- $test\n"); @@ -64,6 +103,7 @@ ($$$$$) print LOG "Test '$name'\n"; print LOG " Level: $level\n"; print LOG " Input: $input\n"; + print LOG " Descriptor: $descriptor\n"; print LOG " Output: $output\n"; print LOG " Options: $options\n"; print LOG " Results:\n"; @@ -83,9 +123,14 @@ ($$$$$) die "Level must be one of CORE, THOROUGH, FUTURE or KNOWNBUG\n"; } - my $failed = -1; + my $failed = 2; if($level & $t_level) { - $failed = run($input, $cmd, $options, $output); + + if ($dry_run) { + return 0; + } + + $failed = run($name, $input, $cmd, $options, $output); if(!$failed) { print LOG "Execution [OK]\n"; @@ -96,8 +141,45 @@ ($$$$$) $included--; } else { my $r; - system "grep \"$result\" '$output' >/dev/null"; - $r = ($included ? $? != 0 : $? == 0); + + my $dir = getcwd(); + my $abs_path = "$dir/$name/$output"; + open(my $fh => $abs_path) || die "Cannot open '$name/$output': $!"; + + # Multi line therefore we run each check across the whole output + if($grep_options eq "activate-multi-line-match") { + local $/ = undef; + binmode $fh; + my $whole_file = <$fh>; + $whole_file =~ s/\r\n/\n/g; + my $is_match = $whole_file =~ /$result/; + $r = ($included ? !$is_match : $is_match); + } + else + { + my $found_line = 0; + while(my $line = <$fh>) { + $line =~ s/\r$//; + if($line =~ /$result/) { + # We've found the line, therefore if it is included we set + # the result to 0 (OK) If it is excluded, we set the result + # to 1 (FAILED) + $r = !$included; + $found_line = 1; + last; + } + } + + if($found_line == 0) { + # None of the lines matched, therefore the result is set to + # 0 (OK) if and only if the line was not meant to be included + $r = $included; + } + + } + close($fh); + + if($r) { print LOG "$result [FAILED]\n"; $failed = 1; @@ -111,18 +193,22 @@ ($$$$$) } } else { print LOG "Execution [SKIPPED]\n"; + return 2; } print LOG "\n"; - return $failed; + # if the test is a KNOWNBUG then the test should fail + my $should_fail = $level eq 8; + + return ($should_fail != $failed); } sub dirs() { my @list; opendir CWD, "."; - @list = grep { !/^\./ && -d "$_" && !/CVS/ && -s "$_/test.desc" } readdir CWD; + @list = grep { !/^\./ && -d "$_" && !/CVS/ } readdir CWD; closedir CWD; @list = sort @list; @@ -148,11 +234,16 @@ ($$$$) -c CMD run tests on CMD - required option -i options in test.desc matching the specified perl regex are ignored + -j run tests in parallel (requires Thread::Pool::Simple) + -n dry-run: print the tests that would be run, but don't actually run them + -p print logs of each failed test (if any) -h show this help and exit -C core: run all essential tests (default if none of C/T/F/K are given) -T thorough: run expensive tests -F future: run checks for future features -K known: run tests associated with known bugs + -D Define - replace \$key string with "value" string in + test descriptors test.pl expects a test.desc file in each subdirectory. The file test.desc @@ -161,6 +252,7 @@ ($$$$)
+ -- @@ -168,61 +260,106 @@ ($$$$) where - is one of CORE, THOROUGH, FUTURE or KNOWNBUG -
is a file with extension .c/.i/.cpp/.ii/.xml/.class - additional options to be passed to CMD - one or more lines of regualar expressions that must occur in the output - one or more lines of expressions that must not occur in output - free form text + is one of CORE, THOROUGH, FUTURE or KNOWNBUG +
is a file with extension .c/.i/.gb/.cpp/.ii/.xml/.class/.jar + additional options to be passed to CMD + The fourth line can optionally be activate-multi-line-match, if this is the + case then each of the rules will be matched over multiple lines (so can contain) + the new line symbol (\\n) inside them. + one or more lines of regualar expressions that must occur in the output + one or more lines of expressions that must not occur in output + free form text EOF exit 1; } use Getopt::Std; +use Getopt::Long qw(:config pass_through bundling); $main::VERSION = 0.1; $Getopt::Std::STANDARD_HELP_VERSION = 1; -our ($opt_c, $opt_i, $opt_h, $opt_C, $opt_T, $opt_F, $opt_K); # the variables for getopt -getopts('c:i:hCTFK') or &main::HELP_MESSAGE(\*STDOUT, "", $main::VERSION, ""); +our ($opt_c, $opt_i, $opt_j, $opt_n, $opt_p, $opt_h, $opt_C, $opt_T, $opt_F, $opt_K, %defines); # the variables for getopt +$opt_j = 0; +GetOptions("D=s", \%defines); +getopts('c:i:j:nphCTFK') or &main::HELP_MESSAGE(\*STDOUT, "", $main::VERSION, ""); $opt_c or &main::HELP_MESSAGE(\*STDOUT, "", $main::VERSION, ""); +(!$opt_j || $has_thread_pool) or &main::HELP_MESSAGE(\*STDOUT, "", $main::VERSION, ""); $opt_h and &main::HELP_MESSAGE(\*STDOUT, "", $main::VERSION, ""); my $t_level = 0; $t_level += 2 if($opt_T); $t_level += 4 if($opt_F); $t_level += 8 if($opt_K); $t_level += 1 if($opt_C || 0 == $t_level); - +my $dry_run = $opt_n; open LOG,">tests.log"; print "Loading\n"; my @tests = @ARGV != 0 ? @ARGV : dirs(); -my $count = @tests; +my $count = 0; +for (@tests){ + my @testfiles = glob "$_/*desc"; + $count += $#testfiles+1; +} print " $count " . (1==$count?"test":"tests") . " found\n\n"; use Cwd qw(getcwd); -my $failures = 0; -my $skips = 0; +my $cwd = getcwd; +my $failures :shared = 0; +my $skips :shared = 0; +my $pool :shared = undef; + +sub do_test($) +{ + my ($test) = @_; + my $failed_skipped = 0; + my @files = glob "$test/*.desc"; + for (0..$#files){ + defined($pool) or print " Running $files[$_]"; + my $start_time = time(); + $failed_skipped = test( + $test, $files[$_], $t_level, $opt_c, $opt_i, $dry_run, \%defines); + my $runtime = time() - $start_time; + + lock($skips); + defined($pool) and print " Running $test $files[$_]"; + if(2 == $failed_skipped) { + $skips++; + print " [SKIPPED]\n"; + } elsif(0 == $failed_skipped) { + print " [OK] in $runtime seconds\n"; + } else { + $failures++; + print " [FAILED]\n"; + } + } +} + +if($opt_j>1 && $has_thread_pool && $count>1) +{ + $pool = Thread::Pool::Simple->new( + min => 2, + max => $opt_j, + load => 3, + do => [\&do_test] + ); +} + print "Running tests\n"; foreach my $test (@tests) { - print " Running $test"; - - my $cwd = getcwd; - chdir $test; - my $failed_skipped = test($test, "test.desc", $t_level, $opt_c, $opt_i); - chdir $cwd; - - if($failed_skipped < 0) { - $skips++; - print " [SKIPPED]\n"; - } elsif(0 == $failed_skipped) { - print " [OK]\n"; - } else { - $failures++; - print " [FAILED]\n"; + if(defined($pool)) + { + $pool->add($test); + } + else + { + do_test($test); } } + +defined($pool) and $pool->join(); + print "\n"; if($failures == 0) { @@ -237,5 +374,41 @@ ($$$$) close LOG; -exit $failures; +if($opt_p && $failures != 0) { + open LOG,") { + chomp $line; + if ($line =~ /^Test '(.+)'/) { + $current_test = $1; + $printed_this_test = 0; + } elsif ($line =~ /Descriptor:\s+([^\s]+)/) { + $descriptor_file = $1; + } elsif ($line =~ /Output:\s+([^\s]+)/) { + $output_file = $1; + } elsif ($line =~ /\[FAILED\]\s*$/) { + # print a descriptive header before dumping the test.desc lines that + # actually weren't matched (and print this header just once) + if(0 == $printed_this_test) { + $printed_this_test = 1; + print "\n\n"; + print "Failed test: $current_test\n"; + open FH, "<$current_test/$output_file"; + while (my $f = ) { + print $f; + } + close FH; + print "\n\nFailed $descriptor_file lines:\n"; + } + print "$line\n"; + } + } +} + +exit $failures; diff --git a/scripts/cpplint.py b/scripts/cpplint.py index 57d9025d7..0d738f589 100644 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -4738,27 +4738,8 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, 'If parameters or arguments require a line break, each parameter should be put on its own line.') # Skip to the end of the bracket start_linenum = nested_close_linenum - else: - if(not Match('^\s*[^,]+,$', arg_line)): - error(filename, start_linenum, 'whitespace/indent', 4, - 'If parameters or arguments require a line break, each parameter should be put on its own line.') start_linenum+=1 - # For the final line we also need to check one parameter on it - # e.g. we require bracket on same line as last parameter - # foo( - # x); - if not Search(r'^\s*[^,]+\)', close_line): - # If this is true, the we failed because we just had the close bracket - if Search(r'[^,]*\)', close_line): - error(filename, close_linenum, 'whitespace/indent', 4, - 'If parameters or arguments require a line break, the closing bracket should be on the same line as the final parameter') - else: - # In this case the problem is we had a bracket - # i.e. more than one parameter on the last line - error(filename, close_linenum, 'whitespace/indent', 4, - 'If parameters or arguments require a line break, each parameter should be put on its own line.') - if (Search(r'\([^\)]*$', elided_prev) and initial_spaces-2 != prev_initial_spaces) and not Search(r'for|while|if|;', elided_prev): error(filename, linenum, 'whitespace/indent', 4, diff --git a/src/2ls/2ls_parse_options.cpp b/src/2ls/2ls_parse_options.cpp index b1363d104..b45bcfeb6 100644 --- a/src/2ls/2ls_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -46,15 +46,17 @@ Author: Daniel Kroening, Peter Schrammel #include "graphml_witness_ext.h" #include +#include #include "2ls_parse_options.h" #include "summary_checker_ai.h" #include "summary_checker_bmc.h" #include "summary_checker_kind.h" +#include "summary_checker_nonterm.h" #include "show.h" #include "horn_encoding.h" -#define UNWIND_GOTO_INTO_LOOP 1 +#define UNWIND_GOTO_INTO_LOOP 0 #define REMOVE_MULTIPLE_DEREFERENCES 1 #define IGNORE_RECURSION 1 #define IGNORE_THREADS 1 @@ -74,11 +76,12 @@ Function: twols_parse_optionst::twols_parse_optionst \*******************************************************************/ twols_parse_optionst::twols_parse_optionst(int argc, const char **argv): -parse_options_baset(TWOLS_OPTIONS, argc, argv), -language_uit(cmdline, ui_message_handler), + parse_options_baset(TWOLS_OPTIONS, argc, argv), + language_uit(cmdline, ui_message_handler), ui_message_handler(cmdline, "2LS " TWOLS_VERSION), recursion_detected(false), - threads_detected(false) + threads_detected(false), + dynamic_memory_detected(false) { } @@ -181,10 +184,19 @@ void twols_parse_optionst::get_command_line_options(optionst &options) else options.set_option("std-invariants", false); + if(cmdline.isset("no-propagation")) + options.set_option("constant-propagation", false); + else + options.set_option("constant-propagation", true); + // magic error label if(cmdline.isset("error-label")) options.set_option("error-label", cmdline.get_value("error-label")); + // max disjunct + if (cmdline.isset("disjunct-limit")) + options.set_option("disjunct-limit", cmdline.get_value("disjunct-limit")); + if(cmdline.isset("havoc")) options.set_option("havoc", true); else if(cmdline.isset("equalities")) @@ -192,6 +204,54 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("equalities", true); options.set_option("std-invariants", true); } + else if(cmdline.isset("heap")) + { + options.set_option("heap", true); + } + else if(cmdline.isset("heap-interval")) + { + options.set_option("heap-interval", true); + if(cmdline.isset("sympath")) + options.set_option("sympath", true); + } + else if(cmdline.isset("heap-zones")) + { + options.set_option("heap-zones", true); + if(cmdline.isset("sympath")) + options.set_option("sympath", true); + } + else if(cmdline.isset("heap-values-refine")) + { + options.set_option("heap-values-refine", true); + options.set_option("heap-interval", true); + if(cmdline.isset("sympath")) + options.set_option("sympath", true); + } + // disjunctive domains only work with inlined code for now + else if(cmdline.isset("disjunctive-intervals")) + { + options.set_option("disjunctive-intervals", true); + options.set_option("disjunctive_domains", true); + options.set_option("inline", true); + if (!cmdline.isset("disjunct-limit")) + options.set_option("disjunct-limit", 2); + } + else if(cmdline.isset("disjunctive-zones")) + { + options.set_option("disjunctive-zones", true); + options.set_option("disjunctive_domains", true); + options.set_option("inline", true); + if (!cmdline.isset("disjunct-limit")) + options.set_option("disjunct-limit", 2); + } + else if(cmdline.isset("disjunctive-octagons")) + { + options.set_option("disjunctive-octagons", true); + options.set_option("disjunctive_domains", true); + options.set_option("inline", true); + if (!cmdline.isset("disjunct-limit")) + options.set_option("disjunct-limit", 2); + } else { if(cmdline.isset("zones")) @@ -263,7 +323,17 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("k-induction", true); options.set_option("inline", true); if(!cmdline.isset("unwind")) - options.set_option("unwind", UINT_MAX); + options.set_option("unwind", std::numeric_limits::max()); + } + + // compute singleton recurrence set - simple nontermination + if(cmdline.isset("nontermination")) + { + options.set_option("nontermination", true); + options.set_option("all-properties", true); + options.set_option("inline", true); + if(!cmdline.isset("unwind")) + options.set_option("unwind", std::numeric_limits::max()); } // do incremental bmc @@ -273,7 +343,7 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("inline", true); options.set_option("havoc", true); if(!cmdline.isset("unwind")) - options.set_option("unwind", UINT_MAX); + options.set_option("unwind", std::numeric_limits::max()); } // check for spuriousness of assertion failures @@ -300,6 +370,7 @@ void twols_parse_optionst::get_command_line_options(optionst &options) options.set_option("competition-mode", true); options.set_option("all-properties", false); options.set_option("inline", true); + options.set_option("give-up-invariants", "2"); } // instrumentation / output @@ -321,8 +392,8 @@ void twols_parse_optionst::get_command_line_options(optionst &options) } #endif - if(cmdline.isset("show-trace")) - options.set_option("show-trace", true); + if(cmdline.isset("trace")) + options.set_option("trace", true); if(cmdline.isset("graphml-witness")) options.set_option("graphml-witness", cmdline.get_value("graphml-witness")); if(cmdline.isset("json-cex")) @@ -371,6 +442,16 @@ int twols_parse_optionst::doit() if(get_goto_program(options, goto_model)) return 6; + const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if((options.get_bool_option("heap") || + options.get_bool_option("heap-interval")) && + !options.get_bool_option("inline")) + { + heap_analysis(goto_model.goto_functions); + add_dynamic_object_symbols(heap_analysis, goto_model); + } + if(cmdline.isset("show-stats")) { show_stats(goto_model, std::cout); @@ -383,7 +464,13 @@ int twols_parse_optionst::doit() { bool simplify=!cmdline.isset("no-simplify"); irep_idt function=cmdline.get_value("function"); - show_ssa(goto_model, function, simplify, std::cout, ui_message_handler); + show_ssa( + goto_model, + heap_analysis, + function, + simplify, + std::cout, + ui_message_handler); return 7; } @@ -420,6 +507,20 @@ int twols_parse_optionst::doit() options.set_option("show-invariants", true); } + if(cmdline.isset("nontermination")) + { + // turn assertions (from generic checks) into assumptions + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &body=f_it->second.body; + Forall_goto_program_instructions(i_it, body) + { + if(i_it->is_assert()) + i_it->type=goto_program_instruction_typet::ASSUME; + } + } + } + #if IGNORE_RECURSION if(recursion_detected) { @@ -456,6 +557,18 @@ int twols_parse_optionst::doit() status() << "Havocking loops and function calls" << eom; else if(options.get_bool_option("equalities")) status() << "Using (dis)equalities domain" << eom; + else if(options.get_bool_option("heap")) + status() << "Using heap domain" << eom; + else if(options.get_bool_option("heap-interval")) + status() << "Using heap domain with interval domain for values" << eom; + else if(options.get_bool_option("heap-zones")) + status() << "Using heap domain with zones domain for values" << eom; + else if(options.get_bool_option("disjunctive-intervals")) + status() << "Using disjunctive intervals domain" << eom; + else if(options.get_bool_option("disjunctive-zones")) + status() << "Using disjunctive zones domain" << eom; + else if(options.get_bool_option("disjunctive-octagons")) + status() << "Using disjunctive octagons domain" << eom; else { if(options.get_bool_option("intervals")) @@ -477,21 +590,46 @@ int twols_parse_optionst::doit() status() << eom; } + // don't use k-induction with dynamic memory + if(options.get_bool_option("competition-mode") && + options.get_bool_option("k-induction") && + dynamic_memory_detected) + { + options.set_option("k-induction", false); + options.set_option("std-invariants", false); + options.set_option("incremental-bmc", false); + options.set_option("unwind", 0); + } + // don't do nontermination with dynamic memory + if(options.get_bool_option("competition-mode") && + (options.get_bool_option("termination") || + options.get_bool_option("nontermination")) && + dynamic_memory_detected) + { + error() << "Termination analysis does not support " + << "dynamic memory allocation" << eom; + report_unknown(); + return 5; + } + try { std::unique_ptr checker; if(!options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) checker=std::unique_ptr( - new summary_checker_ait(options)); + new summary_checker_ait(options, heap_analysis)); if(options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) checker=std::unique_ptr( - new summary_checker_kindt(options)); + new summary_checker_kindt(options, heap_analysis)); if(!options.get_bool_option("k-induction") && options.get_bool_option("incremental-bmc")) checker=std::unique_ptr( - new summary_checker_bmct(options)); + new summary_checker_bmct(options, heap_analysis)); + if(options.get_bool_option("nontermination")) + checker=std::unique_ptr( + new summary_checker_nontermt(options, heap_analysis)); checker->set_message_handler(get_message_handler()); checker->simplify=!cmdline.isset("no-simplify"); @@ -540,7 +678,8 @@ int twols_parse_optionst::doit() bool report_assertions= !options.get_bool_option("preconditions") && - !options.get_bool_option("termination"); + !options.get_bool_option("termination") && + !options.get_bool_option("nontermination"); // do actual analysis switch((*checker)(goto_model)) { @@ -548,23 +687,50 @@ int twols_parse_optionst::doit() if(report_assertions) report_properties(options, goto_model, checker->property_map); report_success(); - if(cmdline.isset("graphml-witness") && - !options.get_bool_option("termination")) + if(cmdline.isset("graphml-witness")) output_graphml_proof(options, goto_model, *checker); retval=0; break; case property_checkert::FAIL: + { if(report_assertions) report_properties(options, goto_model, checker->property_map); - report_failure(); + + // validate trace + bool trace_valid=false; + for(const auto &p : checker->property_map) + { + if(p.second.result!=property_checkert::FAIL) + continue; + + if(options.get_bool_option("trace")) + show_counterexample(goto_model, p.second.error_trace); + + trace_valid= + !p.second.error_trace.steps.empty() && + (options.get_bool_option("nontermination") || + p.second.error_trace.steps.back().is_assert()); + break; + } + if(cmdline.isset("graphml-witness")) { +#if 1 + if(!trace_valid) + { + retval=5; + error() << "Internal witness validation failed" << eom; + report_unknown(); + break; + } +#endif output_graphml_cex(options, goto_model, *checker); } + report_failure(); retval=10; break; - + } case property_checkert::UNKNOWN: if(report_assertions) report_properties(options, goto_model, checker->property_map); @@ -745,8 +911,8 @@ void twols_parse_optionst::show_stats( const code_assignt &assign=to_code_assign(instruction.code); expr_stats_rec(assign.lhs(), stats); expr_stats_rec(assign.rhs(), stats); + break; } - break; case ASSUME: expr_stats_rec(instruction.guard, stats); break; @@ -840,7 +1006,7 @@ void twols_parse_optionst::require_entry( if(goto_model.symbol_table.symbols.find(entry_point)== symbol_table.symbols.end()) - throw "The program has no entry point; please complete linking"; + throw "the program has no entry point; please complete linking"; } /*******************************************************************\ @@ -1015,7 +1181,8 @@ bool twols_parse_optionst::process_goto_program( { status() << "Function Pointer Removal" << eom; remove_function_pointers( - goto_model, cmdline.isset("pointer-check")); + goto_model, + cmdline.isset("pointer-check")); // do partial inlining if(options.get_bool_option("inline-partial")) @@ -1047,7 +1214,9 @@ bool twols_parse_optionst::process_goto_program( status() << "Performing full inlining" << eom; const namespacet ns(goto_model.symbol_table); goto_inlinet goto_inline( - goto_model.goto_functions, ns, ui_message_handler); + goto_model.goto_functions, + ns, + ui_message_handler); goto_inline(); #if IGNORE_RECURSION recursion_detected=goto_inline.recursion_detected(); @@ -1064,6 +1233,13 @@ bool twols_parse_optionst::process_goto_program( #if UNWIND_GOTO_INTO_LOOP unwind_goto_into_loop(goto_model, 2); +#else + if(unwind_goto_into_loop(goto_model, 2)) + { + status() << "Irreducible control flow not supported" << eom; + report_unknown(); + return 5; + } #endif remove_skip(goto_model.goto_functions); @@ -1078,8 +1254,26 @@ bool twols_parse_optionst::process_goto_program( #endif #if 1 + // Find, inline and remove malloc function // TODO: find a better place for that - replace_malloc(goto_model, ""); + Forall_goto_functions(it, goto_model.goto_functions) + { + if(it->first=="malloc" || it->first=="free") + it->second.type.set(ID_C_inlined, true); + } + goto_partial_inline(goto_model, ui_message_handler, 0); + Forall_goto_functions(it, goto_model.goto_functions) + { + if(it->first=="malloc" || it->first=="free") + it->second.body.clear(); + } +#endif + + // create symbols for objects pointed by parameters + create_dynamic_objects(goto_model); + +#if REMOVE_MULTIPLE_DEREFERENCES + remove_multiple_dereferences(goto_model); #endif // recalculate numbers, etc. @@ -1088,12 +1282,31 @@ bool twols_parse_optionst::process_goto_program( // add loop ids goto_model.goto_functions.compute_loop_numbers(); + // Replace malloc + dynamic_memory_detected=replace_malloc( + goto_model, "", options.get_bool_option("pointer-check")); + + // Allow recording of mallocs and memory leaks + if(options.get_bool_option("pointer-check")) + { + allow_record_malloc(goto_model); + } + if(options.get_bool_option("memory-leak-check")) + allow_record_memleak(goto_model); + + split_same_symbolic_object_assignments(goto_model); + + // remove loop heads from function entries + remove_loops_in_entry(goto_model); + // inline __CPROVER_initialize and main if(cmdline.isset("inline-main")) { inline_main(goto_model); } + auto dynobj_instances=split_dynamic_objects(goto_model); + if(!cmdline.isset("independent-properties")) { add_assumptions_after_assertions(goto_model); @@ -1103,12 +1316,14 @@ bool twols_parse_optionst::process_goto_program( filter_assertions(goto_model); #endif - if(!cmdline.isset("no-propagation")) + if(options.get_bool_option("constant-propagation")) { status() << "Constant Propagation" << eom; propagate_constants(goto_model); } + remove_dead_goto(goto_model); + // if we aim to cover, replace // all assertions by false to prevent simplification if(cmdline.isset("cover-assertions")) @@ -1204,7 +1419,8 @@ void twols_parse_optionst::report_properties( xmlt xml_result("result"); xml_result.set_attribute("property", id2string(it->first)); xml_result.set_attribute( - "status", property_checkert::as_string(it->second.result)); + "status", + property_checkert::as_string(it->second.result)); std::cout << xml_result << "\n"; } else @@ -1216,7 +1432,7 @@ void twols_parse_optionst::report_properties( << eom; } - if(cmdline.isset("show-trace") && + if(options.get_bool_option("trace") && it->second.result==property_checkert::FAIL) show_counterexample(goto_model, it->second.error_trace); if(cmdline.isset("json-cex") && @@ -1282,8 +1498,8 @@ void twols_parse_optionst::report_success() xml.data="SUCCESS"; std::cout << xml; std::cout << std::endl; + break; } - break; default: assert(false); @@ -1579,6 +1795,7 @@ void twols_parse_optionst::help() "Backend options:\n" " --all-functions check each function as entry point\n" " --stop-on-fail stop on first failing assertion\n" + " --trace give a counterexample trace for failed properties\n" //NOLINT(*) " --context-sensitive context-sensitive analysis from entry point\n" // NOLINT(*) " --termination compute ranking functions to prove termination\n" // NOLINT(*) " --k-induction use k-induction\n" @@ -1588,8 +1805,16 @@ void twols_parse_optionst::help() " --havoc havoc loops and function calls\n" " --intervals use interval domain\n" " --equalities use equalities and disequalities domain\n" + " --heap use heap domain\n" " --zones use zone domain\n" " --octagons use octagon domain\n" + " --heap-interval use heap domain with interval domain for\n" + " values\n" + " --heap-zones use heap domain with zones domain for values\n" // NOLINT(*) + " --heap-values-refine use heap domain with a dynamic refinement\n" + " of strength of the value domain\n" + " --sympath compute invariant for each symbolic path\n" + " (only usable with --heap-* switches)\n" " --enum-solver use solver based on model enumeration\n" " --binsearch-solver use solver based on binary search\n" " --arrays do not ignore array contents\n" diff --git a/src/2ls/2ls_parse_options.h b/src/2ls/2ls_parse_options.h index d564b1279..af55addab 100644 --- a/src/2ls/2ls_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -16,6 +16,7 @@ Author: Daniel Kroening, Peter Schrammel #include #include +#include class goto_modelt; class optionst; @@ -31,21 +32,35 @@ class optionst; "(non-incremental)" \ "(no-assertions)(no-assumptions)" \ "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ + "(object-bits):" \ "(little-endian)(big-endian)" \ "(error-label):(verbosity):(no-library)" \ "(version)" \ "(i386-linux)(i386-macos)(i386-win32)(win32)(winx64)(gcc)" \ "(ppc-macos)(unsigned-char)" \ - "(havoc)(intervals)(zones)(octagons)(equalities)"\ + "(havoc)" \ + "(intervals)" \ + "(zones)" \ + "(octagons)" \ + "(equalities)" \ + "(heap)" \ + "(heap-interval)" \ + "(heap-zones)" \ + "(heap-values-refine)" \ + "(sympath)" \ + "(disjunctive-intervals)" \ + "(disjunctive-zones)" \ + "(disjunctive-octagons)" \ + "(disjunct-limit)" \ "(enum-solver)(binsearch-solver)(arrays)"\ "(string-abstraction)(no-arch)(arch):(floatbv)(fixedbv)" \ "(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \ "(inline)(inline-main)(inline-partial):" \ - "(context-sensitive)(termination)" \ + "(context-sensitive)(termination)(nontermination)" \ "(lexicographic-ranking-function):(monolithic-ranking-function)" \ "(max-inner-ranking-iterations):" \ "(preconditions)(sufficient)" \ - "(show-locs)(show-vcc)(show-properties)(show-trace)(show-stats)" \ + "(show-locs)(show-vcc)(show-properties)(trace)(show-stats)" \ "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ "(show-invariants)(std-invariants)" \ "(property):(all-properties)(k-induction)(incremental-bmc)" \ @@ -75,6 +90,7 @@ class twols_parse_optionst: ui_message_handlert ui_message_handler; bool recursion_detected; bool threads_detected; + bool dynamic_memory_detected; virtual void register_languages(); void get_command_line_options(optionst &options); @@ -157,11 +173,11 @@ class twols_parse_optionst: void inline_main(goto_modelt &goto_model); void propagate_constants(goto_modelt &goto_model); void nondet_locals(goto_modelt &goto_model); - void unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); + bool unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); exprt evaluate_casts_in_constants( const exprt &expr, - const typet& parent_type, + const typet &parent_type, bool &valid); void remove_multiple_dereferences(goto_modelt &goto_model); void remove_multiple_dereferences( @@ -174,6 +190,24 @@ class twols_parse_optionst: void add_assumptions_after_assertions(goto_modelt &goto_model); void filter_assertions(goto_modelt &goto_model); void split_loopheads(goto_modelt &goto_model); + void remove_loops_in_entry(goto_modelt &goto_model); + void create_dynamic_objects(goto_modelt &goto_model); + void add_dynamic_object_rec(exprt &expr, symbol_tablet &symbol_table); + void add_dynamic_object_symbols( + const ssa_heap_analysist &heap_analysis, + goto_modelt &goto_model); + void split_same_symbolic_object_assignments(goto_modelt &goto_model); + void remove_dead_goto(goto_modelt &goto_model); + void compute_dynobj_instances( + const goto_programt &goto_program, + const dynobj_instance_analysist &analysis, + std::map &instance_counts, + const namespacet &ns); + void create_dynobj_instances( + goto_programt &goto_program, + const std::map &instance_counts, + symbol_tablet &symbol_table); + std::map split_dynamic_objects(goto_modelt &goto_model); }; #endif diff --git a/src/2ls/Makefile b/src/2ls/Makefile index 06b9d4486..5b9362169 100644 --- a/src/2ls/Makefile +++ b/src/2ls/Makefile @@ -6,7 +6,7 @@ SRC = 2ls_main.cpp 2ls_parse_options.cpp \ 2ls_languages.cpp \ show.cpp summary_checker_base.cpp \ summary_checker_ai.cpp summary_checker_bmc.cpp \ - summary_checker_kind.cpp \ + summary_checker_kind.cpp summary_checker_nonterm.cpp \ cover_goals_ext.cpp horn_encoding.cpp \ preprocessing_util.cpp \ instrument_goto.cpp dynamic_cfg.cpp \ diff --git a/src/2ls/cover_goals_ext.h b/src/2ls/cover_goals_ext.h index d777a8a66..a0e9fb3c1 100644 --- a/src/2ls/cover_goals_ext.h +++ b/src/2ls/cover_goals_ext.h @@ -52,7 +52,8 @@ class cover_goals_extt:public messaget incremental_solvert &_solver, const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, - bool _spurious_check, bool _all_properties, + bool _spurious_check, + bool _all_properties, bool _build_error_trace): SSA(_SSA), solver(_solver), diff --git a/src/2ls/dynamic_cfg.cpp b/src/2ls/dynamic_cfg.cpp index 807ec5ed3..f7cd221b4 100644 --- a/src/2ls/dynamic_cfg.cpp +++ b/src/2ls/dynamic_cfg.cpp @@ -33,7 +33,7 @@ void dynamic_cfgt::operator()( build_cfg(goto_program, ssa_unwinder); assumptionst assumptions; - build_from_invariants(ssa, summary, assumptions); + build_from_invariants(ssa, summary.fw_invariant, assumptions); add_assumptions(assumptions); } @@ -193,6 +193,23 @@ void dynamic_cfgt::build_cfg( nodes[node].id.iteration_stack=iteration_stack; nodes[node].is_loop_head=true; } + else + { + // alternative loop head detection when unwinder was not used + for(const auto &incoming : it->incoming_edges) + { + if(incoming->is_backwards_goto() && + incoming!=it) + { + iteration_stack.push_back(0); + loop_node_stack.push_back(node); + max_iteration_stack.push_back(0); + nodes[node].id.iteration_stack=iteration_stack; + nodes[node].is_loop_head=true; + break; + } + } + } const std::set &iedges=incoming_edges[it]; for(const auto &from : iedges) @@ -273,29 +290,20 @@ Function: dynamic_cfgt::build_from_invariants void dynamic_cfgt::build_from_invariants( const unwindable_local_SSAt &ssa, - const summaryt &summary, + const exprt &invariants, assumptionst &assumptions) { - if(summary.fw_invariant.is_nil() || - summary.fw_invariant.is_true()) + if(invariants.is_nil() || invariants.is_true()) return; // expected format /\_i g_i=> inv_i - if(summary.fw_invariant.id()==ID_implies) + if(invariants.id()==ID_implies) { - build_from_invariant( - ssa, summary.fw_invariant, - assumptions); + build_from_invariant(ssa, invariants, assumptions); } - else if(summary.fw_invariant.id()==ID_and) + else if(invariants.id()==ID_and) { - for(unsigned i=0; i void build_from_invariants( const unwindable_local_SSAt &ssa, - const summaryt &summary, + const exprt &invariants, assumptionst &assumptions); void build_from_invariant( const unwindable_local_SSAt &ssa, diff --git a/src/2ls/graphml_witness_ext.cpp b/src/2ls/graphml_witness_ext.cpp index 11fa4bc3b..4ce2952a4 100644 --- a/src/2ls/graphml_witness_ext.cpp +++ b/src/2ls/graphml_witness_ext.cpp @@ -42,7 +42,6 @@ void graphml_witness_extt::operator()( // CFG to CFA const graphmlt::node_indext sink=graphml.add_node(); graphml[sink].node_name="sink"; - graphml[sink].thread_nr=0; graphml[sink].is_violation=false; graphml[sink].has_invariant=false; diff --git a/src/2ls/graphml_witness_ext.h b/src/2ls/graphml_witness_ext.h index df62a2b6d..6294ffc1b 100644 --- a/src/2ls/graphml_witness_ext.h +++ b/src/2ls/graphml_witness_ext.h @@ -28,7 +28,8 @@ class graphml_witness_extt:public graphml_witnesst protected: graphmlt::node_indext add_node( - std::map &loc_to_node, + std::map &loc_to_node, goto_programt::const_targett it); void add_edge( diff --git a/src/2ls/horn_encoding.cpp b/src/2ls/horn_encoding.cpp index fb986a691..cacffe305 100644 --- a/src/2ls/horn_encoding.cpp +++ b/src/2ls/horn_encoding.cpp @@ -81,7 +81,8 @@ void horn_encodingt::translate( ";\n"; // compute SSA - local_SSAt local_SSA(f_it->second, ns, ""); + ssa_heap_analysist heap_analysis(ns); + local_SSAt local_SSA(f_it->second, ns, heap_analysis, ""); const goto_programt &body=f_it->second.body; diff --git a/src/2ls/instrument_goto.cpp b/src/2ls/instrument_goto.cpp index 3e550c6e1..647aaab10 100644 --- a/src/2ls/instrument_goto.cpp +++ b/src/2ls/instrument_goto.cpp @@ -115,8 +115,7 @@ Function: instrument_gotot::instrument_body void instrument_gotot::instrument_body( const local_SSAt &SSA, const exprt &expr, - goto_functionst::goto_functiont &function -) + goto_functionst::goto_functiont &function) { // expected format (/\_j g_j)=> inv const exprt &impl=expr.op0(); diff --git a/src/2ls/preprocessing_util.cpp b/src/2ls/preprocessing_util.cpp index 590827693..4bd55e6a8 100644 --- a/src/2ls/preprocessing_util.cpp +++ b/src/2ls/preprocessing_util.cpp @@ -12,10 +12,10 @@ Author: Peter Schrammel #include #include +#include #include "2ls_parse_options.h" - /*******************************************************************\ Function: twols_parse_optionst::inline_main @@ -138,10 +138,11 @@ Function: twols_parse_optionst::unwind_goto_into_loop \*******************************************************************/ -void twols_parse_optionst::unwind_goto_into_loop( +bool twols_parse_optionst::unwind_goto_into_loop( goto_modelt &goto_model, unsigned k) { + bool result=false; typedef std::vector > loopst; @@ -171,6 +172,7 @@ void twols_parse_optionst::unwind_goto_into_loop( (*s_it)->location_numberlocation_number) { has_goto_into_loop=true; + result=true; break; } } @@ -205,6 +207,8 @@ void twols_parse_optionst::unwind_goto_into_loop( } goto_model.goto_functions.update(); goto_model.goto_functions.compute_loop_numbers(); + + return result; } /*******************************************************************\ @@ -466,3 +470,402 @@ void twols_parse_optionst::split_loopheads(goto_modelt &goto_model) } } } + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_loops_in_entry + + Inputs: + + Outputs: + + Purpose: Remove loop head from entry instruction of a function - + causes problems with input variables naming. If first + instruction is target of back-jump, insert SKIP instruction + before. + +\*******************************************************************/ + +void twols_parse_optionst::remove_loops_in_entry(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + if(f_it->second.body_available() && + f_it->second.body.instructions.begin()->is_target()) + { + auto new_entry= + f_it->second.body.insert_before(f_it->second.body.instructions.begin()); + new_entry->function=f_it->first; + new_entry->make_skip(); + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::create_dynamic_objects + + Inputs: + + Outputs: + + Purpose: Create symbols for objects pointed by parameters of a function. + +\*******************************************************************/ + +void twols_parse_optionst::create_dynamic_objects(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_assign()) + { + code_assignt &code_assign=to_code_assign(i_it->code); + add_dynamic_object_rec(code_assign.lhs(), goto_model.symbol_table); + add_dynamic_object_rec(code_assign.rhs(), goto_model.symbol_table); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::add_dynamic_object_rec + + Inputs: + + Outputs: + + Purpose: For each pointer-typed symbol in an expression which is a parameter, + create symbol for pointed object in the symbol table. + +\*******************************************************************/ + +void twols_parse_optionst::add_dynamic_object_rec( + exprt &expr, symbol_tablet &symbol_table) +{ + if(expr.id()==ID_symbol) + { + const symbolt &symbol= + symbol_table.lookup(to_symbol_expr(expr).get_identifier()); + if(symbol.is_parameter && symbol.type.id()==ID_pointer) + { + // New symbol + symbolt object_symbol; + + object_symbol.base_name=id2string(symbol.base_name)+"'obj"; + object_symbol.name=id2string(symbol.name)+"'obj"; + const typet &pointed_type=symbol.type.subtype(); + // Follow pointed type + if(pointed_type.id()==ID_symbol) + { + const symbolt type_symbol=symbol_table.lookup( + to_symbol_type(pointed_type).get_identifier()); + object_symbol.type=type_symbol.type; + } + else + object_symbol.type=pointed_type; + object_symbol.mode=ID_C; + + symbol_table.add(object_symbol); + } + } + else + { + Forall_operands(it, expr) + add_dynamic_object_rec(*it, symbol_table); + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::add_dynamic_object_symbols + + Inputs: + + Outputs: + + Purpose: Add symbols for all dynamic objects in the program into + the symbol table. + +\*******************************************************************/ + +void twols_parse_optionst::add_dynamic_object_symbols( + const ssa_heap_analysist &heap_analysis, + goto_modelt &goto_model) +{ + forall_goto_functions(f_it, goto_model.goto_functions) + { + forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_function_call()) + { + auto &fun_call=to_code_function_call(i_it->code); + const irep_idt fname= + to_symbol_expr(fun_call.function()).get_identifier(); + auto n_it=i_it; + ++n_it; + for(const symbol_exprt &o : + heap_analysis[n_it].new_caller_objects(fname, i_it)) + { + // New symbol + symbolt object_symbol; + + object_symbol.name=o.get_identifier(); + object_symbol.base_name=id2string(object_symbol.name).substr(5); + object_symbol.is_lvalue=true; + + object_symbol.type=o.type(); + object_symbol.type.set("#dynamic", true); + + object_symbol.mode=ID_C; + + goto_model.symbol_table.add(object_symbol); + } + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::split_same_symbolic_object_assignments + + Inputs: + + Outputs: + + Purpose: Split assignments that have same symbolic dereference object + on both sides into two separate assignments. + +\*******************************************************************/ + +void twols_parse_optionst::split_same_symbolic_object_assignments( + goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + unsigned counter=0; + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_assign()) + { + code_assignt &assign=to_code_assign(i_it->code); + auto lhs_sym_deref=symbolic_dereference(assign.lhs(), ns); + if((lhs_sym_deref.id()==ID_symbol || lhs_sym_deref.id()==ID_member) + && has_symbolic_deref(lhs_sym_deref)) + { + while(lhs_sym_deref.id()==ID_member) + lhs_sym_deref=to_member_expr(lhs_sym_deref).compound(); + + auto rhs_sym_deref=symbolic_dereference(assign.rhs(), ns); + + std::set rhs_symbols; + find_symbols(rhs_sym_deref, rhs_symbols); + + if(rhs_symbols.find(to_symbol_expr(lhs_sym_deref))!=rhs_symbols.end()) + { + symbolt tmp_symbol; + tmp_symbol.type=assign.lhs().type(); + tmp_symbol.name="$symderef_tmp"+i2string(counter++); + tmp_symbol.base_name=tmp_symbol.name; + tmp_symbol.pretty_name=tmp_symbol.name; + + goto_model.symbol_table.add(tmp_symbol); + + auto new_assign=f_it->second.body.insert_after(i_it); + new_assign->make_assignment(); + new_assign->code=code_assignt( + assign.lhs(), tmp_symbol.symbol_expr()); + + assign.lhs()=tmp_symbol.symbol_expr(); + } + } + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_dead_goto + + Inputs: + + Outputs: + + Purpose: Remove dead backwards GOTO instructions (having false as guard) + +\*******************************************************************/ +void twols_parse_optionst::remove_dead_goto(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_backwards_goto()) + { + if(i_it->guard.is_false()) + i_it->make_skip(); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::compute_dynobj_instances + + Inputs: + + Outputs: + + Purpose: For each allocation site, compute the number of objects + that must be used to soundly represent all objects allocated + at the given site. + +\*******************************************************************/ +void twols_parse_optionst::compute_dynobj_instances( + const goto_programt &goto_program, + const dynobj_instance_analysist &analysis, + std::map &instance_counts, + const namespacet &ns) +{ + forall_goto_program_instructions(it, goto_program) + { + auto &analysis_value=analysis[it]; + for(auto &obj : analysis_value.live_pointers) + { + auto must_alias=analysis_value.must_alias_relations.find(obj.first); + if(must_alias==analysis_value.must_alias_relations.end()) + continue; + + std::set alias_classes; + for(auto &expr : obj.second) + { + size_t n; + must_alias->second.get_number(expr, n); + alias_classes.insert(must_alias->second.find_number(n)); + } + + if(instance_counts.find(obj.first)==instance_counts.end() || + instance_counts.at(obj.first) &instance_counts, + symbol_tablet &symbol_table) +{ + Forall_goto_program_instructions(it, goto_program) + { + if(it->is_assign()) + { + auto &assign=to_code_assign(it->code); + if(assign.rhs().get_bool("#malloc_result")) + { + exprt &rhs=assign.rhs(); + exprt &abstract_obj=rhs.id()==ID_if ? to_if_expr(rhs).false_case() + : rhs; + exprt &address=abstract_obj.id()==ID_typecast ? + to_typecast_expr(abstract_obj).op() : abstract_obj; + if(address.id()!=ID_address_of) + continue; + exprt &obj=to_address_of_expr(address).object(); + if(obj.id()!=ID_symbol) + continue; + + if(obj.get_bool("#concrete")) + continue; + + if(instance_counts.find(to_symbol_expr(obj))==instance_counts.end()) + continue; + + size_t count=instance_counts.at(to_symbol_expr(obj)); + if(count<=1) + continue; + + symbolt obj_symbol= + symbol_table.lookup(to_symbol_expr(obj).get_identifier()); + + const std::string name=id2string(obj_symbol.name); + const std::string base_name=id2string(obj_symbol.base_name); + std::string suffix="#"+std::to_string(0); + + obj_symbol.name=name+suffix; + obj_symbol.base_name=base_name+suffix; + symbol_table.add(obj_symbol); + + exprt new_rhs=address_of_exprt(obj_symbol.symbol_expr()); + if(abstract_obj.id()==ID_typecast) + new_rhs=typecast_exprt(new_rhs, rhs.type()); + new_rhs.set("#malloc_result", true); + + for(size_t i=1; ilocation_number)+"#"+ + std::to_string(i); + nondet.base_name=nondet.name; + nondet.pretty_name=nondet.name; + symbol_table.add(nondet); + + suffix="#"+std::to_string(i); + obj_symbol.name=name+suffix; + obj_symbol.base_name=base_name+suffix; + + exprt new_obj=address_of_exprt(obj_symbol.symbol_expr()); + if(abstract_obj.id()==ID_typecast) + new_obj=typecast_exprt(new_obj, rhs.type()); + new_rhs=if_exprt( + nondet.symbol_expr(), new_obj, new_rhs); + new_rhs.set("#malloc_result", true); + } + + abstract_obj=new_rhs; + abstract_obj.set("#malloc_result", true); + } + } + } +} + +std::map twols_parse_optionst::split_dynamic_objects( + goto_modelt &goto_model) +{ + std::map dynobj_instances; + Forall_goto_functions(f_it, goto_model.goto_functions) + { + if(!f_it->second.body_available()) + continue; + namespacet ns(goto_model.symbol_table); + ssa_value_ait value_analysis(f_it->second, ns, ssa_heap_analysist(ns)); + dynobj_instance_analysist do_inst(f_it->second, ns, value_analysis); + + compute_dynobj_instances( + f_it->second.body, do_inst, dynobj_instances, ns); + create_dynobj_instances( + f_it->second.body, dynobj_instances, goto_model.symbol_table); + } + return dynobj_instances; +} diff --git a/src/2ls/show.cpp b/src/2ls/show.cpp index c81d0db95..ce0b64e94 100644 --- a/src/2ls/show.cpp +++ b/src/2ls/show.cpp @@ -18,7 +18,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include +#include #include #include @@ -41,11 +41,17 @@ Function: show_assignments void show_assignments( const goto_functionst::goto_functiont &goto_function, const namespacet &ns, - std::ostream &out) + std::ostream &out, + const ssa_heap_analysist &heap_analysis) { - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); + ssa_objectst ssa_objects(goto_function, ns, heap_analysis); + ssa_value_ait ssa_value_ai(goto_function, ns, heap_analysis); + assignmentst assignments( + goto_function.body, + ns, + ssa_objects, + ssa_value_ai, + heap_analysis); assignments.output(ns, goto_function.body, out); } @@ -69,6 +75,8 @@ void show_assignments( { const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -76,7 +84,7 @@ void show_assignments( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_assignments(f_it->second, ns, out); + show_assignments(f_it->second, ns, out, heap_analysis); } else { @@ -84,7 +92,7 @@ void show_assignments( { out << ">>>> Function " << f_it->first << "\n"; - show_assignments(f_it->second, ns, out); + show_assignments(f_it->second, ns, out, heap_analysis); out << "\n"; } @@ -106,11 +114,17 @@ Function: show_defs void show_defs( const goto_functionst::goto_functiont &goto_function, const namespacet &ns, - std::ostream &out) + std::ostream &out, + const ssa_heap_analysist &heap_analysis) { - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); + ssa_objectst ssa_objects(goto_function, ns, heap_analysis); + ssa_value_ait ssa_value_ai(goto_function, ns, heap_analysis); + assignmentst assignments( + goto_function.body, + ns, + ssa_objects, + ssa_value_ai, + heap_analysis); ssa_ait ssa_analysis(assignments); ssa_analysis(goto_function, ns); ssa_analysis.output(ns, goto_function.body, out); @@ -136,6 +150,8 @@ void show_defs( { const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -143,7 +159,7 @@ void show_defs( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_defs(f_it->second, ns, out); + show_defs(f_it->second, ns, out, heap_analysis); } else { @@ -151,7 +167,7 @@ void show_defs( { out << ">>>> Function " << f_it->first << "\n"; - show_defs(f_it->second, ns, out); + show_defs(f_it->second, ns, out, heap_analysis); out << "\n"; } @@ -235,11 +251,15 @@ Function: show_ssa void show_ssa( const goto_functionst::goto_functiont &goto_function, + const ssa_heap_analysist &heap_analysis, bool simplify, const namespacet &ns, std::ostream &out) { - local_SSAt local_SSA(goto_function, ns); + if(!goto_function.body_available()) + return; + + unwindable_local_SSAt local_SSA(goto_function, ns, heap_analysis); if(simplify) ::simplify(local_SSA, ns); local_SSA.output_verbose(out); @@ -259,6 +279,7 @@ Function: show_ssa void show_ssa( const goto_modelt &goto_model, + const ssa_heap_analysist &heap_analysis, const irep_idt &function, bool simplify, std::ostream &out, @@ -274,7 +295,7 @@ void show_ssa( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_ssa(f_it->second, simplify, ns, out); + show_ssa(f_it->second, heap_analysis, simplify, ns, out); } else { @@ -287,7 +308,7 @@ void show_ssa( out << ">>>> Function " << f_it->first << "\n"; - show_ssa(f_it->second, simplify, ns, out); + show_ssa(f_it->second, heap_analysis, simplify, ns, out); out << "\n"; } @@ -558,10 +579,11 @@ Function: show_value_set void show_value_set( const goto_functionst::goto_functiont &goto_function, const namespacet &ns, - std::ostream &out) + std::ostream &out, + const ssa_heap_analysist &heap_analysis) { - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); + ssa_objectst ssa_objects(goto_function, ns, heap_analysis); + ssa_value_ait ssa_value_ai(goto_function, ns, heap_analysis); ssa_value_ai.output(ns, goto_function, out); } @@ -585,6 +607,8 @@ void show_value_sets( { const namespacet ns(goto_model.symbol_table); + ssa_heap_analysist heap_analysis(ns); + if(!function.empty()) { goto_functionst::function_mapt::const_iterator @@ -592,7 +616,7 @@ void show_value_sets( if(f_it==goto_model.goto_functions.function_map.end()) out << "function " << function << " not found\n"; else - show_value_set(f_it->second, ns, out); + show_value_set(f_it->second, ns, out, heap_analysis); } else { @@ -600,10 +624,9 @@ void show_value_sets( { out << ">>>> Function " << f_it->first << "\n"; - show_value_set(f_it->second, ns, out); + show_value_set(f_it->second, ns, out, heap_analysis); out << "\n"; } } } - diff --git a/src/2ls/show.h b/src/2ls/show.h index 2e9557eac..7051e56a5 100644 --- a/src/2ls/show.h +++ b/src/2ls/show.h @@ -19,9 +19,11 @@ Author: Daniel Kroening, kroening@kroening.com #include class message_handlert; +class ssa_heap_analysist; void show_ssa( const goto_modelt &, + const ssa_heap_analysist &, const irep_idt &function, bool simplify, std::ostream &, diff --git a/src/2ls/summary_checker_ai.cpp b/src/2ls/summary_checker_ai.cpp index 6cda09787..8e60a8739 100644 --- a/src/2ls/summary_checker_ai.cpp +++ b/src/2ls/summary_checker_ai.cpp @@ -9,8 +9,6 @@ Author: Peter Schrammel #include "summary_checker_ai.h" #include -#define TERM_CEX 1 - /*******************************************************************\ Function: summary_checker_ait::operator() @@ -28,7 +26,7 @@ property_checkert::resultt summary_checker_ait::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model, ns); + SSA_functions(goto_model, ns, heap_analysis); ssa_unwinder.init(false, false); @@ -45,40 +43,58 @@ property_checkert::resultt summary_checker_ait::operator()( // properties initialize_property_map(goto_model.goto_functions); - bool preconditions=options.get_bool_option("preconditions"); - bool termination=options.get_bool_option("termination"); - bool trivial_domain=options.get_bool_option("havoc"); - if(!trivial_domain || termination) - { - // forward analysis - summarize(goto_model, true, termination); - } - if(!trivial_domain && preconditions) + property_checkert::resultt result=property_checkert::UNKNOWN; + bool finished=false; + while(!finished) { - // backward analysis - summarize(goto_model, false, termination); - } + bool preconditions=options.get_bool_option("preconditions"); + bool termination=options.get_bool_option("termination"); + bool trivial_domain=options.get_bool_option("havoc"); + if(!trivial_domain || termination) + { + // forward analysis + summarize(goto_model, true, termination); + } + if(!trivial_domain && preconditions) + { + // backward analysis + summarize(goto_model, false, termination); + } - if(preconditions) - { - report_statistics(); - report_preconditions(); - return property_checkert::UNKNOWN; - } + if(preconditions) + { + report_statistics(); + report_preconditions(); + return property_checkert::UNKNOWN; + } - if(termination) - { - report_statistics(); - return report_termination(); - } + if(termination) + { + report_statistics(); + return report_termination(); + } #ifdef SHOW_CALLINGCONTEXTS - if(options.get_bool_option("show-calling-contexts")) - return property_checkert::UNKNOWN; + if(options.get_bool_option("show-calling-contexts")) + return property_checkert::UNKNOWN; #endif - property_checkert::resultt result=check_properties(); - report_statistics(); + result=check_properties(); + report_statistics(); + + if(result==property_checkert::UNKNOWN && + options.get_bool_option("heap-values-refine") && + options.get_bool_option("heap-interval")) + { + summary_db.clear(); + options.set_option("heap-interval", false); + options.set_option("heap-zones", true); + } + else + { + finished=true; + } + } return result; } @@ -158,23 +174,12 @@ property_checkert::resultt summary_checker_ait::report_termination() return property_checkert::PASS; if(one_nonterminate) { -#if TERM_CEX - if(options.get_option("graphml-witness")!="" && - !functions.empty()) - { - property_map.clear(); - incremental_solvert &solver=ssa_db.get_solver(functions.begin()->first); - if(solver()==decision_proceduret::D_SATISFIABLE) - { - irep_idt pid="non-termination"; - property_map[pid].result=property_checkert::FAIL; - ssa_build_goto_tracet build_goto_trace( - *functions.begin()->second, solver.get_solver(), true); - build_goto_trace(property_map[pid].error_trace); - } - } -#endif +#if 0 return property_checkert::FAIL; +#else + // rely on nontermination checker to find counterexample + return property_checkert::UNKNOWN; +#endif } return property_checkert::UNKNOWN; } diff --git a/src/2ls/summary_checker_ai.h b/src/2ls/summary_checker_ai.h index 84eab2bf0..87b477902 100644 --- a/src/2ls/summary_checker_ai.h +++ b/src/2ls/summary_checker_ai.h @@ -14,8 +14,10 @@ Author: Peter Schrammel class summary_checker_ait:public summary_checker_baset { public: - explicit summary_checker_ait(optionst &_options): - summary_checker_baset(_options) + inline summary_checker_ait( + optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_baset(_options, heap_analysis) { } diff --git a/src/2ls/summary_checker_base.cpp b/src/2ls/summary_checker_base.cpp index c910b184c..63aa43f15 100644 --- a/src/2ls/summary_checker_base.cpp +++ b/src/2ls/summary_checker_base.cpp @@ -53,7 +53,8 @@ Function: summary_checker_baset::SSA_functions void summary_checker_baset::SSA_functions( const goto_modelt &goto_model, - const namespacet &ns) + const namespacet &ns, + const ssa_heap_analysist &heap_analysis) { // compute SSA for all the functions forall_goto_functions(f_it, goto_model.goto_functions) @@ -64,7 +65,7 @@ void summary_checker_baset::SSA_functions( continue; status() << "Computing SSA of " << f_it->first << messaget::eom; - ssa_db.create(f_it->first, f_it->second, ns); + ssa_db.create(f_it->first, f_it->second, ns, heap_analysis); local_SSAt &SSA=ssa_db.get(f_it->first); // simplify, if requested @@ -228,6 +229,12 @@ void summary_checker_baset::check_properties( { solver << summary_db.get(f_it->first).fw_invariant; solver << summary_db.get(f_it->first).fw_precondition; + + if(options.get_bool_option("heap") && + summary_db.get(f_it->first).aux_precondition.is_not_nil()) + { + solver << summary_db.get(f_it->first).aux_precondition; + } } // callee summaries @@ -248,7 +255,7 @@ void summary_checker_baset::check_properties( SSA, solver, loophead_selects, property_map, !fully_unwound && options.get_bool_option("spurious-check"), all_properties, - options.get_bool_option("show-trace") || + options.get_bool_option("trace") || options.get_option("graphml-witness")!="" || options.get_option("json-cex")!=""); diff --git a/src/2ls/summary_checker_base.h b/src/2ls/summary_checker_base.h index d4b1ad0e5..0d65062ce 100644 --- a/src/2ls/summary_checker_base.h +++ b/src/2ls/summary_checker_base.h @@ -14,6 +14,7 @@ Author: Peter Schrammel #include #include +#include #include #include #include @@ -27,7 +28,9 @@ class graphml_witness_extt; class summary_checker_baset:public property_checkert { public: - explicit summary_checker_baset(optionst &_options): + summary_checker_baset( + optionst &_options, + const ssa_heap_analysist &_heap_analysis): show_vcc(false), simplify(false), fixed_point(false), @@ -35,6 +38,7 @@ class summary_checker_baset:public property_checkert ssa_db(_options), summary_db(), ssa_unwinder(ssa_db), ssa_inliner(summary_db), + heap_analysis(_heap_analysis), solver_instances(0), solver_calls(0), summaries_used(0), @@ -62,6 +66,8 @@ class summary_checker_baset:public property_checkert ssa_unwindert ssa_unwinder; ssa_inlinert ssa_inliner; + const ssa_heap_analysist &heap_analysis; + unsigned solver_instances; unsigned solver_calls; unsigned summaries_used; @@ -73,7 +79,10 @@ class summary_checker_baset:public property_checkert const goto_programt::const_targett, const local_SSAt::nodet::assertionst::const_iterator &); - void SSA_functions(const goto_modelt &, const namespacet &ns); + void SSA_functions( + const goto_modelt &, + const namespacet &ns, + const ssa_heap_analysist &heap_analysis); void summarize( const goto_modelt &, @@ -81,16 +90,20 @@ class summary_checker_baset:public property_checkert bool termination=false); property_checkert::resultt check_properties(); - void check_properties( + virtual void check_properties( const ssa_dbt::functionst::const_iterator f_it); exprt::operandst get_loophead_selects( - const irep_idt &function_name, const local_SSAt &, prop_convt &); + const irep_idt &function_name, + const local_SSAt &, + prop_convt &); bool is_spurious( const exprt::operandst& loophead_selects, incremental_solvert&); exprt::operandst get_loop_continues( - const irep_idt &function_name, const local_SSAt &, prop_convt &); + const irep_idt &function_name, + const local_SSAt &, + prop_convt &); bool is_fully_unwound( const exprt::operandst& loop_continues, const exprt::operandst& loophead_selects, diff --git a/src/2ls/summary_checker_bmc.cpp b/src/2ls/summary_checker_bmc.cpp index 38e9e925e..4682e2cf8 100644 --- a/src/2ls/summary_checker_bmc.cpp +++ b/src/2ls/summary_checker_bmc.cpp @@ -26,7 +26,7 @@ property_checkert::resultt summary_checker_bmct::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model, ns); + SSA_functions(goto_model, ns, heap_analysis); ssa_unwinder.init(false, true); diff --git a/src/2ls/summary_checker_bmc.h b/src/2ls/summary_checker_bmc.h index 5885f6b36..907b873cf 100644 --- a/src/2ls/summary_checker_bmc.h +++ b/src/2ls/summary_checker_bmc.h @@ -14,8 +14,10 @@ Author: Peter Schrammel class summary_checker_bmct:public summary_checker_baset { public: - explicit summary_checker_bmct(optionst &_options): - summary_checker_baset(_options) + summary_checker_bmct( + optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_baset(_options, heap_analysis) { } diff --git a/src/2ls/summary_checker_kind.cpp b/src/2ls/summary_checker_kind.cpp index 5c13a3a92..47092b8a7 100644 --- a/src/2ls/summary_checker_kind.cpp +++ b/src/2ls/summary_checker_kind.cpp @@ -6,10 +6,9 @@ Author: Peter Schrammel \*******************************************************************/ +#include #include "summary_checker_kind.h" -#define GIVE_UP_INVARIANTS 4 - /*******************************************************************\ Function: summary_checker_kindt::operator() @@ -27,12 +26,14 @@ property_checkert::resultt summary_checker_kindt::operator()( { const namespacet ns(goto_model.symbol_table); - SSA_functions(goto_model, ns); + SSA_functions(goto_model, ns, heap_analysis); ssa_unwinder.init(true, false); property_checkert::resultt result=property_checkert::UNKNOWN; unsigned max_unwind=options.get_unsigned_int_option("unwind"); + unsigned give_up_invariants= + options.get_unsigned_int_option("give-up-invariants"); status() << "Max-unwind is " << max_unwind << eom; ssa_unwinder.init_localunwinders(); @@ -47,7 +48,7 @@ property_checkert::resultt summary_checker_kindt::operator()( result=check_properties(); bool magic_limit_not_reached= - unwind +#include +#include + +#include +#include <2ls/show.h> + +#include + +/*******************************************************************\ + +Function: summary_checker_nontermt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_nontermt::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns, heap_analysis); + + ssa_unwinder.init(false, true); + + property_checkert::resultt result=property_checkert::UNKNOWN; + unsigned max_unwind=options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind=1; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << messaget::eom; + ssa_unwinder.unwind_all(unwind); + if(unwind==51) // use a different nontermination technique + { + result=check_nonterm_linear(); + if(result==property_checkert::PASS) + { + status() << "Termination proved after " + << unwind << " unwinding(s)" << messaget::eom; + report_statistics(); + return result; + } + else if(result==property_checkert::FAIL) + { + status() << "Nonterminating program execution proved after " + << unwind << " unwinding(s)" << messaget::eom; + report_statistics(); + return result; + } + } + result=summary_checker_baset::check_properties(); + if(result==property_checkert::PASS) + { + status() << "Termination proved after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + else if(result==property_checkert::FAIL) + { + status() << "Nonterminating program execution proved after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + } + + report_statistics(); + return result; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties + + Inputs: + + Outputs: + + Purpose: checks the property loop instead of assertion + +\*******************************************************************/ + +void summary_checker_nontermt::check_properties( + const ssa_dbt::functionst::const_iterator f_it) +{ + unwindable_local_SSAt &SSA=*f_it->second; + + ssa_local_unwindert &ssa_local_unwinder=ssa_unwinder.get(f_it->first); + +#if 0 + SSA.output_verbose(std::cout); +#endif + + // solver + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + solver.set_message_handler(get_message_handler()); + + // give SSA to solver + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + + // freeze loop head selects + exprt::operandst loophead_selects= + get_loophead_selects(f_it->first, SSA, *solver.solver); + + exprt enabling_expr=SSA.get_enabling_exprs(); + solver << enabling_expr; + + cover_goals_extt cover_goals( + SSA, + solver, + loophead_selects, + property_map, + false, + false, + options.get_bool_option("trace") || + options.get_option("graphml-witness")!="" || + options.get_option("json-cex")!=""); + + property_map.clear(); + + exprt::operandst ls_guards; + + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + irep_idt property_id( + id2string(n_it->location->function)+"."+ + std::to_string(n_it->location->loop_number)+".term"); + + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[n_it->loophead->location].phi_nodes; + + long store_unwinding=SSA.current_unwinding; + exprt::operandst loop_check_operands; + + symbol_exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(lhguard, *n_it, false); + + for(SSA.current_unwinding=1; + SSA.current_unwinding<=store_unwinding; + SSA.current_unwinding++) + { + exprt::operandst loop_vars; + loop_vars.push_back(lhguard); + + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + symbol_exprt post_var= + SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(post_var, *n_it->loophead, false); + + symbol_exprt phi_var; + phi_var=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var, *n_it->loophead, true); + loop_vars.push_back(equal_exprt(post_var, phi_var)); + } + + loop_check_operands.push_back(conjunction(loop_vars)); + } + SSA.current_unwinding=store_unwinding; + + property_map[property_id].location=n_it->loophead->location; + property_map[property_id].result=UNKNOWN; + cover_goals.goal_map[property_id].conjuncts.push_back( + disjunction(loop_check_operands)); + ls_guards.push_back(not_exprt(lsguard)); + } + } + + solver << conjunction(ls_guards); + + for(cover_goals_extt::goal_mapt::const_iterator + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++) + { + literalt p=solver.convert(disjunction(it->second.conjuncts)); + cover_goals.add(p); + } + + status() << "Running " << solver.solver->decision_procedure_text() + << messaget::eom; + + cover_goals(); + + solver.pop_context(); + + status() << "** " << cover_goals.number_covered() + << " of " << cover_goals.size() << " failed (" + << cover_goals.iterations() << " iterations)" + << messaget::eom; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties_linear + + Inputs: + + Outputs: + + Purpose: searching for periodical recurrence set + +\*******************************************************************/ + +void summary_checker_nontermt::check_properties_linear( + const ssa_dbt::functionst::const_iterator f_it) +{ + unwindable_local_SSAt &SSA=*f_it->second; + + ssa_local_unwindert &ssa_local_unwinder=ssa_unwinder.get(f_it->first); + + // solver + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + solver.set_message_handler(get_message_handler()); + + // give SSA to solver + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + + // freeze loop head selects + exprt::operandst loophead_selects= + get_loophead_selects(f_it->first, SSA, *solver.solver); + + exprt enabling_expr=SSA.get_enabling_exprs(); + solver << enabling_expr; + + property_map.clear(); + + exprt::operandst ls_guards; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + ls_guards.push_back(lsguard); + } + } + + unsigned loop_counter=std::numeric_limits::max(); + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + // we use continues further, therefore we put incrementation here + loop_counter++; + + irep_idt property_id( + id2string(n_it->location->function)+"."+ + std::to_string(n_it->location->loop_number)+".term"); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[n_it->loophead->location].phi_nodes; + + SSA.current_unwinding-=1; + symbol_exprt lhguard_second=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(lhguard_second, *n_it, true); + SSA.current_unwinding+=1; + + symbol_exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(lhguard, *n_it, true); + + exprt::operandst first_linearity_check; + exprt::operandst second_linearity_check; + exprt::operandst constants_computation; + exprt::operandst constants; + exprt::operandst loopback_vars; + exprt::operandst loop_exit_cond; + exprt::operandst neg_loop_exit_cond; + + property_map[property_id].location=n_it->loophead->location; + property_map[property_id].result=UNKNOWN; + + unsigned const_number=0; + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + // first linearity check data preparation + + SSA.current_unwinding-=1; + symbol_exprt phi_var1; + phi_var1=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var1, *n_it->loophead, true); + SSA.current_unwinding+=1; + symbol_exprt phi_var2; + phi_var2=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var2, *n_it->loophead, true); + + // works only for bitvectors + + if((phi_var2.type().id()!=ID_unsignedbv) && + (phi_var2.type().id()!=ID_signedbv)) + { + first_linearity_check.clear(); + break; + } + + // x_k = x_0 + C*k, const$k - k, const$i - C + symbol_exprt constk("const$k", phi_var2.type()); + symbol_exprt const1( + "const$"+std::to_string(const_number++), phi_var2.type()); + + first_linearity_check.push_back( + equal_exprt( + phi_var1, + plus_exprt( + phi_var2, + const1))); + + // get constants data preparation + + constants_computation.push_back( + equal_exprt( + minus_exprt(phi_var1, phi_var2), + const1)); + constants.push_back(const1); + + // loopback vars data preparation + + exprt init_expr; + exprt lb_var; + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->loophead->equalities.begin(); + e_it!=n_it->loophead->equalities.end(); e_it++) + { + if(e_it->rhs().id()==ID_if && + to_symbol_expr(e_it->lhs()).get_identifier()== + phi_var2.get_identifier()) + { + const if_exprt &if_expr=to_if_expr(e_it->rhs()); + init_expr=if_expr.false_case(); + lb_var=if_expr.true_case(); + // should already be renamed for inner loops + break; + } + } + + loopback_vars.push_back(lb_var); + loopback_vars.push_back(init_expr); + loopback_vars.push_back(constk); + } + + if(first_linearity_check.empty()) // nothing to be checked + continue; + + neg_loop_exit_cond.push_back(lhguard); + neg_loop_exit_cond.push_back(not_exprt(lhguard_second)); + loop_exit_cond.push_back(lhguard); + + solver.new_context(); + solver << conjunction(first_linearity_check); + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + continue; + + case decision_proceduret::D_SATISFIABLE: + for(exprt::operandst::iterator it=first_linearity_check.begin(); + it!=first_linearity_check.end(); + ++it) + { + exprt ex=solver.get(it->op1().op1()); + second_linearity_check.push_back( + and_exprt( + *it, + not_exprt(equal_exprt(to_constant_expr(ex), it->op1().op1())))); + } + + solver.pop_context(); + break; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + + solver.new_context(); + solver << disjunction(second_linearity_check); + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + break; + + case decision_proceduret::D_SATISFIABLE: + solver.pop_context(); + continue; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + + // constants extraction + exprt::operandst solver_consts; + + solver.new_context(); + solver << conjunction(constants_computation); + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: // should never happen + solver.pop_context(); + solver.pop_context(); + return; + + case decision_proceduret::D_SATISFIABLE: + for(auto constant : constants) + { + exprt ex=solver.solver->get(constant); + if(ex.type().id()==ID_unsignedbv) + { + // if (constant>UINT_MAX/2)?0-constant:constant + constant_exprt cex=to_constant_expr(ex); + constant_exprt zero=constant_exprt("0", ex.type()); + constant_exprt cex2= + to_constant_expr(simplify_expr(minus_exprt(zero, cex), SSA.ns)); + if_exprt ifex=if_exprt( + binary_relation_exprt(cex, ID_gt, cex2), + cex2, + cex, + ex.type()); + ex=simplify_expr(ifex, SSA.ns); + } + solver_consts.push_back(to_constant_expr((ex))); + } + solver.pop_context(); + break; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + + // loop exit conditions satisfiable check + + solver.new_context(); + + for(unsigned i=0; iget(*(lbv_it+1)); + + // TODO: make this in different way + if(!from_expr(SSA.ns, "", *slvc_it).compare("0")) + { + eq_expr=equal_exprt( + *(lbv_it+1), + to_constant_expr( + simplify_expr( + old_xinit, + SSA.ns))); + } + else + { + // Exists xinit % const != old_xinit % const + eq_expr=equal_exprt( + mod_exprt(*(lbv_it+1), *slvc_it), + mod_exprt( + to_constant_expr(simplify_expr(old_xinit, SSA.ns)), + *slvc_it)); + } + local_constraints.push_back(eq_expr); + ++slvc_it; + lbv_it+=3; + } + constraints.push_back(not_exprt(conjunction(local_constraints))); + solver << not_exprt(conjunction(local_constraints)); + break; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + solver.pop_context(); + return; + } + } + while(result!=decision_proceduret::D_UNSATISFIABLE); + + solver.pop_context(); + + solver << conjunction(loop_exit_cond); + solver << conjunction(constraints); + + switch(solver()) + { + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + break; + + case decision_proceduret::D_SATISFIABLE: + // found nontermination + property_map[property_id].result=FAIL; + solver.pop_context(); + solver.pop_context(); + return; + + default: + error() << "decision procedure has failed" << eom; + solver.pop_context(); + solver.pop_context(); + return; + } + } + } + solver.pop_context(); +} + +/*******************************************************************\ + +Function: summary_checker_nontermt::check_nonterm_linear + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +summary_checker_baset::resultt summary_checker_nontermt::check_nonterm_linear() +{ + // analyze all the functions + for(ssa_dbt::functionst::const_iterator f_it=ssa_db.functions().begin(); + f_it!=ssa_db.functions().end(); f_it++) + { + status() << "Checking properties of " << f_it->first << messaget::eom; + + check_properties_linear(f_it); + } + + summary_checker_baset::resultt result=property_checkert::PASS; + for(property_mapt::const_iterator + p_it=property_map.begin(); p_it!=property_map.end(); p_it++) + { + if(p_it->second.result==FAIL) + return property_checkert::FAIL; + if(p_it->second.result==UNKNOWN) + result=property_checkert::UNKNOWN; + } + + return result; +} diff --git a/src/2ls/summary_checker_nonterm.h b/src/2ls/summary_checker_nonterm.h new file mode 100644 index 000000000..d8a87afa3 --- /dev/null +++ b/src/2ls/summary_checker_nonterm.h @@ -0,0 +1,36 @@ +/*******************************************************************\ + +Module: Summarizer for Nontermination Bit-level analysis + +Author: Stefan Marticek + +\*******************************************************************/ + +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_NONTERM_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_NONTERM_H + +#include "summary_checker_base.h" + +class summary_checker_nontermt:public summary_checker_baset +{ +public: + explicit summary_checker_nontermt( + optionst &_options, + const ssa_heap_analysist &heap_analysis): + summary_checker_baset(_options, heap_analysis) + { + } + + virtual resultt operator()(const goto_modelt &); + + void check_properties( + const ssa_dbt::functionst::const_iterator f_it); + + void check_properties_linear( + const ssa_dbt::functionst::const_iterator f_it); + +protected: + summary_checker_baset::resultt check_nonterm_linear(); +}; + +#endif // CPROVER_2LS_2LS_SUMMARY_CHECKER_NONTERM_H diff --git a/src/2ls/version.h b/src/2ls/version.h index 132ef4309..5817d05a1 100644 --- a/src/2ls/version.h +++ b/src/2ls/version.h @@ -9,6 +9,6 @@ Author: Peter Schrammel #ifndef CPROVER_2LS_2LS_VERSION_H #define CPROVER_2LS_2LS_VERSION_H -#define TWOLS_VERSION "0.5.1" +#define TWOLS_VERSION "0.7.1" #endif diff --git a/src/domains/Makefile b/src/domains/Makefile index 82b6a23b8..1561b946c 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -1,4 +1,7 @@ -SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ +SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp \ + predabs_domain.cpp heap_domain.cpp list_iterator.cpp \ + heap_tpolyhedra_domain.cpp heap_tpolyhedra_sympath_domain.cpp \ + disjunctive_domain.cpp symbolic_path.cpp\ ssa_analyzer.cpp util.cpp incremental_solver.cpp \ strategy_solver_base.cpp strategy_solver_equality.cpp \ linrank_domain.cpp lexlinrank_domain.cpp\ @@ -7,7 +10,10 @@ SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ template_generator_base.cpp template_generator_summary.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ - strategy_solver_predabs.cpp + strategy_solver_predabs.cpp strategy_solver_heap.cpp \ + strategy_solver_heap_tpolyhedra.cpp \ + strategy_solver_heap_tpolyhedra_sympath.cpp \ + strategy_solver_disjunctive.cpp \ #solver_enumeration.cpp include ../config.inc diff --git a/src/domains/disjunctive_domain.cpp b/src/domains/disjunctive_domain.cpp new file mode 100644 index 000000000..bfe864f9a --- /dev/null +++ b/src/domains/disjunctive_domain.cpp @@ -0,0 +1,446 @@ +/*******************************************************************\ + +Module: Disjunctive domain + +Author: Johanan Wahlang + +\*******************************************************************/ + +#ifdef DEBUG +#include +#include +#endif + +#include +#include +#include + +#include "disjunctive_domain.h" +#include "util.h" +#include "domain.h" + +#define ENABLE_HEURISTICS + +/*******************************************************************\ + +Function: disjunctive_domaint::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_domaint::initialize(valuet &value) +{ + disjunctive_valuet &dv=static_cast(value); + if (template_kind==TPOLYHEDRA) + { + for (auto &v : dv) + { + tpolyhedra_domaint *domain=static_cast(base_domain_ptr); + domain->initialize(*v); + } + } + else + { + assert(false); + } +} + +/*******************************************************************\ + +Function: tpolyhedra_domaint::join + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_domaint::join(valuet &value1, const valuet &value2) +{ + disjunctive_valuet &v1=static_cast(value1); + const disjunctive_valuet &v2=static_cast(value2); + v1.resize(v1.size() + v2.size()); + for(std::size_t disjunct=v1.size(); disjunct(value); + + if (template_kind==TPOLYHEDRA) + { + tpolyhedra_domaint *domain=static_cast(base_domain_ptr); + for (auto &v : dv) + { + domain->output_value(out,*v,ns); + out << " || " << std::endl; + } + } + else + { + assert(false); + } +} + +/*******************************************************************\ + +Function: disjunctive_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const +{ + switch (template_kind) + { + case TPOLYHEDRA: + if (templ.size()==0) + { + static_cast(base_domain_ptr)->output_domain(out,ns); + } + for (auto &x:templ) + { + for (auto &y:x.second) + { + out << "Template for edge from disjunct " << y.first << " to disjunct " << x.first << std::endl; + static_cast(y.second)->output_domain(out,ns); + out << std::endl; + } + } + break; + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: disjunctive_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_domaint::project_on_vars( + domaint::valuet &value, + const domaint::var_sett &vars, + exprt &result) +{ + disjunctive_valuet &dv=static_cast(value); + + if (dv.size()==0) + { + result = true_exprt(); + return; + } + result = false_exprt(); + exprt disjunct_result; + if (template_kind==TPOLYHEDRA) + { + tpolyhedra_domaint *domain=static_cast(base_domain_ptr); + for (auto &v : dv) + { + domain->project_on_vars(*v,vars,disjunct_result); + result = or_exprt(result,disjunct_result); + } + } + else + { + assert(false); + } +} + +/*******************************************************************\ + +Function: disjunctive_domaint::merge_heuristic + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +disjunctive_domaint::disjunctt disjunctive_domaint::merge_heuristic(disjunctive_valuet &dv, valuet &value) +{ + if (template_kind==TPOLYHEDRA) + { + tpolyhedra_domaint::templ_valuet &v_new=static_cast(value); + disjunctt d=0; + tpolyhedra_domaint::templ_valuet *v=static_cast(dv[d]); + lex_metrict distance=hausdorff_distance(*v, v_new); + lex_metrict min_distance=distance; + disjunctt min_disjunct=d; + for (; d(dv[d]); + lex_metrict distance=hausdorff_distance(*v, v_new); + if (distancetol) + { + return dv.size(); + } + else + { + return min_disjunct; + } + } + else + { + //TODO: merge heuristic for other templates + std::cout << "Merge heuristic template kind not yet implemented" << std::endl; + assert(false); + } +} + +/*******************************************************************\ + +Function: disjunctive_domaint::hausdorff_distance + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +disjunctive_domaint::lex_metrict disjunctive_domaint::hausdorff_distance( + const tpolyhedra_domaint::templ_valuet &value1, + const tpolyhedra_domaint::templ_valuet &value2) +{ + assert(value1.size()==value2.size()); + unsigned int incomparable=0; + mp_integer dist(0); + for (std::size_t i=0; id2) + { + dist+=d1; + } + else + { + dist+=d2; + } + } + } + return lex_metrict(incomparable,dist); +} + +/*******************************************************************\ + +Function: disjunctive_domaint::distance + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +mp_integer disjunctive_domaint::distance(const constant_exprt &v1, const constant_exprt &v2) +{ + if(v1.type()==v2.type() && + (v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv)) + { + mp_integer vv1,vv2; + to_integer(v1,vv1); + to_integer(v2,vv2); + mp_integer diff(vv1-vv2); + if (diff.is_negative()) + { + return -diff; + } + else + { + return diff; + } + } + else + { + assert(false); // types do not match or are not supported + } +} + +/*******************************************************************\ + +Function: disjunctive_domaint::to_pre_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt disjunctive_domaint::to_pre_constraints( + const disjunctive_domaint::disjunctive_valuet &value) +{ + exprt::operandst c; + + for (auto &x:templ) + { + for (auto &y:x.second) + { + assert(value.size()>y.first); + if (template_kind==TPOLYHEDRA) + { + tpolyhedra_domaint::templ_valuet *v=static_cast(value[y.first]); + c.push_back(static_cast(y.second)->to_pre_constraints(*v)); + } + } + } + return conjunction(c); +} + +/*******************************************************************\ + +Function: disjunctive_domaint::make_not_post_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt disjunctive_domaint::make_not_post_constraints( + const disjunctive_domaint::disjunctive_valuet &value, + disjunctive_domaint::disjunctive_exprst &disjunctive_cond_exprs, + disjunctive_domaint::disjunctive_exprst &disjunctive_value_exprs) +{ + exprt::operandst c; + + for (auto &x:templ) + { + assert(value.size()>x.first); + unsigned sink=x.first; + std::map value_exprs_map; + std::map cond_exprs_map; + for (auto &y:x.second) + { + unsigned src=y.first; + exprt::operandst cond_exprs; + exprt::operandst value_exprs; + if (template_kind==TPOLYHEDRA) + { + tpolyhedra_domaint::templ_valuet *v=static_cast(value[x.first]); + tpolyhedra_domaint *domain=static_cast(y.second); + domain->make_not_post_constraints(*v,cond_exprs,value_exprs); + value_exprs_map.insert(std::make_pair(src,value_exprs)); + cond_exprs_map.insert(std::make_pair(src,cond_exprs)); + } + } + disjunctive_cond_exprs.insert(std::make_pair(sink,cond_exprs_map)); + disjunctive_value_exprs.insert(std::make_pair(sink,value_exprs_map)); + } +} diff --git a/src/domains/disjunctive_domain.h b/src/domains/disjunctive_domain.h new file mode 100644 index 000000000..d4fec326d --- /dev/null +++ b/src/domains/disjunctive_domain.h @@ -0,0 +1,212 @@ +/*******************************************************************\ + +Module: Disjunctive domain + +Author: Johanan Wahlang + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_DISJUNCTIVE_DOMAIN_H +#define CPROVER_2LS_DOMAINS_DISJUNCTIVE_DOMAIN_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "domain.h" +#include "tpolyhedra_domain.h" +#include "symbolic_path.h" + +class disjunctive_domaint:public domaint +{ +public: + enum template_kindt + { + TPOLYHEDRA, HEAP + }; + typedef unsigned disjunctt; + typedef std::map> templatet; + + typedef std::map> disjunctive_exprst; + typedef std::map> disjunctive_literalst; + + class disjunctive_valuet:public valuet, public std::vector + { + }; + + class lex_metrict + { + public: + unsigned int incomparable; + mp_integer distance; + + lex_metrict( + unsigned int _incomparable=0, + mp_integer _distance=0): + incomparable(_incomparable), + distance(_distance) + {} + friend bool operator< (const lex_metrict &m1, const lex_metrict &m2) + { + if (m1.incomparable (const lex_metrict &m1, const lex_metrict &m2) + { + if(m1.incomparable>m2.incomparable) + { + return true; + } + else if (m2.incomparable>m1.incomparable) + { + return false; + } + else + { + return (m1.distance>m2.distance); + } + } + }; + + + class unresolved_edget + { + public: + disjunctt disjunct; + symbolic_patht path; + + inline unresolved_edget() { } + + inline unresolved_edget( + disjunctt _disjunct, + symbolic_patht _path): + disjunct(_disjunct), + path(_path) + {} + }; + + typedef std::vector unresolved_sett; + + struct seen_edget + { + disjunctt source; + symbolic_patht path; + disjunctt sink; + + seen_edget( + disjunctt _source, + symbolic_patht _path, + disjunctt _sink): + source(_source), + path(_path), + sink(_sink) + {} + }; + + typedef std::vector seen_sett; + + disjunctive_domaint( + unsigned int _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &_ns, + const template_kindt _template_kind, + const disjunctt _max, + const lex_metrict _tol): + domaint(_domain_number, _renaming_map, _ns), + template_kind(_template_kind), + max(_max), + templ(), + tol(_tol), + unresolved_set(), + seen_set() + { + if(template_kind==TPOLYHEDRA) + { + base_domain_ptr=new tpolyhedra_domaint(domain_number, renaming_map, _ns); + } + } + + virtual ~disjunctive_domaint() + { + if (base_domain_ptr!=NULL) + delete base_domain_ptr; + for (auto &i:templ) + { + for (auto &j:i.second) + { + if (j.second!=NULL) + delete j.second; + } + } + } + + virtual void initialize(valuet &value); + + virtual void join(valuet &value1, const valuet &value2); + + // printing + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const override; + + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const override; + + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) override; + + inline template_kindt &get_template_kind() + { + return template_kind; + } + inline domaint *base_domain() + { + return base_domain_ptr; + } + + disjunctt merge_heuristic(disjunctive_valuet &dv,valuet &v); + lex_metrict hausdorff_distance( + const tpolyhedra_domaint::templ_valuet &value1, + const tpolyhedra_domaint::templ_valuet &value2); + mp_integer distance(const constant_exprt &v1, const constant_exprt &v2); + + exprt to_pre_constraints(const disjunctive_valuet &value); + exprt make_not_post_constraints( + const disjunctive_valuet &value, + disjunctive_exprst &cond_exprs, + disjunctive_exprst &value_exprs); + +protected: + domaint *base_domain_ptr; + template_kindt template_kind; + disjunctt max; + templatet templ; + lex_metrict tol; + unresolved_sett unresolved_set; + seen_sett seen_set; + + friend class strategy_solver_disjunctivet; +}; + +#endif // CPROVER_2LS_DOMAINS_DISJUNCTIVE_DOMAIN_H diff --git a/src/domains/domain.h b/src/domains/domain.h index cdc10fa83..5f8fde816 100644 --- a/src/domains/domain.h +++ b/src/domains/domain.h @@ -39,7 +39,7 @@ class domaint typedef std::vector var_listt; typedef std::set var_sett; - typedef enum {LOOP, IN, OUT, OUTL} kindt; + typedef enum {LOOP, IN, OUT, OUTL, OUTHEAP} kindt; typedef exprt guardt; diff --git a/src/domains/heap_domain.cpp b/src/domains/heap_domain.cpp new file mode 100644 index 000000000..6c4576dea --- /dev/null +++ b/src/domains/heap_domain.cpp @@ -0,0 +1,1657 @@ +/*******************************************************************\ + +Module: Abstract domain for representing heap + +Author: Viktor Malik + +\*******************************************************************/ + +#include "heap_domain.h" +#include +#include + +/*******************************************************************\ + +Function: heap_domaint::initialize + + Inputs: + + Outputs: + + Purpose: Initialize abstract value. + Clears value with empty value rows corresponding to template. + +\*******************************************************************/ + +void heap_domaint::initialize(domaint::valuet &value) +{ + heap_valuet &val=static_cast(value); + + for(const template_rowt &templ_row : templ) + { + if(templ_row.mem_kind==STACK) + val.emplace_back(new stack_row_valuet(ns)); + else if(templ_row.mem_kind==HEAP) + val.emplace_back( + new heap_row_valuet( + ns, + std::make_pair( + templ_row.dyn_obj, + templ_row.expr))); + else + assert(false); + } +} + +/*******************************************************************\ + +Function: heap_domaint::make_template + + Inputs: + + Outputs: + + Purpose: Create domain template for given set of variables. + Template contains a row for each pointer-typed variable and + field of a dynamic object. + +\*******************************************************************/ + +void heap_domaint::make_template( + const domaint::var_specst &var_specs, + const namespacet &ns) +{ + unsigned long size=var_specs.size(); + templ.clear(); + templ.reserve(size); + + for(const var_spect &v : var_specs) + { + if(v.kind==IN) + continue; + + // Create template for each pointer + const vart &var=v.var; + if(var.type().id()==ID_pointer) + { + const typet &pointed_type=ns.follow(var.type().subtype()); + add_template_row(v, pointed_type); + + if(var.id()==ID_symbol && + id2string(to_symbol_expr(var).get_identifier()).find( + "__CPROVER_deallocated")!=std::string::npos) + { + for(const var_spect &v_other : var_specs) + { + if(!(v_other.var.type().id()==ID_pointer && v_other.kind==LOOP && + v_other.pre_guard==v.pre_guard)) + { + continue; + } + + if(v_other.var.id()==ID_symbol && + id2string(to_symbol_expr(v_other.var).get_identifier()).find( + "__CPROVER_")!=std::string::npos) + { + continue; + } + + add_template_row_pair(v, v_other, pointed_type); + } + } + } + } +} + +/*******************************************************************\ + +Function: heap_domaint::add_template_row + + Inputs: var_spec Variable specification + + Outputs: + + Purpose: Add a template row. + +\*******************************************************************/ + +void heap_domaint::add_template_row( + const var_spect &var_spec, + const typet &pointed_type) +{ + const vart &var=var_spec.var; + + templ.push_back(template_rowt()); + template_rowt &templ_row=templ.back(); + templ_row.expr=var; + templ_row.pre_guard=var_spec.pre_guard; + templ_row.post_guard=var_spec.post_guard; + templ_row.aux_expr=var_spec.aux_expr; + templ_row.kind=var_spec.kind; + + templ_row.mem_kind=STACK; + if(pointed_type.id()==ID_struct) + { + // Check if var is member field of heap object + const std::string identifier=id2string( + to_symbol_expr(var_spec.var).get_identifier()); + for(auto &component : to_struct_type(pointed_type).components()) + { + if(identifier.find("."+id2string(component.get_name()))!= + std::string::npos) + { +#if 0 + // It has shown that using stack rows only is sufficient to prove all + // tested programs and it seems that pointer access paths are not + // necessary. Therefore, we currently disable this code. + templ_row.mem_kind=HEAP; +#endif + templ_row.member=component.get_name(); + + std::string var_id=id2string(to_symbol_expr(var).get_identifier()); + std::string do_id=var_id.substr(0, var_id.find_last_of('.')); + templ_row.dyn_obj=symbol_exprt(do_id, var.type().subtype()); + } + } + } +} + +/*******************************************************************\ + +Function: heap_domaint::add_template_row_pair + + Inputs: var_spec Variable specification + + Outputs: + + Purpose: Add a template row with a pair of variables as expression. + +\*******************************************************************/ + +void heap_domaint::add_template_row_pair( + const domaint::var_spect &var_spec1, + const domaint::var_spect &var_spec2, + const typet &pointed_type) +{ + const exprt var_pair=and_exprt(var_spec1.var, var_spec2.var); + + templ.push_back(template_rowt()); + template_rowt &templ_row=templ.back(); + templ_row.expr=var_pair; + templ_row.pre_guard=var_spec1.pre_guard; + templ_row.post_guard=var_spec1.post_guard; + templ_row.aux_expr=var_spec1.aux_expr; + templ_row.kind=var_spec1.kind; + + templ_row.mem_kind=STACK; +} + +/*******************************************************************\ + +Function: heap_domaint::to_pre_constraints + + Inputs: + + Outputs: Entry constraints expression for a value. + + Purpose: Create entry constraints as a conjuction of entry + expressions for each template row. + +\*******************************************************************/ + +exprt heap_domaint::to_pre_constraints(const heap_valuet &value) const +{ + assert(value.size()==templ.size()); + exprt::operandst c; + for(rowt row=0; row(value[from]); + heap_row_valuet &heap_val_to=static_cast(value[to]); + + bool result=false; + if(heap_val_from.add_all_paths( + heap_val_to, + std::make_pair(templ[to].dyn_obj, templ[to].expr))) + { + result=true; + } + if(from!=to) + { + if(heap_val_to.add_pointed_by(from)) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::add_points_to + + Inputs: + + Outputs: + + Purpose: Add new object pointed by a row. + Calls add_points_to of the given row. + For stack rows, the destination is simply added into pointed + objects set. + For heap rows, a new path is added. + +\*******************************************************************/ + +bool heap_domaint::add_points_to( + const rowt &row, + heap_valuet &value, + const exprt &dest) +{ + assert(row(value); + for(rowt row=0; row " << std::endl + << " "; + break; + case IN: + out << "(IN) "; + break; + case OUT: + case OUTL: + out << "(OUT) "; + break; + case OUTHEAP: + out << "(HEAP) "; + break; + default: + assert(false); + } + out << "( " << from_expr(ns, "", templ_row.expr) << " == " + << from_expr(ns, "", val[row].get_row_expr(templ_row.expr, false)) + << " )" + << std::endl; + } +} + +/*******************************************************************\ + +Function: heap_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::output_domain(std::ostream &out, const namespacet &ns) const +{ + for(unsigned i=0; i " << std::endl << " "; + break; + case IN: + out << "(IN) "; + out << from_expr(ns, "", templ_row.pre_guard) << " ===> " + << std::endl << " "; + break; + case OUT: + case OUTL: + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) << " ===> " + << std::endl << " "; + break; + case OUTHEAP: + out << "(HEAP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) + << " ] ===> " << std::endl << " "; + break; + default: + assert(false); + } + const vart &var=templ_row.expr; + + const std::string info= + templ_row.mem_kind==STACK + ? " --points_to--> Locations" + : " --paths--> Destinations"; + out << i << ": " << from_expr(ns, "", var) << info << std::endl; + } +} + +/*******************************************************************\ + +Function: heap_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::project_on_vars( + domaint::valuet &value, + const domaint::var_sett &vars, + exprt &result) +{ + const heap_valuet &val=static_cast(value); + assert(val.size()==templ.size()); + + exprt::operandst c; + for(rowt row=0; row(value1); + const heap_valuet &val2=static_cast(value2); + assert(val1.size()==templ.size()); + assert(val2.size()==val1.size()); +} + +/*******************************************************************\ + +Function: heap_domaint::get_symbol_loc + + Inputs: Symbol expression. + + Outputs: Number of location, or -1 if symbol is input. + + Purpose: Get location number of a given symbol. + +\*******************************************************************/ + +int heap_domaint::get_symbol_loc(const exprt &expr) +{ + assert(expr.id()==ID_symbol); + std::string expr_id=id2string(to_symbol_expr(expr).get_identifier()); + if(expr_id.find('#')==std::string::npos) + return -1; + std::string loc_str=expr_id.substr(expr_id.find_last_not_of("0123456789")+1); + assert(!loc_str.empty()); + return std::stoi(loc_str); +} + +/*******************************************************************\ + +Function: ptr_equality + + Inputs: Pointer expression (variable) + Value (object or address) of the pointer + + Outputs: Equality between pointer and its value with correct types + + Purpose: + +\*******************************************************************/ + +const exprt ptr_equality( + const exprt &ptr_expr, + const exprt &ptr_value, + const namespacet &ns) +{ + exprt value; + if(ptr_expr.type()==ptr_value.type()) + value=ptr_value; + else if(ns.follow(ptr_expr.type().subtype())==ns.follow(ptr_value.type())) + value=address_of_exprt(ptr_value); + else + { + value=typecast_exprt( + address_of_exprt(ptr_value), + ns.follow(ptr_expr.type())); + } + return equal_exprt(ptr_expr, value); +} + +/*******************************************************************\ + +Function: heap_domaint::stack_row_valuet::get_row_expr + + Inputs: templ_expr Template expression + + Outputs: Formula corresponding to the template row + + Purpose: Stack row is a disjuction of equalities between templ_expr + and addresses of dynamic objects from points_to set. + +\*******************************************************************/ + +exprt heap_domaint::stack_row_valuet::get_row_expr( + const vart &templ_expr, + bool rename_templ_expr) const +{ + if(nondet) + return true_exprt(); + + if(empty()) + return false_exprt(); + else + { + // Points to expression + exprt::operandst result; + for(const exprt &pt : points_to) + { + if(templ_expr.id()==ID_and) + { + result.push_back( + and_exprt( + ptr_equality(templ_expr.op0(), pt.op0(), ns), + ptr_equality(templ_expr.op1(), pt.op1(), ns))); + } + else + result.push_back(ptr_equality(templ_expr, pt, ns)); + } + return disjunction(result); + } +} + +/*******************************************************************\ + +Function: heap_domaint::stack_row_valuet::add_points_to + + Inputs: + + Outputs: + + Purpose: Add new object to the value of a row. The object is simply + added to the set. + +\*******************************************************************/ + +bool heap_domaint::stack_row_valuet::add_points_to(const exprt &expr) +{ + if(points_to.find(expr)==points_to.end()) + points_to.insert(expr); + else + nondet=true; + return true; +} + +/*******************************************************************\ + +Function: heap_domaint::stack_row_valuet::clear + + Inputs: + + Outputs: + + Purpose: Clear stack row value + +\*******************************************************************/ +void heap_domaint::stack_row_valuet::clear() +{ + nondet=false; + points_to.clear(); +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::get_row_expr + + Inputs: templ_expr Template expression + rename_templ_expr True if templ_expr should be renamed + (the corresponding template row is of + OUTHEAP type) + + Outputs: Formula corresponding to the template row + + Purpose: Heap row is a conjunction of paths. + + nondet is TRUE + empty is FALSE + +\*******************************************************************/ + +exprt heap_domaint::heap_row_valuet::get_row_expr( + const vart &templ_expr_, + bool rename_templ_expr) const +{ + if(nondet) + return true_exprt(); + + exprt templ_expr=templ_expr_; + if(rename_templ_expr) + templ_expr=rename_outheap(to_symbol_expr(templ_expr_)); + + if(paths.empty()) + { + if(self_linkage) + { + return equal_exprt(templ_expr, address_of_exprt(dyn_obj.first)); + } + else + return false_exprt(); + } + else + { + exprt::operandst result; + for(const patht &path : paths) + { // path(o.m, d)[O] + const exprt &dest=templ_expr.type()==path.destination.type() ? + path.destination : address_of_exprt(path.destination); + exprt::operandst path_expr; + + // o.m = d + path_expr.push_back(equal_exprt(templ_expr, dest)); + + for(const dyn_objt &obj1 : path.dyn_objects) + { + // o.m = &o' + exprt equ_exprt=equal_exprt(templ_expr, address_of_exprt(obj1.first)); + + exprt::operandst steps_expr; + exprt member_expr=obj1.second; + // o'.m = d + steps_expr.push_back(equal_exprt(member_expr, dest)); + + for(const dyn_objt &obj2 : path.dyn_objects) + { + // o'.m = o'' + steps_expr.push_back( + equal_exprt( + member_expr, + address_of_exprt(obj2.first))); + } + + path_expr.push_back(and_exprt(equ_exprt, disjunction(steps_expr))); + } + + result.push_back(disjunction(path_expr)); + } + return conjunction(result); + } +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_points_to + + Inputs: + + Outputs: + + Purpose: Add new object to the heap row - create new path or set + self_linkage flag in case the object is same as the row + object. + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_points_to(const exprt &dest) +{ + if(dest==dyn_obj.first) + { + if(!add_self_linkage()) + nondet=true; + } + else + { + const dyn_objt through= + self_linkage ? dyn_obj : std::make_pair(nil_exprt(), nil_exprt()); + if(!add_path(dest, through)) + nondet=true; + } + return true; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_path + + Inputs: dest Path destination + dyn_obj Dynamic object that the path goes through + + Outputs: True if the value was changed (a path was added) + + Purpose: Add new path to the heap row + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_path( + const exprt &dest, + const dyn_objt &dyn_obj) +{ + if(paths.find(dest)==paths.end()) + { + // Path does not exist yet + std::set dyn_obj_set; + if(dyn_obj.first.id()!=ID_nil) + { // Path doesn't have zero length + dyn_obj_set.insert(dyn_obj); + } + if(self_linkage) + { + dyn_obj_set.insert(this->dyn_obj); + } + paths.emplace(dest, dyn_obj_set); + return true; + } + else + { + // Path exists already + if(dyn_obj.first.id()!=ID_nil) + // Try to insert new dynamic object on the path + return paths.find(dest)->dyn_objects.insert(dyn_obj).second; + else + return false; + } +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_all_paths + + Inputs: + + Outputs: True if this has changed + + Purpose: Add all paths from other heap row. + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_all_paths( + const heap_row_valuet &other_val, + const dyn_objt &dyn_obj) +{ + bool result=false; + for(auto &path : other_val.paths) + { + bool new_dest=(paths.find(path.destination)==paths.end()); + if(add_path(path.destination, dyn_obj)) + { + if(!new_dest) + paths.erase(dyn_obj.first); + result=true; + for(auto &o : path.dyn_objects) + { + if(add_path(path.destination, o)) + result=true; + } + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_pointed_by + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_pointed_by(const rowt &row) +{ + auto new_pb=pointed_by.insert(row); + return new_pb.second; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::add_self_linkage + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool heap_domaint::heap_row_valuet::add_self_linkage() +{ + bool result; + result=!self_linkage; + self_linkage=true; + if(result) + { + for(const patht &path : paths) + { + path.dyn_objects.insert(dyn_obj); + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::rename_outheap + + Inputs: expr Expression to be renamed + + Outputs: Renamed expression + + Purpose: Rename OUTHEAP row expression (used for post-expr). + Simply remove 'lb' from suffix. + +\*******************************************************************/ + +exprt heap_domaint::heap_row_valuet::rename_outheap(const symbol_exprt &expr) +{ + const std::string id=id2string(expr.get_identifier()); + return symbol_exprt( + id.substr(0, id.rfind("lb"))+id.substr(id.rfind("lb")+2), + expr.type()); +} + +/*******************************************************************\ + +Function: heap_domaint::heap_row_valuet::clear + + Inputs: + + Outputs: Clear heap row value + + Purpose: + +\*******************************************************************/ +void heap_domaint::heap_row_valuet::clear() +{ + nondet=false; + paths.clear(); +} + +/*******************************************************************\ + +Function: heap_domaint::get_new_heap_vars + + Inputs: + + Outputs: List of variables (symbols) that were added to template + during analysis. + + Purpose: + +\*******************************************************************/ + +const std::list heap_domaint::get_new_heap_vars() +{ + std::list result; + for(const template_rowt &row : templ) + { + if(row.kind==OUTHEAP) + { + assert(row.expr.id()==ID_symbol); + symbol_exprt expr=to_symbol_expr(row.expr); + rename(expr); + result.push_back(expr); + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::initialize_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::initialize_domain( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator) +{ + // Bind list iterators + bind_iterators(SSA, precondition, template_generator); + + // Create preconditions for input variables if not exist + exprt::operandst equs; + for(const symbol_exprt ¶m : SSA.params) + create_precondition(param, precondition); + for(const symbol_exprt &global_in : SSA.globals_in) + create_precondition(global_in, precondition); +} + +/*******************************************************************\ + +Function: heap_domaint::bind_iterators + + Inputs: SSA + precondition Calling context + template_generator + + Outputs: + + Purpose: Bind iterators from SSA to actual heap objects from the + calling context. + +\*******************************************************************/ + +void heap_domaint::bind_iterators( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator) +{ + new_heap_row_specs.clear(); + for(const list_iteratort &iterator : SSA.iterators) + { + for(const list_iteratort::accesst &access : iterator.accesses) + { + exprt access_binding=iterator_access_bindings( + iterator.pointer, + iterator.init_pointer, + iterator.iterator_symbol(), + iterator.fields, + access, + 0, + exprt::operandst(), + precondition, + SSA); + + // Special treatment for first element in the list + // @TODO this should be handled better + if(access.fields.size()>1 && access.location!=list_iteratort::IN_LOC) + { + const std::set first=collect_preconditions_rec( + iterator.init_pointer, + precondition); + for(const exprt &value : first) + { + if(value.id()==ID_address_of) + { + assert(to_address_of_expr(value).object().id()==ID_symbol); + const symbol_exprt &first_obj= + to_symbol_expr(to_address_of_expr(value).object()); + const symbol_exprt new_value= + recursive_member_symbol( + first_obj, + access.fields.back(), + access.location, + ns); + const symbol_exprt old_value= + recursive_member_symbol( + first_obj, + access.fields.back(), + list_iteratort::IN_LOC, + ns); + const exprt binding=equal_exprt(new_value, old_value); + access_binding=or_exprt(access_binding, binding); + + add_new_heap_row_spec( + old_value, + static_cast(access.location), + binding); + } + } + } + + iterator_bindings.push_back(access_binding); + } + } + + // Add template rows for bound heap objects + for(const heap_row_spect &row_spec : new_heap_row_specs) + { + new_output_template_row( + row_spec.expr, + row_spec.location_number, + row_spec.post_guard, + SSA, + template_generator); + } +} + +/*******************************************************************\ + +Function: heap_domaint::new_output_template_row + + Inputs: + + Outputs: + + Purpose: Insert new output template row into the template. + +\*******************************************************************/ + +void heap_domaint::new_output_template_row( + const symbol_exprt &var, + const unsigned location_number, + const exprt &post_guard, + const local_SSAt &SSA, + template_generator_baset &template_generator) +{ + template_generator.var_specs.push_back(domaint::var_spect()); + domaint::var_spect &var_spec=template_generator.var_specs.back(); + + local_SSAt::locationt loc=SSA.get_location(location_number); + + const exprt pre_guard=SSA.guard_symbol(loc); + + const symbol_exprt pre_var= + SSA.name(ssa_objectt(var, SSA.ns), local_SSAt::LOOP_BACK, loc); + const symbol_exprt post_var= + SSA.name(ssa_objectt(var, SSA.ns), local_SSAt::OUT, loc); + + var_spec.var=pre_var; + var_spec.pre_guard=pre_guard; + var_spec.post_guard=post_guard; + var_spec.aux_expr=true_exprt(); + var_spec.kind=OUTHEAP; + + renaming_map[pre_var]=post_var; + + assert(var.type().id()==ID_pointer); + const typet &pointed_type=ns.follow(var.type().subtype()); + add_template_row(var_spec, pointed_type); +} + +/*******************************************************************\ + +Function: heap_domaint::create_precondition + + Inputs: Variable and a calling context (precondition) + + Outputs: + + Purpose: Create precondition for given variable at the input of the + function if it does not exist in given calling context. + +\*******************************************************************/ + +void heap_domaint::create_precondition( + const symbol_exprt &var, + const exprt &precondition) +{ + if(var.type().id()==ID_pointer) + { + auto pre=collect_preconditions_rec(var, precondition); + if(pre.empty() || (pre.size()==1 && *(pre.begin())==var)) + { + if(id2string(var.get_identifier()).find('.')==std::string::npos) + { + // For variables, create abstract address + const symbolt *symbol; + if(ns.lookup(id2string(var.get_identifier()), symbol)) + return; + + address_of_exprt init_value(symbol->symbol_expr()); + init_value.type()=symbol->type; + aux_bindings.push_back(equal_exprt(var, init_value)); + } + else + { + // For members of structs, find corresponding object in the calling + // context and return its member + std::string var_id_str=id2string(var.get_identifier()); + const symbol_exprt pointer( + var_id_str.substr(0, var_id_str.rfind("'obj")), + var.type()); + const irep_idt member=var_id_str.substr(var_id_str.rfind(".")); + + exprt::operandst d; + std::set pointed_objs= + collect_preconditions_rec(pointer, precondition); + for(exprt pointed : pointed_objs) + { + if(pointed.id()==ID_address_of) + { + const exprt pointed_object=to_address_of_expr(pointed).object(); + if(pointed_object.id()==ID_symbol) + { + symbol_exprt pointed_member( + id2string(to_symbol_expr(pointed_object).get_identifier())+ + id2string(member), + var.type()); + d.push_back(equal_exprt(var, pointed_member)); + } + } + } + if(!d.empty()) + { + iterator_bindings.push_back(disjunction(d)); + } + } + } + } +} + +/*******************************************************************\ + +Function: heap_domaint::get_iterator_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt heap_domaint::get_iterator_bindings() const +{ + return conjunction(iterator_bindings); +} + +/*******************************************************************\ + +Function: heap_domaint::get_aux_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt heap_domaint::get_aux_bindings() const +{ + return conjunction(aux_bindings); +} + +/*******************************************************************\ + +Function: heap_domaint::get_input_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt heap_domaint::get_input_bindings() const +{ + return and_exprt(get_iterator_bindings(), get_aux_bindings()); +} + +/*******************************************************************\ + +Function: heap_domaint::iterator_access_bindings + + Inputs: src Source pointer (symbolic value) + init_pointed Actual value of the source pointer + iterator_sym Corresponding iterator symbol + fields Set of fields to follow + access Iterator access to be bound + level Current access level + guards + precondition Calling context + SSA + + Outputs: Formula corresponding to bindings + + Purpose: Create bindings of iterator with corresponding dynamic objects. + Function is called recursively, if there is access with multiple + fields. + +\*******************************************************************/ + +const exprt heap_domaint::iterator_access_bindings( + const symbol_exprt &src, + const exprt &init_pointer, + const symbol_exprt &iterator_sym, + const std::vector &fields, + const list_iteratort::accesst &access, + const unsigned level, + exprt::operandst guards, + const exprt &precondition, + const local_SSAt &SSA) +{ + const std::set reachable= + reachable_objects(init_pointer, fields, precondition); + + exprt::operandst d; + for(const symbol_exprt &r : reachable) + { + exprt::operandst c; + + equal_exprt points_to_eq(src, address_of_exprt(r)); + c.push_back(points_to_eq); + + if(level==0) + { + equal_exprt address_eq( + address_canonizer(address_of_exprt(iterator_sym), ns), + address_of_exprt(r)); + c.push_back(address_eq); + } + + equal_exprt access_eq=access.binding(iterator_sym, r, level, ns); + c.push_back(access_eq); + + guards.push_back(conjunction(c)); + + if(level(access.location), + conjunction(guards)); + } + + guards.pop_back(); + + d.push_back(conjunction(c)); + } + + if(!d.empty()) + return disjunction(d); + else + return true_exprt(); +} + +/*******************************************************************\ + +Function: heap_domaint::reachable_objects + + Inputs: src Source expression + fields Set of fields to follow + precondition Calling context + + Outputs: Set of reachable objects + + Purpose: Find all objects reachable from source via the vector of fields + +\*******************************************************************/ + +const std::set heap_domaint::reachable_objects( + const exprt &src, + const std::vector &fields, + const exprt &precondition) const +{ + std::set result; + + if(!(src.id()==ID_symbol || src.id()==ID_member)) + return result; + + std::set pointed_objs; + if(src.id()==ID_member && to_member_expr(src).compound().get_bool(ID_pointed)) + { + const member_exprt &member=to_member_expr(src); + const exprt pointer= + get_pointer(member.compound(), pointed_level(member.compound())-1); + + std::set r= + reachable_objects(pointer, {member.get_component_name()}, precondition); + pointed_objs.insert(r.begin(), r.end()); + } + else + { + if(src.type().id()==ID_pointer) + { + std::set values=collect_preconditions_rec(src, precondition); + for(const exprt &v : values) + { + if(v.id()==ID_address_of) + { + assert(to_address_of_expr(v).object().id()==ID_symbol); + pointed_objs.insert(to_symbol_expr(to_address_of_expr(v).object())); + } + } + } + else + { + assert(src.type().get_bool("#dynamic")); + pointed_objs.insert(to_symbol_expr(src)); + } + } + + for(std::size_t i=0; i reachable_objs=collect_preconditions_rec( + obj_member, + precondition); + for(const exprt &reachable : reachable_objs) + { + if(reachable.id()==ID_address_of) + { + const exprt &reachable_obj=to_address_of_expr(reachable).object(); + assert(reachable_obj.id()==ID_symbol); + + result.insert(to_symbol_expr(reachable_obj)); + } + } + } + if(i!=fields.size()-1) + pointed_objs=result; + } + + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::add_new_heap_row_spec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_domaint::add_new_heap_row_spec( + const symbol_exprt &expr, + const unsigned location_number, + const exprt &post_guard) +{ + auto it=new_heap_row_specs.emplace(expr, location_number, post_guard); + if(!it.second) + { + if(it.first->post_guard!=post_guard) + it.first->post_guard=or_exprt(it.first->post_guard, post_guard); + } +} + +/*******************************************************************\ + +Function: heap_domaint::collect_preconditions_rec + + Inputs: Expression and calling context (precondition) + + Outputs: Set of preconditions corresponding to given expression. + + Purpose: Recursively find all preconditions for the given expression + in the calling context. + Returns right-hand sides of equalities where expr is left-hand + side. + +\*******************************************************************/ + +const std::set heap_domaint::collect_preconditions_rec( + const exprt &expr, + const exprt &precondition) +{ + std::set result; + if(precondition.id()==ID_equal) + { + const equal_exprt &eq=to_equal_expr(precondition); + if((eq.lhs()==expr && eq.rhs()!=expr) || + (eq.lhs().id()==ID_symbol && + expr.id()==ID_symbol && + to_symbol_expr(eq.lhs()).get_identifier()== + to_symbol_expr(expr).get_identifier())) + { + result.insert(eq.rhs()); + } + } + else + { + forall_operands(it, precondition) + { + std::set op_result=collect_preconditions_rec(expr, *it); + result.insert(op_result.begin(), op_result.end()); + } + } + return result; +} + +/*******************************************************************\ + +Function: heap_domaint::restrict_to_sympath + + Inputs: Symbolic path + + Outputs: + + Purpose: Restrict template to a given symbolic path. + For each template row, we add all other loop guards in their + positive or negative form (as specified by path) to aux_expr. + +\*******************************************************************/ +void heap_domaint::restrict_to_sympath(const symbolic_patht &sympath) +{ + for(auto &row : templ) + { + const exprt c=sympath.get_expr(row.pre_guard.op1()); + row.aux_expr=and_exprt(row.aux_expr, c); + } +} + +/*******************************************************************\ + +Function: heap_domaint::clear_aux_symbols + + Inputs: + + Outputs: + + Purpose: Reset aux symbols to true (remove all restricitions). + +\*******************************************************************/ +void heap_domaint::clear_aux_symbols() +{ + for(auto &row : templ) + row.aux_expr=true_exprt(); +} + +/*******************************************************************\ + +Function: heap_domaint::eliminate_sympaths + + Inputs: Vector of symbolic paths + + Outputs: + + Purpose: Restrict template to other paths than those specified. + +\*******************************************************************/ +void heap_domaint::eliminate_sympaths( + const std::vector &sympaths) +{ + for(auto &row : templ) + { + exprt::operandst paths; + for(auto &sympath : sympaths) + { + const exprt path_expr=sympath.get_expr(row.pre_guard.op1()); + paths.push_back(path_expr); + } + row.aux_expr=paths.empty() + ? true_exprt() + : static_cast(not_exprt(disjunction(paths))); + } +} + +/*******************************************************************\ + +Function: heap_domaint::undo_restriction + + Inputs: + + Outputs: + + Purpose: Undo last restriction (remove last conjunct from each aux_expr). + +\*******************************************************************/ +void heap_domaint::undo_restriction() +{ + for(auto &row : templ) + { + if(row.aux_expr.id()==ID_and) + { + row.aux_expr=to_and_expr(row.aux_expr).op0(); + } + } +} diff --git a/src/domains/heap_domain.h b/src/domains/heap_domain.h new file mode 100644 index 000000000..72d0f3167 --- /dev/null +++ b/src/domains/heap_domain.h @@ -0,0 +1,362 @@ +/*******************************************************************\ + +Module: Abstract domain for representing heap + +Author: Viktor Malik + +\*******************************************************************/ +#ifndef CPROVER_2LS_DOMAINS_HEAP_DOMAIN_H +#define CPROVER_2LS_DOMAINS_HEAP_DOMAIN_H + +#include + +#include +#include + +#include + +#include "domain.h" +#include "template_generator_base.h" + +class heap_domaint:public domaint +{ +public: + typedef unsigned rowt; + // Field of a dynamic object (a variable) + typedef vart member_fieldt; + // We represent dynamic object by the object itself and its member field + typedef std::pair dyn_objt; + + typedef enum { STACK, HEAP } mem_kindt; + + heap_domaint( + unsigned int _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns) + { + make_template(var_specs, ns); + } + + struct template_rowt + { + vart expr; + guardt pre_guard; + guardt post_guard; + exprt aux_expr; + kindt kind; + mem_kindt mem_kind; + exprt dyn_obj; + irep_idt member; + }; + typedef std::vector templatet; + + /*******************************************************************\ + Base class for a value of a row + \*******************************************************************/ + struct row_valuet + { + // Row is nondeterministic - row expression is TRUE + bool nondet=false; + + const namespacet &ns; + + explicit row_valuet(const namespacet &ns):ns(ns) {} + + virtual exprt get_row_expr( + const vart &templ_expr, + bool rename_templ_expr) const=0; + + virtual bool empty() const=0; + + virtual bool add_points_to(const exprt &dest)=0; + + virtual void clear()=0; + + virtual ~row_valuet() {} + }; + + /*******************************************************************\ + Stack row - used for pointer-typed stack objects (variables). + Value is a set of objects that the pointer can point to. + \*******************************************************************/ + struct stack_row_valuet:public row_valuet + { + // Set of objects (or NULL) the row variable can point to + std::set points_to; + + explicit stack_row_valuet(const namespacet &ns):row_valuet(ns) {} + + virtual exprt get_row_expr( + const vart &templ_expr, + bool rename_templ_expr) const override; + + virtual bool add_points_to(const exprt &expr) override; + + virtual bool empty() const override + { + return points_to.empty(); + } + + virtual void clear() override; + }; + + /*******************************************************************\ + Heap row - used for pointer-typed fields of dynamic objects. + + Value is a disjunction of conjunctions of paths leading from the dynamic + object via the field. + \*******************************************************************/ + struct heap_row_valuet:public row_valuet + { + /*******************************************************************\ + Path in a heap. Contains: + - destination object + - set of dynamic objects - set of SSA objects that the path is composed of + + Paths are ordered by destination only as it is unique within a value row. + \*******************************************************************/ + struct patht + { + exprt destination; + mutable std::set dyn_objects; + + patht(const exprt &dest_):destination(dest_) {} // NOLINT(*) + + patht(const exprt &dest_, const std::set &dyn_objs_): + destination(dest_), dyn_objects(dyn_objs_) {} + + bool operator<(const patht &rhs) const + { + return destination paths; + + // Set of rows whose variables point to this row + std::set pointed_by; + + // Dynamic object corresponding to the row (contains both object and field) + dyn_objt dyn_obj; + // Self link on an abstract dynamic object + bool self_linkage=false; + + heap_row_valuet(const namespacet &ns, const dyn_objt &dyn_obj_): + row_valuet(ns), dyn_obj(dyn_obj_) {} + + virtual exprt get_row_expr( + const vart &templ_expr_, + bool rename_templ_expr) const override; + + virtual bool add_points_to(const exprt &dest) override; + + virtual bool empty() const override + { + return paths.empty() && !self_linkage; + } + + virtual void clear() override; + + bool add_path(const exprt &dest, const dyn_objt &dyn_obj); + + bool add_all_paths( + const heap_row_valuet &other_val, + const dyn_objt &dyn_obj); + + bool add_pointed_by(const rowt &row); + + bool add_self_linkage(); + + protected: + static exprt rename_outheap(const symbol_exprt &expr); + }; + + // Heap value is a conjunction of rows + class heap_valuet: + public valuet, + public std::vector> + { + public: + row_valuet &operator[](const rowt &row) const + { + return *(this->at(row).get()); + } + }; + + // Initialize value and domain + virtual void initialize(valuet &value) override; + + void initialize_domain( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator); + + // Value -> constraints + exprt to_pre_constraints(const heap_valuet &value) const; + + void make_not_post_constraints( + const heap_valuet &value, + exprt::operandst &cond_exprs, + exprt::operandst &value_exprs); + + // Row -> constraints + exprt get_row_pre_constraint( + const rowt &row, + const row_valuet &row_value) const; + + exprt get_row_post_constraint(const rowt &row, const row_valuet &row_value); + + // Row modifications + bool add_transitivity(const rowt &from, const rowt &to, heap_valuet &value); + + bool add_points_to(const rowt &row, heap_valuet &value, const exprt &dest); + + bool set_nondet(const rowt &row, heap_valuet &value); + + // Printing + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const override; + + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const override; + + // Projection + virtual void + project_on_vars(valuet &value, const var_sett &vars, exprt &result) override; + + // Conversion of solver value to expression + static exprt value_to_ptr_exprt(const exprt &expr); + + // Join of values + virtual void join(valuet &value1, const valuet &value2) override; + + // Restriction to symbolic paths + void restrict_to_sympath(const symbolic_patht &sympath); + void undo_restriction(); + void eliminate_sympaths(const std::vector &sympaths); + void clear_aux_symbols(); + + // Getters for protected fields + const std::list get_new_heap_vars(); + + exprt get_iterator_bindings() const; + exprt get_aux_bindings() const; + exprt get_input_bindings() const; + + bool empty() const + { + return templ.empty(); + } + +protected: + templatet templ; + + // Bindings computed during interprocedural analysis + exprt::operandst iterator_bindings; + exprt::operandst aux_bindings; + + /*******************************************************************\ + Specification of a new heap row that is added dynamically + at the beginning of the analysis, after binding of iterators to the actual + dynamic objects from the calling context. + + Contains row expression and a location where the corresponding + iterator occured. + \*******************************************************************/ + class heap_row_spect + { + public: + symbol_exprt expr; + unsigned location_number; + + mutable exprt post_guard; + + heap_row_spect( + const symbol_exprt &expr, + unsigned location_number, + const exprt &post_guard): + expr(expr), location_number(location_number), post_guard(post_guard) {} + + bool operator<(const heap_row_spect &rhs) const + { + return std::tie(expr, location_number)< + std::tie(rhs.expr, rhs.location_number); + } + + bool operator==(const heap_row_spect &rhs) const + { + return std::tie(expr, location_number)== + std::tie(rhs.expr, rhs.location_number); + } + }; + + // Set of new heap rows added during analysis (used for interprocedural) + std::set new_heap_row_specs; + + void make_template(const var_specst &var_specs, const namespacet &ns); + + void add_template_row(const var_spect &var_spec, const typet &pointed_type); + void add_template_row_pair( + const var_spect &var_spec1, + const var_spect &var_spec2, + const typet &pointed_type); + + // Initializing functions + void bind_iterators( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator); + + void create_precondition(const symbol_exprt &var, const exprt &precondition); + + void new_output_template_row( + const symbol_exprt &var, + const unsigned location_number, + const exprt &post_guard, + const local_SSAt &SSA, + template_generator_baset &template_generator); + + const exprt iterator_access_bindings( + const symbol_exprt &src, + const exprt &init_pointer, + const symbol_exprt &iterator_sym, + const std::vector &fields, + const list_iteratort::accesst &access, + const unsigned level, + exprt::operandst guards, + const exprt &precondition, + const local_SSAt &SSA); + + const std::set reachable_objects( + const exprt &src, + const std::vector &fields, + const exprt &precondition) const; + + static const std::set collect_preconditions_rec( + const exprt &expr, + const exprt &precondition); + + + void add_new_heap_row_spec( + const symbol_exprt &expr, + const unsigned location_number, + const exprt &post_guard); + + // Utility functions + static int get_symbol_loc(const exprt &expr); + + friend class strategy_solver_heapt; +}; + +#endif // CPROVER_2LS_DOMAINS_HEAP_DOMAIN_H diff --git a/src/domains/heap_tpolyhedra_domain.cpp b/src/domains/heap_tpolyhedra_domain.cpp new file mode 100644 index 000000000..c5bd4a636 --- /dev/null +++ b/src/domains/heap_tpolyhedra_domain.cpp @@ -0,0 +1,172 @@ +/*******************************************************************\ + +Module: Combination of heap and template polyhedra abstract domains + +Author: Viktor Malik + +\*******************************************************************/ + +#include "heap_tpolyhedra_domain.h" + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::initialize + + Inputs: + + Outputs: + + Purpose: Initialize abstract value. + +\*******************************************************************/ + +void heap_tpolyhedra_domaint::initialize(domaint::valuet &value) +{ + heap_tpolyhedra_valuet &v=static_cast(value); + + heap_domain.initialize(v.heap_value); + polyhedra_domain.initialize(v.tpolyhedra_value); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::output_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_tpolyhedra_domaint::output_value( + std::ostream &out, + const domaint::valuet &value, + const namespacet &ns) const +{ + const heap_tpolyhedra_valuet &v= + static_cast(value); + + heap_domain.output_value(out, v.heap_value, ns); + polyhedra_domain.output_value(out, v.tpolyhedra_value, ns); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_tpolyhedra_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const +{ + heap_domain.output_domain(out, ns); + polyhedra_domain.output_domain(out, ns); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void heap_tpolyhedra_domaint::project_on_vars( + domaint::valuet &value, + const domaint::var_sett &vars, + exprt &result) +{ + heap_tpolyhedra_valuet &v=static_cast(value); + + exprt heap_result; + heap_domain.project_on_vars(v.heap_value, vars, heap_result); + exprt tpolyhedra_result; + polyhedra_domain.project_on_vars(v.tpolyhedra_value, vars, tpolyhedra_result); + + result=heap_result; + if(tpolyhedra_result!=true_exprt()) + result=and_exprt(result, tpolyhedra_result); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::restrict_to_sympath + + Inputs: Symbolic path + + Outputs: + + Purpose: Restrict template to a given symbolic path. + +\*******************************************************************/ +void heap_tpolyhedra_domaint::restrict_to_sympath( + const symbolic_patht &sympath) +{ + heap_domain.restrict_to_sympath(sympath); + polyhedra_domain.restrict_to_sympath(sympath); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::clear_aux_symbols + + Inputs: + + Outputs: + + Purpose: Reset aux symbols to true (remove all restricitions). + +\*******************************************************************/ +void heap_tpolyhedra_domaint::clear_aux_symbols() +{ + heap_domain.clear_aux_symbols(); + polyhedra_domain.clear_aux_symbols(); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::eliminate_sympaths + + Inputs: Vector of symbolic paths + + Outputs: + + Purpose: Restrict template to other paths than those specified. + +\*******************************************************************/ +void heap_tpolyhedra_domaint::eliminate_sympaths( + const std::vector &sympaths) +{ + heap_domain.eliminate_sympaths(sympaths); + polyhedra_domain.eliminate_sympaths(sympaths); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_domaint::undo_restriction + + Inputs: + + Outputs: + + Purpose: Undo last restriction. + +\*******************************************************************/ +void heap_tpolyhedra_domaint::undo_restriction() +{ + heap_domain.undo_restriction(); + polyhedra_domain.undo_restriction(); +} diff --git a/src/domains/heap_tpolyhedra_domain.h b/src/domains/heap_tpolyhedra_domain.h new file mode 100644 index 000000000..5c2cb9d68 --- /dev/null +++ b/src/domains/heap_tpolyhedra_domain.h @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Combination of heap and template polyhedra abstract domains + +Author: Viktor Malik + +\*******************************************************************/ +#ifndef CPROVER_2LS_DOMAINS_HEAP_TPOLYHEDRA_DOMAIN_H +#define CPROVER_2LS_DOMAINS_HEAP_TPOLYHEDRA_DOMAIN_H + + +#include "domain.h" +#include "tpolyhedra_domain.h" +#include "heap_domain.h" + +class heap_tpolyhedra_domaint:public domaint +{ +public: + enum polyhedra_kindt + { + INTERVAL, ZONES, OCTAGONS + }; + + heap_domaint heap_domain; + tpolyhedra_domaint polyhedra_domain; + + heap_tpolyhedra_domaint( + unsigned int _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &ns, + const polyhedra_kindt polyhedra_kind): + domaint(_domain_number, _renaming_map, ns), + heap_domain(_domain_number, _renaming_map, var_specs, ns), + polyhedra_domain(_domain_number, _renaming_map, ns) + { + if(polyhedra_kind==INTERVAL) + polyhedra_domain.add_interval_template(var_specs, ns); + else if(polyhedra_kind==ZONES) + { + polyhedra_domain.add_difference_template(var_specs, ns); + polyhedra_domain.add_interval_template(var_specs, ns); + } + } + + class heap_tpolyhedra_valuet:public valuet + { + public: + heap_domaint::heap_valuet heap_value; + tpolyhedra_domaint::templ_valuet tpolyhedra_value; + }; + + virtual void initialize(valuet &value) override; + + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const override; + + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const override; + + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) override; + + // Restriction to symbolic paths + void restrict_to_sympath(const symbolic_patht &sympath); + void undo_restriction(); + void eliminate_sympaths(const std::vector &sympaths); + void clear_aux_symbols(); +}; + +#endif // CPROVER_2LS_DOMAINS_HEAP_TPOLYHEDRA_DOMAIN_H diff --git a/src/domains/heap_tpolyhedra_sympath_domain.cpp b/src/domains/heap_tpolyhedra_sympath_domain.cpp new file mode 100644 index 000000000..c13d3bb9c --- /dev/null +++ b/src/domains/heap_tpolyhedra_sympath_domain.cpp @@ -0,0 +1,83 @@ +/*******************************************************************\ + +Module: Abstract domain for computing invariants in heap-tpolyhedra + domain for different symbolic paths in program. + +Author: Viktor Malik + +\*******************************************************************/ + +#include "heap_tpolyhedra_sympath_domain.h" + +/*******************************************************************\ + +Function: heap_tpolyhedra_sympath_domaint::output_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void heap_tpolyhedra_sympath_domaint::output_value( + std::ostream &out, + const domaint::valuet &value, + const namespacet &ns) const +{ + const heap_tpolyhedra_sympath_valuet &v= + static_cast(value); + for(auto &config : v) + { + out << from_expr(ns, "", config.first) << "==>\n"; + heap_tpolyhedra_domain.output_value(out, config.second, ns); + } +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_sympath_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void heap_tpolyhedra_sympath_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const +{ + heap_tpolyhedra_domain.output_domain(out, ns); +} + +/*******************************************************************\ + +Function: heap_tpolyhedra_sympath_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void heap_tpolyhedra_sympath_domaint::project_on_vars( + domaint::valuet &value, + const domaint::var_sett &vars, + exprt &result) +{ + heap_tpolyhedra_sympath_valuet &v= + static_cast(value); + exprt::operandst c; + for(auto &config : v) + { + exprt config_result; + heap_tpolyhedra_domain.project_on_vars(config.second, vars, config_result); + c.push_back(and_exprt(config.first, config_result)); + } + c.push_back(no_loops_path); + + result=c.empty() ? true_exprt() : disjunction(c); +} diff --git a/src/domains/heap_tpolyhedra_sympath_domain.h b/src/domains/heap_tpolyhedra_sympath_domain.h new file mode 100644 index 000000000..3e2ddadb1 --- /dev/null +++ b/src/domains/heap_tpolyhedra_sympath_domain.h @@ -0,0 +1,70 @@ +/*******************************************************************\ + +Module: Abstract domain for computing invariants in heap-tpolyhedra + domain for different symbolic paths in program. + +Author: Viktor Malik + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_HEAP_TPOLYHEDRA_SYMPATH_DOMAIN_H +#define CPROVER_2LS_DOMAINS_HEAP_TPOLYHEDRA_SYMPATH_DOMAIN_H + + +#include "domain.h" +#include "heap_tpolyhedra_domain.h" + +class heap_tpolyhedra_sympath_domaint:public domaint +{ +public: + heap_tpolyhedra_domaint heap_tpolyhedra_domain; + + heap_tpolyhedra_sympath_domaint( + unsigned int _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const local_SSAt &SSA, + const heap_tpolyhedra_domaint::polyhedra_kindt polyhedra_kind): + domaint(_domain_number, _renaming_map, SSA.ns), + heap_tpolyhedra_domain( + _domain_number, _renaming_map, var_specs, SSA.ns, polyhedra_kind) + { + exprt::operandst false_loop_guards; + for(auto &g : SSA.loop_guards) + false_loop_guards.push_back(not_exprt(g.first)); + no_loops_path=conjunction(false_loop_guards); + } + + // Value is a map from expression (symbolic path) to an invariant in heap + // tpolyhedra domain + class heap_tpolyhedra_sympath_valuet: + public valuet, + public std::map + { + }; + + void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const override; + + void output_domain( + std::ostream &out, + const namespacet &ns) const override; + + void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) override; + +protected: + // Special path containing conjunction negations of all loop-select guards + // This must be stored explicitly since the solver is not able to explore this + // path even though it can be feasible + exprt no_loops_path; + + friend class strategy_solver_heap_tpolyhedra_sympatht; +}; + + +#endif // CPROVER_2LS_DOMAINS_HEAP_TPOLYHEDRA_SYMPATH_DOMAIN_H diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 08f5b1ae2..b4a0599c4 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -203,8 +203,10 @@ static inline incremental_solvert &operator<<( *dest.solver << src; #else if(!dest.activation_literals.empty()) - dest.debug_add_to_formula( - or_exprt(src, literal_exprt(!dest.activation_literals.back()))); + { + literal_exprt act_lit(!dest.activation_literals.back()); + dest.debug_add_to_formula(or_exprt(src, act_lit)); + } else dest.debug_add_to_formula(src); #endif diff --git a/src/domains/lexlinrank_domain.cpp b/src/domains/lexlinrank_domain.cpp index 0fc5aa5ea..60424d65e 100644 --- a/src/domains/lexlinrank_domain.cpp +++ b/src/domains/lexlinrank_domain.cpp @@ -611,16 +611,18 @@ void lexlinrank_domaint::project_on_vars( if(is_row_value_false(v[row])) { // (g=> false) - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), - false_exprt())); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + false_exprt())); } else if(is_row_value_true(v[row])) { // (g=> true) - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), - true_exprt())); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + true_exprt())); } else { diff --git a/src/domains/linrank_domain.cpp b/src/domains/linrank_domain.cpp index 064ce3859..fe87be000 100644 --- a/src/domains/linrank_domain.cpp +++ b/src/domains/linrank_domain.cpp @@ -457,8 +457,10 @@ void linrank_domaint::project_on_vars( else if(is_row_value_false(v[row])) { // (g=> false) - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), false_exprt())); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + false_exprt())); } else { @@ -504,9 +506,10 @@ void linrank_domaint::project_on_vars( #endif exprt decreasing=binary_relation_exprt(sum_pre, ID_gt, sum_post); #endif - c.push_back(implies_exprt( - and_exprt(templ[row].pre_guard, templ[row].post_guard), - decreasing)); + c.push_back( + implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), + decreasing)); } } result=conjunction(c); diff --git a/src/domains/list_iterator.cpp b/src/domains/list_iterator.cpp new file mode 100644 index 000000000..cdd70b94e --- /dev/null +++ b/src/domains/list_iterator.cpp @@ -0,0 +1,174 @@ +/*******************************************************************\ + +Module: List iterator - abstraction for iterative access to a linked + list. + +Author: Viktor Malik + +\*******************************************************************/ + +#include +#include +#include "list_iterator.h" + +/*******************************************************************\ + +Function: list_iteratort::add_access + + Inputs: + + Outputs: + + Purpose: Add new access to the iterator corresponding to + an expression from SSA. + +\*******************************************************************/ + +void list_iteratort::add_access( + const member_exprt &expr, + unsigned location_number) const +{ + assert( + expr.compound().get_bool(ID_iterator) && + expr.compound().get_bool(ID_pointed)); + + accesst access; + access.location=location_number; + + unsigned level=pointed_level(expr.compound()); + unsigned iterator_level=it_value_level(expr.compound()); + for(unsigned l=iterator_level; l +#include +#include + +class list_iteratort +{ +public: + // No location (used for input variables) + static const unsigned IN_LOC=std::numeric_limits::max(); + + /*******************************************************************\ + Access to an object from a list iterator. + Contains: + - sequence of fields that are used to access the object from the + iterator + - location: + IN_LOC for read access + location number for write access + \*******************************************************************/ + class accesst + { + public: + std::vector fields; + unsigned location; + + equal_exprt binding( + const symbol_exprt &lhs, + const symbol_exprt &rhs, + const unsigned level, + const namespacet &ns) const; + }; + + // Pointer variable used to iterate the list (induction pointer) + symbol_exprt pointer; + // Initial value of the induction pointer (before the first iteration) + exprt init_pointer; + // Set of fields through which a step is done after each iteration + std::vector fields; + mutable std::list accesses; + + list_iteratort( + const symbol_exprt &pointer, + const exprt &init_pointer, + const std::vector &fields): + pointer(pointer), init_pointer(init_pointer), fields(fields) {} + + bool operator<(const list_iteratort &rhs) const + { + return std::tie(pointer, fields)(domain), solver, SSA.ns) #if 0 +// NOLINTNEXTLINE(*) #define BINSEARCH_SOLVER strategy_solver_binsearch2t(\ *static_cast(domain), solver, SSA.ns) +// NOLINTNEXTLINE(*) #define BINSEARCH_SOLVER strategy_solver_binsearch3t(\ *static_cast(domain), solver, SSA, SSA.ns) #endif @@ -103,6 +110,51 @@ void ssa_analyzert::operator()( *static_cast(domain), solver, SSA.ns); result=new equality_domaint::equ_valuet(); } + else if(template_generator.options.get_bool_option("heap")) + { + strategy_solver=new strategy_solver_heapt( + *static_cast(domain), + solver, + SSA, + precondition, + get_message_handler(), + template_generator); + result=new heap_domaint::heap_valuet(); + } + else if(template_generator.options.get_bool_option("heap-interval") + || template_generator.options.get_bool_option("heap-zones")) + { + if(template_generator.options.get_bool_option("sympath")) + { + strategy_solver=new strategy_solver_heap_tpolyhedra_sympatht( + *static_cast(domain), + solver, + SSA, + precondition, + get_message_handler(), + template_generator); + result= + new heap_tpolyhedra_sympath_domaint::heap_tpolyhedra_sympath_valuet(); + } + else + { + strategy_solver=new strategy_solver_heap_tpolyhedrat( + *static_cast(domain), + solver, + SSA, + precondition, + get_message_handler(), + template_generator); + result=new heap_tpolyhedra_domaint::heap_tpolyhedra_valuet(); + } + } + else if(template_generator.options.get_bool_option("disjunctive-intervals") || + template_generator.options.get_bool_option("disjunctive-zones") || + template_generator.options.get_bool_option("disjunctive-octagons")) + { + strategy_solver=new strategy_solver_disjunctivet(*static_cast(domain),solver,SSA,SSA.ns,template_generator); + result=new disjunctive_domaint::disjunctive_valuet(); + } else { if(template_generator.options.get_bool_option("enum-solver")) @@ -160,3 +212,38 @@ void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) { domain->project_on_vars(*result, vars, _result); } + +/*******************************************************************\ + +Function: ssa_analyzert::update_heap_out + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void ssa_analyzert::update_heap_out(summaryt::var_sett &out) +{ + heap_domaint &heap_domain=static_cast(*domain); + + auto new_heap_vars=heap_domain.get_new_heap_vars(); + out.insert(new_heap_vars.begin(), new_heap_vars.end()); +} + +/*******************************************************************\ + +Function: ssa_analyzert::input_heap_bindings + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +const exprt ssa_analyzert::input_heap_bindings() +{ + return static_cast(*domain).get_iterator_bindings(); +} diff --git a/src/domains/ssa_analyzer.h b/src/domains/ssa_analyzer.h index 1758f4711..5398e883d 100644 --- a/src/domains/ssa_analyzer.h +++ b/src/domains/ssa_analyzer.h @@ -11,6 +11,7 @@ Author: Peter Schrammel #include +#include #include #include "strategy_solver_base.h" @@ -43,6 +44,9 @@ class ssa_analyzert:public messaget void get_result(exprt &result, const domaint::var_sett &vars); + void update_heap_out(summaryt::var_sett &out); + const exprt input_heap_bindings(); + inline unsigned get_number_of_solver_instances() { return solver_instances; } inline unsigned get_number_of_solver_calls() { return solver_calls; } diff --git a/src/domains/strategy_solver_base.cpp b/src/domains/strategy_solver_base.cpp index edf2329ba..c43d17b86 100644 --- a/src/domains/strategy_solver_base.cpp +++ b/src/domains/strategy_solver_base.cpp @@ -8,3 +8,35 @@ Author: Peter Schrammel #include "strategy_solver_base.h" +/*******************************************************************\ + +Function: strategy_solver_baset::find_symbolic_path + + Inputs: + + Outputs: + + Purpose: Find symbolic path that is explored by the current solver + iteration. A path is specified by a conjunction of literals + containing loop-select guards of all loops in program. + +\*******************************************************************/ +void strategy_solver_baset::find_symbolic_path( + std::set> &loop_guards, + const exprt ¤t_guard) +{ + for(const auto &guard : loop_guards) + { + if(guard.first==current_guard) + { + symbolic_path[guard.first]=true; + continue; + } + exprt ls_guard_value=solver.get(guard.first); + exprt lh_guard_value=solver.get(guard.second); + if(ls_guard_value.is_true() && lh_guard_value.is_true()) + symbolic_path[guard.first]=true; + else if(ls_guard_value.is_false()) + symbolic_path[guard.first]=false; + } +} diff --git a/src/domains/strategy_solver_base.h b/src/domains/strategy_solver_base.h index 2e33c0946..7915b5ef9 100644 --- a/src/domains/strategy_solver_base.h +++ b/src/domains/strategy_solver_base.h @@ -12,6 +12,7 @@ Author: Peter Schrammel #include "domain.h" #include "incremental_solver.h" #include "util.h" +#include "symbolic_path.h" class strategy_solver_baset:public messaget { @@ -34,6 +35,8 @@ class strategy_solver_baset:public messaget inline unsigned get_number_of_solver_calls() { return solver_calls; } inline unsigned get_number_of_solver_instances() { return solver_instances; } + symbolic_patht symbolic_path; + protected: incremental_solvert &solver; const namespacet &ns; @@ -45,6 +48,10 @@ class strategy_solver_baset:public messaget // statistics for additional solvers unsigned solver_instances; unsigned solver_calls; + + void find_symbolic_path( + std::set> &loop_guards, + const exprt ¤t_guard=nil_exprt()); }; #endif diff --git a/src/domains/strategy_solver_disjunctive.cpp b/src/domains/strategy_solver_disjunctive.cpp new file mode 100644 index 000000000..88b07a1c0 --- /dev/null +++ b/src/domains/strategy_solver_disjunctive.cpp @@ -0,0 +1,782 @@ +/*******************************************************************\ + +Module: Strategy solver for disjunctive domains + +Author: Johanan Wahlang + +\*******************************************************************/ + +#include "strategy_solver_disjunctive.h" +#include "strategy_solver_enumeration.h" + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool strategy_solver_disjunctivet::iterate( + strategy_solver_baset::invariantt &_inv) +{ + // only iterate on loops for now + assert(template_generator.loop_present); + + disjunctive_domaint::disjunctive_valuet &inv= + static_cast(_inv); + + bool improved=false; + + if (disjunctive_domain.template_kind==disjunctive_domaint::TPOLYHEDRA) + { + tpolyhedra_domaint *domain=static_cast(disjunctive_domain.base_domain()); + + // initial strategy + if (inv.size()==0) + { + tpolyhedra_domaint::templ_valuet result; + domain->initialize(result); + strategy_solver_enumerationt strategy_solver( + *domain,solver,ns); + strategy_solver.iterate(result); + add_new_replication(inv,0,result); + } + + disjunctive_domaint::unresolved_edget e=get_unresolved_edge(inv); + if (e.disjunct==inv.size()) + { + // no more unresolved edges + return improved; + } + + improved=true; // found an unresolved edge + + disjunctive_domaint::disjunctt src=e.disjunct; + disjunctive_domaint::disjunctt sink; + symbolic_patht path=e.path; + + tpolyhedra_domaint::templ_valuet pre=*static_cast(inv[src]); + tpolyhedra_domaint::templ_valuet *post=new tpolyhedra_domaint::templ_valuet(); + domain->initialize(*post); + + get_post(path,pre,*post); + + sink=disjunctive_domain.merge_heuristic(inv, *post); + + if (sink==inv.size()) + { + add_new_replication(inv,sink,*post); + } + else + { + domain->join(*inv[sink],*post); // join value + } + + add_edge(src,path,sink); // add SSA nodes & new templates + + // while (iterate_binsearch(inv)) { } + } + else + { + // TODO: implement disjuntive strategy solver for other base domains + assert(false); + } + + return improved; +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::add_new_replication + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_disjunctivet::add_new_replication( + disjunctive_domaint::disjunctive_valuet &inv, + const disjunctive_domaint::disjunctt d, + const invariantt &value) +{ + if (disjunctive_domain.template_kind==disjunctive_domaint::TPOLYHEDRA) + { + inv.push_back( + new tpolyhedra_domaint::templ_valuet( + static_cast(value))); + + add_loophead(d); // SSA loophead for new disjunct + + for (auto path : all_paths) + { + disjunctive_domaint::unresolved_edget e(d,path); + disjunctive_domain.unresolved_set.push_back(e); + } + } + else + { + assert(false); + } +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::get_unresolved_edge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +disjunctive_domaint::unresolved_edget +strategy_solver_disjunctivet::get_unresolved_edge( + const disjunctive_domaint::disjunctive_valuet &value) +{ + disjunctive_domaint::unresolved_edget e(value.size(),symbolic_patht()); + for (auto it=disjunctive_domain.unresolved_set.begin(); + it!=disjunctive_domain.unresolved_set.end();) + { + solver.new_context(); + disjunctive_domaint::disjunctt d=it->disjunct; + symbolic_patht p=it->path; + + if (disjunctive_domain.template_kind==disjunctive_domaint::TPOLYHEDRA) + { + tpolyhedra_domaint *domain=static_cast(disjunctive_domain.base_domain()); + tpolyhedra_domaint::templ_valuet *v=static_cast(value[d]); + debug() << "Disjunct pre-constraint: " << eom; + debug() << from_expr(domain->to_pre_constraints(*v)) << eom << eom; + solver << domain->to_pre_constraints(*v); + } + debug() << "Path: " << from_expr(p.get_expr()) << eom; + solver<(_domain)); + tpolyhedra_domaint::templ_valuet &pre_inv= + static_cast(_pre_inv); + tpolyhedra_domaint::templ_valuet &post_inv= + static_cast(_post_inv); + domain.restrict_to_sympath(p); + + + domain.output_value(debug(),post_inv,ns); + debug() << "-------------------------------------------------" << eom << eom; + // strategy solver enumeration + solver.new_context(); + + std::cout << "Setting the loop select guard to true" << std::endl; + // std::cout << "pre guard: " << from_expr(domain.templ.begin()->pre_guard.op1()) << std::endl; + solver << domain.templ.begin()->pre_guard.op1(); + + exprt preinv_expr=domain.to_pre_constraints(pre_inv); + #ifdef DEBUG_OUTPUT + debug() << "pre-inv: " << from_expr(ns, "", preinv_expr) << eom; + #endif + + solver << preinv_expr; + + exprt::operandst strategy_cond_exprs; + domain.make_not_post_constraints( + post_inv, strategy_cond_exprs, strategy_value_exprs); + + strategy_cond_literals.resize(strategy_cond_exprs.size()); + + exprt postinv_expr=disjunction(strategy_cond_exprs); + + #ifdef DEBUG_OUTPUT + debug() << "post-inv: "; + #endif + for(std::size_t i=0; i0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); + #endif + + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); + } + #ifdef DEBUG_OUTPUT + debug() << eom; + #endif + + solver << disjunction(strategy_cond_exprs); + + #ifdef DEBUG_OUTPUT + debug() << "solve(): "; + #endif + + if(solver()==decision_proceduret::D_SATISFIABLE) + { + #ifdef DEBUG_OUTPUT + debug() << "SAT" << eom; + #endif + + #ifdef DEBUG_OUTPUT + for(std::size_t i=0; i vars; + find_symbols(preinv_expr, vars); + + for(const auto &var : vars) + { + debug() << "var: " << from_expr(ns, "", var) << "=" + << from_expr(ns, "", solver.get(var)) << eom; + } + } + for(std::size_t i=0; i vars; + find_symbols(strategy_value_exprs[i], vars); + + for(const auto &var : vars) + { + debug() << "var: " << from_expr(ns, "", var) << "=" + << from_expr(ns, "", solver.get(var)) << eom; + } + } + #endif + + for(std::size_t row=0; rowis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } + #endif + } + solver.pop_context(); + + + // strategy_solver_enumerationt strategy_solver( + // domain,solver,ns); + // strategy_solver.iterate(post_inv); + + + domain.output_value(debug(),post_inv,ns); + debug() << "--------------------------------------------------" << eom << eom; + domain.undo_restriction(); + } + else + { + assert(false); + } +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::enumerate_all_paths + + Inputs: + + Outputs: + + Purpose: Enumerate all paths inside the loop + +\*******************************************************************/ + +void strategy_solver_disjunctivet::enumerate_all_paths(guardst &guards) +{ + for (auto &guard : guards) + { + if (all_paths.empty()) + { + symbolic_patht p; + p.path_map[guard] = true; + all_paths.push_back(p); + p.path_map[guard] = false; + all_paths.push_back(p); + } + else + { + std::vector new_paths; + for (auto &path : all_paths) + { + symbolic_patht path_(path); + path.path_map[guard] = true; + path_.path_map[guard] = false; + new_paths.push_back(path_); + } + for (auto &path : new_paths) + { + all_paths.push_back(path); + } + } + } +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::find_loop + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool strategy_solver_disjunctivet::find_loop( + local_SSAt::locationt &loophead_loc, loopt *loop) +{ + local_SSAt::nodest::iterator n_it=SSA.find_node(loophead_loc); + if (n_it==SSA.nodes.end()) + return false; + loop->body_nodes.push_back(*n_it); + auto &node=loop->body_nodes.back(); + for (local_SSAt::nodet::equalitiest::iterator eq_it=node.equalities.begin(); + eq_it!=node.equalities.end();eq_it++) + { + std::string id=id2string(eq_it->lhs().get(ID_identifier)); + if (id.find("phi")!=id.npos) + { + eq_it->rhs()=eq_it->rhs().op1(); // remove loop select & init + } + loop->add_loophead_objects(*eq_it); + } + + for (n_it++;n_it->loophead->location!=loophead_loc;n_it++) + { + loop->body_nodes.push_back(*n_it); + } + + return true; +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::loopt::add_loophead_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_disjunctivet::loopt::add_loophead_objects(exprt expr) +{ + if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol) + { + irep_idt id=expr.get(ID_identifier); + if (find_loophead_object(id)==loophead_objects.end()) + loophead_objects.push_back(id); + } + Forall_operands(it, expr) + add_loophead_objects(*it); +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::loopt::find_loophead_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::vector::iterator + strategy_solver_disjunctivet::loopt::find_loophead_object( + const irep_idt &id) +{ + std::vector::iterator it=loophead_objects.begin(); + for (;it!=loophead_objects.end();it++) + { + if (*it==id) + break; + } + return it; +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_disjunctivet::rename( + exprt &expr, + const std::string &src_suffix="", + const std::string &sink_suffix="") +{ + if(expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol) + { + irep_idt id=expr.get(ID_identifier); + if (loop->find_loophead_object(id)!=loop->loophead_objects.end()) + { + expr.set(ID_identifier,id2string(id)+src_suffix); + } + else + { + expr.set(ID_identifier,id2string(id)+sink_suffix); + } + } + Forall_operands(it, expr) + rename(*it,src_suffix,sink_suffix); +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::add_loophead + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_disjunctivet::add_loophead( + disjunctive_domaint::disjunctt d) +{ + local_SSAt::nodest::iterator n_it=loop->body_nodes.begin(); + loopheads->push_back(*n_it); + local_SSAt::nodet &node=loopheads->back(); + for (auto &eq:node.equalities) + { + rename(eq,"_"+std::to_string(d),""); + solver << eq; + } +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::add_edge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_disjunctivet::add_edge( + disjunctive_domaint::disjunctt src, + const symbolic_patht &path, + disjunctive_domaint::disjunctt sink) +{ + debug() << "Adding new SSA nodes" << eom; + + local_SSAt::nodest::iterator n_it=loop->body_nodes.begin(); + std::string sink_suffix="_"+std::to_string(current_count); + std::string src_suffix="_"+std::to_string(src); + for (n_it++;n_it!=loop->body_nodes.end();n_it++) + { + if (n_it->equalities.empty() && + n_it->constraints.empty() && + n_it->function_calls.empty()) + continue; + + loop_copies->push_back(*n_it); + auto &node=loop_copies->back(); + for (local_SSAt::nodet::equalitiest::iterator e_it=node.equalities.begin(); + e_it!=node.equalities.end();e_it++) + { + rename(*e_it,src_suffix,sink_suffix); + solver << *e_it; + } + for (local_SSAt::nodet::constraintst::iterator c_it=node.constraints.begin(); + c_it!=node.constraints.end();c_it++) + { + rename(*c_it,src_suffix,sink_suffix); + solver << *c_it; + } + for (local_SSAt::nodet::function_callst::iterator f_it=node.function_calls.begin(); + f_it!=node.function_calls.end();f_it++) + { + rename(*f_it,src_suffix,sink_suffix); + } + } + + for (auto &node:*loopheads) + { + for (auto &eq:node.equalities) + { + debug() << "(E) " << from_expr(eq) << eom; + } + debug() << eom; + } + + for (auto &node:*loop_copies) + { + for (auto &eq:node.equalities) + { + debug() << "(E) " << from_expr(eq) << eom; + } + debug() << eom; + } + + // add new edge to seen set + disjunctive_domaint::seen_edget new_edge(src,path,sink); + disjunctive_domain.seen_set.push_back(new_edge); + + // add new template corresponding to new edge + debug() << "Adding new templates" << eom; + + if (disjunctive_domain.template_kind==disjunctive_domaint::TPOLYHEDRA) + { + tpolyhedra_domaint *base_domain=static_cast(disjunctive_domain.base_domain()); + // replace_mapt new_renaming_map; // renaming map for new domain + replace_mapt map; // map from base domain exprts to new domain exprts + for (auto &x:disjunctive_domain.renaming_map) + { + exprt pre_var=x.first; + exprt post_var=x.second; + renaming_map[pre_var]=post_var; // keep old renaming map for non-LOOP vars + rename(pre_var,src_suffix,sink_suffix); + rename(post_var,src_suffix,sink_suffix); + renaming_map[pre_var]=post_var; + map[x.first]=pre_var; + } + + tpolyhedra_domaint *new_domain=new tpolyhedra_domaint(disjunctive_domain.domain_number,renaming_map,ns); + + for (auto &row:base_domain->templ) + { + exprt pre_guard=row.pre_guard; + exprt aux_expr=row.aux_expr; + exprt post_guard=row.post_guard; + exprt expr=row.expr; + if (row.kind==tpolyhedra_domaint::kindt::LOOP) + { + if (map.find(row.pre_guard)==map.end()) + { + rename(pre_guard,src_suffix,sink_suffix); + map[row.pre_guard]=pre_guard; + } + if (map.find(row.aux_expr)==map.end()) + { + rename(aux_expr,src_suffix,sink_suffix); + map[row.aux_expr]=aux_expr; + } + if (map.find(row.post_guard)==map.end()) + { + rename(post_guard,src_suffix,sink_suffix); + map[row.post_guard]=post_guard; + } + replace_expr(map,expr); + pre_guard=map[row.pre_guard]; + post_guard=map[row.post_guard]; + aux_expr=map[row.aux_expr]; + } + new_domain->add_template_row(expr,pre_guard,post_guard,aux_expr,row.kind); + } + + // restrict new domain to symbolic path + symbolic_patht path_; + for (auto p:path.path_map) + { + exprt guard=p.first; + rename(guard,src_suffix,sink_suffix); + path_.path_map[guard]=p.second; + } + new_domain->restrict_to_sympath(path_); + + // domains are sorted by sink, then source + disjunctive_domain.templ[sink][src]=new_domain; + + disjunctive_domain.output_domain(debug(),ns); + debug() << eom; + } + else + { + assert(false); + } + + current_count++; +} + +/*******************************************************************\ + +Function: strategy_solver_disjunctivet::iterate_binsearch + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool strategy_solver_disjunctivet::iterate_binsearch( + disjunctive_domaint::disjunctive_valuet &inv) +{ + // tpolyhedra_domaint::templ_valuet &inv= + // static_cast(_inv); + + bool improved=false; + + solver.new_context(); // for improvement check + + exprt inv_expr=disjunctive_domain.to_pre_constraints(inv); + +#if 1 + debug() << "improvement check: " << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; +#endif + + solver << inv_expr; + + disjunctive_domaint::disjunctive_exprst disjunctive_strategy_cond_exprs; + disjunctive_domain.make_not_post_constraints( + inv, disjunctive_strategy_cond_exprs, disjunctive_strategy_value_exprs); + + // disjunctive_strategy_cond_literals.resize(disjunctive_strategy_cond_exprs.size()); +#if 1 + debug() << "post-inv: "; +#endif + exprt::operandst c; + for (auto &x:disjunctive_strategy_cond_exprs) + { + unsigned sink=x.first; + for (auto &y:x.second) + { + unsigned src=y.first; + + exprt::operandst &strategy_value_exprs=disjunctive_strategy_value_exprs[sink][src]; + exprt::operandst &strategy_cond_exprs=y.second; + disjunctive_strategy_cond_literals[sink][src].resize(strategy_cond_exprs.size()); + bvt &strategy_cond_literals=disjunctive_strategy_cond_literals[sink][src]; + for (std::size_t i=0;i0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); +#endif + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); + } + + c.push_back(disjunction(strategy_cond_exprs)); + } + } + solver << disjunction(c); +#if 1 + debug() << eom; +#endif + +#if 0 + debug() << "solve(): "; +#endif + + if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + {} + else + {} + + return improved; +} \ No newline at end of file diff --git a/src/domains/strategy_solver_disjunctive.h b/src/domains/strategy_solver_disjunctive.h new file mode 100644 index 000000000..a098e8dc2 --- /dev/null +++ b/src/domains/strategy_solver_disjunctive.h @@ -0,0 +1,99 @@ +/*******************************************************************\ + +Module: Strategy solver for disjunctive domains + +Author: Johanan Wahlang + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_DISJUNCTIVE_DOMAIN_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_DISJUNCTIVE_DOMAIN_H + +#include +#include "strategy_solver_base.h" +#include "disjunctive_domain.h" +#include "template_generator_base.h" + +class strategy_solver_disjunctivet:public strategy_solver_baset +{ +public: + class loopt + { + public: + loopt(): + body_nodes() + { + } + local_SSAt::nodest body_nodes; + std::vector loophead_objects; + std::vector::iterator find_loophead_object(const irep_idt &id); + void add_loophead_objects(exprt expr); + }; + + typedef std::vector guardst; + + strategy_solver_disjunctivet( + disjunctive_domaint &_disjunctive_domain, + incremental_solvert &_solver, + local_SSAt &_SSA, + const namespacet &_ns, + template_generator_baset &_template_generator): + strategy_solver_baset(_solver, _ns), + disjunctive_domain(_disjunctive_domain), + SSA(_SSA), + template_generator(_template_generator), + current_count(0), + renaming_map(disjunctive_domain.renaming_map) + { + enumerate_all_paths(template_generator.guards); + + assert(template_generator.loop_present); + + loop=new loopt(); + assert(find_loop(template_generator.loophead_loc,loop)); + + loop_copies=new local_SSAt::nodest(); + loopheads=new local_SSAt::nodest(); + } + + virtual bool iterate(invariantt &inv); + +protected: + disjunctive_domaint &disjunctive_domain; + local_SSAt &SSA; + template_generator_baset &template_generator; + std::vector all_paths; + local_SSAt::nodest *loop_copies; + local_SSAt::nodest *loopheads; + loopt *loop; + unsigned int current_count; + replace_mapt renaming_map; // renaming map for new domains + + // handles on values to retrieve from model + disjunctive_domaint::disjunctive_literalst disjunctive_strategy_cond_literals; + disjunctive_domaint::disjunctive_exprst disjunctive_strategy_value_exprs; + + void enumerate_all_paths(guardst &guards); + void add_new_replication( + disjunctive_domaint::disjunctive_valuet &inv, + const disjunctive_domaint::disjunctt d, + const invariantt &value); + disjunctive_domaint::unresolved_edget get_unresolved_edge( + const disjunctive_domaint::disjunctive_valuet &value); + void get_post( + const symbolic_patht &p, + invariantt &pre_inv, + invariantt &post_inv); + bool find_loop(local_SSAt::locationt &loophead_loc, loopt *loop); + void rename(exprt &expr, + const std::string &src_suffix, + const std::string &sink_suffix); + void add_loophead(disjunctive_domaint::disjunctt d); + void add_edge( + disjunctive_domaint::disjunctt src, + const symbolic_patht &p, + disjunctive_domaint::disjunctt sink); + bool iterate_binsearch(disjunctive_domaint::disjunctive_valuet &inv); +}; + +#endif //CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_DISJUNCTIVE_DOMAIN_H \ No newline at end of file diff --git a/src/domains/strategy_solver_heap.cpp b/src/domains/strategy_solver_heap.cpp new file mode 100644 index 000000000..faf3c1a5f --- /dev/null +++ b/src/domains/strategy_solver_heap.cpp @@ -0,0 +1,505 @@ +/*******************************************************************\ + +Module: Strategy solver for heap shape analysis + +Author: Viktor Malik + +\*******************************************************************/ + +// #define DEBUG_OUTPUT + +#include +#include +#include "strategy_solver_heap.h" + +/*******************************************************************\ + +Function: strategy_solver_heapt::iterate + + Inputs: + + Outputs: + + Purpose: Single iteration of invariant inference using heap shape + domain. + +\*******************************************************************/ + +bool strategy_solver_heapt::iterate(invariantt &_inv) +{ + heap_domaint::heap_valuet &inv=static_cast(_inv); + + bool improved=false; + + solver.new_context(); + + // Entry value constraints + exprt pre_expr=heap_domain.to_pre_constraints(inv); +#ifdef DEBUG_OUTPUT + debug() << "pre-inv: " << from_expr(ns, "", pre_expr) << eom; +#endif + solver << pre_expr; + + // Exit value constraints + exprt::operandst strategy_cond_exprs; + heap_domain.make_not_post_constraints( + inv, + strategy_cond_exprs, + strategy_value_exprs); + + strategy_cond_literals.resize(strategy_cond_exprs.size()); + +#ifdef DEBUG_OUTPUT + debug() << "post-inv: "; +#endif + for(unsigned i=0; i0 ? " || " : "") + << from_expr(ns, "", strategy_cond_exprs[i]); +#endif + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); + } +#ifdef DEBUG_OUTPUT + debug() << eom; +#endif + solver << disjunction(strategy_cond_exprs); + +#ifdef DEBUG_OUTPUT + debug() << "solve(): "; +#endif + + if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { +#ifdef DEBUG_OUTPUT + debug() << "SAT" << eom; +#endif + +#ifdef DEBUG_OUTPUT + for(unsigned i=0; i=0 && !inv[member_val_index].nondet) + { + // Add all paths from obj.next to p + if(heap_domain.add_transitivity( + row, + static_cast(member_val_index), + inv)) + { + improved=true; + const std::string expr_str= + from_expr(ns, "", heap_domain.templ[member_val_index].expr); + debug() << "Add all paths: " << expr_str + << ", through: " << from_expr(ns, "", points_to) << eom; + } + } + } + + // Recursively update all rows that are dependent on this row + if(templ_row.mem_kind==heap_domaint::HEAP) + { + updated_rows.clear(); + if(!inv[row].nondet) + update_rows_rec(row, inv); + else + clear_pointing_rows(row, inv); + } + } + } + } + + else + { + debug() << "UNSAT" << eom; + +#ifdef DEBUG_OUTPUT + for(unsigned i=0; iis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } + + for(unsigned i=0; imax_loc && + (kind==domaint::OUT || kind==domaint::OUTHEAP || loc<=actual_loc)) + { + max_loc=loc; + result=i; + } + } + } + } + return result; +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::update_rows_rec + + Inputs: + + Outputs: + + Purpose: Recursively update rows that point to given row. + +\*******************************************************************/ + +bool strategy_solver_heapt::update_rows_rec( + const heap_domaint::rowt &row, + heap_domaint::heap_valuet &value) +{ + heap_domaint::heap_row_valuet &row_value= + static_cast(value[row]); + const heap_domaint::template_rowt &templ_row=heap_domain.templ[row]; + + updated_rows.insert(row); + bool result=false; + for(const heap_domaint::rowt &ptr : row_value.pointed_by) + { + if(heap_domain.templ[ptr].mem_kind==heap_domaint::HEAP && + heap_domain.templ[ptr].member==templ_row.member) + { + if(heap_domain.add_transitivity(ptr, row, value)) + result=true; + + debug() << "recursively updating row: " << ptr << eom; + debug() << "add all paths: " << from_expr(ns, "", templ_row.expr) + << ", through: " + << from_expr(ns, "", templ_row.dyn_obj) << eom; + // Recursive update is called for each row only once + if(updated_rows.find(ptr)==updated_rows.end()) + result=update_rows_rec(ptr, value) || result; + } + } + return result; +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::print_solver_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heapt::print_solver_expr(const exprt &expr) +{ + debug() << from_expr(ns, "", expr) << ": " + << from_expr(ns, "", solver.get(expr)) << eom; + forall_operands(it, expr)print_solver_expr(*it); +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heapt::initialize( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator) +{ + heap_domain.initialize_domain(SSA, precondition, template_generator); + + const exprt input_bindings=heap_domain.get_input_bindings(); + if(!input_bindings.is_true()) + { + solver << input_bindings; + debug() << "Input bindings:" << eom; + debug() << from_expr(ns, "", input_bindings) << eom; + } + + if(!heap_domain.new_heap_row_specs.empty()) + { + debug() << "New template:" << eom; + heap_domain.output_domain(debug(), ns); + } +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::clear_pointing_rows + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heapt::clear_pointing_rows( + const heap_domaint::rowt &row, + heap_domaint::heap_valuet &value) +{ + heap_domaint::heap_row_valuet &row_value= + static_cast(value[row]); + + std::vector to_remove; + for(auto &ptr : row_value.pointed_by) + { + if(ptr!=row) + { + debug() << "Clearing row: " << ptr << eom; + value[ptr].clear(); + to_remove.push_back(ptr); + } + } + for(auto &r : to_remove) + row_value.pointed_by.erase(r); +} + +/*******************************************************************\ + +Function: strategy_solver_heapt::get_points_to_dest + + Inputs: + + Outputs: + + Purpose: Get an address where the given pointer points to in the current + solver iteration. Returns nil_exprt if the value of the pointer + is nondet. + +\*******************************************************************/ + +const exprt strategy_solver_heapt::get_points_to_dest( + const exprt &pointer, + const exprt &templ_row_expr) +{ + exprt value=solver.get(pointer); + // Value from the solver must be converted into an expression + exprt ptr_value=heap_domain.value_to_ptr_exprt(value); + + if((ptr_value.id()==ID_constant && + to_constant_expr(ptr_value).get_value()==ID_NULL) || + ptr_value.id()==ID_symbol) + { + // Add equality p == NULL or p == symbol + return ptr_value; + } + else if(ptr_value.id()==ID_address_of) + { + // Template row pointer points to the heap (p = &obj) + debug() << from_expr(ns, "", ptr_value) << eom; + assert(ptr_value.id()==ID_address_of); + if(to_address_of_expr(ptr_value).object().id()!=ID_symbol) + { + // If solver did not return address of a symbol, it is considered + // as nondet value. + return nil_exprt(); + } + + symbol_exprt obj=to_symbol_expr( + to_address_of_expr(ptr_value).object()); + + if(obj.type()!=templ_row_expr.type() && + ns.follow(templ_row_expr.type().subtype())!=ns.follow(obj.type())) + { + if(!is_cprover_symbol(templ_row_expr)) + { + // If types disagree, it's a nondet (solver assigned random value) + return nil_exprt(); + } + } + + // Add equality p == &obj + return obj; + } + else + return nil_exprt(); +} diff --git a/src/domains/strategy_solver_heap.h b/src/domains/strategy_solver_heap.h new file mode 100644 index 000000000..757da4d86 --- /dev/null +++ b/src/domains/strategy_solver_heap.h @@ -0,0 +1,67 @@ +/*******************************************************************\ + +Module: Strategy solver for heap shape analysis + +Author: Viktor Malik + +\*******************************************************************/ +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_H + +#include +#include "strategy_solver_base.h" +#include "heap_domain.h" +#include "template_generator_base.h" + +class strategy_solver_heapt:public strategy_solver_baset +{ +public: + strategy_solver_heapt( + heap_domaint &_heap_domain, + incremental_solvert &_solver, + const local_SSAt &SSA, + const exprt &precondition, + message_handlert &message_handler, + template_generator_baset &template_generator): + strategy_solver_baset(_solver, SSA.ns), + heap_domain(_heap_domain), + loop_guards(SSA.loop_guards) + { + set_message_handler(message_handler); + initialize(SSA, precondition, template_generator); + } + + virtual bool iterate(invariantt &_inv) override; + + void initialize( + const local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator); + +protected: + heap_domaint &heap_domain; + std::set> loop_guards; + std::set updated_rows; + + const exprt get_points_to_dest( + const exprt &pointer, + const exprt &templ_row_expr); + + int find_member_row( + const exprt &obj, + const irep_idt &member, + int actual_loc, + const domaint::kindt &kind); + + bool update_rows_rec( + const heap_domaint::rowt &row, + heap_domaint::heap_valuet &value); + void clear_pointing_rows( + const heap_domaint::rowt &row, + heap_domaint::heap_valuet &value); + + void print_solver_expr(const exprt &expr); +}; + + +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_H diff --git a/src/domains/strategy_solver_heap_tpolyhedra.cpp b/src/domains/strategy_solver_heap_tpolyhedra.cpp new file mode 100644 index 000000000..53625a76c --- /dev/null +++ b/src/domains/strategy_solver_heap_tpolyhedra.cpp @@ -0,0 +1,86 @@ +/*******************************************************************\ + +Module: Strategy solver for combination of shape and template + polyhedra domains. + +Author: Viktor Malik + +\*******************************************************************/ + +#include "strategy_solver_heap_tpolyhedra.h" + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedrat::iterate + + Inputs: + + Outputs: + + Purpose: Run iteration of each solver separately, but every time + in the context of the current invariant found by the other + solver. The template polyhedra solving is also restricted to + a symbolic path found by the heap solver. + +\*******************************************************************/ + +bool strategy_solver_heap_tpolyhedrat::iterate( + strategy_solver_baset::invariantt &_inv) +{ + heap_tpolyhedra_domaint::heap_tpolyhedra_valuet &inv= + static_cast(_inv); + + // Run one iteration of heap solver in the context of invariant from + // the template polyhedra solver + solver.new_context(); + solver << heap_tpolyhedra_domain.polyhedra_domain.to_pre_constraints( + inv.tpolyhedra_value); + bool heap_improved=heap_solver.iterate(inv.heap_value); + solver.pop_context(); + + if(heap_improved) + { + // If heap part was improved, restrict template polyhedra part to the found + // symbolic path + symbolic_path=heap_solver.symbolic_path; + heap_tpolyhedra_domain.polyhedra_domain.restrict_to_sympath(symbolic_path); + } + + // Run one interation of the template polyhedra solver in the context of + // invariant from the heap solver + solver.new_context(); + solver << heap_tpolyhedra_domain.heap_domain.to_pre_constraints( + inv.heap_value); + bool tpolyhedra_improved=tpolyhedra_solver.iterate(inv.tpolyhedra_value); + solver.pop_context(); + + if(heap_improved) + heap_tpolyhedra_domain.polyhedra_domain.undo_restriction(); + + return heap_improved || tpolyhedra_improved; +} + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedrat::set_message_handler + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void strategy_solver_heap_tpolyhedrat::set_message_handler( + message_handlert &_message_handler) +{ + heap_solver.set_message_handler(_message_handler); + tpolyhedra_solver.set_message_handler(_message_handler); +} + +void strategy_solver_heap_tpolyhedrat::clear_symbolic_path() +{ + heap_solver.symbolic_path.clear(); + tpolyhedra_solver.symbolic_path.clear(); +} diff --git a/src/domains/strategy_solver_heap_tpolyhedra.h b/src/domains/strategy_solver_heap_tpolyhedra.h new file mode 100644 index 000000000..edf7ce17c --- /dev/null +++ b/src/domains/strategy_solver_heap_tpolyhedra.h @@ -0,0 +1,56 @@ +/*******************************************************************\ + +Module: Strategy solver for combination of shape and template + polyhedra domains. + +Author: Viktor Malik + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_TPOLYHEDRA_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_TPOLYHEDRA_H + + +#include "strategy_solver_base.h" +#include "heap_tpolyhedra_domain.h" +#include "strategy_solver_heap.h" +#include "strategy_solver_binsearch.h" + +class strategy_solver_heap_tpolyhedrat:public strategy_solver_baset +{ +public: + strategy_solver_heap_tpolyhedrat( + heap_tpolyhedra_domaint &_heap_tpolyhedra_domain, + incremental_solvert &_solver, + const local_SSAt &SSA, + const exprt &precondition, + message_handlert &message_handler, + template_generator_baset &template_generator): + strategy_solver_baset(_solver, SSA.ns), + heap_tpolyhedra_domain(_heap_tpolyhedra_domain), + heap_solver( + heap_tpolyhedra_domain.heap_domain, + _solver, + SSA, + precondition, + message_handler, + template_generator), + tpolyhedra_solver(heap_tpolyhedra_domain.polyhedra_domain, _solver, SSA.ns) + { + tpolyhedra_solver.set_message_handler(message_handler); + } + + virtual bool iterate(invariantt &_inv) override; + + virtual void set_message_handler(message_handlert &_message_handler) override; + void clear_symbolic_path(); + +protected: + heap_tpolyhedra_domaint &heap_tpolyhedra_domain; + + strategy_solver_heapt heap_solver; + strategy_solver_binsearcht tpolyhedra_solver; +}; + + +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_TPOLYHEDRA_H diff --git a/src/domains/strategy_solver_heap_tpolyhedra_sympath.cpp b/src/domains/strategy_solver_heap_tpolyhedra_sympath.cpp new file mode 100644 index 000000000..53fcb1546 --- /dev/null +++ b/src/domains/strategy_solver_heap_tpolyhedra_sympath.cpp @@ -0,0 +1,220 @@ +/*******************************************************************\ + +Module: Strategy solver for heap-tpolyhedra domain using symbolic paths + +Author: Viktor Malik + +\*******************************************************************/ + +// #define DEBUG + +#include "strategy_solver_heap_tpolyhedra_sympath.h" + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedra_sympatht::set_message_handler + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void strategy_solver_heap_tpolyhedra_sympatht::set_message_handler( + message_handlert &_message_handler) +{ + solver.set_message_handler(_message_handler); +} + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedra_sympatht::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +bool strategy_solver_heap_tpolyhedra_sympatht::iterate( + strategy_solver_baset::invariantt &_inv) +{ + auto &inv=static_cast + (_inv); + + bool improved; + if(!new_path) + { + // Computing invariant for the same symbolic path + +#ifdef DEBUG + std::cerr << "------------------------------------------\n"; + std::cerr << "Same path\n"; + std::cerr << from_expr(ns, "", symbolic_path.get_expr()) << "\n"; +#endif + + const exprt sympath=symbolic_path.get_expr(); + + domain.heap_tpolyhedra_domain.restrict_to_sympath(symbolic_path); + improved=heap_tpolyhedra_solver.iterate(inv.at(sympath)); + if(!improved) + { + // Invariant for the current symbolic path cannot be improved + +#ifdef DEBUG + std::cerr << "End of path\n"; + std::cerr << "++++++++++++++++++++++++++++++++++++++++++\n"; +#endif + + // Check if the computed path is really feasible + if(!is_current_path_feasible(inv)) + inv.erase(sympath); + + visited_paths.push_back(symbolic_path); + domain.heap_tpolyhedra_domain.clear_aux_symbols(); + domain.heap_tpolyhedra_domain.eliminate_sympaths(visited_paths); + clear_symbolic_path(); + improved=true; + new_path=true; + } + else if(heap_tpolyhedra_solver.symbolic_path.get_expr()!=sympath) + { + // The path has been altered during computation (solver has found another + // loop-select guard that can be true + auto new_sympath=heap_tpolyhedra_solver.symbolic_path.get_expr(); + inv.emplace(new_sympath, std::move(inv.at(sympath))); + inv.erase(sympath); + symbolic_path=heap_tpolyhedra_solver.symbolic_path; +#ifdef DEBUG + std::cerr << "Path altered\n"; + std::cerr << from_expr(ns, "", symbolic_path.get_expr()) << "\n"; +#endif + } + domain.heap_tpolyhedra_domain.undo_restriction(); + } + else + { + // Computing invariant for a new path + heap_tpolyhedra_domaint::heap_tpolyhedra_valuet new_value; + domain.heap_tpolyhedra_domain.initialize(new_value); + improved=heap_tpolyhedra_solver.iterate(new_value); + + if(improved) + { + symbolic_path=heap_tpolyhedra_solver.symbolic_path; +#ifdef DEBUG + std::cerr << "Symbolic path:\n"; + std::cerr << from_expr(ns, "", symbolic_path.get_expr()) << "\n"; +#endif + const exprt sympath=heap_tpolyhedra_solver.symbolic_path.get_expr(); + inv.emplace(sympath, std::move(new_value)); + new_path=false; + } + } + return improved; +} + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedra_sympatht::clear_symbolic_path + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void strategy_solver_heap_tpolyhedra_sympatht::clear_symbolic_path() +{ + symbolic_path.clear(); + heap_tpolyhedra_solver.clear_symbolic_path(); +} + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedra_sympatht::is_current_path_feasible + + Inputs: + + Outputs: + + Purpose: Check if the current symbolic path is feasible while the computed + invariant holds. + A path is reachable iff: + - for each loop whose loop-select guard occurs in positive form, + if its loop head is reachable, then also loop end must be + reachable (g#lb => g#le must be SAT) + - for each loop whose loop-select guard occurs in negative form, + if its loop head is reachable, then its end is not reachable + (g#lb => !g#le must be SAT) + +\*******************************************************************/ +bool strategy_solver_heap_tpolyhedra_sympatht::is_current_path_feasible( + heap_tpolyhedra_sympath_domaint::heap_tpolyhedra_sympath_valuet &value) +{ + bool result=true; + auto sympath=symbolic_path.get_expr(); + solver.new_context(); + + // Path invariant + exprt invariant; + domain.heap_tpolyhedra_domain.project_on_vars( + value.at(sympath), {}, invariant); + solver << invariant; + + for(auto &guard : symbolic_path.path_map) + { + // Build condition of reachability of current loop + exprt loop_cond=loop_conds_map.at(guard.first); + if(!guard.second) + loop_cond.op1()=not_exprt(loop_cond.op1()); + + solver.new_context(); + + // Push states of other loop select gurads and condition of reachablility + // of the current loop + const exprt sympath_no_current=symbolic_path.get_expr(guard.first, true); + solver << sympath_no_current; + solver << loop_cond; + + // If loop is not reachable in the current context of computed summary, + // the path is infeasible + if(solver()==decision_proceduret::D_UNSATISFIABLE) + result=false; + + solver.pop_context(); + } + + solver.pop_context(); + return result; +} + +/*******************************************************************\ + +Function: strategy_solver_heap_tpolyhedra_sympatht::build_loop_conds_map + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void strategy_solver_heap_tpolyhedra_sympatht::build_loop_conds_map( + const local_SSAt &SSA) +{ + for(auto &node : SSA.nodes) + { + if(node.loophead!=SSA.nodes.end()) + { + const exprt ls_guard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, node.location); + const exprt lb_guard=SSA.guard_symbol(node.loophead->location); + const exprt le_guard=SSA.guard_symbol(node.location); + loop_conds_map.emplace(ls_guard, and_exprt(lb_guard, le_guard)); + } + } +} diff --git a/src/domains/strategy_solver_heap_tpolyhedra_sympath.h b/src/domains/strategy_solver_heap_tpolyhedra_sympath.h new file mode 100644 index 000000000..6bca710e3 --- /dev/null +++ b/src/domains/strategy_solver_heap_tpolyhedra_sympath.h @@ -0,0 +1,65 @@ +/*******************************************************************\ + +Module: Strategy solver for heap-tpolyhedra domain using symbolic paths + +Author: Viktor Malik + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_TPOLYHEDRA_SYMPATH_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_TPOLYHEDRA_SYMPATH_H + + +#include "strategy_solver_base.h" +#include "heap_tpolyhedra_sympath_domain.h" +#include "strategy_solver_heap_tpolyhedra.h" + +class strategy_solver_heap_tpolyhedra_sympatht:public strategy_solver_baset +{ +public: + strategy_solver_heap_tpolyhedra_sympatht( + heap_tpolyhedra_sympath_domaint &_domain, + incremental_solvert &_solver, + const local_SSAt &SSA, + const exprt &precondition, + message_handlert &message_handler, + template_generator_baset &template_generator): + strategy_solver_baset(_solver, SSA.ns), + domain(_domain), + heap_tpolyhedra_solver( + domain.heap_tpolyhedra_domain, + _solver, + SSA, + precondition, + message_handler, + template_generator) + { + build_loop_conds_map(SSA); + } + + virtual bool iterate(invariantt &inv) override; + + virtual void set_message_handler(message_handlert &_message_handler) override; + + void clear_symbolic_path(); + +protected: + heap_tpolyhedra_sympath_domaint &domain; + strategy_solver_heap_tpolyhedrat heap_tpolyhedra_solver; + + std::vector visited_paths; + bool new_path=true; + + // Mapping for each loop: + // g#ls -> (g#lh && g#le) + // ^ loop select ^ loop head ^ loop end + // This is used to check feasibility of symbolic paths + std::map loop_conds_map; + void build_loop_conds_map(const local_SSAt &SSA); + + bool is_current_path_feasible( + heap_tpolyhedra_sympath_domaint::heap_tpolyhedra_sympath_valuet &value); +}; + + +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_HEAP_TPOLYHEDRA_SYMPATH_H diff --git a/src/domains/symbolic_path.cpp b/src/domains/symbolic_path.cpp new file mode 100644 index 000000000..858c7cf3d --- /dev/null +++ b/src/domains/symbolic_path.cpp @@ -0,0 +1,42 @@ +/*******************************************************************\ + +Module: Symbolic path in a program + +Author: Viktor Malik + +\*******************************************************************/ + + +#include "symbolic_path.h" + +/*******************************************************************\ + +Function: symbolic_patht::get_expr + + Inputs: + + Outputs: + + Purpose: Get expression correcponding to the path. There is an option + not to include selected loop guard (this is useful when + analysing that loop). + +\*******************************************************************/ +const exprt symbolic_patht::get_expr( + const exprt &except_guard, + bool except_true_only) const +{ + exprt::operandst path; + for(const auto &guard : path_map) + { + if(except_guard.is_not_nil() && guard.first==except_guard && + (!except_true_only || guard.second)) + continue; + + if(guard.second) + path.push_back(guard.first); + else + path.push_back(not_exprt(guard.first)); + } + return path.empty() ? true_exprt() : conjunction(path); +} diff --git a/src/domains/symbolic_path.h b/src/domains/symbolic_path.h new file mode 100644 index 000000000..44974bea5 --- /dev/null +++ b/src/domains/symbolic_path.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Symbolic path in a program + +Author: Viktor Malik + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SYMBOLIC_PATH_H +#define CPROVER_2LS_DOMAINS_SYMBOLIC_PATH_H + +#include +#include + +class symbolic_patht +{ +public: + std::map path_map; + + const exprt get_expr( + const exprt &except_guard=nil_exprt(), + bool except_true_only=false) const; + + bool &operator[](const exprt &expr) { return path_map[expr]; } + + void clear() { path_map.clear(); } +}; + + +#endif // CPROVER_2LS_DOMAINS_SYMBOLIC_PATH_H diff --git a/src/domains/template_generator_base.cpp b/src/domains/template_generator_base.cpp index e62fa1649..e51083c93 100644 --- a/src/domains/template_generator_base.cpp +++ b/src/domains/template_generator_base.cpp @@ -12,10 +12,16 @@ Author: Peter Schrammel #include #include +#include + #include "template_generator_base.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" #include "predabs_domain.h" +#include "heap_domain.h" +#include "heap_tpolyhedra_domain.h" +#include "heap_tpolyhedra_sympath_domain.h" +#include "disjunctive_domain.h" #ifdef DEBUG #include @@ -146,6 +152,7 @@ void template_generator_baset::collect_variables_loop( { // used for renaming map var_listt pre_state_vars, post_state_vars; + unsigned int loop_count=0; // add loop variables for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); @@ -153,6 +160,18 @@ void template_generator_baset::collect_variables_loop( { if(n_it->loophead!=SSA.nodes.end()) // we've found a loop { + loop_present=true; + loop_count++; + if (options.get_bool_option("disjunctive_domains")) + { + assert(loop_count<=1); + } + + loophead_loc=n_it->loophead->location; + + //collect guards for paths + collect_guards(SSA,n_it->loophead,n_it); + exprt pre_guard, post_guard; get_pre_post_guards(SSA, n_it, pre_guard, post_guard); @@ -166,17 +185,44 @@ void template_generator_baset::collect_variables_loop( o_it!=SSA.ssa_objects.objects.end(); o_it++) { - ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); + const std::string id=id2string(o_it->get_identifier()); + ssa_domaint::phi_nodest::const_iterator p_it=phi_nodes.find(id); if(p_it==phi_nodes.end()) // object not modified in this loop continue; + exprt obj_post_guard=post_guard; + + if(id.find("__CPROVER_deallocated")!=std::string::npos) + { + auto record_frees=collect_record_frees(SSA, n_it->loophead, n_it); + exprt::operandst d; + for(auto &r : record_frees) + d.push_back(equal_exprt(r, true_exprt())); + if(!d.empty()) + obj_post_guard=and_exprt(obj_post_guard, disjunction(d)); + } + symbol_exprt pre_var; get_pre_var(SSA, o_it, n_it, pre_var); + + // For fields of dynamic objects, we add a guard that their value is not + // equal to the corresponding input SSA variable that represents a state + // when the object is not allocated. + // Example: dynamic_object$0.next#ls100 != dynamic_object$0.next + if(id.find("ssa::dynamic_object$")!=std::string::npos) + { + exprt &post_var=post_renaming_map[pre_var]; + assert(post_var.id()==ID_symbol); + const irep_idt orig_id=get_original_name(to_symbol_expr(post_var)); + symbol_exprt unallocated(orig_id, post_var.type()); + exprt guard=not_exprt(equal_exprt(post_var, unallocated)); + obj_post_guard=and_exprt(obj_post_guard, guard); + } + exprt init_expr; get_init_expr(SSA, o_it, n_it, init_expr); - add_var(pre_var, pre_guard, post_guard, domaint::LOOP, var_specs); + add_var(pre_var, pre_guard, obj_post_guard, domaint::LOOP, var_specs); #ifdef DEBUG std::cout << "Adding " << from_expr(ns, "", in) << " " << @@ -265,6 +311,38 @@ void template_generator_baset::filter_equality_domain() /*******************************************************************\ +Function: template_generator_baset::filter_heap_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void template_generator_baset::filter_heap_domain() +{ + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(auto &var : new_var_specs) + { + if(var.var.id()==ID_symbol && var.var.type().id()==ID_pointer) + { + if(is_pointed(var.var) && + id2string(to_symbol_expr(var.var).get_identifier()).find(".")!= + std::string::npos) + continue; + // Filter out non-assigned OUT variables + if(var.kind!=domaint::OUT || + ssa_inlinert::get_original_identifier(to_symbol_expr(var.var))!= + to_symbol_expr(var.var).get_identifier()) + var_specs.push_back(var); + } + } +} + +/*******************************************************************\ + Function: template_generator_baset::add_var Inputs: @@ -289,8 +367,9 @@ void template_generator_baset::add_var( exprt post_var=post_renaming_map[var]; exprt aux_var=aux_renaming_map[var]; aux_expr=and_exprt( - implies_exprt(and_exprt(post_guard, not_exprt(init_guard)), - equal_exprt(aux_var, post_var)), + implies_exprt( + and_exprt(post_guard, not_exprt(init_guard)), + equal_exprt(aux_var, post_var)), implies_exprt(init_guard, equal_exprt(aux_var, init_renaming_map[var]))); post_guard=or_exprt(post_guard, init_guard); } @@ -593,11 +672,14 @@ bool template_generator_baset::instantiate_custom_templates( if(contains_new_var) add_post_vars=true; - static_cast(domain_ptr)->add_template_row( - expr, pre_guard, - contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); + static_cast(domain_ptr) + ->add_template_row( + expr, + pre_guard, + contains_new_var ? + and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); } // pred abs domain else if(predabs) @@ -618,11 +700,14 @@ bool template_generator_baset::instantiate_custom_templates( if(contains_new_var) add_post_vars=true; - static_cast(domain_ptr)->add_template_row( - expr, pre_guard, - contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); + static_cast(domain_ptr) + ->add_template_row( + expr, + pre_guard, + contains_new_var ? + and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); } else // neither pred abs, nor polyhedra { @@ -678,44 +763,204 @@ void template_generator_baset::instantiate_standard_domains( domain_ptr= new equality_domaint(domain_number, renaming_map, var_specs, SSA.ns); } + else if(options.get_bool_option("heap")) + { + filter_heap_domain(); + domain_ptr=new heap_domaint(domain_number, renaming_map, var_specs, SSA.ns); + } else if(options.get_bool_option("intervals")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_interval_template(var_specs, SSA.ns); } else if(options.get_bool_option("zones")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_difference_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_difference_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_interval_template(var_specs, SSA.ns); } else if(options.get_bool_option("octagons")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_sum_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_difference_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_sum_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_difference_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_interval_template(var_specs, SSA.ns); } else if(options.get_bool_option("qzones")) { domain_ptr= new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_difference_template( - var_specs, SSA.ns); - static_cast(domain_ptr)->add_quadratic_template( - var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_difference_template(var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_quadratic_template(var_specs, SSA.ns); + } + else if(options.get_bool_option("heap-interval") || + options.get_bool_option("heap-zones")) + { + filter_heap_interval_domain(); + auto polyhedra_kind=options.get_bool_option("heap-interval") + ? heap_tpolyhedra_domaint::INTERVAL + : heap_tpolyhedra_domaint::ZONES; + if(options.get_bool_option("sympath")) + domain_ptr=new heap_tpolyhedra_sympath_domaint( + domain_number, renaming_map, var_specs, SSA, polyhedra_kind); + else + domain_ptr=new heap_tpolyhedra_domaint( + domain_number, renaming_map, var_specs, SSA.ns, polyhedra_kind); + } + else if (options.get_bool_option("disjunctive-intervals")) + { + filter_template_domain(); + disjunctive_domaint::template_kindt template_kind=disjunctive_domaint::TPOLYHEDRA; + disjunctive_domaint::lex_metrict tol(0,mp_integer(0)); + unsigned int max=options.get_unsigned_int_option("disjunct-limit"); + max = 51; + std::cout << "Using a maximum of " << max << " disjuncts" << std::endl; + domain_ptr=new disjunctive_domaint( + domain_number,renaming_map,var_specs,SSA.ns,template_kind,max,tol); + + domaint *base_domain_ptr=static_cast(domain_ptr)->base_domain(); + static_cast(base_domain_ptr)->add_interval_template(var_specs,SSA.ns); + } + else if (options.get_bool_option("disjunctive-zones")) + { + filter_template_domain(); + disjunctive_domaint::template_kindt template_kind=disjunctive_domaint::TPOLYHEDRA; + disjunctive_domaint::lex_metrict tol(0,mp_integer(1)); + unsigned int max=options.get_unsigned_int_option("disjunct-limit"); + std::cout << "Using maximum of " << max << " disjuncts" << std::endl; + domain_ptr=new disjunctive_domaint( + domain_number,renaming_map,var_specs,SSA.ns,template_kind,max,tol); + + domaint *base_domain_ptr=static_cast(domain_ptr)->base_domain(); + static_cast(base_domain_ptr)->add_difference_template(var_specs,SSA.ns); + static_cast(base_domain_ptr)->add_interval_template(var_specs,SSA.ns); + } + else if (options.get_bool_option("disjunctive-octagons")) + { + filter_template_domain(); + disjunctive_domaint::template_kindt template_kind=disjunctive_domaint::TPOLYHEDRA; + disjunctive_domaint::lex_metrict tol(0,mp_integer(1)); + unsigned int max=options.get_unsigned_int_option("disjunct-limit"); + std::cout << "Using maximum of " << max << "disjuncts" << std::endl; + domain_ptr=new disjunctive_domaint( + domain_number,renaming_map,var_specs,SSA.ns,template_kind,max,tol); + + domaint *base_domain_ptr=static_cast(domain_ptr)->base_domain(); + static_cast(base_domain_ptr)->add_sum_template(var_specs,SSA.ns); + static_cast(base_domain_ptr)->add_difference_template(var_specs,SSA.ns); + static_cast(base_domain_ptr)->add_interval_template(var_specs,SSA.ns); } } + +void template_generator_baset::filter_heap_interval_domain() +{ + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(domaint::var_specst::const_iterator v=new_var_specs.begin(); + v!=new_var_specs.end(); v++) + { + const domaint::vart &s=v->var; + + if(s.id()==ID_symbol && is_pointed(s) && + id2string(to_symbol_expr(s).get_identifier()).find(".")!= + std::string::npos) + continue; + + if(s.type().id()==ID_unsignedbv || + s.type().id()==ID_signedbv || + s.type().id()==ID_floatbv) + { + var_specs.push_back(*v); + continue; + } + + if(s.id()==ID_symbol && s.type().id()==ID_pointer) + { + // Filter out non-assigned OUT variables + if(v->kind!=domaint::OUT || + ssa_inlinert::get_original_identifier(to_symbol_expr(s))!= + to_symbol_expr(s).get_identifier()) + { + var_specs.push_back(*v); + continue; + } + } + } +} + +std::vector template_generator_baset::collect_record_frees( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator loop_begin, + local_SSAt::nodest::const_iterator loop_end) +{ + std::vector result; + for(auto &node : SSA.nodes) + { + if(node.location->location_number>loop_begin->location->location_number && + node.location->location_numberlocation->location_number && + node.record_free.is_not_nil()) + { + result.push_back(SSA.read_lhs(node.record_free, node.location)); + } + } + return result; +} + +void template_generator_baset::collect_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator loop_begin, + local_SSAt::nodest::const_iterator loop_end) +{ + auto n_it=loop_begin; + do + { + n_it++; + for (auto eq_it=n_it->equalities.begin();eq_it!=n_it->equalities.end();eq_it++) + { + std::string id=id2string(to_symbol_expr(eq_it->lhs()).get_identifier()); + if (id.find("phi")!=id.npos) + { + collect_guards(eq_it->rhs()); + } + } + } while (n_it!=loop_end); +} + +void template_generator_baset::collect_guards( + const exprt &expr) +{ + if(expr.id()==ID_if) + { + auto it=guards.begin(); + for (;it!=guards.end();it++) + { + if (*it==expr.op0()) + { + break; + } + } + if (it==guards.end()) + { + guards.push_back(expr.op0()); + } + } + forall_operands(it,expr) + { + collect_guards(*it); + } +} \ No newline at end of file diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index 1c9428da2..e9010a12c 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -61,6 +61,10 @@ class template_generator_baset:public messaget optionst options; // copy: we may override options + local_SSAt::locationt loophead_loc; // for disjunctive domains + bool loop_present; + std::vector guards; + protected: const ssa_dbt &ssa_db; const ssa_local_unwindert &ssa_local_unwinder; @@ -71,8 +75,22 @@ class template_generator_baset:public messaget const local_SSAt &SSA, bool forward); + std::vector collect_record_frees( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator loop_begin, + local_SSAt::nodest::const_iterator loop_end); + + void collect_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator loop_begin, + local_SSAt::nodest::const_iterator loop_end); + + void collect_guards(const exprt &expr); + void filter_template_domain(); void filter_equality_domain(); + void filter_heap_domain(); + void filter_heap_interval_domain(); void add_var( const domaint::vart &var_to_add, @@ -102,7 +120,8 @@ class template_generator_baset:public messaget void get_pre_post_guards( const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard); + exprt &pre_guard, + exprt &post_guard); void get_pre_var( const local_SSAt &SSA, local_SSAt::objectst::const_iterator o_it, diff --git a/src/domains/template_generator_callingcontext.cpp b/src/domains/template_generator_callingcontext.cpp index 310ea4303..e4c51cbcb 100644 --- a/src/domains/template_generator_callingcontext.cpp +++ b/src/domains/template_generator_callingcontext.cpp @@ -93,13 +93,17 @@ void template_generator_callingcontextt::collect_variables_callingcontext( v_it!=cs_globals_in.end(); v_it++) { symbol_exprt dummy; - if(ssa_inlinert::find_corresponding_symbol(*v_it, globals_in, dummy)) + if(ssa_inlinert::find_corresponding_symbol(*v_it, globals_in, dummy) || + id2string(v_it->get_identifier()).find("dynamic_object$")!= + std::string::npos) + { add_var( *v_it, guard, guard, domaint::OUT, // the same for both forward and backward var_specs); + } } // TODO: actually, the context should contain both, @@ -113,6 +117,7 @@ void template_generator_callingcontextt::collect_variables_callingcontext( { std::set args; find_symbols(*a_it, args); + exprt arg=*a_it; add_vars(args, guard, guard, domaint::OUT, var_specs); } } diff --git a/src/domains/template_generator_ranking.cpp b/src/domains/template_generator_ranking.cpp index fab04e251..c7a27b0cc 100644 --- a/src/domains/template_generator_ranking.cpp +++ b/src/domains/template_generator_ranking.cpp @@ -133,11 +133,11 @@ void template_generator_rankingt::collect_variables_ranking( filter_ranking_domain(new_var_specs); #ifndef LEXICOGRAPHIC - static_cast(domain_ptr)->add_template( - new_var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_template(new_var_specs, SSA.ns); #else - static_cast(domain_ptr)->add_template( - new_var_specs, SSA.ns); + static_cast(domain_ptr) + ->add_template(new_var_specs, SSA.ns); #endif var_specs.insert( diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index becceba5f..4b83c2d01 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -11,6 +11,7 @@ Author: Peter Schrammel #include "template_generator_summary.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" +#include "domain.h" #include #include @@ -123,7 +124,9 @@ domaint::var_sett template_generator_summaryt::inout_vars() for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::IN || v->kind==domaint::OUT) + if(v->kind==domaint::IN || + v->kind==domaint::OUT || + v->kind==domaint::OUTHEAP) vars.insert(v->var); } return vars; diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 470931c05..c7b12c810 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -17,6 +17,7 @@ Author: Peter Schrammel #include "tpolyhedra_domain.h" #include "util.h" +#include "domain.h" #define SYMB_BOUND_VAR "symb_bound#" @@ -229,9 +230,6 @@ exprt tpolyhedra_domaint::get_row_constraint( const row_valuet &row_value) { assert(rowvar.type().id()==ID_pointer) + continue; + if(v1->var.id()==ID_and) + continue; + var_specst::const_iterator v2=v1; + ++v2; for(; v2!=var_specs.end(); ++v2) { + if(v2->var.id()==ID_and) + continue; + + // Check if both vars are dynamic objects allocated by the same malloc. + // In such case, do not add the template row, since only one of those is + // always allocated and the combined guard would never hold. + if(v1->var.id()==ID_symbol && v2->var.id()==ID_symbol) + { + int v1_index=get_dynobj_line(to_symbol_expr(v1->var).get_identifier()); + int v2_index=get_dynobj_line(to_symbol_expr(v2->var).get_identifier()); + if(v1_index>=0 && v2_index>=0 && v1_index==v2_index) + { + const std::string v1_id=id2string( + to_symbol_expr(v1->var).get_identifier()); + const std::string v2_id=id2string( + to_symbol_expr(v2->var).get_identifier()); + // If the vars are fields of dynamic objects, do not add them if the + // fields are the same. + if(v1_id.find(".")!=std::string::npos && + v2_id.find(".")!=std::string::npos) + { + if(v1_id.substr(v1_id.find_first_of("."))== + v2_id.substr(v2_id.find_first_of("."))) + continue; + } + else + continue; + } + } + kindt k=domaint::merge_kinds(v1->kind, v2->kind); if(k==IN) continue; if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; // TEST: we need better heuristics + if(v2->var.type().id()==ID_pointer) + continue; exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); + if(post_g.is_false()) + continue; // x1-x2 add_template_row( @@ -1166,3 +1180,46 @@ void tpolyhedra_domaint::add_sum_template( } } } + +void tpolyhedra_domaint::restrict_to_sympath(const symbolic_patht &sympath) +{ + for(auto &row : templ) + { + const exprt c=sympath.get_expr(row.pre_guard.op1()); + row.aux_expr=and_exprt(row.aux_expr, c); + } +} + +void tpolyhedra_domaint::clear_aux_symbols() +{ + for(auto &row : templ) + row.aux_expr=true_exprt(); +} + +void tpolyhedra_domaint::undo_restriction() +{ + for(auto &row : templ) + { + if(row.aux_expr.id()==ID_and) + { + row.aux_expr=to_and_expr(row.aux_expr).op0(); + } + } +} + +void tpolyhedra_domaint::eliminate_sympaths( + const std::vector &sympaths) +{ + for(auto &row : templ) + { + exprt::operandst paths; + for(const symbolic_patht &sympath : sympaths) + { + const exprt path_expr=sympath.get_expr(row.pre_guard.op1()); + paths.push_back(path_expr); + } + row.aux_expr=paths.empty() + ? true_exprt() + : static_cast(not_exprt(disjunction(paths))); + } +} diff --git a/src/domains/tpolyhedra_domain.h b/src/domains/tpolyhedra_domain.h index 866a5accb..c51c1ddb8 100644 --- a/src/domains/tpolyhedra_domain.h +++ b/src/domains/tpolyhedra_domain.h @@ -16,6 +16,7 @@ Author: Peter Schrammel #include #include "domain.h" +#include "symbolic_path.h" class tpolyhedra_domaint:public domaint { @@ -73,7 +74,8 @@ class tpolyhedra_domaint:public domaint exprt to_symb_post_constraints(const std::set &symb_rows); exprt get_row_symb_value_constraint( const rowt &row, - const row_valuet &row_value, bool geq=false); + const row_valuet &row_value, + bool geq=false); exprt get_row_symb_pre_constraint( const rowt &row, const row_valuet &row_value); @@ -97,13 +99,18 @@ class tpolyhedra_domaint:public domaint // printing virtual void output_value( - std::ostream &out, const valuet &value, const namespacet &ns) const; + std::ostream &out, + const valuet &value, + const namespacet &ns) const; virtual void output_domain( - std::ostream &out, const namespacet &ns) const; + std::ostream &out, + const namespacet &ns) const; // projection virtual void project_on_vars( - valuet &value, const var_sett &vars, exprt &result); + valuet &value, + const var_sett &vars, + exprt &result); unsigned template_size(); @@ -116,13 +123,22 @@ class tpolyhedra_domaint:public domaint kindt kind); void add_interval_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); void add_difference_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); void add_sum_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); void add_quadratic_template( - const var_specst &var_specs, const namespacet &ns); + const var_specst &var_specs, + const namespacet &ns); + + void restrict_to_sympath(const symbolic_patht &sympath); + void undo_restriction(); + void eliminate_sympaths(const std::vector &sympaths); + void clear_aux_symbols(); symbol_exprt get_row_symb_value(const rowt &row); @@ -131,6 +147,7 @@ class tpolyhedra_domaint:public domaint protected: friend class strategy_solver_binsearcht; friend class strategy_solver_enumerationt; + friend class strategy_solver_disjunctivet; templatet templ; }; diff --git a/src/domains/util.cpp b/src/domains/util.cpp index ec12ad55a..ade7c3231 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -7,6 +7,8 @@ Author: Peter Schrammel \*******************************************************************/ #include +#include +#include #include "util.h" @@ -132,8 +134,9 @@ void extend_expr_types(exprt &expr) expr.op1().type().id()==ID_signedbv)) { typet new_type=signedbv_typet(size0+size1+1); - expr=mult_exprt(typecast_exprt(expr.op0(), new_type), - typecast_exprt(expr.op1(), new_type)); + expr=mult_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } else if(expr.op0().type().id()==ID_floatbv && @@ -687,3 +690,48 @@ void clean_expr(exprt &expr) } } } + +/*******************************************************************\ + +Function: is_cprover_symbol + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool is_cprover_symbol(const exprt &expr) +{ + return expr.id()==ID_symbol && + has_prefix( + id2string(to_symbol_expr(expr).get_identifier()), + CPROVER_PREFIX); +} + +/*******************************************************************\ + +Function: get_dynobj_line + + Inputs: Symbol identifier. + + Outputs: If the symbol is a dynamic object, then the location number of the + malloc call where the object was allocated, otherwise -1. + + Purpose: + +\*******************************************************************/ +int get_dynobj_line(const irep_idt &id) +{ + std::string name=id2string(id); + size_t pos=name.find("dynamic_object$"); + if(pos==std::string::npos) + return -1; + + size_t start=pos+15; + size_t end=name.find_first_not_of("0123456789", pos); + std::string number=name.substr(start, end-start); + return std::stoi(number); +} diff --git a/src/domains/util.h b/src/domains/util.h index de0c316ef..251b18b89 100644 --- a/src/domains/util.h +++ b/src/domains/util.h @@ -21,9 +21,14 @@ constant_exprt simplify_const(const exprt &expr); ieee_floatt simplify_const_float(const exprt &expr); mp_integer simplify_const_int(const exprt &expr); void pretty_print_termination_argument( - std::ostream &out, const namespacet &ns, const exprt &expr); + std::ostream &out, + const namespacet &ns, + const exprt &expr); void merge_and( - exprt & result, const exprt &expr1, const exprt &expr2, const namespacet &ns); + exprt & result, + const exprt &expr1, + const exprt &expr2, + const namespacet &ns); constant_exprt make_zero(const typet &type); constant_exprt make_one(const typet &type); constant_exprt make_minusone(const typet &type); @@ -31,4 +36,8 @@ constant_exprt make_minusone(const typet &type); irep_idt get_original_name(const symbol_exprt &); void clean_expr(exprt &expr); +bool is_cprover_symbol(const exprt &expr); + +int get_dynobj_line(const irep_idt &id); + #endif diff --git a/src/solver/summarizer_base.cpp b/src/solver/summarizer_base.cpp index 9a951bda9..553b0bd3f 100644 --- a/src/solver/summarizer_base.cpp +++ b/src/solver/summarizer_base.cpp @@ -194,7 +194,7 @@ exprt summarizer_baset::compute_calling_context( solver.new_context(); solver << SSA.get_enabling_exprs(); - solver << ssa_inliner.get_summaries(SSA); + solver << ssa_inliner.get_summaries_to_loc(SSA, n_it->location); ssa_analyzert analyzer; analyzer.set_message_handler(get_message_handler()); diff --git a/src/solver/summarizer_bw.h b/src/solver/summarizer_bw.h index 540f8fedd..638a70733 100644 --- a/src/solver/summarizer_bw.h +++ b/src/solver/summarizer_bw.h @@ -23,14 +23,13 @@ Author: Peter Schrammel class summarizer_bwt:public summarizer_baset { public: - explicit summarizer_bwt( - optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner): - summarizer_baset( - _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + summarizer_bwt( + optionst &options, + summary_dbt &summary_db, + ssa_dbt &ssa_db, + ssa_unwindert &ssa_unwinder, + ssa_inlinert &ssa_inliner): + summarizer_baset(options, summary_db, ssa_db, ssa_unwinder, ssa_inliner) { } diff --git a/src/solver/summarizer_fw.cpp b/src/solver/summarizer_fw.cpp index 1048a2b0f..27f8dd1da 100644 --- a/src/solver/summarizer_fw.cpp +++ b/src/solver/summarizer_fw.cpp @@ -15,14 +15,9 @@ Author: Peter Schrammel #include #include "summarizer_fw.h" -#include "summary_db.h" #include #include -#include - -#include -#include // #define SHOW_WHOLE_RESULT @@ -71,6 +66,7 @@ void summarizer_fwt::compute_summary_rec( summary.params=SSA.params; summary.globals_in=SSA.globals_in; summary.globals_out=SSA.globals_out; + summary.set_value_domains(SSA); summary.fw_precondition=precondition; if(!options.get_bool_option("havoc")) @@ -171,6 +167,16 @@ void summarizer_fwt::do_summary( debug() << "whole result: " << from_expr(SSA.ns, "", whole_result) << eom; #endif + if(options.get_bool_option("heap")) + { + analyzer.update_heap_out(summary.globals_out); + const exprt advancer_bindings=analyzer.input_heap_bindings(); + if(!advancer_bindings.is_true()) + { + summary.aux_precondition=advancer_bindings; + } + } + if(context_sensitive && !summary.fw_precondition.is_true()) { summary.fw_transformer= diff --git a/src/solver/summarizer_fw.h b/src/solver/summarizer_fw.h index f21255c55..6ee541620 100644 --- a/src/solver/summarizer_fw.h +++ b/src/solver/summarizer_fw.h @@ -23,14 +23,13 @@ Author: Peter Schrammel class summarizer_fwt:public summarizer_baset { public: - explicit summarizer_fwt( - optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner): - summarizer_baset( - _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + summarizer_fwt( + optionst &options, + summary_dbt &summary_db, + ssa_dbt &ssa_db, + ssa_unwindert &ssa_unwinder, + ssa_inlinert &ssa_inliner): + summarizer_baset(options, summary_db, ssa_db, ssa_unwinder, ssa_inliner) { } diff --git a/src/solver/summary.cpp b/src/solver/summary.cpp index 3a74ba628..ffa76f7ae 100644 --- a/src/solver/summary.cpp +++ b/src/solver/summary.cpp @@ -173,6 +173,26 @@ void summaryt::join(const summaryt &new_summary) /*******************************************************************\ +Function: summaryt::set_value_domains + + Inputs: + + Outputs: + + Purpose: Get value domain for last location from SSA. + +\*******************************************************************/ + +void summaryt::set_value_domains(const local_SSAt &SSA) +{ + const local_SSAt::locationt &entry_loc=SSA.nodes.begin()->location; + const local_SSAt::locationt &exit_loc=(--SSA.nodes.end())->location; + value_domain_in=SSA.ssa_value_ai[entry_loc]; + value_domain_out=SSA.ssa_value_ai[exit_loc]; +} + +/*******************************************************************\ + Function: threeval2string Inputs: diff --git a/src/solver/summary.h b/src/solver/summary.h index 840a42230..f4cd32205 100644 --- a/src/solver/summary.h +++ b/src/solver/summary.h @@ -13,6 +13,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include typedef enum {YES, NO, UNKNOWN} threevalt; @@ -32,13 +33,14 @@ class summaryt bw_postcondition(nil_exprt()), bw_transformer(nil_exprt()), bw_invariant(nil_exprt()), + aux_precondition(nil_exprt()), termination_argument(nil_exprt()), terminates(UNKNOWN), mark_recompute(false) {} var_listt params; var_sett globals_in, globals_out; - + ssa_value_domaint value_domain_in, value_domain_out; predicatet fw_precondition; // accumulated calling contexts (over-approx) // predicatet fw_postcondition; // we are not projecting that out currently predicatet fw_transformer; // forward summary (over-approx) @@ -48,6 +50,8 @@ class summaryt predicatet bw_transformer; // backward summary (over- or under-approx) predicatet bw_invariant; // backward invariant (over- or under-approx) + predicatet aux_precondition; + predicatet termination_argument; threevalt terminates; @@ -58,6 +62,8 @@ class summaryt void join(const summaryt &new_summary); + void set_value_domains(const local_SSAt &SSA); + protected: void combine_or(exprt &olde, const exprt &newe); void combine_and(exprt &olde, const exprt &newe); diff --git a/src/ssa/Makefile b/src/ssa/Makefile index 0c4c0263f..55c63720d 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -1,9 +1,12 @@ -SRC = local_ssa.cpp \ - ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ +SRC = local_ssa.cpp ssa_var_collector.cpp \ + ssa_domain.cpp translate_union_member.cpp \ + malloc_ssa.cpp ssa_pointed_objects.cpp ssa_heap_domain.cpp \ guard_map.cpp ssa_object.cpp assignments.cpp ssa_dereference.cpp \ ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ - unwindable_local_ssa.cpp ssa_db.cpp + unwindable_local_ssa.cpp ssa_db.cpp \ + ssa_pointed_objects.cpp ssa_heap_domain.cpp may_alias_analysis.cpp \ + dynobj_instance_analysis.cpp include ../config.inc include $(CBMC)/src/config.inc diff --git a/src/ssa/address_canonizer.cpp b/src/ssa/address_canonizer.cpp index 2ffab3ff1..d3581611f 100644 --- a/src/ssa/address_canonizer.cpp +++ b/src/ssa/address_canonizer.cpp @@ -12,6 +12,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "address_canonizer.h" +#include "ssa_pointed_objects.h" /*******************************************************************\ @@ -75,6 +76,15 @@ exprt address_canonizer( return sum; } + else if(object.id()==ID_symbol && is_iterator(object)) + { + // address of iterator is dereferenced to a corresponding symbol - + // will be bound to real address during analysis + symbol_exprt iterator_addr( + id2string(to_symbol_expr(object).get_identifier())+"'addr", + address.type()); + return iterator_addr; + } else return address; } diff --git a/src/ssa/assignments.cpp b/src/ssa/assignments.cpp index 2a5bc1c6b..13586685d 100644 --- a/src/ssa/assignments.cpp +++ b/src/ssa/assignments.cpp @@ -7,9 +7,11 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ #include +#include #include "assignments.h" #include "ssa_dereference.h" +#include "local_ssa.h" /*******************************************************************\ @@ -38,6 +40,21 @@ void assignmentst::build_assignment_map( const code_assignt &code_assign=to_code_assign(it->code); exprt lhs_deref=dereference(code_assign.lhs(), ssa_value_ai[it], "", ns); assign(lhs_deref, it, ns); + exprt lhs_symbolic_deref=symbolic_dereference(code_assign.lhs(), ns); + assign(lhs_symbolic_deref, it, ns); + + assign_symbolic_rhs(code_assign.rhs(), it, ns); + + if(code_assign.rhs().get_bool("#malloc_result")) + { + // Create empty assignment into the object (declaration) so that it + // gets non-deterministic value + create_alloc_decl(code_assign.rhs(), true_exprt(), it, ns); + } + } + else if(it->is_assert()) + { + assign_symbolic_rhs(it->guard, it, ns); } else if(it->is_decl()) { @@ -49,19 +66,61 @@ void assignmentst::build_assignment_map( const code_function_callt &code_function_call= to_code_function_call(it->code); - // functions may alter state almost arbitrarily: - // * any global-scoped variables - // * any dirty locals + // Get information from ssa_heap_analysis + auto n_it=it; + ++n_it; + const irep_idt fname=to_symbol_expr( + code_function_call.function()).get_identifier(); + std::list new_objects; + std::set modified_objects; - for(objectst::const_iterator - o_it=ssa_objects.dirty_locals.begin(); - o_it!=ssa_objects.dirty_locals.end(); o_it++) - assign(*o_it, it, ns); + if(ssa_heap_analysis.has_location(n_it)) + { + new_objects=ssa_heap_analysis[n_it].new_caller_objects(fname, it); + modified_objects=ssa_heap_analysis[n_it].modified_objects(fname); + } + + // Assign new objects + for(auto &o : new_objects) + { + assign(o, it, ns); + } for(objectst::const_iterator - o_it=ssa_objects.globals.begin(); + o_it=ssa_objects.globals.begin(); o_it!=ssa_objects.globals.end(); o_it++) - assign(*o_it, it, ns); + { + if(id2string(o_it->get_identifier())==id2string(fname)+"#return_value") + assign(*o_it, it, ns); + } + + // Assign all modified objects + for(const exprt &modified : modified_objects) + { + const exprt arg= + ssa_heap_analysis[n_it].function_map.at(fname). + corresponding_expr(modified, code_function_call.arguments(), 0); + + if(arg!=modified) + { + const exprt arg_deref=dereference(arg, ssa_value_ai[it], "", ns); + assign(arg_deref, it, ns); + + std::set symbols; + find_symbols(arg_deref, symbols); + for(const symbol_exprt &symbol : symbols) + { + if(symbol.type()==arg_deref.type()) + { + auto &aliases=ssa_value_ai[n_it](symbol, ns).value_set; + for(auto &alias : aliases) + { + assign(alias.get_expr(), it, ns); + } + } + } + } + } // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) @@ -121,7 +180,8 @@ void assignmentst::assign( // object? ssa_objectt ssa_object(lhs, ns); - if(ssa_object) + if(ssa_object && + !ssa_object.is_unknown_obj()) // unknown objects are just placeholders { assign(ssa_object, loc, ns); } @@ -184,6 +244,38 @@ void assignmentst::assign( /*******************************************************************\ +Function: assignmentst::build_assertion + + Inputs: + + Outputs: + + Purpose: Adds to assignments dereferences from assertion + +\*******************************************************************/ + +void assignmentst::assign_symbolic_rhs( + const exprt &expr, + const locationt &loc, + const namespacet &ns) +{ + exprt rhs_symbolic_deref=symbolic_dereference(expr, ns); + ssa_objectt rhs_object(rhs_symbolic_deref, ns); + + if(has_symbolic_deref(rhs_symbolic_deref) && rhs_object) + { + rhs_symbolic_deref.set("#is_rhs_assign", true); + assign(rhs_symbolic_deref, loc, ns); + } + else if(has_symbolic_deref(rhs_symbolic_deref)) + { + forall_operands(it, expr) + assign_symbolic_rhs(*it, loc, ns); + } +} + +/*******************************************************************\ + Function: assignmentst::output Inputs: @@ -221,3 +313,66 @@ void assignmentst::output( out << "\n"; } } + +/*******************************************************************\ + +Function: assignmentst::create_alloc_decl + + Inputs: + + Outputs: + + Purpose: Create new fresh symbol for each object (and for each its field) + dynamically allocated at the given location. + +\*******************************************************************/ +void assignmentst::create_alloc_decl( + const exprt &expr, + const exprt &guard, + const locationt loc, + const namespacet &ns) +{ + if(expr.id()==ID_symbol || expr.id()==ID_member) + { + const typet &type=ns.follow(expr.type()); + if(type.id()==ID_struct) + { + for(auto &c : to_struct_type(type).components()) + { + create_alloc_decl( + member_exprt(expr, c.get_name(), c.type()), guard, loc, ns); + } + } + else + { + ssa_objectt ssa_object(expr, ns); + if(ssa_object) + { + // Create declaration + assign(ssa_object, loc, ns); + // Store allocation guard + alloc_guards_map.emplace(std::make_pair(loc, ssa_object), guard); + } + } + } + else if(expr.id()==ID_if) + { + const if_exprt &if_expr=to_if_expr(expr); + create_alloc_decl( + if_expr.true_case(), + and_exprt(guard, if_expr.cond()), + loc, + ns); + create_alloc_decl( + if_expr.false_case(), + and_exprt(guard, not_exprt(if_expr.cond())), + loc, + ns); + } + else if(expr.id()==ID_address_of) + create_alloc_decl( + to_address_of_expr(expr).object(), guard, loc, ns); + else if(expr.id()==ID_typecast) + create_alloc_decl( + to_typecast_expr(expr).op(), guard, loc, ns); +} diff --git a/src/ssa/assignments.h b/src/ssa/assignments.h index 28b36d608..e0184fbc6 100644 --- a/src/ssa/assignments.h +++ b/src/ssa/assignments.h @@ -21,12 +21,16 @@ class assignmentst const ssa_objectst &ssa_objects; const ssa_value_ait &ssa_value_ai; + const ssa_heap_analysist &ssa_heap_analysis; typedef ssa_objectst::objectst objectst; typedef std::map assignment_mapt; assignment_mapt assignment_map; + typedef std::map, exprt> alloc_guards_mapt; + alloc_guards_mapt alloc_guards_map; + bool assigns(locationt loc, const ssa_objectt &object) const { assignment_mapt::const_iterator it=assignment_map.find(loc); @@ -42,13 +46,15 @@ class assignmentst return it->second; } - explicit assignmentst( + assignmentst( const goto_programt &_goto_program, const namespacet &_ns, const ssa_objectst &_ssa_objects, - const ssa_value_ait &_ssa_value_ai): + const ssa_value_ait &_ssa_value_ai, + const ssa_heap_analysist &_ssa_heap_analysis): ssa_objects(_ssa_objects), - ssa_value_ai(_ssa_value_ai) + ssa_value_ai(_ssa_value_ai), + ssa_heap_analysis(_ssa_heap_analysis) { build_assignment_map(_goto_program, _ns); } @@ -62,11 +68,24 @@ class assignmentst void build_assignment_map(const goto_programt &, const namespacet &); void assign( - const exprt &lhs, locationt, + const exprt &lhs, + locationt, const namespacet &ns); void assign( - const ssa_objectt &lhs, locationt, + const ssa_objectt &lhs, + locationt, + const namespacet &ns); + + void assign_symbolic_rhs( + const exprt &expr, + const locationt &loc, + const namespacet &ns); + + void create_alloc_decl( + const exprt &expr, + const exprt &guard, + const locationt loc, const namespacet &ns); }; diff --git a/src/ssa/dynobj_instance_analysis.cpp b/src/ssa/dynobj_instance_analysis.cpp new file mode 100644 index 000000000..78a1682aa --- /dev/null +++ b/src/ssa/dynobj_instance_analysis.cpp @@ -0,0 +1,332 @@ +/*******************************************************************\ + +Module: Analysis of the number of instances of abstract dynamic objects. + In some cases, multiple instances must be used so that the + analysis is sound. + +Author: Viktor Malik, viktor.malik@gmail.com + +\*******************************************************************/ + +#include +#include +#include +#include "dynobj_instance_analysis.h" +#include "ssa_dereference.h" + +/*******************************************************************\ + +Function: has_deref_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +bool has_deref_of(const exprt &expr, const exprt &pointer) +{ + if(expr.id()==ID_dereference && to_dereference_expr(expr).pointer()==pointer) + return true; + forall_operands(it, expr) + { + if(has_deref_of(*it, pointer)) + return true; + } + return false; +} + +/*******************************************************************\ + +Function: remove_dereferences + + Inputs: + + Outputs: + + Purpose: Isolate all dereferences of some pointer in must-alias + paritioning. + +\*******************************************************************/ +void remove_dereferences(const exprt &pointer, must_alias_setst &instances) +{ + for(auto &i : instances) + { + if(has_deref_of(i, pointer)) + instances.isolate(i); + } +} + +/*******************************************************************\ + +Function: replace_pointer_in_deref + + Inputs: + + Outputs: + + Purpose: Replace pointer in derefence expression by another pointer. + +\*******************************************************************/ +void replace_pointer_in_deref(exprt &deref, const exprt &src, const exprt &dest) +{ + if(deref.id()==ID_dereference && to_dereference_expr(deref).pointer()==src) + deref=dereference_exprt(dest, deref.type()); + + Forall_operands(it, deref)replace_pointer_in_deref(*it, src, dest); +} + +/*******************************************************************\ + +Function: add_aliased_dereferences + + Inputs: + + Outputs: + + Purpose: Add dereferences of all aliased pointers to instances. + When dereference of a pointer is put to some must-alias + equivalence class, dereferences of aliased pointers must + be added to the same class as well. + +\*******************************************************************/ +void add_aliased_dereferences(const exprt &pointer, must_alias_setst &instances) +{ + // We must copy instances so that we can alter them while iterating + auto inst_copy=instances; + for(auto &i : inst_copy) + { + if(i.id()==ID_symbol && pointer.id()==ID_symbol && i!=pointer && + instances.same_set(i, pointer)) + { + for(auto &deref_i : inst_copy) + { + if(has_deref_of(deref_i, i)) + { + exprt deref_copy=deref_i; + replace_pointer_in_deref(deref_copy, i, pointer); + instances.make_union(deref_i, deref_copy); + } + } + } + } +} + +/*******************************************************************\ + +Function: dynobj_instance_domaint::rhs_concretisation + + Inputs: + + Outputs: + + Purpose: Concretise pointer expressions that occur at some RHS and + did not occur before (assume they do not alias with anything). + +\*******************************************************************/ +void dynobj_instance_domaint::rhs_concretisation( + const exprt &guard, + ai_domain_baset::locationt loc, + ai_baset &ai, + const namespacet &ns) +{ + forall_operands(it, guard) + { + if(it->id()==ID_symbol || it->id()==ID_member) + { + bool found=false; + for(const auto &i : must_alias_relations) + { + size_t n; + found|=!i.second.get_number(*it, n); + } + if(!found) + { + // 1) now make dereference + const auto &values= + static_cast(ai).value_analysis[loc]; + const auto guard_deref=dereference(guard, values, "", ns); + auto value_set=values(guard_deref, ns).value_set; + // 2) then isolate for all values in value set of dereferences + for(auto &v : value_set) + { + auto &instances=must_alias_relations[v.symbol_expr()]; + instances.isolate(*it); + } + } + } + else + { + rhs_concretisation(*it, loc, ai, ns); + } + } +} + +/*******************************************************************\ + +Function: dynobj_instance_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void dynobj_instance_domaint::transform( + ai_domain_baset::locationt from, + ai_domain_baset::locationt to, + ai_baset &ai, + const namespacet &ns) +{ + if(from->is_assign()) + { + const code_assignt &assignment=to_code_assign(from->code); + const exprt lhs=symbolic_dereference(assignment.lhs(), ns); + + // Do not include CPROVER symbols + if(lhs.id()==ID_symbol && + has_prefix( + id2string(to_symbol_expr(lhs).get_identifier()), + CPROVER_PREFIX)) + return; + + if(assignment.rhs().get_bool("#malloc_result")) + { + // For allocation site, the assigned pointer has no aliases + const auto &values= + static_cast(ai).value_analysis[to]; + const auto lhs_deref=dereference(assignment.lhs(), values, "", ns); + auto value_set=values(lhs_deref, ns).value_set; + for(auto &v : value_set) + must_alias_relations[v.symbol_expr()].isolate(lhs); + } + else + { + // For other assignments, use value analysis to get all pointers pointing + // to a dynamic object and then update must-alias sets. + exprt rhs=assignment.rhs(); + if(rhs.id()==ID_typecast) + rhs=to_typecast_expr(rhs).op(); + + const auto &values= + static_cast(ai).value_analysis[from]; + const auto rhs_deref=dereference(rhs, values, "", ns); + auto value_set=values(rhs_deref, ns).value_set; + for(auto &v : value_set) + { + auto &instances=must_alias_relations[v.symbol_expr()]; + instances.isolate(assignment.lhs()); + instances.make_union(assignment.lhs(), rhs); + + remove_dereferences(assignment.lhs(), instances); + add_aliased_dereferences(assignment.lhs(), instances); + + // Do not include CPROVER objects + // TODO: do it better than check for "malloc" substring + if(!(rhs.id()==ID_symbol && + (id2string(to_symbol_expr(rhs).get_identifier()).find( + "malloc::")!=std::string::npos || + id2string(to_symbol_expr(rhs).get_identifier()).find( + "malloc#")!=std::string::npos || + id2string(to_symbol_expr(rhs).get_identifier()).find( + "malloc$")!=std::string::npos))) + { + live_pointers[v.symbol_expr()].insert(rhs); + } + } + } + } + else if(from->is_goto() || from->is_assume() || from->is_assert()) + rhs_concretisation(from->guard, from, ai, ns); + else if(from->is_dead()) + { + const exprt &symbol=to_code_dead(from->code).symbol(); + const auto &values= + static_cast(ai).value_analysis[from]; + auto value_set=values(symbol, ns).value_set; + for(auto &v : value_set) + { + live_pointers[v.symbol_expr()].erase(symbol); + } + } +} + +/*******************************************************************\ + +Function: dynobj_instance_domaint::merge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +bool dynobj_instance_domaint::merge( + const dynobj_instance_domaint &other, + ai_domain_baset::locationt from, + ai_domain_baset::locationt to) +{ + bool result=false; + for(auto &obj : other.must_alias_relations) + { + if(must_alias_relations.find(obj.first)==must_alias_relations.end()) + { + must_alias_relations.insert(obj); + result=true; + } + else + { + if(must_alias_relations.at(obj.first).join(obj.second)) + result=true; + } + + if(other.live_pointers.find(obj.first)!=other.live_pointers.end()) + { + auto &other_pointers=other.live_pointers.at(obj.first); + live_pointers[obj.first].insert( + other_pointers.begin(), other_pointers.end()); + } + } + return result; +} + +/*******************************************************************\ + +Function: dynobj_instance_domaint::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void dynobj_instance_domaint::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + for(const auto &o : must_alias_relations) + { + out << o.first.get_identifier() << ":\n"; + for(const exprt &p : o.second) + { + size_t n; + o.second.get_number(p, n); + out << " " << o.second.find_number(n) << ": " << from_expr(ns, "", p) + << "\n"; + } + + if(live_pointers.find(o.first)==live_pointers.end()) + continue; + out << "Live: "; + for(const auto &v : live_pointers.at(o.first)) + { + out << from_expr(ns, "", v) << " "; + } + out << "\n"; + } +} diff --git a/src/ssa/dynobj_instance_analysis.h b/src/ssa/dynobj_instance_analysis.h new file mode 100644 index 000000000..2dd170a21 --- /dev/null +++ b/src/ssa/dynobj_instance_analysis.h @@ -0,0 +1,191 @@ +/*******************************************************************\ + +Module: Analysis of the number of instances of abstract dynamic objects. + +Author: Viktor Malik, viktor.malik@gmail.com + +Description: In some cases, multiple instances must be used so that the + analysis is sound. The analysis computes for each allocation + site 'a' and each program location 'i' a set of pointer + expressions that may point to some object allocated at 'a' + in the location 'i'. Then, a must-alias relation is computed + over these sets and the maximum number of equivalence classes + gives the number of required objects for the given allocation + site. + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_DYNOBJ_INSTANCE_ANALYSIS_H +#define CPROVER_2LS_SSA_DYNOBJ_INSTANCE_ANALYSIS_H + + +#include +#include +#include "ssa_object.h" +#include "ssa_value_set.h" + +/*******************************************************************\ + + Set partitioning by must-alias relation (it is an equivalence). + It extends the standard union find structure, particularly using + a custom join and equality. + +\*******************************************************************/ +class must_alias_setst:public union_find +{ +public: + bool join(const must_alias_setst &other) + { + if(!equal(other)) + { + // Find new elements (those that are unique to one of the sets) + auto new_elements=sym_diff_elements(other); + // Copy *this + auto original=*this; + + // Make intersection (into *this) which contains all common elements + // (after retyping to vector) + clear(); + std::set common_elements; + for(auto &e1 : original) + { + if(new_elements.find(e1)!=new_elements.end()) + continue; + + isolate(e1); + for(auto &e2 : common_elements) + if(original.same_set(e1, e2) && other.same_set(e1, e2)) + make_union(e1, e2); + common_elements.insert(e1); + } + + for(auto &e_new : new_elements) + { + bool added=false; + // First, try to find some new element that is already in *this and that + // is in the same class as e_new in one of the sets + auto this_copy(*this); + for(auto &e : this_copy) + { + if(new_elements.find(e)!=new_elements.end() && + (original.same_set(e, e_new) || other.same_set(e, e_new))) + { + make_union(e, e_new); + added=true; + } + } + if(!added) + { + // Find all sets to which e_new should be added by comparing with + // common elements + // The map will contain: set_number(in *this) -> set_representative + std::map dest_sets; + for(auto &e : common_elements) + { + if(original.same_set(e_new, e) || other.same_set(e_new, e)) + { + size_t n; + get_number(e, n); + if(dest_sets.find(n)==dest_sets.end()) + dest_sets.emplace(n, e); + } + } + + // If there is just one set to add e_new to, add it there, otherwise + // isolate it + if(dest_sets.size()==1) + make_union(e_new, dest_sets.begin()->second); + else + isolate(e_new); + } + } + return true; + } + return false; + } + +protected: + // Two partitionings are equal if they contain same elements partitioned + // in same sets (not necessarily having same numbers). + bool equal(const must_alias_setst &other) + { + if(size()!=other.size()) + return false; + + for(auto &e1 : *this) + { + size_t n; + if(other.get_number(e1, n)) + return false; + for(auto &e2 : *this) + if(same_set(e1, e2)!=other.same_set(e1, e2)) + return false; + } + return true; + } + + // Symmetric difference of elements + std::set sym_diff_elements(const must_alias_setst &other) + { + std::set result; + size_t n; + for(auto &e : *this) + if(get_number(e, n)) + result.insert(e); + for(auto &e : other) + if(get_number(e, n)) + result.insert(e); + return result; + } +}; + +class dynobj_instance_domaint:public ai_domain_baset +{ +public: + // Must-alias relation for each dynamic object (corresponding to allocation + // site). + std::map must_alias_relations; + // Set of live pointers pointing to dynamic object. + std::map> live_pointers; + + void transform( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns) override; + void output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const override; + bool merge( + const dynobj_instance_domaint &other, + locationt from, + locationt to); + +private: + void rhs_concretisation( + const exprt &guard, + ai_domain_baset::locationt loc, + ai_baset &ai, + const namespacet &ns); +}; + +class dynobj_instance_analysist:public ait +{ +public: + dynobj_instance_analysist( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns, + ssa_value_ait &_value_ai): + value_analysis(_value_ai) + { + operator()(goto_function, ns); + } + +protected: + ssa_value_ait &value_analysis; + + friend class dynobj_instance_domaint; +}; + +#endif // CPROVER_2LS_SSA_DYNOBJ_INSTANCE_ANALYSIS_H diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index f86fbb092..ca9a27db8 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -22,7 +22,6 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "local_ssa.h" -#include "malloc_ssa.h" #include "ssa_dereference.h" #include "address_canonizer.h" @@ -58,6 +57,8 @@ void local_SSAt::build_SSA() build_guard(i_it); build_assertions(i_it); build_function_call(i_it); +// build_unknown_objs(i_it); + collect_record_frees(i_it); } // collect custom templates in loop heads @@ -136,22 +137,33 @@ void local_SSAt::get_globals( << from_expr(ns, "", read_lhs(it->get_expr(), loc)) << std::endl; #endif - if(!with_returns && - id2string(it->get_identifier()).find( - "#return_value")!=std::string::npos) + if(!with_returns && !is_pointed(it->get_expr()) && + id2string(it->get_identifier()).find("#return_value")!= + std::string::npos) continue; // filter out return values of other functions if(with_returns && returns_for_function!="" && - id2string(it->get_identifier()).find( - "#return_value")!=std::string::npos && - id2string(it->get_identifier()).find( - id2string(returns_for_function)+"#return_value")==std::string::npos) + id2string(it->get_identifier()).find("#return_value")== + id2string(it->get_identifier()).size()- + std::string("#return_value").size() && + id2string(it->get_identifier()).find( + id2string(returns_for_function)+"#return_value")==std::string::npos) continue; + const exprt &root_obj=it->get_root_object(); + if(is_ptr_object(root_obj)) + { + const symbolt *symbol; + irep_idt ptr_obj_id=root_obj.get(ID_ptr_object); + if(ns.lookup(ptr_obj_id, symbol)) + continue; + } + if(rhs_value) { - const exprt &expr=read_rhs(it->get_expr(), loc); + ssa_objectt object(it->get_expr(), ns); + const exprt &expr=read_rhs(object, loc); globals.insert(to_symbol_expr(expr)); } else @@ -190,9 +202,10 @@ void local_SSAt::collect_custom_templates() if(nn_it->templates.empty()) continue; - n_it->loophead->templates.insert(n_it->loophead->templates.end(), - nn_it->templates.begin(), - nn_it->templates.end()); + n_it->loophead->templates.insert( + n_it->loophead->templates.end(), + nn_it->templates.begin(), + nn_it->templates.end()); nn_it->templates.clear(); } } @@ -379,6 +392,10 @@ void local_SSAt::build_phi_nodes(locationt loc) const locationt &iloc=get_location(incoming_it->first); exprt incoming_value=name(*o_it, LOOP_BACK, iloc); exprt incoming_select=name(guard_symbol(), LOOP_SELECT, iloc); + loop_guards.insert( + std::make_pair( + to_symbol_expr(incoming_select), + to_symbol_expr(guard_symbol(loc)))); if(rhs.is_nil()) // first rhs=incoming_value; @@ -448,10 +465,35 @@ void local_SSAt::build_transfer(locationt loc) id2string(code_assign.rhs().get(ID_identifier)). find(TEMPLATE_PREFIX)!=std::string::npos) return; + // build allocation guards map + collect_allocation_guards(code_assign, loc); + exprt deref_lhs=dereference(code_assign.lhs(), loc); exprt deref_rhs=dereference(code_assign.rhs(), loc); - assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); + if(deref_lhs.get_bool("#heap_access") || deref_rhs.get_bool("#heap_access")) + { + exprt symbolic_deref_lhs=symbolic_dereference(code_assign.lhs(), ns); + const exprt rhs=concretise_symbolic_deref_rhs(code_assign.rhs(), ns, loc); + + if(deref_lhs.get_bool("#heap_access") && + has_symbolic_deref(symbolic_deref_lhs)) + { + assign_rec(symbolic_deref_lhs, rhs, true_exprt(), loc); + assign_rec( + deref_lhs, + symbolic_deref_lhs, + true_exprt(), + loc, + true); + } + else + { + assign_rec(deref_lhs, rhs, true_exprt(), loc); + } + } + else + assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); } } @@ -528,22 +570,25 @@ void local_SSAt::build_function_call(locationt loc) return; } - f=to_function_application_expr(read_rhs(f, loc)); assert(f.function().id()==ID_symbol); // no function pointers + + f=to_function_application_expr(read_rhs(f, loc)); + irep_idt fname=to_symbol_expr(f.function()).get_identifier(); // add equalities for arguments unsigned i=0; - for(exprt::operandst::iterator it=f.arguments().begin(); - it!=f.arguments().end(); ++it, ++i) + for(exprt &a : f.arguments()) { - symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i), it->type()); - n_it->equalities.push_back(equal_exprt(*it, arg)); - *it=arg; + const std::string arg_name= + id2string(fname)+"#arg"+i2string(i)+"#"+ + i2string(loc->location_number); + symbol_exprt arg(arg_name, a.type()); + n_it->equalities.push_back(equal_exprt(a, arg)); + a=arg; + ++i; } - n_it->function_calls.push_back( - to_function_application_expr(f)); + n_it->function_calls.push_back(to_function_application_expr(f)); } } @@ -671,7 +716,38 @@ void local_SSAt::build_assertions(locationt loc) { if(loc->is_assert()) { - exprt c=read_rhs(loc->guard, loc); + exprt assert=loc->guard; + if(assert.id()==ID_not && assert.op0().id()==ID_equal && + assert.op0().op1().id()==ID_pointer_object && + assert.op0().op1().op0().id()==ID_symbol) + { + std::string id=id2string( + to_symbol_expr(assert.op0().op1().op0()).get_identifier()); + if(id.find("__CPROVER_deallocated")!=std::string::npos) + { + const exprt &dealloc_symbol=assert.op0().op1().op0(); + exprt::operandst d; + for(auto &global : assignments.ssa_objects.globals) + { + if(global.get_expr().get_bool("#concrete")) + { + d.push_back( + equal_exprt( + dealloc_symbol, + typecast_exprt( + address_of_exprt(global.symbol_expr()), + dealloc_symbol.type()))); + } + } + assert=implies_exprt(disjunction(d), assert); + } + } + + const exprt deref_rhs=dereference(assert, loc); + collect_iterators_rhs(deref_rhs, loc); + + const exprt rhs=concretise_symbolic_deref_rhs(assert, ns, loc); + exprt c=read_rhs(rhs, loc); exprt g=guard_symbol(loc); (--nodes.end())->assertions.push_back(implies_exprt(g, c)); } @@ -696,8 +772,10 @@ void local_SSAt::assertions_to_constraints() n_it!=nodes.end(); n_it++) { - n_it->constraints.insert(n_it->constraints.end(), - n_it->assertions.begin(), n_it->assertions.end()); + n_it->constraints.insert( + n_it->constraints.end(), + n_it->assertions.begin(), + n_it->assertions.end()); } } @@ -1021,9 +1099,10 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const else if(expr.id()==ID_index) { const index_exprt &index_expr=to_index_expr(expr); - return index_exprt(read_rhs(index_expr.array(), loc), - read_rhs(index_expr.index(), loc), - expr.type()); + return index_exprt( + read_rhs(index_expr.array(), loc), + read_rhs(index_expr.index(), loc), + expr.type()); } ssa_objectt object(expr, ns); @@ -1038,6 +1117,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const return tmp; } +#if 0 // Argument is a struct-typed ssa object? // May need to split up into members. const typet &type=ns.follow(expr.type()); @@ -1063,12 +1143,28 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const return result; } +#endif // is this an object we track? if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { - return read_rhs(object, loc); + // If the last definition of an object is at its allocation, we can only use + // the corresponding symbol if the object has truly been allocated + // (allocation guard holds). Otherwise we need to use the last definition + // before the allocation. + auto def_it=ssa_analysis[loc].def_map.find(object.get_identifier()); + if(def_it!=ssa_analysis[loc].def_map.end() && + def_it->second.def.kind==ssa_domaint::deft::ALLOCATION) + { + locationt alloc_loc=def_it->second.def.loc; + return if_exprt( + read_rhs(def_it->second.def.guard, alloc_loc), + read_rhs(object, loc), + read_rhs(object, alloc_loc)); + } + else + return read_rhs(object, loc); } else { @@ -1146,7 +1242,10 @@ symbol_exprt local_SSAt::name( unsigned cnt=loc->location_number; irep_idt new_id=id2string(id)+"#"+ - (kind==PHI?"phi":kind==LOOP_BACK?"lb":kind==LOOP_SELECT?"ls":"")+ + (kind==PHI?"phi": + kind==LOOP_BACK?"lb": + kind==LOOP_SELECT?"ls": + kind==OBJECT_SELECT?"os":"")+ i2string(cnt)+ (kind==LOOP_SELECT?std::string(""):suffix); @@ -1159,6 +1258,8 @@ symbol_exprt local_SSAt::name( if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); + copy_pointed_info(new_symbol_expr, object.get_expr()); + return new_symbol_expr; } @@ -1208,6 +1309,8 @@ symbol_exprt local_SSAt::name_input(const ssa_objectt &object) const if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); + copy_pointed_info(new_symbol_expr, object.get_expr()); + return new_symbol_expr; } @@ -1254,7 +1357,8 @@ void local_SSAt::assign_rec( const exprt &lhs, const exprt &rhs, const exprt &guard, - locationt loc) + locationt loc, + bool fresh_rhs) { const typet &type=ns.follow(lhs.type()); @@ -1274,7 +1378,7 @@ void local_SSAt::assign_rec( { member_exprt new_lhs(lhs, it->get_name(), it->type()); member_exprt new_rhs(rhs, it->get_name(), it->type()); - assign_rec(new_lhs, new_rhs, guard, loc); + assign_rec(new_lhs, new_rhs, guard, loc, fresh_rhs); } return; @@ -1287,7 +1391,11 @@ void local_SSAt::assign_rec( if(assigned.find(lhs_object)!=assigned.end()) { - exprt ssa_rhs=read_rhs(rhs, loc); + collect_iterators_lhs(lhs_object, loc); + collect_iterators_rhs(rhs, loc); + + exprt ssa_rhs=fresh_rhs ? name(ssa_objectt(rhs, ns), OUT, loc) + : read_rhs(rhs, loc); const symbol_exprt ssa_symbol=name(lhs_object, OUT, loc); @@ -1300,7 +1408,7 @@ void local_SSAt::assign_rec( const index_exprt &index_expr=to_index_expr(lhs); exprt ssa_array=index_expr.array(); exprt new_rhs=with_exprt(ssa_array, index_expr.index(), rhs); - assign_rec(index_expr.array(), new_rhs, guard, loc); + assign_rec(index_expr.array(), new_rhs, guard, loc, fresh_rhs); } else if(lhs.id()==ID_member) { @@ -1313,14 +1421,14 @@ void local_SSAt::assign_rec( { union_exprt new_rhs( member_expr.get_component_name(), rhs, compound.type()); - assign_rec(member_expr.struct_op(), new_rhs, guard, loc); + assign_rec(member_expr.struct_op(), new_rhs, guard, loc, fresh_rhs); } else if(compound_type.id()==ID_struct) { exprt member_name(ID_member_name); member_name.set(ID_component_name, member_expr.get_component_name()); with_exprt new_rhs(compound, member_name, rhs); - assign_rec(compound, new_rhs, guard, loc); + assign_rec(compound, new_rhs, guard, loc, fresh_rhs); } } else if(lhs.id()==ID_complex_real) @@ -1330,7 +1438,7 @@ void local_SSAt::assign_rec( const complex_typet &complex_type=to_complex_type(op.type()); exprt imag_op=unary_exprt(ID_complex_imag, op, complex_type.subtype()); complex_exprt new_rhs(rhs, imag_op, complex_type); - assign_rec(op, new_rhs, guard, loc); + assign_rec(op, new_rhs, guard, loc, fresh_rhs); } else if(lhs.id()==ID_complex_imag) { @@ -1339,17 +1447,58 @@ void local_SSAt::assign_rec( const complex_typet &complex_type=to_complex_type(op.type()); exprt real_op=unary_exprt(ID_complex_real, op, complex_type.subtype()); complex_exprt new_rhs(real_op, rhs, complex_type); - assign_rec(op, new_rhs, guard, loc); + assign_rec(op, new_rhs, guard, loc, fresh_rhs); } else if(lhs.id()==ID_if) { const if_exprt &if_expr=to_if_expr(lhs); - assign_rec(if_expr.true_case(), rhs, and_exprt(guard, if_expr.cond()), loc); + + exprt::operandst other_cond_conj; + if(if_expr.true_case().get_bool("#heap_access") && + if_expr.cond().id()==ID_equal) + { + const exprt heap_object=if_expr.true_case(); + const ssa_objectt ptr_object(to_equal_expr(if_expr.cond()).lhs(), ns); + if(ptr_object) + { + const irep_idt ptr_id=ptr_object.get_identifier(); + const exprt cond=read_rhs(if_expr.cond(), loc); + + for(const dyn_obj_assignt &do_assign : dyn_obj_assigns[heap_object]) + { + if(!alias_analysis[loc].aliases.same_set( + ptr_id, do_assign.pointer_id)) + { + other_cond_conj.push_back(do_assign.cond); + } + } + + dyn_obj_assigns[heap_object].emplace_back(ptr_id, cond); + } + } + + exprt cond=if_expr.cond(); + if(!other_cond_conj.empty()) + { + const exprt other_cond=or_exprt( + not_exprt(conjunction(other_cond_conj)), + name(guard_symbol(), OBJECT_SELECT, loc)); + cond=and_exprt(cond, other_cond); + } + exprt orig_rhs=fresh_rhs ? name(ssa_objectt(rhs, ns), OUT, loc) : rhs; + exprt new_rhs=if_exprt(cond, orig_rhs, if_expr.true_case()); + assign_rec( + if_expr.true_case(), + new_rhs, + and_exprt(guard, if_expr.cond()), + loc); + assign_rec( if_expr.false_case(), rhs, and_exprt(guard, not_exprt(if_expr.cond())), - loc); + loc, + fresh_rhs); } else if(lhs.id()==ID_byte_extract_little_endian || lhs.id()==ID_byte_extract_big_endian) @@ -1361,10 +1510,10 @@ void local_SSAt::assign_rec( exprt new_rhs=byte_extract_exprt( byte_extract_expr.id(), rhs, byte_extract_expr.offset(), new_lhs.type()); - assign_rec(new_lhs, new_rhs, guard, loc); + assign_rec(new_lhs, new_rhs, guard, loc, fresh_rhs); } else - throw "UNKNOWN LHS: "+lhs.id_string(); + throw "UNKNOWN LHS: "+lhs.id_string(); // NOLINT(*) } /*******************************************************************\ @@ -1736,3 +1885,406 @@ bool local_SSAt::has_function_calls() const return found; } +/*******************************************************************\ + +Function: local_SSAt::build_unknown_objs + + Inputs: + + Outputs: + + Purpose: If a location is malloc call, create "unknown object" for + return type. This is later used as a placeholder for invalid + of unknown dereference of an object of that type. + +\*******************************************************************/ + +void local_SSAt::build_unknown_objs(locationt loc) +{ + if(loc->is_assign()) + { + const code_assignt &code_assign=to_code_assign(loc->code); + const exprt &rhs=code_assign.rhs(); + if(rhs.get_bool("#malloc_result")) + { + const exprt &malloc_res= + rhs.id()==ID_typecast ? to_typecast_expr(rhs).op() : rhs; + const exprt &addr_of_do= + malloc_res.id()==ID_if ? to_if_expr(malloc_res).true_case() + : malloc_res; + const exprt &dyn_obj=to_address_of_expr(addr_of_do).object(); + const typet &dyn_type=ns.follow(dyn_obj.type()); + + std::string dyn_type_name=dyn_type.id_string(); + if(dyn_type.id()==ID_struct) + dyn_type_name+="_"+id2string(to_struct_type(dyn_type).get_tag()); + irep_idt identifier="ssa::"+dyn_type_name+"_obj$unknown"; + + symbol_exprt unknown_obj(identifier, dyn_obj.type()); + unknown_objs.insert(unknown_obj); + } + } +} + +/*******************************************************************\ + +Function: local_SSAt::unknown_obj_eq + + Inputs: + + Outputs: + + Purpose: Create equality obj.component = &obj, which creates self-loop + on "unknown" objects. + +\*******************************************************************/ + +exprt local_SSAt::unknown_obj_eq( + const symbol_exprt &obj, + const struct_typet::componentt &component) const +{ + const irep_idt identifier= + id2string(obj.get_identifier())+"."+id2string(component.get_name()); + const symbol_exprt member(identifier, component.type()); + return equal_exprt(member, address_of_exprt(obj)); +} + +/*******************************************************************\ + +Function: local_SSAt::collect_iterators_rhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::collect_iterators_rhs(const exprt &expr, locationt loc) +{ + if(expr.id()==ID_member) + { + const member_exprt &member=to_member_expr(expr); + if(member.compound().get_bool(ID_iterator) && + member.compound().id()==ID_symbol) + { + new_iterator_access(to_member_expr(expr), loc, list_iteratort::IN_LOC); + } + } + else + { + forall_operands(it, expr) + collect_iterators_rhs(*it, loc); + } +} + +/*******************************************************************\ + +Function: local_SSAt::collect_iterators_lhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::collect_iterators_lhs( + const ssa_objectt &object, + local_SSAt::locationt loc) +{ + if(is_iterator(object.get_root_object()) && + object.get_root_object().id()==ID_symbol) + { + assert(object.get_expr().id()==ID_member); + new_iterator_access( + to_member_expr(object.get_expr()), + loc, + loc->location_number); + } +} + +/*******************************************************************\ + +Function: local_SSAt::new_iterator_access + + Inputs: + + Outputs: + + Purpose: Create new iterator access + +\*******************************************************************/ + +void local_SSAt::new_iterator_access( + const member_exprt &expr, + local_SSAt::locationt loc, + unsigned inst_loc_number) +{ + assert(is_iterator(expr.compound())); + + const irep_idt pointer_id=expr.compound().get(ID_it_pointer); + const symbolt &pointer_symbol=ns.lookup(pointer_id); + exprt pointer_rhs=read_rhs(pointer_symbol.symbol_expr(), loc); + assert(pointer_rhs.id()==ID_symbol); + + unsigned init_value_level=expr.compound().get_unsigned_int( + ID_it_init_value_level); + const exprt init_pointer=get_pointer(expr.compound(), init_value_level-1); + + list_iteratort iterator( + to_symbol_expr(pointer_rhs), + init_pointer, + get_iterator_fields(expr.compound())); + + auto it=iterators.insert(iterator); + it.first->add_access(expr, inst_loc_number); +} + +/*******************************************************************\ + +Function: local_SSAt::all_symbolic_deref_defined + + Inputs: + + Outputs: + + Purpose: Create new iterator access + +\*******************************************************************/ +bool local_SSAt::all_symbolic_deref_defined( + const exprt &expr, + const namespacet &ns, + locationt loc) const +{ + bool result=true; + ssa_objectt ssa_object(expr, ns); + if(ssa_object && has_symbolic_deref(ssa_object.get_expr())) + { + const ssa_domaint &ssa_domain=ssa_analysis[loc]; + auto def_it=ssa_domain.def_map.find(ssa_object.get_identifier()); + if(def_it==ssa_domain.def_map.end() || def_it->second.def.is_input()) + result=false; + } + else forall_operands(it, expr) + result=result && all_symbolic_deref_defined(*it, ns, loc); + return result; +} + + +/********************************************************************\ + +Function: local_SSAt::concretise_rhs + + Inputs: + + Outputs: + + Purpose: Concretise symbolic rhs and return resulting expr + +\*******************************************************************/ + +exprt local_SSAt::concretise_symbolic_deref_rhs( + const exprt &rhs, + const namespacet &ns, + const locationt loc) +{ + const exprt deref_rhs=dereference(rhs, loc); + const exprt symbolic_deref_rhs=symbolic_dereference(rhs, ns); + ssa_objectt rhs_object(symbolic_deref_rhs, ns); + + if(deref_rhs.get_bool("#heap_access") && rhs_object) + { + if(can_reuse_symderef(rhs_object, ns, loc)) + { + return symbolic_deref_rhs; + } + else + { + assign_rec(symbolic_deref_rhs, deref_rhs, true_exprt(), loc); + return name(ssa_objectt(symbolic_deref_rhs, ns), OUT, loc); + } + } + else + { + exprt rhs_copy=rhs; + Forall_operands(it, rhs_copy) + { + *it=concretise_symbolic_deref_rhs(*it, ns, loc); + } + return rhs_copy; + } + + return + all_symbolic_deref_defined(symbolic_deref_rhs, ns, loc)? + symbolic_deref_rhs:deref_rhs; +} + +/********************************************************************\ + +Function: local_SSAt::collect_allocation_guards + + Inputs: + + Outputs: + + Purpose: Collect allocation guards for the given location + +\*******************************************************************/ + +void local_SSAt::collect_allocation_guards( + const code_assignt &assign, + locationt loc) +{ + if(!assign.rhs().get_bool("#malloc_result")) + return; + + exprt rhs=assign.rhs(); + if(rhs.id()==ID_typecast) + rhs=to_typecast_expr(rhs).op(); + if(rhs.id()==ID_if) + { + get_alloc_guard_rec( + to_if_expr(rhs).true_case(), read_rhs(to_if_expr(rhs).cond(), loc)); + get_alloc_guard_rec( + to_if_expr(rhs).false_case(), + read_rhs(not_exprt(to_if_expr(rhs).cond()), loc)); + } +} + +/********************************************************************\ + +Function: local_SSAt::collect_record_frees + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void local_SSAt::collect_record_frees(local_SSAt::locationt loc) +{ + if(loc->is_decl()) + { + const exprt &symbol=to_code_decl(loc->code).symbol(); + if(symbol.id()!=ID_symbol) + return; + + std::string id=id2string(to_symbol_expr(symbol).get_identifier()); + if(id.find("free::")!=std::string::npos && + id.find("::record")!=std::string::npos) + { + (--nodes.end())->record_free=symbol; + } + } +} + +/********************************************************************\ + +Function: local_SSAt::get_alloc_guard_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void local_SSAt::get_alloc_guard_rec(const exprt &expr, exprt guard) +{ + if(expr.id()==ID_symbol && expr.type().get_bool("#dynamic")) + { + allocation_guards.emplace(to_symbol_expr(expr).get_identifier(), guard); + } + else if(expr.id()==ID_if) + { + get_alloc_guard_rec( + to_if_expr(expr).true_case(), and_exprt(guard, to_if_expr(expr).cond())); + get_alloc_guard_rec( + to_if_expr(expr).false_case(), + and_exprt(guard, not_exprt(to_if_expr(expr).cond()))); + } + else if(expr.id()==ID_typecast) + get_alloc_guard_rec(to_typecast_expr(expr).op(), guard); + else if(expr.id()==ID_address_of) + get_alloc_guard_rec(to_address_of_expr(expr).object(), guard); +} + +/********************************************************************\ + +Function: local_SSAt::can_reuse_symderef + + Inputs: Symbolic deference object, namespace, current location. + + Outputs: True if the symbolic dereference can be reused from the last location + that it was defined in (i.e. it does not have to be redefinef). + Otherwise false. + + Purpose: The symbolic dereference object can be reused if and only if + the pointer it dereferences was not overwritten and any potentially + aliased object (or field) was not overwritten. + +\*******************************************************************/ +bool local_SSAt::can_reuse_symderef( + ssa_objectt &symderef, + const namespacet &ns, + const local_SSAt::locationt loc) +{ + // Get a pointer that is dereferenced in the symbolic deref + const exprt pointer=get_pointer( + symderef.get_root_object(), + pointed_level(symderef.get_root_object())-1); + // Get the last definition of the pointer + const auto pointer_id=ssa_objectt(pointer, ns).get_identifier(); + const auto pointer_def=ssa_analysis[loc].def_map.find( + pointer_id)->second.def; + // Get the last definition of the symbolic dereference + const auto symbolic_id=symderef.get_identifier(); + const auto symbolic_def=ssa_analysis[loc].def_map.find( + symbolic_id)->second.def; + + // If symbolic deref was not created yet, it cannot be reused. + if(!symbolic_def.is_assignment()) + return false; + + // If the pointer that is dereferenced was overwritten, the symbolic deref + // is not valid. + if(pointer_def.is_assignment() && pointer_def.loc->location_number> + symbolic_def.loc->location_number) + return false; + + // Search all aliasing objects (objects potentially pointed by the pointer) + const auto &values=ssa_value_ai[loc](pointer, ns); + for(auto &obj : values.value_set) + { + irep_idt deref_id; + if(symderef.get_expr().id()==ID_member) + { + auto member=member_exprt( + obj.symbol_expr(), + to_member_expr(symderef.get_expr()).get_component_name(), + symderef.type()); + deref_id=ssa_objectt(member, ns).get_identifier(); + } + else + { + deref_id=obj.get_identifier(); + } + // If some potentially aliased object (or field) was overwritten, + // the symbolic dereference cannot be reused. + auto deref_def=ssa_analysis[loc].def_map.find(deref_id); + if(deref_def!=ssa_analysis[loc].def_map.end() && + (deref_def->second.def.is_assignment() || + deref_def->second.def.is_phi()) && + deref_def->second.def.loc->location_number> + symbolic_def.loc->location_number) + { + return false; + } + } + + return true; +} diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 0f8fcc477..56a3dcc4c 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -13,10 +13,14 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "../domains/incremental_solver.h" +#include +#include + #include "ssa_domain.h" #include "guard_map.h" #include "ssa_object.h" +#include "ssa_heap_domain.h" +#include "may_alias_analysis.h" #define TEMPLATE_PREFIX "__CPROVER_template" #define TEMPLATE_DECL TEMPLATE_PREFIX @@ -32,11 +36,15 @@ class local_SSAt inline local_SSAt( const goto_functiont &_goto_function, const namespacet &_ns, + const ssa_heap_analysist &_heap_analysis, const std::string &_suffix=""): ns(_ns), goto_function(_goto_function), - ssa_objects(_goto_function, ns), - ssa_value_ai(_goto_function, ns), - assignments(_goto_function.body, ns, ssa_objects, ssa_value_ai), + heap_analysis(_heap_analysis), + ssa_objects(_goto_function, ns, _heap_analysis), + ssa_value_ai(_goto_function, ns, _heap_analysis), + assignments( + _goto_function.body, ns, ssa_objects, ssa_value_ai, heap_analysis), + alias_analysis(_goto_function, ns), guard_map(_goto_function.body), ssa_analysis(assignments), suffix(_suffix) @@ -49,7 +57,7 @@ class local_SSAt } void output(std::ostream &) const; - void output_verbose(std::ostream &) const; + virtual void output_verbose(std::ostream &) const; // the SSA node for a location class nodet @@ -57,14 +65,13 @@ class local_SSAt public: inline nodet( locationt _location, - std::list::iterator _loophead) - : - enabling_expr(true_exprt()), - marked(false), - location(_location), - loophead(_loophead) - { - } + std::list::iterator _loophead): + enabling_expr(true_exprt()), + marked(false), + location(_location), + loophead(_loophead) + { + } typedef std::vector equalitiest; equalitiest equalities; @@ -89,6 +96,8 @@ class local_SSAt std::list::iterator loophead; // link to loop head node // otherwise points to nodes.end() + exprt record_free=nil_exprt(); + void output(std::ostream &, const namespacet &) const; inline bool empty() const @@ -108,12 +117,14 @@ class local_SSAt void mark_nodes() { for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked=true; + n_it!=nodes.end(); n_it++) + n_it->marked=true; } void unmark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked=false; + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + n_it->marked=false; } // for incremental unwinding @@ -126,6 +137,28 @@ class local_SSAt var_listt params; var_sett globals_in, globals_out; + std::set iterators; + + // unknown heap objects + var_sett unknown_objs; + + // Maps members of dynamic object to a set of pointers used to access those + // objects when assigning them + class dyn_obj_assignt + { + public: + const irep_idt pointer_id; + const exprt cond; + + dyn_obj_assignt(const irep_idt &pointer_id, const exprt &cond): + pointer_id(pointer_id), cond(cond) {} + }; + typedef std::list dyn_obj_assignst; + std::map dyn_obj_assigns; + + // Map dynamic object names to guards of their allocation + std::map allocation_guards; + bool has_function_calls() const; const namespacet &ns; @@ -141,9 +174,11 @@ class local_SSAt exprt edge_guard(locationt from, locationt to) const; // auxiliary functions - enum kindt { PHI, OUT, LOOP_BACK, LOOP_SELECT }; + enum kindt { PHI, OUT, LOOP_BACK, LOOP_SELECT, OBJECT_SELECT }; virtual symbol_exprt name( - const ssa_objectt &, kindt kind, locationt loc) const; + const ssa_objectt &, + kindt kind, + locationt loc) const; symbol_exprt name(const ssa_objectt &, const ssa_domaint::deft &) const; symbol_exprt name_input(const ssa_objectt &) const; virtual exprt nondet_symbol( @@ -160,7 +195,22 @@ class local_SSAt symbol_exprt read_rhs(const ssa_objectt &, locationt loc) const; exprt read_node_in(const ssa_objectt &, locationt loc) const; void assign_rec( - const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); + const exprt &lhs, + const exprt &rhs, + const exprt &guard, + locationt loc, + bool fresh_rhs=false); + + void collect_iterators_rhs(const exprt &expr, locationt loc); + void collect_iterators_lhs(const ssa_objectt &object, locationt loc); + void new_iterator_access( + const member_exprt &expr, + locationt loc, + unsigned inst_loc_number); + + exprt unknown_obj_eq( + const symbol_exprt &obj, + const struct_typet::componentt &component) const; void get_entry_exit_vars(); @@ -169,17 +219,40 @@ class local_SSAt exprt dereference(const exprt &expr, locationt loc) const; + bool all_symbolic_deref_defined( + const exprt &expr, + const namespacet &ns, + locationt loc) const; + + exprt concretise_symbolic_deref_rhs( + const exprt &rhs, + const namespacet &ns, + const locationt loc); + + bool can_reuse_symderef( + ssa_objectt &symderef, + const namespacet &ns, + const locationt loc); + + const ssa_heap_analysist &heap_analysis; + ssa_objectst ssa_objects; typedef ssa_objectst::objectst objectst; ssa_value_ait ssa_value_ai; assignmentst assignments; + may_alias_analysist alias_analysis; + // protected: guard_mapt guard_map; ssa_ait ssa_analysis; std::string suffix; // an extra suffix + // Collect all loop_guards that will represent symbolic paths used in heap + // domain + std::set> loop_guards; + void get_globals( locationt loc, std::set &globals, @@ -190,7 +263,8 @@ class local_SSAt nodest::iterator find_node(locationt loc); nodest::const_iterator find_node(locationt loc) const; void find_nodes( - locationt loc, std::list &_nodes) const; + locationt loc, + std::list &_nodes) const; inline locationt get_location(unsigned location_number) const { @@ -212,6 +286,11 @@ class local_SSAt void build_guard(locationt loc); void build_function_call(locationt loc); void build_assertions(locationt loc); + void build_unknown_objs(locationt loc); + + void collect_allocation_guards(const code_assignt &assign, locationt loc); + void get_alloc_guard_rec(const exprt &expr, exprt old_guard); + void collect_record_frees(locationt loc); // custom templates void collect_custom_templates(); diff --git a/src/ssa/malloc_ssa.cpp b/src/ssa/malloc_ssa.cpp index fd5246b4e..5bc2e0f48 100644 --- a/src/ssa/malloc_ssa.cpp +++ b/src/ssa/malloc_ssa.cpp @@ -15,6 +15,9 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include + +#include #include "malloc_ssa.h" @@ -53,6 +56,105 @@ inline static typet c_sizeof_type_rec(const exprt &expr) /*******************************************************************\ +Function: create_dynamic_object + + Inputs: + + Outputs: + + Purpose: Create new dynamic object, insert it into the symbol table + and return its address. + +\*******************************************************************/ + +exprt create_dynamic_object( + const std::string &suffix, + const typet &type, + symbol_tablet &symbol_table, + bool is_concrete) +{ + symbolt value_symbol; + + value_symbol.base_name="dynamic_object"+suffix; + value_symbol.name="ssa::"+id2string(value_symbol.base_name); + value_symbol.is_lvalue=true; + value_symbol.type=type; + value_symbol.type.set("#dynamic", true); + value_symbol.mode=ID_C; + symbol_table.add(value_symbol); + + address_of_exprt address_of_object; + + if(type.id()==ID_array) + { + address_of_object.type()=pointer_typet(value_symbol.type.subtype()); + index_exprt index_expr(value_symbol.type.subtype()); + index_expr.array()=value_symbol.symbol_expr(); + index_expr.index()=gen_zero(index_type()); + address_of_object.op0()=index_expr; + } + else + { + address_of_object.op0()=value_symbol.symbol_expr(); + if(is_concrete) + address_of_object.op0().set("#concrete", true); + address_of_object.type()=pointer_typet(value_symbol.type); + } + + return address_of_object; +} + +/*******************************************************************\ + +Function: collect_pointer_vars + + Inputs: + + Outputs: + + Purpose: Collect all variables (symbols and their members) of pointer + type with given pointed type. + +\*******************************************************************/ + +std::vector collect_pointer_vars( + const symbol_tablet &symbol_table, + const typet &pointed_type) +{ + namespacet ns(symbol_table); + std::vector pointers; + forall_symbols(it, symbol_table.symbols) + { + if(ns.follow(it->second.type).id()==ID_struct) + { + for(auto &component : to_struct_type( + ns.follow(it->second.type)).components()) + { + if(component.type().id()==ID_pointer) + { + if(ns.follow(component.type().subtype())==ns.follow(pointed_type)) + { + pointers.push_back( + member_exprt( + it->second.symbol_expr(), component.get_name(), + component.type())); + } + } + } + } + if(it->second.type.id()==ID_pointer) + { + if(ns.follow(it->second.type.subtype())==ns.follow(pointed_type)) + { + pointers.push_back(it->second.symbol_expr()); + } + } + } + return pointers; +} + +/*******************************************************************\ + Function: malloc_ssa Inputs: @@ -66,7 +168,9 @@ Function: malloc_ssa exprt malloc_ssa( const side_effect_exprt &code, const std::string &suffix, - symbol_tablet &symbol_table) + symbol_tablet &symbol_table, + bool is_concrete, + bool alloc_concrete) { if(code.operands().size()!=1) throw "malloc expected to have one operand"; @@ -131,37 +235,44 @@ exprt malloc_ssa( std::cout << "OBJECT_TYPE: " << from_type(ns, "", object_type) << std::endl; #endif - // value - symbolt value_symbol; + auto pointers=collect_pointer_vars(symbol_table, object_type); - value_symbol.base_name="dynamic_object"+suffix; - value_symbol.name="ssa::"+id2string(value_symbol.base_name); - value_symbol.is_lvalue=true; - value_symbol.type=object_type; - value_symbol.type.set("#dynamic", true); - value_symbol.mode=ID_C; - symbol_table.add(value_symbol); - - address_of_exprt address_of; - - if(object_type.id()==ID_array) + exprt object=create_dynamic_object( + suffix, object_type, symbol_table, is_concrete); + if(object.type()!=code.type()) + object=typecast_exprt(object, code.type()); + exprt result; + if(!is_concrete && alloc_concrete) { - address_of.type()=pointer_typet(value_symbol.type.subtype()); - index_exprt index_expr(value_symbol.type.subtype()); - index_expr.array()=value_symbol.symbol_expr(); - index_expr.index()=gen_zero(index_type()); - address_of.op0()=index_expr; + exprt concrete_object=create_dynamic_object( + suffix+"$co", object_type, symbol_table, true); + + // Create nondet symbol + symbolt nondet_symbol; + nondet_symbol.base_name="nondet"+suffix; + nondet_symbol.name="ssa::"+id2string(nondet_symbol.base_name); + nondet_symbol.is_lvalue=true; + nondet_symbol.type=bool_typet(); + nondet_symbol.mode=ID_C; + symbol_table.add(nondet_symbol); + + exprt::operandst pointer_equs; + for(auto &ptr : pointers) + { + pointer_equs.push_back(equal_exprt(ptr, concrete_object)); + } + exprt cond=and_exprt( + nondet_symbol.symbol_expr(), + not_exprt(disjunction(pointer_equs))); + + if(concrete_object.type()!=code.type()) + concrete_object=typecast_exprt(concrete_object, code.type()); + result=if_exprt(cond, concrete_object, object); } else - { - address_of.op0()=value_symbol.symbol_expr(); - address_of.type()=pointer_typet(value_symbol.type); - } - - exprt result=address_of; + result=object; - if(result.type()!=code.type()) - result=typecast_exprt(result, code.type()); + result.set("#malloc_result", true); return result; } @@ -179,12 +290,14 @@ Function: replace_malloc_rec \*******************************************************************/ -static void replace_malloc_rec( +static bool replace_malloc_rec( exprt &expr, const std::string &suffix, symbol_tablet &symbol_table, const exprt &malloc_size, - unsigned &counter) + unsigned loc_number, + bool is_concrete, + bool alloc_concrete) { if(expr.id()==ID_side_effect && to_side_effect_expr(expr).get_statement()==ID_malloc) @@ -192,12 +305,33 @@ static void replace_malloc_rec( assert(!malloc_size.is_nil()); expr.op0()=malloc_size; - expr=malloc_ssa(to_side_effect_expr(expr), - "$"+i2string(counter++)+suffix, symbol_table); + expr=malloc_ssa( + to_side_effect_expr(expr), + "$"+i2string(loc_number)+suffix, + symbol_table, + is_concrete, + alloc_concrete); + + return true; } else + { + bool result=false; Forall_operands(it, expr) - replace_malloc_rec(*it, suffix, symbol_table, malloc_size, counter); + { + if(replace_malloc_rec(*it, + suffix, + symbol_table, + malloc_size, + loc_number, + is_concrete, + alloc_concrete)) + { + result=true; + } + } + return result; + } } /*******************************************************************\ @@ -212,16 +346,34 @@ Function: replace_malloc \*******************************************************************/ -void replace_malloc( +bool replace_malloc( goto_modelt &goto_model, - const std::string &suffix) + const std::string &suffix, + bool alloc_concrete) { - unsigned counter=0; + bool result=false; Forall_goto_functions(f_it, goto_model.goto_functions) { + goto_programt::const_targett loop_end=f_it->second.body.instructions.end(); exprt malloc_size=nil_exprt(); Forall_goto_program_instructions(i_it, f_it->second.body) { + if(loop_end==f_it->second.body.instructions.end()) + { + for(const auto &incoming : i_it->incoming_edges) + { + if(incoming->is_backwards_goto() && + incoming!=i_it) + { + loop_end=incoming; + } + } + } + else if(i_it==loop_end) + { + loop_end=f_it->second.body.instructions.end(); + } + if(i_it->is_assign()) { code_assignt &code_assign=to_code_assign(i_it->code); @@ -235,12 +387,125 @@ void replace_malloc( id2string(to_symbol_expr(code_assign.lhs()).get_identifier()); if(lhs_id=="malloc::malloc_size" || lhs_id=="__builtin_alloca::alloca_size") - malloc_size=code_assign.rhs(); + { + namespacet ns(goto_model.symbol_table); + goto_functionst::goto_functiont function_copy=f_it->second; + constant_propagator_ait const_propagator(function_copy, ns); + forall_goto_program_instructions(copy_i_it, function_copy.body) + { + if(copy_i_it->location_number==i_it->location_number) + { + assert(copy_i_it->is_assign()); + malloc_size=to_code_assign(copy_i_it->code).rhs(); + } + } + } + } + if(replace_malloc_rec(code_assign.rhs(), + suffix, + goto_model.symbol_table, + malloc_size, + i_it->location_number, + loop_end==f_it->second.body.instructions.end(), + alloc_concrete)) + { + result=result || (loop_end!=f_it->second.body.instructions.end()); } - replace_malloc_rec(code_assign.rhs(), suffix, - goto_model.symbol_table, malloc_size, counter); } } } + return result; } +/*******************************************************************\ + +Function: set_var_always_to_true + + Inputs: goto_model + name_cond Function returning true for names of variables + to be set. + + Outputs: + + Purpose: Set undefined boolean variable to true. + Finds declaration of a variable whose name matches the given + condition and adds an instruction var = TRUE after + the declaration. + +\*******************************************************************/ + +void set_var_always_to_true( + goto_modelt &goto_model, + std::functionname_cond) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_decl()) + { + code_declt &code_decl=to_code_decl(i_it->code); + if(code_decl.symbol().id()==ID_symbol) + { + std::string decl_id= + id2string(to_symbol_expr(code_decl.symbol()).get_identifier()); + if(name_cond(decl_id)) + { + auto assign=f_it->second.body.insert_after(i_it); + assign->make_assignment(); + assign->code=code_assignt(code_decl.symbol(), true_exprt()); + } + } + } + } + f_it->second.body.compute_location_numbers(); + f_it->second.body.compute_target_numbers(); + f_it->second.body.compute_incoming_edges(); + } +} + +/*******************************************************************\ + +Function: allow_record_malloc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void allow_record_malloc(goto_modelt &goto_model) +{ + set_var_always_to_true( + goto_model, + [](std::string &name) + { + return name.find("malloc::")!=std::string::npos && + name.find("::record_malloc")!=std::string::npos; + }); +} + +/*******************************************************************\ + +Function: allow_record_memleak + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void allow_record_memleak(goto_modelt &goto_model) +{ + set_var_always_to_true( + goto_model, + [](std::string &name) + { + return name.find("malloc::")!=std::string::npos && + name.find("::record_may_leak")!=std::string::npos; + }); +} diff --git a/src/ssa/malloc_ssa.h b/src/ssa/malloc_ssa.h index 5ef4e5ed4..5f3a9ed15 100644 --- a/src/ssa/malloc_ssa.h +++ b/src/ssa/malloc_ssa.h @@ -15,10 +15,16 @@ Author: Daniel Kroening, kroening@kroening.com exprt malloc_ssa( const side_effect_exprt &, const std::string &suffix, - symbol_tablet &); + symbol_tablet &, + bool is_concrete, + bool alloc_concrete); -void replace_malloc( +bool replace_malloc( goto_modelt &goto_model, - const std::string &suffix); + const std::string &suffix, + bool alloc_concrete); + +void allow_record_malloc(goto_modelt &goto_model); +void allow_record_memleak(goto_modelt &goto_model); #endif diff --git a/src/ssa/may_alias_analysis.cpp b/src/ssa/may_alias_analysis.cpp new file mode 100644 index 000000000..4b09b9ec8 --- /dev/null +++ b/src/ssa/may_alias_analysis.cpp @@ -0,0 +1,182 @@ +/*******************************************************************\ + +Module: May-alias analysis for a single function + +Author: Viktor Malik, imalik@fit.vutbr.cz + +\*******************************************************************/ + +#include "may_alias_analysis.h" + +/*******************************************************************\ + +Function: may_alias_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::transform( + ai_domain_baset::locationt from, + ai_domain_baset::locationt to, + ai_baset &ai, + const namespacet &ns) +{ + if(from->is_assign()) + { + const code_assignt &code_assign=to_code_assign(from->code); + + const exprt lhs_deref=dereference(code_assign.lhs(), ns); + const exprt rhs_deref=dereference(code_assign.rhs(), ns); + + std::set aliases; + get_rhs_aliases(rhs_deref, aliases); + assign_lhs_aliases(lhs_deref, aliases); + } +} + +/*******************************************************************\ + +Function: may_alias_domaint::merge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +bool may_alias_domaint::merge( + const may_alias_domaint &other, + ai_domain_baset::locationt from, + ai_domain_baset::locationt to) +{ + bool changed=false; + + // do union + for(aliasest::const_iterator it=other.aliases.begin(); + it!=other.aliases.end(); it++) + { + irep_idt other_root=other.aliases.find(it); + + if(!aliases.same_set(*it, other_root)) + { + aliases.make_union(*it, other_root); + changed=true; + } + } + + return changed; +} + +/*******************************************************************\ + +Function: may_alias_domaint::assign_lhs_aliases + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::assign_lhs_aliases( + const exprt &lhs, + const std::set &alias_set) +{ + if(lhs.type().id()==ID_pointer) + { + if(lhs.id()==ID_symbol) + { + irep_idt identifier=to_symbol_expr(lhs).get_identifier(); + + aliases.isolate(identifier); + + for(const irep_idt &alias : alias_set) + { + aliases.make_union(identifier, alias); + } + } + } +} + +/*******************************************************************\ + +Function: may_alias_domaint::get_rhs_aliases + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::get_rhs_aliases( + const exprt &rhs, + std::set &alias_set) +{ + if(rhs.id()==ID_symbol && + id2string(to_symbol_expr(rhs).get_identifier()).find("__CPROVER_")== + std::string::npos) + { + irep_idt identifier=to_symbol_expr(rhs).get_identifier(); + alias_set.insert(identifier); + + for(aliasest::const_iterator it=aliases.begin(); + it!=aliases.end(); + it++) + if(aliases.same_set(*it, identifier)) + alias_set.insert(*it); + } + else if(rhs.id()==ID_if) + { + get_rhs_aliases(to_if_expr(rhs).true_case(), alias_set); + get_rhs_aliases(to_if_expr(rhs).false_case(), alias_set); + } + else if(rhs.id()==ID_typecast) + { + get_rhs_aliases(to_typecast_expr(rhs).op(), alias_set); + } +} + +/*******************************************************************\ + +Function: may_alias_domaint::dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +const exprt may_alias_domaint::dereference( + const exprt &expr, + const namespacet &ns) +{ + exprt deref=symbolic_dereference(expr, ns); + members_to_symbols(deref, ns); + return deref; +} + +/*******************************************************************\ + +Function: may_alias_domaint::members_to_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void may_alias_domaint::members_to_symbols(exprt &expr, const namespacet &ns) +{ + ssa_objectt object(expr, ns); + if(object) + expr=object.symbol_expr(); + Forall_operands(it, expr)members_to_symbols(*it, ns); +} diff --git a/src/ssa/may_alias_analysis.h b/src/ssa/may_alias_analysis.h new file mode 100644 index 000000000..011763736 --- /dev/null +++ b/src/ssa/may_alias_analysis.h @@ -0,0 +1,52 @@ +/*******************************************************************\ + +Module: May-alias analysis for a single function + +Author: Viktor Malik, imalik@fit.vutbr.cz + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_MAY_ALIAS_ANALYSIS_H +#define CPROVER_2LS_SSA_MAY_ALIAS_ANALYSIS_H + +#include +#include +#include "ssa_value_set.h" + +class may_alias_domaint:public ai_domain_baset +{ +public: + void transform( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns) override; + + bool merge(const may_alias_domaint &other, locationt from, locationt to); + + typedef union_find aliasest; + aliasest aliases; + +protected: + void assign_lhs_aliases( + const exprt &lhs, + const std::set &rhs_alias_set); + void get_rhs_aliases(const exprt &rhs, std::set &alias_set); + + static const exprt dereference(const exprt &expr, const namespacet &ns); + static void members_to_symbols(exprt &expr, const namespacet &ns); +}; + +class may_alias_analysist:public ait +{ +public: + may_alias_analysist( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns) + { + operator()(goto_function, ns); + } +}; + + +#endif // CPROVER_2LS_SSA_MAY_ALIAS_ANALYSIS_H diff --git a/src/ssa/ssa_build_goto_trace.cpp b/src/ssa/ssa_build_goto_trace.cpp index 22891beff..3767e726c 100644 --- a/src/ssa/ssa_build_goto_trace.cpp +++ b/src/ssa/ssa_build_goto_trace.cpp @@ -14,8 +14,6 @@ Author: Daniel Kroening, Peter Schrammel #include "ssa_build_goto_trace.h" -#define TERM_CEX 1 - /*******************************************************************\ Function: ssa_build_goto_tracet::finalize_lhs @@ -79,8 +77,7 @@ bool ssa_build_goto_tracet::can_convert_ssa_expr(const exprt &expr) if(expr.id()==ID_member) { const member_exprt &member=to_member_expr(expr); - can_convert_ssa_expr(member.struct_op()); - return true; + return can_convert_ssa_expr(member.struct_op()); } else if(expr.id()==ID_index) { @@ -218,8 +215,9 @@ bool ssa_build_goto_tracet::record_step( #if 0 std::cout << "ASSIGN " << from_expr(unwindable_local_SSA.ns, "", code_assign) - << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) - << "==" << from_expr(unwindable_local_SSA.ns, "", rhs_simplified) + << ": " << from_expr(unwindable_local_SSA.ns, "", lhs_simplified) + << " := " + << from_expr(unwindable_local_SSA.ns, "", rhs_simplified) << std::endl; #endif step.type=goto_trace_stept::ASSIGNMENT; @@ -309,9 +307,6 @@ void ssa_build_goto_tracet::operator()( unwindable_local_SSA.current_unwindings.clear(); unsigned last_level=0; unsigned step_nr=1; -#if TERM_CEX - bool stop_next=false; -#endif while(current_pc!=unwindable_local_SSA.goto_function.body.instructions.end()) { @@ -336,7 +331,8 @@ void ssa_build_goto_tracet::operator()( std::cout << "loop-head : " << current_pc->location_number << std::endl; std::cout << "unwindings: " << unwindable_local_SSA.odometer_to_string( - unwindable_local_SSA.current_unwindings, 100) + unwindable_local_SSA.current_unwindings, + 100) << std::endl; #endif } @@ -357,25 +353,20 @@ void ssa_build_goto_tracet::operator()( // get successor if(current_pc->is_goto() && taken) { -#if TERM_CEX - if(termination && stop_next) - { - break; - } -#endif if(current_pc->is_backwards_goto()) { + if(unwindable_local_SSA.current_unwindings.back()==0) + break; + // we de-(!)-crement the unwinding counter unwindable_local_SSA.decrement_unwindings(0); #if 0 std::cout << "loop-end : " << current_pc->location_number << std::endl; std::cout << "unwindings: " << unwindable_local_SSA.odometer_to_string( - unwindable_local_SSA.current_unwindings, 100) + unwindable_local_SSA.current_unwindings, + 100) << std::endl; -#endif -#if TERM_CEX - stop_next=true; #endif } current_pc=current_pc->get_target(); diff --git a/src/ssa/ssa_db.h b/src/ssa/ssa_db.h index c7c92ed91..1fa7d4543 100644 --- a/src/ssa/ssa_db.h +++ b/src/ssa/ssa_db.h @@ -64,9 +64,11 @@ class ssa_dbt inline void create( const function_namet &function_name, const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) + const namespacet &ns, + const ssa_heap_analysist &heap_analysis) { - store[function_name]=new unwindable_local_SSAt(goto_function, ns); + store[function_name]= + new unwindable_local_SSAt(goto_function, ns, heap_analysis); } protected: diff --git a/src/ssa/ssa_dereference.cpp b/src/ssa/ssa_dereference.cpp index 0425e7d54..1e3d57d7f 100644 --- a/src/ssa/ssa_dereference.cpp +++ b/src/ssa/ssa_dereference.cpp @@ -319,26 +319,52 @@ exprt dereference_rec( { if(src.id()==ID_dereference) { - const exprt &pointer=to_dereference_expr(src).pointer(); - exprt pointer_deref= - dereference(pointer, ssa_value_domain, nondet_prefix, ns); - - // We use the identifier produced by - // local_SSAt::replace_side_effects_rec - exprt result=symbol_exprt(nondet_prefix, src.type()); - - // query the value sets - const ssa_value_domaint::valuest values= - ssa_value_domain(pointer, ns); - - for(ssa_value_domaint::valuest::value_sett::const_iterator - it=values.value_set.begin(); - it!=values.value_set.end(); - it++) + const exprt &pointer=dereference_rec( + to_dereference_expr(src).pointer(), + ssa_value_domain, + nondet_prefix, + ns); + + const typet &pointed_type=ns.follow(pointer.type().subtype()); + + const ssa_value_domaint::valuest values=ssa_value_domain(pointer, ns); + + exprt result; + if(values.value_set.empty()) { - exprt guard=ssa_alias_guard(src, it->get_expr(), ns); - exprt value=ssa_alias_value(src, it->get_expr(), ns); - result=if_exprt(guard, value, result); + result=pointed_object(pointer, ns); + } + else + { + auto it=values.value_set.begin(); + + if(values.null || values.unknown || + (values.value_set.size()>1 && it->type().get_bool("#dynamic"))) + { + std::string dyn_type_name=pointed_type.id_string(); + if(pointed_type.id()==ID_struct) + dyn_type_name+="_"+id2string(to_struct_type(pointed_type).get_tag()); + irep_idt identifier="ssa::"+dyn_type_name+"_obj$unknown"; + + result=symbol_exprt(identifier, src.type()); + result.set("#unknown_obj", true); + } + else + { + result=ssa_alias_value(src, (it++)->get_expr(), ns); + result.set("#heap_access", result.type().get_bool("#dynamic")); + } + + for(; it!=values.value_set.end(); ++it) + { + exprt guard=ssa_alias_guard(src, it->get_expr(), ns); + exprt value=ssa_alias_value(src, it->get_expr(), ns); + result=if_exprt(guard, value, result); + result.set( + "#heap_access", + result.get_bool("#heap_access") || + value.type().get_bool("#dynamic")); + } } return result; @@ -348,6 +374,7 @@ exprt dereference_rec( member_exprt tmp=to_member_expr(src); tmp.struct_op()= dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); + tmp.set("#heap_access", tmp.struct_op().get_bool("#heap_access")); #ifdef DEBUG std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; @@ -363,6 +390,7 @@ exprt dereference_rec( address_of_exprt tmp=to_address_of_expr(src); tmp.object()= dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); + tmp.set("#heap_access", tmp.object().get_bool("#heap_access")); if(tmp.object().is_nil()) return nil_exprt(); @@ -373,7 +401,11 @@ exprt dereference_rec( { exprt tmp=src; Forall_operands(it, tmp) + { *it=dereference_rec(*it, ssa_value_domain, nondet_prefix, ns); + if(it->get_bool("#heap_access")) + tmp.set("#heap_access", true); + } return tmp; } } diff --git a/src/ssa/ssa_domain.cpp b/src/ssa/ssa_domain.cpp index a9848e8af..c70dec9a7 100644 --- a/src/ssa/ssa_domain.cpp +++ b/src/ssa/ssa_domain.cpp @@ -80,20 +80,53 @@ void ssa_domaint::transform( { if(from->is_assign() || from->is_decl() || from->is_function_call()) { - const std::set &assigns= - static_cast(ai).assignments.get(from); + const auto &assignments=static_cast(ai).assignments; + const std::set &assigns=assignments.get(from); for(std::set::const_iterator o_it=assigns.begin(); o_it!=assigns.end(); o_it++) { + if(o_it->get_expr().get_bool("#is_rhs_assign") && + is_pointed(o_it->get_root_object())) + { + // the second part excluded cases + // when a result of malloc is at the right-handed side + const auto object_ai_it= + static_cast(ai)[from].def_map.find(o_it->get_identifier()); + if(object_ai_it!=static_cast(ai)[from].def_map.end() && + object_ai_it->second.def.is_assignment()) + { + const exprt pointer= + get_pointer( + o_it->get_root_object(), + pointed_level(o_it->get_root_object())-1); + const auto def_pointer= + static_cast(ai)[from] + .def_map.find( + ssa_objectt(pointer, ns).get_identifier())->second.def; + if(!def_pointer.is_assignment() || + def_pointer.loc->location_number< + object_ai_it->second.def.loc->location_number) + { + continue; + } + } + } irep_idt identifier=o_it->get_identifier(); def_entryt &def_entry=def_map[identifier]; def_entry.def.loc=from; - def_entry.def.kind=deft::ASSIGNMENT; def_entry.source=from; + auto guard_it=assignments.alloc_guards_map.find({from, *o_it}); + if(guard_it!=assignments.alloc_guards_map.end()) + { + def_entry.def.kind=deft::ALLOCATION; + def_entry.def.guard=guard_it->second; + } + else + def_entry.def.kind=deft::ASSIGNMENT; } } else if(from->is_dead()) @@ -209,6 +242,37 @@ bool ssa_domaint::merge( /*******************************************************************\ +Function: ssa_domaint::get_object_allocation_def + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +ssa_domaint::def_mapt::const_iterator ssa_domaint::get_object_allocation_def( + const irep_idt &id, + const ssa_domaint::def_mapt &def_map) +{ + auto def=def_map.find(id); + std::string id_str=id2string(id); + if(def!=def_map.end() && + def->second.def.kind==deft::ASSIGNMENT && + id_str.find("ssa::dynamic_object$")!=std::string::npos) + { + // Check if corresponding dynamic object has been allocated in that branch + std::string dyn_obj_id=id_str.substr(0, id_str.find_first_of(".")); + auto dyn_obj_def=def_map.find(dyn_obj_id); + if(dyn_obj_def!=def_map.end() && + dyn_obj_def->second.def.kind==deft::ALLOCATION) + return dyn_obj_def; + } + return def_map.end(); +} + +/*******************************************************************\ + Function: ssa_ait::initialize Inputs: diff --git a/src/ssa/ssa_domain.h b/src/ssa/ssa_domain.h index 679fdbc59..91212247b 100644 --- a/src/ssa/ssa_domain.h +++ b/src/ssa/ssa_domain.h @@ -20,9 +20,10 @@ class ssa_domaint:public ai_domain_baset struct deft { deft():kind(ASSIGNMENT) { } - typedef enum { INPUT, ASSIGNMENT, PHI } kindt; + typedef enum { INPUT, ASSIGNMENT, PHI, ALLOCATION } kindt; kindt kind; locationt loc; + exprt guard=nil_exprt(); inline bool is_input() const { return kind==INPUT; } inline bool is_assignment() const { return kind==ASSIGNMENT; } @@ -42,6 +43,7 @@ class ssa_domaint:public ai_domain_baset case deft::INPUT: out << "INPUT"; break; case deft::ASSIGNMENT: out << d.loc->location_number; break; case deft::PHI: out << "PHI" << d.loc->location_number; break; + case deft::ALLOCATION: out << "ALLOC" << d.loc->location_number; break; } return out; } @@ -84,6 +86,12 @@ class ssa_domaint:public ai_domain_baset const ssa_domaint &b, locationt from, locationt to); + +private: + static std::map::const_iterator + get_object_allocation_def( + const irep_idt &id, + const def_mapt &def_map); }; class ssa_ait:public ait diff --git a/src/ssa/ssa_heap_domain.cpp b/src/ssa/ssa_heap_domain.cpp new file mode 100644 index 000000000..7a641297d --- /dev/null +++ b/src/ssa/ssa_heap_domain.cpp @@ -0,0 +1,682 @@ +/*******************************************************************\ + +Module: Dynamic objects analysis + +Author: Viktor Malik + +\*******************************************************************/ + +#include +#include "ssa_heap_domain.h" + +/*******************************************************************\ + +Function: ssa_heap_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::transform( + const namespacet &ns, + domain_baset::locationt from, + domain_baset::locationt to) +{ + if(from->is_assign()) + { + const code_assignt &assign=to_code_assign(from->code); + assign_lhs_rec(assign.lhs(), assign.rhs(), ns); + } + else if(from->is_function_call() && from->function==to->function) + { + const code_function_callt &fun_call=to_code_function_call(from->code); + assert(fun_call.function().id()==ID_symbol); + const irep_idt &fun_id=to_symbol_expr(fun_call.function()).get_identifier(); + + if(function_map.find(fun_id)!=function_map.end()) + { + unsigned counter=0; + for(auto &obj : function_map.at(fun_id).new_objects) + { + symbol_exprt new_obj=obj.first; + rename_to_caller(new_obj, from, counter); + + const symbolt *symbol; + if(!ns.lookup(new_obj.get_identifier(), symbol)) + new_obj=symbol->symbol_expr(); + + if(function_map[function].new_objects.find(new_obj)== + function_map[function].new_objects.end()) + { + function_map[function].new_objects.insert( + std::make_pair( + new_obj, + std::set())); + } + + for(auto &expr : obj.second) + { + const exprt pointer=function_map.at(fun_id).corresponding_expr( + expr, fun_call.arguments(), 0); + + objectst old_objects=value_map[pointer]; + value_map[pointer]={new_obj}; + + if(is_function_output(pointer, function, ns, false)) + { + function_map[function].new_objects.at(new_obj).insert(pointer); + } + + for(auto &o : old_objects) + { + if(o.id()==ID_symbol && o.type().get_bool("#dynamic") && + new_obj!=o) + function_map[function].new_objects.at(to_symbol_expr(o)).erase( + pointer); + } + } + } + + for(auto &obj : function_map.at(fun_id).modified_objects) + { + const exprt caller_obj=function_map.at(fun_id).corresponding_expr( + obj, fun_call.arguments(), 0); + if(is_function_output(caller_obj, function, ns, false)) + function_map[function].modified_objects.insert(caller_obj); + } + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::merge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_heap_domaint::merge( + const ssa_heap_domaint &other, + domain_baset::locationt to) +{ + bool result=false; + + if(to->function=="" || to->function==other.function) + { + function=other.function; + + // Merge value maps - union + for(auto &other_value : other.value_map) + { + if(value_map.find(other_value.first)==value_map.end()) + { + value_map[other_value.first]=other_value.second; + result=true; + } + else + { + unsigned long old_size=value_map[other_value.first].size(); + value_map[other_value.first].insert( + other_value.second.begin(), + other_value.second.end()); + result=old_size!=value_map[other_value.first].size(); + } + } + } + else + { + function=to->function; + } + + for(auto &f : other.function_map) + { + auto &objects=function_map[f.first].new_objects; + const auto &other_objects=f.second.new_objects; + + if(f.first==function && function==other.function) + { + for(auto &other_object : other_objects) + { + if(objects.find(other_object.first)==objects.end()) + { + objects[other_object.first]=other_object.second; + result=true; + } + else if(!other_object.second.empty()) + { + unsigned long old_size=objects[other_object.first].size(); + std::set intersection; + std::set_intersection( + objects[other_object.first].begin(), + objects[other_object.first].end(), + other_object.second.begin(), + other_object.second.end(), + std::inserter( + intersection, + intersection.begin())); + if(!intersection.empty()) + objects[other_object.first]=intersection; + else + objects.erase(other_object.first); + + if(old_size!=objects[other_object.first].size()) + result=true; + } + } + } + else + { + for(auto &o : other_objects) + { + std::size_t old_size=objects[o.first].size(); + objects[o.first]=o.second; + if(old_size!=objects[o.first].size()) + result=true; + } + } + + function_map[f.first].params=f.second.params; + + std::size_t old_size=function_map[f.first].modified_objects.size(); + function_map[f.first].modified_objects.insert( + f.second.modified_objects.begin(), + f.second.modified_objects.end()); + if(old_size!=function_map[f.first].modified_objects.size()) + result=true; + } + + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::assign_rhs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::assign_rhs( + const exprt &rhs, + const irep_idt &function, + objectst &objects, + const namespacet &ns) +{ + if(rhs.get_bool("#malloc_result")) + { + exprt malloc_result=rhs; + if(malloc_result.id()==ID_typecast) + malloc_result=to_typecast_expr(malloc_result).op(); + assert(malloc_result.id()==ID_address_of); + + const symbol_exprt new_object=to_symbol_expr( + to_address_of_expr(malloc_result).object()); + + function_infot &function_info=function_map[function]; + if(function_info.new_objects.find(new_object)== + function_info.new_objects.end()) + { + function_info.new_objects.insert( + std::make_pair( + new_object, + std::set())); + } + + objects={new_object}; + } + else if(rhs.id()==ID_typecast) + { + assign_rhs(to_typecast_expr(rhs).op(), function, objects, ns); + } + else + { + auto values=value_map.find(rhs); + if(values!=value_map.end()) + { + objects=values->second; + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::is_function_output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_heap_domaint::is_function_output( + const exprt &expr, + const irep_idt &function, + const namespacet &ns, + bool in_deref) +{ + if(expr.id()==ID_dereference) + { + return is_function_output( + to_dereference_expr(expr).pointer(), + function, + ns, + true); + } + else if(expr.id()==ID_member) + { + return is_function_output( + to_member_expr(expr).compound(), + function, + ns, + in_deref); + } + else if(expr.id()==ID_symbol) + { + const symbol_exprt &symbol_expr=to_symbol_expr(expr); + if(id2string(symbol_expr.get_identifier()).find("#return_value")!= + std::string::npos) + { + return symbol_expr.get_identifier()==id2string(function)+"#return_value"; + } + + const symbolt *symbol; + if(!ns.lookup(symbol_expr.get_identifier(), symbol) && + ((in_deref && symbol->is_parameter) || !symbol->is_procedure_local())) + return true; + } + return false; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::set ssa_heap_domaint::value(const exprt &expr) const +{ + std::set result; + if(value_map.find(expr)!=value_map.end()) + { + for(auto &value : value_map.at(expr)) + { + if(value.id()==ID_symbol) + result.insert(to_symbol_expr(value)); + } + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::new_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list ssa_heap_domaint::new_objects() const +{ + return new_objects(function); +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::new_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list ssa_heap_domaint::new_objects( + const irep_idt &fname) const +{ + std::list result; + if(function_map.find(fname)!=function_map.end()) + { + for(auto &obj : function_map.at(fname).new_objects) + result.push_back(obj.first); + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::rename_to_caller + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void ssa_heap_domaint::rename_to_caller( + symbol_exprt &object, + domain_baset::locationt loc, + unsigned &index) const +{ + object.set_identifier( + "ssa::dynamic_object$"+std::to_string(loc->location_number)+"$"+ + std::to_string(index++)); +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::new_caller_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::list ssa_heap_domaint::new_caller_objects( + const irep_idt &fname, + domain_baset::locationt loc) const +{ + std::list result=new_objects(fname); + unsigned counter=0; + for(symbol_exprt &o : result) + { + rename_to_caller(o, loc, counter); + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::modified_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const std::set ssa_heap_domaint::modified_objects( + const irep_idt &fname) const +{ + std::set result; + if(function_map.find(fname)!=function_map.end()) + { + result=function_map.at(fname).modified_objects; + } + return result; +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::assign_lhs_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::assign_lhs_rec( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns) +{ + objectst rhs_objects; + assign_rhs(rhs, function, rhs_objects, ns); + + if(!rhs_objects.empty()) + value_map[lhs]=rhs_objects; + else + value_map.erase(lhs); + + if(is_function_output(lhs, function, ns, false)) + { + auto &objects=function_map[function].new_objects; + for(const exprt &o : rhs_objects) + { + if(o.id()==ID_symbol && o.type().get_bool("#dynamic")) + { + const symbol_exprt new_o=to_symbol_expr(o); + if(objects.find(new_o)!=objects.end()) + { + objects[new_o].insert(lhs); + } + } + } + } + + update_modified(lhs, ns); +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::update_modified + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_domaint::update_modified(const exprt &expr, const namespacet &ns) +{ + if(expr.id()==ID_member) + { + update_modified(to_member_expr(expr).compound(), ns); + } + else if(expr.id()==ID_dereference) + { + for(const exprt &v : value_map[to_dereference_expr(expr).pointer()]) + { + if(is_function_output(v, function, ns, false)) + function_map[function].modified_objects.insert(v); + } + } + else if(is_function_output(expr, function, ns, false)) + function_map[function].modified_objects.insert(expr); +} + +/*******************************************************************\ + +Function: ssa_heap_analysist::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_analysist::initialize(const goto_functionst &goto_functions) +{ + static_analysis_baset::initialize(goto_functions); + + if(goto_functions.function_map.at("main").body_available()) + { + locationt e=goto_functions.function_map.at( + "main").body.instructions.begin(); + ssa_heap_domaint &entry=operator[](e); + + entry.function=e->function; + + forall_goto_functions(f_it, goto_functions) + { + if(f_it->second.body_available()) + { + locationt f_e=f_it->second.body.instructions.begin(); + ssa_heap_domaint &f_entry=operator[](f_e); + for(auto ¶m : f_it->second.type.parameters()) + { + entry.function_map[f_it->first].params.push_back( + param.get_identifier()); + const symbol_exprt param_expr(param.get_identifier(), param.type()); + init_ptr_param(param_expr, f_entry); + } + } + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_analysist::init_ptr_param + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_heap_analysist::init_ptr_param( + const exprt &expr, + ssa_heap_domaint &f_entry) +{ + const typet &type=ns.follow(expr.type()); + if(type.id()==ID_pointer) + { + const dereference_exprt dereference(expr, type.subtype()); + f_entry.value_map[expr].insert(dereference); + init_ptr_param(dereference, f_entry); + } + else if(type.id()==ID_struct) + { + assert(expr.id()==ID_dereference); + for(auto &component : to_struct_type(type).components()) + { + if(component.type().id()==ID_pointer && + ns.follow(component.type().subtype())==type) + { + const member_exprt member(expr, component.get_name(), component.type()); + f_entry.value_map[member].insert(expr); + } + } + } +} + +/*******************************************************************\ + +Function: ssa_heap_domaint::function_infot::corresponding_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const exprt ssa_heap_domaint::function_infot::corresponding_expr( + const exprt &expr, + const code_function_callt::argumentst &arguments, + unsigned deref_level) const +{ + if(expr.id()==ID_symbol) + { + const irep_idt expr_id=to_symbol_expr(expr).get_identifier(); + exprt result=expr; + for(std::size_t i=0; i + +class ssa_heap_domaint:public domain_baset +{ +public: + virtual void transform( + const namespacet &ns, + locationt from, + locationt to) override; + bool merge(const ssa_heap_domaint &, locationt); + + irep_idt function; + + typedef std::set objectst; + std::map value_map; + + const std::set value(const exprt &expr) const; + + class function_infot + { + public: + std::map > new_objects; + std::set modified_objects; + std::vector params; + + const exprt corresponding_expr( + const exprt &expr, + const code_function_callt::argumentst &arguments, + unsigned deref_level) const; + + protected: + const exprt apply_deref(const exprt &expr, unsigned level) const; + }; + + std::map function_map; + + const std::list new_objects() const; + const std::list new_objects(const irep_idt &fname) const; + const std::list + new_caller_objects(const irep_idt &fname, locationt loc) const; + + const std::set modified_objects(const irep_idt &fname) const; +protected: + void assign_lhs_rec(const exprt &lhs, const exprt &rhs, const namespacet &ns); + + void assign_rhs( + const exprt &rhs, + const irep_idt &function, + objectst &objects, + const namespacet &ns); + + bool is_function_output( + const exprt &expr, + const irep_idt &function, + const namespacet &ns, + bool in_deref); + + void rename_to_caller( + symbol_exprt &object, + locationt loc, + unsigned &index) const; + + void update_modified(const exprt &expr, const namespacet &ns); +}; + +class ssa_heap_analysist:public static_analysist +{ +public: + explicit ssa_heap_analysist(const namespacet &_ns): + static_analysist(_ns) {} + + virtual void initialize(const goto_functionst &goto_functions) override; + +protected: + void init_ptr_param(const exprt &expr, ssa_heap_domaint &f_entry); +}; + + +#endif // CPROVER_2LS_SSA_SSA_HEAP_DOMAIN_H diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 2f68901a0..bce39e7e8 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -6,6 +6,8 @@ Author: Peter Schrammel \*******************************************************************/ +#include + #include #include @@ -34,6 +36,8 @@ void ssa_inlinert::get_summary( { counter++; + covered_cs_heap_out.clear(); + // getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; goto_programt::const_targett loc=n_it->location; @@ -62,8 +66,18 @@ void ssa_inlinert::get_summary( std::cout << std::endl; #endif + bindings.push_back(get_replace_new_objects(SSA, *f_it, loc, summary)); + // equalities for arguments - bindings.push_back(get_replace_params(summary.params, *f_it)); + bindings.push_back( + get_replace_params( + summary.params, + *f_it, + cs_globals_in, + cs_globals_out, + SSA, + summary, + loc)); // equalities for globals_in if(forward) @@ -91,15 +105,21 @@ void ssa_inlinert::get_summary( if(forward) bindings.push_back( get_replace_globals_out( - summary.globals_out, cs_globals_in, - cs_globals_out)); + cs_globals_out, + summary, + *f_it, + SSA, + loc)); else bindings.push_back( get_replace_globals_out( - summary.globals_in, cs_globals_out, - cs_globals_in)); + cs_globals_in, + summary, + *f_it, + SSA, + loc)); } /*******************************************************************\ @@ -123,6 +143,27 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) /*******************************************************************\ +Function: ssa_inlinert::get_summaries_to_loc + + Inputs: + + Outputs: + + Purpose: get summary for all function calls up to a given location + +\*******************************************************************/ + +exprt ssa_inlinert::get_summaries_to_loc( + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + exprt::operandst summaries, bindings; + get_summaries(SSA, true, summaries, bindings, loc); + return and_exprt(conjunction(bindings), conjunction(summaries)); +} + +/*******************************************************************\ + Function: ssa_inlinert::get_summaries Inputs: @@ -137,11 +178,15 @@ void ssa_inlinert::get_summaries( const local_SSAt &SSA, bool forward, exprt::operandst &summaries, - exprt::operandst &bindings) + exprt::operandst &bindings, + local_SSAt::locationt loc) { for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) { + if(loc!=local_SSAt::locationt() && + n_it->location->location_number>=loc->location_number) + return; for(local_SSAt::nodet::function_callst::const_iterator f_it= n_it->function_calls.begin(); f_it!=n_it->function_calls.end(); f_it++) @@ -151,8 +196,8 @@ void ssa_inlinert::get_summaries( if(summary_db.exists(fname)) { - get_summary(SSA, n_it, f_it, summary_db.get(fname), - forward, summaries, bindings); + get_summary( + SSA, n_it, f_it, summary_db.get(fname), forward, summaries, bindings); } } } @@ -325,15 +370,17 @@ void ssa_inlinert::replace( { rename(precondition); node->constraints.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } else { rename(precondition); node->assertions.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } exprt transformer; if(forward) @@ -420,20 +467,24 @@ exprt ssa_inlinert::get_replace_globals_in( for(summaryt::var_sett::const_iterator it=globals_in.begin(); it!=globals_in.end(); it++) { - symbol_exprt lhs=*it; // copy - rename(lhs); - symbol_exprt rhs; - if(find_corresponding_symbol(*it, globals, rhs)) + // bind only real globals - filter out heap objects + if(!is_pointed(*it)) { - debug() << "binding: " << lhs.get_identifier() << "==" - << rhs.get_identifier() << eom; - c.push_back(equal_exprt(lhs, rhs)); - } + symbol_exprt lhs=*it; // copy + rename(lhs); + symbol_exprt rhs; + if(find_corresponding_symbol(*it, globals, rhs)) + { + debug() << "binding: " << lhs.get_identifier() << " == " + << rhs.get_identifier() << eom; + c.push_back(equal_exprt(lhs, rhs)); + } #if 0 else warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif + } } return conjunction(c); } @@ -489,7 +540,12 @@ Function: ssa_inlinert::get_replace_params exprt ssa_inlinert::get_replace_params( const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr) + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &SSA, + const summaryt &summary, + const local_SSAt::locationt &loc) { // equalities for arguments exprt::operandst c; @@ -505,9 +561,196 @@ exprt ssa_inlinert::get_replace_params( break; } - exprt lhs=*p_it; // copy + exprt param=*p_it; // copy + exprt arg=*it; // copy + + exprt lhs=param; rename(lhs); - c.push_back(equal_exprt(lhs, *it)); + // bind parameter <-> argument + c.push_back(equal_exprt(lhs, arg)); + + typet arg_type=SSA.ns.follow(arg.type()); + + // Bind objects pointed by parameter/argument pair + std::list args_in={arg}; + std::list args_out={arg}; + std::list params_in={param}; + std::list params_out={param}; + + while(arg_type.id()==ID_pointer) + { + std::list args_deref_in= + apply_dereference(args_in, SSA.ssa_value_ai[loc], SSA.ns); + std::list params_deref_in= + apply_dereference(params_in, summary.value_domain_in, SSA.ns); + + local_SSAt::locationt next_loc=loc; + ++next_loc; + std::list args_deref_out= + apply_dereference(args_out, SSA.ssa_value_ai[next_loc], SSA.ns); + std::list params_deref_out= + apply_dereference(params_out, summary.value_domain_out, SSA.ns); + + const typet arg_symbol_type=arg_type.subtype(); + arg_type=SSA.ns.follow(arg_symbol_type); + + if(contains_iterator(params_deref_out)) + { // If the caller contains iterators, bindings are different since + // objects from caller will appear in the callee summary + assert(!args_deref_in.empty() && !args_deref_out.empty()); + + arg_type=SSA.ns.follow(args_deref_in.begin()->type()); + assert(arg_type.id()==ID_struct); + + for(const exprt &a : args_deref_in) + { + std::list aliases= + apply_dereference({a}, SSA.ssa_value_ai[next_loc], SSA.ns); + aliases.push_front(a); + + for(auto &alias : aliases) + { + const exprt lhs_expr= + param_out_transformer(alias, arg_type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer( + alias, + arg_symbol_type, + params_deref_out.begin()->type(), + SSA, + loc); + // Bind argument address + c.push_back(equal_exprt(lhs_expr, rhs_expr)); + + for(auto &component : to_struct_type(arg_type).components()) + { + const exprt lhs_comp_expr= + param_in_member_transformer(alias, component); + const exprt rhs_comp_expr= + arg_in_member_transformer(alias, component, SSA, loc); + // Bind argument members at the input + c.push_back(equal_exprt(lhs_comp_expr, rhs_comp_expr)); + } + } + } + + for(const exprt &a : args_deref_out) + { + std::list aliases= + apply_dereference({a}, SSA.ssa_value_ai[next_loc], SSA.ns); + aliases.push_front(a); + for(auto &alias : aliases) + { + const typet &alias_type=SSA.ns.follow(alias.type()); + assert(alias_type.id()==ID_struct); + for(auto &component : to_struct_type(alias_type).components()) + { + // Bind argument members at the output (args_deref_out might + // contain different objects than args_deref_in since function + // call may create new objects). + symbol_exprt arg_member( + id2string(to_symbol_expr(alias).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + + symbol_exprt member_lhs_out; + if(find_corresponding_symbol( + arg_member, summary.globals_out, member_lhs_out)) + { + rename(member_lhs_out); + const exprt arg_out= + arg_out_member_transformer(alias, component, SSA, loc); + c.push_back(equal_exprt(member_lhs_out, arg_out)); + } + } + } + } + } + else + { + // Bind objects pointed by argument and parameter when iterator is + // not present + if(params_deref_in.size()==1) + { + const exprt &p_in=params_deref_in.front(); + + exprt::operandst d; + for(const exprt &a_in : args_deref_in) + { + exprt::operandst binding; + const exprt lhs_expr=param_in_transformer(p_in); + const exprt rhs_expr=arg_in_transformer(a_in, SSA, loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(arg_type.id()==ID_struct) + { + for(auto &component : to_struct_type(arg_type).components()) + { + const exprt lhs_comp_expr= + param_in_member_transformer(p_in, component); + const exprt rhs_comp_expr= + arg_in_member_transformer(a_in, component, SSA, loc); + binding.push_back(equal_exprt(lhs_comp_expr, rhs_comp_expr)); + } + } + d.push_back(conjunction(binding)); + } + if(!d.empty()) + c.push_back(disjunction(d)); + + d.clear(); + for(const exprt &p_out : params_deref_out) + { + for(const exprt &a_out : args_deref_out) + { + if(!cs_heap_covered(a_out)) + { + exprt::operandst binding; + + const exprt lhs_expr= + param_out_transformer(p_out, arg_type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer( + a_out, + arg_symbol_type, + p_out.type(), + SSA, + loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(arg_type.id()==ID_struct) + { + for(auto &component : to_struct_type(arg_type).components()) + { + const exprt lhs_comp_expr= + param_out_member_transformer( + p_out, + component, + summary.globals_out); + const exprt rhs_comp_expr= + arg_out_member_transformer(a_out, component, SSA, loc); + binding.push_back( + equal_exprt( + lhs_comp_expr, + rhs_comp_expr)); + } + } + d.push_back(conjunction(binding)); + } + } + } + if(!d.empty()) + c.push_back(disjunction(d)); + } + } + + args_in=args_deref_in; + args_out=args_deref_out; + params_in=params_deref_in; + params_out=params_deref_out; + + if(args_in.empty() && args_out.empty()) + break; + } } return conjunction(c); } @@ -560,24 +803,128 @@ Function: ssa_inlinert::get_replace_globals_out \*******************************************************************/ exprt ssa_inlinert::get_replace_globals_out( - const local_SSAt::var_sett &globals_out, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out) + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + const function_application_exprt &funapp_expr, + const local_SSAt &SSA, + local_SSAt::locationt loc) { // equalities for globals_out exprt::operandst c; for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); it!=cs_globals_out.end(); it++) { - symbol_exprt rhs=*it; // copy symbol_exprt lhs; - if(find_corresponding_symbol(*it, globals_out, lhs)) - rename(lhs); + const exprt rhs=*it; + + + if(is_pointed(*it) || + id2string(it->get_identifier()).find("dynamic_object$")!= + std::string::npos) + { + if(!cs_heap_covered(*it) && + !find_corresponding_symbol(*it, summary.globals_out, lhs)) + { + assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); + c.push_back(equal_exprt(lhs, rhs)); + } + } else - assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); - c.push_back(equal_exprt(lhs, rhs)); + { + if(find_corresponding_symbol(*it, summary.globals_out, lhs)) + { + // Bind function return value + rename(lhs); + c.push_back(equal_exprt(lhs, rhs)); + + typet type=SSA.ns.follow(rhs.type()); + + std::list callee_global={*it}; + std::list caller_global={*it}; + + // Bind all objects pointed by return value + while(type.id()==ID_pointer) + { + local_SSAt::locationt next_loc=loc; + ++next_loc; + std::list caller_deref= + apply_dereference( + caller_global, + SSA.ssa_value_ai[next_loc], + SSA.ns); + std::list callee_deref= + apply_dereference(callee_global, summary.value_domain_out, SSA.ns); + + if(!callee_deref.empty()) + { + const typet symbol_type=type.subtype(); + type=SSA.ns.follow(symbol_type); + + exprt::operandst d; + for(const exprt &callee : callee_deref) + { + for(const exprt &caller : caller_deref) + { + if(!cs_heap_covered(caller)) + { + exprt::operandst binding; + const exprt lhs_expr= + param_out_transformer(callee, type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer( + caller, + symbol_type, + callee.type(), + SSA, + loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(type.id()==ID_struct) + { + for(auto &component : to_struct_type(type).components()) + { + const exprt lhs_comp_expr= + param_out_member_transformer( + callee, + component, + summary.globals_out); + const exprt rhs_comp_expr= + arg_out_member_transformer(caller, component, SSA, loc); + binding.push_back( + equal_exprt( + lhs_comp_expr, + rhs_comp_expr)); + } + } + + d.push_back(conjunction(binding)); + } + } + } + + if(!d.empty()) + c.push_back(disjunction(d)); + } + + callee_global=callee_deref; + caller_global=caller_deref; + + if(caller_global.empty()) + break; + } + } + else + { + if(find_corresponding_symbol(*it, summary.globals_out, lhs)) + rename(lhs); + else + assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); + c.push_back(equal_exprt(lhs, rhs)); + } + } } - return conjunction (c); + return conjunction(c); } /*******************************************************************\ @@ -651,6 +998,23 @@ void ssa_inlinert::rename(exprt &expr) irep_idt id=id2string(sexpr.get_identifier())+"@"+i2string(counter); sexpr.set_identifier(id); } + else if(expr.id()==ID_address_of) + { + irep_idt id; + const exprt &obj=to_address_of_expr(expr).object(); + if(obj.id()==ID_symbol) + { + const std::string &obj_id=id2string(to_symbol_expr(obj).get_identifier()); + if(is_pointed(obj)) + id=get_pointer_id(obj); + else + id=id2string(obj_id)+"'addr"; + + id=id2string(id)+"@"+i2string(counter); + } + symbol_exprt addr_symbol(id, expr.type()); + expr=addr_symbol; + } Forall_operands(op, expr) rename(*op); } @@ -772,6 +1136,11 @@ void ssa_inlinert::rename_to_callee( replace_map[*it]=*p_it; } + replace_expr(replace_map, expr); + + // arguments might contain globals, thus, we have to replace them separately + replace_map.clear(); + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); it!=cs_globals_in.end(); it++) { @@ -784,9 +1153,11 @@ void ssa_inlinert::rename_to_callee( warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif - replace_map[*it]= - symbol_exprt( - id2string(it->get_identifier())+"@"+i2string(++counter), it->type()); + // rename objects not present in globals in to non-suffix version + symbol_exprt to_replace(get_original_identifier(*it), it->type()); + replace_map[*it]=to_replace; + // to propagate #dynamic flag on type + replace_map[to_replace]=to_replace; } } @@ -904,13 +1275,14 @@ irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) char c=id.at(i); if(pos==std::string::npos) { - if(c=='#' || c=='@' || c=='%' || c=='!' || c=='$') + if(c=='#' || c=='@' || c=='%' || c=='!') pos=i; } else { - if(!(c=='#' || c=='@' || c=='%' || c=='!' || c=='$') && + if(!(c=='#' || c=='@' || c=='%' || c=='!') && !(c=='p' || c=='h' || c=='i') && + !(c=='l' || c=='b') && !('0'<=c && c<='9')) pos=std::string::npos; } @@ -920,3 +1292,377 @@ irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) return id; } +/*******************************************************************\ + +Function: ssa_inlinert::apply_dereference + + Inputs: Set of pointers and value analysis + + Outputs: Set of all objects that can be pointed by one of pointers + from the input set. + + Purpose: + +\*******************************************************************/ + +std::list ssa_inlinert::apply_dereference( + const std::list &exprs, + const ssa_value_domaint &value_domain, + const namespacet &ns) +{ + std::list result; + for(const exprt &expr : exprs) + { + if(expr.id()==ID_symbol || expr.id()==ID_address_of) + { + exprt to_query=expr; // copy + if(expr.id()==ID_symbol) + { + to_symbol_expr(to_query).set_identifier( + get_original_identifier(to_symbol_expr(expr))); + } + ssa_value_domaint::valuest values=value_domain(to_query, ns); + for(const ssa_objectt &v : values.value_set) + { + assert(v.get_expr().id()==ID_symbol); + result.push_back(v.get_expr()); + } + } + else if(expr.id()==ID_typecast) + { + std::list tmp=apply_dereference( + {to_typecast_expr(expr).op()}, value_domain, ns); + for(auto &e : tmp) + result.push_back(e); + } + } + return result; +} + +/*******************************************************************\ + +Function: ssa_inlinert::contains_iterator + + Inputs: List of expressions + + Outputs: True if the list contains an advancer + + Purpose: + +\*******************************************************************/ + +bool ssa_inlinert::contains_iterator(const std::list ¶ms) +{ + auto it=std::find_if( + params.begin(), + params.end(), + [](const exprt &p) { return is_iterator(p); }); + return (it!=params.end()); +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_in_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_in_transformer(const exprt ¶m) +{ + assert(param.id()==ID_symbol); + symbol_exprt param_in=to_symbol_expr(param); + rename(param_in); + return param_in; +} + +/*******************************************************************\ + +Function: ssa_inlinert::arg_in_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_in_transformer( + const exprt &arg, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + return SSA.read_rhs(arg, loc); +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_in_member_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_in_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component) +{ + assert(param.id()==ID_symbol); + symbol_exprt param_member( + id2string(to_symbol_expr(param).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + rename(param_member); + return param_member; +} + +/*******************************************************************\ + +Function: ssa_inlinert::arg_in_member_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_in_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + symbol_exprt arg_member( + id2string(to_symbol_expr(arg).get_identifier())+"."+ + id2string(component.get_name()), + component.type()); + return SSA.read_rhs(arg_member, loc); +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_out_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_out_transformer( + const exprt ¶m, + const typet &type, + const local_SSAt::var_sett &globals_out) +{ + assert(param.id()==ID_symbol); + + if(type.id()==ID_struct) + { + address_of_exprt param_addr=address_of_exprt(param); + rename(param_addr); + return param_addr; + } + else + { + symbol_exprt param_out=to_symbol_expr(param); + if(find_corresponding_symbol(to_symbol_expr(param), globals_out, param_out)) + rename(param_out); + return param_out; + } +} + +/*******************************************************************\ + +Function: ssa_inlinert::arg_out_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_out_transformer( + const exprt &arg, + const typet &arg_symbol_type, + const typet ¶m_type, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + const typet &arg_type=SSA.ns.follow(arg_symbol_type); + if(arg_type.id()==ID_struct) + { + assert(arg.id()==ID_symbol); + symbol_exprt arg_symbol=to_symbol_expr(arg); + address_of_exprt arg_addr=address_of_exprt(arg_symbol); + + const symbolt *symbol; + if(!SSA.ns.lookup(arg_symbol.get_identifier(), symbol)) + { + arg_addr=address_of_exprt(symbol->symbol_expr()); + } + + covered_cs_heap_out.insert(arg_symbol); + return arg_addr; + } + else + { + const symbol_exprt &arg_out= + SSA.name(ssa_objectt(arg, SSA.ns), local_SSAt::OUT, loc); + covered_cs_heap_out.insert(arg_out); + return arg_out; + } +} + +/*******************************************************************\ + +Function: ssa_inlinert::param_out_member_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::param_out_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component, + const local_SSAt::var_sett &globals_out) +{ + assert(param.id()==ID_symbol); + + symbol_exprt param_member( + id2string(to_symbol_expr(param).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + symbol_exprt param_out; + assert(find_corresponding_symbol(param_member, globals_out, param_out)); + rename(param_out); + return param_out; +} + +/*******************************************************************\ + +Function: ssa_inlinert::artg_out_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::arg_out_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc) +{ + symbol_exprt arg_member( + id2string(to_symbol_expr(arg).get_identifier())+"."+ + id2string(component.get_name()), component.type()); + const symbol_exprt &arg_member_out= + SSA.name(ssa_objectt(arg_member, SSA.ns), local_SSAt::OUT, loc); + covered_cs_heap_out.insert(arg_member_out); + return arg_member_out; +} + +/*******************************************************************\ + +Function: ssa_inlinert::cs_heap_covered + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_inlinert::cs_heap_covered(const exprt &expr) +{ + return expr.id()==ID_symbol && + covered_cs_heap_out.find(to_symbol_expr(expr))!= + covered_cs_heap_out.end(); +} + +/*******************************************************************\ + +Function: ssa_inlinert::get_replace_new_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_inlinert::get_replace_new_objects( + const local_SSAt &SSA, + const function_application_exprt funapp_expr, + local_SSAt::locationt loc, + const summaryt &summary) +{ + exprt::operandst binding; + + const irep_idt &fname=to_symbol_expr(funapp_expr.function()).get_identifier(); + + auto next_loc=loc; + ++next_loc; + if(SSA.heap_analysis.has_location(next_loc)) + { + const ssa_heap_domaint &heap_domain=SSA.heap_analysis[next_loc]; + + const std::list callee_objects= + heap_domain.new_objects(fname); + const std::list caller_objects= + heap_domain.new_caller_objects(fname, loc); + + auto callee_it=callee_objects.begin(); + for(auto caller_it=caller_objects.begin(); caller_it!=caller_objects.end(); + ++caller_it, ++callee_it) + { + const typet symbol_type=caller_it->type(); + const typet type=SSA.ns.follow(symbol_type); + + const exprt lhs_expr= + param_out_transformer(*callee_it, type, summary.globals_out); + const exprt rhs_expr= + arg_out_transformer(*caller_it, symbol_type, type, SSA, loc); + binding.push_back(equal_exprt(lhs_expr, rhs_expr)); + + if(type.id()==ID_struct) + { + for(auto &component : to_struct_type(type).components()) + { + const exprt lhs_comp_expr= + param_out_member_transformer( + *callee_it, + component, + summary.globals_out); + const exprt rhs_comp_expr= + arg_out_member_transformer(*caller_it, component, SSA, loc); + binding.push_back(equal_exprt(lhs_comp_expr, rhs_comp_expr)); + } + } + } + } + + return conjunction(binding); +} diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index 7cf8d0730..57634bf7a 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -38,17 +38,17 @@ class ssa_inlinert:public messaget const local_SSAt &SSA, bool forward, exprt::operandst &summaries, - exprt::operandst &bindings); + exprt::operandst &bindings, + local_SSAt::locationt loc=local_SSAt::locationt()); exprt get_summaries(const local_SSAt &SSA); + exprt get_summaries_to_loc(const local_SSAt &SSA, local_SSAt::locationt loc); void replace( local_SSAt &SSA, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - // incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - // outgoing globals at call site + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site const summaryt &summary, bool forward, bool preconditions_as_assertions); @@ -62,10 +62,8 @@ class ssa_inlinert:public messaget local_SSAt::nodest &nodes, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - // incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - // outgoing globals at call site + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site const local_SSAt &function); void replace( @@ -105,6 +103,11 @@ class ssa_inlinert:public messaget static irep_idt get_original_identifier(const symbol_exprt &s); + static std::list apply_dereference( + const std::list &exprs, + const ssa_value_domaint &value_domain, + const namespacet &ns); + protected: unsigned counter; summary_dbt &summary_db; @@ -113,6 +116,8 @@ class ssa_inlinert:public messaget local_SSAt::nodet::equalitiest new_equs; std::set rm_function_calls; + std::set covered_cs_heap_out; + void replace_globals_in( const local_SSAt::var_sett &globals_in, const local_SSAt::var_sett &globals); @@ -129,14 +134,69 @@ class ssa_inlinert:public messaget const local_SSAt::var_sett &globals); exprt get_replace_params( const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr); + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &SSA, + const summaryt &summary, + const local_SSAt::locationt &loc); exprt get_replace_globals_out( - const local_SSAt::var_sett &globals_out, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out); + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + const function_application_exprt &funapp_expr, + const local_SSAt &SSA, + local_SSAt::locationt loc); + exprt get_replace_new_objects( + const local_SSAt &SSA, + const function_application_exprt funapp_expr, + local_SSAt::locationt loc, + const summaryt &summary); void rename(exprt &expr); void rename(local_SSAt::nodet &node); + + bool cs_heap_covered(const exprt &expr); + + // Transformation functions for lists of input/output arguments/pointers + // (or their members) for binding purposes + + exprt param_in_transformer(const exprt ¶m); + exprt arg_in_transformer( + const exprt &arg, + const local_SSAt &SSA, + local_SSAt::locationt loc); + exprt param_in_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component); + exprt arg_in_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc); + + exprt param_out_transformer( + const exprt ¶m, + const typet &type, + const local_SSAt::var_sett &globals_out); + + exprt arg_out_transformer( + const exprt &arg, + const typet &arg_symbol_type, + const typet ¶m_type, + const local_SSAt &SSA, + local_SSAt::locationt loc); + exprt param_out_member_transformer( + const exprt ¶m, + const struct_union_typet::componentt &component, + const local_SSAt::var_sett &globals_out); + exprt arg_out_member_transformer( + const exprt &arg, + const struct_union_typet::componentt &component, + const local_SSAt &SSA, + local_SSAt::locationt loc); + + static bool contains_iterator(const std::list ¶ms); }; #endif diff --git a/src/ssa/ssa_object.cpp b/src/ssa/ssa_object.cpp index d3aa56993..eae7869e3 100644 --- a/src/ssa/ssa_object.cpp +++ b/src/ssa/ssa_object.cpp @@ -37,6 +37,18 @@ bool is_ptr_object(const exprt &src) src.get(ID_ptr_object)!=irep_idt(); } +/*******************************************************************\ + +Function: collect_objects_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void collect_objects_rec( const exprt &src, const namespacet &ns, @@ -45,6 +57,55 @@ void collect_objects_rec( /*******************************************************************\ +Function: collect_ptr_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void collect_ptr_objects( + const exprt &expr, + const namespacet &ns, + std::set &objects, + std::set &literals, + bool dynamic) +{ + if(expr.id()==ID_symbol) + { + const symbol_exprt &src=to_symbol_expr(expr); + const typet &type=ns.follow(src.type()); + if(type.id()==ID_pointer) + { + symbol_exprt ptr_object=pointed_object(src, ns); + + const symbolt *symbol; + if(dynamic || + (!ns.lookup(src.get_identifier(), symbol) && + !symbol->is_procedure_local())) + ptr_object.type().set("#dynamic", true); + + if(is_ptr_object(src)) + ptr_object.set(ID_ptr_object, src.get(ID_ptr_object)); + else + ptr_object.set(ID_ptr_object, src.get_identifier()); + + collect_objects_rec(ptr_object, ns, objects, literals); + collect_ptr_objects(ptr_object, ns, objects, literals, dynamic); + } + } + else + { + forall_operands(it, expr) + collect_ptr_objects(*it, ns, objects, literals, dynamic); + } +} + +/*******************************************************************\ + Function: collect_objects_address_of_rec Inputs: @@ -141,6 +202,10 @@ void collect_objects_rec( { if(type.id()==ID_struct) { + std::string id=id2string(ssa_object.get_identifier()); + if(src.type().get_bool("#dynamic") || is_pointed(src)) + objects.insert(ssa_object); + // need to split up const struct_typet &struct_type=to_struct_type(type); @@ -152,6 +217,8 @@ void collect_objects_rec( it++) { member_exprt new_src(src, it->get_name(), it->type()); + copy_pointed_info(new_src, src); + collect_objects_rec(new_src, ns, objects, literals); // recursive call } } @@ -162,6 +229,17 @@ void collect_objects_rec( #endif objects.insert(ssa_object); + + const exprt &root_object=ssa_object.get_root_object(); + if(ssa_object.type().get_bool("#dynamic") || !is_pointed(root_object)) + { + collect_ptr_objects( + ssa_object.symbol_expr(), + ns, + objects, + literals, + false); + } } } else @@ -195,6 +273,7 @@ void ssa_objectst::collect_objects( { symbol_exprt symbol=ns.lookup(*it).symbol_expr(); collect_objects_rec(symbol, ns, objects, literals); + collect_ptr_objects(symbol, ns, objects, literals, false); } // Rummage through body. @@ -203,51 +282,18 @@ void ssa_objectst::collect_objects( collect_objects_rec(it->guard, ns, objects, literals); collect_objects_rec(it->code, ns, objects, literals); } -} - -/*******************************************************************\ - -Function: ssa_objectst::add_ptr_objects - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ -void ssa_objectst::add_ptr_objects( - const namespacet &ns) -{ - objectst tmp; - - for(objectst::const_iterator o_it=objects.begin(); - o_it!=objects.end(); - o_it++) + // Add new objects created within the function + goto_programt::const_targett exit=--(src.body.instructions.end()); + if(heap_analysis.has_location(exit)) { - exprt root_object=o_it->get_root_object(); - if(root_object.id()==ID_symbol) + const std::list &new_objects= + heap_analysis[exit].new_objects(); + for(const symbol_exprt &o : new_objects) { - if(o_it->type().id()==ID_pointer) - { - const symbolt &symbol=ns.lookup(root_object); - if(symbol.is_parameter) - tmp.insert(*o_it); - } + collect_objects_rec(o, ns, objects, literals); } } - - for(objectst::const_iterator o_it=tmp.begin(); - o_it!=tmp.end(); - o_it++) - { - typet type=o_it->type().subtype(); - irep_idt identifier=id2string(o_it->get_identifier())+"'obj"; - symbol_exprt ptr_object(identifier, type); - ptr_object.set(ID_ptr_object, o_it->get_identifier()); - collect_objects_rec(ptr_object, ns, objects, literals); - } } /*******************************************************************\ @@ -280,8 +326,9 @@ void ssa_objectst::categorize_objects( if(root_object.id()==ID_symbol) { - if(is_ptr_object(root_object)) + if(is_ptr_object(root_object) || root_object.type().get_bool("#dynamic")) { + globals.insert(*o_it); } else { @@ -379,10 +426,6 @@ ssa_objectt::identifiert ssa_objectt::object_id_rec( { return identifiert(); } - else if(src.id()==ID_ptr_object) - { - return identifiert(id2string(src.get(ID_identifier))+"'obj"); - } else return identifiert(); } diff --git a/src/ssa/ssa_object.h b/src/ssa/ssa_object.h index 37f4b8c8e..0d2cb18e6 100644 --- a/src/ssa/ssa_object.h +++ b/src/ssa/ssa_object.h @@ -9,7 +9,9 @@ Author: Daniel Kroening, kroening@kroening.com #ifndef CPROVER_2LS_SSA_SSA_OBJECT_H #define CPROVER_2LS_SSA_SSA_OBJECT_H +#include "ssa_pointed_objects.h" #include +#include "ssa_heap_domain.h" class ssa_objectt { @@ -84,6 +86,32 @@ class ssa_objectt return get_root_object_rec(expr); } + bool is_unknown_obj() + { + std::string id_str=id2string(identifier); + return id_str.find("$unknown")!=std::string::npos; + } + + void set_flag(const irep_idt flag, bool value) + { + expr.set(flag, value); + } + + void set_iterator( + const irep_idt &pointer_id, + const std::vector &fields) + { + assert(expr.id()==ID_symbol && expr.get_bool(ID_pointed)); + expr.set(ID_iterator, true); + expr.set(ID_it_pointer, pointer_id); + set_iterator_fields(expr, fields); + expr.set(ID_it_init_value, to_symbol_expr(expr).get_identifier()); + expr.set(ID_it_init_value_level, expr.get(ID_pointed_level)); + const irep_idt new_id=id2string(pointer_id)+id2string("'it"); + to_symbol_expr(expr).set_identifier(new_id); + identifier=identifiert(new_id); + } + protected: exprt expr; identifiert identifier; @@ -103,12 +131,15 @@ class ssa_objectst typedef std::set literalst; literalst literals; + const ssa_heap_analysist &heap_analysis; + ssa_objectst( const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) + const namespacet &ns, + const ssa_heap_analysist &_heap_analysis): + heap_analysis(_heap_analysis) { collect_objects(goto_function, ns); - add_ptr_objects(ns); categorize_objects(goto_function, ns); } @@ -120,9 +151,6 @@ class ssa_objectst void categorize_objects( const goto_functionst::goto_functiont &, const namespacet &); - - void add_ptr_objects( - const namespacet &); }; bool is_ptr_object(const exprt &); diff --git a/src/ssa/ssa_pointed_objects.cpp b/src/ssa/ssa_pointed_objects.cpp new file mode 100644 index 000000000..b5c51fad1 --- /dev/null +++ b/src/ssa/ssa_pointed_objects.cpp @@ -0,0 +1,549 @@ +/*******************************************************************\ + +Module: Library of functions for working with pointed objects + +Author: Viktor Malik + +\*******************************************************************/ + +#include "ssa_pointed_objects.h" +#include "ssa_object.h" + +/*******************************************************************\ + +Function: level_str + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt level_str(const unsigned level, const irep_idt &suffix) +{ + return "#lvl_"+std::to_string(level)+"_"+id2string(suffix); +} + +/*******************************************************************\ + +Function: it_field_str + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const irep_idt it_field_str(const unsigned level) +{ + return id2string(ID_it_field)+"_"+std::to_string(level); +} + +/*******************************************************************\ + +Function: copy_iterator + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void copy_iterator(exprt &dest, const exprt &src) +{ + if(src.get_bool(ID_iterator)) + { + dest.set(ID_iterator, true); + dest.set(ID_it_pointer, src.get(ID_it_pointer)); + dest.set(ID_it_init_value, src.get(ID_it_init_value)); + dest.set(ID_it_init_value_level, src.get(ID_it_init_value_level)); + + unsigned field_cnt=src.get_unsigned_int(ID_it_field_cnt); + dest.set(ID_it_field_cnt, field_cnt); + for(unsigned i=0; i fields) +{ + dest.set(ID_it_field_cnt, (unsigned) fields.size()); + for(unsigned i=0; i get_iterator_fields(const exprt &expr) +{ + assert(is_iterator(expr)); + unsigned cnt=expr.get_unsigned_int(ID_it_field_cnt); + std::vector result; + for(unsigned i=0; i pointer_fields( + const exprt &expr, + const unsigned from_level) +{ + std::vector result; + unsigned max_level=pointed_level(expr); + assert(from_level +#include +#include + +#define ID_pointed "#pointed" +#define ID_pointed_level "#level" +#define ID_pointer_id "id" +#define ID_pointer_subtype "subtype" +#define ID_pointer_sym "sym" +#define ID_pointer_compound "compound" +#define ID_pointer_field "field" +#define ID_iterator "#iterator" +#define ID_it_pointer "#it_pointer" +#define ID_it_field "#it_field" +#define ID_it_field_cnt "#it_field_cnt" +#define ID_it_init_value "#it_init_value" +#define ID_it_init_value_level "#it_init_value_level" + +symbol_exprt pointed_object(const exprt &expr, const namespacet &ns); + +bool is_pointed(const exprt &expr); +bool is_iterator(const exprt &expr); + +unsigned pointed_level(const exprt &expr); +unsigned it_value_level(const exprt &expr); + +const irep_idt pointer_root_id(const exprt &expr); +const irep_idt pointer_level_field(const exprt &expr, const unsigned level); +const std::vector pointer_fields( + const exprt &expr, + const unsigned from_level); + +const exprt get_pointer(const exprt &expr, unsigned level); +const exprt get_pointer_root(const exprt &expr, unsigned level); +const irep_idt get_pointer_id(const exprt &expr); + +void copy_pointed_info(exprt &dest, const exprt &src, const unsigned max_level); +void copy_pointed_info(exprt &dest, const exprt &src); +void copy_iterator(exprt &dest, const exprt &src); + +const exprt symbolic_dereference(const exprt &expr, const namespacet &ns); +bool has_symbolic_deref(const exprt &expr); + +void set_iterator_fields(exprt &dest, const std::vector fields); +const std::vector get_iterator_fields(const exprt &expr); + +const irep_idt iterator_to_initial_id( + const exprt &expr, + const irep_idt &expr_id); + +#endif // CPROVER_2LS_SSA_SSA_POINTED_OBJECTS_H diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index aecf2166c..fa372d597 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -7,6 +7,7 @@ Author: Peter Schrammel, Saurabh Joshi \*******************************************************************/ // #define DEBUG +#define COMPETITION #include @@ -280,8 +281,9 @@ void ssa_local_unwindert::unwind(unsigned k) return; current_enabling_expr= - symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), - bool_typet()); + symbol_exprt( + "unwind::"+id2string(fname)+"::enable"+i2string(k), + bool_typet()); SSA.enabling_exprs.push_back(current_enabling_expr); // TODO: just for exploratory integration, must go away @@ -471,7 +473,9 @@ void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) if(!is_last) // only add assumptions if we are not in %0 iteration { if(is_kinduction) + { node.constraints.push_back(*a_it); + } else if(is_bmc) { // only add in base case @@ -559,8 +563,9 @@ void ssa_local_unwindert::add_loop_connector(loopt &loop) SSA.increment_unwindings(0); } else if(e_it->lhs().id()==ID_symbol && - !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), - "ssa::$guard")) + !has_prefix( + id2string(to_symbol_expr(e_it->lhs()).get_identifier()), + "ssa::$guard")) { // this is needed for while loops node.equalities.push_back(*e_it); SSA.decrement_unwindings(0); @@ -569,8 +574,9 @@ void ssa_local_unwindert::add_loop_connector(loopt &loop) } } // continuation guard and condition - exprt g_rhs=and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); + exprt g_rhs=and_exprt( + SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); SSA.decrement_unwindings(0); exprt g_lhs=SSA.guard_symbol(loop.body_nodes.begin()->location); SSA.increment_unwindings(0); @@ -591,8 +597,10 @@ Function: ssa_local_unwindert::add_exit_merges void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) { - SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, - SSA.nodes.end())); // add new node + SSA.nodes.push_back( + local_SSAt::nodet( + loop.body_nodes.begin()->location, + SSA.nodes.end())); // add new node local_SSAt::nodet &node=SSA.nodes.back(); node.enabling_expr=current_enabling_expr; @@ -658,7 +666,11 @@ void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) it!=loop.assertion_hoisting_map.end(); ++it) { if(!is_last // only add assumptions if we are not in %0 iteration - && is_kinduction && !it->second.assertions.empty()) + && is_kinduction && !it->second.assertions.empty() +#ifdef COMPETITION + && !(it->first->guard.id()==ID_not && + it->first->guard.op0().id()==ID_overflow_shl)) +#endif { exprt e=disjunction(it->second.exit_conditions); SSA.rename(e, loop.body_nodes.begin()->location); @@ -716,8 +728,9 @@ Function: ssa_local_unwindert::get_continuation_condition exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const { SSA.current_location=loop.body_nodes.back().location; // TODO: must go away - return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); + return and_exprt( + SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); } /*******************************************************************\ @@ -895,9 +908,10 @@ void ssa_unwindert::init(bool is_kinduction, bool is_bmc) ssa_dbt::functionst& funcs=ssa_db.functions(); for(auto &f : funcs) { - unwinder_map.insert(unwinder_pairt( - f.first, - ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); + unwinder_map.insert( + unwinder_pairt( + f.first, + ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); } } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 62b7a20d7..840d97b24 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -50,12 +50,16 @@ class ssa_local_unwindert // TODO: these two should be possible with unwindable_local_ssa facilities exprt rename_invariant(const exprt& inv_in) const; void unwinder_rename( - symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const; #endif // TODO: this must go away, should use SSA.rename instead void unwinder_rename( - symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const; class loopt // loop tree { @@ -110,7 +114,8 @@ class ssa_local_unwindert exprt get_continuation_condition(const loopt& loop) const; void loop_continuation_conditions( - const loopt& loop, exprt::operandst &loop_cont) const; + const loopt& loop, + exprt::operandst &loop_cont) const; void add_loop_body(loopt &loop); void add_assertions(loopt &loop, bool is_last); @@ -118,7 +123,10 @@ class ssa_local_unwindert void add_loop_connector(loopt &loop); void add_exit_merges(loopt &loop, unsigned k); equal_exprt build_exit_merge( - exprt e, const exprt &exits, unsigned k, locationt loc); + exprt e, + const exprt &exits, + unsigned k, + locationt loc); void add_hoisted_assertions(loopt &loop, bool is_last); }; diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp index aa362160e..20ba39d47 100644 --- a/src/ssa/ssa_value_set.cpp +++ b/src/ssa/ssa_value_set.cpp @@ -7,15 +7,19 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ // #define DEBUG +// #define COMPETITION #ifdef DEBUG #include #endif +#include + #include #include "ssa_value_set.h" #include "ssa_dereference.h" +#include "ssa_pointed_objects.h" /*******************************************************************\ @@ -57,6 +61,15 @@ void ssa_value_domaint::transform( { const code_function_callt &code_function_call= to_code_function_call(from->code); + const irep_idt &fname=to_symbol_expr( + code_function_call.function()).get_identifier(); + + const ssa_value_ait &value_analysis=static_cast(ai); + + const ssa_heap_domaint *heap_domain=NULL; + if(value_analysis.heap_analysis.has_location(to)) + heap_domain=&value_analysis.heap_analysis[to]; + // functions may alter state almost arbitrarily: // * any global-scoped variables @@ -74,12 +87,107 @@ void ssa_value_domaint::transform( assign(*o_it, it, ns); #endif + std::list objects; + + for(auto &argument : code_function_call.arguments()) + { + exprt arg=argument; + exprt arg_expr=argument; + while(arg.type().id()==ID_pointer) + { + if(arg.id()==ID_symbol) + { + symbol_exprt pointed_obj=pointed_object(arg, ns); + pointed_obj.type().set("#dynamic", true); + + std::set new_objects; + if(heap_domain) + new_objects=heap_domain->value(arg_expr); + if(new_objects.empty()) + { + new_objects.insert(pointed_obj); + } + + auto it=new_objects.begin(); + assign_lhs_rec(arg, address_of_exprt(*it), ns); + objects.push_back(*it); + + for(++it; it!=new_objects.end(); ++it) + { + assign_lhs_rec(arg, address_of_exprt(*it), ns, true); + objects.push_back(*it); + } + + arg_expr=dereference_exprt(arg_expr, arg.type().subtype()); + arg=pointed_obj; + } + else if(arg.id()==ID_address_of) + { + arg=arg_expr=to_address_of_expr(arg).object(); + } + else if(arg.id()==ID_typecast) + { + assert(arg_expr.id()==ID_typecast); + arg=arg_expr=to_typecast_expr(arg).op(); + } + else + break; + } + } + // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) { exprt lhs_deref=dereference(code_function_call.lhs(), *this, "", ns); assign_lhs_rec(lhs_deref, nil_exprt(), ns); } + + // the assignment of return value might be in next instruction + if(to->is_assign() && to_code_assign(to->code).rhs().id()==ID_symbol) + { + const symbol_exprt &return_value=to_symbol_expr( + to_code_assign(to->code).rhs()); + if(return_value.type().id()==ID_pointer && + return_value.get_identifier()==id2string(fname)+"#return_value") + { + std::set new_objects; + if(heap_domain) + new_objects=heap_domain->value(return_value); + if(new_objects.empty()) + { + symbol_exprt pointed_obj=pointed_object(return_value, ns); + pointed_obj.type().set("#dynamic", true); + new_objects.insert(pointed_obj); + } + + auto it=new_objects.begin(); + assign_lhs_rec(return_value, address_of_exprt(*it), ns); + objects.push_back(*it); + + for(++it; it!=new_objects.end(); ++it) + { + assign_lhs_rec(return_value, address_of_exprt(*it), ns, true); + objects.push_back(*it); + } + + if(heap_domain) + { + for(auto &new_o : heap_domain->new_caller_objects(fname, from)) + { + objects.push_back(new_o); + } + } + } + } + + for(const symbol_exprt &o1 : objects) + { + for(const symbol_exprt &o2 : objects) + { + if(o1!=o2 && o1.type()==o2.type()) + value_map[ssa_objectt(o1, ns)].value_set.insert(ssa_objectt(o2, ns)); + } + } } else if(from->is_dead()) { @@ -124,13 +232,20 @@ void ssa_value_domaint::assign_lhs_rec( const struct_typet &struct_type=to_struct_type(lhs_type); const struct_typet::componentst &components=struct_type.components(); + auto rhs_it= + rhs.id()==ID_struct ? rhs.operands().begin() : rhs.operands().end(); + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); it++) { member_exprt new_lhs(lhs, it->get_name(), it->type()); - member_exprt new_rhs(rhs, it->get_name(), it->type()); + exprt new_rhs; + if(rhs_it!=rhs.operands().end()) + new_rhs=*(rhs_it++); + else + new_rhs=member_exprt(rhs, it->get_name(), it->type()); assign_lhs_rec(new_lhs, new_rhs, ns, add); // recursive call } @@ -142,6 +257,12 @@ void ssa_value_domaint::assign_lhs_rec( if(ssa_object) { + // TODO: this is required for interprocedural analysis, + // but interferes with intraprocedural analysis +#if 0 + assign_pointed_rhs_rec(rhs, ns); +#endif + valuest tmp_values; assign_rhs_rec(tmp_values, rhs, ns, false, 0); @@ -246,6 +367,11 @@ void ssa_value_domaint::assign_rhs_rec( } else if(rhs.id()==ID_plus) { +#ifdef COMPETITION + bool pointer=false; + bool arithmetics=false; +#endif + forall_operands(it, rhs) { if(it->type().id()==ID_pointer) @@ -255,8 +381,22 @@ void ssa_value_domaint::assign_rhs_rec( pointer_offset=1; unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, *it, ns, true, a); + +#ifdef COMPETITION + pointer=true; +#endif + } + else if(it->type().id()==ID_unsignedbv || it->type().id()==ID_signedbv) + { +#ifdef COMPETITION + arithmetics=true; +#endif } } + +#ifdef COMPETITION + assert(!(pointer && arithmetics)); +#endif } else if(rhs.id()==ID_minus) { @@ -268,6 +408,12 @@ void ssa_value_domaint::assign_rhs_rec( pointer_offset=1; unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, rhs.op0(), ns, true, a); + +#ifdef COMPETITION + assert( + !(rhs.op1().type().id()==ID_unsignedbv || + rhs.op1().type().id()==ID_signedbv)); +#endif } } else if(rhs.id()==ID_dereference) @@ -430,7 +576,10 @@ Function: ssa_value_domaint::valuest::merge \*******************************************************************/ -bool ssa_value_domaint::valuest::merge(const valuest &src) +bool ssa_value_domaint::valuest::merge( + const valuest &src, + bool is_loop_back, + const irep_idt &object_id) { bool result=false; @@ -457,9 +606,79 @@ bool ssa_value_domaint::valuest::merge(const valuest &src) } // value set - unsigned old_size=value_set.size(); - value_set.insert(src.value_set.begin(), src.value_set.end()); - if(old_size!=value_set.size()) + unsigned long old_size=value_set.size(); + for(const ssa_objectt &v : src.value_set) + { + if(is_loop_back) + { + if(is_pointed(v.get_expr())) + { + unsigned level=pointed_level(v.get_expr())-1; + exprt expr=v.get_expr(); + + auto it=value_set.end(); + + while(level>0) + { + const irep_idt ptr_root_id=pointer_root_id(expr); + it=std::find_if( + value_set.begin(), + value_set.end(), + [&ptr_root_id](const ssa_objectt &o) + { return o.get_identifier()==ptr_root_id; }); + if(it!=value_set.end()) + break; + else + { + expr=get_pointer_root(expr, level--); + } + } + + if(it!=value_set.end()) + { + if(!it->get_expr().get_bool(ID_iterator)) + { + assert(it->get_expr().get_bool(ID_pointed)); + ssa_objectt object_copy(*it); + object_copy.set_iterator( + object_id, + pointer_fields( + v.get_expr(), + level)); + value_set.erase(it); + value_set.insert(object_copy); + result=true; + } + continue; + } + } + if(is_iterator(v.get_expr())) + continue; + } + else + { + if(v.get_expr().get_bool(ID_iterator)) + { + const irep_idt &corresponding_id=iterator_to_initial_id( + v.get_expr(), v.get_identifier()); + + auto it=std::find_if( + value_set.begin(), + value_set.end(), + [&corresponding_id](const ssa_objectt &o) + { return o.get_expr().get_bool(ID_pointed) && + (o.get_identifier()==corresponding_id); }); + if(it!=value_set.end()) + { + if(v!=*it) + result=true; + value_set.erase(it); + } + } + } + value_set.insert(v); + } + if(value_set.size()!=old_size) result=true; // alignment @@ -496,7 +715,9 @@ bool ssa_value_domaint::merge( { if(v_it==value_map.end() || it->firstfirst) { - value_map.insert(v_it, *it); + if(!from->is_backwards_goto() || + !is_iterator(it->first.get_root_object())) + value_map.insert(v_it, *it); result=true; it++; continue; @@ -509,7 +730,8 @@ bool ssa_value_domaint::merge( assert(v_it->first==it->first); - if(v_it->second.merge(it->second)) + if(v_it->second.merge(it->second, from->is_backwards_goto(), + it->first.get_identifier())) result=true; v_it++; @@ -518,3 +740,141 @@ bool ssa_value_domaint::merge( return result; } + +/*******************************************************************\ + +Function: ssa_value_domaint::assign_pointed_rhs_rec + + Inputs: + + Outputs: + + Purpose: Dynamically add p'obj to value set of p + +\*******************************************************************/ + +void ssa_value_domaint::assign_pointed_rhs_rec( + const exprt &rhs, + const namespacet &ns) +{ + ssa_objectt ssa_object(rhs, ns); + + if(ssa_object && ssa_object.type().id()==ID_pointer) + { + if(ssa_object.get_root_object().get_bool("#unknown_obj")) + return; + + value_mapt::const_iterator m_it=value_map.find(ssa_object); + + if(m_it==value_map.end()) + { + const symbol_exprt pointed=pointed_object(rhs, ns); + ssa_objectt pointed_obj(pointed, ns); + value_map[ssa_object].value_set.insert(pointed_obj); + } + } + else + { + forall_operands(it, rhs) + assign_pointed_rhs_rec(*it, ns); + } +} + +/*******************************************************************\ + +Function: ssa_value_ait::initialize + + Inputs: GOTO function + + Outputs: + + Purpose: Initialize value sets for pointer parameters and pointer-typed + fields of objects pointed by parameters. + +\*******************************************************************/ + +void ssa_value_ait::initialize( + const goto_functionst::goto_functiont &goto_function) +{ + ait::initialize(goto_function); + + // Initialize value sets for pointer parameters + + if(!goto_function.type.parameters().empty()) + { + locationt e=goto_function.body.instructions.begin(); + ssa_value_domaint &entry=operator[](e); + + for(auto ¶m : goto_function.type.parameters()) + { + const symbol_exprt param_expr(param.get_identifier(), param.type()); + assign_ptr_param(param_expr, entry); + } + } +} + +/*******************************************************************\ + +Function: ssa_value_ait::assign_ptr_param_rec + + Inputs: Expression to be initialized and entry record of value set analysis + + Outputs: + + Purpose: Initialize value set for the given expression and recursively for all + structure members and all pointed objects. + Pointer-typed variable p initially points to abstract object p'obj. + Pointer-typed field of structure initially points to advancer. + +\*******************************************************************/ + +void ssa_value_ait::assign_ptr_param( + const exprt &expr, + ssa_value_domaint &entry) +{ + const typet &type=ns.follow(expr.type()); + if(type.id()==ID_pointer) + { + if(expr.id()==ID_symbol) + { + // pointer variable + symbol_exprt pointed_expr=pointed_object(expr, ns); + assign(expr, pointed_expr, entry); + assign_ptr_param(pointed_expr, entry); + } + } + else if(type.id()==ID_struct) + { + // split structure into fields + for(auto &component : to_struct_type(type).components()) + { + const member_exprt member(expr, component.get_name(), component.type()); + assign_ptr_param(member, entry); + } + } +} + +/*******************************************************************\ + +Function: ssa_value_ait::assign + + Inputs: Pointer variable src, pointed object dest and analysis entry. + + Outputs: + + Purpose: Insert object to value set of another object in the given entry. + +\*******************************************************************/ + +void ssa_value_ait::assign( + const exprt &src, + const exprt &dest, + ssa_value_domaint &entry) +{ + ssa_objectt src_obj(src, ns); + ssa_objectt dest_obj(dest, ns); + if(src_obj && dest_obj) + { + entry.value_map[src_obj].value_set.insert(dest_obj); + } +} diff --git a/src/ssa/ssa_value_set.h b/src/ssa/ssa_value_set.h index 4e1f8afc1..e2e79c791 100644 --- a/src/ssa/ssa_value_set.h +++ b/src/ssa/ssa_value_set.h @@ -12,13 +12,16 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "ssa_object.h" +#include "ssa_heap_domain.h" class ssa_value_domaint:public ai_domain_baset { public: virtual void transform(locationt, locationt, ai_baset &, const namespacet &); virtual void output( - std::ostream &, const ai_baset &, const namespacet &) const; + std::ostream &, + const ai_baset &, + const namespacet &) const; bool merge(const ssa_value_domaint &, locationt, locationt); struct valuest @@ -40,7 +43,10 @@ class ssa_value_domaint:public ai_domain_baset void output(std::ostream &, const namespacet &) const; - bool merge(const valuest &src); + bool merge( + const valuest &src, + bool is_loop_back=false, + const irep_idt &object_id=irep_idt()); inline void clear() { @@ -66,22 +72,27 @@ class ssa_value_domaint:public ai_domain_baset protected: void assign_lhs_rec( - const exprt &lhs, const exprt &rhs, + const exprt &lhs, + const exprt &rhs, const namespacet &, bool add=false); void assign_rhs_rec( - valuest &lhs, const exprt &rhs, + valuest &lhs, + const exprt &rhs, const namespacet &, bool offset, unsigned alignment) const; void assign_rhs_rec_address_of( - valuest &lhs, const exprt &rhs, + valuest &lhs, + const exprt &rhs, const namespacet &, bool offset, unsigned alignment) const; + void assign_pointed_rhs_rec(const exprt &rhs, const namespacet &ns); + static unsigned merge_alignment(unsigned a, unsigned b) { // could use lcm here @@ -100,12 +111,26 @@ class ssa_value_ait:public ait public: ssa_value_ait( const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) + const namespacet &ns_, + const ssa_heap_analysist &_heap_analysis): + ns(ns_), + heap_analysis(_heap_analysis) { - operator()(goto_function, ns); + operator()(goto_function, ns_); } protected: + virtual void initialize( + const goto_functionst::goto_functiont &goto_function) override; + + void assign_ptr_param(const exprt &expr, ssa_value_domaint &entry); + + void assign(const exprt &src, const exprt &dest, ssa_value_domaint &entry); + + const namespacet &ns; + + const ssa_heap_analysist &heap_analysis; + friend class ssa_value_domaint; }; diff --git a/src/ssa/ssa_var_collector.cpp b/src/ssa/ssa_var_collector.cpp new file mode 100644 index 000000000..7bf67d28a --- /dev/null +++ b/src/ssa/ssa_var_collector.cpp @@ -0,0 +1,245 @@ +/*******************************************************************\ + +Module: Template Generator for Summaries, Invariants and Preconditions + +Author: Peter Schrammel, Stefan Marticek + +\*******************************************************************/ + +#include "ssa_var_collector.h" + +/*******************************************************************\ + +Function: template_generator_baset::add_var + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_var_collectort::add_var( + const domaint::vart &var, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) +{ + exprt aux_expr=true_exprt(); + if(std_invariants && pre_guard.id()==ID_and) + { + exprt init_guard=and_exprt(pre_guard.op0(), not_exprt(pre_guard.op1())); + exprt post_var=post_renaming_map[var]; + exprt aux_var=aux_renaming_map[var]; + exprt aux_equals_post=equal_exprt(aux_var, post_var); + exprt aux_equals_init=equal_exprt(aux_var, init_renaming_map[var]); + aux_expr= + and_exprt( + implies_exprt( + and_exprt(post_guard, not_exprt(init_guard)), + aux_equals_post), + implies_exprt( + init_guard, + aux_equals_init)); + post_guard=or_exprt(post_guard, init_guard); + } + if(var.type().id()!=ID_array) + { + var_specs.push_back(domaint::var_spect()); + domaint::var_spect &var_spec=var_specs.back(); + var_spec.pre_guard=pre_guard; + var_spec.post_guard=post_guard; + var_spec.aux_expr=aux_expr; + var_spec.kind=kind; + var_spec.var=var; + } + + // arrays + if(var.type().id()==ID_array && options.get_bool_option("arrays")) + { + const array_typet &array_type=to_array_type(var.type()); + mp_integer size; + to_integer(array_type.size(), size); + for(mp_integer i=0; ilocation->location_number << std::endl; + assert(n_it->loophead!=SSA.nodes.end()); + std::cout << "pre-location: " + << n_it->loophead->location->location_number << std::endl; +#endif + exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard), *n_it, true); + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + pre_guard=and_exprt(lhguard, lsguard); + + exprt pguard=SSA.guard_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard), *n_it, false); + exprt pcond=SSA.cond_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond), *n_it, false); + post_guard=and_exprt(pguard, pcond); +} + +/*******************************************************************\ + +Function: template_generator_baset::get_pre_var + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_var_collectort::get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var) +{ + pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); + + symbol_exprt post_var=SSA.read_rhs(*o_it, n_it->location); + ssa_local_unwinder.unwinder_rename(post_var, *n_it, false); + post_renaming_map[pre_var]=post_var; + + rename_aux_post(post_var); + aux_renaming_map[pre_var]=post_var; +} + +/*******************************************************************\ + +Function: template_generator_baset::get_init_expr + + Inputs: + + Outputs: + + Purpose: supposes that loop head PHIs are of the form + xphi=gls?xlb:x0 + +\*******************************************************************/ + +void ssa_var_collectort::get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr) +{ + symbol_exprt phi_var= + SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var, *n_it->loophead, true); + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->loophead->equalities.begin(); + e_it!=n_it->loophead->equalities.end(); e_it++) + { + if(e_it->rhs().id()==ID_if && + to_symbol_expr(e_it->lhs()).get_identifier()==phi_var.get_identifier()) + { + const if_exprt &if_expr=to_if_expr(e_it->rhs()); + init_expr=if_expr.false_case(); + // should already be renamed for inner loops + break; + } + } + + symbol_exprt pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); + init_renaming_map[pre_var]=init_expr; +} + +/*******************************************************************\ + +Function: ssa_var_collectort::collect_variables_loop + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_var_collectort::collect_variables_loop( + const local_SSAt &SSA, + bool forward) +{ + // used for renaming map + var_listt pre_state_vars, post_state_vars; + + // add loop variables + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + exprt pre_guard, post_guard; + get_pre_post_guards(SSA, n_it, pre_guard, post_guard); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[n_it->loophead->location].phi_nodes; + + // Record the objects modified by the loop to get + // 'primed' (post-state) and 'unprimed' (pre-state) variables. + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + symbol_exprt pre_var; + get_pre_var(SSA, o_it, n_it, pre_var); + exprt init_expr; + get_init_expr(SSA, o_it, n_it, init_expr); + add_var(pre_var, pre_guard, post_guard, domaint::LOOP, var_specs); + +#ifdef DEBUG + std::cout << "Adding " << from_expr(ns, "", in) << " " + << from_expr(ns, "", out) << std::endl; +#endif + } + } + } +} diff --git a/src/ssa/ssa_var_collector.h b/src/ssa/ssa_var_collector.h new file mode 100644 index 000000000..1c69981aa --- /dev/null +++ b/src/ssa/ssa_var_collector.h @@ -0,0 +1,79 @@ +/*******************************************************************\ + +Module: SSA var collector class + +Author: Peter Schrammel, Stefan Marticek + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_VAR_COLLECTOR_H +#define CPROVER_2LS_SSA_SSA_VAR_COLLECTOR_H + +#include + +#include "local_ssa.h" +#include "ssa_unwinder.h" + +#include + +class ssa_var_collectort +{ +public: + typedef strategy_solver_baset::var_listt var_listt; + + explicit ssa_var_collectort( + optionst &_options, + ssa_local_unwindert &_ssa_local_unwinder): + options(_options), + ssa_local_unwinder(_ssa_local_unwinder) + { + std_invariants=options.get_bool_option("std-invariants"); + } + + domaint::var_specst var_specs; + replace_mapt post_renaming_map; + replace_mapt init_renaming_map; + replace_mapt aux_renaming_map; + + optionst options; // copy: we may override options + + void add_var( + const domaint::vart &var_to_add, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + + void get_pre_post_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, + exprt &post_guard); + + void get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var); + + void get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr); + + void rename_aux_post(symbol_exprt &expr) + { + expr.set_identifier(id2string(expr.get_identifier())+"'"); + } + + virtual void collect_variables_loop( + const local_SSAt &SSA, + bool forward); + +protected: + bool std_invariants; // include value at loop entry + const ssa_local_unwindert &ssa_local_unwinder; +}; + +#endif // CPROVER_2LS_SSA_SSA_VAR_COLLECTOR_H diff --git a/src/ssa/unwindable_local_ssa.cpp b/src/ssa/unwindable_local_ssa.cpp index 8b49f8e76..da31f1b7a 100644 --- a/src/ssa/unwindable_local_ssa.cpp +++ b/src/ssa/unwindable_local_ssa.cpp @@ -282,7 +282,7 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) if(expr.id()==ID_symbol) { symbol_exprt &s=to_symbol_expr(expr); - locationt def_loc; + locationt def_loc=goto_function.body.instructions.end(); // we could reuse name(), // but then we would have to search in the ssa_objects // ENHANCEMENT: maybe better to attach base name, ssa name, @@ -294,13 +294,15 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) s.set_identifier(id2string(id)+unwind_suffix); #if 0 - std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; - std::cout << "DEF_LEVEL: " << def_level << std::endl; - std::cout << "O.size: " << current_unwindings.size() << std::endl; - std::cout << "current: " << current_unwinding << std::endl; std::cout << "RENAME_SYMBOL: " << id << " --> " << s.get_identifier() << std::endl; + std::cout << "DEF_LOC: " + << (def_loc!=goto_function.body.instructions.end() + ? def_loc->location_number : -1) << std::endl; + std::cout << "DEF_LEVEL: " << def_level << std::endl; + std::cout << "O.size: " << current_unwindings.size() << std::endl; + std::cout << "current: " << current_unwinding << std::endl << std::endl; #endif } if(expr.id()==ID_nondet_symbol) @@ -348,6 +350,8 @@ irep_idt unwindable_local_SSAt::get_ssa_name( pos1+=2; else if(s.substr(pos1+1, 2)=="ls") pos1+=2; + else if(s.substr(pos1+1, 2)=="os") + pos1+=2; else if(s.substr(pos1+1, 3)=="phi") pos1+=3; else if((pos2==pos1+13) && (s.substr(pos1+1, 12)=="return_value")) @@ -459,3 +463,45 @@ void unwindable_local_SSAt::compute_loop_hierarchy() } while(i_it!=goto_function.body.instructions.begin()); } + +/*******************************************************************\ + +Function: unwindable_local_SSAt::output_verbose + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void unwindable_local_SSAt::output_verbose(std::ostream &out) const +{ + for(const auto &node : nodes) + { + if(node.empty()) + continue; + out << "*** " << node.location->location_number + << " " << node.location->source_location << "\n"; + node.output(out, ns); + for(const auto &e : node.equalities) + { + std::set symbols; + find_symbols(e, symbols); + for(const auto &s : symbols) + { + if(s.type().get_bool("#dynamic")) + out << s.get_identifier() << "\n"; + } + } + if(node.loophead!=nodes.end()) + out << "loop back to location " + << node.loophead->location->location_number << "\n"; + if(!node.enabling_expr.is_true()) + out << "enabled if " + << from_expr(ns, "", node.enabling_expr) << "\n"; + out << "\n"; + } + out << "(enable) " << from_expr(ns, "", get_enabling_exprs()) << "\n\n"; +} diff --git a/src/ssa/unwindable_local_ssa.h b/src/ssa/unwindable_local_ssa.h index 462e89169..11f518225 100644 --- a/src/ssa/unwindable_local_ssa.h +++ b/src/ssa/unwindable_local_ssa.h @@ -12,6 +12,7 @@ Author: Peter Schrammel, Saurabh Joshi #include #include "local_ssa.h" +#include "ssa_heap_domain.h" class unwindable_local_SSAt:public local_SSAt { @@ -19,8 +20,9 @@ class unwindable_local_SSAt:public local_SSAt unwindable_local_SSAt( const goto_functiont &_goto_function, const namespacet &_ns, + const ssa_heap_analysist &heap_analysis, const std::string &_suffix=""): - local_SSAt(_goto_function, _ns, _suffix), + local_SSAt(_goto_function, _ns, heap_analysis, _suffix), current_unwinding(-1) { compute_loop_hierarchy(); @@ -36,7 +38,10 @@ class unwindable_local_SSAt:public local_SSAt return name(obj, kind, loc, loc); } symbol_exprt name( - const ssa_objectt &, kindt, locationt def_loc, locationt current_loc) const; + const ssa_objectt &, + kindt, + locationt def_loc, + locationt current_loc) const; virtual exprt nondet_symbol( std::string prefix, const typet &type, @@ -76,6 +81,8 @@ class unwindable_local_SSAt:public local_SSAt locationt &loc, odometert &odometer) const; + void output_verbose(std::ostream &) const override; + protected: irep_idt get_ssa_name(const symbol_exprt &, locationt &loc) const;