Re: [Linux-ia64] gcc bug

From: Jim Wilson <wilson_at_cygnus.com>
Date: 2001-01-27 15:20:31
There was a comment about non-int bitfields.  This isn't allowed by the ISO C
standard, but it is a very common extension.  Most C compilers support it.
Gcc will give a warning about this construct if you use -pedantic.  This
isn't related to the problem though.

The problem with the code is that it uses invalid casts.  ISO C says that
if you access an object using a type other than what the object was declared
with, then the result is undefined.  There are a few exceptions, signedness
doesn't matter, qualifiers don't matter, aggregate types containing the type
are OK, and char is OK.  (In the 1989 ANSI C standard, this is in section 3.3
Expressions.)  This rule exists so that C compilers can do type-based alias
analysis.  Gcc tries to do aliasing based on addresses first, but if it gets
confused by address arithmetic, then it will try type-based alias analysis.
So in this case, it decides that the read of the unsigned int w and the
store to struct f w can't alias because the types are different, and then it
emits an instruction schedule that causes the code to perform differently than
what you expected.  This code would probably work on other targets.  The
lack of addressing modes in IA-64 makes it harder for the alias analysis pass
to figure out whether two addresses are the same, and hence we fall back on
type-based aliasing more often than we would on say an IA-32 machine.

Gcc does support type punning via unions, though the ISO C standard technically
does not allow this.  If you write the code like this it should work
union g {
  struct f f;
  unsigned long l;
};

  ((union g*)&w)->f.lo = ((union g*)&value1)->f.lo;
  ((union g*)&w)->f.hi = ((union g*)&value2)->f.hi;

However, this is a bit cryptic.  It would be clearer, and you will get much
more efficient code, if you write it something like this.  Using & usually
forces values onto the stack.  If we do it this way, we do everything in
registers until the write to r.

union g {
  struct f f;
  unsigned long l;
};

doit (unsigned long value1, unsigned long value2)
{
  union g w, t, u;

  t.l = value1;
  w.f.lo = t.f.lo;
  u.l = value2;
  w.f.hi = u.f.hi;
  r = w.l;
}

int
main (int argc, char **argv)
{
  doit(0xbeef, 0xfeed00000000);
  printf ("%lx\n", r);
}

Jim
Received on Fri Jan 26 20:17:05 2001

This archive was generated by hypermail 2.1.8 : 2005-08-02 09:20:01 EST