X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Foptabs.c;h=ecaff987e043c145918f916e39e61719b5770a8c;hb=351e36110d5ddcded2aca81b692f2e9267dbf60d;hp=cfed9040180c40f6e7b6971200b82429ec92585b;hpb=029a6a72be70d91946be3ba0b444a4373735f276;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/optabs.c b/gcc/optabs.c index cfed9040180..ecaff987e04 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -1,6 +1,6 @@ /* Expand the basic unary and binary arithmetic operations, for GNU compiler. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GCC. @@ -294,9 +294,6 @@ optab_for_tree_code (enum tree_code code, tree type) case MIN_EXPR: return TYPE_UNSIGNED (type) ? umin_optab : smin_optab; - case REALIGN_STORE_EXPR: - return vec_realign_store_optab; - case REALIGN_LOAD_EXPR: return vec_realign_load_optab; @@ -427,7 +424,7 @@ simplify_expand_binop (enum machine_mode mode, optab binoptab, /* Like simplify_expand_binop, but always put the result in TARGET. Return true if the expansion succeeded. */ -static bool +bool force_expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, rtx target, int unsignedp, enum optab_methods methods) @@ -759,6 +756,168 @@ expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab, return true; } +/* Subroutine of expand_binop. Perform a double word multiplication of + operands OP0 and OP1 both of mode MODE, which is exactly twice as wide + as the target's word_mode. This function return NULL_RTX if anything + goes wrong, in which case it may have already emitted instructions + which need to be deleted. + + If we want to multiply two two-word values and have normal and widening + multiplies of single-word values, we can do this with three smaller + multiplications. Note that we do not make a REG_NO_CONFLICT block here + because we are not operating on one word at a time. + + The multiplication proceeds as follows: + _______________________ + [__op0_high_|__op0_low__] + _______________________ + * [__op1_high_|__op1_low__] + _______________________________________________ + _______________________ + (1) [__op0_low__*__op1_low__] + _______________________ + (2a) [__op0_low__*__op1_high_] + _______________________ + (2b) [__op0_high_*__op1_low__] + _______________________ + (3) [__op0_high_*__op1_high_] + + + This gives a 4-word result. Since we are only interested in the + lower 2 words, partial result (3) and the upper words of (2a) and + (2b) don't need to be calculated. Hence (2a) and (2b) can be + calculated using non-widening multiplication. + + (1), however, needs to be calculated with an unsigned widening + multiplication. If this operation is not directly supported we + try using a signed widening multiplication and adjust the result. + This adjustment works as follows: + + If both operands are positive then no adjustment is needed. + + If the operands have different signs, for example op0_low < 0 and + op1_low >= 0, the instruction treats the most significant bit of + op0_low as a sign bit instead of a bit with significance + 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low + with 2**BITS_PER_WORD - op0_low, and two's complements the + result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to + the result. + + Similarly, if both operands are negative, we need to add + (op0_low + op1_low) * 2**BITS_PER_WORD. + + We use a trick to adjust quickly. We logically shift op0_low right + (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to + op0_high (op1_high) before it is used to calculate 2b (2a). If no + logical shift exists, we do an arithmetic right shift and subtract + the 0 or -1. */ + +static rtx +expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target, + bool umulp, enum optab_methods methods) +{ + int low = (WORDS_BIG_ENDIAN ? 1 : 0); + int high = (WORDS_BIG_ENDIAN ? 0 : 1); + rtx wordm1 = umulp ? NULL_RTX : GEN_INT (BITS_PER_WORD - 1); + rtx product, adjust, product_high, temp; + + rtx op0_high = operand_subword_force (op0, high, mode); + rtx op0_low = operand_subword_force (op0, low, mode); + rtx op1_high = operand_subword_force (op1, high, mode); + rtx op1_low = operand_subword_force (op1, low, mode); + + /* If we're using an unsigned multiply to directly compute the product + of the low-order words of the operands and perform any required + adjustments of the operands, we begin by trying two more multiplications + and then computing the appropriate sum. + + We have checked above that the required addition is provided. + Full-word addition will normally always succeed, especially if + it is provided at all, so we don't worry about its failure. The + multiplication may well fail, however, so we do handle that. */ + + if (!umulp) + { + /* ??? This could be done with emit_store_flag where available. */ + temp = expand_binop (word_mode, lshr_optab, op0_low, wordm1, + NULL_RTX, 1, methods); + if (temp) + op0_high = expand_binop (word_mode, add_optab, op0_high, temp, + NULL_RTX, 0, OPTAB_DIRECT); + else + { + temp = expand_binop (word_mode, ashr_optab, op0_low, wordm1, + NULL_RTX, 0, methods); + if (!temp) + return NULL_RTX; + op0_high = expand_binop (word_mode, sub_optab, op0_high, temp, + NULL_RTX, 0, OPTAB_DIRECT); + } + + if (!op0_high) + return NULL_RTX; + } + + adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low, + NULL_RTX, 0, OPTAB_DIRECT); + if (!adjust) + return NULL_RTX; + + /* OP0_HIGH should now be dead. */ + + if (!umulp) + { + /* ??? This could be done with emit_store_flag where available. */ + temp = expand_binop (word_mode, lshr_optab, op1_low, wordm1, + NULL_RTX, 1, methods); + if (temp) + op1_high = expand_binop (word_mode, add_optab, op1_high, temp, + NULL_RTX, 0, OPTAB_DIRECT); + else + { + temp = expand_binop (word_mode, ashr_optab, op1_low, wordm1, + NULL_RTX, 0, methods); + if (!temp) + return NULL_RTX; + op1_high = expand_binop (word_mode, sub_optab, op1_high, temp, + NULL_RTX, 0, OPTAB_DIRECT); + } + + if (!op1_high) + return NULL_RTX; + } + + temp = expand_binop (word_mode, smul_optab, op1_high, op0_low, + NULL_RTX, 0, OPTAB_DIRECT); + if (!temp) + return NULL_RTX; + + /* OP1_HIGH should now be dead. */ + + adjust = expand_binop (word_mode, add_optab, adjust, temp, + adjust, 0, OPTAB_DIRECT); + + if (target && !REG_P (target)) + target = NULL_RTX; + + if (umulp) + product = expand_binop (mode, umul_widen_optab, op0_low, op1_low, + target, 1, OPTAB_DIRECT); + else + product = expand_binop (mode, smul_widen_optab, op0_low, op1_low, + target, 1, OPTAB_DIRECT); + + if (!product) + return NULL_RTX; + + product_high = operand_subword (product, high, 1, mode); + adjust = expand_binop (word_mode, add_optab, product_high, adjust, + REG_P (product_high) ? product_high : adjust, + 0, OPTAB_DIRECT); + emit_move_insn (product_high, adjust); + return product; +} + /* Wrapper around expand_binop which takes an rtx code to specify the operation to perform, not an optab pointer. All other arguments are the same. */ @@ -1375,6 +1534,11 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, } emit_move_insn (target_piece, newx); } + else + { + if (x != target_piece) + emit_move_insn (target_piece, x); + } carry_in = carry_out; } @@ -1402,197 +1566,51 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, delete_insns_since (last); } - /* If we want to multiply two two-word values and have normal and widening - multiplies of single-word values, we can do this with three smaller - multiplications. Note that we do not make a REG_NO_CONFLICT block here - because we are not operating on one word at a time. - - The multiplication proceeds as follows: - _______________________ - [__op0_high_|__op0_low__] - _______________________ - * [__op1_high_|__op1_low__] - _______________________________________________ - _______________________ - (1) [__op0_low__*__op1_low__] - _______________________ - (2a) [__op0_low__*__op1_high_] - _______________________ - (2b) [__op0_high_*__op1_low__] - _______________________ - (3) [__op0_high_*__op1_high_] - - - This gives a 4-word result. Since we are only interested in the - lower 2 words, partial result (3) and the upper words of (2a) and - (2b) don't need to be calculated. Hence (2a) and (2b) can be - calculated using non-widening multiplication. - - (1), however, needs to be calculated with an unsigned widening - multiplication. If this operation is not directly supported we - try using a signed widening multiplication and adjust the result. - This adjustment works as follows: - - If both operands are positive then no adjustment is needed. - - If the operands have different signs, for example op0_low < 0 and - op1_low >= 0, the instruction treats the most significant bit of - op0_low as a sign bit instead of a bit with significance - 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low - with 2**BITS_PER_WORD - op0_low, and two's complements the - result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to - the result. - - Similarly, if both operands are negative, we need to add - (op0_low + op1_low) * 2**BITS_PER_WORD. - - We use a trick to adjust quickly. We logically shift op0_low right - (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to - op0_high (op1_high) before it is used to calculate 2b (2a). If no - logical shift exists, we do an arithmetic right shift and subtract - the 0 or -1. */ + /* Attempt to synthesize double word multiplies using a sequence of word + mode multiplications. We first attempt to generate a sequence using a + more efficient unsigned widening multiply, and if that fails we then + try using a signed widening multiply. */ if (binoptab == smul_optab && class == MODE_INT && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing - && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing - && ((umul_widen_optab->handlers[(int) mode].insn_code - != CODE_FOR_nothing) - || (smul_widen_optab->handlers[(int) mode].insn_code - != CODE_FOR_nothing))) + && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) { - int low = (WORDS_BIG_ENDIAN ? 1 : 0); - int high = (WORDS_BIG_ENDIAN ? 0 : 1); - rtx op0_high = operand_subword_force (op0, high, mode); - rtx op0_low = operand_subword_force (op0, low, mode); - rtx op1_high = operand_subword_force (op1, high, mode); - rtx op1_low = operand_subword_force (op1, low, mode); - rtx product = 0; - rtx op0_xhigh = NULL_RTX; - rtx op1_xhigh = NULL_RTX; - - /* If the target is the same as one of the inputs, don't use it. This - prevents problems with the REG_EQUAL note. */ - if (target == op0 || target == op1 - || (target != 0 && !REG_P (target))) - target = 0; - - /* Multiply the two lower words to get a double-word product. - If unsigned widening multiplication is available, use that; - otherwise use the signed form and compensate. */ - - if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - product = expand_binop (mode, umul_widen_optab, op0_low, op1_low, - target, 1, OPTAB_DIRECT); + rtx product = NULL_RTX; - /* If we didn't succeed, delete everything we did so far. */ - if (product == 0) + if (umul_widen_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing) + { + product = expand_doubleword_mult (mode, op0, op1, target, + true, methods); + if (!product) delete_insns_since (last); - else - op0_xhigh = op0_high, op1_xhigh = op1_high; } - if (product == 0 + if (product == NULL_RTX && smul_widen_optab->handlers[(int) mode].insn_code - != CODE_FOR_nothing) + != CODE_FOR_nothing) { - rtx wordm1 = GEN_INT (BITS_PER_WORD - 1); - product = expand_binop (mode, smul_widen_optab, op0_low, op1_low, - target, 1, OPTAB_DIRECT); - op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1, - NULL_RTX, 1, next_methods); - if (op0_xhigh) - op0_xhigh = expand_binop (word_mode, add_optab, op0_high, - op0_xhigh, op0_xhigh, 0, next_methods); - else - { - op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1, - NULL_RTX, 0, next_methods); - if (op0_xhigh) - op0_xhigh = expand_binop (word_mode, sub_optab, op0_high, - op0_xhigh, op0_xhigh, 0, - next_methods); - } - - op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1, - NULL_RTX, 1, next_methods); - if (op1_xhigh) - op1_xhigh = expand_binop (word_mode, add_optab, op1_high, - op1_xhigh, op1_xhigh, 0, next_methods); - else - { - op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1, - NULL_RTX, 0, next_methods); - if (op1_xhigh) - op1_xhigh = expand_binop (word_mode, sub_optab, op1_high, - op1_xhigh, op1_xhigh, 0, - next_methods); - } + product = expand_doubleword_mult (mode, op0, op1, target, + false, methods); + if (!product) + delete_insns_since (last); } - /* If we have been able to directly compute the product of the - low-order words of the operands and perform any required adjustments - of the operands, we proceed by trying two more multiplications - and then computing the appropriate sum. - - We have checked above that the required addition is provided. - Full-word addition will normally always succeed, especially if - it is provided at all, so we don't worry about its failure. The - multiplication may well fail, however, so we do handle that. */ - - if (product && op0_xhigh && op1_xhigh) + if (product != NULL_RTX) { - rtx product_high = operand_subword (product, high, 1, mode); - rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh, - NULL_RTX, 0, OPTAB_DIRECT); - - if (!REG_P (product_high)) - product_high = force_reg (word_mode, product_high); - - if (temp != 0) - temp = expand_binop (word_mode, add_optab, temp, product_high, - product_high, 0, next_methods); - - if (temp != 0 && temp != product_high) - emit_move_insn (product_high, temp); - - if (temp != 0) - temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, - NULL_RTX, 0, OPTAB_DIRECT); - - if (temp != 0) - temp = expand_binop (word_mode, add_optab, temp, - product_high, product_high, - 0, next_methods); - - if (temp != 0 && temp != product_high) - emit_move_insn (product_high, temp); - - emit_move_insn (operand_subword (product, high, 1, mode), product_high); - - if (temp != 0) + if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { - if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - { - temp = emit_move_insn (product, product); - set_unique_reg_note (temp, - REG_EQUAL, - gen_rtx_fmt_ee (MULT, mode, - copy_rtx (op0), - copy_rtx (op1))); - } - - return product; + temp = emit_move_insn (target ? target : product, product); + set_unique_reg_note (temp, + REG_EQUAL, + gen_rtx_fmt_ee (MULT, mode, + copy_rtx (op0), + copy_rtx (op1))); } + return product; } - - /* If we get here, we couldn't do it for some reason even though we - originally thought we could. Delete anything we've emitted in - trying to do it. */ - - delete_insns_since (last); } /* It can't be open-coded in this mode. @@ -2131,6 +2149,131 @@ expand_parity (enum machine_mode mode, rtx op0, rtx target) return 0; } +/* Extract the OMODE lowpart from VAL, which has IMODE. Under certain + conditions, VAL may already be a SUBREG against which we cannot generate + a further SUBREG. In this case, we expect forcing the value into a + register will work around the situation. */ + +static rtx +lowpart_subreg_maybe_copy (enum machine_mode omode, rtx val, + enum machine_mode imode) +{ + rtx ret; + ret = lowpart_subreg (omode, val, imode); + if (ret == NULL) + { + val = force_reg (imode, val); + ret = lowpart_subreg (omode, val, imode); + gcc_assert (ret != NULL); + } + return ret; +} + +/* Expand a floating point absolute value or negation operation via a + logical operation on the sign bit. */ + +static rtx +expand_absneg_bit (enum rtx_code code, enum machine_mode mode, + rtx op0, rtx target) +{ + const struct real_format *fmt; + int bitpos, word, nwords, i; + enum machine_mode imode; + HOST_WIDE_INT hi, lo; + rtx temp, insns; + + /* The format has to have a simple sign bit. */ + fmt = REAL_MODE_FORMAT (mode); + if (fmt == NULL) + return NULL_RTX; + + bitpos = fmt->signbit_rw; + if (bitpos < 0) + return NULL_RTX; + + /* Don't create negative zeros if the format doesn't support them. */ + if (code == NEG && !fmt->has_signed_zero) + return NULL_RTX; + + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) + { + imode = int_mode_for_mode (mode); + if (imode == BLKmode) + return NULL_RTX; + word = 0; + nwords = 1; + } + else + { + imode = word_mode; + + if (FLOAT_WORDS_BIG_ENDIAN) + word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD; + else + word = bitpos / BITS_PER_WORD; + bitpos = bitpos % BITS_PER_WORD; + nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD; + } + + if (bitpos < HOST_BITS_PER_WIDE_INT) + { + hi = 0; + lo = (HOST_WIDE_INT) 1 << bitpos; + } + else + { + hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); + lo = 0; + } + if (code == ABS) + lo = ~lo, hi = ~hi; + + if (target == 0 || target == op0) + target = gen_reg_rtx (mode); + + if (nwords > 1) + { + start_sequence (); + + for (i = 0; i < nwords; ++i) + { + rtx targ_piece = operand_subword (target, i, 1, mode); + rtx op0_piece = operand_subword_force (op0, i, mode); + + if (i == word) + { + temp = expand_binop (imode, code == ABS ? and_optab : xor_optab, + op0_piece, + immed_double_const (lo, hi, imode), + targ_piece, 1, OPTAB_LIB_WIDEN); + if (temp != targ_piece) + emit_move_insn (targ_piece, temp); + } + else + emit_move_insn (targ_piece, op0_piece); + } + + insns = get_insns (); + end_sequence (); + + temp = gen_rtx_fmt_e (code, mode, copy_rtx (op0)); + emit_no_conflict_block (insns, target, op0, NULL_RTX, temp); + } + else + { + temp = expand_binop (imode, code == ABS ? and_optab : xor_optab, + gen_lowpart (imode, op0), + immed_double_const (lo, hi, imode), + gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN); + target = lowpart_subreg_maybe_copy (mode, temp, imode); + + set_unique_reg_note (get_last_insn (), REG_EQUAL, + gen_rtx_fmt_e (code, mode, copy_rtx (op0))); + } + + return target; +} + /* Generate code to perform an operation specified by UNOPTAB on operand OP0, with result having machine-mode MODE. @@ -2282,54 +2425,27 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, return target; } - /* Try negating floating point values by flipping the sign bit. */ - if (unoptab->code == NEG && class == MODE_FLOAT - && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT) + if (unoptab->code == NEG) { - const struct real_format *fmt = REAL_MODE_FORMAT (mode); - enum machine_mode imode = int_mode_for_mode (mode); - int bitpos = (fmt != 0) ? fmt->signbit : -1; - - if (imode != BLKmode && bitpos >= 0 && fmt->has_signed_zero) + /* Try negating floating point values by flipping the sign bit. */ + if (class == MODE_FLOAT) { - HOST_WIDE_INT hi, lo; - rtx last = get_last_insn (); - - /* Handle targets with different FP word orders. */ - if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN) - { - int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; - int word = nwords - (bitpos / BITS_PER_WORD) - 1; - bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD; - } + temp = expand_absneg_bit (NEG, mode, op0, target); + if (temp) + return temp; + } - if (bitpos < HOST_BITS_PER_WIDE_INT) - { - hi = 0; - lo = (HOST_WIDE_INT) 1 << bitpos; - } - else - { - hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); - lo = 0; - } - temp = expand_binop (imode, xor_optab, - gen_lowpart (imode, op0), - immed_double_const (lo, hi, imode), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - if (temp != 0) - { - rtx insn; - if (target == 0) - target = gen_reg_rtx (mode); - insn = emit_move_insn (target, gen_lowpart (mode, temp)); - set_unique_reg_note (insn, REG_EQUAL, - gen_rtx_fmt_e (NEG, mode, - copy_rtx (op0))); - return target; - } - delete_insns_since (last); - } + /* If there is no negation pattern, and we have no negative zero, + try subtracting from zero. */ + if (!HONOR_SIGNED_ZEROS (mode)) + { + temp = expand_binop (mode, (unoptab == negv_optab + ? subv_optab : sub_optab), + CONST0_RTX (mode), op0, target, + unsignedp, OPTAB_DIRECT); + if (temp) + return temp; + } } /* Try calculating parity (x) as popcount (x) % 2. */ @@ -2340,15 +2456,6 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, return temp; } - /* If there is no negation pattern, try subtracting from zero. */ - if (unoptab == neg_optab && class == MODE_INT) - { - temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0, - target, unsignedp, OPTAB_DIRECT); - if (temp) - return temp; - } - try_libcall: /* Now try a library call in this mode. */ if (unoptab->handlers[(int) mode].libfunc) @@ -2432,10 +2539,9 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, } } - /* If there is no negate operation, try doing a subtract from zero. - The US Software GOFAST library needs this. FIXME: This is *wrong* - for floating-point operations due to negative zeros! */ - if (unoptab->code == NEG) + /* One final attempt at implementing negation via subtraction, + this time allowing widening of the operand. */ + if (unoptab->code == NEG && !HONOR_SIGNED_ZEROS (mode)) { rtx temp; temp = expand_binop (mode, @@ -2443,7 +2549,7 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, CONST0_RTX (mode), op0, target, unsignedp, OPTAB_LIB_WIDEN); if (temp) - return temp; + return temp; } return 0; @@ -2474,57 +2580,16 @@ expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target, return temp; /* For floating point modes, try clearing the sign bit. */ - if (GET_MODE_CLASS (mode) == MODE_FLOAT - && GET_MODE_BITSIZE (mode) <= 2 * HOST_BITS_PER_WIDE_INT) + if (GET_MODE_CLASS (mode) == MODE_FLOAT) { - const struct real_format *fmt = REAL_MODE_FORMAT (mode); - enum machine_mode imode = int_mode_for_mode (mode); - int bitpos = (fmt != 0) ? fmt->signbit : -1; - - if (imode != BLKmode && bitpos >= 0) - { - HOST_WIDE_INT hi, lo; - rtx last = get_last_insn (); - - /* Handle targets with different FP word orders. */ - if (FLOAT_WORDS_BIG_ENDIAN != WORDS_BIG_ENDIAN) - { - int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; - int word = nwords - (bitpos / BITS_PER_WORD) - 1; - bitpos = word * BITS_PER_WORD + bitpos % BITS_PER_WORD; - } - - if (bitpos < HOST_BITS_PER_WIDE_INT) - { - hi = 0; - lo = (HOST_WIDE_INT) 1 << bitpos; - } - else - { - hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); - lo = 0; - } - temp = expand_binop (imode, and_optab, - gen_lowpart (imode, op0), - immed_double_const (~lo, ~hi, imode), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - if (temp != 0) - { - rtx insn; - if (target == 0) - target = gen_reg_rtx (mode); - insn = emit_move_insn (target, gen_lowpart (mode, temp)); - set_unique_reg_note (insn, REG_EQUAL, - gen_rtx_fmt_e (ABS, mode, - copy_rtx (op0))); - return target; - } - delete_insns_since (last); - } + temp = expand_absneg_bit (ABS, mode, op0, target); + if (temp) + return temp; } /* If we have a MAX insn, we can do this as MAX (x, -x). */ - if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + if (smax_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing + && !HONOR_SIGNED_ZEROS (mode)) { rtx last = get_last_insn (); @@ -2612,6 +2677,239 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target, OK_DEFER_POP; return target; } + +/* A subroutine of expand_copysign, perform the copysign operation using the + abs and neg primitives advertised to exist on the target. The assumption + is that we have a split register file, and leaving op0 in fp registers, + and not playing with subregs so much, will help the register allocator. */ + +static rtx +expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target, + int bitpos, bool op0_is_abs) +{ + enum machine_mode imode; + HOST_WIDE_INT hi, lo; + int word; + rtx label; + + if (target == op1) + target = NULL_RTX; + + if (!op0_is_abs) + { + op0 = expand_unop (mode, abs_optab, op0, target, 0); + if (op0 == NULL) + return NULL_RTX; + target = op0; + } + else + { + if (target == NULL_RTX) + target = copy_to_reg (op0); + else + emit_move_insn (target, op0); + } + + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) + { + imode = int_mode_for_mode (mode); + if (imode == BLKmode) + return NULL_RTX; + op1 = gen_lowpart (imode, op1); + } + else + { + imode = word_mode; + if (FLOAT_WORDS_BIG_ENDIAN) + word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD; + else + word = bitpos / BITS_PER_WORD; + bitpos = bitpos % BITS_PER_WORD; + op1 = operand_subword_force (op1, word, mode); + } + + if (bitpos < HOST_BITS_PER_WIDE_INT) + { + hi = 0; + lo = (HOST_WIDE_INT) 1 << bitpos; + } + else + { + hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); + lo = 0; + } + + op1 = expand_binop (imode, and_optab, op1, + immed_double_const (lo, hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + label = gen_label_rtx (); + emit_cmp_and_jump_insns (op1, const0_rtx, EQ, NULL_RTX, imode, 1, label); + + if (GET_CODE (op0) == CONST_DOUBLE) + op0 = simplify_unary_operation (NEG, mode, op0, mode); + else + op0 = expand_unop (mode, neg_optab, op0, target, 0); + if (op0 != target) + emit_move_insn (target, op0); + + emit_label (label); + + return target; +} + + +/* A subroutine of expand_copysign, perform the entire copysign operation + with integer bitmasks. BITPOS is the position of the sign bit; OP0_IS_ABS + is true if op0 is known to have its sign bit clear. */ + +static rtx +expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target, + int bitpos, bool op0_is_abs) +{ + enum machine_mode imode; + HOST_WIDE_INT hi, lo; + int word, nwords, i; + rtx temp, insns; + + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) + { + imode = int_mode_for_mode (mode); + if (imode == BLKmode) + return NULL_RTX; + word = 0; + nwords = 1; + } + else + { + imode = word_mode; + + if (FLOAT_WORDS_BIG_ENDIAN) + word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD; + else + word = bitpos / BITS_PER_WORD; + bitpos = bitpos % BITS_PER_WORD; + nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD; + } + + if (bitpos < HOST_BITS_PER_WIDE_INT) + { + hi = 0; + lo = (HOST_WIDE_INT) 1 << bitpos; + } + else + { + hi = (HOST_WIDE_INT) 1 << (bitpos - HOST_BITS_PER_WIDE_INT); + lo = 0; + } + + if (target == 0 || target == op0 || target == op1) + target = gen_reg_rtx (mode); + + if (nwords > 1) + { + start_sequence (); + + for (i = 0; i < nwords; ++i) + { + rtx targ_piece = operand_subword (target, i, 1, mode); + rtx op0_piece = operand_subword_force (op0, i, mode); + + if (i == word) + { + if (!op0_is_abs) + op0_piece = expand_binop (imode, and_optab, op0_piece, + immed_double_const (~lo, ~hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + op1 = expand_binop (imode, and_optab, + operand_subword_force (op1, i, mode), + immed_double_const (lo, hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + temp = expand_binop (imode, ior_optab, op0_piece, op1, + targ_piece, 1, OPTAB_LIB_WIDEN); + if (temp != targ_piece) + emit_move_insn (targ_piece, temp); + } + else + emit_move_insn (targ_piece, op0_piece); + } + + insns = get_insns (); + end_sequence (); + + emit_no_conflict_block (insns, target, op0, op1, NULL_RTX); + } + else + { + op1 = expand_binop (imode, and_optab, gen_lowpart (imode, op1), + immed_double_const (lo, hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + op0 = gen_lowpart (imode, op0); + if (!op0_is_abs) + op0 = expand_binop (imode, and_optab, op0, + immed_double_const (~lo, ~hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); + + temp = expand_binop (imode, ior_optab, op0, op1, + gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN); + target = lowpart_subreg_maybe_copy (mode, temp, imode); + } + + return target; +} + +/* Expand the C99 copysign operation. OP0 and OP1 must be the same + scalar floating point mode. Return NULL if we do not know how to + expand the operation inline. */ + +rtx +expand_copysign (rtx op0, rtx op1, rtx target) +{ + enum machine_mode mode = GET_MODE (op0); + const struct real_format *fmt; + bool op0_is_abs; + rtx temp; + + gcc_assert (SCALAR_FLOAT_MODE_P (mode)); + gcc_assert (GET_MODE (op1) == mode); + + /* First try to do it with a special instruction. */ + temp = expand_binop (mode, copysign_optab, op0, op1, + target, 0, OPTAB_DIRECT); + if (temp) + return temp; + + fmt = REAL_MODE_FORMAT (mode); + if (fmt == NULL || !fmt->has_signed_zero) + return NULL_RTX; + + op0_is_abs = false; + if (GET_CODE (op0) == CONST_DOUBLE) + { + if (real_isneg (CONST_DOUBLE_REAL_VALUE (op0))) + op0 = simplify_unary_operation (ABS, mode, op0, mode); + op0_is_abs = true; + } + + if (fmt->signbit_ro >= 0 + && (GET_CODE (op0) == CONST_DOUBLE + || (neg_optab->handlers[mode].insn_code != CODE_FOR_nothing + && abs_optab->handlers[mode].insn_code != CODE_FOR_nothing))) + { + temp = expand_copysign_absneg (mode, op0, op1, target, + fmt->signbit_ro, op0_is_abs); + if (temp) + return temp; + } + + if (fmt->signbit_rw < 0) + return NULL_RTX; + return expand_copysign_bit (mode, op0, op1, target, + fmt->signbit_rw, op0_is_abs); +} /* Generate an instruction whose insn-code is INSN_CODE, with two operands: an output TARGET and an input OP0. @@ -4740,6 +5038,7 @@ init_optabs (void) btrunc_optab = init_optab (UNKNOWN); nearbyint_optab = init_optab (UNKNOWN); rint_optab = init_optab (UNKNOWN); + lrint_optab = init_optab (UNKNOWN); sincos_optab = init_optab (UNKNOWN); sin_optab = init_optab (UNKNOWN); asin_optab = init_optab (UNKNOWN); @@ -4749,6 +5048,7 @@ init_optabs (void) exp10_optab = init_optab (UNKNOWN); exp2_optab = init_optab (UNKNOWN); expm1_optab = init_optab (UNKNOWN); + ldexp_optab = init_optab (UNKNOWN); logb_optab = init_optab (UNKNOWN); ilogb_optab = init_optab (UNKNOWN); log_optab = init_optab (UNKNOWN); @@ -4757,6 +5057,8 @@ init_optabs (void) log1p_optab = init_optab (UNKNOWN); tan_optab = init_optab (UNKNOWN); atan_optab = init_optab (UNKNOWN); + copysign_optab = init_optab (UNKNOWN); + strlen_optab = init_optab (UNKNOWN); cbranch_optab = init_optab (UNKNOWN); cmov_optab = init_optab (UNKNOWN); @@ -4767,6 +5069,9 @@ init_optabs (void) vec_set_optab = init_optab (UNKNOWN); vec_init_optab = init_optab (UNKNOWN); vec_realign_load_optab = init_optab (UNKNOWN); + movmisalign_optab = init_optab (UNKNOWN); + + powi_optab = init_optab (UNKNOWN); /* Conversions. */ sext_optab = init_convert_optab (SIGN_EXTEND); @@ -4854,6 +5159,8 @@ init_optabs (void) init_floating_libfuncs (le_optab, "le", '2'); init_floating_libfuncs (unord_optab, "unord", '2'); + init_floating_libfuncs (powi_optab, "powi", '2'); + /* Conversions. */ init_interclass_conv_libfuncs (sfloat_optab, "float", MODE_INT, MODE_FLOAT); @@ -5082,7 +5389,7 @@ vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode) tree t_op0, t_op1; rtx rtx_op0, rtx_op1; - if (TREE_CODE_CLASS (TREE_CODE (cond)) != '<') + if (!COMPARISON_CLASS_P (cond)) { /* This is unlikely. While generating VEC_COND_EXPR, auto vectorizer ensures that condition is a relational