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);
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
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)
-{
- 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)
- {
- this_add_optab = addv_optab;
- this_sub_optab = subv_optab;
- this_neg_optab = negv_optab;
- this_mul_optab = smulv_optab;
- }
-
- /* Don't fetch these from memory more than once. */
- real0 = force_reg (submode, real0);
- real1 = force_reg (submode, real1);
-
- if (imag0 != 0)
- imag0 = force_reg (submode, imag0);
-
- imag1 = force_reg (submode, imag1);
-
- /* Divisor: c*c + d*d. */
- temp1 = expand_binop (submode, this_mul_optab, real1, real1,
- NULL_RTX, unsignedp, methods);
-
- temp2 = expand_binop (submode, this_mul_optab, imag1, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0 || temp2 == 0)
- return 0;
-
- divisor = expand_binop (submode, this_add_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
- if (divisor == 0)
- return 0;
-
- if (imag0 == 0)
- {
- /* Mathematically, ((a)(c-id))/divisor. */
- /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)). */
-
- /* Calculate the dividend. */
- real_t = expand_binop (submode, this_mul_optab, real0, real1,
- NULL_RTX, unsignedp, methods);
-
- imag_t = expand_binop (submode, this_mul_optab, real0, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (real_t == 0 || imag_t == 0)
- return 0;
-
- imag_t = expand_unop (submode, this_neg_optab, imag_t,
- NULL_RTX, unsignedp);
- }
- else
- {
- /* Mathematically, ((a+ib)(c-id))/divider. */
- /* Calculate the dividend. */
- temp1 = expand_binop (submode, this_mul_optab, real0, real1,
- NULL_RTX, unsignedp, methods);
-
- temp2 = expand_binop (submode, this_mul_optab, imag0, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0 || temp2 == 0)
- return 0;
-
- real_t = expand_binop (submode, this_add_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
-
- temp1 = expand_binop (submode, this_mul_optab, imag0, real1,
- NULL_RTX, unsignedp, methods);
-
- temp2 = expand_binop (submode, this_mul_optab, real0, imag1,
- NULL_RTX, unsignedp, methods);
-
- if (temp1 == 0 || temp2 == 0)
- return 0;
-
- imag_t = expand_binop (submode, this_sub_optab, temp1, temp2,
- NULL_RTX, unsignedp, methods);
-
- if (real_t == 0 || imag_t == 0)
- return 0;
- }
-
- 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);
-
- return 1;
-}
-\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)
-{
- 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 (binoptab == sdivv_optab)
- {
- this_add_optab = addv_optab;
- this_sub_optab = subv_optab;
- this_neg_optab = negv_optab;
- this_mul_optab = smulv_optab;
- }
-
- /* Don't fetch these from memory more than once. */
- real0 = force_reg (submode, real0);
- real1 = force_reg (submode, real1);
-
- if (imag0 != 0)
- imag0 = force_reg (submode, imag0);
-
- imag1 = force_reg (submode, imag1);
-
- /* XXX What's an "unsigned" complex number? */
- if (unsignedp)
- {
- temp1 = real1;
- temp2 = imag1;
- }
- else
- {
- temp1 = expand_abs (submode, real1, NULL_RTX, unsignedp, 1);
- temp2 = expand_abs (submode, imag1, NULL_RTX, unsignedp, 1);
- }
-
- if (temp1 == 0 || temp2 == 0)
- return 0;
-
- 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. */
-
- if (imag0 == 0)
- {
- 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;
- }
- 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;
- }
-
- 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. */
-
- if (imag0 == 0)
- {
- /* 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 (real_t == 0 || imag_t == 0)
- return 0;
- }
- else
- {
- /* Compute (a+ib)/(c+id) as
- (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d). */
-
- 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;
- }
-
- 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);
-
- emit_label (lab2);
-
- return 1;
-}
-\f
/* 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
case MIN_EXPR:
return TYPE_UNSIGNED (type) ? umin_optab : smin_optab;
+ case REALIGN_STORE_EXPR:
+ return vec_realign_store_optab;
+
+ case REALIGN_LOAD_EXPR:
+ return vec_realign_load_optab;
+
default:
break;
}
return NULL;
}
}
+\f
+
+/* Generate code to perform an operation specified by TERNARY_OPTAB
+ on operands OP0, OP1 and OP2, with result having machine-mode MODE.
+
+ UNSIGNEDP is for the case where we have to widen the operands
+ to perform the operation. It says to use zero-extension.
+
+ 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 (!target
+ || ! (*insn_data[icode].operand[0].predicate) (target, mode))
+ temp = gen_reg_rtx (mode);
+ else
+ 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;
+}
+
+
+/* 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. */
+
+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
+ return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods);
+}
+
+/* Like simplify_expand_binop, but always put the result in TARGET.
+ Return true if the expansion succeeded. */
+
+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;
+}
+
+/* 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)
+{
+ if (into_target != 0)
+ if (!force_expand_binop (word_mode, binoptab, outof_input, superword_op1,
+ into_target, unsignedp, methods))
+ return false;
+
+ if (outof_target != 0)
+ {
+ /* For a signed right shift, we must fill OUTOF_TARGET with copies
+ of the sign bit, otherwise we must fill it with zeros. */
+ if (binoptab != ashr_optab)
+ emit_move_insn (outof_target, CONST0_RTX (word_mode));
+ else
+ if (!force_expand_binop (word_mode, binoptab,
+ outof_input, GEN_INT (BITS_PER_WORD - 1),
+ outof_target, unsignedp, methods))
+ return false;
+ }
+ return true;
+}
+
+/* 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. */
+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;
+
+ reverse_unsigned_shift = (binoptab == ashl_optab ? lshr_optab : ashl_optab);
+ unsigned_shift = (binoptab == ashl_optab ? ashl_optab : lshr_optab);
+
+ /* The low OP1 bits of INTO_TARGET come from the high bits of OUTOF_INPUT.
+ We therefore need to shift OUTOF_INPUT by (BITS_PER_WORD - OP1) bits in
+ the opposite direction to BINOPTAB. */
+ if (CONSTANT_P (op1) || shift_mask >= BITS_PER_WORD)
+ {
+ carries = outof_input;
+ tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+ tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
+ 0, true, methods);
+ }
+ else
+ {
+ /* 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;
+}
+
+
+#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;
+
+ /* Put the superword version of the output into OUTOF_SUPERWORD and
+ INTO_SUPERWORD. */
+ outof_superword = outof_target != 0 ? gen_reg_rtx (word_mode) : 0;
+ if (outof_target != 0 && subword_op1 == superword_op1)
+ {
+ /* The value INTO_TARGET >> SUBWORD_OP1, which we later store in
+ OUTOF_TARGET, is the same as the value of INTO_SUPERWORD. */
+ into_superword = outof_target;
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_superword, 0, unsignedp, methods))
+ return false;
+ }
+ else
+ {
+ into_superword = gen_reg_rtx (word_mode);
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_superword, into_superword,
+ unsignedp, methods))
+ return false;
+ }
+
+ /* 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
+
+/* Expand a doubleword shift (ashl, ashr or lshr) using word-mode shifts.
+ OUTOF_INPUT and INTO_INPUT are the two word-sized halves of the first
+ input operand; the shift moves bits in the direction OUTOF_INPUT->
+ INTO_TARGET. OUTOF_TARGET and INTO_TARGET are the equivalent words
+ of the target. OP1 is the shift count and OP1_MODE is its mode.
+ If OP1 is constant, it will have been truncated as appropriate
+ and is known to be nonzero.
+
+ If SHIFT_MASK is zero, the result of word shifts is undefined when the
+ shift count is outside the range [0, BITS_PER_WORD). This routine must
+ avoid generating such shifts for OP1s in the range [0, BITS_PER_WORD * 2).
+
+ If SHIFT_MASK is nonzero, all word-mode shift counts are effectively
+ masked by it and shifts in the range [BITS_PER_WORD, SHIFT_MASK) will
+ fill with zeros or sign bits as appropriate.
+
+ If SHIFT_MASK is BITS_PER_WORD - 1, this routine will synthesize
+ a doubleword shift whose equivalent mask is BITS_PER_WORD * 2 - 1.
+ Doing this preserves semantics required by SHIFT_COUNT_TRUNCATED.
+ In all other cases, shifts by values outside [0, BITS_PER_UNIT * 2)
+ are undefined.
+
+ BINOPTAB, UNSIGNEDP and METHODS are as for expand_binop. This function
+ may not use INTO_INPUT after modifying INTO_TARGET, and similarly for
+ OUTOF_INPUT and OUTOF_TARGET. OUTOF_TARGET can be null if the parent
+ function wants to calculate it itself.
+
+ Return true if the shift could be successfully synthesized. */
+
+static bool
+expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab,
+ rtx outof_input, rtx into_input, rtx op1,
+ rtx outof_target, rtx into_target,
+ int unsignedp, enum optab_methods methods,
+ unsigned HOST_WIDE_INT shift_mask)
+{
+ rtx superword_op1, tmp, cmp1, cmp2;
+ rtx subword_label, done_label;
+ enum rtx_code cmp_code;
+
+ /* See if word-mode shifts by BITS_PER_WORD...BITS_PER_WORD * 2 - 1 will
+ fill the result with sign or zero bits as appropriate. If so, the value
+ of OUTOF_TARGET will always be (SHIFT OUTOF_INPUT OP1). Recursively call
+ this routine to calculate INTO_TARGET (which depends on both OUTOF_INPUT
+ and INTO_INPUT), then emit code to set up OUTOF_TARGET.
+
+ This isn't worthwhile for constant shifts since the optimizers will
+ cope better with in-range shift counts. */
+ if (shift_mask >= BITS_PER_WORD
+ && outof_target != 0
+ && !CONSTANT_P (op1))
+ {
+ if (!expand_doubleword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ 0, into_target,
+ unsignedp, methods, shift_mask))
+ return false;
+ if (!force_expand_binop (word_mode, binoptab, outof_input, op1,
+ outof_target, unsignedp, methods))
+ return false;
+ return true;
+ }
+
+ /* Set CMP_CODE, CMP1 and CMP2 so that the rtx (CMP_CODE CMP1 CMP2)
+ is true when the effective shift value is less than BITS_PER_WORD.
+ Set SUPERWORD_OP1 to the shift count that should be used to shift
+ OUTOF_INPUT into INTO_TARGET when the condition is false. */
+ tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+ if (!CONSTANT_P (op1) && shift_mask == BITS_PER_WORD - 1)
+ {
+ /* Set CMP1 to OP1 & BITS_PER_WORD. The result is zero iff OP1
+ is a subword shift count. */
+ cmp1 = simplify_expand_binop (op1_mode, and_optab, op1, tmp,
+ 0, true, methods);
+ cmp2 = CONST0_RTX (op1_mode);
+ cmp_code = EQ;
+ superword_op1 = op1;
+ }
+ else
+ {
+ /* Set CMP1 to OP1 - BITS_PER_WORD. */
+ cmp1 = simplify_expand_binop (op1_mode, sub_optab, op1, tmp,
+ 0, true, methods);
+ cmp2 = CONST0_RTX (op1_mode);
+ cmp_code = LT;
+ superword_op1 = cmp1;
+ }
+ if (cmp1 == 0)
+ return false;
+
+ /* 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);
+ }
+
+#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
+
+ /* As a last resort, use branches to select the correct alternative. */
+ subword_label = gen_label_rtx ();
+ done_label = gen_label_rtx ();
+
+ do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
+ 0, 0, subword_label);
+
+ if (!expand_superword_shift (binoptab, outof_input, superword_op1,
+ outof_target, into_target,
+ unsignedp, methods))
+ return false;
+
+ emit_jump_insn (gen_jump (done_label));
+ emit_barrier ();
+ emit_label (subword_label);
+
+ if (!expand_subword_shift (op1_mode, binoptab,
+ outof_input, into_input, op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ return false;
+
+ emit_label (done_label);
+ return true;
+}
\f
/* Wrapper around expand_binop which takes an rtx code to specify
the operation to perform, not an optab pointer. All other
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 ();
if ((binoptab == lshr_optab || binoptab == ashl_optab
|| binoptab == ashr_optab)
&& class == MODE_INT
- && GET_CODE (op1) == CONST_INT
+ && (GET_CODE (op1) == CONST_INT || !optimize_size)
&& GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
&& binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
{
- rtx insns, inter, equiv_value;
- rtx into_target, outof_target;
- rtx into_input, outof_input;
- int shift_count, left_shift, outof_word;
-
- /* If TARGET is the same as one of the operands, the REG_EQUAL note
- won't be accurate, so use a new target. */
- if (target == 0 || target == op0 || target == op1)
- target = gen_reg_rtx (mode);
-
- start_sequence ();
-
- shift_count = INTVAL (op1);
-
- /* OUTOF_* is the word we are shifting bits away from, and
- INTO_* is the word that we are shifting bits towards, thus
- they differ depending on the direction of the shift and
- WORDS_BIG_ENDIAN. */
-
- left_shift = binoptab == ashl_optab;
- outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
+ unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
+ enum machine_mode op1_mode;
- 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);
+ double_shift_mask = targetm.shift_truncation_mask (mode);
+ shift_mask = targetm.shift_truncation_mask (word_mode);
+ op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode;
- if (inter != 0 && inter != into_target)
- emit_move_insn (into_target, inter);
+ /* Apply the truncation to constant shifts. */
+ if (double_shift_mask > 0 && GET_CODE (op1) == CONST_INT)
+ op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
- /* For a signed right shift, we must fill the word we are shifting
- out of with copies of the sign bit. Otherwise it is zeroed. */
- if (inter != 0 && binoptab != ashr_optab)
- inter = CONST0_RTX (word_mode);
- else if (inter != 0)
- inter = expand_binop (word_mode, binoptab,
- outof_input,
- GEN_INT (BITS_PER_WORD - 1),
- outof_target, unsignedp, next_methods);
+ if (op1 == CONST0_RTX (op1_mode))
+ return op0;
- if (inter != 0 && inter != outof_target)
- emit_move_insn (outof_target, inter);
- }
- else
+ /* Make sure that this is a combination that expand_doubleword_shift
+ can handle. See the comments there for details. */
+ if (double_shift_mask == 0
+ || (shift_mask == BITS_PER_WORD - 1
+ && double_shift_mask == BITS_PER_WORD * 2 - 1))
{
- rtx carries;
- optab reverse_unsigned_shift, unsigned_shift;
-
- /* 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. */
+ rtx insns, equiv_value;
+ rtx into_target, outof_target;
+ rtx into_input, outof_input;
+ int left_shift, outof_word;
- reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab);
+ /* 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);
- /* 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. */
+ start_sequence ();
- unsigned_shift = (left_shift ? ashl_optab : lshr_optab);
-
- carries = expand_binop (word_mode, reverse_unsigned_shift,
- outof_input,
- GEN_INT (BITS_PER_WORD - shift_count),
- 0, unsignedp, next_methods);
-
- 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);
+ /* 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. */
- if (inter != 0)
- inter = expand_binop (word_mode, binoptab, outof_input,
- op1, outof_target, unsignedp, next_methods);
+ left_shift = binoptab == ashl_optab;
+ outof_word = left_shift ^ ! WORDS_BIG_ENDIAN;
- if (inter != 0 && inter != outof_target)
- emit_move_insn (outof_target, inter);
- }
+ outof_target = operand_subword (target, outof_word, 1, mode);
+ into_target = operand_subword (target, 1 - outof_word, 1, mode);
- 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 ();
}
}
delete_insns_since (last);
}
- /* Open-code the vector operations if we have no hardware support
- for them. */
- if (class == MODE_VECTOR_INT || class == MODE_VECTOR_FLOAT)
- return expand_vector_binop (mode, binoptab, op0, op1, target,
- unsignedp, methods);
-
- /* We need to open-code the complex type operations: '+, -, * and /' */
-
- /* At this point we allow operations between two similar complex
- numbers, and also if one of the operands is not a complex number
- but rather of MODE_FLOAT or MODE_INT. However, the caller
- must make sure that the MODE of the non-complex operand matches
- the SUBMODE of the complex operand. */
-
- if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)
- {
- rtx real0 = 0, imag0 = 0;
- rtx real1 = 0, imag1 = 0;
- rtx realr, imagr, res;
- rtx seq, result;
- int ok = 0;
-
- /* Find the correct mode for the real and imaginary parts. */
- enum machine_mode submode = GET_MODE_INNER (mode);
-
- if (submode == BLKmode)
- abort ();
-
- start_sequence ();
-
- if (GET_MODE (op0) == mode)
- {
- real0 = gen_realpart (submode, op0);
- imag0 = gen_imagpart (submode, op0);
- }
- else
- real0 = op0;
-
- if (GET_MODE (op1) == mode)
- {
- real1 = gen_realpart (submode, op1);
- imag1 = gen_imagpart (submode, op1);
- }
- else
- real1 = op1;
-
- if (real0 == 0 || real1 == 0 || ! (imag0 != 0 || imag1 != 0))
- abort ();
-
- result = gen_reg_rtx (mode);
- realr = gen_realpart (submode, result);
- imagr = gen_imagpart (submode, result);
-
- switch (binoptab->code)
- {
- 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)
- {
- rtx equiv = gen_rtx_fmt_ee (binoptab->code, mode,
- copy_rtx (op0), copy_rtx (op1));
- emit_no_conflict_block (seq, result, op0, op1, equiv);
- return result;
- }
- }
-
/* It can't be open-coded in this mode.
Use a library call if one is available and caller says that's ok. */
/* The second operand of a shift must always be extended. */
xop1 = widen_operand (xop1, wider_mode, mode, unsignedp,
- no_extend && binoptab != ashl_optab);
-
- temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
- unsignedp, methods);
- if (temp)
- {
- if (class != MODE_INT)
- {
- if (target == 0)
- target = gen_reg_rtx (mode);
- convert_move (target, temp, 0);
- return target;
- }
- else
- return gen_lowpart (mode, temp);
- }
- else
- delete_insns_since (last);
- }
- }
- }
-
- 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 (REG_P (target)
- && (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);
- 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);
-
- 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);
- }
- 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 (REG_P (target)
- && (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);
-
- res = expand_unop (submode, unoptab, a, t, unsignedp);
+ no_extend && binoptab != ashl_optab);
- if (t)
- emit_move_insn (t, res);
- else
- store_bit_field (target, subbitsize, i * subbitsize, submode, res);
+ temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX,
+ unsignedp, methods);
+ if (temp)
+ {
+ if (class != MODE_INT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (mode, temp);
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
}
- seq = get_insns ();
- end_sequence ();
- emit_insn (seq);
-
- return target;
+ delete_insns_since (entry_last);
+ return 0;
}
\f
/* Expand a binary operator which has both signed and unsigned forms.
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);
mode of OP0. Returns 1 if the call was successful. */
bool
-expand_twoval_binop_libfunc (optab binoptab, rtx op0, rtx op1,
+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 ();
/* 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),
+ 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,
+ NULL_RTX, LCT_CONST,
libval_mode, 2,
- op0, mode,
+ op0, mode,
op1, mode);
/* Get the part of VAL containing the value that we want. */
libval = simplify_gen_subreg (mode, libval, libval_mode,
insns = get_insns ();
end_sequence ();
/* Move the into the desired location. */
- emit_libcall_block (insns, targ0 ? targ0 : targ1, libval,
+ emit_libcall_block (insns, targ0 ? targ0 : targ1, libval,
gen_rtx_fmt_ee (code, mode, op0, op1));
-
+
return true;
}
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)
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)
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 ();
-
- if (flag_force_mem)
- op0 = force_not_mem (op0);
-
- last = get_last_insn ();
-
- 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.
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);
WIDER_MODE (UNSIGNEDP determines whether it is an unsigned conversion), and
that it is accepted by the operand predicate. Return the new value. */
-rtx
+static rtx
prepare_operand (int icode, rtx x, int opnum, enum machine_mode mode,
enum machine_mode wider_mode, int unsignedp)
{
break;
}
}
-
+
if (mode == VOIDmode)
abort ();
rtx insns;
rtx value;
rtx libfunc;
-
+
convert_optab tab = unsignedp ? ufix_optab : sfix_optab;
libfunc = tab->handlers[GET_MODE (to)][GET_MODE (from)].libfunc;
if (!libfunc)
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);
round_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);
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);
init_all_optabs ();
/* Initialize the optabs with the names of the library functions. */
- if (TARGET_LIBGCC_LIBFUNCS)
- {
- init_integral_libfuncs (add_optab, "add", '3');
- init_floating_libfuncs (add_optab, "add", '3');
- init_integral_libfuncs (addv_optab, "addv", '3');
- init_floating_libfuncs (addv_optab, "add", '3');
- init_integral_libfuncs (sub_optab, "sub", '3');
- init_floating_libfuncs (sub_optab, "sub", '3');
- init_integral_libfuncs (subv_optab, "subv", '3');
- init_floating_libfuncs (subv_optab, "sub", '3');
- init_integral_libfuncs (smul_optab, "mul", '3');
- init_floating_libfuncs (smul_optab, "mul", '3');
- init_integral_libfuncs (smulv_optab, "mulv", '3');
- init_floating_libfuncs (smulv_optab, "mul", '3');
- init_integral_libfuncs (sdiv_optab, "div", '3');
- init_floating_libfuncs (sdiv_optab, "div", '3');
- init_integral_libfuncs (sdivv_optab, "divv", '3');
- init_integral_libfuncs (udiv_optab, "udiv", '3');
- init_integral_libfuncs (sdivmod_optab, "divmod", '4');
- init_integral_libfuncs (udivmod_optab, "udivmod", '4');
- init_integral_libfuncs (smod_optab, "mod", '3');
- init_integral_libfuncs (umod_optab, "umod", '3');
- init_floating_libfuncs (ftrunc_optab, "ftrunc", '2');
- init_integral_libfuncs (and_optab, "and", '3');
- init_integral_libfuncs (ior_optab, "ior", '3');
- init_integral_libfuncs (xor_optab, "xor", '3');
- init_integral_libfuncs (ashl_optab, "ashl", '3');
- init_integral_libfuncs (ashr_optab, "ashr", '3');
- init_integral_libfuncs (lshr_optab, "lshr", '3');
- init_integral_libfuncs (smin_optab, "min", '3');
- init_floating_libfuncs (smin_optab, "min", '3');
- init_integral_libfuncs (smax_optab, "max", '3');
- init_floating_libfuncs (smax_optab, "max", '3');
- init_integral_libfuncs (umin_optab, "umin", '3');
- init_integral_libfuncs (umax_optab, "umax", '3');
- init_integral_libfuncs (neg_optab, "neg", '2');
- init_floating_libfuncs (neg_optab, "neg", '2');
- init_integral_libfuncs (negv_optab, "negv", '2');
- init_floating_libfuncs (negv_optab, "neg", '2');
- init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2');
- init_integral_libfuncs (ffs_optab, "ffs", '2');
- init_integral_libfuncs (clz_optab, "clz", '2');
- init_integral_libfuncs (ctz_optab, "ctz", '2');
- init_integral_libfuncs (popcount_optab, "popcount", '2');
- init_integral_libfuncs (parity_optab, "parity", '2');
-
- /* 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');
-
- /* EQ etc are floating point only. */
- init_floating_libfuncs (eq_optab, "eq", '2');
- init_floating_libfuncs (ne_optab, "ne", '2');
- init_floating_libfuncs (gt_optab, "gt", '2');
- init_floating_libfuncs (ge_optab, "ge", '2');
- init_floating_libfuncs (lt_optab, "lt", '2');
- init_floating_libfuncs (le_optab, "le", '2');
- init_floating_libfuncs (unord_optab, "unord", '2');
-
- /* Conversions. */
- init_interclass_conv_libfuncs (sfloat_optab, "float",
- MODE_INT, MODE_FLOAT);
- init_interclass_conv_libfuncs (sfix_optab, "fix",
- MODE_FLOAT, MODE_INT);
- init_interclass_conv_libfuncs (ufix_optab, "fixuns",
- MODE_FLOAT, MODE_INT);
-
- /* 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);
- }
+ init_integral_libfuncs (add_optab, "add", '3');
+ init_floating_libfuncs (add_optab, "add", '3');
+ init_integral_libfuncs (addv_optab, "addv", '3');
+ init_floating_libfuncs (addv_optab, "add", '3');
+ init_integral_libfuncs (sub_optab, "sub", '3');
+ init_floating_libfuncs (sub_optab, "sub", '3');
+ init_integral_libfuncs (subv_optab, "subv", '3');
+ init_floating_libfuncs (subv_optab, "sub", '3');
+ init_integral_libfuncs (smul_optab, "mul", '3');
+ init_floating_libfuncs (smul_optab, "mul", '3');
+ init_integral_libfuncs (smulv_optab, "mulv", '3');
+ init_floating_libfuncs (smulv_optab, "mul", '3');
+ init_integral_libfuncs (sdiv_optab, "div", '3');
+ init_floating_libfuncs (sdiv_optab, "div", '3');
+ init_integral_libfuncs (sdivv_optab, "divv", '3');
+ init_integral_libfuncs (udiv_optab, "udiv", '3');
+ init_integral_libfuncs (sdivmod_optab, "divmod", '4');
+ init_integral_libfuncs (udivmod_optab, "udivmod", '4');
+ init_integral_libfuncs (smod_optab, "mod", '3');
+ init_integral_libfuncs (umod_optab, "umod", '3');
+ init_floating_libfuncs (ftrunc_optab, "ftrunc", '2');
+ init_integral_libfuncs (and_optab, "and", '3');
+ init_integral_libfuncs (ior_optab, "ior", '3');
+ init_integral_libfuncs (xor_optab, "xor", '3');
+ init_integral_libfuncs (ashl_optab, "ashl", '3');
+ init_integral_libfuncs (ashr_optab, "ashr", '3');
+ init_integral_libfuncs (lshr_optab, "lshr", '3');
+ init_integral_libfuncs (smin_optab, "min", '3');
+ init_floating_libfuncs (smin_optab, "min", '3');
+ init_integral_libfuncs (smax_optab, "max", '3');
+ init_floating_libfuncs (smax_optab, "max", '3');
+ init_integral_libfuncs (umin_optab, "umin", '3');
+ init_integral_libfuncs (umax_optab, "umax", '3');
+ init_integral_libfuncs (neg_optab, "neg", '2');
+ init_floating_libfuncs (neg_optab, "neg", '2');
+ init_integral_libfuncs (negv_optab, "negv", '2');
+ init_floating_libfuncs (negv_optab, "neg", '2');
+ init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2');
+ init_integral_libfuncs (ffs_optab, "ffs", '2');
+ init_integral_libfuncs (clz_optab, "clz", '2');
+ init_integral_libfuncs (ctz_optab, "ctz", '2');
+ init_integral_libfuncs (popcount_optab, "popcount", '2');
+ init_integral_libfuncs (parity_optab, "parity", '2');
+
+ /* 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');
+
+ /* EQ etc are floating point only. */
+ init_floating_libfuncs (eq_optab, "eq", '2');
+ init_floating_libfuncs (ne_optab, "ne", '2');
+ init_floating_libfuncs (gt_optab, "gt", '2');
+ init_floating_libfuncs (ge_optab, "ge", '2');
+ init_floating_libfuncs (lt_optab, "lt", '2');
+ init_floating_libfuncs (le_optab, "le", '2');
+ init_floating_libfuncs (unord_optab, "unord", '2');
+
+ /* Conversions. */
+ init_interclass_conv_libfuncs (sfloat_optab, "float",
+ MODE_INT, MODE_FLOAT);
+ init_interclass_conv_libfuncs (sfix_optab, "fix",
+ MODE_FLOAT, MODE_INT);
+ init_interclass_conv_libfuncs (ufix_optab, "fixuns",
+ MODE_FLOAT, MODE_INT);
+
+ /* 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. */
int k;
/* Dump the arithmetic optabs. */
- for (i = 0; i != (int) OTI_MAX; i++)
+ for (i = 0; i != (int) OTI_MAX; i++)
for (j = 0; j < NUM_MACHINE_MODES; ++j)
{
optab o;
{
if (GET_CODE (h->libfunc) != SYMBOL_REF)
abort ();
- fprintf (stderr, "%s\t%s:\t%s\n",
+ fprintf (stderr, "%s\t%s:\t%s\n",
GET_RTX_NAME (o->code),
GET_MODE_NAME (j),
XSTR (h->libfunc, 0));
{
if (GET_CODE (h->libfunc) != SYMBOL_REF)
abort ();
- fprintf (stderr, "%s\t%s\t%s:\t%s\n",
+ fprintf (stderr, "%s\t%s\t%s:\t%s\n",
GET_RTX_NAME (o->code),
GET_MODE_NAME (j),
GET_MODE_NAME (k),
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"