davidbrown wrote on Friday, November 03, 2017:
Using a union of pointers is not safe. When you have this:
union xPointer {
uint8_t *u8;
uint16_t *u16;
uint32_t *u32;
uint32_t uint32;
};
The compiler is allowed to assume that u16 and u32 fields never point to the same data, that any changes made through one of the pointers cannot affect changes visible through the other, and that this also applies to mixes when you have more than one xPointer union.
Code that uses a union of pointers like this works by luck, not by design - and changes to optimisation levels and details about the alignments of the pointers can change that luck.
Pointers to char types - like uint8_t - may alias data accessed through other pointers. But even then, there are complications.
I have not studied the memcpy code closely enough to see if it is correct or to see what is actually going wrong (I also don’t have the same type of ARM for testing it).
My general advice is do not use a union of pointers - and be very careful of any sort of mixes of pointer types. Let the compiler do it - it will, baring compiler bugs, get the aliasing issues right.
Note also that newer gcc (like gcc 6) does more aggressive alias analysis than older versions - code that worked in practice may now fail. This is not a compiler bug - it is a matter of incorrect code that happened to work before.
If the code in question can use gcc extensions, then you would be wiser to use types defined like this:
typedef uint8_t __attribute__((may_alias)) uint8_a;
typedef uint16_t __attribute__((may_alias)) uint16_a;
typedef uint32_t __attribute__((may_alias)) uint32_a;
union xPointer {
uint8_a *u8;
uint16_a *u16;
uint32_a *u32;
uint32_t uint32;
};
In this case, you are specifically telling gcc that the pointers may alias.
It is also possible to use the “-fno-strict-aliasing” compiler switch, but normally you want type-based alias analysis optimisations for the rest of the code. It can be worth testing, however.
Since I don’t have the opportunity to test the code (no Cortex-A chips), and haven’t spotted anything obvious in the generated assembly, I cannot be sure - but aliasing issues are the most likely cause for this kind of problem. Compiler bugs cannot, of course, be ruled out. All we know for sure is that something somewhere is wrong, and should be fixed - code should not break when compiled at -O3.
It makes sense to try to identify exactly what is wrong here, so that either the memcpy.c code can be fixed, or the compiler bug reported and fixed. That applies even if the right answer to your question is simply to use gcc’s builtin memcpy.
I would greatly appreciate hearing results of testing the demo project memcpy with -O3 and -Os, first using the “-fno-strict-aliasing” flag, and then using the “may_alias” types given above.