aboutsummaryrefslogtreecommitdiff
path: root/gcc/expmed.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/expmed.c')
-rw-r--r--gcc/expmed.c266
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,