aboutsummaryrefslogtreecommitdiff
path: root/gcc/expmed.c
diff options
context:
space:
mode:
authorRichard Sandiford <richard@codesourcery.com>2007-07-27 07:39:09 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2007-07-27 07:39:09 +0000
commit6d7db3c5dfd52841132978ce53c041c2f3520562 (patch)
tree8ce86442f4e4bba58a4e2f1291077a1a62140ae8 /gcc/expmed.c
parentab34041dfea64dec723185694ad33af0b8d76f78 (diff)
downloadgcc-6d7db3c5dfd52841132978ce53c041c2f3520562.zip
gcc-6d7db3c5dfd52841132978ce53c041c2f3520562.tar.gz
gcc-6d7db3c5dfd52841132978ce53c041c2f3520562.tar.bz2
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
Diffstat (limited to 'gcc/expmed.c')
-rw-r--r--gcc/expmed.c690
1 files changed, 304 insertions, 386 deletions
diff --git a/gcc/expmed.c b/gcc/expmed.c
index f159e99..4f8c58d 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -327,26 +327,33 @@ mode_for_extraction (enum extraction_pattern pattern, int opno)
return data->operand[opno].mode;
}
-
-/* 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.
- 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. */
+/* Return true if X, of mode MODE, matches the predicate for operand
+ OPNO of instruction ICODE. Allow volatile memories, regardless of
+ the ambient volatile_ok setting. */
-/* ??? Note that there are two different ideas here for how
- to determine the size to count bits within, for a register.
- One is BITS_PER_WORD, and the other is the size of operand 3
- of the insv pattern.
+static bool
+check_predicate_volatile_ok (enum insn_code icode, int opno,
+ rtx x, enum machine_mode mode)
+{
+ bool save_volatile_ok, result;
- If operand 3 of the insv pattern is VOIDmode, then we will use BITS_PER_WORD
- else, we use the mode of operand 3. */
+ save_volatile_ok = volatile_ok;
+ 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
-store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
- unsigned HOST_WIDE_INT bitnum, enum machine_mode fieldmode,
- rtx value)
+ If FALLBACK_P is true, fall back to store_fixed_bit_field if we have
+ no other way of implementing the operation. If FALLBACK_P is false,
+ return false instead. */
+
+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
= (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,
lies completely outside that register. This can occur if the source
code contains an out-of-bounds access to a small array. */
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
available. */
@@ -434,7 +441,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
{
emit_insn (seq);
emit_insn (pat);
- return dest;
+ return true;
}
}
@@ -466,7 +473,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
op0 = simplify_gen_subreg (fieldmode, op0, GET_MODE (op0),
byte_offset);
emit_move_insn (op0, value);
- return value;
+ return true;
}
/* 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,
+ (offset * UNITS_PER_WORD)),
value));
- return value;
+ return true;
}
/* Handle fields bigger than a word. */
@@ -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 nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
unsigned int i;
+ rtx last;
/* 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
@@ -569,6 +577,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
if (fieldmode == VOIDmode)
fieldmode = smallest_mode_for_size (nwords * BITS_PER_WORD, MODE_INT);
+ last = get_last_insn ();
for (i = 0; i < nwords; i++)
{
/* 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,
* BITS_PER_WORD,
0)
: (int) i * BITS_PER_WORD);
+ rtx value_word = operand_subword_force (value, wordnum, fieldmode);
- store_bit_field (op0, MIN (BITS_PER_WORD,
- bitsize - i * BITS_PER_WORD),
- bitnum + bit_offset, word_mode,
- operand_subword_force (value, wordnum, fieldmode));
+ if (!store_bit_field_1 (op0, MIN (BITS_PER_WORD,
+ bitsize - i * BITS_PER_WORD),
+ bitnum + bit_offset, word_mode,
+ 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
@@ -639,74 +653,27 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
&& ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
&& (bitsize + bitpos > GET_MODE_BITSIZE (op_mode)))
&& 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;
rtx value1;
rtx xop0 = op0;
rtx last = get_last_insn ();
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. */
if (MEM_P (xop0))
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. */
if (GET_CODE (xop0) == SUBREG)
/* We can't just change the mode, because this might clobber op0,
and we will need the original value of op0 if insv fails. */
- xop0 = gen_rtx_SUBREG (maxmode, SUBREG_REG (xop0), SUBREG_BYTE (xop0));
- if (REG_P (xop0) && GET_MODE (xop0) != maxmode)
- xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
+ xop0 = gen_rtx_SUBREG (op_mode, SUBREG_REG (xop0), SUBREG_BYTE (xop0));
+ if (REG_P (xop0) && GET_MODE (xop0) != op_mode)
+ xop0 = gen_rtx_SUBREG (op_mode, xop0, 0);
/* On big-endian machines, we count bits from the most significant.
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,
/* We have been counting XBITPOS within UNIT.
Count instead within the size of the register. */
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;
- if (GET_MODE (value) != maxmode)
+ if (GET_MODE (value) != op_mode)
{
if (GET_MODE_BITSIZE (GET_MODE (value)) >= 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 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;
- tmp = simplify_subreg (maxmode, value1, GET_MODE (value), 0);
+ tmp = simplify_subreg (op_mode, value1, GET_MODE (value), 0);
if (! tmp)
- tmp = simplify_gen_subreg (maxmode,
+ tmp = simplify_gen_subreg (op_mode,
force_reg (GET_MODE (value),
value1),
GET_MODE (value), 0);
value1 = tmp;
}
else
- value1 = gen_lowpart (maxmode, value1);
+ value1 = gen_lowpart (op_mode, value1);
}
else if (GET_CODE (value) == CONST_INT)
- value1 = gen_int_mode (INTVAL (value), maxmode);
+ value1 = gen_int_mode (INTVAL (value), op_mode);
else
/* Parse phase is supposed to make VALUE's data 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,
/* If this machine's insv insists on a register,
get VALUE1 into a register. */
if (! ((*insn_data[(int) CODE_FOR_insv].operand[3].predicate)
- (value1, maxmode)))
- value1 = force_reg (maxmode, value1);
+ (value1, op_mode)))
+ value1 = force_reg (op_mode, value1);
pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1);
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
+ 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);
- store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
}
}
- else
- insv_loses:
- /* Insv is not available; store using shifts and boolean ops. */
- store_fixed_bit_field (op0, offset, bitsize, bitpos, value);
- return value;
+
+ if (!fallback_p)
+ return false;
+
+ 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
@@ -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
- 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.
+/* A subroutine of extract_bit_field_1 that converts return value X
+ to either MODE or TMODE. MODE, TMODE and UNSIGNEDP are arguments
+ to extract_bit_field. */
- 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.
+static rtx
+convert_extracted_bit_field (rtx x, enum machine_mode mode,
+ enum machine_mode tmode, bool unsignedp)
+{
+ if (GET_MODE (x) == tmode || GET_MODE (x) == mode)
+ return x;
- TOTAL_SIZE is the size in bytes of the containing structure,
- or -1 if varying.
+ /* If the x 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;
- 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. */
+ smode = mode_for_size (GET_MODE_BITSIZE (tmode), MODE_INT, 0);
+ x = convert_to_mode (smode, x, unsignedp);
+ x = force_reg (smode, x);
+ return gen_lowpart (tmode, x);
+ }
-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 convert_to_mode (tmode, x, unsignedp);
+}
+
+/* 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
= (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD;
unsigned HOST_WIDE_INT offset, bitpos;
rtx op0 = str_rtx;
- rtx spec_target = target;
- rtx spec_target_subreg = 0;
enum machine_mode int_mode;
- enum machine_mode extv_mode = mode_for_extraction (EP_extv, 0);
- enum machine_mode extzv_mode = mode_for_extraction (EP_extzv, 0);
+ enum machine_mode ext_mode;
enum machine_mode mode1;
+ enum insn_code icode;
int byte_offset;
if (tmode == VOIDmode)
@@ -1409,299 +1454,172 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize,
}
/* Now OFFSET is nonzero only for memory operands. */
-
- if (unsignedp)
+ ext_mode = mode_for_extraction (unsignedp ? EP_extzv : EP_extv, 0);
+ 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
- && bitsize > 0
- && GET_MODE_BITSIZE (extzv_mode) >= bitsize
- && ! ((REG_P (op0) || GET_CODE (op0) == SUBREG)
- && (bitsize + bitpos > GET_MODE_BITSIZE (extzv_mode))))
- {
- unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
- rtx bitsize_rtx, bitpos_rtx;
- 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;
- }
+ unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
+ rtx bitsize_rtx, bitpos_rtx;
+ rtx last = get_last_insn ();
+ rtx xop0 = op0;
+ rtx xtarget = target;
+ rtx xspec_target = target;
+ rtx xspec_target_subreg = 0;
+ rtx pat;
- /* If op0 is a register, we need it in MAXMODE (which is usually
- SImode). to make it acceptable to the format of extzv. */
- if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode)
- goto extzv_loses;
- if (REG_P (xop0) && GET_MODE (xop0) != maxmode)
- xop0 = gen_rtx_SUBREG (maxmode, xop0, 0);
+ /* If op0 is a register, we need it in EXT_MODE to make it
+ acceptable to the format of ext(z)v. */
+ if (REG_P (xop0) && GET_MODE (xop0) != ext_mode)
+ xop0 = gen_rtx_SUBREG (ext_mode, xop0, 0);
+ if (MEM_P (xop0))
+ /* 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.
- If the bit field insn does not, we must invert. */
- if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
- xbitpos = unit - bitsize - xbitpos;
+ /* On big-endian machines, we count bits from the most significant.
+ If the bit field insn does not, we must invert. */
+ if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+ xbitpos = unit - bitsize - xbitpos;
- /* Now convert from counting within UNIT to counting in MAXMODE. */
- if (BITS_BIG_ENDIAN && !MEM_P (xop0))
- xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
+ /* Now convert from counting within UNIT to counting in EXT_MODE. */
+ if (BITS_BIG_ENDIAN && !MEM_P (xop0))
+ xbitpos += GET_MODE_BITSIZE (ext_mode) - unit;
- unit = GET_MODE_BITSIZE (maxmode);
+ unit = GET_MODE_BITSIZE (ext_mode);
- if (xtarget == 0)
- xtarget = xspec_target = gen_reg_rtx (tmode);
+ if (xtarget == 0)
+ 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))
- {
- 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);
+ xtarget = gen_lowpart (ext_mode, xtarget);
+ if (GET_MODE_SIZE (ext_mode)
+ > GET_MODE_SIZE (GET_MODE (xspec_target)))
+ xspec_target_subreg = xtarget;
}
+ else
+ xtarget = gen_reg_rtx (ext_mode);
+ }
- /* If this machine's extzv insists on a register target,
- make sure we have one. */
- if (! ((*insn_data[(int) CODE_FOR_extzv].operand[0].predicate)
- (xtarget, maxmode)))
- xtarget = gen_reg_rtx (maxmode);
+ /* If this machine's ext(z)v insists on a register target,
+ make sure we have one. */
+ if (!insn_data[(int) icode].operand[0].predicate (xtarget, ext_mode))
+ xtarget = gen_reg_rtx (ext_mode);
- bitsize_rtx = GEN_INT (bitsize);
- bitpos_rtx = GEN_INT (xbitpos);
+ bitsize_rtx = GEN_INT (bitsize);
+ bitpos_rtx = GEN_INT (xbitpos);
- pat = gen_extzv (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);
- target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
- bitpos, target, 1);
- }
+ pat = (unsignedp
+ ? gen_extzv (xtarget, xop0, bitsize_rtx, bitpos_rtx)
+ : gen_extv (xtarget, xop0, bitsize_rtx, bitpos_rtx));
+ if (pat)
+ {
+ emit_insn (pat);
+ if (xtarget == xspec_target)
+ return xtarget;
+ if (xtarget == xspec_target_subreg)
+ return xspec_target;
+ return convert_extracted_bit_field (xtarget, mode, tmode, unsignedp);
}
- else
- extzv_loses:
- target = extract_fixed_bit_field (int_mode, op0, offset, bitsize,
- bitpos, target, 1);
+ delete_insns_since (last);
}
- 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 the bit field insn does not, we must invert. */
- if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
- xbitpos = unit - bitsize - xbitpos;
-
- /* XBITPOS counts within a size of UNIT.
- Adjust to count within a size of MAXMODE. */
- if (BITS_BIG_ENDIAN && !MEM_P (xop0))
- xbitpos += (GET_MODE_BITSIZE (maxmode) - unit);
+ /* If OP0 is a memory, try copying it to a register and seeing if a
+ cheap register alternative is available. */
+ if (ext_mode != MAX_MACHINE_MODE && 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 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)
- xtarget = xspec_target = gen_reg_rtx (tmode);
+ /* 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;
- 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))
- {
- 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);
- }
+ rtx last, result, xop0;
- /* If this machine's extv insists on a register target,
- make sure we have one. */
- if (! ((*insn_data[(int) CODE_FOR_extv].operand[0].predicate)
- (xtarget, maxmode)))
- xtarget = gen_reg_rtx (maxmode);
+ last = get_last_insn ();
- bitsize_rtx = GEN_INT (bitsize);
- bitpos_rtx = GEN_INT (xbitpos);
+ /* Fetch it to a register in that size. */
+ 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);
- 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);
- }
- return target;
+ if (!fallback_p)
+ return NULL;
+
+ 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