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?

Mat
  • 202,337
  • 40
  • 393
  • 406
DrTodd13
  • 81
  • 3
  • Did you compile with optimization? And why are you using `__restrict` rather than `restrict`, which is a C99 keyword? – Keith Thompson Jun 08 '12 at 01:38
  • I did compile with optimizations and __restrict versus restrict makes no difference. – DrTodd13 Jun 08 '12 at 01:50
  • 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. – DrTodd13 Jun 08 '12 at 02:04
  • 1
    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. – Morwenn Jun 14 '12 at 12:39
  • 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? – steabert Aug 15 '12 at 06:48

1 Answers1

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.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • "`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) – user541686 Aug 18 '12 at 09:04
  • 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). – Bo Persson Aug 18 '12 at 09:14
  • “`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). – Pascal Cuoq Aug 18 '12 at 11:59