This patch fixes a wrong-code bug in store_bit_field.  When storing a
multi-word BLKmode value (such as from a RECORD_TYPE), the last word
is trashed on big-endian targets.  See the PR for the gory details.
Bootstrapped and regression tested powerpc64-linux.  Fixes 74
testsuite failures.  OK for mainline?  And earlier versions of gcc?

        PR middle-end/51926
        * expmed.c (store_bit_field_1): Properly handle last word of
        BLKmode value when bigendian.

Index: gcc/expmed.c
===================================================================
--- gcc/expmed.c        (revision 183228)
+++ gcc/expmed.c        (working copy)
@@ -551,19 +551,27 @@ store_bit_field_1 (rtx str_rtx, unsigned
          /* 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 ? nwords - i - 1 : i);
+         unsigned int nbits = (i == nwords - 1
+                               ? bitsize - i * BITS_PER_WORD
+                               : BITS_PER_WORD);
          unsigned int bit_offset = (backwards
-                                    ? MAX ((int) bitsize - ((int) i + 1)
-                                           * BITS_PER_WORD,
-                                           0)
-                                    : (int) i * BITS_PER_WORD);
+                                    ? bitsize - i * BITS_PER_WORD - nbits
+                                    : i * BITS_PER_WORD);
          rtx value_word = operand_subword_force (value, wordnum, fieldmode);
 
-         if (!store_bit_field_1 (op0, MIN (BITS_PER_WORD,
-                                           bitsize - i * BITS_PER_WORD),
-                                 bitnum + bit_offset,
+         if (WORDS_BIG_ENDIAN && !backwards && nbits != BITS_PER_WORD)
+           {
+             /* Original fieldmode was BLKmode.  The value in this
+                case consists of bits in memory order, so any unused
+                bits in the last word are in the least significant
+                position.  */
+             value_word = expand_shift (RSHIFT_EXPR, word_mode, value_word,
+                                        BITS_PER_WORD - nbits,
+                                        value_word, 1);
+           }
+         if (!store_bit_field_1 (op0, nbits, bitnum + bit_offset,
                                  bitregion_start, bitregion_end,
-                                 word_mode,
-                                 value_word, fallback_p))
+                                 word_mode, value_word, fallback_p))
            {
              delete_insns_since (last);
              return false;

-- 
Alan Modra
Australia Development Lab, IBM

Reply via email to