combine: Fix find_split_point handling of constant store into ZERO_EXTRACT [PR93908]
authorJakub Jelinek <jakub@redhat.com>
Tue, 25 Feb 2020 12:56:47 +0000 (13:56 +0100)
committerJakub Jelinek <jakub@redhat.com>
Tue, 25 Feb 2020 12:56:47 +0000 (13:56 +0100)
git is miscompiled on s390x-linux with -O2 -march=zEC12 -mtune=z13.
I've managed to reduce it into the following testcase.  The problem is that
during combine we see the s->k = -1; bitfield store and change the SET_SRC
from a pseudo into a constant:
(set (zero_extract:DI (mem/j:HI (plus:DI (reg/v/f:DI 60 [ s ])
                (const_int 10 [0xa])) [0 +0 S2 A16])
        (const_int 2 [0x2])
        (const_int 7 [0x7]))
    (const_int -1 [0xffffffffffffffff]))
This on s390x with the above option isn't recognized as valid instruction,
so find_split_point decides to handle it as IOR or IOR/AND.
src is -1, mask is 3 and pos is 7.
src != mask (this is also incorrect, we want to set all (both) bits in the
bitfield), so we go for IOR/AND, but instead of trying
mem = (mem & ~0x180) | ((-1 << 7) & 0x180)
we actually try
mem = (mem & ~0x180) | (-1 << 7)
and that is further simplified into:
mem = mem | (-1 << 7)
aka
mem = mem | 0xff80
which doesn't set just the 2-bit bitfield, but also many other bitfields
that shouldn't be touched.
We really should do:
mem = mem | 0x180
instead.
The problem is that we assume that no bits but those low len (2 here) will
be set in the SET_SRC, but there is nothing that can prevent that, we just
should ignore the other bits.

The following patch fixes it by masking src with mask, this way already
the src == mask test will DTRT, and as the code for or_mask uses
gen_int_mode, if the most significant bit is set after shifting it left by
pos, it will be properly sign-extended.

2020-02-25  Jakub Jelinek  <jakub@redhat.com>

PR rtl-optimization/93908
* combine.c (find_split_point): For store into ZERO_EXTRACT, and src
with mask.

* gcc.c-torture/execute/pr93908.c: New test.

gcc/ChangeLog
gcc/combine.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/pr93908.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/pr93908.c [new file with mode: 0644]

index 78191a6b8c365ecb910a7a59ac7bb85d26ae1a3f..78a8d94eab9ce6ba144a9359a7fed2878bdb3f07 100644 (file)
@@ -1,3 +1,9 @@
+2020-02-25  Jakub Jelinek  <jakub@redhat.com>
+
+       PR rtl-optimization/93908
+       * combine.c (find_split_point): For store into ZERO_EXTRACT, and src
+       with mask.
+
 2019-02-25  Eric Botcazou  <ebotcazou@adacore.com>
 
        * dwarf2out.c (dwarf2out_size_function): Run in early-DWARF mode.
index d44b9c3bf950e52dce5089f7297a9fc7fb28dcfd..58366a6d3316eb10c96a810e1275f90a6c878f03 100644 (file)
@@ -5130,10 +5130,9 @@ find_split_point (rtx *loc, rtx_insn *insn, bool set_src)
        {
          HOST_WIDE_INT pos = INTVAL (XEXP (SET_DEST (x), 2));
          unsigned HOST_WIDE_INT len = INTVAL (XEXP (SET_DEST (x), 1));
-         unsigned HOST_WIDE_INT src = INTVAL (SET_SRC (x));
          rtx dest = XEXP (SET_DEST (x), 0);
-         unsigned HOST_WIDE_INT mask
-           = (HOST_WIDE_INT_1U << len) - 1;
+         unsigned HOST_WIDE_INT mask = (HOST_WIDE_INT_1U << len) - 1;
+         unsigned HOST_WIDE_INT src = INTVAL (SET_SRC (x)) & mask;
          rtx or_mask;
 
          if (BITS_BIG_ENDIAN)
index 3a22e8608df00faf171e3e5654e3673f9b2310ec..922ae5c4b7a4a3eced048525d504ffb9387c8151 100644 (file)
@@ -1,3 +1,8 @@
+2020-02-25  Jakub Jelinek  <jakub@redhat.com>
+
+       PR rtl-optimization/93908
+       * gcc.c-torture/execute/pr93908.c: New test.
+
 2019-02-25  Eric Botcazou  <ebotcazou@adacore.com>
 
        * gnat.dg/lto24.adb: New test.
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr93908.c b/gcc/testsuite/gcc.c-torture/execute/pr93908.c
new file mode 100644 (file)
index 0000000..cb6e3dd
--- /dev/null
@@ -0,0 +1,54 @@
+/* PR rtl-optimization/93908 */
+
+struct T
+{
+  int b;
+  int c;
+  unsigned short d;
+  unsigned e:1, f:1, g:1, h:2, i:1, j:1;
+  signed int k:2;
+};
+
+struct S
+{
+  struct T s;
+  char c[64];
+} buf[2];
+
+__attribute__ ((noipa)) void *
+baz (void)
+{
+  static int cnt;
+  return (void *) &buf[cnt++];
+}
+
+static inline __attribute__ ((always_inline)) struct T *
+bar (const char *a)
+{
+  struct T *s;
+  s = baz ();
+  s->b = 1;
+  s->k = -1;
+  return s;
+}
+
+__attribute__ ((noipa)) void
+foo (const char *x, struct T **y)
+{
+  struct T *l = bar (x);
+  struct T *m = bar (x);
+  y[0] = l;
+  y[1] = m;
+}
+
+int
+main ()
+{
+  struct T *r[2];
+  foo ("foo", r);
+  if (r[0]->e || r[0]->f || r[0]->g || r[0]->h || r[0]->i || r[0]->j || r[0]->k != -1)
+    __builtin_abort ();
+  if (r[1]->e || r[1]->f || r[1]->g || r[1]->h || r[1]->i || r[1]->j || r[1]->k != -1)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/pr93908.c b/gcc/testsuite/gcc.target/s390/pr93908.c
new file mode 100644 (file)
index 0000000..e594905
--- /dev/null
@@ -0,0 +1,5 @@
+/* PR rtl-optimization/93908 */
+/* { dg-do run { target s390_zEC12_hw } } */
+/* { dg-options "-O2 -march=zEC12 -mtune=z13" } */
+
+#include "../../gcc.c-torture/execute/pr93908.c"