diff options
Diffstat (limited to 'gcc/expmed.c')
-rw-r--r-- | gcc/expmed.c | 266 |
1 files changed, 209 insertions, 57 deletions
diff --git a/gcc/expmed.c b/gcc/expmed.c index a06576c4fdd..97a15130e4f 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -48,24 +48,24 @@ static void store_fixed_bit_field (rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, - rtx); + rtx, bool); static void store_fixed_bit_field_1 (rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, - rtx); + rtx, bool); static void store_split_bit_field (rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, - rtx); + rtx, bool); static rtx extract_fixed_bit_field (machine_mode, rtx, unsigned HOST_WIDE_INT, - unsigned HOST_WIDE_INT, rtx, int); + unsigned HOST_WIDE_INT, rtx, int, bool); static rtx extract_fixed_bit_field_1 (machine_mode, rtx, unsigned HOST_WIDE_INT, - unsigned HOST_WIDE_INT, rtx, int); + unsigned HOST_WIDE_INT, rtx, int, bool); static rtx lshift_value (machine_mode, unsigned HOST_WIDE_INT, int); static rtx extract_split_bit_field (rtx, unsigned HOST_WIDE_INT, - unsigned HOST_WIDE_INT, int); + unsigned HOST_WIDE_INT, int, bool); static void do_cmp_and_jump (rtx, rtx, enum rtx_code, machine_mode, rtx_code_label *); static rtx expand_smod_pow2 (machine_mode, rtx, HOST_WIDE_INT); static rtx expand_sdiv_pow2 (machine_mode, rtx, HOST_WIDE_INT); @@ -323,6 +323,94 @@ negate_rtx (machine_mode mode, rtx x) return result; } +/* Whether reverse storage order is supported on the target. */ +static int reverse_storage_order_supported = -1; + +/* Check whether reverse storage order is supported on the target. */ + +static void +check_reverse_storage_order_support (void) +{ + if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN) + { + reverse_storage_order_supported = 0; + sorry ("reverse scalar storage order"); + } + else + reverse_storage_order_supported = 1; +} + +/* Whether reverse FP storage order is supported on the target. */ +static int reverse_float_storage_order_supported = -1; + +/* Check whether reverse FP storage order is supported on the target. */ + +static void +check_reverse_float_storage_order_support (void) +{ + if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN) + { + reverse_float_storage_order_supported = 0; + sorry ("reverse floating-point scalar storage order"); + } + else + reverse_float_storage_order_supported = 1; +} + +/* Return an rtx representing value of X with reverse storage order. + MODE is the intended mode of the result, + useful if X is a CONST_INT. */ + +rtx +flip_storage_order (enum machine_mode mode, rtx x) +{ + enum machine_mode int_mode; + rtx result; + + if (mode == QImode) + return x; + + if (COMPLEX_MODE_P (mode)) + { + rtx real = read_complex_part (x, false); + rtx imag = read_complex_part (x, true); + + real = flip_storage_order (GET_MODE_INNER (mode), real); + imag = flip_storage_order (GET_MODE_INNER (mode), imag); + + return gen_rtx_CONCAT (mode, real, imag); + } + + if (__builtin_expect (reverse_storage_order_supported < 0, 0)) + check_reverse_storage_order_support (); + + if (SCALAR_INT_MODE_P (mode)) + int_mode = mode; + else + { + if (FLOAT_MODE_P (mode) + && __builtin_expect (reverse_float_storage_order_supported < 0, 0)) + check_reverse_float_storage_order_support (); + + int_mode = mode_for_size (GET_MODE_PRECISION (mode), MODE_INT, 0); + if (int_mode == BLKmode) + { + sorry ("reverse storage order for %smode", GET_MODE_NAME (mode)); + return x; + } + x = gen_lowpart (int_mode, x); + } + + result = simplify_unary_operation (BSWAP, int_mode, x, int_mode); + if (result == 0) + result = expand_unop (int_mode, bswap_optab, x, NULL_RTX, 1); + + if (int_mode != mode) + result = gen_lowpart (mode, result); + + return result; +} + /* Adjust bitfield memory MEM so that it points to the first unit of mode MODE that contains a bitfield of size BITSIZE at bit position BITNUM. If MODE is BLKmode, return a reference to every byte in the bitfield. @@ -626,7 +714,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitregion_start, unsigned HOST_WIDE_INT bitregion_end, machine_mode fieldmode, - rtx value, bool fallback_p) + rtx value, bool reverse, bool fallback_p) { rtx op0 = str_rtx; rtx orig_value; @@ -642,7 +730,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, int outer_mode_size = GET_MODE_SIZE (GET_MODE (op0)); int byte_offset = 0; - /* Paradoxical subregs need special handling on big endian machines. */ + /* Paradoxical subregs need special handling on big-endian machines. */ if (SUBREG_BYTE (op0) == 0 && inner_mode_size < outer_mode_size) { int difference = inner_mode_size - outer_mode_size; @@ -704,6 +792,8 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, sub = simplify_gen_subreg (GET_MODE (op0), value, fieldmode, 0); if (sub) { + if (reverse) + sub = flip_storage_order (GET_MODE (op0), sub); emit_move_insn (op0, sub); return true; } @@ -714,6 +804,8 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, bitnum / BITS_PER_UNIT); if (sub) { + if (reverse) + value = flip_storage_order (fieldmode, value); emit_move_insn (sub, value); return true; } @@ -726,6 +818,8 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, if (simple_mem_bitfield_p (op0, bitsize, bitnum, fieldmode)) { op0 = adjust_bitfield_address (op0, fieldmode, bitnum / BITS_PER_UNIT); + if (reverse) + value = flip_storage_order (fieldmode, value); emit_move_insn (op0, value); return true; } @@ -752,6 +846,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, can be done with a movstrict instruction. */ if (!MEM_P (op0) + && !reverse && lowpart_bit_field_p (bitnum, bitsize, GET_MODE (op0)) && bitsize == GET_MODE_BITSIZE (fieldmode) && optab_handler (movstrict_optab, fieldmode) != CODE_FOR_nothing) @@ -795,7 +890,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, be less than full. However, only do that if the value is not BLKmode. */ - unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode; + const bool backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode; unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; unsigned int i; rtx_insn *last; @@ -818,7 +913,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ? GET_MODE_SIZE (fieldmode) / UNITS_PER_WORD - i - 1 : i); - unsigned int bit_offset = (backwards + unsigned int bit_offset = (backwards ^ reverse ? MAX ((int) bitsize - ((int) i + 1) * BITS_PER_WORD, 0) @@ -828,7 +923,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT 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 + to make sure that for big-endian machines the higher order bits are used. */ if (new_bitsize < BITS_PER_WORD && BYTES_BIG_ENDIAN && !backwards) value_word = simplify_expand_binop (word_mode, lshr_optab, @@ -842,7 +937,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, bitnum + bit_offset, bitregion_start, bitregion_end, word_mode, - value_word, fallback_p)) + value_word, reverse, fallback_p)) { delete_insns_since (last); return false; @@ -878,7 +973,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, return false; store_split_bit_field (op0, bitsize, bitnum, bitregion_start, - bitregion_end, value); + bitregion_end, value, reverse); return true; } } @@ -889,6 +984,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, extraction_insn insv; if (!MEM_P (op0) + && !reverse && get_best_reg_extraction_insn (&insv, EP_insv, GET_MODE_BITSIZE (GET_MODE (op0)), fieldmode) @@ -897,7 +993,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, /* If OP0 is a memory, try copying it to a register and seeing if a cheap register alternative is available. */ - if (MEM_P (op0)) + if (MEM_P (op0) && !reverse) { if (get_best_mem_extraction_insn (&insv, EP_insv, bitsize, bitnum, fieldmode) @@ -917,7 +1013,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, rtx tempreg = copy_to_reg (xop0); if (store_bit_field_1 (tempreg, bitsize, bitpos, bitregion_start, bitregion_end, - fieldmode, orig_value, false)) + fieldmode, orig_value, reverse, false)) { emit_move_insn (xop0, tempreg); return true; @@ -930,7 +1026,7 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, return false; store_fixed_bit_field (op0, bitsize, bitnum, bitregion_start, - bitregion_end, value); + bitregion_end, value, reverse); return true; } @@ -943,7 +1039,9 @@ store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, These two fields are 0, if the C++ memory model does not apply, or we are not interested in keeping track of bitfield regions. - FIELDMODE is the machine-mode of the FIELD_DECL node for this field. */ + FIELDMODE is the machine-mode of the FIELD_DECL node for this field. + + If REVERSE is true, the store is to be done in reverse order. */ void store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, @@ -951,7 +1049,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitregion_start, unsigned HOST_WIDE_INT bitregion_end, machine_mode fieldmode, - rtx value) + rtx value, bool reverse) { /* Handle -fstrict-volatile-bitfields in the cases where it applies. */ if (strict_volatile_bitfield_p (str_rtx, bitsize, bitnum, fieldmode, @@ -965,6 +1063,8 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, { str_rtx = adjust_bitfield_address (str_rtx, fieldmode, bitnum / BITS_PER_UNIT); + if (reverse) + value = flip_storage_order (fieldmode, value); gcc_assert (bitnum % BITS_PER_UNIT == 0); emit_move_insn (str_rtx, value); } @@ -977,7 +1077,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, gcc_assert (bitnum + bitsize <= GET_MODE_BITSIZE (fieldmode)); temp = copy_to_reg (str_rtx); if (!store_bit_field_1 (temp, bitsize, bitnum, 0, 0, - fieldmode, value, true)) + fieldmode, value, reverse, true)) gcc_unreachable (); emit_move_insn (str_rtx, temp); @@ -1010,19 +1110,21 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, if (!store_bit_field_1 (str_rtx, bitsize, bitnum, bitregion_start, bitregion_end, - fieldmode, value, true)) + fieldmode, value, reverse, true)) gcc_unreachable (); } /* Use shifts and boolean operations to store VALUE into a bit field of - width BITSIZE in OP0, starting at bit BITNUM. */ + width BITSIZE in OP0, starting at bit BITNUM. + + If REVERSE is true, the store is to be done in reverse order. */ static void store_fixed_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitnum, unsigned HOST_WIDE_INT bitregion_start, unsigned HOST_WIDE_INT bitregion_end, - rtx value) + rtx value, bool reverse) { /* There is a case not handled here: a structure with a known alignment of just a halfword @@ -1045,14 +1147,14 @@ store_fixed_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, /* The only way this should occur is if the field spans word boundaries. */ store_split_bit_field (op0, bitsize, bitnum, bitregion_start, - bitregion_end, value); + bitregion_end, value, reverse); return; } op0 = narrow_bit_field_mem (op0, mode, bitsize, bitnum, &bitnum); } - store_fixed_bit_field_1 (op0, bitsize, bitnum, value); + store_fixed_bit_field_1 (op0, bitsize, bitnum, value, reverse); } /* Helper function for store_fixed_bit_field, stores @@ -1061,7 +1163,7 @@ store_fixed_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, static void store_fixed_bit_field_1 (rtx op0, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitnum, - rtx value) + rtx value, bool reverse) { machine_mode mode; rtx temp; @@ -1074,7 +1176,7 @@ store_fixed_bit_field_1 (rtx op0, unsigned HOST_WIDE_INT bitsize, /* Note that bitsize + bitnum can be greater than GET_MODE_BITSIZE (mode) for invalid input, such as f5 from gcc.dg/pr48335-2.c. */ - if (BYTES_BIG_ENDIAN) + if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN) /* BITNUM is the distance between our msb and that of the containing datum. Convert it to the distance from the lsb. */ @@ -1120,6 +1222,9 @@ store_fixed_bit_field_1 (rtx op0, unsigned HOST_WIDE_INT bitsize, bitnum, NULL_RTX, 1); } + if (reverse) + value = flip_storage_order (mode, value); + /* Now clear the chosen bits in OP0, except that if VALUE is -1 we need not bother. */ /* We keep the intermediates in registers to allow CSE to combine @@ -1129,8 +1234,10 @@ store_fixed_bit_field_1 (rtx op0, unsigned HOST_WIDE_INT bitsize, if (! all_one) { - temp = expand_binop (mode, and_optab, temp, - mask_rtx (mode, bitnum, bitsize, 1), + rtx mask = mask_rtx (mode, bitnum, bitsize, 1); + if (reverse) + mask = flip_storage_order (mode, mask); + temp = expand_binop (mode, and_optab, temp, mask, NULL_RTX, 1, OPTAB_LIB_WIDEN); temp = force_reg (mode, temp); } @@ -1158,6 +1265,8 @@ store_fixed_bit_field_1 (rtx op0, unsigned HOST_WIDE_INT bitsize, (within the word). VALUE is the value to store. + If REVERSE is true, the store is to be done in reverse order. + This does not yet handle fields wider than BITS_PER_WORD. */ static void @@ -1165,10 +1274,9 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitpos, unsigned HOST_WIDE_INT bitregion_start, unsigned HOST_WIDE_INT bitregion_end, - rtx value) + rtx value, bool reverse) { - unsigned int unit; - unsigned int bitsdone = 0; + unsigned int unit, total_bits, bitsdone = 0; /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that much at a time. */ @@ -1199,12 +1307,14 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, : word_mode, value)); } + total_bits = GET_MODE_BITSIZE (GET_MODE (value)); + while (bitsdone < bitsize) { unsigned HOST_WIDE_INT thissize; - rtx part, word; unsigned HOST_WIDE_INT thispos; unsigned HOST_WIDE_INT offset; + rtx part, word; offset = (bitpos + bitsdone) / unit; thispos = (bitpos + bitsdone) % unit; @@ -1229,13 +1339,18 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, thissize = MIN (bitsize - bitsdone, BITS_PER_WORD); thissize = MIN (thissize, unit - thispos); - if (BYTES_BIG_ENDIAN) + if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN) { /* Fetch successively less significant portions. */ if (CONST_INT_P (value)) part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) >> (bitsize - bitsdone - thissize)) & (((HOST_WIDE_INT) 1 << thissize) - 1)); + /* Likewise, but the source is little-endian. */ + else if (reverse) + part = extract_fixed_bit_field (word_mode, value, thissize, + bitsize - bitsdone - thissize, + NULL_RTX, 1, false); else { int total_bits = GET_MODE_BITSIZE (GET_MODE (value)); @@ -1244,7 +1359,7 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, endianness compensation) to fetch the piece we want. */ part = extract_fixed_bit_field (word_mode, value, thissize, total_bits - bitsize + bitsdone, - NULL_RTX, 1); + NULL_RTX, 1, false); } } else @@ -1254,9 +1369,14 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsdone) & (((HOST_WIDE_INT) 1 << thissize) - 1)); + /* Likewise, but the source is big-endian. */ + else if (reverse) + part = extract_fixed_bit_field (word_mode, value, thissize, + total_bits - bitsdone - thissize, + NULL_RTX, 1, false); else part = extract_fixed_bit_field (word_mode, value, thissize, - bitsdone, NULL_RTX, 1); + bitsdone, NULL_RTX, 1, false); } /* If OP0 is a register, then handle OFFSET here. @@ -1294,7 +1414,8 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, it is just an out-of-bounds access. Ignore it. */ if (word != const0_rtx) store_fixed_bit_field (word, thissize, offset * unit + thispos, - bitregion_start, bitregion_end, part); + bitregion_start, bitregion_end, part, + reverse); bitsdone += thissize; } } @@ -1419,7 +1540,7 @@ static rtx extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target, machine_mode mode, machine_mode tmode, - bool fallback_p) + bool reverse, bool fallback_p) { rtx op0 = str_rtx; machine_mode int_mode; @@ -1445,6 +1566,8 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, && bitnum == 0 && bitsize == GET_MODE_BITSIZE (GET_MODE (op0))) { + if (reverse) + op0 = flip_storage_order (mode, op0); /* We're trying to extract a full register from itself. */ return op0; } @@ -1561,6 +1684,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, as the least significant bit of the value is the least significant bit of either OP0 or a word of OP0. */ if (!MEM_P (op0) + && !reverse && lowpart_bit_field_p (bitnum, bitsize, GET_MODE (op0)) && bitsize == GET_MODE_BITSIZE (mode1) && TRULY_NOOP_TRUNCATION_MODES_P (mode1, GET_MODE (op0))) @@ -1576,6 +1700,8 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, if (simple_mem_bitfield_p (op0, bitsize, bitnum, mode1)) { op0 = adjust_bitfield_address (op0, mode1, bitnum / BITS_PER_UNIT); + if (reverse) + op0 = flip_storage_order (mode1, op0); return convert_extracted_bit_field (op0, mode, tmode, unsignedp); } @@ -1588,7 +1714,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, This is because the most significant word is the one which may be less than full. */ - unsigned int backwards = WORDS_BIG_ENDIAN; + const bool backwards = WORDS_BIG_ENDIAN; unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; unsigned int i; rtx_insn *last; @@ -1615,7 +1741,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1 : i); /* Offset from start of field in OP0. */ - unsigned int bit_offset = (backwards + unsigned int bit_offset = (backwards ^ reverse ? MAX ((int) bitsize - ((int) i + 1) * BITS_PER_WORD, 0) @@ -1625,7 +1751,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, = extract_bit_field_1 (op0, MIN (BITS_PER_WORD, bitsize - i * BITS_PER_WORD), bitnum + bit_offset, 1, target_part, - mode, word_mode, fallback_p); + mode, word_mode, reverse, fallback_p); gcc_assert (target_part); if (!result_part) @@ -1675,7 +1801,8 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, { if (!fallback_p) return NULL_RTX; - target = extract_split_bit_field (op0, bitsize, bitnum, unsignedp); + target = extract_split_bit_field (op0, bitsize, bitnum, unsignedp, + reverse); return convert_extracted_bit_field (target, mode, tmode, unsignedp); } } @@ -1685,6 +1812,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, enum extraction_pattern pattern = unsignedp ? EP_extzv : EP_extv; extraction_insn extv; if (!MEM_P (op0) + && !reverse /* ??? We could limit the structure size to the part of OP0 that contains the field, with appropriate checks for endianness and TRULY_NOOP_TRUNCATION. */ @@ -1701,7 +1829,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, /* If OP0 is a memory, try copying it to a register and seeing if a cheap register alternative is available. */ - if (MEM_P (op0)) + if (MEM_P (op0) & !reverse) { if (get_best_mem_extraction_insn (&extv, pattern, bitsize, bitnum, tmode)) @@ -1726,7 +1854,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, xop0 = copy_to_reg (xop0); rtx result = extract_bit_field_1 (xop0, bitsize, bitpos, unsignedp, target, - mode, tmode, false); + mode, tmode, reverse, false); if (result) return result; delete_insns_since (last); @@ -1744,9 +1872,21 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, /* Should probably push op0 out to memory and then do a load. */ gcc_assert (int_mode != BLKmode); - target = extract_fixed_bit_field (int_mode, op0, bitsize, bitnum, - target, unsignedp); - return convert_extracted_bit_field (target, mode, tmode, unsignedp); + target = extract_fixed_bit_field (int_mode, op0, bitsize, bitnum, target, + unsignedp, reverse); + + /* Complex values must be reversed piecewise, so we need to undo the global + reversal, convert to the complex mode and reverse again. */ + if (reverse && COMPLEX_MODE_P (tmode)) + { + target = flip_storage_order (int_mode, target); + target = convert_extracted_bit_field (target, mode, tmode, unsignedp); + target = flip_storage_order (tmode, target); + } + else + target = convert_extracted_bit_field (target, mode, tmode, unsignedp); + + return target; } /* Generate code to extract a byte-field from STR_RTX @@ -1760,6 +1900,8 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, TMODE is the mode the caller would like the value to have; but the value may be returned with type MODE instead. + If REVERSE is true, the extraction is to be done in reverse order. + If a TARGET is specified and we can store in it at no extra cost, we do so, and return TARGET. Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred @@ -1768,7 +1910,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, rtx extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target, - machine_mode mode, machine_mode tmode) + machine_mode mode, machine_mode tmode, bool reverse) { machine_mode mode1; @@ -1790,6 +1932,8 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, { rtx result = adjust_bitfield_address (str_rtx, mode1, bitnum / BITS_PER_UNIT); + if (reverse) + result = flip_storage_order (mode1, result); gcc_assert (bitnum % BITS_PER_UNIT == 0); return convert_extracted_bit_field (result, mode, tmode, unsignedp); } @@ -1801,13 +1945,15 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, } return extract_bit_field_1 (str_rtx, bitsize, bitnum, unsignedp, - target, mode, tmode, true); + target, mode, tmode, reverse, true); } /* Use shifts and boolean operations to extract a field of BITSIZE bits from bit BITNUM of OP0. UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). + If REVERSE is true, the extraction is to be done in reverse order. + If TARGET is nonzero, attempts to store the value there and return TARGET, but this is not guaranteed. If TARGET is not used, create a pseudo-reg of mode TMODE for the value. */ @@ -1816,7 +1962,7 @@ static rtx extract_fixed_bit_field (machine_mode tmode, rtx op0, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitnum, rtx target, - int unsignedp) + int unsignedp, bool reverse) { if (MEM_P (op0)) { @@ -1827,13 +1973,14 @@ extract_fixed_bit_field (machine_mode tmode, rtx op0, if (mode == VOIDmode) /* The only way this should occur is if the field spans word boundaries. */ - return extract_split_bit_field (op0, bitsize, bitnum, unsignedp); + return extract_split_bit_field (op0, bitsize, bitnum, unsignedp, + reverse); op0 = narrow_bit_field_mem (op0, mode, bitsize, bitnum, &bitnum); } return extract_fixed_bit_field_1 (tmode, op0, bitsize, bitnum, - target, unsignedp); + target, unsignedp, reverse); } /* Helper function for extract_fixed_bit_field, extracts @@ -1843,7 +1990,7 @@ static rtx extract_fixed_bit_field_1 (machine_mode tmode, rtx op0, unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitnum, rtx target, - int unsignedp) + int unsignedp, bool reverse) { machine_mode mode = GET_MODE (op0); gcc_assert (SCALAR_INT_MODE_P (mode)); @@ -1852,13 +1999,15 @@ extract_fixed_bit_field_1 (machine_mode tmode, rtx op0, for invalid input, such as extract equivalent of f5 from gcc.dg/pr48335-2.c. */ - if (BYTES_BIG_ENDIAN) + if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN) /* BITNUM is the distance between our msb and that of OP0. Convert it to the distance from the lsb. */ bitnum = GET_MODE_BITSIZE (mode) - bitsize - bitnum; /* Now BITNUM is always the distance between the field's lsb and that of OP0. We have reduced the big-endian case to the little-endian case. */ + if (reverse) + op0 = flip_storage_order (mode, op0); if (unsignedp) { @@ -1930,11 +2079,14 @@ lshift_value (machine_mode mode, unsigned HOST_WIDE_INT value, OP0 is the REG, SUBREG or MEM rtx for the first of the two words. BITSIZE is the field width; BITPOS, position of its first bit, in the word. - UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. */ + UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. + + If REVERSE is true, the extraction is to be done in reverse order. */ static rtx extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, - unsigned HOST_WIDE_INT bitpos, int unsignedp) + unsigned HOST_WIDE_INT bitpos, int unsignedp, + bool reverse) { unsigned int unit; unsigned int bitsdone = 0; @@ -1989,11 +2141,11 @@ extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, whose meaning is determined by BYTES_PER_UNIT. OFFSET is in UNITs, and UNIT is in bits. */ part = extract_fixed_bit_field (word_mode, word, thissize, - offset * unit + thispos, 0, 1); + offset * unit + thispos, 0, 1, reverse); bitsdone += thissize; /* Shift this part into place for the result. */ - if (BYTES_BIG_ENDIAN) + if (reverse ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN) { if (bitsize != bitsdone) part = expand_shift (LSHIFT_EXPR, word_mode, part, |