typedef struct {
void * field1;
} s1;
void func1(void) {
s1 my_s1;
s1 * __restrict my_s1_ptr = &my_s1;
*((int*)((char*)my_s1_ptr->field1 + 4)) = 0;
*((int*)((char*)my_s1_ptr->field1 + 8)) = 1;
*((int*)((char*)my_s1_ptr->field1 + 12)) = 2;
*((int*)((char*)my_s1_ptr->field1 + 16)) = 3;
}
It seems that for version 11.1 of the Intel compiler and version 4.6 of gcc that the compiler reloads my_s1_ptr->field1 for each of the last 4 statements. My understanding of __restrict would suggest to me that the last 3 loads should be redundant and could be eliminated. Yes, I know the code is weird but there is a reason it is structured this way. I would just like to be able to get the compiler to eliminate the redundant loads. Any idea how to convince it to do that?
restrict says that two pointers cannot point to overlapping memory regions. The most common usage is for function arguments. This restricts how the function can be called, but allows for more compile optimizations. If the caller does not follow the restrict contract, undefined behavior can occur.
For example, char const * restrict is a type. Actually, it only applies to pointer types, i.e. T * restrict . So int restrict is invalid, but int * restrict is valid. In a function, a parameter T * restrict p means that the allocated object pointed at by p is only pointed at by p .
In the C programming language (after 99 standard), a new keyword is introduced known as restrict. restrict keyword is mainly used in pointer declarations as a type qualifier for pointers. It doesn't add any new functionality.
If a function has two pointers pa and pb , with the same value, we say the pointers alias each other. This introduces constraints on the order of instruction execution. If two write accesses that alias occur in program order, they must happen in the same order on the processor and cannot be re-ordered.
s1 * __restrict
means that this is the only pointer to a particular s1
, so no alias for that type. It doesn't mean that there will be no alias for other pointer types, like void*
, int*
, or char*
.
Using a char*
is especially troublesome for the compiler, because a char*
is specifically allowed to be used to access the bytes of other types. (char
also means byte, and can be used to access the underlying memory of other types).
If the compiler cannot prove that your assignment will never, ever change what's pointed to, it will have to reload the pointer each time. For example, how can it tell that void* field1
isn't pointing to itself?
And wouldn't something like this work without all the casts?
int* p = my_s1.field1;
p[1] = 0;
p[2] = 1;
p[3] = 2;
p[4] = 3;
Assuming an int
is 4 bytes, and that field1
actually points to an array of those.
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