noalias macro bug [was: Linux and aliasing?]

mark@codesourcery.com mark@codesourcery.com
Tue Jun 8 10:38:00 GMT 1999


>>>>> "Joerg" == Joerg Pommnitz <pommnitz@darmstadt.gmd.de> writes:

You included the following program:

  #include <stdio.h>

  #define noalias(type, ptr) \
    (((union { type __x__; __typeof__(*(ptr)) __y__;} *)(ptr))->__x__)

  int
  main ()
  {
      int a = 0x12345678;
      unsigned short *b = &noalias(unsigned short, &a);

      printf ("%x\n", a);
      b[1] = 0;
      printf ("%x\n", a);

      return 0;
  }

and noted that:

  pommnitz ~>gcc -O2 -o alias alias.c 
  pommnitz ~>./alias 
  12345678
  12345678

You correctly concluded that this means the "noalias" macro didn't
work and asked:

  Is this an egcs bug, is the macro at fault or am I doing something
  stupid? 

None of the above.  It's not a GCC bug.  From the manual section on
-fstrict-aliasing:

     Pay special attention to code like this:
          union a_union {
            int i;
            double d;
          };
          
          int f() {
            a_union t;
            t.d = 3.0;
            return t.i;
          }
     The practice of reading from a different union member than the one
     most recently written to (called "type-punning") is common.  Even
     with `-fstrict-aliasing', type-punning is allowed, provided the
     memory is accessed through the union type.  So, the code above
     will work as expected.  However, this code might not:
          int f() {
            a_union t;
            int* ip;
            t.d = 3.0;
            ip = &t.i;
            return *ip;
          }

In other words, as in Linus' proposal, the "union trick" works only
when accessing directly through the union.  You squirelled the pointer
away, and then used it later, thereby losing the effects of the
noalias macro.  The following variant of your program works as you
expect:

  extern void printf(const char*, ...);

  #define noalias(type, ptr) \
    (((union { type __x__; __typeof__(*(ptr)) __y__;} *)(ptr))->__x__)

  typedef unsigned short usa[2];

  int
  main ()
  {
      int a = 0x12345678;
      printf ("%x\n", a);
      noalias(usa, &a)[1] = 0;
      printf ("%x\n", a);

      return 0;
  }

Here's what I get:

  linux1.codesourcery.com% ./a.out
  12345678
  5678

which is I think what you expected.  

Kernel people: is the use of the self-documenting noalias macro, as
demonstrated here, really so hard?

--
Mark Mitchell                   mark@codesourcery.com
CodeSourcery, LLC               http://www.codesourcery.com


More information about the Gcc mailing list