X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fexpmed.c;h=5268b318f8dc6537cc91c150aec70a356cef1095;hb=0f4d0c21b0d4036be52bd17a0a5014ee099f181c;hp=4008c08d2230a2dc44cc00398d5f6feef8eb5bdb;hpb=595d88b592402d3f629fa77662f81ea572a8a1bf;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/expmed.c b/gcc/expmed.c index 4008c08d223..5268b318f8d 100644 --- a/gcc/expmed.c +++ b/gcc/expmed.c @@ -1,14 +1,14 @@ /* Medium-level subroutines: convert bit-field store and extract and shifts, multiplies and divides to rtl instructions. Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -17,9 +17,8 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" @@ -37,6 +36,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "real.h" #include "recog.h" #include "langhooks.h" +#include "df.h" +#include "target.h" static void store_fixed_bit_field (rtx, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT, @@ -149,7 +150,7 @@ init_expmed (void) PUT_CODE (&all.reg, REG); /* Avoid using hard regs in ways which may be unsupported. */ - REGNO (&all.reg) = LAST_VIRTUAL_REGISTER + 1; + SET_REGNO (&all.reg, LAST_VIRTUAL_REGISTER + 1); PUT_CODE (&all.plus, PLUS); XEXP (&all.plus, 0) = &all.reg; @@ -326,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; + + 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. - If operand 3 of the insv pattern is VOIDmode, then we will use BITS_PER_WORD - else, we use the mode of operand 3. */ + 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. */ -rtx -store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, - unsigned HOST_WIDE_INT bitnum, enum machine_mode fieldmode, - 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 = (MEM_P (str_rtx)) ? BITS_PER_UNIT : BITS_PER_WORD; @@ -389,13 +397,13 @@ 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. */ if (VECTOR_MODE_P (GET_MODE (op0)) && !MEM_P (op0) - && (vec_set_optab->handlers[GET_MODE (op0)].insn_code + && (optab_handler (vec_set_optab, GET_MODE (op0))->insn_code != CODE_FOR_nothing) && fieldmode == GET_MODE_INNER (GET_MODE (op0)) && bitsize == GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0))) @@ -403,7 +411,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, { enum machine_mode outermode = GET_MODE (op0); enum machine_mode innermode = GET_MODE_INNER (outermode); - int icode = (int) vec_set_optab->handlers[outermode].insn_code; + int icode = (int) optab_handler (vec_set_optab, outermode)->insn_code; int pos = bitnum / GET_MODE_BITSIZE (innermode); rtx rtxpos = GEN_INT (pos); rtx src = value; @@ -433,7 +441,7 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, { emit_insn (seq); emit_insn (pat); - return dest; + return true; } } @@ -465,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 @@ -509,10 +517,10 @@ store_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, if (!MEM_P (op0) && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0) && bitsize == GET_MODE_BITSIZE (fieldmode) - && (movstrict_optab->handlers[fieldmode].insn_code + && (optab_handler (movstrict_optab, fieldmode)->insn_code != CODE_FOR_nothing)) { - int icode = movstrict_optab->handlers[fieldmode].insn_code; + int icode = optab_handler (movstrict_optab, fieldmode)->insn_code; /* Get appropriate low part of the value being stored. */ if (GET_CODE (value) == CONST_INT || REG_P (value)) @@ -542,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. */ @@ -558,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 @@ -568,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; @@ -578,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 @@ -638,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. */ @@ -716,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) { @@ -730,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 @@ -758,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 @@ -942,7 +976,10 @@ store_fixed_bit_field (rtx op0, unsigned HOST_WIDE_INT offset, } if (op0 != temp) - emit_move_insn (op0, temp); + { + op0 = copy_rtx (op0); + emit_move_insn (op0, temp); + } } /* Store a bit field that is split across multiple accessible memory objects. @@ -1066,40 +1103,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) @@ -1126,18 +1175,48 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, return op0; } + /* See if we can get a better vector mode before extracting. */ + if (VECTOR_MODE_P (GET_MODE (op0)) + && !MEM_P (op0) + && GET_MODE_INNER (GET_MODE (op0)) != tmode) + { + enum machine_mode new_mode; + int nunits = GET_MODE_NUNITS (GET_MODE (op0)); + + if (GET_MODE_CLASS (tmode) == MODE_FLOAT) + new_mode = MIN_MODE_VECTOR_FLOAT; + else if (GET_MODE_CLASS (tmode) == MODE_FRACT) + new_mode = MIN_MODE_VECTOR_FRACT; + else if (GET_MODE_CLASS (tmode) == MODE_UFRACT) + new_mode = MIN_MODE_VECTOR_UFRACT; + else if (GET_MODE_CLASS (tmode) == MODE_ACCUM) + new_mode = MIN_MODE_VECTOR_ACCUM; + else if (GET_MODE_CLASS (tmode) == MODE_UACCUM) + new_mode = MIN_MODE_VECTOR_UACCUM; + else + new_mode = MIN_MODE_VECTOR_INT; + + for (; new_mode != VOIDmode ; new_mode = GET_MODE_WIDER_MODE (new_mode)) + if (GET_MODE_NUNITS (new_mode) == nunits + && GET_MODE_INNER (new_mode) == tmode + && targetm.vector_mode_supported_p (new_mode)) + break; + if (new_mode != VOIDmode) + op0 = gen_lowpart (new_mode, op0); + } + /* Use vec_extract patterns for extracting parts of vectors whenever available. */ if (VECTOR_MODE_P (GET_MODE (op0)) && !MEM_P (op0) - && (vec_extract_optab->handlers[GET_MODE (op0)].insn_code + && (optab_handler (vec_extract_optab, GET_MODE (op0))->insn_code != CODE_FOR_nothing) && ((bitnum + bitsize - 1) / GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0))) == bitnum / GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (op0))))) { enum machine_mode outermode = GET_MODE (op0); enum machine_mode innermode = GET_MODE_INNER (outermode); - int icode = (int) vec_extract_optab->handlers[outermode].insn_code; + int icode = (int) optab_handler (vec_extract_optab, outermode)->insn_code; unsigned HOST_WIDE_INT pos = bitnum / GET_MODE_BITSIZE (innermode); rtx rtxpos = GEN_INT (pos); rtx src = op0; @@ -1176,6 +1255,8 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, { emit_insn (seq); emit_insn (pat); + if (mode0 != mode) + return gen_lowpart (tmode, dest); return dest; } } @@ -1261,18 +1342,15 @@ extract_bit_field (rtx str_rtx, unsigned HOST_WIDE_INT bitsize, || (offset * BITS_PER_UNIT % bitsize == 0 && MEM_ALIGN (op0) % bitsize == 0))))) { - if (mode1 != GET_MODE (op0)) + if (MEM_P (op0)) + op0 = adjust_address (op0, mode1, offset); + else if (mode1 != GET_MODE (op0)) { - if (MEM_P (op0)) - op0 = adjust_address (op0, mode1, offset); - else - { - rtx sub = simplify_gen_subreg (mode1, op0, GET_MODE (op0), - byte_offset); - if (sub == NULL) - goto no_subreg_mode_swap; - op0 = sub; - } + rtx sub = simplify_gen_subreg (mode1, op0, GET_MODE (op0), + byte_offset); + if (sub == NULL) + goto no_subreg_mode_swap; + op0 = sub; } if (mode1 != mode) return convert_to_mode (tmode, op0, unsignedp); @@ -1384,299 +1462,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 @@ -1887,152 +1838,6 @@ lshift_value (enum machine_mode mode, rtx value, int bitpos, int bitsize) return immed_double_const (low, high, mode); } -/* Extract a bit field from a memory by forcing the alignment of the - memory. This efficient only if the field spans at least 4 boundaries. - - OP0 is the MEM. - BITSIZE is the field width; BITPOS is the position of the first bit. - UNSIGNEDP is true if the result should be zero-extended. */ - -static rtx -extract_force_align_mem_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, - unsigned HOST_WIDE_INT bitpos, - int unsignedp) -{ - enum machine_mode mode, dmode; - unsigned int m_bitsize, m_size; - unsigned int sign_shift_up, sign_shift_dn; - rtx base, a1, a2, v1, v2, comb, shift, result, start; - - /* Choose a mode that will fit BITSIZE. */ - mode = smallest_mode_for_size (bitsize, MODE_INT); - m_size = GET_MODE_SIZE (mode); - m_bitsize = GET_MODE_BITSIZE (mode); - - /* Choose a mode twice as wide. Fail if no such mode exists. */ - dmode = mode_for_size (m_bitsize * 2, MODE_INT, false); - if (dmode == BLKmode) - return NULL; - - do_pending_stack_adjust (); - start = get_last_insn (); - - /* At the end, we'll need an additional shift to deal with sign/zero - extension. By default this will be a left+right shift of the - appropriate size. But we may be able to eliminate one of them. */ - sign_shift_up = sign_shift_dn = m_bitsize - bitsize; - - if (STRICT_ALIGNMENT) - { - base = plus_constant (XEXP (op0, 0), bitpos / BITS_PER_UNIT); - bitpos %= BITS_PER_UNIT; - - /* We load two values to be concatenate. There's an edge condition - that bears notice -- an aligned value at the end of a page can - only load one value lest we segfault. So the two values we load - are at "base & -size" and "(base + size - 1) & -size". If base - is unaligned, the addresses will be aligned and sequential; if - base is aligned, the addresses will both be equal to base. */ - - a1 = expand_simple_binop (Pmode, AND, force_operand (base, NULL), - GEN_INT (-(HOST_WIDE_INT)m_size), - NULL, true, OPTAB_LIB_WIDEN); - mark_reg_pointer (a1, m_bitsize); - v1 = gen_rtx_MEM (mode, a1); - set_mem_align (v1, m_bitsize); - v1 = force_reg (mode, validize_mem (v1)); - - a2 = plus_constant (base, GET_MODE_SIZE (mode) - 1); - a2 = expand_simple_binop (Pmode, AND, force_operand (a2, NULL), - GEN_INT (-(HOST_WIDE_INT)m_size), - NULL, true, OPTAB_LIB_WIDEN); - v2 = gen_rtx_MEM (mode, a2); - set_mem_align (v2, m_bitsize); - v2 = force_reg (mode, validize_mem (v2)); - - /* Combine these two values into a double-word value. */ - if (m_bitsize == BITS_PER_WORD) - { - comb = gen_reg_rtx (dmode); - emit_insn (gen_rtx_CLOBBER (VOIDmode, comb)); - emit_move_insn (gen_rtx_SUBREG (mode, comb, 0), v1); - emit_move_insn (gen_rtx_SUBREG (mode, comb, m_size), v2); - } - else - { - if (BYTES_BIG_ENDIAN) - comb = v1, v1 = v2, v2 = comb; - v1 = convert_modes (dmode, mode, v1, true); - if (v1 == NULL) - goto fail; - v2 = convert_modes (dmode, mode, v2, true); - v2 = expand_simple_binop (dmode, ASHIFT, v2, GEN_INT (m_bitsize), - NULL, true, OPTAB_LIB_WIDEN); - if (v2 == NULL) - goto fail; - comb = expand_simple_binop (dmode, IOR, v1, v2, NULL, - true, OPTAB_LIB_WIDEN); - if (comb == NULL) - goto fail; - } - - shift = expand_simple_binop (Pmode, AND, base, GEN_INT (m_size - 1), - NULL, true, OPTAB_LIB_WIDEN); - shift = expand_mult (Pmode, shift, GEN_INT (BITS_PER_UNIT), NULL, 1); - - if (bitpos != 0) - { - if (sign_shift_up <= bitpos) - bitpos -= sign_shift_up, sign_shift_up = 0; - shift = expand_simple_binop (Pmode, PLUS, shift, GEN_INT (bitpos), - NULL, true, OPTAB_LIB_WIDEN); - } - } - else - { - unsigned HOST_WIDE_INT offset = bitpos / BITS_PER_UNIT; - bitpos %= BITS_PER_UNIT; - - /* When strict alignment is not required, we can just load directly - from memory without masking. If the remaining BITPOS offset is - small enough, we may be able to do all operations in MODE as - opposed to DMODE. */ - if (bitpos + bitsize <= m_bitsize) - dmode = mode; - comb = adjust_address (op0, dmode, offset); - - if (sign_shift_up <= bitpos) - bitpos -= sign_shift_up, sign_shift_up = 0; - shift = GEN_INT (bitpos); - } - - /* Shift down the double-word such that the requested value is at bit 0. */ - if (shift != const0_rtx) - comb = expand_simple_binop (dmode, unsignedp ? LSHIFTRT : ASHIFTRT, - comb, shift, NULL, unsignedp, OPTAB_LIB_WIDEN); - if (comb == NULL) - goto fail; - - /* If the field exactly matches MODE, then all we need to do is return the - lowpart. Otherwise, shift to get the sign bits set properly. */ - result = force_reg (mode, gen_lowpart (mode, comb)); - - if (sign_shift_up) - result = expand_simple_binop (mode, ASHIFT, result, - GEN_INT (sign_shift_up), - NULL_RTX, 0, OPTAB_LIB_WIDEN); - if (sign_shift_dn) - result = expand_simple_binop (mode, unsignedp ? LSHIFTRT : ASHIFTRT, - result, GEN_INT (sign_shift_dn), - NULL_RTX, 0, OPTAB_LIB_WIDEN); - - return result; - - fail: - delete_insns_since (start); - return NULL; -} - /* Extract a bit field that is split across two words and return an RTX for the result. @@ -2054,16 +1859,7 @@ extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, if (REG_P (op0) || GET_CODE (op0) == SUBREG) unit = BITS_PER_WORD; else - { - unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD); - if (0 && bitsize / unit > 2) - { - rtx tmp = extract_force_align_mem_bit_field (op0, bitsize, bitpos, - unsignedp); - if (tmp) - return tmp; - } - } + unit = MIN (MEM_ALIGN (op0), BITS_PER_WORD); while (bitsdone < bitsize) { @@ -2150,6 +1946,66 @@ extract_split_bit_field (rtx op0, unsigned HOST_WIDE_INT bitsize, NULL_RTX, 0); } +/* Try to read the low bits of SRC as an rvalue of mode MODE, preserving + the bit pattern. SRC_MODE is the mode of SRC; if this is smaller than + MODE, fill the upper bits with zeros. Fail if the layout of either + mode is unknown (as for CC modes) or if the extraction would involve + unprofitable mode punning. Return the value on success, otherwise + return null. + + This is different from gen_lowpart* in these respects: + + - the returned value must always be considered an rvalue + + - when MODE is wider than SRC_MODE, the extraction involves + a zero extension + + - when MODE is smaller than SRC_MODE, the extraction involves + a truncation (and is thus subject to TRULY_NOOP_TRUNCATION). + + In other words, this routine performs a computation, whereas the + gen_lowpart* routines are conceptually lvalue or rvalue subreg + operations. */ + +rtx +extract_low_bits (enum machine_mode mode, enum machine_mode src_mode, rtx src) +{ + enum machine_mode int_mode, src_int_mode; + + if (mode == src_mode) + return src; + + if (CONSTANT_P (src)) + return simplify_gen_subreg (mode, src, src_mode, + subreg_lowpart_offset (mode, src_mode)); + + if (GET_MODE_CLASS (mode) == MODE_CC || GET_MODE_CLASS (src_mode) == MODE_CC) + return NULL_RTX; + + if (GET_MODE_BITSIZE (mode) == GET_MODE_BITSIZE (src_mode) + && MODES_TIEABLE_P (mode, src_mode)) + { + rtx x = gen_lowpart_common (mode, src); + if (x) + return x; + } + + src_int_mode = int_mode_for_mode (src_mode); + int_mode = int_mode_for_mode (mode); + if (src_int_mode == BLKmode || int_mode == BLKmode) + return NULL_RTX; + + if (!MODES_TIEABLE_P (src_int_mode, src_mode)) + return NULL_RTX; + if (!MODES_TIEABLE_P (int_mode, mode)) + return NULL_RTX; + + src = gen_lowpart (src_int_mode, src); + src = convert_modes (int_mode, src_int_mode, src, true); + src = gen_lowpart (mode, src); + return src; +} + /* Add INC into TARGET. */ void @@ -2956,10 +2812,9 @@ expand_mult_const (enum machine_mode mode, rtx op0, HOST_WIDE_INT val, int opno; enum machine_mode nmode; - /* Avoid referencing memory over and over. - For speed, but also for correctness when mem is volatile. */ - if (MEM_P (op0)) - op0 = force_reg (mode, op0); + /* Avoid referencing memory over and over and invalid sharing + on SUBREGs. */ + op0 = force_reg (mode, op0); /* ACCUM starts out either as OP0 or as a zero, depending on the first operation. */ @@ -3070,7 +2925,8 @@ expand_mult_const (enum machine_mode mode, rtx op0, HOST_WIDE_INT val, insn = get_last_insn (); set_unique_reg_note (insn, REG_EQUAL, - gen_rtx_MULT (nmode, tem, GEN_INT (val_so_far))); + gen_rtx_MULT (nmode, tem, + GEN_INT (val_so_far))); } if (variant == negate_variant) @@ -3471,7 +3327,7 @@ expand_mult_highpart_optab (enum machine_mode mode, rtx op0, rtx op1, /* Try widening multiplication. */ moptab = unsignedp ? umul_widen_optab : smul_widen_optab; - if (moptab->handlers[wider_mode].insn_code != CODE_FOR_nothing + if (optab_handler (moptab, wider_mode)->insn_code != CODE_FOR_nothing && mul_widen_cost[wider_mode] < max_cost) { tem = expand_binop (wider_mode, moptab, op0, narrow_op1, 0, @@ -3481,7 +3337,7 @@ expand_mult_highpart_optab (enum machine_mode mode, rtx op0, rtx op1, } /* Try widening the mode and perform a non-widening multiplication. */ - if (smul_optab->handlers[wider_mode].insn_code != CODE_FOR_nothing + if (optab_handler (smul_optab, wider_mode)->insn_code != CODE_FOR_nothing && size - 1 < BITS_PER_WORD && mul_cost[wider_mode] + shift_cost[mode][size-1] < max_cost) { @@ -3508,7 +3364,7 @@ expand_mult_highpart_optab (enum machine_mode mode, rtx op0, rtx op1, /* Try widening multiplication of opposite signedness, and adjust. */ moptab = unsignedp ? smul_widen_optab : umul_widen_optab; - if (moptab->handlers[wider_mode].insn_code != CODE_FOR_nothing + if (optab_handler (moptab, wider_mode)->insn_code != CODE_FOR_nothing && size - 1 < BITS_PER_WORD && (mul_widen_cost[wider_mode] + 2 * shift_cost[mode][size-1] + 4 * add_cost[mode] < max_cost)) @@ -3629,7 +3485,7 @@ expand_smod_pow2 (enum machine_mode mode, rtx op0, HOST_WIDE_INT d) use a LSHIFTRT, 1 ADD, 1 SUB and an AND. */ temp = gen_rtx_LSHIFTRT (mode, result, shift); - if (lshr_optab->handlers[mode].insn_code == CODE_FOR_nothing + if (optab_handler (lshr_optab, mode)->insn_code == CODE_FOR_nothing || rtx_cost (temp, SET) > COSTS_N_INSNS (2)) { temp = expand_binop (mode, xor_optab, op0, signmask, @@ -3928,15 +3784,15 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode, for (compute_mode = mode; compute_mode != VOIDmode; compute_mode = GET_MODE_WIDER_MODE (compute_mode)) - if (optab1->handlers[compute_mode].insn_code != CODE_FOR_nothing - || optab2->handlers[compute_mode].insn_code != CODE_FOR_nothing) + if (optab_handler (optab1, compute_mode)->insn_code != CODE_FOR_nothing + || optab_handler (optab2, compute_mode)->insn_code != CODE_FOR_nothing) break; if (compute_mode == VOIDmode) for (compute_mode = mode; compute_mode != VOIDmode; compute_mode = GET_MODE_WIDER_MODE (compute_mode)) - if (optab1->handlers[compute_mode].libfunc - || optab2->handlers[compute_mode].libfunc) + if (optab_libfunc (optab1, compute_mode) + || optab_libfunc (optab2, compute_mode)) break; /* If we still couldn't find a mode, use MODE, but expand_binop will @@ -4149,7 +4005,14 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode, int lgup, post_shift; rtx mlr; HOST_WIDE_INT d = INTVAL (op1); - unsigned HOST_WIDE_INT abs_d = d >= 0 ? d : -d; + unsigned HOST_WIDE_INT abs_d; + + /* Since d might be INT_MIN, we have to cast to + unsigned HOST_WIDE_INT before negating to avoid + undefined signed overflow. */ + abs_d = (d >= 0 + ? (unsigned HOST_WIDE_INT) d + : - (unsigned HOST_WIDE_INT) d); /* n rem d = n rem -d */ if (rem_flag && d < 0) @@ -4176,11 +4039,13 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode, : sdiv_pow2_cheap[compute_mode]) /* We assume that cheap metric is true if the optab has an expander for this mode. */ - && (((rem_flag ? smod_optab : sdiv_optab) - ->handlers[compute_mode].insn_code + && ((optab_handler ((rem_flag ? smod_optab + : sdiv_optab), + compute_mode)->insn_code != CODE_FOR_nothing) - || (sdivmod_optab->handlers[compute_mode] - .insn_code != CODE_FOR_nothing))) + || (optab_handler(sdivmod_optab, + compute_mode) + ->insn_code != CODE_FOR_nothing))) ; else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d)) { @@ -4192,9 +4057,9 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode, } if (sdiv_pow2_cheap[compute_mode] - && ((sdiv_optab->handlers[compute_mode].insn_code + && ((optab_handler (sdiv_optab, compute_mode)->insn_code != CODE_FOR_nothing) - || (sdivmod_optab->handlers[compute_mode].insn_code + || (optab_handler (sdivmod_optab, compute_mode)->insn_code != CODE_FOR_nothing))) quotient = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, op0, @@ -4842,7 +4707,7 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode, = sign_expand_binop (compute_mode, umod_optab, smod_optab, op0, op1, target, unsignedp, - ((optab2->handlers[compute_mode].insn_code + ((optab_handler (optab2, compute_mode)->insn_code != CODE_FOR_nothing) ? OPTAB_DIRECT : OPTAB_WIDEN)); if (remainder == 0) @@ -4870,7 +4735,7 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode, = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, op0, op1, rem_flag ? NULL_RTX : target, unsignedp, - ((optab2->handlers[compute_mode].insn_code + ((optab_handler (optab2, compute_mode)->insn_code != CODE_FOR_nothing) ? OPTAB_DIRECT : OPTAB_WIDEN)); @@ -5012,20 +4877,20 @@ make_tree (tree type, rtx x) make_tree (type, XEXP (x, 1))); case LSHIFTRT: - t = lang_hooks.types.unsigned_type (type); + t = unsigned_type_for (type); return fold_convert (type, build2 (RSHIFT_EXPR, t, make_tree (t, XEXP (x, 0)), make_tree (type, XEXP (x, 1)))); case ASHIFTRT: - t = lang_hooks.types.signed_type (type); + t = signed_type_for (type); return fold_convert (type, build2 (RSHIFT_EXPR, t, make_tree (t, XEXP (x, 0)), make_tree (type, XEXP (x, 1)))); case DIV: if (TREE_CODE (type) != REAL_TYPE) - t = lang_hooks.types.signed_type (type); + t = signed_type_for (type); else t = type; @@ -5033,7 +4898,7 @@ make_tree (tree type, rtx x) make_tree (t, XEXP (x, 0)), make_tree (t, XEXP (x, 1)))); case UDIV: - t = lang_hooks.types.unsigned_type (type); + t = unsigned_type_for (type); return fold_convert (type, build2 (TRUNC_DIV_EXPR, t, make_tree (t, XEXP (x, 0)), make_tree (t, XEXP (x, 1)))); @@ -5390,7 +5255,7 @@ emit_store_flag (rtx target, enum rtx_code code, rtx op0, rtx op1, for (compare_mode = mode; compare_mode != VOIDmode; compare_mode = GET_MODE_WIDER_MODE (compare_mode)) { - icode = cstore_optab->handlers[(int) compare_mode].insn_code; + icode = optab_handler (cstore_optab, compare_mode)->insn_code; if (icode != CODE_FOR_nothing) break; } @@ -5554,9 +5419,9 @@ emit_store_flag (rtx target, enum rtx_code code, rtx op0, rtx op1, that is compensated by the subsequent overflow when subtracting one / negating. */ - if (abs_optab->handlers[mode].insn_code != CODE_FOR_nothing) + if (optab_handler (abs_optab, mode)->insn_code != CODE_FOR_nothing) tem = expand_unop (mode, abs_optab, op0, subtarget, 1); - else if (ffs_optab->handlers[mode].insn_code != CODE_FOR_nothing) + else if (optab_handler (ffs_optab, mode)->insn_code != CODE_FOR_nothing) tem = expand_unop (mode, ffs_optab, op0, subtarget, 1); else if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) {