I recently asked this question:
Using this pointer causes strange deoptimization in hot loop
The problem was that I was writing to an array of type uint8_t
and the compiler treated it as if it could alias with the this
pointer of the method (of type struct T*
), because void*
and char*
(=uint8_t*
) can always alias any other pointer in C++. This behaviour caused a missed optimization opportunity. I want to avoid this, of course. So the question is: Can I declare an uint8_t
array that enforces strict aliasing, i.e., that the compiler treats as never aliased with any pointer of another type? I.e., I am looking for something like a strict_uint8_t
type that is an uint8_t
with special aliasing behaviour. Is there a way to achieve this?
Example code to show what I mean, borrowed from other question and simplified. For more details, read the linked question and its accepted answer:
struct T{
uint8_t* target;
void unpack3bit(char* source, int size) {
while(size > 0){
uint64_t t = *reinterpret_cast<uint64_t*>(source);
/** `this->target` cannot be cached in a register here but has
to be reloaded 16 times because the compiler
thinks that `this->target` could alias with `this` itself.
What I want is a special uint8_t type that does not trigger
this behaviour. */
this->target[0] = t & 0x7;
this->target[1] = (t >> 3) & 0x7;
this->target[2] = (t >> 6) & 0x7;
this->target[3] = (t >> 9) & 0x7;
this->target[4] = (t >> 12) & 0x7;
this->target[5] = (t >> 15) & 0x7;
this->target[6] = (t >> 18) & 0x7;
this->target[7] = (t >> 21) & 0x7;
this->target[8] = (t >> 24) & 0x7;
this->target[9] = (t >> 27) & 0x7;
this->target[10] = (t >> 30) & 0x7;
this->target[11] = (t >> 33) & 0x7;
this->target[12] = (t >> 36) & 0x7;
this->target[13] = (t >> 39) & 0x7;
this->target[14] = (t >> 42) & 0x7;
this->target[15] = (t >> 45) & 0x7;
source+=6;
size-=6;
target+=16;
}
}
};
I believe you'll get rid of the aliasing if you pass both pointers through a function where the pointers are declared with restrict. That's non-standard compiler extension though, e.g. in the case of g++:
Online: http://goo.gl/SCjpL6
You can use a fixed-size enumeration with base type
uint8_t
:If you want to be able to convert to and from
uint8_t
transparently, you can wrap it in astruct
with converting constructor and conversion operator:This appears to eliminate the aliasing pessimization in gcc and clang: https://godbolt.org/g/9Ta98b
(Note: the previous approach, using a bitfield, worked in gcc but not in clang.)
In visual studio you can use
__declspec(restict)
for functions and__restrict
for variables to tell the compiler that the pointer is alias free. I believe that in other compilers like GCC there is a__restrict__
attribute (but I'm not sure). For more info see here