c++ - Strict aliasing seems inconsistant -
had couple of bugs strict aliasing , thought try fix of them. having looked in detail @ seems gcc doesn't issue warning, , things impossible implement. @ least understanding every below broken. understanding wrong, there correct way these things, or code have technically break rule , covered system tests?
the bugs code char , unsigned char buffers mixed, e.g. below:
size_t process(char *buf, char *end) { char *p = buf; processsome((unsigned char**)&p, (unsigned char*)end); //gcc decided p not changed processsome , returned 0 return (size_t)(p - buf); } changing below seemed fix problem, although still involves cast not sure why works , warning free:
size_t process(char *buf, char *end) { unsigned char *buf2 = (unsigned char *)buf; unsigned char *p = buf2; unsigned char *end2 = (unsigned char*)end; processsome(&p, end2); return (size_t)(p - buf2); } also there bunch of other places seem work without warnings
//contains unsigned char* of data. possibly network, disk, etc. //the buffer contents 8 byte aligned. const buffer *buffer = foo(); const uint16_t *utf16text = (const uint16_t*)buffer->getdata();//const unsigned char* //... read utf16text. not seem ever warning //also seems work fine size_t len = calculateworstcaselength(...); buffer *buffer = new buffer(len * 2); uint16_t *utf16 = (uint16_t*)buffer->getdata();//unsigned char* len = dosomeprocessing(utf16, len, ...); buffer->truncate(len * 2); send(buffer); and with...
struct hash128 { unsigned char data[16]; }; ... size_t operator ()(const hash128 &hash) { return *(size_t*)hash.data;//warning } a non char case. doesn't have warning, , if bad, how avoid (both ways seem work)?
int *x = fromsomewhere();//aligned 16 bytes, array of 4 __m128i xmm = _mm_load_si128((__m128*i)x); __m128i xmm2 = *(__m128i*)x; looking @ other api's there seems various cases understanding violate rule (have not come across linux/gcc specfic one, sure there 1 somewhere).
cocreateinstance has void** output param requiring explicit pointer cast. direct3d has well.
large_integer union have read/writes different members (e.g. code might use high/low, other might read int64).
i recall cpython implementation quite happily casts pyobject* bunch of other stuff happens have same memory layout @ start.
a lot of hash implementations have seen cast input buffer uint32_t*, perhaps use uint8_t handle 1-3 bytes @ end.
pretty every memory allocator implementation have seen uses char* or unsigned char*, must cast desired type (possibly via returned void*, internally allocate @ least char)
first, pointers char , unsigned char pretty exempted rules regarding string aliasing; allowed convert type of pointer char* or unsigned char*, , @ pointed object array of char or unsigned char. now, regards code:
size_t process(char *buf, char *end) { char *p = buf; processsome((unsigned char**)&p, (unsigned char*)end); //gcc decided p not changed processsome , returned 0 return (size_t)(p - buf); } the issue here you're trying @ char* if unsigned char*. that's not guaranteed. given cast visible, g++ being bit obtuse not turning strict aliasing analysis off automatically, technically, covered standard.
in
size_t process(char *buf, char *end) { unsigned char *buf2 = (unsigned char *)buf; unsigned char *p = buf2; unsigned char *end2 = (unsigned char*)end; processsome(&p, end2); return (size_t)(p - buf2); } on other hand, of conversions involve char* , unsigned char*, both of may alias anything, compiler required make work.
with regards rest, don't return type of buffer->getdata() is, it's hard say. if char*, unsigned char* or void*, code legal (except missing cast in second use of buffer->getdata()). long of casts involve char*, unsigned char* or void* (ignoring const qualifiers), compiler required assume there possible aliasing: when original pointer has 1 of these types, have been created means of cast pointer target type, , language guarantees can convert pointer 1 of these types, , original type, , recover same value. (of course, if char* wasn't uint16_t, may end alignment problems, compiler can't know this.)
with regards last example, don't indicate type of hash.data, it's hard say; if char*, void* or unsigned char*, language guarantees code (technically, provided char pointer created converting size_t*; in practice, provided pointer sufficiently aligned , pointed bytes not form trapping value size_t).
in general: guaranteed way of "type punning" memcpy. otherwise, pointer casts, such doing, guaranteed long or void*, char* or unsigned char*, @ least far aliasing concerned. (from 1 of these result in alignment problems, or accessing trapping value if dereference it.)
note may additional guarantees other standards. posix requires like:
void (*pf)(); *((void**)&pf) = ... to work, example. (generally, casting , dereferencing work, g++, if don't else in function aliasing might relevant.)
and of compilers know allow using union type punning, of time. (and @ least some, including g++, fail legal uses of union in other cases. correctly handling union tricky compiler writer if union isn't visible.)
Comments
Post a Comment