c99 __restrict and compiler optimization
Asked Answered
A

1

6
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?

Arvie answered 8/6, 2012 at 1:21 Comment(5)
Did you compile with optimization? And why are you using __restrict rather than restrict, which is a C99 keyword?Onagraceous
I did compile with optimizations and __restrict versus restrict makes no difference.Arvie
I just tried several variations on a pointer to the field instead of a pointer to the struct and it didn't change anything. In the real code, there may be multiple pointers pointing to what field1 points to so it isn't valid to add restrict to field1 (but even when I tried it didn't eliminate the load). If I copy field1 out to a restrict local variable pointer then the redundant loads are eliminated but like I said this violates the semantics of the program.Arvie
AFAIK, restrict does not always optimize. It "may allow the compiler" to perform optimizations. The compiler is not required to perform those optimizations. Anyway, I find it still strange that it does not in your case.Ezzell
is your example correct? field1 doesn't point to anything when you dereference it... Secondly, since my_s1_ptr->field1 isn't restricted, isn't it normal it reloads that pointer, or do you mean it reloads my_s1_ptr?Haemophilia
U
3

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.

Ultranationalism answered 18/8, 2012 at 9:0 Comment(3)
"char also means byte" -> Does the compiler have to watch out for any char, or just unsigned char? (Not sure, that's why I'm asking. :P)Cordovan
We mostly use unsigned char, because then we know the signedness, but plain char might be the same and can also be used to access memory (whether actually signed or not).Ultranationalism
s1 * __restrict means that this is the only pointer to a particular s1”: I don't know about __restrict, but this is not true of C99's restrict, for instance in void *memcpy(void * restrict s1,const void * restrict s2,size_t n);. Section 6.7.3.1 defines a notion of pointer expression based on an object and there is no provision for pointer conversions (allowing memcpy to be prototyped as it is).Brabant

© 2022 - 2024 — McMap. All rights reserved.