Unable to verify assign clause - Frama-C - loops

In my example below, frama-c isn't able to prove my assign clause in the function contract and I am not sure why. I would really appreciate any help.
/*#
# requires n>0;
# requires \valid(a+(0..n-1));
# ensures \forall int i; (n>i>=0 ==> a[i]==0);
#*/
void f(int n, float *a) {
/*#
# loop invariant n>=0;
# loop invariant test: \forall int j; (n>j>i ==> a[j]==0);
# loop assigns i, a[0..n-1];
#*/
for (int i=n-1; i>=0; i--) {
a[i] = 0;
}
}
Here is my ouput:
[wp] 8 goals scheduled
[wp] [Alt-Ergo] Goal typed_f_loop_assign_part3 : Unknown (Qed:24ms) (406ms)
[wp] Proved goals: 7 / 8
Qed: 5 (4ms-13ms-24ms)
Alt-Ergo: 2 (20ms-32ms) (28) (unknown: 1)
I'm just zeroing out an array in reverse order in this program.

You are missing a loop invariant on i. As a result, WP has no idea of the range of values it can take, and cannot prove that the indexes of a that are being written are 0 .. n-1. Just add
# loop invariant -1 <= i <= n-1;
(Notice that at the end of the last iteration, i is -1, not 0.)

Related

Why is my ACSL contract failing on my copy function?

I'm new to ACSL and I tried to replicate the function contract of this copy function provided by "ACSL by Example" from the Fraunhofer Society. The Code below works perfectly and every goal gets proven.
/*#
predicate IsValidRange(uint8_t* a, integer n) =
(0 <= n) && \valid(a+(0.. n-1));
*/
/*#
predicate
IsEqual{A,B}(uint8_t* a, integer n, uint8_t* b) =
\forall integer i; 0 <= i < n ==>
\at(a[i], A) == \at(b[i], B);
*/
/*#
requires IsValidRange(a, n);
requires IsValidRange(b, n);
requires \separated(a+(0..n-1), b+(0..n-1));
assigns b[0..n-1];
ensures IsEqual{Here,Here}(a, n, b);
*/
void copy(const uint8_t *a, uint8_t n, uint8_t *b)
{
uint8_t i = 0;
/*#
loop invariant 0 <= i <= n;
loop invariant IsEqual{Here,Here}(a, i, b);
loop assigns b[0..n-1], i;
loop variant n-i;
*/
while (i < n)
{
b[i] = a[i];
++i;
//# assert 0 < i <= n;
}
}
I tried to adjust the specifications to fit my slightly different copy function. This one cannot be proven. Am I missing some important assertions that would assist the automatic prover or is my approach fundamentally flawed?
/*#
requires IsValidRange(src, len);
requires IsValidRange(dst, len);
requires len != 0;
requires \separated(src+(0 .. len-1), dst+(0 .. len-1));
assigns dst[0 .. \old(len)-1];
ensures IsEqual{Here,Here}(src, \old(len), dst);
*/
void Copybytes(
uint8_t const *src,
uint8_t *dst,
uint8_t len
)
{
uint8_t temp;
//# assert 0 != len;
/*#
loop invariant bound: 0 <= len <= \at(len,LoopEntry);
loop invariant equal: IsEqual{Here,Here}(src, \at(len,LoopEntry)-len, dst);
loop assigns dst, src, len, temp;
loop variant len;
*/
do
{
temp = *src;
src++;
*dst = temp;
dst++;
//# assert 0 < len <= \at(len,LoopEntry);
} while (--len != 0u);
}
This is the wp output I get after running the code:
[wp] 17 goals scheduled
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_ensures : Timeout (Qed:11ms) (10s)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_loop_invariant_equal_prese
rved : Timeout (Qed:9ms) (10s)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_assigns_part4 : Timeout (Qed:6ms) (10s) (cached)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_assert_2 : Timeout (Qed:6ms) (10s)
[wp] [Alt-Ergo 2.4.0] Goal typed_Copybytes_loop_assigns_part2 : Timeout (Qed:7ms) (10s)
[wp] [Cache] found:3, updated:6
[wp] Proved goals: 12 / 17
Qed: 8 (1ms-3ms-7ms)
Alt-Ergo 2.4.0: 4 (16ms-31ms) (86) (interrupted: 5) (cached: 3)
Your loop assigns is incorrect: you should mention that the loop body is modifying the content of the dst buffer, not only the pointer itself. Something like loop assigns dst, src, len, temp, dst[0 .. \at(len, Pre)]; should be better.
Note that, in theory, you could restrict the range of changed cells to dst[len .. \at(len,Pre)]. However, in practice, WP can easily get confused when confronted to an assigned block whose length changes from one loop step to the other, so that it is usually better to give the full extent of the range directly (the drawback is that you might have to write another invariant specifying that the elements in dst[0 .. len - 1] haven't changed yet, but as far as I can tell this is not needed here, since we never use the value of these elements anywhere).
Finally, a small tip: assigns-related goal are usually quite easy to prove. If you see that some of them cannot be discharged, checking that you haven't forgotten something there is a good place to start investigating.

Frama-C does not verify zeroing-array example from https://frama-c.com/html/acsl.html

The example in this question seems to work only for fixed-length arrays, however the similar code below from https://frama-c.com/html/acsl.html doesn't seem to pass. As soon as I change the code to be include the requirement && n == 42 (or any other positive integer) it passes.
When it fails it says [wp] [Alt-Ergo 2.4.1] Goal typed_set_to_0_loop_invariant_established : Timeout (Qed:1ms) (1') (cached)
/*#
requires \valid(a+(0..n-1));
assigns a[0..n-1];
ensures
\forall integer i;
0 <= i < n ==> a[i] == 0;
*/
void set_to_0(int* a, int n){
int i;
/*#
loop invariant 0 <= i <= n;
loop invariant
\forall integer j;
0 <= j < i ==> a[j] == 0;
loop assigns i, a[0..n-1];
loop variant n-i;
*/
for(i = 0; i < n; ++i)
a[i] = 0;
}
Any tips on how to proceed / what the intended behaviour/flags are?
Actually, the loop invariant 0 <= i <= n is not true if n is strictly negative. A loop invariant must hold the first time you reach the loop (this is what the "established" part means, as opposed to the "preserved" part in which you must check that it holds at the end of any loop step, assuming it was true at the beginning of said step), even if you don't end up entering the loop at all (which is obviously the case here if n<0). You must thus add a requires n >=0; in the contract of the function.
EDIT
I forgot to mention that another solution is of course to make n and i unsigned (or even better to #include <stddef.h> and use size_t): this way, you are guaranteed a positive number without having to write an additional requires)

Timeout during the verification of two array addition [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am trying to verify the addition of two 2d arrays but I constantly take a timeout error regardless of the solver that I use.
The code that I am trying to verify is the following:
typedef struct{
float mem[3];
}BaseMatrix3x1;
/*# requires \valid(b1) && \valid(b2);
# ensures A: \forall integer i; 0 <= i < 3 ==>
b1->mem[i] == \old(b1)->mem[i] + b2->mem[i];
# assigns b1->mem[0..2];
#*/
void baseMatrixAssignAdd3x1(BaseMatrix3x1 *b1, BaseMatrix3x1 *b2){
/*# loop invariant 0 <= i <= 3;
loop invariant \forall integer k;
0 <= k < i ==>
\at(b1->mem[k], LoopCurrent) ==
\at(b1->mem[k], LoopEntry) + \at(b2->mem[k], LoopEntry);
loop assigns i, b1->mem[0..2];*/
for(unsigned int i=0; i<3; i++){
b1->mem[i] += b2->mem[i];
}
}
The second loop invariant is the one that causes all the solvers to timeout.
Do you have any suggestions?
Edit:
I fixed the assign error (which was not the problem though).
I don't call this function somewhere yet, I am just trying to prove the loop invariants. From my understanding, in order to verify a function, we do not care about the way that this function will be called. We care only about the Pre and Post conditions that we have.
First, what is incorrect/missing in your code:
you never mention that b1 and b2 are pointers to different matrices. Your loop assigns are incorrect without this information, because b2->mem[0..2] also gets assigned. You need to add a requires \separated(b1, b2); assumption
you postcondition is incorrect, because \old only applies to the pointer b1 (which remains unchanged in the function anyway), while it should apply to b1->mem. You should have written \old(b1->mem[i]) instead.
you're missing an important loop invariant, namely that b1->mem[i..2] has not been modified (yet). Since your loop assigns mentions that all of b1->mem may be assigned at every iteration, you need to add an invariant on the unchanged parts.
Next, one apparent limitation of the WP plugin that prevents a full proof:
the support for the label LoopCurrent seems insufficient. But, in your loop invariants, LoopCurrent is the default label. So you can always replace \at(P, LoopCurrent) by P.
Here is a fully annotated version of your code that the WP plugin is able to prove, using Alt-Ergo as prover.
/*#
requires \valid(b1) && \valid(b2);
requires \separated(b1, b2);
ensures A: \forall integer i; 0 <= i < 3 ==>
b1->mem[i] == \old(b1->mem[i]) + b2->mem[i];
assigns b1->mem[0..2];
#*/
void baseMatrixAssignAdd3x1(BaseMatrix3x1 *b1, BaseMatrix3x1 *b2){
/*# loop invariant 0 <= i <= 3;
loop invariant \forall integer k;
k >= i ==> b1->mem[k] == \at(b1->mem[k], Pre);
loop invariant \forall integer k;
0 <= k < i ==> b1->mem[k] == \at(b1->mem[k], LoopEntry) + b2->mem[k];
loop assigns i, b1->mem[0..2];*/
for(unsigned int i=0; i<3; i++){
b1->mem[i] += b2->mem[i];
}
}

Loop invariant mistake

This is from an old midterm. I am looking over it trying to study for my final.
Fun(int n, int A[]){
for(i = 0;i < A.length; i += 2){
A[i] = n;
}
return;
}
It asks for the loop invariant, at the location just after the loop begins, before the assignment of A[i]. It also asks what the invariant and exit conditions imply about what the loop achieves when it exits.
I answered:
Loop invariant is i < A.length
Exit conditions are i >= A.length
This with i+=2 implies that the array A has entries equal to n on every even number n entry less than or equal to A.length
I was not awarded full credit, and think it may be due to the loop invariant. Can anyone clarify?

GCC: vectorization difference between two similar loops

When compiling with gcc -O3, why does the following loop not vectorize (automatically):
#define SIZE (65536)
int a[SIZE], b[SIZE], c[SIZE];
int foo () {
int i, j;
for (i=0; i<SIZE; i++){
for (j=i; j<SIZE; j++) {
a[i] = b[i] > c[j] ? b[i] : c[j];
}
}
return a[0];
}
when the following one does?
#define SIZE (65536)
int a[SIZE], b[SIZE], c[SIZE];
int foov () {
int i, j;
for (i=0; i<SIZE; i++){
for (j=i; j<SIZE; j++) {
a[i] += b[i] > c[j] ? b[i] : c[j];
}
}
return a[0];
}
The only difference is whether the result of the expression in the inner loop is assigned to a[i], or added to a[i].
For reference -ftree-vectorizer-verbose=6 gives the following output for the first (non-vectorizing) loop.
v.c:8: note: not vectorized: inner-loop count not invariant.
v.c:9: note: Unknown alignment for access: c
v.c:9: note: Alignment of access forced using peeling.
v.c:9: note: not vectorized: live stmt not supported: D.2700_5 = c[j_20];
v.c:5: note: vectorized 0 loops in function.
And the same output for the loop that vectorizes is:
v.c:8: note: not vectorized: inner-loop count not invariant.
v.c:9: note: Unknown alignment for access: c
v.c:9: note: Alignment of access forced using peeling.
v.c:9: note: vect_model_load_cost: aligned.
v.c:9: note: vect_model_load_cost: inside_cost = 1, outside_cost = 0 .
v.c:9: note: vect_model_simple_cost: inside_cost = 1, outside_cost = 1 .
v.c:9: note: vect_model_reduction_cost: inside_cost = 1, outside_cost = 6 .
v.c:9: note: cost model: prologue peel iters set to vf/2.
v.c:9: note: cost model: epilogue peel iters set to vf/2 because peeling for alignment is unknown .
v.c:9: note: Cost model analysis:
Vector inside of loop cost: 3
Vector outside of loop cost: 27
Scalar iteration cost: 3
Scalar outside cost: 7
prologue iterations: 2
epilogue iterations: 2
Calculated minimum iters for profitability: 8
v.c:9: note: Profitability threshold = 7
v.c:9: note: Profitability threshold is 7 loop iterations.
v.c:9: note: LOOP VECTORIZED.
v.c:5: note: vectorized 1 loops in function.
In the first case: the code overwrites the same memory location a[i] in each iteration. This inherently sequentializes the loop as the loop iterations are not independent.
(In reality, only the final iteration is actually needed. So the entire inner loop could be taken out.)
In the second case: GCC recognizes the loop as a reduction operation - for which it has special case handling to vectorize.
Compiler vectorization is often implemented as some sort of "pattern matching". Meaning the compiler analyzes code to see if it fits a certain pattern that it's able to vectorize. If it does, it gets vectorized. If it doesn't, then it doesn't.
This seems to be a corner case where the first loop doesn't fit any of the pre-coded patterns that GCC can handle. But the second case fits the "vectorizable reduction" pattern.
Here's the relevant part of GCC's source code that spits out that "not vectorized: live stmt not supported: " message:
http://svn.open64.net/svnroot/open64/trunk/osprey-gcc-4.2.0/gcc/tree-vect-analyze.c
if (STMT_VINFO_LIVE_P (stmt_info))
{
ok = vectorizable_reduction (stmt, NULL, NULL);
if (ok)
need_to_vectorize = true;
else
ok = vectorizable_live_operation (stmt, NULL, NULL);
if (!ok)
{
if (vect_print_dump_info (REPORT_UNVECTORIZED_LOOPS))
{
fprintf (vect_dump,
"not vectorized: live stmt not supported: ");
print_generic_expr (vect_dump, stmt, TDF_SLIM);
}
return false;
}
}
From just the line:
vectorizable_reduction (stmt, NULL, NULL);
It's clear that GCC is checking to see if it matches a "vectorizable reduction" pattern.
GCC vectorizer is probably not smart enough to vectorize the first loop. The addition case is easier to vectorize because a + 0 == a. Consider SIZE==4:
0 1 2 3 i
0 X
1 X X
2 X X X
3 X X X X
j
X denotes the combinations of i and j when a will be assigned to or increased. For the case of addition, we can compute the results of b[i] > c[j] ? b[i] : c[j] for, say, j==1 and i==0..4 and put it into vector D. Then we only need to zero D[2..3] and add resulting vector to a[0..3]. For the case of assignment, it is a little more trickier. We must not only zero D[2..3], but also zero A[0..1] and only then combine the results. I guess this is where the vectorizer is failing.
The first loop is equivalent to
#define SIZE (65536)
int a[SIZE], b[SIZE], c[SIZE];
int foo () {
int i, j;
for (i=0; i<SIZE; i++){
a[i] = b[i] > c[SIZE - 1] ? b[i] : c[SIZE - 1];
}
return a[0];
}
The problem with the original expression is that it really doesn't make that much sense, so it's not too surprising gcc isn't able to vectorize it.
First one just trivially changing a[] many times(temporary).
Second one uses the last value of a[] each time(not temporary).
Until a version of patch, you could use "volatile" variable to vectorize.
Use
int * c=malloc(sizeof(int));
to make it aligned;
v.c:9: note: Unknown alignment for access: c
Shows "c" having different storage area than b and a.
I assume "movaps"-like instructions for being "vectorized" (from SSE-AVX instruction list)
Here: http://gcc.gnu.org/projects/tree-ssa/vectorization.html#using
the 6th and 7th examples show similar difficulties.

Resources