aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2020-12-11 11:10:17 +0100
committerJakub Jelinek <jakub@redhat.com>2020-12-11 11:10:17 +0100
commit3e60ddeb8220ed388819bb3f14e8caa9309fd3c2 (patch)
tree68ee16eb872a982e6b92cacd5bb660f493f4457a /gcc
parent499651e43854ea65303eb55324263c25256c9735 (diff)
downloadgcc-3e60ddeb8220ed388819bb3f14e8caa9309fd3c2.zip
gcc-3e60ddeb8220ed388819bb3f14e8caa9309fd3c2.tar.gz
gcc-3e60ddeb8220ed388819bb3f14e8caa9309fd3c2.tar.bz2
expansion: Sign or zero extend on MEM_REF stores into SUBREG with SUBREG_PROMOTED_VAR_P [PR98190]
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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/expr.c24
-rw-r--r--gcc/testsuite/gcc.dg/pr98190.c33
2 files changed, 57 insertions, 0 deletions
diff --git a/gcc/expr.c b/gcc/expr.c
index 798285e..a56594e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -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
index 0000000..bfdd17d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr98190.c
@@ -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;
+}