re PR rtl-optimization/65401 (make_field_assignment broken for big-endian)
authorJakub Jelinek <jakub@redhat.com>
Sat, 14 Mar 2015 08:54:08 +0000 (09:54 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Sat, 14 Mar 2015 08:54:08 +0000 (09:54 +0100)
PR rtl-optimization/65401
* combine.c (rtx_equal_for_field_assignment_p): Add widen_x
argument.  If true, adjust_address_nv of x with big-endian
correction for the mode widening to GET_MODE (y).
(make_field_assignment): Don't do MEM mode widening here.
Use MEM_P instead of GET_CODE == MEM.

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

From-SVN: r221433

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

index 5bcfdb9786d319cf75350d309f2bc657b6e60a9c..e62f67b5ce5ccf7898c46ffb1783fd4f0797e95e 100644 (file)
@@ -1,3 +1,12 @@
+2015-03-14  Jakub Jelinek  <jakub@redhat.com>
+
+       PR rtl-optimization/65401
+       * combine.c (rtx_equal_for_field_assignment_p): Add widen_x
+       argument.  If true, adjust_address_nv of x with big-endian
+       correction for the mode widening to GET_MODE (y).
+       (make_field_assignment): Don't do MEM mode widening here.
+       Use MEM_P instead of GET_CODE == MEM.
+
 2015-03-13  Ilya Verbin  <ilya.verbin@intel.com>
 
        * varpool.c (varpool_node::get_create): Don't set 'offloadable' flag for
index f779117cd7174f463c4eccd88fdb0a9f14a1f311..71e5690459d5e61f052c02920ab7c9f2d38b2dc4 100644 (file)
@@ -475,7 +475,7 @@ static rtx force_to_mode (rtx, machine_mode,
                          unsigned HOST_WIDE_INT, int);
 static rtx if_then_else_cond (rtx, rtx *, rtx *);
 static rtx known_cond (rtx, enum rtx_code, rtx, rtx);
-static int rtx_equal_for_field_assignment_p (rtx, rtx);
+static int rtx_equal_for_field_assignment_p (rtx, rtx, bool = false);
 static rtx make_field_assignment (rtx);
 static rtx apply_distributive_law (rtx);
 static rtx distribute_and_simplify_rtx (rtx, int);
@@ -9184,8 +9184,23 @@ known_cond (rtx x, enum rtx_code cond, rtx reg, rtx val)
    assignment as a field assignment.  */
 
 static int
-rtx_equal_for_field_assignment_p (rtx x, rtx y)
+rtx_equal_for_field_assignment_p (rtx x, rtx y, bool widen_x)
 {
+  if (widen_x && GET_MODE (x) != GET_MODE (y))
+    {
+      if (GET_MODE_SIZE (GET_MODE (x)) > GET_MODE_SIZE (GET_MODE (y)))
+       return 0;
+      if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
+       return 0;
+      /* For big endian, adjust the memory offset.  */
+      if (BYTES_BIG_ENDIAN)
+       x = adjust_address_nv (x, GET_MODE (y),
+                              -subreg_lowpart_offset (GET_MODE (x),
+                                                      GET_MODE (y)));
+      else
+       x = adjust_address_nv (x, GET_MODE (y), 0);
+    }
+
   if (x == y || rtx_equal_p (x, y))
     return 1;
 
@@ -9339,16 +9354,15 @@ make_field_assignment (rtx x)
   /* The second SUBREG that might get in the way is a paradoxical
      SUBREG around the first operand of the AND.  We want to 
      pretend the operand is as wide as the destination here.   We
-     do this by creating a new MEM in the wider mode for the sole
+     do this by adjusting the MEM to wider mode for the sole
      purpose of the call to rtx_equal_for_field_assignment_p.   Also
      note this trick only works for MEMs.  */
   else if (GET_CODE (rhs) == AND
           && paradoxical_subreg_p (XEXP (rhs, 0))
-          && GET_CODE (SUBREG_REG (XEXP (rhs, 0))) == MEM
+          && MEM_P (SUBREG_REG (XEXP (rhs, 0)))
           && CONST_INT_P (XEXP (rhs, 1))
-          && rtx_equal_for_field_assignment_p (gen_rtx_MEM (GET_MODE (dest),
-                                                            XEXP (SUBREG_REG (XEXP (rhs, 0)), 0)),
-                                               dest))
+          && rtx_equal_for_field_assignment_p (SUBREG_REG (XEXP (rhs, 0)),
+                                               dest, true))
     c1 = INTVAL (XEXP (rhs, 1)), other = lhs;
   else if (GET_CODE (lhs) == AND
           && CONST_INT_P (XEXP (lhs, 1))
@@ -9357,16 +9371,15 @@ make_field_assignment (rtx x)
   /* The second SUBREG that might get in the way is a paradoxical
      SUBREG around the first operand of the AND.  We want to 
      pretend the operand is as wide as the destination here.   We
-     do this by creating a new MEM in the wider mode for the sole
+     do this by adjusting the MEM to wider mode for the sole
      purpose of the call to rtx_equal_for_field_assignment_p.   Also
      note this trick only works for MEMs.  */
   else if (GET_CODE (lhs) == AND
           && paradoxical_subreg_p (XEXP (lhs, 0))
-          && GET_CODE (SUBREG_REG (XEXP (lhs, 0))) == MEM
+          && MEM_P (SUBREG_REG (XEXP (lhs, 0)))
           && CONST_INT_P (XEXP (lhs, 1))
-          && rtx_equal_for_field_assignment_p (gen_rtx_MEM (GET_MODE (dest),
-                                                            XEXP (SUBREG_REG (XEXP (lhs, 0)), 0)),
-                                               dest))
+          && rtx_equal_for_field_assignment_p (SUBREG_REG (XEXP (lhs, 0)),
+                                               dest, true))
     c1 = INTVAL (XEXP (lhs, 1)), other = rhs;
   else
     return x;
index 152d36cf77f4b90980722c0316c11ae1a1dfd591..d9a46cbfb6690632d12efad84e9900a884008075 100644 (file)
@@ -1,3 +1,8 @@
+2015-03-14  Jakub Jelinek  <jakub@redhat.com>
+
+       PR rtl-optimization/65401
+       * gcc.c-torture/execute/pr65401.c: New test.
+
 2015-03-13  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
 
        PR target/64600
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr65401.c b/gcc/testsuite/gcc.c-torture/execute/pr65401.c
new file mode 100644 (file)
index 0000000..82cfafc
--- /dev/null
@@ -0,0 +1,59 @@
+/* PR rtl-optimization/65401 */
+
+struct S { unsigned short s[64]; };
+
+__attribute__((noinline, noclone)) void
+foo (struct S *x)
+{
+  unsigned int i;
+  unsigned char *s;
+
+  s = (unsigned char *) x->s;
+  for (i = 0; i < 64; i++)
+    x->s[i] = s[i * 2] | (s[i * 2 + 1] << 8);
+}  
+
+__attribute__((noinline, noclone)) void
+bar (struct S *x)
+{
+  unsigned int i;
+  unsigned char *s;
+
+  s = (unsigned char *) x->s;
+  for (i = 0; i < 64; i++)
+    x->s[i] = (s[i * 2] << 8) | s[i * 2 + 1];
+}  
+
+int
+main ()
+{
+  unsigned int i;
+  struct S s;
+  if (sizeof (unsigned short) != 2)
+    return 0;
+  for (i = 0; i < 64; i++)
+    s.s[i] = i + ((64 - i) << 8);
+  foo (&s);
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+  for (i = 0; i < 64; i++)
+    if (s.s[i] != (64 - i) + (i << 8))
+      __builtin_abort ();
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+  for (i = 0; i < 64; i++)
+    if (s.s[i] != i + ((64 - i) << 8))
+      __builtin_abort ();
+#endif
+  for (i = 0; i < 64; i++)
+    s.s[i] = i + ((64 - i) << 8);
+  bar (&s);
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+  for (i = 0; i < 64; i++)
+    if (s.s[i] != (64 - i) + (i << 8))
+      __builtin_abort ();
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+  for (i = 0; i < 64; i++)
+    if (s.s[i] != i + ((64 - i) << 8))
+      __builtin_abort ();
+#endif
+  return 0;
+}