expansion: Sign or zero extend on MEM_REF stores into SUBREG with SUBREG_PROMOTED_VAR...
authorJakub Jelinek <jakub@redhat.com>
Fri, 11 Dec 2020 10:10:17 +0000 (11:10 +0100)
committerJakub Jelinek <jakub@redhat.com>
Fri, 11 Dec 2020 10:10:17 +0000 (11:10 +0100)
Some targets decide to promote certain scalar variables to wider mode,
so their DECL_RTL is a SUBREG with SUBREG_PROMOTED_VAR_P.
When storing to such vars, store_expr takes care of sign or zero extending,
but if we store e.g. through MEM_REF into them, no sign or zero extension
happens and that leads to wrong-code e.g. on the following testcase on
aarch64-linux.

The following patch uses store_expr if we overwrite all the bits and it is
not reversed storage order, i.e. something that store_expr handles normally,
and otherwise (if the most significant bit is (or for pdp11 might be, but
pdp11 doesn't promote) being modified), the code extends manually.

2020-12-11  Jakub Jelinek  <jakub@redhat.com>

PR middle-end/98190
* expr.c (expand_assignment): If to_rtx is a promoted SUBREG,
ensure sign or zero extension either through use of store_expr
or by extending manually.

* gcc.dg/pr98190.c: New test.

gcc/expr.c
gcc/testsuite/gcc.dg/pr98190.c [new file with mode: 0644]

index 798285e..a56594e 100644 (file)
@@ -5451,6 +5451,30 @@ expand_assignment (tree to, tree from, bool nontemporal)
                                               mode1, to_rtx, to, from,
                                               reversep))
            result = NULL;
+         else if (SUBREG_P (to_rtx)
+                  && SUBREG_PROMOTED_VAR_P (to_rtx))
+           {
+             /* If to_rtx is a promoted subreg, we need to zero or sign
+                extend the value afterwards.  */
+             if (TREE_CODE (to) == MEM_REF
+                 && !REF_REVERSE_STORAGE_ORDER (to)
+                 && known_eq (bitpos, 0)
+                 && known_eq (bitsize, GET_MODE_BITSIZE (GET_MODE (to_rtx))))
+               result = store_expr (from, to_rtx, 0, nontemporal, false);
+             else
+               {
+                 rtx to_rtx1
+                   = lowpart_subreg (subreg_unpromoted_mode (to_rtx),
+                                     SUBREG_REG (to_rtx),
+                                     subreg_promoted_mode (to_rtx));
+                 result = store_field (to_rtx1, bitsize, bitpos,
+                                       bitregion_start, bitregion_end,
+                                       mode1, from, get_alias_set (to),
+                                       nontemporal, reversep);
+                 convert_move (SUBREG_REG (to_rtx), to_rtx1,
+                               SUBREG_PROMOTED_SIGN (to_rtx));
+               }
+           }
          else
            result = store_field (to_rtx, bitsize, bitpos,
                                  bitregion_start, bitregion_end,
diff --git a/gcc/testsuite/gcc.dg/pr98190.c b/gcc/testsuite/gcc.dg/pr98190.c
new file mode 100644 (file)
index 0000000..bfdd17d
--- /dev/null
@@ -0,0 +1,33 @@
+/* PR middle-end/98190 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+static int __attribute__((noipa))
+foo (const char *p, const char *q, const int len)
+{
+  for (int i = 0; i < len; p++, q++, i++)
+    {
+      int equal;
+      _Bool x, y;
+      __builtin_memcpy ((char *) &x, p, sizeof x);
+      __builtin_memcpy ((char *) &y, q, sizeof y);
+      equal = (x == y);
+      if (equal <= 0)
+       return equal;
+    }
+  return 1;
+}
+
+int
+main ()
+{
+  const _Bool buf[4] = { 1, 0, 0, 0 };
+#ifdef __aarch64__
+  register long x4 asm ("x4") = 0xdeadbeefULL;
+  register long x5 asm ("x5") = 0xcafebabeULL;
+  asm volatile (""::"r" (x4), "r" (x5));
+#endif
+  if (foo ((char *) &buf[0], (char *) &buf[0], 1) != 1)
+    __builtin_abort ();
+  return 0;
+}