/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GCC.
case REALIGN_LOAD_EXPR:
return vec_realign_load_optab;
+ case WIDEN_SUM_EXPR:
+ return TYPE_UNSIGNED (type) ? usum_widen_optab : ssum_widen_optab;
+
+ case DOT_PROD_EXPR:
+ return TYPE_UNSIGNED (type) ? udot_prod_optab : sdot_prod_optab;
+
case REDUC_MAX_EXPR:
return TYPE_UNSIGNED (type) ? reduc_umax_optab : reduc_smax_optab;
}
\f
+/* Expand vector widening operations.
+
+ There are two different classes of operations handled here:
+ 1) Operations whose result is wider than all the arguments to the operation.
+ Examples: VEC_UNPACK_HI/LO_EXPR, VEC_WIDEN_MULT_HI/LO_EXPR
+ In this case OP0 and optionally OP1 would be initialized,
+ but WIDE_OP wouldn't (not relevant for this case).
+ 2) Operations whose result is of the same size as the last argument to the
+ operation, but wider than all the other arguments to the operation.
+ Examples: WIDEN_SUM_EXPR, VEC_DOT_PROD_EXPR.
+ In the case WIDE_OP, OP0 and optionally OP1 would be initialized.
+
+ E.g, when called to expand the following operations, this is how
+ the arguments will be initialized:
+ nops OP0 OP1 WIDE_OP
+ widening-sum 2 oprnd0 - oprnd1
+ widening-dot-product 3 oprnd0 oprnd1 oprnd2
+ widening-mult 2 oprnd0 oprnd1 -
+ type-promotion (vec-unpack) 1 oprnd0 - - */
+
+rtx
+expand_widen_pattern_expr (tree exp, rtx op0, rtx op1, rtx wide_op, rtx target,
+ int unsignedp)
+{
+ tree oprnd0, oprnd1, oprnd2;
+ enum machine_mode wmode = 0, tmode0, tmode1 = 0;
+ optab widen_pattern_optab;
+ int icode;
+ enum machine_mode xmode0, xmode1 = 0, wxmode = 0;
+ rtx temp;
+ rtx pat;
+ rtx xop0, xop1, wxop;
+ int nops = TREE_CODE_LENGTH (TREE_CODE (exp));
+
+ oprnd0 = TREE_OPERAND (exp, 0);
+ tmode0 = TYPE_MODE (TREE_TYPE (oprnd0));
+ widen_pattern_optab =
+ optab_for_tree_code (TREE_CODE (exp), TREE_TYPE (oprnd0));
+ icode = (int) widen_pattern_optab->handlers[(int) tmode0].insn_code;
+ gcc_assert (icode != CODE_FOR_nothing);
+ xmode0 = insn_data[icode].operand[1].mode;
+
+ if (nops >= 2)
+ {
+ oprnd1 = TREE_OPERAND (exp, 1);
+ tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
+ xmode1 = insn_data[icode].operand[2].mode;
+ }
+
+ /* The last operand is of a wider mode than the rest of the operands. */
+ if (nops == 2)
+ {
+ wmode = tmode1;
+ wxmode = xmode1;
+ }
+ else if (nops == 3)
+ {
+ gcc_assert (tmode1 == tmode0);
+ gcc_assert (op1);
+ oprnd2 = TREE_OPERAND (exp, 2);
+ wmode = TYPE_MODE (TREE_TYPE (oprnd2));
+ wxmode = insn_data[icode].operand[3].mode;
+ }
+
+ if (!wide_op)
+ wmode = wxmode = insn_data[icode].operand[0].mode;
+
+ if (!target
+ || ! (*insn_data[icode].operand[0].predicate) (target, wmode))
+ temp = gen_reg_rtx (wmode);
+ else
+ temp = target;
+
+ xop0 = op0;
+ xop1 = op1;
+ wxop = wide_op;
+
+ /* 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) != xmode0 && xmode0 != VOIDmode)
+ xop0 = convert_modes (xmode0,
+ GET_MODE (op0) != VOIDmode
+ ? GET_MODE (op0)
+ : tmode0,
+ xop0, unsignedp);
+
+ if (op1)
+ if (GET_MODE (op1) != xmode1 && xmode1 != VOIDmode)
+ xop1 = convert_modes (xmode1,
+ GET_MODE (op1) != VOIDmode
+ ? GET_MODE (op1)
+ : tmode1,
+ xop1, unsignedp);
+
+ if (wide_op)
+ if (GET_MODE (wide_op) != wxmode && wxmode != VOIDmode)
+ wxop = convert_modes (wxmode,
+ GET_MODE (wide_op) != VOIDmode
+ ? GET_MODE (wide_op)
+ : wmode,
+ wxop, unsignedp);
+
+ /* Now, if insn's predicates don't allow our operands, put them into
+ pseudo regs. */
+
+ if (! (*insn_data[icode].operand[1].predicate) (xop0, xmode0)
+ && xmode0 != VOIDmode)
+ xop0 = copy_to_mode_reg (xmode0, xop0);
+
+ if (op1)
+ {
+ if (! (*insn_data[icode].operand[2].predicate) (xop1, xmode1)
+ && xmode1 != VOIDmode)
+ xop1 = copy_to_mode_reg (xmode1, xop1);
+
+ if (wide_op)
+ {
+ if (! (*insn_data[icode].operand[3].predicate) (wxop, wxmode)
+ && wxmode != VOIDmode)
+ wxop = copy_to_mode_reg (wxmode, wxop);
+
+ pat = GEN_FCN (icode) (temp, xop0, xop1, wxop);
+ }
+ else
+ pat = GEN_FCN (icode) (temp, xop0, xop1);
+ }
+ else
+ {
+ if (wide_op)
+ {
+ if (! (*insn_data[icode].operand[2].predicate) (wxop, wxmode)
+ && wxmode != VOIDmode)
+ wxop = copy_to_mode_reg (wxmode, wxop);
+
+ pat = GEN_FCN (icode) (temp, xop0, wxop);
+ }
+ else
+ pat = GEN_FCN (icode) (temp, xop0);
+ }
+
+ emit_insn (pat);
+ return temp;
+}
+
/* Generate code to perform an operation specified by TERNARY_OPTAB
on operands OP0, OP1 and OP2, with result having machine-mode MODE.
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);
+ {
+ rtx x = simplify_binary_operation (binoptab->code, mode, op0, op1);
+
+ if (x)
+ return x;
+ }
+
+ return expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods);
}
/* Like simplify_expand_binop, but always put the result in TARGET.
subword_label = gen_label_rtx ();
done_label = gen_label_rtx ();
+ NO_DEFER_POP;
do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
0, 0, subword_label);
+ OK_DEFER_POP;
if (!expand_superword_shift (binoptab, outof_input, superword_op1,
outof_target, into_target,
return expand_binop (mode, binop, op0, op1, target, unsignedp, methods);
}
+/* Return whether OP0 and OP1 should be swapped when expanding a commutative
+ binop. Order them according to commutative_operand_precedence and, if
+ possible, try to put TARGET or a pseudo first. */
+static bool
+swap_commutative_operands_with_target (rtx target, rtx op0, rtx op1)
+{
+ int op0_prec = commutative_operand_precedence (op0);
+ int op1_prec = commutative_operand_precedence (op1);
+
+ if (op0_prec < op1_prec)
+ return true;
+
+ if (op0_prec > op1_prec)
+ return false;
+
+ /* With equal precedence, both orders are ok, but it is better if the
+ first operand is TARGET, or if both TARGET and OP0 are pseudos. */
+ if (target == 0 || REG_P (target))
+ return (REG_P (op1) && !REG_P (op0)) || target == op1;
+ else
+ return rtx_equal_p (op1, target);
+}
+
+
/* Generate code to perform an operation specified by BINOPTAB
on operands OP0 and OP1, with result having machine-mode MODE.
|| binoptab->code == ROTATERT);
rtx entry_last = get_last_insn ();
rtx last;
+ bool first_pass_p = true;
class = GET_MODE_CLASS (mode);
{
commutative_op = 1;
- 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 (swap_commutative_operands_with_target (target, op0, op1))
{
temp = op1;
op1 = op0;
}
}
+ retry:
+
/* If we can do it with a three-operand insn, do so. */
if (methods != OPTAB_MUST_WIDEN
delete_insns_since (last);
}
+ /* If we were trying to rotate by a constant value, and that didn't
+ work, try rotating the other direction before falling back to
+ shifts and bitwise-or. */
+ if (first_pass_p
+ && (binoptab == rotl_optab || binoptab == rotr_optab)
+ && class == MODE_INT
+ && GET_CODE (op1) == CONST_INT
+ && INTVAL (op1) > 0
+ && (unsigned int) INTVAL (op1) < GET_MODE_BITSIZE (mode))
+ {
+ first_pass_p = false;
+ op1 = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1));
+ binoptab = binoptab == rotl_optab ? rotr_optab : rotl_optab;
+ goto retry;
+ }
+
/* If this is a multiply, see if we can do a widening operation that
takes operands of this mode and makes a wider mode. */
- if (binoptab == smul_optab && GET_MODE_WIDER_MODE (mode) != VOIDmode
+ if (binoptab == smul_optab
+ && GET_MODE_WIDER_MODE (mode) != VOIDmode
&& (((unsignedp ? umul_widen_optab : smul_widen_optab)
->handlers[(int) GET_MODE_WIDER_MODE (mode)].insn_code)
!= CODE_FOR_nothing))
can open-code the operation. Check for a widening multiply at the
wider mode as well. */
- if ((class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ if (CLASS_HAS_WIDER_MODES_P (class)
&& methods != OPTAB_DIRECT && methods != OPTAB_LIB)
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
if (binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
if (expand_doubleword_shift (op1_mode, binoptab,
outof_input, into_input, op1,
outof_target, into_target,
- unsignedp, methods, shift_mask))
+ unsignedp, next_methods, shift_mask))
{
insns = get_insns ();
end_sequence ();
&& ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing
&& lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing)
{
- rtx insns, equiv_value;
+ rtx insns;
rtx into_target, outof_target;
rtx into_input, outof_input;
rtx inter;
if (inter != 0)
{
- if (binoptab->code != UNKNOWN)
- equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1);
- else
- equiv_value = 0;
-
- /* We can't make this a no conflict block if this is a word swap,
- because the word swap case fails if the input and output values
- are in the same register. */
- if (shift_count != BITS_PER_WORD)
- emit_no_conflict_block (insns, target, op0, op1, equiv_value);
- else
- emit_insn (insns);
-
-
+ /* One may be tempted to wrap the insns in a REG_NO_CONFLICT
+ block to help the register allocator a bit. But a multi-word
+ rotate will need all the input bits when setting the output
+ bits, so there clearly is a conflict between the input and
+ output registers. So we can't use a no-conflict block here. */
+ emit_insn (insns);
return target;
}
}
/* Look for a wider mode of the same class for which it appears we can do
the operation. */
- if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ if (CLASS_HAS_WIDER_MODES_P (class))
{
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
if ((binoptab->handlers[(int) wider_mode].insn_code
/* 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 (CLASS_HAS_WIDER_MODES_P (class))
{
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ 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
/* 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 (CLASS_HAS_WIDER_MODES_P (class))
{
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
if (binoptab->handlers[(int) wider_mode].insn_code
widen_clz (enum machine_mode mode, rtx op0, rtx target)
{
enum mode_class class = GET_MODE_CLASS (mode);
- if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ if (CLASS_HAS_WIDER_MODES_P (class))
{
enum machine_mode wider_mode;
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
if (clz_optab->handlers[(int) wider_mode].insn_code
expand_parity (enum machine_mode mode, rtx op0, rtx target)
{
enum mode_class class = GET_MODE_CLASS (mode);
- if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ if (CLASS_HAS_WIDER_MODES_P (class))
{
enum machine_mode wider_mode;
for (wider_mode = mode; wider_mode != VOIDmode;
goto try_libcall;
}
- if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ if (CLASS_HAS_WIDER_MODES_P (class))
+ 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)
if (temp)
{
- if (class != MODE_INT)
+ if (class != MODE_INT
+ || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+ GET_MODE_BITSIZE (wider_mode)))
{
if (target == 0)
target = gen_reg_rtx (mode);
if (unoptab->code == NEG)
{
/* Try negating floating point values by flipping the sign bit. */
- if (class == MODE_FLOAT)
+ if (SCALAR_FLOAT_MODE_P (mode))
{
temp = expand_absneg_bit (NEG, mode, op0, target);
if (temp)
target = gen_reg_rtx (outmode);
emit_libcall_block (insns, target, value,
- gen_rtx_fmt_e (unoptab->code, mode, op0));
+ gen_rtx_fmt_e (unoptab->code, outmode, op0));
return target;
}
/* 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 (CLASS_HAS_WIDER_MODES_P (class))
{
- for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode;
+ 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
return temp;
/* For floating point modes, try clearing the sign bit. */
- if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ if (SCALAR_FLOAT_MODE_P (mode))
{
temp = expand_absneg_bit (ABS, mode, op0, target);
if (temp)
emit_move_insn (target, op0);
NO_DEFER_POP;
- /* If this mode is an integer too wide to compare properly,
- compare word by word. Rely on CSE to optimize constant cases. */
- if (GET_MODE_CLASS (mode) == MODE_INT
- && ! can_compare_p (GE, mode, ccp_jump))
- do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx,
- NULL_RTX, op1);
- else
- do_compare_rtx_and_jump (target, CONST0_RTX (mode), GE, 0, mode,
- NULL_RTX, NULL_RTX, op1);
+ do_compare_rtx_and_jump (target, CONST0_RTX (mode), GE, 0, mode,
+ NULL_RTX, NULL_RTX, op1);
op0 = expand_unop (mode, result_unsignedp ? neg_optab : negv_optab,
target, target, 0);
bool must_stay;
};
-/* Called via note_stores by emit_no_conflict_block. Set P->must_stay
- if the currently examined clobber / store has to stay in the list of
- insns that constitute the actual no_conflict block. */
+/* Called via note_stores by emit_no_conflict_block and emit_libcall_block.
+ Set P->must_stay if the currently examined clobber / store has to stay
+ in the list of insns that constitute the actual no_conflict block /
+ libcall block. */
static void
no_conflict_move_test (rtx dest, rtx set, void *p0)
{
return;
/* If this insn sets / clobbers a register that feeds one of the insns
already in the list, this insn has to stay too. */
- else if (reg_mentioned_p (dest, PATTERN (p->first))
+ else if (reg_overlap_mentioned_p (dest, PATTERN (p->first))
+ || (CALL_P (p->first) && (find_reg_fusage (p->first, USE, dest)))
|| reg_used_between_p (dest, p->first, p->insn)
/* Likewise if this insn depends on a register set by a previous
- insn in the list. */
+ insn in the list, or if it sets a result (presumably a hard
+ register) that is set or clobbered by a previous insn.
+ N.B. the modified_*_p (SET_DEST...) tests applied to a MEM
+ SET_DEST perform the former check on the address, and the latter
+ check on the MEM. */
|| (GET_CODE (set) == SET
&& (modified_in_p (SET_SRC (set), p->first)
- || modified_between_p (SET_SRC (set), p->first, p->insn))))
+ || modified_in_p (SET_DEST (set), p->first)
+ || modified_between_p (SET_SRC (set), p->first, p->insn)
+ || modified_between_p (SET_DEST (set), p->first, p->insn))))
p->must_stay = true;
}
+/* Encapsulate the block starting at FIRST and ending with LAST, which is
+ logically equivalent to EQUIV, so it gets manipulated as a unit if it
+ is possible to do so. */
+
+static void
+maybe_encapsulate_block (rtx first, rtx last, rtx equiv)
+{
+ if (!flag_non_call_exceptions || !may_trap_p (equiv))
+ {
+ /* We can't attach the REG_LIBCALL and REG_RETVAL notes when the
+ encapsulated region would not be in one basic block, i.e. when
+ there is a control_flow_insn_p insn between FIRST and LAST. */
+ bool attach_libcall_retval_notes = true;
+ rtx insn, next = NEXT_INSN (last);
+
+ for (insn = first; insn != next; insn = NEXT_INSN (insn))
+ if (control_flow_insn_p (insn))
+ {
+ attach_libcall_retval_notes = false;
+ break;
+ }
+
+ if (attach_libcall_retval_notes)
+ {
+ REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
+ REG_NOTES (first));
+ REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first,
+ REG_NOTES (last));
+ }
+ }
+}
+
/* Emit code to perform a series of operations on a multi-word quantity, one
word at a time.
else
first = NEXT_INSN (prev);
- /* Encapsulate the block so it gets manipulated as a unit. */
- REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
- REG_NOTES (first));
- REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, REG_NOTES (last));
+ maybe_encapsulate_block (first, last, equiv);
return last;
}
next = NEXT_INSN (insn);
if (set != 0 && REG_P (SET_DEST (set))
- && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER
- && (insn == insns
- || ((! INSN_P(insns)
- || ! reg_mentioned_p (SET_DEST (set), PATTERN (insns)))
- && ! reg_used_between_p (SET_DEST (set), insns, insn)
- && ! modified_in_p (SET_SRC (set), insns)
- && ! modified_between_p (SET_SRC (set), insns, insn))))
+ && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER)
{
- if (PREV_INSN (insn))
- NEXT_INSN (PREV_INSN (insn)) = next;
- else
- insns = next;
+ struct no_conflict_data data;
+
+ data.target = const0_rtx;
+ data.first = insns;
+ data.insn = insn;
+ data.must_stay = 0;
+ note_stores (PATTERN (insn), no_conflict_move_test, &data);
+ if (! data.must_stay)
+ {
+ if (PREV_INSN (insn))
+ NEXT_INSN (PREV_INSN (insn)) = next;
+ else
+ insns = next;
- if (next)
- PREV_INSN (next) = PREV_INSN (insn);
+ if (next)
+ PREV_INSN (next) = PREV_INSN (insn);
- add_insn (insn);
+ add_insn (insn);
+ }
}
/* Some ports use a loop to copy large arguments onto the stack.
else
first = NEXT_INSN (prev);
- /* Encapsulate the block so it gets manipulated as a unit. */
- if (!flag_non_call_exceptions || !may_trap_p (equiv))
- {
- /* We can't attach the REG_LIBCALL and REG_RETVAL notes
- when the encapsulated region would not be in one basic block,
- i.e. when there is a control_flow_insn_p insn between FIRST and LAST.
- */
- bool attach_libcall_retval_notes = true;
- next = NEXT_INSN (last);
- for (insn = first; insn != next; insn = NEXT_INSN (insn))
- if (control_flow_insn_p (insn))
- {
- attach_libcall_retval_notes = false;
- break;
- }
-
- if (attach_libcall_retval_notes)
- {
- REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last,
- REG_NOTES (first));
- REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first,
- REG_NOTES (last));
- }
- }
+ maybe_encapsulate_block (first, last, equiv);
}
\f
/* Nonzero if we can perform a comparison of mode MODE straightforwardly.
enum machine_mode mode = *pmode;
rtx x = *px, y = *py;
int unsignedp = *punsignedp;
- enum mode_class class;
-
- class = GET_MODE_CLASS (mode);
/* If we are inside an appropriately-short loop and we are optimizing,
force expensive constants into a register. */
/* Handle a lib call just for the mode we are using. */
- if (cmp_optab->handlers[(int) mode].libfunc && class != MODE_FLOAT)
+ if (cmp_optab->handlers[(int) mode].libfunc && !SCALAR_FLOAT_MODE_P (mode))
{
rtx libfunc = cmp_optab->handlers[(int) mode].libfunc;
rtx result;
result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST_MAKE_BLOCK,
word_mode, 2, x, mode, y, mode);
+ /* There are two kinds of comparison routines. Biased routines
+ return 0/1/2, and unbiased routines return -1/0/1. Other parts
+ of gcc expect that the comparison operation is equivalent
+ to the modified comparison. For signed comparisons compare the
+ result against 1 in the biased case, and zero in the unbiased
+ case. For unsigned comparisons always compare against 1 after
+ biasing the unbiased result by adding 1. This gives us a way to
+ represent LTU. */
*px = result;
*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 = const1_rtx;
+
+ if (!TARGET_LIB_INT_CMP_BIASED)
{
- *py = const0_rtx;
- *punsignedp = 1;
+ if (*punsignedp)
+ *px = plus_constant (result, 1);
+ else
+ *py = const0_rtx;
}
return;
}
- gcc_assert (class == MODE_FLOAT);
+ gcc_assert (SCALAR_FLOAT_MODE_P (mode));
prepare_float_lib_cmp (px, py, pcomparison, pmode, punsignedp);
}
return;
}
- if (class != MODE_INT && class != MODE_FLOAT
- && class != MODE_COMPLEX_FLOAT)
+ if (!CLASS_HAS_WIDER_MODES_P (class))
break;
wider_mode = GET_MODE_WIDER_MODE (wider_mode);
rtx libfunc = 0;
bool reversed_p = false;
- for (mode = orig_mode; mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode))
+ for (mode = orig_mode;
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
{
if ((libfunc = code_to_optab[comparison]->handlers[mode].libfunc))
break;
enum insn_code icode;
rtx target = to;
enum machine_mode fmode, imode;
+ bool can_do_signed = false;
/* Crash now, because we won't be able to decide which mode to use. */
gcc_assert (GET_MODE (from) != VOIDmode);
continue;
icode = can_float_p (fmode, imode, unsignedp);
- if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp)
- icode = can_float_p (fmode, imode, 0), doing_unsigned = 0;
+ if (icode == CODE_FOR_nothing && unsignedp)
+ {
+ enum insn_code scode = can_float_p (fmode, imode, 0);
+ if (scode != CODE_FOR_nothing)
+ can_do_signed = true;
+ if (imode != GET_MODE (from))
+ icode = scode, doing_unsigned = 0;
+ }
if (icode != CODE_FOR_nothing)
{
}
}
- /* Unsigned integer, and no way to convert directly.
- Convert as signed, then conditionally adjust the result. */
- if (unsignedp)
+ /* Unsigned integer, and no way to convert directly. For binary
+ floating point modes, convert as signed, then conditionally adjust
+ the result. */
+ if (unsignedp && can_do_signed && !DECIMAL_FLOAT_MODE_P (GET_MODE (to)))
{
rtx label = gen_label_rtx ();
rtx temp;
This is not needed. Consider, for instance conversion from SFmode
into DImode.
- The hot path trought the code is dealing with inputs smaller than 2^63
+ The hot path through the code is dealing with inputs smaller than 2^63
and doing just the conversion, so there is no bits to lose.
In the other path we know the value is positive in the range 2^63..2^64-1
init_floating_libfuncs (optab optable, const char *opname, int suffix)
{
init_libfuncs (optable, MIN_MODE_FLOAT, MAX_MODE_FLOAT, opname, suffix);
+ init_libfuncs (optable, MIN_MODE_DECIMAL_FLOAT, MAX_MODE_DECIMAL_FLOAT,
+ opname, suffix);
}
/* Initialize the libfunc fields of an entire group of entries of an
/* Zap the nonsensical SYMBOL_REF_DECL for this. What we're left with
are the flags assigned by targetm.encode_section_info. */
- SYMBOL_REF_DECL (symbol) = 0;
+ SET_SYMBOL_REF_DECL (symbol, 0);
return symbol;
}
umul_highpart_optab = init_optab (UNKNOWN);
smul_widen_optab = init_optab (UNKNOWN);
umul_widen_optab = init_optab (UNKNOWN);
+ usmul_widen_optab = init_optab (UNKNOWN);
sdiv_optab = init_optab (DIV);
sdivv_optab = init_optabv (DIV);
sdivmod_optab = init_optab (UNKNOWN);
reduc_splus_optab = init_optab (UNKNOWN);
reduc_uplus_optab = init_optab (UNKNOWN);
+ ssum_widen_optab = init_optab (UNKNOWN);
+ usum_widen_optab = init_optab (UNKNOWN);
+ sdot_prod_optab = init_optab (UNKNOWN);
+ udot_prod_optab = init_optab (UNKNOWN);
+
vec_extract_optab = init_optab (UNKNOWN);
vec_set_optab = init_optab (UNKNOWN);
vec_init_optab = init_optab (UNKNOWN);
sync_lock_test_and_set[i] = CODE_FOR_nothing;
sync_lock_release[i] = CODE_FOR_nothing;
-#ifdef HAVE_SECONDARY_RELOADS
reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
-#endif
}
/* Fill in the optabs with the insns we support. */
/* Conversions. */
init_interclass_conv_libfuncs (sfloat_optab, "float",
MODE_INT, MODE_FLOAT);
+ init_interclass_conv_libfuncs (sfloat_optab, "float",
+ MODE_INT, MODE_DECIMAL_FLOAT);
+ init_interclass_conv_libfuncs (ufloat_optab, "floatun",
+ MODE_INT, MODE_FLOAT);
+ init_interclass_conv_libfuncs (ufloat_optab, "floatun",
+ MODE_INT, MODE_DECIMAL_FLOAT);
init_interclass_conv_libfuncs (sfix_optab, "fix",
MODE_FLOAT, MODE_INT);
+ init_interclass_conv_libfuncs (sfix_optab, "fix",
+ MODE_DECIMAL_FLOAT, MODE_INT);
init_interclass_conv_libfuncs (ufix_optab, "fixuns",
MODE_FLOAT, MODE_INT);
+ init_interclass_conv_libfuncs (ufix_optab, "fixuns",
+ MODE_DECIMAL_FLOAT, MODE_INT);
+ init_interclass_conv_libfuncs (ufloat_optab, "floatuns",
+ MODE_INT, MODE_DECIMAL_FLOAT);
/* sext_optab is also used for FLOAT_EXTEND. */
init_intraclass_conv_libfuncs (sext_optab, "extend", MODE_FLOAT, true);
+ init_intraclass_conv_libfuncs (sext_optab, "extend", MODE_DECIMAL_FLOAT, true);
+ init_interclass_conv_libfuncs (sext_optab, "extend", MODE_FLOAT, MODE_DECIMAL_FLOAT);
+ init_interclass_conv_libfuncs (sext_optab, "extend", MODE_DECIMAL_FLOAT, MODE_FLOAT);
init_intraclass_conv_libfuncs (trunc_optab, "trunc", MODE_FLOAT, false);
+ init_intraclass_conv_libfuncs (trunc_optab, "trunc", MODE_DECIMAL_FLOAT, false);
+ init_interclass_conv_libfuncs (trunc_optab, "trunc", MODE_FLOAT, MODE_DECIMAL_FLOAT);
+ init_interclass_conv_libfuncs (trunc_optab, "trunc", MODE_DECIMAL_FLOAT, MODE_FLOAT);
/* 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. */
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);
+ NULL_RTX, VOIDmode, EXPAND_NORMAL);
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);
+ NULL_RTX, VOIDmode, EXPAND_NORMAL);
if (!insn_data[icode].operand[2].predicate (rtx_op2, mode)
&& mode != VOIDmode)
rtx_op2 = force_reg (mode, rtx_op2);