Does test_func
the following snippet trigger undefined behavior under the strict aliasing rules when the two arguments partially overlap?
That is the second argument is a member of the first:
#include <stdio.h>
typedef struct
{
//... Other fields
int x;
//... Other fields
} A;
int test_func(A *a, int *x)
{
a->x = 0;
*x = 1;
return a->x;
}
int main()
{
A a = {0};
printf("%d\n", test_func(&a, &a.x));
return 0;
}
Is the compiler allowed to think test_func
will just return 0, based on the assumption that A*
and int*
will not alias? so the *x
cannot overwrite the member?
Strict aliasing refers to when a pointer is converted to another pointer type, after which the contents are accessed. Strict aliasing means that the involved pointed-at types must be compatible. That does not apply here.
There is however the term pointer aliasing, meaning that two pointers can refer to the same memory. The compiler is not allowed to assume that this is the case here. If it wants to do optimizations like those you describe, it would perhaps have to add machine code that compares the pointers with each other, to determine if they are the same or not. Which in itself would make the function slightly slower.
To help the compiler optimize such code, you can declare the pointers as restrict
, which tells the compiler that the programmer guarantees that the pointers are not pointing at the same memory.
Your function compiled with gcc -O3
results in this machine code:
0x00402D09 mov $0x1,%edx
Which basically means that the whole function was replaced (inlined) with "set a.x to 1".
But if I rewrite your function as
int test_func(A* restrict a, int* restrict x)
{
a->x = 0;
*x = 1;
return a->x;
}
and compile with gcc -O3
, it does return 0. Because I have now told the compiler that a->X
and x
do not point at the same memory, so it can assume that *x = 1;
does not affect the result and skip the line *x = 1;
or sequence it before the line a->x = 0;
.
The optimized machine code of the restrict version actually skips the whole function call, since it knows that the value is already 0 as per your initialization.
This is of course a bug, but the programmer is to blame for it, for careless use of restrict
.
This is not a violation of strict aliasing. The strict aliasing rule says (simplified) that you can access the value of an object only using an lvalue expression of a compatible type. In this case, the object you're accessing is the member x
of main
's a
variable. This member has type int
. And the expression you use to access it (*x
) also has type int
. So there's no problem.
You may be confusing strict aliasing with restrict
. If you had used the restrict
keyword in the declaration of one of the pointer parameters, the code would be invalid because restrict
prevents you from using different pointers to access the same object - but this is a different issue than strict aliasing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With