Hi! Some target 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. I've bootstrapped/regtested an earlier version of this patch on {x86_64,i686,aarch64,armv7hl,powerpc64le}-linux; that version head instead an assert that bitpos is 0 and bitsize GET_MODE_BITSIZE of to_rtx. That resulted in g++.dg/warn/Wstrict-aliasing-bogus-char-1.C gcc.dg/pr87273.c gcc.dg/torture/pr91656-1.c gcc.dg/tree-ssa/pr92085-2.c gcc.dg/tree-ssa/pr94703.c regressions on powerpc64le and gcc.c-torture/execute/pr93213.c on powerpc64le and aarch64, but all of those succeed now with this version. Ok for trunk? 2020-12-10 Jakub Jelinek <ja...@redhat.com> PR middle-end/98190 * expr.c (expand_assignment): If to_rtx is a promoted SUBREG, ensure sign or zero extension when the most significant bit has been overwritten, either through use of store_expr or by extending manually. * gcc.dg/pr98190.c: New test. --- gcc/expr.c.jj 2020-12-09 23:50:41.385776978 +0100 +++ gcc/expr.c 2020-12-10 09:35:56.231058928 +0100 @@ -5451,6 +5451,43 @@ expand_assignment (tree to, tree from, b 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 = to_rtx; + /* Optimize by checking if the overwritten bits + include the most significant bit. */ + if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN + || (BYTES_BIG_ENDIAN && known_eq (bitpos, 0)) + || (!BYTES_BIG_ENDIAN + && known_eq (bitpos + bitsize, + GET_MODE_BITSIZE (GET_MODE (to_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); + if (to_rtx1 != to_rtx) + { + to_rtx1 + = convert_to_mode (subreg_promoted_mode (to_rtx), + to_rtx1, + SUBREG_PROMOTED_SIGN (to_rtx)); + emit_move_insn (SUBREG_REG (to_rtx), to_rtx1); + } + } + } else result = store_field (to_rtx, bitsize, bitpos, bitregion_start, bitregion_end, --- gcc/testsuite/gcc.dg/pr98190.c.jj 2020-12-10 09:08:54.838216477 +0100 +++ gcc/testsuite/gcc.dg/pr98190.c 2020-12-10 09:08:54.838216477 +0100 @@ -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; +} Jakub