diff --git a/regression/goto-analyzer-simplify/Makefile b/regression/goto-analyzer-simplify/Makefile new file mode 100644 index 00000000000..08fe97ae88c --- /dev/null +++ b/regression/goto-analyzer-simplify/Makefile @@ -0,0 +1,31 @@ + +default: tests.log + +test: + @if ! ../test.pl -c ../chain.sh ; then \ + ../failed-tests-printer.pl ; \ + exit 1; \ + fi + +tests.log: + @if ! ../test.pl -c ../chain.sh ; then \ + ../failed-tests-printer.pl ; \ + exit 1; \ + fi + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @for dir in *; do \ + rm -f tests.log; \ + if [ -d "$$dir" ]; then \ + cd "$$dir"; \ + rm -f *.out *.gb; \ + cd ..; \ + fi \ + done diff --git a/regression/goto-analyzer-simplify/chain.sh b/regression/goto-analyzer-simplify/chain.sh new file mode 100755 index 00000000000..5fbf28733ed --- /dev/null +++ b/regression/goto-analyzer-simplify/chain.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +src_dir=../../../src + +goto_analyzer=$src_dir/goto-analyzer/goto-analyzer + +options=$1 +file_name=${2%.c} + +echo options: $options +echo file name : $file_name + +$goto_analyzer $file_name.c $options --simplify $file_name_simp.out +$goto_analyzer $file_name_simp.out --show-goto-functions diff --git a/regression/goto-analyzer-simplify/partial-simplify-array/main.c b/regression/goto-analyzer-simplify/partial-simplify-array/main.c new file mode 100644 index 00000000000..bf80fa1399a --- /dev/null +++ b/regression/goto-analyzer-simplify/partial-simplify-array/main.c @@ -0,0 +1,7 @@ +extern int arr[]; + +void main() +{ + int index = 0; + int j = arr[index]; +} diff --git a/regression/goto-analyzer-simplify/partial-simplify-array/test.desc b/regression/goto-analyzer-simplify/partial-simplify-array/test.desc new file mode 100644 index 00000000000..7c0b5afe8c1 --- /dev/null +++ b/regression/goto-analyzer-simplify/partial-simplify-array/test.desc @@ -0,0 +1,5 @@ +CORE +main.c +"--variable-sensitivity --vsd-arrays" + +arr\[0l\] diff --git a/regression/goto-analyzer-simplify/simplify-complex-expression/main.c b/regression/goto-analyzer-simplify/simplify-complex-expression/main.c new file mode 100644 index 00000000000..3f0df80ad1a --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-complex-expression/main.c @@ -0,0 +1,12 @@ +int nondet_int(void); + +int main(void) +{ + int r = nondet_int(); + r = r + 1; + if(r == 2) + { + return 1; + } + return 0; +} diff --git a/regression/goto-analyzer-simplify/simplify-complex-expression/test.desc b/regression/goto-analyzer-simplify/simplify-complex-expression/test.desc new file mode 100644 index 00000000000..2e489c2332f --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-complex-expression/test.desc @@ -0,0 +1,14 @@ +CORE +main.c +--variable-sensitivity +r == 2 +^SIGNAL=0$ +^EXIT=6$ +-- +-- + +Checks for a bug that occurred while changing the simplifier, +where a variable would be replaced by the RHS of its last assignment, +even if the value of that expression had changed since then; +Most egregiously when the RHS contained the symbol on the LHS (thus leading +to a recursive definition). diff --git a/regression/goto-analyzer-simplify/simplify-lhs-array-index/main.c b/regression/goto-analyzer-simplify/simplify-lhs-array-index/main.c new file mode 100644 index 00000000000..2c22f8b87a1 --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-array-index/main.c @@ -0,0 +1,15 @@ +void main() +{ + int arr[4] = {1, 2, 3, 4}; + + // No simplification + arr[0] = 1; + + // Can simplify + int constant = 1; + arr[constant] = 2; + + // No simplification + int nondet; + arr[nondet] = 3; +} diff --git a/regression/goto-analyzer-simplify/simplify-lhs-array-index/test.desc b/regression/goto-analyzer-simplify/simplify-lhs-array-index/test.desc new file mode 100644 index 00000000000..8ce04ba76ef --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-array-index/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +"--variable-sensitivity --vsd-arrays" + +arr\[0l\] = +arr\[1l\] = +arr\[\(signed long int\)nondet\] = diff --git a/regression/goto-analyzer-simplify/simplify-lhs-array-pointers-index/main.c b/regression/goto-analyzer-simplify/simplify-lhs-array-pointers-index/main.c new file mode 100644 index 00000000000..db8b61e792b --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-array-pointers-index/main.c @@ -0,0 +1,33 @@ +void main() +{ + int symbol_a; + int symbol_b; + + int nondet; + int *nondet_pointer; + if(nondet > 0) + { + nondet_pointer = &symbol_a; + } + else + { + nondet_pointer = &symbol_b; + } + + int *arr[] = {&symbol_a, &symbol_b, nondet_pointer}; + + // Simplify the pointer + *arr[0] = 1; + + // Simplify index and the pointer + int constant1 = 1; + *arr[constant1] = 2; + + // Simplify the index but not the pointer + int constant2 = 2; + *arr[constant2] = 3; + + // No simplification + int nondet_index; + *arr[nondet_index] = 4; +} diff --git a/regression/goto-analyzer-simplify/simplify-lhs-array-pointers-index/test.desc b/regression/goto-analyzer-simplify/simplify-lhs-array-pointers-index/test.desc new file mode 100644 index 00000000000..4129ec58d45 --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-array-pointers-index/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +"--variable-sensitivity --vsd-arrays --vsd-pointers" + +symbol_a = 1 +symbol_b = 2 +\*arr\[2l\] = 3; +\*arr\[\(signed long int\)nondet_index\] = 4; diff --git a/regression/goto-analyzer-simplify/simplify-lhs-dereference/main.c b/regression/goto-analyzer-simplify/simplify-lhs-dereference/main.c new file mode 100644 index 00000000000..5b3be78d13c --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-dereference/main.c @@ -0,0 +1,22 @@ +void main() +{ + int symbol; + + int *pointer = &symbol; + + // Simplify + *pointer = 5; + + int nondet; + if(nondet > 0) + { + pointer = &nondet; + } + else + { + pointer = &symbol; + } + + // No simplification + *pointer = 6; +} diff --git a/regression/goto-analyzer-simplify/simplify-lhs-dereference/test.desc b/regression/goto-analyzer-simplify/simplify-lhs-dereference/test.desc new file mode 100644 index 00000000000..bc05dc76913 --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-dereference/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +"--variable-sensitivity --vsd-pointers" + +symbol = 5 +\*pointer = 6 diff --git a/regression/goto-analyzer-simplify/simplify-lhs-member/main.c b/regression/goto-analyzer-simplify/simplify-lhs-member/main.c new file mode 100644 index 00000000000..d375cf28e26 --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-member/main.c @@ -0,0 +1,42 @@ +struct test_struct +{ + int *pointer_component; + int array[5]; +}; + +void main() +{ + int symbol; + + struct test_struct value; + + // Simplify a pointer inside a struct + int symbol; + value.pointer_component = &symbol; + + // Simplify + *value.pointer_component = 5; + + int nondet; + if(nondet > 0) + { + value.pointer_component = &nondet; + } + else + { + value.pointer_component = &symbol; + } + + // No simplification + *value.pointer_component = 6; + + // Simplify an array + + // Can simplify + int constant = 1; + value.array[constant] = 2; + + // No simplification + int nondet; + value.array[nondet] = 3; +} diff --git a/regression/goto-analyzer-simplify/simplify-lhs-member/test.desc b/regression/goto-analyzer-simplify/simplify-lhs-member/test.desc new file mode 100644 index 00000000000..ea66f6e269a --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-member/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +"--variable-sensitivity --vsd-arrays --vsd-pointers --vsd-structs" + +symbol = 5 +\*value\.pointer_component = 6 +value\.array\[1l\] = 2 +value\.array\[\(signed long int\)nondet\] = 3 diff --git a/regression/goto-analyzer-simplify/simplify-lhs-pointer-array-dereference/main.c b/regression/goto-analyzer-simplify/simplify-lhs-pointer-array-dereference/main.c new file mode 100644 index 00000000000..7ba0d604c7e --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-pointer-array-dereference/main.c @@ -0,0 +1,20 @@ +void main() +{ + int array[] = {1, 2, 3, 4, 5}; + + int *pointer = array; + + // Simplify -> array[0] = 5 + *pointer = 5; + + int nondet; + pointer[nondet] = 6; + + // TODO: Currently writing to an offset pointer sets the domain to top + // so recreate the variables to reign the domain back in + int new_array[] = {1, 2, 3, 4, 5}; + int *new_pointer = new_array; + + int constant = 1; + new_pointer[constant] = 7; +} diff --git a/regression/goto-analyzer-simplify/simplify-lhs-pointer-array-dereference/test.desc b/regression/goto-analyzer-simplify/simplify-lhs-pointer-array-dereference/test.desc new file mode 100644 index 00000000000..c2903b287f7 --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-lhs-pointer-array-dereference/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +"--variable-sensitivity --vsd-arrays --vsd-pointers" +array\[0l\] = 5 +array\[\(signed long int\)nondet\] = 6 +new_array\[1l\] = 7 diff --git a/regression/goto-analyzer-simplify/simplify-multiply-by-zero/main.c b/regression/goto-analyzer-simplify/simplify-multiply-by-zero/main.c new file mode 100644 index 00000000000..cdb243ad05c --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-multiply-by-zero/main.c @@ -0,0 +1,8 @@ +int nondet_int(void); + +int main(void) +{ + int K = nondet_int(); + int x = 0; + return K * x; +} diff --git a/regression/goto-analyzer-simplify/simplify-multiply-by-zero/test.desc b/regression/goto-analyzer-simplify/simplify-multiply-by-zero/test.desc new file mode 100644 index 00000000000..6675045b32a --- /dev/null +++ b/regression/goto-analyzer-simplify/simplify-multiply-by-zero/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +"--variable-sensitivity" +^SIGNAL=0$ +^EXIT=6$ +main#return_value = 0; +-- +-- +Tests that a multiplication +of variable*variable can be simplified +if one of the variables can be evaluated to 0. \ No newline at end of file diff --git a/regression/goto-analyzer/constant_assertions_01/main.c b/regression/goto-analyzer/constant_assertions_01/main.c new file mode 100644 index 00000000000..2eb59ab12a2 --- /dev/null +++ b/regression/goto-analyzer/constant_assertions_01/main.c @@ -0,0 +1,17 @@ +#include + +int nondet_int(void); + +int main(int argc, char **argv) +{ + int x = nondet_int(); + int y = nondet_int(); + + __CPROVER_assert(0, "0"); + __CPROVER_assert(0 && 1, "0 && 1"); + __CPROVER_assert(0 || 0, "0 || 0"); + __CPROVER_assert(0 && x, "0 && x"); + __CPROVER_assert(y && 0, "y && 0"); + + return 0; +} diff --git a/regression/goto-analyzer/constant_assertions_01/test.desc b/regression/goto-analyzer/constant_assertions_01/test.desc new file mode 100644 index 00000000000..0692e81292f --- /dev/null +++ b/regression/goto-analyzer/constant_assertions_01/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--verify --variable-sensitivity +^\[main.assertion.1\] line 10 0: FAILURE \(if reachable\)$ +^\[main.assertion.2\] line 11 0 && 1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] line 12 0 || 0: FAILURE \(if reachable\)$ +^\[main.assertion.4\] line 13 0 && x: FAILURE \(if reachable\)$ +^\[main.assertion.5\] line 14 y && 0: FAILURE \(if reachable\)$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_assertions_02/main.c b/regression/goto-analyzer/constant_assertions_02/main.c new file mode 100644 index 00000000000..d77496524ea --- /dev/null +++ b/regression/goto-analyzer/constant_assertions_02/main.c @@ -0,0 +1,17 @@ +#include + +int nondet_int(void); + +int main(int argc, char **argv) +{ + int x = nondet_int(); + int y = nondet_int(); + + __CPROVER_assert(1, "1"); + __CPROVER_assert(0 || 1, "0 || 1"); + __CPROVER_assert(1 && 1, "1 && 1"); + __CPROVER_assert(1 || x, "1 || x"); + __CPROVER_assert(y || 1, "y || 1"); + + return 0; +} diff --git a/regression/goto-analyzer/constant_assertions_02/test.desc b/regression/goto-analyzer/constant_assertions_02/test.desc new file mode 100644 index 00000000000..851893237f4 --- /dev/null +++ b/regression/goto-analyzer/constant_assertions_02/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--verify --variable-sensitivity +^\[main\.assertion\.1\] line 10 1: SUCCESS +^\[main\.assertion\.2\] line 11 0 || 1: SUCCESS +^\[main\.assertion\.3\] line 12 1 && 1: SUCCESS +^\[main\.assertion\.4\] line 13 1 || x: SUCCESS +^\[main\.assertion\.5\] line 14 y || 1: SUCCESS +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_01/main.c b/regression/goto-analyzer/constant_propagation_01/main.c index 6689d8d4562..394d0df7742 100644 --- a/regression/goto-analyzer/constant_propagation_01/main.c +++ b/regression/goto-analyzer/constant_propagation_01/main.c @@ -1,13 +1,13 @@ +#include int main() { int i, j=20; - + if (j==20) { int x=1,y=2,z; z=x+y; - assert(z==3); + __CPROVER_assert(z == 3, "z==3"); } - } diff --git a/regression/goto-analyzer/constant_propagation_01/test-vsd.desc b/regression/goto-analyzer/constant_propagation_01/test-vsd.desc new file mode 100644 index 00000000000..2259a9f01cc --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_01/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 7, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 0, assigns: 17, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_02/main.c b/regression/goto-analyzer/constant_propagation_02/main.c index db0c8dc3f3e..63c39f97d17 100644 --- a/regression/goto-analyzer/constant_propagation_02/main.c +++ b/regression/goto-analyzer/constant_propagation_02/main.c @@ -3,10 +3,10 @@ int main() { int i=0, j=2; - if (i==0) + if(i == 0) { i++; j++; } - assert(j!=3); + __CPROVER_assert(j != 3, "j!=3"); } diff --git a/regression/goto-analyzer/constant_propagation_02/test-vsd.desc b/regression/goto-analyzer/constant_propagation_02/test-vsd.desc new file mode 100644 index 00000000000..090c7d3b26e --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_02/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 8, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 0, assigns: 16, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_03/main.c b/regression/goto-analyzer/constant_propagation_03/main.c index 09a5434dead..dacc46b372c 100644 --- a/regression/goto-analyzer/constant_propagation_03/main.c +++ b/regression/goto-analyzer/constant_propagation_03/main.c @@ -3,10 +3,10 @@ int main() { int i=0, j=2; - if (i==0) + if(i == 0) { i++; j++; } - assert(j==3); + __CPROVER_assert(j == 3, "j==3"); } diff --git a/regression/goto-analyzer/constant_propagation_03/test-vsd.desc b/regression/goto-analyzer/constant_propagation_03/test-vsd.desc new file mode 100644 index 00000000000..090c7d3b26e --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_03/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 8, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 0, assigns: 16, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_04/main.c b/regression/goto-analyzer/constant_propagation_04/main.c index 2c6c3f39db1..66290476992 100644 --- a/regression/goto-analyzer/constant_propagation_04/main.c +++ b/regression/goto-analyzer/constant_propagation_04/main.c @@ -3,10 +3,10 @@ int main() { int i=0, j=2; - if (i<50) + if(i < 50) { i++; j++; } - assert(j==3); + __CPROVER_assert(j == 3, "j==3"); } diff --git a/regression/goto-analyzer/constant_propagation_04/test-vsd.desc b/regression/goto-analyzer/constant_propagation_04/test-vsd.desc new file mode 100644 index 00000000000..090c7d3b26e --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_04/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 8, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 0, assigns: 16, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_05/main.c b/regression/goto-analyzer/constant_propagation_05/main.c index b740fc135c0..3d947bbdd96 100644 --- a/regression/goto-analyzer/constant_propagation_05/main.c +++ b/regression/goto-analyzer/constant_propagation_05/main.c @@ -3,10 +3,10 @@ int main() { int i=0, j=2; - if (i<50) + if(i < 50) { i++; j++; } - assert(j!=3); + __CPROVER_assert(j != 3, "j!=3"); } diff --git a/regression/goto-analyzer/constant_propagation_05/test-vsd.desc b/regression/goto-analyzer/constant_propagation_05/test-vsd.desc new file mode 100644 index 00000000000..a8d6ab67685 --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_05/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] .* j!=3: FAILURE \(if reachable\)$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_05/test.desc b/regression/goto-analyzer/constant_propagation_05/test.desc index 2a351a220f9..72286f84e2e 100644 --- a/regression/goto-analyzer/constant_propagation_05/test.desc +++ b/regression/goto-analyzer/constant_propagation_05/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 11 assertion j != 3: FAILURE \(if reachable\)$ +^\[main\.assertion\.1\] .* j!=3: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_06/main.c b/regression/goto-analyzer/constant_propagation_06/main.c index 6290aa89e6b..eb3234177c2 100644 --- a/regression/goto-analyzer/constant_propagation_06/main.c +++ b/regression/goto-analyzer/constant_propagation_06/main.c @@ -3,11 +3,11 @@ int main() { int i=0, j=2; - while (i<50) + while(i < 50) { i++; j++; } - assert(i<51); + __CPROVER_assert(i < 51, "i<51"); } diff --git a/regression/goto-analyzer/constant_propagation_06/test-vsd.desc b/regression/goto-analyzer/constant_propagation_06/test-vsd.desc new file mode 100644 index 00000000000..6d9a2758650 --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_06/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion.1\] .* i\s*<\s*51: UNKNOWN$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_06/test.desc b/regression/goto-analyzer/constant_propagation_06/test.desc index fd34f106869..dd5af473380 100644 --- a/regression/goto-analyzer/constant_propagation_06/test.desc +++ b/regression/goto-analyzer/constant_propagation_06/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 11 assertion i\s*<\s*51: UNKNOWN$ +^\[main.assertion.1\] .* i\s*<\s*51: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_07/main.c b/regression/goto-analyzer/constant_propagation_07/main.c index 1fb6fee8e15..83db927383e 100644 --- a/regression/goto-analyzer/constant_propagation_07/main.c +++ b/regression/goto-analyzer/constant_propagation_07/main.c @@ -17,7 +17,7 @@ int main() j = j + 1; i = i + 1; } - assert(!(i < 2)); + __CPROVER_assert(!(i < 2), "!(i < 2)"); } } return 0; diff --git a/regression/goto-analyzer/constant_propagation_07/test-vsd.desc b/regression/goto-analyzer/constant_propagation_07/test-vsd.desc new file mode 100644 index 00000000000..d58e42e198b --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_07/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 3, assigns: 9, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 1, assigns: 18, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_08/main.c b/regression/goto-analyzer/constant_propagation_08/main.c index 295e23c636d..9bc63eda848 100644 --- a/regression/goto-analyzer/constant_propagation_08/main.c +++ b/regression/goto-analyzer/constant_propagation_08/main.c @@ -10,7 +10,7 @@ int main() else a[1]=2; - assert(a[0]==1 || a[1]==2); + __CPROVER_assert(a[0] == 1 || a[1] == 2, "a[0]==1 || a[1]==2"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_08/test-vsd.desc b/regression/goto-analyzer/constant_propagation_08/test-vsd.desc new file mode 100644 index 00000000000..ebc46cfd84f --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_08/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 2, assigns: 8, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 3, assigns: 17, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_08/test.desc b/regression/goto-analyzer/constant_propagation_08/test.desc index 07a97596c15..52d013d15f5 100644 --- a/regression/goto-analyzer/constant_propagation_08/test.desc +++ b/regression/goto-analyzer/constant_propagation_08/test.desc @@ -1,4 +1,4 @@ -FUTURE +KNOWNBUG main.c --constants --simplify out.gb ^EXIT=0$ diff --git a/regression/goto-analyzer/constant_propagation_09/main.c b/regression/goto-analyzer/constant_propagation_09/main.c index 9bd38159f67..8b004115196 100644 --- a/regression/goto-analyzer/constant_propagation_09/main.c +++ b/regression/goto-analyzer/constant_propagation_09/main.c @@ -6,7 +6,7 @@ int main() if (a[0]==0) a[0]=1; - assert(a[0]==0); + __CPROVER_assert(a[0] == 0, "a[0]==0"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_09/test-vsd.desc b/regression/goto-analyzer/constant_propagation_09/test-vsd.desc new file mode 100644 index 00000000000..810a5ef1d63 --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_09/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion.1\] line \d+ a\[0\]==0: FAILURE \(if reachable\)$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_09/test.desc b/regression/goto-analyzer/constant_propagation_09/test.desc index 1c0f0d10583..3edf55e5260 100644 --- a/regression/goto-analyzer/constant_propagation_09/test.desc +++ b/regression/goto-analyzer/constant_propagation_09/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 9 assertion a\[(\(signed( long)? long int\))?0\] == 0: FAILURE \(if reachable\)$ +^\[main.assertion.1\] line \d+ a\[0\]==0: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_10/main.c b/regression/goto-analyzer/constant_propagation_10/main.c index 217faa4c9a7..dc79ad434ca 100644 --- a/regression/goto-analyzer/constant_propagation_10/main.c +++ b/regression/goto-analyzer/constant_propagation_10/main.c @@ -7,7 +7,7 @@ int main() if (a[0]==0) a[0]=1; - assert(a[0]==2); + __CPROVER_assert(a[0] == 2, "a[0]==2"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_10/test-vsd.desc b/regression/goto-analyzer/constant_propagation_10/test-vsd.desc new file mode 100644 index 00000000000..202eec092cd --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_10/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line 10 a\[0\]==2: FAILURE \(if reachable\)$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_10/test.desc b/regression/goto-analyzer/constant_propagation_10/test.desc index e34ac581388..0caf5a98314 100644 --- a/regression/goto-analyzer/constant_propagation_10/test.desc +++ b/regression/goto-analyzer/constant_propagation_10/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 10 assertion a\[(\(signed( long)? long int\))?0\] == 2: FAILURE \(if reachable\)$ +^\[main\.assertion\.1\] line 10 a\[0\]==2: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_11/main.c b/regression/goto-analyzer/constant_propagation_11/main.c index cefa0c479ab..ed387e3b762 100644 --- a/regression/goto-analyzer/constant_propagation_11/main.c +++ b/regression/goto-analyzer/constant_propagation_11/main.c @@ -6,7 +6,7 @@ int main() if (a[0]==0) a[0]=1; - assert(a[0]==1 /*|| a[0]==2*/); + __CPROVER_assert(a[0] == 1 /*|| a[0]==2*/, "a[0]==1 /*|| a[0]==2*/"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_11/test-vsd.desc b/regression/goto-analyzer/constant_propagation_11/test-vsd.desc new file mode 100644 index 00000000000..7324b882f4f --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_11/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 8, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 1, assigns: 14, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_11/test.desc b/regression/goto-analyzer/constant_propagation_11/test.desc index 8b5049d2894..a095b629bdb 100644 --- a/regression/goto-analyzer/constant_propagation_11/test.desc +++ b/regression/goto-analyzer/constant_propagation_11/test.desc @@ -1,8 +1,9 @@ CORE main.c ---constants --verify +--constants --simplify out.gb ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 9 assertion a\[(\(signed( long)? long int\))?0\] == 1: SUCCESS$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 8, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 1, assigns: 14, function calls: 2$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_12/main.c b/regression/goto-analyzer/constant_propagation_12/main.c index a8379b64712..c5f55fdda8a 100644 --- a/regression/goto-analyzer/constant_propagation_12/main.c +++ b/regression/goto-analyzer/constant_propagation_12/main.c @@ -6,7 +6,7 @@ int main() if (i==0) y=1; - assert(y==1); + __CPROVER_assert(y == 1, "y==1"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_12/test-vsd.desc b/regression/goto-analyzer/constant_propagation_12/test-vsd.desc new file mode 100644 index 00000000000..d4209868c6c --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_12/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --simplify out.gb +^EXIT=0$ +^SIGNAL=0$ +^Simplified: assert: 1, assume: 0, goto: 1, assigns: 7, function calls: 0$ +^Unmodified: assert: 0, assume: 0, goto: 1, assigns: 15, function calls: 2$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_13/main.c b/regression/goto-analyzer/constant_propagation_13/main.c index a4307c4d431..377b9490213 100644 --- a/regression/goto-analyzer/constant_propagation_13/main.c +++ b/regression/goto-analyzer/constant_propagation_13/main.c @@ -6,7 +6,7 @@ int main() if (i==0) y=1; - assert(y==0); + __CPROVER_assert(y == 0, "y==0"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_13/test-vsd.desc b/regression/goto-analyzer/constant_propagation_13/test-vsd.desc new file mode 100644 index 00000000000..ba3e0e23577 --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_13/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] .* y==0: FAILURE \(if reachable\)$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_13/test.desc b/regression/goto-analyzer/constant_propagation_13/test.desc index 91a78a1df7a..5da13a53d59 100644 --- a/regression/goto-analyzer/constant_propagation_13/test.desc +++ b/regression/goto-analyzer/constant_propagation_13/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 9 assertion y == 0: FAILURE \(if reachable\)$ +^\[main\.assertion\.1\] .* y==0: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_14/main.c b/regression/goto-analyzer/constant_propagation_14/main.c index 7a659b87c60..34021bdd6aa 100644 --- a/regression/goto-analyzer/constant_propagation_14/main.c +++ b/regression/goto-analyzer/constant_propagation_14/main.c @@ -5,11 +5,11 @@ int main() if (a[0]==0) a[0]=1; - else + else a[0]=2; - assert(a[0]==1 || a[0]==2); - assert(a[0]==1 && a[0]==2); + __CPROVER_assert(a[0] == 1 || a[0] == 2, "a[0]==1 || a[0]==2"); + __CPROVER_assert(a[0] == 1 && a[0] == 2, "a[0]==1 && a[0]==2"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_14/test-vsd.desc b/regression/goto-analyzer/constant_propagation_14/test-vsd.desc new file mode 100644 index 00000000000..cfab18921ef --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_14/test-vsd.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --verify +^EXIT=0$ +^SIGNAL=0$ +\[main\.assertion\.1\] .* a\[0\]==1 || a\[0\]==2: SUCCESS$ +\[main\.assertion\.2\] .* a\[0\]==1 && a\[0\]==2: FAILURE \(if reachable\)$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_14/test.desc b/regression/goto-analyzer/constant_propagation_14/test.desc index e74316180d5..29453ed0c2c 100644 --- a/regression/goto-analyzer/constant_propagation_14/test.desc +++ b/regression/goto-analyzer/constant_propagation_14/test.desc @@ -3,7 +3,7 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 11 assertion tmp_if_expr: SUCCESS$ -^\[main.assertion.2\] line 12 assertion tmp_if_expr\$0: FAILURE \(if reachable\)$ +\[main\.assertion\.1\] .* a\[0\]==1 || a\[0\]==2: SUCCESS$ +\[main\.assertion\.2\] .* a\[0\]==1 && a\[0\]==2: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_15/main.c b/regression/goto-analyzer/constant_propagation_15/main.c index e3dd672831d..b06e7a486de 100644 --- a/regression/goto-analyzer/constant_propagation_15/main.c +++ b/regression/goto-analyzer/constant_propagation_15/main.c @@ -6,7 +6,7 @@ int main() if (a[0]==0) a[0]=1; - assert(a[0]==2); + __CPROVER_assert(a[0] == 2, "a[0]==2"); return 0; } diff --git a/regression/goto-analyzer/constant_propagation_15/test-vsd.desc b/regression/goto-analyzer/constant_propagation_15/test-vsd.desc new file mode 100644 index 00000000000..4a463c2811a --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_15/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] .* a\[0\]==2: FAILURE \(if reachable\)$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_15/test.desc b/regression/goto-analyzer/constant_propagation_15/test.desc index bf760877b4a..e16cf25d80b 100644 --- a/regression/goto-analyzer/constant_propagation_15/test.desc +++ b/regression/goto-analyzer/constant_propagation_15/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 9 assertion a\[(\(signed( long)? long int\))?0\] == 2: FAILURE \(if reachable\)$ +^\[main\.assertion\.1\] .* a\[0\]==2: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_16/test-vsd.desc b/regression/goto-analyzer/constant_propagation_16/test-vsd.desc new file mode 100644 index 00000000000..e8bc7e6b3c7 --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_16/test-vsd.desc @@ -0,0 +1,7 @@ +CORE +main.c +--show --variable-sensitivity +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_17/main.c b/regression/goto-analyzer/constant_propagation_17/main.c index 40b04edfdd0..b080d5e33cc 100644 --- a/regression/goto-analyzer/constant_propagation_17/main.c +++ b/regression/goto-analyzer/constant_propagation_17/main.c @@ -4,11 +4,11 @@ int main() { int i=0, j=2; - while (i<50) + while(i < 50) { i++; j++; } - assert(i<51); + __CPROVER_assert(i < 51, "i<51"); } diff --git a/regression/goto-analyzer/constant_propagation_17/test-vsd.desc b/regression/goto-analyzer/constant_propagation_17/test-vsd.desc new file mode 100644 index 00000000000..a3599c9d0fb --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_17/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] .* i\s*<\s*51: (UNKNOWN|FAILURE \(if reachable\))$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_17/test.desc b/regression/goto-analyzer/constant_propagation_17/test.desc index 31c2928c262..94b0034f356 100644 --- a/regression/goto-analyzer/constant_propagation_17/test.desc +++ b/regression/goto-analyzer/constant_propagation_17/test.desc @@ -3,6 +3,6 @@ main.c --constants --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 12 assertion i\s*<\s*51: (UNKNOWN|FAILURE \(if reachable\))$ +^\[main\.assertion\.1\] .* i\s*<\s*51: (UNKNOWN|FAILURE \(if reachable\))$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_18/test-vsd.desc b/regression/goto-analyzer/constant_propagation_18/test-vsd.desc new file mode 100644 index 00000000000..604a018b834 --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_18/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --verify --vsd-pointers +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion.1\] line 7 assertion \*p == 1: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/constant_propagation_19/test-vsd.desc b/regression/goto-analyzer/constant_propagation_19/test-vsd.desc new file mode 100644 index 00000000000..c18c3beb95b --- /dev/null +++ b/regression/goto-analyzer/constant_propagation_19/test-vsd.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --verify --vsd-pointers +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion.1\] line 8 assertion x == 42: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/demo/main.c b/regression/goto-analyzer/demo/main.c new file mode 100644 index 00000000000..57d32980518 --- /dev/null +++ b/regression/goto-analyzer/demo/main.c @@ -0,0 +1,119 @@ +#include + +// To demonstrate run +// $ goto-analyzer main.c --variable --verify --pointers --structs +// Will show the asserts + +// To see the pointer optimizations run +// $ goto-analyzer main.c --variable --simplify out.gb --pointers --structs +// $ goto-analyzer out.gb --show-goto-functions + +void func(int unknwon); +int main() +{ + func(4); +} + +// Pass in an unknown to show when we don't know what branch is taken + +void func(int unknown) +{ + int i = 0, j = 2; + if(i == 0) + { + i++; + j++; + } + + // Knows we took if statement so can conclude assertion is true + __CPROVER_assert(j == 3, "j==3"); // Verified + + int value = 4; + + int *p2v = &value; + int **pp2v = &p2v; + + __CPROVER_assert(*p2v == 4, "*p2v==4"); + __CPROVER_assert(**pp2v == 4, "**pp2v==4"); + + value = 10; + + // Tracks the value pointed to has changed + __CPROVER_assert(*p2v == 10, "*p2v==10"); + __CPROVER_assert(**pp2v == 10, "**pp2v==10"); + + *p2v = 15; + __CPROVER_assert(value == 15, "value==15"); + __CPROVER_assert(*p2v == 15, "*p2v==15"); + __CPROVER_assert(**pp2v == 15, "**pp2v==15"); + + **pp2v = 20; + __CPROVER_assert(value == 20, "value==20"); + __CPROVER_assert(*p2v == 20, "*p2v==20"); + __CPROVER_assert(**pp2v == 20, "**pp2v==20"); + + int other = 5; + p2v = &other; + + __CPROVER_assert(*p2v == 5, "*p2v==5"); + __CPROVER_assert(**pp2v == 5, "**pp2v==5"); + + if(unknown > 10) + { + p2v = &value; + } + else + { + p2v = &other; + } + + __CPROVER_assert( + pp2v == &p2v, "pp2v==&p2v"); // success (even though p2v has changed) + __CPROVER_assert( + *p2v == 10, + "*p2v==10"); // unknown since we don't know anymore what p2v points to + __CPROVER_assert(**pp2v == 10, "**pp2v==10"); // unknown + + // Running this through --simplify will yield: + // yp = &x + int x = 4; + int *xp = &x; + int *yp = xp; + + int **ypp = &yp; + **ypp = *yp; + + int array[4] = {0, 1, 2, 3}; + + __CPROVER_assert(array[0] == 0, "array[0] == 0"); // Success + __CPROVER_assert(array[3] == 3, "array[3] == 3"); // Success + + if(unknown > 10) + { + array[0] = 4; + array[1] = 1; + array[2] = 5; + } + else + { + array[0] = 4; + array[2] = 10; + } + + __CPROVER_assert(array[0] == 4, "array[0] == 4"); // Success + __CPROVER_assert(array[1] == 1, "array[1] == 1"); // Success + __CPROVER_assert(array[2] == 5, "array[2] == 5"); // Unknown + __CPROVER_assert(array[3] == 3, "array[3] == 3"); // Success + + typedef struct + { + int a; + int b; + } struct_t; + + struct_t s; + s.a = 1; + + __CPROVER_assert(s.a == 1, "s.a == 1"); + __CPROVER_assert(s.a == 2, "s.a == 2"); +} diff --git a/regression/goto-analyzer/intervals_01/main.c b/regression/goto-analyzer/intervals_01/main.c index aeb37b99e07..6481a350c00 100644 --- a/regression/goto-analyzer/intervals_01/main.c +++ b/regression/goto-analyzer/intervals_01/main.c @@ -4,26 +4,26 @@ int main() int i, j=20; if(i>=20) - assert(i>=10); + __CPROVER_assert(i >= 10, "i>=10"); if(i>=10 && i<=20) - assert(i!=30); + __CPROVER_assert(i != 30, "i!=30"); if(i>=10 && i<=20) - assert(i!=15); // fails + __CPROVER_assert(i != 15, "i!=15"); // fails if(i<1 && i>10) - assert(0); + __CPROVER_assert(0, "0"); if(i>=10 && j>=i) - assert(j>=10); + __CPROVER_assert(j >= 10, "j>=10"); if(i>=j) - assert(i>=j); // fails + __CPROVER_assert(i >= j, "i>=j"); // fails if(i>10) - assert(i>=11); + __CPROVER_assert(i >= 11, "i>=11"); if(i<=100 && j=\s*10: SUCCESS$ -^\[main.assertion.2\] line 10 assertion i\s*!=\s*30: SUCCESS$ -^\[main.assertion.3\] line 13 assertion i\s*!=\s*15: UNKNOWN$ -^\[main.assertion.4\] line 16 assertion 0: SUCCESS \(unreachable\)$ -^\[main.assertion.5\] line 19 assertion j\s*>=\s*10: SUCCESS$ -^\[main.assertion.6\] line 22 assertion i\s*>=\s*j: UNKNOWN$ -^\[main.assertion.7\] line 25 assertion i\s*>=\s*11: SUCCESS$ -^\[main.assertion.8]\ line 28 assertion j\s*<\s*100: SUCCESS$ +^\[main.assertion.1\] .* i\s*>=\s*10: SUCCESS$ +^\[main.assertion.2\] .* i\s*!=\s*30: SUCCESS$ +^\[main.assertion.3\] .* i\s*!=\s*15: UNKNOWN$ +^\[main.assertion.4\] .* 0: SUCCESS \(unreachable\)$ +^\[main.assertion.5\] .* j\s*>=\s*10: SUCCESS$ +^\[main.assertion.6\] .* i\s*>=\s*j: UNKNOWN$ +^\[main.assertion.7\] .* i\s*>=\s*11: SUCCESS$ +^\[main.assertion.8]\ .* j\s*<\s*100: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_02/main.c b/regression/goto-analyzer/intervals_02/main.c index 00659353325..3887eac5234 100644 --- a/regression/goto-analyzer/intervals_02/main.c +++ b/regression/goto-analyzer/intervals_02/main.c @@ -2,7 +2,7 @@ int main(){ int x; if (x > 0 && x < 20) { - assert(x > -10 && x < 100); + __CPROVER_assert(x > -10 && x < 100, "x > -10 && x < 100"); } return 0; } diff --git a/regression/goto-analyzer/intervals_02/test.desc b/regression/goto-analyzer/intervals_02/test.desc index e518380684b..480d556bd35 100644 --- a/regression/goto-analyzer/intervals_02/test.desc +++ b/regression/goto-analyzer/intervals_02/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 5 assertion x > -10 \&\& x < 100: SUCCESS$ +^\[main.assertion.1\] line 5 x > -10 \&\& x < 100: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_03/main.c b/regression/goto-analyzer/intervals_03/main.c index de8c1eead32..816d67172dc 100644 --- a/regression/goto-analyzer/intervals_03/main.c +++ b/regression/goto-analyzer/intervals_03/main.c @@ -3,7 +3,7 @@ int main(){ int x; if (x > 0) { if (x < 20) { - assert(x > -10 || x < 100); + __CPROVER_assert(x > -10 || x < 100, "x > -10 || x < 100"); } } return 0; diff --git a/regression/goto-analyzer/intervals_03/test.desc b/regression/goto-analyzer/intervals_03/test.desc index 00726fdd6f0..f5fc98f9f56 100644 --- a/regression/goto-analyzer/intervals_03/test.desc +++ b/regression/goto-analyzer/intervals_03/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 6 assertion x > -10 \|\| x < 100: SUCCESS$ +^\[main.assertion.1\] line 6 x > -10 \|\| x < 100: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_04/main.c b/regression/goto-analyzer/intervals_04/main.c index 1ca11e32143..af96021bfb5 100644 --- a/regression/goto-analyzer/intervals_04/main.c +++ b/regression/goto-analyzer/intervals_04/main.c @@ -5,7 +5,7 @@ int main() if(i>0) if(i<3) - assert(i>=1 && i<=2); + __CPROVER_assert(i >= 1 && i <= 2, "i>=1 && i<=2"); return 0; } diff --git a/regression/goto-analyzer/intervals_04/test.desc b/regression/goto-analyzer/intervals_04/test.desc index f34e816cf39..b612277f1bd 100644 --- a/regression/goto-analyzer/intervals_04/test.desc +++ b/regression/goto-analyzer/intervals_04/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 8 assertion i >= 1 && i <= 2: SUCCESS$ +^\[main.assertion.1\] line 8 i>=1 && i<=2: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_05/main.c b/regression/goto-analyzer/intervals_05/main.c index 2cd79130dc9..9bd6851b160 100644 --- a/regression/goto-analyzer/intervals_05/main.c +++ b/regression/goto-analyzer/intervals_05/main.c @@ -5,7 +5,7 @@ int main() if(i>0) if(i<3) - assert(i>=1 || i<=2); + __CPROVER_assert(i >= 1 || i <= 2, "i>=1 || i<=2"); return 0; } diff --git a/regression/goto-analyzer/intervals_05/test.desc b/regression/goto-analyzer/intervals_05/test.desc index 0d3af8f5307..d75c1c9243e 100644 --- a/regression/goto-analyzer/intervals_05/test.desc +++ b/regression/goto-analyzer/intervals_05/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 8 assertion i >= 1 \|\| i <= 2: SUCCESS$ +^\[main.assertion.1\] line 8 i>=1 \|\| i<=2: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_06/main.c b/regression/goto-analyzer/intervals_06/main.c index 0e8a1f37c13..aa55440a7eb 100644 --- a/regression/goto-analyzer/intervals_06/main.c +++ b/regression/goto-analyzer/intervals_06/main.c @@ -3,7 +3,7 @@ int main(){ int x; if (x > 0) { if (x < 20) { - assert(x < -10 || x > 100); + __CPROVER_assert(x < -10 || x > 100, "x < -10 || x > 100"); } } return 0; diff --git a/regression/goto-analyzer/intervals_07/main.c b/regression/goto-analyzer/intervals_07/main.c index 75da9413b97..d4c5a079e53 100644 --- a/regression/goto-analyzer/intervals_07/main.c +++ b/regression/goto-analyzer/intervals_07/main.c @@ -3,7 +3,7 @@ int main(){ int x; if (x > 0) { if (x < 20) { - assert(x < -10 && x > 100); + __CPROVER_assert(x < -10 && x > 100, "x < -10 && x > 100"); } } return 0; diff --git a/regression/goto-analyzer/intervals_08/main.c b/regression/goto-analyzer/intervals_08/main.c index 3bcb7fe69c7..5dfe4a86efc 100644 --- a/regression/goto-analyzer/intervals_08/main.c +++ b/regression/goto-analyzer/intervals_08/main.c @@ -2,7 +2,7 @@ int main(){ int x; if (x > 0 && x < 20) { - assert(x < -10 && x < 100); + __CPROVER_assert(x < -10 && x < 100, "x < -10 && x < 100"); } return 0; } diff --git a/regression/goto-analyzer/intervals_09/main.c b/regression/goto-analyzer/intervals_09/main.c index 73b8e73dc85..af96021bfb5 100644 --- a/regression/goto-analyzer/intervals_09/main.c +++ b/regression/goto-analyzer/intervals_09/main.c @@ -2,10 +2,10 @@ int main() { int i; - + if(i>0) if(i<3) - assert(i>=1 && i<=2); - + __CPROVER_assert(i >= 1 && i <= 2, "i>=1 && i<=2"); + return 0; -} +} diff --git a/regression/goto-analyzer/intervals_09/test.desc b/regression/goto-analyzer/intervals_09/test.desc index d9a544fef3b..60d294f9c2c 100644 --- a/regression/goto-analyzer/intervals_09/test.desc +++ b/regression/goto-analyzer/intervals_09/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 8 assertion i >= 1 \&\& i <= 2: SUCCESS$ +^\[main.assertion.1\] line 8 i>=1 \&\& i<=2: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_10/main.c b/regression/goto-analyzer/intervals_10/main.c index b245b3f5b7c..b2a014bda2e 100644 --- a/regression/goto-analyzer/intervals_10/main.c +++ b/regression/goto-analyzer/intervals_10/main.c @@ -4,17 +4,17 @@ int main() int i, j; if(i<=100 && j100); // fails + __CPROVER_assert(j > 100, "j>100"); // fails if(i<=100 && j=-1 && x[i]<=1); x_aux[i]=0; y_aux[i]=0; @@ -27,12 +27,14 @@ int main() { /* Num, x values */ for (j = 0; j < Blen; j++) { y[i] = y[i] + B[j]*x_aux[j]; - assert(y[i]>=-1.0f && y[i]<=1.0f); //success + __CPROVER_assert( + y[i] >= -1.0f && y[i] <= 1.0f, "y[i]>=-1.0f && y[i]<=1.0f"); //success } /* Den, y values */ for(j=0;j=-1.0f && y[i]<=1.0f); //fails + __CPROVER_assert( + y[i] >= -1.0f && y[i] <= 1.0f, "y[i]>=-1.0f && y[i]<=1.0f"); //fails } /* Update past y values */ for(j=Alen-2;j>=1;j--) diff --git a/regression/goto-analyzer/intervals_12/main.c b/regression/goto-analyzer/intervals_12/main.c index 506e118f898..c5ff1c34061 100644 --- a/regression/goto-analyzer/intervals_12/main.c +++ b/regression/goto-analyzer/intervals_12/main.c @@ -4,10 +4,10 @@ int main (void) { int j; if (i <= 0 && j < i) - assert(j < 0); + __CPROVER_assert(j < 0, "j < 0"); if (j < i && i <= 0) - assert(j < 0); + __CPROVER_assert(j < 0, "j < 0"); return 0; } diff --git a/regression/goto-analyzer/intervals_13/main.c b/regression/goto-analyzer/intervals_13/main.c index b631969e8f6..90b191c1dee 100644 --- a/regression/goto-analyzer/intervals_13/main.c +++ b/regression/goto-analyzer/intervals_13/main.c @@ -4,26 +4,26 @@ int main() int i, j=20; if(i>=20) - assert(i>=10); // success + __CPROVER_assert(i >= 10, "i>=10"); // success if(i>=10 && i<=20) - assert(i!=30); // success + __CPROVER_assert(i != 30, "i!=30"); // success if(i>=10 && i<=20) - assert(i!=15); // fails + __CPROVER_assert(i != 15, "i!=15"); // fails if(i<1 && i>10) - assert(0); // success + __CPROVER_assert(0, "0"); // success if(i>=10 && j>=i) - assert(j>=10); // success + __CPROVER_assert(j >= 10, "j>=10"); // success if(i>=j) - assert(i>=j); // unknown + __CPROVER_assert(i >= j, "i>=j"); // unknown if(i>10) - assert(i>=11); // success + __CPROVER_assert(i >= 11, "i>=11"); // success if(i<=100 && j=\s*10: SUCCESS$ -^\[main.assertion.2\] line 10 assertion i\s*!=\s*30: SUCCESS$ -^\[main.assertion.3\] line 13 assertion i\s*!=\s*15: UNKNOWN$ -^\[main.assertion.4\] line 16 assertion 0: SUCCESS \(unreachable\)$ -^\[main.assertion.5\] line 19 assertion j\s*>=\s*10: SUCCESS$ -^\[main.assertion.6\] line 22 assertion i\s*>=\s*j: UNKNOWN$ -^\[main.assertion.7\] line 25 assertion i\s*>=\s*11: SUCCESS$ -^\[main.assertion.8\] line 28 assertion j\s*<\s*100: SUCCESS$ +^\[main.assertion.1\] .* i\s*>=\s*10: SUCCESS$ +^\[main.assertion.2\] .* i\s*!=\s*30: SUCCESS$ +^\[main.assertion.3\] .* i\s*!=\s*15: UNKNOWN$ +^\[main.assertion.4\] .* 0: SUCCESS \(unreachable\)$ +^\[main.assertion.5\] .* j\s*>=\s*10: SUCCESS$ +^\[main.assertion.6\] .* i\s*>=\s*j: UNKNOWN$ +^\[main.assertion.7\] .* i\s*>=\s*11: SUCCESS$ +^\[main.assertion.8\] .* j\s*<\s*100: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_14/main.c b/regression/goto-analyzer/intervals_14/main.c index 7e4d53d5e06..7e0d54b7f4b 100644 --- a/regression/goto-analyzer/intervals_14/main.c +++ b/regression/goto-analyzer/intervals_14/main.c @@ -3,13 +3,13 @@ int main() { int i=0, j=2; - while (i<=50) + while(i <= 50) { i++; j++; } - assert(i<50); - assert(i<51); - assert(i<52); + __CPROVER_assert(i < 50, "i<50"); + __CPROVER_assert(i < 51, "i<51"); + __CPROVER_assert(i < 52, "i<52"); } diff --git a/regression/goto-analyzer/intervals_15/main.c b/regression/goto-analyzer/intervals_15/main.c index 52ef248b772..1a74d4d2afc 100644 --- a/regression/goto-analyzer/intervals_15/main.c +++ b/regression/goto-analyzer/intervals_15/main.c @@ -3,11 +3,11 @@ int main() { int i=0, j=2; - while (i<=50) + while(i <= 50) { i++; j++; } - assert(j<52); + __CPROVER_assert(j < 52, "j<52"); } diff --git a/regression/goto-analyzer/intervals_15/test.desc b/regression/goto-analyzer/intervals_15/test.desc index 8e8c765b39c..d5e0811dfaf 100644 --- a/regression/goto-analyzer/intervals_15/test.desc +++ b/regression/goto-analyzer/intervals_15/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 11 assertion j\s*<\s*52: UNKNOWN$ +^\[main\.assertion\.1\] .* j\s*<\s*52: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_16/main.c b/regression/goto-analyzer/intervals_16/main.c index 52ef248b772..1a74d4d2afc 100644 --- a/regression/goto-analyzer/intervals_16/main.c +++ b/regression/goto-analyzer/intervals_16/main.c @@ -3,11 +3,11 @@ int main() { int i=0, j=2; - while (i<=50) + while(i <= 50) { i++; j++; } - assert(j<52); + __CPROVER_assert(j < 52, "j<52"); } diff --git a/regression/goto-analyzer/intervals_16/test.desc b/regression/goto-analyzer/intervals_16/test.desc index 7c318bbb9a4..5e533a83ad8 100644 --- a/regression/goto-analyzer/intervals_16/test.desc +++ b/regression/goto-analyzer/intervals_16/test.desc @@ -3,6 +3,6 @@ main.c --intervals ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] line 11 assertion j\s*<\s*52: (UNKNOWN|FAILURE \(if reachable\))$ +^\[main\.assertion\.1\] .* j\s*<\s*52: (UNKNOWN|FAILURE \(if reachable\))$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/intervals_17/main.c b/regression/goto-analyzer/intervals_17/main.c new file mode 100644 index 00000000000..1f55b8559a3 --- /dev/null +++ b/regression/goto-analyzer/intervals_17/main.c @@ -0,0 +1,17 @@ + +int main() +{ + int x; + __CPROVER_assume(x >= 0); + __CPROVER_assume(x <= 10); + + int y; + __CPROVER_assume(y >= 5); + __CPROVER_assume(y <= 15); + + __CPROVER_assume(y < x); + + __CPROVER_assert(y <= 10, "y <= 10"); + + return 0; +} diff --git a/regression/goto-analyzer/intervals_17/test.desc b/regression/goto-analyzer/intervals_17/test.desc new file mode 100644 index 00000000000..cecc1cc3321 --- /dev/null +++ b/regression/goto-analyzer/intervals_17/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--intervals --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line 14 y <= 10: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/minimal-reproducer-for-struct-problem/minimal_reproducer_for_struct_problem.c b/regression/goto-analyzer/minimal-reproducer-for-struct-problem/minimal_reproducer_for_struct_problem.c new file mode 100644 index 00000000000..caf67389387 --- /dev/null +++ b/regression/goto-analyzer/minimal-reproducer-for-struct-problem/minimal_reproducer_for_struct_problem.c @@ -0,0 +1,28 @@ +#include + +int main(int argc, char *argv[]) +{ + // Test if we can represent constant structs + struct int_struct + { + int a; + }; + + struct int_struct x = {0}; + x.a = 0; + __CPROVER_assert(x.a == 0, "x.a==0"); + __CPROVER_assert(x.a == 1, "x.a==1"); + + if(argc > 2) + { + __CPROVER_assert(x.a == 0, "x.a==0"); + __CPROVER_assert(x.a == 1, "x.a==1"); + x.a = 1; + __CPROVER_assert(x.a == 0, "x.a==0"); + __CPROVER_assert(x.a == 1, "x.a==1"); + } + __CPROVER_assert(x.a == 0, "x.a==0"); + __CPROVER_assert(x.a == 1, "x.a==1"); + + return 0; +} diff --git a/regression/goto-analyzer/minimal-reproducer-for-struct-problem/test.desc b/regression/goto-analyzer/minimal-reproducer-for-struct-problem/test.desc new file mode 100644 index 00000000000..b8834858e4c --- /dev/null +++ b/regression/goto-analyzer/minimal-reproducer-for-struct-problem/test.desc @@ -0,0 +1,15 @@ +CORE +minimal_reproducer_for_struct_problem.c +--variable-sensitivity --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion.1\] line 13 x.a==0: SUCCESS$ +^\[main.assertion.2\] line 14 x.a==1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] line 18 x.a==0: SUCCESS$ +^\[main.assertion.4\] line 19 x.a==1: FAILURE \(if reachable\)$ +^\[main.assertion.5\] line 21 x.a==0: FAILURE \(if reachable\)$ +^\[main.assertion.6\] line 22 x.a==1: SUCCESS$ +^\[main.assertion.7\] line 24 x.a==0: UNKNOWN$ +^\[main.assertion.8\] line 25 x.a==1: UNKNOWN$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-function-call-array/main.c b/regression/goto-analyzer/sensitivity-function-call-array/main.c new file mode 100644 index 00000000000..e6a99068a53 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-array/main.c @@ -0,0 +1,32 @@ +#include + +int *bar(int *arr_unmodified, int *arr_modified); + +int main(int argc, char *argv[]) +{ + int arr_x[] = {1, 2, 3, 4}; + int arr_y[] = {1, 2, 3, 4}; + int *p2arr = arr_x; + + p2arr = bar(arr_x, arr_y); + assert(*arr_y == 2); + // assert(arr_y[1]==3); + + assert(p2arr == arr_y); + assert(*p2arr == 2); + // assert(p2arr[1]==3); +} + +int *bar(int *arr_unmodified, int *arr_modified) +{ + assert(*arr_unmodified == 1); + // assert(arr_unmodified[1]==2); + + (*arr_modified) += 1; + // arr_modified[1] = 3; + + assert(*arr_modified == 2); + // assert(arr_modified[1]==3); + + return arr_modified; +} diff --git a/regression/goto-analyzer/sensitivity-function-call-array/test.desc b/regression/goto-analyzer/sensitivity-function-call-array/test.desc new file mode 100644 index 00000000000..8b33d4a540b --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-array/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ assertion \*arr_y == 2: SUCCESS$ +^\[main\.assertion\.2\] line \d+ assertion p2arr == arr_y: SUCCESS$ +^\[main\.assertion\.3\] line \d+ assertion \*p2arr == 2: SUCCESS$ +^\[bar\.assertion\.1\] line \d+ assertion \*arr_unmodified == 1: SUCCESS$ +^\[bar\.assertion\.2\] line \d+ assertion \*arr_modified == 2: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-function-call-opaque/main.c b/regression/goto-analyzer/sensitivity-function-call-opaque/main.c new file mode 100644 index 00000000000..f7fb09c85eb --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-opaque/main.c @@ -0,0 +1,24 @@ +#include + +int global_value; + +int opaque(int other, int *side_effect); + +int main(int argc, char *argv[]) +{ + int x = 3; + int y = 4; + + global_value = 4; + + int z = bar(x + 1, &y); + + assert(x == 3); // Success + assert(y == 4); // Unknown - the opaque function could have modified it + assert(z == 0); // Unknown - the opaque function could have returned anything + assert( + global_value == + 4); // Unknown - the opaque function could have modified this + + return 0; +} diff --git a/regression/goto-analyzer/sensitivity-function-call-opaque/test.desc b/regression/goto-analyzer/sensitivity-function-call-opaque/test.desc new file mode 100644 index 00000000000..e2a521902af --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-opaque/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ assertion x == 3: SUCCESS$ +^\[main\.assertion\.2\] line \d+ assertion y == 4: UNKNOWN$ +^\[main\.assertion\.3\] line \d+ assertion z == 0: UNKNOWN$ +^\[main\.assertion\.4\] line \d+ assertion global_value == 4: UNKNOWN$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-function-call-pointer/main.c b/regression/goto-analyzer/sensitivity-function-call-pointer/main.c new file mode 100644 index 00000000000..8b58f431931 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-pointer/main.c @@ -0,0 +1,25 @@ +#include + +int *bar(int *unmodified, int *modifed); + +int main(int argc, char *argv[]) +{ + int x = 3; + int y = 4; + int *p2x = &x; + p2x = bar(&x, &y); + assert(y == 5); + assert(p2x == &y); + assert(*p2x == 5); +} + +int *bar(int *unmodified, int *modifed) +{ + assert(*unmodified == 3); + + (*modifed) += 1; + + assert(*modifed == 5); + + return modifed; +} diff --git a/regression/goto-analyzer/sensitivity-function-call-pointer/test.desc b/regression/goto-analyzer/sensitivity-function-call-pointer/test.desc new file mode 100644 index 00000000000..d067eb420fe --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-pointer/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ assertion y == 5: SUCCESS$ +^\[main\.assertion\.2\] line \d+ assertion p2x == &y: SUCCESS$ +^\[main\.assertion\.3\] line \d+ assertion \*p2x == 5: SUCCESS$ +^\[bar\.assertion\.1\] line \d+ assertion \*unmodified == 3: SUCCESS$ +^\[bar\.assertion\.2\] line \d+ assertion \*modifed == 5: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-function-call-primitive/main.c b/regression/goto-analyzer/sensitivity-function-call-primitive/main.c new file mode 100644 index 00000000000..1c7a50e0983 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-primitive/main.c @@ -0,0 +1,14 @@ +#include + +int bar(int other) +{ + assert(other == 4); + return other + 1; +} + +int main(int argc, char *argv[]) +{ + int x = 3; + int y = bar(x + 1); + assert(y == 5); +} diff --git a/regression/goto-analyzer/sensitivity-function-call-primitive/test.desc b/regression/goto-analyzer/sensitivity-function-call-primitive/test.desc new file mode 100644 index 00000000000..39579c64cea --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-primitive/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[bar\.assertion\.1\] line \d+ assertion other == 4: SUCCESS$ +^\[main\.assertion\.1\] line \d+ assertion y == 5: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-function-call-recursive/test.desc b/regression/goto-analyzer/sensitivity-function-call-recursive/test.desc index 6303fef7078..8c4ef982cec 100644 --- a/regression/goto-analyzer/sensitivity-function-call-recursive/test.desc +++ b/regression/goto-analyzer/sensitivity-function-call-recursive/test.desc @@ -1,6 +1,6 @@ -FUTURE +CORE main.c ---variable --pointers --arrays --structs --verify +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify ^EXIT=0$ ^SIGNAL=0$ ^\[main\.assertion\.1\] .* assertion y==4: UNKNOWN$ diff --git a/regression/goto-analyzer/sensitivity-function-call-varargs/main.c b/regression/goto-analyzer/sensitivity-function-call-varargs/main.c new file mode 100644 index 00000000000..920ff4ab43d --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-varargs/main.c @@ -0,0 +1,26 @@ +#include +#include + +int bar(int size, ...) +{ + int total = 0; + va_list args; + va_start(args, size); + for(int i = 0; i < size; ++i) + { + int val = va_arg(args, int); + total += val; + } + + va_end(args); + return total; +} + +int main(int argc, char *argv[]) +{ + int y = bar(4, 1, 2, 2, 1); + assert(y == 6); + + int z = bar(0); + assert(z == 0); +} diff --git a/regression/goto-analyzer/sensitivity-function-call-varargs/test.desc b/regression/goto-analyzer/sensitivity-function-call-varargs/test.desc new file mode 100644 index 00000000000..d7caf6fb09c --- /dev/null +++ b/regression/goto-analyzer/sensitivity-function-call-varargs/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ assertion y == 6: UNKNOWN$ +^\[main\.assertion\.2\] line \d+ assertion z == 0: UNKNOWN$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-arrays/sensitivity_dependency_arrays.c b/regression/goto-analyzer/sensitivity-last-written-locations-arrays/sensitivity_dependency_arrays.c new file mode 100644 index 00000000000..8c28a4a1b2f --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-arrays/sensitivity_dependency_arrays.c @@ -0,0 +1,50 @@ +void do_arrays() +{ + int bool_; + int bool_1; + int bool_2; + + int x[2]; + + // Simple variables. + x[0] = 10; + x[1] = 20; + x[0] = 30; + x[1] = 40; + x[1] = x[0]; + x[0] = x[1]; + x[0] = 5; + x[0] = x[0] + 10; + x[1] = 10; + + if(bool_) + { + x[0] = 20; + } + else + { + x[0] = 20; + } + + x[0] = 0; + + if(bool_1) + { + x[0] = 3; + if(bool_2) + { + x[0] = 5; + } + } + else + { + x[0] = 7; + } + x[1] = 10; + x[0] = 20; +} + +int main() +{ + do_arrays(); +} diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-arrays/test.desc b/regression/goto-analyzer/sensitivity-last-written-locations-arrays/test.desc new file mode 100644 index 00000000000..49bf22bca7e --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-arrays/test.desc @@ -0,0 +1,38 @@ +CORE +sensitivity_dependency_arrays.c +--variable-sensitivity --vsd-arrays --vsd-pointers --vsd-structs --show +// Enable multi-line checking +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +main#return_value \(\) -> TOP @ \[1\] +__CPROVER_dead_object \(\) -> TOP @ \[5\] +__CPROVER_deallocated \(\) -> TOP @ \[6\] +__CPROVER_malloc_is_new_array \(\) -> FALSE @ \[9\] +__CPROVER_malloc_object \(\) -> TOP @ \[10\] +__CPROVER_malloc_size \(\) -> 0ull? @ \[11\] +__CPROVER_memory_leak \(\) -> TOP @ \[13\] +__CPROVER_next_thread_key \(\) -> 0ul @ \[15\] +__CPROVER_pipe_count \(\) -> 0u @ \[16\] +__CPROVER_rounding_mode \(\) -> 0 @ \[17\] +__CPROVER_thread_id \(\) -> 0ul @ \[18\] +__CPROVER_threads_exited \(\) -> TOP @ \[21\] +do_arrays::1::bool_ \(\) -> TOP @ \[23\] +do_arrays::1::bool_1 \(\) -> TOP @ \[24\] +do_arrays::1::bool_2 \(\) -> TOP @ \[25\] +do_arrays::1::x \(\) -> \{\[0\] = 10 @ \[27\]\n\} @ \[27\] +do_arrays::1::x \(\) -> \{\[0\] = 10 @ \[27\]\n\[1\] = 20 @ \[28\]\n\} @ \[28\] +do_arrays::1::x \(\) -> \{\[0\] = 30 @ \[29\]\n\[1\] = 20 @ \[28\]\n\} @ \[29\] +do_arrays::1::x \(\) -> \{\[0\] = 30 @ \[29\]\n\[1\] = 40 @ \[30\]\n\} @ \[30\] +do_arrays::1::x \(\) -> \{\[0\] = 30 @ \[29\]\n\[1\] = 30 @ \[31\]\n\} @ \[31\] +do_arrays::1::x \(\) -> \{\[0\] = 30 @ \[32\]\n\[1\] = 30 @ \[31\]\n\} @ \[32\] +do_arrays::1::x \(\) -> \{\[0\] = 5 @ \[33\]\n\[1\] = 30 @ \[31\]\n\} @ \[33\] +do_arrays::1::x \(\) -> \{\[0\] = 15 @ \[34\]\n\[1\] = 30 @ \[31\]\n\} @ \[34\] +do_arrays::1::x \(\) -> \{\[0\] = 15 @ \[34\]\n\[1\] = 10 @ \[35\]\n\} @ \[35\] +do_arrays::1::x \(\) -> \{\[0\] = 20 @ \[37\]\n\[1\] = 10 @ \[35\]\n\} @ \[37\] +do_arrays::1::x \(\) -> \{\[0\] = 20 @ \[37\, 39\]\n\[1\] = 10 @ \[35\]\n\} @ \[37\, 39\] +do_arrays::1::x \(\) -> \{\[0\] = 0 @ \[41]\n\[1\] = 10 @ \[35\]\n\} @ \[41\] +do_arrays::1::x \(\) -> \{\[0\] = 3 @ \[43]\n\[1\] = 10 @ \[35\]\n\} @ \[43\] +do_arrays::1::x \(\) -> \{\[0\] = TOP @ \[43\, 45]\n\[1\] = 10 @ \[35\]\n\} @ \[43\, 45\] +do_arrays::1::x \(\) -> \{\[0\] = TOP @ \[43\, 45\, 48]\n\[1\] = 10 @ \[50\]\n\} @ \[50\] +do_arrays::1::x \(\) -> \{\[0\] = 20 @ \[51]\n\[1\] = 10 @ \[50\]\n\} @ \[51\] diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-pointers/sensitivity_dependency_pointers.c b/regression/goto-analyzer/sensitivity-last-written-locations-pointers/sensitivity_dependency_pointers.c new file mode 100644 index 00000000000..84e1366830f --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-pointers/sensitivity_dependency_pointers.c @@ -0,0 +1,30 @@ +void do_pointers() +{ + int bool_; + int bool_1; + int bool_2; + + int x = 10; + int *x_p; + + int y = 20; + int *y_p; + + x_p = &x; + + *x_p = 30; + x = 40; + + *x_p = *y_p; + x = 50; + y_p = &x; + + *x_p = 60; + + int j = *y_p; +} + +int main() +{ + do_pointers(); +} diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-pointers/test.desc b/regression/goto-analyzer/sensitivity-last-written-locations-pointers/test.desc new file mode 100644 index 00000000000..e5b107c0156 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-pointers/test.desc @@ -0,0 +1,37 @@ +CORE +sensitivity_dependency_pointers.c +--variable-sensitivity --vsd-arrays --vsd-pointers --vsd-structs --show +// Enable multi-line checking +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +main#return_value \(\) -> TOP @ \[1\] +__CPROVER_dead_object \(\) -> TOP @ \[5\] +__CPROVER_deallocated \(\) -> TOP @ \[6\] +__CPROVER_malloc_is_new_array \(\) -> FALSE @ \[9\] +__CPROVER_malloc_object \(\) -> TOP @ \[10\] +__CPROVER_malloc_size \(\) -> 0ull? @ \[11\] +__CPROVER_memory_leak \(\) -> TOP @ \[13\] +__CPROVER_next_thread_id \(\) -> 0ul @ \[14\] +__CPROVER_pipe_count \(\) -> 0u @ \[16\] +__CPROVER_rounding_mode \(\) -> 0 @ \[17\] +__CPROVER_thread_id \(\) -> 0ul @ \[18\] +__CPROVER_threads_exited \(\) -> TOP @ \[21\] +do_pointers::1::bool_ \(\) -> TOP @ \[23\] +do_pointers::1::bool_1 \(\) -> TOP @ \[24\] +do_pointers::1::bool_2 \(\) -> TOP @ \[25\] +do_pointers::1::x \(\) -> TOP @ \[26\] +do_pointers::1::x \(\) -> 10 @ \[27\] +do_pointers::1::x_p \(\) -> TOP @ \[28\] +do_pointers::1::y \(\) -> TOP @ \[29\] +do_pointers::1::y \(\) -> 20 @ \[30\] +do_pointers::1::y_p \(\) -> TOP @ \[31\] +do_pointers::1::x_p \(\) -> ptr ->\(do_pointers::1::x\) @ \[32\] +do_pointers::1::x \(\) -> 30 @ \[33\] +do_pointers::1::x \(\) -> 40 @ \[34\] +do_pointers::1::x \(\) -> TOP @ \[35\] +do_pointers::1::x \(\) -> 50 @ \[36\] +do_pointers::1::y_p \(\) -> ptr ->\(do_pointers::1::x\) @ \[37\] +do_pointers::1::x \(\) -> 60 @ \[38\] +do_pointers::1::j \(\) -> TOP @ \[39\] +do_pointers::1::j \(\) -> 60 @ \[40\] diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-structs/sensitivity_dependency_structs.c b/regression/goto-analyzer/sensitivity-last-written-locations-structs/sensitivity_dependency_structs.c new file mode 100644 index 00000000000..5bfc8ce6eea --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-structs/sensitivity_dependency_structs.c @@ -0,0 +1,60 @@ +struct Ages +{ + int x; + int y; +}; + +void do_structs() +{ + int bool_; + int bool_1; + int bool_2; + + struct Ages st; + + // Simple variables. + st.x = 10; + st.y = 20; + st.x = 30; + st.y = 40; + st.y = st.x; + st.x = st.y; + st.x = 5; + st.x = st.x + 10; + st.y = 10; + + if(bool_) + { + st.x = 20; + } + else + { + st.x = 20; + } + + st.x = 0; + + if(bool_1) + { + st.x = 3; + if(bool_2) + { + st.x = 5; + } + } + else + { + st.x = 7; + } + st.y = 10; + st.x = 20; + + struct Ages new_age; + + new_age = st; +} + +int main() +{ + do_structs(); +} diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-structs/test.desc b/regression/goto-analyzer/sensitivity-last-written-locations-structs/test.desc new file mode 100644 index 00000000000..e575ef34cc1 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-structs/test.desc @@ -0,0 +1,43 @@ +CORE +sensitivity_dependency_structs.c +--variable-sensitivity --vsd-arrays --vsd-pointers --vsd-structs --show +// Enable multi-line checking +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +main#return_value \(\) -> TOP @ \[1\] +__CPROVER_dead_object \(\) -> TOP @ \[5\] +__CPROVER_deallocated \(\) -> TOP @ \[6\] +__CPROVER_malloc_is_new_array \(\) -> FALSE @ \[9\] +__CPROVER_malloc_object \(\) -> TOP @ \[10\] +__CPROVER_malloc_size \(\) -> 0ull? @ \[11\] +__CPROVER_memory_leak \(\) -> TOP @ \[13\] +__CPROVER_next_thread_id \(\) -> 0ul @ \[14\] +__CPROVER_pipe_count \(\) -> 0u @ \[16\] +__CPROVER_rounding_mode \(\) -> 0 @ \[17\] +__CPROVER_thread_id \(\) -> 0ul @ \[18\] +__CPROVER_threads_exited \(\) -> TOP @ \[21\] +do_structs::1::bool_ \(\) -> TOP @ \[23\] +do_structs::1::bool_1 \(\) -> TOP @ \[24\] +do_structs::1::bool_2 \(\) -> TOP @ \[25\] +do_structs::1::st \(\) -> \{\} @ \[26\] +do_structs::1::st \(\) -> \{.x=10 @ \[27\]\} @ \[27\] +do_structs::1::st \(\) -> \{.x=10 @ \[27\]\, .y=20 @ \[28\]\} @ \[28\] +do_structs::1::st \(\) -> \{.x=30 @ \[29\]\, .y=20 @ \[28\]\} @ \[29\] +do_structs::1::st \(\) -> \{.x=30 @ \[29\]\, .y=40 @ \[30\]\} @ \[30\] +do_structs::1::st \(\) -> \{.x=30 @ \[29\]\, .y=30 @ \[31\]\} @ \[31\] +do_structs::1::st \(\) -> \{.x=30 @ \[32\]\, .y=30 @ \[31\]\} @ \[32\] +do_structs::1::st \(\) -> \{.x=5 @ \[33\]\, .y=30 @ \[31\]\} @ \[33\] +do_structs::1::st \(\) -> \{.x=15 @ \[34\]\, .y=30 @ \[31\]\} @ \[34\] +do_structs::1::st \(\) -> \{.x=15 @ \[34\]\, .y=10 @ \[35\]\} @ \[35\] +do_structs::1::st \(\) -> \{.x=20 @ \[37\]\, .y=10 @ \[35\]\} @ \[37\] +do_structs::1::st \(\) -> \{.x=20 @ \[37\, 39\]\, .y=10 @ \[35\]\} @ \[37\, 39\] +do_structs::1::st \(\) -> \{.x=0 @ \[41\]\, .y=10 @ \[35\]\} @ \[41\] +do_structs::1::st \(\) -> \{.x=3 @ \[43\]\, .y=10 @ \[35\]\} @ \[43\] +do_structs::1::st \(\) -> \{.x=TOP @ \[43\, 45\]\, .y=10 @ \[35\]\} @ \[43\, 45\] +do_structs::1::st \(\) -> \{.x=TOP @ \[43\, 45\, 48\]\, .y=10 @ \[35\]\} @ \[43\, 45\, 48\] +do_structs::1::st \(\) -> \{.x=TOP @ \[43\, 45\, 48\]\, .y=10 @ \[50\]\} @ \[50\] +do_structs::1::st \(\) -> \{.x=20 @ \[51\]\, .y=10 @ \[50\]\} @ \[51\] +do_structs::1::new_age \(\) -> \{\} @ \[52\] +do_structs::1::new_age \(\) -> \{.x=20 @ \[53\]\, .y=10 @ \[53\]\} @ \[53\] +-- diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-variables/sensitivity_dependency_variables.c b/regression/goto-analyzer/sensitivity-last-written-locations-variables/sensitivity_dependency_variables.c new file mode 100644 index 00000000000..1860fcfee45 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-variables/sensitivity_dependency_variables.c @@ -0,0 +1,63 @@ +int global_x; // Should assign to 0. + +void do_variables() +{ + int bool_; + int bool_1; + int bool_2; + + global_x = 5; + + // Simple variables. + int x = 10; + int y = 20; + x = 30; + y = 40; + y = x; + x = y; + x = 5; + x = x + 10; + y = 10; + + if(bool_) + { + x = 20; + } + else + { + x = 20; + } + + x = 50; + + if(bool_) + { + x = 20; + } + else + { + x = 30; + } + + x = 0; + + if(bool_1) + { + x = 3; + if(bool_2) + { + x = 5; + } + } + else + { + x = 7; + } + y = 10; + x = 20; +} + +int main() +{ + do_variables(); +} diff --git a/regression/goto-analyzer/sensitivity-last-written-locations-variables/test.desc b/regression/goto-analyzer/sensitivity-last-written-locations-variables/test.desc new file mode 100644 index 00000000000..498891f5696 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-last-written-locations-variables/test.desc @@ -0,0 +1,49 @@ +CORE +sensitivity_dependency_variables.c +--variable-sensitivity --vsd-arrays --vsd-pointers --vsd-structs --show +^EXIT=0$ +^SIGNAL=0$ +main#return_value \(\) -> TOP @ \[1\] +__CPROVER_alloca_object \(\) -> TOP @ \[4\] +__CPROVER_dead_object \(\) -> TOP @ \[5\] +__CPROVER_deallocated \(\) -> TOP @ \[6\] +__CPROVER_malloc_is_new_array \(\) -> FALSE @ \[9\] +__CPROVER_malloc_object \(\) -> TOP @ \[10\] +__CPROVER_malloc_size \(\) -> 0ull? @ \[11\] +__CPROVER_memory_leak \(\) -> TOP @ \[13\] +__CPROVER_next_thread_id \(\) -> 0ul @ \[14\] +__CPROVER_next_thread_key \(\) -> 0ul @ \[15\] +__CPROVER_pipe_count \(\) -> 0u @ \[16\] +__CPROVER_rounding_mode \(\) -> 0 @ \[17\] +__CPROVER_thread_id \(\) -> 0ul @ \[18\] +__CPROVER_thread_key_dtors \(\) -> TOP @ \[19\] +__CPROVER_thread_keys \(\) -> TOP @ \[20\] +__CPROVER_threads_exited \(\) -> TOP @ \[21\] +global_x \(\) -> 0 @ \[22\] +do_variables::1::bool_ \(\) -> TOP @ \[24\] +do_variables::1::bool_1 \(\) -> TOP @ \[25\] +do_variables::1::bool_2 \(\) -> TOP @ \[26\] +global_x \(\) -> 5 @ \[27\] +do_variables::1::x \(\) -> TOP @ \[28\] +do_variables::1::x \(\) -> 10 @ \[29\] +do_variables::1::y \(\) -> TOP @ \[30\] +do_variables::1::y \(\) -> 20 @ \[31\] +do_variables::1::x \(\) -> 30 @ \[32\] +do_variables::1::y \(\) -> 40 @ \[33\] +do_variables::1::y \(\) -> 30 @ \[34\] +do_variables::1::x \(\) -> 30 @ \[35\] +do_variables::1::x \(\) -> 5 @ \[36\] +do_variables::1::x \(\) -> 15 @ \[37\] +do_variables::1::y \(\) -> 10 @ \[38\] +do_variables::1::x \(\) -> 20 @ \[40\] +do_variables::1::x \(\) -> 20 @ \[40\, 42\] +do_variables::1::x \(\) -> 50 @ \[44\] +do_variables::1::x \(\) -> 20 @ \[46\] +do_variables::1::x \(\) -> TOP @ \[46\, 48\] +do_variables::1::x \(\) -> 0 @ \[50\] +do_variables::1::x \(\) -> 3 @ \[52\] +do_variables::1::x \(\) -> TOP @ \[52\, 54\] +do_variables::1::x \(\) -> TOP @ \[52\, 54\, 57\] +do_variables::1::y \(\) -> 10 @ \[59\] +do_variables::1::x \(\) -> 20 @ \[60\] +-- diff --git a/regression/goto-analyzer/sensitivity-test-common-files/array_of_array_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/array_of_array_sensitivity_tests.c index 63af187ade2..d5ca7bccd16 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/array_of_array_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/array_of_array_sensitivity_tests.c @@ -8,34 +8,34 @@ int main(int argc, char *argv[]) int b[3][3]={{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; // Test if we can represent uniform constant arrays - assert(a[1][2]==0); - assert(a[1][2]==1); + __CPROVER_assert(a[1][2] == 0, "a[1][2]==0"); + __CPROVER_assert(a[1][2] == 1, "a[1][2]==1"); // Test if we can represent constant arrays which aren't uniform - assert(b[1][2]==5); - assert(b[1][2]==0); + __CPROVER_assert(b[1][2] == 5, "b[1][2]==5"); + __CPROVER_assert(b[1][2] == 0, "b[1][2]==0"); // Test alternative syntax for accessing an array value - assert(*(b[1]+2)==5); - assert(*(b[1]+2)==0); - assert((*(b+1))[2]==5); - assert((*(b+1))[2]==0); - assert(*(*(b+1)+2)==5); - assert(*(*(b+1)+2)==0); - assert(1[b][2]==5); - assert(1[b][2]==0); - assert(*(1[b]+2)==5); - assert(*(1[b]+2)==0); - assert((*(1+b))[2]==5); - assert((*(1+b))[2]==0); - assert(*(*(1+b)+2)==5); - assert(*(*(1+b)+2)==0); - assert(2[1[b]]==5); - assert(2[1[b]]==0); - assert(*(2+1[b])==5); - assert(*(2+1[b])==0); - assert(*(2+*(1+b))==5); - assert(*(2+*(1+b))==0); + __CPROVER_assert(*(b[1] + 2) == 5, "*(b[1]+2)==5"); + __CPROVER_assert(*(b[1] + 2) == 0, "*(b[1]+2)==0"); + __CPROVER_assert((*(b + 1))[2] == 5, "(*(b+1))[2]==5"); + __CPROVER_assert((*(b + 1))[2] == 0, "(*(b+1))[2]==0"); + __CPROVER_assert(*(*(b + 1) + 2) == 5, "*(*(b+1)+2)==5"); + __CPROVER_assert(*(*(b + 1) + 2) == 0, "*(*(b+1)+2)==0"); + __CPROVER_assert(1 [b][2] == 5, "1[b][2]==5"); + __CPROVER_assert(1 [b][2] == 0, "1[b][2]==0"); + __CPROVER_assert(*(1 [b] + 2) == 5, "*(1[b]+2)==5"); + __CPROVER_assert(*(1 [b] + 2) == 0, "*(1[b]+2)==0"); + __CPROVER_assert((*(1 + b))[2] == 5, "(*(1+b))[2]==5"); + __CPROVER_assert((*(1 + b))[2] == 0, "(*(1+b))[2]==0"); + __CPROVER_assert(*(*(1 + b) + 2) == 5, "*(*(1+b)+2)==5"); + __CPROVER_assert(*(*(1 + b) + 2) == 0, "*(*(1+b)+2)==0"); + __CPROVER_assert(2 [1 [b]] == 5, "2[1[b]]==5"); + __CPROVER_assert(2 [1 [b]] == 0, "2[1[b]]==0"); + __CPROVER_assert(*(2 + 1 [b]) == 5, "*(2+1[b])==5"); + __CPROVER_assert(*(2 + 1 [b]) == 0, "*(2+1[b])==0"); + __CPROVER_assert(*(2 + *(1 + b)) == 5, "*(2+*(1+b))==5"); + __CPROVER_assert(*(2 + *(1 + b)) == 0, "*(2+*(1+b))==0"); // Test how well we can deal with merging for an array value when there is one // possible value @@ -43,9 +43,9 @@ int main(int argc, char *argv[]) { a[0][1]=0; } - assert(a[0][1]==0); - assert(a[0][1]==1); - assert(a[0][2]==0); + __CPROVER_assert(a[0][1] == 0, "a[0][1]==0"); + __CPROVER_assert(a[0][1] == 1, "a[0][1]==1"); + __CPROVER_assert(a[0][2] == 0, "a[0][2]==0"); // Test how well we can deal with merging for an array value when there are // two possible values @@ -53,9 +53,9 @@ int main(int argc, char *argv[]) { b[0][1]=2; } - assert(b[0][1]==2); - assert(b[0][1]==3); - assert(b[0][2]==2); + __CPROVER_assert(b[0][1] == 2, "b[0][1]==2"); + __CPROVER_assert(b[0][1] == 3, "b[0][1]==3"); + __CPROVER_assert(b[0][2] == 2, "b[0][2]==2"); // Reset this change to ensure tests later work as expected b[0][1]=1; @@ -77,74 +77,74 @@ int main(int argc, char *argv[]) // Test how well we can deal with merging for an index on a uniform array when // the index has one possible value - assert(a[i][1]==0); - assert(a[i][1]==1); - assert(a[1][i]==0); - assert(a[1][i]==1); - assert(a[i][i]==0); - assert(a[i][i]==1); + __CPROVER_assert(a[i][1] == 0, "a[i][1]==0"); + __CPROVER_assert(a[i][1] == 1, "a[i][1]==1"); + __CPROVER_assert(a[1][i] == 0, "a[1][i]==0"); + __CPROVER_assert(a[1][i] == 1, "a[1][i]==1"); + __CPROVER_assert(a[i][i] == 0, "a[i][i]==0"); + __CPROVER_assert(a[i][i] == 1, "a[i][i]==1"); // Test how well we can deal with merging for an index on a uniform array when // the index has two possible values - assert(a[j][1]==0); - assert(a[j][1]==1); - assert(a[1][j]==0); - assert(a[1][j]==1); - assert(a[j][j]==0); - assert(a[j][j]==1); + __CPROVER_assert(a[j][1] == 0, "a[j][1]==0"); + __CPROVER_assert(a[j][1] == 1, "a[j][1]==1"); + __CPROVER_assert(a[1][j] == 0, "a[1][j]==0"); + __CPROVER_assert(a[1][j] == 1, "a[1][j]==1"); + __CPROVER_assert(a[j][j] == 0, "a[j][j]==0"); + __CPROVER_assert(a[j][j] == 1, "a[j][j]==1"); // Test how well we can deal with merging for an index on a non-uniform array - assert(b[i][1]==1); - assert(b[i][1]==11); - assert(b[1][i]==3); - assert(b[1][i]==11); - assert(b[i][i]==0); - assert(b[i][i]==11); + __CPROVER_assert(b[i][1] == 1, "b[i][1]==1"); + __CPROVER_assert(b[i][1] == 11, "b[i][1]==11"); + __CPROVER_assert(b[1][i] == 3, "b[1][i]==3"); + __CPROVER_assert(b[1][i] == 11, "b[1][i]==11"); + __CPROVER_assert(b[i][i] == 0, "b[i][i]==0"); + __CPROVER_assert(b[i][i] == 11, "b[i][i]==11"); // Test how well we can deal with merging for an index on a non-uniform array - assert(b[j][1]==1); - assert(b[j][1]==11); - assert(b[1][j]==3); - assert(b[1][j]==11); - assert(b[j][j]==0); - assert(b[j][j]==11); + __CPROVER_assert(b[j][1] == 1, "b[j][1]==1"); + __CPROVER_assert(b[j][1] == 11, "b[j][1]==11"); + __CPROVER_assert(b[1][j] == 3, "b[1][j]==3"); + __CPROVER_assert(b[1][j] == 11, "b[1][j]==11"); + __CPROVER_assert(b[j][j] == 0, "b[j][j]==0"); + __CPROVER_assert(b[j][j] == 11, "b[j][j]==11"); // Test how we deal with reading off the end of an array - assert(a[100][0]==0); - assert(a[0][100]==0); + __CPROVER_assert(a[100][0] == 0, "a[100][0]==0"); + __CPROVER_assert(a[0][100] == 0, "a[0][100]==0"); // Test how we deal with writing off the end of an array int c=0; a[100][0]=1; - assert(c==0); + __CPROVER_assert(c == 0, "c==0"); c=0; a[0][100]=1; - assert(c==0); + __CPROVER_assert(c == 0, "c==0"); // Test how we deal with merging for an index with one possible value when // writing to an array int ei[3][3]={{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; ei[i][1]=1; - assert(ei[0][1]==1); - assert(ei[0][1]==0); - assert(ei[2][1]==0); - assert(ei[2][1]==1); + __CPROVER_assert(ei[0][1] == 1, "ei[0][1]==1"); + __CPROVER_assert(ei[0][1] == 0, "ei[0][1]==0"); + __CPROVER_assert(ei[2][1] == 0, "ei[2][1]==0"); + __CPROVER_assert(ei[2][1] == 1, "ei[2][1]==1"); // Test how we deal with merging for an index with two possible values when // writing to an array int ej[3][3]={{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; ej[j][1]=1; - assert(ej[0][1]==0); - assert(ej[2][1]==0); + __CPROVER_assert(ej[0][1] == 0, "ej[0][1]==0"); + __CPROVER_assert(ej[2][1] == 0, "ej[2][1]==0"); // Test how we deal with merging for an index with two possible values when // it means writing to an array element that may be out of bounds int ek[3][3]={{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; c=0; ek[k][1]=1; - assert(ek[0][1]==0); - assert(c==0); + __CPROVER_assert(ek[0][1] == 0, "ek[0][1]==0"); + __CPROVER_assert(c == 0, "c==0"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/array_of_pointer_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/array_of_pointer_sensitivity_tests.c index 907fc4180ad..a7a2ec49825 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/array_of_pointer_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/array_of_pointer_sensitivity_tests.c @@ -25,30 +25,30 @@ int main(int argc, char *argv[]) int *b[3]={&b0, &b1, &b2}; // Test if we can represent uniform constant arrays - assert(a[1]==&a0); - assert(a[1]==&a3); - assert(*a[1]==0); - assert(*a[1]==3); + __CPROVER_assert(a[1] == &a0, "a[1]==&a0"); + __CPROVER_assert(a[1] == &a3, "a[1]==&a3"); + __CPROVER_assert(*a[1] == 0, "*a[1]==0"); + __CPROVER_assert(*a[1] == 3, "*a[1]==3"); // Test if we can represent constant arrays which aren't uniform - assert(b[1]==&b1); - assert(b[1]==&b3); - assert(*b[1]==11); - assert(*b[1]==13); + __CPROVER_assert(b[1] == &b1, "b[1]==&b1"); + __CPROVER_assert(b[1] == &b3, "b[1]==&b3"); + __CPROVER_assert(*b[1] == 11, "*b[1]==11"); + __CPROVER_assert(*b[1] == 13, "*b[1]==13"); // Test alternative syntax for accessing an array value - assert(*(b+1)==&b1); - assert(*(b+1)==&b3); - assert(*(1+b)==&b1); - assert(*(1+b)==&b3); - assert(1[b]==&b1); - assert(1[b]==&b3); - assert(**(b+1)==11); - assert(**(b+1)==13); - assert(**(1+b)==11); - assert(**(1+b)==13); - assert(*1[b]==11); - assert(*1[b]==13); + __CPROVER_assert(*(b + 1) == &b1, "*(b+1)==&b1"); + __CPROVER_assert(*(b + 1) == &b3, "*(b+1)==&b3"); + __CPROVER_assert(*(1 + b) == &b1, "*(1+b)==&b1"); + __CPROVER_assert(*(1 + b) == &b3, "*(1+b)==&b3"); + __CPROVER_assert(1 [b] == &b1, "1[b]==&b1"); + __CPROVER_assert(1 [b] == &b3, "1[b]==&b3"); + __CPROVER_assert(**(b + 1) == 11, "**(b+1)==11"); + __CPROVER_assert(**(b + 1) == 13, "**(b+1)==13"); + __CPROVER_assert(**(1 + b) == 11, "**(1+b)==11"); + __CPROVER_assert(**(1 + b) == 13, "**(1+b)==13"); + __CPROVER_assert(*1 [b] == 11, "*1[b]==11"); + __CPROVER_assert(*1 [b] == 13, "*1[b]==13"); // c and d are arrays whose values requiring merging paths in the CFG. For // c[0] there is only one possibility after merging and for d[0] there are @@ -62,14 +62,14 @@ int main(int argc, char *argv[]) } // Test how well we can deal with merging for an array value - assert(c[0]==&c0); - assert(c[0]==&c3); - assert(d[0]==&d0); - assert(d[0]==&d3); - assert(*c[0]==20); - assert(*c[0]==23); - assert(*d[0]==30); - assert(*d[0]==33); + __CPROVER_assert(c[0] == &c0, "c[0]==&c0"); + __CPROVER_assert(c[0] == &c3, "c[0]==&c3"); + __CPROVER_assert(d[0] == &d0, "d[0]==&d0"); + __CPROVER_assert(d[0] == &d3, "d[0]==&d3"); + __CPROVER_assert(*c[0] == 20, "*c[0]==20"); + __CPROVER_assert(*c[0] == 23, "*c[0]==23"); + __CPROVER_assert(*d[0] == 30, "*d[0]==30"); + __CPROVER_assert(*d[0] == 33, "*d[0]==33"); // The variables i, j and k will be used as indexes into arrays of size 3. // They all require merging paths in the CFG. For i there is only one value on @@ -87,33 +87,33 @@ int main(int argc, char *argv[]) } // Test how well we can deal with merging for an index on a uniform array - assert(a[i]==&a0); - assert(a[i]==&a3); - assert(a[j]==&a0); - assert(a[j]==&a3); - assert(*a[i]==0); - assert(*a[i]==3); - assert(*a[j]==0); - assert(*a[j]==3); + __CPROVER_assert(a[i] == &a0, "a[i]==&a0"); + __CPROVER_assert(a[i] == &a3, "a[i]==&a3"); + __CPROVER_assert(a[j] == &a0, "a[j]==&a0"); + __CPROVER_assert(a[j] == &a3, "a[j]==&a3"); + __CPROVER_assert(*a[i] == 0, "*a[i]==0"); + __CPROVER_assert(*a[i] == 3, "*a[i]==3"); + __CPROVER_assert(*a[j] == 0, "*a[j]==0"); + __CPROVER_assert(*a[j] == 3, "*a[j]==3"); // Test how well we can deal with merging for an index on a non-uniform array - assert(b[i]==&b0); - assert(b[i]==&b1); - assert(b[j]==&b0); - assert(b[j]==&b3); - assert(*b[i]==10); - assert(*b[i]==11); - assert(*b[j]==10); - assert(*b[j]==13); + __CPROVER_assert(b[i] == &b0, "b[i]==&b0"); + __CPROVER_assert(b[i] == &b1, "b[i]==&b1"); + __CPROVER_assert(b[j] == &b0, "b[j]==&b0"); + __CPROVER_assert(b[j] == &b3, "b[j]==&b3"); + __CPROVER_assert(*b[i] == 10, "*b[i]==10"); + __CPROVER_assert(*b[i] == 11, "*b[i]==11"); + __CPROVER_assert(*b[j] == 10, "*b[j]==10"); + __CPROVER_assert(*b[j] == 13, "*b[j]==13"); // Test how we deal with reading off the end of an array - assert(a[100]==&a2); - assert(*a[100]==2); + __CPROVER_assert(a[100] == &a2, "a[100]==&a2"); + __CPROVER_assert(*a[100] == 2, "*a[100]==2"); // Test how we deal with writing off the end of an array a[100]=&a2; - assert(b[1]==&b1); - assert(*b[1]==11); + __CPROVER_assert(b[1] == &b1, "b[1]==&b1"); + __CPROVER_assert(*b[1] == 11, "*b[1]==11"); // Test how we deal with merging for an index with one possible value when // writing to an array @@ -121,14 +121,14 @@ int main(int argc, char *argv[]) int ei1=41; int *ei[3]={&ei0, &ei0, &ei0}; ei[i]=&ei1; - assert(ei[0]==&ei1); - assert(ei[0]==&ei0); - assert(ei[2]==&ei0); - assert(ei[2]==&ei1); - assert(*ei[0]==41); - assert(*ei[0]==40); - assert(*ei[2]==40); - assert(*ei[2]==41); + __CPROVER_assert(ei[0] == &ei1, "ei[0]==&ei1"); + __CPROVER_assert(ei[0] == &ei0, "ei[0]==&ei0"); + __CPROVER_assert(ei[2] == &ei0, "ei[2]==&ei0"); + __CPROVER_assert(ei[2] == &ei1, "ei[2]==&ei1"); + __CPROVER_assert(*ei[0] == 41, "*ei[0]==41"); + __CPROVER_assert(*ei[0] == 40, "*ei[0]==40"); + __CPROVER_assert(*ei[2] == 40, "*ei[2]==40"); + __CPROVER_assert(*ei[2] == 41, "*ei[2]==41"); // Test how we deal with merging for an index with two possible values when // writing to an array @@ -136,12 +136,12 @@ int main(int argc, char *argv[]) int ej1=51; int *ej[3]={&ej0, &ej0, &ej0}; ej[j]=&ej1; - assert(ej[0]==&ej0); - assert(ej[2]==&ej0); - assert(ej[2]==&ej1); - assert(*ej[0]==50); - assert(*ej[2]==50); - assert(*ej[2]==51); + __CPROVER_assert(ej[0] == &ej0, "ej[0]==&ej0"); + __CPROVER_assert(ej[2] == &ej0, "ej[2]==&ej0"); + __CPROVER_assert(ej[2] == &ej1, "ej[2]==&ej1"); + __CPROVER_assert(*ej[0] == 50, "*ej[0]==50"); + __CPROVER_assert(*ej[2] == 50, "*ej[2]==50"); + __CPROVER_assert(*ej[2] == 51, "*ej[2]==51"); // Test how we deal with merging for an index with two possible values when // it means writing to an array element that may be out of bounds @@ -149,8 +149,28 @@ int main(int argc, char *argv[]) int ek1=61; int *ek[3]={&ek0, &ek0, &ek0}; ek[k]=&ek1; - assert(ek[0]==&ek0); - assert(*ek[0]==60); + __CPROVER_assert(ek[0] == &ek0, "ek[0]==&ek0"); + __CPROVER_assert(*ek[0] == 60, "*ek[0]==60"); + + // Test writing to an unknown index (i.e. a merging write of the pointer) + int x = 4; + int y = 5; + int *ps[2] = {&x, &y}; + int i; + if(argc > 2) + { + i = 0; + } + else + { + i = 1; + } + *(ps[i]) = 4; + + __CPROVER_assert(*ps[0] == 4, "*ps[0]==4"); + __CPROVER_assert(*ps[1] == 4, "*ps[1]==4"); + __CPROVER_assert(x == 4, "x==4"); + __CPROVER_assert(y == 4, "y==4"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/array_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/array_sensitivity_tests.c index 68bace194c8..eec18d869ff 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/array_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/array_sensitivity_tests.c @@ -8,20 +8,20 @@ int main(int argc, char *argv[]) int b[3]={1, 0, 0}; // Test if we can represent uniform constant arrays - assert(a[1]==0); - assert(a[1]==1); + __CPROVER_assert(a[1] == 0, "a[1]==0"); + __CPROVER_assert(a[1] == 1, "a[1]==1"); // Test if we can represent constant arrays which aren't uniform - assert(b[1]==0); - assert(b[1]==1); + __CPROVER_assert(b[1] == 0, "b[1]==0"); + __CPROVER_assert(b[1] == 1, "b[1]==1"); // Test alternative syntax for accessing an array value - assert(*(b+1)==0); - assert(*(b+1)==1); - assert(*(1+b)==0); - assert(*(1+b)==1); - assert(1[b]==0); - assert(1[b]==1); + __CPROVER_assert(*(b + 1) == 0, "*(b+1)==0"); + __CPROVER_assert(*(b + 1) == 1, "*(b+1)==1"); + __CPROVER_assert(*(1 + b) == 0, "*(1+b)==0"); + __CPROVER_assert(*(1 + b) == 1, "*(1+b)==1"); + __CPROVER_assert(1 [b] == 0, "1[b]==0"); + __CPROVER_assert(1 [b] == 1, "1[b]==1"); // c and d are arrays whose values requiring merging paths in the CFG. For // c[0] there is only one possibility after merging and for d[0] there are @@ -35,11 +35,11 @@ int main(int argc, char *argv[]) } // Test how well we can deal with merging for an array value - assert(c[0]==0); - assert(c[0]==1); - assert(d[0]==0); - assert(d[0]==2); - assert(d[1]==0); + __CPROVER_assert(c[0] == 0, "c[0]==0"); + __CPROVER_assert(c[0] == 1, "c[0]==1"); + __CPROVER_assert(d[0] == 0, "d[0]==0"); + __CPROVER_assert(d[0] == 2, "d[0]==2"); + __CPROVER_assert(d[1] == 0, "d[1]==0"); // The variables i, j and k will be used as indexes into arrays of size 3. // They all require merging paths in the CFG. For i there is only one value on @@ -57,45 +57,45 @@ int main(int argc, char *argv[]) } // Test how well we can deal with merging for an index on a uniform array - assert(a[i]==0); - assert(a[i]==1); - assert(a[j]==0); - assert(a[j]==1); + __CPROVER_assert(a[i] == 0, "a[i]==0"); + __CPROVER_assert(a[i] == 1, "a[i]==1"); + __CPROVER_assert(a[j] == 0, "a[j]==0"); + __CPROVER_assert(a[j] == 1, "a[j]==1"); // Test how well we can deal with merging for an index on a non-uniform array - assert(b[i]==1); - assert(b[i]==0); - assert(b[j]==0); - assert(b[j]==1); + __CPROVER_assert(b[i] == 1, "b[i]==1"); + __CPROVER_assert(b[i] == 0, "b[i]==0"); + __CPROVER_assert(b[j] == 0, "b[j]==0"); + __CPROVER_assert(b[j] == 1, "b[j]==1"); // Test how we deal with reading off the end of an array - assert(a[100]==0); + __CPROVER_assert(a[100] == 0, "a[100]==0"); // Test how we deal with writing off the end of an array a[100]=1; - assert(b[1]==0); + __CPROVER_assert(b[1] == 0, "b[1]==0"); // Test how we deal with merging for an index with one possible value when // writing to an array int ei[3]={0, 0, 0}; ei[i]=1; - assert(ei[0]==1); - assert(ei[0]==0); - assert(ei[2]==0); - assert(ei[2]==1); + __CPROVER_assert(ei[0] == 1, "ei[0]==1"); + __CPROVER_assert(ei[0] == 0, "ei[0]==0"); + __CPROVER_assert(ei[2] == 0, "ei[2]==0"); + __CPROVER_assert(ei[2] == 1, "ei[2]==1"); // Test how we deal with merging for an index with two possible values when // writing to an array int ej[3]={0, 0, 0}; ej[j]=1; - assert(ej[0]==0); - assert(ej[2]==0); + __CPROVER_assert(ej[0] == 0, "ej[0]==0"); + __CPROVER_assert(ej[2] == 0, "ej[2]==0"); // Test how we deal with merging for an index with two possible values when // it means writing to an array element that may be out of bounds int ek[3]={0, 0, 0}; ek[k]=1; - assert(ek[0]==0); + __CPROVER_assert(ek[0] == 0, "ek[0]==0"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/char_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/char_sensitivity_tests.c index 9e87454b68e..19c8cbf6eb7 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/char_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/char_sensitivity_tests.c @@ -4,7 +4,7 @@ int main(int argc, char *argv[]) { // Test if we can represent constant chars char x='a'; - assert(x=='a'); - assert(x=='b'); + __CPROVER_assert(x == 'a', "x=='a'"); + __CPROVER_assert(x == 'b', "x=='b'"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/float_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/float_sensitivity_tests.c index 1f73fae4a6c..a4bb5450655 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/float_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/float_sensitivity_tests.c @@ -4,7 +4,7 @@ int main(int argc, char *argv[]) { // Test if we can represent constant floats float x=0.0; - assert(x==0.0); - assert(x==1.0); + __CPROVER_assert(x == 0.0, "x==0.0"); + __CPROVER_assert(x == 1.0, "x==1.0"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/int_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/int_sensitivity_tests.c index ab4265a051f..8d7215ded25 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/int_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/int_sensitivity_tests.c @@ -10,46 +10,68 @@ int main(int argc, char *argv[]) { y=1; } - assert(x==0); - assert(x==1); - assert(x==y); + __CPROVER_assert(x == 0, "x==0"); + __CPROVER_assert(x == 1, "x==1"); + __CPROVER_assert(x == y, "x==y"); - assert(x<1); - assert(x<-1); - assert(x-1); - assert(x>1); - assert(x>y); + __CPROVER_assert(x > -1, "x>-1"); + __CPROVER_assert(x > 1, "x>1"); + __CPROVER_assert(x > y, "x>y"); - assert(x!=1); - assert(x!=0); - assert(x!=y); + __CPROVER_assert(x != 1, "x!=1"); + __CPROVER_assert(x != 0, "x!=0"); + __CPROVER_assert(x != y, "x!=y"); - assert(!(x==1)); - assert(!(x==0)); - assert(!(x==y)); + __CPROVER_assert(!(x == 1), "!(x==1)"); + __CPROVER_assert(!(x == 0), "!(x==0)"); + __CPROVER_assert(!(x == y), "!(x==y)"); // Test how well we can represent an int when it has more than one possible // value - assert(y<2); - assert(y>2); - assert(y==1); + __CPROVER_assert(y < 2, "y<2"); + __CPROVER_assert(y > 2, "y>2"); + __CPROVER_assert(y == 1, "y==1"); // Try copying a variable and then modifying the original int z=x; x=10; - assert(z==0); - assert(z==10); + __CPROVER_assert(z == 0, "z==0"); + __CPROVER_assert(z == 10, "z==10"); // Test how we treat assertions in unreachable code x=0; if(0) { - assert(x==0); - assert(x==1); - assert(y==0); + __CPROVER_assert(x == 0, "x==0"); + __CPROVER_assert(x == 1, "x==1"); + __CPROVER_assert(y == 0, "y==0"); } + // Try merging two states with multiple variables + + int a1 = 0; + int a2 = 0; + int a3 = 0; + int a4 = 0; + int a5 = 0; + if(argc > 2) + { + a1 = argc; + a2 = argc; + a3 = argc; + // all three variables are now top in this branch + } + + // all three asserts are unverifiable + __CPROVER_assert(a1 == 0, "a1==0"); + __CPROVER_assert(a2 == 0, "a2==0"); + __CPROVER_assert(a3 == 0, "a3==0"); + __CPROVER_assert(a4 == 0, "a4==0"); + __CPROVER_assert(a5 == 0, "a5==0"); + return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/pointer_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/pointer_sensitivity_tests.c index be328fa4a5d..5a0fa9a1475 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/pointer_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/pointer_sensitivity_tests.c @@ -10,31 +10,31 @@ int main(int argc, char *argv[]) int *x=&a; int *x2=&a; int *y=&b; - assert(x==&a); - assert(x==&b); - assert(x==x2); - assert(x==y); + __CPROVER_assert(x == &a, "x==&a"); + __CPROVER_assert(x == &b, "x==&b"); + __CPROVER_assert(x == x2, "x==x2"); + __CPROVER_assert(x == y, "x==y"); // Reading from a dereferenced pointer - assert(*x==0); - assert(*x==1); + __CPROVER_assert(*x == 0, "*x==0"); + __CPROVER_assert(*x == 1, "*x==1"); // Modify the referenced value and access it through the pointer again a=1; - assert(*x==1); - assert(*x==0); + __CPROVER_assert(*x == 1, "*x==1"); + __CPROVER_assert(*x == 0, "*x==0"); // Writing to a dereferenced pointer *x=2; - assert(a==2); - assert(a==0); + __CPROVER_assert(a == 2, "a==2"); + __CPROVER_assert(a == 0, "a==0"); // Conditionally reassign the pointer, but to the same value if(argc>2) { x=&a; } - assert(x==&a); + __CPROVER_assert(x == &a, "x==&a"); // Conditionally reassign the pointer, to a different value this time if(argc>3) @@ -45,9 +45,9 @@ int main(int argc, char *argv[]) { x=&c; } - assert(*x==0); - assert(x==&a); - assert(x==&b); + __CPROVER_assert(*x == 0, "*x==0"); + __CPROVER_assert(x == &a, "x==&a"); + __CPROVER_assert(x == &b, "x==&b"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_array_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_array_sensitivity_tests.c index db9fe720e15..4ae885d4a95 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_array_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_array_sensitivity_tests.c @@ -6,21 +6,41 @@ int main(int argc, char *argv[]) // Test reading from an array using a pointer int a[3]={1, 2, 3}; int *p=a; - assert(p==&a[0]); - assert(*p==1); + __CPROVER_assert(p == &a[0], "p==&a[0]"); + + // Read through pointer + __CPROVER_assert(*p == 1, "*p==1"); + + // Read through offset pointer + __CPROVER_assert(p[1] == 2, "p[1]==2"); + __CPROVER_assert(1 [p] == 2, "1[p]==2"); + + __CPROVER_assert(*(p + 1) == 2, "*(p+1)==2"); + __CPROVER_assert(*(1 + p) == 2, "*(1+p)==2"); // Test pointer arithmetic int *q=&a[1]; - assert(q==p+1); - assert(*q==2); + __CPROVER_assert(q == p + 1, "q==p+1"); + __CPROVER_assert(*q == 2, "*q==2"); + __CPROVER_assert(*(q - 1) == 1, "*(q-1)==1"); // Test pointer diffs ptrdiff_t x=1; - assert(q-p==x); + __CPROVER_assert(q - p == x, "q-p==x"); // Test writing into an array using a pointer *q=4; - assert(a[1]==4); + __CPROVER_assert(a[1] == 4, "a[1]==4"); + + p[1] = 5; + __CPROVER_assert(a[1] == 5, "a[1]==5"); + + *(p + 1) = 6; + __CPROVER_assert(a[1] == 6, "a[1]==6"); + + *(1 + p) = 7; + __CPROVER_assert(a[1] == 7, "a[1]==7"); + a[1]=2; // We now explore pointers and indexes each with more than one possible value @@ -37,23 +57,23 @@ int main(int argc, char *argv[]) // Test reading from an array using a pointer with more than one possible // value - assert(*r==2); - assert(*r==1); - assert(*s==0); - assert(*s==1); + __CPROVER_assert(*r == 2, "*r==2"); + __CPROVER_assert(*r == 1, "*r==1"); + __CPROVER_assert(*s == 0, "*s==0"); + __CPROVER_assert(*s == 1, "*s==1"); // Test pointer arithmetic with an unknown index int *t=&a[i]; - assert(t==p+i); + __CPROVER_assert(t == p + i, "t==p+i"); // Test pointer diffs with an unknown index ptrdiff_t y=i; - assert(t-p==y); + __CPROVER_assert(t - p == y, "t-p==y"); // Test writing into an array using a pointer with an unknown index *r=5; - assert(a[i]==5); - assert(a[1]==5); + __CPROVER_assert(a[i] == 5, "a[i]==5"); + __CPROVER_assert(a[1] == 5, "a[1]==5"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_pointer_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_pointer_sensitivity_tests.c index ee29a7059db..6ea079e21e1 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_pointer_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_pointer_sensitivity_tests.c @@ -9,16 +9,16 @@ int main(int argc, char *argv[]) int **x=&p; // Reading from a pointer to a pointer that's been dereferenced twice - assert(**x==0); - assert(**x==1); + __CPROVER_assert(**x == 0, "**x==0"); + __CPROVER_assert(**x == 1, "**x==1"); a=1; - assert(**x==1); - assert(**x==0); + __CPROVER_assert(**x == 1, "**x==1"); + __CPROVER_assert(**x == 0, "**x==0"); // Writing to a pointer to a pointer that's been dereferenced twice **x=2; - assert(a==2); - assert(a==1); + __CPROVER_assert(a == 2, "a==2"); + __CPROVER_assert(a == 1, "a==1"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_struct_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_struct_sensitivity_tests.c index e0092afae4a..b0cfc99e040 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_struct_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/pointer_to_struct_sensitivity_tests.c @@ -12,17 +12,42 @@ int main(int argc, char *argv[]) x.a=0; x.b=1.0; struct int_float *p=&x; - assert((*p).a==0); - assert((*p).a==1); + __CPROVER_assert((*p).a == 0, "(*p).a==0"); + __CPROVER_assert((*p).a == 1, "(*p).a==1"); // Test alternative syntax - assert(p->a==0); - assert(p->a==1); + __CPROVER_assert(p->a == 0, "p->a==0"); + __CPROVER_assert(p->a == 1, "p->a==1"); // Test writing to the struct through the pointer p->b=2.0; - assert(p->b==2.0); - assert(p->b==1.0); + __CPROVER_assert(p->b == 2.0, "p->b==2.0"); + __CPROVER_assert(p->b == 1.0, "p->b==1.0"); + + // pointers to components + int *comp_p = &x.a; + __CPROVER_assert(comp_p == &x.a, "comp_p==&x.a"); + __CPROVER_assert(comp_p == &x.b, "comp_p==&x.b"); + __CPROVER_assert(*comp_p == 0, "*comp_p==0"); + __CPROVER_assert(*comp_p == 1, "*comp_p==1"); + + float *compb_p = &x.b; + __CPROVER_assert(compb_p == &x.a, "compb_p==&x.a"); + __CPROVER_assert(compb_p == &x.b, "compb_p==&x.b"); + __CPROVER_assert(*compb_p == 2.0, "*compb_p==2.0"); + __CPROVER_assert(*compb_p == 1.0, "*compb_p==1.0"); + + // Use pointer implicitly pointing at the first component + int *implicit_p = &x; + __CPROVER_assert(implicit_p == &x.a, "implicit_p==&x.a"); + __CPROVER_assert(implicit_p == &x, "implicit_p==&x"); + __CPROVER_assert(*implicit_p == 0, "*implicit_p==0"); + __CPROVER_assert(*implicit_p == 1, "*implicit_p==1"); + + // Write through pointer implicitly pointing at the first component + *implicit_p = 5; + __CPROVER_assert(x.a == 5, "x.a==5"); + __CPROVER_assert(x.a == 1, "x.a==1"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/struct_of_array_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/struct_of_array_sensitivity_tests.c index d5924b71ed7..c68fb8837a4 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/struct_of_array_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/struct_of_array_sensitivity_tests.c @@ -15,19 +15,19 @@ int main(int argc, char *argv[]) x.b[0]=3.0f; x.b[1]=4.0f; x.b[2]=5.0f; - assert(x.a[0]==0); - assert(*(x.a+0)==0); - assert(*(0+x.a)==0); - assert(0[x.a]==0); + __CPROVER_assert(x.a[0] == 0, "x.a[0]==0"); + __CPROVER_assert(*(x.a + 0) == 0, "*(x.a+0)==0"); + __CPROVER_assert(*(0 + x.a) == 0, "*(0+x.a)==0"); + __CPROVER_assert(0 [x.a] == 0, "0[x.a]==0"); // Test merging when there is only one value on both paths if(argc>2) { x.a[0]=0; } - assert(x.a[0]==0); - assert(x.a[1]==1); - assert(x.b[0]==3.0f); + __CPROVER_assert(x.a[0] == 0, "x.a[0]==0"); + __CPROVER_assert(x.a[1] == 1, "x.a[1]==1"); + __CPROVER_assert(x.b[0] == 3.0f, "x.b[0]==3.0f"); // Test merging when there is one value for a and two values for b, to test if // we are representing them separately @@ -36,12 +36,12 @@ int main(int argc, char *argv[]) x.a[0]=0; x.b[2]=15.0f; } - assert(x.a[0]==0); - assert(x.a[1]==1); - assert(x.b[2]>0.0f); - assert(x.b[2]==15.0f); - assert(x.b[2]==1.0f); - assert(x.b[0]==3.0f); + __CPROVER_assert(x.a[0] == 0, "x.a[0]==0"); + __CPROVER_assert(x.a[1] == 1, "x.a[1]==1"); + __CPROVER_assert(x.b[2] > 0.0f, "x.b[2]>0.0f"); + __CPROVER_assert(x.b[2] == 15.0f, "x.b[2]==15.0f"); + __CPROVER_assert(x.b[2] == 1.0f, "x.b[2]==1.0f"); + __CPROVER_assert(x.b[0] == 3.0f, "x.b[0]==3.0f"); // Test merging when there are two values for a and b if(argc>4) @@ -49,10 +49,10 @@ int main(int argc, char *argv[]) x.a[0]=11; x.b[2]=25.0f; } - assert(x.a[0]<12); - assert(x.a[0]>2); - assert(x.a[0]==0); - assert(x.a[1]==1); + __CPROVER_assert(x.a[0] < 12, "x.a[0]<12"); + __CPROVER_assert(x.a[0] > 2, "x.a[0]>2"); + __CPROVER_assert(x.a[0] == 0, "x.a[0]==0"); + __CPROVER_assert(x.a[1] == 1, "x.a[1]==1"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/struct_of_pointer_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/struct_of_pointer_sensitivity_tests.c index 3cde8011c2b..dbc196c7f35 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/struct_of_pointer_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/struct_of_pointer_sensitivity_tests.c @@ -18,14 +18,14 @@ int main(int argc, char *argv[]) struct int_float x; x.a=&a1; x.b=&b1; - assert(x.a==&a1); - assert(x.a==&a2); - assert(x.b==&b1); - assert(x.b==&b2); - assert(*x.a==0); - assert(*x.a==100); - assert(*x.b==10.0f); - assert(*x.b==110.0f); + __CPROVER_assert(x.a == &a1, "x.a==&a1"); + __CPROVER_assert(x.a == &a2, "x.a==&a2"); + __CPROVER_assert(x.b == &b1, "x.b==&b1"); + __CPROVER_assert(x.b == &b2, "x.b==&b2"); + __CPROVER_assert(*x.a == 0, "*x.a==0"); + __CPROVER_assert(*x.a == 100, "*x.a==100"); + __CPROVER_assert(*x.b == 10.0f, "*x.b==10.0f"); + __CPROVER_assert(*x.b == 110.0f, "*x.b==110.0f"); // Test merging when there is only one value on both paths if(argc>2) @@ -33,10 +33,10 @@ int main(int argc, char *argv[]) x.a=&a1; x.b=&b1; } - assert(x.a==&a1); - assert(x.a==&a2); - assert(*x.a==0); - assert(*x.a==100); + __CPROVER_assert(x.a == &a1, "x.a==&a1"); + __CPROVER_assert(x.a == &a2, "x.a==&a2"); + __CPROVER_assert(*x.a == 0, "*x.a==0"); + __CPROVER_assert(*x.a == 100, "*x.a==100"); // Test merging when there is one value for a and two values for b, to test if // we are representing them separately @@ -45,12 +45,12 @@ int main(int argc, char *argv[]) x.a=&a1; x.b=&b2; } - assert(x.a==&a1); - assert(x.b==&b2); - assert(x.b==&b3); - assert(*x.a==0); - assert(*x.b==11.0f); - assert(*x.b==12.0f); + __CPROVER_assert(x.a == &a1, "x.a==&a1"); + __CPROVER_assert(x.b == &b2, "x.b==&b2"); + __CPROVER_assert(x.b == &b3, "x.b==&b3"); + __CPROVER_assert(*x.a == 0, "*x.a==0"); + __CPROVER_assert(*x.b == 11.0f, "*x.b==11.0f"); + __CPROVER_assert(*x.b == 12.0f, "*x.b==12.0f"); // Test merging when there are two values for a and b if(argc>4) @@ -58,14 +58,14 @@ int main(int argc, char *argv[]) x.a=&a2; x.b=&b3; } - assert(x.a==&a2); - assert(x.a==&a3); - assert(x.b==&b3); - assert(x.b==&b4); - assert(*x.a==1); - assert(*x.a==2); - assert(*x.b==12.0f); - assert(*x.b==13.0f); + __CPROVER_assert(x.a == &a2, "x.a==&a2"); + __CPROVER_assert(x.a == &a3, "x.a==&a3"); + __CPROVER_assert(x.b == &b3, "x.b==&b3"); + __CPROVER_assert(x.b == &b4, "x.b==&b4"); + __CPROVER_assert(*x.a == 1, "*x.a==1"); + __CPROVER_assert(*x.a == 2, "*x.a==2"); + __CPROVER_assert(*x.b == 12.0f, "*x.b==12.0f"); + __CPROVER_assert(*x.b == 13.0f, "*x.b==13.0f"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/struct_of_struct_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/struct_of_struct_sensitivity_tests.c index ac6a32a0086..85f1cc1afc7 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/struct_of_struct_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/struct_of_struct_sensitivity_tests.c @@ -18,16 +18,16 @@ int main(int argc, char *argv[]) x.s1.b=1.0; x.s2.a=2; x.s2.b=3.0f; - assert(x.s1.a==0); - assert(x.s2.b==3.0f); + __CPROVER_assert(x.s1.a == 0, "x.s1.a==0"); + __CPROVER_assert(x.s2.b == 3.0f, "x.s2.b==3.0f"); // Test merging when there is only one value on both paths if(argc>2) { x.s1.a=0; } - assert(x.s1.a==0); - assert(x.s1.a==10); + __CPROVER_assert(x.s1.a == 0, "x.s1.a==0"); + __CPROVER_assert(x.s1.a == 10, "x.s1.a==10"); // Test merging when there is one value for s1 and two values for s2, to test // if we are representing them separately @@ -36,9 +36,9 @@ int main(int argc, char *argv[]) x.s1.b=1.0f; x.s2.b=13.0f; } - assert(x.s1.b==1.0f); - assert(x.s2.b==3.0f); - assert(x.s2.b==0.0f); + __CPROVER_assert(x.s1.b == 1.0f, "x.s1.b==1.0f"); + __CPROVER_assert(x.s2.b == 3.0f, "x.s2.b==3.0f"); + __CPROVER_assert(x.s2.b == 0.0f, "x.s2.b==0.0f"); // Test merging when there are two values for s1 and s2 if(argc>4) @@ -46,10 +46,10 @@ int main(int argc, char *argv[]) x.s1.a=20; x.s2.a=22; } - assert(x.s1.a==20); - assert(x.s1.a<30); - assert(x.s2.a==22); - assert(x.s2.a<30); + __CPROVER_assert(x.s1.a == 20, "x.s1.a==20"); + __CPROVER_assert(x.s1.a < 30, "x.s1.a<30"); + __CPROVER_assert(x.s2.a == 22, "x.s2.a==22"); + __CPROVER_assert(x.s2.a < 30, "x.s2.a<30"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-common-files/struct_sensitivity_tests.c b/regression/goto-analyzer/sensitivity-test-common-files/struct_sensitivity_tests.c index a4ad229e34c..2804a4805a2 100644 --- a/regression/goto-analyzer/sensitivity-test-common-files/struct_sensitivity_tests.c +++ b/regression/goto-analyzer/sensitivity-test-common-files/struct_sensitivity_tests.c @@ -11,8 +11,8 @@ int main(int argc, char *argv[]) struct int_float x={0, 1.0f}; x.a=0; x.b=1.0f; - assert(x.a==0); - assert(x.a==1); + __CPROVER_assert(x.a == 0, "x.a==0"); + __CPROVER_assert(x.a == 1, "x.a==1"); // Test merging when there is only one value on both paths if(argc>2) @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) x.a=0; x.b=1.0f; } - assert(x.a==0); + __CPROVER_assert(x.a == 0, "x.a==0"); // Test merging when there is one value for a and two values for b, to test if // we are representing them separately @@ -29,9 +29,9 @@ int main(int argc, char *argv[]) x.a=0; x.b=2.0f; } - assert(x.a==0); - assert(x.b>0.0f); - assert(x.b==1.0f); + __CPROVER_assert(x.a == 0, "x.a==0"); + __CPROVER_assert(x.b > 0.0f, "x.b>0.0f"); + __CPROVER_assert(x.b == 1.0f, "x.b==1.0f"); // Test merging when there are two values for a and b if(argc>4) @@ -39,9 +39,9 @@ int main(int argc, char *argv[]) x.a=1; x.b=2.0f; } - assert(x.a<2); - assert(x.a>2); - assert(x.a==1); + __CPROVER_assert(x.a < 2, "x.a<2"); + __CPROVER_assert(x.a > 2, "x.a>2"); + __CPROVER_assert(x.a == 1, "x.a==1"); return 0; } diff --git a/regression/goto-analyzer/sensitivity-test-constants-array-loop/main.c b/regression/goto-analyzer/sensitivity-test-constants-array-loop/main.c new file mode 100644 index 00000000000..f9ac19c71b1 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-test-constants-array-loop/main.c @@ -0,0 +1,12 @@ +static int array[2]; + +void main(void) +{ + int i; + + for(i = 0; i < 1; i++) + array[i] = 5; + + __CPROVER_assert(array[0] == 5, "array[0] == 5"); + __CPROVER_assert(array[1] == 5, "array[1] == 5"); +} diff --git a/regression/goto-analyzer/sensitivity-test-constants-array-loop/test.desc b/regression/goto-analyzer/sensitivity-test-constants-array-loop/test.desc new file mode 100644 index 00000000000..5024310f764 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-test-constants-array-loop/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-arrays --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion.1\] line \d+ array\[0\] == 5: UNKNOWN$ +^\[main.assertion.2\] line \d+ array\[1\] == 5: UNKNOWN$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-array/test.desc b/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-array/test.desc index e1c50e98ed7..7961c6e1129 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-array/test.desc @@ -1,73 +1,73 @@ -FUTURE +CORE sensitivity_test_constants_array_of_constants_array.c ---variable --arrays --verify +--variable-sensitivity --vsd-arrays --verify --vsd-pointers ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]\[2\]==0: SUCCESS$ -^\[main.assertion.2\] .* assertion a\[1\]\[2\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion b\[1\]\[2\]==5: SUCCESS$ -^\[main.assertion.4\] .* assertion b\[1\]\[2\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion \*\(b\[1\]\+2\)==5: SUCCESS$ -^\[main.assertion.6\] .* assertion \*\(b\[1\]\+2\)==0: FAILURE \(if reachable\)$ -^\[main.assertion.7\] .* assertion \(\*\(b\+1\)\)\[2\]==5: SUCCESS$ -^\[main.assertion.8\] .* assertion \(\*\(b\+1\)\)\[2\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.9\] .* assertion \*\(\*\(b\+1\)\+2\)==5: SUCCESS$ -^\[main.assertion.10\] .* assertion \*\(\*\(b\+1\)\+2\)==0: FAILURE \(if reachable\)$ -^\[main.assertion.11\] .* assertion 1\[b\]\[2\]==5: SUCCESS$ -^\[main.assertion.12\] .* assertion 1\[b\]\[2\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.13\] .* assertion \*\(1\[b\]\+2\)==5: SUCCESS$ -^\[main.assertion.14\] .* assertion \*\(1\[b\]\+2\)==0: FAILURE \(if reachable\)$ -^\[main.assertion.15\] .* assertion \(\*\(1\+b\)\)\[2\]==5: UNKNOWN$ -^\[main.assertion.16\] .* assertion \(\*\(1\+b\)\)\[2\]==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion \*\(\*\(1\+b\)\+2\)==5: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*\(\*\(1\+b\)\+2\)==0: UNKNOWN$ -^\[main.assertion.19\] .* assertion 2\[1\[b\]\]==5: SUCCESS$ -^\[main.assertion.20\] .* assertion 2\[1\[b\]\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.21\] .* assertion \*\(2\+1\[b\]\)==5: UNKNOWN$ -^\[main.assertion.22\] .* assertion \*\(2\+1\[b\]\)==0: UNKNOWN$ -^\[main.assertion.23\] .* assertion \*\(2\+\*\(1\+b\)\)==5: UNKNOWN$ -^\[main.assertion.24\] .* assertion \*\(2\+\*\(1\+b\)\)==0: UNKNOWN$ -^\[main.assertion.25\] .* assertion a\[0\]\[1\]==0: SUCCESS$ -^\[main.assertion.26\] .* assertion a\[0\]\[1\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.27\] .* assertion a\[0\]\[2\]==0: SUCCESS$ -^\[main.assertion.28\] .* assertion b\[0\]\[1\]==2: UNKNOWN$ -^\[main.assertion.29\] .* assertion b\[0\]\[1\]==3: UNKNOWN$ -^\[main.assertion.30\] .* assertion b\[0\]\[2\]==2: SUCCESS$ -^\[main.assertion.31\] .* assertion a\[i\]\[1\]==0: SUCCESS$ -^\[main.assertion.32\] .* assertion a\[i\]\[1\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.33\] .* assertion a\[1\]\[i\]==0: SUCCESS$ -^\[main.assertion.34\] .* assertion a\[1\]\[i\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.35\] .* assertion a\[i\]\[i\]==0: SUCCESS$ -^\[main.assertion.36\] .* assertion a\[i\]\[i\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.37\] .* assertion a\[j\]\[1\]==0: UNKNOWN$ -^\[main.assertion.38\] .* assertion a\[j\]\[1\]==1: UNKNOWN$ -^\[main.assertion.39\] .* assertion a\[1\]\[j\]==0: UNKNOWN$ -^\[main.assertion.40\] .* assertion a\[1\]\[j\]==1: UNKNOWN$ -^\[main.assertion.41\] .* assertion a\[j\]\[j\]==0: UNKNOWN$ -^\[main.assertion.42\] .* assertion a\[j\]\[j\]==1: UNKNOWN$ -^\[main.assertion.43\] .* assertion b\[i\]\[1\]==1: SUCCESS$ -^\[main.assertion.44\] .* assertion b\[i\]\[1\]==11: FAILURE \(if reachable\)$ -^\[main.assertion.45\] .* assertion b\[1\]\[i\]==3: SUCCESS$ -^\[main.assertion.46\] .* assertion b\[1\]\[i\]==11: FAILURE \(if reachable\)$ -^\[main.assertion.47\] .* assertion b\[i\]\[i\]==0: SUCCESS$ -^\[main.assertion.48\] .* assertion b\[i\]\[i\]==11: FAILURE \(if reachable\)$ -^\[main.assertion.49\] .* assertion b\[j\]\[1\]==1: UNKNOWN$ -^\[main.assertion.50\] .* assertion b\[j\]\[1\]==11: UNKNOWN$ -^\[main.assertion.51\] .* assertion b\[1\]\[j\]==3: UNKNOWN$ -^\[main.assertion.52\] .* assertion b\[1\]\[j\]==11: UNKNOWN$ -^\[main.assertion.53\] .* assertion b\[j\]\[j\]==0: UNKNOWN$ -^\[main.assertion.54\] .* assertion b\[j\]\[j\]==11: UNKNOWN$ -^\[main.assertion.55\] .* assertion a\[100\]\[0\]==0: UNKNOWN$ -^\[main.assertion.56\] .* assertion a\[0\]\[100\]==0: UNKNOWN$ -^\[main.assertion.57\] .* assertion c==0: SUCCESS$ -^\[main.assertion.58\] .* assertion c==0: SUCCESS$ -^\[main.assertion.59\] .* assertion ei\[0\]\[1\]==1: SUCCESS$ -^\[main.assertion.60\] .* assertion ei\[0\]\[1\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.61\] .* assertion ei\[2\]\[1\]==0: SUCCESS$ -^\[main.assertion.62\] .* assertion ei\[2\]\[1\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.63\] .* assertion ej\[0\]\[1\]==0: UNKNOWN$ -^\[main.assertion.64\] .* assertion ej\[2\]\[1\]==0: UNKNOWN$ -^\[main.assertion.65\] .* assertion ek\[0\]\[1\]==0: UNKNOWN$ -^\[main.assertion.66\] .* assertion c==0: SUCCESS$ +^\[main.assertion.1\] .* a\[1\]\[2\]==0: SUCCESS$ +^\[main.assertion.2\] .* a\[1\]\[2\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* b\[1\]\[2\]==5: SUCCESS$ +^\[main.assertion.4\] .* b\[1\]\[2\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* \*\(b\[1\]\+2\)==5: SUCCESS$ +^\[main.assertion.6\] .* \*\(b\[1\]\+2\)==0: FAILURE \(if reachable\)$ +^\[main.assertion.7\] .* \(\*\(b\+1\)\)\[2\]==5: SUCCESS$ +^\[main.assertion.8\] .* \(\*\(b\+1\)\)\[2\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.9\] .* \*\(\*\(b\+1\)\+2\)==5: SUCCESS$ +^\[main.assertion.10\] .* \*\(\*\(b\+1\)\+2\)==0: FAILURE \(if reachable\)$ +^\[main.assertion.11\] .* 1\[b\]\[2\]==5: SUCCESS$ +^\[main.assertion.12\] .* 1\[b\]\[2\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.13\] .* \*\(1\[b\]\+2\)==5: SUCCESS$ +^\[main.assertion.14\] .* \*\(1\[b\]\+2\)==0: FAILURE \(if reachable\)$ +^\[main.assertion.15\] .* \(\*\(1\+b\)\)\[2\]==5: SUCCESS$ +^\[main.assertion.16\] .* \(\*\(1\+b\)\)\[2\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.17\] .* \*\(\*\(1\+b\)\+2\)==5: SUCCESS$ +^\[main.assertion.18\] .* \*\(\*\(1\+b\)\+2\)==0: FAILURE \(if reachable\)$ +^\[main.assertion.19\] .* 2\[1\[b\]\]==5: SUCCESS$ +^\[main.assertion.20\] .* 2\[1\[b\]\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.21\] .* \*\(2\+1\[b\]\)==5: UNKNOWN$ +^\[main.assertion.22\] .* \*\(2\+1\[b\]\)==0: UNKNOWN$ +^\[main.assertion.23\] .* \*\(2\+\*\(1\+b\)\)==5: UNKNOWN$ +^\[main.assertion.24\] .* \*\(2\+\*\(1\+b\)\)==0: UNKNOWN$ +^\[main.assertion.25\] .* a\[0\]\[1\]==0: SUCCESS$ +^\[main.assertion.26\] .* a\[0\]\[1\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.27\] .* a\[0\]\[2\]==0: SUCCESS$ +^\[main.assertion.28\] .* b\[0\]\[1\]==2: UNKNOWN$ +^\[main.assertion.29\] .* b\[0\]\[1\]==3: UNKNOWN$ +^\[main.assertion.30\] .* b\[0\]\[2\]==2: SUCCESS$ +^\[main.assertion.31\] .* a\[i\]\[1\]==0: SUCCESS$ +^\[main.assertion.32\] .* a\[i\]\[1\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.33\] .* a\[1\]\[i\]==0: SUCCESS$ +^\[main.assertion.34\] .* a\[1\]\[i\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.35\] .* a\[i\]\[i\]==0: SUCCESS$ +^\[main.assertion.36\] .* a\[i\]\[i\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.37\] .* a\[j\]\[1\]==0: UNKNOWN$ +^\[main.assertion.38\] .* a\[j\]\[1\]==1: UNKNOWN$ +^\[main.assertion.39\] .* a\[1\]\[j\]==0: UNKNOWN$ +^\[main.assertion.40\] .* a\[1\]\[j\]==1: UNKNOWN$ +^\[main.assertion.41\] .* a\[j\]\[j\]==0: UNKNOWN$ +^\[main.assertion.42\] .* a\[j\]\[j\]==1: UNKNOWN$ +^\[main.assertion.43\] .* b\[i\]\[1\]==1: SUCCESS$ +^\[main.assertion.44\] .* b\[i\]\[1\]==11: FAILURE \(if reachable\)$ +^\[main.assertion.45\] .* b\[1\]\[i\]==3: SUCCESS$ +^\[main.assertion.46\] .* b\[1\]\[i\]==11: FAILURE \(if reachable\)$ +^\[main.assertion.47\] .* b\[i\]\[i\]==0: SUCCESS$ +^\[main.assertion.48\] .* b\[i\]\[i\]==11: FAILURE \(if reachable\)$ +^\[main.assertion.49\] .* b\[j\]\[1\]==1: UNKNOWN$ +^\[main.assertion.50\] .* b\[j\]\[1\]==11: UNKNOWN$ +^\[main.assertion.51\] .* b\[1\]\[j\]==3: UNKNOWN$ +^\[main.assertion.52\] .* b\[1\]\[j\]==11: UNKNOWN$ +^\[main.assertion.53\] .* b\[j\]\[j\]==0: UNKNOWN$ +^\[main.assertion.54\] .* b\[j\]\[j\]==11: UNKNOWN$ +^\[main.assertion.55\] .* a\[100\]\[0\]==0: UNKNOWN$ +^\[main.assertion.56\] .* a\[0\]\[100\]==0: UNKNOWN$ +^\[main.assertion.57\] .* c==0: SUCCESS$ +^\[main.assertion.58\] .* c==0: SUCCESS$ +^\[main.assertion.59\] .* ei\[0\]\[1\]==1: SUCCESS$ +^\[main.assertion.60\] .* ei\[0\]\[1\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.61\] .* ei\[2\]\[1\]==0: SUCCESS$ +^\[main.assertion.62\] .* ei\[2\]\[1\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.63\] .* ej\[0\]\[1\]==0: UNKNOWN$ +^\[main.assertion.64\] .* ej\[2\]\[1\]==0: UNKNOWN$ +^\[main.assertion.65\] .* ek\[0\]\[1\]==0: UNKNOWN$ +^\[main.assertion.66\] .* c==0: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-pointer/test.desc index 732c3a3bfa6..2a3a51682d4 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-array-of-constants-pointer/test.desc @@ -1,71 +1,75 @@ -FUTURE +CORE sensitivity_test_constants_array_of_constants_pointer.c ---variable --arrays --pointers --verify +--variable-sensitivity --vsd-arrays --vsd-pointers --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]==&a0: SUCCESS$ -^\[main.assertion.2\] .* assertion a\[1\]==&a3: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion \*a\[1\]==0: SUCCESS$ -^\[main.assertion.4\] .* assertion \*a\[1\]==3: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion b\[1\]==&b1: SUCCESS$ -^\[main.assertion.6\] .* assertion b\[1\]==&b3: FAILURE \(if reachable\)$ -^\[main.assertion.7\] .* assertion \*b\[1\]==11: SUCCESS$ -^\[main.assertion.8\] .* assertion \*b\[1\]==13: FAILURE \(if reachable\)$ -^\[main.assertion.9\] .* assertion \*\(b\+1\)==&b1: SUCCESS$ -^\[main.assertion.10\] .* assertion \*\(b\+1\)==&b3: FAILURE \(if reachable\)$ -^\[main.assertion.11\] .* assertion \*\(1\+b\)==&b1: UNKNOWN$ -^\[main.assertion.12\] .* assertion \*\(1\+b\)==&b3: UNKNOWN$ -^\[main.assertion.13\] .* assertion 1\[b\]==&b1: SUCCESS$ -^\[main.assertion.14\] .* assertion 1\[b\]==&b3: FAILURE \(if reachable\)$ -^\[main.assertion.15\] .* assertion \*\*\(b\+1\)==11: SUCCESS$ -^\[main.assertion.16\] .* assertion \*\*\(b\+1\)==13: FAILURE \(if reachable\)$ -^\[main.assertion.17\] .* assertion \*\*\(1\+b\)==11: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*\*\(1\+b\)==13: UNKNOWN$ -^\[main.assertion.19\] .* assertion \*1\[b\]==11: SUCCESS$ -^\[main.assertion.20\] .* assertion \*1\[b\]==13: FAILURE \(if reachable\)$ -^\[main.assertion.21\] .* assertion c\[0\]==&c0: UNKNOWN$ -^\[main.assertion.22\] .* assertion c\[0\]==&c3: UNKNOWN$ -^\[main.assertion.23\] .* assertion d\[0\]==&d0: UNKNOWN$ -^\[main.assertion.24\] .* assertion d\[0\]==&d3: UNKNOWN$ -^\[main.assertion.25\] .* assertion \*c\[0\]==20: UNKNOWN$ -^\[main.assertion.26\] .* assertion \*c\[0\]==23: UNKNOWN$ -^\[main.assertion.27\] .* assertion \*d\[0\]==30: UNKNOWN$ -^\[main.assertion.28\] .* assertion \*d\[0\]==33: UNKNOWN$ -^\[main.assertion.29\] .* assertion a\[i\]==&a0: SUCCESS$ -^\[main.assertion.30\] .* assertion a\[i\]==&a3: FAILURE \(if reachable\)$ -^\[main.assertion.31\] .* assertion a\[j\]==&a0: UNKNOWN$ -^\[main.assertion.32\] .* assertion a\[j\]==&a3: UNKNOWN$ -^\[main.assertion.33\] .* assertion \*a\[i\]==0: SUCCESS$ -^\[main.assertion.34\] .* assertion \*a\[i\]==3: FAILURE \(if reachable\)$ -^\[main.assertion.35\] .* assertion \*a\[j\]==0: UNKNOWN$ -^\[main.assertion.36\] .* assertion \*a\[j\]==3: UNKNOWN$ -^\[main.assertion.37\] .* assertion b\[i\]==&b0: SUCCESS$ -^\[main.assertion.38\] .* assertion b\[i\]==&b1: FAILURE \(if reachable\)$ -^\[main.assertion.39\] .* assertion b\[j\]==&b0: UNKNOWN$ -^\[main.assertion.40\] .* assertion b\[j\]==&b3: UNKNOWN$ -^\[main.assertion.41\] .* assertion \*b\[i\]==10: SUCCESS$ -^\[main.assertion.42\] .* assertion \*b\[i\]==11: FAILURE \(if reachable\)$ -^\[main.assertion.43\] .* assertion \*b\[j\]==10: UNKNOWN$ -^\[main.assertion.44\] .* assertion \*b\[j\]==13: UNKNOWN$ -^\[main.assertion.45\] .* assertion a\[100\]==&a2: UNKNOWN$ -^\[main.assertion.46\] .* assertion \*a\[100\]==2: UNKNOWN$ -^\[main.assertion.47\] .* assertion b\[1\]==&b1: SUCCESS$ -^\[main.assertion.48\] .* assertion \*b\[1\]==11: SUCCESS$ -^\[main.assertion.49\] .* assertion ei\[0\]==&ei1: SUCCESS$ -^\[main.assertion.50\] .* assertion ei\[0\]==&ei0: FAILURE \(if reachable\)$ -^\[main.assertion.51\] .* assertion ei\[2\]==&ei0: SUCCESS$ -^\[main.assertion.52\] .* assertion ei\[2\]==&ei1: FAILURE \(if reachable\)$ -^\[main.assertion.53\] .* assertion \*ei\[0\]==41: SUCCESS$ -^\[main.assertion.54\] .* assertion \*ei\[0\]==40: FAILURE \(if reachable\)$ -^\[main.assertion.55\] .* assertion \*ei\[2\]==40: SUCCESS$ -^\[main.assertion.56\] .* assertion \*ei\[2\]==41: FAILURE \(if reachable\)$ -^\[main.assertion.57\] .* assertion ej\[0\]==&ej0: UNKNOWN$ -^\[main.assertion.58\] .* assertion ej\[2\]==&ej0: UNKNOWN$ -^\[main.assertion.59\] .* assertion ej\[2\]==&ej1: UNKNOWN$ -^\[main.assertion.60\] .* assertion \*ej\[0\]==50: UNKNOWN$ -^\[main.assertion.61\] .* assertion \*ej\[2\]==50: UNKNOWN$ -^\[main.assertion.62\] .* assertion \*ej\[2\]==51: UNKNOWN$ -^\[main.assertion.63\] .* assertion ek\[0\]==&ek0: UNKNOWN$ -^\[main.assertion.64\] .* assertion \*ek\[0\]==60: UNKNOWN$ +^\[main.assertion.1\] .* a\[1\]==&a0: SUCCESS$ +^\[main.assertion.2\] .* a\[1\]==&a3: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* \*a\[1\]==0: SUCCESS$ +^\[main.assertion.4\] .* \*a\[1\]==3: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* b\[1\]==&b1: SUCCESS$ +^\[main.assertion.6\] .* b\[1\]==&b3: FAILURE \(if reachable\)$ +^\[main.assertion.7\] .* \*b\[1\]==11: SUCCESS$ +^\[main.assertion.8\] .* \*b\[1\]==13: FAILURE \(if reachable\)$ +^\[main.assertion.9\] .* \*\(b\+1\)==&b1: SUCCESS$ +^\[main.assertion.10\] .* \*\(b\+1\)==&b3: FAILURE \(if reachable\)$ +^\[main.assertion.11\] .* \*\(1\+b\)==&b1: SUCCESS$ +^\[main.assertion.12\] .* \*\(1\+b\)==&b3: FAILURE \(if reachable\)$ +^\[main.assertion.13\] .* 1\[b\]==&b1: SUCCESS$ +^\[main.assertion.14\] .* 1\[b\]==&b3: FAILURE \(if reachable\)$ +^\[main.assertion.15\] .* \*\*\(b\+1\)==11: SUCCESS$ +^\[main.assertion.16\] .* \*\*\(b\+1\)==13: FAILURE \(if reachable\)$ +^\[main.assertion.17\] .* \*\*\(1\+b\)==11: SUCCESS$ +^\[main.assertion.18\] .* \*\*\(1\+b\)==13: FAILURE \(if reachable\)$ +^\[main.assertion.19\] .* \*1\[b\]==11: SUCCESS$ +^\[main.assertion.20\] .* \*1\[b\]==13: FAILURE \(if reachable\)$ +^\[main.assertion.21\] .* c\[0\]==&c0: UNKNOWN$ +^\[main.assertion.22\] .* c\[0\]==&c3: UNKNOWN$ +^\[main.assertion.23\] .* d\[0\]==&d0: UNKNOWN$ +^\[main.assertion.24\] .* d\[0\]==&d3: UNKNOWN$ +^\[main.assertion.25\] .* \*c\[0\]==20: UNKNOWN$ +^\[main.assertion.26\] .* \*c\[0\]==23: UNKNOWN$ +^\[main.assertion.27\] .* \*d\[0\]==30: UNKNOWN$ +^\[main.assertion.28\] .* \*d\[0\]==33: UNKNOWN$ +^\[main.assertion.29\] .* a\[i\]==&a0: SUCCESS$ +^\[main.assertion.30\] .* a\[i\]==&a3: FAILURE \(if reachable\)$ +^\[main.assertion.31\] .* a\[j\]==&a0: UNKNOWN$ +^\[main.assertion.32\] .* a\[j\]==&a3: UNKNOWN$ +^\[main.assertion.33\] .* \*a\[i\]==0: SUCCESS$ +^\[main.assertion.34\] .* \*a\[i\]==3: FAILURE \(if reachable\)$ +^\[main.assertion.35\] .* \*a\[j\]==0: UNKNOWN$ +^\[main.assertion.36\] .* \*a\[j\]==3: UNKNOWN$ +^\[main.assertion.37\] .* b\[i\]==&b0: SUCCESS$ +^\[main.assertion.38\] .* b\[i\]==&b1: FAILURE \(if reachable\)$ +^\[main.assertion.39\] .* b\[j\]==&b0: UNKNOWN$ +^\[main.assertion.40\] .* b\[j\]==&b3: UNKNOWN$ +^\[main.assertion.41\] .* \*b\[i\]==10: SUCCESS$ +^\[main.assertion.42\] .* \*b\[i\]==11: FAILURE \(if reachable\)$ +^\[main.assertion.43\] .* \*b\[j\]==10: UNKNOWN$ +^\[main.assertion.44\] .* \*b\[j\]==13: UNKNOWN$ +^\[main.assertion.45\] .* a\[100\]==&a2: UNKNOWN$ +^\[main.assertion.46\] .* \*a\[100\]==2: UNKNOWN$ +^\[main.assertion.47\] .* b\[1\]==&b1: SUCCESS$ +^\[main.assertion.48\] .* \*b\[1\]==11: SUCCESS$ +^\[main.assertion.49\] .* ei\[0\]==&ei1: SUCCESS$ +^\[main.assertion.50\] .* ei\[0\]==&ei0: FAILURE \(if reachable\)$ +^\[main.assertion.51\] .* ei\[2\]==&ei0: SUCCESS$ +^\[main.assertion.52\] .* ei\[2\]==&ei1: FAILURE \(if reachable\)$ +^\[main.assertion.53\] .* \*ei\[0\]==41: SUCCESS$ +^\[main.assertion.54\] .* \*ei\[0\]==40: FAILURE \(if reachable\)$ +^\[main.assertion.55\] .* \*ei\[2\]==40: SUCCESS$ +^\[main.assertion.56\] .* \*ei\[2\]==41: FAILURE \(if reachable\)$ +^\[main.assertion.57\] .* ej\[0\]==&ej0: UNKNOWN$ +^\[main.assertion.58\] .* ej\[2\]==&ej0: UNKNOWN$ +^\[main.assertion.59\] .* ej\[2\]==&ej1: UNKNOWN$ +^\[main.assertion.60\] .* \*ej\[0\]==50: UNKNOWN$ +^\[main.assertion.61\] .* \*ej\[2\]==50: UNKNOWN$ +^\[main.assertion.62\] .* \*ej\[2\]==51: UNKNOWN$ +^\[main.assertion.63\] .* ek\[0\]==&ek0: UNKNOWN$ +^\[main.assertion.64\] .* \*ek\[0\]==60: UNKNOWN$ +^\[main\.assertion\.65\] .* \*ps\[0\]==4: SUCCESS$ +^\[main\.assertion\.66\] .* \*ps\[1\]==4: UNKNOWN$ +^\[main\.assertion\.67\] .* x==4: SUCCESS$ +^\[main\.assertion\.68\] .* y==4: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-array-of-two-value-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-constants-array-of-two-value-pointer/test.desc index bb6c1ee2f7a..39ca55caf4e 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-array-of-two-value-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-array-of-two-value-pointer/test.desc @@ -1,71 +1,75 @@ -FUTURE +CORE sensitivity_test_constants_array_of_two_value_pointer.c ---variable --arrays --verify +--variable-sensitivity --vsd-arrays --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]==&a0: UNKNOWN$ -^\[main.assertion.2\] .* assertion a\[1\]==&a3: UNKNOWN$ -^\[main.assertion.3\] .* assertion \*a\[1\]==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion \*a\[1\]==3: UNKNOWN$ -^\[main.assertion.5\] .* assertion b\[1\]==&b1: UNKNOWN$ -^\[main.assertion.6\] .* assertion b\[1\]==&b3: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*b\[1\]==11: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*b\[1\]==13: UNKNOWN$ -^\[main.assertion.9\] .* assertion \*\(b\+1\)==&b1: UNKNOWN$ -^\[main.assertion.10\] .* assertion \*\(b\+1\)==&b3: UNKNOWN$ -^\[main.assertion.11\] .* assertion \*\(1\+b\)==&b1: UNKNOWN$ -^\[main.assertion.12\] .* assertion \*\(1\+b\)==&b3: UNKNOWN$ -^\[main.assertion.13\] .* assertion 1\[b\]==&b1: UNKNOWN$ -^\[main.assertion.14\] .* assertion 1\[b\]==&b3: UNKNOWN$ -^\[main.assertion.15\] .* assertion \*\*\(b\+1\)==11: UNKNOWN$ -^\[main.assertion.16\] .* assertion \*\*\(b\+1\)==13: UNKNOWN$ -^\[main.assertion.17\] .* assertion \*\*\(1\+b\)==11: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*\*\(1\+b\)==13: UNKNOWN$ -^\[main.assertion.19\] .* assertion \*1\[b\]==11: UNKNOWN$ -^\[main.assertion.20\] .* assertion \*1\[b\]==13: UNKNOWN$ -^\[main.assertion.21\] .* assertion c\[0\]==&c0: UNKNOWN$ -^\[main.assertion.22\] .* assertion c\[0\]==&c3: UNKNOWN$ -^\[main.assertion.23\] .* assertion d\[0\]==&d0: UNKNOWN$ -^\[main.assertion.24\] .* assertion d\[0\]==&d3: UNKNOWN$ -^\[main.assertion.25\] .* assertion \*c\[0\]==20: UNKNOWN$ -^\[main.assertion.26\] .* assertion \*c\[0\]==23: UNKNOWN$ -^\[main.assertion.27\] .* assertion \*d\[0\]==30: UNKNOWN$ -^\[main.assertion.28\] .* assertion \*d\[0\]==33: UNKNOWN$ -^\[main.assertion.29\] .* assertion a\[i\]==&a0: UNKNOWN$ -^\[main.assertion.30\] .* assertion a\[i\]==&a3: UNKNOWN$ -^\[main.assertion.31\] .* assertion a\[j\]==&a0: UNKNOWN$ -^\[main.assertion.32\] .* assertion a\[j\]==&a3: UNKNOWN$ -^\[main.assertion.33\] .* assertion \*a\[i\]==0: UNKNOWN$ -^\[main.assertion.34\] .* assertion \*a\[i\]==3: UNKNOWN$ -^\[main.assertion.35\] .* assertion \*a\[j\]==0: UNKNOWN$ -^\[main.assertion.36\] .* assertion \*a\[j\]==3: UNKNOWN$ -^\[main.assertion.37\] .* assertion b\[i\]==&b0: UNKNOWN$ -^\[main.assertion.38\] .* assertion b\[i\]==&b1: UNKNOWN$ -^\[main.assertion.39\] .* assertion b\[j\]==&b0: UNKNOWN$ -^\[main.assertion.40\] .* assertion b\[j\]==&b3: UNKNOWN$ -^\[main.assertion.41\] .* assertion \*b\[i\]==10: UNKNOWN$ -^\[main.assertion.42\] .* assertion \*b\[i\]==11: UNKNOWN$ -^\[main.assertion.43\] .* assertion \*b\[j\]==10: UNKNOWN$ -^\[main.assertion.44\] .* assertion \*b\[j\]==13: UNKNOWN$ -^\[main.assertion.45\] .* assertion a\[100\]==&a2: UNKNOWN$ -^\[main.assertion.46\] .* assertion \*a\[100\]==2: UNKNOWN$ -^\[main.assertion.47\] .* assertion b\[1\]==&b1: UNKNOWN$ -^\[main.assertion.48\] .* assertion \*b\[1\]==11: UNKNOWN$ -^\[main.assertion.49\] .* assertion ei\[0\]==&ei1: UNKNOWN$ -^\[main.assertion.50\] .* assertion ei\[0\]==&ei0: UNKNOWN$ -^\[main.assertion.51\] .* assertion ei\[2\]==&ei0: UNKNOWN$ -^\[main.assertion.52\] .* assertion ei\[2\]==&ei1: UNKNOWN$ -^\[main.assertion.53\] .* assertion \*ei\[0\]==41: UNKNOWN$ -^\[main.assertion.54\] .* assertion \*ei\[0\]==40: UNKNOWN$ -^\[main.assertion.55\] .* assertion \*ei\[2\]==40: UNKNOWN$ -^\[main.assertion.56\] .* assertion \*ei\[2\]==41: UNKNOWN$ -^\[main.assertion.57\] .* assertion ej\[0\]==&ej0: UNKNOWN$ -^\[main.assertion.58\] .* assertion ej\[2\]==&ej0: UNKNOWN$ -^\[main.assertion.59\] .* assertion ej\[2\]==&ej1: UNKNOWN$ -^\[main.assertion.60\] .* assertion \*ej\[0\]==50: UNKNOWN$ -^\[main.assertion.61\] .* assertion \*ej\[2\]==50: UNKNOWN$ -^\[main.assertion.62\] .* assertion \*ej\[2\]==51: UNKNOWN$ -^\[main.assertion.63\] .* assertion ek\[0\]==&ek0: UNKNOWN$ -^\[main.assertion.64\] .* assertion \*ek\[0\]==60: UNKNOWN$ +^\[main.assertion.1\] .* a\[1\]==&a0: UNKNOWN$ +^\[main.assertion.2\] .* a\[1\]==&a3: UNKNOWN$ +^\[main.assertion.3\] .* \*a\[1\]==0: UNKNOWN$ +^\[main.assertion.4\] .* \*a\[1\]==3: UNKNOWN$ +^\[main.assertion.5\] .* b\[1\]==&b1: UNKNOWN$ +^\[main.assertion.6\] .* b\[1\]==&b3: UNKNOWN$ +^\[main.assertion.7\] .* \*b\[1\]==11: UNKNOWN$ +^\[main.assertion.8\] .* \*b\[1\]==13: UNKNOWN$ +^\[main.assertion.9\] .* \*\(b\+1\)==&b1: UNKNOWN$ +^\[main.assertion.10\] .* \*\(b\+1\)==&b3: UNKNOWN$ +^\[main.assertion.11\] .* \*\(1\+b\)==&b1: UNKNOWN$ +^\[main.assertion.12\] .* \*\(1\+b\)==&b3: UNKNOWN$ +^\[main.assertion.13\] .* 1\[b\]==&b1: UNKNOWN$ +^\[main.assertion.14\] .* 1\[b\]==&b3: UNKNOWN$ +^\[main.assertion.15\] .* \*\*\(b\+1\)==11: UNKNOWN$ +^\[main.assertion.16\] .* \*\*\(b\+1\)==13: UNKNOWN$ +^\[main.assertion.17\] .* \*\*\(1\+b\)==11: UNKNOWN$ +^\[main.assertion.18\] .* \*\*\(1\+b\)==13: UNKNOWN$ +^\[main.assertion.19\] .* \*1\[b\]==11: UNKNOWN$ +^\[main.assertion.20\] .* \*1\[b\]==13: UNKNOWN$ +^\[main.assertion.21\] .* c\[0\]==&c0: UNKNOWN$ +^\[main.assertion.22\] .* c\[0\]==&c3: UNKNOWN$ +^\[main.assertion.23\] .* d\[0\]==&d0: UNKNOWN$ +^\[main.assertion.24\] .* d\[0\]==&d3: UNKNOWN$ +^\[main.assertion.25\] .* \*c\[0\]==20: UNKNOWN$ +^\[main.assertion.26\] .* \*c\[0\]==23: UNKNOWN$ +^\[main.assertion.27\] .* \*d\[0\]==30: UNKNOWN$ +^\[main.assertion.28\] .* \*d\[0\]==33: UNKNOWN$ +^\[main.assertion.29\] .* a\[i\]==&a0: UNKNOWN$ +^\[main.assertion.30\] .* a\[i\]==&a3: UNKNOWN$ +^\[main.assertion.31\] .* a\[j\]==&a0: UNKNOWN$ +^\[main.assertion.32\] .* a\[j\]==&a3: UNKNOWN$ +^\[main.assertion.33\] .* \*a\[i\]==0: UNKNOWN$ +^\[main.assertion.34\] .* \*a\[i\]==3: UNKNOWN$ +^\[main.assertion.35\] .* \*a\[j\]==0: UNKNOWN$ +^\[main.assertion.36\] .* \*a\[j\]==3: UNKNOWN$ +^\[main.assertion.37\] .* b\[i\]==&b0: UNKNOWN$ +^\[main.assertion.38\] .* b\[i\]==&b1: UNKNOWN$ +^\[main.assertion.39\] .* b\[j\]==&b0: UNKNOWN$ +^\[main.assertion.40\] .* b\[j\]==&b3: UNKNOWN$ +^\[main.assertion.41\] .* \*b\[i\]==10: UNKNOWN$ +^\[main.assertion.42\] .* \*b\[i\]==11: UNKNOWN$ +^\[main.assertion.43\] .* \*b\[j\]==10: UNKNOWN$ +^\[main.assertion.44\] .* \*b\[j\]==13: UNKNOWN$ +^\[main.assertion.45\] .* a\[100\]==&a2: UNKNOWN$ +^\[main.assertion.46\] .* \*a\[100\]==2: UNKNOWN$ +^\[main.assertion.47\] .* b\[1\]==&b1: UNKNOWN$ +^\[main.assertion.48\] .* \*b\[1\]==11: UNKNOWN$ +^\[main.assertion.49\] .* ei\[0\]==&ei1: UNKNOWN$ +^\[main.assertion.50\] .* ei\[0\]==&ei0: UNKNOWN$ +^\[main.assertion.51\] .* ei\[2\]==&ei0: UNKNOWN$ +^\[main.assertion.52\] .* ei\[2\]==&ei1: UNKNOWN$ +^\[main.assertion.53\] .* \*ei\[0\]==41: UNKNOWN$ +^\[main.assertion.54\] .* \*ei\[0\]==40: UNKNOWN$ +^\[main.assertion.55\] .* \*ei\[2\]==40: UNKNOWN$ +^\[main.assertion.56\] .* \*ei\[2\]==41: UNKNOWN$ +^\[main.assertion.57\] .* ej\[0\]==&ej0: UNKNOWN$ +^\[main.assertion.58\] .* ej\[2\]==&ej0: UNKNOWN$ +^\[main.assertion.59\] .* ej\[2\]==&ej1: UNKNOWN$ +^\[main.assertion.60\] .* \*ej\[0\]==50: UNKNOWN$ +^\[main.assertion.61\] .* \*ej\[2\]==50: UNKNOWN$ +^\[main.assertion.62\] .* \*ej\[2\]==51: UNKNOWN$ +^\[main.assertion.63\] .* ek\[0\]==&ek0: UNKNOWN$ +^\[main.assertion.64\] .* \*ek\[0\]==60: UNKNOWN$ +^\[main\.assertion\.65\] .* \*ps\[0\]==4: UNKNOWN$ +^\[main\.assertion\.66\] .* \*ps\[1\]==4: UNKNOWN$ +^\[main\.assertion\.67\] .* x==4: UNKNOWN$ +^\[main\.assertion\.68\] .* y==4: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-array/test.desc b/regression/goto-analyzer/sensitivity-test-constants-array/test.desc index 99372ff72af..8689736d76e 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-array/test.desc @@ -1,39 +1,39 @@ -FUTURE +CORE sensitivity_test_constants_array.c ---variable --arrays --verify +--variable-sensitivity --vsd-arrays --verify --vsd-pointers ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]==0: SUCCESS$ -^\[main.assertion.2\] .* assertion a\[1\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion b\[1\]==0: SUCCESS$ -^\[main.assertion.4\] .* assertion b\[1\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion \*\(b\+1\)==0: SUCCESS$ -^\[main.assertion.6\] .* assertion \*\(b\+1\)==1: FAILURE \(if reachable\)$ -^\[main.assertion.7\] .* assertion \*\(1\+b\)==0: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*\(1\+b\)==1: UNKNOWN$ -^\[main.assertion.9\] .* assertion 1\[b\]==0: SUCCESS$ -^\[main.assertion.10\] .* assertion 1\[b\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.11\] .* assertion c\[0\]==0: SUCCESS$ -^\[main.assertion.12\] .* assertion c\[0\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.13\] .* assertion d\[0\]==0: UNKNOWN$ -^\[main.assertion.14\] .* assertion d\[0\]==2: UNKNOWN$ -^\[main.assertion.15\] .* assertion d\[1\]==0: SUCCESS$ -^\[main.assertion.16\] .* assertion a\[i\]==0: SUCCESS$ -^\[main.assertion.17\] .* assertion a\[i\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.18\] .* assertion a\[j\]==0: UNKNOWN$ -^\[main.assertion.19\] .* assertion a\[j\]==1: UNKNOWN$ -^\[main.assertion.20\] .* assertion b\[i\]==1: SUCCESS$ -^\[main.assertion.21\] .* assertion b\[i\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.22\] .* assertion b\[j\]==0: UNKNOWN$ -^\[main.assertion.23\] .* assertion b\[j\]==1: UNKNOWN$ -^\[main.assertion.24\] .* assertion a\[100\]==0: UNKNOWN$ -^\[main.assertion.25\] .* assertion b\[1\]==0: SUCCESS$ -^\[main.assertion.26\] .* assertion ei\[0\]==1: SUCCESS$ -^\[main.assertion.27\] .* assertion ei\[0\]==0: FAILURE \(if reachable\)$ -^\[main.assertion.28\] .* assertion ei\[2\]==0: SUCCESS$ -^\[main.assertion.29\] .* assertion ei\[2\]==1: FAILURE \(if reachable\)$ -^\[main.assertion.30\] .* assertion ej\[0\]==0: UNKNOWN$ -^\[main.assertion.31\] .* assertion ej\[2\]==0: UNKNOWN$ -^\[main.assertion.32\] .* assertion ek\[0\]==0: UNKNOWN$ +^\[main.assertion.1\] .* a\[1\]==0: SUCCESS$ +^\[main.assertion.2\] .* a\[1\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* b\[1\]==0: SUCCESS$ +^\[main.assertion.4\] .* b\[1\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* \*\(b\+1\)==0: SUCCESS$ +^\[main.assertion.6\] .* \*\(b\+1\)==1: FAILURE \(if reachable\)$ +^\[main.assertion.7\] .* \*\(1\+b\)==0: SUCCESS$ +^\[main.assertion.8\] .* \*\(1\+b\)==1: FAILURE \(if reachable\)$ +^\[main.assertion.9\] .* 1\[b\]==0: SUCCESS$ +^\[main.assertion.10\] .* 1\[b\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.11\] .* c\[0\]==0: SUCCESS$ +^\[main.assertion.12\] .* c\[0\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.13\] .* d\[0\]==0: UNKNOWN$ +^\[main.assertion.14\] .* d\[0\]==2: UNKNOWN$ +^\[main.assertion.15\] .* d\[1\]==0: SUCCESS$ +^\[main.assertion.16\] .* a\[i\]==0: SUCCESS$ +^\[main.assertion.17\] .* a\[i\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.18\] .* a\[j\]==0: UNKNOWN$ +^\[main.assertion.19\] .* a\[j\]==1: UNKNOWN$ +^\[main.assertion.20\] .* b\[i\]==1: SUCCESS$ +^\[main.assertion.21\] .* b\[i\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.22\] .* b\[j\]==0: UNKNOWN$ +^\[main.assertion.23\] .* b\[j\]==1: UNKNOWN$ +^\[main.assertion.24\] .* a\[100\]==0: UNKNOWN$ +^\[main.assertion.25\] .* b\[1\]==0: SUCCESS$ +^\[main.assertion.26\] .* ei\[0\]==1: SUCCESS$ +^\[main.assertion.27\] .* ei\[0\]==0: FAILURE \(if reachable\)$ +^\[main.assertion.28\] .* ei\[2\]==0: SUCCESS$ +^\[main.assertion.29\] .* ei\[2\]==1: FAILURE \(if reachable\)$ +^\[main.assertion.30\] .* ej\[0\]==0: UNKNOWN$ +^\[main.assertion.31\] .* ej\[2\]==0: UNKNOWN$ +^\[main.assertion.32\] .* ek\[0\]==0: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-char/test.desc b/regression/goto-analyzer/sensitivity-test-constants-char/test.desc index 27efb249b66..b5e8dc61dd2 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-char/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-char/test.desc @@ -1,9 +1,9 @@ -FUTURE +CORE sensitivity_test_constants_char.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x=='a': SUCCESS$ -^\[main.assertion.2\] .* assertion x=='b': FAILURE \(if reachable\)$ +^\[main.assertion.1\] .* x=='a': SUCCESS$ +^\[main.assertion.2\] .* x=='b': FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-float/test.desc b/regression/goto-analyzer/sensitivity-test-constants-float/test.desc index 4f7f3d7a299..03aca800b8c 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-float/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-float/test.desc @@ -1,9 +1,9 @@ -FUTURE +CORE sensitivity_test_constants_float.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x==0.0: SUCCESS$ -^\[main.assertion.2\] .* assertion x==1.0: FAILURE \(if reachable\)$ +^\[main.assertion.1\] .* x==0.0: SUCCESS$ +^\[main.assertion.2\] .* x==1.0: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-int/test.desc b/regression/goto-analyzer/sensitivity-test-constants-int/test.desc index e92a19ef927..83c3f7bc565 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-int/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-int/test.desc @@ -1,30 +1,35 @@ -FUTURE +CORE sensitivity_test_constants_int.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x==0: SUCCESS$ -^\[main.assertion.2\] .* assertion x==1: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion x==y: UNKNOWN$ -^\[main.assertion.4\] .* assertion x<1: SUCCESS$ -^\[main.assertion.5\] .* assertion x<-1: FAILURE \(if reachable\)$ -^\[main.assertion.6\] .* assertion x-1: SUCCESS$ -^\[main.assertion.8\] .* assertion x>1: FAILURE \(if reachable\)$ -^\[main.assertion.9\] .* assertion x>y: UNKNOWN$ -^\[main.assertion.10\] .* assertion x!=1: SUCCESS$ -^\[main.assertion.11\] .* assertion x!=0: FAILURE \(if reachable\)$ -^\[main.assertion.12\] .* assertion x!=y: UNKNOWN$ -^\[main.assertion.13\] .* assertion !\(x==1\): SUCCESS$ -^\[main.assertion.14\] .* assertion !\(x==0\): FAILURE \(if reachable\)$ -^\[main.assertion.15\] .* assertion !\(x==y\): UNKNOWN$ -^\[main.assertion.16\] .* assertion y<2: UNKNOWN$ -^\[main.assertion.17\] .* assertion y>2: UNKNOWN$ -^\[main.assertion.18\] .* assertion y==1: UNKNOWN$ -^\[main.assertion.19\] .* assertion z==0: SUCCESS$ -^\[main.assertion.20\] .* assertion z==10: FAILURE \(if reachable\)$ -^\[main.assertion.21\] .* assertion x==0: SUCCESS \(unreachable\)$ -^\[main.assertion.22\] .* assertion x==1: SUCCESS \(unreachable\)$ -^\[main.assertion.23\] .* assertion y==0: SUCCESS \(unreachable\)$ +^\[main.assertion.1\] .* x==0: SUCCESS$ +^\[main.assertion.2\] .* x==1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* x==y: UNKNOWN$ +^\[main.assertion.4\] .* x<1: SUCCESS$ +^\[main.assertion.5\] .* x<-1: FAILURE \(if reachable\)$ +^\[main.assertion.6\] .* x-1: SUCCESS$ +^\[main.assertion.8\] .* x>1: FAILURE \(if reachable\)$ +^\[main.assertion.9\] .* x>y: UNKNOWN$ +^\[main.assertion.10\] .* x!=1: SUCCESS$ +^\[main.assertion.11\] .* x!=0: FAILURE \(if reachable\)$ +^\[main.assertion.12\] .* x!=y: UNKNOWN$ +^\[main.assertion.13\] .* !\(x==1\): SUCCESS$ +^\[main.assertion.14\] .* !\(x==0\): FAILURE \(if reachable\)$ +^\[main.assertion.15\] .* !\(x==y\): UNKNOWN$ +^\[main.assertion.16\] .* y<2: UNKNOWN$ +^\[main.assertion.17\] .* y>2: UNKNOWN$ +^\[main.assertion.18\] .* y==1: UNKNOWN$ +^\[main.assertion.19\] .* z==0: SUCCESS$ +^\[main.assertion.20\] .* z==10: FAILURE \(if reachable\)$ +^\[main.assertion.21\] .* x==0: SUCCESS \(unreachable\)$ +^\[main.assertion.22\] .* x==1: SUCCESS \(unreachable\)$ +^\[main.assertion.23\] .* y==0: SUCCESS \(unreachable\)$ +^\[main.assertion.24\] .* a1==0: UNKNOWN$ +^\[main.assertion.25\] .* a2==0: UNKNOWN$ +^\[main.assertion.26\] .* a3==0: UNKNOWN$ +^\[main.assertion.27\] .* a4==0: SUCCESS$ +^\[main.assertion.28\] .* a5==0: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-array/test.desc b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-array/test.desc index da6eb8848c7..cb8b1fc0385 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-array/test.desc @@ -1,21 +1,29 @@ -FUTURE +CORE sensitivity_test_constants_pointer_to_constants_array.c ---variable --pointers --arrays --verify +--variable-sensitivity --vsd-pointers --vsd-arrays --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion p==&a\[0\]: SUCCESS$ -^\[main.assertion.2\] .* assertion \*p==1: SUCCESS$ -^\[main.assertion.3\] .* assertion q==p\+1: UNKNOWN$ -^\[main.assertion.4\] .* assertion \*q==2: UNKNOWN$ -^\[main.assertion.5\] .* assertion q-p==x: UNKNOWN$ -^\[main.assertion.6\] .* assertion a\[1\]==4: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*r==2: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*r==1: UNKNOWN$ -^\[main.assertion.9\] .* assertion \*s==0: UNKNOWN$ -^\[main.assertion.10\] .* assertion \*s==1: UNKNOWN$ -^\[main.assertion.11\] .* assertion t==p\+i: UNKNOWN$ -^\[main.assertion.12\] .* assertion t-p==y: UNKNOWN$ -^\[main.assertion.13\] .* assertion a\[i\]==5: UNKNOWN$ -^\[main.assertion.14\] .* assertion a\[1\]==5: UNKNOWN$ +^\[main.assertion.1\] .* p==&a\[0\]: SUCCESS$ +^\[main.assertion.2\] .* \*p==1: SUCCESS$ +^\[main\.assertion\.3\] .* p\[1\]==2: SUCCESS$ +^\[main\.assertion\.4\] .* 1\[p\]==2: SUCCESS$ +^\[main\.assertion\.5\] .* \*\(p\+1\)==2: SUCCESS$ +^\[main\.assertion\.6\] .* \*\(1\+p\)==2: SUCCESS$ +^\[main\.assertion\.7\] .* q==p\+1: SUCCESS$ +^\[main\.assertion\.8\] .* \*q==2: SUCCESS$ +^\[main\.assertion\.9\] .* \*\(q-1\)==1: SUCCESS$ +^\[main\.assertion\.10\] .* q-p==x: UNKNOWN$ +^\[main\.assertion\.11\] .* a\[1\]==4: SUCCESS$ +^\[main\.assertion\.12\] .* a\[1\]==5: SUCCESS$ +^\[main\.assertion\.13\] .* a\[1\]==6: SUCCESS$ +^\[main\.assertion\.14\] .* a\[1\]==7: SUCCESS$ +^\[main\.assertion\.15\] .* \*r==2: UNKNOWN$ +^\[main\.assertion\.16\] .* \*r==1: UNKNOWN$ +^\[main\.assertion\.17\] .* \*s==0: UNKNOWN$ +^\[main\.assertion\.18\] .* \*s==1: UNKNOWN$ +^\[main\.assertion\.19\] .* t==p\+i: UNKNOWN$ +^\[main\.assertion\.20\] .* t-p==y: UNKNOWN$ +^\[main\.assertion\.21\] .* a\[i\]==5: UNKNOWN$ +^\[main\.assertion\.22\] .* a\[1\]==5: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-pointer/test.desc index b8f474c3d55..4d60991a5f3 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-pointer/test.desc @@ -1,13 +1,13 @@ -FUTURE +CORE sensitivity_test_constants_pointer_to_constants_pointer.c ---variable --pointers --verify +--variable-sensitivity --vsd-pointers --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion \*\*x==0: SUCCESS$ -^\[main.assertion.2\] .* assertion \*\*x==1: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion \*\*x==1: SUCCESS$ -^\[main.assertion.4\] .* assertion \*\*x==0: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion a==2: SUCCESS$ -^\[main.assertion.6\] .* assertion a==1: FAILURE \(if reachable\)$ +^\[main.assertion.1\] .* \*\*x==0: SUCCESS$ +^\[main.assertion.2\] .* \*\*x==1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* \*\*x==1: SUCCESS$ +^\[main.assertion.4\] .* \*\*x==0: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* a==2: SUCCESS$ +^\[main.assertion.6\] .* a==1: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-struct/test.desc b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-struct/test.desc index 234895fc2dd..c573d6f5438 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-constants-struct/test.desc @@ -1,16 +1,30 @@ -FUTURE +CORE sensitivity_test_constants_pointer_to_constants_struct.c ---variable --pointers --structs --verify +--variable-sensitivity --vsd-pointers --vsd-structs --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion \(\*p\).a==0: SUCCESS$ -^\[main.assertion.2\] .* assertion \(\*p\).a==1: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion p->a==0: SUCCESS$ -^\[main.assertion.4\] .* assertion p->a==1: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion p->b==2.0: SUCCESS$ -^\[main.assertion.6\] .* assertion p->b==1.0: FAILURE \(if reachable\)$ +^\[main\.assertion\.1\] .* \(\*p\).a==0: SUCCESS$ +^\[main\.assertion\.2\] .* \(\*p\).a==1: FAILURE \(if reachable\)$ +^\[main\.assertion\.3\] .* p->a==0: SUCCESS$ +^\[main\.assertion\.4\] .* p->a==1: FAILURE \(if reachable\)$ +^\[main\.assertion\.5\] .* p->b==2.0: SUCCESS$ +^\[main\.assertion\.6\] .* p->b==1.0: FAILURE \(if reachable\)$ +^\[main\.assertion\.9\] .* \*comp_p==0: SUCCESS$ +^\[main\.assertion\.10\] .* \*comp_p==1: FAILURE \(if reachable\)$ +^\[main\.assertion\.13\] .* \*compb_p==2.0: SUCCESS$ +^\[main\.assertion\.14\] .* \*compb_p==1.0: FAILURE \(if reachable\)$ +^\[main\.assertion\.17\] .* \*implicit_p==0: UNKNOWN$ +^\[main\.assertion\.18\] .* \*implicit_p==1: UNKNOWN$ +^\[main\.assertion\.19\] .* x.a==5: UNKNOWN$ +^\[main\.assertion\.20\] .* x.a==1: UNKNOWN$ -- ^warning: ignoring -- -The final two assertions are the wrong way round as modifying the pointer -does not seem to be propogating through. See #96 +The following assertions are not checked since simplify_expr doesn't handle +them. See issue diffblue/cbmc-toyota#145 +^\[main\.assertion\.7\] .* comp_p==&x.a: SUCCESS +^\[main\.assertion\.8\] .* comp_p==&x.b: FAILURE \(if reachable\)$ +^\[main\.assertion\.11\] .* compb_p==&x.a: FAILURE \(if reachable\)$ +^\[main\.assertion\.12\] .* compb_p==&x.b: SUCCESS +^\[main\.assertion\.15\] .* implicit_p==&x.a: SUCCESS +^\[main\.assertion\.16\] .* implicit_p==&x: SUCCESS diff --git a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-array/test.desc b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-array/test.desc index 57f31ec109a..d099fabd811 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-array/test.desc @@ -1,21 +1,29 @@ -FUTURE +CORE sensitivity_test_constants_pointer_to_two_value_array.c ---variable --pointers --verify +--variable-sensitivity --vsd-pointers --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion p==&a\[0\]: SUCCESS$ -^\[main.assertion.2\] .* assertion \*p==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion q==p\+1: UNKNOWN$ -^\[main.assertion.4\] .* assertion \*q==2: UNKNOWN$ -^\[main.assertion.5\] .* assertion q-p==x: UNKNOWN$ -^\[main.assertion.6\] .* assertion a\[1\]==4: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*r==2: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*r==1: UNKNOWN$ -^\[main.assertion.9\] .* assertion \*s==0: UNKNOWN$ -^\[main.assertion.10\] .* assertion \*s==1: UNKNOWN$ -^\[main.assertion.11\] .* assertion t==p\+i: UNKNOWN$ -^\[main.assertion.12\] .* assertion t-p==y: UNKNOWN$ -^\[main.assertion.13\] .* assertion a\[i\]==5: UNKNOWN$ -^\[main.assertion.14\] .* assertion a\[1\]==5: UNKNOWN$ +^\[main.assertion.1\] .* p==&a\[0\]: SUCCESS$ +^\[main.assertion.2\] .* \*p==1: UNKNOWN$ +^\[main\.assertion\.3\] .* p\[1\]==2: UNKNOWN$ +^\[main\.assertion\.4\] .* 1\[p\]==2: UNKNOWN$ +^\[main\.assertion\.5\] .* \*\(p\+1\)==2: UNKNOWN$ +^\[main\.assertion\.6\] .* \*\(1\+p\)==2: UNKNOWN$ +^\[main.assertion\.7\] .* q==p\+1: SUCCESS$ +^\[main.assertion\.8\] .* \*q==2: UNKNOWN$ +^\[main\.assertion\.9\] .* \*\(q-1\)==1: UNKNOWN$ +^\[main.assertion.10\] .* q-p==x: UNKNOWN$ +^\[main.assertion.11\] .* a\[1\]==4: UNKNOWN$ +^\[main.assertion.12\] .* a\[1\]==5: UNKNOWN$ +^\[main\.assertion\.13\] .* a\[1\]==6: UNKNOWN$ +^\[main\.assertion\.14\] .* a\[1\]==7: UNKNOWN$ +^\[main.assertion.15\] .* \*r==2: UNKNOWN$ +^\[main.assertion.16\] .* \*r==1: UNKNOWN$ +^\[main.assertion.17\] .* \*s==0: UNKNOWN$ +^\[main.assertion.18\] .* \*s==1: UNKNOWN$ +^\[main.assertion.19\] .* t==p\+i: UNKNOWN$ +^\[main.assertion.20\] .* t-p==y: UNKNOWN$ +^\[main.assertion.21\] .* a\[i\]==5: UNKNOWN$ +^\[main.assertion.22\] .* a\[1\]==5: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-struct/test.desc b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-struct/test.desc index 05bdde68a64..890b32ec30c 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-pointer-to-two-value-struct/test.desc @@ -1,13 +1,27 @@ -FUTURE +CORE sensitivity_test_constants_pointer_to_two_value_struct.c ---variable --pointers --verify +--variable-sensitivity --vsd-pointers --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion \(\*p\).a==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion \(\*p\).a==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion p->a==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion p->a==1: UNKNOWN$ -^\[main.assertion.5\] .* assertion p->b==2.0: UNKNOWN$ -^\[main.assertion.6\] .* assertion p->b==1.0: UNKNOWN$ +^\[main.assertion.1\] .* \(\*p\).a==0: UNKNOWN$ +^\[main.assertion.2\] .* \(\*p\).a==1: UNKNOWN$ +^\[main.assertion.3\] .* p->a==0: UNKNOWN$ +^\[main.assertion.4\] .* p->a==1: UNKNOWN$ +^\[main.assertion.5\] .* p->b==2.0: UNKNOWN$ +^\[main.assertion.6\] .* p->b==1.0: UNKNOWN$ +\[main\.assertion\.7\] .* comp_p==&x\.a: UNKNOWN$ +\[main\.assertion\.8\] .* comp_p==&x\.b: UNKNOWN$ +\[main\.assertion\.9\] .* \*comp_p==0: UNKNOWN$ +\[main\.assertion\.10\] .* \*comp_p==1: UNKNOWN$ +\[main\.assertion\.11\] .* compb_p==&x\.a: UNKNOWN$ +\[main\.assertion\.12\] .* compb_p==&x\.b: UNKNOWN$ +\[main\.assertion\.13\] .* \*compb_p==2\.0: UNKNOWN$ +\[main\.assertion\.14\] .* \*compb_p==1\.0: UNKNOWN$ +\[main\.assertion\.15\] .* implicit_p==&x\.a: UNKNOWN$ +\[main\.assertion\.16\] .* implicit_p==&x: UNKNOWN$ +\[main\.assertion\.17\] .* \*implicit_p==0: UNKNOWN$ +\[main\.assertion\.18\] .* \*implicit_p==1: UNKNOWN$ +\[main\.assertion\.19\] .* x\.a==5: UNKNOWN$ +\[main\.assertion\.20\] .* x\.a==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-constants-pointer/test.desc index 887f9ec7b8f..c9d705b1faf 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-pointer/test.desc @@ -1,21 +1,21 @@ -FUTURE +CORE sensitivity_test_constants_pointer.c ---variable --pointers --verify +--variable-sensitivity --vsd-pointers --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x==&a: SUCCESS$ -^\[main.assertion.2\] .* assertion x==&b: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion x==x2: SUCCESS$ -^\[main.assertion.4\] .* assertion x==y: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion \*x==0: SUCCESS$ -^\[main.assertion.6\] .* assertion \*x==1: FAILURE \(if reachable\)$ -^\[main.assertion.7\] .* assertion \*x==1: SUCCESS$ -^\[main.assertion.8\] .* assertion \*x==0: FAILURE \(if reachable\)$ -^\[main.assertion.9\] .* assertion a==2: SUCCESS$ -^\[main.assertion.10\] .* assertion a==0: FAILURE \(if reachable\)$ -^\[main.assertion.11\] .* assertion x==&a: SUCCESS$ -^\[main.assertion.12\] .* assertion \*x==0: UNKNOWN$ -^\[main.assertion.13\] .* assertion x==&a: UNKNOWN$ -^\[main.assertion.14\] .* assertion x==&b: UNKNOWN$ +^\[main.assertion.1\] .* x==&a: SUCCESS$ +^\[main.assertion.2\] .* x==&b: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* x==x2: SUCCESS$ +^\[main.assertion.4\] .* x==y: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* \*x==0: SUCCESS$ +^\[main.assertion.6\] .* \*x==1: FAILURE \(if reachable\)$ +^\[main.assertion.7\] .* \*x==1: SUCCESS$ +^\[main.assertion.8\] .* \*x==0: FAILURE \(if reachable\)$ +^\[main.assertion.9\] .* a==2: SUCCESS$ +^\[main.assertion.10\] .* a==0: FAILURE \(if reachable\)$ +^\[main.assertion.11\] .* x==&a: SUCCESS$ +^\[main.assertion.12\] .* \*x==0: UNKNOWN$ +^\[main.assertion.13\] .* x==&a: UNKNOWN$ +^\[main.assertion.14\] .* x==&b: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-array/test.desc b/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-array/test.desc index efd52978d50..133606bab62 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-array/test.desc @@ -1,24 +1,24 @@ -FUTURE +CORE sensitivity_test_constants_struct_of_constants_array.c ---variable --structs --arrays --verify +--variable-sensitivity --vsd-structs --vsd-arrays --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a\[0\]==0: SUCCESS$ -^\[main.assertion.2\] .* assertion \*\(x.a\+0\)==0: SUCCESS$ -^\[main.assertion.3\] .* assertion \*\(0\+x.a\)==0: SUCCESS$ -^\[main.assertion.4\] .* assertion 0\[x.a\]==0: SUCCESS$ -^\[main.assertion.5\] .* assertion x.a\[0\]==0: SUCCESS$ -^\[main.assertion.6\] .* assertion x.a\[1\]==1: SUCCESS$ -^\[main.assertion.7\] .* assertion x.b\[0\]==3.0f: SUCCESS$ -^\[main.assertion.8\] .* assertion x.a\[0\]==0: SUCCESS$ -^\[main.assertion.9\] .* assertion x.a\[1\]==1: SUCCESS$ -^\[main.assertion.10\] .* assertion x.b\[2\]>0.0f: UNKNOWN$ -^\[main.assertion.11\] .* assertion x.b\[2\]==15.0f: UNKNOWN$ -^\[main.assertion.12\] .* assertion x.b\[2\]==1.0f: UNKNOWN$ -^\[main.assertion.13\] .* assertion x.b\[0\]==3.0f: SUCCESS$ -^\[main.assertion.14\] .* assertion x.a\[0\]<12: UNKNOWN$ -^\[main.assertion.15\] .* assertion x.a\[0\]>2: UNKNOWN$ -^\[main.assertion.16\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion x.a\[1\]==1: SUCCESS$ +^\[main.assertion.1\] .* x.a\[0\]==0: SUCCESS$ +^\[main.assertion.2\] .* \*\(x.a\+0\)==0: SUCCESS$ +^\[main.assertion.3\] .* \*\(0\+x.a\)==0: SUCCESS$ +^\[main.assertion.4\] .* 0\[x.a\]==0: SUCCESS$ +^\[main.assertion.5\] .* x.a\[0\]==0: SUCCESS$ +^\[main.assertion.6\] .* x.a\[1\]==1: SUCCESS$ +^\[main.assertion.7\] .* x.b\[0\]==3.0f: SUCCESS$ +^\[main.assertion.8\] .* x.a\[0\]==0: SUCCESS$ +^\[main.assertion.9\] .* x.a\[1\]==1: SUCCESS$ +^\[main.assertion.10\] .* x.b\[2\]>0.0f: UNKNOWN$ +^\[main.assertion.11\] .* x.b\[2\]==15.0f: UNKNOWN$ +^\[main.assertion.12\] .* x.b\[2\]==1.0f: UNKNOWN$ +^\[main.assertion.13\] .* x.b\[0\]==3.0f: SUCCESS$ +^\[main.assertion.14\] .* x.a\[0\]<12: UNKNOWN$ +^\[main.assertion.15\] .* x.a\[0\]>2: UNKNOWN$ +^\[main.assertion.16\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.17\] .* x.a\[1\]==1: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-pointer/test.desc index 1cfdb337e9a..21b5a2d5d1a 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-pointer/test.desc @@ -1,33 +1,33 @@ -FUTURE +CORE sensitivity_test_constants_struct_of_constants_pointer.c ---variable --structs --pointers --verify +--variable-sensitivity --vsd-structs --vsd-pointers --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a==&a1: SUCCESS$ -^\[main.assertion.2\] .* assertion x.a==&a2: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion x.b==&b1: SUCCESS$ -^\[main.assertion.4\] .* assertion x.b==&b2: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion \*x.a==0: SUCCESS$ -^\[main.assertion.6\] .* assertion \*x.a==100: FAILURE \(if reachable\)$ -^\[main.assertion.7\] .* assertion \*x.b==10.0f: SUCCESS$ -^\[main.assertion.8\] .* assertion \*x.b==110.0f: FAILURE \(if reachable\)$ -^\[main.assertion.9\] .* assertion x.a==&a1: SUCCESS$ -^\[main.assertion.10\] .* assertion x.a==&a2: FAILURE \(if reachable\)$ -^\[main.assertion.11\] .* assertion \*x.a==0: SUCCESS$ -^\[main.assertion.12\] .* assertion \*x.a==100: FAILURE \(if reachable\)$ -^\[main.assertion.13\] .* assertion x.a==&a1: SUCCESS$ -^\[main.assertion.14\] .* assertion x.b==&b2: UNKNOWN$ -^\[main.assertion.15\] .* assertion x.b==&b3: UNKNOWN$ -^\[main.assertion.16\] .* assertion \*x.a==0: SUCCESS$ -^\[main.assertion.17\] .* assertion \*x.b==11.0f: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*x.b==12.0f: UNKNOWN$ -^\[main.assertion.19\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.20\] .* assertion x.a==&a3: UNKNOWN$ -^\[main.assertion.21\] .* assertion x.b==&b3: UNKNOWN$ -^\[main.assertion.22\] .* assertion x.b==&b4: UNKNOWN$ -^\[main.assertion.23\] .* assertion \*x.a==1: UNKNOWN$ -^\[main.assertion.24\] .* assertion \*x.a==2: UNKNOWN$ -^\[main.assertion.25\] .* assertion \*x.b==12.0f: UNKNOWN$ -^\[main.assertion.26\] .* assertion \*x.b==13.0f: UNKNOWN$ +^\[main.assertion.1\] .* x.a==&a1: SUCCESS$ +^\[main.assertion.2\] .* x.a==&a2: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* x.b==&b1: SUCCESS$ +^\[main.assertion.4\] .* x.b==&b2: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* \*x.a==0: SUCCESS$ +^\[main.assertion.6\] .* \*x.a==100: FAILURE \(if reachable\)$ +^\[main.assertion.7\] .* \*x.b==10.0f: SUCCESS$ +^\[main.assertion.8\] .* \*x.b==110.0f: FAILURE \(if reachable\)$ +^\[main.assertion.9\] .* x.a==&a1: SUCCESS$ +^\[main.assertion.10\] .* x.a==&a2: FAILURE \(if reachable\)$ +^\[main.assertion.11\] .* \*x.a==0: SUCCESS$ +^\[main.assertion.12\] .* \*x.a==100: FAILURE \(if reachable\)$ +^\[main.assertion.13\] .* x.a==&a1: SUCCESS$ +^\[main.assertion.14\] .* x.b==&b2: UNKNOWN$ +^\[main.assertion.15\] .* x.b==&b3: UNKNOWN$ +^\[main.assertion.16\] .* \*x.a==0: SUCCESS$ +^\[main.assertion.17\] .* \*x.b==11.0f: UNKNOWN$ +^\[main.assertion.18\] .* \*x.b==12.0f: UNKNOWN$ +^\[main.assertion.19\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.20\] .* x.a==&a3: UNKNOWN$ +^\[main.assertion.21\] .* x.b==&b3: UNKNOWN$ +^\[main.assertion.22\] .* x.b==&b4: UNKNOWN$ +^\[main.assertion.23\] .* \*x.a==1: UNKNOWN$ +^\[main.assertion.24\] .* \*x.a==2: UNKNOWN$ +^\[main.assertion.25\] .* \*x.b==12.0f: UNKNOWN$ +^\[main.assertion.26\] .* \*x.b==13.0f: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-struct/test.desc b/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-struct/test.desc index 25cfc93eee0..b1022a4e455 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-struct-of-constants-struct/test.desc @@ -1,18 +1,18 @@ -FUTURE +CORE sensitivity_test_constants_struct_of_constants_struct.c ---variable --structs --verify +--variable-sensitivity --vsd-structs --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.s1.a==0: SUCCESS$ -^\[main.assertion.2\] .* assertion x.s2.b==3.0f: SUCCESS$ -^\[main.assertion.3\] .* assertion x.s1.a==0: SUCCESS$ -^\[main.assertion.4\] .* assertion x.s1.a==10: FAILURE \(if reachable\)$ -^\[main.assertion.5\] .* assertion x.s1.b==1.0f: SUCCESS$ -^\[main.assertion.6\] .* assertion x.s2.b==3.0f: UNKNOWN$ -^\[main.assertion.7\] .* assertion x.s2.b==0.0f: UNKNOWN$ -^\[main.assertion.8\] .* assertion x.s1.a==20: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.s1.a<30: UNKNOWN$ -^\[main.assertion.10\] .* assertion x.s2.a==22: UNKNOWN$ -^\[main.assertion.11\] .* assertion x.s2.a<30: UNKNOWN$ +^\[main.assertion.1\] .* x.s1.a==0: SUCCESS$ +^\[main.assertion.2\] .* x.s2.b==3.0f: SUCCESS$ +^\[main.assertion.3\] .* x.s1.a==0: SUCCESS$ +^\[main.assertion.4\] .* x.s1.a==10: FAILURE \(if reachable\)$ +^\[main.assertion.5\] .* x.s1.b==1.0f: SUCCESS$ +^\[main.assertion.6\] .* x.s2.b==3.0f: UNKNOWN$ +^\[main.assertion.7\] .* x.s2.b==0.0f: UNKNOWN$ +^\[main.assertion.8\] .* x.s1.a==20: UNKNOWN$ +^\[main.assertion.9\] .* x.s1.a<30: UNKNOWN$ +^\[main.assertion.10\] .* x.s2.a==22: UNKNOWN$ +^\[main.assertion.11\] .* x.s2.a<30: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-array/test.desc b/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-array/test.desc index 9ac2425b7bf..3b131b882ba 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-array/test.desc @@ -1,24 +1,24 @@ -FUTURE +CORE sensitivity_test_constants_struct_of_two_value_array.c ---variable --structs --verify +--variable-sensitivity --vsd-structs --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion \*\(x.a\+0\)==0: UNKNOWN$ -^\[main.assertion.3\] .* assertion \*\(0\+x.a\)==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion 0\[x.a\]==0: UNKNOWN$ -^\[main.assertion.5\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.6\] .* assertion x.a\[1\]==1: UNKNOWN$ -^\[main.assertion.7\] .* assertion x.b\[0\]==3.0f: UNKNOWN$ -^\[main.assertion.8\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.a\[1\]==1: UNKNOWN$ -^\[main.assertion.10\] .* assertion x.b\[2\]>0.0f: UNKNOWN$ -^\[main.assertion.11\] .* assertion x.b\[2\]==15.0f: UNKNOWN$ -^\[main.assertion.12\] .* assertion x.b\[2\]==1.0f: UNKNOWN$ -^\[main.assertion.13\] .* assertion x.b\[0\]==3.0f: UNKNOWN$ -^\[main.assertion.14\] .* assertion x.a\[0\]<12: UNKNOWN$ -^\[main.assertion.15\] .* assertion x.a\[0\]>2: UNKNOWN$ -^\[main.assertion.16\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion x.a\[1\]==1: UNKNOWN$ +^\[main.assertion.1\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.2\] .* \*\(x.a\+0\)==0: UNKNOWN$ +^\[main.assertion.3\] .* \*\(0\+x.a\)==0: UNKNOWN$ +^\[main.assertion.4\] .* 0\[x.a\]==0: UNKNOWN$ +^\[main.assertion.5\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.6\] .* x.a\[1\]==1: UNKNOWN$ +^\[main.assertion.7\] .* x.b\[0\]==3.0f: UNKNOWN$ +^\[main.assertion.8\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.9\] .* x.a\[1\]==1: UNKNOWN$ +^\[main.assertion.10\] .* x.b\[2\]>0.0f: UNKNOWN$ +^\[main.assertion.11\] .* x.b\[2\]==15.0f: UNKNOWN$ +^\[main.assertion.12\] .* x.b\[2\]==1.0f: UNKNOWN$ +^\[main.assertion.13\] .* x.b\[0\]==3.0f: UNKNOWN$ +^\[main.assertion.14\] .* x.a\[0\]<12: UNKNOWN$ +^\[main.assertion.15\] .* x.a\[0\]>2: UNKNOWN$ +^\[main.assertion.16\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.17\] .* x.a\[1\]==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-pointer/test.desc index 599a644d13d..fa9989d07f5 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-struct-of-two-value-pointer/test.desc @@ -1,33 +1,33 @@ -FUTURE +CORE sensitivity_test_constants_struct_of_two_value_pointer.c ---variable --structs --verify +--variable-sensitivity --vsd-structs --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a==&a1: UNKNOWN$ -^\[main.assertion.2\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.3\] .* assertion x.b==&b1: UNKNOWN$ -^\[main.assertion.4\] .* assertion x.b==&b2: UNKNOWN$ -^\[main.assertion.5\] .* assertion \*x.a==0: UNKNOWN$ -^\[main.assertion.6\] .* assertion \*x.a==100: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*x.b==10.0f: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*x.b==110.0f: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.a==&a1: UNKNOWN$ -^\[main.assertion.10\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.11\] .* assertion \*x.a==0: UNKNOWN$ -^\[main.assertion.12\] .* assertion \*x.a==100: UNKNOWN$ -^\[main.assertion.13\] .* assertion x.a==&a1: UNKNOWN$ -^\[main.assertion.14\] .* assertion x.b==&b2: UNKNOWN$ -^\[main.assertion.15\] .* assertion x.b==&b3: UNKNOWN$ -^\[main.assertion.16\] .* assertion \*x.a==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion \*x.b==11.0f: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*x.b==12.0f: UNKNOWN$ -^\[main.assertion.19\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.20\] .* assertion x.a==&a3: UNKNOWN$ -^\[main.assertion.21\] .* assertion x.b==&b3: UNKNOWN$ -^\[main.assertion.22\] .* assertion x.b==&b4: UNKNOWN$ -^\[main.assertion.23\] .* assertion \*x.a==1: UNKNOWN$ -^\[main.assertion.24\] .* assertion \*x.a==2: UNKNOWN$ -^\[main.assertion.25\] .* assertion \*x.b==12.0f: UNKNOWN$ -^\[main.assertion.26\] .* assertion \*x.b==13.0f: UNKNOWN$ +^\[main.assertion.1\] .* x.a==&a1: UNKNOWN$ +^\[main.assertion.2\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.3\] .* x.b==&b1: UNKNOWN$ +^\[main.assertion.4\] .* x.b==&b2: UNKNOWN$ +^\[main.assertion.5\] .* \*x.a==0: UNKNOWN$ +^\[main.assertion.6\] .* \*x.a==100: UNKNOWN$ +^\[main.assertion.7\] .* \*x.b==10.0f: UNKNOWN$ +^\[main.assertion.8\] .* \*x.b==110.0f: UNKNOWN$ +^\[main.assertion.9\] .* x.a==&a1: UNKNOWN$ +^\[main.assertion.10\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.11\] .* \*x.a==0: UNKNOWN$ +^\[main.assertion.12\] .* \*x.a==100: UNKNOWN$ +^\[main.assertion.13\] .* x.a==&a1: UNKNOWN$ +^\[main.assertion.14\] .* x.b==&b2: UNKNOWN$ +^\[main.assertion.15\] .* x.b==&b3: UNKNOWN$ +^\[main.assertion.16\] .* \*x.a==0: UNKNOWN$ +^\[main.assertion.17\] .* \*x.b==11.0f: UNKNOWN$ +^\[main.assertion.18\] .* \*x.b==12.0f: UNKNOWN$ +^\[main.assertion.19\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.20\] .* x.a==&a3: UNKNOWN$ +^\[main.assertion.21\] .* x.b==&b3: UNKNOWN$ +^\[main.assertion.22\] .* x.b==&b4: UNKNOWN$ +^\[main.assertion.23\] .* \*x.a==1: UNKNOWN$ +^\[main.assertion.24\] .* \*x.a==2: UNKNOWN$ +^\[main.assertion.25\] .* \*x.b==12.0f: UNKNOWN$ +^\[main.assertion.26\] .* \*x.b==13.0f: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-constants-struct/test.desc b/regression/goto-analyzer/sensitivity-test-constants-struct/test.desc index 72da73df05e..ca84cffa314 100644 --- a/regression/goto-analyzer/sensitivity-test-constants-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-constants-struct/test.desc @@ -1,16 +1,16 @@ -FUTURE +CORE sensitivity_test_constants_struct.c ---variable --structs --verify +--variable-sensitivity --vsd-structs --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a==0: SUCCESS$ -^\[main.assertion.2\] .* assertion x.a==1: FAILURE \(if reachable\)$ -^\[main.assertion.3\] .* assertion x.a==0: SUCCESS$ -^\[main.assertion.4\] .* assertion x.a==0: SUCCESS$ -^\[main.assertion.5\] .* assertion x.b>0.0f: UNKNOWN$ -^\[main.assertion.6\] .* assertion x.b==1.0f: UNKNOWN$ -^\[main.assertion.7\] .* assertion x.a<2: UNKNOWN$ -^\[main.assertion.8\] .* assertion x.a>2: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.a==1: UNKNOWN$ +^\[main.assertion.1\] .* x.a==0: SUCCESS$ +^\[main.assertion.2\] .* x.a==1: FAILURE \(if reachable\)$ +^\[main.assertion.3\] .* x.a==0: SUCCESS$ +^\[main.assertion.4\] .* x.a==0: SUCCESS$ +^\[main.assertion.5\] .* x.b>0.0f: UNKNOWN$ +^\[main.assertion.6\] .* x.b==1.0f: UNKNOWN$ +^\[main.assertion.7\] .* x.a<2: UNKNOWN$ +^\[main.assertion.8\] .* x.a>2: UNKNOWN$ +^\[main.assertion.9\] .* x.a==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-data-dependency-context/data-dependency-context.c b/regression/goto-analyzer/sensitivity-test-data-dependency-context/data-dependency-context.c new file mode 100644 index 00000000000..de388175719 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-test-data-dependency-context/data-dependency-context.c @@ -0,0 +1,49 @@ +struct structt +{ + int a; + int b; +}; + +struct structt st; +int ar[2]; +int arr[3]; + +extern int in; +int out1, out2, out3; + +void main(void) +{ + int i; + if(in == 1) + st.a++; + + if(in == 2) + st.b++; + + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; + + if(in == 1) + ar[0]++; + + if(in == 2) + ar[1]++; + + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; + + arr[0] = 1; + arr[1] = 2; + arr[2] = 3; + + if(in > 0) + { + arr[2] = arr[1]; + } + + out1 = arr[0]; + out2 = arr[1]; + out3 = arr[2]; +} diff --git a/regression/goto-analyzer/sensitivity-test-data-dependency-context/test.desc b/regression/goto-analyzer/sensitivity-test-data-dependency-context/test.desc new file mode 100644 index 00000000000..f2f3dcdc1b7 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-test-data-dependency-context/test.desc @@ -0,0 +1,12 @@ +CORE +data-dependency-context.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-data-dependencies --show +// Enable multi-line checking +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +st \(\) -> \{.a=.* @ \[2, 55\]\[Data dependencies: 55, 2\]\[Data dominators: \], .b=.* @ \[5, 55\]\[Data dependencies: 55, 5\]\[Data dominators: \]\} @ \[2, 5, 55\]\[Data dependencies: 55, 5, 2\]\[Data dominators: 55\] +ar \(\) -> \{\[0\] = TOP @ \[11\, 49\]\[Data dependencies: 49\, 11\]\[Data dominators: \]\n\[1\] = TOP @ \[14\, 49\]\[Data dependencies: 49\, 14\]\[Data dominators: \]\n\} @ \[11\, 14\, 49\]\[Data dependencies: 49\, 14\, 11\]\[Data dominators: 49\] +arr \(\) -> \{\[0\] = 1 @ \[19\]\[Data dependencies: 19\]\[Data dominators: 19\]\n\[1\] = 2 @ \[20\]\[Data dependencies: 20\]\[Data dominators: 20\]\n\[2\] = TOP @ \[21, 23\]\[Data dependencies: 23, 21\]\[Data dominators: \]\n\} @ \[21, 23\]\[Data dependencies: 23, 21\]\[Data dominators: 50\] +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-struct-initialization/struct-initialization.c b/regression/goto-analyzer/sensitivity-test-struct-initialization/struct-initialization.c new file mode 100644 index 00000000000..0e31047a7eb --- /dev/null +++ b/regression/goto-analyzer/sensitivity-test-struct-initialization/struct-initialization.c @@ -0,0 +1,54 @@ +struct lexigraphically_ordered_struct +{ + int a; + int b; +}; + +struct lexigraphically_unordered_struct +{ + int b; + int a; +}; + +struct lexigraphically_ordered_struct los; +struct lexigraphically_ordered_struct los2 = {.a = 3, .b = 4}; +struct lexigraphically_ordered_struct los3 = {.b = 4, .a = 3}; +struct lexigraphically_ordered_struct los4 = {.a = 3}; +struct lexigraphically_ordered_struct los5 = {.b = 4}; +struct lexigraphically_ordered_struct los6 = {3, 4}; + +struct lexigraphically_unordered_struct lus; +struct lexigraphically_unordered_struct lus2 = {.a = 3, .b = 4}; +struct lexigraphically_unordered_struct lus3 = {.b = 4, .a = 3}; +struct lexigraphically_unordered_struct lus4 = {.a = 3}; +struct lexigraphically_unordered_struct lus5 = {.b = 4}; +struct lexigraphically_unordered_struct lus6 = {4, 3}; + +void main(void) +{ + __CPROVER_assert(los.a == 0, "los.a==0"); + __CPROVER_assert(los.b == 0, "los.b==0"); + __CPROVER_assert(los2.a == 3, "los2.a==3"); + __CPROVER_assert(los2.b == 4, "los2.b==4"); + __CPROVER_assert(los3.a == 3, "los3.a==3"); + __CPROVER_assert(los3.b == 4, "los3.b==4"); + __CPROVER_assert(los4.a == 3, "los4.a==3"); + __CPROVER_assert(los4.b == 0, "los4.b==0"); + __CPROVER_assert(los5.a == 0, "los5.a==0"); + __CPROVER_assert(los5.b == 4, "los5.b==4"); + __CPROVER_assert(los6.a == 3, "los6.a==3"); + __CPROVER_assert(los6.b == 4, "los6.b==4"); + + __CPROVER_assert(lus.a == 0, "lus.a==0"); + __CPROVER_assert(lus.b == 0, "lus.b==0"); + __CPROVER_assert(lus2.a == 3, "lus2.a==3"); + __CPROVER_assert(lus2.b == 4, "lus2.b==4"); + __CPROVER_assert(lus3.a == 3, "lus3.a==3"); + __CPROVER_assert(lus3.b == 4, "lus3.b==4"); + __CPROVER_assert(lus4.a == 3, "lus4.a==3"); + __CPROVER_assert(lus4.b == 0, "lus4.b==0"); + __CPROVER_assert(lus5.a == 0, "lus5.a==0"); + __CPROVER_assert(lus5.b == 4, "lus5.b==4"); + __CPROVER_assert(lus6.a == 3, "lus6.a==3"); + __CPROVER_assert(lus6.b == 4, "lus6.b==4"); +} diff --git a/regression/goto-analyzer/sensitivity-test-struct-initialization/test.desc b/regression/goto-analyzer/sensitivity-test-struct-initialization/test.desc new file mode 100644 index 00000000000..2ca2c036272 --- /dev/null +++ b/regression/goto-analyzer/sensitivity-test-struct-initialization/test.desc @@ -0,0 +1,31 @@ +CORE +struct-initialization.c +--variable-sensitivity --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ los.a==0: SUCCESS$ +^\[main\.assertion\.2\] line \d+ los.b==0: SUCCESS$ +^\[main\.assertion\.3\] line \d+ los2.a==3: SUCCESS$ +^\[main\.assertion\.4\] line \d+ los2.b==4: SUCCESS$ +^\[main\.assertion\.5\] line \d+ los3.a==3: SUCCESS$ +^\[main\.assertion\.6\] line \d+ los3.b==4: SUCCESS$ +^\[main\.assertion\.7\] line \d+ los4.a==3: SUCCESS$ +^\[main\.assertion\.8\] line \d+ los4.b==0: SUCCESS$ +^\[main\.assertion\.9\] line \d+ los5.a==0: SUCCESS$ +^\[main\.assertion\.10\] line \d+ los5.b==4: SUCCESS$ +^\[main\.assertion\.11\] line \d+ los6.a==3: SUCCESS$ +^\[main\.assertion\.12\] line \d+ los6.b==4: SUCCESS$ +^\[main\.assertion\.13\] line \d+ lus.a==0: SUCCESS$ +^\[main\.assertion\.14\] line \d+ lus.b==0: SUCCESS$ +^\[main\.assertion\.15\] line \d+ lus2.a==3: SUCCESS$ +^\[main\.assertion\.16\] line \d+ lus2.b==4: SUCCESS$ +^\[main\.assertion\.17\] line \d+ lus3.a==3: SUCCESS$ +^\[main\.assertion\.18\] line \d+ lus3.b==4: SUCCESS$ +^\[main\.assertion\.19\] line \d+ lus4.a==3: SUCCESS$ +^\[main\.assertion\.20\] line \d+ lus4.b==0: SUCCESS$ +^\[main\.assertion\.21\] line \d+ lus5.a==0: SUCCESS$ +^\[main\.assertion\.22\] line \d+ lus5.b==4: SUCCESS$ +^\[main\.assertion\.23\] line \d+ lus6.a==3: SUCCESS$ +^\[main\.assertion\.24\] line \d+ lus6.b==4: SUCCESS$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-array/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-array/test.desc index c75dec8b757..8b6844fc44e 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-array/test.desc @@ -1,73 +1,73 @@ -FUTURE +CORE sensitivity_test_two_value_array_of_two_value_array.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]\[2\]==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion a\[1\]\[2\]==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion b\[1\]\[2\]==5: UNKNOWN$ -^\[main.assertion.4\] .* assertion b\[1\]\[2\]==0: UNKNOWN$ -^\[main.assertion.5\] .* assertion \*\(b\[1\]\+2\)==5: UNKNOWN$ -^\[main.assertion.6\] .* assertion \*\(b\[1\]\+2\)==0: UNKNOWN$ -^\[main.assertion.7\] .* assertion \(\*\(b\+1\)\)\[2\]==5: UNKNOWN$ -^\[main.assertion.8\] .* assertion \(\*\(b\+1\)\)\[2\]==0: UNKNOWN$ -^\[main.assertion.9\] .* assertion \*\(\*\(b\+1\)\+2\)==5: UNKNOWN$ -^\[main.assertion.10\] .* assertion \*\(\*\(b\+1\)\+2\)==0: UNKNOWN$ -^\[main.assertion.11\] .* assertion 1\[b\]\[2\]==5: UNKNOWN$ -^\[main.assertion.12\] .* assertion 1\[b\]\[2\]==0: UNKNOWN$ -^\[main.assertion.13\] .* assertion \*\(1\[b\]\+2\)==5: UNKNOWN$ -^\[main.assertion.14\] .* assertion \*\(1\[b\]\+2\)==0: UNKNOWN$ -^\[main.assertion.15\] .* assertion \(\*\(1\+b\)\)\[2\]==5: UNKNOWN$ -^\[main.assertion.16\] .* assertion \(\*\(1\+b\)\)\[2\]==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion \*\(\*\(1\+b\)\+2\)==5: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*\(\*\(1\+b\)\+2\)==0: UNKNOWN$ -^\[main.assertion.19\] .* assertion 2\[1\[b\]\]==5: UNKNOWN$ -^\[main.assertion.20\] .* assertion 2\[1\[b\]\]==0: UNKNOWN$ -^\[main.assertion.21\] .* assertion \*\(2\+1\[b\]\)==5: UNKNOWN$ -^\[main.assertion.22\] .* assertion \*\(2\+1\[b\]\)==0: UNKNOWN$ -^\[main.assertion.23\] .* assertion \*\(2\+\*\(1\+b\)\)==5: UNKNOWN$ -^\[main.assertion.24\] .* assertion \*\(2\+\*\(1\+b\)\)==0: UNKNOWN$ -^\[main.assertion.25\] .* assertion a\[0\]\[1\]==0: UNKNOWN$ -^\[main.assertion.26\] .* assertion a\[0\]\[1\]==1: UNKNOWN$ -^\[main.assertion.27\] .* assertion a\[0\]\[2\]==0: UNKNOWN$ -^\[main.assertion.28\] .* assertion b\[0\]\[1\]==2: UNKNOWN$ -^\[main.assertion.29\] .* assertion b\[0\]\[1\]==3: UNKNOWN$ -^\[main.assertion.30\] .* assertion b\[0\]\[2\]==2: UNKNOWN$ -^\[main.assertion.31\] .* assertion a\[i\]\[1\]==0: UNKNOWN$ -^\[main.assertion.32\] .* assertion a\[i\]\[1\]==1: UNKNOWN$ -^\[main.assertion.33\] .* assertion a\[1\]\[i\]==0: UNKNOWN$ -^\[main.assertion.34\] .* assertion a\[1\]\[i\]==1: UNKNOWN$ -^\[main.assertion.35\] .* assertion a\[i\]\[i\]==0: UNKNOWN$ -^\[main.assertion.36\] .* assertion a\[i\]\[i\]==1: UNKNOWN$ -^\[main.assertion.37\] .* assertion a\[j\]\[1\]==0: UNKNOWN$ -^\[main.assertion.38\] .* assertion a\[j\]\[1\]==1: UNKNOWN$ -^\[main.assertion.39\] .* assertion a\[1\]\[j\]==0: UNKNOWN$ -^\[main.assertion.40\] .* assertion a\[1\]\[j\]==1: UNKNOWN$ -^\[main.assertion.41\] .* assertion a\[j\]\[j\]==0: UNKNOWN$ -^\[main.assertion.42\] .* assertion a\[j\]\[j\]==1: UNKNOWN$ -^\[main.assertion.43\] .* assertion b\[i\]\[1\]==1: UNKNOWN$ -^\[main.assertion.44\] .* assertion b\[i\]\[1\]==11: UNKNOWN$ -^\[main.assertion.45\] .* assertion b\[1\]\[i\]==3: UNKNOWN$ -^\[main.assertion.46\] .* assertion b\[1\]\[i\]==11: UNKNOWN$ -^\[main.assertion.47\] .* assertion b\[i\]\[i\]==0: UNKNOWN$ -^\[main.assertion.48\] .* assertion b\[i\]\[i\]==11: UNKNOWN$ -^\[main.assertion.49\] .* assertion b\[j\]\[1\]==1: UNKNOWN$ -^\[main.assertion.50\] .* assertion b\[j\]\[1\]==11: UNKNOWN$ -^\[main.assertion.51\] .* assertion b\[1\]\[j\]==3: UNKNOWN$ -^\[main.assertion.52\] .* assertion b\[1\]\[j\]==11: UNKNOWN$ -^\[main.assertion.53\] .* assertion b\[j\]\[j\]==0: UNKNOWN$ -^\[main.assertion.54\] .* assertion b\[j\]\[j\]==11: UNKNOWN$ -^\[main.assertion.55\] .* assertion a\[100\]\[0\]==0: UNKNOWN$ -^\[main.assertion.56\] .* assertion a\[0\]\[100\]==0: UNKNOWN$ -^\[main.assertion.57\] .* assertion c==0: SUCCESS$ -^\[main.assertion.58\] .* assertion c==0: SUCCESS$ -^\[main.assertion.59\] .* assertion ei\[0\]\[1\]==1: UNKNOWN$ -^\[main.assertion.60\] .* assertion ei\[0\]\[1\]==0: UNKNOWN$ -^\[main.assertion.61\] .* assertion ei\[2\]\[1\]==0: UNKNOWN$ -^\[main.assertion.62\] .* assertion ei\[2\]\[1\]==1: UNKNOWN$ -^\[main.assertion.63\] .* assertion ej\[0\]\[1\]==0: UNKNOWN$ -^\[main.assertion.64\] .* assertion ej\[2\]\[1\]==0: UNKNOWN$ -^\[main.assertion.65\] .* assertion ek\[0\]\[1\]==0: UNKNOWN$ -^\[main.assertion.66\] .* assertion c==0: SUCCESS$ +^\[main.assertion.1\] .* a\[1\]\[2\]==0: UNKNOWN$ +^\[main.assertion.2\] .* a\[1\]\[2\]==1: UNKNOWN$ +^\[main.assertion.3\] .* b\[1\]\[2\]==5: UNKNOWN$ +^\[main.assertion.4\] .* b\[1\]\[2\]==0: UNKNOWN$ +^\[main.assertion.5\] .* \*\(b\[1\]\+2\)==5: UNKNOWN$ +^\[main.assertion.6\] .* \*\(b\[1\]\+2\)==0: UNKNOWN$ +^\[main.assertion.7\] .* \(\*\(b\+1\)\)\[2\]==5: UNKNOWN$ +^\[main.assertion.8\] .* \(\*\(b\+1\)\)\[2\]==0: UNKNOWN$ +^\[main.assertion.9\] .* \*\(\*\(b\+1\)\+2\)==5: UNKNOWN$ +^\[main.assertion.10\] .* \*\(\*\(b\+1\)\+2\)==0: UNKNOWN$ +^\[main.assertion.11\] .* 1\[b\]\[2\]==5: UNKNOWN$ +^\[main.assertion.12\] .* 1\[b\]\[2\]==0: UNKNOWN$ +^\[main.assertion.13\] .* \*\(1\[b\]\+2\)==5: UNKNOWN$ +^\[main.assertion.14\] .* \*\(1\[b\]\+2\)==0: UNKNOWN$ +^\[main.assertion.15\] .* \(\*\(1\+b\)\)\[2\]==5: UNKNOWN$ +^\[main.assertion.16\] .* \(\*\(1\+b\)\)\[2\]==0: UNKNOWN$ +^\[main.assertion.17\] .* \*\(\*\(1\+b\)\+2\)==5: UNKNOWN$ +^\[main.assertion.18\] .* \*\(\*\(1\+b\)\+2\)==0: UNKNOWN$ +^\[main.assertion.19\] .* 2\[1\[b\]\]==5: UNKNOWN$ +^\[main.assertion.20\] .* 2\[1\[b\]\]==0: UNKNOWN$ +^\[main.assertion.21\] .* \*\(2\+1\[b\]\)==5: UNKNOWN$ +^\[main.assertion.22\] .* \*\(2\+1\[b\]\)==0: UNKNOWN$ +^\[main.assertion.23\] .* \*\(2\+\*\(1\+b\)\)==5: UNKNOWN$ +^\[main.assertion.24\] .* \*\(2\+\*\(1\+b\)\)==0: UNKNOWN$ +^\[main.assertion.25\] .* a\[0\]\[1\]==0: UNKNOWN$ +^\[main.assertion.26\] .* a\[0\]\[1\]==1: UNKNOWN$ +^\[main.assertion.27\] .* a\[0\]\[2\]==0: UNKNOWN$ +^\[main.assertion.28\] .* b\[0\]\[1\]==2: UNKNOWN$ +^\[main.assertion.29\] .* b\[0\]\[1\]==3: UNKNOWN$ +^\[main.assertion.30\] .* b\[0\]\[2\]==2: UNKNOWN$ +^\[main.assertion.31\] .* a\[i\]\[1\]==0: UNKNOWN$ +^\[main.assertion.32\] .* a\[i\]\[1\]==1: UNKNOWN$ +^\[main.assertion.33\] .* a\[1\]\[i\]==0: UNKNOWN$ +^\[main.assertion.34\] .* a\[1\]\[i\]==1: UNKNOWN$ +^\[main.assertion.35\] .* a\[i\]\[i\]==0: UNKNOWN$ +^\[main.assertion.36\] .* a\[i\]\[i\]==1: UNKNOWN$ +^\[main.assertion.37\] .* a\[j\]\[1\]==0: UNKNOWN$ +^\[main.assertion.38\] .* a\[j\]\[1\]==1: UNKNOWN$ +^\[main.assertion.39\] .* a\[1\]\[j\]==0: UNKNOWN$ +^\[main.assertion.40\] .* a\[1\]\[j\]==1: UNKNOWN$ +^\[main.assertion.41\] .* a\[j\]\[j\]==0: UNKNOWN$ +^\[main.assertion.42\] .* a\[j\]\[j\]==1: UNKNOWN$ +^\[main.assertion.43\] .* b\[i\]\[1\]==1: UNKNOWN$ +^\[main.assertion.44\] .* b\[i\]\[1\]==11: UNKNOWN$ +^\[main.assertion.45\] .* b\[1\]\[i\]==3: UNKNOWN$ +^\[main.assertion.46\] .* b\[1\]\[i\]==11: UNKNOWN$ +^\[main.assertion.47\] .* b\[i\]\[i\]==0: UNKNOWN$ +^\[main.assertion.48\] .* b\[i\]\[i\]==11: UNKNOWN$ +^\[main.assertion.49\] .* b\[j\]\[1\]==1: UNKNOWN$ +^\[main.assertion.50\] .* b\[j\]\[1\]==11: UNKNOWN$ +^\[main.assertion.51\] .* b\[1\]\[j\]==3: UNKNOWN$ +^\[main.assertion.52\] .* b\[1\]\[j\]==11: UNKNOWN$ +^\[main.assertion.53\] .* b\[j\]\[j\]==0: UNKNOWN$ +^\[main.assertion.54\] .* b\[j\]\[j\]==11: UNKNOWN$ +^\[main.assertion.55\] .* a\[100\]\[0\]==0: UNKNOWN$ +^\[main.assertion.56\] .* a\[0\]\[100\]==0: UNKNOWN$ +^\[main.assertion.57\] .* c==0: SUCCESS$ +^\[main.assertion.58\] .* c==0: SUCCESS$ +^\[main.assertion.59\] .* ei\[0\]\[1\]==1: UNKNOWN$ +^\[main.assertion.60\] .* ei\[0\]\[1\]==0: UNKNOWN$ +^\[main.assertion.61\] .* ei\[2\]\[1\]==0: UNKNOWN$ +^\[main.assertion.62\] .* ei\[2\]\[1\]==1: UNKNOWN$ +^\[main.assertion.63\] .* ej\[0\]\[1\]==0: UNKNOWN$ +^\[main.assertion.64\] .* ej\[2\]\[1\]==0: UNKNOWN$ +^\[main.assertion.65\] .* ek\[0\]\[1\]==0: UNKNOWN$ +^\[main.assertion.66\] .* c==0: SUCCESS$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-pointer/test.desc index 3d06e1f7e0d..b1f64ea2215 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-array-of-two-value-pointer/test.desc @@ -1,71 +1,75 @@ -FUTURE +CORE sensitivity_test_two_value_array_of_two_value_pointer.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]==&a0: UNKNOWN$ -^\[main.assertion.2\] .* assertion a\[1\]==&a3: UNKNOWN$ -^\[main.assertion.3\] .* assertion \*a\[1\]==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion \*a\[1\]==3: UNKNOWN$ -^\[main.assertion.5\] .* assertion b\[1\]==&b1: UNKNOWN$ -^\[main.assertion.6\] .* assertion b\[1\]==&b3: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*b\[1\]==11: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*b\[1\]==13: UNKNOWN$ -^\[main.assertion.9\] .* assertion \*\(b\+1\)==&b1: UNKNOWN$ -^\[main.assertion.10\] .* assertion \*\(b\+1\)==&b3: UNKNOWN$ -^\[main.assertion.11\] .* assertion \*\(1\+b\)==&b1: UNKNOWN$ -^\[main.assertion.12\] .* assertion \*\(1\+b\)==&b3: UNKNOWN$ -^\[main.assertion.13\] .* assertion 1\[b\]==&b1: UNKNOWN$ -^\[main.assertion.14\] .* assertion 1\[b\]==&b3: UNKNOWN$ -^\[main.assertion.15\] .* assertion \*\*\(b\+1\)==11: UNKNOWN$ -^\[main.assertion.16\] .* assertion \*\*\(b\+1\)==13: UNKNOWN$ -^\[main.assertion.17\] .* assertion \*\*\(1\+b\)==11: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*\*\(1\+b\)==13: UNKNOWN$ -^\[main.assertion.19\] .* assertion \*1\[b\]==11: UNKNOWN$ -^\[main.assertion.20\] .* assertion \*1\[b\]==13: UNKNOWN$ -^\[main.assertion.21\] .* assertion c\[0\]==&c0: UNKNOWN$ -^\[main.assertion.22\] .* assertion c\[0\]==&c3: UNKNOWN$ -^\[main.assertion.23\] .* assertion d\[0\]==&d0: UNKNOWN$ -^\[main.assertion.24\] .* assertion d\[0\]==&d3: UNKNOWN$ -^\[main.assertion.25\] .* assertion \*c\[0\]==20: UNKNOWN$ -^\[main.assertion.26\] .* assertion \*c\[0\]==23: UNKNOWN$ -^\[main.assertion.27\] .* assertion \*d\[0\]==30: UNKNOWN$ -^\[main.assertion.28\] .* assertion \*d\[0\]==33: UNKNOWN$ -^\[main.assertion.29\] .* assertion a\[i\]==&a0: UNKNOWN$ -^\[main.assertion.30\] .* assertion a\[i\]==&a3: UNKNOWN$ -^\[main.assertion.31\] .* assertion a\[j\]==&a0: UNKNOWN$ -^\[main.assertion.32\] .* assertion a\[j\]==&a3: UNKNOWN$ -^\[main.assertion.33\] .* assertion \*a\[i\]==0: UNKNOWN$ -^\[main.assertion.34\] .* assertion \*a\[i\]==3: UNKNOWN$ -^\[main.assertion.35\] .* assertion \*a\[j\]==0: UNKNOWN$ -^\[main.assertion.36\] .* assertion \*a\[j\]==3: UNKNOWN$ -^\[main.assertion.37\] .* assertion b\[i\]==&b0: UNKNOWN$ -^\[main.assertion.38\] .* assertion b\[i\]==&b1: UNKNOWN$ -^\[main.assertion.39\] .* assertion b\[j\]==&b0: UNKNOWN$ -^\[main.assertion.40\] .* assertion b\[j\]==&b3: UNKNOWN$ -^\[main.assertion.41\] .* assertion \*b\[i\]==10: UNKNOWN$ -^\[main.assertion.42\] .* assertion \*b\[i\]==11: UNKNOWN$ -^\[main.assertion.43\] .* assertion \*b\[j\]==10: UNKNOWN$ -^\[main.assertion.44\] .* assertion \*b\[j\]==13: UNKNOWN$ -^\[main.assertion.45\] .* assertion a\[100\]==&a2: UNKNOWN$ -^\[main.assertion.46\] .* assertion \*a\[100\]==2: UNKNOWN$ -^\[main.assertion.47\] .* assertion b\[1\]==&b1: UNKNOWN$ -^\[main.assertion.48\] .* assertion \*b\[1\]==11: UNKNOWN$ -^\[main.assertion.49\] .* assertion ei\[0\]==&ei1: UNKNOWN$ -^\[main.assertion.50\] .* assertion ei\[0\]==&ei0: UNKNOWN$ -^\[main.assertion.51\] .* assertion ei\[2\]==&ei0: UNKNOWN$ -^\[main.assertion.52\] .* assertion ei\[2\]==&ei1: UNKNOWN$ -^\[main.assertion.53\] .* assertion \*ei\[0\]==41: UNKNOWN$ -^\[main.assertion.54\] .* assertion \*ei\[0\]==40: UNKNOWN$ -^\[main.assertion.55\] .* assertion \*ei\[2\]==40: UNKNOWN$ -^\[main.assertion.56\] .* assertion \*ei\[2\]==41: UNKNOWN$ -^\[main.assertion.57\] .* assertion ej\[0\]==&ej0: UNKNOWN$ -^\[main.assertion.58\] .* assertion ej\[2\]==&ej0: UNKNOWN$ -^\[main.assertion.59\] .* assertion ej\[2\]==&ej1: UNKNOWN$ -^\[main.assertion.60\] .* assertion \*ej\[0\]==50: UNKNOWN$ -^\[main.assertion.61\] .* assertion \*ej\[2\]==50: UNKNOWN$ -^\[main.assertion.62\] .* assertion \*ej\[2\]==51: UNKNOWN$ -^\[main.assertion.63\] .* assertion ek\[0\]==&ek0: UNKNOWN$ -^\[main.assertion.64\] .* assertion \*ek\[0\]==60: UNKNOWN$ +^\[main.assertion.1\] .* a\[1\]==&a0: UNKNOWN$ +^\[main.assertion.2\] .* a\[1\]==&a3: UNKNOWN$ +^\[main.assertion.3\] .* \*a\[1\]==0: UNKNOWN$ +^\[main.assertion.4\] .* \*a\[1\]==3: UNKNOWN$ +^\[main.assertion.5\] .* b\[1\]==&b1: UNKNOWN$ +^\[main.assertion.6\] .* b\[1\]==&b3: UNKNOWN$ +^\[main.assertion.7\] .* \*b\[1\]==11: UNKNOWN$ +^\[main.assertion.8\] .* \*b\[1\]==13: UNKNOWN$ +^\[main.assertion.9\] .* \*\(b\+1\)==&b1: UNKNOWN$ +^\[main.assertion.10\] .* \*\(b\+1\)==&b3: UNKNOWN$ +^\[main.assertion.11\] .* \*\(1\+b\)==&b1: UNKNOWN$ +^\[main.assertion.12\] .* \*\(1\+b\)==&b3: UNKNOWN$ +^\[main.assertion.13\] .* 1\[b\]==&b1: UNKNOWN$ +^\[main.assertion.14\] .* 1\[b\]==&b3: UNKNOWN$ +^\[main.assertion.15\] .* \*\*\(b\+1\)==11: UNKNOWN$ +^\[main.assertion.16\] .* \*\*\(b\+1\)==13: UNKNOWN$ +^\[main.assertion.17\] .* \*\*\(1\+b\)==11: UNKNOWN$ +^\[main.assertion.18\] .* \*\*\(1\+b\)==13: UNKNOWN$ +^\[main.assertion.19\] .* \*1\[b\]==11: UNKNOWN$ +^\[main.assertion.20\] .* \*1\[b\]==13: UNKNOWN$ +^\[main.assertion.21\] .* c\[0\]==&c0: UNKNOWN$ +^\[main.assertion.22\] .* c\[0\]==&c3: UNKNOWN$ +^\[main.assertion.23\] .* d\[0\]==&d0: UNKNOWN$ +^\[main.assertion.24\] .* d\[0\]==&d3: UNKNOWN$ +^\[main.assertion.25\] .* \*c\[0\]==20: UNKNOWN$ +^\[main.assertion.26\] .* \*c\[0\]==23: UNKNOWN$ +^\[main.assertion.27\] .* \*d\[0\]==30: UNKNOWN$ +^\[main.assertion.28\] .* \*d\[0\]==33: UNKNOWN$ +^\[main.assertion.29\] .* a\[i\]==&a0: UNKNOWN$ +^\[main.assertion.30\] .* a\[i\]==&a3: UNKNOWN$ +^\[main.assertion.31\] .* a\[j\]==&a0: UNKNOWN$ +^\[main.assertion.32\] .* a\[j\]==&a3: UNKNOWN$ +^\[main.assertion.33\] .* \*a\[i\]==0: UNKNOWN$ +^\[main.assertion.34\] .* \*a\[i\]==3: UNKNOWN$ +^\[main.assertion.35\] .* \*a\[j\]==0: UNKNOWN$ +^\[main.assertion.36\] .* \*a\[j\]==3: UNKNOWN$ +^\[main.assertion.37\] .* b\[i\]==&b0: UNKNOWN$ +^\[main.assertion.38\] .* b\[i\]==&b1: UNKNOWN$ +^\[main.assertion.39\] .* b\[j\]==&b0: UNKNOWN$ +^\[main.assertion.40\] .* b\[j\]==&b3: UNKNOWN$ +^\[main.assertion.41\] .* \*b\[i\]==10: UNKNOWN$ +^\[main.assertion.42\] .* \*b\[i\]==11: UNKNOWN$ +^\[main.assertion.43\] .* \*b\[j\]==10: UNKNOWN$ +^\[main.assertion.44\] .* \*b\[j\]==13: UNKNOWN$ +^\[main.assertion.45\] .* a\[100\]==&a2: UNKNOWN$ +^\[main.assertion.46\] .* \*a\[100\]==2: UNKNOWN$ +^\[main.assertion.47\] .* b\[1\]==&b1: UNKNOWN$ +^\[main.assertion.48\] .* \*b\[1\]==11: UNKNOWN$ +^\[main.assertion.49\] .* ei\[0\]==&ei1: UNKNOWN$ +^\[main.assertion.50\] .* ei\[0\]==&ei0: UNKNOWN$ +^\[main.assertion.51\] .* ei\[2\]==&ei0: UNKNOWN$ +^\[main.assertion.52\] .* ei\[2\]==&ei1: UNKNOWN$ +^\[main.assertion.53\] .* \*ei\[0\]==41: UNKNOWN$ +^\[main.assertion.54\] .* \*ei\[0\]==40: UNKNOWN$ +^\[main.assertion.55\] .* \*ei\[2\]==40: UNKNOWN$ +^\[main.assertion.56\] .* \*ei\[2\]==41: UNKNOWN$ +^\[main.assertion.57\] .* ej\[0\]==&ej0: UNKNOWN$ +^\[main.assertion.58\] .* ej\[2\]==&ej0: UNKNOWN$ +^\[main.assertion.59\] .* ej\[2\]==&ej1: UNKNOWN$ +^\[main.assertion.60\] .* \*ej\[0\]==50: UNKNOWN$ +^\[main.assertion.61\] .* \*ej\[2\]==50: UNKNOWN$ +^\[main.assertion.62\] .* \*ej\[2\]==51: UNKNOWN$ +^\[main.assertion.63\] .* ek\[0\]==&ek0: UNKNOWN$ +^\[main.assertion.64\] .* \*ek\[0\]==60: UNKNOWN$ +^\[main\.assertion\.65\] .* \*ps\[0\]==4: UNKNOWN$ +^\[main\.assertion\.66\] .* \*ps\[1\]==4: UNKNOWN$ +^\[main\.assertion\.67\] .* x==4: SUCCESS$ +^\[main\.assertion\.68\] .* y==4: FAILURE \(if reachable\)$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-array/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-array/test.desc index 8e076119b4e..badda851b39 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-array/test.desc @@ -1,39 +1,39 @@ -FUTURE +CORE sensitivity_test_two_value_array.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion a\[1\]==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion a\[1\]==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion b\[1\]==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion b\[1\]==1: UNKNOWN$ -^\[main.assertion.5\] .* assertion \*\(b\+1\)==0: UNKNOWN$ -^\[main.assertion.6\] .* assertion \*\(b\+1\)==1: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*\(1\+b\)==0: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*\(1\+b\)==1: UNKNOWN$ -^\[main.assertion.9\] .* assertion 1\[b\]==0: UNKNOWN$ -^\[main.assertion.10\] .* assertion 1\[b\]==1: UNKNOWN$ -^\[main.assertion.11\] .* assertion c\[0\]==0: UNKNOWN$ -^\[main.assertion.12\] .* assertion c\[0\]==1: UNKNOWN$ -^\[main.assertion.13\] .* assertion d\[0\]==0: UNKNOWN$ -^\[main.assertion.14\] .* assertion d\[0\]==2: UNKNOWN$ -^\[main.assertion.15\] .* assertion d\[1\]==0: UNKNOWN$ -^\[main.assertion.16\] .* assertion a\[i\]==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion a\[i\]==1: UNKNOWN$ -^\[main.assertion.18\] .* assertion a\[j\]==0: UNKNOWN$ -^\[main.assertion.19\] .* assertion a\[j\]==1: UNKNOWN$ -^\[main.assertion.20\] .* assertion b\[i\]==1: UNKNOWN$ -^\[main.assertion.21\] .* assertion b\[i\]==0: UNKNOWN$ -^\[main.assertion.22\] .* assertion b\[j\]==0: UNKNOWN$ -^\[main.assertion.23\] .* assertion b\[j\]==1: UNKNOWN$ -^\[main.assertion.24\] .* assertion a\[100\]==0: UNKNOWN$ -^\[main.assertion.25\] .* assertion b\[1\]==0: UNKNOWN$ -^\[main.assertion.26\] .* assertion ei\[0\]==1: UNKNOWN$ -^\[main.assertion.27\] .* assertion ei\[0\]==0: UNKNOWN$ -^\[main.assertion.28\] .* assertion ei\[2\]==0: UNKNOWN$ -^\[main.assertion.29\] .* assertion ei\[2\]==1: UNKNOWN$ -^\[main.assertion.30\] .* assertion ej\[0\]==0: UNKNOWN$ -^\[main.assertion.31\] .* assertion ej\[2\]==0: UNKNOWN$ -^\[main.assertion.32\] .* assertion ek\[0\]==0: UNKNOWN$ +^\[main.assertion.1\] .* a\[1\]==0: UNKNOWN$ +^\[main.assertion.2\] .* a\[1\]==1: UNKNOWN$ +^\[main.assertion.3\] .* b\[1\]==0: UNKNOWN$ +^\[main.assertion.4\] .* b\[1\]==1: UNKNOWN$ +^\[main.assertion.5\] .* \*\(b\+1\)==0: UNKNOWN$ +^\[main.assertion.6\] .* \*\(b\+1\)==1: UNKNOWN$ +^\[main.assertion.7\] .* \*\(1\+b\)==0: UNKNOWN$ +^\[main.assertion.8\] .* \*\(1\+b\)==1: UNKNOWN$ +^\[main.assertion.9\] .* 1\[b\]==0: UNKNOWN$ +^\[main.assertion.10\] .* 1\[b\]==1: UNKNOWN$ +^\[main.assertion.11\] .* c\[0\]==0: UNKNOWN$ +^\[main.assertion.12\] .* c\[0\]==1: UNKNOWN$ +^\[main.assertion.13\] .* d\[0\]==0: UNKNOWN$ +^\[main.assertion.14\] .* d\[0\]==2: UNKNOWN$ +^\[main.assertion.15\] .* d\[1\]==0: UNKNOWN$ +^\[main.assertion.16\] .* a\[i\]==0: UNKNOWN$ +^\[main.assertion.17\] .* a\[i\]==1: UNKNOWN$ +^\[main.assertion.18\] .* a\[j\]==0: UNKNOWN$ +^\[main.assertion.19\] .* a\[j\]==1: UNKNOWN$ +^\[main.assertion.20\] .* b\[i\]==1: UNKNOWN$ +^\[main.assertion.21\] .* b\[i\]==0: UNKNOWN$ +^\[main.assertion.22\] .* b\[j\]==0: UNKNOWN$ +^\[main.assertion.23\] .* b\[j\]==1: UNKNOWN$ +^\[main.assertion.24\] .* a\[100\]==0: UNKNOWN$ +^\[main.assertion.25\] .* b\[1\]==0: UNKNOWN$ +^\[main.assertion.26\] .* ei\[0\]==1: UNKNOWN$ +^\[main.assertion.27\] .* ei\[0\]==0: UNKNOWN$ +^\[main.assertion.28\] .* ei\[2\]==0: UNKNOWN$ +^\[main.assertion.29\] .* ei\[2\]==1: UNKNOWN$ +^\[main.assertion.30\] .* ej\[0\]==0: UNKNOWN$ +^\[main.assertion.31\] .* ej\[2\]==0: UNKNOWN$ +^\[main.assertion.32\] .* ek\[0\]==0: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-array/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-array/test.desc index 1591fb003ca..2e5544cb994 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-array/test.desc @@ -1,21 +1,29 @@ -FUTURE +CORE sensitivity_test_two_value_pointer_to_two_value_array.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion p==&a\[0\]: UNKNOWN$ -^\[main.assertion.2\] .* assertion \*p==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion q==p\+1: UNKNOWN$ -^\[main.assertion.4\] .* assertion \*q==2: UNKNOWN$ -^\[main.assertion.5\] .* assertion q-p==x: UNKNOWN$ -^\[main.assertion.6\] .* assertion a\[1\]==4: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*r==2: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*r==1: UNKNOWN$ -^\[main.assertion.9\] .* assertion \*s==0: UNKNOWN$ -^\[main.assertion.10\] .* assertion \*s==1: UNKNOWN$ -^\[main.assertion.11\] .* assertion t==p\+i: UNKNOWN$ -^\[main.assertion.12\] .* assertion t-p==y: UNKNOWN$ -^\[main.assertion.13\] .* assertion a\[i\]==5: UNKNOWN$ -^\[main.assertion.14\] .* assertion a\[1\]==5: UNKNOWN$ +^\[main\.assertion\.1\] .* p==&a\[0\]: UNKNOWN$ +^\[main\.assertion\.2\] .* \*p==1: UNKNOWN$ +^\[main\.assertion\.3\] .* p\[1\]==2: UNKNOWN$ +^\[main\.assertion\.4\] .* 1\[p\]==2: UNKNOWN$ +^\[main\.assertion\.5\] .* \*\(p\+1\)==2: UNKNOWN$ +^\[main\.assertion\.6\] .* \*\(1\+p\)==2: UNKNOWN$ +^\[main\.assertion\.7\] .* q==p\+1: UNKNOWN$ +^\[main\.assertion\.8\] .* \*q==2: UNKNOWN$ +^\[main\.assertion\.9\] .* \*\(q-1\)==1: UNKNOWN$ +^\[main\.assertion\.10\] .* q-p==x: UNKNOWN$ +^\[main\.assertion\.11\] .* a\[1\]==4: UNKNOWN$ +^\[main\.assertion\.12\] .* a\[1\]==5: UNKNOWN$ +^\[main\.assertion\.13\] .* a\[1\]==6: UNKNOWN$ +^\[main\.assertion\.14\] .* a\[1\]==7: UNKNOWN$ +^\[main\.assertion\.15\] .* \*r==2: UNKNOWN$ +^\[main\.assertion\.16\] .* \*r==1: UNKNOWN$ +^\[main\.assertion\.17\] .* \*s==0: UNKNOWN$ +^\[main\.assertion\.18\] .* \*s==1: UNKNOWN$ +^\[main\.assertion\.19\] .* t==p\+i: UNKNOWN$ +^\[main\.assertion\.20\] .* t-p==y: UNKNOWN$ +^\[main\.assertion\.21\] .* a\[i\]==5: UNKNOWN$ +^\[main\.assertion\.22\] .* a\[1\]==5: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-pointer/test.desc index cdf43ec8535..0cab798c26f 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-pointer/test.desc @@ -1,13 +1,13 @@ -FUTURE +CORE sensitivity_test_two_value_pointer_to_two_value_pointer.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion \*\*x==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion \*\*x==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion \*\*x==1: UNKNOWN$ -^\[main.assertion.4\] .* assertion \*\*x==0: UNKNOWN$ -^\[main.assertion.5\] .* assertion a==2: UNKNOWN$ -^\[main.assertion.6\] .* assertion a==1: UNKNOWN$ +^\[main.assertion.1\] .* \*\*x==0: UNKNOWN$ +^\[main.assertion.2\] .* \*\*x==1: UNKNOWN$ +^\[main.assertion.3\] .* \*\*x==1: UNKNOWN$ +^\[main.assertion.4\] .* \*\*x==0: UNKNOWN$ +^\[main.assertion.5\] .* a==2: UNKNOWN$ +^\[main.assertion.6\] .* a==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-struct/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-struct/test.desc index 131d372007f..013ff96d085 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-pointer-to-two-value-struct/test.desc @@ -1,13 +1,26 @@ -FUTURE +CORE sensitivity_test_two_value_pointer_to_two_value_struct.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion \(\*p\).a==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion \(\*p\).a==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion p->a==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion p->a==1: UNKNOWN$ -^\[main.assertion.5\] .* assertion p->b==2.0: UNKNOWN$ -^\[main.assertion.6\] .* assertion p->b==1.0: UNKNOWN$ +^\[main.assertion.1\] .* \(\*p\).a==0: UNKNOWN$ +^\[main.assertion.2\] .* \(\*p\).a==1: UNKNOWN$ +^\[main.assertion.3\] .* p->a==0: UNKNOWN$ +^\[main.assertion.4\] .* p->a==1: UNKNOWN$ +^\[main.assertion.5\] .* p->b==2.0: UNKNOWN$ +^\[main.assertion.6\] .* p->b==1.0: UNKNOWN$ +^\[main.assertion.8\] .* comp_p==&x.b: UNKNOWN$ +^\[main.assertion.9\] .* \*comp_p==0: UNKNOWN$ +^\[main.assertion.10\] .* \*comp_p==1: UNKNOWN$ +^\[main.assertion.11\] .* compb_p==&x.a: UNKNOWN$ +^\[main.assertion.12\] .* compb_p==&x.b: UNKNOWN$ +^\[main.assertion.13\] .* \*compb_p==2.0: UNKNOWN$ +^\[main.assertion.14\] .* \*compb_p==1.0: UNKNOWN$ +^\[main.assertion.15\] .* implicit_p==&x.a: UNKNOWN$ +^\[main.assertion.16\] .* implicit_p==&x: UNKNOWN$ +^\[main.assertion.17\] .* \*implicit_p==0: UNKNOWN$ +^\[main.assertion.18\] .* \*implicit_p==1: UNKNOWN$ +^\[main.assertion.19\] .* x.a==5: UNKNOWN$ +^\[main.assertion.20\] .* x.a==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-pointer/test.desc index 8ad08e49dc7..ba2ce783e65 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-pointer/test.desc @@ -1,21 +1,21 @@ -FUTURE +CORE sensitivity_test_two_value_pointer.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x==&a: UNKNOWN$ -^\[main.assertion.2\] .* assertion x==&b: UNKNOWN$ -^\[main.assertion.3\] .* assertion x==x2: UNKNOWN$ -^\[main.assertion.4\] .* assertion x==y: UNKNOWN$ -^\[main.assertion.5\] .* assertion \*x==0: UNKNOWN$ -^\[main.assertion.6\] .* assertion \*x==1: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*x==1: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*x==0: UNKNOWN$ -^\[main.assertion.9\] .* assertion a==2: UNKNOWN$ -^\[main.assertion.10\] .* assertion a==0: UNKNOWN$ -^\[main.assertion.11\] .* assertion x==&a: UNKNOWN$ -^\[main.assertion.12\] .* assertion \*x==0: UNKNOWN$ -^\[main.assertion.13\] .* assertion x==&a: UNKNOWN$ -^\[main.assertion.14\] .* assertion x==&b: UNKNOWN$ +^\[main\.assertion\.1\] .* x==&a: UNKNOWN$ +^\[main\.assertion\.2\] .* x==&b: UNKNOWN$ +^\[main\.assertion\.3\] .* x==x2: UNKNOWN$ +^\[main\.assertion\.4\] .* x==y: UNKNOWN$ +^\[main\.assertion\.5\] .* \*x==0: UNKNOWN$ +^\[main\.assertion\.6\] .* \*x==1: UNKNOWN$ +^\[main\.assertion\.7\] .* \*x==1: UNKNOWN$ +^\[main\.assertion\.8\] .* \*x==0: UNKNOWN$ +^\[main\.assertion\.9\] .* a==2: UNKNOWN$ +^\[main\.assertion\.10\] .* a==0: UNKNOWN$ +^\[main\.assertion\.11\] .* x==&a: UNKNOWN$ +^\[main\.assertion\.12\] .* \*x==0: UNKNOWN$ +^\[main\.assertion\.13\] .* x==&a: UNKNOWN$ +^\[main\.assertion\.14\] .* x==&b: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-array/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-array/test.desc index 243aa1735a8..ce077813a27 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-array/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-array/test.desc @@ -1,24 +1,24 @@ -FUTURE +CORE sensitivity_test_two_value_struct_of_two_value_array.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion \*\(x.a\+0\)==0: UNKNOWN$ -^\[main.assertion.3\] .* assertion \*\(0\+x.a\)==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion 0\[x.a\]==0: UNKNOWN$ -^\[main.assertion.5\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.6\] .* assertion x.a\[1\]==1: UNKNOWN$ -^\[main.assertion.7\] .* assertion x.b\[0\]==3.0f: UNKNOWN$ -^\[main.assertion.8\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.a\[1\]==1: UNKNOWN$ -^\[main.assertion.10\] .* assertion x.b\[2\]>0.0f: UNKNOWN$ -^\[main.assertion.11\] .* assertion x.b\[2\]==15.0f: UNKNOWN$ -^\[main.assertion.12\] .* assertion x.b\[2\]==1.0f: UNKNOWN$ -^\[main.assertion.13\] .* assertion x.b\[0\]==3.0f: UNKNOWN$ -^\[main.assertion.14\] .* assertion x.a\[0\]<12: UNKNOWN$ -^\[main.assertion.15\] .* assertion x.a\[0\]>2: UNKNOWN$ -^\[main.assertion.16\] .* assertion x.a\[0\]==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion x.a\[1\]==1: UNKNOWN$ +^\[main.assertion.1\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.2\] .* \*\(x.a\+0\)==0: UNKNOWN$ +^\[main.assertion.3\] .* \*\(0\+x.a\)==0: UNKNOWN$ +^\[main.assertion.4\] .* 0\[x.a\]==0: UNKNOWN$ +^\[main.assertion.5\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.6\] .* x.a\[1\]==1: UNKNOWN$ +^\[main.assertion.7\] .* x.b\[0\]==3.0f: UNKNOWN$ +^\[main.assertion.8\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.9\] .* x.a\[1\]==1: UNKNOWN$ +^\[main.assertion.10\] .* x.b\[2\]>0.0f: UNKNOWN$ +^\[main.assertion.11\] .* x.b\[2\]==15.0f: UNKNOWN$ +^\[main.assertion.12\] .* x.b\[2\]==1.0f: UNKNOWN$ +^\[main.assertion.13\] .* x.b\[0\]==3.0f: UNKNOWN$ +^\[main.assertion.14\] .* x.a\[0\]<12: UNKNOWN$ +^\[main.assertion.15\] .* x.a\[0\]>2: UNKNOWN$ +^\[main.assertion.16\] .* x.a\[0\]==0: UNKNOWN$ +^\[main.assertion.17\] .* x.a\[1\]==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-pointer/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-pointer/test.desc index 6f339498c07..4180765e734 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-pointer/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-pointer/test.desc @@ -1,33 +1,33 @@ -FUTURE +CORE sensitivity_test_two_value_struct_of_two_value_pointer.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a==&a1: UNKNOWN$ -^\[main.assertion.2\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.3\] .* assertion x.b==&b1: UNKNOWN$ -^\[main.assertion.4\] .* assertion x.b==&b2: UNKNOWN$ -^\[main.assertion.5\] .* assertion \*x.a==0: UNKNOWN$ -^\[main.assertion.6\] .* assertion \*x.a==100: UNKNOWN$ -^\[main.assertion.7\] .* assertion \*x.b==10.0f: UNKNOWN$ -^\[main.assertion.8\] .* assertion \*x.b==110.0f: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.a==&a1: UNKNOWN$ -^\[main.assertion.10\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.11\] .* assertion \*x.a==0: UNKNOWN$ -^\[main.assertion.12\] .* assertion \*x.a==100: UNKNOWN$ -^\[main.assertion.13\] .* assertion x.a==&a1: UNKNOWN$ -^\[main.assertion.14\] .* assertion x.b==&b2: UNKNOWN$ -^\[main.assertion.15\] .* assertion x.b==&b3: UNKNOWN$ -^\[main.assertion.16\] .* assertion \*x.a==0: UNKNOWN$ -^\[main.assertion.17\] .* assertion \*x.b==11.0f: UNKNOWN$ -^\[main.assertion.18\] .* assertion \*x.b==12.0f: UNKNOWN$ -^\[main.assertion.19\] .* assertion x.a==&a2: UNKNOWN$ -^\[main.assertion.20\] .* assertion x.a==&a3: UNKNOWN$ -^\[main.assertion.21\] .* assertion x.b==&b3: UNKNOWN$ -^\[main.assertion.22\] .* assertion x.b==&b4: UNKNOWN$ -^\[main.assertion.23\] .* assertion \*x.a==1: UNKNOWN$ -^\[main.assertion.24\] .* assertion \*x.a==2: UNKNOWN$ -^\[main.assertion.25\] .* assertion \*x.b==12.0f: UNKNOWN$ -^\[main.assertion.26\] .* assertion \*x.b==13.0f: UNKNOWN$ +^\[main.assertion.1\] .* x.a==&a1: UNKNOWN$ +^\[main.assertion.2\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.3\] .* x.b==&b1: UNKNOWN$ +^\[main.assertion.4\] .* x.b==&b2: UNKNOWN$ +^\[main.assertion.5\] .* \*x.a==0: UNKNOWN$ +^\[main.assertion.6\] .* \*x.a==100: UNKNOWN$ +^\[main.assertion.7\] .* \*x.b==10.0f: UNKNOWN$ +^\[main.assertion.8\] .* \*x.b==110.0f: UNKNOWN$ +^\[main.assertion.9\] .* x.a==&a1: UNKNOWN$ +^\[main.assertion.10\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.11\] .* \*x.a==0: UNKNOWN$ +^\[main.assertion.12\] .* \*x.a==100: UNKNOWN$ +^\[main.assertion.13\] .* x.a==&a1: UNKNOWN$ +^\[main.assertion.14\] .* x.b==&b2: UNKNOWN$ +^\[main.assertion.15\] .* x.b==&b3: UNKNOWN$ +^\[main.assertion.16\] .* \*x.a==0: UNKNOWN$ +^\[main.assertion.17\] .* \*x.b==11.0f: UNKNOWN$ +^\[main.assertion.18\] .* \*x.b==12.0f: UNKNOWN$ +^\[main.assertion.19\] .* x.a==&a2: UNKNOWN$ +^\[main.assertion.20\] .* x.a==&a3: UNKNOWN$ +^\[main.assertion.21\] .* x.b==&b3: UNKNOWN$ +^\[main.assertion.22\] .* x.b==&b4: UNKNOWN$ +^\[main.assertion.23\] .* \*x.a==1: UNKNOWN$ +^\[main.assertion.24\] .* \*x.a==2: UNKNOWN$ +^\[main.assertion.25\] .* \*x.b==12.0f: UNKNOWN$ +^\[main.assertion.26\] .* \*x.b==13.0f: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-struct/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-struct/test.desc index d4fb9dbbbd4..6d412839f2f 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-struct-of-two-value-struct/test.desc @@ -1,18 +1,18 @@ -FUTURE +CORE sensitivity_test_two_value_struct_of_two_value_struct.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.s1.a==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion x.s2.b==3.0f: UNKNOWN$ -^\[main.assertion.3\] .* assertion x.s1.a==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion x.s1.a==10: UNKNOWN$ -^\[main.assertion.5\] .* assertion x.s1.b==1.0f: UNKNOWN$ -^\[main.assertion.6\] .* assertion x.s2.b==3.0f: UNKNOWN$ -^\[main.assertion.7\] .* assertion x.s2.b==0.0f: UNKNOWN$ -^\[main.assertion.8\] .* assertion x.s1.a==20: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.s1.a<30: UNKNOWN$ -^\[main.assertion.10\] .* assertion x.s2.a==22: UNKNOWN$ -^\[main.assertion.11\] .* assertion x.s2.a<30: UNKNOWN$ +^\[main.assertion.1\] .* x.s1.a==0: UNKNOWN$ +^\[main.assertion.2\] .* x.s2.b==3.0f: UNKNOWN$ +^\[main.assertion.3\] .* x.s1.a==0: UNKNOWN$ +^\[main.assertion.4\] .* x.s1.a==10: UNKNOWN$ +^\[main.assertion.5\] .* x.s1.b==1.0f: UNKNOWN$ +^\[main.assertion.6\] .* x.s2.b==3.0f: UNKNOWN$ +^\[main.assertion.7\] .* x.s2.b==0.0f: UNKNOWN$ +^\[main.assertion.8\] .* x.s1.a==20: UNKNOWN$ +^\[main.assertion.9\] .* x.s1.a<30: UNKNOWN$ +^\[main.assertion.10\] .* x.s2.a==22: UNKNOWN$ +^\[main.assertion.11\] .* x.s2.a<30: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/sensitivity-test-two-value-struct/test.desc b/regression/goto-analyzer/sensitivity-test-two-value-struct/test.desc index 9d0b92caee5..544aa1a6656 100644 --- a/regression/goto-analyzer/sensitivity-test-two-value-struct/test.desc +++ b/regression/goto-analyzer/sensitivity-test-two-value-struct/test.desc @@ -1,16 +1,16 @@ -FUTURE +CORE sensitivity_test_two_value_struct.c ---variable --verify +--variable-sensitivity --verify ^EXIT=0$ ^SIGNAL=0$ -^\[main.assertion.1\] .* assertion x.a==0: UNKNOWN$ -^\[main.assertion.2\] .* assertion x.a==1: UNKNOWN$ -^\[main.assertion.3\] .* assertion x.a==0: UNKNOWN$ -^\[main.assertion.4\] .* assertion x.a==0: UNKNOWN$ -^\[main.assertion.5\] .* assertion x.b>0.0f: UNKNOWN$ -^\[main.assertion.6\] .* assertion x.b==1.0f: UNKNOWN$ -^\[main.assertion.7\] .* assertion x.a<2: UNKNOWN$ -^\[main.assertion.8\] .* assertion x.a>2: UNKNOWN$ -^\[main.assertion.9\] .* assertion x.a==1: UNKNOWN$ +^\[main.assertion.1\] .* x.a==0: UNKNOWN$ +^\[main.assertion.2\] .* x.a==1: UNKNOWN$ +^\[main.assertion.3\] .* x.a==0: UNKNOWN$ +^\[main.assertion.4\] .* x.a==0: UNKNOWN$ +^\[main.assertion.5\] .* x.b>0.0f: UNKNOWN$ +^\[main.assertion.6\] .* x.b==1.0f: UNKNOWN$ +^\[main.assertion.7\] .* x.a<2: UNKNOWN$ +^\[main.assertion.8\] .* x.a>2: UNKNOWN$ +^\[main.assertion.9\] .* x.a==1: UNKNOWN$ -- ^warning: ignoring diff --git a/regression/goto-analyzer/unreachable_assertions_01/main.c b/regression/goto-analyzer/unreachable_assertions_01/main.c new file mode 100644 index 00000000000..35979cef8ce --- /dev/null +++ b/regression/goto-analyzer/unreachable_assertions_01/main.c @@ -0,0 +1,32 @@ +#include + +int nondet_int(void); + +int main(int argc, char **argv) +{ + int a = 1; + int b = 2; + int x = nondet_int(); + int y = nondet_int(); + + if(a == b) + __CPROVER_assert(0, "0"); // Trivial false + + if(a == b) + __CPROVER_assert(1, "1"); // Trivial true + + if(a == b) + __CPROVER_assert(x == y, "x == y"); // Undetermined + + if(a == b) + __CPROVER_assert( + !(x == y) || (x + 1 + a == b + y), + "!(x == y) || (x + 1 + a == b + y)"); // Non-trivial true + + if(a == b) + __CPROVER_assert( + !(!(x == y) || (x + 1 + a == b + y)), + "!(!(x == y) || (x + 1 + a == b + y)"); // Non-trivial false + + return 0; +} diff --git a/regression/goto-analyzer/unreachable_assertions_01/test.desc b/regression/goto-analyzer/unreachable_assertions_01/test.desc new file mode 100644 index 00000000000..7bbc197986e --- /dev/null +++ b/regression/goto-analyzer/unreachable_assertions_01/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--verify --variable-sensitivity +^\[main\.assertion\.1\] line 13 0: SUCCESS \(unreachable\)$ +^\[main\.assertion\.2\] line 16 1: SUCCESS \(unreachable\)$ +^\[main\.assertion\.3\] line 19 x == y: SUCCESS \(unreachable\)$ +^\[main\.assertion\.4\] line 22 !\(x == y\) || \(x + 1 + a == b + y\): SUCCESS \(unreachable\)$ +^\[main\.assertion\.5\] line 25 !\(!\(x == y\) || \(x + 1 + a == b + y\)\): SUCCESS \(unreachable\)$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/value-set-function-pointers-arrays/main.c b/regression/goto-analyzer/value-set-function-pointers-arrays/main.c new file mode 100644 index 00000000000..22d42a5f48d --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-arrays/main.c @@ -0,0 +1,41 @@ +#include + +typedef int (*fptr_t)(int); + +int f(int x) +{ + return x + 1; +} +int g(int x) +{ + return x; +} +int h(int x) +{ + return x - 1; +} + +int main(void) +{ + int nondet_choice; + + // Reading from array + fptr_t fun1; + fptr_t fun_array1[] = {f, g}; + if(nondet_choice) + fun1 = fun_array1[0]; + else + fun1 = fun_array1[1]; + fun1(5); + + // Writing to array + fptr_t fun_array2[2]; + if(nondet_choice) + fun_array2[0] = f; + else + fun_array2[0] = g; + fptr_t fun_array3[2]; + fun_array3[0] = fun_array2[0]; + fun_array3[1] = fun_array2[1]; + fun_array3[0](5); +} diff --git a/regression/goto-analyzer/value-set-function-pointers-arrays/test.desc b/regression/goto-analyzer/value-set-function-pointers-arrays/test.desc new file mode 100644 index 00000000000..8f89cd771a0 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-arrays/test.desc @@ -0,0 +1,13 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --show --pointer-check --three-way-merge +^file main.c line 29 function main: replacing function pointer by 2 possible targets$ +^file main.c line 40 function main: replacing function pointer by 2 possible targets$ +^main::1::fun1 \(\) -> value-set-begin: ptr ->\([fg]\), ptr ->\([fg]\), :value-set-end$ +^main::1::fun_array3 \(\) -> \{\[0\] = value-set-begin: ptr ->\([fg]\), ptr ->\([fg]\), :value-set-end$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +^main::1::fun1 \(\) -> value-set-begin: .*ptr ->\(h\).* :value-set-end$ +^main::1::fun_array3 \(\) -> \{\[0\] = value-set-begin: .*ptr ->\(h\).* :value-set-end$ diff --git a/regression/goto-analyzer/value-set-function-pointers-incremented/main.c b/regression/goto-analyzer/value-set-function-pointers-incremented/main.c new file mode 100644 index 00000000000..6c47bc5e1c4 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-incremented/main.c @@ -0,0 +1,30 @@ +#include + +typedef int (*fptr_t)(int); +fptr_t fun_global, fun_global_show; + +int f(int x) +{ + return x + 1; +} +int g(int x) +{ + return x; +} +int h(int x) +{ + return x - 1; +} + +int main(void) +{ + // This line is needed so that g is considered as a possibility for the TOP + // value + fptr_t dummy = g; + + // function pointer incremented should be top + fptr_t fun_incremented = f; + ++fun_incremented; + fun_incremented(5); + fptr_t fun_incremented_show = fun_incremented; +} diff --git a/regression/goto-analyzer/value-set-function-pointers-incremented/test.desc b/regression/goto-analyzer/value-set-function-pointers-incremented/test.desc new file mode 100644 index 00000000000..57e14147e00 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-incremented/test.desc @@ -0,0 +1,10 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --show --pointer-check +^file main.c line 28 function main: replacing function pointer by 2 possible targets$ +^main::1::fun_incremented_show \(\) -> TOP$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +^main::1::fun_incremented_show \(\) -> value-set-begin: .* :value-set-end$ diff --git a/regression/goto-analyzer/value-set-function-pointers-simple/main.c b/regression/goto-analyzer/value-set-function-pointers-simple/main.c new file mode 100644 index 00000000000..11debd48721 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-simple/main.c @@ -0,0 +1,53 @@ +#include + +typedef int (*fptr_t)(int); +fptr_t fun_global, fun_global_show; + +int f(int x) +{ + return x + 1; +} +int g(int x) +{ + return x; +} +int h(int x) +{ + return x - 1; +} + +int main(void) +{ + int nondet_choice; + + // Variable never written to should be top + fptr_t fun0; + fun0(5); + + fptr_t fun1 = f; + fun1(5); + + fptr_t fun2 = f; + if(nondet_choice) + fun2 = g; + fun2(5); + fptr_t fun2_show = fun2; + + fptr_t fun3; + if(nondet_choice) + fun3 = f; + else + fun3 = g; + fun3(5); + fptr_t fun3_show = fun3; + + // Global variable + if(nondet_choice) + fun_global = f; + else + fun_global = g; + fun_global(5); + fun_global_show = fun_global; + + return 0; +} diff --git a/regression/goto-analyzer/value-set-function-pointers-simple/test.desc b/regression/goto-analyzer/value-set-function-pointers-simple/test.desc new file mode 100644 index 00000000000..6906c50e1a6 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-simple/test.desc @@ -0,0 +1,26 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --show --pointer-check --three-way-merge +^file main.c line 25 function main: replacing function pointer by 2 possible targets$ +^file main.c line 28 function main: replacing function pointer by 2 possible targets$ +^file main.c line 33 function main: replacing function pointer by 2 possible targets$ +^file main.c line 41 function main: replacing function pointer by 2 possible targets$ +^file main.c line 49 function main: replacing function pointer by 2 possible targets$ +^main::1::fun0 \(\) -> TOP$ +^main::1::fun1 \(\) -> value-set-begin: ptr ->\(f\), :value-set-end$ +^main::1::fun2_show \(\) -> value-set-begin: (TOP, )?ptr ->\([fg]\), (TOP, )?ptr ->\([fg]\), (TOP, )?:value-set-end$ +^main::1::fun3_show \(\) -> value-set-begin: ptr ->\([fg]\), ptr ->\([fg]\), :value-set-end$ +^fun_global_show \(\) -> value-set-begin: (TOP, )?ptr ->\([fg]\), (TOP, )?ptr ->\([fg]\), (TOP, )?:value-set-end$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +^main::1::fun0_show \(\) -> value-set-begin: .* :value-set-end$ +^main::1::fun1_show \(\) -> value-set-begin: .*ptr ->\(h\).* :value-set-end$ +^main::1::fun2_show \(\) -> value-set-begin: .*ptr ->\(h\).* :value-set-end$ +^main::1::fun3_show \(\) -> \{\[0\] = value-set-begin: .*ptr ->\(h\).* :value-set-end$ +^fun_global_show \(\) -> value-set-begin: .*ptr ->\(h\).* :value-set-end$ +-- +These TOP values in the sets shouldn't exist. +They're caused by a quirk in the implementation, see +https://github.com/diffblue/cbmc/issues/5307 which has been filed to fix this bug. diff --git a/regression/goto-analyzer/value-set-function-pointers-structs/main.c b/regression/goto-analyzer/value-set-function-pointers-structs/main.c new file mode 100644 index 00000000000..4e084010387 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-structs/main.c @@ -0,0 +1,55 @@ +#include + +typedef int (*fptr_t)(int); + +int f(int x) +{ + return x + 1; +} +int g(int x) +{ + return x; +} +int h(int x) +{ + return x - 1; +} + +struct struct_containing_fptr +{ + int i; + fptr_t fptr; + double d; +}; + +int main(void) +{ + int nondet_choice; + + // Read from struct + struct struct_containing_fptr s0, s1; + s0.fptr = f; + s1.fptr = g; + fptr_t fun1; + if(nondet_choice) + fun1 = s0.fptr; + else + fun1 = s1.fptr; + fun1(5); + + // Write to struct + struct struct_containing_fptr s2; + if(nondet_choice) + s2.fptr = f; + else + s2.fptr = g; + s2.fptr(5); + + // Array of structs + struct struct_containing_fptr s_array[3]; + s_array[0] = s0; + s_array[1] = s1; + s_array[2] = s2; + fptr_t fun2 = (s_array + 1)->fptr; + fun2(5); +} diff --git a/regression/goto-analyzer/value-set-function-pointers-structs/test.desc b/regression/goto-analyzer/value-set-function-pointers-structs/test.desc new file mode 100644 index 00000000000..b568837d705 --- /dev/null +++ b/regression/goto-analyzer/value-set-function-pointers-structs/test.desc @@ -0,0 +1,16 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --show --pointer-check +^file main.c line 38 function main: replacing function pointer by 2 possible targets$ +^file main.c line 46 function main: replacing function pointer by 2 possible targets$ +^file main.c line 54 function main: replacing function pointer by 2 possible targets$ +^main::1::fun1 \(\) -> value-set-begin: ptr ->\([fg]\), ptr ->\([fg]\), :value-set-end$ +^main::1::s2 \(\) -> \{\.fptr=value-set-begin: ptr ->\([fg]\), ptr ->\([fg]\), :value-set-end\}$ +^main::1::fun2 \(\) -> value-set-begin: ptr ->\(g\), :value-set-end$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +^main::1::fun1 \(\) -> value-set-begin: .*ptr ->\(h\).* :value-set-end$ +^main::1::s2 \(\) -> \{\.fptr=value-set-begin: .*ptr ->\(h\).* :value-set-end\}$ +^main::1::fun2 \(\) -> value-set-begin: .*ptr ->\(h\).* :value-set-end$ diff --git a/regression/goto-analyzer/value-set-simple/main.c b/regression/goto-analyzer/value-set-simple/main.c new file mode 100644 index 00000000000..ed3f58f2319 --- /dev/null +++ b/regression/goto-analyzer/value-set-simple/main.c @@ -0,0 +1,50 @@ +#include + +int global_int = 0; +int global_int_show = 0; + +int main(void) +{ + int nondet_choice; + + if(nondet_choice) + global_int = 1; + else + global_int = 2; + global_int_show = global_int; + + assert(global_int == 2); + assert(global_int == 1 || global_int == 2); + assert(global_int > 0); + assert(global_int > 3); + + double local_double; + + if(nondet_choice) + local_double = 1.0; + else + local_double = 2.0; + double local_double_show = local_double; + + assert(local_double == 2.0); + assert(local_double == 1.0 || local_double == 2.0); + assert(local_double > 0.0); + assert(local_double > 3.0); + + double d1 = 1.0; + double d2 = 2.0; + double *local_double_ptr; + + if(nondet_choice) + local_double_ptr = &d1; + else + local_double_ptr = &d2; + double *local_double_ptr_show = local_double_ptr; + + assert(local_double_ptr == &d2); + assert(local_double_ptr == &d1 || local_double == &d2); + assert(*local_double_ptr > 0.0); + assert(*local_double_ptr > 3.0); + + return 0; +} diff --git a/regression/goto-analyzer/value-set-simple/test_show.desc b/regression/goto-analyzer/value-set-simple/test_show.desc new file mode 100644 index 00000000000..592aca1bc00 --- /dev/null +++ b/regression/goto-analyzer/value-set-simple/test_show.desc @@ -0,0 +1,10 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --show --pointer-check +^global_int_show \(\) -> value-set-begin: [12], [12], :value-set-end$ +^main::1::local_double_show \(\) -> value-set-begin: [12]\.0, [12]\.0, :value-set-end$ +^main::1::local_double_ptr_show \(\) -> value-set-begin: ptr ->\(main::1::d[12]\), ptr ->\(main::1::d[12]\), :value-set-end$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/value-set-simple/test_verify.desc b/regression/goto-analyzer/value-set-simple/test_verify.desc new file mode 100644 index 00000000000..c4c3bf6a647 --- /dev/null +++ b/regression/goto-analyzer/value-set-simple/test_verify.desc @@ -0,0 +1,22 @@ +KNOWNBUG +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --verify --pointer-check +^\[main.assertion.1\] line 16 assertion global_int == 2: UNKNOWN$ +^\[main.assertion.2\] line 17 assertion global_int == 1 \|\| global_int == 2: SUCCESS$ +^\[main.assertion.3\] line 18 assertion global_int > 0: SUCCESS$ +^\[main.assertion.4\] line 19 assertion global_int > 3: FAILURE \(if reachable\)$ +^\[main.assertion.5\] line 29 assertion local_double == 2.0: UNKNOWN$ +^\[main.assertion.6\] line 30 assertion local_double == 1.0 \|\| local_double == 2.0: SUCCESS$ +^\[main.assertion.7\] line 31 assertion local_double > 0.0: SUCCESS$ +^\[main.assertion.8\] line 32 assertion local_double > 3.0: FAILURE \(if reachable\)$ +^\[main.assertion.9\] line 44 assertion local_double_ptr == &d2: UNKNOWN$ +^\[main.assertion.10\] line 45 assertion local_double_ptr == &d1 \|\| local_double == &d2: SUCCESS$ +^\[main.assertion.11\] line 46 assertion \*local_double_ptr > 0.0: SUCCESS$ +^\[main.assertion.12\] line 47 assertion \*local_double_ptr > 3.0: FAILURE \(if reachable\)$ +^Summary: 6 pass, 3 fail if reachable, 3 unknown$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +-- +ADA-494 has been filed to fix this. diff --git a/regression/goto-analyzer/value-set-structs/main.c b/regression/goto-analyzer/value-set-structs/main.c new file mode 100644 index 00000000000..422534487e2 --- /dev/null +++ b/regression/goto-analyzer/value-set-structs/main.c @@ -0,0 +1,67 @@ +#include + +struct my_struct +{ + int i; + double d; + char str[2]; +}; + +int main(void) +{ + int nondet_choice, nondet_choice2; + + struct my_struct s; + + if(nondet_choice) + s.d = 1.0; + else + s.d = 2.0; + + if(nondet_choice) + { + s.str[0] = 'x'; + s.str[1] = '\n'; + } + else + { + s.str[0] = 'y'; + s.str[1] = '\n'; + } + + struct my_struct s_show = s; + + assert(s.i == 0); + + assert(s.d == 1.0); + assert(s.d == 1.0 || s.d == 2.0); + assert(s.d > 0.0); + assert(s.d > 10.0); + + assert(s.str[0] == 'x'); + assert(s.str[0] == 'x' || s.str[0] == 'y'); + assert(s.str[1] == '\n'); + + struct my_struct t = {1, 3.0, {'z', '\n'}}; + struct my_struct u; + + if(nondet_choice2) + u = s; + else + u = t; + + struct my_struct u_show = u; + + assert(u.i == 1); + + assert(u.d == 3.0); + assert(u.d == 1.0 || u.d == 2.0 || u.d == 3.0); + assert(u.d > 0.0); + assert(u.d > 10.0); + + assert(u.str[0] == 'z'); + assert(u.str[0] == 'x' || u.str[0] == 'y' || u.str[0] == 'z'); + assert(u.str[1] == '\n'); + + return 0; +} diff --git a/regression/goto-analyzer/value-set-structs/test_show.desc b/regression/goto-analyzer/value-set-structs/test_show.desc new file mode 100644 index 00000000000..68001656a3a --- /dev/null +++ b/regression/goto-analyzer/value-set-structs/test_show.desc @@ -0,0 +1,10 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --show --pointer-check +activate-multi-line-match +main::1::s_show \(\) -> \{\.d=value-set-begin: [12]\.0, [12]\.0, :value-set-end, \.str=\{\[0\] = value-set-begin: '[xy]', '[xy]', :value-set-end\n\[1\] = value-set-begin: '\\n', :value-set-end +main::1::u_show \(\) -> \{\.d=value-set-begin: [123]\.0, [123].0, [123]\.0, :value-set-end, \.str=\{\[0\] = value-set-begin: '[xyz]', '[xyz]', '[xyz]', :value-set-end\n\[1\] = value-set-begin: '\\n', :value-set-end +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/value-set-structs/test_verify.desc b/regression/goto-analyzer/value-set-structs/test_verify.desc new file mode 100644 index 00000000000..8aeaa4c90b5 --- /dev/null +++ b/regression/goto-analyzer/value-set-structs/test_verify.desc @@ -0,0 +1,26 @@ +KNOWNBUG +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --vsd-value-sets --verify --pointer-check +^\[main\.assertion\.1\] line 33 assertion s\.i == 0: UNKNOWN$ +^\[main\.assertion\.2\] line 35 assertion s\.d == 1.0: UNKNOWN$ +^\[main\.assertion\.3\] line 36 assertion s\.d == 1.0 \|\| s.d == 2.0: SUCCESS$ +^\[main\.assertion\.4\] line 37 assertion s\.d > 0.0: SUCCESS$ +^\[main\.assertion\.5\] line 38 assertion s\.d > 10.0: FAILURE \(if reachable\)$ +^\[main\.assertion\.6\] line 40 assertion s\.str\[0\] == 'x': UNKNOWN$ +^\[main\.assertion\.7\] line 41 assertion s\.str\[0\] == 'x' \|\| s.str\[0\] == 'y': SUCCESS$ +^\[main\.assertion\.8\] line 42 assertion s\.str\[1\] == '\\n': SUCCESS$ +^\[main\.assertion\.9\] line 55 assertion u\.i == 1: UNKNOWN$ +^\[main\.assertion\.10\] line 57 assertion u\.d == 3.0: UNKNOWN$ +^\[main\.assertion\.11\] line 58 assertion u\.d == 1.0 \|\| u\.d == 2.0 \|\| u\.d == 3.0: SUCCESS$ +^\[main\.assertion\.12\] line 59 assertion u\.d > 0.0: SUCCESS$ +^\[main\.assertion\.13\] line 60 assertion u\.d > 10.0: FAILURE \(if reachable\)$ +^\[main\.assertion\.14\] line 62 assertion u\.str\[0\] == 'z': UNKNOWN$ +^\[main\.assertion\.15\] line 63 assertion u\.str\[0\] == 'x' \|\| u.str\[0\] == 'y' \|\| u.str\[0\] == 'z': SUCCESS$ +^\[main\.assertion\.16\] line 64 assertion u\.str\[1\] == '\\n': SUCCESS$ +^Summary: 8 pass, 2 fail if reachable, 6 unknown$ +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +-- +ADA-494 has been filed to fix this. diff --git a/regression/goto-analyzer/variable-sensitivity-annihiliator-test/main.c b/regression/goto-analyzer/variable-sensitivity-annihiliator-test/main.c new file mode 100644 index 00000000000..ca69ea73386 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-annihiliator-test/main.c @@ -0,0 +1,25 @@ +extern int g_in1; +extern int g_in2; + +int g_out; + +void func(void); + +void main(void) +{ + g_in1 = 1; + + func(); +} + +void func(void) +{ + if(g_in1 == 0) + g_out = 1; // unreachable. + + if(g_in2 == 0) + g_out = 2; + + if(g_in1 == 0 && g_in2 == 0) + g_out = 3; // unreachable, but not . +} diff --git a/regression/goto-analyzer/variable-sensitivity-annihiliator-test/test.desc b/regression/goto-analyzer/variable-sensitivity-annihiliator-test/test.desc new file mode 100644 index 00000000000..c935c4d26ca --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-annihiliator-test/test.desc @@ -0,0 +1,10 @@ +CORE +main.c +--variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --unreachable-instructions +^EXIT=0$ +^SIGNAL=0$ +line 18 function func +g_out = 1; +line 24 function func +g_out = 3; +-- diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-array/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-array/main.c new file mode 100644 index 00000000000..4f0b403fe4f --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-array/main.c @@ -0,0 +1,27 @@ +void param_test_val(int array[], int x) +{ + array[1] = x; +} + +void pass_param() +{ + int b[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + param_test_val(b, 5); + + // This assertion should be true since b[0] is unmodified + __CPROVER_assert(b[0] == 0, "b[0]==0"); + + // This assertion should be true since b[1] can only have one value + __CPROVER_assert(b[1] == 5, "b[1]==5"); + + param_test_val(b, 6); + + // Both these assertions shoul be unknown since the domain for + // param_test_val, x is TOP so we don't know what is written + __CPROVER_assert(b[1] == 5, "b[1]==5"); + __CPROVER_assert(b[1] == 6, "b[1]==6"); + + // b[0] is still not modified so this assertion should still be true + __CPROVER_assert(b[0] == 0, "b[0]==0"); +} \ No newline at end of file diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-array/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-array/test.desc new file mode 100644 index 00000000000..620a8f713d0 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-array/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--function pass_param --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +\[pass_param\.assertion\.1\] line 13 b\[0\]==0: SUCCESS +\[pass_param\.assertion\.2\] line 16 b\[1\]==5: SUCCESS +\[pass_param\.assertion\.3\] line 22 b\[1\]==5: UNKNOWN +\[pass_param\.assertion\.4\] line 23 b\[1\]==6: UNKNOWN +\[pass_param\.assertion\.5\] line 26 b\[0\]==0: SUCCESS +-- diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-goto-after-function/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-goto-after-function/main.c new file mode 100644 index 00000000000..993f0ee5ace --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-goto-after-function/main.c @@ -0,0 +1,27 @@ +void param_function(int *old_val, int new_val) +{ + *old_val = new_val; +} + +void param_function_extra(int *old_val, int new_val) +{ + *old_val = new_val; +} + +void test_param_function(int nondet) +{ + int a = 5; + int b; + __CPROVER_assert(a == 5, "a==5"); + if(nondet) + { + a = 6; + b = 7; + goto whatever; + } + param_function(&a, 10); + param_function_extra(&b, 7); +whatever: + __CPROVER_assert(a == 10, "a==10"); + __CPROVER_assert(b == 7, "b==7"); +} \ No newline at end of file diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-goto-after-function/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-goto-after-function/test.desc new file mode 100644 index 00000000000..652444a902a --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-goto-after-function/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--function test_param_function --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +\[test_param_function.assertion\.1\] line \d+ a==5: SUCCESS +\[test_param_function.assertion\.2\] line \d+ a==10: UNKNOWN +\[test_param_function.assertion\.3\] line \d+ b==7: SUCCESS +-- diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-non-terminating-function/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-non-terminating-function/main.c new file mode 100644 index 00000000000..589d00a5582 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-non-terminating-function/main.c @@ -0,0 +1,32 @@ +int non_terminating(int *a, int val) +{ + while(1) + { + a = val; + } + return a; +} + +int non_terminating_extra(int *a, int val) +{ + while(1) + { + a = val; + } + return a; +} + +void test_non_terminating() +{ + int one_val = 5; + int second_val = 6; + __CPROVER_assert(one_val == 5, "one_val==5"); + __CPROVER_assert(second_val == 6, "second_val==6"); + + non_terminating(&one_val, 10); + __CPROVER_assert(one_val == 10, "one_val==10"); + non_terminating_extra(&second_val, 12); + __CPROVER_assert(second_val == 12, "second_val==12"); + non_terminating_extra(&second_val, 13); + __CPROVER_assert(second_val == 13, "second_val==13"); +} \ No newline at end of file diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-non-terminating-function/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-non-terminating-function/test.desc new file mode 100644 index 00000000000..5983fafc9c7 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-non-terminating-function/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--function test_non_terminating --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +\[test_non_terminating.assertion\.1\] line 23 one_val==5: SUCCESS +\[test_non_terminating.assertion\.2\] line 24 second_val==6: SUCCESS +\[test_non_terminating.assertion\.3\] line 27 one_val==10: SUCCESS \(unreachable\) +\[test_non_terminating.assertion\.4\] line 29 second_val==12: SUCCESS \(unreachable\) +\[test_non_terminating.assertion\.5\] line 31 second_val==13: SUCCESS \(unreachable\) +-- diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple/main.c new file mode 100644 index 00000000000..d5f1909a364 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple/main.c @@ -0,0 +1,74 @@ +#include + +int global; + +void simple_function(void) +{ + int j = 0; +} + +void pointer_function(int *write_to, int value) +{ + *write_to = value; +} + +void modify_global_function(int new_val) +{ + global = new_val; +} + +void simple_test() +{ + int i = 0; + global = 0; + simple_function(); + __CPROVER_assert(i == 0, "i==0"); + __CPROVER_assert(global == 0, "global==0"); + + i = 1; + global = 2; + simple_function(); + __CPROVER_assert(i == 1, "i==1"); + __CPROVER_assert(global == 2, "global==2"); +} + +void pointer_test() +{ + int i = 0; + int j = 1; + pointer_function(&i, 10); + + __CPROVER_assert(j == 1, "j==1"); + __CPROVER_assert(i == 10, "i==10"); + + j = 2; + pointer_function(&i, 10); + + __CPROVER_assert(j == 2, "j==2"); + __CPROVER_assert(i == 10, "i==10"); + + j = 3; + pointer_function(&i, 11); + + __CPROVER_assert(j == 3, "j==3"); + __CPROVER_assert( + i == 11, "i==11"); // unknown since value has top for pointer_function + + j = 4; + pointer_function(&i, 478); + + __CPROVER_assert(j == 4, "j==4"); + __CPROVER_assert( + i == 11, "i==11"); // unknown since value has top for pointer_function +} + +void global_test() +{ + global = 0; + modify_global_function(42); + __CPROVER_assert(global == 42, "global==42"); + + modify_global_function(50); + __CPROVER_assert( + global == 50, "global==50"); // unknown since new_val will be top +} diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple/test.desc new file mode 100644 index 00000000000..8f66b407435 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple/test.desc @@ -0,0 +1,15 @@ +CORE +main.c +--function pointer_test --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify --three-way-merge +^EXIT=0$ +^SIGNAL=0$ +^\[pointer_test.assertion\.1\] line \d+ j==1: SUCCESS$ +^\[pointer_test.assertion\.2\] line \d+ i==10: SUCCESS$ +^\[pointer_test.assertion\.3\] line \d+ j==2: SUCCESS$ +^\[pointer_test.assertion\.4\] line \d+ i==10: SUCCESS$ +^\[pointer_test.assertion\.5\] line \d+ j==3: SUCCESS$ +^\[pointer_test.assertion\.6\] line \d+ i==11: UNKNOWN$ +^\[pointer_test.assertion\.7\] line \d+ j==4: SUCCESS$ +^\[pointer_test.assertion\.8\] line \d+ i==11: UNKNOWN$ +-- +j==2, j==3 and j==4 are the bits that specifically need three-way-merge diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_2/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_2/main.c new file mode 100644 index 00000000000..d5f1909a364 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_2/main.c @@ -0,0 +1,74 @@ +#include + +int global; + +void simple_function(void) +{ + int j = 0; +} + +void pointer_function(int *write_to, int value) +{ + *write_to = value; +} + +void modify_global_function(int new_val) +{ + global = new_val; +} + +void simple_test() +{ + int i = 0; + global = 0; + simple_function(); + __CPROVER_assert(i == 0, "i==0"); + __CPROVER_assert(global == 0, "global==0"); + + i = 1; + global = 2; + simple_function(); + __CPROVER_assert(i == 1, "i==1"); + __CPROVER_assert(global == 2, "global==2"); +} + +void pointer_test() +{ + int i = 0; + int j = 1; + pointer_function(&i, 10); + + __CPROVER_assert(j == 1, "j==1"); + __CPROVER_assert(i == 10, "i==10"); + + j = 2; + pointer_function(&i, 10); + + __CPROVER_assert(j == 2, "j==2"); + __CPROVER_assert(i == 10, "i==10"); + + j = 3; + pointer_function(&i, 11); + + __CPROVER_assert(j == 3, "j==3"); + __CPROVER_assert( + i == 11, "i==11"); // unknown since value has top for pointer_function + + j = 4; + pointer_function(&i, 478); + + __CPROVER_assert(j == 4, "j==4"); + __CPROVER_assert( + i == 11, "i==11"); // unknown since value has top for pointer_function +} + +void global_test() +{ + global = 0; + modify_global_function(42); + __CPROVER_assert(global == 42, "global==42"); + + modify_global_function(50); + __CPROVER_assert( + global == 50, "global==50"); // unknown since new_val will be top +} diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_2/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_2/test.desc new file mode 100644 index 00000000000..e9f61c5c785 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_2/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--function simple_test --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify --three-way-merge +^EXIT=0$ +^SIGNAL=0$ +^\[simple_test\.assertion\.1\] line \d+ i==0: SUCCESS$ +^\[simple_test\.assertion\.2\] line \d+ global==0: SUCCESS$ +^\[simple_test\.assertion\.3\] line \d+ i==1: SUCCESS$ +^\[simple_test\.assertion\.4\] line \d+ global==2: SUCCESS$ +-- +i==1 and global==2 specifically need three-way-merge \ No newline at end of file diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_3/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_3/main.c new file mode 100644 index 00000000000..d5f1909a364 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_3/main.c @@ -0,0 +1,74 @@ +#include + +int global; + +void simple_function(void) +{ + int j = 0; +} + +void pointer_function(int *write_to, int value) +{ + *write_to = value; +} + +void modify_global_function(int new_val) +{ + global = new_val; +} + +void simple_test() +{ + int i = 0; + global = 0; + simple_function(); + __CPROVER_assert(i == 0, "i==0"); + __CPROVER_assert(global == 0, "global==0"); + + i = 1; + global = 2; + simple_function(); + __CPROVER_assert(i == 1, "i==1"); + __CPROVER_assert(global == 2, "global==2"); +} + +void pointer_test() +{ + int i = 0; + int j = 1; + pointer_function(&i, 10); + + __CPROVER_assert(j == 1, "j==1"); + __CPROVER_assert(i == 10, "i==10"); + + j = 2; + pointer_function(&i, 10); + + __CPROVER_assert(j == 2, "j==2"); + __CPROVER_assert(i == 10, "i==10"); + + j = 3; + pointer_function(&i, 11); + + __CPROVER_assert(j == 3, "j==3"); + __CPROVER_assert( + i == 11, "i==11"); // unknown since value has top for pointer_function + + j = 4; + pointer_function(&i, 478); + + __CPROVER_assert(j == 4, "j==4"); + __CPROVER_assert( + i == 11, "i==11"); // unknown since value has top for pointer_function +} + +void global_test() +{ + global = 0; + modify_global_function(42); + __CPROVER_assert(global == 42, "global==42"); + + modify_global_function(50); + __CPROVER_assert( + global == 50, "global==50"); // unknown since new_val will be top +} diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_3/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_3/test.desc new file mode 100644 index 00000000000..ba0b1531e42 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-simple_3/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--function global_test --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[global_test\.assertion\.1\] line \d+ global==42: SUCCESS$ +^\[global_test\.assertion\.2\] line \d+ global==50: UNKNOWN$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-struct/main.c b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-struct/main.c new file mode 100644 index 00000000000..7d4bcb60cd5 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-struct/main.c @@ -0,0 +1,46 @@ +struct human +{ + int age; + float height; +}; + +struct human another_human = {18, 1.73}; + +void param_function(struct human a_human) +{ + __CPROVER_assert(a_human.age == 24, "a_human.age==24"); + __CPROVER_assert(a_human.height == 1.80, "a_human.height==1.80"); +} + +void param_function_val(struct human *a_human, int val) +{ + a_human->age = val; +} + +void pass_param() +{ + struct human human_instance; + human_instance.age = 24; + human_instance.height = 1.80f; + __CPROVER_assert(human_instance.age == 24, "human_instance.age==24"); + __CPROVER_assert( + human_instance.height == 1.80f, "human_instance.height==1.80"); + param_function_val(&human_instance, 10); + + __CPROVER_assert(human_instance.age == 10, "human_instance.age==10"); + __CPROVER_assert(human_instance.age == 24, "human_instance.age==24"); + __CPROVER_assert( + human_instance.height == 1.80f, "human_instance.height==1.80"); + + param_function_val(&human_instance, 32); + __CPROVER_assert(human_instance.age == 32, "human_instance.age==32"); + __CPROVER_assert(human_instance.age == 10, "human_instance.age==10"); + __CPROVER_assert( + human_instance.height == 1.80f, "human_instance.height==1.80"); +} + +void global_struct_test() +{ + __CPROVER_assert(another_human.age == 18, "another_human.age==18"); + __CPROVER_assert(another_human.height == 1.73f, "another_human.height==1.73"); +} \ No newline at end of file diff --git a/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-struct/test.desc b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-struct/test.desc new file mode 100644 index 00000000000..2312189b723 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-assign-aware-merge-struct/test.desc @@ -0,0 +1,14 @@ +CORE +main.c +--function pass_param --variable-sensitivity --vsd-pointers --vsd-arrays --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +^\[pass_param\.assertion\.1\] line \d+ human_instance.age==24: SUCCESS$ +^\[pass_param\.assertion\.2\] line \d+ human_instance.height==1.80: SUCCESS$ +^\[pass_param\.assertion\.3\] line \d+ human_instance.age==10: SUCCESS$ +^\[pass_param\.assertion\.4\] line \d+ human_instance.age==24: FAILURE \(if reachable\)$ +^\[pass_param\.assertion\.5\] line \d+ human_instance.height==1.80: SUCCESS$ +^\[pass_param\.assertion\.6\] line \d+ human_instance.age==32: UNKNOWN$ +^\[pass_param\.assertion\.7\] line \d+ human_instance.age==10: UNKNOWN$ +^\[pass_param\.assertion\.8\] line \d+ human_instance.height==1.80: SUCCESS$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-bit-field-constants/main.c b/regression/goto-analyzer/variable-sensitivity-bit-field-constants/main.c new file mode 100644 index 00000000000..ff8af481d95 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bit-field-constants/main.c @@ -0,0 +1,15 @@ +struct bitfield_struct +{ + unsigned char byte; + unsigned char bitfield : 1; +}; + +extern struct bitfield_struct bs; + +void main(void) +{ + bs.byte = 10; + bs.bitfield = 1; + __CPROVER_assert(bs.byte == 10, "bs.byte==10"); + __CPROVER_assert(bs.bitfield == 1, "bs.bitfield==1"); +} diff --git a/regression/goto-analyzer/variable-sensitivity-bit-field-constants/test.desc b/regression/goto-analyzer/variable-sensitivity-bit-field-constants/test.desc new file mode 100644 index 00000000000..fba07697856 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bit-field-constants/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--function main --variable-sensitivity --vsd-structs --verify +^EXIT=0$ +^SIGNAL=0$ +\[main\.assertion\.1\] line \d+ bs\.byte==10: SUCCESS +\[main\.assertion\.2\] line \d+ bs\.bitfield==1: SUCCESS +-- diff --git a/regression/goto-analyzer/variable-sensitivity-bit-field-intervals/main.c b/regression/goto-analyzer/variable-sensitivity-bit-field-intervals/main.c new file mode 100644 index 00000000000..ff8af481d95 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bit-field-intervals/main.c @@ -0,0 +1,15 @@ +struct bitfield_struct +{ + unsigned char byte; + unsigned char bitfield : 1; +}; + +extern struct bitfield_struct bs; + +void main(void) +{ + bs.byte = 10; + bs.bitfield = 1; + __CPROVER_assert(bs.byte == 10, "bs.byte==10"); + __CPROVER_assert(bs.bitfield == 1, "bs.bitfield==1"); +} diff --git a/regression/goto-analyzer/variable-sensitivity-bit-field-intervals/test.desc b/regression/goto-analyzer/variable-sensitivity-bit-field-intervals/test.desc new file mode 100644 index 00000000000..1598b5061e4 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bit-field-intervals/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--function main --variable-sensitivity --vsd-structs --vsd-intervals --verify +^EXIT=0$ +^SIGNAL=0$ +\[main\.assertion\.1\] line \d+ bs\.byte==10: SUCCESS +\[main\.assertion\.2\] line \d+ bs\.bitfield==1: SUCCESS +-- diff --git a/regression/goto-analyzer/variable-sensitivity-bug-01/main.c b/regression/goto-analyzer/variable-sensitivity-bug-01/main.c new file mode 100644 index 00000000000..54f89eed952 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bug-01/main.c @@ -0,0 +1,14 @@ +struct st +{ + int a; + int b; +}; + +struct st sts_inf; + +void func(struct st *inf); // no body + +void main(void) +{ + func(&sts_inf); // assertion failed here +} diff --git a/regression/goto-analyzer/variable-sensitivity-bug-01/test.desc b/regression/goto-analyzer/variable-sensitivity-bug-01/test.desc new file mode 100644 index 00000000000..69e3f769a07 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bug-01/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --show +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-bug-02/main.c b/regression/goto-analyzer/variable-sensitivity-bug-02/main.c new file mode 100644 index 00000000000..a768b30cd4c --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bug-02/main.c @@ -0,0 +1,34 @@ +// map.c +int map(int t_x, int *p_map) // mismatch :( +{ + int n = p_map[0]; + int *p_map_x = &p_map[1]; + int *p_map_y = p_map_x + n; // assertion failed here + int i; + + if(t_x < p_map_x[0]) + return p_map_y[0]; + + for(i = 1; i < n; i++) + if(t_x < p_map_x[i]) + { + int x0 = p_map_x[i - 1]; + int x1 = p_map_x[i]; + int y0 = p_map_y[i - 1]; + int y1 = p_map_y[i]; + return (y1 - y0) / (x1 - x0) * (t_x - x0) + y0; + } + + return p_map_y[n - 1]; +} + +/// file2.c +int map(int t_x, volatile const int *p_map); +volatile const int s_map_data[] = {2, 10, 20, 100, 110}; +extern int g_x; +int g_y; + +void main(void) +{ + g_y = map(g_x, s_map_data); +} diff --git a/regression/goto-analyzer/variable-sensitivity-bug-02/test.desc b/regression/goto-analyzer/variable-sensitivity-bug-02/test.desc new file mode 100644 index 00000000000..cefac4ef378 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bug-02/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --show +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-bug-03/main.c b/regression/goto-analyzer/variable-sensitivity-bug-03/main.c new file mode 100644 index 00000000000..220668b2d5f --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bug-03/main.c @@ -0,0 +1,7 @@ +void main(void) +{ + const int s_map_data[] = {2, 10, 20, 100, 110}; + + int *p_map_x = &s_map_data[1]; + int *p_map_y = p_map_x + 1; // assertion failed here +} diff --git a/regression/goto-analyzer/variable-sensitivity-bug-03/test.desc b/regression/goto-analyzer/variable-sensitivity-bug-03/test.desc new file mode 100644 index 00000000000..cefac4ef378 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-bug-03/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--variable-sensitivity --vsd-structs --vsd-arrays --vsd-pointers --show +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-01/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph-01/main.c new file mode 100644 index 00000000000..f310e959dfa --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-01/main.c @@ -0,0 +1,8 @@ +void func() +{ +} + +void main(void) +{ + func(); +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-01/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph-01/test.desc new file mode 100644 index 00000000000..9d9f776c343 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-01/test.desc @@ -0,0 +1,12 @@ +CORE +main.c +--show --dependence-graph-vs +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +Function: main\n.*\n.*\n.*\nControl dependencies: [0-9]+ +Function: func\n.*\n.*\n.*\nControl dependencies: [0-9]+ +Function: __CPROVER_initialize\n.*\n.*\n.*\nControl dependencies: [0-9]+ +-- +Function: __CPROVER__start\n.*\n.*\n.*\nControl dependencies: [0-9]+ +^warning: ignoring diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-02/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph-02/main.c new file mode 100644 index 00000000000..33eb267e9d1 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-02/main.c @@ -0,0 +1,11 @@ +void main(void) +{ + int a; + + if(a) + { + a = 1; + } + + a = 2; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-02/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph-02/test.desc new file mode 100644 index 00000000000..1f0332d5afc --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-02/test.desc @@ -0,0 +1,10 @@ +CORE +main.c +--show --dependence-graph-vs +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +Control dependencies: [0-9]+ \[TRUE\]\n.*\n.*\n\s+a = 1; +Control dependencies: [0-9]+ \[UNCONDITIONAL\]\n.*\n.*\n\s+a = 2; +-- +^warning: ignoring diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-merge/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph-merge/main.c new file mode 100644 index 00000000000..f8f967a2371 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-merge/main.c @@ -0,0 +1,22 @@ +void cal(void) +{ +} + +const int g_N = 5; +int x = 0; + +struct +{ + int idx; +} s_str = {0}; + +void main(void) +{ + for(int i = 0; i < g_N; i++) + { + cal(); + s_str.idx = s_str.idx + 1; + } + + __CPROVER_assert(s_str.idx > 1, "s_str.idx > 1"); +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-merge/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph-merge/test.desc new file mode 100644 index 00000000000..4106d2b61f4 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-merge/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--dependence-graph-vs --vsd-structs --vsd-arrays --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main\.assertion\.1\] line \d+ s_str.idx > 1: UNKNOWN$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/file1.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/file1.c new file mode 100644 index 00000000000..dc56693e452 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/file1.c @@ -0,0 +1,23 @@ +struct structt +{ + int a; + int b; +}; + +extern struct structt st; +extern int in; +int out1, out2, out3; + +void struct_task(void) +{ + int i; + if(in == 1) + st.a++; + + if(in == 2) + st.b++; + + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/file2.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/file2.c new file mode 100644 index 00000000000..702c037f655 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/file2.c @@ -0,0 +1,16 @@ +extern int ar[2]; +extern int in; +int out1, out2, out3; + +void array_task(void) +{ + if(in == 1) + ar[0]++; + + if(in == 2) + ar[1]++; + + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/main.c new file mode 100644 index 00000000000..39567223aaf --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/main.c @@ -0,0 +1,61 @@ +struct structt +{ + int a; + int b; +}; + +struct structt st; +int ar[2]; +int arr[3]; + +extern int in; +int out1, out2, out3; + +void struct_task(void); +void array_task(void); + +void main(void) +{ + if(in == 1) + st.a++; + + if(in == 2) + st.b++; + + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; + + if(in == 1) + ar[0]++; + + if(in == 2) + ar[1]++; + + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; + + arr[0] = 1; + arr[1] = 2; + arr[2] = 3; + + if(in > 0) + { + arr[2] = arr[1]; + } + + out1 = arr[0]; + out2 = arr[1]; + out3 = arr[2]; + + struct_task(); + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; + + array_task(); + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/test.desc new file mode 100644 index 00000000000..13ab739f402 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph-toyota/test.desc @@ -0,0 +1,37 @@ +CORE +main.c +file1.c file2.c --dependence-graph-vs --vsd-structs --vsd-arrays --show +^EXIT=0$ +^SIGNAL=0$ +Data dependencies: 61 \[st.a\] +Data dependencies: 61 \[st.b\] +Data dependencies: 1 \[st.a\], 61 \[st.a\] +Data dependencies: 4 \[st.b\], 61 \[st.b\] +Data dependencies: 1 \[st.a\], 4 \[st.b\], 61 \[st.a, st.b\] +Data dependencies: 55 \[ar\[\([^)]*\)0\]\] +Data dependencies: 55 \[ar\[\([^)]*\)1\]\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\] +Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)0\], ar\[\([^)]*\)1\]\] +Data dependencies: 19 \[arr\[\([^)]*\)1\]\] +Data dependencies: 18 \[arr\[\([^)]*\)0\]\] +Data dependencies: 19 \[arr\[\([^)]*\)1\]\] +Data dependencies: 20 \[arr\[\([^)]*\)2\]\], 22 \[arr\[\([^)]*\)2\]\] +Data dependencies: 1 \[st.a\], 61 \[st.a\], 65 \[st.a\] +Data dependencies: 4 \[st.b\], 61 \[st.b\], 68 \[st.b\] +Data dependencies: 1 \[st.a\], 4 \[st.b\], 61 \[st.a, st.b\], 65 \[st.a\], 68 \[st.b\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\], 76 \[ar\[\([^)]*\)0\]\] +Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\], 79 \[ar\[\([^)]*\)1\]\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)0\], ar\[\([^)]*\)1\]\], 76 \[ar\[\([^)]*\)0\]\], 79 \[ar\[\([^)]*\)1\]\] +Data dependencies: 1 \[st.a\], 61 \[st.a\] +Data dependencies: 4 \[st.b\], 61 \[st.b\] +Data dependencies: 1 \[st.a\], 61 \[st.a\], 65 \[st.a\] +Data dependencies: 4 \[st.b\], 61 \[st.b\], 68 \[st.b\] +Data dependencies: 1 \[st.a\], 4 \[st.b\], 61 \[st.a, st.b\], 65 \[st.a\], 68 \[st.b\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\] +Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\], 76 \[ar\[\([^)]*\)0\]\] +Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\], 79 \[ar\[\([^)]*\)1\]\] +Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)0\], ar\[\([^)]*\)1\]\], 76 \[ar\[\([^)]*\)0\]\], 79 \[ar\[\([^)]*\)1\]\] +-- +^warning: ignoring diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph/file1.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph/file1.c new file mode 100644 index 00000000000..dc56693e452 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph/file1.c @@ -0,0 +1,23 @@ +struct structt +{ + int a; + int b; +}; + +extern struct structt st; +extern int in; +int out1, out2, out3; + +void struct_task(void) +{ + int i; + if(in == 1) + st.a++; + + if(in == 2) + st.b++; + + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph/file2.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph/file2.c new file mode 100644 index 00000000000..702c037f655 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph/file2.c @@ -0,0 +1,16 @@ +extern int ar[2]; +extern int in; +int out1, out2, out3; + +void array_task(void) +{ + if(in == 1) + ar[0]++; + + if(in == 2) + ar[1]++; + + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph/main.c new file mode 100644 index 00000000000..f11e8b48266 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph/main.c @@ -0,0 +1,61 @@ +struct structt +{ + int a; + int b; +}; + +struct structt st; +int ar[2]; +int arr[3]; + +extern int in; +int out1, out2, out3; + +void struct_task(void); +void array_task(void); + +void main(void) +{ + if(in == 1) + st.a++; + + if(in == 2) + st.b++; + + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; + + if(in == 1) + ar[0]++; + + if(out1 == 2) + ar[1]++; + + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; + + arr[0] = 1; + arr[1] = 2; + arr[2] = 3; + + if(in > 0) + { + arr[2] = arr[1]; + } + + out1 = arr[0]; + out2 = arr[1]; + out3 = arr[2]; + + struct_task(); + out1 = st.a; + out2 = st.b; + out3 = st.a + st.b; + + array_task(); + out1 = ar[0]; + out2 = ar[1]; + out3 = ar[0] + ar[1]; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph/test.desc new file mode 100644 index 00000000000..61cdcb706db --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph/test.desc @@ -0,0 +1,38 @@ +CORE +main.c +file1.c file2.c --dependence-graph-vs --vsd-structs --vsd-arrays --show +^EXIT=0$ +^SIGNAL=0$ +^Data dependencies: 61 \[st.a\]$ +^Data dependencies: 61 \[st.b\]$ +^Data dependencies: 1 \[st.a\], 61 \[st.a\]$ +^Data dependencies: 4 \[st.b\], 61 \[st.b\]$ +^Data dependencies: 1 \[st.a\], 4 \[st.b\], 61 \[st.a, st.b\]$ +^Data dependencies: 55 \[ar\[\([^)]*\)0\]\]$ +^Data dependencies: 6 \[out1\]$ +^Data dependencies: 55 \[ar\[\([^)]*\)1\]\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\]$ +^Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)0\], ar\[\([^)]*\)1\]\]$ +^Data dependencies: 19 \[arr\[\([^)]*\)1\]\]$ +^Data dependencies: 18 \[arr\[\([^)]*\)0\]\]$ +^Data dependencies: 19 \[arr\[\([^)]*\)1\]\]$ +^Data dependencies: 20 \[arr\[\([^)]*\)2\]\], 22 \[arr\[\([^)]*\)2\]\]$ +^Data dependencies: 1 \[st.a\], 61 \[st.a\], 65 \[st.a\]$ +^Data dependencies: 4 \[st.b\], 61 \[st.b\], 68 \[st.b\]$ +^Data dependencies: 1 \[st.a\], 4 \[st.b\], 61 \[st.a, st.b\], 65 \[st.a\], 68 \[st.b\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\], 76 \[ar\[\([^)]*\)0\]\]$ +^Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\], 79 \[ar\[\([^)]*\)1\]\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)0\], ar\[\([^)]*\)1\]\], 76 \[ar\[\([^)]*\)0\]\], 79 \[ar\[\([^)]*\)1\]\]$ +^Data dependencies: 1 \[st.a\], 61 \[st.a\]$ +^Data dependencies: 4 \[st.b\], 61 \[st.b\]$ +^Data dependencies: 1 \[st.a\], 61 \[st.a\], 65 \[st.a\]$ +^Data dependencies: 4 \[st.b\], 61 \[st.b\], 68 \[st.b\]$ +^Data dependencies: 1 \[st.a\], 4 \[st.b\], 61 \[st.a, st.b\], 65 \[st.a\], 68 \[st.b\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\]$ +^Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 55 \[ar\[\([^)]*\)0\]\], 76 \[ar\[\([^)]*\)0\]\]$ +^Data dependencies: 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)1\]\], 79 \[ar\[\([^)]*\)1\]\]$ +^Data dependencies: 10 \[ar\[\([^)]*\)0\]\], 13 \[ar\[\([^)]*\)1\]\], 55 \[ar\[\([^)]*\)0\], ar\[\([^)]*\)1\]\], 76 \[ar\[\([^)]*\)0\]\], 79 \[ar\[\([^)]*\)1\]\]$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph15/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph15/main.c new file mode 100644 index 00000000000..ffb8590fb31 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph15/main.c @@ -0,0 +1,23 @@ +int f2(int, const int *); +extern const int g_map[]; + +int g_out1; +int g_out2; + +extern int g_in; + +void main(void) +{ + int t1; + int t2; + + t1 = g_in; + t2 = f2(t1, g_map); + + if(t2 >= 0) + g_out1 = t2; + else + g_out1 = 0; + + g_out2 = t1; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph15/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph15/test.desc new file mode 100644 index 00000000000..6217396b36b --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph15/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--show --dependence-graph-vs +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +Control dependencies: [0-9]+ \[UNCONDITIONAL\]\n(.*\n){2,3}.*g_out2 = t1; +Control dependencies: [0-9]+ \[TRUE\]\n(.*\n){2,3}.*g_out1 = t2; +Control dependencies: [0-9]+ \[FALSE\]\n(.*\n){2,3}.*g_out1 = 0; +-- +^warning: ignoring diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph16/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph16/main.c new file mode 100644 index 00000000000..39542ee75ea --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph16/main.c @@ -0,0 +1,16 @@ +int f2(int, const int *); +extern const int g_map[]; + +int g_out1; +int g_out2; + +extern int g_in; + +void main(void) +{ + int t1; + int t2; + + t1 = g_in; + t2 = f2(t1, g_map); +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph16/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph16/test.desc new file mode 100644 index 00000000000..d9c98288288 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph16/test.desc @@ -0,0 +1,10 @@ +CORE +main.c +--dependence-graph-vs --show +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +// Assignment from the result of a function call to a function with no body +// should have a data dependency on the function +^Data dependencies: 3 \[f2\]\n(.*\n).*\/\/ 4 file .*main.c line 15 function main\n.*t2 = NONDET\([^)]*\); +-- diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph17/main.c b/regression/goto-analyzer/variable-sensitivity-dependence-graph17/main.c new file mode 100644 index 00000000000..7ef25c7f352 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph17/main.c @@ -0,0 +1,23 @@ +extern int g_in; + +int g_a[3]; + +int g_out1; + +void main(void) +{ + int i; + + g_a[0] = 1; + g_a[1] = 2; + g_a[2] = 3; + + i = 0; + if(g_in > 0) + { + i = 1; + g_a[2] = g_a[1]; + } + + g_out1 = g_a[i]; +} diff --git a/regression/goto-analyzer/variable-sensitivity-dependence-graph17/test.desc b/regression/goto-analyzer/variable-sensitivity-dependence-graph17/test.desc new file mode 100644 index 00000000000..a509f3ec3fe --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-dependence-graph17/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--dependence-graph-vs --vsd-arrays --show +activate-multi-line-match +^EXIT=0$ +^SIGNAL=0$ +\*\*\*\* 9 file .*main\.c line 22 function main\nControl dependencies: 37 \[UNCONDITIONAL\]\nData dependencies: 1 \[g_a\[\([^)]*\)i\]\], 2 \[g_a\[\([^)]*\)i\]\], 3 \[g_a\[\([^)]*\)i\]\], 7 \[g_a\[\([^)]*\)i\]\] +-- diff --git a/regression/goto-analyzer/variable-sensitivity-floating-point-simplification/main.c b/regression/goto-analyzer/variable-sensitivity-floating-point-simplification/main.c new file mode 100644 index 00000000000..60fa538c805 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-floating-point-simplification/main.c @@ -0,0 +1,41 @@ +#include + +int main(void) +{ + const float one = 1.0f; + // Check that rounding mode affects behavior + + // default rounding mode towards nearest + float big_0_1 = one / 10.0f; + __CPROVER_rounding_mode = 1; // round to -∞ + float small_0_1 = one / 10.0f; + assert(small_0_1 < big_0_1); + + // Check that exact operations still work with unknown rounding mode + int some_condition; + if(some_condition) + { + __CPROVER_rounding_mode = 3; + } + // rounding mode is TOP now + + // regardless of rounding mode, + // 1/10 is definitely smaller than 0.2 + assert(one / 10.0f < 0.2f); + + // This is unknown because + // we don't know the value of one_tenth_ish + // (could be slightly less or slightly more than 0.1) + // This is contrast to above, where we didn't need + // to know the exact value of one/10.0f, just + // that it is less than 0.2f + float one_tenth_ish = one / 10.0f; + assert(one_tenth_ish < 0.2f); + + // regardless of rounding mode, + // 10/5 is still 2 + float five = 5.0f; + assert(10.0f / five == 2.0f); + + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-floating-point-simplification/test.desc b/regression/goto-analyzer/variable-sensitivity-floating-point-simplification/test.desc new file mode 100644 index 00000000000..a6456a135d5 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-floating-point-simplification/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --verify +^EXIT=0$ +^SIGNAL=0$ +^\[main.assertion\.1\] line 12 assertion small_0_1 < big_0_1: SUCCESS +^\[main.assertion\.2\] line 24 assertion one / 10.0f < 0.2f: SUCCESS +^\[main.assertion\.3\] line 33 assertion one_tenth_ish < 0.2f: UNKNOWN +^\[main.assertion\.4\] line 38 assertion 10.0f / five == 2.0f: SUCCESS diff --git a/regression/goto-analyzer/variable-sensitivity-function-call/main.c b/regression/goto-analyzer/variable-sensitivity-function-call/main.c new file mode 100644 index 00000000000..604487528f2 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-function-call/main.c @@ -0,0 +1,20 @@ +int f(const int *y); + +extern const int g_map[]; + +int g_out1; +int g_out2; + +extern int g_in; +// int g_in; + +void main(void) +{ + int t1; + int t2; + + t1 = 5; + t2 = f(g_map); + + __CPROVER_assert(t1 == 5, "t1 == 5 after calling function with missing body"); +} diff --git a/regression/goto-analyzer/variable-sensitivity-function-call/test.desc b/regression/goto-analyzer/variable-sensitivity-function-call/test.desc new file mode 100644 index 00000000000..57b44330204 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-function-call/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--variable-sensitivity --verify +^EXIT=0$ +^SIGNAL=0$ +\[main\.assertion\.1\] line 19 t1 == 5 after calling function with missing body: SUCCESS +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-arith-01/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-01/main.c new file mode 100644 index 00000000000..37c9c1b362f --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-01/main.c @@ -0,0 +1,14 @@ + +int main(void) +{ + int x; + int y; + int z; + + x = 1; + y = 2; + + z = x + y; + + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-arith-01/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-01/test.desc new file mode 100644 index 00000000000..64eeff47b67 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-01/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --show +^EXIT=0$ +^SIGNAL=0$ +main::1::z \(\) -> \[3, 3\] @ \[5\] +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-arith-02/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-02/main.c new file mode 100644 index 00000000000..c379294b826 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-02/main.c @@ -0,0 +1,20 @@ + +int main(void) +{ + int x; + int y; + int z; + int nondet; + + x = 1; + y = 2; + + if(nondet) + { + y = 3; + } + + z = x + y; + + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-arith-02/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-02/test.desc new file mode 100644 index 00000000000..0aa44fd1a9d --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-02/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --show +^EXIT=0$ +^SIGNAL=0$ +main::1::z \(\) -> \[3, 4\] @ \[9\] +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-arith-03/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-03/main.c new file mode 100644 index 00000000000..28d6464e1a3 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-03/main.c @@ -0,0 +1,16 @@ + +int main(void) +{ + int x; + int y; + int z; + int a; + + x = 1; + y = 2; + z = 3; + + a = x + y + z; + + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-arith-03/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-03/test.desc new file mode 100644 index 00000000000..44ca7caa05e --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-arith-03/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --show +^EXIT=0$ +^SIGNAL=0$ +main::1::a \(\) -> \[6, 6\] @ \[7\] +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-array-constant-access/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-array-constant-access/main.c new file mode 100644 index 00000000000..41aa58a0e10 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-array-constant-access/main.c @@ -0,0 +1,8 @@ +int main(void) +{ + int arr[] = {1, 2, 3}; + int second_value = arr[1]; + arr[1] = 10; + int second_value_after_write = arr[1]; + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-array-constant-access/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-array-constant-access/test.desc new file mode 100644 index 00000000000..21de235a5b4 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-array-constant-access/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--show --variable-sensitivity --vsd-intervals --vsd-arrays +^EXIT=0$ +^SIGNAL=0$ +main::1::second_value \(\) -> \[2, 2\] @ \[3\] +main::1::second_value_after_write \(\) -> \[A, A\] @ \[6\] diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-array-interval-access/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-array-interval-access/main.c new file mode 100644 index 00000000000..12756805712 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-array-interval-access/main.c @@ -0,0 +1,33 @@ +int main(void) +{ + int arr[] = {1, 2, 3}; + int ix; + if(ix) + { + ix = 0; + } + else + { + ix = 2; + } + // ix is between 0 and 2 + // so this is between 1 and 3 + int arr_at_ix = arr[ix]; + int write_ix; + if(write_ix) + { + write_ix = 0; + } + else + { + write_ix = 1; + } + arr[write_ix] = 4; + int arr_0_after_write = arr[0]; + int arr_1_after_write = arr[1]; + // We can't write to arr[2] + // because write_ix is between 0 and 1 + // so this should be unchanged + int arr_2_after_write = arr[2]; + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-array-interval-access/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-array-interval-access/test.desc new file mode 100644 index 00000000000..75c987273d7 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-array-interval-access/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--show --variable-sensitivity --vsd-intervals --vsd-arrays +^EXIT=0$ +^SIGNAL=0$ +main::1::arr_at_ix \(\) -> \[1, 3\] @ \[9\] +main::1::arr_0_after_write \(\) -> \[1, 4\] @ \[18\] +main::1::arr_1_after_write \(\) -> \[2, 4\] @ \[20\] +main::1::arr_2_after_write \(\) -> \[3, 3\] @ \[22\] diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-complex-structures/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-complex-structures/main.c new file mode 100644 index 00000000000..ab08a8125a1 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-complex-structures/main.c @@ -0,0 +1,42 @@ +#include +#include + +struct Vec2 +{ + int x; + int y; +}; + +void MakeZero(struct Vec2 *vec) +{ + vec->x = 0; + vec->y = 0; +} + +void MakeOne(struct Vec2 *vec) +{ + vec->x = 1; + vec->y = 1; +} + +int main(void) +{ + struct Vec2 vec = {-10, 10}; + struct Vec2 vecMinusTenAndTen = vec; + int nondet_condition; + if(nondet_condition) + { + MakeZero(&vec); + struct Vec2 vecZero = vec; + } + else + { + MakeOne(&vec); + struct Vec2 vecOne = vec; + } + struct Vec2 vecZeroOrOne = vec; + vec.x = 13; + vec.y = 42; + struct Vec2 vecThirteenAndFourtyTwo = vec; + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-complex-structures/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-complex-structures/test.desc new file mode 100644 index 00000000000..3d95482d9fe --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-complex-structures/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --vsd-structs --vsd-pointers --show +^EXIT=0$ +^SIGNAL=0$ +main::1::vecMinusTenAndTen \(\) -> \{.x=\[FFFFFFF6, FFFFFFF6\] \@ \[3\], .y=\[A, A\] \@ \[3\]\} @ \[3\] +main::1::1::vecZero \(\) -> \{.x=\[0, 0\] \@ \[8\], .y=\[0, 0\] \@ \[8\]\} \@ \[8\] +main::1::2::vecOne \(\) -> \{.x=\[1, 1\] \@ \[13\], .y=\[1, 1\] \@ \[13\]\} \@ \[13\] +main::1::vecZeroOrOne \(\) -> \{.x=\[0, 1\] \@ \[17\], .y=\[0, 1\] \@ \[17\]\} \@ \[17\] +main::1::vecThirteenAndFourtyTwo \(\) -> \{.x=\[D, D\] \@ \[21\], .y=\[2A, 2A\] \@ \[21\]\} \@ \[21\] +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-float/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-float/main.c new file mode 100644 index 00000000000..b5e23018ece --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-float/main.c @@ -0,0 +1,22 @@ +#include + +int main(void) +{ + float x = 0.0f; + float zero = x; + int nondet_condition; + if(nondet_condition) + { + x = 1.0f; + float one = x; + } + else + { + x = -1.0f; + float minus_one = x; + } + float between_minus_one_and_one = x; + x = 13.0f; + float thirteen = x; + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-float/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-float/test.desc new file mode 100644 index 00000000000..42e4bdf0dda --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-float/test.desc @@ -0,0 +1,11 @@ +KNOWNBUG +main.c +--variable-sensitivity --vsd-intervals --show +main::1::zero \(\) -> \[00000000000000000000000000000000, 00000000000000000000000000000000\] +main::1::1::one \(\) -> \[00111111100000000000000000000000, 00111111100000000000000000000000\] +main::1::2::minus_one \(\) -> \[10111111100000000000000000000000, 10111111100000000000000000000000\] +main::1::between_minus_one_and_one \(\) -> \[10111111100000000000000000000000, 00111111100000000000000000000000\] +main::1::thirteen \(\) -> \[01000001010100000000000000000000, 01000001010100000000000000000000\] +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-logical-01/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-logical-01/main.c new file mode 100644 index 00000000000..cdaf3191c4e --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-logical-01/main.c @@ -0,0 +1,22 @@ +#include + +extern int x; + +int main(void) +{ + bool b1; + bool b2; + + b1 = true; + b2 = !b1; + + bool b3; + if(x) + b3 = true; + else + b3 = false; + + int i = b3 ? 10 : 20; + + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-logical-01/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-logical-01/test.desc new file mode 100644 index 00000000000..cb215c854f6 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-logical-01/test.desc @@ -0,0 +1,9 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --show +^EXIT=0$ +^SIGNAL=0$ +main::1::b1 \(\) -> \[1, 1\] @ \[2\] +main::1::b2 \(\) -> \[0, 0\] @ \[3\] +main::1::i \(\) -> \[A\, 14\] @ \[11\] +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-mixed-float-bool/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-mixed-float-bool/main.c new file mode 100644 index 00000000000..325ceca964f --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-mixed-float-bool/main.c @@ -0,0 +1,18 @@ +void main(int argc, char *argv[]) +{ + float x = 2.5; + float y = 1.5; + int z = 0; + + if(x > y) + z = 1; + + __CPROVER_assert(z == 1, "x > y, z == 1"); + + y = 2.6; + + if(x > y) + z = 3; + + __CPROVER_assert(z == 1, "x < y, z == 1"); +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-mixed-float-bool/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-mixed-float-bool/test.desc new file mode 100644 index 00000000000..ba62f266d57 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-mixed-float-bool/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --verify +\[main\.assertion\.1\] line 10 x > y, z == 1: SUCCESS +\[main\.assertion\.2\] line 17 x < y, z == 1: SUCCESS +^EXIT=0$ +^SIGNAL=0$ +-- diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-multiplication/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values-multiplication/main.c new file mode 100644 index 00000000000..3f2ed350f3e --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-multiplication/main.c @@ -0,0 +1,7 @@ +extern int g_in; + +void main(void) +{ + int r = 4 * g_in; + int s = 0 * r; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values-multiplication/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values-multiplication/test.desc new file mode 100644 index 00000000000..607e9eea619 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values-multiplication/test.desc @@ -0,0 +1,7 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --show +^EXIT=0$ +^SIGNAL=0$ +main::1::r \(\) -> TOP @ \[1\] +main::1::s \(\) -> \[0, 0\] @ \[3\] diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values/main.c b/regression/goto-analyzer/variable-sensitivity-interval-values/main.c new file mode 100644 index 00000000000..6f6d0c71cdd --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values/main.c @@ -0,0 +1,22 @@ +#include + +int main(void) +{ + int x = 0; + int zero = x; + int nondet_condition; + if(nondet_condition) + { + x = 1; + int one = x; + } + else + { + x = -1; + int minus_one = x; + } + int between_minus_one_and_one = x; + x = 13; + int thirteen = x; + return 0; +} diff --git a/regression/goto-analyzer/variable-sensitivity-interval-values/test.desc b/regression/goto-analyzer/variable-sensitivity-interval-values/test.desc new file mode 100644 index 00000000000..7c089db88b4 --- /dev/null +++ b/regression/goto-analyzer/variable-sensitivity-interval-values/test.desc @@ -0,0 +1,11 @@ +CORE +main.c +--variable-sensitivity --vsd-intervals --show +^EXIT=0$ +^SIGNAL=0$ +main::1::zero \(\) -> \[0, 0\] @ \[3\] +main::1::1::one \(\) -> \[1, 1\] @ \[8\] +main::1::2::minus_one \(\) -> \[FFFFFFFF, FFFFFFFF\] @ \[13\] +main::1::between_minus_one_and_one \(\) -> \[FFFFFFFF, 1\] @ \[17\] +main::1::thirteen \(\) -> \[D, D\] @ \[20\] +-- diff --git a/regression/goto-analyzer/write-stack-types/test.desc b/regression/goto-analyzer/write-stack-types/test.desc new file mode 100644 index 00000000000..647f6609d04 --- /dev/null +++ b/regression/goto-analyzer/write-stack-types/test.desc @@ -0,0 +1,8 @@ +CORE +write-stack-address-of.c +--variable-sensitivity --vsd-structs --vsd-pointers --verify +^EXIT=0$ +^SIGNAL=0$ +^\[func1\.assertion\.1\] line \d+ func1.a == 0: UNKNOWN$ +-- +^warning: ignoring diff --git a/regression/goto-analyzer/write-stack-types/write-stack-address-of.c b/regression/goto-analyzer/write-stack-types/write-stack-address-of.c new file mode 100644 index 00000000000..1d7331b84af --- /dev/null +++ b/regression/goto-analyzer/write-stack-types/write-stack-address-of.c @@ -0,0 +1,36 @@ +typedef unsigned short uint16; + +struct st +{ + uint16 data1; +} str1; + +extern struct +{ + struct st data2; +} g_sts; + +void func1(uint16 a) +{ + // Expect the assert to be 'Unknown' because we would expect 'a' to be TOP + // here because p->data1 passed in as argument should be TOP. + __CPROVER_assert(a == 0, "func1.a == 0"); +} + +void func2(struct st *p) +{ + func1(p->data1); +} + +void main(void) +{ + const int s_map_data[] = {2, 10, 20, 100, 110}; + + int *p_map_x = &s_map_data[1]; + int *p_map_y = p_map_x + 1; + + // Tests that the write_stack handles &smap_data[1] + __CPROVER_assert(*p_map_y == 20, "*main.p_map_y == 20"); + + func2(&g_sts.data2); +} diff --git a/src/analyses/Makefile b/src/analyses/Makefile index 510ab7646cd..27c490303d7 100644 --- a/src/analyses/Makefile +++ b/src/analyses/Makefile @@ -33,6 +33,33 @@ SRC = ai.cpp \ static_analysis.cpp \ uncaught_exceptions_analysis.cpp \ uninitialized_domain.cpp \ + variable-sensitivity/abstract_object.cpp \ + variable-sensitivity/abstract_enviroment.cpp \ + variable-sensitivity/abstract_value.cpp \ + variable-sensitivity/array_abstract_object.cpp \ + variable-sensitivity/constant_abstract_value.cpp \ + variable-sensitivity/constant_pointer_abstract_object.cpp \ + variable-sensitivity/context_abstract_object.cpp \ + variable-sensitivity/write_location_context.cpp \ + variable-sensitivity/pointer_abstract_object.cpp \ + variable-sensitivity/struct_abstract_object.cpp \ + variable-sensitivity/variable_sensitivity_domain.cpp \ + variable-sensitivity/variable_sensitivity_object_factory.cpp \ + variable-sensitivity/full_struct_abstract_object.cpp \ + variable-sensitivity/constant_array_abstract_object.cpp \ + variable-sensitivity/union_abstract_object.cpp \ + variable-sensitivity/write_stack.cpp \ + variable-sensitivity/write_stack_entry.cpp \ + variable-sensitivity/data_dependency_context.cpp \ + variable-sensitivity/value_set_abstract_object.cpp \ + variable-sensitivity/variable_sensitivity_dependence_graph.cpp \ + variable-sensitivity/interval_abstract_value.cpp \ + variable-sensitivity/interval_array_abstract_object.cpp \ + variable-sensitivity/value_set_abstract_object.cpp \ + variable-sensitivity/value_set_abstract_value.cpp \ + variable-sensitivity/value_set_pointer_abstract_object.cpp \ + variable-sensitivity/value_set_array_abstract_object.cpp \ + variable-sensitivity/three_way_merge_abstract_interpreter.cpp \ # Empty last line INCLUDES= -I .. diff --git a/src/analyses/ai_storage.h b/src/analyses/ai_storage.h index a36b1eb146d..8880fac7e10 100644 --- a/src/analyses/ai_storage.h +++ b/src/analyses/ai_storage.h @@ -144,6 +144,7 @@ class trace_map_storaget : public ai_storage_baset // A couple of older domains make direct use of the state map class invariant_propagationt; class dependence_grapht; +class variable_sensitivity_dependence_grapht; /// The most conventional storage; one domain per location class location_sensitive_storaget : public trace_map_storaget @@ -161,6 +162,7 @@ class location_sensitive_storaget : public trace_map_storaget // Support some older domains that explicitly iterate across the state map friend invariant_propagationt; friend dependence_grapht; + friend variable_sensitivity_dependence_grapht; // Based on dependence_grapht state_mapt &internal(void) { return state_map; diff --git a/src/analyses/variable-sensitivity/abstract_enviroment.cpp b/src/analyses/variable-sensitivity/abstract_enviroment.cpp new file mode 100644 index 00000000000..bf3cba22e6c --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_enviroment.cpp @@ -0,0 +1,533 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abstract_enviroment.h" +#include "abstract_object_statistics.h" + +#ifdef DEBUG +# include +#endif + +abstract_object_pointert +abstract_environmentt::eval(const exprt &expr, const namespacet &ns) const +{ + if(bottom) + return abstract_object_factory(expr.type(), ns, false, true); + + // first try to canonicalise, including constant folding + const exprt &simplified_expr = simplify_expr(expr, ns); + + const irep_idt simplified_id = simplified_expr.id(); + if(simplified_id == ID_symbol) + { + const symbol_exprt &symbol(to_symbol_expr(simplified_expr)); + const auto &symbol_entry = map.find(symbol.get_identifier()); + if(!symbol_entry.has_value()) + { + return abstract_object_factory(simplified_expr.type(), ns, true); + } + else + { + abstract_object_pointert found_symbol_value = symbol_entry.value(); + return found_symbol_value; + } + } + else if(simplified_id == ID_member) + { + member_exprt member_expr(to_member_expr(simplified_expr)); + + const exprt &parent = member_expr.compound(); + + abstract_object_pointert parent_abstract_object = eval(parent, ns); + return parent_abstract_object->read(*this, member_expr, ns); + } + else if(simplified_id == ID_address_of) + { + abstract_object_pointert pointer_object = + abstract_object_factory(simplified_expr.type(), simplified_expr, ns); + + // Store the abstract object in the pointer + return pointer_object; + } + else if(simplified_id == ID_dereference) + { + dereference_exprt dereference(to_dereference_expr(simplified_expr)); + abstract_object_pointert pointer_abstract_object = + eval(dereference.pointer(), ns); + + return pointer_abstract_object->read(*this, nil_exprt(), ns); + } + else if(simplified_id == ID_index) + { + index_exprt index_expr(to_index_expr(simplified_expr)); + abstract_object_pointert array_abstract_object = + eval(index_expr.array(), ns); + + return array_abstract_object->read(*this, index_expr, ns); + } + else if(simplified_id == ID_array) + { + return abstract_object_factory(simplified_expr.type(), simplified_expr, ns); + } + else if(simplified_id == ID_struct) + { + return abstract_object_factory(simplified_expr.type(), simplified_expr, ns); + } + else if(simplified_id == ID_constant) + { + return abstract_object_factory( + simplified_expr.type(), to_constant_expr(simplified_expr), ns); + } + else + { + // No special handling required by the abstract environment + // delegate to the abstract object + if(simplified_expr.operands().size() > 0) + { + return eval_expression(simplified_expr, ns); + } + else + { + // It is important that this is top as the abstract object may not know + // how to handle the expression + return abstract_object_factory(simplified_expr.type(), ns, true); + } + } +} + +bool abstract_environmentt::assign( + const exprt &expr, + const abstract_object_pointert value, + const namespacet &ns) +{ + PRECONDITION(value); + + if(value->is_bottom()) + { + bool bottom_at_start = this->is_bottom(); + this->make_bottom(); + return !bottom_at_start; + } + + abstract_object_pointert lhs_value = nullptr; + // Build a stack of index, member and dereference accesses which + // we will work through the relevant abstract objects + exprt s = expr; + std::stack stactions; // I'm not a continuation, honest guv' + while(s.id() != ID_symbol) + { + if(s.id() == ID_index) + { + stactions.push(s); + s = to_index_expr(s).array(); + } + else if(s.id() == ID_member) + { + stactions.push(s); + s = to_member_expr(s).struct_op(); + } + else if(s.id() == ID_dereference) + { + stactions.push(s); + s = to_dereference_expr(s).pointer(); + } + else + { + lhs_value = eval(s, ns); + break; + } + } + + if(!lhs_value) + { + INVARIANT(s.id() == ID_symbol, "Have a symbol or a stack"); + const symbol_exprt &symbol_expr(to_symbol_expr(s)); + if(!map.has_key(symbol_expr.get_identifier())) + { + lhs_value = abstract_object_factory(symbol_expr.type(), ns, true, false); + } + else + { + lhs_value = map.find(symbol_expr.get_identifier()).value(); + } + } + + abstract_object_pointert final_value; + + // This is the root abstract object that is in the map of abstract objects + // It might not have the same type as value if the above stack isn't empty + + if(!stactions.empty()) + { + // The symbol is not in the map - it is therefore top + final_value = write(lhs_value, value, stactions, ns, false); + } + else + { + // If we don't have a symbol on the LHS, then we must have some expression + // that we can write to (i.e. a pointer, an array, a struct) This appears + // to be none of that. + if(s.id() != ID_symbol) + { + throw "invalid l-value"; + } + // We can assign the AO directly to the symbol + final_value = value; + } + + const typet &lhs_type = ns.follow(lhs_value->type()); + const typet &rhs_type = ns.follow(final_value->type()); + + // Write the value for the root symbol back into the map + INVARIANT( + lhs_type == rhs_type, + "Assignment types must match" + "\n" + "lhs_type :" + + lhs_type.pretty() + + "\n" + "rhs_type :" + + rhs_type.pretty()); + // If LHS was directly the symbol + if(s.id() == ID_symbol) + { + symbol_exprt symbol_expr = to_symbol_expr(s); + + if(final_value != lhs_value) + { + CHECK_RETURN(!symbol_expr.get_identifier().empty()); + map.insert_or_replace(symbol_expr.get_identifier(), final_value); + } + } + return true; +} + +abstract_object_pointert abstract_environmentt::write( + abstract_object_pointert lhs, + abstract_object_pointert rhs, + std::stack remaining_stack, + const namespacet &ns, + bool merge_write) +{ + PRECONDITION(!remaining_stack.empty()); + const exprt &next_expr = remaining_stack.top(); + remaining_stack.pop(); + + const irep_idt &stack_head_id = next_expr.id(); + + // Each handler takes the abstract object referenced, copies it, + // writes according to the type of expression (e.g. for ID_member) + // we would (should!) have an abstract_struct_objectt which has a + // write_member which will attempt to update the abstract object for the + // relevant member. This modified abstract object is returned and this + // is inserted back into the map + if(stack_head_id == ID_index) + { + return lhs->write( + *this, ns, remaining_stack, to_index_expr(next_expr), rhs, merge_write); + } + else if(stack_head_id == ID_member) + { + return lhs->write( + *this, ns, remaining_stack, to_member_expr(next_expr), rhs, merge_write); + } + else if(stack_head_id == ID_dereference) + { + return lhs->write( + *this, ns, remaining_stack, nil_exprt(), rhs, merge_write); + } + else + { + UNREACHABLE; + return nullptr; + } +} + +bool abstract_environmentt::assume(const exprt &expr, const namespacet &ns) +{ + // We should only attempt to assume Boolean things + // This should be enforced by the well-structured-ness of the + // goto-program and the way assume is used. + + PRECONDITION(expr.type().id() == ID_bool); + + // Evaluate the expression + abstract_object_pointert res = eval(expr, ns); + + exprt possibly_constant = res->to_constant(); + + if(possibly_constant.id() != ID_nil) // I.E. actually a value + { + // Should be of the right type + INVARIANT( + possibly_constant.type().id() == ID_bool, "simplication preserves type"); + + if(possibly_constant.is_false()) + { + bool currently_bottom = is_bottom(); + make_bottom(); + return !currently_bottom; + } + } + + /* TODO : full implementation here + * Note that this is *very* syntax dependent so some normalisation would help + * 1. split up conjuncts, handle each part separately + * 2. check how many variables the term contains + * 0 = this should have been simplified away + * 2+ = ignore as this is a non-relational domain + * 1 = extract the expression for the variable, + * care must be taken for things like a[i] + * which can be used if i can be resolved to a constant + * 3. use abstract_object_factory to build an abstract_objectt + * of the correct type (requires a little extension) + * This allows constant domains to handle x==23, + * intervals to handle x < 4, etc. + * 4. eval the current value of the variable + * 5. compute the meet (not merge!) of the two abstract_objectt's + * 6. assign the new value back to the environment. + */ + + return false; +} + +abstract_object_pointert abstract_environmentt::abstract_object_factory( + const typet &type, + const namespacet &ns, + bool top, + bool bottom) const +{ + exprt empty_constant_expr = nil_exprt(); + return abstract_object_factory( + type, top, bottom, empty_constant_expr, *this, ns); +} + +abstract_object_pointert abstract_environmentt::abstract_object_factory( + const typet &type, + const exprt &e, + const namespacet &ns) const +{ + return abstract_object_factory(type, false, false, e, *this, ns); +} + +abstract_object_pointert abstract_environmentt::abstract_object_factory( + const typet &type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + return variable_sensitivity_object_factoryt::instance().get_abstract_object( + type, top, bottom, e, environment, ns); +} + +bool abstract_environmentt::merge(const abstract_environmentt &env) +{ + // for each entry in the incoming environment we need to either add it + // if it is new, or merge with the existing key if it is not present + + if(bottom) + { + *this = env; + return !env.bottom; + } + else if(env.bottom) + { + return false; + } + else + { + // For each element in the intersection of map and env.map merge + // If the result of the merge is top, remove from the map + bool modified = false; + decltype(env.map)::delta_viewt delta_view; + env.map.get_delta_view(map, delta_view); + for(const auto &entry : delta_view) + { + bool object_modified = false; + abstract_object_pointert new_object = abstract_objectt::merge( + entry.get_other_map_value(), entry.m, object_modified); + modified |= object_modified; + map.replace(entry.k, new_object); + } + + return modified; + } +} + +void abstract_environmentt::havoc(const std::string &havoc_string) +{ + // TODO(tkiley): error reporting + make_top(); +} + +void abstract_environmentt::make_top() +{ + // since we assume anything is not in the map is top this is sufficient + map.clear(); + bottom = false; +} + +void abstract_environmentt::make_bottom() +{ + map.clear(); + bottom = true; +} + +bool abstract_environmentt::is_bottom() const +{ + return map.empty() && bottom; +} + +bool abstract_environmentt::is_top() const +{ + return map.empty() && !bottom; +} + +void abstract_environmentt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + out << "{\n"; + + decltype(map)::viewt view; + map.get_view(view); + for(const auto &entry : view) + { + out << entry.first << " () -> "; + entry.second->output(out, ai, ns); + out << "\n"; + } + out << "}\n"; +} + +bool abstract_environmentt::verify() const +{ + decltype(map)::viewt view; + map.get_view(view); + for(const auto &entry : view) + { + if(entry.second == nullptr) + { + return false; + } + } + return true; +} + +abstract_object_pointert abstract_environmentt::eval_expression( + const exprt &e, + const namespacet &ns) const +{ + // We create a temporary top abstract object (according to the + // type of the expression), and call expression transform on it. + // The value of the temporary abstract object is ignored, its + // purpose is just to dispatch the expression transform call to + // a concrete subtype of abstract_objectt. + abstract_object_pointert eval_obj = + abstract_object_factory(e.type(), ns, true); + + std::vector operands; + + for(const auto &op : e.operands()) + { + operands.push_back(eval(op, ns)); + } + + return eval_obj->expression_transform(e, operands, *this, ns); +} + +void abstract_environmentt::erase(const symbol_exprt &expr) +{ + map.erase_if_exists(expr.get_identifier()); +} + +std::vector +abstract_environmentt::modified_symbols( + const abstract_environmentt &first, + const abstract_environmentt &second) +{ + // Find all symbols who have different write locations in each map + std::vector symbols_diff; + decltype(first.map)::viewt view; + first.map.get_view(view); + for(const auto &entry : view) + { + const auto &second_entry = second.map.find(entry.first); + if(second_entry.has_value()) + { + if(second_entry.value().get()->has_been_modified(entry.second)) + { + CHECK_RETURN(!entry.first.empty()); + symbols_diff.push_back(entry.first); + } + } + } + + // Add any symbols that are only in the second map + for(const auto &entry : second.map.get_view()) + { + const auto &second_entry = first.map.find(entry.first); + if(!second_entry.has_value()) + { + CHECK_RETURN(!entry.first.empty()); + symbols_diff.push_back(entry.first); + } + } + return symbols_diff; +} + +static std::size_t count_globals(const namespacet &ns) +{ + auto const &symtab = ns.get_symbol_table(); + auto val = std::count_if( + symtab.begin(), + symtab.end(), + [](const symbol_tablet::const_iteratort::value_type &sym) { + return sym.second.is_lvalue && sym.second.is_static_lifetime; + }); + return val; +} + +abstract_object_statisticst +abstract_environmentt::gather_statistics(const namespacet &ns) const +{ + abstract_object_statisticst statistics = {}; + statistics.number_of_globals = count_globals(ns); + decltype(map)::viewt view; + map.get_view(view); + abstract_object_visitedt visited; + for(auto const &object : view) + { + if(visited.find(object.second) == visited.end()) + { + object.second->get_statistics(statistics, visited, *this, ns); + } + } + return statistics; +} + +abstract_environmentt::abstract_environmentt() : bottom(true) +{ +} diff --git a/src/analyses/variable-sensitivity/abstract_enviroment.h b/src/analyses/variable-sensitivity/abstract_enviroment.h new file mode 100644 index 00000000000..2b3faf4fce3 --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_enviroment.h @@ -0,0 +1,253 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// An abstract version of a program environment. Each variable has +/// an abstract object rather than a value. If these are top then +/// they are not explicitly stored so that the memory used is +/// proportional to what is known rather than just the number of +/// variables. +/// Note the use of sharing_mapt is critical for scalability. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_ENVIROMENT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_ENVIROMENT_H + +#include +#include +#include +#include +#include + +#include "abstract_object_statistics.h" +#include +#include +#include + +class abstract_environmentt +{ +public: + using map_keyt = irep_idt; + abstract_environmentt(); + /// These three are really the heart of the method + + /// Evaluate the value of an expression relative to the current domain + /// + /// \param expr: the expression to evaluate + /// \param ns: the current namespace + /// + /// \return The abstract_object representing the value of the expression + virtual abstract_object_pointert + eval(const exprt &expr, const namespacet &ns) const; + + /// Assign a value to an expression + /// + /// \param expr: the expression to assign to + /// \param value: the value to assign to the expression + /// \param ns: the namespace + /// + /// \return A boolean, true if the assignment has changed the domain. + /// + /// Assign is in principe simple, it updates the map with the new + /// abstract object. The challenge is how to handle write to compound + /// objects, for example: + /// a[i].x.y = 23; + /// In this case we clearly want to update a, but we need to delegate to + /// the object in a so that it updates the right part of it (depending on + /// what kind of array abstraction it is). So, as we find the variable + /// ('a' in this case) we build a stack of which part of it is accessed. + /// + /// As abstractions may split the assignment into multiple writes (for + /// example pointers that could point to several locations, arrays with + /// non-constant indexes), each of which has to handle the rest of the + /// compound write, thus the stack is passed (to write, which does the + /// actual updating) as an explicit argument rather than just via + /// recursion. + /// + /// The same use case (but for the opposite reason; because you will only + /// update one of the multiple objects) is also why a merge_write flag is + /// needed. + virtual bool assign( + const exprt &expr, + const abstract_object_pointert value, + const namespacet &ns); + + /// Reduces the domain based on a condition + /// + /// \param expr: the expression that is to be assumed + /// \param ns: the current namespace + /// + /// \return True if the assume changed the domain. + /// + /// Reduces the domain to (an over-approximation) of the cases + /// when the the expression holds. Used to implement assume + /// statements and conditional branches. + /// It would be valid to simply return false here because it + /// is an over-approximation. We try to do better than that. + /// The better the implementation the more precise the results + /// will be. + virtual bool assume(const exprt &expr, const namespacet &ns); + + /// Used within assign to do the actual dispatch + /// + /// \param lhs: the abstract object for the left hand side of the write + /// (i.e. the one to update). + /// \param rhs: the value we are trying to write to the left hand side + /// \param remaining_stack: what is left of the stack before the rhs can + /// replace or be merged with the rhs + /// \param ns: the namespace + /// \param merge_write: Are we replacing the left hand side with the + /// right hand side (e.g. we know for a fact that + /// we are overwriting this object) or could the + /// write in fact not take place and therefore we + /// should merge to model the case where it did not. + /// + /// \return A modified version of the rhs after the write has taken place + /// + /// Write an abstract object onto another respecting a stack of + /// member, index and dereference access. This ping-pongs between + /// this method and the relevant write methods in abstract_struct, + /// abstract_pointer and abstract_array until the stack is empty + virtual abstract_object_pointert write( + abstract_object_pointert lhs, + abstract_object_pointert rhs, + std::stack remaining_stack, + const namespacet &ns, + bool merge_write); + + /// Delete a symbol from the map. This is necessary if the + /// symbol falls out of scope and should no longer be tracked. + /// + /// \param expr: A symbol to delete from the map + void erase(const symbol_exprt &expr); + + /// Look at the configuration for the sensitivity and create an + /// appropriate abstract_object + /// + /// \param type: the type of the object whose state should be tracked + /// \param top: does the type of the object start as top + /// \param bottom: does the type of the object start as bottom in + /// the two-value domain + /// \param ns: the current variable namespace + /// + /// \return The abstract object that has been created + virtual abstract_object_pointert abstract_object_factory( + const typet &type, + const namespacet &ns, + bool top = true, + bool bottom = false) const; + + /// For converting constants in the program + /// + /// \param type: the type of the object whose state should be tracked + /// \param e: the starting value of the symbol + /// \param ns: the current variable namespace + /// + /// \return The abstract object that has been created + /// + /// Look at the configuration for the sensitivity and create an + /// appropriate abstract_object, assigning an appropriate value + /// Maybe the two abstract_object_factory methods should be + /// compacted to one call... + virtual abstract_object_pointert abstract_object_factory( + const typet &type, + const exprt &e, + const namespacet &ns) const; + + /// Computes the join between "this" and "b" + /// + /// \param env: the other environment + /// + /// \return A Boolean, true when the merge has changed something + virtual bool merge(const abstract_environmentt &env); + + /// This should be used as a default case / everything else has failed + /// The string is so that I can easily find and diagnose cases where this + /// occurs + /// + /// \param havoc_string: diagnostic string to track down havoc causing. + /// + /// Set the domain to top + virtual void havoc(const std::string &havoc_string); + + /// Set the domain to top (i.e. everything) + void make_top(); + + /// Set the domain to top (i.e. no possible states / unreachable) + void make_bottom(); + + /// Gets whether the domain is bottom + bool is_bottom() const; + + /// Gets whether the domain is top + bool is_top() const; + + /// Print out all the values in the abstract object map + /// + /// \param out: the stream to write to + /// \param ai: the abstract interpreter that contains this domain + /// \param ns: the current namespace + void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) + const; + + /// Check the structural invariants are maintained. + /// In this case this is checking there aren't any null pointer mapped values + bool verify() const; + + /// For our implementation of variable sensitivity domains, we need + /// to be able to efficiently find symbols that have changed between + /// different domains. To do this, we need to be able to quickly + /// find which symbols have new written locations, which we do by + /// finding the intersection between two different domains + /// (environments). + /// + /// Inputs are two abstract_environmentt's that need to be + /// intersected for, so that we can find symbols that have changed + /// between different domains. + /// + /// \return An std::vector containing the symbols that are present + /// in both environments. + static std::vector modified_symbols( + const abstract_environmentt &first, + const abstract_environmentt &second); + + abstract_object_statisticst gather_statistics(const namespacet &ns) const; + +protected: + bool bottom; + + // We may need to break out more of these cases into these + virtual abstract_object_pointert + eval_expression(const exprt &e, const namespacet &ns) const; + + sharing_mapt map; + +private: + /// Look at the configuration for the sensitivity and create an + /// appropriate abstract_object + /// + /// \param type: the type of the object whose state should be tracked + /// \param top: does the type of the object start as top in + /// the two-value domain + /// \param bottom: does the type of the object start as bottom in + /// the two-value domain + /// \param e: the starting value of the symbol if top and bottom + /// are both false + /// \param environment: the current environment (normally *this) + /// \param ns: the current variable namespace + /// + /// \return The abstract object that has been created + abstract_object_pointert abstract_object_factory( + const typet &type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) const; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_ENVIROMENT_H diff --git a/src/analyses/variable-sensitivity/abstract_object.cpp b/src/analyses/variable-sensitivity/abstract_object.cpp new file mode 100644 index 00000000000..a3cfb5b04ae --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_object.cpp @@ -0,0 +1,293 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "abstract_object.h" + +abstract_objectt::abstract_objectt(const typet &type) + : t(type), bottom(false), top(true) +{ +} + +abstract_objectt::abstract_objectt(const typet &type, bool top, bool bottom) + : t(type), bottom(bottom), top(top) +{ + PRECONDITION(!(top && bottom)); +} + +abstract_objectt::abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : t(expr.type()), bottom(false), top(true) +{ +} + +abstract_objectt::abstract_objectt( + const typet &type, + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : t(type), bottom(false), top(true) +{ +} + +const typet &abstract_objectt::type() const +{ + return t; +} + +abstract_object_pointert +abstract_objectt::merge(abstract_object_pointert other) const +{ + return abstract_object_merge(other); +} + +abstract_object_pointert abstract_objectt::abstract_object_merge( + const abstract_object_pointert other) const +{ + if(is_top() || other->bottom) + return this->abstract_object_merge_internal(other); + + internal_abstract_object_pointert merged = mutable_clone(); + merged->make_top(); + merged->bottom = false; + return merged->abstract_object_merge_internal(other); +} + +abstract_object_pointert abstract_objectt::abstract_object_merge_internal( + const abstract_object_pointert other) const +{ + // Default implementation + return shared_from_this(); +} + +abstract_object_pointert +abstract_objectt::meet(const abstract_object_pointert &other) const +{ + return abstract_object_meet(other); +} + +abstract_object_pointert abstract_objectt::abstract_object_meet( + const abstract_object_pointert &other) const +{ + if(is_bottom() || other->top) + return this->abstract_object_meet_internal(other); + + internal_abstract_object_pointert met = mutable_clone(); + met->bottom = true; + met->top = false; + return met->abstract_object_meet_internal(other); +} + +abstract_object_pointert abstract_objectt::abstract_object_meet_internal( + const abstract_object_pointert &other) const +{ + // Default implementation + return shared_from_this(); +} + +abstract_object_pointert abstract_objectt::expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + exprt copy = expr; + + for(exprt &op : copy.operands()) + { + abstract_object_pointert op_abstract_object = environment.eval(op, ns); + const exprt &const_op = op_abstract_object->to_constant(); + op = const_op.is_nil() ? op : const_op; + } + + simplify(copy, ns); + + for(const exprt &op : copy.operands()) + { + abstract_object_pointert op_abstract_object = environment.eval(op, ns); + const exprt &const_op = op_abstract_object->to_constant(); + + if(const_op.is_nil()) + { + return environment.abstract_object_factory(copy.type(), ns, true); + } + } + + return environment.abstract_object_factory(copy.type(), copy, ns); +} + +abstract_object_pointert abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return shared_from_this(); +} + +abstract_object_pointert abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + return environment.abstract_object_factory(type(), ns, true); +} + +bool abstract_objectt::is_top() const +{ + return top; +} + +bool abstract_objectt::is_bottom() const +{ + return bottom; +} + +bool abstract_objectt::verify() const +{ + return !(top && bottom); +} + +exprt abstract_objectt::to_constant() const +{ + return nil_exprt(); +} + +void abstract_objectt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(top) + { + out << "TOP"; + } + else if(bottom) + { + out << "BOTTOM"; + } + else + { + out << "Unknown"; + } +} + +abstract_object_pointert abstract_objectt::merge( + abstract_object_pointert op1, + abstract_object_pointert op2, + bool &out_modifications) +{ + abstract_object_pointert result = op1->should_use_base_merge(op2) + ? op1->abstract_object_merge(op2) + : op1->merge(op2); + // If no modifications, we will return the original pointer + out_modifications = result != op1; + + return result; +} + +bool abstract_objectt::should_use_base_merge( + const abstract_object_pointert other) const +{ + return is_top() || other->is_bottom() || other->is_top(); +} + +abstract_object_pointert abstract_objectt::meet( + abstract_object_pointert op1, + abstract_object_pointert op2, + bool &out_modifications) +{ + abstract_object_pointert result = op1->should_use_base_meet(op2) + ? op1->abstract_object_meet(op2) + : op1->meet(op2); + // If no modifications, we will return the original pointer + out_modifications = result != op1; + + return result; +} + +bool abstract_objectt::should_use_base_meet( + const abstract_object_pointert &other) const +{ + return is_bottom() || other->is_bottom() || other->is_top(); +} + +abstract_object_pointert abstract_objectt::update_location_context( + const locationst &locations, + const bool update_sub_elements) const +{ + return shared_from_this(); +} + +void abstract_objectt::dump_map( + std::ostream out, + const abstract_objectt::shared_mapt &m) +{ + shared_mapt::viewt view; + m.get_view(view); + + out << "{"; + bool first = true; + for(auto &item : view) + { + out << (first ? "" : ", ") << item.first; + first = false; + } + out << "}"; +} + +void abstract_objectt::dump_map_diff( + std::ostream out, + const abstract_objectt::shared_mapt &m1, + const abstract_objectt::shared_mapt &m2) +{ + shared_mapt::delta_viewt delta_view; + m1.get_delta_view(m2, delta_view, false); + + out << "DELTA{"; + bool first = true; + for(auto &item : delta_view) + { + out << (first ? "" : ", ") << item.k << "=" << item.is_in_both_maps(); + first = false; + } + out << "}"; +} + +abstract_object_pointert abstract_objectt::unwrap_context() const +{ + return shared_from_this(); +} + +void abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + const auto &this_ptr = shared_from_this(); + PRECONDITION(visited.find(this_ptr) == visited.end()); + visited.insert(this_ptr); +} diff --git a/src/analyses/variable-sensitivity/abstract_object.h b/src/analyses/variable-sensitivity/abstract_object.h new file mode 100644 index 00000000000..91c851dfb86 --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_object.h @@ -0,0 +1,584 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// abstract_objectt is the top of the inheritance heirarchy of objects +/// used to represent individual variables in the general non-relational +/// domain. It is a two element abstraction (i.e. it is either top or +/// bottom). Within the hierarchy of objects under it, child classes are +/// more precise abstractions (the converse doesn't hold to avoid +/// diamonds and inheriting unnecessary fields). Thus the common parent +/// of two classes is an abstraction capable of representing both. This +/// is important for understanding merge. +/// +/// These objects are intended to be used in a copy-on-write style, which +/// is why their interface differs a bit from ai_domain_baset's +/// modify-in-place style of interface. +/// +/// Although these can represent bottom (this variable cannot take any +/// value) it is not common for them to do so. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_OBJECT_H + +#include +#include +#include +#include +#include +#include +#include + +#include "abstract_object_statistics.h" +#include +#include +#include + +class typet; +class constant_exprt; +class abstract_environmentt; +class namespacet; + +#define CLONE \ + internal_abstract_object_pointert mutable_clone() const override \ + { \ + typedef std::remove_const< \ + std::remove_reference::type>::type current_typet; \ + return internal_abstract_object_pointert(new current_typet(*this)); \ + } + +/// Merge is designed to allow different abstractions to be merged +/// gracefully. There are two real use-cases for this: +/// +/// 1. Having different abstractions for the variable in different +/// parts of the program. +/// 2. Allowing different domains to write to ambiguous locations +/// for example, if a stores multiple values (maybe one per +/// location) with a constant for each, i does not represent one +/// single value (top, non-unit interval, etc.) and v is something +/// other than constant, then +/// a[i] = v +/// will cause this to happen. +/// +/// To handle this, merge is dispatched to the first abstract object being +/// merged, which switches based on the type of the other object. If it can +/// merge then it merges, otherwise it calls the parent merge. + +template +using sharing_ptrt = std::shared_ptr; // NOLINT(*) + +typedef sharing_ptrt abstract_object_pointert; +using abstract_object_visitedt = std::set; + +class abstract_objectt : public std::enable_shared_from_this +{ +public: + /// \param type: the type the abstract_object is representing + explicit abstract_objectt(const typet &type); + + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + /// + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + abstract_objectt(const typet &type, bool top, bool bottom); + + /// Construct an abstract object from the expression + /// + /// \param expr: The expression to use as the starting pointer for an abstract + /// object + /// \param environment: The environment this abstract object is + /// being created in + /// \param ns: The namespace + abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /// Ctor for building object of types that differ from the types of input + /// expressions + /// + /// \param type explicitly declared type the resulting object should have + /// \param expr expression used to build the object + /// \param environment abstract environment to evaluate the expression + /// \param ns namespace to uncover names inside the expression + abstract_objectt( + const typet &type, + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + virtual ~abstract_objectt() + { + } + + /// Get the real type of the variable this abstract object is representing. + /// + /// \return The program type this abstract object represents + const typet &type() const; + + /// Find out if the abstract object is top + /// + /// \return Returns true if the abstract object is representing the top + /// (i.e. we don't know anything about the value). + virtual bool is_top() const; + + /// Find out if the abstract object is bottom + /// + /// \return Returns true if the abstract object is representing the bottom. + virtual bool is_bottom() const; + + /// \brief Verify the internal structure of an abstract_object is correct + /// \return true if the abstract_object is correctly constructed, or false + /// otherwise + virtual bool verify() const; + + virtual void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const; + + /// Interface for transforms + /// + /// \param expr: the expression to evaluate and find the result of it. + /// This will be the symbol referred to be op0() + /// \param operands: an abstract_object (pointer) that represent + /// the possible values of each operand + /// \param environment: the abstract environment in which the + /// expression is being evaluated + /// \param ns: the current variable namespace + /// + /// \return Returns the abstract_object representing the result of + /// this expression to the maximum precision available. + /// + /// To try and resolve different expressions with the maximum level + /// of precision available. + virtual abstract_object_pointert expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const; + + /// Converts to a constant expression if possible + /// + /// \return Returns an exprt representing the value if the value is known and + /// constant. Otherwise returns the nil expression + /// + /// If abstract element represents a single value, then that value, + /// otherwise nil. E.G. if it is an interval then this will be x if it is + /// [x,x] This is the (sort of) dual to the constant_exprt constructor + /// that allows an object to be built from a value. + virtual exprt to_constant() const; + + /** + * A helper function to evaluate an abstract object contained + * within a container object. More precise abstractions may override this + * to return more precise results. + * + * \param env the abstract environment + * \param specifier a modifier expression, such as an array index or field + * specifier used to indicate access to a specific component + * \param ns the current namespace + * + * \return the abstract_objectt representing the value of the read component. + */ + virtual abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const; + + /** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ + virtual abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const; + + /// Print the value of the abstract object + /// + /// \param out: the stream to write to + /// \param ai: the abstract interpreter that contains the abstract domain + /// (that contains the object ... ) + /// \param ns: the current namespace + virtual void output( + std::ostream &out, + const class ai_baset &ai, + const namespacet &ns) const; + + typedef std::set locationst; + typedef sharing_mapt + shared_mapt; + + static void dump_map(std::ostream out, const shared_mapt &m); + /** + * \brief Dump all elements in m1 that are different or missing in m2 + * + * \param out the stream to write output to + * \param m1 the 'target' sharing_map + * \param m2 the reference sharing map + */ + static void + dump_map_diff(std::ostream out, const shared_mapt &m1, const shared_mapt &m2); + + abstract_object_pointert clone() const + { + return abstract_object_pointert(mutable_clone()); + } + + /** + * Determine whether 'this' abstract_object has been modified in comparison + * to a previous 'before' state. + * \param before The abstract_object_pointert to use as a reference to + * compare against + * \return true if 'this' is considered to have been modified in comparison + * to 'before', false otherwise. + */ + virtual bool has_been_modified(const abstract_object_pointert before) const + { + /// Default implementation, with no other information to go on + /// falls back to relying on copy-on-write and pointer inequality + /// to indicate if an abstract_objectt has been modified + return this != before.get(); + }; + + /// Clones the first parameter and merges it with the second. + /// + /// \param op1: the first abstract object to merge, this object determines + /// the sensitivity of the output and is the object compared + /// against to choose whether this merge changed anything + /// \param op2: the second abstract object to merge + /// \param out_modifications: reference to a flag indicating modification + /// + /// \return The merged abstract object with the same sensitivity as the + /// first parameter. out_modifications will be true if the resulting + /// abstract object is different from op1 + static abstract_object_pointert merge( + abstract_object_pointert op1, + abstract_object_pointert op2, + bool &out_modifications); + + /// Interface method for the meet operation. Decides whether to use the base + /// implementation or if a more precise abstraction is attainable. + /// \param op1 lhs object for meet + /// \param op2 rhs object for meet + /// \param out_modifications reference to a flag indicating modification + /// (result is not op1) + /// \return resulting object after meet + static abstract_object_pointert meet( + abstract_object_pointert op1, + abstract_object_pointert op2, + bool &out_modifications); + + /// Base implementation of the meet operation: only used if no more precise + /// abstraction can be used, can only result in {TOP, BOTTOM, one of the + /// original objects} + /// \param other pointer to the abstract object to meet + /// \return the resulting abstract object pointer + virtual abstract_object_pointert + meet(const abstract_object_pointert &other) const; + + /** + * Update the location context for an abstract object, potentially + * propogating the update to any children of this abstract object. + * + * \param locations the set of locations to be updated + * \param update_sub_elements if true, propogate the update operation to any + * children of this abstract object + * + * \return a clone of this abstract object with it's location context + * updated + */ + virtual abstract_object_pointert update_location_context( + const locationst &locations, + const bool update_sub_elements) const; + + // Const versions must perform copy-on-write + abstract_object_pointert make_top() const + { + if(is_top()) + return shared_from_this(); + + internal_abstract_object_pointert clone = mutable_clone(); + clone->make_top(); + return clone; + } + + abstract_object_pointert clear_top() const + { + if(!is_top()) + return shared_from_this(); + + internal_abstract_object_pointert clone = mutable_clone(); + clone->clear_top(); + return clone; + } + + virtual abstract_object_pointert unwrap_context() const; + + /** + * Pure virtual interface required of a client that can apply a copy-on-write + * operation to a given abstract_object_pointert. + */ + struct abstract_object_visitort + { + virtual abstract_object_pointert + visit(const abstract_object_pointert element) const = 0; + }; + + /** + * Apply a visitor operation to all sub elements of this abstract_object. + * A sub element might be a member of a struct, or an element of an array, + * for instance, but this is entirely determined by the particular + * derived instance of abstract_objectt. + * + * \param visitor an instance of a visitor class that will be applied to + * all sub elements + * \return A new abstract_object if it's contents is modifed, or this if + * no modification is needed + */ + virtual abstract_object_pointert + visit_sub_elements(const abstract_object_visitort &visitor) const + { + return shared_from_this(); + } + + virtual size_t internal_hash() const + { + return std::hash{}(shared_from_this()); + } + + virtual bool internal_equality(const abstract_object_pointert &other) const + { + return shared_from_this() == other; + } + +private: + /// To enforce copy-on-write these are private and have read-only accessors + typet t; + bool bottom; + bool top; + + // Hooks to allow a sub-class to perform its own operations on + // setting/clearing top + virtual void make_top_internal() + { + } + virtual void clear_top_internal() + { + } + + /** + * Helper function for abstract_objectt::abstract_object_merge to perform any + * additional actions after the base abstract_object_merge has completed it's + * actions but immediately prior to it returning. As such, this function gives + * the ability to perform additional work for a merge. + * + * This default implementation just returns itself. + * + * \param other the object to merge with this + * + * \return the result of the merge + */ + virtual abstract_object_pointert + abstract_object_merge_internal(const abstract_object_pointert other) const; + + /// Helper function for base meet, in case additional work was needed. Base + /// implementation simply return pointer to itself. + /// \param other pointer to the other object + /// \return the resulting object + virtual abstract_object_pointert + abstract_object_meet_internal(const abstract_object_pointert &other) const; + +protected: + template + using internal_sharing_ptrt = std::shared_ptr; + + typedef internal_sharing_ptrt + internal_abstract_object_pointert; + + // Macro is not used as this does not override + virtual internal_abstract_object_pointert mutable_clone() const + { + return internal_abstract_object_pointert(new abstract_objectt(*this)); + } + + /// Create a new abstract object that is the result of the merge, unless + /// the object would be unchanged, then would return itself. + /// + /// \param other: The object to merge with this + /// + /// \return Returns the result of the abstract object. + abstract_object_pointert + abstract_object_merge(const abstract_object_pointert other) const; + + /// To detect the cases where the base merge is sufficient to do a merge + /// We can't do if this->is_bottom() since we want the specific + /// + /// \param other: the object being merged with + /// + /// \return Returns true if the base class is capable of doing + /// a complete merge + bool should_use_base_merge(const abstract_object_pointert other) const; + + /// Create a new abstract object that is the result of the merge, unless + /// the object would be unchanged, then would return itself. + /// + /// \param other: The object to merge with this + /// + /// \return Returns the result of the merge. + virtual abstract_object_pointert merge(abstract_object_pointert other) const; + + /// Helper function for base meet. Two cases: return itself (if trivially + /// contained in other); return BOTTOM otherwise. + /// \param other pointer to the other object + /// \return the resulting object + abstract_object_pointert + abstract_object_meet(const abstract_object_pointert &other) const; + + /// Helper function to decide if base meet implementation should be used + /// \param other pointer to the other object to meet + /// \return true if base implementation would yield the most precise + /// abstraction anyway + bool should_use_base_meet(const abstract_object_pointert &other) const; + + template + static bool merge_maps( + const std::map &map1, + const std::map &map2, + std::map &out_map); + + template + static bool merge_shared_maps( + const sharing_mapt &map1, + const sharing_mapt &map2, + sharing_mapt &out_map); + + // The one exception is merge in descendant classes, which needs this + void make_top() + { + top = true; + this->make_top_internal(); + } + void clear_top() + { + top = false; + this->clear_top_internal(); + } +}; + +template +bool abstract_objectt::merge_maps( + const std::map &m1, + const std::map &m2, + std::map &out_map) +{ + out_map.clear(); + + typedef std::map abstract_object_mapt; + + bool modified = false; + + std::vector> intersection_set; + std::set_intersection( + m1.cbegin(), + m1.cend(), + m2.cbegin(), + m2.cend(), + std::back_inserter(intersection_set), + []( + const std::pair &op1, + const std::pair &op2) { + return op1.first < op2.first; + }); + + for(const typename abstract_object_mapt::value_type &entry : intersection_set) + { + // merge entries + + const abstract_object_pointert &v1 = m1.at(entry.first); + const abstract_object_pointert &v2 = m2.at(entry.first); + + bool changes = false; + abstract_object_pointert v_new = abstract_objectt::merge(v1, v2, changes); + + modified |= changes; + + out_map[entry.first] = v_new; + } + + return modified; +} + +template +bool abstract_objectt::merge_shared_maps( + const sharing_mapt &m1, + const sharing_mapt &m2, + sharing_mapt &out_map) +{ + bool modified = false; + + typename sharing_mapt:: + delta_viewt delta_view; + m1.get_delta_view(m2, delta_view, true); + + for(auto &item : delta_view) + { + bool changes = false; + abstract_object_pointert v_new = + abstract_objectt::merge(item.m, item.get_other_map_value(), changes); + if(changes) + { + modified = true; + out_map.replace(item.k, v_new); + } + } + + return modified; +} + +struct abstract_hashert +{ + typedef abstract_object_pointert argument_typet; + typedef std::size_t result_typet; + result_typet operator()(argument_typet const &s) const noexcept + { + return s->internal_hash(); + } +}; + +struct abstract_equalert +{ + typedef abstract_object_pointert argument_typet; + typedef std::size_t result_typet; + bool operator()(argument_typet const &left, argument_typet const &right) const + noexcept + { + return left->internal_equality(right); + } +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/abstract_object_statistics.h b/src/analyses/variable-sensitivity/abstract_object_statistics.h new file mode 100644 index 00000000000..133b11ff38f --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_object_statistics.h @@ -0,0 +1,31 @@ +/*******************************************************************\ + +Module: Variable Sensitivity Domain + +Author: Hannes Steffenhagen + +\*******************************************************************/ + +/// \file +/// Statistics gathering for the variable senstivity domain + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_OBJECT_STATISTICS_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_OBJECT_STATISTICS_H + +#include + +#include +struct abstract_object_statisticst +{ + std::size_t number_of_interval_abstract_objects = 0; + std::size_t number_of_single_value_intervals = 0; + std::size_t number_of_structs = 0; + std::size_t number_of_arrays = 0; + std::size_t number_of_pointers = 0; + std::size_t number_of_constants = 0; + std::size_t number_of_globals = 0; + /// An underestimation of the memory usage of the abstract objects + memory_sizet objects_memory_usage; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_OBJECT_STATISTICS_H diff --git a/src/analyses/variable-sensitivity/abstract_value.cpp b/src/analyses/variable-sensitivity/abstract_value.cpp new file mode 100644 index 00000000000..53f7c506faf --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_value.cpp @@ -0,0 +1,31 @@ +/*******************************************************************\ + + Module: Analyses Variable Sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include "abstract_value.h" + +abstract_valuet::abstract_valuet(const typet &type) : abstract_objectt(type) +{ +} + +abstract_valuet::abstract_valuet(const typet &type, bool top, bool bottom) + : abstract_objectt(type, top, bottom) +{ +} + +abstract_valuet::abstract_valuet( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_objectt(expr, environment, ns) +{ +} diff --git a/src/analyses/variable-sensitivity/abstract_value.h b/src/analyses/variable-sensitivity/abstract_value.h new file mode 100644 index 00000000000..9fd77214fdd --- /dev/null +++ b/src/analyses/variable-sensitivity/abstract_value.h @@ -0,0 +1,51 @@ +/*******************************************************************\ + + Module: Analyses Variable Sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// The parent class of all abstractions that represent a base type + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_VALUE_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_VALUE_H + +#include + +class abstract_valuet : public abstract_objectt +{ +public: + /// \param type: the type the abstract_value is representing + explicit abstract_valuet(const typet &type); + + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + /// + /// \param type: the type the abstract_value is representing + /// \param top: is the abstract_value starting as top + /// \param bottom: is the abstract_value starting as bottom + abstract_valuet(const typet &type, bool top, bool bottom); + + /// Construct an abstract value from the expression + /// + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: The environment this abstract object is + /// being created in + /// \param ns: the namespace + abstract_valuet( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + virtual ~abstract_valuet() + { + } + +protected: + CLONE +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ABSTRACT_VALUE_H diff --git a/src/analyses/variable-sensitivity/array_abstract_object.cpp b/src/analyses/variable-sensitivity/array_abstract_object.cpp new file mode 100644 index 00000000000..b1822f54303 --- /dev/null +++ b/src/analyses/variable-sensitivity/array_abstract_object.cpp @@ -0,0 +1,102 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include "array_abstract_object.h" +#include +#include +#include + +array_abstract_objectt::array_abstract_objectt(const typet &t) + : abstract_objectt(t) +{ + PRECONDITION(t.id() == ID_array); +} + +array_abstract_objectt::array_abstract_objectt( + const typet &t, + bool tp, + bool bttm) + : abstract_objectt(t, tp, bttm) +{ + PRECONDITION(t.id() == ID_array); +} + +array_abstract_objectt::array_abstract_objectt( + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_objectt(e, environment, ns) +{ + PRECONDITION(e.type().id() == ID_array); +} + +abstract_object_pointert array_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return this->read_index(env, to_index_expr(specifier), ns); +} + +abstract_object_pointert array_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + return this->write_index( + environment, ns, stack, to_index_expr(specifier), value, merging_write); +} + +abstract_object_pointert array_abstract_objectt::read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const +{ + array_typet array_type(to_array_type(type())); + const typet &subtype = array_type.subtype(); + + // if we are bottom then so are the values in the array + // otherwise the values are top + return env.abstract_object_factory(subtype, ns, !is_bottom(), is_bottom()); +} + +sharing_ptrt array_abstract_objectt::write_index( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const index_exprt &index_expr, + const abstract_object_pointert value, + bool merging_write) const +{ + // TODO(tkiley): Should this in fact havoc since we can't verify + // that we are not writing past the end of the array - Martin said + // default should be not to, but perhaps for soundness the base class should + // havoc and the default should derive from this. + if(is_top() || is_bottom()) + { + return std::dynamic_pointer_cast(clone()); + } + else + { + return sharing_ptrt( + new array_abstract_objectt(type(), true, false)); + } +} + +void array_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + abstract_objectt::get_statistics(statistics, visited, env, ns); + ++statistics.number_of_arrays; +} diff --git a/src/analyses/variable-sensitivity/array_abstract_object.h b/src/analyses/variable-sensitivity/array_abstract_object.h new file mode 100644 index 00000000000..d4f73fee432 --- /dev/null +++ b/src/analyses/variable-sensitivity/array_abstract_object.h @@ -0,0 +1,132 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// The base type of all abstract array representations + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ARRAY_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ARRAY_ABSTRACT_OBJECT_H + +#include +#include + +class abstract_environmentt; +class index_exprt; + +class array_abstract_objectt : public abstract_objectt +{ +public: + /// \param type: the type the abstract_object is representing + explicit array_abstract_objectt(const typet &type); + + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + /// + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + array_abstract_objectt(const typet &type, bool top, bool bottom); + + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: the environment the abstract object is + /// being created in + /// \param ns: the namespace + explicit array_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /** + * A helper function to evaluate an abstract object contained + * within a container object. More precise abstractions may override this + * to return more precise results. + * + * \param env the abstract environment + * \param specifier a modifier expression, such as an array index or field + * specifier used to indicate access to a specific component + * \param ns the current namespace + * + * \return the abstract_objectt representing the value of the read component. + */ + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + /** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + +protected: + CLONE + + /// A helper function to read elements from an array. More precise + /// abstractions may override this to provide more precise results. + /// + /// \param env: the environment + /// \param index: the expression used to access the specific value + /// in the array + /// \param ns: the current variable namespace + /// + /// \return An abstract object representing the value in the array + virtual abstract_object_pointert read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const; + + /// A helper function to evaluate writing to a component of a struct. + /// More precise abstractions may override this to + /// update what they are storing for a specific component. + /// + /// \param environment: the abstract environment + /// \param ns: the namespace + /// \param stack: the remaining stack of expressions on the LHS to evaluate + /// \param index_expr: the expression uses to access a specific index + /// \param value: the value we are trying to assign to that value in the array + /// \param merging_write: ? + /// + /// \return The array_abstract_objectt representing the result of writing + /// to a specific component. In this case this will always be top + /// as we are not tracking the value in the array. + virtual sharing_ptrt write_index( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const index_exprt &index_expr, + const abstract_object_pointert value, + bool merging_write) const; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_ARRAY_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/constant_abstract_value.cpp b/src/analyses/variable-sensitivity/constant_abstract_value.cpp new file mode 100644 index 00000000000..58af0459797 --- /dev/null +++ b/src/analyses/variable-sensitivity/constant_abstract_value.cpp @@ -0,0 +1,219 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "abstract_enviroment.h" +#include "constant_abstract_value.h" + +constant_abstract_valuet::constant_abstract_valuet(typet t) + : abstract_valuet(t), value() +{ +} + +constant_abstract_valuet::constant_abstract_valuet(typet t, bool tp, bool bttm) + : abstract_valuet(t, tp, bttm), value() +{ +} + +constant_abstract_valuet::constant_abstract_valuet( + const exprt e, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_valuet(e.type(), false, false), value(e) +{ +} + +abstract_object_pointert constant_abstract_valuet::expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + // try finding the rounding mode. If it's not constant, try + // seeing if we can get the same result with all rounding modes + auto rounding_mode_symbol = + symbol_exprt(CPROVER_PREFIX "rounding_mode", signedbv_typet(32)); + auto rounding_mode_value = environment.eval(rounding_mode_symbol, ns); + auto rounding_mode_constant = rounding_mode_value->to_constant(); + if(rounding_mode_constant.is_nil()) + { + return try_transform_expr_with_all_rounding_modes(expr, environment, ns); + } + + exprt adjusted_expr = expr; + adjust_float_expressions(adjusted_expr, ns); + exprt constant_replaced_expr = adjusted_expr; + constant_replaced_expr.operands().clear(); + + // Two passes over the expression - one for simplification, + // another to check if there are any top subexpressions left + for(const exprt &op : adjusted_expr.operands()) + { + abstract_object_pointert lhs_abstract_object = environment.eval(op, ns); + const exprt &lhs_value = lhs_abstract_object->to_constant(); + if(lhs_value.is_nil()) + { + // do not give up if a sub-expression is not a constant, + // because the whole expression may still be simplified in some cases + constant_replaced_expr.operands().push_back(op); + } + else + { + // rebuild the operands list with constant versions of + // any symbols + constant_replaced_expr.operands().push_back(lhs_value); + } + } + exprt simplified = simplify_expr(constant_replaced_expr, ns); + + for(const exprt &op : simplified.operands()) + { + abstract_object_pointert lhs_abstract_object = environment.eval(op, ns); + const exprt &lhs_value = lhs_abstract_object->to_constant(); + if(lhs_value.is_nil()) + { + return environment.abstract_object_factory( + simplified.type(), ns, true, false); + } + } + + return environment.abstract_object_factory(simplified.type(), simplified, ns); +} + +abstract_object_pointert +constant_abstract_valuet::try_transform_expr_with_all_rounding_modes( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + const symbol_exprt rounding_mode_symbol = + symbol_exprt(CPROVER_PREFIX "rounding_mode", signedbv_typet(32)); + // NOLINTNEXTLINE (whitespace/braces) + auto rounding_modes = std::array{ + // NOLINTNEXTLINE (whitespace/braces) + {ieee_floatt::ROUND_TO_EVEN, + ieee_floatt::ROUND_TO_ZERO, + ieee_floatt::ROUND_TO_MINUS_INF, + // NOLINTNEXTLINE (whitespace/braces) + ieee_floatt::ROUND_TO_PLUS_INF}}; + std::vector possible_results; + for(auto current_rounding_mode : rounding_modes) + { + abstract_environmentt child_env(environment); + child_env.assign( + rounding_mode_symbol, + child_env.abstract_object_factory( + signedbv_typet(32), + from_integer( + mp_integer(static_cast(current_rounding_mode)), + signedbv_typet(32)), + ns), + ns); + + // Dummy vector as the called expression_transform() ignores it + std::vector dummy; + possible_results.push_back( + expression_transform(expr, dummy, child_env, ns)); + } + auto first = possible_results.front()->to_constant(); + for(auto const &possible_result : possible_results) + { + auto current = possible_result->to_constant(); + if(current.is_nil() || current != first) + { + return environment.abstract_object_factory(expr.type(), ns); + } + } + return possible_results.front(); +} + +exprt constant_abstract_valuet::to_constant() const +{ + if(!is_top() && !is_bottom()) + { + return this->value; + } + else + { + return abstract_objectt::to_constant(); + } +} + +void constant_abstract_valuet::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(!is_top() && !is_bottom()) + { + out << from_expr(to_constant_expr(value)); + } + else + { + abstract_objectt::output(out, ai, ns); + } +} + +abstract_object_pointert +constant_abstract_valuet::merge(abstract_object_pointert other) const +{ + constant_abstract_value_pointert cast_other = + std::dynamic_pointer_cast(other); + if(cast_other) + { + return merge_constant_constant(cast_other); + } + else + { + // TODO(tkiley): How do we set the result to be toppish? Does it matter? + return abstract_valuet::merge(other); + } +} + +abstract_object_pointert constant_abstract_valuet::merge_constant_constant( + constant_abstract_value_pointert other) const +{ + if(is_bottom()) + { + return std::make_shared(*other); + } + else + { + // Can we actually merge these value + if(value == other->value) + { + return shared_from_this(); + } + else + { + return abstract_valuet::merge(other); + } + } +} + +void constant_abstract_valuet::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + abstract_valuet::get_statistics(statistics, visited, env, ns); + ++statistics.number_of_constants; + statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this)); +} diff --git a/src/analyses/variable-sensitivity/constant_abstract_value.h b/src/analyses/variable-sensitivity/constant_abstract_value.h new file mode 100644 index 00000000000..fcbf209ff68 --- /dev/null +++ b/src/analyses/variable-sensitivity/constant_abstract_value.h @@ -0,0 +1,113 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// An abstraction of a single value that just stores a constant. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_ABSTRACT_VALUE_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_ABSTRACT_VALUE_H + +#include + +#include +#include + +class constant_abstract_valuet : public abstract_valuet +{ +private: + typedef sharing_ptrt + constant_abstract_value_pointert; + +public: + explicit constant_abstract_valuet(typet t); + constant_abstract_valuet(typet t, bool tp, bool bttm); + constant_abstract_valuet( + const exprt e, + const abstract_environmentt &environment, + const namespacet &ns); + + virtual ~constant_abstract_valuet() + { + } + + /// Interface for transforms + /// + /// \param expr: the expression to evaluate and find the result of it. + /// This will be the symbol referred to be op0() + /// \param operands: an abstract_object (pointer) that represent + /// the possible values of each operand + /// \param environment: the abstract environment in which the + /// expression is being evaluated + /// \param ns: the current variable namespace + /// + /// \return Returns the abstract_object representing the result of + /// this expression to the maximum precision available. + /// + /// Uses the rewriter to constant fold expressions where possible. + abstract_object_pointert expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const override; + + exprt to_constant() const override; + + void output( + std::ostream &out, + const class ai_baset &ai, + const class namespacet &ns) const override; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + + size_t internal_hash() const override + { + return std::hash{}(value.pretty()); + } + + bool internal_equality(const abstract_object_pointert &other) const override + { + auto cast_other = + std::dynamic_pointer_cast(other); + return cast_other && value == cast_other->value; + } + +protected: + CLONE + + /// Attempts to do a constant/constant merge if both are constants, + /// otherwise falls back to the parent merge + /// + /// \param other: the abstract object to merge with + /// + /// \return Returns the result of the merge + abstract_object_pointert merge(abstract_object_pointert other) const override; + + abstract_object_pointert try_transform_expr_with_all_rounding_modes( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) const; + +private: + /// Merges another constant abstract value into this one + /// + /// \param other: the abstract object to merge with + /// + /// \return Returns a new abstract object that is the result of the merge + /// unless the merge is the same as this abstract object, in which + /// case it returns this. + abstract_object_pointert + merge_constant_constant(constant_abstract_value_pointert other) const; + + exprt value; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_ABSTRACT_VALUE_H diff --git a/src/analyses/variable-sensitivity/constant_array_abstract_object.cpp b/src/analyses/variable-sensitivity/constant_array_abstract_object.cpp new file mode 100644 index 00000000000..f1e19585b88 --- /dev/null +++ b/src/analyses/variable-sensitivity/constant_array_abstract_object.cpp @@ -0,0 +1,394 @@ +/*******************************************************************\ + + Module: Variable Sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ +#include + +#include +#include +#include +#include + +#include "constant_array_abstract_object.h" + +constant_array_abstract_objectt::constant_array_abstract_objectt(typet type) + : array_abstract_objectt(type) +{ + DATA_INVARIANT(verify(), "Structural invariants maintained"); +} + +constant_array_abstract_objectt::constant_array_abstract_objectt( + typet type, + bool top, + bool bottom) + : array_abstract_objectt(type, top, bottom) +{ + DATA_INVARIANT(verify(), "Structural invariants maintained"); +} + +constant_array_abstract_objectt::constant_array_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : array_abstract_objectt(expr, environment, ns) +{ + if(expr.id() == ID_array) + { + int index = 0; + for(const exprt &entry : expr.operands()) + { + map.insert(mp_integer(index), environment.eval(entry, ns)); + ++index; + } + clear_top(); + } + DATA_INVARIANT(verify(), "Structural invariants maintained"); +} + +bool constant_array_abstract_objectt::verify() const +{ + // Either the object is top or bottom (=> map empty) + // or the map is not empty => neither top nor bottom + return array_abstract_objectt::verify() && + (is_top() || is_bottom()) == map.empty(); +} + +void constant_array_abstract_objectt::make_top_internal() +{ + // A structural invariant of constant_array_abstract_objectt is that + // (is_top() || is_bottom()) => map.empty() + map.clear(); +} + +abstract_object_pointert +constant_array_abstract_objectt::merge(abstract_object_pointert other) const +{ + auto cast_other = + std::dynamic_pointer_cast(other); + if(cast_other) + { + return constant_array_merge(cast_other); + } + else + { + // TODO(tkiley): How do we set the result to be toppish? Does it matter? + return array_abstract_objectt::merge(other); + } +} + +abstract_object_pointert constant_array_abstract_objectt::constant_array_merge( + const constant_array_pointert other) const +{ + if(is_bottom()) + { + return std::make_shared(*other); + } + else + { + const auto &result = + std::dynamic_pointer_cast( + mutable_clone()); + + bool modified = + abstract_objectt::merge_shared_maps( + map, other->map, result->map); + + if(!modified) + { + DATA_INVARIANT(verify(), "Structural invariants maintained"); + return shared_from_this(); + } + else + { + INVARIANT(!result->is_top(), "Merge of maps will not generate top"); + INVARIANT(!result->is_bottom(), "Merge of maps will not generate bottom"); + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + } +} + +void constant_array_abstract_objectt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(is_top() || is_bottom()) + { + array_abstract_objectt::output(out, ai, ns); + } + else + { + shared_array_mapt::sorted_viewt view; + map.get_view(view); + + out << "{"; + for(const auto &entry : view) + { + out << "[" << entry.first << "] = "; + entry.second->output(out, ai, ns); + out << "\n"; + } + out << "}"; + } +} + +abstract_object_pointert constant_array_abstract_objectt::read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const +{ + if(is_top()) + { + return env.abstract_object_factory(index.type(), ns, true); + } + else + { + PRECONDITION(!is_bottom()); + mp_integer index_value; + if(eval_index(index, env, ns, index_value)) + { + auto const value = map.find(index_value); + + // Here we are assuming it is always in bounds + if(!value.has_value()) + { + return env.abstract_object_factory(type().subtype(), ns, true, false); + } + else + { + return value.value(); + } + } + else + { + // Although we don't know where we are reading from, and therefore + // we should be returning a TOP value, we may still have useful + // additional information in elements of the array - such as write + // histories so we merge all the known array elements with TOP and return + // that. + + // Create a new TOP value of the appropriate element type + abstract_object_pointert result = + env.abstract_object_factory(type().subtype(), ns, true, false); + + // Merge each known element into the TOP value + shared_array_mapt::viewt known_elements; + map.get_view(known_elements); + bool dummy; + for(const auto &element : known_elements) + { + result = abstract_objectt::merge(result, element.second, dummy); + } + + return result; + } + } +} + +sharing_ptrt +constant_array_abstract_objectt::write_index( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const index_exprt &index_expr, + const abstract_object_pointert value, + bool merging_write) const +{ + if(is_bottom()) + { + return array_abstract_objectt::write_index( + environment, ns, stack, index_expr, value, merging_write); + } + + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + if(!stack.empty()) + { + mp_integer index_value; + if(eval_index(index_expr, environment, ns, index_value)) + { + // We were able to evaluate the index to a value, which we + // assume is in bounds... + auto const old_value = map.find(index_value); + + if(!old_value.has_value()) + { + result->map.insert( + index_value, + environment.write( + get_top_entry(environment, ns), value, stack, ns, merging_write)); + } + else + { + result->map.replace( + index_value, + environment.write( + old_value.value(), value, stack, ns, merging_write)); + } + + result->clear_top(); + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + else + { + // We were not able to evaluate the index to a value + shared_array_mapt::viewt view; + map.get_view(view); + + for(const auto &starting_value : view) + { + // Merging write since we don't know which index we are writing to + result->map.replace( + starting_value.first, + environment.write(starting_value.second, value, stack, ns, true)); + + result->clear_top(); + } + + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + } + else + { + mp_integer index_value; + + if(eval_index(index_expr, environment, ns, index_value)) + { + // We were able to evalute the index expression to a constant + if(merging_write) + { + if(is_top()) + { + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + + INVARIANT(!result->map.empty(), "If not top, map cannot be empty"); + + auto const old_value = result->map.find(index_value); + + if(!old_value.has_value()) // Array element is top + { + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + + bool dummy; + + result->map.replace( + index_value, + abstract_objectt::merge(old_value.value(), value, dummy)); + + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + else + { + auto const old_value = result->map.find(index_value); + if(old_value.has_value()) + { + if(value != abstract_object_pointert{old_value.value()}) + { + result->map.replace(index_value, value); + } + } + else + { + result->map.insert(index_value, value); + } + result->clear_top(); + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + } + + // try to write to all + // TODO(tkiley): Merge with each entry + return array_abstract_objectt::write_index( + environment, ns, stack, index_expr, value, merging_write); + } +} + +abstract_object_pointert constant_array_abstract_objectt::get_top_entry( + const abstract_environmentt &env, + const namespacet &ns) const +{ + return env.abstract_object_factory(type().subtype(), ns, true, false); +} + +bool constant_array_abstract_objectt::eval_index( + const index_exprt &index, + const abstract_environmentt &env, + const namespacet &ns, + mp_integer &out_index) const +{ + abstract_object_pointert index_abstract_object = env.eval(index.index(), ns); + exprt value = index_abstract_object->to_constant(); + if(value.is_constant()) + { + constant_exprt constant_index = to_constant_expr(value); + bool result = to_integer(constant_index, out_index); + return !result; + } + else + { + return false; + } +} + +abstract_object_pointert constant_array_abstract_objectt::visit_sub_elements( + const abstract_object_visitort &visitor) const +{ + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + bool modified = false; + + shared_array_mapt::viewt view; + result->map.get_view(view); + + for(auto &item : view) + { + auto newval = visitor.visit(item.second); + if(newval != item.second) + { + result->map.replace(item.first, std::move(newval)); + modified = true; + } + } + + if(modified) + { + return result; + } + else + { + return shared_from_this(); + } +} + +void constant_array_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + array_abstract_objectt::get_statistics(statistics, visited, env, ns); + shared_array_mapt::viewt view; + map.get_view(view); + for(const auto &object : view) + { + if(visited.find(object.second) == visited.end()) + { + object.second->get_statistics(statistics, visited, env, ns); + } + } + statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this)); +} diff --git a/src/analyses/variable-sensitivity/constant_array_abstract_object.h b/src/analyses/variable-sensitivity/constant_array_abstract_object.h new file mode 100644 index 00000000000..faf749d5bcd --- /dev/null +++ b/src/analyses/variable-sensitivity/constant_array_abstract_object.h @@ -0,0 +1,201 @@ +/*******************************************************************\ + + Module: Variable Sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// An abstraction of an array value + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_ARRAY_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_ARRAY_ABSTRACT_OBJECT_H + +#include +#include + +#include +#include + +class ai_baset; +class abstract_environmentt; + +class constant_array_abstract_objectt : public array_abstract_objectt +{ +public: + typedef sharing_ptrt const + constant_array_pointert; + + /// \param type: the type the abstract_object is representing + explicit constant_array_abstract_objectt(typet type); + + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + /// + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + constant_array_abstract_objectt(typet type, bool top, bool bottom); + + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: the environment the abstract object is + /// being created in + /// \param ns: the namespace + constant_array_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + virtual ~constant_array_abstract_objectt() + { + } + + /// the current known value about this object. For this array we + /// print: { [0] - write_index( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const index_exprt &index_expr, + const abstract_object_pointert value, + bool merging_write) const override; + + /// Tries to do an array/array merge if merging with a constant array + /// If it can't, falls back to parent merge + /// + /// \param other: The object to merge in + /// + /// \return Returns the result of the merge. + abstract_object_pointert merge(abstract_object_pointert other) const override; + + /// To validate that the struct object is in a valid state. + /// This means either it is top or bottom, or if neither of those + /// then there exists something in the map of components. + /// If there is something in the map, then it can't be top or bottom + /// + /// \return Returns true if the struct is valid + bool verify() const override; + + /// \brief Perform any additional structural modifications when setting this + /// object to TOP + void make_top_internal() override; + + /// Evaluates the index and tries to convert it to a constant integer + /// + /// \param index: the index expression showing where to access the array + /// \param env: the abstract environment + /// \param ns: the namespace + /// \param out_index: the index if it can be converted to a constant + /// + /// \return An abstract object pointer of type type().subtype() (i.e. the + /// type of the array's values). + virtual bool eval_index( + const index_exprt &index, + const abstract_environmentt &env, + const namespacet &ns, + mp_integer &out_index) const; + +private: + // Since we don't store for any index where the value is top + // we don't use a regular array but instead a map of array indices + // to the value at that index + struct mp_integer_hasht + { + size_t operator()(const mp_integer &i) const + { + return std::hash{}(i.to_ulong()); + } + }; + + typedef sharing_mapt< + mp_integer, + abstract_object_pointert, + false, + mp_integer_hasht> + shared_array_mapt; + + shared_array_mapt map; + + /// Short hand method for creating a top element of the array + /// + /// \param env: the abstract environment + /// \param ns: the namespace + /// + /// \return An abstract object pointer of type type().subtype() (i.e. the + /// type of the array's values). + /// + abstract_object_pointert + get_top_entry(const abstract_environmentt &env, const namespacet &ns) const; + + /// Merges an array into this array + /// + /// \param other: The object to merge in + /// + /// \return Returns a new abstract object that is the result of the merge + /// unless the merge is the same as this abstract object, in which + /// case it returns this.. + abstract_object_pointert + constant_array_merge(const constant_array_pointert other) const; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_ARRAY_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/constant_pointer_abstract_object.cpp b/src/analyses/variable-sensitivity/constant_pointer_abstract_object.cpp new file mode 100644 index 00000000000..cb6582dce39 --- /dev/null +++ b/src/analyses/variable-sensitivity/constant_pointer_abstract_object.cpp @@ -0,0 +1,235 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include + +#include "constant_pointer_abstract_object.h" +#include +#include +#include +#include + +constant_pointer_abstract_objectt::constant_pointer_abstract_objectt( + const typet &type) + : pointer_abstract_objectt(type) +{ + PRECONDITION(type.id() == ID_pointer); +} + +constant_pointer_abstract_objectt::constant_pointer_abstract_objectt( + const typet &type, + bool top, + bool bottom) + : pointer_abstract_objectt(type, top, bottom) +{ + PRECONDITION(type.id() == ID_pointer); +} + +constant_pointer_abstract_objectt::constant_pointer_abstract_objectt( + const constant_pointer_abstract_objectt &old) + : pointer_abstract_objectt(old), value_stack(old.value_stack) +{ +} + +constant_pointer_abstract_objectt::constant_pointer_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : pointer_abstract_objectt(expr, environment, ns), + value_stack(expr, environment, ns) +{ + PRECONDITION(expr.type().id() == ID_pointer); + if(value_stack.is_top_value()) + { + make_top(); + } + else + { + clear_top(); + } +} + +abstract_object_pointert +constant_pointer_abstract_objectt::merge(abstract_object_pointert other) const +{ + auto cast_other = + std::dynamic_pointer_cast(other); + if(cast_other) + { + return merge_constant_pointers(cast_other); + } + else + { + // TODO(tkiley): How do we set the result to be toppish? + return pointer_abstract_objectt::merge(other); + } +} + +abstract_object_pointert +constant_pointer_abstract_objectt::merge_constant_pointers( + const constant_pointer_abstract_pointert other) const +{ + if(is_bottom()) + { + return std::make_shared(*other); + } + else + { + bool matching_pointer = + value_stack.to_expression() == other->value_stack.to_expression(); + + if(matching_pointer) + { + return shared_from_this(); + } + else + { + return pointer_abstract_objectt::merge(other); + } + } +} + +exprt constant_pointer_abstract_objectt::to_constant() const +{ + if(is_top() || is_bottom()) + { + return pointer_abstract_objectt::to_constant(); + } + else + { + // TODO(tkiley): I think we would like to eval this before using it + // in the to_constant. + return value_stack.to_expression(); + } +} + +void constant_pointer_abstract_objectt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(is_top() || is_bottom() || value_stack.is_top_value()) + { + pointer_abstract_objectt::output(out, ai, ns); + } + else + { + out << "ptr ->("; + const exprt &value = value_stack.to_expression(); + if(value.id() == ID_address_of) + { + const auto &addressee = to_address_of_expr(value).object(); + if(addressee.id() == ID_symbol) + { + const symbol_exprt &symbol_pointed_to(to_symbol_expr(addressee)); + + out << symbol_pointed_to.get_identifier(); + } + else if(addressee.id() == ID_index) + { + auto const &array_index = to_index_expr(addressee); + auto const &array = array_index.array(); + if(array.id() == ID_symbol) + { + auto const &array_symbol = to_symbol_expr(array); + out << array_symbol.get_identifier() << "["; + if(array_index.index().id() == ID_constant) + out << to_constant_expr(array_index.index()).get_value(); + else + out << "?"; + out << "]"; + } + } + } + + out << ")"; + } +} + +abstract_object_pointert constant_pointer_abstract_objectt::read_dereference( + const abstract_environmentt &env, + const namespacet &ns) const +{ + if(is_top() || is_bottom() || value_stack.is_top_value()) + { + // Return top if dereferencing a null pointer or we are top + bool is_value_top = is_top() || value_stack.is_top_value(); + return env.abstract_object_factory( + type().subtype(), ns, is_value_top, !is_value_top); + } + else + { + return env.eval( + to_address_of_expr(value_stack.to_expression()).object(), ns); + } +} + +sharing_ptrt +constant_pointer_abstract_objectt::write_dereference( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const abstract_object_pointert new_value, + bool merging_write) const +{ + if(is_top() || is_bottom() || value_stack.is_top_value()) + { + return pointer_abstract_objectt::write_dereference( + environment, ns, stack, new_value, merging_write); + } + else + { + if(stack.empty()) + { + // We should not be changing the type of an abstract object + PRECONDITION(new_value->type() == ns.follow(type().subtype())); + + // Get an expression that we can assign to + exprt value = to_address_of_expr(value_stack.to_expression()).object(); + if(merging_write) + { + abstract_object_pointert pointed_value = environment.eval(value, ns); + bool modifications; + abstract_object_pointert merged_value = + abstract_objectt::merge(pointed_value, new_value, modifications); + environment.assign(value, merged_value, ns); + } + else + { + environment.assign(value, new_value, ns); + } + } + else + { + exprt value = to_address_of_expr(value_stack.to_expression()).object(); + abstract_object_pointert pointed_value = environment.eval(value, ns); + abstract_object_pointert modified_value = + environment.write(pointed_value, new_value, stack, ns, merging_write); + environment.assign(value, modified_value, ns); + + // but the pointer itself does not change! + } + return std::dynamic_pointer_cast( + shared_from_this()); + } +} + +void constant_pointer_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + pointer_abstract_objectt::get_statistics(statistics, visited, env, ns); + // don't bother following nullptr + if(!is_top() && !is_bottom() && !value_stack.is_top_value()) + { + read_dereference(env, ns)->get_statistics(statistics, visited, env, ns); + } + statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this)); +} diff --git a/src/analyses/variable-sensitivity/constant_pointer_abstract_object.h b/src/analyses/variable-sensitivity/constant_pointer_abstract_object.h new file mode 100644 index 00000000000..e81eac82737 --- /dev/null +++ b/src/analyses/variable-sensitivity/constant_pointer_abstract_object.h @@ -0,0 +1,143 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// An abstraction of a pointer that tracks a single pointer +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_POINTER_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_POINTER_ABSTRACT_OBJECT_H + +#include + +#include +#include + +class constant_pointer_abstract_objectt : public pointer_abstract_objectt +{ +private: + typedef sharing_ptrt + constant_pointer_abstract_pointert; + +public: + /// \param type: the type the abstract_object is representing + explicit constant_pointer_abstract_objectt(const typet &type); + + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + /// + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + constant_pointer_abstract_objectt(const typet &type, bool top, bool bottom); + + /// \param old: the abstract object to copy from + constant_pointer_abstract_objectt( + const constant_pointer_abstract_objectt &old); + + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: the environment in which we evaluate expr + /// \param ns: the current namespace + constant_pointer_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /// To try and find a constant expression for this abstract object + /// + /// \return Returns an expression representing the value if it can. + /// Returns a nil expression if it can be more than one value. + /// Returns null_pointer expression if it must be null + /// Returns an address_of_exprt with the value set to the + /// result of to_constant called on whatever abstract object this + /// pointer is pointing to. + /// + exprt to_constant() const override; + + /// Print the value of the pointer. Either NULL if nullpointer or + /// ptr -> ( output of what the pointer is pointing to). + /// + /// \param out: the stream to write to + /// \param ai: the domain in which this object appears + /// given as ai_baset so that the interface is the same + /// across all domains + /// \param ns: the current namespace + void output(std::ostream &out, const ai_baset &ai, const namespacet &ns) + const override; + + /// A helper function to dereference a value from a pointer. Providing + /// the pointer can only be pointing at one thing, returns an abstract + /// object representing that thing. If null or top will return top. + /// + /// \param env: the environment + /// \param ns: the namespace + /// + /// \return An abstract object representing the value this pointer is pointing + /// to + abstract_object_pointert read_dereference( + const abstract_environmentt &env, + const namespacet &ns) const override; + + /// A helper function to evaluate writing to a pointers value. + /// If the pointer can only be pointing to one element that it overwrites + /// that element (or merges if merging_write) with the new value. + /// If don't know what we are pointing to, we delegate to the parent. + /// + /// \param environment: the environment + /// \param ns: the namespace + /// \param stack: the remaining stack + /// \param value: the value to write to the dereferenced pointer + /// \param merging_write: is it a merging write (i.e. we aren't certain + /// we are writing to this particular pointer therefore + /// the value should be merged with whatever is already + /// there or we are certain we are writing to this + /// pointer so therefore the value can be replaced + /// + /// \return A modified abstract object representing this pointer after it + /// has been written to. + sharing_ptrt write_dereference( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const abstract_object_pointert value, + bool merging_write) const override; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + +protected: + /// Set this abstract object to be the result of merging this + /// abstract object. This calls the merge_constant_pointers if + /// we are trying to merge a constant pointer we use the constant pointer + /// constant pointer merge + /// + /// \param op1: the pointer being merged + /// + /// \return Returns the result of the merge. + abstract_object_pointert merge(abstract_object_pointert op1) const override; + + CLONE + +private: + /// Merges two constant pointers. If they are pointing at the same + /// value, we merge, otherwise we set to top. + /// + /// \param other: the pointer being merged + /// + /// \return Returns a new abstract object that is the result of the merge + /// unless the merge is the same as this abstract object, in which + /// case it returns this. + abstract_object_pointert + merge_constant_pointers(const constant_pointer_abstract_pointert other) const; + + write_stackt value_stack; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONSTANT_POINTER_ABSTRACT_OBJECT_H // NOLINT(*) diff --git a/src/analyses/variable-sensitivity/context_abstract_object.cpp b/src/analyses/variable-sensitivity/context_abstract_object.cpp new file mode 100644 index 00000000000..b8d5dda02d3 --- /dev/null +++ b/src/analyses/variable-sensitivity/context_abstract_object.cpp @@ -0,0 +1,187 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity context_abstract_object + + Author: Diffblue Ltd + +\*******************************************************************/ + +#include "context_abstract_object.h" + +abstract_object_pointert context_abstract_objectt::get_child() const +{ + return child_abstract_object; +} + +void context_abstract_objectt::set_child(const abstract_object_pointert &child) +{ + child_abstract_object = child; +} + +void context_abstract_objectt::make_top_internal() +{ + if(!child_abstract_object->is_top()) + set_child(child_abstract_object->make_top()); +} + +void context_abstract_objectt::clear_top_internal() +{ + if(child_abstract_object->is_top()) + set_child(child_abstract_object->clear_top()); +} + +/** + * A helper function to evaluate an abstract object contained + * within a container object. More precise abstractions may override this + * to return more precise results. + * + * \param env the abstract environment + * \param specifier a modifier expression, such as an array index or field + * specifier used to indicate access to a specific component + * \param ns the current namespace + * + * \return the abstract_objectt representing the value of the read component. + * For the dependency context, the operation is simply delegated to the + * child object + */ +abstract_object_pointert context_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return child_abstract_object->read(env, specifier, ns); +} + +/** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ +abstract_object_pointert context_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + abstract_object_pointert updated_child = child_abstract_object->write( + environment, ns, stack, specifier, value, merging_write); + + // Only perform an update if the write to the child has in fact changed it... + if(updated_child == child_abstract_object) + return shared_from_this(); + + // Need to ensure the result of the write is still wrapped in a context + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + // Update the child and record the updated write locations + result->set_child(updated_child); + + return result; +} + +/** + * Try to resolve an expression with the maximum level of precision + * available. + * + * \param expr the expression to evaluate and find the result of. This will + * be the symbol referred to be op0() + * \param operands: the operands to use instead of expr.operands() + * \param environment the abstract environment in which to resolve 'expr' + * \param ns the current namespace + * + * \return the resolved expression + */ +abstract_object_pointert context_abstract_objectt::expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + PRECONDITION(expr.operands().size() == operands.size()); + + std::vector child_operands; + + std::transform( + operands.begin(), + operands.end(), + std::back_inserter(child_operands), + [](const abstract_object_pointert &op) { + PRECONDITION(op != nullptr); + auto p = std::dynamic_pointer_cast(op); + INVARIANT(p, "Operand shall be of type context_abstract_objectt"); + return p->child_abstract_object; + }); + + return child_abstract_object->expression_transform( + expr, child_operands, environment, ns); +} + +/** + * Output a representation of the value of this abstract object + * + * \param out the stream to write to + * \param ai the abstract interpreter that contains the abstract domain + * (that contains the object ... ) + * \param ns the current namespace + */ +void context_abstract_objectt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + child_abstract_object->output(out, ai, ns); +} + +/** + * Determine whether 'this' abstract_object has been modified in comparison + * to a previous 'before' state. + * \param before The abstract_object_pointert to use as a reference to + * compare against + * \return true if 'this' is considered to have been modified in comparison + * to 'before', false otherwise. + */ +bool context_abstract_objectt::has_been_modified( + const abstract_object_pointert before) const +{ + // Default implementation, with no other information to go on + // falls back to relying on copy-on-write and pointer inequality + // to indicate if an abstract_objectt has been modified + auto before_context = + std::dynamic_pointer_cast(before); + + return this->child_abstract_object.get() != + before_context->child_abstract_object.get(); +} + +abstract_object_pointert context_abstract_objectt::unwrap_context() const +{ + return child_abstract_object->unwrap_context(); +} + +void context_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + abstract_objectt::get_statistics(statistics, visited, env, ns); + if(visited.find(child_abstract_object) == visited.end()) + { + child_abstract_object->get_statistics(statistics, visited, env, ns); + } + statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this)); +} diff --git a/src/analyses/variable-sensitivity/context_abstract_object.h b/src/analyses/variable-sensitivity/context_abstract_object.h new file mode 100644 index 00000000000..c86868ca50f --- /dev/null +++ b/src/analyses/variable-sensitivity/context_abstract_object.h @@ -0,0 +1,126 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity context_abstract_object + + Author: Diffblue Ltd + +\*******************************************************************/ +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONTEXT_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONTEXT_ABSTRACT_OBJECT_H + +#include + +/** + * \file + * General implementation of a an abstract_objectt which can track + * side information in the form of a 'context' associated with a wrapped + * abstract_objectt of some other type. This class is not intended to be + * instantiated directly - instead it is intended to be inherited from for + * other classes to define what the context actually means. + */ +class context_abstract_objectt : public abstract_objectt +{ +public: + // These constructors mirror those in the base abstract_objectt, but with + // the addition of an extra argument which is the abstract_objectt to wrap. + explicit context_abstract_objectt( + const abstract_object_pointert child, + const typet &type) + : abstract_objectt(type) + { + child_abstract_object = child; + } + + context_abstract_objectt( + const abstract_object_pointert child, + const typet &type, + bool top, + bool bottom) + : abstract_objectt(type, top, bottom) + { + child_abstract_object = child; + } + + explicit context_abstract_objectt( + const abstract_object_pointert child, + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_objectt(expr, environment, ns) + { + child_abstract_object = child; + } + + virtual ~context_abstract_objectt() + { + } + + virtual const typet &type() const + { + return child_abstract_object->type(); + } + + bool is_top() const override + { + return child_abstract_object->is_top(); + } + + bool is_bottom() const override + { + return child_abstract_object->is_bottom(); + } + + exprt to_constant() const override + { + return child_abstract_object->to_constant(); + } + + abstract_object_pointert expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const override; + + void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) + const override; + + abstract_object_pointert unwrap_context() const override; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + + abstract_object_pointert get_child() const; + +protected: + CLONE + + // The abstract_objectt that will be wrapped in a context + abstract_object_pointert child_abstract_object; + + void set_child(const abstract_object_pointert &child); + + // These are internal hooks that allow sub-classes to perform additional + // actions when an abstract_object is set/unset to TOP + void make_top_internal() override; + void clear_top_internal() override; + + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + bool has_been_modified(const abstract_object_pointert before) const override; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_CONTEXT_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/data_dependency_context.cpp b/src/analyses/variable-sensitivity/data_dependency_context.cpp new file mode 100644 index 00000000000..ea3072d3d7c --- /dev/null +++ b/src/analyses/variable-sensitivity/data_dependency_context.cpp @@ -0,0 +1,366 @@ +/*******************************************************************\ + +Module: analyses variable-sensitivity data_dependency_context + +Author: Diffblue Ltd + +\*******************************************************************/ + +/** + * \file + * Maintain data dependencies as a context in the variable sensitivity domain + */ + +#include + +#include "data_dependency_context.h" + +/** + * Determine whether 'this' abstract_object has been modified in comparison + * to a previous 'before' state. + * + * \param before the abstract_object_pointert to use as a reference to + * compare against + * + * \return true if 'this' is considered to have been modified in comparison + * to 'before', false otherwise. + */ +bool data_dependency_contextt::has_been_modified( + const abstract_object_pointert before) const +{ + if(this->write_location_contextt::has_been_modified(before)) + return true; + + auto cast_before = + std::dynamic_pointer_cast(before); + + if(!cast_before) + { + // The other context is not something we understand, so must assume + // that the abstract_object has been modified + return true; + } + + // Check whether the data dependencies have changed as well + abstract_objectt::locationst intersection; + std::set_intersection( + data_deps.cbegin(), + data_deps.cend(), + cast_before->data_deps.cbegin(), + cast_before->data_deps.cend(), + std::inserter(intersection, intersection.end()), + location_ordert()); + bool all_matched = intersection.size() == data_deps.size() && + intersection.size() == cast_before->data_deps.size(); + + if(!all_matched) + return true; + + intersection.clear(); + std::set_intersection( + data_dominators.cbegin(), + data_dominators.cend(), + cast_before->data_dominators.cbegin(), + cast_before->data_dominators.cend(), + std::inserter(intersection, intersection.end()), + location_ordert()); + + all_matched = intersection.size() == data_dominators.size() && + intersection.size() == cast_before->data_dominators.size(); + + return !all_matched; +} + +/** + * Insert the given set of data dependencies into the data dependencies set + * for this data_dependency_context object. + * + * \param dependencies the set of dependencies to add + * \return a new data_dependency_context if new dependencies were added, + * or 'this' if no addtional dependencies were added. + */ +abstract_object_pointert data_dependency_contextt::insert_data_deps( + const dependencest &dependencies) const +{ + // If this is the first write to the context then it is also used as + // the initial set of data dependency dominators as well. + const bool first_write = data_deps.empty(); + dependencest new_dependencies; + + // Workout what new data dependencies need to be inserted + if(first_write) + { + new_dependencies = dependencies; + } + else + { + std::set_difference( + dependencies.begin(), + dependencies.end(), + data_deps.begin(), + data_deps.end(), + std::inserter(new_dependencies, new_dependencies.begin()), + location_ordert{}); + } + + // If there are no new dependencies to add, just return + if(new_dependencies.empty()) + return shared_from_this(); + + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + for(auto l : new_dependencies) + { + result->data_deps.insert(l); + } + + if(first_write) + { + // If this was the first insertion of any dependencies, then these + // data dependencies are also data dominators as well + for(auto l : new_dependencies) + { + result->data_dominators.insert(l); + } + } + return result; +} + +/** + * Set the given set of data dependencies for this data_dependency_context + * object. + * + * \param dependencies the set of dependencies to set + * \return a new data_dependency_context if new dependencies were set, + * or 'this' if the dependencies were not changed. + */ +abstract_object_pointert +data_dependency_contextt::set_data_deps(const dependencest &dependencies) const +{ + // If the dependencies will not change, just return 'this' + abstract_objectt::locationst intersection; + + std::set_intersection( + data_deps.cbegin(), + data_deps.cend(), + dependencies.cbegin(), + dependencies.cend(), + std::inserter(intersection, intersection.end()), + location_ordert()); + if( + intersection.size() == data_deps.size() && + intersection.size() == dependencies.size()) + return shared_from_this(); + + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + result->data_deps = dependencies; + + // If this is the first write to the context then it is also used as + // the initial set of data dependency dominators as well. + if(data_deps.empty()) + { + result->data_dominators = dependencies; + } + return result; +} + +/** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ +abstract_object_pointert data_dependency_contextt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + const auto updated_parent = + std::dynamic_pointer_cast( + this->write_location_contextt::write( + environment, ns, stack, specifier, value, merging_write)); + + const auto cast_value = + std::dynamic_pointer_cast(value); + + return updated_parent->set_data_deps(cast_value->data_deps); +} + +/** + * Update the location context for an abstract object, potentially + * propogating the update to any children of this abstract object. + * + * \param locations the set of locations to be updated + * \param update_sub_elements if true, propogate the update operation to any + * children of this abstract object + * + * \return a clone of this abstract object with its location context + * updated + */ +abstract_object_pointert data_dependency_contextt::update_location_context( + const abstract_objectt::locationst &locations, + const bool update_sub_elements) const +{ + const auto updated_parent = + std::dynamic_pointer_cast( + this->write_location_contextt::update_location_context( + locations, update_sub_elements)); + + return updated_parent->set_data_deps(locations); +} + +/** + * Create a new abstract object that is the result of merging this abstract + * object with a given abstract_object + * + * \param other the abstract object to merge with + * + * \return the result of the merge, or 'this' if the merge would not change + * the current abstract object + */ +abstract_object_pointert +data_dependency_contextt::merge(abstract_object_pointert other) const +{ + auto cast_other = + std::dynamic_pointer_cast(other); + + if(cast_other) + { + const auto merged_parent = + std::dynamic_pointer_cast( + this->write_location_contextt::merge(other)); + + const auto updated_parent = + std::dynamic_pointer_cast( + merged_parent->insert_data_deps(cast_other->data_deps)); + + const auto &result = std::dynamic_pointer_cast( + updated_parent->mutable_clone()); + + // On a merge, data_dominators are the intersection of this object and the + // other object. In other words, the dominators at this merge point are + // those dominators that exist in all possible execution paths to this + // merge point. + result->data_dominators.clear(); + std::set_intersection( + data_dominators.begin(), + data_dominators.end(), + cast_other->data_dominators.begin(), + cast_other->data_dominators.end(), + std::inserter(result->data_dominators, result->data_dominators.end()), + location_ordert()); + + // It is critically important that we only return a newly constructed result + // abstract object *iff* the data has actually changed, otherwise AI may + // never reach a fixpoint + if(has_been_modified(result)) + return result; + else + return shared_from_this(); + } + + return abstract_objectt::merge(other); +} + +/** + * Helper function for abstract_objectt::abstract_object_merge to perform any + * additional actions after the base abstract_object_merge has completed its + * actions but immediately prior to it returning. As such, this function gives + * the ability to perform additional work for a merge. + * + * For the dependency context, this additional work is the tracking of + * last_written_locations across the merge + * + * \param other the object to merge with this + * + * \return the result of the merge + */ +abstract_object_pointert +data_dependency_contextt::abstract_object_merge_internal( + const abstract_object_pointert other) const +{ + auto other_context = + std::dynamic_pointer_cast(other); + + if(other_context) + { + const auto merged_parent = + std::dynamic_pointer_cast( + this->write_location_contextt::abstract_object_merge_internal(other)); + + return merged_parent->insert_data_deps(other_context->data_deps); + } + return shared_from_this(); +} + +/** + * Return the set of data dependencies associated with this node + * + * \return set of data dependencies + */ +std::set +data_dependency_contextt::get_data_dependencies() const +{ + std::set result; + for(const auto &d : data_deps) + result.insert(d); + return result; +} + +/** + * Return the set of data dominators associated with this node + * + * \return set of data dominators + */ +std::set +data_dependency_contextt::get_data_dominators() const +{ + std::set result; + for(const auto &d : data_dominators) + result.insert(d); + return result; +} + +void data_dependency_contextt::output( + std::ostream &out, + const class ai_baset &ai, + const namespacet &ns) const +{ + this->write_location_contextt::output(out, ai, ns); + + out << "[Data dependencies: "; + + bool comma = false; + for(auto d : data_deps) + { + out << (comma ? ", " : "") << d->location_number; + comma = true; + } + out << ']'; + + out << "[Data dominators: "; + + comma = false; + for(auto d : data_dominators) + { + out << (comma ? ", " : "") << d->location_number; + comma = true; + } + out << ']'; +} diff --git a/src/analyses/variable-sensitivity/data_dependency_context.h b/src/analyses/variable-sensitivity/data_dependency_context.h new file mode 100644 index 00000000000..26c824ac49d --- /dev/null +++ b/src/analyses/variable-sensitivity/data_dependency_context.h @@ -0,0 +1,116 @@ +/*******************************************************************\ + +Module: analyses variable-sensitivity data_dependency_context + +Author: Diffblue Ltd + +\*******************************************************************/ + +/** + * \file + * Maintain data dependencies as a context in the variable sensitivity domain + */ + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_DATA_DEPENDENCY_CONTEXT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_DATA_DEPENDENCY_CONTEXT_H + +#include "variable_sensitivity_domain.h" +#include "write_location_context.h" + +class data_dependency_contextt : public write_location_contextt +{ +public: + // These constructors mirror those in the base abstract_objectt, but with + // the addition of an extra argument which is the abstract_objectt to wrap. + explicit data_dependency_contextt( + const abstract_object_pointert child, + const typet &type) + : write_location_contextt(child, type) + { + } + + data_dependency_contextt( + const abstract_object_pointert child, + const typet &type, + bool top, + bool bottom) + : write_location_contextt(child, type, top, bottom) + { + } + + explicit data_dependency_contextt( + const abstract_object_pointert child, + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : write_location_contextt(child, expr, environment, ns) + { + } + + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + abstract_object_pointert update_location_context( + const abstract_objectt::locationst &locations, + const bool update_sub_elements) const override; + + bool has_been_modified(const abstract_object_pointert before) const override; + + std::set get_data_dependencies() const; + std::set get_data_dominators() const; + + void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) + const override; + +protected: + CLONE + + abstract_object_pointert merge(abstract_object_pointert other) const override; + + abstract_object_pointert abstract_object_merge_internal( + const abstract_object_pointert other) const override; + +private: + class location_ordert + { + public: + bool operator()( + goto_programt::const_targett instruction, + goto_programt::const_targett other_instruction) const + { + return instruction->location_number > other_instruction->location_number; + } + }; + typedef std::set dependencest; + dependencest data_deps; + dependencest data_dominators; + + abstract_object_pointert + insert_data_deps(const dependencest &dependencies) const; + + abstract_object_pointert + set_data_deps(const dependencest &dependencies) const; + + abstract_object_pointert insert_data_deps(const locationst &locations) const + { + // `locationst` is unsorted, so convert this to a sorted `dependenciest` + dependencest dependencies(locations.begin(), locations.end()); + + return insert_data_deps(dependencies); + } + + abstract_object_pointert set_data_deps(const locationst &locations) const + { + // `locationst` is unsorted, so convert this to a sorted `dependenciest` + dependencest dependencies(locations.begin(), locations.end()); + + return set_data_deps(dependencies); + } +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_DATA_DEPENDENCY_CONTEXT_H diff --git a/src/analyses/variable-sensitivity/full_struct_abstract_object.cpp b/src/analyses/variable-sensitivity/full_struct_abstract_object.cpp new file mode 100644 index 00000000000..7453fe5eb1e --- /dev/null +++ b/src/analyses/variable-sensitivity/full_struct_abstract_object.cpp @@ -0,0 +1,335 @@ +/*******************************************************************\ + +Module: Struct abstract object + +Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "full_struct_abstract_object.h" + +// #define DEBUG + +#ifdef DEBUG +# include +#endif + +full_struct_abstract_objectt::full_struct_abstract_objectt( + const full_struct_abstract_objectt &ao) + : struct_abstract_objectt(ao), map(ao.map) +{ +} + +full_struct_abstract_objectt::full_struct_abstract_objectt(const typet &t) + : struct_abstract_objectt(t) +{ + PRECONDITION(t.id() == ID_struct); + DATA_INVARIANT(verify(), "Structural invariants maintained"); +} + +full_struct_abstract_objectt::full_struct_abstract_objectt( + const typet &t, + bool top, + bool bottom) + : struct_abstract_objectt(t, top, bottom) +{ + PRECONDITION(t.id() == ID_struct); + DATA_INVARIANT(verify(), "Structural invariants maintained"); +} + +full_struct_abstract_objectt::full_struct_abstract_objectt( + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) + : struct_abstract_objectt(e, environment, ns) +{ + PRECONDITION(ns.follow(e.type()).id() == ID_struct); + + const struct_typet struct_type_def = to_struct_type(ns.follow(e.type())); + + bool did_initialize_values = false; + auto struct_type_it = struct_type_def.components().begin(); + for(auto param_it = e.operands().begin(); param_it != e.operands().end(); + ++param_it, ++struct_type_it) + { + map.insert_or_replace( + struct_type_it->get_name(), + environment.abstract_object_factory(param_it->type(), *param_it, ns)); + did_initialize_values = true; + } + + if(did_initialize_values) + { + clear_top(); + } + + DATA_INVARIANT(verify(), "Structural invariants maintained"); +} + +abstract_object_pointert full_struct_abstract_objectt::read_component( + const abstract_environmentt &environment, + const member_exprt &member_expr, + const namespacet &ns) const +{ +#ifdef DEBUG + std::cout << "Reading component " << member_expr.get_component_name() << '\n'; +#endif + + if(is_top()) + { + return environment.abstract_object_factory(member_expr.type(), ns, true); + } + else + { + PRECONDITION(!is_bottom()); + + const irep_idt c = member_expr.get_component_name(); + + auto const value = map.find(c); + + if(value.has_value()) + { + return value.value(); + } + else + { + return environment.abstract_object_factory(member_expr.type(), ns, true); + } + } +} + +sharing_ptrt +full_struct_abstract_objectt::write_component( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack &stack, + const member_exprt &member_expr, + const abstract_object_pointert value, + bool merging_write) const +{ +#ifdef DEBUG + std::cout << "Writing component " << member_expr.get_component_name() << '\n'; +#endif + + if(is_bottom()) + { + return sharing_ptrt( + new full_struct_abstract_objectt( + member_expr.compound().type(), false, true)); + } + + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + if(!stack.empty()) + { + abstract_object_pointert starting_value; + const irep_idt c = member_expr.get_component_name(); + auto const old_value = map.find(c); + if(!old_value.has_value()) + { + starting_value = environment.abstract_object_factory( + member_expr.type(), ns, true, false); + result->map.insert( + c, environment.write(starting_value, value, stack, ns, merging_write)); + } + else + { + result->map.replace( + c, + environment.write(old_value.value(), value, stack, ns, merging_write)); + } + + result->clear_top(); + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + else + { +#ifdef DEBUG + std::cout << "Setting component" << std::endl; +#endif + + const irep_idt c = member_expr.get_component_name(); + auto const old_value = result->map.find(c); + + if(merging_write) + { + if(is_top()) // struct is top + { + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + + INVARIANT(!result->map.empty(), "If not top, map cannot be empty"); + + if(!old_value.has_value()) // component is top + { + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + + bool dummy; + + result->map.replace( + c, abstract_objectt::merge(old_value.value(), value, dummy)); + } + else + { + if(old_value.has_value()) + { + result->map.replace(c, value); + } + else + { + result->map.insert(c, value); + } + result->clear_top(); + INVARIANT(!result->is_bottom(), "top != bottom"); + } + + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + + return result; + } +} + +void full_struct_abstract_objectt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + // To ensure that a consistent ordering of fields is output, use + // the underlying type declaration for this struct to determine + // the ordering + struct_union_typet type_decl = to_struct_union_type(ns.follow(type())); + + bool first = true; + + out << "{"; + for(const auto &field : type_decl.components()) + { + auto value = map.find(field.get_name()); + if(value.has_value()) + { + if(!first) + { + out << ", "; + } + out << '.' << field.get_name() << '='; + static_cast(value.value()) + ->output(out, ai, ns); + first = false; + } + } + out << "}"; +} + +bool full_struct_abstract_objectt::verify() const +{ + // Either the object is top or bottom (=> map empty) + // or the map is not empty => neither top nor bottom + return (is_top() || is_bottom()) == map.empty(); +} + +abstract_object_pointert +full_struct_abstract_objectt::merge(abstract_object_pointert other) const +{ + constant_struct_pointert cast_other = + std::dynamic_pointer_cast(other); + if(cast_other) + { + return merge_constant_structs(cast_other); + } + else + { + // TODO(tkiley): How do we set the result to be toppish? Does it matter? + return struct_abstract_objectt::merge(other); + } +} + +abstract_object_pointert full_struct_abstract_objectt::merge_constant_structs( + constant_struct_pointert other) const +{ + if(is_bottom()) + { + return std::make_shared(*other); + } + else + { + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + bool modified = abstract_objectt::merge_shared_maps( + map, other->map, result->map); + + if(!modified) + { + DATA_INVARIANT(verify(), "Structural invariants maintained"); + return shared_from_this(); + } + else + { + INVARIANT(!result->is_top(), "Merge of maps will not generate top"); + INVARIANT(!result->is_bottom(), "Merge of maps will not generate bottom"); + DATA_INVARIANT(result->verify(), "Structural invariants maintained"); + return result; + } + } +} + +abstract_object_pointert full_struct_abstract_objectt::visit_sub_elements( + const abstract_object_visitort &visitor) const +{ + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + bool modified = false; + + shared_struct_mapt::viewt view; + result->map.get_view(view); + + for(auto &item : view) + { + auto newval = visitor.visit(item.second); + if(newval != item.second) + { + result->map.replace(item.first, newval); + modified = true; + } + } + + if(modified) + { + return result; + } + else + { + return shared_from_this(); + } +} + +void full_struct_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + struct_abstract_objectt::get_statistics(statistics, visited, env, ns); + shared_struct_mapt::viewt view; + map.get_view(view); + for(auto const &object : view) + { + if(visited.find(object.second) == visited.end()) + { + object.second->get_statistics(statistics, visited, env, ns); + } + } + statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this)); +} diff --git a/src/analyses/variable-sensitivity/full_struct_abstract_object.h b/src/analyses/variable-sensitivity/full_struct_abstract_object.h new file mode 100644 index 00000000000..c6a058ea9e2 --- /dev/null +++ b/src/analyses/variable-sensitivity/full_struct_abstract_object.h @@ -0,0 +1,168 @@ +/*******************************************************************\ + +Module: Struct abstract object + +Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// An abstraction of a structure that stores one abstract object per field +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_FULL_STRUCT_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_FULL_STRUCT_ABSTRACT_OBJECT_H + +#include +#include +#include +#include +#include + +class abstract_environmentt; +class member_exprt; + +class full_struct_abstract_objectt : public struct_abstract_objectt +{ +public: + typedef sharing_ptrt constant_struct_pointert; + + /** + * \brief Explicit copy-constructor to make it clear that the shared_map + * used to store the values of fields is copy-constructed as well + * to ensure it shares as much data as possible. + */ + full_struct_abstract_objectt(const full_struct_abstract_objectt &ao); + + /// \param type: the type the abstract_object is representing + explicit full_struct_abstract_objectt(const typet &type); + + /// Start the abstract object at either top or bottom or + /// neither asserts if both top and bottom are true + /// + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + full_struct_abstract_objectt(const typet &type, bool top, bool bottom); + + /// \param expr: the expression to use as the starting pointer for an + /// abstract object + /// \param environment: the environment in which we evaluate expr + /// \param ns: the current namespace + full_struct_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /// To provide a human readable string to the out representing + /// the current known value about this object. For this array we + /// print: { .component_name= + shared_struct_mapt; + shared_struct_mapt map; + + /// Performs an element wise merge of the map for each struct + /// + /// \param other: the other object being merged + /// + /// \return Returns a new abstract object that is the result of the merge + /// unless the merge is the same as this abstract object, in which + /// case it returns this. + abstract_object_pointert + merge_constant_structs(constant_struct_pointert other) const; + +protected: + CLONE + + /// A helper function to evaluate the abstract object contained + /// within a struct. More precise abstractions may override + /// this to return more precise results. + /// + /// \param environment: the abstract environment + /// \param member_expr: the expression uses to access a specific component + /// \param ns: the current namespace + /// + /// \return The abstract object representing the value of that + /// component. For this abstraction this will always be top + /// since we are not tracking the struct. + abstract_object_pointert read_component( + const abstract_environmentt &environment, + const member_exprt &member_expr, + const namespacet &ns) const override; + + /// A helper function to evaluate writing to a component of a + /// struct. More precise abstractions may override this to + /// update what they are storing for a specific component. + /// + /// \param environment: the abstract environment + /// \param ns: the current namespace + /// \param stack: the remaining stack of expressions on the LHS to evaluate + /// \param member_expr: the expression uses to access a specific component + /// \param value: the value we are trying to write to the component + /// \param merging_write: whether to over-write or to merge with the + /// current value. In other words is there + /// any certainty that this write will happen. + /// + /// \return The struct_abstract_objectt representing the result of + /// writing to a specific component. In this case this will + /// always be top as we are not tracking the value of this + /// struct. + sharing_ptrt write_component( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack &stack, + const member_exprt &member_expr, + const abstract_object_pointert value, + bool merging_write) const override; + + /// Function: full_struct_abstract_objectt::verify + /// + /// \return Returns true if the struct is valid + /// + /// To validate that the struct object is in a valid state. + /// This means either it is top or bottom, or if neither of those + /// then there exists something in the map of components. + /// If there is something in the map, then it can't be top or bottom + bool verify() const override; + + /// To merge an abstract object into this abstract object. If + /// the other is also a struct, we perform a constant_structs merge + /// Otherwise we call back to the parent merge. + /// + /// \param other: the other object being merged + /// + /// \return Returns the result of the merge. + abstract_object_pointert merge(abstract_object_pointert other) const override; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_FULL_STRUCT_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/interval_abstract_value.cpp b/src/analyses/variable-sensitivity/interval_abstract_value.cpp new file mode 100644 index 00000000000..b7800c415a9 --- /dev/null +++ b/src/analyses/variable-sensitivity/interval_abstract_value.cpp @@ -0,0 +1,558 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity intervals + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include +#include + +#include +#include + +#include "abstract_enviroment.h" + +#include "context_abstract_object.h" +#include "interval_abstract_value.h" + +static inline exprt look_through_casts(exprt e) +{ + while(e.id() == ID_typecast) + { + e = to_typecast_expr(e).op(); + } + return e; +} + +static inline bool +bvint_value_is_max(const typet &type, const mp_integer &value) +{ + PRECONDITION(type.id() == ID_signedbv || type.id() == ID_unsignedbv); + if(type.id() == ID_signedbv) + { + return to_signedbv_type(type).largest() == value; + } + else + { + return to_unsignedbv_type(type).largest() == value; + } +} + +static inline bool +bvint_value_is_min(const typet &type, const mp_integer &value) +{ + PRECONDITION(type.id() == ID_signedbv || type.id() == ID_unsignedbv); + if(type.id() == ID_signedbv) + { + return to_signedbv_type(type).smallest() == value; + } + else + { + return to_unsignedbv_type(type).smallest() == value; + } +} + +static inline constant_interval_exprt +interval_from_x_le_value(const exprt &value) +{ + return constant_interval_exprt(min_exprt(value.type()), value); +} + +static inline constant_interval_exprt +interval_from_x_ge_value(const exprt &value) +{ + return constant_interval_exprt(value, max_exprt(value.type())); +} + +static inline mp_integer force_value_from_expr(const exprt &value) +{ + PRECONDITION(constant_interval_exprt::is_int(value.type())); + optionalt maybe_integer_value = numeric_cast(value); + INVARIANT(maybe_integer_value.has_value(), "Input has to have a value"); + return maybe_integer_value.value(); +} + +static inline constant_interval_exprt +interval_from_x_lt_value(const exprt &value) +{ + mp_integer integer_value = force_value_from_expr(value); + if(!bvint_value_is_min(value.type(), integer_value)) + return constant_interval_exprt( + min_exprt(value.type()), from_integer(integer_value - 1, value.type())); + else + return constant_interval_exprt::bottom(value.type()); +} + +static inline constant_interval_exprt +interval_from_x_gt_value(const exprt &value) +{ + mp_integer integer_value = force_value_from_expr(value); + if(!bvint_value_is_max(value.type(), integer_value)) + return constant_interval_exprt( + from_integer(integer_value + 1, value.type()), max_exprt(value.type())); + else + return constant_interval_exprt::bottom(value.type()); +} + +static inline bool represents_interval(exprt e) +{ + e = look_through_casts(e); + return (e.id() == ID_constant_interval || e.id() == ID_constant); +} + +static inline constant_interval_exprt make_interval_expr(exprt e) +{ + e = look_through_casts(e); + if(e.id() == ID_constant_interval) + { + return to_constant_interval_expr(e); + } + else if(e.id() == ID_constant) + { + return constant_interval_exprt(e, e); + } + else + { + // not directly representable, so just return TOP + return constant_interval_exprt(e.type()); + } +} + +static inline irep_idt invert_relation(const irep_idt &relation) +{ + PRECONDITION( + relation == ID_le || relation == ID_lt || relation == ID_ge || + relation == ID_gt || relation == ID_equal); + if(relation == ID_le) + return ID_ge; + if(relation == ID_ge) + return ID_le; + if(relation == ID_lt) + return ID_gt; + if(relation == ID_gt) + return ID_lt; + return relation; +} + +/// Builds an interval representing all values satisfying the input expression. +/// The expression is expected to be a comparison between an integer constant +/// and a variable (symbol) +/// \param e the relation expression that should be satisfied +/// \return the constant interval expression representing the values +static inline constant_interval_exprt interval_from_relation(const exprt &e) +{ + PRECONDITION(e.operands().size() == 2); + const auto &relation = e.id(); + const auto &binary_e = to_binary_expr(e); + const auto &lhs = binary_e.lhs(); + const auto &rhs = binary_e.rhs(); + PRECONDITION( + relation == ID_le || relation == ID_lt || relation == ID_ge || + relation == ID_gt || relation == ID_equal); + PRECONDITION(lhs.id() == ID_constant || lhs.id() == ID_symbol); + PRECONDITION(rhs.id() == ID_constant || rhs.id() == ID_symbol); + PRECONDITION(lhs.id() != rhs.id()); + + const auto the_constant_part_of_the_relation = + (rhs.id() == ID_symbol ? lhs : rhs); + const auto maybe_inverted_relation = + (rhs.id() == ID_symbol ? invert_relation(relation) : relation); + + if(maybe_inverted_relation == ID_le) + return interval_from_x_le_value(the_constant_part_of_the_relation); + if(maybe_inverted_relation == ID_lt) + return interval_from_x_lt_value(the_constant_part_of_the_relation); + if(maybe_inverted_relation == ID_ge) + return interval_from_x_ge_value(the_constant_part_of_the_relation); + if(maybe_inverted_relation == ID_gt) + return interval_from_x_gt_value(the_constant_part_of_the_relation); + INVARIANT( + maybe_inverted_relation == ID_equal, "We excluded other cases above"); + return constant_interval_exprt( + the_constant_part_of_the_relation, the_constant_part_of_the_relation); +} + +interval_abstract_valuet::interval_abstract_valuet(typet t) + : abstract_valuet(t), interval(t) +{ +} + +interval_abstract_valuet::interval_abstract_valuet(typet t, bool tp, bool bttm) + : abstract_valuet(t, tp, bttm), interval(t) +{ +} + +interval_abstract_valuet::interval_abstract_valuet( + const constant_interval_exprt e) + : interval_abstract_valuet(e, 0) +{ +} + +exprt interval_abstract_valuet::to_constant() const +{ + // Attempt to reduce this interval to a constant expression + if(interval.is_single_value_interval()) + { + // Interval is the equivalent of a constant, so reduce it to a constant + return to_constant_expr(interval.get_lower()); + } + return abstract_objectt::to_constant(); +#if 0 + if(!is_top() && !is_bottom()) + { + return this->interval; + } + else + { + return abstract_objectt::to_constant(); + } +#endif +} + +abstract_object_pointert interval_abstract_valuet::expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + std::size_t num_operands = expr.operands().size(); + PRECONDITION(operands.size() == num_operands); + + std::vector> interval_operands; + interval_operands.reserve(num_operands); + + for(const auto &op : operands) + { + auto iav = std::dynamic_pointer_cast(op); + if(!iav) + { + // The operand isn't an interval - if it's an integral constant we can + // convert it into an interval. + + if(constant_interval_exprt::is_int(op->type())) + { + const auto op_as_constant = op->to_constant(); + if(op_as_constant.is_nil()) + { + auto top_object = + environment.abstract_object_factory(expr.type(), ns, true); + auto top_context_object = + std::dynamic_pointer_cast( + top_object); + CHECK_RETURN(top_context_object); + return top_context_object->get_child(); + } + const auto ivop = + environment.abstract_object_factory(op->type(), op_as_constant, ns); + const auto ivop_context = + std::dynamic_pointer_cast(ivop); + if(ivop_context) + { + iav = std::dynamic_pointer_cast( + ivop_context->get_child()); + } + else + iav = std::dynamic_pointer_cast(ivop); + } + CHECK_RETURN( + !std::dynamic_pointer_cast(iav)); + + if(!iav) + { + // If we could not convert the operand into an interval, + // e.g. if its type is not something we can represent as an interval, + // try dispatching the expression_transform under that type instead. + return op->expression_transform(expr, operands, environment, ns); + } + } + + INVARIANT(iav, "Should be an interval abstract value"); + interval_operands.push_back(iav); + } + + const typet &type = expr.type(); + + if(num_operands == 0) + return environment.abstract_object_factory(type, ns, true); + + if(expr.id() == ID_plus) + { + constant_exprt zero = constant_interval_exprt::zero(type); + constant_interval_exprt interval(zero); + INVARIANT(interval.is_zero(), "Starting interval must be zero"); + + for(const auto &iav : interval_operands) + { + const constant_interval_exprt &interval_next = iav->interval; + interval = interval.plus(interval_next); + } + + INVARIANT( + interval.type() == type, + "Type of result interval should match expression type"); + + return environment.abstract_object_factory(type, interval, ns); + } + else if(num_operands == 1) + { + const constant_interval_exprt &interval = interval_operands[0]->interval; + + if(expr.id() == ID_typecast) + { + const typecast_exprt &tce = to_typecast_expr(expr); + + const constant_interval_exprt &new_interval = + interval.typecast(tce.type()); + + INVARIANT( + new_interval.type() == type, + "Type of result interval should match expression type"); + + return environment.abstract_object_factory(tce.type(), new_interval, ns); + } + else + { + const constant_interval_exprt &interval_result = interval.eval(expr.id()); + INVARIANT( + interval_result.type() == type, + "Type of result interval should match expression type"); + return environment.abstract_object_factory(type, interval_result, ns); + } + } + else if(num_operands == 2) + { + const constant_interval_exprt &interval0 = interval_operands[0]->interval; + const constant_interval_exprt &interval1 = interval_operands[1]->interval; + + constant_interval_exprt interval = interval0.eval(expr.id(), interval1); + + INVARIANT( + interval.type() == type, + "Type of result interval should match expression type"); + + return environment.abstract_object_factory(type, interval, ns); + } + else if(num_operands == 3) + { + if(expr.id() == ID_if) + { + const constant_interval_exprt &condition_interval = + interval_operands[0]->interval; + const constant_interval_exprt &true_interval = + interval_operands[1]->interval; + const constant_interval_exprt &false_interval = + interval_operands[2]->interval; + + // Check the value of the condition interval + if(condition_interval.is_definitely_false().is_unknown()) + { + // Value of the condition is both true and false, so + // combine the intervals of both the true and false expressions + return environment.abstract_object_factory( + type, + constant_interval_exprt( + constant_interval_exprt::get_min( + true_interval.get_lower(), false_interval.get_lower()), + constant_interval_exprt::get_max( + true_interval.get_upper(), false_interval.get_upper())), + ns); + } + if(condition_interval.is_definitely_false().is_true()) + { + // The condition is definitely false, so return only + // the interval from the 'false' expression + return environment.abstract_object_factory( + false_interval.type(), false_interval, ns); + } + if(condition_interval.is_definitely_true().is_true()) + { + // The condition is definitely true, so return only + // the interval from the 'true' expression + return environment.abstract_object_factory( + true_interval.type(), true_interval, ns); + } + } + } + + return environment.abstract_object_factory(type, ns, true); +} + +void interval_abstract_valuet::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(!is_top() && !is_bottom()) + { + std::string lower_string; + std::string upper_string; + + if(interval.get_lower().id() == ID_min) + { + lower_string = "-INF"; + } + else + { + INVARIANT( + interval.get_lower().id() == ID_constant, + "We only support constant limits"); + lower_string = + id2string(to_constant_expr(interval.get_lower()).get_value()); + } + + if(interval.get_upper().id() == ID_max) + { + upper_string = "+INF"; + } + else + { + INVARIANT( + interval.get_lower().id() == ID_constant, + "We only support constant limits"); + upper_string = + id2string(to_constant_expr(interval.get_upper()).get_value()); + } + + out << "[" << lower_string << ", " << upper_string << "]"; + } + else + { + abstract_objectt::output(out, ai, ns); + } +} + +abstract_object_pointert +interval_abstract_valuet::merge(abstract_object_pointert other) const +{ + interval_abstract_value_pointert cast_other = + std::dynamic_pointer_cast(other); + if(cast_other) + { + return merge_intervals(cast_other); + } + else + { + return abstract_valuet::merge(other); + } +} + +/// Merge another interval abstract object with this one +/// \param other The interval abstract object to merge with +/// \return This if the other interval is subsumed by this, +/// other if this is subsumed by other. +/// Otherwise, a new interval abstract object +/// with the smallest interval that subsumes both +/// this and other +abstract_object_pointert interval_abstract_valuet::merge_intervals( + interval_abstract_value_pointert other) const +{ + if(is_bottom() || other->interval.contains(interval)) + { + return other; + } + else if(other->is_bottom() || interval.contains(other->interval)) + { + return shared_from_this(); + } + else + { + return std::make_shared( + constant_interval_exprt( + constant_interval_exprt::get_min( + interval.get_lower(), other->interval.get_lower()), + constant_interval_exprt::get_max( + interval.get_upper(), other->interval.get_upper())), + std::max(merge_count, other->merge_count) + 1); + } +} + +abstract_object_pointert +interval_abstract_valuet::meet(const abstract_object_pointert &other) const +{ + interval_abstract_value_pointert cast_other = + std::dynamic_pointer_cast(other); + if(cast_other) + { + return meet_intervals(cast_other); + } + else + { + return abstract_valuet::meet(other); + } +} + +/// Meet another interval abstract object with this one +/// \param other The interval abstract object to meet with +/// \return This if the other interval subsumes this, +/// other if this subsumes other. +/// Otherwise, a new interval abstract object +/// with the intersection interval (of this and other) +abstract_object_pointert interval_abstract_valuet::meet_intervals( + interval_abstract_value_pointert other) const +{ + if(is_bottom() || other->interval.contains(interval)) + { + return shared_from_this(); + } + else if(other->is_bottom() || interval.contains(other->interval)) + { + return other; + } + else + { + auto lower_bound = constant_interval_exprt::get_max( + interval.get_lower(), other->interval.get_lower()); + auto upper_bound = constant_interval_exprt::get_min( + interval.get_upper(), other->interval.get_upper()); + + if(constant_interval_exprt::less_than(upper_bound, lower_bound)) + return std::make_shared( + interval.type(), false, true); + return std::make_shared( + constant_interval_exprt(lower_bound, upper_bound), + std::max(merge_count, other->merge_count) + 1); + } +} + +interval_abstract_valuet::interval_abstract_valuet( + const exprt e, + const abstract_environmentt &environment, + const namespacet &ns) + : interval_abstract_valuet( + represents_interval(e) + ? make_interval_expr(e) + : (e.operands().size() == 2 ? interval_from_relation(e) + : constant_interval_exprt(e.type()))) +{ +} + +interval_abstract_valuet::interval_abstract_valuet( + const constant_interval_exprt e, + int merge_count) + : abstract_valuet(e.type(), e.is_top() || merge_count > 10, e.is_bottom()), + interval(e), + merge_count(merge_count) +{ +} + +const constant_interval_exprt &interval_abstract_valuet::get_interval() const +{ + return interval; +} + +void interval_abstract_valuet::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + abstract_valuet::get_statistics(statistics, visited, env, ns); + ++statistics.number_of_interval_abstract_objects; + if(interval.is_single_value_interval()) + { + ++statistics.number_of_single_value_intervals; + } + statistics.objects_memory_usage += memory_sizet::from_bytes(sizeof(*this)); +} diff --git a/src/analyses/variable-sensitivity/interval_abstract_value.h b/src/analyses/variable-sensitivity/interval_abstract_value.h new file mode 100644 index 00000000000..f0a1ebd8e83 --- /dev/null +++ b/src/analyses/variable-sensitivity/interval_abstract_value.h @@ -0,0 +1,96 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity intervals + + Author: Diffblue Ltd. + +\*******************************************************************/ + +/// \file +/// An interval to represent a set of possible values. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_INTERVAL_ABSTRACT_VALUE_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_INTERVAL_ABSTRACT_VALUE_H + +#include + +#include + +#include "abstract_object_statistics.h" +#include +#include + +class interval_abstract_valuet : public abstract_valuet +{ +private: + typedef sharing_ptrt + interval_abstract_value_pointert; + +public: + explicit interval_abstract_valuet(typet t); + interval_abstract_valuet(typet t, bool tp, bool bttm); + + interval_abstract_valuet(const constant_interval_exprt e, int merge_count); + + explicit interval_abstract_valuet(const constant_interval_exprt e); + + interval_abstract_valuet( + const exprt e, + const abstract_environmentt &environment, + const namespacet &ns); + + ~interval_abstract_valuet() override = default; + + exprt to_constant() const override; + + /// Interface for transforms + /// + /// \param expr: the expression to evaluate and find the result of it. + /// This will be the symbol referred to be op0() + /// \param operands: an abstract_object (pointer) that represent + /// the possible values of each operand + /// \param environment: the abstract environment in which the + /// expression is being evaluated + /// \param ns: the current variable namespace + /// + /// \return Returns the abstract_object representing the result of + /// this expression to the maximum precision available. + /// + /// Perform the interval transforms + abstract_object_pointert expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const override; + + void output( + std::ostream &out, + const class ai_baset &ai, + const class namespacet &ns) const override; + + const constant_interval_exprt &get_interval() const; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + +protected: + CLONE + abstract_object_pointert merge(abstract_object_pointert other) const override; + abstract_object_pointert + meet(const abstract_object_pointert &other) const override; + +private: + abstract_object_pointert + merge_intervals(interval_abstract_value_pointert other) const; + abstract_object_pointert + meet_intervals(interval_abstract_value_pointert other) const; + + constant_interval_exprt interval; + + int merge_count; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_INTERVAL_ABSTRACT_VALUE_H diff --git a/src/analyses/variable-sensitivity/interval_array_abstract_object.cpp b/src/analyses/variable-sensitivity/interval_array_abstract_object.cpp new file mode 100644 index 00000000000..e40225c8f47 --- /dev/null +++ b/src/analyses/variable-sensitivity/interval_array_abstract_object.cpp @@ -0,0 +1,165 @@ +/*******************************************************************\ + +Module: analyses variable-sensitivity interval-values arrays + +Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "interval_array_abstract_object.h" +#include "abstract_enviroment.h" +#include "interval_abstract_value.h" +#include + +interval_array_abstract_objectt::interval_array_abstract_objectt(typet type) + : constant_array_abstract_objectt(type) +{ +} + +interval_array_abstract_objectt::interval_array_abstract_objectt( + typet type, + bool top, + bool bottom) + : constant_array_abstract_objectt(type, top, bottom) +{ +} + +interval_array_abstract_objectt::interval_array_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : constant_array_abstract_objectt(expr, environment, ns) +{ +} + +static constant_interval_exprt eval_and_get_as_interval( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) +{ + auto evaluated_index = environment.eval(expr, ns); + auto evaluated_index_interval = + std::dynamic_pointer_cast( + evaluated_index->unwrap_context()); + INVARIANT( + evaluated_index_interval != nullptr, + "Expecting expression to evaluate to index"); + return evaluated_index_interval->get_interval(); +} + +sharing_ptrt +interval_array_abstract_objectt::write_index( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const index_exprt &index_expr, + const abstract_object_pointert value, + bool merging_write) const +{ + auto index_interval = + eval_and_get_as_interval(index_expr.index(), environment, ns); + + if(index_interval.is_single_value_interval()) + { + return constant_array_abstract_objectt::write_index( + environment, + ns, + stack, + index_exprt(index_expr.array(), index_interval.get_lower()), + value, + merging_write); + } + else if( + !index_interval.is_top() && !index_interval.is_bottom() && + index_interval.get_lower().id() != ID_min && + index_interval.get_upper().id() != ID_max) + { + auto ix = index_interval.get_lower(); + auto interval_end = index_interval.get_upper(); + sharing_ptrt result = shared_from_this(); + while(!result->is_top() && + simplify_expr(binary_predicate_exprt(ix, ID_gt, interval_end), ns) + .is_false()) + { + auto array_after_write_at_index = + constant_array_abstract_objectt::write_index( + environment, + ns, + stack, + index_exprt(index_expr.index(), ix), + value, + merging_write); + bool dontcare; + result = + abstract_objectt::merge(result, array_after_write_at_index, dontcare); + ix = simplify_expr(plus_exprt(ix, from_integer(1, ix.type())), ns); + } + return std::dynamic_pointer_cast(result); + } + return std::dynamic_pointer_cast(make_top()); +} + +abstract_object_pointert interval_array_abstract_objectt::read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const +{ + auto evaluated_index_value = eval_and_get_as_interval(index.index(), env, ns); + auto const &index_interval = to_constant_interval_expr(evaluated_index_value); + if( + !index_interval.is_top() && !index_interval.is_bottom() && + index_interval.get_lower().id() != ID_min && + index_interval.get_upper().id() != ID_max) + { + auto ix = index_interval.get_lower(); + auto interval_end = index_interval.get_upper(); + abstract_object_pointert value; + while((!value || !value->is_top()) && + simplify_expr(binary_relation_exprt(ix, ID_gt, interval_end), ns) + .is_false()) + { + auto value_at_index = constant_array_abstract_objectt::read_index( + env, index_exprt(index.array(), ix), ns); + if(value != nullptr) + { + bool dont_care; + value = abstract_objectt::merge(value, value_at_index, dont_care); + } + else + { + value = value_at_index; + } + ix = simplify_expr(plus_exprt(ix, from_integer(1, ix.type())), ns); + } + return value; + } + return env.abstract_object_factory(type().subtype(), ns); +} + +bool interval_array_abstract_objectt::eval_index( + const index_exprt &index, + const abstract_environmentt &env, + const namespacet &ns, + mp_integer &out_index) const +{ + auto index_interval = eval_and_get_as_interval(index.index(), env, ns); + if(index_interval.is_single_value_interval()) + { + out_index = + numeric_cast_v(to_constant_expr(index_interval.get_lower())); + return true; + } + return false; +} + +void interval_array_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + constant_array_abstract_objectt::get_statistics(statistics, visited, env, ns); + statistics.objects_memory_usage += memory_sizet::from_bytes( + // the size we add by inheriting + sizeof(*this) - sizeof(constant_array_abstract_objectt)); +} diff --git a/src/analyses/variable-sensitivity/interval_array_abstract_object.h b/src/analyses/variable-sensitivity/interval_array_abstract_object.h new file mode 100644 index 00000000000..e626742cd71 --- /dev/null +++ b/src/analyses/variable-sensitivity/interval_array_abstract_object.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: analyses variable-sensitivity interval-values arrays + +Author: Diffblue Ltd. + +\*******************************************************************/ + +/// \file +/// An abstraction of an array using intervals +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_INTERVAL_ARRAY_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_INTERVAL_ARRAY_ABSTRACT_OBJECT_H + +#include "constant_array_abstract_object.h" + +class interval_array_abstract_objectt : public constant_array_abstract_objectt +{ +public: + explicit interval_array_abstract_objectt(typet type); + + interval_array_abstract_objectt(typet type, bool top, bool bottom); + + interval_array_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + +protected: + CLONE + abstract_object_pointert read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const override; + + sharing_ptrt write_index( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const index_exprt &index_expr, + const abstract_object_pointert value, + bool merging_write) const override; + + bool eval_index( + const index_exprt &index, + const abstract_environmentt &env, + const namespacet &ns, + mp_integer &out_index) const override; + +public: + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_INTERVAL_ARRAY_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/module_dependencies.txt b/src/analyses/variable-sensitivity/module_dependencies.txt new file mode 100644 index 00000000000..36e4dbb826a --- /dev/null +++ b/src/analyses/variable-sensitivity/module_dependencies.txt @@ -0,0 +1,5 @@ +analyses +ansi-c +goto-programs +langapi # should go away +util diff --git a/src/analyses/variable-sensitivity/pointer_abstract_object.cpp b/src/analyses/variable-sensitivity/pointer_abstract_object.cpp new file mode 100644 index 00000000000..63b75a38c3f --- /dev/null +++ b/src/analyses/variable-sensitivity/pointer_abstract_object.cpp @@ -0,0 +1,96 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ +#include +#include + +#include + +#include "pointer_abstract_object.h" + +pointer_abstract_objectt::pointer_abstract_objectt(const typet &t) + : abstract_objectt(t) +{ + PRECONDITION(t.id() == ID_pointer); +} + +pointer_abstract_objectt::pointer_abstract_objectt( + const typet &type, + bool top, + bool bottom) + : abstract_objectt(type, top, bottom) +{ + PRECONDITION(type.id() == ID_pointer); +} + +pointer_abstract_objectt::pointer_abstract_objectt( + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_objectt(e, environment, ns) +{ + PRECONDITION(e.type().id() == ID_pointer); +} + +abstract_object_pointert pointer_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return read_dereference(env, ns); +} + +abstract_object_pointert pointer_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + return write_dereference(environment, ns, stack, value, merging_write); +} + +abstract_object_pointert pointer_abstract_objectt::read_dereference( + const abstract_environmentt &env, + const namespacet &ns) const +{ + pointer_typet pointer_type(to_pointer_type(type())); + const typet &pointed_to_type = pointer_type.subtype(); + + return env.abstract_object_factory(pointed_to_type, ns, true, false); +} + +sharing_ptrt +pointer_abstract_objectt::write_dereference( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const abstract_object_pointert value, + bool merging_write) const +{ + if(is_top() || is_bottom()) + { + environment.havoc("Writing to a 2value pointer"); + return std::dynamic_pointer_cast(clone()); + } + else + { + return sharing_ptrt( + new pointer_abstract_objectt(type(), true, false)); + } +} + +void pointer_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + abstract_objectt::get_statistics(statistics, visited, env, ns); + ++statistics.number_of_pointers; +} diff --git a/src/analyses/variable-sensitivity/pointer_abstract_object.h b/src/analyses/variable-sensitivity/pointer_abstract_object.h new file mode 100644 index 00000000000..e89bc3d13f9 --- /dev/null +++ b/src/analyses/variable-sensitivity/pointer_abstract_object.h @@ -0,0 +1,131 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// The base of all pointer abstractions +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_POINTER_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_POINTER_ABSTRACT_OBJECT_H + +#include + +#include + +class typet; +class constant_exprt; +class abstract_environmentt; + +class pointer_abstract_objectt : public abstract_objectt +{ +public: + /// \param type: the type the abstract_object is representing + explicit pointer_abstract_objectt(const typet &type); + + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + /// + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + pointer_abstract_objectt(const typet &type, bool top, bool bottom); + + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: the environment in which the pointer is being + /// created + /// \param ns: the current namespace + explicit pointer_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /** + * A helper function to evaluate an abstract object contained + * within a container object. More precise abstractions may override this + * to return more precise results. + * + * \param env the abstract environment + * \param specifier a modifier expression, such as an array index or field + * specifier used to indicate access to a specific component + * \param ns the current namespace + * + * \return the abstract_objectt representing the value of the read component. + */ + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + /** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + +protected: + CLONE + + /// A helper function to read elements from an array. More precise + /// abstractions may override this to provide more precise results. + /// + /// \param env: the environment + /// \param ns: the namespace + /// + /// \return An abstract object representing the value being pointed to + virtual abstract_object_pointert read_dereference( + const abstract_environmentt &env, + const namespacet &ns) const; + + /// A helper function to evaluate writing to a pointers value. More + /// precise abstractions may override this provide more precise results. + /// + /// \param environment: the abstract environment + /// \param ns: the namespace + /// \param stack: the remaining stack of expressions on the LHS to evaluate + /// \param value: the value we are trying to assign to what the pointer is + /// pointing to + /// \param merging_write: is it a merging write (i.e. we aren't certain + /// we are writing to this particular pointer therefore + /// the value should be merged with whatever is already + /// there or we are certain we are writing to this + /// pointer so therefore the value can be replaced + /// + /// \return A modified abstract object representing this pointer after it + /// has been written to. + virtual sharing_ptrt write_dereference( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const abstract_object_pointert value, + bool merging_write) const; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_POINTER_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/struct_abstract_object.cpp b/src/analyses/variable-sensitivity/struct_abstract_object.cpp new file mode 100644 index 00000000000..174a69c699d --- /dev/null +++ b/src/analyses/variable-sensitivity/struct_abstract_object.cpp @@ -0,0 +1,98 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include "struct_abstract_object.h" + +struct_abstract_objectt::struct_abstract_objectt(const typet &t) + : abstract_objectt(t) +{ + PRECONDITION(t.id() == ID_struct); +} + +struct_abstract_objectt::struct_abstract_objectt( + const typet &t, + bool tp, + bool bttm) + : abstract_objectt(t, tp, bttm) +{ + PRECONDITION(t.id() == ID_struct); +} + +struct_abstract_objectt::struct_abstract_objectt( + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_objectt(e, environment, ns) +{ + PRECONDITION(ns.follow(e.type()).id() == ID_struct); +} + +abstract_object_pointert struct_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return this->read_component(env, to_member_expr(specifier), ns); +} + +abstract_object_pointert struct_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + return this->write_component( + environment, ns, stack, to_member_expr(specifier), value, merging_write); +} + +abstract_object_pointert struct_abstract_objectt::read_component( + const abstract_environmentt &environment, + const member_exprt &member_expr, + const namespacet &ns) const +{ + // If we are bottom then so are the components + // otherwise the components could be anything + return environment.abstract_object_factory( + member_expr.type(), ns, !is_bottom(), is_bottom()); +} + +sharing_ptrt struct_abstract_objectt::write_component( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack &stack, + const member_exprt &member_expr, + const abstract_object_pointert value, + bool merging_write) const +{ + if(is_top() || is_bottom()) + { + return std::dynamic_pointer_cast(clone()); + } + else + { + return sharing_ptrt( + new struct_abstract_objectt(type(), true, false)); + } +} + +void struct_abstract_objectt::get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const +{ + abstract_objectt::get_statistics(statistics, visited, env, ns); + ++statistics.number_of_structs; +} diff --git a/src/analyses/variable-sensitivity/struct_abstract_object.h b/src/analyses/variable-sensitivity/struct_abstract_object.h new file mode 100644 index 00000000000..d20d9ac2bbb --- /dev/null +++ b/src/analyses/variable-sensitivity/struct_abstract_object.h @@ -0,0 +1,137 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// The base of all structure abstractions + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_STRUCT_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_STRUCT_ABSTRACT_OBJECT_H + +#include +#include + +class abstract_environmentt; +class member_exprt; + +class struct_abstract_objectt : public abstract_objectt +{ +public: + /// \param type: the type the abstract_object is representing + explicit struct_abstract_objectt(const typet &type); + + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + /// + /// \return Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + struct_abstract_objectt(const typet &type, bool top, bool bottom); + + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: the environment in which the pointer is being + /// created + /// \param ns: the current namespace + explicit struct_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /** + * A helper function to evaluate an abstract object contained + * within a container object. More precise abstractions may override this + * to return more precise results. + * + * \param env the abstract environment + * \param specifier a modifier expression, such as an array index or field + * specifier used to indicate access to a specific component + * \param ns the current namespace + * + * \return the abstract_objectt representing the value of the read component. + */ + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + /** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + void get_statistics( + abstract_object_statisticst &statistics, + abstract_object_visitedt &visited, + const abstract_environmentt &env, + const namespacet &ns) const override; + +protected: + CLONE + + /// A helper function to evaluate the abstract object contained + /// within a struct. More precise abstractions may override this + /// to return more precise results. + /// + /// \param environment: the abstract environment + /// \param member_expr: the expression uses to access a specific component + /// \param ns: the current namespace + /// + /// \return The abstract object representing the value of that component. For + /// this abstraction this will always be top since we are not tracking + /// the struct. + /// + virtual abstract_object_pointert read_component( + const abstract_environmentt &environment, + const member_exprt &member_expr, + const namespacet &ns) const; + + /// A helper function to evaluate writing to a component of a struct. + /// More precise abstractions may override this to + /// update what they are storing for a specific component. + /// + /// \param environment: the abstract environment + /// \param ns: the current namespace + /// \param stack: the remaining stack of expressions on the LHS to evaluate + /// \param member_expr: the expression uses to access a specific component + /// \param value: the value we are trying to write to the component + /// \param merging_write: whether to over-write or to merge with the + /// current value. In other words is there + /// any certainty that this write will happen. + /// + /// \return The struct_abstract_objectt representing the result of writing + /// to a specific component. In this case this will always be top + /// as we are not tracking the value of this struct. + virtual sharing_ptrt write_component( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack &stack, + const member_exprt &member_expr, + const abstract_object_pointert value, + bool merging_write) const; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_STRUCT_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/three_way_merge_abstract_interpreter.cpp b/src/analyses/variable-sensitivity/three_way_merge_abstract_interpreter.cpp new file mode 100644 index 00000000000..0a63c04995d --- /dev/null +++ b/src/analyses/variable-sensitivity/three_way_merge_abstract_interpreter.cpp @@ -0,0 +1,157 @@ +/*******************************************************************\ + +Module: Variable sensitivity domain + +Author: Martin Brain, martin.brain@cs.ox.ac.uk + +Date: August 2020 + +\*******************************************************************/ + +/// \file +/// An abstract interpreter, based on the default recursive-interprocedural +/// that uses variable sensitivity domain's three-way-merge operation on +/// returning from a function call. This gives some of the precision of +/// context-sensitive analysis but at a fraction of the cost. The really +/// key thing is that it distinguishes between variables that are modified in +/// the function (or things called from it) and those that appear modified +/// because they have different values at different call sites. This is +/// especially important for preserving the value of (unmodified) global +/// variables. + +#include +#include + +bool ai_three_way_merget::visit_edge_function_call( + const irep_idt &calling_function_id, + trace_ptrt p_call, + locationt l_return, + const irep_idt &callee_function_id, + working_sett &working_set, + const goto_programt &callee, + const goto_functionst &goto_functions, + const namespacet &ns) +{ + // There are four locations that matter. + locationt l_call_site = p_call->current_location(); + locationt l_callee_start = callee.instructions.begin(); + locationt l_callee_end = --callee.instructions.end(); + locationt l_return_site = l_return; + + PRECONDITION(l_call_site->is_function_call()); + DATA_INVARIANT( + l_callee_end->is_end_function(), + "The last instruction of a goto_program must be END_FUNCTION"); + + // Histories for call_site and callee_start are easy + trace_ptrt p_call_site = p_call; + + auto next = p_call_site->step( + l_callee_start, + *(storage->abstract_traces_before(l_callee_start)), + ai_history_baset::no_caller_history); + if(next.first == ai_history_baset::step_statust::BLOCKED) + { + // Unexpected... + // We can't three-way merge without a callee start so + return false; + } + trace_ptrt p_callee_start = next.second; + + // Handle the function call recursively + { + working_sett catch_working_set; // The starting trace for the next fixpoint + + // Do the edge from the call site to the beginning of the function + // (This will compute p_callee_start but that is OK) + bool new_data = visit_edge( + calling_function_id, + p_call, + callee_function_id, + l_callee_start, + ai_history_baset:: + no_caller_history, // Not needed as p_call already has the info + ns, + catch_working_set); + + // do we need to do/re-do the fixedpoint of the body? + if(new_data) + fixedpoint( + get_next(catch_working_set), // Should be p_callee_start... + callee_function_id, + callee, + goto_functions, + ns); + } + + // Now we can give histories for the return part + + // Find the histories at the end of the function + auto traces = storage->abstract_traces_before(l_callee_end); + + bool new_data = false; // Whether we have changed a domain in the caller + + // As with recursive-interprocedural, there are potentially multiple histories + // at the end, or maybe none. Only some of these will be 'descendents' of + // p_call_site and p_callee_start + for(auto p_callee_end : *traces) + { + // First off, is it even reachable? + const statet &s_callee_end = get_state(p_callee_end); + + if(s_callee_end.is_bottom()) + continue; // Unreachable in callee -- no need to merge + + // Can it return to p_call_site? + auto return_step = p_callee_end->step( + l_return_site, + *(storage->abstract_traces_before(l_return_site)), + p_call_site); // Because it is a return edge! + if(return_step.first == ai_history_baset::step_statust::BLOCKED) + continue; // Can't return -- no need to merge + + // The fourth history! + trace_ptrt p_return_site = return_step.second; + + const std::unique_ptr ptr_s_callee_end_copy( + make_temporary_state(s_callee_end)); + auto tmp = + dynamic_cast(&(*ptr_s_callee_end_copy)); + INVARIANT(tmp != nullptr, "Three-way merge requires domain support"); + variable_sensitivity_domaint &s_working = *tmp; + + // Apply transformer + // This is for an end_function instruction which normally doesn't do much + // but in VSD it does, so this cannot be omitted. + s_working.transform( + callee_function_id, + l_callee_end, + calling_function_id, + l_return_site, + *this, + ns); + + // TODO : this is probably needed to avoid three_way_merge modifying one of + // its arguments as it goes. A better solution would be to refactor + // merge_three_way_function_return. + const std::unique_ptr ptr_s_working_copy( + make_temporary_state(s_working)); + + s_working.merge_three_way_function_return( + get_state(p_call_site), + get_state(p_callee_start), + *ptr_s_working_copy, + ns); + + if( + merge(s_working, p_callee_end, p_return_site) || + (return_step.first == ai_history_baset::step_statust::NEW && + !s_working.is_bottom())) + { + put_in_working_set(working_set, p_return_site); + new_data = true; + } + } + + return new_data; +} diff --git a/src/analyses/variable-sensitivity/three_way_merge_abstract_interpreter.h b/src/analyses/variable-sensitivity/three_way_merge_abstract_interpreter.h new file mode 100644 index 00000000000..9e2d0a94ef2 --- /dev/null +++ b/src/analyses/variable-sensitivity/three_way_merge_abstract_interpreter.h @@ -0,0 +1,53 @@ +/*******************************************************************\ + +Module: Variable sensitivity domain + +Author: Martin Brain, martin.brain@cs.ox.ac.uk + +Date: August 2020 + +\*******************************************************************/ + +/// \file +/// An abstract interpreter, based on the default recursive-interprocedural +/// that uses variable sensitivity domain's three-way-merge operation on +/// returning from a function call. This gives some of the precision of +/// context-sensitive analysis but at a fraction of the cost. The really +/// key thing is that it distinguishes between variables that are modified in +/// the function (or things called from it) and those that appear modified +/// because they have different values at different call sites. This is +/// especially important for preserving the value of (unmodified) global +/// variables. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_THREE_WAY_MERGE_ABSTRACT_INTERPRETER_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_THREE_WAY_MERGE_ABSTRACT_INTERPRETER_H + +#include + +class ai_three_way_merget : public ai_recursive_interproceduralt +{ +public: + ai_three_way_merget( + std::unique_ptr &&hf, + std::unique_ptr &&df, + std::unique_ptr &&st) + : ai_recursive_interproceduralt(std::move(hf), std::move(df), std::move(st)) + { + } + +protected: + // Like ai_recursive_interproceduralt we hook the handling of function calls. + // Much of this is the same as ai_recursive_interproceduralt's handling but + // on function return the three-way merge is used. + bool visit_edge_function_call( + const irep_idt &calling_function_id, + trace_ptrt p_call, + locationt l_return, + const irep_idt &callee_function_id, + working_sett &working_set, + const goto_programt &callee, + const goto_functionst &goto_functions, + const namespacet &ns) override; +}; + +#endif diff --git a/src/analyses/variable-sensitivity/union_abstract_object.cpp b/src/analyses/variable-sensitivity/union_abstract_object.cpp new file mode 100644 index 00000000000..f4e4e902190 --- /dev/null +++ b/src/analyses/variable-sensitivity/union_abstract_object.cpp @@ -0,0 +1,85 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Fotis Koutoulakis, fotis.koutoulakis@diffblue.com + +\*******************************************************************/ + +#include +#include +#include + +#include "union_abstract_object.h" + +union_abstract_objectt::union_abstract_objectt(const typet &type) + : abstract_objectt(type) +{ + PRECONDITION(type.id() == ID_union); +} + +union_abstract_objectt::union_abstract_objectt( + const typet &type, + bool top, + bool bottom) + : abstract_objectt(type, top, bottom) +{ + PRECONDITION(type.id() == ID_union); +} + +union_abstract_objectt::union_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_objectt(expr, environment, ns) +{ + PRECONDITION(ns.follow(expr.type()).id() == ID_union); +} + +abstract_object_pointert union_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return read_component(env, to_member_expr(specifier), ns); +} + +abstract_object_pointert union_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + return write_component( + environment, ns, stack, to_member_expr(specifier), value, merging_write); +} + +abstract_object_pointert union_abstract_objectt::read_component( + const abstract_environmentt &environment, + const member_exprt &member_expr, + const namespacet &ns) const +{ + return environment.abstract_object_factory( + member_expr.type(), ns, !is_bottom(), is_bottom()); +} + +sharing_ptrt union_abstract_objectt::write_component( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack &stack, + const member_exprt &member_expr, + const abstract_object_pointert value, + bool merging_write) const +{ + if(is_top() || is_bottom()) + { + return std::dynamic_pointer_cast(clone()); + } + else + { + return sharing_ptrt( + new union_abstract_objectt(type(), true, false)); + } +} diff --git a/src/analyses/variable-sensitivity/union_abstract_object.h b/src/analyses/variable-sensitivity/union_abstract_object.h new file mode 100644 index 00000000000..69c947af72d --- /dev/null +++ b/src/analyses/variable-sensitivity/union_abstract_object.h @@ -0,0 +1,129 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Fotis Koutoulakis, fotis.koutoulakis@diffblue.com + +\*******************************************************************/ + +/// \file +/// The base of all union abstractions + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_UNION_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_UNION_ABSTRACT_OBJECT_H + +#include +#include + +class abstract_environmentt; +class member_exprt; + +class union_abstract_objectt : public abstract_objectt +{ +public: + /// \param type: the type the abstract_object is representing + explicit union_abstract_objectt(const typet &type); + + /// Start the abstract object at either top or bottom or neither + /// Asserts if both top and bottom are true + /// + /// \param type: the type the abstract_object is representing + /// \param top: is the abstract_object starting as top + /// \param bottom: is the abstract_object starting as bottom + union_abstract_objectt(const typet &type, bool top, bool bottom); + + /// \param expr: the expression to use as the starting pointer for + /// an abstract object + /// \param environment: the environment the abstract object is going + /// to be evaluated in. + /// \param ns: the current namespace + explicit union_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /** + * A helper function to evaluate an abstract object contained + * within a container object. More precise abstractions may override this + * to return more precise results. + * + * \param env the abstract environment + * \param specifier a modifier expression, such as an array index or field + * specifier used to indicate access to a specific component + * \param ns the current namespace + * + * \return the abstract_objectt representing the value of the read component. + */ + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + /** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + +protected: + CLONE + + /// A helper function to evaluate the abstract object contained + /// within a union. More precise abstractions may override this + /// to return more precise results. + /// + /// \param environment: the abstract environment + /// \param member_expr: the expression uses to access a specific component + /// \param ns: the current namespace + /// + /// The abstract object representing the value of that component. For + /// this abstraction this will always be top since we are not tracking + /// the union. + virtual abstract_object_pointert read_component( + const abstract_environmentt &environment, + const member_exprt &member_expr, + const namespacet &ns) const; + + /// A helper function to evaluate writing to a component of a union. + /// More precise abstractions may override this to + /// update what they are storing for a specific component. + /// + /// \param environment: the abstract environment + /// \param ns: the current namespace + /// \param stack: the remaining stack of expressions on the LHS to evaluate + /// \param member_expr: the expression uses to access a specific component + /// \param value: the value we are trying to write to the component + /// \param merging_write: whether to over-write or to merge with the + /// current value. In other words is there + /// any certainty that this write will happen. + /// + /// \return The union_abstract_objectt representing the result of writing + /// to a specific component. In this case this will always be top + /// as we are not tracking the value of this union. + virtual sharing_ptrt write_component( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack &stack, + const member_exprt &member_expr, + const abstract_object_pointert value, + bool merging_write) const; +}; +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_UNION_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/value_set_abstract_object.cpp b/src/analyses/variable-sensitivity/value_set_abstract_object.cpp new file mode 100644 index 00000000000..9a64a17aae6 --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_abstract_object.cpp @@ -0,0 +1,305 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: diffblue + +\*******************************************************************/ + +/// \file +/// Value Set Abstract Object + +#include +#include +#include +#include +#include +#include +#include +#include + +value_set_abstract_objectt::value_set_abstract_objectt(const typet &type) + : abstract_valuet(type), my_type(type_to_abstract_type(type)) +{ + switch(my_type) + { + case abstract_typet::POINTER: + values.insert(std::make_shared(type)); + break; + case abstract_typet::CONSTANT: + values.insert(std::make_shared(type)); + break; + case abstract_typet::UNSUPPORTED: + UNREACHABLE; + } + verify(); +} + +value_set_abstract_objectt::value_set_abstract_objectt( + const typet &type, + bool top, + bool bottom) + : abstract_valuet(type, top, bottom), my_type(type_to_abstract_type(type)) +{ + switch(my_type) + { + case abstract_typet::POINTER: + values.insert( + std::make_shared(type, top, bottom)); + break; + case abstract_typet::CONSTANT: + values.insert( + std::make_shared(type, top, bottom)); + break; + case abstract_typet::UNSUPPORTED: + UNREACHABLE; + } + verify(); +} + +value_set_abstract_objectt::value_set_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : abstract_valuet(expr.type(), false, false), + my_type(type_to_abstract_type(expr.type())) +{ + switch(my_type) + { + case abstract_typet::POINTER: + values.insert(std::make_shared( + expr, environment, ns)); + break; + case abstract_typet::CONSTANT: + values.insert( + std::make_shared(expr, environment, ns)); + break; + case abstract_typet::UNSUPPORTED: + UNREACHABLE; + } + verify(); +} + +abstract_object_pointert value_set_abstract_objectt::expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const +{ + std::size_t num_operands = expr.operands().size(); + PRECONDITION(operands.size() == num_operands); + + std::vector collective_operands; + collective_operands.reserve(num_operands); + for(const auto &op : operands) + { + auto vsab = std::dynamic_pointer_cast( + maybe_unwrap_context(op)); + INVARIANT(vsab, "should be a value set abstract object"); + collective_operands.push_back(vsab->get_values()); + } + + abstract_object_sett resulting_objects; + for_each_comb( + collective_operands, + [&resulting_objects, this, &expr, &environment, &ns]( + const std::vector &ops) { + resulting_objects.insert( + (*values.begin())->expression_transform(expr, ops, environment, ns)); + }); + + return resolve_new_values(resulting_objects); +} + +abstract_object_pointert value_set_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + abstract_object_sett new_values; + for(const auto &st_value : values) + new_values.insert(st_value->read(env, specifier, ns)); + + return resolve_new_values(new_values); +} + +abstract_object_pointert value_set_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + abstract_object_sett new_values; + for(const auto &st_value : values) + { + new_values.insert( + st_value->write(environment, ns, stack, specifier, value, merging_write)); + } + return resolve_new_values(new_values); +} + +abstract_object_pointert value_set_abstract_objectt::resolve_new_values( + const abstract_object_sett &new_values) const +{ + PRECONDITION(!new_values.empty()); + + if(new_values == values) + return shared_from_this(); + + abstract_object_sett unwrapped_values; + for(auto const &value : new_values) + { + unwrapped_values.insert( + maybe_extract_single_value(maybe_unwrap_context(value))); + } + + abstract_typet new_type = get_type(*unwrapped_values.begin()); + if( + unwrapped_values.size() > max_value_set_size && + new_type == abstract_typet::CONSTANT) + { + return to_interval(unwrapped_values); + } + if(unwrapped_values.size() == 1 && new_type == abstract_typet::UNSUPPORTED) + { + return (*unwrapped_values.begin()); + } + + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + if( + unwrapped_values.size() > max_value_set_size || + new_type == abstract_typet::UNSUPPORTED) + { + result->make_top(); + } + else + { + result->set_values(unwrapped_values); + } + return result; +} + +abstract_object_pointert +value_set_abstract_objectt::merge(abstract_object_pointert other) const +{ + auto cast_other = + std::dynamic_pointer_cast(other); + if(cast_other && cast_other->get_my_type() == get_my_type()) + { + auto union_values = values; + union_values.insert( + cast_other->get_values().begin(), cast_other->get_values().end()); + return resolve_new_values(union_values); + } + else + return abstract_objectt::merge(other); +} + +abstract_object_pointert value_set_abstract_objectt::to_interval( + const abstract_object_sett &other_values) const +{ + PRECONDITION(!other_values.empty()); + if(get_type(*other_values.begin()) == abstract_typet::POINTER) + return std::make_shared( + type(), true, false); + PRECONDITION(get_type(*other_values.begin()) == abstract_typet::CONSTANT); + + exprt lower_expr = (*other_values.begin())->to_constant(); + exprt upper_expr = (*other_values.begin())->to_constant(); + for(const auto &value : other_values) + { + const auto &value_expr = value->to_constant(); + lower_expr = constant_interval_exprt::get_min(lower_expr, value_expr); + upper_expr = constant_interval_exprt::get_min(upper_expr, value_expr); + } + return std::make_shared( + constant_interval_exprt(lower_expr, upper_expr)); +} + +bool value_set_abstract_objectt::verify() const +{ + CHECK_RETURN(my_type != abstract_typet::UNSUPPORTED); + for(const auto &value : values) + { + CHECK_RETURN( + !std::dynamic_pointer_cast(value)); + CHECK_RETURN(get_type(value) == my_type); + } + return true; +} + +value_set_abstract_objectt::abstract_typet +value_set_abstract_objectt::get_type(const abstract_object_pointert &other) +{ + PRECONDITION( + !std::dynamic_pointer_cast(other)); + PRECONDITION(!std::dynamic_pointer_cast(other)); + PRECONDITION( + !std::dynamic_pointer_cast(other)); + PRECONDITION(!std::dynamic_pointer_cast(other)); + PRECONDITION( + !std::dynamic_pointer_cast(other)); + + if(std::dynamic_pointer_cast(other)) + return abstract_typet::POINTER; + if(std::dynamic_pointer_cast(other)) + return abstract_typet::CONSTANT; + UNREACHABLE; + return abstract_typet::UNSUPPORTED; +} + +abstract_object_pointert value_set_abstract_objectt::maybe_extract_single_value( + const abstract_object_pointert &maybe_singleton) +{ + auto const &value_as_set = + std::dynamic_pointer_cast( + maybe_singleton); + if(value_as_set) + { + PRECONDITION(value_as_set->get_values().size() == 1); + PRECONDITION(!std::dynamic_pointer_cast( + *value_as_set->get_values().begin())); + + return *value_as_set->get_values().begin(); + } + else + return maybe_singleton; +} + +abstract_object_pointert value_set_abstract_objectt::maybe_unwrap_context( + const abstract_object_pointert &maybe_wrapped) +{ + auto const &context_value = + std::dynamic_pointer_cast(maybe_wrapped); + + return context_value ? context_value->unwrap_context() : maybe_wrapped; +} + +void value_set_abstract_objectt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(is_top()) + { + out << "TOP"; + } + else if(is_bottom()) + { + out << "BOTTOM"; + } + else + { + out << "value-set-begin: "; + for(auto const &value : values) + { + value->output(out, ai, ns); + out << ", "; + } + out << ":value-set-end"; + } +} diff --git a/src/analyses/variable-sensitivity/value_set_abstract_object.h b/src/analyses/variable-sensitivity/value_set_abstract_object.h new file mode 100644 index 00000000000..a614dbc9e15 --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_abstract_object.h @@ -0,0 +1,228 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: diffblue + +\*******************************************************************/ + +/// \file +/// Value Set Abstract Object + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ABSTRACT_OBJECT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ABSTRACT_OBJECT_H + +#include + +#include + +class value_set_abstract_objectt : public abstract_valuet +{ +public: + using abstract_object_sett = std::unordered_set< + abstract_object_pointert, + abstract_hashert, + abstract_equalert>; + + /// \copydoc abstract_objectt::abstract_objectt(const typet&) + explicit value_set_abstract_objectt(const typet &type); + + /// \copydoc abstract_objectt::abstract_objectt(const typet &, bool, bool) + value_set_abstract_objectt(const typet &type, bool top, bool bottom); + + value_set_abstract_objectt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + /// \copydoc abstract_objectt::to_constant + exprt to_constant() const override + { + verify(); + return values.size() == 1 ? (*values.begin())->to_constant() + : abstract_objectt::to_constant(); + } + + /// \copydoc abstract_objectt::expression_transform + /// + /// Transforms the \p expr for every combination of \p operands (since these + /// can be value-sets as well). + abstract_object_pointert expression_transform( + const exprt &expr, + const std::vector &operands, + const abstract_environmentt &environment, + const namespacet &ns) const override; + + /// Getter for the set of stored abstract objects. + /// \return the values represented by this abstract object + const abstract_object_sett &get_values() const + { + return values; + } + + /// Setter for updating the stored values + /// \param other_values: the new (non-empty) set of values + void set_values(const abstract_object_sett &other_values) + { + PRECONDITION(!other_values.empty()); + my_type = get_type(*other_values.begin()); + values = other_values; + verify(); + } + + /// Distinguish the type of abstract objects stored in this value-set. + enum class abstract_typet + { + CONSTANT, + POINTER, + UNSUPPORTED + }; + + /// Getter for the type of stored values + /// \return the abstract-type stored here + abstract_typet get_my_type() const + { + return my_type; + } + + /// The threshold size for value-sets: past this threshold the object is + /// either converted to interval or marked as `top`. + static const size_t max_value_set_size = 10; + + /// \copydoc abstract_objectt::read + /// + /// Delegate reading to stored values. + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + /// \copydoc abstract_objectt::write + /// + /// Delegate writing to stored values. + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + /// Enforce casting to interval. + /// \return the stored values abstracted to an interval + abstract_object_pointert get_as_interval() const + { + return to_interval(values); + } + + void output(std::ostream &out, const ai_baset &ai, const namespacet &ns) + const override; + +protected: + CLONE + + /// Update the set of stored values to \p new_values. Build a new abstract + /// object of the right type if necessary. + /// \param new_values: potentially new set of values + /// \return the abstract object representing \p new_values (either 'this' or + /// something new) + abstract_object_pointert + resolve_new_values(const abstract_object_sett &new_values) const; + + /// \copydoc abstract_object::merge + abstract_object_pointert merge(abstract_object_pointert other) const override; + +private: + // data + abstract_typet my_type; + abstract_object_sett values; + + /// Cast the set of values \p other_values to an interval. + /// \param other_values: the value-set to be abstracted into an interval + /// \return the interval-abstract-object containing \p other_values + abstract_object_pointert + to_interval(const abstract_object_sett &other_values) const; + + /// \copydoc abstract_objectt::verify + bool verify() const override; + + /// Recursively construct a combination \p sub_con from \p super_con and once + /// constructed call \p f. + /// \param super_con: vector of some containers storing the values + /// \param sub_con: the one combination being currently constructed + /// \param f: callable with side-effects + template + void apply_comb( + const std::vector &super_con, + std::vector &sub_con, + F f) const + { + size_t n = sub_con.size(); + if(n == super_con.size()) + f(sub_con); + else + { + for(const auto &value : super_con[n]) + { + sub_con.push_back(value); + apply_comb(super_con, sub_con, f); + sub_con.pop_back(); + } + } + } + + /// Call the function \p f on every combination of elements in \p super_con. + /// Hence the arity of \p f is `super_con.size()`. <{1,2},{1},{1,2,3}> -> + /// f(1,1,1), f(1,1,2), f(1,1,3), f(2,1,1), f(2,1,2), f(2,1,3). + /// \param super_con: vector of some containers storing the values + /// \param f: callable with side-effects + template + void for_each_comb(const std::vector &super_con, F f) const + { + std::vector sub_con; + apply_comb(super_con, sub_con, f); + } + + /// Determine abstract-type of an abstract object \p other. + /// \param other: the abstract object to get the type of + /// \return the abstract-type of \p other + static abstract_typet get_type(const abstract_object_pointert &other); + + /// Determine abstract-type of an expression-type \p type. + /// \param type: the expression type to get the abstract-type of + /// \return the abstract-type of \p type + static abstract_typet type_to_abstract_type(const typet &type) + { + if(type.id() == ID_pointer) + { + return abstract_typet::POINTER; + } + else if( + type.id() == ID_signedbv || type.id() == ID_unsignedbv || + type.id() == ID_fixedbv || type.id() == ID_c_bool || + type.id() == ID_bool || type.id() == ID_integer || + type.id() == ID_c_bit_field || type.id() == ID_floatbv) + { + return abstract_typet::CONSTANT; + } + else + { + return abstract_typet::UNSUPPORTED; + } + } + + /// Helper for converting singleton value sets into its only value. + /// \p maybe_singleton: either a set of abstract values or a single value + /// \return an abstract value without context + static abstract_object_pointert + maybe_extract_single_value(const abstract_object_pointert &maybe_singleton); + + /// Helper for converting context objects into its abstract-value children + /// \p maybe_wrapped: either an abstract value (or a set of those) or one + /// wrapped in a context + /// \return an abstract value without context (though it might be as set) + static abstract_object_pointert + maybe_unwrap_context(const abstract_object_pointert &maybe_wrapped); +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/value_set_abstract_value.cpp b/src/analyses/variable-sensitivity/value_set_abstract_value.cpp new file mode 100644 index 00000000000..7007a709789 --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_abstract_value.cpp @@ -0,0 +1,127 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-value-set + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "value_set_abstract_value.h" + +#include + +value_set_abstract_valuet::value_set_abstract_valuet( + const typet &type, + bool top, + bool bottom) + : abstract_valuet{type, top, bottom}, values{} +{ +} + +const value_set_abstract_valuet::valuest & +value_set_abstract_valuet::get_values() const +{ + PRECONDITION(!is_top()); + PRECONDITION(!is_bottom()); + return values; +} + +abstract_object_pointert +value_set_abstract_valuet::merge(abstract_object_pointert other) const +{ + if(is_top()) + { + return shared_from_this(); + } + + if(other->is_top()) + { + return other; + } + + if(is_bottom()) + { + return other; + } + + if(other->is_bottom()) + { + return shared_from_this(); + } + + if( + auto other_value_set = + dynamic_cast(other.get())) + { + valuest merged_values{values}; + auto const &other_values = other_value_set->get_values(); + merged_values.insert(other_values.begin(), other_values.end()); + return std::make_shared( + type(), std::move(merged_values)); + } + return abstract_objectt::merge(other); +} + +value_set_abstract_valuet::value_set_abstract_valuet( + const typet &type, + valuest values) + : abstract_valuet{type, values.size() > max_value_set_size, values.empty()}, + values{std::move(values)} +{ + if(values.size() > max_value_set_size) + { + this->values.clear(); + } +} + +value_set_abstract_valuet::value_set_abstract_valuet( + exprt expr, + const abstract_environmentt &environment, + const namespacet &ns) + : value_set_abstract_valuet{expr.type(), valuest{expr}} +{ +} + +exprt value_set_abstract_valuet::to_constant() const +{ + if(!is_top() && !is_bottom() && values.size() == 1) + { + return *values.begin(); + } + else + { + return nil_exprt{}; + } +} + +void value_set_abstract_valuet::output( + std::ostream &out, + const class ai_baset &, + const namespacet &ns) const +{ + if(is_bottom()) + { + out << "BOTTOM"; + return; + } + else if(is_top()) + { + out << "TOP"; + return; + } + + std::vector vals; + for(const auto &elem : values) + { + vals.push_back(expr2c(elem, ns)); + } + + std::sort(vals.begin(), vals.end()); + + out << "{ "; + for(const auto &val : vals) + { + out << val << " "; + } + out << "}"; +} diff --git a/src/analyses/variable-sensitivity/value_set_abstract_value.h b/src/analyses/variable-sensitivity/value_set_abstract_value.h new file mode 100644 index 00000000000..11cb98d5841 --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_abstract_value.h @@ -0,0 +1,61 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-value-set + + Author: Diffblue Ltd. + +\*******************************************************************/ + +/// \file +/// Value sets for primitives + +// NOLINTNEXTLINE(whitespace/line_length) +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ABSTRACT_VALUE_H +// NOLINTNEXTLINE(whitespace/line_length) +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ABSTRACT_VALUE_H + +#include "abstract_value.h" + +#include + +class value_set_abstract_valuet : public abstract_valuet +{ +public: + using valuest = std::unordered_set; + + explicit value_set_abstract_valuet( + const typet &type, + bool top = true, + bool bottom = false); + value_set_abstract_valuet( + exprt expr, + const abstract_environmentt &environment, + const namespacet &ns); + value_set_abstract_valuet(const typet &type, valuest values); + + /// Get the possible values for this abstract object. + /// Only meaningful when this is neither TOP nor BOTTOM. + const valuest &get_values() const; + + CLONE + + /// TODO arbitrary limit, make configurable + static constexpr std::size_t max_value_set_size = 10; + + exprt to_constant() const override; + + void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) + const override; + +protected: + abstract_object_pointert merge(abstract_object_pointert other) const override; + +private: + /// If BOTTOM then empty. + /// If neither BOTTOM or TOP then the exact set of values. + /// If TOP this field doesn't mean anything and shouldn't be looked at. + valuest values; +}; + +// NOLINTNEXTLINE(whitespace/line_length) +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ABSTRACT_VALUE_H diff --git a/src/analyses/variable-sensitivity/value_set_array_abstract_object.cpp b/src/analyses/variable-sensitivity/value_set_array_abstract_object.cpp new file mode 100644 index 00000000000..57770c6fbf3 --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_array_abstract_object.cpp @@ -0,0 +1,56 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-value-set + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "value_set_array_abstract_object.h" + +value_set_array_abstract_objectt::value_set_array_abstract_objectt( + const typet &type) + : array_abstract_objectt(type) +{ +} + +abstract_object_pointert value_set_array_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return array_abstract_objectt::read(env, specifier, ns); +} + +abstract_object_pointert value_set_array_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + const exprt &specifier, + abstract_object_pointert value, + bool merging_write) const +{ + return array_abstract_objectt::write( + environment, ns, stack, specifier, value, merging_write); +} + +abstract_object_pointert value_set_array_abstract_objectt::read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const +{ + return array_abstract_objectt::read_index(env, index, ns); +} + +sharing_ptrt +value_set_array_abstract_objectt::write_index( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + const index_exprt &index_expr, + abstract_object_pointert value, + bool merging_write) const +{ + return array_abstract_objectt::write_index( + environment, ns, stack, index_expr, value, merging_write); +} diff --git a/src/analyses/variable-sensitivity/value_set_array_abstract_object.h b/src/analyses/variable-sensitivity/value_set_array_abstract_object.h new file mode 100644 index 00000000000..3318e6dece5 --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_array_abstract_object.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-value-set + + Author: Diffblue Ltd. + +\*******************************************************************/ + +/// \file +/// Arrays with value sets as indices + +// NOLINTNEXTLINE(whitespace/line_length) +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ARRAY_ABSTRACT_OBJECT_H +// NOLINTNEXTLINE(whitespace/line_length) +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ARRAY_ABSTRACT_OBJECT_H + +#include "array_abstract_object.h" + +class value_set_array_abstract_objectt : public array_abstract_objectt +{ +public: + explicit value_set_array_abstract_objectt(const typet &type); + + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + const exprt &specifier, + abstract_object_pointert value, + bool merging_write) const override; + + CLONE +protected: + abstract_object_pointert read_index( + const abstract_environmentt &env, + const index_exprt &index, + const namespacet &ns) const override; + + sharing_ptrt write_index( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + const index_exprt &index_expr, + abstract_object_pointert value, + bool merging_write) const override; +}; + +// NOLINTNEXTLINE(whitespace/line_length) +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_ARRAY_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/value_set_pointer_abstract_object.cpp b/src/analyses/variable-sensitivity/value_set_pointer_abstract_object.cpp new file mode 100644 index 00000000000..60213bb724a --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_pointer_abstract_object.cpp @@ -0,0 +1,60 @@ + +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-value-set + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "value_set_pointer_abstract_object.h" + +abstract_object_pointert +value_set_pointer_abstract_objectt::merge(abstract_object_pointert other) const +{ + return shared_from_this(); +} + +abstract_object_pointert value_set_pointer_abstract_objectt::read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const +{ + return pointer_abstract_objectt::read(env, specifier, ns); +} + +abstract_object_pointert value_set_pointer_abstract_objectt::write( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + const exprt &specifier, + abstract_object_pointert value, + bool merging_write) const +{ + return pointer_abstract_objectt::write( + environment, ns, stack, specifier, value, merging_write); +} + +value_set_pointer_abstract_objectt::value_set_pointer_abstract_objectt( + const typet &type) + : pointer_abstract_objectt(type) +{ +} +abstract_object_pointert value_set_pointer_abstract_objectt::read_dereference( + const abstract_environmentt &env, + const namespacet &ns) const +{ + return pointer_abstract_objectt::read_dereference(env, ns); +} + +sharing_ptrt +value_set_pointer_abstract_objectt::write_dereference( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + abstract_object_pointert value, + bool merging_write) const +{ + return pointer_abstract_objectt::write_dereference( + environment, ns, stack, value, merging_write); +} diff --git a/src/analyses/variable-sensitivity/value_set_pointer_abstract_object.h b/src/analyses/variable-sensitivity/value_set_pointer_abstract_object.h new file mode 100644 index 00000000000..7dd04d4a43b --- /dev/null +++ b/src/analyses/variable-sensitivity/value_set_pointer_abstract_object.h @@ -0,0 +1,54 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-value-set + + Author: Diffblue Ltd. + +\*******************************************************************/ + +/// \file +/// Pointers pointing to a limited-size set of possible targets + +// NOLINTNEXTLINE(whitespace/line_length) +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_POINTER_ABSTRACT_OBJECT_H +// NOLINTNEXTLINE(whitespace/line_length) +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_POINTER_ABSTRACT_OBJECT_H + +#include "pointer_abstract_object.h" + +class value_set_pointer_abstract_objectt : public pointer_abstract_objectt +{ +public: + explicit value_set_pointer_abstract_objectt(const typet &type); + + abstract_object_pointert merge(abstract_object_pointert other) const override; + + abstract_object_pointert read( + const abstract_environmentt &env, + const exprt &specifier, + const namespacet &ns) const override; + + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + const exprt &specifier, + abstract_object_pointert value, + bool merging_write) const override; + + CLONE +protected: + abstract_object_pointert read_dereference( + const abstract_environmentt &env, + const namespacet &ns) const override; + + sharing_ptrt write_dereference( + abstract_environmentt &environment, + const namespacet &ns, + std::stack stack, + abstract_object_pointert value, + bool merging_write) const override; +}; + +// NOLINTNEXTLINE(whitespace/line_length) +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_POINTER_ABSTRACT_OBJECT_H diff --git a/src/analyses/variable-sensitivity/variable_sensitivity_dependence_graph.cpp b/src/analyses/variable-sensitivity/variable_sensitivity_dependence_graph.cpp new file mode 100644 index 00000000000..877bff46724 --- /dev/null +++ b/src/analyses/variable-sensitivity/variable_sensitivity_dependence_graph.cpp @@ -0,0 +1,661 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-dependence-graph + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "variable_sensitivity_dependence_graph.h" +#include "data_dependency_context.h" + +#include +#include +#include +#include + +/** + * Evaluate an expression and accumulate all the data dependencies + * involved in the evaluation. + * + * \param expr the expression to evaluate + * \param ns the namespace to use + * \param deps the destination in which to accumlate data dependencies + */ +void variable_sensitivity_dependence_domaint::eval_data_deps( + const exprt &expr, + const namespacet &ns, + data_depst &deps) const +{ + const auto res = + std::dynamic_pointer_cast(eval(expr, ns)); + + if(res->get_data_dependencies().size() > 0) + { + // If the expression was able to be eval'ed to something with data + // dependencies, then that's all we need to gather. + for(const auto &dep : res->get_data_dependencies()) + deps[dep].insert(expr); + } + else + { + // If the expression could not be eval'ed to something with data + // dependencies, then it may have been some sort of compound expression, + // so attempt to eval the data dependencies for all the operands, taking + // the union of them all. + for(const exprt &op : expr.operands()) + { + eval_data_deps(op, ns, deps); + } + } +} + +/** + * Compute the abstract transformer for a single instruction. For + * the data dependencies, this involves calculating all the data + * dependencies that exist in the 'to' instruction. + * + * \param function_from the function of the instruction before the abstract + * domain + * \param from the instruction before the abstract domain + * \param function_to the function of the instruction after the abstract domain + * \param to the instruction after the abstract domain + * \param ai the abstract interpreter + * \param ns the namespace + */ +void variable_sensitivity_dependence_domaint::transform( + const irep_idt &function_from, + locationt from, + const irep_idt &function_to, + locationt to, + ai_baset &ai, + const namespacet &ns) +{ + variable_sensitivity_domaint::transform( + function_from, from, function_to, to, ai, ns); + + variable_sensitivity_dependence_grapht *dep_graph = + dynamic_cast(&ai); + DATA_INVARIANT( + dep_graph != nullptr, "Domains should all be of the same type"); + + // propagate control dependencies across function calls + if(from->is_function_call()) + { + if(function_from == function_to) + { + control_dependencies(function_from, from, function_to, to, *dep_graph); + } + else + { + // edge to function entry point + const goto_programt::const_targett next = std::next(from); + + variable_sensitivity_dependence_domaint *s = + dynamic_cast( + &(dep_graph->get_state(next))); + DATA_INVARIANT(s != nullptr, "Domains should all be of the same type"); + + if(s->has_values.is_false()) + { + s->has_values = tvt::unknown(); + s->has_changed = true; + } + + // modify abstract state of return location + if(s->merge_control_dependencies( + control_deps, + control_dep_candidates, + control_dep_calls, + control_dep_call_candidates)) + s->has_changed = true; + + control_deps.clear(); + control_dep_candidates.clear(); + + control_dep_calls.clear(); + control_dep_calls.insert(from); + + control_dep_call_candidates.clear(); + control_dep_call_candidates.insert(from); + } + } + else + control_dependencies(function_from, from, function_to, to, *dep_graph); + + // Find all the data dependencies in the the 'to' expression + data_dependencies(from, to, *dep_graph, ns); +} + +void variable_sensitivity_dependence_domaint::data_dependencies( + goto_programt::const_targett from, + goto_programt::const_targett to, + variable_sensitivity_dependence_grapht &dep_graph, + const namespacet &ns) +{ + // Find all the data dependencies in the the 'to' expression + domain_data_deps.clear(); + if(to->is_assign()) + { + const code_assignt &inst = to_code_assign(to->code); + const exprt &rhs = inst.rhs(); + + // Handle return value of a 'no body' function + if(rhs.id() == ID_side_effect) + { + const side_effect_exprt &see = to_side_effect_expr(rhs); + if(see.get_statement() == ID_nondet) + { + if(from->is_function_call()) + { + const code_function_callt &cfc = to_code_function_call(from->code); + const exprt &call = cfc.function(); + + if(call.id() == ID_symbol) + { + const irep_idt id = to_symbol_expr(call).id(); + const goto_functionst &goto_functions = dep_graph.goto_functions; + + goto_functionst::function_mapt::const_iterator it = + goto_functions.function_map.find(id); + + if(it != goto_functions.function_map.end()) + { + if(!it->second.body_available()) // body not available + { + domain_data_deps[from].insert(call); + } + } + else // function not in map + { + domain_data_deps[from].insert(call); + } + } + else // complex call expression + { + domain_data_deps[from].insert(call); + } + } + } + } + else + { + // Just an ordinary assignement + eval_data_deps(rhs, ns, domain_data_deps); + } + } + else if(to->is_function_call()) + { + const code_function_callt &call = to_code_function_call(to->code); + const code_function_callt::argumentst &args = call.arguments(); + for(const auto &arg : args) + { + eval_data_deps(arg, ns, domain_data_deps); + } + } + else if(to->is_goto()) + { + eval_data_deps(to->guard, ns, domain_data_deps); + } +} + +void variable_sensitivity_dependence_domaint::control_dependencies( + const irep_idt &from_function, + goto_programt::const_targett from, + const irep_idt &to_function, + goto_programt::const_targett to, + variable_sensitivity_dependence_grapht &dep_graph) +{ + // Better Slicing of Programs with Jumps and Switches + // Kumar and Horwitz, FASE'02: + // "Node N is control dependent on node M iff N postdominates, in + // the CFG, one but not all of M's CFG successors." + // + // The "successor" above refers to an immediate successor of M. + + // Candidates for M for "to" are "from" and all existing control + // dependencies on nodes. "from" is added if it is a goto or assume + // instruction + + // Add new candidates + + if(from->is_goto() || from->is_assume()) + control_dep_candidates.insert(from); + else if(from->is_end_function()) + { + control_deps.clear(); + control_dep_candidates.clear(); + control_dep_calls.clear(); + control_dep_call_candidates.clear(); + return; + } + + // Compute postdominators if needed + + const goto_functionst &goto_functions = dep_graph.goto_functions; + + const irep_idt id = from_function; + cfg_post_dominatorst &pd_tmp = dep_graph.cfg_post_dominators()[id]; + + goto_functionst::function_mapt::const_iterator f_it = + goto_functions.function_map.find(id); + + if(f_it == goto_functions.function_map.end()) + return; + + const goto_programt &goto_program = f_it->second.body; + + if(pd_tmp.cfg.size() == 0) // have we computed the dominators already? + { + pd_tmp(goto_program); + } + + const cfg_post_dominatorst &pd = pd_tmp; + + // Check all candidates + + for(const auto &cd : control_dep_candidates) + { + // check all CFG successors of M + // special case: assumptions also introduce a control dependency + bool post_dom_all = !cd->is_assume(); + bool post_dom_one = false; + + // we could hard-code assume and goto handling here to improve + // performance + const cfg_post_dominatorst::cfgt::nodet &m = pd.get_node(cd); + + // successors of M + for(const auto &edge : m.out) + { + const cfg_post_dominatorst::cfgt::nodet &m_s = pd.cfg[edge.first]; + + if(m_s.dominators.find(to) != m_s.dominators.end()) + post_dom_one = true; + else + post_dom_all = false; + } + + if(post_dom_all || !post_dom_one) + { + control_deps.erase(cd); + } + else + { + tvt branch = tvt::unknown(); + + if(cd->is_goto() && !cd->is_backwards_goto()) + { + goto_programt::const_targett t = cd->get_target(); + branch = + to->location_number >= t->location_number ? tvt(false) : tvt(true); + } + + control_deps.insert(std::make_pair(cd, branch)); + } + } + + if(control_deps.empty()) + { + util_inplace_set_union(control_dep_calls, control_dep_call_candidates); + } + else + { + control_dep_calls.clear(); + } + + // add edges to the graph + for(const auto &c_dep : control_deps) + dep_graph.add_dep(vs_dep_edget::kindt::CTRL, c_dep.first, to); +} + +bool variable_sensitivity_dependence_domaint::merge_control_dependencies( + const control_depst &other_control_deps, + const control_dep_candidatest &other_control_dep_candidates, + const control_dep_callst &other_control_dep_calls, + const control_dep_callst &other_control_dep_call_candidates) +{ + bool changed = false; + + // Merge control dependencies + + control_depst::iterator it = control_deps.begin(); + + for(const auto &c_dep : other_control_deps) + { + // find position to insert + while(it != control_deps.end() && it->first < c_dep.first) + ++it; + + if(it == control_deps.end() || c_dep.first < it->first) + { + // hint points at position that will follow the new element + control_deps.insert(it, c_dep); + changed = true; + } + else + { + INVARIANT( + it != control_deps.end(), "Property of branch needed for safety"); + INVARIANT( + !(it->first < c_dep.first), "Property of loop needed for safety"); + INVARIANT( + !(c_dep.first < it->first), "Property of loop needed for safety"); + + tvt &branch1 = it->second; + const tvt &branch2 = c_dep.second; + + if(branch1 != branch2 && !branch1.is_unknown()) + { + branch1 = tvt::unknown(); + changed = true; + } + + ++it; + } + } + + // Merge control dependency candidates + + size_t n = control_dep_candidates.size(); + + control_dep_candidates.insert( + other_control_dep_candidates.begin(), other_control_dep_candidates.end()); + + changed |= n != control_dep_candidates.size(); + + // Merge call control dependencies + + n = control_dep_calls.size(); + + control_dep_calls.insert( + other_control_dep_calls.begin(), other_control_dep_calls.end()); + + changed |= n != control_dep_calls.size(); + + // Merge call control dependency candidates + + n = control_dep_call_candidates.size(); + + control_dep_call_candidates.insert( + other_control_dep_call_candidates.begin(), + other_control_dep_call_candidates.end()); + + changed |= n != control_dep_call_candidates.size(); + + return changed; +} + +/** + * Computes the join between "this" and "b" + * + * \param b the other domain + * \param from the location preceding the 'b' domain + * \param to the current location + * \return true if something has changed in the merge + */ +bool variable_sensitivity_dependence_domaint::merge( + const variable_sensitivity_domaint &b, + locationt from, + locationt to) +{ + bool changed = false; + + changed = variable_sensitivity_domaint::merge(b, from, to); + changed |= has_values.is_false() || has_changed; + + // Handle data dependencies + const auto &cast_b = + dynamic_cast(b); + + // Merge data dependencies + for(auto bdep : cast_b.domain_data_deps) + { + for(exprt bexpr : bdep.second) + { + auto result = domain_data_deps[bdep.first].insert(bexpr); + changed |= result.second; + } + } + + changed |= merge_control_dependencies( + cast_b.control_deps, + cast_b.control_dep_candidates, + cast_b.control_dep_calls, + cast_b.control_dep_call_candidates); + + has_changed = false; + has_values = tvt::unknown(); + + return changed; +} + +/** + * Perform a context aware merge of the changes that have been applied + * between function_start and the current state. Anything that has not been + * modified will be taken from the \p function_call domain. + * + * \param function_call: The local of the merge - values from here will be + * taken if they have not been modified + * \param function_start: The base of the merge - changes that have been made + * between here and the end will be retained. + * \param function_end: The end of the merge - changes that have been made +/// between the start and here will be retained. + * \param ns: The global namespace + */ +void variable_sensitivity_dependence_domaint::merge_three_way_function_return( + const ai_domain_baset &function_call, + const ai_domain_baset &function_start, + const ai_domain_baset &function_end, + const namespacet &ns) +{ + // The gathering of the data dependencies for the domain is handled by the + // 'transform' and simply relies on the underlying domains with their + // data_dependency_context to be correct. Therefore all we need to ensure at + // the three way merge is that the underlying variable sensitivity domain + // does its three way merge. + variable_sensitivity_domaint::merge_three_way_function_return( + function_call, function_start, function_end, ns); +} + +/** + * Basic text output of the abstract domain + * + * \param out the stream to output onto + * \param ai the abstract domain + * \param ns the namespace + */ +void variable_sensitivity_dependence_domaint::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + if(!control_deps.empty() || !control_dep_calls.empty()) + { + out << "Control dependencies: "; + for(control_depst::const_iterator it = control_deps.begin(); + it != control_deps.end(); + ++it) + { + if(it != control_deps.begin()) + out << ","; + + const goto_programt::const_targett cd = it->first; + const tvt branch = it->second; + + out << cd->location_number << " [" << branch << "]"; + } + + for(control_dep_callst::const_iterator it = control_dep_calls.begin(); + it != control_dep_calls.end(); + ++it) + { + if(!control_deps.empty() || it != control_dep_calls.begin()) + out << ","; + + out << (*it)->location_number << " [UNCONDITIONAL]"; + } + + out << "\n"; + } + + if(!domain_data_deps.empty()) + { + out << "Data dependencies: "; + bool first = true; + for(auto &dep : domain_data_deps) + { + if(!first) + out << ", "; + + out << dep.first->location_number; + out << " ["; + bool first_expr = true; + for(auto &expr : dep.second) + { + if(!first_expr) + out << ", "; + + out << from_expr(ns, "", expr); + first_expr = false; + } + out << "]"; + + first = false; + } + out << std::endl; + } +} + +/** + * \brief Outputs the current value of the domain. + * + * \param ai the abstract interpreter + * \param ns the namespace + * + * \return the domain, formatted as a JSON object. + */ +jsont variable_sensitivity_dependence_domaint::output_json( + const ai_baset &ai, + const namespacet &ns) const +{ + json_arrayt graph; + + for(const auto &cd : control_deps) + { + const goto_programt::const_targett target = cd.first; + const tvt branch = cd.second; + + json_objectt &link = graph.push_back().make_object(); + + link["locationNumber"] = + json_numbert(std::to_string(target->location_number)); + link["sourceLocation"] = json(target->source_location); + link["type"] = json_stringt("control"); + link["branch"] = json_stringt(branch.to_string()); + } + + for(const auto &target : control_dep_calls) + { + json_objectt &link = graph.push_back().make_object(); + link["locationNumber"] = + json_numbert(std::to_string(target->location_number)); + link["sourceLocation"] = json(target->source_location); + link["type"] = json_stringt("control"); + link["branch"] = json_stringt("UNCONDITIONAL"); + } + + for(const auto &dep : domain_data_deps) + { + json_objectt &link = graph.push_back().make_object(); + link["locationNumber"] = + json_numbert(std::to_string(dep.first->location_number)); + link["sourceLocation"] = json(dep.first->source_location); + json_stringt(dep.first->source_location.as_string()); + link["type"] = json_stringt("data"); + + const std::set &expr_set = dep.second; + json_arrayt &expressions = link["expressions"].make_array(); + + for(const exprt &e : expr_set) + { + json_objectt &object = expressions.push_back().make_object(); + object["expression"] = json_stringt(from_expr(ns, "", e)); + object["certainty"] = json_stringt("maybe"); + } + } + + return std::move(graph); +} + +void variable_sensitivity_dependence_domaint::populate_dep_graph( + variable_sensitivity_dependence_grapht &dep_graph, + goto_programt::const_targett this_loc) const +{ + for(const auto &c_dep : control_deps) + dep_graph.add_dep(vs_dep_edget::kindt::CTRL, c_dep.first, this_loc); + + for(const auto &c_dep : control_dep_calls) + dep_graph.add_dep(vs_dep_edget::kindt::CTRL, c_dep, this_loc); + + for(const auto &d_dep : domain_data_deps) + dep_graph.add_dep(vs_dep_edget::kindt::DATA, d_dep.first, this_loc); +} + +/// This ensures that all domains are constructed with the node ID that links +/// them to the graph part of the dependency graph. Using a factory is a tad +/// verbose but it works well with the ait infrastructure. +class variable_sensitivity_dependence_domain_factoryt + : public ai_domain_factoryt +{ +public: + explicit variable_sensitivity_dependence_domain_factoryt( + variable_sensitivity_dependence_grapht &_dg) + : dg(_dg) + { + } + + std::unique_ptr make(locationt l) const override + { + auto node_id = dg.add_node(); + dg.nodes[node_id].PC = l; + auto p = util_make_unique(node_id); + CHECK_RETURN(p->is_bottom()); + + return std::unique_ptr(p.release()); + } + +private: + variable_sensitivity_dependence_grapht &dg; +}; + +variable_sensitivity_dependence_grapht::variable_sensitivity_dependence_grapht( + const goto_functionst &goto_functions, + const namespacet &_ns) + : ai_three_way_merget( + util_make_unique>(), + util_make_unique(*this), + util_make_unique()), + goto_functions(goto_functions), + ns(_ns) +{ +} + +void variable_sensitivity_dependence_grapht::add_dep( + vs_dep_edget::kindt kind, + goto_programt::const_targett from, + goto_programt::const_targett to) +{ + const node_indext n_from = (*this)[from].get_node_id(); + DATA_INVARIANT(n_from < size(), "Node ids must be within the graph"); + const node_indext n_to = (*this)[to].get_node_id(); + DATA_INVARIANT(n_to < size(), "Node ids must be within the graph"); + + // add_edge is redundant as the subsequent operations also insert + // entries into the edge maps (implicitly) + + // add_edge(n_from, n_to); + + nodes[n_from].out[n_to].add(kind); + nodes[n_to].in[n_from].add(kind); +} diff --git a/src/analyses/variable-sensitivity/variable_sensitivity_dependence_graph.h b/src/analyses/variable-sensitivity/variable_sensitivity_dependence_graph.h new file mode 100644 index 00000000000..fa745c83355 --- /dev/null +++ b/src/analyses/variable-sensitivity/variable_sensitivity_dependence_graph.h @@ -0,0 +1,279 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity variable-sensitivity-dependence-graph + + Author: Diffblue Ltd. + +\*******************************************************************/ + +/// \file +/// A forked and modified version of analyses/dependence_graph.{h,cpp} +/// that uses VSD to track and generate the dependencies. +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_DEPENDENCE_GRAPH_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_DEPENDENCE_GRAPH_H + +#include "three_way_merge_abstract_interpreter.h" +#include "variable_sensitivity_domain.h" + +#include +#include +#include + +#include + +class variable_sensitivity_dependence_grapht; + +class vs_dep_edget +{ +public: + enum class kindt + { + NONE, + CTRL, + DATA, + BOTH + }; + + void add(kindt _kind) + { + switch(kind) + { + case kindt::NONE: + kind = _kind; + break; + case kindt::DATA: + case kindt::CTRL: + if(kind != _kind) + kind = kindt::BOTH; + break; + case kindt::BOTH: + break; + } + } + + kindt get() const + { + return kind; + } + +protected: + kindt kind; +}; + +struct vs_dep_nodet : public graph_nodet +{ + typedef graph_nodet::edget edget; + typedef graph_nodet::edgest edgest; + + goto_programt::const_targett PC; +}; + +class variable_sensitivity_dependence_domaint + : public variable_sensitivity_domaint +{ +public: + typedef grapht::node_indext node_indext; + + explicit variable_sensitivity_dependence_domaint(node_indext id) + : node_id(id), has_values(false), has_changed(false) + { + } + + void transform( + const irep_idt &function_from, + locationt from, + const irep_idt &function_to, + locationt to, + ai_baset &ai, + const namespacet &ns) override; + + void make_bottom() override + { + variable_sensitivity_domaint::make_bottom(); + has_values = tvt(false); + has_changed = false; + domain_data_deps.clear(); + control_deps.clear(); + control_dep_candidates.clear(); + control_dep_calls.clear(); + control_dep_call_candidates.clear(); + } + + void make_top() override + { + variable_sensitivity_domaint::make_top(); + has_values = tvt(true); + has_changed = false; + domain_data_deps.clear(); + control_deps.clear(); + control_dep_candidates.clear(); + control_dep_calls.clear(); + control_dep_call_candidates.clear(); + } + + void make_entry() override + { + make_top(); + } + + bool is_bottom() const override + { + return variable_sensitivity_domaint::is_bottom() && has_values.is_false(); + } + + bool is_top() const override + { + return variable_sensitivity_domaint::is_top() && has_values.is_true(); + } + + bool merge( + const variable_sensitivity_domaint &b, + locationt from, + locationt to) override; + + void merge_three_way_function_return( + const ai_domain_baset &function_call, + const ai_domain_baset &function_start, + const ai_domain_baset &function_end, + const namespacet &ns) override; + + void output(std::ostream &out, const ai_baset &ai, const namespacet &ns) + const override; + + jsont output_json(const ai_baset &ai, const namespacet &ns) const override; + + void populate_dep_graph( + variable_sensitivity_dependence_grapht &, + goto_programt::const_targett) const; + + node_indext get_node_id() const + { + DATA_INVARIANT( + node_id != std::numeric_limits::max(), + "Check for the old uninitialised value"); + return node_id; + } + +private: + node_indext node_id; + tvt has_values; + bool has_changed; + + class dependency_ordert + { + public: + bool operator()( + const goto_programt::const_targett &a, + const goto_programt::const_targett &b) const + { + return a->location_number < b->location_number; + } + }; + typedef std:: + map, dependency_ordert> + data_depst; + data_depst domain_data_deps; + + typedef std::map control_depst; + control_depst control_deps; + + typedef std::set control_dep_candidatest; + control_dep_candidatest control_dep_candidates; + + typedef std::set control_dep_callst; + control_dep_callst control_dep_calls; + control_dep_callst control_dep_call_candidates; + + void eval_data_deps(const exprt &expr, const namespacet &ns, data_depst &deps) + const; + + void control_dependencies( + const irep_idt &from_function, + goto_programt::const_targett from, + const irep_idt &to_function, + goto_programt::const_targett to, + variable_sensitivity_dependence_grapht &dep_graph); + + void data_dependencies( + goto_programt::const_targett from, + goto_programt::const_targett to, + variable_sensitivity_dependence_grapht &dep_graph, + const namespacet &ns); + + bool merge_control_dependencies( + const control_depst &other_control_deps, + const control_dep_candidatest &other_control_dep_candidates, + const control_dep_callst &other_control_dep_calls, + const control_dep_callst &other_control_dep_call_candidates); +}; + +class variable_sensitivity_dependence_domain_factoryt; + +class variable_sensitivity_dependence_grapht : public ai_three_way_merget, + public grapht +{ +protected: + using ai_baset::get_state; + + // Legacy-style mutable access to the storage + virtual statet &get_state(locationt l) + { + auto &s = dynamic_cast(*storage); + return s.get_state(l, *domain_factory); + } + + variable_sensitivity_dependence_domaint &operator[](locationt l) + { + return dynamic_cast( + get_state(l)); + } + +public: + using grapht::operator[]; + + friend class variable_sensitivity_dependence_domaint; + + typedef std::map post_dominators_mapt; + + explicit variable_sensitivity_dependence_grapht( + const goto_functionst &goto_functions, + const namespacet &_ns); + + void + initialize(const irep_idt &function_id, const goto_programt &goto_program) + { + ai_recursive_interproceduralt::initialize(function_id, goto_program); + } + + void finalize() + { + for(const auto &location_state : + static_cast(*storage).internal()) + { + std::static_pointer_cast( + location_state.second) + ->populate_dep_graph(*this, location_state.first); + } + } + + void add_dep( + vs_dep_edget::kindt kind, + goto_programt::const_targett from, + goto_programt::const_targett to); + + post_dominators_mapt &cfg_post_dominators() + { + return post_dominators; + } + +protected: + friend variable_sensitivity_dependence_domain_factoryt; + friend variable_sensitivity_dependence_domaint; + const goto_functionst &goto_functions; + const namespacet &ns; + + post_dominators_mapt post_dominators; +}; + +// NOLINTNEXTLINE(whitespace/line_length) +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_DEPENDENCE_GRAPH_H diff --git a/src/analyses/variable-sensitivity/variable_sensitivity_domain.cpp b/src/analyses/variable-sensitivity/variable_sensitivity_domain.cpp new file mode 100644 index 00000000000..fb631ba1b87 --- /dev/null +++ b/src/analyses/variable-sensitivity/variable_sensitivity_domain.cpp @@ -0,0 +1,451 @@ +/*******************************************************************\ + +Module: Abstract Interpretation + +Author: Martin Brain + +Date: April 2016 + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "variable_sensitivity_domain.h" +#include + +#ifdef DEBUG +# include +#endif + +void variable_sensitivity_domaint::transform( + const irep_idt &function_from, + locationt from, + const irep_idt &function_to, + locationt to, + ai_baset &ai, + const namespacet &ns) +{ +#ifdef DEBUG + std::cout << "Transform from/to:\n"; + std::cout << from->location_number << " --> " << to->location_number << '\n'; +#endif + + const goto_programt::instructiont &instruction = *from; + switch(instruction.type) + { + case DECL: + { + const abstract_objectt::locationst write_location = {from}; + abstract_object_pointert top_object = + abstract_state + .abstract_object_factory( + to_code_decl(instruction.code).symbol().type(), ns, true) + ->update_location_context(write_location, true); + abstract_state.assign( + to_code_decl(instruction.code).symbol(), top_object, ns); + } + // We now store top. + break; + + case DEAD: + { + // Remove symbol from map, the only time this occurs now (keep TOP.) + // It should be the case that DEAD only provides symbols for deletion. + const exprt &expr = to_code_dead(instruction.code).symbol(); + if(expr.id() == ID_symbol) + { + abstract_state.erase(to_symbol_expr(expr)); + } + } + break; + + case ASSIGN: + { + // TODO : check return values + const code_assignt &inst = to_code_assign(instruction.code); + const abstract_objectt::locationst write_location = {from}; + abstract_object_pointert rhs = + abstract_state.eval(inst.rhs(), ns) + ->update_location_context(write_location, true); + abstract_state.assign(inst.lhs(), rhs, ns); + } + break; + + case GOTO: + { + if(1) // (flow_sensitivity == FLOW_SENSITIVE) + { + // Get the next line + locationt next = from; + next++; + // Is this a GOTO to the next line (i.e. pointless) + if(next != from->get_target()) + { + if(to == from->get_target()) + { + // The AI is exploring the branch where the jump is taken + abstract_state.assume(instruction.guard, ns); + } + else + { + // Exploring the path where the jump is not taken - therefore assume + // the condition is false + abstract_state.assume(not_exprt(instruction.guard), ns); + } + } + // ignore jumps to the next line, we can assume nothing + } + } + break; + + case ASSUME: + abstract_state.assume(instruction.guard, ns); + break; + + case FUNCTION_CALL: + { + transform_function_call(from, to, ai, ns); + break; + } + + case END_FUNCTION: + { + // erase parameters + + const irep_idt id = function_from; + const symbolt &symbol = ns.lookup(id); + + const code_typet &type = to_code_type(symbol.type); + + for(const auto ¶m : type.parameters()) + { + // Top the arguments to the function + abstract_state.assign( + symbol_exprt(param.get_identifier(), param.type()), + abstract_state.abstract_object_factory(param.type(), ns, true, false), + ns); + } + + break; + } + + /***************************************************************/ + + case ASSERT: + // Conditions on the program, do not alter the data or information + // flow and thus can be ignored. + // Checking of assertions can only be reasonably done once the fix-point + // has been computed, i.e. after all of the calls to transform. + break; + + case SKIP: + case LOCATION: + // Can ignore + break; + + case RETURN: + throw "return instructions should be removed first"; + + case START_THREAD: + case END_THREAD: + case ATOMIC_BEGIN: + case ATOMIC_END: + throw "threading not supported"; + + case THROW: + case CATCH: + throw "exceptions not handled"; + + case OTHER: + // throw "other"; + break; + + case NO_INSTRUCTION_TYPE: + break; + case INCOMPLETE_GOTO: + break; + default: + throw "unrecognised instruction type"; + } + + DATA_INVARIANT(abstract_state.verify(), "Structural invariant"); +} + +void variable_sensitivity_domaint::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + abstract_state.output(out, ai, ns); +} + +void variable_sensitivity_domaint::make_bottom() +{ + abstract_state.make_bottom(); + return; +} + +void variable_sensitivity_domaint::make_top() +{ + abstract_state.make_top(); +} + +void variable_sensitivity_domaint::make_entry() +{ + abstract_state.make_top(); +} + +bool variable_sensitivity_domaint::merge( + const variable_sensitivity_domaint &b, + locationt from, + locationt to) +{ +#ifdef DEBUG + std::cout << "Merging from/to:\n " << from->location_number << " --> " + << to->location_number << '\n'; +#endif + + // Use the abstract_environment merge + bool any_changes = abstract_state.merge(b.abstract_state); + + DATA_INVARIANT(abstract_state.verify(), "Structural invariant"); + return any_changes; +} + +bool variable_sensitivity_domaint::ai_simplify( + exprt &condition, + const namespacet &ns) const +{ + abstract_object_pointert res = abstract_state.eval(condition, ns); + exprt c = res->to_constant(); + + if(c.id() == ID_nil) + { + bool no_simplification = true; + + // Try to simplify recursively any child operations + for(exprt &op : condition.operands()) + { + no_simplification &= ai_simplify(op, ns); + } + + return no_simplification; + } + else + { + bool condition_changed = (condition != c); + condition = c; + return !condition_changed; + } +} + +bool variable_sensitivity_domaint::is_bottom() const +{ + return abstract_state.is_bottom(); +} + +bool variable_sensitivity_domaint::is_top() const +{ + return abstract_state.is_top(); +} + +std::vector variable_sensitivity_domaint::get_modified_symbols( + const variable_sensitivity_domaint &other) const +{ + return abstract_environmentt::modified_symbols( + abstract_state, other.abstract_state); +} + +void variable_sensitivity_domaint::transform_function_call( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns) +{ + PRECONDITION(from->type == FUNCTION_CALL); + + const code_function_callt &function_call = to_code_function_call(from->code); + const exprt &function = function_call.function(); + + const locationt next = std::next(from); + + if(function.id() == ID_symbol) + { + // called function identifier + const symbol_exprt &symbol_expr = to_symbol_expr(function); + const irep_idt function_id = symbol_expr.get_identifier(); + + const code_function_callt::argumentst &called_arguments = + function_call.arguments(); + + if(to->location_number == next->location_number) + { + if(ignore_function_call_transform(function_id)) + { + return; + } + + if(0) // Sound on opaque function calls + { + abstract_state.havoc("opaque function call"); + } + else + { + // For any parameter that is a non-const pointer, top the value it is + // pointing at. + for(const exprt &called_arg : called_arguments) + { + if( + called_arg.type().id() == ID_pointer && + !called_arg.type().subtype().get_bool(ID_C_constant)) + { + abstract_object_pointert pointer_value = + abstract_state.eval(called_arg, ns); + + CHECK_RETURN(pointer_value); + + // Write top to the pointer + pointer_value->write( + abstract_state, + ns, + std::stack(), + nil_exprt(), + abstract_state.abstract_object_factory( + called_arg.type().subtype(), ns, true), + false); + } + } + + // Top any global values + for(const auto &symbol : ns.get_symbol_table().symbols) + { + if(symbol.second.is_static_lifetime) + { + abstract_state.assign( + symbol_exprt(symbol.first, symbol.second.type), + abstract_state.abstract_object_factory( + symbol.second.type, ns, true), + ns); + } + } + } + } + else + { + // we have an actual call + const symbolt &symbol = ns.lookup(function_id); + const code_typet &code_type = to_code_type(symbol.type); + const code_typet::parameterst &declaration_parameters = + code_type.parameters(); + + code_typet::parameterst::const_iterator parameter_it = + declaration_parameters.begin(); + + for(const exprt &called_arg : called_arguments) + { + if(parameter_it == declaration_parameters.end()) + { + INVARIANT( + code_type.has_ellipsis(), "Only case for insufficient args"); + break; + } + + // Evaluate the expression that is being + // passed into the function call (called_arg) + abstract_object_pointert param_val = + abstract_state.eval(called_arg, ns) + ->update_location_context({from}, true); + + // Assign the evaluated value to the symbol associated with the + // parameter of the function + const symbol_exprt parameter_expr( + parameter_it->get_identifier(), called_arg.type()); + abstract_state.assign(parameter_expr, param_val, ns); + + ++parameter_it; + } + + // Too few arguments so invalid code + DATA_INVARIANT( + parameter_it == declaration_parameters.end(), + "Number of arguments should match parameters"); + } + } + else + { + PRECONDITION(to == next); + abstract_state.havoc("unknown opaque function call"); + } +} + +bool variable_sensitivity_domaint::ignore_function_call_transform( + const irep_idt &function_id) const +{ + static const std::set ignored_internal_function = { + CPROVER_PREFIX "set_must", + CPROVER_PREFIX "get_must", + CPROVER_PREFIX "set_may", + CPROVER_PREFIX "get_may", + CPROVER_PREFIX "cleanup", + CPROVER_PREFIX "clear_may", + CPROVER_PREFIX "clear_must"}; + + return ignored_internal_function.find(function_id) != + ignored_internal_function.cend(); +} + +void variable_sensitivity_domaint::merge_three_way_function_return( + const ai_domain_baset &function_call, + const ai_domain_baset &function_start, + const ai_domain_baset &function_end, + const namespacet &ns) +{ + const variable_sensitivity_domaint &cast_function_call = + static_cast(function_call); + + const variable_sensitivity_domaint &cast_function_start = + static_cast(function_start); + + const variable_sensitivity_domaint &cast_function_end = + static_cast(function_end); + + const std::vector &modified_symbol_names = + cast_function_start.get_modified_symbols(cast_function_end); + + std::vector modified_symbols; + modified_symbols.reserve(modified_symbol_names.size()); + std::transform( + modified_symbol_names.begin(), + modified_symbol_names.end(), + std::back_inserter(modified_symbols), + [&ns](const irep_idt &id) { return ns.lookup(id).symbol_expr(); }); + + abstract_state = cast_function_call.abstract_state; + apply_domain(modified_symbols, cast_function_end, ns); + + return; +} + +void variable_sensitivity_domaint::apply_domain( + std::vector modified_symbols, + const variable_sensitivity_domaint &source, + const namespacet &ns) +{ + for(const auto &symbol : modified_symbols) + { + abstract_object_pointert value = source.abstract_state.eval(symbol, ns); + abstract_state.assign(symbol, value, ns); + } +} + +#ifdef ENABLE_STATS +abstract_object_statisticst +variable_sensitivity_domaint::gather_statistics(const namespacet &ns) const +{ + return abstract_state.gather_statistics(ns); +} +#endif diff --git a/src/analyses/variable-sensitivity/variable_sensitivity_domain.h b/src/analyses/variable-sensitivity/variable_sensitivity_domain.h new file mode 100644 index 00000000000..eda719e1c34 --- /dev/null +++ b/src/analyses/variable-sensitivity/variable_sensitivity_domain.h @@ -0,0 +1,257 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Thomas Kiley, thomas.kiley@diffblue.com + +\*******************************************************************/ + +/// \file +/// There are different ways of handling arrays, structures, unions and +/// pointers. interval_domaint and constant_propagator_domaint +/// basically ignores them which is imprecise at best and out-right +/// wrong at worst. For one project we needed to do better. We could +/// have implemented a particular way of handling them in an existing +/// domain, created a new one with it, etc. This would work but it +/// means duplicate code and it is is inflexible when the same person +/// / the next person comes along and says "actually, we really care +/// about the pointer precision but less so the array so could you +/// just ...". Thus the idea was to do this properly: +/// +/// 1. Build a "non-relational domain" and allow the abstractions used for +/// individual variables to be different. +/// +/// 2. Give the user the option of which abstractions are used for structs, +/// unions, arrays and pointers. These are the sensitivity options +/// discussed above. +/// +/// 3. Have the domain options control which kind of abstractions are used +/// for the individual values, i.e. constants, intervals, etc. +/// +/// +/// This is implemented in three parts: +/// +/// abstract_objectt : The base / interface for abstractions of a single +/// variable. The interface for these is effectively create (as top, +/// bottom, from a constant or copy), transform, merge, convert to constant +/// if possible. Child classes add additional bits of interface, for +/// example array abstractions need a "read element" and a "write element" +/// method, structures need a "read field" and "write field", etc. These +/// objects are intended to be immutable and thus operations tend to produce +/// pointers. This is so that we can easily produce copy-on-write maps of +/// them which we need for scaling and performance. There are also children +/// of these for the constant abstraction of one value, the interval +/// abstraction of one value (to be implemented), etc. +/// +/// abstract_environment : This contains the map from variable names for +/// abstract_objectt's (the "non-relational" part of the domain). The map +/// itself if copy-on-write for performance and scalability but this is all +/// wrapped up nicely in danpoe's sharing_map. The interface here is +/// evaluate (exprt -> abstract_objectt*), assign (name, abstract_objectt* +/// -> bool), assume (exprt -> bool) and merge. It has a factory to build +/// abstract_objectt* from types or constants but apart from that, doesn't +/// know anything about which actual abstract_objectt's are being used. As +/// long as program variables that are arrays have an abstract_objectt which +/// has the array interface, and so on for structs, unions, etc. then the +/// abstractions used for values can be freely mixed and matched in any way +/// the user can get the factory to build. +/// +/// variable_sensitivity_domaint : Implements the ai_domain_baset interface +/// using an abstract_environment. The only real code here is the +/// 'transform' method which looks at the instruction type and converts that +/// into calls to eval, assume, assign and merge. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_DOMAIN_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_DOMAIN_H + +#include +#include +#include + +#include +#include + +class variable_sensitivity_domaint : public ai_domain_baset +{ +public: + /// Compute the abstract transformer for a single instruction + /// + /// \param function_from: the name of the function containing from + /// \param from: the instruction before the abstract domain + /// \param function_to: the name of the function containing to + /// \param to: the instruction after the abstract domain + /// \param ai: the abstract interpreter + /// \param ns: the namespace + void transform( + const irep_idt &function_from, + locationt from, + const irep_idt &function_to, + locationt to, + ai_baset &ai, + const namespacet &ns) override; + + /// Sets the domain to bottom (no states / unreachable). + void make_bottom() override; + + /// Sets the domain to top (all states). + void make_top() override; + + /// Set up a reasonable entry-point state + void make_entry() override; + + /// Basic text output of the abstract domain + /// + /// \param out: the output stream + /// \param ai: the abstract interpreter + /// \param ns: the namespace + void output(std::ostream &out, const ai_baset &ai, const namespacet &ns) + const override; + + void output(std::ostream &out) const; + + /// Computes the join between "this" and "b". + /// + /// \param b: the other domain + /// \param from: it's preceding location + /// \param to: it's current location + /// + /// \return true if something has changed. + virtual bool + merge(const variable_sensitivity_domaint &b, locationt from, locationt to); + + /// Perform a context aware merge of the changes that have been applied + /// between function_start and the current state. Anything that has not been + /// modified will be taken from the \p function_call domain. + /// \param function_call: The local of the merge - values from here will be + /// taken if they have not been modified + /// \param function_start: The base of the merge - changes that have been made + /// between here and the end will be retained. + /// \param function_end: The end of the merge - changes that have been made + /// between the start and here will be retained. + /// \param ns: The global namespace + virtual void merge_three_way_function_return( + const ai_domain_baset &function_call, + const ai_domain_baset &function_start, + const ai_domain_baset &function_end, + const namespacet &ns); + + /// Use the information in the domain to simplify the expression + /// with respect to the current location. This may be able to + /// reduce some values to constants. + /// + /// \param condition: the expression to simplify + /// \param ns: the namespace + /// + /// \return True if no simplification was made + bool ai_simplify(exprt &condition, const namespacet &ns) const override; + + /// Find out if the domain is currently unreachable. + /// + /// \return True if the domain is bottom (i.e. unreachable). + bool is_bottom() const override; + + /// Is the domain completely top at this state + /// + /// \return True if the domain is top + bool is_top() const override; + + virtual abstract_object_pointert + eval(const exprt &expr, const namespacet &ns) const + { + return abstract_state.eval(expr, ns); + } + +private: + /// Used by variable_sensitivity_domaint::transform to handle + /// FUNCTION_CALL transforms. This is copying the values of the arguments + /// into new symbols corresponding to the declared parameter names. + /// + /// If the function call is opaque we currently top the return value + /// and the value of any things whose address is passed into the function. + /// + /// \param from: the location to transform from which is a function call + /// \param to: the destination of the transform + /// (potentially inside the function call) + /// \param ai: the abstract interpreter + /// \param ns: the namespace of the current state + void transform_function_call( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns); + + /// Used to specify which CPROVER internal functions should be skipped + /// over when doing function call transforms + /// + /// \param function_id: the name of the function being called + /// + /// \return Returns true if the function should be ignored + bool ignore_function_call_transform(const irep_idt &function_id) const; + + /// Get symbols that have been modified since this domain and other + /// \param other: The domain that things may have been modified in + /// \return A list of symbols whose write location is different + std::vector + get_modified_symbols(const variable_sensitivity_domaint &other) const; + + /// Given a domain and some symbols, apply those symbols values + /// to the current domain + /// \param modified_symbols: The symbols to write + /// \param target: The domain to take the values from + /// \param ns: The global namespace + void apply_domain( + std::vector modified_symbols, + const variable_sensitivity_domaint &target, + const namespacet &ns); + + abstract_environmentt abstract_state; + +#ifdef ENABLE_STATS +public: + abstract_object_statisticst gather_statistics(const namespacet &ns) const; +#endif +}; + +#ifdef ENABLE_STATS +template <> +struct get_domain_statisticst +{ + abstract_object_statisticst total_statistics = {}; + void + add_entry(const variable_sensitivity_domaint &domain, const namespacet &ns) + { + auto statistics = domain.gather_statistics(ns); + total_statistics.number_of_interval_abstract_objects += + statistics.number_of_interval_abstract_objects; + total_statistics.number_of_globals += statistics.number_of_globals; + total_statistics.number_of_single_value_intervals += + statistics.number_of_single_value_intervals; + total_statistics.number_of_constants += statistics.number_of_constants; + total_statistics.number_of_pointers += statistics.number_of_constants; + total_statistics.number_of_arrays += statistics.number_of_arrays; + total_statistics.number_of_structs += statistics.number_of_arrays; + total_statistics.objects_memory_usage += statistics.objects_memory_usage; + } + + void print(std::ostream &out) const + { + out << "<< Begin Variable Sensitivity Domain Statistics >>\n" + << " Memory Usage: " + << total_statistics.objects_memory_usage.to_string() << '\n' + << " Number of structs: " << total_statistics.number_of_structs << '\n' + << " Number of arrays: " << total_statistics.number_of_arrays << '\n' + << " Number of pointers: " << total_statistics.number_of_pointers + << '\n' + << " Number of constants: " << total_statistics.number_of_constants + << '\n' + << " Number of intervals: " + << total_statistics.number_of_interval_abstract_objects << '\n' + << " Number of single value intervals: " + << total_statistics.number_of_single_value_intervals << '\n' + << " Number of globals: " << total_statistics.number_of_globals << '\n' + << "<< End Variable Sensitivity Domain Statistics >>\n"; + } +}; +#endif + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_DOMAIN_H diff --git a/src/analyses/variable-sensitivity/variable_sensitivity_object_factory.cpp b/src/analyses/variable-sensitivity/variable_sensitivity_object_factory.cpp new file mode 100644 index 00000000000..01cdc1cd19d --- /dev/null +++ b/src/analyses/variable-sensitivity/variable_sensitivity_object_factory.cpp @@ -0,0 +1,138 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Owen Jones, owen.jones@diffblue.com + +\*******************************************************************/ +#include "variable_sensitivity_object_factory.h" +#include "interval_array_abstract_object.h" +#include "util/namespace.h" + +variable_sensitivity_object_factoryt + variable_sensitivity_object_factoryt::s_instance; + +variable_sensitivity_object_factoryt::ABSTRACT_OBJECT_TYPET +variable_sensitivity_object_factoryt::get_abstract_object_type(const typet type) +{ + ABSTRACT_OBJECT_TYPET abstract_object_type = TWO_VALUE; + + if( + type.id() == ID_signedbv || type.id() == ID_unsignedbv || + type.id() == ID_fixedbv || type.id() == ID_c_bool || type.id() == ID_bool || + type.id() == ID_integer || type.id() == ID_c_bit_field) + { + abstract_object_type = + configuration.advanced_sensitivities.intervals ? INTERVAL : CONSTANT; + } + else if(type.id() == ID_floatbv) + { + abstract_object_type = CONSTANT; + } + else if(type.id() == ID_array) + { + abstract_object_type = configuration.primitive_sensitivity.array_sensitivity + ? ARRAY_SENSITIVE + : ARRAY_INSENSITIVE; + } + else if(type.id() == ID_pointer) + { + abstract_object_type = + configuration.primitive_sensitivity.pointer_sensitivity + ? POINTER_SENSITIVE + : POINTER_INSENSITIVE; + } + else if(type.id() == ID_struct) + { + abstract_object_type = + configuration.primitive_sensitivity.struct_sensitivity + ? STRUCT_SENSITIVE + : STRUCT_INSENSITIVE; + } + else if(type.id() == ID_union) + { + abstract_object_type = UNION_INSENSITIVE; + } + if( + configuration.advanced_sensitivities.value_set && + (abstract_object_type == INTERVAL || abstract_object_type == CONSTANT || + abstract_object_type == POINTER_INSENSITIVE || + abstract_object_type == POINTER_SENSITIVE)) + { + abstract_object_type = VALUE_SET; + } + + return abstract_object_type; +} + +abstract_object_pointert +variable_sensitivity_object_factoryt::get_abstract_object( + const typet type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) +{ + if(!initialized) + { + throw "variable_sensitivity_object_factoryt::get_abstract_object() " \ + "called without first calling " \ + "variable_sensitivity_object_factoryt::set_options()\n"; + } + + typet followed_type = ns.follow(type); + ABSTRACT_OBJECT_TYPET abstract_object_type = + get_abstract_object_type(followed_type); + + switch(abstract_object_type) + { + case CONSTANT: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case INTERVAL: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case ARRAY_SENSITIVE: + return configuration.advanced_sensitivities.intervals + ? initialize_abstract_object( + followed_type, top, bottom, e, environment, ns) + : initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case ARRAY_INSENSITIVE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case POINTER_SENSITIVE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case POINTER_INSENSITIVE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case STRUCT_SENSITIVE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case STRUCT_INSENSITIVE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case UNION_INSENSITIVE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case TWO_VALUE: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + case VALUE_SET: + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + default: + UNREACHABLE; + return initialize_abstract_object( + followed_type, top, bottom, e, environment, ns); + } +} + +void variable_sensitivity_object_factoryt::set_options( + const vsd_configt &options) +{ + this->configuration = options; + initialized = true; +} diff --git a/src/analyses/variable-sensitivity/variable_sensitivity_object_factory.h b/src/analyses/variable-sensitivity/variable_sensitivity_object_factory.h new file mode 100644 index 00000000000..02ea4553e1f --- /dev/null +++ b/src/analyses/variable-sensitivity/variable_sensitivity_object_factory.h @@ -0,0 +1,284 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity + + Author: Owen Jones owen.jones@diffblue.com + +\*******************************************************************/ + +/// \file +/// Tracks the user-supplied configuration for VSD and build the +/// correct type of abstract object when needed. Note this is a factory +/// within the domain and so is lower-level than the abstract domain +/// factory that is part of the ai_baset interface. + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_OBJECT_FACTORY_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_OBJECT_FACTORY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct vsd_configt +{ + struct + { + bool struct_sensitivity; + bool array_sensitivity; + bool pointer_sensitivity; + } primitive_sensitivity; + + struct + { + bool data_dependency_context; + bool last_write_context; + } context_tracking; + + struct + { + bool intervals; + bool value_set; + } advanced_sensitivities; + + static vsd_configt from_options(const optionst &options) + { + vsd_configt config{}; + + if( + options.get_bool_option("value-set") && + options.get_bool_option("data-dependencies")) + { + throw invalid_command_line_argument_exceptiont{ + "Value set is not currently supported with data dependency analysis", + "--value-set --data-dependencies", + "--data-dependencies"}; + } + + config.primitive_sensitivity.struct_sensitivity = + options.get_bool_option("structs"); + config.primitive_sensitivity.array_sensitivity = + options.get_bool_option("arrays"); + config.primitive_sensitivity.pointer_sensitivity = + options.get_bool_option("pointers"); + + // This should always be on (for efficeny with 3-way merge) + // Does not work with value set + config.context_tracking.last_write_context = + !options.get_bool_option("value-set"); + config.context_tracking.data_dependency_context = + options.get_bool_option("data-dependencies"); + config.advanced_sensitivities.intervals = + options.get_bool_option("interval"); + config.advanced_sensitivities.value_set = + options.get_bool_option("value-set"); + + return config; + } + + static vsd_configt constant_domain() + { + vsd_configt config{}; + config.primitive_sensitivity.pointer_sensitivity = true; + config.primitive_sensitivity.array_sensitivity = true; + config.primitive_sensitivity.struct_sensitivity = true; + config.context_tracking.last_write_context = true; + return config; + } + + static vsd_configt value_set() + { + vsd_configt config{}; + config.primitive_sensitivity.pointer_sensitivity = true; + config.primitive_sensitivity.array_sensitivity = true; + config.primitive_sensitivity.struct_sensitivity = true; + config.advanced_sensitivities.value_set = true; + return config; + } + + static vsd_configt intervals() + { + vsd_configt config{}; + config.primitive_sensitivity.pointer_sensitivity = true; + config.primitive_sensitivity.array_sensitivity = true; + config.primitive_sensitivity.struct_sensitivity = true; + config.context_tracking.last_write_context = true; + config.advanced_sensitivities.intervals = true; + return config; + } +}; + +class variable_sensitivity_object_factoryt +{ +public: + static variable_sensitivity_object_factoryt &instance() + { + return s_instance; + } + + /// Get the appropriate abstract object for the variable under + /// consideration. + /// + /// \param type: the type of the variable + /// \param top: whether the abstract object should be top in the + /// two-value domain + /// \param bottom: whether the abstract object should be bottom in the + /// two-value domain + /// \param e: if top and bottom are false this expression is used as the + /// starting pointer for the abstract object + /// \param environment: the current abstract environment + /// \param ns: namespace, used when following the input type + /// + /// \return An abstract object of the appropriate type. + abstract_object_pointert get_abstract_object( + const typet type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns); + + /// Called once to record the appropriate variables from the command line + /// options so that they can be accessed easily when they are needed. + /// + /// \param options: the command line options + void set_options(const vsd_configt &options); + +private: + variable_sensitivity_object_factoryt() : initialized(false) + { + } + static variable_sensitivity_object_factoryt s_instance; + enum ABSTRACT_OBJECT_TYPET + { + TWO_VALUE, + CONSTANT, + INTERVAL, + ARRAY_SENSITIVE, + ARRAY_INSENSITIVE, + POINTER_SENSITIVE, + POINTER_INSENSITIVE, + STRUCT_SENSITIVE, + STRUCT_INSENSITIVE, + // TODO: plug in UNION_SENSITIVE HERE + UNION_INSENSITIVE, + VALUE_SET + }; + + /// Decide which abstract object type to use for the variable in question. + /// + /// \param type: the type of the variable the abstract object is + /// meant to represent + /// + /// \return An enum indicating the abstract object type to use. + ABSTRACT_OBJECT_TYPET get_abstract_object_type(const typet type); + template + abstract_object_pointert initialize_abstract_object( + const typet type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &enviroment, + const namespacet &ns); + template + abstract_object_pointert initialize_context_abstract_object( + const typet type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &enviroment, + const namespacet &ns); + vsd_configt configuration; + bool initialized; +}; + +/// Function: variable_sensitivity_object_factoryt::initialize_abstract_object +/// Initialize the abstract object class and return it. +/// +/// \param type: the type of the variable +/// \param top: whether the abstract object should be top in the +/// two-value domain +/// \param bottom: whether the abstract object should be bottom in the +/// two-value domain +/// \param e: if top and bottom are false this expression is used as the +/// starting pointer for the abstract object +/// \param environment: the current abstract environment +/// \param ns: namespace, used when following the input type +/// +/// \return An abstract object of the appropriate type. +/// +template +abstract_object_pointert +variable_sensitivity_object_factoryt::initialize_abstract_object( + const typet type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) +{ + if(configuration.context_tracking.data_dependency_context) + return initialize_context_abstract_object< + abstract_object_classt, + data_dependency_contextt>(type, top, bottom, e, environment, ns); + if(configuration.context_tracking.last_write_context) + return initialize_context_abstract_object< + abstract_object_classt, + write_location_contextt>(type, top, bottom, e, environment, ns); + else + { + if(top || bottom) + { + return abstract_object_pointert( + new abstract_object_classt(type, top, bottom)); + } + else + { + PRECONDITION(type == ns.follow(e.type())); + return abstract_object_pointert( + new abstract_object_classt(e, environment, ns)); + } + } +} + +template +abstract_object_pointert +variable_sensitivity_object_factoryt::initialize_context_abstract_object( + const typet type, + bool top, + bool bottom, + const exprt &e, + const abstract_environmentt &environment, + const namespacet &ns) +{ + if(top || bottom) + { + return abstract_object_pointert(new context_classt( + abstract_object_pointert(new abstract_object_classt(type, top, bottom)), + type, + top, + bottom)); + } + else + { + PRECONDITION(type == ns.follow(e.type())); + return abstract_object_pointert(new context_classt( + abstract_object_pointert(new abstract_object_classt(e, environment, ns)), + e, + environment, + ns)); + } +} + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VARIABLE_SENSITIVITY_OBJECT_FACTORY_H // NOLINT(*) diff --git a/src/analyses/variable-sensitivity/write_location_context.cpp b/src/analyses/variable-sensitivity/write_location_context.cpp new file mode 100644 index 00000000000..41b501969a3 --- /dev/null +++ b/src/analyses/variable-sensitivity/write_location_context.cpp @@ -0,0 +1,351 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity write_location_context + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include + +#include +#include + +#include "write_location_context.h" + +/** + * Update the location context for an abstract object, potentially + * propagating the update to any children of this abstract object. + * + * \param locations the set of locations to be updated + * \param update_sub_elements if true, propogate the update operation to any + * children of this abstract object + * + * \return a clone of this abstract object with its location context + * updated + */ +abstract_object_pointert write_location_contextt::update_location_context( + const abstract_objectt::locationst &locations, + const bool update_sub_elements) const +{ + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + if(update_sub_elements) + { + abstract_object_pointert visited_child = + child_abstract_object + ->update_location_context(locations, update_sub_elements) + ->visit_sub_elements(location_update_visitort(locations)); + result->set_child(visited_child); + } + + result->set_last_written_locations(locations); + return result; +} + +abstract_objectt::locationst +write_location_contextt::get_last_written_locations() const +{ + return last_written_locations; +} + +/** + * A helper function to evaluate writing to a component of an + * abstract object. More precise abstractions may override this to + * update what they are storing for a specific component. + * + * \param environment the abstract environment + * \param ns the current namespace + * \param stack the remaining stack of expressions on the LHS to evaluate + * \param specifier the expression uses to access a specific component + * \param value the value we are trying to write to the component + * \param merging_write if true, this and all future writes will be merged + * with the current value + * + * \return the abstract_objectt representing the result of writing + * to a specific component. + */ +abstract_object_pointert write_location_contextt::write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const +{ + abstract_object_pointert updated_child = child_abstract_object->write( + environment, ns, stack, specifier, value, merging_write); + + // Only perform an update if the write to the child has in fact changed it... + if(updated_child == child_abstract_object) + return shared_from_this(); + + // Need to ensure the result of the write is still wrapped in a dependency + // context + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + + // Update the child and record the updated write locations + result->set_child(updated_child); + auto value_context = + std::dynamic_pointer_cast(value); + + if(value_context) + { + result->set_last_written_locations( + value_context->get_last_written_locations()); + } + + return result; +} + +/** + * Create a new abstract object that is the result of merging this abstract + * object with a given abstract_object + * + * \param other the abstract object to merge with + * + * \return the result of the merge, or 'this' if the merge would not change + * the current abstract object + */ +abstract_object_pointert +write_location_contextt::merge(abstract_object_pointert other) const +{ + auto cast_other = + std::dynamic_pointer_cast(other); + + if(cast_other) + { + bool child_modified = false; + + auto merged_child = abstract_objectt::merge( + child_abstract_object, cast_other->child_abstract_object, child_modified); + + abstract_objectt::locationst location_union = + get_location_union(cast_other->get_last_written_locations()); + // If the union is larger than the initial set, then update. + bool merge_locations = + location_union.size() > get_last_written_locations().size(); + + if(child_modified || merge_locations) + { + const auto &result = + std::dynamic_pointer_cast(mutable_clone()); + if(child_modified) + { + result->set_child(merged_child); + } + if(merge_locations) + { + result->set_last_written_locations(location_union); + } + + return result; + } + + return shared_from_this(); + } + + return abstract_objectt::merge(other); +} + +/** + * Helper function for abstract_objectt::abstract_object_merge to perform any + * additional actions after the base abstract_object_merge has completed its + * actions but immediately prior to it returning. As such, this function gives + * the ability to perform additional work for a merge. + * + * For the dependency context, this additional work is the tracking of + * last_written_locations across the merge + * + * \param other the object to merge with this + * + * \return the result of the merge + */ +abstract_object_pointert +write_location_contextt::abstract_object_merge_internal( + const abstract_object_pointert other) const +{ + auto other_context = + std::dynamic_pointer_cast(other); + + if(other_context) + { + abstract_objectt::locationst location_union = + get_location_union(other_context->get_last_written_locations()); + + // If the union is larger than the initial set, then update. + if(location_union.size() > get_last_written_locations().size()) + { + abstract_object_pointert result = mutable_clone(); + return result->update_location_context(location_union, false); + } + } + return shared_from_this(); +} + +/** + * Sets the last written locations for this context + * + * \param locations the locations to set + */ +void write_location_contextt::set_last_written_locations( + const abstract_objectt::locationst &locations) +{ + last_written_locations = locations; +} + +/** + * Output a representation of the value of this abstract object + * + * \param out the stream to write to + * \param ai the abstract interpreter that contains the abstract domain + * (that contains the object ... ) + * \param ns the current namespace + */ +void write_location_contextt::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + context_abstract_objectt::output(out, ai, ns); + + // Output last written locations immediately following the child output + out << " @ "; + output_last_written_locations(out, last_written_locations); +} + +/** + * Construct the union of the location set of the current object, and a + * the provided location set. + * + * \param locations the set of locations to be unioned with this context + * + * \return the union of this objects location set, and 'locations' + */ +abstract_objectt::locationst +write_location_contextt::get_location_union(const locationst &locations) const +{ + locationst existing_locations = get_last_written_locations(); + existing_locations.insert(locations.begin(), locations.end()); + + return existing_locations; +} + +/** + * Determine whether 'this' abstract_object has been modified in comparison + * to a previous 'before' state. + * + * \param before the abstract_object_pointert to use as a reference to + * compare against + * + * \return true if 'this' is considered to have been modified in comparison + * to 'before', false otherwise. + */ +bool write_location_contextt::has_been_modified( + const abstract_object_pointert before) const +{ + if(this == before.get()) + { + // copy-on-write means pointer equality implies no modifications + return false; + } + + auto before_context = + std::dynamic_pointer_cast(before); + + if(!before_context) + { + // The other context is not something we understand, so must assume + // that the abstract_object has been modified + return true; + } + + // Even if the pointers are different, it maybe that it has changed only + // because of a merge operation, rather than an actual write. Given that + // this class has knowledge of where writes have occured, use that + // information to determine if any writes have happened and use that as the + // proxy for whether the value has changed or not. + // + // For two sets of last written locations to match, + // each location in one set must be equal to precisely one location + // in the other, since a set can assume at most one match + const abstract_objectt::locationst &first_write_locations = + before_context->get_last_written_locations(); + const abstract_objectt::locationst &second_write_locations = + get_last_written_locations(); + + class location_ordert + { + public: + bool operator()( + goto_programt::const_targett instruction, + goto_programt::const_targett other_instruction) const + { + return instruction->location_number > other_instruction->location_number; + } + }; + + typedef std::set + sorted_locationst; + + sorted_locationst lhs_location; + for(const auto &entry : first_write_locations) + { + lhs_location.insert(entry); + } + + sorted_locationst rhs_location; + for(const auto &entry : second_write_locations) + { + rhs_location.insert(entry); + } + + abstract_objectt::locationst intersection; + std::set_intersection( + lhs_location.cbegin(), + lhs_location.cend(), + rhs_location.cbegin(), + rhs_location.cend(), + std::inserter(intersection, intersection.end()), + location_ordert()); + bool all_matched = intersection.size() == first_write_locations.size() && + intersection.size() == second_write_locations.size(); + + return !all_matched; +} + +/** + * Internal helper function to format and output a given set of locations + * + * \param out the stream on which to output the locations + * \param locations the set of locations to output + */ +void write_location_contextt::output_last_written_locations( + std::ostream &out, + const abstract_objectt::locationst &locations) +{ + out << "["; + bool comma = false; + + std::set sorted_locations; + for(auto location : locations) + { + sorted_locations.insert(location->location_number); + } + + for(auto location_number : sorted_locations) + { + if(!comma) + { + out << location_number; + comma = true; + } + else + { + out << ", " << location_number; + } + } + out << "]"; +} diff --git a/src/analyses/variable-sensitivity/write_location_context.h b/src/analyses/variable-sensitivity/write_location_context.h new file mode 100644 index 00000000000..229ef7fe383 --- /dev/null +++ b/src/analyses/variable-sensitivity/write_location_context.h @@ -0,0 +1,129 @@ +/*******************************************************************\ + + Module: analyses variable-sensitivity write_location_context + + Author: Diffblue Ltd. + +\*******************************************************************/ + +/** + * \file + * Maintain a context in the variable sensitvity domain that records + * write locations for a given abstract_objectt. This enables more + * accurate merging at three_way_merge. + */ + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_LOCATION_CONTEXT_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_LOCATION_CONTEXT_H + +#include +#include +#include + +/** + * General implementation of an abstract_objectt which tracks the + * last written locations for a given abstract_objectt. + * Instances of this class are constructed with an abstract_object_pointert, + * to which most operations are delegated, while at the same time this + * class handles the tracking of the 'last_written_location' information. + * + * Instances of this class are best constructed via the templated version + * of this, 'context_abstract_objectt' which provides the same + * constructors as the standard 'abstract_objectt' class. + */ +class write_location_contextt : public context_abstract_objectt +{ +public: + explicit write_location_contextt( + const abstract_object_pointert child, + const typet &type) + : context_abstract_objectt(child, type) + { + } + + write_location_contextt( + const abstract_object_pointert child, + const typet &type, + bool top, + bool bottom) + : context_abstract_objectt(child, type, top, bottom) + { + } + + explicit write_location_contextt( + const abstract_object_pointert child, + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) + : context_abstract_objectt(child, expr, environment, ns) + { + } + + virtual ~write_location_contextt() + { + } + + // Standard abstract_objectt interface + + bool has_been_modified(const abstract_object_pointert before) const override; + + abstract_object_pointert update_location_context( + const abstract_objectt::locationst &locations, + const bool update_sub_elements) const override; + + // A visitor class to update the last_written_locations of any visited + // abstract_object with a given set of locations. + class location_update_visitort + : public abstract_objectt::abstract_object_visitort + { + public: + explicit location_update_visitort(const locationst &locations) + : locations(locations) + { + } + + abstract_object_pointert visit(const abstract_object_pointert element) const + { + return element->update_location_context(locations, true); + } + + private: + const locationst &locations; + }; + + locationst get_location_union(const locationst &locations) const; + + void output(std::ostream &out, const class ai_baset &ai, const namespacet &ns) + const override; + +protected: + CLONE + + abstract_object_pointert merge(abstract_object_pointert other) const override; + + abstract_object_pointert abstract_object_merge_internal( + const abstract_object_pointert other) const override; + + abstract_object_pointert write( + abstract_environmentt &environment, + const namespacet &ns, + const std::stack stack, + const exprt &specifier, + const abstract_object_pointert value, + bool merging_write) const override; + + static void output_last_written_locations( + std::ostream &out, + const abstract_objectt::locationst &locations); + + virtual abstract_objectt::locationst get_last_written_locations() const; + +private: + // To enforce copy-on-write these are private and have read-only accessors + abstract_objectt::locationst last_written_locations; + + void + set_last_written_locations(const abstract_objectt::locationst &locations); +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_LOCATION_CONTEXT_H diff --git a/src/analyses/variable-sensitivity/write_stack.cpp b/src/analyses/variable-sensitivity/write_stack.cpp new file mode 100644 index 00000000000..c270ef4bfe9 --- /dev/null +++ b/src/analyses/variable-sensitivity/write_stack.cpp @@ -0,0 +1,264 @@ +/*******************************************************************\ + + Module: Variable Sensitivity Domain + + Author: DiffBlue Limited. + +\*******************************************************************/ + +/// \file +/// Represents a stack of write operations to an addressable memory +/// location + +#include "write_stack.h" + +#include + +#include +#include +#include +#include + +/// Build a topstack +write_stackt::write_stackt() : stack(), top_stack(true) +{ +} + +/// Construct a write stack from an expression +/// \param expr: The expression to represent +/// \param environment: The environment to evaluate any expressions in +/// \param ns: The global namespace +write_stackt::write_stackt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) +{ + top_stack = false; + if(expr.type().id() == ID_array) + { + // We are assigning an array to a pointer, which is equivalent to assigning + // the first element of that arary + // &(expr)[0] + construct_stack_to_pointer( + address_of_exprt(index_exprt(expr, from_integer(0, size_type()))), + environment, + ns); + } + else + { + construct_stack_to_pointer(expr, environment, ns); + } +} + +/// Add to the stack the elements to get to a pointer +/// \param expr: An expression of type pointer to construct the stack to +/// \param environment: The environment to evaluate any expressions in +/// \param ns: The global namespace +void write_stackt::construct_stack_to_pointer( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) +{ + PRECONDITION(expr.type().id() == ID_pointer); + + if(expr.id() == ID_address_of) + { + // resovle reminder, can either be a symbol, member or index of + // otherwise unsupported + construct_stack_to_lvalue( + to_address_of_expr(expr).object(), environment, ns); + } + else if(expr.id() == ID_plus || expr.id() == ID_minus) + { + exprt base; + exprt offset; + const integral_resultt &which_side = + get_which_side_integral(expr, base, offset); + INVARIANT( + which_side != integral_resultt::NEITHER_INTEGRAL, + "An offset must be an integral amount"); + + if(expr.id() == ID_minus) + { + // can't get a valid pointer by subtracting from a constant number + if(which_side == integral_resultt::LHS_INTEGRAL) + { + top_stack = true; + return; + } + offset = unary_minus_exprt(offset); + } + + abstract_object_pointert offset_value = environment.eval(offset, ns); + + add_to_stack( + std::make_shared(offset_value), environment, ns); + + // Build the pointer part + construct_stack_to_pointer(base, environment, ns); + + if(!top_stack) + { + // check the symbol at the bottom of the stack + std::shared_ptr entry = *stack.cbegin(); + INVARIANT( + entry->get_access_expr().id() == ID_symbol, + "The base should be an addressable location (i.e. symbol)"); + + if(entry->get_access_expr().type().id() != ID_array) + { + top_stack = true; + } + } + } + else + { + // unknown expression type - play it safe and set to top + top_stack = true; + } +} + +/// Construct a stack up to a specific l-value (i.e. symbol or position in an +/// array or struct) +/// \param expr: The expression representing a l-value +/// \param environment: The environment to evaluate any expressions in +/// \param ns: The global namespace +void write_stackt::construct_stack_to_lvalue( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns) +{ + if(!top_stack) + { + if(expr.id() == ID_member) + { + add_to_stack(std::make_shared(expr), environment, ns); + construct_stack_to_lvalue( + to_member_expr(expr).struct_op(), environment, ns); + } + else if(expr.id() == ID_symbol) + { + add_to_stack(std::make_shared(expr), environment, ns); + } + else if(expr.id() == ID_index) + { + construct_stack_to_array_index(to_index_expr(expr), environment, ns); + } + else + { + top_stack = true; + } + } +} + +/// Construct a stack for an array position l-value. +/// \param index_expr: The index expression to construct to. +/// \param environment: The environment to evaluate any expressions in +/// \param ns: The global namespace +void write_stackt::construct_stack_to_array_index( + const index_exprt &index_expr, + const abstract_environmentt &environment, + const namespacet &ns) +{ + abstract_object_pointert offset_value = + environment.eval(index_expr.index(), ns); + + add_to_stack(std::make_shared(offset_value), environment, ns); + construct_stack_to_lvalue(index_expr.array(), environment, ns); +} + +/// Convert the stack to an expression that can be used to write to. +/// \return The expression representing the stack, with nil_exprt expressions +/// for top elements. +exprt write_stackt::to_expression() const +{ + // A top stack is useless and its expression should not be evaluated + PRECONDITION(!is_top_value()); + exprt access_expr = nil_exprt(); + for(const std::shared_ptr &entry : stack) + { + exprt new_expr = entry->get_access_expr(); + if(access_expr.id() == ID_nil) + { + access_expr = new_expr; + } + else + { + if(new_expr.operands().size() == 0) + { + new_expr.operands().resize(1); + } + new_expr.operands()[0] = access_expr; + + // If neccesary, complete the type of the new access expression + entry->adjust_access_type(new_expr); + + access_expr = new_expr; + } + } + address_of_exprt top_expr(access_expr); + return std::move(top_expr); +} + +/// Is the stack representing an unknown value and hence can't be written to +/// or read from. +/// \return True if the stack is top. +bool write_stackt::is_top_value() const +{ + return top_stack; +} + +/// Add an element to the top of the stack. This will squash in with the top +/// element if possible. +/// \param entry_pointer: The new element for the stack. +/// \param environment: The environment to evaluate any expressions in +/// \param ns: The global namespace +void write_stackt::add_to_stack( + std::shared_ptr entry_pointer, + const abstract_environmentt environment, + const namespacet &ns) +{ + if( + stack.empty() || + !stack.front()->try_squash_in(entry_pointer, environment, ns)) + { + stack.insert(stack.begin(), entry_pointer); + } +} + +/// Utility function to find out which side of a binary operation has an +/// integral type, if any. +/// \param expr: The (binary) expression to evaluate. +/// \param [out] out_base_expr: The sub-expression which is not integral typed +/// \param [out] out_integral_expr: The subexpression which is integraled typed. +/// \return: An enum specifying whether the integral type is the LHS (op0), +/// RHS (op1) or neither. +write_stackt::integral_resultt write_stackt::get_which_side_integral( + const exprt &expr, + exprt &out_base_expr, + exprt &out_integral_expr) +{ + PRECONDITION(expr.operands().size() == 2); + const auto binary_e = to_binary_expr(expr); + static const std::unordered_set integral_types = { + ID_signedbv, ID_unsignedbv, ID_integer}; + if(integral_types.find(binary_e.lhs().type().id()) != integral_types.cend()) + { + out_integral_expr = binary_e.lhs(); + out_base_expr = binary_e.rhs(); + return integral_resultt::LHS_INTEGRAL; + } + else if( + integral_types.find(binary_e.rhs().type().id()) != integral_types.cend()) + { + out_integral_expr = binary_e.rhs(); + out_base_expr = binary_e.lhs(); + return integral_resultt::RHS_INTEGRAL; + } + else + { + out_integral_expr.make_nil(); + out_base_expr.make_nil(); + return integral_resultt::NEITHER_INTEGRAL; + } +} diff --git a/src/analyses/variable-sensitivity/write_stack.h b/src/analyses/variable-sensitivity/write_stack.h new file mode 100644 index 00000000000..b43521afda5 --- /dev/null +++ b/src/analyses/variable-sensitivity/write_stack.h @@ -0,0 +1,71 @@ +/*******************************************************************\ + + Module: Variable Sensitivity Domain + + Author: DiffBlue Limited. + +\*******************************************************************/ + +/// \file +/// Represents a stack of write operations to an addressable memory +/// location + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_STACK_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_STACK_H + +#include + +class write_stackt +{ +public: + typedef std::vector> + continuation_stack_storet; + + write_stackt(); + + write_stackt( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + exprt to_expression() const; + bool is_top_value() const; + +private: + void construct_stack_to_pointer( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + void construct_stack_to_lvalue( + const exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + void construct_stack_to_array_index( + const index_exprt &expr, + const abstract_environmentt &environment, + const namespacet &ns); + + void add_to_stack( + std::shared_ptr entry_pointer, + const abstract_environmentt environment, + const namespacet &ns); + + enum class integral_resultt + { + LHS_INTEGRAL, + RHS_INTEGRAL, + NEITHER_INTEGRAL + }; + + static integral_resultt get_which_side_integral( + const exprt &expr, + exprt &out_base_expr, + exprt &out_integral_expr); + + continuation_stack_storet stack; + bool top_stack; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_STACK_H diff --git a/src/analyses/variable-sensitivity/write_stack_entry.cpp b/src/analyses/variable-sensitivity/write_stack_entry.cpp new file mode 100644 index 00000000000..8d98ea78c42 --- /dev/null +++ b/src/analyses/variable-sensitivity/write_stack_entry.cpp @@ -0,0 +1,107 @@ +/*******************************************************************\ + + Module: Analyses Variable Sensitivity + + Author: DiffBlue Limited. + +\*******************************************************************/ + +/// \file +/// Represents an entry in the write_stackt + +#include + +#include + +#include "write_stack_entry.h" + +/// Try to combine a new stack element with the current top of the stack +/// \param new_entry: The new entry to combine with +/// \param enviroment: The enviroment to evalaute things in +/// \param ns: The global namespace +/// \return True if this stack entry and thew new entry were combined +bool write_stack_entryt::try_squash_in( + std::shared_ptr new_entry, + const abstract_environmentt &enviroment, + const namespacet &ns) +{ + return false; +} + +/// Build a simple entry based off a single expression +/// \param expr: The expression being represented +simple_entryt::simple_entryt(exprt expr) : simple_entry(expr) +{ + // Invalid simple expression added to the stack + PRECONDITION(expr.id() == ID_member || expr.id() == ID_symbol); +} + +/// Get the expression part needed to read this stack entry. For simple +/// expressions this is just the expression itself. +/// \return The expression to read this part of the stack +exprt simple_entryt::get_access_expr() const +{ + return simple_entry; +} + +/// For a simple entry, no type adjustment is needed for the access expression +void simple_entryt::adjust_access_type(exprt &expr) const +{ +} + +offset_entryt::offset_entryt(abstract_object_pointert offset_value) + : offset(offset_value) +{ + // The type of the abstract object should be an integral number + static const std::unordered_set integral_types = { + ID_signedbv, ID_unsignedbv, ID_integer}; + PRECONDITION( + integral_types.find(offset_value->type().id()) != integral_types.cend()); +} + +/// Get the expression part needed to read this stack entry. For offset entries +/// this is an index expression with the index() part the offset. +/// It is important to note that the returned index_exprt does not have a type, +/// so it will be necessary for the caller to update the type whenever the index +/// expression is completed using `adjust_access_type` on the resulting exprt. +/// \return The untyped expression to read this part of the stack +exprt offset_entryt::get_access_expr() const +{ + // This constructs a something that is basicallyt '(null)[offset])' + // meaning that we don't know what the type is at this point, as the + // array part will be filled in later. + return index_exprt(nil_exprt(), offset->to_constant()); +} + +/// For an offset entry, the type of the access expression can only be +/// determined once the access expression has been completed with the next +/// entry on the write stack. +void offset_entryt::adjust_access_type(exprt &expr) const +{ + PRECONDITION(expr.id() == ID_index); + expr.type() = to_index_expr(expr).array().type().subtype(); +} + +/// Try to combine a new stack element with the current top of the stack. This +/// will succeed if they are both offsets as we can combine these offsets into +/// the sum of the offsets +/// \param new_entry: The new entry to combine with +/// \param enviroment: The enviroment to evalaute things in +/// \param ns: The global namespace +/// \return True if this stack entry and thew new entry were combined +bool offset_entryt::try_squash_in( + std::shared_ptr new_entry, + const abstract_environmentt &enviroment, + const namespacet &ns) +{ + std::shared_ptr cast_entry = + std::dynamic_pointer_cast(new_entry); + if(cast_entry) + { + plus_exprt result_offset( + cast_entry->offset->to_constant(), offset->to_constant()); + offset = enviroment.eval(result_offset, ns); + return true; + } + return false; +} diff --git a/src/analyses/variable-sensitivity/write_stack_entry.h b/src/analyses/variable-sensitivity/write_stack_entry.h new file mode 100644 index 00000000000..53efb9d5eaf --- /dev/null +++ b/src/analyses/variable-sensitivity/write_stack_entry.h @@ -0,0 +1,60 @@ +/*******************************************************************\ + + Module: Analyses Variable Sensitivity + + Author: DiffBlue Limited. + +\*******************************************************************/ + +/// \file +/// Represents an entry in the write_stackt + +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_STACK_ENTRY_H +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_STACK_ENTRY_H + +#include +#include + +#include +#include +#include + +class write_stack_entryt +{ +public: + virtual ~write_stack_entryt() = default; + virtual exprt get_access_expr() const = 0; + virtual void adjust_access_type(exprt &expr) const = 0; + virtual bool try_squash_in( + std::shared_ptr new_entry, + const abstract_environmentt &enviroment, + const namespacet &ns); +}; + +class simple_entryt : public write_stack_entryt +{ +public: + explicit simple_entryt(exprt expr); + exprt get_access_expr() const override; + void adjust_access_type(exprt &expr) const override; + +private: + exprt simple_entry; +}; + +class offset_entryt : public write_stack_entryt +{ +public: + explicit offset_entryt(abstract_object_pointert offset_value); + exprt get_access_expr() const override; + void adjust_access_type(exprt &expr) const override; + bool try_squash_in( + std::shared_ptr new_entry, + const abstract_environmentt &enviroment, + const namespacet &ns) override; + +private: + abstract_object_pointert offset; +}; + +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_WRITE_STACK_ENTRY_H diff --git a/src/goto-analyzer/goto_analyzer_parse_options.cpp b/src/goto-analyzer/goto_analyzer_parse_options.cpp index 57236b4a6dd..1b11efd2611 100644 --- a/src/goto-analyzer/goto_analyzer_parse_options.cpp +++ b/src/goto-analyzer/goto_analyzer_parse_options.cpp @@ -51,6 +51,10 @@ Author: Daniel Kroening, kroening@kroening.com #include #include #include +#include +#include +#include +#include #include #include @@ -256,6 +260,8 @@ void goto_analyzer_parse_optionst::get_command_line_options(optionst &options) // Abstract interpreter choice if(cmdline.isset("recursive-interprocedural")) options.set_option("recursive-interprocedural", true); + else if(cmdline.isset("three-way-merge")) + options.set_option("three-way-merge", true); else if(cmdline.isset("legacy-ait") || cmdline.isset("location-sensitive")) { options.set_option("legacy-ait", true); @@ -356,6 +362,31 @@ void goto_analyzer_parse_optionst::get_command_line_options(optionst &options) options.set_option("non-null", true); options.set_option("domain set", true); } + else if(cmdline.isset("vsd") || cmdline.isset("variable-sensitivity")) + { + options.set_option("vsd", true); + options.set_option("domain set", true); + + // Configuration of VSD + options.set_option("pointers", cmdline.isset("vsd-pointers")); + options.set_option("arrays", cmdline.isset("vsd-arrays")); + options.set_option("structs", cmdline.isset("vsd-structs")); + options.set_option( + "data-dependencies", cmdline.isset("vsd-data-dependencies")); + options.set_option("interval", cmdline.isset("vsd-intervals")); + options.set_option("value-set", cmdline.isset("vsd-value-sets")); + } + else if(cmdline.isset("dependence-graph-vs")) + { + options.set_option("dependence-graph-vs", true); + options.set_option("domain set", true); + + // Configuration of variable sensitivity domain + options.set_option("pointers", cmdline.isset("vsd-pointers")); + options.set_option("arrays", cmdline.isset("vsd-arrays")); + options.set_option("structs", cmdline.isset("vsd-structs")); + options.set_option("data-dependencies", true); + } // Reachability questions, when given with a domain swap from specific // to general tasks so that they can use the domain & parameterisations. @@ -416,7 +447,9 @@ ai_baset *goto_analyzer_parse_optionst::build_analyzer( const namespacet &ns) { // These support all of the option categories - if(options.get_bool_option("recursive-interprocedural")) + if( + options.get_bool_option("recursive-interprocedural") || + options.get_bool_option("three-way-merge")) { // Build the history factory std::unique_ptr hf = nullptr; @@ -450,8 +483,14 @@ ai_baset *goto_analyzer_parse_optionst::build_analyzer( df = util_make_unique< ai_domain_factory_default_constructort>(); } + else if(options.get_bool_option("vsd")) + { + df = util_make_unique< + ai_domain_factory_default_constructort>(); + } // non-null is not fully supported, despite the historical options // dependency-graph is quite heavily tied to the legacy-ait infrastructure + // dependency-graph-vs is very similar to dependency-graph // Build the storage object std::unique_ptr st = nullptr; @@ -473,7 +512,15 @@ ai_baset *goto_analyzer_parse_optionst::build_analyzer( return new ai_recursive_interproceduralt( std::move(hf), std::move(df), std::move(st)); } - UNREACHABLE; + else if(options.get_bool_option("three-way-merge")) + { + // Only works with VSD + if(options.get_bool_option("vsd")) + { + return new ai_three_way_merget( + std::move(hf), std::move(df), std::move(st)); + } + } } } else if(options.get_bool_option("legacy-ait")) @@ -487,6 +534,15 @@ ai_baset *goto_analyzer_parse_optionst::build_analyzer( { return new dependence_grapht(ns); } + else if(options.get_bool_option("dependence-graph-vs")) + { + return new variable_sensitivity_dependence_grapht( + goto_model.goto_functions, ns); + } + else if(options.get_bool_option("vsd")) + { + return new ait(); + } else if(options.get_bool_option("intervals")) { return new ait(); @@ -702,6 +758,18 @@ int goto_analyzer_parse_optionst::perform_analysis(const optionst &options) if(options.get_bool_option("general-analysis")) { + // TODO : replace with the domain factory infrastructure + try + { + variable_sensitivity_object_factoryt::instance().set_options( + vsd_configt::from_options(options)); + } + catch(const invalid_command_line_argument_exceptiont &e) + { + log.error() << e.what() << messaget::eom; + return CPROVER_EXIT_USAGE_ERROR; + } + // Output file factory const std::string outfile=options.get_option("outfile"); @@ -886,6 +954,8 @@ void goto_analyzer_parse_optionst::help() // NOLINTNEXTLINE(whitespace/line_length) " --recursive-interprocedural use recursion to handle interprocedural reasoning\n" // NOLINTNEXTLINE(whitespace/line_length) + " --three-way-merge use VSD's three-way merge on return from function call\n" + // NOLINTNEXTLINE(whitespace/line_length) " --legacy-ait recursion for function and one domain per location\n" // NOLINTNEXTLINE(whitespace/line_length) " --legacy-concurrent legacy-ait with an extended fixed-point for concurrency\n" @@ -915,6 +985,16 @@ void goto_analyzer_parse_optionst::help() " --intervals an interval for each variable\n" " --non-null tracks which pointers are non-null\n" " --dependence-graph data and control dependencies between instructions\n" // NOLINT(*) + " --vsd a configurable non-relational domain\n" // NOLINT(*) + " --dependence-graph-vs dependencies between instructions using VSD\n" // NOLINT(*) + "\n" + "Variable sensitivity domain (VSD) options:\n" + " --vsd-structs struct field sensitive analysis\n" + " --vsd-arrays array entry sensitive analysis\n" + " --vsd-pointers pointer sensitive analysis\n" + " --vsd-value-sets use value sets\n" + " --vsd-data-dependencies track data dependencies\n" + " --vsd-intervals use intervals\n" "\n" "Storage options:\n" // NOLINTNEXTLINE(whitespace/line_length) diff --git a/src/goto-analyzer/goto_analyzer_parse_options.h b/src/goto-analyzer/goto_analyzer_parse_options.h index 4e745c28aee..6a65438aa06 100644 --- a/src/goto-analyzer/goto_analyzer_parse_options.h +++ b/src/goto-analyzer/goto_analyzer_parse_options.h @@ -115,6 +115,7 @@ class optionst; #define GOTO_ANALYSER_OPTIONS_AI \ "(recursive-interprocedural)" \ + "(three-way-merge)" \ "(legacy-ait)" \ "(legacy-concurrent)" @@ -129,7 +130,17 @@ class optionst; "(intervals)" \ "(non-null)" \ "(constants)" \ - "(dependence-graph)" + "(dependence-graph)" \ + "(vsd)(variable-sensitivity)" \ + "(dependence-graph-vs)" \ + +#define GOTO_ANALYSER_OPTIONS_VSD \ + "(vsd-structs)" \ + "(vsd-arrays)" \ + "(vsd-pointers)" \ + "(vsd-value-sets)" \ + "(vsd-data-dependencies)" \ + "(vsd-intervals)" \ #define GOTO_ANALYSER_OPTIONS_STORAGE \ "(one-domain-per-history)" \ @@ -167,7 +178,8 @@ class optionst; "(location-sensitive)(concurrent)" \ GOTO_ANALYSER_OPTIONS_HISTORY \ GOTO_ANALYSER_OPTIONS_DOMAIN \ - GOTO_ANALYSER_OPTIONS_STORAGE \ + GOTO_ANALYSER_OPTIONS_VSD \ + GOTO_ANALYSER_OPTIONS_STORAGE \ GOTO_ANALYSER_OPTIONS_OUTPUT \ GOTO_ANALYSER_OPTIONS_SPECIFIC_ANALYSES \ // clang-format on diff --git a/src/goto-analyzer/static_show_domain.cpp b/src/goto-analyzer/static_show_domain.cpp index 3fe68b43495..3d0b132268c 100644 --- a/src/goto-analyzer/static_show_domain.cpp +++ b/src/goto-analyzer/static_show_domain.cpp @@ -11,6 +11,7 @@ Author: Martin Brain, martin.brain@cs.ox.ac.uk #include #include +#include /// Runs the analyzer and then prints out the domain /// \param goto_model: the program analyzed @@ -31,16 +32,38 @@ void static_show_domain( { out << ai.output_xml(goto_model); } - else if(options.get_bool_option("dot") && - options.get_bool_option("dependence-graph")) + else if( + options.get_bool_option("dot") && + (options.get_bool_option("dependence-graph") || + options.get_bool_option("dependence-graph-vs"))) { - const dependence_grapht *d=dynamic_cast(&ai); - INVARIANT(d!=nullptr, - "--dependence-graph sets ai to be a dependence_graph"); + // It would be nice to cast this to a grapht but C++ templates and + // inheritance need some care to work together. + if(options.get_bool_option("dependence-graph")) + { + auto d = dynamic_cast(&ai); + INVARIANT( + d != nullptr, + "--dependence-graph should set ai to be a dependence_grapht"); - out << "digraph g {\n"; - d->output_dot(out); - out << "}\n"; + out << "digraph g {\n"; + d->output_dot(out); + out << "}\n"; + } + else if(options.get_bool_option("dependence-graph-vs")) + { + auto d = + dynamic_cast(&ai); + INVARIANT( + d != nullptr, + "--dependence-graph-vsd should set ai to be a " + "variable_sensitivity_dependence_grapht"); + + out << "digraph g {\n"; + d->output_dot(out); + out << "}\n"; + } + UNREACHABLE; } else { diff --git a/src/util/Makefile b/src/util/Makefile index 8e800b92ded..d50c5b54322 100644 --- a/src/util/Makefile +++ b/src/util/Makefile @@ -46,6 +46,7 @@ SRC = allocate_objects.cpp \ mathematical_expr.cpp \ mathematical_types.cpp \ memory_info.cpp \ + memory_units.cpp \ merge_irep.cpp \ message.cpp \ mp_arith.cpp \ diff --git a/src/util/memory_units.cpp b/src/util/memory_units.cpp new file mode 100644 index 00000000000..901541d0536 --- /dev/null +++ b/src/util/memory_units.cpp @@ -0,0 +1,115 @@ +/*******************************************************************\ + +Module: Memory units + +Author: Hannes Steffenhagen + +\*******************************************************************/ + +#include "memory_units.h" + +#include + +memory_sizet::memory_sizet() : bytes(0) +{ +} +memory_sizet::memory_sizet(std::size_t bytes) : bytes(bytes) +{ +} +memory_sizet::memory_sizet(const memory_sizet &other) : bytes(other.bytes) +{ +} +memory_sizet::memory_sizet(memory_sizet &&other) : bytes(other.bytes) +{ +} + +memory_sizet &memory_sizet::operator=(const memory_sizet &other) +{ + bytes = other.bytes; + return *this; +} + +memory_sizet &memory_sizet::operator=(memory_sizet &&other) noexcept +{ + bytes = other.bytes; + return *this; +} + +memory_sizet memory_sizet::from_bytes(std::size_t bytes) +{ + return memory_sizet(bytes); +} + +std::size_t memory_sizet::get_bytes() const +{ + return bytes; +} + +std::size_t memory_sizet::get_kibibytes() const +{ + return bytes / 1024; +} + +std::size_t memory_sizet::get_mebibytes() const +{ + return bytes / (1024 * 1024); +} + +std::size_t memory_sizet::get_gibibytes() const +{ + return bytes / (1024 * 1024 * 1024); +} + +std::string memory_sizet::to_string() const +{ + std::size_t remainder = get_bytes(); + std::ostringstream out; + const std::size_t gib = remainder / (1024 * 1024 * 1024); + remainder -= gib * 1024 * 1024 * 1024; + if(gib > 0) + { + out << gib << si_gibibyte_symbol; + } + const std::size_t mib = remainder / (1024 * 1024); + remainder -= mib * 1024 * 1024; + if(mib > 0) + { + if(gib > 0) + { + out << ' '; + } + out << mib << si_mebibyte_symbol; + } + const std::size_t kib = remainder / 1024; + remainder -= kib * 1024; + if(kib > 0) + { + if(mib > 0 || gib > 0) + { + out << ' '; + } + out << kib << si_kibibyte_symbol; + } + if(gib > 0 || mib > 0 || kib > 0) + { + out << ' '; + } + out << remainder << si_byte_symbol; + return out.str(); +} + +const char *memory_sizet::si_byte_symbol = "B"; +const char *memory_sizet::si_kibibyte_symbol = "KiB"; +const char *memory_sizet::si_mebibyte_symbol = "MiB"; +const char *memory_sizet::si_gibibyte_symbol = "GiB"; + +memory_sizet &memory_sizet::operator+=(const memory_sizet &other) +{ + bytes += other.bytes; + return *this; +} + +memory_sizet memory_sizet::operator+(const memory_sizet &other) const +{ + return memory_sizet(*this) += other; +} diff --git a/src/util/memory_units.h b/src/util/memory_units.h new file mode 100644 index 00000000000..f536c6fd993 --- /dev/null +++ b/src/util/memory_units.h @@ -0,0 +1,46 @@ +/*******************************************************************\ + +Module: Memory units + +Author: Hannes Steffenhagen + +\*******************************************************************/ + +#ifndef CPROVER_UTIL_MEMORY_UNITS_H +#define CPROVER_UTIL_MEMORY_UNITS_H + +#include +#include + +class memory_sizet +{ +public: + static memory_sizet from_bytes(std::size_t bytes); + + memory_sizet(); + memory_sizet(const memory_sizet &); + memory_sizet(memory_sizet &&); + + memory_sizet &operator=(const memory_sizet &); + memory_sizet &operator=(memory_sizet &&) noexcept; + + memory_sizet &operator+=(const memory_sizet &); + memory_sizet operator+(const memory_sizet &) const; + + std::size_t get_bytes() const; + std::size_t get_kibibytes() const; + std::size_t get_mebibytes() const; + std::size_t get_gibibytes() const; + std::string to_string() const; + + static const char *si_byte_symbol; + static const char *si_kibibyte_symbol; + static const char *si_mebibyte_symbol; + static const char *si_gibibyte_symbol; + +private: + std::size_t bytes; + explicit memory_sizet(std::size_t bytes); +}; + +#endif // CPROVER_UTIL_MEMORY_UNITS_H diff --git a/src/util/sharing_map.h b/src/util/sharing_map.h index 256fa24e8db..c8d79b1ec72 100644 --- a/src/util/sharing_map.h +++ b/src/util/sharing_map.h @@ -54,6 +54,16 @@ Author: Daniel Poetzl typename equalT> \ type sharing_mapt +#define SHARING_MAPTV(return_type, V) \ + template < \ + typename keyT, \ + typename valueT, \ + bool fail_if_equal, \ + typename hashT, \ + typename equalT> \ + template \ + return_type sharing_mapt + /// Macro to abbreviate the out-of-class definitions of methods of sharing_mapt /// with a return type that is defined within the class. /// @@ -278,6 +288,19 @@ class sharing_mapt template void insert(const key_type &k, valueU &&m); + template + void insert_or_replace(const key_type &k, valueU &&m) + { + if(has_key(k)) + { + replace(k, std::forward(m)); + } + else + { + insert(k, std::forward(m)); + } + } + /// Replace element, element must exist in map /// /// Complexity: @@ -363,6 +386,17 @@ class sharing_mapt /// View of the key-value pairs in the map. A view is a list of pairs with /// the components being const references to the keys and values in the map. typedef std::vector viewt; + typedef std::set sorted_viewt; + + static void insert_view_item(viewt &v, view_itemt &&vi) + { + v.push_back(vi); + } + + static void insert_view_item(sorted_viewt &v, view_itemt &&vi) + { + v.insert(vi); + } class delta_view_itemt { @@ -413,7 +447,14 @@ class sharing_mapt /// - Best case: O(N + H) /// /// \param [out] view: Empty view - void get_view(viewt &view) const; + template + void get_view(V &view) const; + viewt get_view() const + { + viewt result; + get_view(result); + return result; + } /// Get a delta view of the elements in the map /// @@ -453,6 +494,10 @@ class sharing_mapt delta_viewt &delta_view, const bool only_common = true) const; + delta_viewt get_delta_view( + const sharing_mapt &other, + const bool only_common = true) const; + /// Call a function for every key-value pair in the map. /// /// Complexity: as \ref sharing_mapt::get_view @@ -779,7 +824,7 @@ ::get_sharing_stats_map(Iterator begin, Iterator end) } #endif -SHARING_MAPT(void)::get_view(viewt &view) const +SHARING_MAPTV(void, view_type)::get_view(view_type &view) const { SM_ASSERT(view.empty()); @@ -787,7 +832,7 @@ SHARING_MAPT(void)::get_view(viewt &view) const return; auto f = [&view](const key_type &k, const mapped_type &m) { - view.push_back(view_itemt(k, m)); + insert_view_item(view, view_itemt(k, m)); }; iterate(map, f); @@ -1062,6 +1107,15 @@ ::get_delta_view( while(!stack.empty()); } +SHARING_MAPT2(, delta_viewt)::get_delta_view( + const sharing_mapt &other, + const bool only_common) const +{ + delta_viewt delta_view; + get_delta_view(other, delta_view, only_common); + return delta_view; +} + SHARING_MAPT2(, nodet &)::get_leaf_node(const key_type &k) { SM_ASSERT(has_key(k)); diff --git a/src/util/std_expr.h b/src/util/std_expr.h index cb36b59912e..d0d0c7b28cb 100644 --- a/src/util/std_expr.h +++ b/src/util/std_expr.h @@ -114,6 +114,20 @@ class symbol_exprt : public nullary_exprt } }; +// NOLINTNEXTLINE(readability/namespace) +namespace std +{ +template <> +// NOLINTNEXTLINE(readability/identifiers) +struct hash<::symbol_exprt> +{ + size_t operator()(const ::symbol_exprt &sym) + { + return irep_id_hash()(sym.get_identifier()); + } +}; +} // namespace std + /// Expression to hold a symbol (variable) with extra accessors to /// ID_c_static_lifetime and ID_C_thread_local class decorated_symbol_exprt:public symbol_exprt diff --git a/src/util/string_container.cpp b/src/util/string_container.cpp index 262634af190..bc81c861409 100644 --- a/src/util/string_container.cpp +++ b/src/util/string_container.cpp @@ -12,6 +12,8 @@ Author: Daniel Kroening, kroening@kroening.com #include "string_container.h" #include +#include +#include string_ptrt::string_ptrt(const char *_s):s(_s), len(strlen(_s)) { @@ -74,3 +76,36 @@ unsigned string_containert::get(const std::string &s) return r; } + +void string_container_statisticst::dump_on_stream(std::ostream &out) const +{ + auto total_memory_usage = strings_memory_usage + vector_memory_usage + + map_memory_usage + list_memory_usage; + out << "String container statistics:" + << "\n string count: " << string_count + << "\n string memory usage: " << strings_memory_usage.to_string() + << "\n vector memory usage: " << vector_memory_usage.to_string() + << "\n map memory usage: " << map_memory_usage.to_string() + << "\n list memory usage: " << list_memory_usage.to_string() + << "\n total memory usage: " << total_memory_usage.to_string() << '\n'; +} + +string_container_statisticst string_containert::compute_statistics() const +{ + string_container_statisticst result; + result.string_count = string_vector.size(); + result.vector_memory_usage = memory_sizet::from_bytes( + sizeof(string_vector) + + sizeof(string_vectort::value_type) * string_vector.capacity()); + result.strings_memory_usage = memory_sizet::from_bytes(std::accumulate( + begin(string_vector), + end(string_vector), + std::size_t(0), + [](std::size_t sz, const std::string *s) { return sz + s->capacity(); })); + result.map_memory_usage = memory_sizet::from_bytes( + sizeof(hash_table) + hash_table.size() * sizeof(hash_tablet::value_type)); + + result.list_memory_usage = memory_sizet::from_bytes( + sizeof(string_list) + 2 * sizeof(void *) * string_list.size()); + return result; +} diff --git a/src/util/string_container.h b/src/util/string_container.h index b63cb38905e..10d2ea4d3e6 100644 --- a/src/util/string_container.h +++ b/src/util/string_container.h @@ -16,6 +16,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include "memory_units.h" #include "string_hash.h" struct string_ptrt @@ -44,6 +45,21 @@ class string_ptr_hash size_t operator()(const string_ptrt s) const { return hash_string(s.s); } }; +/// Has estimated statistics about string container +/// (estimated because this only uses public information, +/// which disregards any internal control structures that +/// might be in use) +struct string_container_statisticst +{ + std::size_t string_count; + memory_sizet strings_memory_usage; + memory_sizet vector_memory_usage; + memory_sizet map_memory_usage; + memory_sizet list_memory_usage; + + void dump_on_stream(std::ostream &out) const; +}; + class string_containert { public: @@ -73,6 +89,8 @@ class string_containert return *string_vector[no]; } + string_container_statisticst compute_statistics() const; + protected: // the 'unsigned' ought to be size_t typedef std::unordered_map diff --git a/unit/Makefile b/unit/Makefile index 75fb0e52368..905b2a47bce 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -14,6 +14,16 @@ SRC += analyses/ai/ai.cpp \ analyses/does_remove_const/does_expr_lose_const.cpp \ analyses/does_remove_const/does_type_preserve_const_correctness.cpp \ analyses/does_remove_const/is_type_at_least_as_const_as.cpp \ + analyses/variable-sensitivity/abstract_object/merge.cpp \ + analyses/variable-sensitivity/constant_abstract_value/merge.cpp \ + analyses/variable-sensitivity/constant_array_abstract_object/merge.cpp \ + analyses/variable-sensitivity/eval.cpp \ + analyses/variable-sensitivity/interval_abstract_value/meet.cpp \ + analyses/variable-sensitivity/full_struct_abstract_object/merge.cpp \ + analyses/variable-sensitivity/last_written_location.cpp \ + analyses/variable-sensitivity/value_set/abstract_value.cpp \ + analyses/variable-sensitivity/value_set/array_abstract_object.cpp \ + analyses/variable-sensitivity/value_set/pointer_abstract_object.cpp \ ansi-c/max_malloc_size.cpp \ ansi-c/type2name.cpp \ big-int/big-int.cpp \ diff --git a/unit/analyses/variable-sensitivity/abstract_object/merge.cpp b/unit/analyses/variable-sensitivity/abstract_object/merge.cpp new file mode 100644 index 00000000000..2c18baf656e --- /dev/null +++ b/unit/analyses/variable-sensitivity/abstract_object/merge.cpp @@ -0,0 +1,111 @@ +/*******************************************************************\ + + Module: Unit tests for variable/sensitivity/abstract_object::merge + + Author: DiffBlue Limited. + +\*******************************************************************/ + +#include + +#include +#include + +SCENARIO( + "merge_abstract_object", + "[core][analyses][variable-sensitivity][abstract_object][merge]") +{ + GIVEN("Two abstract objects of type pointer") + { + const typet object_type = signedbv_typet(32); + WHEN("Both are top") + { + abstract_object_pointert op1 = + std::make_shared(object_type, true, false); + + abstract_object_pointert op2 = + std::make_shared(object_type, true, false); + + bool modifications; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modifications); + + THEN("The result is the original abstract object") + { + // Simple correctness of merge + REQUIRE_FALSE(modifications); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("The first is top, the second is bottom") + { + abstract_object_pointert op1 = + std::make_shared(object_type, true, false); + + abstract_object_pointert op2 = + std::make_shared(object_type, false, true); + + bool modifications; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modifications); + + THEN("The result is the original abstract object") + { + // Simple correctness of merge + REQUIRE_FALSE(modifications); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("The first is bottom and the second is top") + { + abstract_object_pointert op1 = + std::make_shared(object_type, false, true); + abstract_object_pointert op2 = + std::make_shared(object_type, true, false); + + bool modifications; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modifications); + + THEN("The result is top and a new abstract object") + { + // Simple correctness of merge + REQUIRE(modifications); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + REQUIRE_FALSE(op1 == result); + } + } + WHEN("Both are bottom") + { + abstract_object_pointert op1 = + std::make_shared(object_type, false, true); + abstract_object_pointert op2 = + std::make_shared(object_type, false, true); + + bool modifications; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modifications); + + THEN("The result is the original abstract object") + { + // Simple correctness of merge + REQUIRE_FALSE(modifications); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/abstract_object/module_dependencies.txt b/unit/analyses/variable-sensitivity/abstract_object/module_dependencies.txt new file mode 100644 index 00000000000..ac96be3c1ef --- /dev/null +++ b/unit/analyses/variable-sensitivity/abstract_object/module_dependencies.txt @@ -0,0 +1,3 @@ +analyses +testing-utils +util diff --git a/unit/analyses/variable-sensitivity/constant_abstract_value/merge.cpp b/unit/analyses/variable-sensitivity/constant_abstract_value/merge.cpp new file mode 100644 index 00000000000..1447da92a69 --- /dev/null +++ b/unit/analyses/variable-sensitivity/constant_abstract_value/merge.cpp @@ -0,0 +1,580 @@ +/*******************************************************************\ + + Module: Unit tests for constant_abstract_valuet::merge + + Author: DiffBlue Limited. + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SCENARIO( + "merge_constant_abstract_value", + "[core][analyses][variable-sensitivity][constant_abstract_value][merge]") +{ + GIVEN("An environment with two values: 1 and 2") + { + const exprt val1 = from_integer(1, integer_typet()); + const exprt val2 = from_integer(2, integer_typet()); + + abstract_environmentt enviroment; + enviroment.make_top(); + symbol_tablet symbol_table; + namespacet ns(symbol_table); + + WHEN( + "merging constant AO with value " + "with a constant AO with the same value") + { + abstract_object_pointert op1 = + std::make_shared(val1, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(val1, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("The original abstract object should be returned unchanged") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(cast_result->to_constant() == val1); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "merging constant AO with value " + "with a constant AO with the different value") + { + abstract_object_pointert op1 = + std::make_shared(val1, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(val2, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("A new constant abstract object set to top should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // We currently don't require the value has any reasonable value + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN("merging constant AO with value with a constant AO set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("A new AO of the same type set to top ") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // We currently don't require the value has any reasonable value + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN("merging constant AO with value with a constant AO set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("The original AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(cast_result->to_constant() == val1); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN("merging constant AO set to top with a constant AO with a value") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + + abstract_object_pointert op2 = + std::make_shared(val1, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("WE should return the same value") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("merging constant AO set to top with a constant AO set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("WE should return the same value") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("merging constant AO set to top with a constant AO set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("WE should return the same value") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("merging constant AO set to bottom with a constant AO with a value") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("We should get an abstract object that has the same value as op2") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + REQUIRE(result->to_constant() == val1); + } + } + WHEN("merging constant AO set to bottom with a constant AO set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("We should get a top abstract object back") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + } + } + WHEN("merging constant AO set to bottom with a constant AO set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("We should get the same (bottom) AO back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + // Optimization correctness + REQUIRE(result == op1); + } + } + WHEN("merging constant AO with value with a abstract object set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("We should get a new AO of the same type but set to top") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // We currently don't require the value has any reasonable value + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN("merging constant AO with value with a abstract object set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("We should get the same constant AO back") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(cast_result->to_constant() == val1); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN("merging constant AO set to top with a abstract object set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("We should get the same abstract object back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("merging constant AO set to top with a abstract object set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Should get the same abstract object back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("merging constant AO set to bottom with a abstract object set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Return a new top abstract object of the same type") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // We don't optimize for returning the second parameter if we can + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("merging constant AO set to bottom with a AO set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Return the original abstract object") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + // Optimization check + REQUIRE(result == op1); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("merging AO set to top with a constant AO with a value") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1, enviroment, ns); + + bool modified; + + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("merging AO set to top with a constant AO set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("merging AO set to top with a constant AO set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), true, false); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("merging AO set to bottom with a constant AO with a value") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The a new top AO should be returned") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(typeid(result.get()) == typeid(const abstract_objectt *)); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + } + + // We don't optimize for returning the second parameter if we can + } + WHEN("merging AO set to bottom with a constant AO set to top") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The a new top AO should be returned") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // We don't optimize for returning the second parameter if we can + } + } + WHEN("merging AO set to bottom with a constant AO set to bottom") + { + abstract_object_pointert op1 = + std::make_shared(val1.type(), false, true); + abstract_object_pointert op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + REQUIRE(result == op1); + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/constant_abstract_value/module_dependencies.txt b/unit/analyses/variable-sensitivity/constant_abstract_value/module_dependencies.txt new file mode 100644 index 00000000000..ac96be3c1ef --- /dev/null +++ b/unit/analyses/variable-sensitivity/constant_abstract_value/module_dependencies.txt @@ -0,0 +1,3 @@ +analyses +testing-utils +util diff --git a/unit/analyses/variable-sensitivity/constant_array_abstract_object/merge.cpp b/unit/analyses/variable-sensitivity/constant_array_abstract_object/merge.cpp new file mode 100644 index 00000000000..540bc5fc98e --- /dev/null +++ b/unit/analyses/variable-sensitivity/constant_array_abstract_object/merge.cpp @@ -0,0 +1,702 @@ +/*******************************************************************\ + + Module: Unit tests for constant_array_abstract_object::merge + + Author: DiffBlue Limited. + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef constant_array_abstract_objectt::constant_array_pointert + constant_array_abstract_object_pointert; + +// Util + +class array_utilt +{ +public: + array_utilt(const abstract_environmentt &enviroment, const namespacet &ns) + : enviroment(enviroment), ns(ns) + { + } + + constant_array_abstract_objectt::constant_array_pointert + build_array(const exprt &array_expr) + { + return std::make_shared( + array_expr, enviroment, ns); + } + + constant_array_abstract_objectt::constant_array_pointert + build_top_array(const typet &array_type) + { + return std::make_shared( + array_type, true, false); + } + + constant_array_abstract_objectt::constant_array_pointert + build_bottom_array(const typet &array_type) + { + return std::make_shared( + array_type, false, true); + } + + exprt read_index( + constant_array_abstract_object_pointert array_object, + const index_exprt &index) const + { + return array_object->read(enviroment, index, ns)->to_constant(); + } + +private: + const abstract_environmentt &enviroment; + const namespacet ns; +}; + +SCENARIO( + "merge_constant_array_abstract_object", + "[core]" + "[analyses][variable-sensitivity][constant_array_abstract_object][merge]") +{ + GIVEN("Two arrays of size 3, whose first elements are the same") + { + const array_typet array_type( + integer_typet(), from_integer(3, integer_typet())); + + // int val1[3] = {1, 2, 3} + exprt::operandst val1_op; + val1_op.push_back(from_integer(1, integer_typet())); + val1_op.push_back(from_integer(2, integer_typet())); + val1_op.push_back(from_integer(3, integer_typet())); + exprt val1 = array_exprt(val1_op, array_type); + + // int val2[3] = {1, 4, 5} + exprt::operandst val2_op; + val2_op.push_back(from_integer(1, integer_typet())); + val2_op.push_back(from_integer(4, integer_typet())); + val2_op.push_back(from_integer(5, integer_typet())); + exprt val2 = array_exprt(val2_op, array_type); + + // index_exprt for reading from an array + const index_exprt i0 = + index_exprt(nil_exprt(), from_integer(0, integer_typet())); + const index_exprt i1 = + index_exprt(nil_exprt(), from_integer(1, integer_typet())); + const index_exprt i2 = + index_exprt(nil_exprt(), from_integer(2, integer_typet())); + + abstract_environmentt enviroment; + enviroment.make_top(); + symbol_tablet symbol_table; + namespacet ns(symbol_table); + + variable_sensitivity_object_factoryt::instance().set_options( + vsd_configt::constant_domain()); + + array_utilt util(enviroment, ns); + + abstract_object_pointert result; + bool modified = false; + + WHEN("Merging two constant array AOs with the same array") + { + auto op1 = util.build_array(val1); + auto op2 = util.build_array(val1); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("The original constant array AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == val1.operands()[0]); + REQUIRE(util.read_index(cast_result, i1) == val1.operands()[1]); + REQUIRE(util.read_index(cast_result, i2) == val1.operands()[2]); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN("Merging two constant array AOs with different value arrays") + { + auto op1 = util.build_array(val1); + auto op2 = util.build_array(val2); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + + THEN( + "A new constant array AO whose first value is the same and " + "the other two are top") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == val1.operands()[0]); + REQUIRE(util.read_index(cast_result, i1) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i2) == nil_exprt()); + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN( + "Merging a constant array AO with a value " + "with a constant array AO set to top") + { + auto op1 = util.build_array(val1); + auto op2 = util.build_top_array(array_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("A new constant array AO set to top should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i1) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i2) == nil_exprt()); + + // We can't reuse the abstract object as the value has changed + REQUIRE(result != op1); + } + } + WHEN( + "Merging a constant array AO with a value " + "with a constant array AO set to bottom") + { + auto op1 = util.build_array(val1); + auto op2 = util.build_bottom_array(array_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("The original const AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == val1.operands()[0]); + REQUIRE(util.read_index(cast_result, i1) == val1.operands()[1]); + REQUIRE(util.read_index(cast_result, i2) == val1.operands()[2]); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant array AO set to top " + "with a constant array AO with a value") + { + auto op1 = util.build_top_array(array_type); + auto op2 = util.build_array(val1); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("The original constant array AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i1) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i2) == nil_exprt()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant array AO set to top " + "with a constant array AO set to top") + { + auto op1 = util.build_top_array(array_type); + auto op2 = util.build_top_array(array_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("The original constant array AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i1) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i2) == nil_exprt()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant array AO set to top " + "with a constant array AO set to bottom") + { + auto op1 = util.build_top_array(array_type); + auto op2 = util.build_bottom_array(array_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("The original constant array AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i1) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i2) == nil_exprt()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant array AO set to bottom " + "with a constant array AO with a value") + { + auto op1 = util.build_bottom_array(array_type); + auto op2 = util.build_array(val1); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("A new AO should be returned with op2s valuee") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == val1.operands()[0]); + REQUIRE(util.read_index(cast_result, i1) == val1.operands()[1]); + REQUIRE(util.read_index(cast_result, i2) == val1.operands()[2]); + + // Is optimal + REQUIRE(result != op1); + } + } + WHEN( + "Merging a constant array AO set to bottom " + "with a constant array AO set to top") + { + auto op1 = util.build_bottom_array(array_type); + auto op2 = util.build_top_array(array_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("A new constant array AO should be returned set to top ") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i1) == nil_exprt()); + REQUIRE(util.read_index(cast_result, i2) == nil_exprt()); + + // Is optimal + REQUIRE(result != op1); + } + } + WHEN( + "Merging a constant array AO set to bottom " + "with a constant array AO set to bottom") + { + auto op1 = util.build_bottom_array(array_type); + auto op2 = util.build_bottom_array(array_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("The original bottom AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE(cast_result->is_bottom()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging constant array AO with value " + "with a abstract object set to top") + { + const auto &op1 = util.build_array(val1); + const auto &op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + + THEN("We should get a new AO of the same type but set to top") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN( + "Merging constant array AO with value " + "with a abstract object set to bottom") + { + const auto &op1 = util.build_array(val1); + const auto &op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast( + result); + THEN("We should get the same constant array AO back") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_index(cast_result, i0) == val1.operands()[0]); + REQUIRE(util.read_index(cast_result, i1) == val1.operands()[1]); + REQUIRE(util.read_index(cast_result, i2) == val1.operands()[2]); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging constant array AO set to top " + "with a abstract object set to top") + { + const auto &op1 = util.build_top_array(array_type); + const auto &op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("We should get the same abstract object back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast( + result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN( + "Merging constant array AO set to top " + "with a abstract object set to bottom") + { + const auto &op1 = util.build_top_array(array_type); + const auto &op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Should get the same abstract object back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast( + result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN( + "Merging constant array AO set to bottom " + " with a abstract object set to top") + { + const auto &op1 = util.build_bottom_array(array_type); + const auto &op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Return a new top abstract object of the same type") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // We don't optimize for returning the second parameter if we can + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast( + result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("Merging constant array AO set to bottom with a AO set to bottom") + { + const auto &op1 = util.build_bottom_array(array_type); + const auto &op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Return the original abstract object") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + // Optimization check + REQUIRE(result == op1); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast( + result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("Merging AO set to top with a constant array AO with a value") + { + const auto &op1 = + std::make_shared(val1.type(), true, false); + const auto &op2 = util.build_array(val1); + + bool modified; + + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("Merging AO set to top with a constant array AO set to top") + { + const auto &op1 = + std::make_shared(val1.type(), true, false); + const auto &op2 = util.build_top_array(array_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("Merging AO set to top with a constant array AO set to bottom") + { + const auto &op1 = + std::make_shared(val1.type(), true, false); + const auto &op2 = util.build_bottom_array(array_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("Merging AO set to bottom with a constant array AO with a value") + { + const auto &op1 = + std::make_shared(val1.type(), false, true); + const auto &op2 = util.build_array(val1); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The a new top AO should be returned") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(typeid(result.get()) == typeid(const abstract_objectt *)); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + } + + // We don't optimize for returning the second parameter if we can + } + WHEN("Merging AO set to bottom with a constant array AO set to top") + { + const auto &op1 = + std::make_shared(val1.type(), false, true); + const auto &op2 = util.build_top_array(array_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The a new top AO should be returned") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // We don't optimize for returning the second parameter if we can + } + } + WHEN("Merging AO set to bottom with a constant array AO set to bottom") + { + const auto &op1 = + std::make_shared(val1.type(), false, true); + const auto &op2 = util.build_bottom_array(array_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + REQUIRE(result == op1); + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/constant_array_abstract_object/module_dependencies.txt b/unit/analyses/variable-sensitivity/constant_array_abstract_object/module_dependencies.txt new file mode 100644 index 00000000000..ac96be3c1ef --- /dev/null +++ b/unit/analyses/variable-sensitivity/constant_array_abstract_object/module_dependencies.txt @@ -0,0 +1,3 @@ +analyses +testing-utils +util diff --git a/unit/analyses/variable-sensitivity/eval.cpp b/unit/analyses/variable-sensitivity/eval.cpp new file mode 100644 index 00000000000..3a0e19f3edb --- /dev/null +++ b/unit/analyses/variable-sensitivity/eval.cpp @@ -0,0 +1,100 @@ +// Copyright 2016-2020 Diffblue Limited. + +#include +#include +#include +#include +#include +#include + +static symbolt simple_symbol(const irep_idt &identifier, const typet &type) +{ + symbolt b1; + b1.name = b1.base_name = b1.pretty_name = identifier; + b1.type = type; + return b1; +} + +SCENARIO( + "eval", + "[core][analyses][variable-sensitivity][interval_abstract_value]") +{ + GIVEN("An environment with intervals domain") + { + variable_sensitivity_object_factoryt::instance().set_options( + vsd_configt::intervals()); + abstract_environmentt environment; + environment.make_top(); // Domains are bottom on construction + + symbol_tablet symbol_table; + namespacet ns{symbol_table}; + + signedbv_typet number_type{32}; + const auto &b1 = simple_symbol("b1", number_type); + symbol_table.add(b1); + + WHEN("Evaluating expression with an unknown value") + { + // b1 == 0 ? 1 : 0 + if_exprt condition{ + equal_exprt{b1.symbol_expr(), from_integer(0, number_type)}, + from_integer(1, number_type), + from_integer(0, number_type)}; + + const auto result = environment.eval(condition, ns); + + THEN("Should get a wrapped range of 0..1") + { + REQUIRE( + std::dynamic_pointer_cast(result)); + REQUIRE_FALSE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + const auto unwrapped = + std::dynamic_pointer_cast(result) + ->unwrap_context(); + auto result_as_interval = + std::dynamic_pointer_cast(unwrapped); + REQUIRE(result_as_interval); + REQUIRE( + result_as_interval->get_interval().get_lower() == + from_integer(0, number_type)); + REQUIRE( + result_as_interval->get_interval().get_upper() == + from_integer(1, number_type)); + } + + WHEN("Assigning the symbol a value") + { + // b1 = 0 + environment.assign( + b1.symbol_expr(), + variable_sensitivity_object_factoryt::instance().get_abstract_object( + number_type, + false, + false, + from_integer(0, number_type), + environment, + ns), + ns); + + const auto result_after_assignment = environment.eval(condition, ns); + + THEN("Should get a wrapped interval of one element") + { + REQUIRE(std::dynamic_pointer_cast( + result_after_assignment)); + const auto unwrapped = + std::dynamic_pointer_cast( + result_after_assignment) + ->unwrap_context(); + auto result_as_interval = + std::dynamic_pointer_cast( + unwrapped); + REQUIRE(result_as_interval); + REQUIRE( + result_as_interval->to_constant() == from_integer(1, number_type)); + } + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/full_struct_abstract_object/merge.cpp b/unit/analyses/variable-sensitivity/full_struct_abstract_object/merge.cpp new file mode 100644 index 00000000000..efaead79969 --- /dev/null +++ b/unit/analyses/variable-sensitivity/full_struct_abstract_object/merge.cpp @@ -0,0 +1,712 @@ +/*******************************************************************\ + + Module: Unit tests for full_struct_abstract_object::merge + + Author: DiffBlue Limited. + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +typedef constant_array_abstract_objectt::constant_array_pointert + constant_array_abstract_object_pointert; + +// Util + +class struct_utilt +{ +public: + struct_utilt(abstract_environmentt &enviroment, const namespacet &ns) + : enviroment(enviroment), ns(ns) + { + } + + exprt read_component( + full_struct_abstract_objectt::constant_struct_pointert struct_object, + const member_exprt &component) const + { + return struct_object->read(enviroment, component, ns)->to_constant(); + } + + // At the moment the full_struct_abstract_object does not support + // initialization directly from an exprt so we manually write the components + full_struct_abstract_objectt::constant_struct_pointert + build_struct(const struct_exprt &starting_value) + { + std::shared_ptr result = + std::make_shared( + starting_value, enviroment, ns); + + struct_typet struct_type(to_struct_type(starting_value.type())); + size_t comp_index = 0; + for(const exprt &op : starting_value.operands()) + { + const auto &component = struct_type.components()[comp_index]; + std::shared_ptr new_result = result->write( + enviroment, + ns, + std::stack(), + member_exprt(nil_exprt(), component.get_name(), component.type()), + enviroment.eval(op, ns), + false); + result = std::dynamic_pointer_cast( + new_result); + + ++comp_index; + } + + return result; + } + + full_struct_abstract_objectt::constant_struct_pointert + build_top_struct(const typet &struct_type) + { + return std::make_shared( + struct_type, true, false); + } + + full_struct_abstract_objectt::constant_struct_pointert + build_bottom_struct(const typet &struct_type) + { + return std::make_shared( + struct_type, false, true); + } + +private: + abstract_environmentt &enviroment; + const namespacet ns; +}; + +SCENARIO( + "merge_full_struct_abstract_object", + "[core]" + "[analyses][variable-sensitivity][full_struct_abstract_object][merge]") +{ + GIVEN("Two structs with 3 components, whose 1st component are the same") + { + // struct val1 = {.a = 1, .b = 2, .c = 3} + struct_typet struct_type; + struct_union_typet::componentt comp_a("a", integer_typet()); + struct_union_typet::componentt comp_b("b", integer_typet()); + struct_union_typet::componentt comp_c("c", integer_typet()); + struct_type.components().push_back(comp_a); + struct_type.components().push_back(comp_b); + struct_type.components().push_back(comp_c); + + exprt::operandst val1_op; + val1_op.push_back(from_integer(1, integer_typet())); + val1_op.push_back(from_integer(2, integer_typet())); + val1_op.push_back(from_integer(3, integer_typet())); + struct_exprt val1(val1_op, struct_type); + + // struct val1 = {.a = 1, .b = 4, .c = 5} + exprt::operandst val2_op; + val2_op.push_back(from_integer(1, integer_typet())); + val2_op.push_back(from_integer(4, integer_typet())); + val2_op.push_back(from_integer(5, integer_typet())); + struct_exprt val2(val2_op, struct_type); + + // index_exprt for reading from an array + const member_exprt a(nil_exprt(), "a", integer_typet()); + const member_exprt b(nil_exprt(), "b", integer_typet()); + const member_exprt c(nil_exprt(), "c", integer_typet()); + + abstract_environmentt enviroment; + enviroment.make_top(); + symbol_tablet symbol_table; + namespacet ns(symbol_table); + + variable_sensitivity_object_factoryt::instance().set_options( + vsd_configt::constant_domain()); + + struct_utilt util(enviroment, ns); + + abstract_object_pointert result; + bool modified = false; + + WHEN("Merging two constant struct AOs with the same contents") + { + auto op1 = util.build_struct(val1); + auto op2 = util.build_struct(val1); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("The original struct AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == val1.op0()); + REQUIRE(util.read_component(cast_result, b) == val1.op1()); + REQUIRE(util.read_component(cast_result, c) == val1.op2()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN("Merging two constant struct AOs with the different contents") + { + auto op1 = util.build_struct(val1); + auto op2 = util.build_struct(val2); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN( + "A new constant struct AO whose a component is the same and the " + "b and c are set to top") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == val1.op0()); + REQUIRE(util.read_component(cast_result, b) == nil_exprt()); + REQUIRE(util.read_component(cast_result, c) == nil_exprt()); + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN( + "Merging a constant struct AO with a value " + "with a constant struct AO set to top") + { + auto op1 = util.build_struct(val1); + auto op2 = util.build_top_struct(struct_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("A new constant struct AO set to top should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == nil_exprt()); + REQUIRE(util.read_component(cast_result, b) == nil_exprt()); + REQUIRE(util.read_component(cast_result, c) == nil_exprt()); + + // We can't reuse the abstract object as the value has changed + REQUIRE(result != op1); + } + } + WHEN( + "Merging a constant struct AO with a value " + "with a constant struct AO set to bottom") + { + auto op1 = util.build_struct(val1); + auto op2 = util.build_bottom_struct(struct_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("The original const AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == val1.op0()); + REQUIRE(util.read_component(cast_result, b) == val1.op1()); + REQUIRE(util.read_component(cast_result, c) == val1.op2()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant struct AO set to top " + "with a constant struct AO with a value") + { + auto op1 = util.build_top_struct(struct_type); + auto op2 = util.build_struct(val1); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("The original constant struct AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == nil_exprt()); + REQUIRE(util.read_component(cast_result, b) == nil_exprt()); + REQUIRE(util.read_component(cast_result, c) == nil_exprt()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant struct AO set to top " + "with a constant struct AO set to top") + { + auto op1 = util.build_top_struct(struct_type); + auto op2 = util.build_top_struct(struct_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("The original constant struct AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == nil_exprt()); + REQUIRE(util.read_component(cast_result, b) == nil_exprt()); + REQUIRE(util.read_component(cast_result, c) == nil_exprt()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant struct AO set to top " + "with a constant struct AO set to bottom") + { + auto op1 = util.build_top_struct(struct_type); + auto op2 = util.build_bottom_struct(struct_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("The original constant struct AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == nil_exprt()); + REQUIRE(util.read_component(cast_result, b) == nil_exprt()); + REQUIRE(util.read_component(cast_result, c) == nil_exprt()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging a constant struct AO set to bottom " + "with a constant struct AO with a value") + { + auto op1 = util.build_bottom_struct(struct_type); + auto op2 = util.build_struct(val1); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("A new AO should be returned with op2s valuee") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == val1.op0()); + REQUIRE(util.read_component(cast_result, b) == val1.op1()); + REQUIRE(util.read_component(cast_result, c) == val1.op2()); + + // Is optimal + REQUIRE(result != op1); + } + } + WHEN( + "Merging a constant struct AO set to bottom " + "with a constant struct AO set to top") + { + auto op1 = util.build_bottom_struct(struct_type); + auto op2 = util.build_top_struct(struct_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("A new constant struct AO should be returned set to top ") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == nil_exprt()); + REQUIRE(util.read_component(cast_result, b) == nil_exprt()); + REQUIRE(util.read_component(cast_result, c) == nil_exprt()); + + // Is optimal + REQUIRE(result != op1); + } + } + WHEN( + "Merging a constant struct AO set to bottom " + "with a constant struct AO set to bottom") + { + auto op1 = util.build_bottom_struct(struct_type); + auto op2 = util.build_bottom_struct(struct_type); + + result = abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("The original bottom AO should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE(cast_result->is_bottom()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging constant struct AO with value " + "with a abstract object set to top") + { + const auto &op1 = util.build_struct(val1); + const auto &op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("We should get a new AO of the same type but set to top") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE(modified); + REQUIRE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // Since it has modified, we definitely shouldn't be reusing the pointer + REQUIRE_FALSE(result == op1); + } + } + WHEN( + "Merging constant struct AO with value " + "with a abstract object set to bottom") + { + const auto &op1 = util.build_struct(val1); + const auto &op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + THEN("We should get the same constant struct AO back") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(util.read_component(cast_result, a) == val1.op0()); + REQUIRE(util.read_component(cast_result, b) == val1.op1()); + REQUIRE(util.read_component(cast_result, c) == val1.op2()); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "Merging constant struct AO set to top " + "with a abstract object set to top") + { + const auto &op1 = util.build_top_struct(struct_type); + const auto &op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("We should get the same abstract object back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN( + "Merging constant struct AO set to top " + "with a abstract object set to bottom") + { + const auto &op1 = util.build_top_struct(struct_type); + const auto &op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Should get the same abstract object back") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN( + "Merging constant struct AO set to bottom " + " with a abstract object set to top") + { + const auto &op1 = util.build_bottom_struct(struct_type); + const auto &op2 = + std::make_shared(val1.type(), true, false); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Return a new top abstract object of the same type") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // We don't optimize for returning the second parameter if we can + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("Merging constant struct AO set to bottom with a AO set to bottom") + { + const auto &op1 = util.build_bottom_struct(struct_type); + const auto &op2 = + std::make_shared(val1.type(), false, true); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("Return the original abstract object") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + // Optimization check + REQUIRE(result == op1); + + // Is type still correct + const auto &cast_result = + std::dynamic_pointer_cast(result); + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + } + } + WHEN("Merging AO set to top with a constant struct AO with a value") + { + const auto &op1 = + std::make_shared(val1.type(), true, false); + const auto &op2 = util.build_struct(val1); + + bool modified; + + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("Merging AO set to top with a constant struct AO set to top") + { + const auto &op1 = + std::make_shared(val1.type(), true, false); + const auto &op2 = util.build_top_struct(struct_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("Merging AO set to top with a constant struct AO set to bottom") + { + const auto &op1 = + std::make_shared(val1.type(), true, false); + const auto &op2 = util.build_bottom_struct(struct_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // Is optimal + REQUIRE(op1 == result); + } + } + WHEN("Merging AO set to bottom with a constant struct AO with a value") + { + const auto &op1 = + std::make_shared(val1.type(), false, true); + const auto &op2 = util.build_struct(val1); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The a new top AO should be returned") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(typeid(result.get()) == typeid(const abstract_objectt *)); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + } + + // We don't optimize for returning the second parameter if we can + } + WHEN("Merging AO set to bottom with a constant struct AO set to top") + { + const auto &op1 = + std::make_shared(val1.type(), false, true); + const auto &op2 = util.build_top_struct(struct_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The a new top AO should be returned") + { + // Simple correctness of merge + REQUIRE(modified); + REQUIRE(result->is_top()); + REQUIRE_FALSE(result->is_bottom()); + + // We don't optimize for returning the second parameter if we can + } + } + WHEN("Merging AO set to bottom with a constant struct AO set to bottom") + { + const auto &op1 = + std::make_shared(val1.type(), false, true); + const auto &op2 = util.build_bottom_struct(struct_type); + + bool modified; + abstract_object_pointert result = + abstract_objectt::merge(op1, op2, modified); + + THEN("The original AO should be returned") + { + // Simple correctness of merge + REQUIRE_FALSE(modified); + REQUIRE_FALSE(result->is_top()); + REQUIRE(result->is_bottom()); + + REQUIRE(result == op1); + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/full_struct_abstract_object/module_dependencies.txt b/unit/analyses/variable-sensitivity/full_struct_abstract_object/module_dependencies.txt new file mode 100644 index 00000000000..ac96be3c1ef --- /dev/null +++ b/unit/analyses/variable-sensitivity/full_struct_abstract_object/module_dependencies.txt @@ -0,0 +1,3 @@ +analyses +testing-utils +util diff --git a/unit/analyses/variable-sensitivity/interval_abstract_value/meet.cpp b/unit/analyses/variable-sensitivity/interval_abstract_value/meet.cpp new file mode 100644 index 00000000000..6d9499818a6 --- /dev/null +++ b/unit/analyses/variable-sensitivity/interval_abstract_value/meet.cpp @@ -0,0 +1,287 @@ +/*******************************************************************\ + + Module: Unit tests for interval_abstract_valuet::meet + + Author: DiffBlue Limited. + +\*******************************************************************/ + +#include + +#include +#include +#include + +SCENARIO( + "meet_interval_abstract_value", + "[core][analyses][variable-sensitivity][interval_abstract_value][meet]") +{ + GIVEN("An environment with two intervals: 1--10 and 2--5") + { + const typet type = signedbv_typet(32); + const exprt val1 = from_integer(1, type); + REQUIRE(constant_interval_exprt::is_int(val1.type())); + const exprt val10 = from_integer(10, type); + const exprt val11 = from_integer(11, type); + REQUIRE(constant_interval_exprt::is_int(val10.type())); + const auto interval1_10 = constant_interval_exprt(val1, val10); + + const auto varx = symbol_exprt(type); + const auto x_le_10 = binary_relation_exprt(varx, ID_le, val10); + const auto lt_10_x = binary_relation_exprt(val10, ID_lt, varx); + + const exprt val2 = from_integer(2, type); + const exprt val15 = from_integer(15, type); + const auto interval2_15 = constant_interval_exprt(val2, val15); + + const auto interval1_2 = constant_interval_exprt(val1, val2); + const auto interval10_15 = constant_interval_exprt(val10, val15); + + const auto temp_expr = unsignedbv_typet(32); + const auto max_value = temp_expr.largest_expr(); + const auto x_ge_max = binary_relation_exprt(varx, ID_ge, max_value); + const auto x_gt_max = binary_relation_exprt(varx, ID_gt, max_value); + + abstract_environmentt enviroment; + enviroment.make_top(); + symbol_tablet symbol_table; + namespacet ns(symbol_table); + + WHEN( + "meeting constant AO with interval " + "with a constant AO with the same interval") + { + abstract_object_pointert op1 = std::make_shared( + interval1_10, enviroment, ns); + abstract_object_pointert op2 = std::make_shared( + interval1_10, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("The original abstract object should be returned unchanged") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE_FALSE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + REQUIRE(cast_result->get_interval() == interval1_10); + + // Is optimal + REQUIRE(result == op1); + } + } + WHEN( + "meeting constant AO with interval " + "with a constant AO with the different interval") + { + abstract_object_pointert op1 = + std::make_shared(interval1_2, enviroment, ns); + abstract_object_pointert op2 = std::make_shared( + interval10_15, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("A new constant abstract object set to bottom should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE(cast_result->is_bottom()); + + // Intersection should have this exact value + REQUIRE_FALSE(result == op1); + } + } + WHEN( + "meeting constant AO with interval " + "with a constant AO with the different interval") + { + abstract_object_pointert op1 = std::make_shared( + interval1_10, enviroment, ns); + abstract_object_pointert op2 = std::make_shared( + interval2_15, enviroment, ns); + + const auto interval2_10 = constant_interval_exprt(val2, val10); + abstract_object_pointert op3 = std::make_shared( + interval2_10, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN("A new constant abstract object set to top should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // Intersection should have this exact value + REQUIRE(cast_result->get_interval() == interval2_10); + } + } + WHEN( + "build constant AO with interval using le_expression " + "then meet with a constant AO with the different interval") + { + abstract_object_pointert op1 = + std::make_shared(x_le_10, enviroment, ns); + abstract_object_pointert op2 = std::make_shared( + interval2_15, enviroment, ns); + + const auto interval2_10 = constant_interval_exprt(val2, val10); + abstract_object_pointert op3 = std::make_shared( + interval2_10, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN( + "A new constant abstract object set to intersection should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // Intersection should have this exact value + REQUIRE(cast_result->get_interval() == interval2_10); + } + } + WHEN( + "build constant AO with interval using lt_expression " + "then meet with a constant AO with the different interval") + { + abstract_object_pointert op1 = + std::make_shared(lt_10_x, enviroment, ns); + abstract_object_pointert op2 = std::make_shared( + interval2_15, enviroment, ns); + + const auto interval11_15 = constant_interval_exprt(val11, val15); + abstract_object_pointert op3 = std::make_shared( + interval11_15, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN( + "A new constant abstract object set to intersection should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // Intersection should have this exact value + REQUIRE(cast_result->get_interval() == interval11_15); + } + } + WHEN( + "build constant AO with interval using ge_max_expression " + "then meet with a constant AO with the different interval") + { + abstract_object_pointert op1 = + std::make_shared(lt_10_x, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(x_ge_max, enviroment, ns); + + const auto intervalmax_max = + constant_interval_exprt(max_value, max_exprt(max_value.type())); + abstract_object_pointert op3 = std::make_shared( + intervalmax_max, enviroment, ns); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN( + "A new constant abstract object set to intersection should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE_FALSE(cast_result->is_bottom()); + + // Intersection should have this exact value + REQUIRE(cast_result->get_interval() == intervalmax_max); + } + } + WHEN( + "build constant AO with interval using gt_max_expression " + "then meet with a constant AO with the different interval") + { + abstract_object_pointert op1 = + std::make_shared(lt_10_x, enviroment, ns); + abstract_object_pointert op2 = + std::make_shared(x_gt_max, enviroment, ns); + + const auto &op2_cast = + std::dynamic_pointer_cast(op2); + REQUIRE(op2_cast->is_bottom()); + + const auto interval10_max = + constant_interval_exprt(val10, max_exprt(val10.type())); + + bool modified; + abstract_object_pointert result = + abstract_objectt::meet(op1, op2, modified); + + const auto &cast_result = + std::dynamic_pointer_cast(result); + + THEN( + "A new constant abstract object set to intersection should be returned") + { + // Though we may become top or bottom, the type should be unchanged + REQUIRE(cast_result); + + // Correctness of meet + REQUIRE(modified); + REQUIRE_FALSE(cast_result->is_top()); + REQUIRE(cast_result->is_bottom()); + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/interval_abstract_value/module_dependencies.txt b/unit/analyses/variable-sensitivity/interval_abstract_value/module_dependencies.txt new file mode 100644 index 00000000000..f4c536408fa --- /dev/null +++ b/unit/analyses/variable-sensitivity/interval_abstract_value/module_dependencies.txt @@ -0,0 +1,3 @@ +analyses +testing-utils + diff --git a/unit/analyses/variable-sensitivity/last_written_location.cpp b/unit/analyses/variable-sensitivity/last_written_location.cpp new file mode 100644 index 00000000000..7aa3d519a93 --- /dev/null +++ b/unit/analyses/variable-sensitivity/last_written_location.cpp @@ -0,0 +1,137 @@ +/*******************************************************************\ + + Module: Write Stack Unit Tests + + Author: DiffBlue Limited. + +\*******************************************************************/ + +/// \file +/// Unit tests for testing of correct tracking of +/// last written location by objects + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #include + +SCENARIO( + "Constructing two environments to make sure we correctly identify modified " + "symbols", + "[core][analyses][variable-sensitivity][last-written-location]") +{ + GIVEN("Two identifiers that contain integer values") + { + const irep_idt identifier = "hello"; + auto first_val = symbol_exprt(identifier, integer_typet()); + symbolt first_sym; + first_sym.name = first_val.get_identifier(); + + auto rhs_val = from_integer(5, integer_typet()); + + const irep_idt second_identifier = "world"; + auto second_val = symbol_exprt(second_identifier, integer_typet()); + symbolt second_sym; + second_sym.name = second_val.get_identifier(); + + symbol_tablet symbol_table; + + symbol_table.add(first_sym); + symbol_table.add(second_sym); + namespacet ns(symbol_table); + + variable_sensitivity_object_factoryt::instance().set_options( + vsd_configt::constant_domain()); + + WHEN("The identifiers get inserted into two environments") + { + abstract_environmentt env; + + auto first_eval_rhs = env.eval(rhs_val, ns); + auto first_eval_res = env.eval(first_val, ns); + + auto second_eval_res = env.eval(second_val, ns); + auto rhs_val_2 = from_integer(10, integer_typet()); + auto second_eval_rhs = env.eval(rhs_val_2, ns); + + env.assign(first_val, first_eval_rhs, ns); + env.assign(second_val, second_eval_rhs, ns); + + abstract_environmentt second_env; + second_env.assign(first_val, first_eval_rhs, ns); + second_env.assign(second_val, second_eval_rhs, ns); + + THEN("The modified symbols between the two domains should be none") + { + auto changed_vals = + abstract_environmentt::modified_symbols(env, second_env); + REQUIRE(changed_vals.size() == 0); + } + } + } + GIVEN("Two identifiers that contain integer values") + { + const irep_idt identifier = "hello"; + auto first_val = symbol_exprt(identifier, integer_typet()); + symbolt first_sym; + first_sym.name = first_val.get_identifier(); + + auto rhs_val = from_integer(5, integer_typet()); + + const irep_idt second_identifier = "world"; + auto second_val = symbol_exprt(second_identifier, integer_typet()); + symbolt second_sym; + second_sym.name = second_val.get_identifier(); + + symbol_tablet symbol_table; + + symbol_table.add(first_sym); + symbol_table.add(second_sym); + namespacet ns(symbol_table); + + WHEN( + "The identifiers get inserted into two environments, but one of " + "them has a different value in one of the environments") + { + abstract_environmentt env; + + auto first_eval_rhs = env.eval(rhs_val, ns); + auto first_eval_res = env.eval(first_val, ns); + + auto second_eval_res = env.eval(second_val, ns); + auto rhs_val_2 = from_integer(10, integer_typet()); + auto second_eval_rhs = env.eval(rhs_val_2, ns); + + env.assign(first_val, first_eval_rhs, ns); + env.assign(second_val, second_eval_rhs, ns); + + auto rhs_val_3 = from_integer(20, integer_typet()); + + abstract_environmentt second_env; + auto new_rhs_val = second_env.eval(rhs_val_3, ns); + second_env.assign(first_val, first_eval_rhs, ns); + second_env.assign(second_val, new_rhs_val, ns); + + THEN("The modified symbols between the two domains should be none") + { + auto changed_vals = + abstract_environmentt::modified_symbols(env, second_env); + REQUIRE(changed_vals.size() == 0); + } + } + } +} diff --git a/unit/analyses/variable-sensitivity/module_dependencies.txt b/unit/analyses/variable-sensitivity/module_dependencies.txt new file mode 100644 index 00000000000..ac96be3c1ef --- /dev/null +++ b/unit/analyses/variable-sensitivity/module_dependencies.txt @@ -0,0 +1,3 @@ +analyses +testing-utils +util diff --git a/unit/analyses/variable-sensitivity/value_set/abstract_value.cpp b/unit/analyses/variable-sensitivity/value_set/abstract_value.cpp new file mode 100644 index 00000000000..8d78e530377 --- /dev/null +++ b/unit/analyses/variable-sensitivity/value_set/abstract_value.cpp @@ -0,0 +1,361 @@ +/*******************************************************************\ + +Module: Unit tests for value set abstract values + +Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "value_set_test_common.h" +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace +{ +// NOLINTNEXTLINE(readability/identifiers) +class ContainsAllOf + : public Catch::MatcherBase +{ +private: + std::vector should_contain_values; + +public: + template + explicit ContainsAllOf(Args &&... should_contain_values) + : should_contain_values{std::forward(should_contain_values)...} + { + } + + bool match(const value_set_abstract_valuet::valuest &values) const override + { + for(auto const &value : should_contain_values) + { + if(values.count(value) == 0) + { + return false; + } + } + return true; + } + + std::string describe() const override + { + std::ostringstream oss{}; + auto const ns = namespacet{symbol_tablet{}}; + oss << "contains all of { "; + bool first = true; + for(auto const &value : should_contain_values) + { + if(!first) + { + oss << ", "; + } + oss << expr2c(value, ns); + } + oss << " }"; + return oss.str(); + } +}; +} // namespace + +TEST_CASE( + "A value set abstract object created from type is top", + VALUE_SET_TEST_TAGS) +{ + value_set_abstract_valuet abstract_value(signedbv_typet{32}); + REQUIRE(abstract_value.is_top()); + REQUIRE(!abstract_value.is_bottom()); +} + +TEST_CASE( + "A value set created from a single value represents that value", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value = from_integer(10, type); + auto const value_set = value_set_abstract_valuet{type, {value}}; + + REQUIRE(!value_set.is_top()); + REQUIRE(!value_set.is_bottom()); + REQUIRE(value_set.get_values().size() == 1); + REQUIRE_THAT(value_set.get_values(), ContainsAllOf{value}); +} + +TEST_CASE( + "A value set created from multiple values represents all of them", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value1 = from_integer(10, type); + auto const value2 = from_integer(36, type); + auto const value3 = from_integer(42, type); + auto const value_set = + value_set_abstract_valuet{type, {value1, value2, value3}}; + + REQUIRE(!value_set.is_top()); + REQUIRE(!value_set.is_bottom()); + REQUIRE(value_set.get_values().size() == 3); + REQUIRE_THAT(value_set.get_values(), ContainsAllOf(value1, value2, value3)); +} + +TEST_CASE( + "A value set created from an empty set is bottom", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value_set = + value_set_abstract_valuet{type, value_set_abstract_valuet::valuest{}}; + REQUIRE(!value_set.is_top()); + REQUIRE(value_set.is_bottom()); +} + +TEST_CASE( + "A value set created with too many elements is TOP", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto values = value_set_abstract_valuet::valuest{}; + for(std::size_t i = 0; i <= value_set_abstract_valuet::max_value_set_size; + ++i) + { + values.insert(from_integer(i, type)); + } + auto const value_set = value_set_abstract_valuet{type, values}; + + REQUIRE(value_set.is_top()); + REQUIRE(!value_set.is_bottom()); +} + +TEST_CASE( + "A value set created by merging two single value-value sets contains both " + "values", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value1 = from_integer(10, type); + auto const value2 = from_integer(42, type); + using valuest = value_set_abstract_valuet::valuest; + auto const value_set1 = + std::make_shared(type, valuest{value1}); + auto const value_set2 = + std::make_shared(type, valuest{value2}); + + bool out_modifications; + auto const merged_abstract_object = + abstract_objectt::merge(value_set1, value_set2, out_modifications); + auto const merged_value_set = + std::dynamic_pointer_cast( + merged_abstract_object); + + REQUIRE(merged_value_set != nullptr); + REQUIRE(!merged_value_set->is_top()); + REQUIRE(!merged_value_set->is_bottom()); + REQUIRE(merged_value_set->get_values().size() == 2); + REQUIRE_THAT(merged_value_set->get_values(), ContainsAllOf(value1, value2)); +} + +TEST_CASE( + "A value set created by merging two multi-value value sets contains all " + "values from both of them", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value1 = from_integer(10, type); + auto const value2 = from_integer(20, type); + auto const value3 = from_integer(30, type); + using valuest = value_set_abstract_valuet::valuest; + auto const value_set1 = + std::make_shared(type, valuest{value1, value2}); + auto const value_set2 = + std::make_shared(type, valuest{value2, value3}); + + bool out_modifications; + auto const merged_abstracted_object = + abstract_objectt::merge(value_set1, value_set2, out_modifications); + auto const merged_value_set = + std::dynamic_pointer_cast( + merged_abstracted_object); + + REQUIRE(merged_value_set != nullptr); + REQUIRE(!merged_value_set->is_top()); + REQUIRE(!merged_value_set->is_bottom()); + REQUIRE(merged_value_set->get_values().size() == 3); + REQUIRE_THAT( + merged_value_set->get_values(), ContainsAllOf(value1, value2, value3)); +} + +TEST_CASE( + "The result of merging two value sets is TOP if both are non-top value sets " + "and the resulting set would have too many elements", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + using valuest = value_set_abstract_valuet::valuest; + auto values = valuest{}; + for(std::size_t i = 0; i < value_set_abstract_valuet::max_value_set_size; ++i) + { + values.insert(from_integer(i, type)); + } + + auto const straw_that_broke_the_camels_back = + from_integer(value_set_abstract_valuet::max_value_set_size, type); + + auto const value_set1 = + std::make_shared(type, values); + REQUIRE(!value_set1->is_top()); + + auto const value_set2 = std::make_shared( + type, valuest{straw_that_broke_the_camels_back}); + REQUIRE(!value_set2->is_top()); + + bool out_modifications; + auto const merged_abstract_object = + abstract_objectt::merge(value_set1, value_set2, out_modifications); + auto const merged_value_set = + std::dynamic_pointer_cast( + merged_abstract_object); + + REQUIRE(merged_value_set != nullptr); + REQUIRE(merged_value_set->is_top()); + REQUIRE(!merged_value_set->is_bottom()); +} + +TEST_CASE( + "The result of merging two value sets is top if one of them is TOP", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value = from_integer(10, type); + using valuest = value_set_abstract_valuet::valuest; + auto const value_set1 = + std::make_shared(type, valuest{value}); + auto const value_set2 = std::make_shared(type); + + bool out_modifications; + auto const merged_abstract_object = + abstract_objectt::merge(value_set1, value_set2, out_modifications); + + REQUIRE(merged_abstract_object->is_top()); + REQUIRE(!merged_abstract_object->is_bottom()); +} + +TEST_CASE( + "Make sure the output method works correctly with a value set with 0 " + "elements", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value_set = + value_set_abstract_valuet{type, value_set_abstract_valuet::valuest{}}; + + std::stringstream ss; + value_set.output( + ss, ait{}, namespacet{symbol_tablet{}}); + REQUIRE(ss.str() == "BOTTOM"); +} + +TEST_CASE( + "Make sure the output method works correctly with a value set with 1 element", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value = from_integer(10, type); + auto const value_set = value_set_abstract_valuet{type, {value}}; + + std::stringstream ss; + value_set.output( + ss, ait{}, namespacet{symbol_tablet{}}); + REQUIRE(ss.str() == "{ 10 }"); +} + +TEST_CASE( + "Make sure the output method works correctly with a value set with 3 " + "elements", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value1 = from_integer(10, type); + auto const value2 = from_integer(12, type); + auto const value3 = from_integer(14, type); + auto const value_set = + value_set_abstract_valuet{type, {value1, value2, value3}}; + + std::stringstream ss; + value_set.output( + ss, ait{}, namespacet{symbol_tablet{}}); + REQUIRE(ss.str() == "{ 10 12 14 }"); +} + +TEST_CASE( + "Make sure that the output method works with a TOP value set", + VALUE_SET_TEST_TAGS) +{ + // Build and ensure value set is TOP + auto const type = signedbv_typet{32}; + auto values = value_set_abstract_valuet::valuest{}; + for(std::size_t i = 0; i <= value_set_abstract_valuet::max_value_set_size; + ++i) + { + values.insert(from_integer(i, type)); + } + auto const value_set = value_set_abstract_valuet{type, values}; + REQUIRE(value_set.is_top()); + + std::stringstream ss; + value_set.output( + ss, ait{}, namespacet{symbol_tablet{}}); + REQUIRE(ss.str() == "TOP"); +} + +TEST_CASE( + "Value set abstract value that is TOP can not be turned into a constant", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value_set = value_set_abstract_valuet{type}; + + REQUIRE(value_set.to_constant() == nil_exprt{}); +} + +TEST_CASE( + "Value set abstract value that is bottom can not be turned into a constant", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value_set = + value_set_abstract_valuet{type, value_set_abstract_valuet::valuest{}}; + + REQUIRE(value_set.to_constant() == nil_exprt{}); +} + +TEST_CASE( + "Value set with multiple possible values can not be turned into a constant", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto values = value_set_abstract_valuet::valuest{}; + values.insert(from_integer(0, type)); + values.insert(from_integer(1, type)); + auto const value_set = value_set_abstract_valuet{type, values}; + + REQUIRE(value_set.to_constant() == nil_exprt{}); +} + +TEST_CASE( + "Value set with exactly one possible value can be turned into a constant", + VALUE_SET_TEST_TAGS) +{ + auto const type = signedbv_typet{32}; + auto const value = from_integer(0, type); + auto const value_set = + value_set_abstract_valuet{type, value_set_abstract_valuet::valuest{value}}; + + REQUIRE(value_set.to_constant() == value); +} diff --git a/unit/analyses/variable-sensitivity/value_set/array_abstract_object.cpp b/unit/analyses/variable-sensitivity/value_set/array_abstract_object.cpp new file mode 100644 index 00000000000..f798d441b77 --- /dev/null +++ b/unit/analyses/variable-sensitivity/value_set/array_abstract_object.cpp @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: Unit tests for value set array abstract values + +Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "value_set_test_common.h" +#include +#include + +TEST_CASE( + "A value set array abstract object created from type is top", + VALUE_SET_TEST_TAGS) +{ + value_set_array_abstract_objectt abstract_object{ + array_typet{signedbv_typet{32}, from_integer(1, unsignedbv_typet{32})}}; + REQUIRE(abstract_object.is_top()); + REQUIRE(!abstract_object.is_bottom()); +} diff --git a/unit/analyses/variable-sensitivity/value_set/module_dependencies.txt b/unit/analyses/variable-sensitivity/value_set/module_dependencies.txt new file mode 100644 index 00000000000..d44f66043f7 --- /dev/null +++ b/unit/analyses/variable-sensitivity/value_set/module_dependencies.txt @@ -0,0 +1,4 @@ +analyses +ansi-c +testing-utils +util diff --git a/unit/analyses/variable-sensitivity/value_set/pointer_abstract_object.cpp b/unit/analyses/variable-sensitivity/value_set/pointer_abstract_object.cpp new file mode 100644 index 00000000000..a796d0a898e --- /dev/null +++ b/unit/analyses/variable-sensitivity/value_set/pointer_abstract_object.cpp @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Unit tests for value set pointer abstract objects + +Author: Diffblue Ltd. + +\*******************************************************************/ + +#include "value_set_test_common.h" +#include + +TEST_CASE( + "A value set pointer abstract object created from type is top", + VALUE_SET_TEST_TAGS) +{ + value_set_pointer_abstract_objectt abstract_object( + pointer_typet(signedbv_typet{32}, 32)); + REQUIRE(abstract_object.is_top()); + REQUIRE(!abstract_object.is_bottom()); +} diff --git a/unit/analyses/variable-sensitivity/value_set/value_set_test_common.h b/unit/analyses/variable-sensitivity/value_set/value_set_test_common.h new file mode 100644 index 00000000000..55e23d02f39 --- /dev/null +++ b/unit/analyses/variable-sensitivity/value_set/value_set_test_common.h @@ -0,0 +1,18 @@ +/*******************************************************************\ + +Module: Unit tests for value set pointer abstract objects + +Author: Diffblue Ltd. + +\*******************************************************************/ + +// NOLINTNEXTLINE(whitespace/line_length) +#ifndef CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_VALUE_SET_TEST_COMMON_H +// NOLINTNEXTLINE(whitespace/line_length) +#define CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_VALUE_SET_TEST_COMMON_H + +#include +#define VALUE_SET_TEST_TAGS "[core][analyses][variable-sensitivity][value-set]" + +// NOLINTNEXTLINE(whitespace/line_length) +#endif // CPROVER_ANALYSES_VARIABLE_SENSITIVITY_VALUE_SET_VALUE_SET_TEST_COMMON_H diff --git a/unit/util/sharing_map.cpp b/unit/util/sharing_map.cpp index 41cefc9aedb..61ebac21c35 100644 --- a/unit/util/sharing_map.cpp +++ b/unit/util/sharing_map.cpp @@ -492,7 +492,7 @@ TEST_CASE("Sharing map views and iteration", "[core][util]") SECTION("View") { - typedef std::pair pt; + typedef std::pair pt; sharing_map_standardt sm; sharing_map_standardt::viewt view;