From 6d7db3c5dfd52841132978ce53c041c2f3520562 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Fri, 27 Jul 2007 07:39:09 +0000 Subject: [PATCH] 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 --- gcc/ChangeLog | 20 + gcc/expmed.c | 690 +++++++++++++++------------------- gcc/expr.h | 4 +- gcc/testsuite/ChangeLog | 4 + gcc/testsuite/gcc.target/mips/ins-1.c | 16 + 5 files changed, 346 insertions(+), 388 deletions(-) create mode 100644 gcc/testsuite/gcc.target/mips/ins-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index db34fcd8224..8b2c4526180 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2007-07-27 Richard Sandiford + + * 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 * df.h (df_mw_hardreg): Turn df_ref_type and df_ref_flags diff --git a/gcc/expmed.c b/gcc/expmed.c index f159e996258..4f8c58d0f4d 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 diff --git a/gcc/expr.h b/gcc/expr.h index cc69c58321c..64604d75f95 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -731,8 +731,8 @@ enum extraction_pattern { EP_insv, EP_extv, EP_extzv }; extern enum machine_mode mode_for_extraction (enum extraction_pattern, int); -extern rtx store_bit_field (rtx, unsigned HOST_WIDE_INT, - unsigned HOST_WIDE_INT, enum machine_mode, rtx); +extern void store_bit_field (rtx, unsigned HOST_WIDE_INT, + unsigned HOST_WIDE_INT, enum machine_mode, rtx); extern rtx extract_bit_field (rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, int, rtx, enum machine_mode, enum machine_mode); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 16e0a7e1ea5..50f12361f08 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2007-07-27 Richard Sandiford + + * gcc.target/mips/ins-1.c: New test. + 2007-07-26 Nathan Froyd PR/19232 diff --git a/gcc/testsuite/gcc.target/mips/ins-1.c b/gcc/testsuite/gcc.target/mips/ins-1.c new file mode 100644 index 00000000000..abce7d55396 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/ins-1.c @@ -0,0 +1,16 @@ +/* { 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; +} -- 2.11.4.GIT