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;
}
}
};
You can use a fixed-size enumeration with base type uint8_t
:
enum strict_uint8_t : uint8_t {};
If you want to be able to convert to and from uint8_t
transparently, you can wrap it in a struct
with converting constructor and conversion operator:
struct strict_uint8_t {
enum : uint8_t {} i;
strict_uint8_t(uint8_t i) : i{i} {}
operator uint8_t() const { return i; }
};
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With