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