From 82f620e2ba4c440c5e89bb1f73d10a11ed0f2eb4 Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Fri, 13 Mar 2020 09:16:29 +0100 Subject: [PATCH] Fix unaligned load with small memcpy on the ARM store_integral_bit_field is ready to handle BLKmode fields, there is even a subtlety with their handling on big-endian targets, see e.g. PR middle-end/50325, but not if they are unaligned, so the fix is simply to call extract_bit_field for them in order to generate an unaligned load. As a bonus, this subsumes the big-endian specific path that was added under PR middle-end/50325. PR middle-end/92071 * expmed.c (store_integral_bit_field): For fields larger than a word, call extract_bit_field on the value if the mode is BLKmode. Remove specific path for big-endian targets and tidy things up a little bit. --- gcc/ChangeLog | 7 +++ gcc/expmed.c | 55 +++++++++---------- gcc/testsuite/ChangeLog | 4 ++ .../gcc.c-torture/compile/20200313-1.c | 14 +++++ 4 files changed, 50 insertions(+), 30 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/compile/20200313-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f1a1984e8c7..7b573c07abc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2019-03-13 Eric Botcazou + + PR middle-end/92071 + * expmed.c (store_integral_bit_field): For fields larger than a word, + call extract_bit_field on the value if the mode is BLKmode. Remove + specific path for big-endian targets and tidy things up a little bit. + 2020-03-12 Richard Sandiford PR rtl-optimization/90275 diff --git a/gcc/expmed.c b/gcc/expmed.c index 04610276209..e7c03fbf92c 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -933,8 +933,7 @@ store_integral_bit_field (rtx op0, opt_scalar_int_mode op0_mode, However, only do that if the value is not BLKmode. */ const bool backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode; - unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; - unsigned int i; + const int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; rtx_insn *last; /* This is the mode we must force value to, so that there will be enough @@ -950,35 +949,31 @@ store_integral_bit_field (rtx op0, opt_scalar_int_mode op0_mode, value_mode = smallest_int_mode_for_size (nwords * BITS_PER_WORD); last = get_last_insn (); - for (i = 0; i < nwords; i++) + for (int i = 0; i < nwords; i++) { - /* If I is 0, use the low-order word in both field and target; - if I is 1, use the next to lowest word; and so on. */ - unsigned int wordnum = (backwards - ? GET_MODE_SIZE (value_mode) / UNITS_PER_WORD - - i - 1 - : i); - unsigned int bit_offset = (backwards ^ reverse - ? MAX ((int) bitsize - ((int) i + 1) - * BITS_PER_WORD, - 0) - : (int) i * BITS_PER_WORD); - rtx value_word = operand_subword_force (value, wordnum, value_mode); - unsigned HOST_WIDE_INT new_bitsize = - MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD); - - /* If the remaining chunk doesn't have full wordsize we have - to make sure that for big-endian machines the higher order - bits are used. */ - if (new_bitsize < BITS_PER_WORD && BYTES_BIG_ENDIAN && !backwards) - { - int shift = BITS_PER_WORD - new_bitsize; - rtx shift_rtx = gen_int_shift_amount (word_mode, shift); - value_word = simplify_expand_binop (word_mode, lshr_optab, - value_word, shift_rtx, - NULL_RTX, true, - OPTAB_LIB_WIDEN); - } + /* Number of bits to be stored in this iteration, i.e. BITS_PER_WORD + except maybe for the last iteration. */ + const unsigned HOST_WIDE_INT new_bitsize + = MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD); + /* Bit offset from the starting bit number in the target. */ + const unsigned int bit_offset + = backwards ^ reverse + ? MAX ((int) bitsize - (i + 1) * BITS_PER_WORD, 0) + : i * BITS_PER_WORD; + /* Starting word number in the value. */ + const unsigned int wordnum + = backwards + ? GET_MODE_SIZE (value_mode) / UNITS_PER_WORD - (i + 1) + : i; + /* The chunk of the value in word_mode. We use bit-field extraction + in BLKmode to handle unaligned memory references and to shift the + last chunk right on big-endian machines if need be. */ + rtx value_word + = fieldmode == BLKmode + ? extract_bit_field (value, new_bitsize, wordnum * BITS_PER_WORD, + 1, NULL_RTX, word_mode, word_mode, false, + NULL) + : operand_subword_force (value, wordnum, value_mode); if (!store_bit_field_1 (op0, new_bitsize, bitnum + bit_offset, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6d8eac22c31..e695b9bf26f 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2019-03-13 Eric Botcazou + + * gcc.c-torture/compile/20200313-1.c: New test. + 2020-03-12 Jeff Law PR rtl-optimization/90275 diff --git a/gcc/testsuite/gcc.c-torture/compile/20200313-1.c b/gcc/testsuite/gcc.c-torture/compile/20200313-1.c new file mode 100644 index 00000000000..4791badff59 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/compile/20200313-1.c @@ -0,0 +1,14 @@ +/* PR middle-end/92071 */ +/* Testcase by David Binderman */ + +void *a; +union U { double c; char d[8]; }; +void bar (union U); + +void +foo (void) +{ + union U b; + __builtin_memcpy (b.d, a, 8); + bar (b); +} -- 2.30.2