In this testcase:

  struct s
  {
    unsigned long a:2;
    unsigned long b:40;
    unsigned long c:22;
  };
  
  f (struct s s)
  {
    g (((unsigned long) (s.b-8)) + 8);
  }

fold breaks the code.  Specifically, after a few transformations we're asked
to fold this.  (I changed the big constant to be in symbolic form for better
understanding):

    <unnamed-unsigned:40> D.40719;
   (uint64_t) (D.40719 + ((1<<40) - 8)) + 8

The cast is removed by STRIP_NOPS so with explicit precision and signedness on
the operations we have (the constants are of types of the corresponding
operation):

   (D.40719 +:u40 ((1<<40) - 8)) +:u64 8

split_trees/associate_trees code combines the two constant resulting in:

  (uint64_t) D.40719 +:64 1<<40

Ouch.

The problem is obviously that we remove a zero-extension that would ensure the
modulo-2^40 nature of the addition (remove the overflow).

I see two ways to fix this:

1. Change STRIP_NOPS not to remove the cast if it's changing the precision
(not just when it changes machine modes).  And then audit code quality where
we need to and are allowed to look through these casts.

The above case does not qualify because the first operation may not overflow
into the >40 bits whereas the second may.  For example with 2 as s.b the
substraction should yield 0xff,ffff,fffa and the addition 0x100,0000,0002.
(Note that there in *no* default integer promotion on bitfield types wider
than int [1].)

2. Just fix this particular case of folding (split_trees/associate_trees) and
hope everything else works ;).

I am for 1, FWIW, but I know this issue has some history [2], so I'd like to
hear others' opinion before I started working on a patch.

Adam

[1] http://gcc.gnu.org/ml/gcc/2008-01/msg00215.html
[2] http://gcc.gnu.org/ml/gcc-patches/2004-07/msg01478.html

Reply via email to