On February 28, 2017 5:00:41 PM GMT+01:00, Jakub Jelinek <ja...@redhat.com> 
wrote:
>Hi!
>
>The following testcases are miscompiled on little endian targets.
>The bug occurs if we are merging bitfield stores, there is a signed
>bitfield
>with bitlen multiple of BITS_PER_UNIT but not equal to the bitsize of
>the
>corresponding mode, the bitfield doesn't start on multiple of
>BITS_PER_UNIT,
>we store negative constant to it and store to field overlapping 7 bits
>above
>this bitfield is done before store to this bitfield.
>
>In particular in the testcases, we have bitfield with bitpos 19, bitlen
>24
>and store value -5 to it.
>That means we use byte_size of 5 (SImode bytesize + 1 extra byte for
>shifting), and we end up with 0xfb, 0xff, 0xff, 0xff, 0x00 bytes in
>tmpbuf.
>Next, we find out that there is padding of 1 byte, so decrement
>byte_size to
>4 (again, real byte size of the non-shifted bitfield is 3, but 1 extra
>byte
>for shifting), but as bitlen is a multiple of BITS_PER_UNIT, we don't
>clear
>anything after those 0xfb, 0xff, 0xff bytes.  And then
>shift_bytes_in_array
>just shifts all bytes left and we end up with 0xd8, 0xff, 0xff, 0xff
>when we want 0xd8, 0xff, 0xff, 0x07.
>Finally these bytes are ored into the real buffer where we've
>previously
>cleared the bitrange (which is why having 0xff in the last byte is
>harmful).
>
>Now, if bitlen is not a multiple of BITS_PER_UNIT, we already clear
>that
>extra byte plus some bits:
>-         else
>-           clear_bit_region (tmpbuf, bitlen,
>-                             byte_size * BITS_PER_UNIT - bitlen);
>Here byte_size is still the actual byte size + 1 and thus say if
>instead of
>bitlen 24 we had bitlen 23, we would have cleared 0xfb, 0xff, 0xff,
>0xff,
>0x00 tmpbuf to 0xfb, 0xff, 0x7f, 0x00, 0x00.
>
>For big endian, my understanding is that we also rely on
>tmpbuf[byte_size - 1]
>being 0, but we should not suffer from this problem, we clear it first
>everywhere, then
>  if (native_encode_expr (expr, tmpbuf, byte_size, 0) == 0)
>should really just write byte_size - 1 bytes due to the mode (though,
>I'd
>say it would be much clearer if we called
>  if (native_encode_expr (expr, tmpbuf, byte_size - 1, 0) == 0)
>because that is what we want (ok to make this change?).  Then the
>padding
>adjustment for big-endian is actually tmpbuf += padding, so we move the
>start of the buffer, and for obvious reason don't want to access any
>bytes
>before tmpbuf during shifting.
>
>The following patch fixes that, bootstrapped/regtested on x86_64-linux
>and i686-linux, ok for trunk (without or with the above
>native_encode_expr
>call change)?

With the native encode change is fine with me.

Richard.

>Other options would be to do there tmpbuf[byte_size - 1] = '\0';
>unconditionally (we rely on it everywhere, just in other cases it
>should
>be already cleared), or do such clearing inside of the two
>shift_bytes_in_array* functions.
>
>2017-02-28  Jakub Jelinek  <ja...@redhat.com>
>
>       PR tree-optimization/79737
>       * gimple-ssa-store-merging.c (encode_tree_to_bitpos): If bitlen is
>       a multiple of BITS_PER_UNIT and !BYTES_BIG_ENDIAN, clear
>       tmpbuf[byte_size - 1].  Formatting fix.
>       (shift_bytes_in_array_right): Formatting fix.
>
>       * gcc.c-torture/execute/pr79737-1.c: New test.
>       * gcc.c-torture/execute/pr79737-2.c: New test.
>
>--- gcc/gimple-ssa-store-merging.c.jj  2017-01-01 12:45:38.000000000
>+0100
>+++ gcc/gimple-ssa-store-merging.c     2017-02-28 10:34:05.848174576 +0100
>@@ -253,9 +253,9 @@ shift_bytes_in_array_right (unsigned cha
>       unsigned prev_carry_over = carry_over;
>       carry_over = ptr[i] & carry_mask;
> 
>-     carry_over <<= (unsigned char) BITS_PER_UNIT - amnt;
>-     ptr[i] >>= amnt;
>-     ptr[i] |= prev_carry_over;
>+      carry_over <<= (unsigned char) BITS_PER_UNIT - amnt;
>+      ptr[i] >>= amnt;
>+      ptr[i] |= prev_carry_over;
>     }
> }
> 
>@@ -352,8 +352,9 @@ encode_tree_to_bitpos (tree expr, unsign
> {
>   unsigned int first_byte = bitpos / BITS_PER_UNIT;
>   tree tmp_int = expr;
>-  bool sub_byte_op_p = (bitlen % BITS_PER_UNIT) || (bitpos %
>BITS_PER_UNIT)
>-                      || mode_for_size (bitlen, MODE_INT, 0) == BLKmode;
>+  bool sub_byte_op_p = ((bitlen % BITS_PER_UNIT)
>+                      || (bitpos % BITS_PER_UNIT)
>+                      || mode_for_size (bitlen, MODE_INT, 0) == BLKmode);
> 
>   if (!sub_byte_op_p)
> return (native_encode_expr (tmp_int, ptr + first_byte, total_bytes, 0)
>@@ -418,25 +419,27 @@ encode_tree_to_bitpos (tree expr, unsign
>      contain a sign bit due to sign-extension).  */
>   unsigned int padding
>    = byte_size - ROUND_UP (bitlen, BITS_PER_UNIT) / BITS_PER_UNIT - 1;
>-  if (padding != 0
>-      || bitlen % BITS_PER_UNIT != 0)
>+  /* On big-endian the padding is at the 'front' so just skip the
>initial
>+     bytes.  */
>+  if (BYTES_BIG_ENDIAN)
>+    tmpbuf += padding;
>+
>+  byte_size -= padding;
>+
>+  if (bitlen % BITS_PER_UNIT != 0)
>     {
>-      /* On big-endian the padding is at the 'front' so just skip the
>initial
>-       bytes.  */
>       if (BYTES_BIG_ENDIAN)
>-      tmpbuf += padding;
>-
>-      byte_size -= padding;
>-      if (bitlen % BITS_PER_UNIT != 0)
>-      {
>-        if (BYTES_BIG_ENDIAN)
>-          clear_bit_region_be (tmpbuf, BITS_PER_UNIT - 1,
>-                               BITS_PER_UNIT - (bitlen % BITS_PER_UNIT));
>-        else
>-          clear_bit_region (tmpbuf, bitlen,
>-                            byte_size * BITS_PER_UNIT - bitlen);
>-      }
>-    }
>+      clear_bit_region_be (tmpbuf, BITS_PER_UNIT - 1,
>+                           BITS_PER_UNIT - (bitlen % BITS_PER_UNIT));
>+      else
>+      clear_bit_region (tmpbuf, bitlen,
>+                        byte_size * BITS_PER_UNIT - bitlen);
>+    }
>+  /* Left shifting relies on the last byte being clear if bitlen is
>+     a multiple of BITS_PER_UNIT, which might not be clear if
>+     there are padding bytes.  */
>+  else if (!BYTES_BIG_ENDIAN)
>+    tmpbuf[byte_size - 1] = '\0';
> 
>   /* Clear the bit region in PTR where the bits from TMPBUF will be
>      inserted into.  */
>--- gcc/testsuite/gcc.c-torture/execute/pr79737-1.c.jj 2017-02-28
>10:36:13.678474604 +0100
>+++ gcc/testsuite/gcc.c-torture/execute/pr79737-1.c    2017-02-28
>10:36:00.861645051 +0100
>@@ -0,0 +1,37 @@
>+/* PR tree-optimization/79737 */
>+
>+#pragma pack(1)
>+struct S
>+{
>+  int b:18;
>+  int c:1;
>+  int d:24;
>+  int e:15;
>+  int f:14;
>+} i;
>+int g, j, k;
>+static struct S h;
>+
>+void
>+foo ()
>+{
>+  for (j = 0; j < 6; j++)
>+    k = 0;
>+  for (; k < 3; k++)
>+    {
>+      struct S m = { 5, 0, -5, 9, 5 };
>+      h = m;
>+      if (g)
>+      i = m;
>+      h.e = 0;
>+    }
>+}
>+
>+int
>+main ()
>+{
>+  foo ();
>+  if (h.e != 0)
>+    __builtin_abort ();
>+  return 0;
>+}
>--- gcc/testsuite/gcc.c-torture/execute/pr79737-2.c.jj 2017-02-28
>10:36:16.993430520 +0100
>+++ gcc/testsuite/gcc.c-torture/execute/pr79737-2.c    2017-02-28
>10:41:25.217325124 +0100
>@@ -0,0 +1,41 @@
>+/* PR tree-optimization/79737 */
>+
>+#pragma pack(1)
>+struct S
>+{
>+  int b:18;
>+  int c:1;
>+  int d:24;
>+  int e:15;
>+  int f:14;
>+} i, j;
>+
>+void
>+foo ()
>+{
>+  i.e = 0;
>+  i.b = 5;
>+  i.c = 0;
>+  i.d = -5;
>+  i.f = 5;
>+}
>+
>+void
>+bar ()
>+{
>+  j.b = 5;
>+  j.c = 0;
>+  j.d = -5;
>+  j.e = 0;
>+  j.f = 5;
>+}
>+
>+int
>+main ()
>+{
>+  foo ();
>+  bar ();
>+  asm volatile ("" : : : "memory");
>+  if (i.b != j.b || i.c != j.c || i.d != j.d || i.e != j.e || i.f !=
>j.f)
>+    __builtin_abort ();
>+}
>
>       Jakub

Reply via email to