X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Foptabs.c;h=2ed499e441fd63aeef4265bf95e2ea5eb5daeff0;hb=a866e0f2b523463de856075b7872e8acf2556c67;hp=0dfc093a7c14e2fa540f0e4289d809ed01e5d8da;hpb=209fb75310003c1eee06d3b0590340a0cd705dad;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/optabs.c b/gcc/optabs.c index 0dfc093a7c1..2ed499e441f 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. @@ -16,8 +16,8 @@ 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, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ #include "config.h" @@ -84,6 +84,12 @@ enum insn_code setcc_gen_code[NUM_RTX_CODE]; enum insn_code movcc_gen_code[NUM_MACHINE_MODES]; #endif +/* Indexed by the machine mode, gives the insn code for vector conditional + operation. */ + +enum insn_code vcond_gen_code[NUM_MACHINE_MODES]; +enum insn_code vcondu_gen_code[NUM_MACHINE_MODES]; + /* The insn generating function can not take an rtx_code argument. TRAP_RTX is used as an rtx argument. Its code is replaced with the code to be used in the trap insn and all other fields are ignored. */ @@ -92,13 +98,6 @@ static GTY(()) rtx trap_rtx; static int add_equal_note (rtx, rtx, enum rtx_code, rtx, rtx); static rtx widen_operand (rtx, enum machine_mode, enum machine_mode, int, int); -static int expand_cmplxdiv_straight (rtx, rtx, rtx, rtx, rtx, rtx, - enum machine_mode, int, - enum optab_methods, enum mode_class, - optab); -static int expand_cmplxdiv_wide (rtx, rtx, rtx, rtx, rtx, rtx, - enum machine_mode, int, enum optab_methods, - enum mode_class, optab); static void prepare_cmp_insn (rtx *, rtx *, enum rtx_code *, rtx, enum machine_mode *, int *, enum can_compare_purpose); @@ -121,15 +120,14 @@ static void emit_cmp_and_jump_insn_1 (rtx, rtx, enum machine_mode, enum rtx_code, int, rtx); static void prepare_float_lib_cmp (rtx *, rtx *, enum rtx_code *, enum machine_mode *, int *); -static rtx expand_vector_binop (enum machine_mode, optab, rtx, rtx, rtx, int, - enum optab_methods); -static rtx expand_vector_unop (enum machine_mode, optab, rtx, rtx, int); static rtx widen_clz (enum machine_mode, rtx, rtx); static rtx expand_parity (enum machine_mode, rtx, rtx); +static enum rtx_code get_rtx_code (enum tree_code, bool); +static rtx vector_compare_rtx (tree, bool, enum insn_code); #ifndef HAVE_conditional_trap #define HAVE_conditional_trap 0 -#define gen_conditional_trap(a,b) (abort (), NULL_RTX) +#define gen_conditional_trap(a,b) (gcc_unreachable (), NULL_RTX) #endif /* Add a REG_EQUAL note to the last insn in INSNS. TARGET is being set to @@ -148,10 +146,7 @@ add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1) rtx last_insn, insn, set; rtx note; - if (! insns - || ! INSN_P (insns) - || NEXT_INSN (insns) == NULL_RTX) - abort (); + gcc_assert (insns && INSN_P (insns) && NEXT_INSN (insns)); if (GET_RTX_CLASS (code) != RTX_COMM_ARITH && GET_RTX_CLASS (code) != RTX_BIN_ARITH @@ -241,391 +236,752 @@ widen_operand (rtx op, enum machine_mode mode, enum machine_mode oldmode, return result; } -/* Generate code to perform a straightforward complex divide. */ - -static int -expand_cmplxdiv_straight (rtx real0, rtx real1, rtx imag0, rtx imag1, - rtx realr, rtx imagr, enum machine_mode submode, - int unsignedp, enum optab_methods methods, - enum mode_class class, optab binoptab) +/* Return the optab used for computing the operation given by + the tree code, CODE. This function is not always usable (for + example, it cannot give complete results for multiplication + or division) but probably ought to be relied on more widely + throughout the expander. */ +optab +optab_for_tree_code (enum tree_code code, tree type) { - rtx divisor; - rtx real_t, imag_t; - rtx temp1, temp2; - rtx res; - optab this_add_optab = add_optab; - optab this_sub_optab = sub_optab; - optab this_neg_optab = neg_optab; - optab this_mul_optab = smul_optab; - - if (binoptab == sdivv_optab) + bool trapv; + switch (code) { - this_add_optab = addv_optab; - this_sub_optab = subv_optab; - this_neg_optab = negv_optab; - this_mul_optab = smulv_optab; - } - - /* Don't fetch these from memory more than once. */ - real0 = force_reg (submode, real0); - real1 = force_reg (submode, real1); + case BIT_AND_EXPR: + return and_optab; - if (imag0 != 0) - imag0 = force_reg (submode, imag0); + case BIT_IOR_EXPR: + return ior_optab; - imag1 = force_reg (submode, imag1); + case BIT_NOT_EXPR: + return one_cmpl_optab; - /* Divisor: c*c + d*d. */ - temp1 = expand_binop (submode, this_mul_optab, real1, real1, - NULL_RTX, unsignedp, methods); + case BIT_XOR_EXPR: + return xor_optab; - temp2 = expand_binop (submode, this_mul_optab, imag1, imag1, - NULL_RTX, unsignedp, methods); - - if (temp1 == 0 || temp2 == 0) - return 0; + case TRUNC_MOD_EXPR: + case CEIL_MOD_EXPR: + case FLOOR_MOD_EXPR: + case ROUND_MOD_EXPR: + return TYPE_UNSIGNED (type) ? umod_optab : smod_optab; - divisor = expand_binop (submode, this_add_optab, temp1, temp2, - NULL_RTX, unsignedp, methods); - if (divisor == 0) - return 0; + case RDIV_EXPR: + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case EXACT_DIV_EXPR: + return TYPE_UNSIGNED (type) ? udiv_optab : sdiv_optab; - if (imag0 == 0) - { - /* Mathematically, ((a)(c-id))/divisor. */ - /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)). */ + case LSHIFT_EXPR: + return ashl_optab; - /* Calculate the dividend. */ - real_t = expand_binop (submode, this_mul_optab, real0, real1, - NULL_RTX, unsignedp, methods); + case RSHIFT_EXPR: + return TYPE_UNSIGNED (type) ? lshr_optab : ashr_optab; - imag_t = expand_binop (submode, this_mul_optab, real0, imag1, - NULL_RTX, unsignedp, methods); + case LROTATE_EXPR: + return rotl_optab; - if (real_t == 0 || imag_t == 0) - return 0; + case RROTATE_EXPR: + return rotr_optab; - imag_t = expand_unop (submode, this_neg_optab, imag_t, - NULL_RTX, unsignedp); - } - else - { - /* Mathematically, ((a+ib)(c-id))/divider. */ - /* Calculate the dividend. */ - temp1 = expand_binop (submode, this_mul_optab, real0, real1, - NULL_RTX, unsignedp, methods); + case MAX_EXPR: + return TYPE_UNSIGNED (type) ? umax_optab : smax_optab; - temp2 = expand_binop (submode, this_mul_optab, imag0, imag1, - NULL_RTX, unsignedp, methods); + case MIN_EXPR: + return TYPE_UNSIGNED (type) ? umin_optab : smin_optab; - if (temp1 == 0 || temp2 == 0) - return 0; + case REALIGN_LOAD_EXPR: + return vec_realign_load_optab; - real_t = expand_binop (submode, this_add_optab, temp1, temp2, - NULL_RTX, unsignedp, methods); + case REDUC_MAX_EXPR: + return TYPE_UNSIGNED (type) ? reduc_umax_optab : reduc_smax_optab; - temp1 = expand_binop (submode, this_mul_optab, imag0, real1, - NULL_RTX, unsignedp, methods); + case REDUC_MIN_EXPR: + return TYPE_UNSIGNED (type) ? reduc_umin_optab : reduc_smin_optab; - temp2 = expand_binop (submode, this_mul_optab, real0, imag1, - NULL_RTX, unsignedp, methods); + case REDUC_PLUS_EXPR: + return TYPE_UNSIGNED (type) ? reduc_uplus_optab : reduc_splus_optab; - if (temp1 == 0 || temp2 == 0) - return 0; + case VEC_LSHIFT_EXPR: + return vec_shl_optab; - imag_t = expand_binop (submode, this_sub_optab, temp1, temp2, - NULL_RTX, unsignedp, methods); + case VEC_RSHIFT_EXPR: + return vec_shr_optab; - if (real_t == 0 || imag_t == 0) - return 0; + default: + break; } - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, real_t, divisor, - realr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - real_t, divisor, realr, unsignedp); - - if (res == 0) - return 0; + trapv = flag_trapv && INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type); + switch (code) + { + case PLUS_EXPR: + return trapv ? addv_optab : add_optab; - if (res != realr) - emit_move_insn (realr, res); + case MINUS_EXPR: + return trapv ? subv_optab : sub_optab; - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, imag_t, divisor, - imagr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - imag_t, divisor, imagr, unsignedp); + case MULT_EXPR: + return trapv ? smulv_optab : smul_optab; - if (res == 0) - return 0; + case NEGATE_EXPR: + return trapv ? negv_optab : neg_optab; - if (res != imagr) - emit_move_insn (imagr, res); + case ABS_EXPR: + return trapv ? absv_optab : abs_optab; - return 1; + default: + return NULL; + } } -/* Generate code to perform a wide-input-range-acceptable complex divide. */ -static int -expand_cmplxdiv_wide (rtx real0, rtx real1, rtx imag0, rtx imag1, rtx realr, - rtx imagr, enum machine_mode submode, int unsignedp, - enum optab_methods methods, enum mode_class class, - optab binoptab) -{ - rtx ratio, divisor; - rtx real_t, imag_t; - rtx temp1, temp2, lab1, lab2; - enum machine_mode mode; - rtx res; - optab this_add_optab = add_optab; - optab this_sub_optab = sub_optab; - optab this_neg_optab = neg_optab; - optab this_mul_optab = smul_optab; +/* Generate code to perform an operation specified by TERNARY_OPTAB + on operands OP0, OP1 and OP2, with result having machine-mode MODE. - if (binoptab == sdivv_optab) - { - this_add_optab = addv_optab; - this_sub_optab = subv_optab; - this_neg_optab = negv_optab; - this_mul_optab = smulv_optab; - } + UNSIGNEDP is for the case where we have to widen the operands + to perform the operation. It says to use zero-extension. - /* Don't fetch these from memory more than once. */ - real0 = force_reg (submode, real0); - real1 = force_reg (submode, real1); + If TARGET is nonzero, the value + is generated there, if it is convenient to do so. + In all cases an rtx is returned for the locus of the value; + this may or may not be TARGET. */ - if (imag0 != 0) - imag0 = force_reg (submode, imag0); +rtx +expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0, + rtx op1, rtx op2, rtx target, int unsignedp) +{ + int icode = (int) ternary_optab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_data[icode].operand[1].mode; + enum machine_mode mode1 = insn_data[icode].operand[2].mode; + enum machine_mode mode2 = insn_data[icode].operand[3].mode; + rtx temp; + rtx pat; + rtx xop0 = op0, xop1 = op1, xop2 = op2; - imag1 = force_reg (submode, imag1); + gcc_assert (ternary_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing); - /* XXX What's an "unsigned" complex number? */ - if (unsignedp) - { - temp1 = real1; - temp2 = imag1; - } + if (!target || !insn_data[icode].operand[0].predicate (target, mode)) + temp = gen_reg_rtx (mode); else - { - temp1 = expand_abs (submode, real1, NULL_RTX, unsignedp, 1); - temp2 = expand_abs (submode, imag1, NULL_RTX, unsignedp, 1); - } - - if (temp1 == 0 || temp2 == 0) - return 0; + temp = target; + + /* In case the insn wants input operands in modes different from + those of the actual operands, convert the operands. It would + seem that we don't need to convert CONST_INTs, but we do, so + that they're properly zero-extended, sign-extended or truncated + for their mode. */ + + if (GET_MODE (op0) != mode0 && mode0 != VOIDmode) + xop0 = convert_modes (mode0, + GET_MODE (op0) != VOIDmode + ? GET_MODE (op0) + : mode, + xop0, unsignedp); + + if (GET_MODE (op1) != mode1 && mode1 != VOIDmode) + xop1 = convert_modes (mode1, + GET_MODE (op1) != VOIDmode + ? GET_MODE (op1) + : mode, + xop1, unsignedp); + + if (GET_MODE (op2) != mode2 && mode2 != VOIDmode) + xop2 = convert_modes (mode2, + GET_MODE (op2) != VOIDmode + ? GET_MODE (op2) + : mode, + xop2, unsignedp); + + /* Now, if insn's predicates don't allow our operands, put them into + pseudo regs. */ + + if (!insn_data[icode].operand[1].predicate (xop0, mode0) + && mode0 != VOIDmode) + xop0 = copy_to_mode_reg (mode0, xop0); + + if (!insn_data[icode].operand[2].predicate (xop1, mode1) + && mode1 != VOIDmode) + xop1 = copy_to_mode_reg (mode1, xop1); + + if (!insn_data[icode].operand[3].predicate (xop2, mode2) + && mode2 != VOIDmode) + xop2 = copy_to_mode_reg (mode2, xop2); + + pat = GEN_FCN (icode) (temp, xop0, xop1, xop2); + + emit_insn (pat); + return temp; +} - mode = GET_MODE (temp1); - lab1 = gen_label_rtx (); - emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX, - mode, unsignedp, lab1); - /* |c| >= |d|; use ratio d/c to scale dividend and divisor. */ +/* Like expand_binop, but return a constant rtx if the result can be + calculated at compile time. The arguments and return value are + otherwise the same as for expand_binop. */ - if (class == MODE_COMPLEX_FLOAT) - ratio = expand_binop (submode, binoptab, imag1, real1, - NULL_RTX, unsignedp, methods); +static rtx +simplify_expand_binop (enum machine_mode mode, optab binoptab, + rtx op0, rtx op1, rtx target, int unsignedp, + enum optab_methods methods) +{ + if (CONSTANT_P (op0) && CONSTANT_P (op1)) + return simplify_gen_binary (binoptab->code, mode, op0, op1); else - ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode, - imag1, real1, NULL_RTX, unsignedp); - - if (ratio == 0) - return 0; + return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods); +} - /* Calculate divisor. */ +/* Like simplify_expand_binop, but always put the result in TARGET. + Return true if the expansion succeeded. */ - temp1 = expand_binop (submode, this_mul_optab, imag1, ratio, - NULL_RTX, unsignedp, methods); +bool +force_expand_binop (enum machine_mode mode, optab binoptab, + rtx op0, rtx op1, rtx target, int unsignedp, + enum optab_methods methods) +{ + rtx x = simplify_expand_binop (mode, binoptab, op0, op1, + target, unsignedp, methods); + if (x == 0) + return false; + if (x != target) + emit_move_insn (target, x); + return true; +} - if (temp1 == 0) - return 0; +/* Generate insns for VEC_LSHIFT_EXPR, VEC_RSHIFT_EXPR. */ - divisor = expand_binop (submode, this_add_optab, temp1, real1, - NULL_RTX, unsignedp, methods); +rtx +expand_vec_shift_expr (tree vec_shift_expr, rtx target) +{ + enum insn_code icode; + rtx rtx_op1, rtx_op2; + enum machine_mode mode1; + enum machine_mode mode2; + enum machine_mode mode = TYPE_MODE (TREE_TYPE (vec_shift_expr)); + tree vec_oprnd = TREE_OPERAND (vec_shift_expr, 0); + tree shift_oprnd = TREE_OPERAND (vec_shift_expr, 1); + optab shift_optab; + rtx pat; - if (divisor == 0) - return 0; + switch (TREE_CODE (vec_shift_expr)) + { + case VEC_RSHIFT_EXPR: + shift_optab = vec_shr_optab; + break; + case VEC_LSHIFT_EXPR: + shift_optab = vec_shl_optab; + break; + default: + gcc_unreachable (); + } - /* Calculate dividend. */ + icode = (int) shift_optab->handlers[(int) mode].insn_code; + gcc_assert (icode != CODE_FOR_nothing); - if (imag0 == 0) - { - real_t = real0; + mode1 = insn_data[icode].operand[1].mode; + mode2 = insn_data[icode].operand[2].mode; - /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)). */ + rtx_op1 = expand_expr (vec_oprnd, NULL_RTX, VOIDmode, EXPAND_NORMAL); + if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1) + && mode1 != VOIDmode) + rtx_op1 = force_reg (mode1, rtx_op1); - imag_t = expand_binop (submode, this_mul_optab, real0, ratio, - NULL_RTX, unsignedp, methods); + rtx_op2 = expand_expr (shift_oprnd, NULL_RTX, VOIDmode, EXPAND_NORMAL); + if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2) + && mode2 != VOIDmode) + rtx_op2 = force_reg (mode2, rtx_op2); - if (imag_t == 0) - return 0; + if (!target + || ! (*insn_data[icode].operand[0].predicate) (target, mode)) + target = gen_reg_rtx (mode); - imag_t = expand_unop (submode, this_neg_optab, imag_t, - NULL_RTX, unsignedp); + /* Emit instruction */ + pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2); + gcc_assert (pat); + emit_insn (pat); - if (real_t == 0 || imag_t == 0) - return 0; - } - else - { - /* Compute (a+ib)/(c+id) as - (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)). */ + return target; +} - temp1 = expand_binop (submode, this_mul_optab, imag0, ratio, - NULL_RTX, unsignedp, methods); +/* This subroutine of expand_doubleword_shift handles the cases in which + the effective shift value is >= BITS_PER_WORD. The arguments and return + value are the same as for the parent routine, except that SUPERWORD_OP1 + is the shift count to use when shifting OUTOF_INPUT into INTO_TARGET. + INTO_TARGET may be null if the caller has decided to calculate it. */ - if (temp1 == 0) - return 0; +static bool +expand_superword_shift (optab binoptab, rtx outof_input, rtx superword_op1, + rtx outof_target, rtx into_target, + int unsignedp, enum optab_methods methods) +{ + if (into_target != 0) + if (!force_expand_binop (word_mode, binoptab, outof_input, superword_op1, + into_target, unsignedp, methods)) + return false; - real_t = expand_binop (submode, this_add_optab, temp1, real0, - NULL_RTX, unsignedp, methods); + if (outof_target != 0) + { + /* For a signed right shift, we must fill OUTOF_TARGET with copies + of the sign bit, otherwise we must fill it with zeros. */ + if (binoptab != ashr_optab) + emit_move_insn (outof_target, CONST0_RTX (word_mode)); + else + if (!force_expand_binop (word_mode, binoptab, + outof_input, GEN_INT (BITS_PER_WORD - 1), + outof_target, unsignedp, methods)) + return false; + } + return true; +} - temp1 = expand_binop (submode, this_mul_optab, real0, ratio, - NULL_RTX, unsignedp, methods); +/* This subroutine of expand_doubleword_shift handles the cases in which + the effective shift value is < BITS_PER_WORD. The arguments and return + value are the same as for the parent routine. */ - if (temp1 == 0) - return 0; +static bool +expand_subword_shift (enum machine_mode op1_mode, optab binoptab, + rtx outof_input, rtx into_input, rtx op1, + rtx outof_target, rtx into_target, + int unsignedp, enum optab_methods methods, + unsigned HOST_WIDE_INT shift_mask) +{ + optab reverse_unsigned_shift, unsigned_shift; + rtx tmp, carries; - imag_t = expand_binop (submode, this_sub_optab, imag0, temp1, - NULL_RTX, unsignedp, methods); + reverse_unsigned_shift = (binoptab == ashl_optab ? lshr_optab : ashl_optab); + unsigned_shift = (binoptab == ashl_optab ? ashl_optab : lshr_optab); - if (real_t == 0 || imag_t == 0) - return 0; + /* The low OP1 bits of INTO_TARGET come from the high bits of OUTOF_INPUT. + We therefore need to shift OUTOF_INPUT by (BITS_PER_WORD - OP1) bits in + the opposite direction to BINOPTAB. */ + if (CONSTANT_P (op1) || shift_mask >= BITS_PER_WORD) + { + carries = outof_input; + tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode); + tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1, + 0, true, methods); } - - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, real_t, divisor, - realr, unsignedp, methods); else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - real_t, divisor, realr, unsignedp); + { + /* We must avoid shifting by BITS_PER_WORD bits since that is either + the same as a zero shift (if shift_mask == BITS_PER_WORD - 1) or + has unknown behavior. Do a single shift first, then shift by the + remainder. It's OK to use ~OP1 as the remainder if shift counts + are truncated to the mode size. */ + carries = expand_binop (word_mode, reverse_unsigned_shift, + outof_input, const1_rtx, 0, unsignedp, methods); + if (shift_mask == BITS_PER_WORD - 1) + { + tmp = immed_double_const (-1, -1, op1_mode); + tmp = simplify_expand_binop (op1_mode, xor_optab, op1, tmp, + 0, true, methods); + } + else + { + tmp = immed_double_const (BITS_PER_WORD - 1, 0, op1_mode); + tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1, + 0, true, methods); + } + } + if (tmp == 0 || carries == 0) + return false; + carries = expand_binop (word_mode, reverse_unsigned_shift, + carries, tmp, 0, unsignedp, methods); + if (carries == 0) + return false; + + /* Shift INTO_INPUT logically by OP1. This is the last use of INTO_INPUT + so the result can go directly into INTO_TARGET if convenient. */ + tmp = expand_binop (word_mode, unsigned_shift, into_input, op1, + into_target, unsignedp, methods); + if (tmp == 0) + return false; + + /* Now OR in the bits carried over from OUTOF_INPUT. */ + if (!force_expand_binop (word_mode, ior_optab, tmp, carries, + into_target, unsignedp, methods)) + return false; + + /* Use a standard word_mode shift for the out-of half. */ + if (outof_target != 0) + if (!force_expand_binop (word_mode, binoptab, outof_input, op1, + outof_target, unsignedp, methods)) + return false; + + return true; +} - if (res == 0) - return 0; - if (res != realr) - emit_move_insn (realr, res); +#ifdef HAVE_conditional_move +/* Try implementing expand_doubleword_shift using conditional moves. + The shift is by < BITS_PER_WORD if (CMP_CODE CMP1 CMP2) is true, + otherwise it is by >= BITS_PER_WORD. SUBWORD_OP1 and SUPERWORD_OP1 + are the shift counts to use in the former and latter case. All other + arguments are the same as the parent routine. */ + +static bool +expand_doubleword_shift_condmove (enum machine_mode op1_mode, optab binoptab, + enum rtx_code cmp_code, rtx cmp1, rtx cmp2, + rtx outof_input, rtx into_input, + rtx subword_op1, rtx superword_op1, + rtx outof_target, rtx into_target, + int unsignedp, enum optab_methods methods, + unsigned HOST_WIDE_INT shift_mask) +{ + rtx outof_superword, into_superword; - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, imag_t, divisor, - imagr, unsignedp, methods); + /* Put the superword version of the output into OUTOF_SUPERWORD and + INTO_SUPERWORD. */ + outof_superword = outof_target != 0 ? gen_reg_rtx (word_mode) : 0; + if (outof_target != 0 && subword_op1 == superword_op1) + { + /* The value INTO_TARGET >> SUBWORD_OP1, which we later store in + OUTOF_TARGET, is the same as the value of INTO_SUPERWORD. */ + into_superword = outof_target; + if (!expand_superword_shift (binoptab, outof_input, superword_op1, + outof_superword, 0, unsignedp, methods)) + return false; + } else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - imag_t, divisor, imagr, unsignedp); + { + into_superword = gen_reg_rtx (word_mode); + if (!expand_superword_shift (binoptab, outof_input, superword_op1, + outof_superword, into_superword, + unsignedp, methods)) + return false; + } - if (res == 0) - return 0; + /* Put the subword version directly in OUTOF_TARGET and INTO_TARGET. */ + if (!expand_subword_shift (op1_mode, binoptab, + outof_input, into_input, subword_op1, + outof_target, into_target, + unsignedp, methods, shift_mask)) + return false; + + /* Select between them. Do the INTO half first because INTO_SUPERWORD + might be the current value of OUTOF_TARGET. */ + if (!emit_conditional_move (into_target, cmp_code, cmp1, cmp2, op1_mode, + into_target, into_superword, word_mode, false)) + return false; + + if (outof_target != 0) + if (!emit_conditional_move (outof_target, cmp_code, cmp1, cmp2, op1_mode, + outof_target, outof_superword, + word_mode, false)) + return false; + + return true; +} +#endif - if (res != imagr) - emit_move_insn (imagr, res); +/* Expand a doubleword shift (ashl, ashr or lshr) using word-mode shifts. + OUTOF_INPUT and INTO_INPUT are the two word-sized halves of the first + input operand; the shift moves bits in the direction OUTOF_INPUT-> + INTO_TARGET. OUTOF_TARGET and INTO_TARGET are the equivalent words + of the target. OP1 is the shift count and OP1_MODE is its mode. + If OP1 is constant, it will have been truncated as appropriate + and is known to be nonzero. + + If SHIFT_MASK is zero, the result of word shifts is undefined when the + shift count is outside the range [0, BITS_PER_WORD). This routine must + avoid generating such shifts for OP1s in the range [0, BITS_PER_WORD * 2). + + If SHIFT_MASK is nonzero, all word-mode shift counts are effectively + masked by it and shifts in the range [BITS_PER_WORD, SHIFT_MASK) will + fill with zeros or sign bits as appropriate. + + If SHIFT_MASK is BITS_PER_WORD - 1, this routine will synthesize + a doubleword shift whose equivalent mask is BITS_PER_WORD * 2 - 1. + Doing this preserves semantics required by SHIFT_COUNT_TRUNCATED. + In all other cases, shifts by values outside [0, BITS_PER_UNIT * 2) + are undefined. + + BINOPTAB, UNSIGNEDP and METHODS are as for expand_binop. This function + may not use INTO_INPUT after modifying INTO_TARGET, and similarly for + OUTOF_INPUT and OUTOF_TARGET. OUTOF_TARGET can be null if the parent + function wants to calculate it itself. + + Return true if the shift could be successfully synthesized. */ + +static bool +expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab, + rtx outof_input, rtx into_input, rtx op1, + rtx outof_target, rtx into_target, + int unsignedp, enum optab_methods methods, + unsigned HOST_WIDE_INT shift_mask) +{ + rtx superword_op1, tmp, cmp1, cmp2; + rtx subword_label, done_label; + enum rtx_code cmp_code; + + /* See if word-mode shifts by BITS_PER_WORD...BITS_PER_WORD * 2 - 1 will + fill the result with sign or zero bits as appropriate. If so, the value + of OUTOF_TARGET will always be (SHIFT OUTOF_INPUT OP1). Recursively call + this routine to calculate INTO_TARGET (which depends on both OUTOF_INPUT + and INTO_INPUT), then emit code to set up OUTOF_TARGET. + + This isn't worthwhile for constant shifts since the optimizers will + cope better with in-range shift counts. */ + if (shift_mask >= BITS_PER_WORD + && outof_target != 0 + && !CONSTANT_P (op1)) + { + if (!expand_doubleword_shift (op1_mode, binoptab, + outof_input, into_input, op1, + 0, into_target, + unsignedp, methods, shift_mask)) + return false; + if (!force_expand_binop (word_mode, binoptab, outof_input, op1, + outof_target, unsignedp, methods)) + return false; + return true; + } - lab2 = gen_label_rtx (); - emit_jump_insn (gen_jump (lab2)); - emit_barrier (); + /* Set CMP_CODE, CMP1 and CMP2 so that the rtx (CMP_CODE CMP1 CMP2) + is true when the effective shift value is less than BITS_PER_WORD. + Set SUPERWORD_OP1 to the shift count that should be used to shift + OUTOF_INPUT into INTO_TARGET when the condition is false. */ + tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode); + if (!CONSTANT_P (op1) && shift_mask == BITS_PER_WORD - 1) + { + /* Set CMP1 to OP1 & BITS_PER_WORD. The result is zero iff OP1 + is a subword shift count. */ + cmp1 = simplify_expand_binop (op1_mode, and_optab, op1, tmp, + 0, true, methods); + cmp2 = CONST0_RTX (op1_mode); + cmp_code = EQ; + superword_op1 = op1; + } + else + { + /* Set CMP1 to OP1 - BITS_PER_WORD. */ + cmp1 = simplify_expand_binop (op1_mode, sub_optab, op1, tmp, + 0, true, methods); + cmp2 = CONST0_RTX (op1_mode); + cmp_code = LT; + superword_op1 = cmp1; + } + if (cmp1 == 0) + return false; - emit_label (lab1); + /* If we can compute the condition at compile time, pick the + appropriate subroutine. */ + tmp = simplify_relational_operation (cmp_code, SImode, op1_mode, cmp1, cmp2); + if (tmp != 0 && GET_CODE (tmp) == CONST_INT) + { + if (tmp == const0_rtx) + return expand_superword_shift (binoptab, outof_input, superword_op1, + outof_target, into_target, + unsignedp, methods); + else + return expand_subword_shift (op1_mode, binoptab, + outof_input, into_input, op1, + outof_target, into_target, + unsignedp, methods, shift_mask); + } - /* |d| > |c|; use ratio c/d to scale dividend and divisor. */ +#ifdef HAVE_conditional_move + /* Try using conditional moves to generate straight-line code. */ + { + rtx start = get_last_insn (); + if (expand_doubleword_shift_condmove (op1_mode, binoptab, + cmp_code, cmp1, cmp2, + outof_input, into_input, + op1, superword_op1, + outof_target, into_target, + unsignedp, methods, shift_mask)) + return true; + delete_insns_since (start); + } +#endif - if (class == MODE_COMPLEX_FLOAT) - ratio = expand_binop (submode, binoptab, real1, imag1, - NULL_RTX, unsignedp, methods); - else - ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode, - real1, imag1, NULL_RTX, unsignedp); + /* As a last resort, use branches to select the correct alternative. */ + subword_label = gen_label_rtx (); + done_label = gen_label_rtx (); - if (ratio == 0) - return 0; + do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode, + 0, 0, subword_label); - /* Calculate divisor. */ + if (!expand_superword_shift (binoptab, outof_input, superword_op1, + outof_target, into_target, + unsignedp, methods)) + return false; - temp1 = expand_binop (submode, this_mul_optab, real1, ratio, - NULL_RTX, unsignedp, methods); + emit_jump_insn (gen_jump (done_label)); + emit_barrier (); + emit_label (subword_label); - if (temp1 == 0) - return 0; + if (!expand_subword_shift (op1_mode, binoptab, + outof_input, into_input, op1, + outof_target, into_target, + unsignedp, methods, shift_mask)) + return false; - divisor = expand_binop (submode, this_add_optab, temp1, imag1, - NULL_RTX, unsignedp, methods); + emit_label (done_label); + 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_] - if (divisor == 0) - return 0; - /* Calculate dividend. */ + 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. - if (imag0 == 0) - { - /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d). */ + (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: - real_t = expand_binop (submode, this_mul_optab, real0, ratio, - NULL_RTX, unsignedp, methods); + If both operands are positive then no adjustment is needed. - imag_t = expand_unop (submode, this_neg_optab, real0, - NULL_RTX, unsignedp); + 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. - if (real_t == 0 || imag_t == 0) - return 0; - } - else - { - /* Compute (a+ib)/(c+id) as - (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d). */ + Similarly, if both operands are negative, we need to add + (op0_low + op1_low) * 2**BITS_PER_WORD. - temp1 = expand_binop (submode, this_mul_optab, real0, ratio, - NULL_RTX, unsignedp, methods); + 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. */ - if (temp1 == 0) - return 0; +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); + } - real_t = expand_binop (submode, this_add_optab, temp1, imag0, - NULL_RTX, unsignedp, methods); + if (!op0_high) + return NULL_RTX; + } - temp1 = expand_binop (submode, this_mul_optab, imag0, ratio, - NULL_RTX, unsignedp, methods); + adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low, + NULL_RTX, 0, OPTAB_DIRECT); + if (!adjust) + return NULL_RTX; - if (temp1 == 0) - return 0; + /* OP0_HIGH should now be dead. */ - imag_t = expand_binop (submode, this_sub_optab, temp1, real0, - NULL_RTX, unsignedp, methods); + 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 (real_t == 0 || imag_t == 0) - return 0; + if (!op1_high) + return NULL_RTX; } - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, real_t, divisor, - realr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - real_t, divisor, realr, unsignedp); - - if (res == 0) - return 0; + temp = expand_binop (word_mode, smul_optab, op1_high, op0_low, + NULL_RTX, 0, OPTAB_DIRECT); + if (!temp) + return NULL_RTX; - if (res != realr) - emit_move_insn (realr, res); + /* OP1_HIGH should now be dead. */ - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, imag_t, divisor, - imagr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - imag_t, divisor, imagr, unsignedp); + adjust = expand_binop (word_mode, add_optab, adjust, temp, + adjust, 0, OPTAB_DIRECT); - if (res == 0) - return 0; + if (target && !REG_P (target)) + target = NULL_RTX; - if (res != imagr) - emit_move_insn (imagr, res); + 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); - emit_label (lab2); + if (!product) + return NULL_RTX; - return 1; + 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 @@ -637,8 +993,7 @@ expand_simple_binop (enum machine_mode mode, enum rtx_code code, rtx op0, enum optab_methods methods) { optab binop = code_to_optab[(int) code]; - if (binop == 0) - abort (); + gcc_assert (binop); return expand_binop (mode, binop, op0, op1, target, unsignedp, methods); } @@ -675,11 +1030,6 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, class = GET_MODE_CLASS (mode); - op0 = protect_from_queue (op0, 0); - op1 = protect_from_queue (op1, 0); - if (target) - target = protect_from_queue (target, 1); - if (flag_force_mem) { /* Load duplicate non-volatile operands once. */ @@ -704,15 +1054,23 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, binoptab = add_optab; } - /* If we are inside an appropriately-short loop and one operand is an - expensive constant, force it into a register. */ - if (CONSTANT_P (op0) && preserve_subexpressions_p () + /* If we are inside an appropriately-short loop and we are optimizing, + force expensive constants into a register. */ + if (CONSTANT_P (op0) && optimize && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1)) - op0 = force_reg (mode, op0); + { + if (GET_MODE (op0) != VOIDmode) + op0 = convert_modes (mode, VOIDmode, op0, unsignedp); + op0 = force_reg (mode, op0); + } - if (CONSTANT_P (op1) && preserve_subexpressions_p () + if (CONSTANT_P (op1) && optimize && ! shift_op && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1)) - op1 = force_reg (mode, op1); + { + if (GET_MODE (op1) != VOIDmode) + op1 = convert_modes (mode, VOIDmode, op1, unsignedp); + op1 = force_reg (mode, op1); + } /* Record where to delete back to if we backtrack. */ last = get_last_insn (); @@ -729,9 +1087,9 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, { commutative_op = 1; - if (((target == 0 || GET_CODE (target) == REG) - ? ((GET_CODE (op1) == REG - && GET_CODE (op0) != REG) + if (((target == 0 || REG_P (target)) + ? ((REG_P (op1) + && !REG_P (op0)) || target == op1) : rtx_equal_p (op1, target)) || GET_CODE (op0) == CONST_INT) @@ -795,15 +1153,15 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, /* Now, if insn's predicates don't allow our operands, put them into pseudo regs. */ - if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0) + if (!insn_data[icode].operand[1].predicate (xop0, mode0) && mode0 != VOIDmode) xop0 = copy_to_mode_reg (mode0, xop0); - if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1) + if (!insn_data[icode].operand[2].predicate (xop1, mode1) && mode1 != VOIDmode) xop1 = copy_to_mode_reg (mode1, xop1); - if (! (*insn_data[icode].operand[0].predicate) (temp, mode)) + if (!insn_data[icode].operand[0].predicate (temp, mode)) temp = gen_reg_rtx (mode); pat = GEN_FCN (icode) (temp, xop0, xop1); @@ -957,118 +1315,71 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, if ((binoptab == lshr_optab || binoptab == ashl_optab || binoptab == ashr_optab) && class == MODE_INT - && GET_CODE (op1) == CONST_INT + && (GET_CODE (op1) == CONST_INT || !optimize_size) && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) { - rtx insns, inter, equiv_value; - rtx into_target, outof_target; - rtx into_input, outof_input; - int shift_count, left_shift, outof_word; - - /* If TARGET is the same as one of the operands, the REG_EQUAL note - won't be accurate, so use a new target. */ - if (target == 0 || target == op0 || target == op1) - target = gen_reg_rtx (mode); - - start_sequence (); - - shift_count = INTVAL (op1); - - /* OUTOF_* is the word we are shifting bits away from, and - INTO_* is the word that we are shifting bits towards, thus - they differ depending on the direction of the shift and - WORDS_BIG_ENDIAN. */ - - left_shift = binoptab == ashl_optab; - outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; - - outof_target = operand_subword (target, outof_word, 1, mode); - into_target = operand_subword (target, 1 - outof_word, 1, mode); + unsigned HOST_WIDE_INT shift_mask, double_shift_mask; + enum machine_mode op1_mode; - outof_input = operand_subword_force (op0, outof_word, mode); - into_input = operand_subword_force (op0, 1 - outof_word, mode); - - if (shift_count >= BITS_PER_WORD) - { - inter = expand_binop (word_mode, binoptab, - outof_input, - GEN_INT (shift_count - BITS_PER_WORD), - into_target, unsignedp, next_methods); + double_shift_mask = targetm.shift_truncation_mask (mode); + shift_mask = targetm.shift_truncation_mask (word_mode); + op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode; - if (inter != 0 && inter != into_target) - emit_move_insn (into_target, inter); + /* Apply the truncation to constant shifts. */ + if (double_shift_mask > 0 && GET_CODE (op1) == CONST_INT) + op1 = GEN_INT (INTVAL (op1) & double_shift_mask); - /* For a signed right shift, we must fill the word we are shifting - out of with copies of the sign bit. Otherwise it is zeroed. */ - if (inter != 0 && binoptab != ashr_optab) - inter = CONST0_RTX (word_mode); - else if (inter != 0) - inter = expand_binop (word_mode, binoptab, - outof_input, - GEN_INT (BITS_PER_WORD - 1), - outof_target, unsignedp, next_methods); + if (op1 == CONST0_RTX (op1_mode)) + return op0; - if (inter != 0 && inter != outof_target) - emit_move_insn (outof_target, inter); - } - else + /* Make sure that this is a combination that expand_doubleword_shift + can handle. See the comments there for details. */ + if (double_shift_mask == 0 + || (shift_mask == BITS_PER_WORD - 1 + && double_shift_mask == BITS_PER_WORD * 2 - 1)) { - rtx carries; - optab reverse_unsigned_shift, unsigned_shift; + rtx insns, equiv_value; + rtx into_target, outof_target; + rtx into_input, outof_input; + int left_shift, outof_word; - /* For a shift of less then BITS_PER_WORD, to compute the carry, - we must do a logical shift in the opposite direction of the - desired shift. */ + /* If TARGET is the same as one of the operands, the REG_EQUAL note + won't be accurate, so use a new target. */ + if (target == 0 || target == op0 || target == op1) + target = gen_reg_rtx (mode); - reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab); + start_sequence (); - /* For a shift of less than BITS_PER_WORD, to compute the word - shifted towards, we need to unsigned shift the orig value of - that word. */ + /* OUTOF_* is the word we are shifting bits away from, and + INTO_* is the word that we are shifting bits towards, thus + they differ depending on the direction of the shift and + WORDS_BIG_ENDIAN. */ - unsigned_shift = (left_shift ? ashl_optab : lshr_optab); + left_shift = binoptab == ashl_optab; + outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; - carries = expand_binop (word_mode, reverse_unsigned_shift, - outof_input, - GEN_INT (BITS_PER_WORD - shift_count), - 0, unsignedp, next_methods); + outof_target = operand_subword (target, outof_word, 1, mode); + into_target = operand_subword (target, 1 - outof_word, 1, mode); - if (carries == 0) - inter = 0; - else - inter = expand_binop (word_mode, unsigned_shift, into_input, - op1, 0, unsignedp, next_methods); - - if (inter != 0) - inter = expand_binop (word_mode, ior_optab, carries, inter, - into_target, unsignedp, next_methods); - - if (inter != 0 && inter != into_target) - emit_move_insn (into_target, inter); - - if (inter != 0) - inter = expand_binop (word_mode, binoptab, outof_input, - op1, outof_target, unsignedp, next_methods); + outof_input = operand_subword_force (op0, outof_word, mode); + into_input = operand_subword_force (op0, 1 - outof_word, mode); - if (inter != 0 && inter != outof_target) - emit_move_insn (outof_target, inter); - } - - insns = get_insns (); - end_sequence (); - - if (inter != 0) - { - if (binoptab->code != UNKNOWN) - equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1); - else - equiv_value = 0; + if (expand_doubleword_shift (op1_mode, binoptab, + outof_input, into_input, op1, + outof_target, into_target, + unsignedp, methods, shift_mask)) + { + insns = get_insns (); + end_sequence (); - emit_no_conflict_block (insns, target, op0, op1, equiv_value); - return target; + equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1); + emit_no_conflict_block (insns, target, op0, op1, equiv_value); + return target; + } + end_sequence (); } } @@ -1089,7 +1400,7 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, /* If TARGET is the same as one of the operands, the REG_EQUAL note won't be accurate, so use a new target. Do this also if target is not a REG, first because having a register instead may open optimization - oportunities, and second because if target and op0 happen to be MEMs + opportunities, and second because if target and op0 happen to be MEMs designating the same location, we would risk clobbering it too early in the code sequence we generate below. */ if (target == 0 || target == op0 || target == op1 || ! REG_P (target)) @@ -1225,11 +1536,11 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, xtarget = gen_reg_rtx (mode); - if (target == 0 || GET_CODE (target) != REG) + if (target == 0 || !REG_P (target)) target = xtarget; /* Indicate for flow that the entire target reg is being set. */ - if (GET_CODE (target) == REG) + if (REG_P (target)) emit_insn (gen_rtx_CLOBBER (VOIDmode, xtarget)); /* Do the actual arithmetic. */ @@ -1288,6 +1599,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; } @@ -1315,447 +1631,50 @@ 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 && GET_CODE (target) != REG)) - 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) - { - 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); - } - } - - /* 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) - { - 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) - { - 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; - } - } - - /* 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); - } - - /* Open-code the vector operations if we have no hardware support - for them. */ - if (class == MODE_VECTOR_INT || class == MODE_VECTOR_FLOAT) - return expand_vector_binop (mode, binoptab, op0, op1, target, - unsignedp, methods); - - /* We need to open-code the complex type operations: '+, -, * and /' */ - - /* At this point we allow operations between two similar complex - numbers, and also if one of the operands is not a complex number - but rather of MODE_FLOAT or MODE_INT. However, the caller - must make sure that the MODE of the non-complex operand matches - the SUBMODE of the complex operand. */ - - if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) - { - rtx real0 = 0, imag0 = 0; - rtx real1 = 0, imag1 = 0; - rtx realr, imagr, res; - rtx seq, result; - int ok = 0; - - /* Find the correct mode for the real and imaginary parts. */ - enum machine_mode submode = GET_MODE_INNER (mode); - - if (submode == BLKmode) - abort (); - - start_sequence (); - - if (GET_MODE (op0) == mode) - { - real0 = gen_realpart (submode, op0); - imag0 = gen_imagpart (submode, op0); - } - else - real0 = op0; - - if (GET_MODE (op1) == mode) + != CODE_FOR_nothing) { - real1 = gen_realpart (submode, op1); - imag1 = gen_imagpart (submode, op1); + product = expand_doubleword_mult (mode, op0, op1, target, + false, methods); + if (!product) + delete_insns_since (last); } - else - real1 = op1; - - if (real0 == 0 || real1 == 0 || ! (imag0 != 0 || imag1 != 0)) - abort (); - result = gen_reg_rtx (mode); - realr = gen_realpart (submode, result); - imagr = gen_imagpart (submode, result); - - switch (binoptab->code) + if (product != NULL_RTX) { - case PLUS: - /* (a+ib) + (c+id) = (a+c) + i(b+d) */ - case MINUS: - /* (a+ib) - (c+id) = (a-c) + i(b-d) */ - res = expand_binop (submode, binoptab, real0, real1, - realr, unsignedp, methods); - - if (res == 0) - break; - else if (res != realr) - emit_move_insn (realr, res); - - if (imag0 != 0 && imag1 != 0) - res = expand_binop (submode, binoptab, imag0, imag1, - imagr, unsignedp, methods); - else if (imag0 != 0) - res = imag0; - else if (binoptab->code == MINUS) - res = expand_unop (submode, - binoptab == subv_optab ? negv_optab : neg_optab, - imag1, imagr, unsignedp); - else - res = imag1; - - if (res == 0) - break; - else if (res != imagr) - emit_move_insn (imagr, res); - - ok = 1; - break; - - case MULT: - /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */ - - if (imag0 != 0 && imag1 != 0) - { - rtx temp1, temp2; - - /* Don't fetch these from memory more than once. */ - real0 = force_reg (submode, real0); - real1 = force_reg (submode, real1); - imag0 = force_reg (submode, imag0); - imag1 = force_reg (submode, imag1); - - temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX, - unsignedp, methods); - - temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX, - unsignedp, methods); - - if (temp1 == 0 || temp2 == 0) - break; - - res = (expand_binop - (submode, - binoptab == smulv_optab ? subv_optab : sub_optab, - temp1, temp2, realr, unsignedp, methods)); - - if (res == 0) - break; - else if (res != realr) - emit_move_insn (realr, res); - - temp1 = expand_binop (submode, binoptab, real0, imag1, - NULL_RTX, unsignedp, methods); - - /* Avoid expanding redundant multiplication for the common - case of squaring a complex number. */ - if (rtx_equal_p (real0, real1) && rtx_equal_p (imag0, imag1)) - temp2 = temp1; - else - temp2 = expand_binop (submode, binoptab, real1, imag0, - NULL_RTX, unsignedp, methods); - - if (temp1 == 0 || temp2 == 0) - break; - - res = (expand_binop - (submode, - binoptab == smulv_optab ? addv_optab : add_optab, - temp1, temp2, imagr, unsignedp, methods)); - - if (res == 0) - break; - else if (res != imagr) - emit_move_insn (imagr, res); - - ok = 1; - } - else - { - /* Don't fetch these from memory more than once. */ - real0 = force_reg (submode, real0); - real1 = force_reg (submode, real1); - - res = expand_binop (submode, binoptab, real0, real1, - realr, unsignedp, methods); - if (res == 0) - break; - else if (res != realr) - emit_move_insn (realr, res); - - if (imag0 != 0) - res = expand_binop (submode, binoptab, - real1, imag0, imagr, unsignedp, methods); - else - res = expand_binop (submode, binoptab, - real0, imag1, imagr, unsignedp, methods); - - if (res == 0) - break; - else if (res != imagr) - emit_move_insn (imagr, res); - - ok = 1; - } - break; - - case DIV: - /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */ - - if (imag1 == 0) - { - /* (a+ib) / (c+i0) = (a/c) + i(b/c) */ - - /* Don't fetch these from memory more than once. */ - real1 = force_reg (submode, real1); - - /* Simply divide the real and imaginary parts by `c' */ - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, real0, real1, - realr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - real0, real1, realr, unsignedp); - - if (res == 0) - break; - else if (res != realr) - emit_move_insn (realr, res); - - if (class == MODE_COMPLEX_FLOAT) - res = expand_binop (submode, binoptab, imag0, real1, - imagr, unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - imag0, real1, imagr, unsignedp); - - if (res == 0) - break; - else if (res != imagr) - emit_move_insn (imagr, res); - - ok = 1; - } - else + if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { - switch (flag_complex_divide_method) - { - case 0: - ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1, - realr, imagr, submode, - unsignedp, methods, - class, binoptab); - break; - - case 1: - ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1, - realr, imagr, submode, - unsignedp, methods, - class, binoptab); - break; - - default: - abort (); - } + 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))); } - break; - - default: - abort (); - } - - seq = get_insns (); - end_sequence (); - - if (ok) - { - rtx equiv = gen_rtx_fmt_ee (binoptab->code, mode, - copy_rtx (op0), copy_rtx (op1)); - emit_no_conflict_block (seq, result, op0, op1, equiv); - return result; + return product; } } @@ -1874,224 +1793,6 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, delete_insns_since (entry_last); return 0; } - -/* Like expand_binop, but for open-coding vectors binops. */ - -static rtx -expand_vector_binop (enum machine_mode mode, optab binoptab, rtx op0, - rtx op1, rtx target, int unsignedp, - enum optab_methods methods) -{ - enum machine_mode submode, tmode; - int size, elts, subsize, subbitsize, i; - rtx t, a, b, res, seq; - enum mode_class class; - - class = GET_MODE_CLASS (mode); - - size = GET_MODE_SIZE (mode); - submode = GET_MODE_INNER (mode); - - /* Search for the widest vector mode with the same inner mode that is - still narrower than MODE and that allows to open-code this operator. - Note, if we find such a mode and the handler later decides it can't - do the expansion, we'll be called recursively with the narrower mode. */ - for (tmode = GET_CLASS_NARROWEST_MODE (class); - GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode); - tmode = GET_MODE_WIDER_MODE (tmode)) - { - if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode) - && binoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing) - submode = tmode; - } - - switch (binoptab->code) - { - case AND: - case IOR: - case XOR: - tmode = int_mode_for_mode (mode); - if (tmode != BLKmode) - submode = tmode; - case PLUS: - case MINUS: - case MULT: - case DIV: - subsize = GET_MODE_SIZE (submode); - subbitsize = GET_MODE_BITSIZE (submode); - elts = size / subsize; - - /* If METHODS is OPTAB_DIRECT, we don't insist on the exact mode, - but that we operate on more than one element at a time. */ - if (subsize == GET_MODE_UNIT_SIZE (mode) && methods == OPTAB_DIRECT) - return 0; - - start_sequence (); - - /* Errors can leave us with a const0_rtx as operand. */ - if (GET_MODE (op0) != mode) - op0 = copy_to_mode_reg (mode, op0); - if (GET_MODE (op1) != mode) - op1 = copy_to_mode_reg (mode, op1); - - if (!target) - target = gen_reg_rtx (mode); - - for (i = 0; i < elts; ++i) - { - /* If this is part of a register, and not the first item in the - word, we can't store using a SUBREG - that would clobber - previous results. - And storing with a SUBREG is only possible for the least - significant part, hence we can't do it for big endian - (unless we want to permute the evaluation order. */ - if (GET_CODE (target) == REG - && (BYTES_BIG_ENDIAN - ? subsize < UNITS_PER_WORD - : ((i * subsize) % UNITS_PER_WORD) != 0)) - t = NULL_RTX; - else - t = simplify_gen_subreg (submode, target, mode, i * subsize); - if (CONSTANT_P (op0)) - a = simplify_gen_subreg (submode, op0, mode, i * subsize); - else - a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp, - NULL_RTX, submode, submode, size); - if (CONSTANT_P (op1)) - b = simplify_gen_subreg (submode, op1, mode, i * subsize); - else - b = extract_bit_field (op1, subbitsize, i * subbitsize, unsignedp, - NULL_RTX, submode, submode, size); - - if (binoptab->code == DIV) - { - if (class == MODE_VECTOR_FLOAT) - res = expand_binop (submode, binoptab, a, b, t, - unsignedp, methods); - else - res = expand_divmod (0, TRUNC_DIV_EXPR, submode, - a, b, t, unsignedp); - } - else - res = expand_binop (submode, binoptab, a, b, t, - unsignedp, methods); - - if (res == 0) - break; - - if (t) - emit_move_insn (t, res); - else - store_bit_field (target, subbitsize, i * subbitsize, submode, res, - size); - } - break; - - default: - abort (); - } - - seq = get_insns (); - end_sequence (); - emit_insn (seq); - - return target; -} - -/* Like expand_unop but for open-coding vector unops. */ - -static rtx -expand_vector_unop (enum machine_mode mode, optab unoptab, rtx op0, - rtx target, int unsignedp) -{ - enum machine_mode submode, tmode; - int size, elts, subsize, subbitsize, i; - rtx t, a, res, seq; - - size = GET_MODE_SIZE (mode); - submode = GET_MODE_INNER (mode); - - /* Search for the widest vector mode with the same inner mode that is - still narrower than MODE and that allows to open-code this operator. - Note, if we find such a mode and the handler later decides it can't - do the expansion, we'll be called recursively with the narrower mode. */ - for (tmode = GET_CLASS_NARROWEST_MODE (GET_MODE_CLASS (mode)); - GET_MODE_SIZE (tmode) < GET_MODE_SIZE (mode); - tmode = GET_MODE_WIDER_MODE (tmode)) - { - if (GET_MODE_INNER (tmode) == GET_MODE_INNER (mode) - && unoptab->handlers[(int) tmode].insn_code != CODE_FOR_nothing) - submode = tmode; - } - /* If there is no negate operation, try doing a subtract from zero. */ - if (unoptab == neg_optab && GET_MODE_CLASS (submode) == MODE_INT - /* Avoid infinite recursion when an - error has left us with the wrong mode. */ - && GET_MODE (op0) == mode) - { - rtx temp; - temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0, - target, unsignedp, OPTAB_DIRECT); - if (temp) - return temp; - } - - if (unoptab == one_cmpl_optab) - { - tmode = int_mode_for_mode (mode); - if (tmode != BLKmode) - submode = tmode; - } - - subsize = GET_MODE_SIZE (submode); - subbitsize = GET_MODE_BITSIZE (submode); - elts = size / subsize; - - /* Errors can leave us with a const0_rtx as operand. */ - if (GET_MODE (op0) != mode) - op0 = copy_to_mode_reg (mode, op0); - - if (!target) - target = gen_reg_rtx (mode); - - start_sequence (); - - for (i = 0; i < elts; ++i) - { - /* If this is part of a register, and not the first item in the - word, we can't store using a SUBREG - that would clobber - previous results. - And storing with a SUBREG is only possible for the least - significant part, hence we can't do it for big endian - (unless we want to permute the evaluation order. */ - if (GET_CODE (target) == REG - && (BYTES_BIG_ENDIAN - ? subsize < UNITS_PER_WORD - : ((i * subsize) % UNITS_PER_WORD) != 0)) - t = NULL_RTX; - else - t = simplify_gen_subreg (submode, target, mode, i * subsize); - if (CONSTANT_P (op0)) - a = simplify_gen_subreg (submode, op0, mode, i * subsize); - else - a = extract_bit_field (op0, subbitsize, i * subbitsize, unsignedp, - t, submode, submode, size); - - res = expand_unop (submode, unoptab, a, t, unsignedp); - - if (t) - emit_move_insn (t, res); - else - store_bit_field (target, subbitsize, i * subbitsize, submode, res, - size); - } - - seq = get_insns (); - end_sequence (); - emit_insn (seq); - - return target; -} /* Expand a binary operator which has both signed and unsigned forms. UOPTAB is the optab for unsigned operations, and SOPTAB is for @@ -2147,11 +1848,10 @@ sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab, return 0; } -/* Generate code to perform an operation specified by BINOPTAB - on operands OP0 and OP1, with two results to TARG1 and TARG2. +/* Generate code to perform an operation specified by UNOPPTAB + on operand OP0, with two results to TARG0 and TARG1. We assume that the order of the operands for the instruction - is TARG0, OP0, OP1, TARG1, which would fit a pattern like - [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. + is TARG0, TARG1, OP0. Either TARG0 or TARG1 may be zero, but what that means is that the result is not actually wanted. We will generate it into @@ -2160,8 +1860,8 @@ sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab, Returns 1 if this operation can be performed; 0 if not. */ int -expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1, - int unsignedp) +expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1, + int unsignedp) { enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1); enum mode_class class; @@ -2171,40 +1871,128 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1, class = GET_MODE_CLASS (mode); - op0 = protect_from_queue (op0, 0); - op1 = protect_from_queue (op1, 0); - if (flag_force_mem) - { - op0 = force_not_mem (op0); - op1 = force_not_mem (op1); - } - - /* If we are inside an appropriately-short loop and one operand is an - expensive constant, force it into a register. */ - if (CONSTANT_P (op0) && preserve_subexpressions_p () - && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1)) - op0 = force_reg (mode, op0); - - if (CONSTANT_P (op1) && preserve_subexpressions_p () - && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1)) - op1 = force_reg (mode, op1); + op0 = force_not_mem (op0); - if (targ0) - targ0 = protect_from_queue (targ0, 1); - else + if (!targ0) targ0 = gen_reg_rtx (mode); - if (targ1) - targ1 = protect_from_queue (targ1, 1); - else + if (!targ1) targ1 = gen_reg_rtx (mode); /* Record where to go back to if we fail. */ last = get_last_insn (); - if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { - int icode = (int) binoptab->handlers[(int) mode].insn_code; + int icode = (int) unoptab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_data[icode].operand[2].mode; + rtx pat; + rtx xop0 = op0; + + if (GET_MODE (xop0) != VOIDmode + && GET_MODE (xop0) != mode0) + xop0 = convert_to_mode (mode0, xop0, unsignedp); + + /* Now, if insn doesn't accept these operands, put them into pseudos. */ + if (!insn_data[icode].operand[2].predicate (xop0, mode0)) + xop0 = copy_to_mode_reg (mode0, xop0); + + /* We could handle this, but we should always be called with a pseudo + for our targets and all insns should take them as outputs. */ + gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode)); + gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode)); + + pat = GEN_FCN (icode) (targ0, targ1, xop0); + if (pat) + { + emit_insn (pat); + return 1; + } + else + delete_insns_since (last); + } + + /* It can't be done in this mode. Can we do it in a wider mode? */ + + if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + { + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if (unoptab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + { + rtx t0 = gen_reg_rtx (wider_mode); + rtx t1 = gen_reg_rtx (wider_mode); + rtx cop0 = convert_modes (wider_mode, mode, op0, unsignedp); + + if (expand_twoval_unop (unoptab, cop0, t0, t1, unsignedp)) + { + convert_move (targ0, t0, unsignedp); + convert_move (targ1, t1, unsignedp); + return 1; + } + else + delete_insns_since (last); + } + } + } + + delete_insns_since (entry_last); + return 0; +} + +/* Generate code to perform an operation specified by BINOPTAB + on operands OP0 and OP1, with two results to TARG1 and TARG2. + We assume that the order of the operands for the instruction + is TARG0, OP0, OP1, TARG1, which would fit a pattern like + [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. + + Either TARG0 or TARG1 may be zero, but what that means is that + the result is not actually wanted. We will generate it into + a dummy pseudo-reg and discard it. They may not both be zero. + + Returns 1 if this operation can be performed; 0 if not. */ + +int +expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1, + int unsignedp) +{ + enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1); + enum mode_class class; + enum machine_mode wider_mode; + rtx entry_last = get_last_insn (); + rtx last; + + class = GET_MODE_CLASS (mode); + + if (flag_force_mem) + { + op0 = force_not_mem (op0); + op1 = force_not_mem (op1); + } + + /* If we are inside an appropriately-short loop and we are optimizing, + force expensive constants into a register. */ + if (CONSTANT_P (op0) && optimize + && rtx_cost (op0, binoptab->code) > COSTS_N_INSNS (1)) + op0 = force_reg (mode, op0); + + if (CONSTANT_P (op1) && optimize + && rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1)) + op1 = force_reg (mode, op1); + + if (!targ0) + targ0 = gen_reg_rtx (mode); + if (!targ1) + targ1 = gen_reg_rtx (mode); + + /* Record where to go back to if we fail. */ + last = get_last_insn (); + + if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) binoptab->handlers[(int) mode].insn_code; enum machine_mode mode0 = insn_data[icode].operand[1].mode; enum machine_mode mode1 = insn_data[icode].operand[2].mode; rtx pat; @@ -2231,17 +2019,16 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1, xop1, unsignedp); /* Now, if insn doesn't accept these operands, put them into pseudos. */ - if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)) + if (!insn_data[icode].operand[1].predicate (xop0, mode0)) xop0 = copy_to_mode_reg (mode0, xop0); - if (! (*insn_data[icode].operand[2].predicate) (xop1, mode1)) + if (!insn_data[icode].operand[2].predicate (xop1, mode1)) xop1 = copy_to_mode_reg (mode1, xop1); /* We could handle this, but we should always be called with a pseudo for our targets and all insns should take them as outputs. */ - if (! (*insn_data[icode].operand[0].predicate) (targ0, mode) - || ! (*insn_data[icode].operand[3].predicate) (targ1, mode)) - abort (); + gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode)); + gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode)); pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1); if (pat) @@ -2284,6 +2071,54 @@ expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1, delete_insns_since (entry_last); return 0; } + +/* Expand the two-valued library call indicated by BINOPTAB, but + preserve only one of the values. If TARG0 is non-NULL, the first + value is placed into TARG0; otherwise the second value is placed + into TARG1. Exactly one of TARG0 and TARG1 must be non-NULL. The + value stored into TARG0 or TARG1 is equivalent to (CODE OP0 OP1). + This routine assumes that the value returned by the library call is + as if the return value was of an integral mode twice as wide as the + mode of OP0. Returns 1 if the call was successful. */ + +bool +expand_twoval_binop_libfunc (optab binoptab, rtx op0, rtx op1, + rtx targ0, rtx targ1, enum rtx_code code) +{ + enum machine_mode mode; + enum machine_mode libval_mode; + rtx libval; + rtx insns; + + /* Exactly one of TARG0 or TARG1 should be non-NULL. */ + gcc_assert (!targ0 != !targ1); + + mode = GET_MODE (op0); + if (!binoptab->handlers[(int) mode].libfunc) + return false; + + /* The value returned by the library function will have twice as + many bits as the nominal MODE. */ + libval_mode = smallest_mode_for_size (2 * GET_MODE_BITSIZE (mode), + MODE_INT); + start_sequence (); + libval = emit_library_call_value (binoptab->handlers[(int) mode].libfunc, + NULL_RTX, LCT_CONST, + libval_mode, 2, + op0, mode, + op1, mode); + /* Get the part of VAL containing the value that we want. */ + libval = simplify_gen_subreg (mode, libval, libval_mode, + targ0 ? 0 : GET_MODE_SIZE (mode)); + insns = get_insns (); + end_sequence (); + /* Move the into the desired location. */ + emit_libcall_block (insns, targ0 ? targ0 : targ1, libval, + gen_rtx_fmt_ee (code, mode, op0, op1)); + + return true; +} + /* Wrapper around expand_unop which takes an rtx code to specify the operation to perform, not an optab pointer. All other @@ -2293,8 +2128,7 @@ expand_simple_unop (enum machine_mode mode, enum rtx_code code, rtx op0, rtx target, int unsignedp) { optab unop = code_to_optab[(int) code]; - if (unop == 0) - abort (); + gcc_assert (unop); return expand_unop (mode, unop, op0, target, unsignedp); } @@ -2376,6 +2210,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. @@ -2399,15 +2358,8 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, class = GET_MODE_CLASS (mode); - op0 = protect_from_queue (op0, 0); - if (flag_force_mem) - { - op0 = force_not_mem (op0); - } - - if (target) - target = protect_from_queue (target, 1); + op0 = force_not_mem (op0); if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { @@ -2426,10 +2378,10 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, /* Now, if insn doesn't accept our operand, put it into a pseudo. */ - if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)) + if (!insn_data[icode].operand[1].predicate (xop0, mode0)) xop0 = copy_to_mode_reg (mode0, xop0); - if (! (*insn_data[icode].operand[0].predicate) (temp, mode)) + if (!insn_data[icode].operand[0].predicate (temp, mode)) temp = gen_reg_rtx (mode); pat = GEN_FCN (icode) (temp, xop0); @@ -2534,96 +2486,27 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, return target; } - /* Open-code the complex negation operation. */ - else if (unoptab->code == NEG - && (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)) - { - rtx target_piece; - rtx x; - rtx seq; - - /* Find the correct mode for the real and imaginary parts. */ - enum machine_mode submode = GET_MODE_INNER (mode); - - if (submode == BLKmode) - abort (); - - if (target == 0) - target = gen_reg_rtx (mode); - - start_sequence (); - - target_piece = gen_imagpart (submode, target); - x = expand_unop (submode, unoptab, - gen_imagpart (submode, op0), - target_piece, unsignedp); - if (target_piece != x) - emit_move_insn (target_piece, x); - - target_piece = gen_realpart (submode, target); - x = expand_unop (submode, unoptab, - gen_realpart (submode, op0), - target_piece, unsignedp); - if (target_piece != x) - emit_move_insn (target_piece, x); - - seq = get_insns (); - end_sequence (); - - emit_no_conflict_block (seq, target, op0, 0, - gen_rtx_fmt_e (unoptab->code, mode, - copy_rtx (op0))); - 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. */ @@ -2666,9 +2549,6 @@ expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target, return target; } - if (class == MODE_VECTOR_FLOAT || class == MODE_VECTOR_INT) - return expand_vector_unop (mode, unoptab, op0, target, unsignedp); - /* It can't be done in this mode. Can we do it in a wider mode? */ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) @@ -2720,9 +2600,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. */ - 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, @@ -2730,7 +2610,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; @@ -2761,57 +2641,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 (); @@ -2866,15 +2705,15 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target, /* It is safe to use the target if it is the same as the source if this is also a pseudo register */ - if (op0 == target && GET_CODE (op0) == REG + if (op0 == target && REG_P (op0) && REGNO (op0) >= FIRST_PSEUDO_REGISTER) safe = 1; op1 = gen_label_rtx (); if (target == 0 || ! safe || GET_MODE (target) != mode - || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target)) - || (GET_CODE (target) == REG + || (MEM_P (target) && MEM_VOLATILE_P (target)) + || (REG_P (target) && REGNO (target) < FIRST_PSEUDO_REGISTER)) target = gen_reg_rtx (mode); @@ -2899,205 +2738,238 @@ expand_abs (enum machine_mode mode, rtx op0, rtx target, OK_DEFER_POP; return target; } - -/* Emit code to compute the absolute value of OP0, with result to - TARGET if convenient. (TARGET may be 0.) The return value says - where the result actually is to be found. - - MODE is the mode of the operand; the mode of the result is - different but can be deduced from MODE. - UNSIGNEDP is relevant for complex integer modes. */ +/* 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. */ -rtx -expand_complex_abs (enum machine_mode mode, rtx op0, rtx target, - int unsignedp) +static rtx +expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target, + int bitpos, bool op0_is_abs) { - enum mode_class class = GET_MODE_CLASS (mode); - enum machine_mode wider_mode; - rtx temp; - rtx entry_last = get_last_insn (); - rtx last; - rtx pat; - optab this_abs_optab; - - /* Find the correct mode for the real and imaginary parts. */ - enum machine_mode submode = GET_MODE_INNER (mode); + enum machine_mode imode; + HOST_WIDE_INT hi, lo; + int word; + rtx label; - if (submode == BLKmode) - abort (); + if (target == op1) + target = NULL_RTX; - op0 = protect_from_queue (op0, 0); - - if (flag_force_mem) + if (!op0_is_abs) { - op0 = force_not_mem (op0); + op0 = expand_unop (mode, abs_optab, op0, target, 0); + if (op0 == NULL) + return NULL_RTX; + target = op0; } - - last = get_last_insn (); - - if (target) - target = protect_from_queue (target, 1); - - this_abs_optab = ! unsignedp && flag_trapv - && (GET_MODE_CLASS(mode) == MODE_INT) - ? absv_optab : abs_optab; - - if (this_abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + else { - int icode = (int) this_abs_optab->handlers[(int) mode].insn_code; - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - rtx xop0 = op0; + if (target == NULL_RTX) + target = copy_to_reg (op0); + else + emit_move_insn (target, op0); + } - if (target) - temp = target; + 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 - temp = gen_reg_rtx (submode); + word = bitpos / BITS_PER_WORD; + bitpos = bitpos % BITS_PER_WORD; + op1 = operand_subword_force (op1, word, mode); + } - if (GET_MODE (xop0) != VOIDmode - && GET_MODE (xop0) != mode0) - xop0 = convert_to_mode (mode0, xop0, unsignedp); + 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; + } - /* Now, if insn doesn't accept our operand, put it into a pseudo. */ + op1 = expand_binop (imode, and_optab, op1, + immed_double_const (lo, hi, imode), + NULL_RTX, 1, OPTAB_LIB_WIDEN); - if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0)) - xop0 = copy_to_mode_reg (mode0, xop0); + label = gen_label_rtx (); + emit_cmp_and_jump_insns (op1, const0_rtx, EQ, NULL_RTX, imode, 1, label); - if (! (*insn_data[icode].operand[0].predicate) (temp, submode)) - temp = gen_reg_rtx (submode); + 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); - pat = GEN_FCN (icode) (temp, xop0); - if (pat) - { - if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX - && ! add_equal_note (pat, temp, this_abs_optab->code, xop0, - NULL_RTX)) - { - delete_insns_since (last); - return expand_unop (mode, this_abs_optab, op0, NULL_RTX, - unsignedp); - } + emit_label (label); - emit_insn (pat); + return target; +} - return temp; - } - else - delete_insns_since (last); - } - /* It can't be done in this mode. Can we open-code it in a wider mode? */ +/* 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. */ - for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) - { - if (this_abs_optab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - { - rtx xop0 = op0; - - xop0 = convert_modes (wider_mode, mode, xop0, unsignedp); - temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); +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 (temp) - { - if (class != MODE_COMPLEX_INT) - { - if (target == 0) - target = gen_reg_rtx (submode); - convert_move (target, temp, 0); - return target; - } - else - return gen_lowpart (submode, temp); - } - else - delete_insns_since (last); - } + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) + { + imode = int_mode_for_mode (mode); + if (imode == BLKmode) + return NULL_RTX; + word = 0; + nwords = 1; } - - /* Open-code the complex absolute-value operation - if we can open-code sqrt. Otherwise it's not worth while. */ - if (sqrt_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing - && ! flag_trapv) + else { - rtx real, imag, total; - - real = gen_realpart (submode, op0); - imag = gen_imagpart (submode, op0); - - /* Square both parts. */ - real = expand_mult (submode, real, real, NULL_RTX, 0); - imag = expand_mult (submode, imag, imag, NULL_RTX, 0); + imode = word_mode; - /* Sum the parts. */ - total = expand_binop (submode, add_optab, real, imag, NULL_RTX, - 0, OPTAB_LIB_WIDEN); - - /* Get sqrt in TARGET. Set TARGET to where the result is. */ - target = expand_unop (submode, sqrt_optab, total, target, 0); - if (target == 0) - delete_insns_since (last); + if (FLOAT_WORDS_BIG_ENDIAN) + word = (GET_MODE_BITSIZE (mode) - bitpos) / BITS_PER_WORD; else - return target; + word = bitpos / BITS_PER_WORD; + bitpos = bitpos % BITS_PER_WORD; + nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD; } - /* Now try a library call in this mode. */ - if (this_abs_optab->handlers[(int) mode].libfunc) + if (bitpos < HOST_BITS_PER_WIDE_INT) { - rtx insns; - rtx value; + 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 (); - /* Pass 1 for NO_QUEUE so we don't lose any increments - if the libcall is cse'd or moved. */ - value = emit_library_call_value (abs_optab->handlers[(int) mode].libfunc, - NULL_RTX, LCT_CONST, submode, 1, op0, mode); + 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 (); - target = gen_reg_rtx (submode); - emit_libcall_block (insns, target, value, - gen_rtx_fmt_e (this_abs_optab->code, mode, op0)); - - return target; + 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); } - /* It can't be done in this mode. Can we do it in a wider mode? */ + return target; +} - for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) - { - if ((this_abs_optab->handlers[(int) wider_mode].insn_code - != CODE_FOR_nothing) - || this_abs_optab->handlers[(int) wider_mode].libfunc) - { - rtx xop0 = op0; +/* 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. */ - xop0 = convert_modes (wider_mode, mode, xop0, unsignedp); +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; - temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); + gcc_assert (SCALAR_FLOAT_MODE_P (mode)); + gcc_assert (GET_MODE (op1) == mode); - if (temp) - { - if (class != MODE_COMPLEX_INT) - { - if (target == 0) - target = gen_reg_rtx (submode); - convert_move (target, temp, 0); - return target; - } - else - return gen_lowpart (submode, temp); - } - else - delete_insns_since (last); - } + /* 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; } - delete_insns_since (entry_last); - return 0; + 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, @@ -3113,9 +2985,7 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code) enum machine_mode mode0 = insn_data[icode].operand[1].mode; rtx pat; - temp = target = protect_from_queue (target, 1); - - op0 = protect_from_queue (op0, 0); + temp = target; /* Sign and zero extension from memory is often done specially on RISC machines, so forcing into a register here can pessimize @@ -3125,11 +2995,11 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code) /* Now, if insn does not accept our operands, put them into pseudos. */ - if (! (*insn_data[icode].operand[1].predicate) (op0, mode0)) + if (!insn_data[icode].operand[1].predicate (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); - if (! (*insn_data[icode].operand[0].predicate) (temp, GET_MODE (temp)) - || (flag_force_mem && GET_CODE (temp) == MEM)) + if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp)) + || (flag_force_mem && MEM_P (temp))) temp = gen_reg_rtx (GET_MODE (temp)); pat = GEN_FCN (icode) (temp, op0); @@ -3143,6 +3013,39 @@ emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code) emit_move_insn (target, temp); } +struct no_conflict_data +{ + rtx target, first, insn; + bool must_stay; +}; + +/* Called via note_stores by emit_no_conflict_block. Set P->must_stay + if the currently examined clobber / store has to stay in the list of + insns that constitute the actual no_conflict block. */ +static void +no_conflict_move_test (rtx dest, rtx set, void *p0) +{ + struct no_conflict_data *p= p0; + + /* If this inns directly contributes to setting the target, it must stay. */ + if (reg_overlap_mentioned_p (p->target, dest)) + p->must_stay = true; + /* If we haven't committed to keeping any other insns in the list yet, + there is nothing more to check. */ + else if (p->insn == p->first) + return; + /* If this insn sets / clobbers a register that feeds one of the insns + already in the list, this insn has to stay too. */ + else if (reg_mentioned_p (dest, PATTERN (p->first)) + || reg_used_between_p (dest, p->first, p->insn) + /* Likewise if this insn depends on a register set by a previous + insn in the list. */ + || (GET_CODE (set) == SET + && (modified_in_p (SET_SRC (set), p->first) + || modified_between_p (SET_SRC (set), p->first, p->insn)))) + p->must_stay = true; +} + /* Emit code to perform a series of operations on a multi-word quantity, one word at a time. @@ -3176,11 +3079,11 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv) { rtx prev, next, first, last, insn; - if (GET_CODE (target) != REG || reload_in_progress) + if (!REG_P (target) || reload_in_progress) return emit_insn (insns); else for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) != INSN + if (!NONJUMP_INSN_P (insn) || find_reg_note (insn, REG_LIBCALL, NULL_RTX)) return emit_insn (insns); @@ -3188,8 +3091,8 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv) these from the list. */ for (insn = insns; insn; insn = next) { - rtx set = 0, note; - int i; + rtx note; + struct no_conflict_data data; next = NEXT_INSN (insn); @@ -3200,23 +3103,12 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv) if ((note = find_reg_note (insn, REG_RETVAL, NULL)) != NULL) remove_note (insn, note); - if (GET_CODE (PATTERN (insn)) == SET || GET_CODE (PATTERN (insn)) == USE - || GET_CODE (PATTERN (insn)) == CLOBBER) - set = PATTERN (insn); - else if (GET_CODE (PATTERN (insn)) == PARALLEL) - { - for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) - if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) - { - set = XVECEXP (PATTERN (insn), 0, i); - break; - } - } - - if (set == 0) - abort (); - - if (! reg_overlap_mentioned_p (target, SET_DEST (set))) + data.target = target; + data.first = insns; + data.insn = insn; + data.must_stay = 0; + note_stores (PATTERN (insn), no_conflict_move_test, &data); + if (! data.must_stay) { if (PREV_INSN (insn)) NEXT_INSN (PREV_INSN (insn)) = next; @@ -3242,11 +3134,11 @@ emit_no_conflict_block (rtx insns, rtx target, rtx op0, rtx op1, rtx equiv) next = NEXT_INSN (insn); add_insn (insn); - if (op1 && GET_CODE (op1) == REG) + if (op1 && REG_P (op1)) REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op1, REG_NOTES (insn)); - if (op0 && GET_CODE (op0) == REG) + if (op0 && REG_P (op0)) REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op0, REG_NOTES (insn)); } @@ -3324,7 +3216,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) if (flag_non_call_exceptions && may_trap_p (equiv)) { for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) + if (CALL_P (insn)) { rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); @@ -3338,7 +3230,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) goto (unless there is already a REG_EH_REGION note, in which case we update it). */ for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) + if (CALL_P (insn)) { rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); @@ -3369,7 +3261,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) next = NEXT_INSN (insn); - if (set != 0 && GET_CODE (SET_DEST (set)) == REG + if (set != 0 && REG_P (SET_DEST (set)) && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER && (insn == insns || ((! INSN_P(insns) @@ -3391,7 +3283,7 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) /* Some ports use a loop to copy large arguments onto the stack. Don't move anything outside such a loop. */ - if (GET_CODE (insn) == CODE_LABEL) + if (LABEL_P (insn)) break; } @@ -3454,23 +3346,6 @@ emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) } } -/* Generate code to store zero in X. */ - -void -emit_clr_insn (rtx x) -{ - emit_move_insn (x, const0_rtx); -} - -/* Generate code to store 1 in X - assuming it contains zero beforehand. */ - -void -emit_0_to_1_insn (rtx x) -{ - emit_move_insn (x, const1_rtx); -} - /* Nonzero if we can perform a comparison of mode MODE straightforwardly. PURPOSE describes how this comparison will be used. CODE is the rtx comparison code we will be using. @@ -3504,7 +3379,6 @@ can_compare_p (enum rtx_code code, enum machine_mode mode, if (purpose == ccp_store_flag && cstore_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) return 1; - mode = GET_MODE_WIDER_MODE (mode); } while (mode != VOIDmode); @@ -3526,7 +3400,8 @@ can_compare_p (enum rtx_code code, enum machine_mode mode, comparison or emitting a library call to perform the comparison if no insn is available to handle it. The values which are passed in through pointers can be modified; the caller - should perform the comparison on the modified values. */ + should perform the comparison on the modified values. Constant + comparisons must have already been folded. */ static void prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, @@ -3540,11 +3415,6 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, class = GET_MODE_CLASS (mode); - /* They could both be VOIDmode if both args are immediate constants, - but we should fold that at an earlier stage. - With no special code here, this will call abort, - reminding the programmer to implement such folding. */ - if (mode != BLKmode && flag_force_mem) { /* Load duplicate non-volatile operands once. */ @@ -3560,22 +3430,21 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, } } - /* If we are inside an appropriately-short loop and one operand is an - expensive constant, force it into a register. */ - if (CONSTANT_P (x) && preserve_subexpressions_p () + /* If we are inside an appropriately-short loop and we are optimizing, + force expensive constants into a register. */ + if (CONSTANT_P (x) && optimize && rtx_cost (x, COMPARE) > COSTS_N_INSNS (1)) x = force_reg (mode, x); - if (CONSTANT_P (y) && preserve_subexpressions_p () + if (CONSTANT_P (y) && optimize && rtx_cost (y, COMPARE) > COSTS_N_INSNS (1)) y = force_reg (mode, y); #ifdef HAVE_cc0 - /* Abort if we have a non-canonical comparison. The RTL documentation - states that canonical comparisons are required only for targets which - have cc0. */ - if (CONSTANT_P (x) && ! CONSTANT_P (y)) - abort (); + /* Make sure if we have a canonical comparison. The RTL + documentation states that canonical comparisons are required only + for targets which have cc0. */ + gcc_assert (!CONSTANT_P (x) || CONSTANT_P (y)); #endif /* Don't let both operands fail to indicate the mode. */ @@ -3594,13 +3463,7 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, rtx opalign = GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT); - if (size == 0) - abort (); - - emit_queue (); - x = protect_from_queue (x, 0); - y = protect_from_queue (y, 0); - size = protect_from_queue (size, 0); + gcc_assert (size); /* Try to use a memory block compare insn - either cmpstr or cmpmem will do. */ @@ -3632,19 +3495,13 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, return; } - /* Otherwise call a library function, memcmp if we've got it, - bcmp otherwise. */ -#ifdef TARGET_MEM_FUNCTIONS + /* Otherwise call a library function, memcmp. */ libfunc = memcmp_libfunc; length_type = sizetype; -#else - libfunc = bcmp_libfunc; - length_type = integer_type_node; -#endif result_mode = TYPE_MODE (integer_type_node); cmp_mode = TYPE_MODE (length_type); size = convert_to_mode (TYPE_MODE (length_type), size, - TREE_UNSIGNED (length_type)); + TYPE_UNSIGNED (length_type)); result = emit_library_call_value (libfunc, 0, LCT_PURE_MAKE_BLOCK, result_mode, 3, @@ -3687,20 +3544,24 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK, word_mode, 2, x, mode, y, mode); - /* Integer comparison returns a result that must be compared against 1, - so that even if we do an unsigned compare afterward, - there is still a value that can represent the result "less than". */ *px = result; - *py = const1_rtx; *pmode = word_mode; + if (TARGET_LIB_INT_CMP_BIASED) + /* Integer comparison returns a result that must be compared + against 1, so that even if we do an unsigned compare + afterward, there is still a value that can represent the + result "less than". */ + *py = const1_rtx; + else + { + *py = const0_rtx; + *punsignedp = 1; + } return; } - if (class == MODE_FLOAT) - prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp); - - else - abort (); + gcc_assert (class == MODE_FLOAT); + prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp); } /* Before emitting an insn with code ICODE, make sure that X, which is going @@ -3708,16 +3569,14 @@ prepare_cmp_insn (rtx *px, rtx *py, enum rtx_code *pcomparison, rtx size, WIDER_MODE (UNSIGNEDP determines whether it is an unsigned conversion), and that it is accepted by the operand predicate. Return the new value. */ -rtx +static rtx prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode, enum machine_mode wider_mode, int unsignedp) { - x = protect_from_queue (x, 0); - if (mode != wider_mode) x = convert_modes (wider_mode, mode, x, unsignedp); - if (! (*insn_data[icode].operand[opnum].predicate) + if (!insn_data[icode].operand[opnum].predicate (x, insn_data[icode].operand[opnum].mode)) { if (no_new_pseudos) @@ -3752,7 +3611,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode, icode = cbranch_optab->handlers[(int) wider_mode].insn_code; if (icode != CODE_FOR_nothing - && (*insn_data[icode].operand[0].predicate) (test, wider_mode)) + && insn_data[icode].operand[0].predicate (test, wider_mode)) { x = prepare_operand (icode, x, 1, mode, wider_mode, unsignedp); y = prepare_operand (icode, y, 2, mode, wider_mode, unsignedp); @@ -3768,7 +3627,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode, x = prepare_operand (icode, x, 0, mode, wider_mode, unsignedp); emit_insn (GEN_FCN (icode) (x)); if (label) - emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label)); + emit_jump_insn (bcc_gen_fctn[(int) comparison] (label)); return; } @@ -3781,7 +3640,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode, y = prepare_operand (icode, y, 1, mode, wider_mode, unsignedp); emit_insn (GEN_FCN (icode) (x, y)); if (label) - emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label)); + emit_jump_insn (bcc_gen_fctn[(int) comparison] (label)); return; } @@ -3793,7 +3652,7 @@ emit_cmp_and_jump_insn_1 (rtx x, rtx y, enum machine_mode mode, } while (wider_mode != VOIDmode); - abort (); + gcc_unreachable (); } /* Generate code to compare X with Y so that the condition codes are @@ -3824,22 +3683,19 @@ emit_cmp_and_jump_insns (rtx x, rtx y, enum rtx_code comparison, rtx size, { /* If we're not emitting a branch, this means some caller is out of sync. */ - if (! label) - abort (); + gcc_assert (label); op0 = y, op1 = x; comparison = swap_condition (comparison); } #ifdef HAVE_cc0 - /* If OP0 is still a constant, then both X and Y must be constants. Force - X into a register to avoid aborting in emit_cmp_insn due to non-canonical - RTL. */ + /* If OP0 is still a constant, then both X and Y must be constants. + Force X into a register to create canonical RTL. */ if (CONSTANT_P (op0)) op0 = force_reg (mode, op0); #endif - emit_queue (); if (unsignedp) comparison = unsigned_condition (comparison); @@ -3866,12 +3722,14 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison, { enum rtx_code comparison = *pcomparison; enum rtx_code swapped = swap_condition (comparison); - rtx x = protect_from_queue (*px, 0); - rtx y = protect_from_queue (*py, 0); + enum rtx_code reversed = reverse_condition_maybe_unordered (comparison); + rtx x = *px; + rtx y = *py; enum machine_mode orig_mode = GET_MODE (x); enum machine_mode mode; rtx value, target, insns, equiv; rtx libfunc = 0; + bool reversed_p = false; for (mode = orig_mode; mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) { @@ -3885,10 +3743,17 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison, comparison = swapped; break; } + + if ((libfunc = code_to_optab[reversed]->handlers[mode].libfunc) + && FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, reversed)) + { + comparison = reversed; + reversed_p = true; + break; + } } - if (mode == VOIDmode) - abort (); + gcc_assert (mode != VOIDmode); if (mode != orig_mode) { @@ -3946,7 +3811,7 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison, break; default: - abort (); + gcc_unreachable (); } equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode, equiv, true_rtx, false_rtx); @@ -3962,10 +3827,9 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison, target = gen_reg_rtx (word_mode); emit_libcall_block (insns, target, value, equiv); - if (comparison == UNORDERED || FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison)) - comparison = NE; + comparison = reversed_p ? EQ : NE; *px = target; *py = const0_rtx; @@ -3979,8 +3843,8 @@ prepare_float_lib_cmp (rtx *px, rtx *py, enum rtx_code *pcomparison, void emit_indirect_jump (rtx loc) { - if (! ((*insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate) - (loc, Pmode))) + if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate + (loc, Pmode)) loc = copy_to_mode_reg (Pmode, loc); emit_jump_insn (gen_indirect_jump (loc)); @@ -4058,29 +3922,22 @@ emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1, op3 = force_not_mem (op3); } - if (target) - target = protect_from_queue (target, 1); - else + if (!target) target = gen_reg_rtx (mode); subtarget = target; - emit_queue (); - - op2 = protect_from_queue (op2, 0); - op3 = protect_from_queue (op3, 0); - /* If the insn doesn't accept these operands, put them in pseudos. */ - if (! (*insn_data[icode].operand[0].predicate) + if (!insn_data[icode].operand[0].predicate (subtarget, insn_data[icode].operand[0].mode)) subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode); - if (! (*insn_data[icode].operand[2].predicate) + if (!insn_data[icode].operand[2].predicate (op2, insn_data[icode].operand[2].mode)) op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2); - if (! (*insn_data[icode].operand[3].predicate) + if (!insn_data[icode].operand[3].predicate (op3, insn_data[icode].operand[3].mode)) op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3); @@ -4199,29 +4056,22 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1, op3 = force_not_mem (op3); } - if (target) - target = protect_from_queue (target, 1); - else + if (!target) target = gen_reg_rtx (mode); - subtarget = target; - - emit_queue (); - - op2 = protect_from_queue (op2, 0); - op3 = protect_from_queue (op3, 0); - /* If the insn doesn't accept these operands, put them in pseudos. */ - if (! (*insn_data[icode].operand[0].predicate) - (subtarget, insn_data[icode].operand[0].mode)) + if (!insn_data[icode].operand[0].predicate + (target, insn_data[icode].operand[0].mode)) subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode); + else + subtarget = target; - if (! (*insn_data[icode].operand[2].predicate) + if (!insn_data[icode].operand[2].predicate (op2, insn_data[icode].operand[2].mode)) op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2); - if (! (*insn_data[icode].operand[3].predicate) + if (!insn_data[icode].operand[3].predicate (op3, insn_data[icode].operand[3].mode)) op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3); @@ -4254,11 +4104,7 @@ emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1, /* These functions attempt to generate an insn body, rather than emitting the insn, but if the gen function already emits them, we - make no attempt to turn them back into naked patterns. - - They do not protect from queued increments, - because they may be used 1) in protect_from_queue itself - and 2) in other passes where there is no queue. */ + make no attempt to turn them back into naked patterns. */ /* Generate and return an insn body to add Y to X. */ @@ -4267,15 +4113,14 @@ gen_add2_insn (rtx x, rtx y) { int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; - if (! ((*insn_data[icode].operand[0].predicate) - (x, insn_data[icode].operand[0].mode)) - || ! ((*insn_data[icode].operand[1].predicate) - (x, insn_data[icode].operand[1].mode)) - || ! ((*insn_data[icode].operand[2].predicate) - (y, insn_data[icode].operand[2].mode))) - abort (); + gcc_assert (insn_data[icode].operand[0].predicate + (x, insn_data[icode].operand[0].mode)); + gcc_assert (insn_data[icode].operand[1].predicate + (x, insn_data[icode].operand[1].mode)); + gcc_assert (insn_data[icode].operand[2].predicate + (y, insn_data[icode].operand[2].mode)); - return (GEN_FCN (icode) (x, x, y)); + return GEN_FCN (icode) (x, x, y); } /* Generate and return an insn body to add r1 and c, @@ -4286,15 +4131,15 @@ gen_add3_insn (rtx r0, rtx r1, rtx c) int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code; if (icode == CODE_FOR_nothing - || ! ((*insn_data[icode].operand[0].predicate) - (r0, insn_data[icode].operand[0].mode)) - || ! ((*insn_data[icode].operand[1].predicate) - (r1, insn_data[icode].operand[1].mode)) - || ! ((*insn_data[icode].operand[2].predicate) - (c, insn_data[icode].operand[2].mode))) + || !(insn_data[icode].operand[0].predicate + (r0, insn_data[icode].operand[0].mode)) + || !(insn_data[icode].operand[1].predicate + (r1, insn_data[icode].operand[1].mode)) + || !(insn_data[icode].operand[2].predicate + (c, insn_data[icode].operand[2].mode))) return NULL_RTX; - return (GEN_FCN (icode) (r0, r1, c)); + return GEN_FCN (icode) (r0, r1, c); } int @@ -4302,20 +4147,19 @@ have_add2_insn (rtx x, rtx y) { int icode; - if (GET_MODE (x) == VOIDmode) - abort (); + gcc_assert (GET_MODE (x) != VOIDmode); icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; if (icode == CODE_FOR_nothing) return 0; - if (! ((*insn_data[icode].operand[0].predicate) - (x, insn_data[icode].operand[0].mode)) - || ! ((*insn_data[icode].operand[1].predicate) - (x, insn_data[icode].operand[1].mode)) - || ! ((*insn_data[icode].operand[2].predicate) - (y, insn_data[icode].operand[2].mode))) + if (!(insn_data[icode].operand[0].predicate + (x, insn_data[icode].operand[0].mode)) + || !(insn_data[icode].operand[1].predicate + (x, insn_data[icode].operand[1].mode)) + || !(insn_data[icode].operand[2].predicate + (y, insn_data[icode].operand[2].mode))) return 0; return 1; @@ -4328,15 +4172,14 @@ gen_sub2_insn (rtx x, rtx y) { int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; - if (! ((*insn_data[icode].operand[0].predicate) - (x, insn_data[icode].operand[0].mode)) - || ! ((*insn_data[icode].operand[1].predicate) - (x, insn_data[icode].operand[1].mode)) - || ! ((*insn_data[icode].operand[2].predicate) - (y, insn_data[icode].operand[2].mode))) - abort (); + gcc_assert (insn_data[icode].operand[0].predicate + (x, insn_data[icode].operand[0].mode)); + gcc_assert (insn_data[icode].operand[1].predicate + (x, insn_data[icode].operand[1].mode)); + gcc_assert (insn_data[icode].operand[2].predicate + (y, insn_data[icode].operand[2].mode)); - return (GEN_FCN (icode) (x, x, y)); + return GEN_FCN (icode) (x, x, y); } /* Generate and return an insn body to subtract r1 and c, @@ -4347,15 +4190,15 @@ gen_sub3_insn (rtx r0, rtx r1, rtx c) int icode = (int) sub_optab->handlers[(int) GET_MODE (r0)].insn_code; if (icode == CODE_FOR_nothing - || ! ((*insn_data[icode].operand[0].predicate) - (r0, insn_data[icode].operand[0].mode)) - || ! ((*insn_data[icode].operand[1].predicate) - (r1, insn_data[icode].operand[1].mode)) - || ! ((*insn_data[icode].operand[2].predicate) - (c, insn_data[icode].operand[2].mode))) + || !(insn_data[icode].operand[0].predicate + (r0, insn_data[icode].operand[0].mode)) + || !(insn_data[icode].operand[1].predicate + (r1, insn_data[icode].operand[1].mode)) + || !(insn_data[icode].operand[2].predicate + (c, insn_data[icode].operand[2].mode))) return NULL_RTX; - return (GEN_FCN (icode) (r0, r1, c)); + return GEN_FCN (icode) (r0, r1, c); } int @@ -4363,20 +4206,19 @@ have_sub2_insn (rtx x, rtx y) { int icode; - if (GET_MODE (x) == VOIDmode) - abort (); + gcc_assert (GET_MODE (x) != VOIDmode); icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; if (icode == CODE_FOR_nothing) return 0; - if (! ((*insn_data[icode].operand[0].predicate) - (x, insn_data[icode].operand[0].mode)) - || ! ((*insn_data[icode].operand[1].predicate) - (x, insn_data[icode].operand[1].mode)) - || ! ((*insn_data[icode].operand[2].predicate) - (y, insn_data[icode].operand[2].mode))) + if (!(insn_data[icode].operand[0].predicate + (x, insn_data[icode].operand[0].mode)) + || !(insn_data[icode].operand[1].predicate + (x, insn_data[icode].operand[1].mode)) + || !(insn_data[icode].operand[2].predicate + (y, insn_data[icode].operand[2].mode))) return 0; return 1; @@ -4490,8 +4332,7 @@ expand_float (rtx to, rtx from, int unsignedp) enum machine_mode fmode, imode; /* Crash now, because we won't be able to decide which mode to use. */ - if (GET_MODE (from) == VOIDmode) - abort (); + gcc_assert (GET_MODE (from) != VOIDmode); /* Look for an insn to do the conversion. Do it in the specified modes if possible; otherwise convert either input, output or both to @@ -4515,9 +4356,6 @@ expand_float (rtx to, rtx from, int unsignedp) if (icode != CODE_FOR_nothing) { - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - if (imode != GET_MODE (from)) from = convert_to_mode (imode, from, unsignedp); @@ -4541,11 +4379,6 @@ expand_float (rtx to, rtx from, int unsignedp) rtx temp; REAL_VALUE_TYPE offset; - emit_queue (); - - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - if (flag_force_mem) from = force_not_mem (from); @@ -4573,7 +4406,7 @@ expand_float (rtx to, rtx from, int unsignedp) /* Don't use TARGET if it isn't a register, is a hard register, or is the wrong mode. */ - if (GET_CODE (target) != REG + if (!REG_P (target) || REGNO (target) < FIRST_PSEUDO_REGISTER || GET_MODE (target) != fmode) target = gen_reg_rtx (fmode); @@ -4620,7 +4453,7 @@ expand_float (rtx to, rtx from, int unsignedp) unsigned operand, do it in a pseudo-register. */ if (GET_MODE (to) != fmode - || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER) + || !REG_P (to) || REGNO (to) < FIRST_PSEUDO_REGISTER) target = gen_reg_rtx (fmode); /* Convert as signed integer to floating. */ @@ -4653,9 +4486,6 @@ expand_float (rtx to, rtx from, int unsignedp) rtx value; convert_optab tab = unsignedp ? ufloat_optab : sfloat_optab; - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode)) from = convert_to_mode (SImode, from, unsignedp); @@ -4663,8 +4493,7 @@ expand_float (rtx to, rtx from, int unsignedp) from = force_not_mem (from); libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc; - if (!libfunc) - abort (); + gcc_assert (libfunc); start_sequence (); @@ -4721,9 +4550,6 @@ expand_fix (rtx to, rtx from, int unsignedp) if (icode != CODE_FOR_nothing) { - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - if (fmode != GET_MODE (from)) from = convert_to_mode (fmode, from, 0); @@ -4783,10 +4609,6 @@ expand_fix (rtx to, rtx from, int unsignedp) lab1 = gen_label_rtx (); lab2 = gen_label_rtx (); - emit_queue (); - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); - if (flag_force_mem) from = force_not_mem (from); @@ -4851,14 +4673,10 @@ expand_fix (rtx to, rtx from, int unsignedp) rtx insns; rtx value; rtx libfunc; - + convert_optab tab = unsignedp ? ufix_optab : sfix_optab; libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc; - if (!libfunc) - abort (); - - to = protect_from_queue (to, 1); - from = protect_from_queue (from, 0); + gcc_assert (libfunc); if (flag_force_mem) from = force_not_mem (from); @@ -5208,6 +5026,12 @@ init_optabs (void) movcc_gen_code[i] = CODE_FOR_nothing; #endif + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + vcond_gen_code[i] = CODE_FOR_nothing; + vcondu_gen_code[i] = CODE_FOR_nothing; + } + add_optab = init_optab (PLUS); addv_optab = init_optabv (PLUS); sub_optab = init_optab (MINUS); @@ -5225,6 +5049,8 @@ init_optabs (void) udivmod_optab = init_optab (UNKNOWN); smod_optab = init_optab (MOD); umod_optab = init_optab (UMOD); + fmod_optab = init_optab (UNKNOWN); + drem_optab = init_optab (UNKNOWN); ftrunc_optab = init_optab (UNKNOWN); and_optab = init_optab (AND); ior_optab = init_optab (IOR); @@ -5271,29 +5097,57 @@ init_optabs (void) parity_optab = init_optab (PARITY); sqrt_optab = init_optab (SQRT); floor_optab = init_optab (UNKNOWN); + lfloor_optab = init_optab (UNKNOWN); ceil_optab = init_optab (UNKNOWN); + lceil_optab = init_optab (UNKNOWN); round_optab = init_optab (UNKNOWN); 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); cos_optab = init_optab (UNKNOWN); + acos_optab = init_optab (UNKNOWN); exp_optab = init_optab (UNKNOWN); 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); log10_optab = init_optab (UNKNOWN); log2_optab = init_optab (UNKNOWN); + 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); cstore_optab = init_optab (UNKNOWN); push_optab = init_optab (UNKNOWN); + reduc_smax_optab = init_optab (UNKNOWN); + reduc_umax_optab = init_optab (UNKNOWN); + reduc_smin_optab = init_optab (UNKNOWN); + reduc_umin_optab = init_optab (UNKNOWN); + reduc_splus_optab = init_optab (UNKNOWN); + reduc_uplus_optab = init_optab (UNKNOWN); + vec_extract_optab = init_optab (UNKNOWN); vec_set_optab = init_optab (UNKNOWN); vec_init_optab = init_optab (UNKNOWN); + vec_shl_optab = init_optab (UNKNOWN); + vec_shr_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); zext_optab = init_convert_optab (ZERO_EXTEND); @@ -5307,10 +5161,33 @@ init_optabs (void) for (i = 0; i < NUM_MACHINE_MODES; i++) { - movstr_optab[i] = CODE_FOR_nothing; - clrstr_optab[i] = CODE_FOR_nothing; + movmem_optab[i] = CODE_FOR_nothing; cmpstr_optab[i] = CODE_FOR_nothing; cmpmem_optab[i] = CODE_FOR_nothing; + setmem_optab[i] = CODE_FOR_nothing; + + sync_add_optab[i] = CODE_FOR_nothing; + sync_sub_optab[i] = CODE_FOR_nothing; + sync_ior_optab[i] = CODE_FOR_nothing; + sync_and_optab[i] = CODE_FOR_nothing; + sync_xor_optab[i] = CODE_FOR_nothing; + sync_nand_optab[i] = CODE_FOR_nothing; + sync_old_add_optab[i] = CODE_FOR_nothing; + sync_old_sub_optab[i] = CODE_FOR_nothing; + sync_old_ior_optab[i] = CODE_FOR_nothing; + sync_old_and_optab[i] = CODE_FOR_nothing; + sync_old_xor_optab[i] = CODE_FOR_nothing; + sync_old_nand_optab[i] = CODE_FOR_nothing; + sync_new_add_optab[i] = CODE_FOR_nothing; + sync_new_sub_optab[i] = CODE_FOR_nothing; + sync_new_ior_optab[i] = CODE_FOR_nothing; + sync_new_and_optab[i] = CODE_FOR_nothing; + sync_new_xor_optab[i] = CODE_FOR_nothing; + sync_new_nand_optab[i] = CODE_FOR_nothing; + sync_compare_and_swap[i] = CODE_FOR_nothing; + sync_compare_and_swap_cc[i] = CODE_FOR_nothing; + sync_lock_test_and_set[i] = CODE_FOR_nothing; + sync_lock_release[i] = CODE_FOR_nothing; #ifdef HAVE_SECONDARY_RELOADS reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing; @@ -5365,7 +5242,8 @@ init_optabs (void) init_integral_libfuncs (popcount_optab, "popcount", '2'); init_integral_libfuncs (parity_optab, "parity", '2'); - /* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */ + /* Comparison libcalls for integers MUST come in pairs, + signed/unsigned. */ init_integral_libfuncs (cmp_optab, "cmp", '2'); init_integral_libfuncs (ucmp_optab, "ucmp", '2'); init_floating_libfuncs (cmp_optab, "cmp", '2'); @@ -5379,10 +5257,15 @@ 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); - init_interclass_conv_libfuncs (sfix_optab, "fix", MODE_FLOAT, MODE_INT); - init_interclass_conv_libfuncs (ufix_optab, "fixuns", MODE_FLOAT, MODE_INT); + init_interclass_conv_libfuncs (sfloat_optab, "float", + MODE_INT, MODE_FLOAT); + init_interclass_conv_libfuncs (sfix_optab, "fix", + MODE_FLOAT, MODE_INT); + init_interclass_conv_libfuncs (ufix_optab, "fixuns", + MODE_FLOAT, MODE_INT); /* sext_optab is also used for FLOAT_EXTEND. */ init_intraclass_conv_libfuncs (sext_optab, "extend", MODE_FLOAT, true); @@ -5401,16 +5284,10 @@ init_optabs (void) abort_libfunc = init_one_libfunc ("abort"); memcpy_libfunc = init_one_libfunc ("memcpy"); memmove_libfunc = init_one_libfunc ("memmove"); - bcopy_libfunc = init_one_libfunc ("bcopy"); memcmp_libfunc = init_one_libfunc ("memcmp"); - bcmp_libfunc = init_one_libfunc ("__gcc_bcmp"); memset_libfunc = init_one_libfunc ("memset"); - bzero_libfunc = init_one_libfunc ("bzero"); setbits_libfunc = init_one_libfunc ("__setbits"); - unwind_resume_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS - ? "_Unwind_SjLj_Resume" - : "_Unwind_Resume"); #ifndef DONT_USE_BUILTIN_SETJMP setjmp_libfunc = init_one_libfunc ("__builtin_setjmp"); longjmp_libfunc = init_one_libfunc ("__builtin_longjmp"); @@ -5429,7 +5306,6 @@ init_optabs (void) = init_one_libfunc ("__cyg_profile_func_exit"); gcov_flush_libfunc = init_one_libfunc ("__gcov_flush"); - gcov_init_libfunc = init_one_libfunc ("__gcov_init"); if (HAVE_conditional_trap) trap_rtx = gen_rtx_fmt_ee (EQ, VOIDmode, NULL_RTX, NULL_RTX); @@ -5437,6 +5313,62 @@ init_optabs (void) /* Allow the target to add more libcalls or rename some, etc. */ targetm.init_libfuncs (); } + +#ifdef DEBUG + +/* Print information about the current contents of the optabs on + STDERR. */ + +static void +debug_optab_libfuncs (void) +{ + int i; + int j; + int k; + + /* Dump the arithmetic optabs. */ + for (i = 0; i != (int) OTI_MAX; i++) + for (j = 0; j < NUM_MACHINE_MODES; ++j) + { + optab o; + struct optab_handlers *h; + + o = optab_table[i]; + h = &o->handlers[j]; + if (h->libfunc) + { + gcc_assert (GET_CODE (h->libfunc) = SYMBOL_REF); + fprintf (stderr, "%s\t%s:\t%s\n", + GET_RTX_NAME (o->code), + GET_MODE_NAME (j), + XSTR (h->libfunc, 0)); + } + } + + /* Dump the conversion optabs. */ + for (i = 0; i < (int) CTI_MAX; ++i) + for (j = 0; j < NUM_MACHINE_MODES; ++j) + for (k = 0; k < NUM_MACHINE_MODES; ++k) + { + convert_optab o; + struct optab_handlers *h; + + o = &convert_optab_table[i]; + h = &o->handlers[j][k]; + if (h->libfunc) + { + gcc_assert (GET_CODE (h->libfunc) = SYMBOL_REF); + fprintf (stderr, "%s\t%s\t%s:\t%s\n", + GET_RTX_NAME (o->code), + GET_MODE_NAME (j), + GET_MODE_NAME (k), + XSTR (h->libfunc, 0)); + } + } +} + +#endif /* DEBUG */ + /* Generate insns to trap with code TCODE if OP1 and OP2 satisfy condition CODE. Return 0 on failure. */ @@ -5470,6 +5402,7 @@ gen_cond_trap (enum rtx_code code ATTRIBUTE_UNUSED, rtx op1, emit_insn (GEN_FCN (icode) (op1, op2)); PUT_CODE (trap_rtx, code); + gcc_assert (HAVE_conditional_trap); insn = gen_conditional_trap (trap_rtx, tcode); if (insn) { @@ -5481,4 +5414,673 @@ gen_cond_trap (enum rtx_code code ATTRIBUTE_UNUSED, rtx op1, return insn; } +/* Return rtx code for TCODE. Use UNSIGNEDP to select signed + or unsigned operation code. */ + +static enum rtx_code +get_rtx_code (enum tree_code tcode, bool unsignedp) +{ + enum rtx_code code; + switch (tcode) + { + case EQ_EXPR: + code = EQ; + break; + case NE_EXPR: + code = NE; + break; + case LT_EXPR: + code = unsignedp ? LTU : LT; + break; + case LE_EXPR: + code = unsignedp ? LEU : LE; + break; + case GT_EXPR: + code = unsignedp ? GTU : GT; + break; + case GE_EXPR: + code = unsignedp ? GEU : GE; + break; + + case UNORDERED_EXPR: + code = UNORDERED; + break; + case ORDERED_EXPR: + code = ORDERED; + break; + case UNLT_EXPR: + code = UNLT; + break; + case UNLE_EXPR: + code = UNLE; + break; + case UNGT_EXPR: + code = UNGT; + break; + case UNGE_EXPR: + code = UNGE; + break; + case UNEQ_EXPR: + code = UNEQ; + break; + case LTGT_EXPR: + code = LTGT; + break; + + default: + gcc_unreachable (); + } + return code; +} + +/* Return comparison rtx for COND. Use UNSIGNEDP to select signed or + unsigned operators. Do not generate compare instruction. */ + +static rtx +vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode) +{ + enum rtx_code rcode; + tree t_op0, t_op1; + rtx rtx_op0, rtx_op1; + + /* This is unlikely. While generating VEC_COND_EXPR, auto vectorizer + ensures that condition is a relational operation. */ + gcc_assert (COMPARISON_CLASS_P (cond)); + + rcode = get_rtx_code (TREE_CODE (cond), unsignedp); + t_op0 = TREE_OPERAND (cond, 0); + t_op1 = TREE_OPERAND (cond, 1); + + /* Expand operands. */ + rtx_op0 = expand_expr (t_op0, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op0)), 1); + rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)), 1); + + if (!insn_data[icode].operand[4].predicate (rtx_op0, GET_MODE (rtx_op0)) + && GET_MODE (rtx_op0) != VOIDmode) + rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0); + + if (!insn_data[icode].operand[5].predicate (rtx_op1, GET_MODE (rtx_op1)) + && GET_MODE (rtx_op1) != VOIDmode) + rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1); + + return gen_rtx_fmt_ee (rcode, VOIDmode, rtx_op0, rtx_op1); +} + +/* Return insn code for VEC_COND_EXPR EXPR. */ + +static inline enum insn_code +get_vcond_icode (tree expr, enum machine_mode mode) +{ + enum insn_code icode = CODE_FOR_nothing; + + if (TYPE_UNSIGNED (TREE_TYPE (expr))) + icode = vcondu_gen_code[mode]; + else + icode = vcond_gen_code[mode]; + return icode; +} + +/* Return TRUE iff, appropriate vector insns are available + for vector cond expr expr in VMODE mode. */ + +bool +expand_vec_cond_expr_p (tree expr, enum machine_mode vmode) +{ + if (get_vcond_icode (expr, vmode) == CODE_FOR_nothing) + return false; + return true; +} + +/* Generate insns for VEC_COND_EXPR. */ + +rtx +expand_vec_cond_expr (tree vec_cond_expr, rtx target) +{ + enum insn_code icode; + rtx comparison, rtx_op1, rtx_op2, cc_op0, cc_op1; + enum machine_mode mode = TYPE_MODE (TREE_TYPE (vec_cond_expr)); + bool unsignedp = TYPE_UNSIGNED (TREE_TYPE (vec_cond_expr)); + + icode = get_vcond_icode (vec_cond_expr, mode); + if (icode == CODE_FOR_nothing) + return 0; + + if (!target) + target = gen_reg_rtx (mode); + + /* Get comparison rtx. First expand both cond expr operands. */ + comparison = vector_compare_rtx (TREE_OPERAND (vec_cond_expr, 0), + unsignedp, icode); + cc_op0 = XEXP (comparison, 0); + cc_op1 = XEXP (comparison, 1); + /* Expand both operands and force them in reg, if required. */ + rtx_op1 = expand_expr (TREE_OPERAND (vec_cond_expr, 1), + NULL_RTX, VOIDmode, 1); + if (!insn_data[icode].operand[1].predicate (rtx_op1, mode) + && mode != VOIDmode) + rtx_op1 = force_reg (mode, rtx_op1); + + rtx_op2 = expand_expr (TREE_OPERAND (vec_cond_expr, 2), + NULL_RTX, VOIDmode, 1); + if (!insn_data[icode].operand[2].predicate (rtx_op2, mode) + && mode != VOIDmode) + rtx_op2 = force_reg (mode, rtx_op2); + + /* Emit instruction! */ + emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2, + comparison, cc_op0, cc_op1)); + + return target; +} + + +/* This is an internal subroutine of the other compare_and_swap expanders. + MEM, OLD_VAL and NEW_VAL are as you'd expect for a compare-and-swap + operation. TARGET is an optional place to store the value result of + the operation. ICODE is the particular instruction to expand. Return + the result of the operation. */ + +static rtx +expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val, + rtx target, enum insn_code icode) +{ + enum machine_mode mode = GET_MODE (mem); + rtx insn; + + if (!target || !insn_data[icode].operand[0].predicate (target, mode)) + target = gen_reg_rtx (mode); + + if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode) + old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1); + if (!insn_data[icode].operand[2].predicate (old_val, mode)) + old_val = force_reg (mode, old_val); + + if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode) + new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1); + if (!insn_data[icode].operand[3].predicate (new_val, mode)) + new_val = force_reg (mode, new_val); + + insn = GEN_FCN (icode) (target, mem, old_val, new_val); + if (insn == NULL_RTX) + return NULL_RTX; + emit_insn (insn); + + return target; +} + +/* Expand a compare-and-swap operation and return its value. */ + +rtx +expand_val_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code icode = sync_compare_and_swap[mode]; + + if (icode == CODE_FOR_nothing) + return NULL_RTX; + + return expand_val_compare_and_swap_1 (mem, old_val, new_val, target, icode); +} + +/* Expand a compare-and-swap operation and store true into the result if + the operation was successful and false otherwise. Return the result. + Unlike other routines, TARGET is not optional. */ + +rtx +expand_bool_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code icode; + rtx subtarget, label0, label1; + + /* If the target supports a compare-and-swap pattern that simultaneously + sets some flag for success, then use it. Otherwise use the regular + compare-and-swap and follow that immediately with a compare insn. */ + icode = sync_compare_and_swap_cc[mode]; + switch (icode) + { + default: + subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val, + NULL_RTX, icode); + if (subtarget != NULL_RTX) + break; + + /* FALLTHRU */ + case CODE_FOR_nothing: + icode = sync_compare_and_swap[mode]; + if (icode == CODE_FOR_nothing) + return NULL_RTX; + + /* Ensure that if old_val == mem, that we're not comparing + against an old value. */ + if (MEM_P (old_val)) + old_val = force_reg (mode, old_val); + + subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val, + NULL_RTX, icode); + if (subtarget == NULL_RTX) + return NULL_RTX; + + emit_cmp_insn (subtarget, old_val, EQ, const0_rtx, mode, true); + } + + /* If the target has a sane STORE_FLAG_VALUE, then go ahead and use a + setcc instruction from the beginning. We don't work too hard here, + but it's nice to not be stupid about initial code gen either. */ + if (STORE_FLAG_VALUE == 1) + { + icode = setcc_gen_code[EQ]; + if (icode != CODE_FOR_nothing) + { + enum machine_mode cmode = insn_data[icode].operand[0].mode; + rtx insn; + + subtarget = target; + if (!insn_data[icode].operand[0].predicate (target, cmode)) + subtarget = gen_reg_rtx (cmode); + + insn = GEN_FCN (icode) (subtarget); + if (insn) + { + emit_insn (insn); + if (GET_MODE (target) != GET_MODE (subtarget)) + { + convert_move (target, subtarget, 1); + subtarget = target; + } + return subtarget; + } + } + } + + /* Without an appropriate setcc instruction, use a set of branches to + get 1 and 0 stored into target. Presumably if the target has a + STORE_FLAG_VALUE that isn't 1, then this will get cleaned up by ifcvt. */ + + label0 = gen_label_rtx (); + label1 = gen_label_rtx (); + + emit_jump_insn (bcc_gen_fctn[EQ] (label0)); + emit_move_insn (target, const0_rtx); + emit_jump_insn (gen_jump (label1)); + emit_barrier (); + emit_label (label0); + emit_move_insn (target, const1_rtx); + emit_label (label1); + + return target; +} + +/* This is a helper function for the other atomic operations. This function + emits a loop that contains SEQ that iterates until a compare-and-swap + operation at the end succeeds. MEM is the memory to be modified. SEQ is + a set of instructions that takes a value from OLD_REG as an input and + produces a value in NEW_REG as an output. Before SEQ, OLD_REG will be + set to the current contents of MEM. After SEQ, a compare-and-swap will + attempt to update MEM with NEW_REG. The function returns true when the + loop was generated successfully. */ + +static bool +expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code icode; + rtx label, cmp_reg, subtarget; + + /* The loop we want to generate looks like + + cmp_reg = mem; + label: + old_reg = cmp_reg; + seq; + cmp_reg = compare-and-swap(mem, old_reg, new_reg) + if (cmp_reg != old_reg) + goto label; + + Note that we only do the plain load from memory once. Subsequent + iterations use the value loaded by the compare-and-swap pattern. */ + + label = gen_label_rtx (); + cmp_reg = gen_reg_rtx (mode); + + emit_move_insn (cmp_reg, mem); + emit_label (label); + emit_move_insn (old_reg, cmp_reg); + if (seq) + emit_insn (seq); + + /* If the target supports a compare-and-swap pattern that simultaneously + sets some flag for success, then use it. Otherwise use the regular + compare-and-swap and follow that immediately with a compare insn. */ + icode = sync_compare_and_swap_cc[mode]; + switch (icode) + { + default: + subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg, + cmp_reg, icode); + if (subtarget != NULL_RTX) + { + gcc_assert (subtarget == cmp_reg); + break; + } + + /* FALLTHRU */ + case CODE_FOR_nothing: + icode = sync_compare_and_swap[mode]; + if (icode == CODE_FOR_nothing) + return false; + + subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg, + cmp_reg, icode); + if (subtarget == NULL_RTX) + return false; + if (subtarget != cmp_reg) + emit_move_insn (cmp_reg, subtarget); + + emit_cmp_insn (cmp_reg, old_reg, EQ, const0_rtx, mode, true); + } + + /* ??? Mark this jump predicted not taken? */ + emit_jump_insn (bcc_gen_fctn[NE] (label)); + + return true; +} + +/* This function generates the atomic operation MEM CODE= VAL. In this + case, we do not care about any resulting value. Returns NULL if we + cannot generate the operation. */ + +rtx +expand_sync_operation (rtx mem, rtx val, enum rtx_code code) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code icode; + rtx insn; + + /* Look to see if the target supports the operation directly. */ + switch (code) + { + case PLUS: + icode = sync_add_optab[mode]; + break; + case IOR: + icode = sync_ior_optab[mode]; + break; + case XOR: + icode = sync_xor_optab[mode]; + break; + case AND: + icode = sync_and_optab[mode]; + break; + case NOT: + icode = sync_nand_optab[mode]; + break; + + case MINUS: + icode = sync_sub_optab[mode]; + if (icode == CODE_FOR_nothing) + { + icode = sync_add_optab[mode]; + if (icode != CODE_FOR_nothing) + { + val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1); + code = PLUS; + } + } + break; + + default: + gcc_unreachable (); + } + + /* Generate the direct operation, if present. */ + if (icode != CODE_FOR_nothing) + { + if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) + val = convert_modes (mode, GET_MODE (val), val, 1); + if (!insn_data[icode].operand[1].predicate (val, mode)) + val = force_reg (mode, val); + + insn = GEN_FCN (icode) (mem, val); + if (insn) + { + emit_insn (insn); + return const0_rtx; + } + } + + /* Failing that, generate a compare-and-swap loop in which we perform the + operation with normal arithmetic instructions. */ + if (sync_compare_and_swap[mode] != CODE_FOR_nothing) + { + rtx t0 = gen_reg_rtx (mode), t1; + + start_sequence (); + + t1 = t0; + if (code == NOT) + { + t1 = expand_simple_unop (mode, NOT, t1, NULL_RTX, true); + code = AND; + } + t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX, + true, OPTAB_LIB_WIDEN); + + insn = get_insns (); + end_sequence (); + + if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn)) + return const0_rtx; + } + + return NULL_RTX; +} + +/* This function generates the atomic operation MEM CODE= VAL. In this + case, we do care about the resulting value: if AFTER is true then + return the value MEM holds after the operation, if AFTER is false + then return the value MEM holds before the operation. TARGET is an + optional place for the result value to be stored. */ + +rtx +expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code, + bool after, rtx target) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code old_code, new_code, icode; + bool compensate; + rtx insn; + + /* Look to see if the target supports the operation directly. */ + switch (code) + { + case PLUS: + old_code = sync_old_add_optab[mode]; + new_code = sync_new_add_optab[mode]; + break; + case IOR: + old_code = sync_old_ior_optab[mode]; + new_code = sync_new_ior_optab[mode]; + break; + case XOR: + old_code = sync_old_xor_optab[mode]; + new_code = sync_new_xor_optab[mode]; + break; + case AND: + old_code = sync_old_and_optab[mode]; + new_code = sync_new_and_optab[mode]; + break; + case NOT: + old_code = sync_old_nand_optab[mode]; + new_code = sync_new_nand_optab[mode]; + break; + + case MINUS: + old_code = sync_old_sub_optab[mode]; + new_code = sync_new_sub_optab[mode]; + if (old_code == CODE_FOR_nothing && new_code == CODE_FOR_nothing) + { + old_code = sync_old_add_optab[mode]; + new_code = sync_new_add_optab[mode]; + if (old_code != CODE_FOR_nothing || new_code != CODE_FOR_nothing) + { + val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1); + code = PLUS; + } + } + break; + + default: + gcc_unreachable (); + } + + /* If the target does supports the proper new/old operation, great. But + if we only support the opposite old/new operation, check to see if we + can compensate. In the case in which the old value is supported, then + we can always perform the operation again with normal arithmetic. In + the case in which the new value is supported, then we can only handle + this in the case the operation is reversible. */ + compensate = false; + if (after) + { + icode = new_code; + if (icode == CODE_FOR_nothing) + { + icode = old_code; + if (icode != CODE_FOR_nothing) + compensate = true; + } + } + else + { + icode = old_code; + if (icode == CODE_FOR_nothing + && (code == PLUS || code == MINUS || code == XOR)) + { + icode = new_code; + if (icode != CODE_FOR_nothing) + compensate = true; + } + } + + /* If we found something supported, great. */ + if (icode != CODE_FOR_nothing) + { + if (!target || !insn_data[icode].operand[0].predicate (target, mode)) + target = gen_reg_rtx (mode); + + if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) + val = convert_modes (mode, GET_MODE (val), val, 1); + if (!insn_data[icode].operand[2].predicate (val, mode)) + val = force_reg (mode, val); + + insn = GEN_FCN (icode) (target, mem, val); + if (insn) + { + emit_insn (insn); + + /* If we need to compensate for using an operation with the + wrong return value, do so now. */ + if (compensate) + { + if (!after) + { + if (code == PLUS) + code = MINUS; + else if (code == MINUS) + code = PLUS; + } + + if (code == NOT) + target = expand_simple_unop (mode, NOT, target, NULL_RTX, true); + target = expand_simple_binop (mode, code, target, val, NULL_RTX, + true, OPTAB_LIB_WIDEN); + } + + return target; + } + } + + /* Failing that, generate a compare-and-swap loop in which we perform the + operation with normal arithmetic instructions. */ + if (sync_compare_and_swap[mode] != CODE_FOR_nothing) + { + rtx t0 = gen_reg_rtx (mode), t1; + + if (!target || !register_operand (target, mode)) + target = gen_reg_rtx (mode); + + start_sequence (); + + if (!after) + emit_move_insn (target, t0); + t1 = t0; + if (code == NOT) + { + t1 = expand_simple_unop (mode, NOT, t1, NULL_RTX, true); + code = AND; + } + t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX, + true, OPTAB_LIB_WIDEN); + if (after) + emit_move_insn (target, t1); + + insn = get_insns (); + end_sequence (); + + if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn)) + return target; + } + + return NULL_RTX; +} + +/* This function expands a test-and-set operation. Ideally we atomically + store VAL in MEM and return the previous value in MEM. Some targets + may not support this operation and only support VAL with the constant 1; + in this case while the return value will be 0/1, but the exact value + stored in MEM is target defined. TARGET is an option place to stick + the return value. */ + +rtx +expand_sync_lock_test_and_set (rtx mem, rtx val, rtx target) +{ + enum machine_mode mode = GET_MODE (mem); + enum insn_code icode; + rtx insn; + + /* If the target supports the test-and-set directly, great. */ + icode = sync_lock_test_and_set[mode]; + if (icode != CODE_FOR_nothing) + { + if (!target || !insn_data[icode].operand[0].predicate (target, mode)) + target = gen_reg_rtx (mode); + + if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) + val = convert_modes (mode, GET_MODE (val), val, 1); + if (!insn_data[icode].operand[2].predicate (val, mode)) + val = force_reg (mode, val); + + insn = GEN_FCN (icode) (target, mem, val); + if (insn) + { + emit_insn (insn); + return target; + } + } + + /* Otherwise, use a compare-and-swap loop for the exchange. */ + if (sync_compare_and_swap[mode] != CODE_FOR_nothing) + { + if (!target || !register_operand (target, mode)) + target = gen_reg_rtx (mode); + if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) + val = convert_modes (mode, GET_MODE (val), val, 1); + if (expand_compare_and_swap_loop (mem, target, val, NULL_RTX)) + return target; + } + + return NULL_RTX; +} + #include "gt-optabs.h"