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