Commit 6d7db3c5 by Richard Sandiford Committed by Richard Sandiford

expr.h (store_bit_field): Don't return a value.

gcc/
	* expr.h (store_bit_field): Don't return a value.
	* expmed.c (check_predicate_volatile_ok): New function.
	(store_bit_field_1): New function, extracted from store_bit_field.
	Take a fallback_p argument and return true if the operation succeeded.
	Only use store_fixed_bit_field if fallback_p.  Don't recompute
	mode_for_extraction; use op_mode instead.  Try forcing memories
	into registers if the insv expander fails.
	(store_bit_field): Use store_bit_field_1 with fallback_p true.
	Don't return a value.
	(convert_extracted_bit_field): New function, extracted from
	store_bit_field.
	(extract_bit_field_1): Likewise.  Take a fallback_p argument
	and return NULL if the operation succeeded.  Only use
	extract_fixed_bit_field if fallback_p.  Only calculate one
	extraction mode.  Combine code for extv and extzv.  Try forcing
	memories into registers if the ext(z)v expander fails.
	(extract_bit_field): Use extract_bit_field_1 with fallback_p true.

gcc/testsuite/
	* gcc.target/mips/ins-1.c: New test.

From-SVN: r126972
parent ab34041d
2007-07-27 Richard Sandiford <richard@codesourcery.com>
* expr.h (store_bit_field): Don't return a value.
* expmed.c (check_predicate_volatile_ok): New function.
(store_bit_field_1): New function, extracted from store_bit_field.
Take a fallback_p argument and return true if the operation succeeded.
Only use store_fixed_bit_field if fallback_p. Don't recompute
mode_for_extraction; use op_mode instead. Try forcing memories
into registers if the insv expander fails.
(store_bit_field): Use store_bit_field_1 with fallback_p true.
Don't return a value.
(convert_extracted_bit_field): New function, extracted from
store_bit_field.
(extract_bit_field_1): Likewise. Take a fallback_p argument
and return NULL if the operation succeeded. Only use
extract_fixed_bit_field if fallback_p. Only calculate one
extraction mode. Combine code for extv and extzv. Try forcing
memories into registers if the ext(z)v expander fails.
(extract_bit_field): Use extract_bit_field_1 with fallback_p true.
2007-07-27 Richard Sandiford <rsandifo@nildram.co.uk> 2007-07-27 Richard Sandiford <rsandifo@nildram.co.uk>
* df.h (df_mw_hardreg): Turn df_ref_type and df_ref_flags * df.h (df_mw_hardreg): Turn df_ref_type and df_ref_flags
......
...@@ -327,26 +327,33 @@ mode_for_extraction (enum extraction_pattern pattern, int opno) ...@@ -327,26 +327,33 @@ mode_for_extraction (enum extraction_pattern pattern, int opno)
return data->operand[opno].mode; return data->operand[opno].mode;
} }
/* Return true if X, of mode MODE, matches the predicate for operand
/* Generate code to store value from rtx VALUE OPNO of instruction ICODE. Allow volatile memories, regardless of
into a bit-field within structure STR_RTX the ambient volatile_ok setting. */
containing BITSIZE bits starting at bit BITNUM.
FIELDMODE is the machine-mode of the FIELD_DECL node for this field.
ALIGN is the alignment that STR_RTX is known to have.
TOTAL_SIZE is the size of the structure in bytes, or -1 if varying. */
/* ??? Note that there are two different ideas here for how static bool
to determine the size to count bits within, for a register. check_predicate_volatile_ok (enum insn_code icode, int opno,
One is BITS_PER_WORD, and the other is the size of operand 3 rtx x, enum machine_mode mode)
of the insv pattern. {
bool save_volatile_ok, result;
If operand 3 of the insv pattern is VOIDmode, then we will use BITS_PER_WORD save_volatile_ok = volatile_ok;
else, we use the mode of operand 3. */ result = insn_data[(int) icode].operand[opno].predicate (x, mode);
volatile_ok = save_volatile_ok;
return result;
}
/* A subroutine of store_bit_field, with the same arguments. Return true
if the operation could be implemented.
rtx If FALLBACK_P is true, fall back to store_fixed_bit_field if we have
store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, no other way of implementing the operation. If FALLBACK_P is false,
unsigned HOST_WIDE_INT bitnum, enum machine_mode fieldmode, return false instead. */
rtx value)
static bool
store_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, enum machine_mode fieldmode,
rtx value, bool fallback_p)
{ {
unsigned int unit unsigned int unit
= (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD; = (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD;
...@@ -390,7 +397,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -390,7 +397,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
lies completely outside that register. This can occur if the source lies completely outside that register. This can occur if the source
code contains an out-of-bounds access to a small array. */ code contains an out-of-bounds access to a small array. */
if (REG_P (op0) && bitnum >= GET_MODE_BITSIZE (GET_MODE (op0))) if (REG_P (op0) && bitnum >= GET_MODE_BITSIZE (GET_MODE (op0)))
return value; return true;
/* Use vec_set patterns for inserting parts of vectors whenever /* Use vec_set patterns for inserting parts of vectors whenever
available. */ available. */
...@@ -434,7 +441,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -434,7 +441,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
{ {
emit_insn (seq); emit_insn (seq);
emit_insn (pat); emit_insn (pat);
return dest; return true;
} }
} }
...@@ -466,7 +473,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -466,7 +473,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
op0 = simplify_gen_subreg (fieldmode, op0, GET_MODE (op0), op0 = simplify_gen_subreg (fieldmode, op0, GET_MODE (op0),
byte_offset); byte_offset);
emit_move_insn (op0, value); emit_move_insn (op0, value);
return value; return true;
} }
/* Make sure we are playing with integral modes. Pun with subregs /* Make sure we are playing with integral modes. Pun with subregs
...@@ -543,7 +550,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -543,7 +550,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
+ (offset * UNITS_PER_WORD)), + (offset * UNITS_PER_WORD)),
value)); value));
return value; return true;
} }
/* Handle fields bigger than a word. */ /* Handle fields bigger than a word. */
...@@ -559,6 +566,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -559,6 +566,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode; unsigned int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; unsigned int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
unsigned int i; unsigned int i;
rtx last;
/* This is the mode we must force value to, so that there will be enough /* This is the mode we must force value to, so that there will be enough
subwords to extract. Note that fieldmode will often (always?) be subwords to extract. Note that fieldmode will often (always?) be
...@@ -569,6 +577,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -569,6 +577,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
if (fieldmode == VOIDmode) if (fieldmode == VOIDmode)
fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT); fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);
last = get_last_insn ();
for (i = 0; i < nwords; i++) for (i = 0; i < nwords; i++)
{ {
/* If I is 0, use the low-order word in both field and target; /* If I is 0, use the low-order word in both field and target;
...@@ -579,13 +588,18 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -579,13 +588,18 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
* BITS_PER_WORD, * BITS_PER_WORD,
0) 0)
: (int) i * BITS_PER_WORD); : (int) i * BITS_PER_WORD);
rtx value_word = operand_subword_force (value, wordnum, fieldmode);
store_bit_field (op0, MIN (BITS_PER_WORD, if (!store_bit_field_1 (op0, MIN (BITS_PER_WORD,
bitsize - i * BITS_PER_WORD), bitsize - i * BITS_PER_WORD),
bitnum + bit_offset, word_mode, bitnum + bit_offset, word_mode,
operand_subword_force (value, wordnum, fieldmode)); value_word, fallback_p))
{
delete_insns_since (last);
return false;
}
} }
return value; return true;
} }
/* From here on we can assume that the field to be stored in is /* From here on we can assume that the field to be stored in is
...@@ -639,74 +653,27 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -639,74 +653,27 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
&& ! ((REG_P (op0) || GET_CODE (op0) == SUBREG) && ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))) && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode)))
&& insn_data[CODE_FOR_insv].operand[1].predicate (GEN_INT (bitsize), && insn_data[CODE_FOR_insv].operand[1].predicate (GEN_INT (bitsize),
VOIDmode)) VOIDmode)
&& check_predicate_volatile_ok (CODE_FOR_insv, 0, op0, VOIDmode))
{ {
int xbitpos = bitpos; int xbitpos = bitpos;
rtx value1; rtx value1;
rtx xop0 = op0; rtx xop0 = op0;
rtx last = get_last_insn (); rtx last = get_last_insn ();
rtx pat; rtx pat;
enum machine_mode maxmode = mode_for_extraction (EP_insv, 3);
int save_volatile_ok = volatile_ok;
volatile_ok = 1;
/* If this machine's insv can only insert into a register, copy OP0
into a register and save it back later. */
if (MEM_P (op0)
&& ! ((*insn_data[(int) CODE_FOR_insv].operand[0].predicate)
(op0, VOIDmode)))
{
rtx tempreg;
enum machine_mode bestmode;
/* Get the mode to use for inserting into this field. If OP0 is
BLKmode, get the smallest mode consistent with the alignment. If
OP0 is a non-BLKmode object that is no wider than MAXMODE, use its
mode. Otherwise, use the smallest mode containing the field. */
if (GET_MODE (op0) == BLKmode
|| GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode))
bestmode
= get_best_mode (bitsize, bitnum, MEM_ALIGN (op0), maxmode,
MEM_VOLATILE_P (op0));
else
bestmode = GET_MODE (op0);
if (bestmode == VOIDmode
|| GET_MODE_SIZE (bestmode) < GET_MODE_SIZE (fieldmode)
|| (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
goto insv_loses;
/* Adjust address to point to the containing unit of that mode.
Compute offset as multiple of this unit, counting in bytes. */
unit = GET_MODE_BITSIZE (bestmode);
offset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
bitpos = bitnum % unit;
op0 = adjust_address (op0, bestmode, offset);
/* Fetch that unit, store the bitfield in it, then store
the unit. */
tempreg = copy_to_reg (op0);
store_bit_field (tempreg, bitsize, bitpos, fieldmode, orig_value);
emit_move_insn (op0, tempreg);
return value;
}
volatile_ok = save_volatile_ok;
/* Add OFFSET into OP0's address. */ /* Add OFFSET into OP0's address. */
if (MEM_P (xop0)) if (MEM_P (xop0))
xop0 = adjust_address (xop0, byte_mode, offset); xop0 = adjust_address (xop0, byte_mode, offset);
/* If xop0 is a register, we need it in MAXMODE /* If xop0 is a register, we need it in OP_MODE
to make it acceptable to the format of insv. */ to make it acceptable to the format of insv. */
if (GET_CODE (xop0) == SUBREG) if (GET_CODE (xop0) == SUBREG)
/* We can't just change the mode, because this might clobber op0, /* We can't just change the mode, because this might clobber op0,
and we will need the original value of op0 if insv fails. */ and we will need the original value of op0 if insv fails. */
xop0 = gen_rtx_SUBREG (maxmode, SUBREG_REG (xop0), SUBREG_BYTE (xop0)); xop0 = gen_rtx_SUBREG (op_mode, SUBREG_REG (xop0), SUBREG_BYTE (xop0));
if (REG_P (xop0) && GET_MODE (xop0) != maxmode) if (REG_P (xop0) && GET_MODE (xop0) != op_mode)
xop0 = gen_rtx_SUBREG (maxmode, xop0, 0); xop0 = gen_rtx_SUBREG (op_mode, xop0, 0);
/* On big-endian machines, we count bits from the most significant. /* On big-endian machines, we count bits from the most significant.
If the bit field insn does not, we must invert. */ If the bit field insn does not, we must invert. */
...@@ -717,13 +684,13 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -717,13 +684,13 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
/* We have been counting XBITPOS within UNIT. /* We have been counting XBITPOS within UNIT.
Count instead within the size of the register. */ Count instead within the size of the register. */
if (BITS_BIG_ENDIAN && !MEM_P (xop0)) if (BITS_BIG_ENDIAN && !MEM_P (xop0))
xbitpos += GET_MODE_BITSIZE (maxmode) - unit; xbitpos += GET_MODE_BITSIZE (op_mode) - unit;
unit = GET_MODE_BITSIZE (maxmode); unit = GET_MODE_BITSIZE (op_mode);
/* Convert VALUE to maxmode (which insv insn wants) in VALUE1. */ /* Convert VALUE to op_mode (which insv insn wants) in VALUE1. */
value1 = value; value1 = value;
if (GET_MODE (value) != maxmode) if (GET_MODE (value) != op_mode)
{ {
if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize) if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize)
{ {
...@@ -731,23 +698,23 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -731,23 +698,23 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
if it has all the bits we will actually use. However, if it has all the bits we will actually use. However,
if we must narrow it, be sure we do it correctly. */ if we must narrow it, be sure we do it correctly. */
if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode)) if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (op_mode))
{ {
rtx tmp; rtx tmp;
tmp = simplify_subreg (maxmode, value1, GET_MODE (value), 0); tmp = simplify_subreg (op_mode, value1, GET_MODE (value), 0);
if (! tmp) if (! tmp)
tmp = simplify_gen_subreg (maxmode, tmp = simplify_gen_subreg (op_mode,
force_reg (GET_MODE (value), force_reg (GET_MODE (value),
value1), value1),
GET_MODE (value), 0); GET_MODE (value), 0);
value1 = tmp; value1 = tmp;
} }
else else
value1 = gen_lowpart (maxmode, value1); value1 = gen_lowpart (op_mode, value1);
} }
else if (GET_CODE (value) == CONST_INT) else if (GET_CODE (value) == CONST_INT)
value1 = gen_int_mode (INTVAL (value), maxmode); value1 = gen_int_mode (INTVAL (value), op_mode);
else else
/* Parse phase is supposed to make VALUE's data type /* Parse phase is supposed to make VALUE's data type
match that of the component reference, which is a type match that of the component reference, which is a type
...@@ -759,23 +726,89 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -759,23 +726,89 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
/* If this machine's insv insists on a register, /* If this machine's insv insists on a register,
get VALUE1 into a register. */ get VALUE1 into a register. */
if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate) if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
(value1, maxmode))) (value1, op_mode)))
value1 = force_reg (maxmode, value1); value1 = force_reg (op_mode, value1);
pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1); pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
if (pat) if (pat)
emit_insn (pat); {
emit_insn (pat);
return true;
}
delete_insns_since (last);
}
/* If OP0 is a memory, try copying it to a register and seeing if a
cheap register alternative is available. */
if (HAVE_insv && MEM_P (op0))
{
enum machine_mode bestmode;
/* Get the mode to use for inserting into this field. If OP0 is
BLKmode, get the smallest mode consistent with the alignment. If
OP0 is a non-BLKmode object that is no wider than OP_MODE, use its
mode. Otherwise, use the smallest mode containing the field. */
if (GET_MODE (op0) == BLKmode
|| (op_mode != MAX_MACHINE_MODE
&& GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (op_mode)))
bestmode = get_best_mode (bitsize, bitnum, MEM_ALIGN (op0),
(op_mode == MAX_MACHINE_MODE
? VOIDmode : op_mode),
MEM_VOLATILE_P (op0));
else else
bestmode = GET_MODE (op0);
if (bestmode != VOIDmode
&& GET_MODE_SIZE (bestmode) >= GET_MODE_SIZE (fieldmode)
&& !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
{ {
rtx last, tempreg, xop0;
unsigned HOST_WIDE_INT xoffset, xbitpos;
last = get_last_insn ();
/* Adjust address to point to the containing unit of
that mode. Compute the offset as a multiple of this unit,
counting in bytes. */
unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit;
xop0 = adjust_address (op0, bestmode, xoffset);
/* Fetch that unit, store the bitfield in it, then store
the unit. */
tempreg = copy_to_reg (xop0);
if (store_bit_field_1 (tempreg, bitsize, xbitpos,
fieldmode, orig_value, false))
{
emit_move_insn (xop0, tempreg);
return true;
}
delete_insns_since (last); delete_insns_since (last);
store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
} }
} }
else
insv_loses: if (!fallback_p)
/* Insv is not available; store using shifts and boolean ops. */ return false;
store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
return value; store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
return true;
}
/* Generate code to store value from rtx VALUE
into a bit-field within structure STR_RTX
containing BITSIZE bits starting at bit BITNUM.
FIELDMODE is the machine-mode of the FIELD_DECL node for this field. */
void
store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, enum machine_mode fieldmode,
rtx value)
{
if (!store_bit_field_1 (str_rtx, bitsize, bitnum, fieldmode, value, true))
gcc_unreachable ();
} }
/* Use shifts and boolean operations to store VALUE /* Use shifts and boolean operations to store VALUE
...@@ -1067,40 +1100,52 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, ...@@ -1067,40 +1100,52 @@ store_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize,
} }
} }
/* Generate code to extract a byte-field from STR_RTX /* A subroutine of extract_bit_field_1 that converts return value X
containing BITSIZE bits, starting at BITNUM, to either MODE or TMODE. MODE, TMODE and UNSIGNEDP are arguments
and put it in TARGET if possible (if TARGET is nonzero). to extract_bit_field. */
Regardless of TARGET, we return the rtx for where the value is placed.
STR_RTX is the structure containing the byte (a REG or MEM). static rtx
UNSIGNEDP is nonzero if this is an unsigned bit field. convert_extracted_bit_field (rtx x, enum machine_mode mode,
MODE is the natural mode of the field value once extracted. enum machine_mode tmode, bool unsignedp)
TMODE is the mode the caller would like the value to have; {
but the value may be returned with type MODE instead. if (GET_MODE (x) == tmode || GET_MODE (x) == mode)
return x;
TOTAL_SIZE is the size in bytes of the containing structure, /* If the x mode is not a scalar integral, first convert to the
or -1 if varying. integer mode of that size and then access it as a floating-point
value via a SUBREG. */
if (!SCALAR_INT_MODE_P (tmode))
{
enum machine_mode smode;
If a TARGET is specified and we can store in it at no extra cost, smode = mode_for_size (GET_MODE_BITSIZE (tmode), MODE_INT, 0);
we do so, and return TARGET. x = convert_to_mode (smode, x, unsignedp);
Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred x = force_reg (smode, x);
if they are equally easy. */ return gen_lowpart (tmode, x);
}
rtx return convert_to_mode (tmode, x, unsignedp);
extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, }
unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target,
enum machine_mode mode, enum machine_mode tmode) /* A subroutine of extract_bit_field, with the same arguments.
If FALLBACK_P is true, fall back to extract_fixed_bit_field
if we can find no other means of implementing the operation.
if FALLBACK_P is false, return NULL instead. */
static rtx
extract_bit_field_1 (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target,
enum machine_mode mode, enum machine_mode tmode,
bool fallback_p)
{ {
unsigned int unit unsigned int unit
= (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD; = (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD;
unsigned HOST_WIDE_INT offset, bitpos; unsigned HOST_WIDE_INT offset, bitpos;
rtx op0 = str_rtx; rtx op0 = str_rtx;
rtx spec_target = target;
rtx spec_target_subreg = 0;
enum machine_mode int_mode; enum machine_mode int_mode;
enum machine_mode extv_mode = mode_for_extraction (EP_extv, 0); enum machine_mode ext_mode;
enum machine_mode extzv_mode = mode_for_extraction (EP_extzv, 0);
enum machine_mode mode1; enum machine_mode mode1;
enum insn_code icode;
int byte_offset; int byte_offset;
if (tmode == VOIDmode) if (tmode == VOIDmode)
...@@ -1409,299 +1454,172 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, ...@@ -1409,299 +1454,172 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
} }
/* Now OFFSET is nonzero only for memory operands. */ /* Now OFFSET is nonzero only for memory operands. */
ext_mode = mode_for_extraction (unsignedp ? EP_extzv : EP_extv, 0);
if (unsignedp) icode = unsignedp ? CODE_FOR_extzv : CODE_FOR_extv;
if (ext_mode != MAX_MACHINE_MODE
&& bitsize > 0
&& GET_MODE_BITSIZE (ext_mode) >= bitsize
/* If op0 is a register, we need it in EXT_MODE to make it
acceptable to the format of ext(z)v. */
&& !(GET_CODE (op0) == SUBREG && GET_MODE (op0) != ext_mode)
&& !((REG_P (op0) || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (ext_mode)))
&& check_predicate_volatile_ok (icode, 1, op0, GET_MODE (op0)))
{ {
if (HAVE_extzv unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
&& bitsize > 0 rtx bitsize_rtx, bitpos_rtx;
&& GET_MODE_BITSIZE (extzv_mode) >= bitsize rtx last = get_last_insn ();
&& ! ((REG_P (op0) || GET_CODE (op0) == SUBREG) rtx xop0 = op0;
&& (bitsize + bitpos > GET_MODE_BITSIZE (extzv_mode)))) rtx xtarget = target;
{ rtx xspec_target = target;
unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset; rtx xspec_target_subreg = 0;
rtx bitsize_rtx, bitpos_rtx; rtx pat;
rtx last = get_last_insn ();
rtx xop0 = op0;
rtx xtarget = target;
rtx xspec_target = spec_target;
rtx xspec_target_subreg = spec_target_subreg;
rtx pat;
enum machine_mode maxmode = mode_for_extraction (EP_extzv, 0);
if (MEM_P (xop0))
{
int save_volatile_ok = volatile_ok;
volatile_ok = 1;
/* Is the memory operand acceptable? */
if (! ((*insn_data[(int) CODE_FOR_extzv].operand[1].predicate)
(xop0, GET_MODE (xop0))))
{
/* No, load into a reg and extract from there. */
enum machine_mode bestmode;
/* Get the mode to use for inserting into this field. If
OP0 is BLKmode, get the smallest mode consistent with the
alignment. If OP0 is a non-BLKmode object that is no
wider than MAXMODE, use its mode. Otherwise, use the
smallest mode containing the field. */
if (GET_MODE (xop0) == BLKmode
|| (GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (maxmode)))
bestmode = get_best_mode (bitsize, bitnum,
MEM_ALIGN (xop0), maxmode,
MEM_VOLATILE_P (xop0));
else
bestmode = GET_MODE (xop0);
if (bestmode == VOIDmode
|| (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (xop0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (xop0)))
goto extzv_loses;
/* Compute offset as multiple of this unit,
counting in bytes. */
unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit;
xop0 = adjust_address (xop0, bestmode, xoffset);
/* Make sure register is big enough for the whole field. */
if (xoffset * BITS_PER_UNIT + unit
< offset * BITS_PER_UNIT + bitsize)
goto extzv_loses;
/* Fetch it to a register in that size. */
xop0 = force_reg (bestmode, xop0);
/* XBITPOS counts within UNIT, which is what is expected. */
}
else
/* Get ref to first byte containing part of the field. */
xop0 = adjust_address (xop0, byte_mode, xoffset);
volatile_ok = save_volatile_ok;
}
/* If op0 is a register, we need it in MAXMODE (which is usually /* If op0 is a register, we need it in EXT_MODE to make it
SImode). to make it acceptable to the format of extzv. */ acceptable to the format of ext(z)v. */
if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) if (REG_P (xop0) && GET_MODE (xop0) != ext_mode)
goto extzv_loses; xop0 = gen_rtx_SUBREG (ext_mode, xop0, 0);
if (REG_P (xop0) && GET_MODE (xop0) != maxmode) if (MEM_P (xop0))
xop0 = gen_rtx_SUBREG (maxmode, xop0, 0); /* Get ref to first byte containing part of the field. */
xop0 = adjust_address (xop0, byte_mode, xoffset);
/* On big-endian machines, we count bits from the most significant. /* On big-endian machines, we count bits from the most significant.
If the bit field insn does not, we must invert. */ If the bit field insn does not, we must invert. */
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
xbitpos = unit - bitsize - xbitpos; xbitpos = unit - bitsize - xbitpos;
/* Now convert from counting within UNIT to counting in MAXMODE. */ /* Now convert from counting within UNIT to counting in EXT_MODE. */
if (BITS_BIG_ENDIAN && !MEM_P (xop0)) if (BITS_BIG_ENDIAN && !MEM_P (xop0))
xbitpos += GET_MODE_BITSIZE (maxmode) - unit; xbitpos += GET_MODE_BITSIZE (ext_mode) - unit;
unit = GET_MODE_BITSIZE (maxmode); unit = GET_MODE_BITSIZE (ext_mode);
if (xtarget == 0) if (xtarget == 0)
xtarget = xspec_target = gen_reg_rtx (tmode); xtarget = xspec_target = gen_reg_rtx (tmode);
if (GET_MODE (xtarget) != maxmode) if (GET_MODE (xtarget) != ext_mode)
{
if (REG_P (xtarget))
{ {
if (REG_P (xtarget)) xtarget = gen_lowpart (ext_mode, xtarget);
{ if (GET_MODE_SIZE (ext_mode)
int wider = (GET_MODE_SIZE (maxmode) > GET_MODE_SIZE (GET_MODE (xspec_target)))
> GET_MODE_SIZE (GET_MODE (xtarget))); xspec_target_subreg = xtarget;
xtarget = gen_lowpart (maxmode, xtarget);
if (wider)
xspec_target_subreg = xtarget;
}
else
xtarget = gen_reg_rtx (maxmode);
} }
else
xtarget = gen_reg_rtx (ext_mode);
}
/* If this machine's extzv insists on a register target, /* If this machine's ext(z)v insists on a register target,
make sure we have one. */ make sure we have one. */
if (! ((*insn_data[(int) CODE_FOR_extzv].operand[0].predicate) if (!insn_data[(int) icode].operand[0].predicate (xtarget, ext_mode))
(xtarget, maxmode))) xtarget = gen_reg_rtx (ext_mode);
xtarget = gen_reg_rtx (maxmode);
bitsize_rtx = GEN_INT (bitsize); bitsize_rtx = GEN_INT (bitsize);
bitpos_rtx = GEN_INT (xbitpos); bitpos_rtx = GEN_INT (xbitpos);
pat = gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx); pat = (unsignedp
if (pat) ? gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx)
{ : gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx));
emit_insn (pat); if (pat)
target = xtarget; {
spec_target = xspec_target; emit_insn (pat);
spec_target_subreg = xspec_target_subreg; if (xtarget == xspec_target)
} return xtarget;
else if (xtarget == xspec_target_subreg)
{ return xspec_target;
delete_insns_since (last); return convert_extracted_bit_field (xtarget, mode, tmode, unsignedp);
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, 1);
}
} }
else delete_insns_since (last);
extzv_loses:
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, 1);
} }
else
{
if (HAVE_extv
&& bitsize > 0
&& GET_MODE_BITSIZE (extv_mode) >= bitsize
&& ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (extv_mode))))
{
int xbitpos = bitpos, xoffset = offset;
rtx bitsize_rtx, bitpos_rtx;
rtx last = get_last_insn ();
rtx xop0 = op0, xtarget = target;
rtx xspec_target = spec_target;
rtx xspec_target_subreg = spec_target_subreg;
rtx pat;
enum machine_mode maxmode = mode_for_extraction (EP_extv, 0);
if (MEM_P (xop0))
{
/* Is the memory operand acceptable? */
if (! ((*insn_data[(int) CODE_FOR_extv].operand[1].predicate)
(xop0, GET_MODE (xop0))))
{
/* No, load into a reg and extract from there. */
enum machine_mode bestmode;
/* Get the mode to use for inserting into this field. If
OP0 is BLKmode, get the smallest mode consistent with the
alignment. If OP0 is a non-BLKmode object that is no
wider than MAXMODE, use its mode. Otherwise, use the
smallest mode containing the field. */
if (GET_MODE (xop0) == BLKmode
|| (GET_MODE_SIZE (GET_MODE (op0))
> GET_MODE_SIZE (maxmode)))
bestmode = get_best_mode (bitsize, bitnum,
MEM_ALIGN (xop0), maxmode,
MEM_VOLATILE_P (xop0));
else
bestmode = GET_MODE (xop0);
if (bestmode == VOIDmode
|| (SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (xop0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (xop0)))
goto extv_loses;
/* Compute offset as multiple of this unit,
counting in bytes. */
unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit;
xop0 = adjust_address (xop0, bestmode, xoffset);
/* Make sure register is big enough for the whole field. */
if (xoffset * BITS_PER_UNIT + unit
< offset * BITS_PER_UNIT + bitsize)
goto extv_loses;
/* Fetch it to a register in that size. */
xop0 = force_reg (bestmode, xop0);
/* XBITPOS counts within UNIT, which is what is expected. */
}
else
/* Get ref to first byte containing part of the field. */
xop0 = adjust_address (xop0, byte_mode, xoffset);
}
/* If op0 is a register, we need it in MAXMODE (which is usually
SImode) to make it acceptable to the format of extv. */
if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
goto extv_loses;
if (REG_P (xop0) && GET_MODE (xop0) != maxmode)
xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
/* On big-endian machines, we count bits from the most significant. /* If OP0 is a memory, try copying it to a register and seeing if a
If the bit field insn does not, we must invert. */ cheap register alternative is available. */
if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) if (ext_mode != MAX_MACHINE_MODE && MEM_P (op0))
xbitpos = unit - bitsize - xbitpos; {
enum machine_mode bestmode;
/* XBITPOS counts within a size of UNIT.
Adjust to count within a size of MAXMODE. */ /* Get the mode to use for inserting into this field. If
if (BITS_BIG_ENDIAN && !MEM_P (xop0)) OP0 is BLKmode, get the smallest mode consistent with the
xbitpos += (GET_MODE_BITSIZE (maxmode) - unit); alignment. If OP0 is a non-BLKmode object that is no
wider than EXT_MODE, use its mode. Otherwise, use the
smallest mode containing the field. */
if (GET_MODE (op0) == BLKmode
|| (ext_mode != MAX_MACHINE_MODE
&& GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (ext_mode)))
bestmode = get_best_mode (bitsize, bitnum, MEM_ALIGN (op0),
(ext_mode == MAX_MACHINE_MODE
? VOIDmode : ext_mode),
MEM_VOLATILE_P (op0));
else
bestmode = GET_MODE (op0);
unit = GET_MODE_BITSIZE (maxmode); if (bestmode != VOIDmode
&& !(SLOW_UNALIGNED_ACCESS (bestmode, MEM_ALIGN (op0))
&& GET_MODE_BITSIZE (bestmode) > MEM_ALIGN (op0)))
{
unsigned HOST_WIDE_INT xoffset, xbitpos;
if (xtarget == 0) /* Compute the offset as a multiple of this unit,
xtarget = xspec_target = gen_reg_rtx (tmode); counting in bytes. */
unit = GET_MODE_BITSIZE (bestmode);
xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode);
xbitpos = bitnum % unit;
if (GET_MODE (xtarget) != maxmode) /* Make sure the register is big enough for the whole field. */
if (xoffset * BITS_PER_UNIT + unit
>= offset * BITS_PER_UNIT + bitsize)
{ {
if (REG_P (xtarget)) rtx last, result, xop0;
{
int wider = (GET_MODE_SIZE (maxmode)
> GET_MODE_SIZE (GET_MODE (xtarget)));
xtarget = gen_lowpart (maxmode, xtarget);
if (wider)
xspec_target_subreg = xtarget;
}
else
xtarget = gen_reg_rtx (maxmode);
}
/* If this machine's extv insists on a register target, last = get_last_insn ();
make sure we have one. */
if (! ((*insn_data[(int) CODE_FOR_extv].operand[0].predicate)
(xtarget, maxmode)))
xtarget = gen_reg_rtx (maxmode);
bitsize_rtx = GEN_INT (bitsize); /* Fetch it to a register in that size. */
bitpos_rtx = GEN_INT (xbitpos); xop0 = adjust_address (op0, bestmode, xoffset);
xop0 = force_reg (bestmode, xop0);
result = extract_bit_field_1 (xop0, bitsize, xbitpos,
unsignedp, target,
mode, tmode, false);
if (result)
return result;
pat = gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx);
if (pat)
{
emit_insn (pat);
target = xtarget;
spec_target = xspec_target;
spec_target_subreg = xspec_target_subreg;
}
else
{
delete_insns_since (last); delete_insns_since (last);
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, 0);
} }
} }
else
extv_loses:
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, 0);
} }
if (target == spec_target)
return target;
if (target == spec_target_subreg)
return spec_target;
if (GET_MODE (target) != tmode && GET_MODE (target) != mode)
{
/* If the target mode is not a scalar integral, first convert to the
integer mode of that size and then access it as a floating-point
value via a SUBREG. */
if (!SCALAR_INT_MODE_P (tmode))
{
enum machine_mode smode
= mode_for_size (GET_MODE_BITSIZE (tmode), MODE_INT, 0);
target = convert_to_mode (smode, target, unsignedp);
target = force_reg (smode, target);
return gen_lowpart (tmode, target);
}
return convert_to_mode (tmode, target, unsignedp); if (!fallback_p)
} return NULL;
return target;
target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
bitpos, target, unsignedp);
return convert_extracted_bit_field (target, mode, tmode, unsignedp);
}
/* Generate code to extract a byte-field from STR_RTX
containing BITSIZE bits, starting at BITNUM,
and put it in TARGET if possible (if TARGET is nonzero).
Regardless of TARGET, we return the rtx for where the value is placed.
STR_RTX is the structure containing the byte (a REG or MEM).
UNSIGNEDP is nonzero if this is an unsigned bit field.
MODE is the natural mode of the field value once extracted.
TMODE is the mode the caller would like the value to have;
but the value may be returned with type MODE instead.
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
if they are equally easy. */
rtx
extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
unsigned HOST_WIDE_INT bitnum, int unsignedp, rtx target,
enum machine_mode mode, enum machine_mode tmode)
{
return extract_bit_field_1 (str_rtx, bitsize, bitnum, unsignedp,
target, mode, tmode, true);
} }
/* Extract a bit field using shifts and boolean operations /* Extract a bit field using shifts and boolean operations
......
...@@ -731,8 +731,8 @@ enum extraction_pattern { EP_insv, EP_extv, EP_extzv }; ...@@ -731,8 +731,8 @@ enum extraction_pattern { EP_insv, EP_extv, EP_extzv };
extern enum machine_mode extern enum machine_mode
mode_for_extraction (enum extraction_pattern, int); mode_for_extraction (enum extraction_pattern, int);
extern rtx store_bit_field (rtx, unsigned HOST_WIDE_INT, extern void store_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, enum machine_mode, rtx); unsigned HOST_WIDE_INT, enum machine_mode, rtx);
extern rtx extract_bit_field (rtx, unsigned HOST_WIDE_INT, extern rtx extract_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int, rtx, unsigned HOST_WIDE_INT, int, rtx,
enum machine_mode, enum machine_mode); enum machine_mode, enum machine_mode);
......
2007-07-27 Richard Sandiford <richard@codesourcery.com>
* gcc.target/mips/ins-1.c: New test.
2007-07-26 Nathan Froyd <froydnj@codesourcery.com> 2007-07-26 Nathan Froyd <froydnj@codesourcery.com>
PR/19232 PR/19232
/* { dg-do compile } */
/* { dg-mips-options "-O -march=mips32r2 -mno-mips16" } */
/* { dg-final { scan-assembler "\tins\t" } } */
struct
{
unsigned int i : 2;
unsigned int j : 3;
unsigned int k : 4;
} s;
void
foo (void)
{
s.j = 1;
}
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