/* 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 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GCC.
rtx libfunc_table[LTI_MAX];
-/* Tables of patterns for extending one integer mode to another. */
-enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2];
-
-/* Tables of patterns for converting between fixed and floating point. */
-enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
-enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
-enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+/* Tables of patterns for converting one mode to another. */
+convert_optab convert_optab_table[CTI_MAX];
/* Contains the optab used for each rtx code. */
optab code_to_optab[NUM_RTX_CODE + 1];
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. */
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);
static enum insn_code can_fix_p (enum machine_mode, enum machine_mode, int,
int *);
static enum insn_code can_float_p (enum machine_mode, enum machine_mode, int);
-static rtx ftruncify (rtx);
static optab new_optab (void);
+static convert_optab new_convert_optab (void);
static inline optab init_optab (enum rtx_code);
static inline optab init_optabv (enum rtx_code);
+static inline convert_optab init_convert_optab (enum rtx_code);
static void init_libfuncs (optab, int, int, const char *, int);
static void init_integral_libfuncs (optab, const char *, int);
static void init_floating_libfuncs (optab, const char *, int);
+static void init_interclass_conv_libfuncs (convert_optab, const char *,
+ enum mode_class, enum mode_class);
+static void init_intraclass_conv_libfuncs (convert_optab, const char *,
+ enum mode_class, bool);
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
|| NEXT_INSN (insns) == NULL_RTX)
abort ();
- if (GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2'
- && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<')
+ if (GET_RTX_CLASS (code) != RTX_COMM_ARITH
+ && GET_RTX_CLASS (code) != RTX_BIN_ARITH
+ && GET_RTX_CLASS (code) != RTX_COMM_COMPARE
+ && GET_RTX_CLASS (code) != RTX_COMPARE
+ && GET_RTX_CLASS (code) != RTX_UNARY)
return 1;
if (GET_CODE (target) == ZERO_EXTRACT)
}
}
- if (GET_RTX_CLASS (code) == '1')
+ if (GET_RTX_CLASS (code) == RTX_UNARY)
note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
else
note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
return result;
}
\f
-/* 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;
- }
+ case BIT_AND_EXPR:
+ return and_optab;
- /* Don't fetch these from memory more than once. */
- real0 = force_reg (submode, real0);
- real1 = force_reg (submode, real1);
+ case BIT_IOR_EXPR:
+ return ior_optab;
- if (imag0 != 0)
- imag0 = force_reg (submode, imag0);
+ case BIT_NOT_EXPR:
+ return one_cmpl_optab;
- imag1 = force_reg (submode, imag1);
+ case BIT_XOR_EXPR:
+ return xor_optab;
- /* Divisor: c*c + d*d. */
- temp1 = expand_binop (submode, this_mul_optab, real1, real1,
- NULL_RTX, unsignedp, methods);
+ case TRUNC_MOD_EXPR:
+ case CEIL_MOD_EXPR:
+ case FLOOR_MOD_EXPR:
+ case ROUND_MOD_EXPR:
+ return TYPE_UNSIGNED (type) ? umod_optab : smod_optab;
- temp2 = expand_binop (submode, this_mul_optab, imag1, imag1,
- NULL_RTX, unsignedp, methods);
+ 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 (temp1 == 0 || temp2 == 0)
- return 0;
+ case LSHIFT_EXPR:
+ return ashl_optab;
- divisor = expand_binop (submode, this_add_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
- if (divisor == 0)
- return 0;
+ case RSHIFT_EXPR:
+ return TYPE_UNSIGNED (type) ? lshr_optab : ashr_optab;
- if (imag0 == 0)
- {
- /* Mathematically, ((a)(c-id))/divisor. */
- /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)). */
+ case LROTATE_EXPR:
+ return rotl_optab;
+
+ case RROTATE_EXPR:
+ return rotr_optab;
- /* Calculate the dividend. */
- real_t = expand_binop (submode, this_mul_optab, real0, real1,
- NULL_RTX, unsignedp, methods);
+ case MAX_EXPR:
+ return TYPE_UNSIGNED (type) ? umax_optab : smax_optab;
- imag_t = expand_binop (submode, this_mul_optab, real0, imag1,
- NULL_RTX, unsignedp, methods);
+ case MIN_EXPR:
+ return TYPE_UNSIGNED (type) ? umin_optab : smin_optab;
- if (real_t == 0 || imag_t == 0)
- return 0;
+ case REALIGN_STORE_EXPR:
+ return vec_realign_store_optab;
- imag_t = expand_unop (submode, this_neg_optab, imag_t,
- NULL_RTX, unsignedp);
+ case REALIGN_LOAD_EXPR:
+ return vec_realign_load_optab;
+
+ default:
+ break;
}
- else
+
+ trapv = flag_trapv && INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type);
+ switch (code)
{
- /* Mathematically, ((a+ib)(c-id))/divider. */
- /* Calculate the dividend. */
- temp1 = expand_binop (submode, this_mul_optab, real0, real1,
- NULL_RTX, unsignedp, methods);
+ case PLUS_EXPR:
+ return trapv ? addv_optab : add_optab;
- temp2 = expand_binop (submode, this_mul_optab, imag0, imag1,
- NULL_RTX, unsignedp, methods);
+ case MINUS_EXPR:
+ return trapv ? subv_optab : sub_optab;
- if (temp1 == 0 || temp2 == 0)
- return 0;
+ case MULT_EXPR:
+ return trapv ? smulv_optab : smul_optab;
- real_t = expand_binop (submode, this_add_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
+ case NEGATE_EXPR:
+ return trapv ? negv_optab : neg_optab;
- temp1 = expand_binop (submode, this_mul_optab, imag0, real1,
- NULL_RTX, unsignedp, methods);
+ case ABS_EXPR:
+ return trapv ? absv_optab : abs_optab;
- temp2 = expand_binop (submode, this_mul_optab, real0, imag1,
- NULL_RTX, unsignedp, methods);
+ default:
+ return NULL;
+ }
+}
+\f
- if (temp1 == 0 || temp2 == 0)
- return 0;
+/* Generate code to perform an operation specified by TERNARY_OPTAB
+ on operands OP0, OP1 and OP2, with result having machine-mode MODE.
- imag_t = expand_binop (submode, this_sub_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
+ UNSIGNEDP is for the case where we have to widen the operands
+ to perform the operation. It says to use zero-extension.
- if (real_t == 0 || imag_t == 0)
- return 0;
- }
+ 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. */
+
+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;
+
+ if (ternary_optab->handlers[(int) mode].insn_code == CODE_FOR_nothing)
+ abort ();
- if (class == MODE_COMPLEX_FLOAT)
- res = expand_binop (submode, binoptab, real_t, divisor,
- realr, unsignedp, methods);
+ if (!target
+ || ! (*insn_data[icode].operand[0].predicate) (target, mode))
+ temp = gen_reg_rtx (mode);
else
- res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
- real_t, divisor, realr, unsignedp);
+ 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;
+}
- if (res == 0)
- return 0;
- if (res != realr)
- emit_move_insn (realr, res);
+/* 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)
- res = expand_binop (submode, binoptab, imag_t, divisor,
- imagr, 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
- res = expand_divmod (0, TRUNC_DIV_EXPR, submode,
- imag_t, divisor, imagr, unsignedp);
-
- if (res == 0)
- return 0;
+ return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods);
+}
- if (res != imagr)
- emit_move_insn (imagr, res);
+/* Like simplify_expand_binop, but always put the result in TARGET.
+ Return true if the expansion succeeded. */
- return 1;
+static 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;
}
-\f
-/* 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)
+/* 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. */
+
+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)
{
- 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;
+ if (into_target != 0)
+ if (!force_expand_binop (word_mode, binoptab, outof_input, superword_op1,
+ into_target, unsignedp, methods))
+ return false;
- if (binoptab == sdivv_optab)
+ if (outof_target != 0)
{
- this_add_optab = addv_optab;
- this_sub_optab = subv_optab;
- this_neg_optab = negv_optab;
- this_mul_optab = smulv_optab;
+ /* 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;
+}
- /* Don't fetch these from memory more than once. */
- real0 = force_reg (submode, real0);
- real1 = force_reg (submode, real1);
+/* 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 (imag0 != 0)
- imag0 = force_reg (submode, imag0);
+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;
- imag1 = force_reg (submode, imag1);
+ reverse_unsigned_shift = (binoptab == ashl_optab ? lshr_optab : ashl_optab);
+ unsigned_shift = (binoptab == ashl_optab ? ashl_optab : lshr_optab);
- /* XXX What's an "unsigned" complex number? */
- if (unsignedp)
+ /* 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)
{
- temp1 = real1;
- temp2 = imag1;
+ 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);
}
else
{
- temp1 = expand_abs (submode, real1, NULL_RTX, unsignedp, 1);
- temp2 = expand_abs (submode, imag1, NULL_RTX, unsignedp, 1);
+ /* 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 (temp1 == 0 || temp2 == 0)
- return 0;
-
- 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. */
-
- if (class == MODE_COMPLEX_FLOAT)
- ratio = expand_binop (submode, binoptab, imag1, real1,
- NULL_RTX, unsignedp, methods);
- else
- ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode,
- imag1, real1, NULL_RTX, unsignedp);
-
- if (ratio == 0)
- return 0;
-
- /* Calculate divisor. */
-
- temp1 = expand_binop (submode, this_mul_optab, imag1, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0)
- return 0;
-
- divisor = expand_binop (submode, this_add_optab, temp1, real1,
- NULL_RTX, unsignedp, methods);
-
- if (divisor == 0)
- return 0;
- /* Calculate dividend. */
+#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 (imag0 == 0)
+ /* 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)
{
- real_t = real0;
-
- /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)). */
-
- imag_t = expand_binop (submode, this_mul_optab, real0, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (imag_t == 0)
- return 0;
-
- imag_t = expand_unop (submode, this_neg_optab, imag_t,
- NULL_RTX, unsignedp);
-
- if (real_t == 0 || imag_t == 0)
- return 0;
+ /* 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
{
- /* Compute (a+ib)/(c+id) as
- (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)). */
-
- temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0)
- return 0;
-
- real_t = expand_binop (submode, this_add_optab, temp1, real0,
- NULL_RTX, unsignedp, methods);
-
- temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0)
- return 0;
-
- imag_t = expand_binop (submode, this_sub_optab, imag0, temp1,
- NULL_RTX, unsignedp, methods);
-
- if (real_t == 0 || imag_t == 0)
- return 0;
+ 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 (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;
-
- if (res != realr)
- emit_move_insn (realr, res);
-
- 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);
-
- if (res == 0)
- return 0;
-
- if (res != imagr)
- emit_move_insn (imagr, res);
-
- lab2 = gen_label_rtx ();
- emit_jump_insn (gen_jump (lab2));
- emit_barrier ();
-
- emit_label (lab1);
-
- /* |d| > |c|; use ratio c/d to scale dividend and divisor. */
-
- 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);
-
- if (ratio == 0)
- return 0;
-
- /* Calculate divisor. */
-
- temp1 = expand_binop (submode, this_mul_optab, real1, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0)
- return 0;
-
- divisor = expand_binop (submode, this_add_optab, temp1, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (divisor == 0)
- return 0;
-
- /* Calculate dividend. */
+ /* 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 (imag0 == 0)
+/* 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))
{
- /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d). */
-
- real_t = expand_binop (submode, this_mul_optab, real0, ratio,
- NULL_RTX, unsignedp, methods);
-
- imag_t = expand_unop (submode, this_neg_optab, real0,
- NULL_RTX, unsignedp);
+ 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;
+ }
- if (real_t == 0 || imag_t == 0)
- return 0;
+ /* 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
{
- /* Compute (a+ib)/(c+id) as
- (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d). */
-
- temp1 = expand_binop (submode, this_mul_optab, real0, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0)
- return 0;
-
- real_t = expand_binop (submode, this_add_optab, temp1, imag0,
- NULL_RTX, unsignedp, methods);
-
- temp1 = expand_binop (submode, this_mul_optab, imag0, ratio,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0)
- return 0;
-
- imag_t = expand_binop (submode, this_sub_optab, temp1, real0,
- NULL_RTX, unsignedp, methods);
-
- if (real_t == 0 || imag_t == 0)
- return 0;
+ /* 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;
- 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 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);
+ }
- if (res == 0)
- return 0;
+#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 (res != realr)
- emit_move_insn (realr, res);
+ /* As a last resort, use branches to select the correct alternative. */
+ subword_label = gen_label_rtx ();
+ done_label = gen_label_rtx ();
- 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);
+ do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
+ 0, 0, subword_label);
- if (res == 0)
- return 0;
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_target, into_target,
+ unsignedp, methods))
+ return false;
- if (res != imagr)
- emit_move_insn (imagr, res);
+ emit_jump_insn (gen_jump (done_label));
+ emit_barrier ();
+ emit_label (subword_label);
- emit_label (lab2);
+ if (!expand_subword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ return false;
- return 1;
+ emit_label (done_label);
+ return true;
}
\f
/* Wrapper around expand_binop which takes an rtx code to specify
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. */
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 ();
try to make the first operand a register.
Even better, try to make it the same as the target.
Also try to make the last operand a constant. */
- if (GET_RTX_CLASS (binoptab->code) == 'c'
+ if (GET_RTX_CLASS (binoptab->code) == RTX_COMM_ARITH
|| binoptab == smul_widen_optab
|| binoptab == umul_widen_optab
|| binoptab == smul_highpart_optab
{
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)
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;
+ unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
+ enum machine_mode op1_mode;
- /* 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);
+ 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;
- start_sequence ();
+ /* Apply the truncation to constant shifts. */
+ if (double_shift_mask > 0 && GET_CODE (op1) == CONST_INT)
+ op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
- shift_count = INTVAL (op1);
+ if (op1 == CONST0_RTX (op1_mode))
+ return op0;
- /* 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);
-
- 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);
-
- if (inter != 0 && inter != into_target)
- emit_move_insn (into_target, inter);
-
- /* 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 (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);
-
- if (inter != 0 && inter != outof_target)
- emit_move_insn (outof_target, inter);
- }
-
- insns = get_insns ();
- end_sequence ();
+ outof_input = operand_subword_force (op0, outof_word, mode);
+ into_input = operand_subword_force (op0, 1 - outof_word, mode);
- 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 ();
}
}
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)
+ 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
+ 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))
target = gen_reg_rtx (mode);
start_sequence ();
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. */
/* 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 && !REG_P (target)))
target = 0;
/* Multiply the two lower words to get a double-word product.
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;
- rtx equiv_value;
- 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 ();
-
- if (! target)
- target = gen_reg_rtx (mode);
-
- start_sequence ();
-
- realr = gen_realpart (submode, target);
- imagr = gen_imagpart (submode, target);
-
- if (GET_MODE (op0) == mode)
- {
- real0 = gen_realpart (submode, op0);
- imag0 = gen_imagpart (submode, op0);
- }
- else
- real0 = op0;
-
- if (GET_MODE (op1) == mode)
- {
- real1 = gen_realpart (submode, op1);
- imag1 = gen_imagpart (submode, op1);
- }
- else
- real1 = op1;
-
- if (real0 == 0 || real1 == 0 || ! (imag0 != 0 || imag1 != 0))
- abort ();
-
- switch (binoptab->code)
- {
- 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
- {
- 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 ();
- }
- }
- break;
-
- default:
- abort ();
- }
-
- seq = get_insns ();
- end_sequence ();
-
- if (ok)
- {
- if (binoptab->code != UNKNOWN)
- equiv_value
- = gen_rtx_fmt_ee (binoptab->code, mode,
- copy_rtx (op0), copy_rtx (op1));
- else
- equiv_value = 0;
-
- emit_no_conflict_block (seq, target, op0, op1, equiv_value);
-
- return target;
- }
- }
-
/* It can't be open-coded in this mode.
Use a library call if one is available and caller says that's ok. */
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;
-}
\f
/* Expand a binary operator which has both signed and unsigned forms.
UOPTAB is the optab for unsigned operations, and SOPTAB is for
if (temp || methods == OPTAB_WIDEN)
return temp;
- /* Use the right width lib call if that exists. */
- temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
- if (temp || methods == OPTAB_LIB)
- return temp;
+ /* Use the right width lib call if that exists. */
+ temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
+ if (temp || methods == OPTAB_LIB)
+ return temp;
+
+ /* Must widen and use a lib call, use either signed or unsigned. */
+ temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+ unsignedp, methods);
+ if (temp != 0)
+ return temp;
+ if (unsignedp)
+ return expand_binop (mode, uoptab, op0, op1, target,
+ unsignedp, methods);
+ return 0;
+}
+\f
+/* 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, 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
+ 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_unop (optab unoptab, rtx op0, 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);
+
+ 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 (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+ {
+ 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. */
+ if (! (*insn_data[icode].operand[0].predicate) (targ0, mode)
+ || ! (*insn_data[icode].operand[1].predicate) (targ1, mode))
+ abort ();
+
+ 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);
+ }
+ }
+ }
- /* Must widen and use a lib call, use either signed or unsigned. */
- temp = expand_binop (mode, &wide_soptab, op0, op1, target,
- unsignedp, methods);
- if (temp != 0)
- return temp;
- if (unsignedp)
- return expand_binop (mode, uoptab, op0, op1, target,
- unsignedp, methods);
+ delete_insns_since (entry_last);
return 0;
}
\f
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 ()
+ /* 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) && preserve_subexpressions_p ()
+ if (CONSTANT_P (op1) && optimize
&& rtx_cost (op1, binoptab->code) > COSTS_N_INSNS (1))
op1 = force_reg (mode, op1);
- 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. */
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. */
+ if (!((targ0 != NULL_RTX) ^ (targ1 != NULL_RTX)))
+ abort ();
+
+ 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;
+}
+
\f
/* Wrapper around expand_unop which takes an rtx code to specify
the operation to perform, not an optab pointer. All other
temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
true);
if (temp != 0)
- temp = expand_binop (wider_mode, and_optab, temp, GEN_INT (1),
+ temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
target, true, OPTAB_DIRECT);
if (temp == 0)
delete_insns_since (last);
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)
{
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)
immed_double_const (lo, hi, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
if (temp != 0)
- return gen_lowpart (mode, temp);
+ {
+ 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);
}
}
return temp;
}
+ /* If there is no negation pattern, try subtracting from zero. */
+ if (unoptab == neg_optab && class == MODE_INT)
+ {
+ temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
+ target, unsignedp, OPTAB_DIRECT);
+ if (temp)
+ return temp;
+ }
+
try_libcall:
/* Now try a library call in this mode. */
if (unoptab->handlers[(int) mode].libfunc)
have them return something that isn't a double-word. */
if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab
|| unoptab == popcount_optab || unoptab == parity_optab)
- outmode = TYPE_MODE (integer_type_node);
+ outmode
+ = GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node)));
start_sequence ();
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)
}
/* If there is no negate operation, try doing a subtract from zero.
- The US Software GOFAST library needs this. */
+ The US Software GOFAST library needs this. FIXME: This is *wrong*
+ for floating-point operations due to negative zeros! */
if (unoptab->code == NEG)
{
rtx temp;
immed_double_const (~lo, ~hi, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
if (temp != 0)
- return gen_lowpart (mode, temp);
+ {
+ 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);
}
}
/* 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);
return target;
}
\f
-/* 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. */
-
-rtx
-expand_complex_abs (enum machine_mode mode, rtx op0, rtx target,
- int unsignedp)
-{
- 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);
-
- if (submode == BLKmode)
- abort ();
-
- op0 = protect_from_queue (op0, 0);
-
- if (flag_force_mem)
- {
- op0 = force_not_mem (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)
- {
- 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)
- temp = target;
- else
- temp = gen_reg_rtx (submode);
-
- if (GET_MODE (xop0) != VOIDmode
- && GET_MODE (xop0) != mode0)
- xop0 = convert_to_mode (mode0, xop0, unsignedp);
-
- /* Now, if insn doesn't accept our operand, put it into a pseudo. */
-
- if (! (*insn_data[icode].operand[1].predicate) (xop0, mode0))
- xop0 = copy_to_mode_reg (mode0, xop0);
-
- if (! (*insn_data[icode].operand[0].predicate) (temp, submode))
- temp = gen_reg_rtx (submode);
-
- 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_insn (pat);
-
- return temp;
- }
- else
- delete_insns_since (last);
- }
-
- /* It can't be done in this mode. Can we open-code it in a wider mode? */
-
- 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);
-
- 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);
- }
- }
-
- /* 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)
- {
- 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);
-
- /* 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);
- else
- return target;
- }
-
- /* Now try a library call in this mode. */
- if (this_abs_optab->handlers[(int) mode].libfunc)
- {
- rtx insns;
- rtx value;
-
- 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);
- 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;
- }
-
- /* It can't be done in this mode. Can we do it in a wider mode? */
-
- 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;
-
- xop0 = convert_modes (wider_mode, mode, xop0, unsignedp);
-
- temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp);
-
- 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);
- }
- }
-
- delete_insns_since (entry_last);
- return 0;
-}
-\f
/* Generate an instruction whose insn-code is INSN_CODE,
with two operands: an output TARGET and an input OP0.
TARGET *must* be nonzero, and the output is always stored there.
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
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))
+ || (flag_force_mem && MEM_P (temp)))
temp = gen_reg_rtx (GET_MODE (temp));
pat = GEN_FCN (icode) (temp, op0);
{
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);
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));
}
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);
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);
if (note != 0)
- XEXP (note, 0) = GEN_INT (-1);
+ XEXP (note, 0) = constm1_rtx;
else
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1),
+ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx,
REG_NOTES (insn));
}
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)
/* 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;
}
}
}
\f
-/* 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.
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);
}
}
- /* 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);
if (mode == BLKmode)
{
+ enum machine_mode cmp_mode, result_mode;
+ enum insn_code cmp_code;
+ tree length_type;
+ rtx libfunc;
rtx result;
- enum machine_mode result_mode;
- rtx opalign ATTRIBUTE_UNUSED
+ rtx opalign
= GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT);
- emit_queue ();
- x = protect_from_queue (x, 0);
- y = protect_from_queue (y, 0);
-
if (size == 0)
abort ();
-#ifdef HAVE_cmpmemqi
- if (HAVE_cmpmemqi
- && GET_CODE (size) == CONST_INT
- && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
- {
- result_mode = insn_data[(int) CODE_FOR_cmpmemqi].operand[0].mode;
- result = gen_reg_rtx (result_mode);
- emit_insn (gen_cmpmemqi (result, x, y, size, opalign));
- }
- else
-#endif
-#ifdef HAVE_cmpmemhi
- if (HAVE_cmpmemhi
- && GET_CODE (size) == CONST_INT
- && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
- {
- result_mode = insn_data[(int) CODE_FOR_cmpmemhi].operand[0].mode;
- result = gen_reg_rtx (result_mode);
- emit_insn (gen_cmpmemhi (result, x, y, size, opalign));
- }
- else
-#endif
-#ifdef HAVE_cmpmemsi
- if (HAVE_cmpmemsi)
- {
- result_mode = insn_data[(int) CODE_FOR_cmpmemsi].operand[0].mode;
- result = gen_reg_rtx (result_mode);
- size = protect_from_queue (size, 0);
- emit_insn (gen_cmpmemsi (result, x, y,
- convert_to_mode (SImode, size, 1),
- opalign));
- }
- else
-#endif
-#ifdef HAVE_cmpstrqi
- if (HAVE_cmpstrqi
- && GET_CODE (size) == CONST_INT
- && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode)))
- {
- result_mode = insn_data[(int) CODE_FOR_cmpstrqi].operand[0].mode;
- result = gen_reg_rtx (result_mode);
- emit_insn (gen_cmpstrqi (result, x, y, size, opalign));
- }
- else
-#endif
-#ifdef HAVE_cmpstrhi
- if (HAVE_cmpstrhi
- && GET_CODE (size) == CONST_INT
- && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode)))
- {
- result_mode = insn_data[(int) CODE_FOR_cmpstrhi].operand[0].mode;
- result = gen_reg_rtx (result_mode);
- emit_insn (gen_cmpstrhi (result, x, y, size, opalign));
- }
- else
-#endif
-#ifdef HAVE_cmpstrsi
- if (HAVE_cmpstrsi)
- {
- result_mode = insn_data[(int) CODE_FOR_cmpstrsi].operand[0].mode;
- result = gen_reg_rtx (result_mode);
- size = protect_from_queue (size, 0);
- emit_insn (gen_cmpstrsi (result, x, y,
- convert_to_mode (SImode, size, 1),
- opalign));
- }
- else
-#endif
+
+ /* Try to use a memory block compare insn - either cmpstr
+ or cmpmem will do. */
+ for (cmp_mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ cmp_mode != VOIDmode;
+ cmp_mode = GET_MODE_WIDER_MODE (cmp_mode))
{
-#ifdef TARGET_MEM_FUNCTIONS
- result = emit_library_call_value (memcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
- TYPE_MODE (integer_type_node), 3,
- XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
- convert_to_mode (TYPE_MODE (sizetype), size,
- TREE_UNSIGNED (sizetype)),
- TYPE_MODE (sizetype));
-#else
- result = emit_library_call_value (bcmp_libfunc, NULL_RTX, LCT_PURE_MAKE_BLOCK,
- TYPE_MODE (integer_type_node), 3,
- XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
- convert_to_mode (TYPE_MODE (integer_type_node),
- size,
- TREE_UNSIGNED (integer_type_node)),
- TYPE_MODE (integer_type_node));
-#endif
+ cmp_code = cmpmem_optab[cmp_mode];
+ if (cmp_code == CODE_FOR_nothing)
+ cmp_code = cmpstr_optab[cmp_mode];
+ if (cmp_code == CODE_FOR_nothing)
+ continue;
+
+ /* Must make sure the size fits the insn's mode. */
+ if ((GET_CODE (size) == CONST_INT
+ && INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode)))
+ || (GET_MODE_BITSIZE (GET_MODE (size))
+ > GET_MODE_BITSIZE (cmp_mode)))
+ continue;
+
+ result_mode = insn_data[cmp_code].operand[0].mode;
+ result = gen_reg_rtx (result_mode);
+ size = convert_to_mode (cmp_mode, size, 1);
+ emit_insn (GEN_FCN (cmp_code) (result, x, y, size, opalign));
- result_mode = TYPE_MODE (integer_type_node);
+ *px = result;
+ *py = const0_rtx;
+ *pmode = result_mode;
+ return;
}
+
+ /* Otherwise call a library function, memcmp. */
+ libfunc = memcmp_libfunc;
+ length_type = sizetype;
+ result_mode = TYPE_MODE (integer_type_node);
+ cmp_mode = TYPE_MODE (length_type);
+ size = convert_to_mode (TYPE_MODE (length_type), size,
+ TYPE_UNSIGNED (length_type));
+
+ result = emit_library_call_value (libfunc, 0, LCT_PURE_MAKE_BLOCK,
+ result_mode, 3,
+ XEXP (x, 0), Pmode,
+ XEXP (y, 0), Pmode,
+ size, cmp_mode);
*px = result;
*py = const0_rtx;
*pmode = result_mode;
return;
}
+ /* Don't allow operands to the compare to trap, as that can put the
+ compare and branch in different basic blocks. */
+ if (flag_non_call_exceptions)
+ {
+ if (may_trap_p (x))
+ x = force_reg (mode, x);
+ if (may_trap_p (y))
+ y = force_reg (mode, y);
+ }
+
*px = x;
*py = y;
if (can_compare_p (*pcomparison, mode, purpose))
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;
}
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);
op0 = force_reg (mode, op0);
#endif
- emit_queue ();
if (unsignedp)
comparison = unsigned_condition (comparison);
{
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 before_call;
+ rtx value, target, insns, equiv;
rtx libfunc = 0;
- rtx result;
+ bool reversed_p = false;
for (mode = orig_mode; mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode))
{
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)
y = convert_to_mode (mode, y, 0);
}
- before_call = get_last_insn ();
-
- result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
- word_mode, 2, x, mode, y, mode);
-
- /* If we're optimizing attach a REG_EQUAL note describing the semantics
- of the libcall to the RTL. The allows the RTL optimizers to delete
- the libcall if the condition can be determined at compile-time. */
- if (optimize
- && ! side_effects_p (x)
- && ! side_effects_p (y))
+ /* Attach a REG_EQUAL note describing the semantics of the libcall to
+ the RTL. The allows the RTL optimizers to delete the libcall if the
+ condition can be determined at compile-time. */
+ if (comparison == UNORDERED)
{
- /* Search backwards through the insns emitted above looking for
- the instruction with the REG_RETVAL note. */
- rtx last = get_last_insn ();
- while (last != before_call)
+ rtx temp = simplify_gen_relational (NE, word_mode, mode, x, x);
+ equiv = simplify_gen_relational (NE, word_mode, mode, y, y);
+ equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
+ temp, const_true_rtx, equiv);
+ }
+ else
+ {
+ equiv = simplify_gen_relational (comparison, word_mode, mode, x, y);
+ if (! FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
{
- if (find_reg_note (last, REG_RETVAL, NULL))
- break;
- last = PREV_INSN (last);
- }
+ rtx true_rtx, false_rtx;
- if (last != before_call)
- {
- rtx equiv;
- if (comparison == UNORDERED)
+ switch (comparison)
{
- rtx temp = simplify_gen_relational (NE, word_mode,
- mode, x, x);
- equiv = simplify_gen_relational (NE, word_mode,
- mode, y, y);
- equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode,
- word_mode, temp,
- const_true_rtx, equiv);
- }
- else
- {
- equiv = simplify_gen_relational (comparison, word_mode,
- mode, x, y);
- if (! FLOAT_LIB_COMPARE_RETURNS_BOOL (mode, comparison))
- {
- rtx true_rtx, false_rtx;
-
- switch (comparison)
- {
- case EQ:
- true_rtx = const0_rtx;
- false_rtx = const_true_rtx;
- break;
-
- case NE:
- true_rtx = const_true_rtx;
- false_rtx = const0_rtx;
- break;
-
- case GT:
- true_rtx = const1_rtx;
- false_rtx = const0_rtx;
- break;
-
- case GE:
- true_rtx = const0_rtx;
- false_rtx = constm1_rtx;
- break;
-
- case LT:
- true_rtx = constm1_rtx;
- false_rtx = const0_rtx;
- break;
-
- case LE:
- true_rtx = const0_rtx;
- false_rtx = const1_rtx;
- break;
-
- default:
- abort ();
- }
- equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode,
- word_mode, equiv,
- true_rtx, false_rtx);
- }
+ case EQ:
+ true_rtx = const0_rtx;
+ false_rtx = const_true_rtx;
+ break;
+
+ case NE:
+ true_rtx = const_true_rtx;
+ false_rtx = const0_rtx;
+ break;
+
+ case GT:
+ true_rtx = const1_rtx;
+ false_rtx = const0_rtx;
+ break;
+
+ case GE:
+ true_rtx = const0_rtx;
+ false_rtx = constm1_rtx;
+ break;
+
+ case LT:
+ true_rtx = constm1_rtx;
+ false_rtx = const0_rtx;
+ break;
+
+ case LE:
+ true_rtx = const0_rtx;
+ false_rtx = const1_rtx;
+ break;
+
+ default:
+ abort ();
}
- set_unique_reg_note (last, REG_EQUAL, equiv);
+ equiv = simplify_gen_ternary (IF_THEN_ELSE, word_mode, word_mode,
+ equiv, true_rtx, false_rtx);
}
}
+ start_sequence ();
+ value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
+ word_mode, 2, x, mode, y, mode);
+ insns = get_insns ();
+ end_sequence ();
+
+ 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 = result;
+ *px = target;
*py = const0_rtx;
*pmode = word_mode;
*pcomparison = comparison;
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)
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))
+ (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)
(op2, insn_data[icode].operand[2].mode))
\f
/* 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. */
can_extend_p (enum machine_mode to_mode, enum machine_mode from_mode,
int unsignedp)
{
+ convert_optab tab;
#ifdef HAVE_ptr_extend
if (unsignedp < 0)
return CODE_FOR_ptr_extend;
- else
#endif
- return extendtab[(int) to_mode][(int) from_mode][unsignedp != 0];
+
+ tab = unsignedp ? zext_optab : sext_optab;
+ return tab->handlers[to_mode][from_mode].insn_code;
}
/* Generate the body of an insn to extend Y (with mode MFROM)
gen_extend_insn (rtx x, rtx y, enum machine_mode mto,
enum machine_mode mfrom, int unsignedp)
{
- return (GEN_FCN (extendtab[(int) mto][(int) mfrom][unsignedp != 0]) (x, y));
+ enum insn_code icode = can_extend_p (mto, mfrom, unsignedp);
+ return GEN_FCN (icode) (x, y);
}
\f
/* can_fix_p and can_float_p say whether the target machine
can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
int unsignedp, int *truncp_ptr)
{
- *truncp_ptr = 0;
- if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp != 0]
- != CODE_FOR_nothing)
- return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp != 0];
+ convert_optab tab;
+ enum insn_code icode;
+
+ tab = unsignedp ? ufixtrunc_optab : sfixtrunc_optab;
+ icode = tab->handlers[fixmode][fltmode].insn_code;
+ if (icode != CODE_FOR_nothing)
+ {
+ *truncp_ptr = 0;
+ return icode;
+ }
- if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing)
+ /* FIXME: This requires a port to define both FIX and FTRUNC pattern
+ for this to work. We need to rework the fix* and ftrunc* patterns
+ and documentation. */
+ tab = unsignedp ? ufix_optab : sfix_optab;
+ icode = tab->handlers[fixmode][fltmode].insn_code;
+ if (icode != CODE_FOR_nothing
+ && ftrunc_optab->handlers[fltmode].insn_code != CODE_FOR_nothing)
{
*truncp_ptr = 1;
- return fixtab[(int) fltmode][(int) fixmode][unsignedp != 0];
+ return icode;
}
+
+ *truncp_ptr = 0;
return CODE_FOR_nothing;
}
can_float_p (enum machine_mode fltmode, enum machine_mode fixmode,
int unsignedp)
{
- return floattab[(int) fltmode][(int) fixmode][unsignedp != 0];
+ convert_optab tab;
+
+ tab = unsignedp ? ufloat_optab : sfloat_optab;
+ return tab->handlers[fltmode][fixmode].insn_code;
}
\f
/* Generate code to convert FROM to floating point
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);
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);
/* 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);
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. */
goto done;
}
- /* No hardware instruction available; call a library routine to convert from
- SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode. */
+ /* No hardware instruction available; call a library routine. */
{
- rtx libfcn;
+ rtx libfunc;
rtx insns;
rtx value;
-
- to = protect_from_queue (to, 1);
- from = protect_from_queue (from, 0);
+ convert_optab tab = unsignedp ? ufloat_optab : sfloat_optab;
if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
from = convert_to_mode (SImode, from, unsignedp);
if (flag_force_mem)
from = force_not_mem (from);
- if (GET_MODE (to) == SFmode)
- {
- if (GET_MODE (from) == SImode)
- libfcn = floatsisf_libfunc;
- else if (GET_MODE (from) == DImode)
- libfcn = floatdisf_libfunc;
- else if (GET_MODE (from) == TImode)
- libfcn = floattisf_libfunc;
- else
- abort ();
- }
- else if (GET_MODE (to) == DFmode)
- {
- if (GET_MODE (from) == SImode)
- libfcn = floatsidf_libfunc;
- else if (GET_MODE (from) == DImode)
- libfcn = floatdidf_libfunc;
- else if (GET_MODE (from) == TImode)
- libfcn = floattidf_libfunc;
- else
- abort ();
- }
- else if (GET_MODE (to) == XFmode)
- {
- if (GET_MODE (from) == SImode)
- libfcn = floatsixf_libfunc;
- else if (GET_MODE (from) == DImode)
- libfcn = floatdixf_libfunc;
- else if (GET_MODE (from) == TImode)
- libfcn = floattixf_libfunc;
- else
- abort ();
- }
- else if (GET_MODE (to) == TFmode)
- {
- if (GET_MODE (from) == SImode)
- libfcn = floatsitf_libfunc;
- else if (GET_MODE (from) == DImode)
- libfcn = floatditf_libfunc;
- else if (GET_MODE (from) == TImode)
- libfcn = floattitf_libfunc;
- else
- abort ();
- }
- else
+ libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
+ if (!libfunc)
abort ();
start_sequence ();
- value = emit_library_call_value (libfcn, NULL_RTX, LCT_CONST,
+ value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
GET_MODE (to), 1, from,
GET_MODE (from));
insns = get_insns ();
}
}
\f
-/* expand_fix: generate code to convert FROM to fixed point
- and store in TO. FROM must be floating point. */
-
-static rtx
-ftruncify (rtx x)
-{
- rtx temp = gen_reg_rtx (GET_MODE (x));
- return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0);
-}
+/* Generate code to convert FROM to fixed point and store in TO. FROM
+ must be floating point. */
void
expand_fix (rtx to, rtx from, int unsignedp)
rtx target = to;
enum machine_mode fmode, imode;
int must_trunc = 0;
- rtx libfcn = 0;
/* We first try to find a pair of modes, one real and one integer, at
least as wide as FROM and TO, respectively, in which we can open-code
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);
if (must_trunc)
- from = ftruncify (from);
+ {
+ rtx temp = gen_reg_rtx (GET_MODE (from));
+ from = expand_unop (GET_MODE (from), ftrunc_optab, from,
+ temp, 0);
+ }
if (imode != GET_MODE (to))
target = gen_reg_rtx (imode);
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);
expand_fix (target, from, unsignedp);
}
- else if (GET_MODE (from) == SFmode)
- {
- if (GET_MODE (to) == SImode)
- libfcn = unsignedp ? fixunssfsi_libfunc : fixsfsi_libfunc;
- else if (GET_MODE (to) == DImode)
- libfcn = unsignedp ? fixunssfdi_libfunc : fixsfdi_libfunc;
- else if (GET_MODE (to) == TImode)
- libfcn = unsignedp ? fixunssfti_libfunc : fixsfti_libfunc;
- else
- abort ();
- }
- else if (GET_MODE (from) == DFmode)
- {
- if (GET_MODE (to) == SImode)
- libfcn = unsignedp ? fixunsdfsi_libfunc : fixdfsi_libfunc;
- else if (GET_MODE (to) == DImode)
- libfcn = unsignedp ? fixunsdfdi_libfunc : fixdfdi_libfunc;
- else if (GET_MODE (to) == TImode)
- libfcn = unsignedp ? fixunsdfti_libfunc : fixdfti_libfunc;
- else
- abort ();
- }
- else if (GET_MODE (from) == XFmode)
- {
- if (GET_MODE (to) == SImode)
- libfcn = unsignedp ? fixunsxfsi_libfunc : fixxfsi_libfunc;
- else if (GET_MODE (to) == DImode)
- libfcn = unsignedp ? fixunsxfdi_libfunc : fixxfdi_libfunc;
- else if (GET_MODE (to) == TImode)
- libfcn = unsignedp ? fixunsxfti_libfunc : fixxfti_libfunc;
- else
- abort ();
- }
- else if (GET_MODE (from) == TFmode)
- {
- if (GET_MODE (to) == SImode)
- libfcn = unsignedp ? fixunstfsi_libfunc : fixtfsi_libfunc;
- else if (GET_MODE (to) == DImode)
- libfcn = unsignedp ? fixunstfdi_libfunc : fixtfdi_libfunc;
- else if (GET_MODE (to) == TImode)
- libfcn = unsignedp ? fixunstfti_libfunc : fixtfti_libfunc;
- else
- abort ();
- }
else
- abort ();
-
- if (libfcn)
{
rtx insns;
rtx value;
+ rtx libfunc;
- to = protect_from_queue (to, 1);
- from = protect_from_queue (from, 0);
+ convert_optab tab = unsignedp ? ufix_optab : sfix_optab;
+ libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
+ if (!libfunc)
+ abort ();
if (flag_force_mem)
from = force_not_mem (from);
start_sequence ();
- value = emit_library_call_value (libfcn, NULL_RTX, LCT_CONST,
+ value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
GET_MODE (to), 1, from,
GET_MODE (from));
insns = get_insns ();
return op;
}
+static convert_optab
+new_convert_optab (void)
+{
+ int i, j;
+ convert_optab op = ggc_alloc (sizeof (struct convert_optab));
+ for (i = 0; i < NUM_MACHINE_MODES; i++)
+ for (j = 0; j < NUM_MACHINE_MODES; j++)
+ {
+ op->handlers[i][j].insn_code = CODE_FOR_nothing;
+ op->handlers[i][j].libfunc = 0;
+ }
+ return op;
+}
+
/* Same, but fill in its code as CODE, and write it into the
code_to_optab table. */
static inline optab
return op;
}
+/* Conversion optabs never go in the code_to_optab table. */
+static inline convert_optab
+init_convert_optab (enum rtx_code code)
+{
+ convert_optab op = new_convert_optab ();
+ op->code = code;
+ return op;
+}
+
/* Initialize the libfunc fields of an entire group of entries in some
optab. Each entry is set equal to a string consisting of a leading
pair of underscores followed by a generic operation name followed by
- a mode name (downshifted to lower case) followed by a single character
+ a mode name (downshifted to lowercase) followed by a single character
representing the number of operands for the given operation (which is
usually one of the characters '2', '3', or '4').
static void
init_floating_libfuncs (optab optable, const char *opname, int suffix)
{
- enum machine_mode fmode, dmode, lmode;
-
- fmode = float_type_node ? TYPE_MODE (float_type_node) : VOIDmode;
- dmode = double_type_node ? TYPE_MODE (double_type_node) : VOIDmode;
- lmode = long_double_type_node ? TYPE_MODE (long_double_type_node) : VOIDmode;
-
- if (fmode != VOIDmode)
- init_libfuncs (optable, fmode, fmode, opname, suffix);
- if (dmode != fmode && dmode != VOIDmode)
- init_libfuncs (optable, dmode, dmode, opname, suffix);
- if (lmode != dmode && lmode != VOIDmode)
- init_libfuncs (optable, lmode, lmode, opname, suffix);
+ init_libfuncs (optable, MIN_MODE_FLOAT, MAX_MODE_FLOAT, opname, suffix);
+}
+
+/* Initialize the libfunc fields of an entire group of entries of an
+ inter-mode-class conversion optab. The string formation rules are
+ similar to the ones for init_libfuncs, above, but instead of having
+ a mode name and an operand count these functions have two mode names
+ and no operand count. */
+static void
+init_interclass_conv_libfuncs (convert_optab tab, const char *opname,
+ enum mode_class from_class,
+ enum mode_class to_class)
+{
+ enum machine_mode first_from_mode = GET_CLASS_NARROWEST_MODE (from_class);
+ enum machine_mode first_to_mode = GET_CLASS_NARROWEST_MODE (to_class);
+ size_t opname_len = strlen (opname);
+ size_t max_mname_len = 0;
+
+ enum machine_mode fmode, tmode;
+ const char *fname, *tname;
+ const char *q;
+ char *libfunc_name, *suffix;
+ char *p;
+
+ for (fmode = first_from_mode;
+ fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ max_mname_len = MAX (max_mname_len, strlen (GET_MODE_NAME (fmode)));
+
+ for (tmode = first_to_mode;
+ tmode != VOIDmode;
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ max_mname_len = MAX (max_mname_len, strlen (GET_MODE_NAME (tmode)));
+
+ libfunc_name = alloca (2 + opname_len + 2*max_mname_len + 1 + 1);
+ libfunc_name[0] = '_';
+ libfunc_name[1] = '_';
+ memcpy (&libfunc_name[2], opname, opname_len);
+ suffix = libfunc_name + opname_len + 2;
+
+ for (fmode = first_from_mode; fmode != VOIDmode;
+ fmode = GET_MODE_WIDER_MODE (fmode))
+ for (tmode = first_to_mode; tmode != VOIDmode;
+ tmode = GET_MODE_WIDER_MODE (tmode))
+ {
+ fname = GET_MODE_NAME (fmode);
+ tname = GET_MODE_NAME (tmode);
+
+ p = suffix;
+ for (q = fname; *q; p++, q++)
+ *p = TOLOWER (*q);
+ for (q = tname; *q; p++, q++)
+ *p = TOLOWER (*q);
+
+ *p = '\0';
+
+ tab->handlers[tmode][fmode].libfunc
+ = init_one_libfunc (ggc_alloc_string (libfunc_name,
+ p - libfunc_name));
+ }
+}
+
+/* Initialize the libfunc fields of an entire group of entries of an
+ intra-mode-class conversion optab. The string formation rules are
+ similar to the ones for init_libfunc, above. WIDENING says whether
+ the optab goes from narrow to wide modes or vice versa. These functions
+ have two mode names _and_ an operand count. */
+static void
+init_intraclass_conv_libfuncs (convert_optab tab, const char *opname,
+ enum mode_class class, bool widening)
+{
+ enum machine_mode first_mode = GET_CLASS_NARROWEST_MODE (class);
+ size_t opname_len = strlen (opname);
+ size_t max_mname_len = 0;
+
+ enum machine_mode nmode, wmode;
+ const char *nname, *wname;
+ const char *q;
+ char *libfunc_name, *suffix;
+ char *p;
+
+ for (nmode = first_mode; nmode != VOIDmode;
+ nmode = GET_MODE_WIDER_MODE (nmode))
+ max_mname_len = MAX (max_mname_len, strlen (GET_MODE_NAME (nmode)));
+
+ libfunc_name = alloca (2 + opname_len + 2*max_mname_len + 1 + 1);
+ libfunc_name[0] = '_';
+ libfunc_name[1] = '_';
+ memcpy (&libfunc_name[2], opname, opname_len);
+ suffix = libfunc_name + opname_len + 2;
+
+ for (nmode = first_mode; nmode != VOIDmode;
+ nmode = GET_MODE_WIDER_MODE (nmode))
+ for (wmode = GET_MODE_WIDER_MODE (nmode); wmode != VOIDmode;
+ wmode = GET_MODE_WIDER_MODE (wmode))
+ {
+ nname = GET_MODE_NAME (nmode);
+ wname = GET_MODE_NAME (wmode);
+
+ p = suffix;
+ for (q = widening ? nname : wname; *q; p++, q++)
+ *p = TOLOWER (*q);
+ for (q = widening ? wname : nname; *q; p++, q++)
+ *p = TOLOWER (*q);
+
+ *p++ = '2';
+ *p = '\0';
+
+ tab->handlers[widening ? wmode : nmode]
+ [widening ? nmode : wmode].libfunc
+ = init_one_libfunc (ggc_alloc_string (libfunc_name,
+ p - libfunc_name));
+ }
}
+
rtx
init_one_libfunc (const char *name)
{
optable->handlers[mode].libfunc = 0;
}
+/* Call this to reset the function entry for one conversion optab
+ (OPTABLE) from mode FMODE to mode TMODE to NAME, which should be
+ either 0 or a string constant. */
+void
+set_conv_libfunc (convert_optab optable, enum machine_mode tmode,
+ enum machine_mode fmode, const char *name)
+{
+ if (name)
+ optable->handlers[tmode][fmode].libfunc = init_one_libfunc (name);
+ else
+ optable->handlers[tmode][fmode].libfunc = 0;
+}
+
/* Call this once to initialize the contents of the optabs
appropriately for the current target machine. */
void
init_optabs (void)
{
- unsigned int i, j, k;
+ unsigned int i;
/* Start by initializing all tables to contain CODE_FOR_nothing. */
- for (i = 0; i < ARRAY_SIZE (fixtab); i++)
- for (j = 0; j < ARRAY_SIZE (fixtab[0]); j++)
- for (k = 0; k < ARRAY_SIZE (fixtab[0][0]); k++)
- fixtab[i][j][k] = CODE_FOR_nothing;
-
- for (i = 0; i < ARRAY_SIZE (fixtrunctab); i++)
- for (j = 0; j < ARRAY_SIZE (fixtrunctab[0]); j++)
- for (k = 0; k < ARRAY_SIZE (fixtrunctab[0][0]); k++)
- fixtrunctab[i][j][k] = CODE_FOR_nothing;
-
- for (i = 0; i < ARRAY_SIZE (floattab); i++)
- for (j = 0; j < ARRAY_SIZE (floattab[0]); j++)
- for (k = 0; k < ARRAY_SIZE (floattab[0][0]); k++)
- floattab[i][j][k] = CODE_FOR_nothing;
-
- for (i = 0; i < ARRAY_SIZE (extendtab); i++)
- for (j = 0; j < ARRAY_SIZE (extendtab[0]); j++)
- for (k = 0; k < ARRAY_SIZE (extendtab[0][0]); k++)
- extendtab[i][j][k] = CODE_FOR_nothing;
-
for (i = 0; i < NUM_RTX_CODE; i++)
setcc_gen_code[i] = CODE_FOR_nothing;
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);
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);
floor_optab = init_optab (UNKNOWN);
ceil_optab = init_optab (UNKNOWN);
round_optab = init_optab (UNKNOWN);
- trunc_optab = init_optab (UNKNOWN);
+ btrunc_optab = init_optab (UNKNOWN);
nearbyint_optab = init_optab (UNKNOWN);
+ rint_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);
+ 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);
strlen_optab = init_optab (UNKNOWN);
cstore_optab = init_optab (UNKNOWN);
push_optab = init_optab (UNKNOWN);
+ vec_extract_optab = init_optab (UNKNOWN);
+ vec_set_optab = init_optab (UNKNOWN);
+ vec_init_optab = init_optab (UNKNOWN);
+ vec_realign_load_optab = init_optab (UNKNOWN);
+
+ /* Conversions. */
+ sext_optab = init_convert_optab (SIGN_EXTEND);
+ zext_optab = init_convert_optab (ZERO_EXTEND);
+ trunc_optab = init_convert_optab (TRUNCATE);
+ sfix_optab = init_convert_optab (FIX);
+ ufix_optab = init_convert_optab (UNSIGNED_FIX);
+ sfixtrunc_optab = init_convert_optab (UNKNOWN);
+ ufixtrunc_optab = init_convert_optab (UNKNOWN);
+ sfloat_optab = init_convert_optab (FLOAT);
+ ufloat_optab = init_convert_optab (UNSIGNED_FLOAT);
+
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;
+ clrmem_optab[i] = CODE_FOR_nothing;
+ cmpstr_optab[i] = CODE_FOR_nothing;
+ cmpmem_optab[i] = CODE_FOR_nothing;
#ifdef HAVE_SECONDARY_RELOADS
reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
/* Fill in the optabs with the insns we support. */
init_all_optabs ();
-#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC
- /* This flag says the same insns that convert to a signed fixnum
- also convert validly to an unsigned one. */
- for (i = 0; i < NUM_MACHINE_MODES; i++)
- for (j = 0; j < NUM_MACHINE_MODES; j++)
- fixtrunctab[i][j][1] = fixtrunctab[i][j][0];
-#endif
-
/* Initialize the optabs with the names of the library functions. */
init_integral_libfuncs (add_optab, "add", '3');
init_floating_libfuncs (add_optab, "add", '3');
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');
init_floating_libfuncs (le_optab, "le", '2');
init_floating_libfuncs (unord_optab, "unord", '2');
- /* Use cabs for DC complex abs, since systems generally have cabs.
- Don't define any libcall for SCmode, so that cabs will be used. */
- abs_optab->handlers[(int) DCmode].libfunc
- = init_one_libfunc ("cabs");
+ /* 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);
+
+ /* sext_optab is also used for FLOAT_EXTEND. */
+ init_intraclass_conv_libfuncs (sext_optab, "extend", MODE_FLOAT, true);
+ init_intraclass_conv_libfuncs (trunc_optab, "trunc", MODE_FLOAT, false);
+
+ /* Use cabs for double complex abs, since systems generally have cabs.
+ Don't define any libcall for float complex, so that cabs will be used. */
+ if (complex_double_type_node)
+ abs_optab->handlers[TYPE_MODE (complex_double_type_node)].libfunc
+ = init_one_libfunc ("cabs");
/* The ffs function operates on `int'. */
ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)].libfunc
= init_one_libfunc ("ffs");
- extendsfdf2_libfunc = init_one_libfunc ("__extendsfdf2");
- extendsfxf2_libfunc = init_one_libfunc ("__extendsfxf2");
- extendsftf2_libfunc = init_one_libfunc ("__extendsftf2");
- extenddfxf2_libfunc = init_one_libfunc ("__extenddfxf2");
- extenddftf2_libfunc = init_one_libfunc ("__extenddftf2");
-
- truncdfsf2_libfunc = init_one_libfunc ("__truncdfsf2");
- truncxfsf2_libfunc = init_one_libfunc ("__truncxfsf2");
- trunctfsf2_libfunc = init_one_libfunc ("__trunctfsf2");
- truncxfdf2_libfunc = init_one_libfunc ("__truncxfdf2");
- trunctfdf2_libfunc = init_one_libfunc ("__trunctfdf2");
-
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_unregister_libfunc
= init_one_libfunc ("_Unwind_SjLj_Unregister");
- floatsisf_libfunc = init_one_libfunc ("__floatsisf");
- floatdisf_libfunc = init_one_libfunc ("__floatdisf");
- floattisf_libfunc = init_one_libfunc ("__floattisf");
-
- floatsidf_libfunc = init_one_libfunc ("__floatsidf");
- floatdidf_libfunc = init_one_libfunc ("__floatdidf");
- floattidf_libfunc = init_one_libfunc ("__floattidf");
-
- floatsixf_libfunc = init_one_libfunc ("__floatsixf");
- floatdixf_libfunc = init_one_libfunc ("__floatdixf");
- floattixf_libfunc = init_one_libfunc ("__floattixf");
-
- floatsitf_libfunc = init_one_libfunc ("__floatsitf");
- floatditf_libfunc = init_one_libfunc ("__floatditf");
- floattitf_libfunc = init_one_libfunc ("__floattitf");
-
- fixsfsi_libfunc = init_one_libfunc ("__fixsfsi");
- fixsfdi_libfunc = init_one_libfunc ("__fixsfdi");
- fixsfti_libfunc = init_one_libfunc ("__fixsfti");
-
- fixdfsi_libfunc = init_one_libfunc ("__fixdfsi");
- fixdfdi_libfunc = init_one_libfunc ("__fixdfdi");
- fixdfti_libfunc = init_one_libfunc ("__fixdfti");
-
- fixxfsi_libfunc = init_one_libfunc ("__fixxfsi");
- fixxfdi_libfunc = init_one_libfunc ("__fixxfdi");
- fixxfti_libfunc = init_one_libfunc ("__fixxfti");
-
- fixtfsi_libfunc = init_one_libfunc ("__fixtfsi");
- fixtfdi_libfunc = init_one_libfunc ("__fixtfdi");
- fixtfti_libfunc = init_one_libfunc ("__fixtfti");
-
- fixunssfsi_libfunc = init_one_libfunc ("__fixunssfsi");
- fixunssfdi_libfunc = init_one_libfunc ("__fixunssfdi");
- fixunssfti_libfunc = init_one_libfunc ("__fixunssfti");
-
- fixunsdfsi_libfunc = init_one_libfunc ("__fixunsdfsi");
- fixunsdfdi_libfunc = init_one_libfunc ("__fixunsdfdi");
- fixunsdfti_libfunc = init_one_libfunc ("__fixunsdfti");
-
- fixunsxfsi_libfunc = init_one_libfunc ("__fixunsxfsi");
- fixunsxfdi_libfunc = init_one_libfunc ("__fixunsxfdi");
- fixunsxfti_libfunc = init_one_libfunc ("__fixunsxfti");
-
- fixunstfsi_libfunc = init_one_libfunc ("__fixunstfsi");
- fixunstfdi_libfunc = init_one_libfunc ("__fixunstfdi");
- fixunstfti_libfunc = init_one_libfunc ("__fixunstfti");
-
/* For function entry/exit instrumentation. */
profile_function_entry_libfunc
= init_one_libfunc ("__cyg_profile_func_enter");
= 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);
/* 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)
+ {
+ if (GET_CODE (h->libfunc) != SYMBOL_REF)
+ abort ();
+ 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)
+ {
+ if (GET_CODE (h->libfunc) != SYMBOL_REF)
+ abort ();
+ 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 */
+
\f
/* Generate insns to trap with code TCODE if OP1 and OP2 satisfy condition
CODE. Return 0 on failure. */
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:
+ abort ();
+ }
+ 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;
+
+ if (TREE_CODE_CLASS (TREE_CODE (cond)) != '<')
+ {
+ /* This is unlikely. While generating VEC_COND_EXPR,
+ auto vectorizer ensures that condition is a relational
+ operation. */
+ abort ();
+ }
+ else
+ {
+ 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;
+}
#include "gt-optabs.h"