Commit b8ab7fc8 by Richard Sandiford Committed by Richard Sandiford

expmed.c (store_split_bit_field): Update the calls to extract_fixed_bit_field.

gcc/
	* expmed.c (store_split_bit_field): Update the calls to
	extract_fixed_bit_field.  In the big-endian case, always
	use the mode of OP0 to count the number of significant bits.
	(extract_bit_field_1): Remove unit, offset, bitpos and
	byte_offset from the outermost scope.  Express conditions in terms
	of bitnum rather than offset, bitpos and byte_offset.  Move the
	computation of MODE1 to the block that needs it.  Use MODE unless
	the TMODE-based mode_for_size calculation succeeds.  Split the
	plain move cases into two, one for memory accesses and one for
	register accesses.  Generalize the memory case, freeing it from
	the old register-based endian checks.  Move the INT_MODE calculation
	above the code that needs it.  Use simplify_gen_subreg to handle
	multiword OP0s.  If the field still spans several words, pass it
	directly to extract_split_bit_field.  Assume after that point
	that both targets and register sources fit within a word.
	Replace x-prefixed variables with non-prefixed forms.
	Compute the bitpos for ext(z)v register operands directly in the
	chosen unit size, rather than going through an intermediate
	BITS_PER_WORD unit size.  Simplify the containment check
	used when forcing OP0 into a register.  Update the call to
	extract_fixed_bit_field.
	(extract_fixed_bit_field): Replace the bitpos and offset parameters
	with a single bitnum parameter, of the same form as extract_bit_field.
	Assume that OP0 contains the full field.  Simplify the memory offset
	calculation and containment check for volatile bitfields.  Make the
	offset explicit when volatile bitfields force a misaligned access.
	Remove WARNED and fix long lines.  Assert that the processed OP0
	has an integral mode.
	(store_split_bit_field): Update the call to store_fixed_bit_field.

From-SVN: r192741
parent bebf0797
2012-10-23 Richard Sandiford <rdsandiford@googlemail.com> 2012-10-23 Richard Sandiford <rdsandiford@googlemail.com>
* expmed.c (store_split_bit_field): Update the calls to
extract_fixed_bit_field. In the big-endian case, always
use the mode of OP0 to count the number of significant bits.
(extract_bit_field_1): Remove unit, offset, bitpos and
byte_offset from the outermost scope. Express conditions in terms
of bitnum rather than offset, bitpos and byte_offset. Move the
computation of MODE1 to the block that needs it. Use MODE unless
the TMODE-based mode_for_size calculation succeeds. Split the
plain move cases into two, one for memory accesses and one for
register accesses. Generalize the memory case, freeing it from
the old register-based endian checks. Move the INT_MODE calculation
above the code that needs it. Use simplify_gen_subreg to handle
multiword OP0s. If the field still spans several words, pass it
directly to extract_split_bit_field. Assume after that point
that both targets and register sources fit within a word.
Replace x-prefixed variables with non-prefixed forms.
Compute the bitpos for ext(z)v register operands directly in the
chosen unit size, rather than going through an intermediate
BITS_PER_WORD unit size. Simplify the containment check
used when forcing OP0 into a register. Update the call to
extract_fixed_bit_field.
(extract_fixed_bit_field): Replace the bitpos and offset parameters
with a single bitnum parameter, of the same form as extract_bit_field.
Assume that OP0 contains the full field. Simplify the memory offset
calculation and containment check for volatile bitfields. Make the
offset explicit when volatile bitfields force a misaligned access.
Remove WARNED and fix long lines. Assert that the processed OP0
has an integral mode.
(store_split_bit_field): Update the call to store_fixed_bit_field.
2012-10-23 Richard Sandiford <rdsandiford@googlemail.com>
* expmed.c (lowpart_bit_field_p): New function. * expmed.c (lowpart_bit_field_p): New function.
(store_bit_field_1): Remove unit, offset, bitpos and byte_offset (store_bit_field_1): Remove unit, offset, bitpos and byte_offset
from the outermost scope. Express conditions in terms of bitnum from the outermost scope. Express conditions in terms of bitnum
...@@ -57,7 +57,6 @@ static void store_split_bit_field (rtx, unsigned HOST_WIDE_INT, ...@@ -57,7 +57,6 @@ static void store_split_bit_field (rtx, unsigned HOST_WIDE_INT,
rtx); rtx);
static rtx extract_fixed_bit_field (enum machine_mode, rtx, static rtx extract_fixed_bit_field (enum machine_mode, rtx,
unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, rtx, int, bool); unsigned HOST_WIDE_INT, rtx, int, bool);
static rtx mask_rtx (enum machine_mode, int, int, int); static rtx mask_rtx (enum machine_mode, int, int, int);
static rtx lshift_value (enum machine_mode, rtx, int, int); static rtx lshift_value (enum machine_mode, rtx, int, int);
...@@ -1128,29 +1127,22 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, ...@@ -1128,29 +1127,22 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
if (BYTES_BIG_ENDIAN) if (BYTES_BIG_ENDIAN)
{ {
int total_bits;
/* We must do an endian conversion exactly the same way as it is
done in extract_bit_field, so that the two calls to
extract_fixed_bit_field will have comparable arguments. */
if (!MEM_P (value) || GET_MODE (value) == BLKmode)
total_bits = BITS_PER_WORD;
else
total_bits = GET_MODE_BITSIZE (GET_MODE (value));
/* Fetch successively less significant portions. */ /* Fetch successively less significant portions. */
if (CONST_INT_P (value)) if (CONST_INT_P (value))
part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
>> (bitsize - bitsdone - thissize)) >> (bitsize - bitsdone - thissize))
& (((HOST_WIDE_INT) 1 << thissize) - 1)); & (((HOST_WIDE_INT) 1 << thissize) - 1));
else else
{
int total_bits = GET_MODE_BITSIZE (GET_MODE (value));
/* The args are chosen so that the last part includes the /* The args are chosen so that the last part includes the
lsb. Give extract_bit_field the value it needs (with lsb. Give extract_bit_field the value it needs (with
endianness compensation) to fetch the piece we want. */ endianness compensation) to fetch the piece we want. */
part = extract_fixed_bit_field (word_mode, value, 0, thissize, part = extract_fixed_bit_field (word_mode, value, thissize,
total_bits - bitsize + bitsdone, total_bits - bitsize + bitsdone,
NULL_RTX, 1, false); NULL_RTX, 1, false);
} }
}
else else
{ {
/* Fetch successively more significant portions. */ /* Fetch successively more significant portions. */
...@@ -1159,7 +1151,7 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, ...@@ -1159,7 +1151,7 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
>> bitsdone) >> bitsdone)
& (((HOST_WIDE_INT) 1 << thissize) - 1)); & (((HOST_WIDE_INT) 1 << thissize) - 1));
else else
part = extract_fixed_bit_field (word_mode, value, 0, thissize, part = extract_fixed_bit_field (word_mode, value, thissize,
bitsdone, NULL_RTX, 1, false); bitsdone, NULL_RTX, 1, false);
} }
...@@ -1240,14 +1232,10 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1240,14 +1232,10 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
enum machine_mode mode, enum machine_mode tmode, enum machine_mode mode, enum machine_mode tmode,
bool fallback_p) bool fallback_p)
{ {
unsigned int unit
= (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD;
unsigned HOST_WIDE_INT offset, bitpos;
rtx op0 = str_rtx; rtx op0 = str_rtx;
enum machine_mode int_mode; enum machine_mode int_mode;
enum machine_mode ext_mode; enum machine_mode ext_mode;
enum machine_mode mode1; enum machine_mode mode1;
int byte_offset;
if (tmode == VOIDmode) if (tmode == VOIDmode)
tmode = mode; tmode = mode;
...@@ -1365,37 +1353,10 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1365,37 +1353,10 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
} }
} }
/* Extraction of a full-word or multi-word value from a structure
in a register or aligned memory can be done with just a SUBREG.
A subword value in the least significant part of a register
can also be extracted with a SUBREG. For this, we need the
byte offset of the value in op0. */
bitpos = bitnum % unit;
offset = bitnum / unit;
byte_offset = bitpos / BITS_PER_UNIT + offset * UNITS_PER_WORD;
/* If OP0 is a register, BITPOS must count within a word.
But as we have it, it counts within whatever size OP0 now has.
On a bigendian machine, these are not the same, so convert. */
if (BYTES_BIG_ENDIAN
&& !MEM_P (op0)
&& unit > GET_MODE_BITSIZE (GET_MODE (op0)))
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
/* ??? We currently assume TARGET is at least as big as BITSIZE. /* ??? We currently assume TARGET is at least as big as BITSIZE.
If that's wrong, the solution is to test for it and set TARGET to 0 If that's wrong, the solution is to test for it and set TARGET to 0
if needed. */ if needed. */
/* Only scalar integer modes can be converted via subregs. There is an
additional problem for FP modes here in that they can have a precision
which is different from the size. mode_for_size uses precision, but
we want a mode based on the size, so we must avoid calling it for FP
modes. */
mode1 = (SCALAR_INT_MODE_P (tmode)
? mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0)
: mode);
/* If the bitfield is volatile, we need to make sure the access /* If the bitfield is volatile, we need to make sure the access
remains on a type-aligned boundary. */ remains on a type-aligned boundary. */
if (GET_CODE (op0) == MEM if (GET_CODE (op0) == MEM
...@@ -1404,39 +1365,48 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1404,39 +1365,48 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
&& flag_strict_volatile_bitfields > 0) && flag_strict_volatile_bitfields > 0)
goto no_subreg_mode_swap; goto no_subreg_mode_swap;
if (((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode) /* Only scalar integer modes can be converted via subregs. There is an
&& bitpos % BITS_PER_WORD == 0) additional problem for FP modes here in that they can have a precision
|| (mode1 != BLKmode which is different from the size. mode_for_size uses precision, but
/* ??? The big endian test here is wrong. This is correct we want a mode based on the size, so we must avoid calling it for FP
if the value is in a register, and if mode_for_size is not modes. */
the same mode as op0. This causes us to get unnecessarily mode1 = mode;
inefficient code from the Thumb port when -mbig-endian. */ if (SCALAR_INT_MODE_P (tmode))
&& (BYTES_BIG_ENDIAN
? bitpos + bitsize == BITS_PER_WORD
: bitpos == 0)))
&& ((!MEM_P (op0)
&& TRULY_NOOP_TRUNCATION_MODES_P (mode1, GET_MODE (op0))
&& GET_MODE_SIZE (mode1) != 0
&& byte_offset % GET_MODE_SIZE (mode1) == 0)
|| (MEM_P (op0)
&& (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (op0))
|| (offset * BITS_PER_UNIT % bitsize == 0
&& MEM_ALIGN (op0) % bitsize == 0)))))
{ {
if (MEM_P (op0)) enum machine_mode try_mode = mode_for_size (bitsize,
op0 = adjust_bitfield_address (op0, mode1, offset); GET_MODE_CLASS (tmode), 0);
else if (mode1 != GET_MODE (op0)) if (try_mode != BLKmode)
mode1 = try_mode;
}
gcc_assert (mode1 != BLKmode);
/* Extraction of a full MODE1 value can be done with a subreg as long
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)
&& lowpart_bit_field_p (bitnum, bitsize, GET_MODE (op0))
&& bitsize == GET_MODE_BITSIZE (mode1)
&& TRULY_NOOP_TRUNCATION_MODES_P (mode1, GET_MODE (op0)))
{ {
rtx sub = simplify_gen_subreg (mode1, op0, GET_MODE (op0), rtx sub = simplify_gen_subreg (mode1, op0, GET_MODE (op0),
byte_offset); bitnum / BITS_PER_UNIT);
if (sub == NULL) if (sub)
goto no_subreg_mode_swap; return convert_extracted_bit_field (sub, mode, tmode, unsignedp);
op0 = sub;
} }
if (mode1 != mode)
return convert_to_mode (tmode, op0, unsignedp); /* Extraction of a full MODE1 value can be done with a load as long as
return op0; the field is on a byte boundary and is sufficiently aligned. */
if (MEM_P (op0)
&& bitnum % BITS_PER_UNIT == 0
&& bitsize == GET_MODE_BITSIZE (mode1)
&& (!SLOW_UNALIGNED_ACCESS (mode1, MEM_ALIGN (op0))
|| (bitnum % bitsize == 0
&& MEM_ALIGN (op0) % bitsize == 0)))
{
op0 = adjust_bitfield_address (op0, mode1, bitnum / BITS_PER_UNIT);
return convert_extracted_bit_field (op0, mode, tmode, unsignedp);
} }
no_subreg_mode_swap: no_subreg_mode_swap:
/* Handle fields bigger than a word. */ /* Handle fields bigger than a word. */
...@@ -1517,35 +1487,25 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1517,35 +1487,25 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
GET_MODE_BITSIZE (mode) - bitsize, NULL_RTX, 0); GET_MODE_BITSIZE (mode) - bitsize, NULL_RTX, 0);
} }
/* From here on we know the desired field is smaller than a word. */ /* If OP0 is a multi-word register, narrow it to the affected word.
If the region spans two words, defer to extract_split_bit_field. */
/* Check if there is a correspondingly-sized integer field, so we can if (!MEM_P (op0) && GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD)
safely extract it as one size of integer, if necessary; then
truncate or extend to the size that is wanted; then use SUBREGs or
convert_to_mode to get one of the modes we really wanted. */
int_mode = int_mode_for_mode (tmode);
if (int_mode == BLKmode)
int_mode = int_mode_for_mode (mode);
/* Should probably push op0 out to memory and then do a load. */
gcc_assert (int_mode != BLKmode);
/* OFFSET is the number of words or bytes (UNIT says which)
from STR_RTX to the first word or byte containing part of the field. */
if (!MEM_P (op0))
{ {
if (offset != 0 op0 = simplify_gen_subreg (word_mode, op0, GET_MODE (op0),
|| GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) bitnum / BITS_PER_WORD * UNITS_PER_WORD);
bitnum %= BITS_PER_WORD;
if (bitnum + bitsize > BITS_PER_WORD)
{ {
if (!REG_P (op0)) if (!fallback_p)
op0 = copy_to_reg (op0); return NULL_RTX;
op0 = gen_rtx_SUBREG (mode_for_size (BITS_PER_WORD, MODE_INT, 0), target = extract_split_bit_field (op0, bitsize, bitnum, unsignedp);
op0, (offset * UNITS_PER_WORD)); return convert_extracted_bit_field (target, mode, tmode, unsignedp);
} }
offset = 0;
} }
/* Now OFFSET is nonzero only for memory operands. */ /* From here on we know the desired field is smaller than a word.
If OP0 is a register, it too fits within a word. */
ext_mode = mode_for_extraction (unsignedp ? EP_extzv : EP_extv, 0); ext_mode = mode_for_extraction (unsignedp ? EP_extzv : EP_extv, 0);
if (ext_mode != MAX_MACHINE_MODE if (ext_mode != MAX_MACHINE_MODE
&& bitsize > 0 && bitsize > 0
...@@ -1556,30 +1516,34 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1556,30 +1516,34 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
&& flag_strict_volatile_bitfields > 0) && flag_strict_volatile_bitfields > 0)
/* If op0 is a register, we need it in EXT_MODE to make it /* If op0 is a register, we need it in EXT_MODE to make it
acceptable to the format of ext(z)v. */ acceptable to the format of ext(z)v. */
&& !(GET_CODE (op0) == SUBREG && GET_MODE (op0) != ext_mode) && !(GET_CODE (op0) == SUBREG && GET_MODE (op0) != ext_mode))
&& !((REG_P (op0) || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode))))
{ {
struct expand_operand ops[4]; struct expand_operand ops[4];
unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset; unsigned HOST_WIDE_INT bitpos = bitnum;
rtx xop0 = op0; rtx xop0 = op0;
rtx xtarget = target; rtx xtarget = target;
rtx xspec_target = target; rtx xspec_target = target;
rtx xspec_target_subreg = 0; rtx xspec_target_subreg = 0;
unsigned unit = GET_MODE_BITSIZE (ext_mode);
/* If op0 is a register, we need it in EXT_MODE to make it /* If op0 is a register, we need it in EXT_MODE to make it
acceptable to the format of ext(z)v. */ acceptable to the format of ext(z)v. */
if (REG_P (xop0) && GET_MODE (xop0) != ext_mode) if (REG_P (xop0) && GET_MODE (xop0) != ext_mode)
xop0 = gen_lowpart_SUBREG (ext_mode, xop0); xop0 = gen_lowpart_SUBREG (ext_mode, xop0);
if (MEM_P (xop0))
/* Get ref to first byte containing part of the field. */
xop0 = adjust_bitfield_address (xop0, byte_mode, xoffset);
/* Now convert from counting within UNIT to counting in EXT_MODE. */
if (BYTES_BIG_ENDIAN && !MEM_P (xop0))
xbitpos += GET_MODE_BITSIZE (ext_mode) - unit;
unit = GET_MODE_BITSIZE (ext_mode); if (MEM_P (xop0))
{
/* Get a reference to the first byte of the field. */
xop0 = adjust_bitfield_address (xop0, byte_mode,
bitpos / BITS_PER_UNIT);
bitpos %= BITS_PER_UNIT;
}
else
{
/* Convert from counting within OP0 to counting in EXT_MODE. */
if (BYTES_BIG_ENDIAN)
bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
}
/* If BITS_BIG_ENDIAN is zero on a BYTES_BIG_ENDIAN machine, we count /* If BITS_BIG_ENDIAN is zero on a BYTES_BIG_ENDIAN machine, we count
"backwards" from the size of the unit we are extracting from. "backwards" from the size of the unit we are extracting from.
...@@ -1587,7 +1551,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1587,7 +1551,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
BYTES/BITS_BIG_ENDIAN machine. */ BYTES/BITS_BIG_ENDIAN machine. */
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
xbitpos = unit - bitsize - xbitpos; bitpos = unit - bitsize - bitpos;
if (xtarget == 0) if (xtarget == 0)
xtarget = xspec_target = gen_reg_rtx (tmode); xtarget = xspec_target = gen_reg_rtx (tmode);
...@@ -1613,7 +1577,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1613,7 +1577,7 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
create_output_operand (&ops[0], xtarget, ext_mode); create_output_operand (&ops[0], xtarget, ext_mode);
create_fixed_operand (&ops[1], xop0); create_fixed_operand (&ops[1], xop0);
create_integer_operand (&ops[2], bitsize); create_integer_operand (&ops[2], bitsize);
create_integer_operand (&ops[3], xbitpos); create_integer_operand (&ops[3], bitpos);
if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv, if (maybe_expand_insn (unsignedp ? CODE_FOR_extzv : CODE_FOR_extv,
4, ops)) 4, ops))
{ {
...@@ -1652,26 +1616,25 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1652,26 +1616,25 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
&& !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0)) && !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0))) && GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
{ {
unsigned HOST_WIDE_INT xoffset, xbitpos; unsigned HOST_WIDE_INT offset, bitpos;
/* Compute the offset as a multiple of this unit, /* Compute the offset as a multiple of this unit,
counting in bytes. */ counting in bytes. */
unit = GET_MODE_BITSIZE (bestmode); unsigned int unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit; bitpos = bitnum % unit;
/* Make sure the register is big enough for the whole field. */ /* Make sure the register is big enough for the whole field. */
if (xoffset * BITS_PER_UNIT + unit if (bitpos + bitsize <= unit)
>= offset * BITS_PER_UNIT + bitsize)
{ {
rtx last, result, xop0; rtx last, result, xop0;
last = get_last_insn (); last = get_last_insn ();
/* Fetch it to a register in that size. */ /* Fetch it to a register in that size. */
xop0 = adjust_bitfield_address (op0, bestmode, xoffset); xop0 = adjust_bitfield_address (op0, bestmode, offset);
xop0 = force_reg (bestmode, xop0); xop0 = force_reg (bestmode, xop0);
result = extract_bit_field_1 (xop0, bitsize, xbitpos, result = extract_bit_field_1 (xop0, bitsize, bitpos,
unsignedp, packedp, target, unsignedp, packedp, target,
mode, tmode, false); mode, tmode, false);
if (result) if (result)
...@@ -1685,8 +1648,16 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1685,8 +1648,16 @@ extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
if (!fallback_p) if (!fallback_p)
return NULL; return NULL;
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, /* Find a correspondingly-sized integer field, so we can apply
bitpos, target, unsignedp, packedp); shifts and masks to it. */
int_mode = int_mode_for_mode (tmode);
if (int_mode == BLKmode)
int_mode = int_mode_for_mode (mode);
/* 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, packedp);
return convert_extracted_bit_field (target, mode, tmode, unsignedp); return convert_extracted_bit_field (target, mode, tmode, unsignedp);
} }
...@@ -1716,16 +1687,8 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1716,16 +1687,8 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
target, mode, tmode, true); target, mode, tmode, true);
} }
/* Extract a bit field using shifts and boolean operations /* Use shifts and boolean operations to extract a field of BITSIZE bits
Returns an rtx to represent the value. from bit BITNUM of OP0.
OP0 addresses a register (word) or memory (byte).
BITPOS says which bit within the word or byte the bit field starts in.
OFFSET says how many bytes farther the bit field starts;
it is 0 if OP0 is a register.
BITSIZE says how many bits long the bit field is.
(If OP0 is a register, it may be narrower than a full word,
but BITPOS still counts within a full word,
which is significant on bigendian machines.)
UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value).
PACKEDP is true if the field has the packed attribute. PACKEDP is true if the field has the packed attribute.
...@@ -1736,21 +1699,13 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1736,21 +1699,13 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
static rtx static rtx
extract_fixed_bit_field (enum machine_mode tmode, rtx op0, extract_fixed_bit_field (enum machine_mode tmode, rtx op0,
unsigned HOST_WIDE_INT offset,
unsigned HOST_WIDE_INT bitsize, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitpos, rtx target, unsigned HOST_WIDE_INT bitnum, rtx target,
int unsignedp, bool packedp) int unsignedp, bool packedp)
{ {
unsigned int total_bits = BITS_PER_WORD;
enum machine_mode mode; enum machine_mode mode;
if (GET_CODE (op0) == SUBREG || REG_P (op0)) if (MEM_P (op0))
{
/* Special treatment for a bit field split across two registers. */
if (bitsize + bitpos > BITS_PER_WORD)
return extract_split_bit_field (op0, bitsize, bitpos, unsignedp);
}
else
{ {
/* Get the proper mode to use for this field. We want a mode that /* Get the proper mode to use for this field. We want a mode that
includes the entire field. If such a mode would be larger than includes the entire field. If such a mode would be larger than
...@@ -1767,105 +1722,89 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0, ...@@ -1767,105 +1722,89 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0,
mode = tmode; mode = tmode;
} }
else else
mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, 0, 0, mode = get_best_mode (bitsize, bitnum, 0, 0,
MEM_ALIGN (op0), word_mode, MEM_VOLATILE_P (op0)); MEM_ALIGN (op0), word_mode, MEM_VOLATILE_P (op0));
if (mode == VOIDmode) if (mode == VOIDmode)
/* The only way this should occur is if the field spans word /* The only way this should occur is if the field spans word
boundaries. */ boundaries. */
return extract_split_bit_field (op0, bitsize, return extract_split_bit_field (op0, bitsize, bitnum, unsignedp);
bitpos + offset * BITS_PER_UNIT,
unsignedp);
total_bits = GET_MODE_BITSIZE (mode);
/* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to unsigned int total_bits = GET_MODE_BITSIZE (mode);
be in the range 0 to total_bits-1, and put any excess bytes in HOST_WIDE_INT bit_offset = bitnum - bitnum % total_bits;
OFFSET. */
if (bitpos >= total_bits)
{
offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT);
bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT)
* BITS_PER_UNIT);
}
/* If we're accessing a volatile MEM, we can't do the next /* If we're accessing a volatile MEM, we can't apply BIT_OFFSET
alignment step if it results in a multi-word access where we if it results in a multi-word access where we otherwise wouldn't
otherwise wouldn't have one. So, check for that case have one. So, check for that case here. */
here. */
if (MEM_P (op0) if (MEM_P (op0)
&& MEM_VOLATILE_P (op0) && MEM_VOLATILE_P (op0)
&& flag_strict_volatile_bitfields > 0 && flag_strict_volatile_bitfields > 0
&& bitpos + bitsize <= total_bits && bitnum % BITS_PER_UNIT + bitsize <= total_bits
&& bitpos + bitsize + (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT > total_bits) && bitnum % GET_MODE_BITSIZE (mode) + bitsize > total_bits)
{ {
if (STRICT_ALIGNMENT) if (STRICT_ALIGNMENT)
{ {
static bool informed_about_misalignment = false; static bool informed_about_misalignment = false;
bool warned;
if (packedp) if (packedp)
{ {
if (bitsize == total_bits) if (bitsize == total_bits)
warned = warning_at (input_location, OPT_fstrict_volatile_bitfields, warning_at (input_location, OPT_fstrict_volatile_bitfields,
"multiple accesses to volatile structure member" "multiple accesses to volatile structure"
" because of packed attribute"); " member because of packed attribute");
else else
warned = warning_at (input_location, OPT_fstrict_volatile_bitfields, warning_at (input_location, OPT_fstrict_volatile_bitfields,
"multiple accesses to volatile structure bitfield" "multiple accesses to volatile structure"
" because of packed attribute"); " bitfield because of packed attribute");
return extract_split_bit_field (op0, bitsize, return extract_split_bit_field (op0, bitsize, bitnum,
bitpos + offset * BITS_PER_UNIT,
unsignedp); unsignedp);
} }
if (bitsize == total_bits) if (bitsize == total_bits)
warned = warning_at (input_location, OPT_fstrict_volatile_bitfields, warning_at (input_location, OPT_fstrict_volatile_bitfields,
"mis-aligned access used for structure member"); "mis-aligned access used for structure member");
else else
warned = warning_at (input_location, OPT_fstrict_volatile_bitfields, warning_at (input_location, OPT_fstrict_volatile_bitfields,
"mis-aligned access used for structure bitfield"); "mis-aligned access used for structure bitfield");
if (! informed_about_misalignment && warned) if (! informed_about_misalignment)
{ {
informed_about_misalignment = true; informed_about_misalignment = true;
inform (input_location, inform (input_location,
"when a volatile object spans multiple type-sized locations," "when a volatile object spans multiple type-sized"
" the compiler must choose between using a single mis-aligned access to" " locations, the compiler must choose between using"
" preserve the volatility, or using multiple aligned accesses to avoid" " a single mis-aligned access to preserve the"
" runtime faults; this code may fail at runtime if the hardware does" " volatility, or using multiple aligned accesses"
" not allow this access"); " to avoid runtime faults; this code may fail at"
" runtime if the hardware does not allow this"
" access");
} }
} }
bit_offset = bitnum - bitnum % BITS_PER_UNIT;
} }
else op0 = adjust_bitfield_address (op0, mode, bit_offset / BITS_PER_UNIT);
{ bitnum -= bit_offset;
/* Get ref to an aligned byte, halfword, or word containing the field.
Adjust BITPOS to be position within a word,
and OFFSET to be the offset of that word.
Then alter OP0 to refer to that word. */
bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT;
offset -= (offset % (total_bits / BITS_PER_UNIT));
}
op0 = adjust_bitfield_address (op0, mode, offset);
} }
mode = GET_MODE (op0); mode = GET_MODE (op0);
gcc_assert (SCALAR_INT_MODE_P (mode));
/* Note that bitsize + bitnum can be greater than GET_MODE_BITSIZE (mode)
for invalid input, such as extract equivalent of f5 from
gcc.dg/pr48335-2.c. */
if (BYTES_BIG_ENDIAN) if (BYTES_BIG_ENDIAN)
/* BITPOS is the distance between our msb and that of OP0. /* BITNUM is the distance between our msb and that of OP0.
Convert it to the distance from the lsb. */ Convert it to the distance from the lsb. */
bitpos = total_bits - bitsize - bitpos; bitnum = GET_MODE_BITSIZE (mode) - bitsize - bitnum;
/* Now BITPOS is always the distance between the field's lsb and that of OP0. /* 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. */ We have reduced the big-endian case to the little-endian case. */
if (unsignedp) if (unsignedp)
{ {
if (bitpos) if (bitnum)
{ {
/* If the field does not already start at the lsb, /* If the field does not already start at the lsb,
shift it so it does. */ shift it so it does. */
...@@ -1873,7 +1812,7 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0, ...@@ -1873,7 +1812,7 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0,
rtx subtarget = (target != 0 && REG_P (target) ? target : 0); rtx subtarget = (target != 0 && REG_P (target) ? target : 0);
if (tmode != mode) if (tmode != mode)
subtarget = 0; subtarget = 0;
op0 = expand_shift (RSHIFT_EXPR, mode, op0, bitpos, subtarget, 1); op0 = expand_shift (RSHIFT_EXPR, mode, op0, bitnum, subtarget, 1);
} }
/* Convert the value to the desired mode. */ /* Convert the value to the desired mode. */
if (mode != tmode) if (mode != tmode)
...@@ -1882,7 +1821,7 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0, ...@@ -1882,7 +1821,7 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0,
/* Unless the msb of the field used to be the msb when we shifted, /* Unless the msb of the field used to be the msb when we shifted,
mask out the upper bits. */ mask out the upper bits. */
if (GET_MODE_BITSIZE (mode) != bitpos + bitsize) if (GET_MODE_BITSIZE (mode) != bitnum + bitsize)
return expand_binop (GET_MODE (op0), and_optab, op0, return expand_binop (GET_MODE (op0), and_optab, op0,
mask_rtx (GET_MODE (op0), 0, bitsize, 0), mask_rtx (GET_MODE (op0), 0, bitsize, 0),
target, 1, OPTAB_LIB_WIDEN); target, 1, OPTAB_LIB_WIDEN);
...@@ -1897,7 +1836,7 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0, ...@@ -1897,7 +1836,7 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0,
for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode)) mode = GET_MODE_WIDER_MODE (mode))
if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos) if (GET_MODE_BITSIZE (mode) >= bitsize + bitnum)
{ {
op0 = convert_to_mode (mode, op0, 0); op0 = convert_to_mode (mode, op0, 0);
break; break;
...@@ -1906,9 +1845,9 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0, ...@@ -1906,9 +1845,9 @@ extract_fixed_bit_field (enum machine_mode tmode, rtx op0,
if (mode != tmode) if (mode != tmode)
target = 0; target = 0;
if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos)) if (GET_MODE_BITSIZE (mode) != (bitsize + bitnum))
{ {
int amount = GET_MODE_BITSIZE (mode) - (bitsize + bitpos); int amount = GET_MODE_BITSIZE (mode) - (bitsize + bitnum);
/* Maybe propagate the target for the shift. */ /* Maybe propagate the target for the shift. */
rtx subtarget = (target != 0 && REG_P (target) ? target : 0); rtx subtarget = (target != 0 && REG_P (target) ? target : 0);
op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1); op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1);
...@@ -2014,11 +1953,9 @@ extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, ...@@ -2014,11 +1953,9 @@ extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
/* Extract the parts in bit-counting order, /* Extract the parts in bit-counting order,
whose meaning is determined by BYTES_PER_UNIT. whose meaning is determined by BYTES_PER_UNIT.
OFFSET is in UNITs, and UNIT is in bits. OFFSET is in UNITs, and UNIT is in bits. */
extract_fixed_bit_field wants offset in bytes. */ part = extract_fixed_bit_field (word_mode, word, thissize,
part = extract_fixed_bit_field (word_mode, word, offset * unit + thispos, 0, 1, false);
offset * unit / BITS_PER_UNIT,
thissize, thispos, 0, 1, false);
bitsdone += thissize; bitsdone += thissize;
/* Shift this part into place for the result. */ /* Shift this part into place for the result. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment