is portable aliasing possible in C++?

Andy Webber andy@aligature.com
Thu Sep 4 16:11:00 GMT 2014


Our goal is to avoid bugs caused by strict aliasing in our networking
libraries.  My question is how to guarantee that we're not violating
the aliasing rules while also getting the most optimization.  I've
read through a ton of information about this online and in some gcc
discussions, but I don't see a consensus.

Memcpy always works, but is dependent on optimization to avoid copies.
The union of values is guaranteed to work by C++11, but may involve
copies.  In the past we've always used reinterpret_cast, but I don't
believe that to be guaranteed by the standard. The __may_alias__
attribute is specific to gcc.  Placement new changes the dynamic type
of the memory which I believe guarantees correct access through a
layout*.

Two interesting gcc discussions:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286#c1 (placement new
does not change the dynamic type as it should)
https://gcc.gnu.org/ml/gcc-patches/2014-07/msg01440.html (Strenghten
assumption about dynamic type changes (placement new))
https://gcc.gnu.org/gcc-4.4/porting_to.html (gcc 4.4 release notes)

Each test works when built with -O3 on gcc-4.8.3, but I would like to
standardize across compilers and versions.  The optimization
information generated by -fdump-tree-all is interesting here as it
shows slightly different optimization for each case though
reinterpret_cast and placement new generate identical code in the end.

Here are tests illustrating the different options:

#include <cstring>
#include <memory>

struct layout
{
   int i;
   short s;
   char c;
};

template<typename T, typename U>
void verify(T t, U u)
{
   if(t != u)
      abort();
}

void mem_copy()
{
   char storage[sizeof(layout)];
   storage[0] = 1;
   storage[1] = 0;
   storage[2] = 0;
   storage[3] = 0;
   storage[4] = 2;
   storage[5] = 0;
   storage[6] = 3;
   storage[7] = 0;

   layout l;

   std::memcpy(&l, &storage, sizeof(layout));

   verify(l.i, 1);
   verify(l.s, 2);
   verify(l.c, 3);
}

void union_copy()
{
   union both
   {
      char storage[sizeof(layout)];
      layout l;
   };

   both b;

   b.storage[0] = 1;
   b.storage[1] = 0;
   b.storage[2] = 0;
   b.storage[3] = 0;
   b.storage[4] = 2;
   b.storage[5] = 0;
   b.storage[6] = 3;
   b.storage[7] = 0;

   verify(b.l.i, 1);
   verify(b.l.s, 2);
   verify(b.l.c, 3);
}

int placement_new()
{
   char storage[sizeof(layout)];
   storage[0] = 1;
   storage[1] = 0;
   storage[2] = 0;
   storage[3] = 0;
   storage[4] = 2;
   storage[5] = 0;
   storage[6] = 3;
   storage[7] = 0;

   auto l = new(storage) layout;

   verify(l->i, 1);
   verify(l->s, 2);
   verify(l->c, 3);
}

struct layout_alias: layout {} __attribute__((__may_alias__));
int may_alias()
{
   char storage[sizeof(layout)];
   storage[0] = 1;
   storage[1] = 0;
   storage[2] = 0;
   storage[3] = 0;
   storage[4] = 2;
   storage[5] = 0;
   storage[6] = 3;
   storage[7] = 0;

   auto l = reinterpret_cast<layout_alias*>(storage);

   verify(l->i, 1);
   verify(l->s, 2);
   verify(l->c, 3);
}

int reint_cast()
{
   char storage[sizeof(layout)];
   storage[0] = 1;
   storage[1] = 0;
   storage[2] = 0;
   storage[3] = 0;
   storage[4] = 2;
   storage[5] = 0;
   storage[6] = 3;
   storage[7] = 0;

   auto l = reinterpret_cast<layout*>(storage);
   verify(l->i, 1);
   verify(l->s, 2);
   verify(l->c, 3);
}


int main()
{
   mem_copy();
   union_copy();
   placement_new();
   may_alias();
   reint_cast();
}



More information about the Gcc-help mailing list