5

The answer in the another question: Strict aliasing rule and 'char *' pointers says that using a char* to examine the binary contents of a T object is ok. But using a T* to overlay on a char buffer is not ok.

Now I have a function that takes a char buffer with binary data. And does things like this while reading it:

// unsigned char *pData used to walk through the buffer.
uint32_t value = *(unit32_t*)pData;
pData += 4;

If I break the strict aliasing by doing this, what other, more effective ways available? Will compilers optimize memcpy calls when they are called with small amount of bytes?

Community
  • 1
  • 1
Calmarius
  • 18,570
  • 18
  • 110
  • 157

2 Answers2

3

If I break the strict aliasing by doing this ...

Yes, you do

what other, more effective ways available?

If the buffer must be char, you need to use memcpy into uint32_t before accessing the value. Of course if all your values were uint32_ts, you could make a buffer of uint32_ts, and pass it to the function that fills it with chars, because strict aliasing is a one-way prohibition, i.e. using a uint32_t* as a char* is allowed.

Will compilers optimize memcpy calls when they are called with small amount of bytes?

Many CPUs have built-in instructions for memcpy. Modern compilers use these instructions for good efficiency.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
2

Will compilers optimize memcpy calls when they are called with small amount of bytes?

For this sample code:

#define _CRT_SECURE_NO_WARNINGS // To allow usage of scanf in vc++2015
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    // printf and scanf to prevent code elimination
    char bytes[ 4 ];
    scanf( "%s", bytes ); 
    char buffer[ 4 ];
    memcpy( buffer, bytes, 4 );
    printf( "%s", buffer );

    return 0;
}

Visual C++ 2015 generated this assembly output (release build, x64):

; memcpy was replaced by a simple register move
mov    eax, DWORD PTR bytes$[rsp] 
lea    rdx, QWORD PTR buffer$[rsp]                   ; setting arguments 
lea    rcx, OFFSET FLAT:??_C@_02DKCKIIND@?$CFs?$AA@ ; for printf call
; at this point copied array was actually stored in memory
mov    DWORD PTR buffer$[rsp], eax     
call   printf

So yes, modern compilers won't even call the procedure.

  • To what extent can one be confident that `memcpy(&temp,p,sizeof *p); temp|= 0x80808080; memcpy(p, &temp, sizeof *p);` will end up being as efficient as disabling strict aliasing and using `*((uint32_t*)p)|=0x80808080;` – supercat Oct 13 '15 at 05:28
  • @supercat, I tried this with msvc, it ended up being a single `or dword ptr [esp+8], 80808080h` instruction, no memcpy calls, no single move. Modern optimizers do a great job. Of course, as with any optimization there is no strong guarantee that a particular compiler will do particular optimizations on a particular piece of code but the memcpy function is so fundamental and used so many times either directly or indirectly that you may expect it to be highly optimized for all frequent use cases... –  Oct 13 '15 at 11:02
  • @supercat, When it comes to performance I wouldn't disable strict aliasing since the reason it exists is to allow more optimizations and by doing this you may actually make your code slower. –  Oct 13 '15 at 11:03
  • What optimizations are achievable with strict aliasing that are not achievable via "restrict"? It seems to me type-based aliasing sometimes lucks into making a few optimizations, but only if the type of pointer and the type of some other object happen to be different; creating a "restrict" pointer to the object which won't alias and then accessing the other object via that pointer would give the compiler the same useful semantic guarantees as type-based aliasing, but only when that was actually desired. – supercat Oct 13 '15 at 13:24
  • @supercat, I didn't think about `restrict`. It's likely that manually restricting pointers while prohibiting strict aliasing rule can give us the same optimizations. I can't come up with an example where these two approaches would behave differently. This could be a good question here. –  Oct 13 '15 at 15:51
  • I'm not sure how I could ask such a question without coming across as argumentative. Somehow people have bought into the myth that the Strict Aliasing Rule has been an accepted part of C for decades, when the reality is that the only reason there wasn't a clamor to get rid of it in C99 is that compiler writers had been ignoring it for the previous decade and were expected to do so forever. There are a few cases where optimizing C99 code using restrict would cause that code to run slightly slower on an old/simple compiler [with "restrict" defined as an empty macro], but nowhere near as bad... – supercat Oct 13 '15 at 16:01
  • ...as trying to run strictly-conformant C89 code on such a compiler. Something like `int temp; memcpy(&temp, p, sizeof temp); temp++; memcpy(p, &temp, sizeof temp);` may yield the same code on a modern compiler as `*((int*)p)++;` would have yielded on an old one, but on many old or simple compilers it could well be ten times slower. Given that "restrict" can do everything the Strict Aliasing Rule can do and a whole lot more besides, I consider the SAR to be at best obsolete. – supercat Oct 13 '15 at 16:05