/* 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, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011 Free Software Foundation, Inc.
This file is part of GCC.
}
if (GET_RTX_CLASS (code) == RTX_UNARY)
- note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
+ switch (code)
+ {
+ case FFS:
+ case CLZ:
+ case CTZ:
+ case CLRSB:
+ case POPCOUNT:
+ case PARITY:
+ case BSWAP:
+ if (GET_MODE (op0) != VOIDmode && GET_MODE (target) != GET_MODE (op0))
+ {
+ note = gen_rtx_fmt_e (code, GET_MODE (op0), copy_rtx (op0));
+ if (GET_MODE_SIZE (GET_MODE (op0))
+ > GET_MODE_SIZE (GET_MODE (target)))
+ note = simplify_gen_unary (TRUNCATE, GET_MODE (target),
+ note, GET_MODE (op0));
+ else
+ note = simplify_gen_unary (ZERO_EXTEND, GET_MODE (target),
+ note, GET_MODE (op0));
+ break;
+ }
+ /* FALLTHRU */
+ default:
+ note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0));
+ break;
+ }
else
note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1));
return 1;
}
\f
+/* Given two input operands, OP0 and OP1, determine what the correct from_mode
+ for a widening operation would be. In most cases this would be OP0, but if
+ that's a constant it'll be VOIDmode, which isn't useful. */
+
+static enum machine_mode
+widened_mode (enum machine_mode to_mode, rtx op0, rtx op1)
+{
+ enum machine_mode m0 = GET_MODE (op0);
+ enum machine_mode m1 = GET_MODE (op1);
+ enum machine_mode result;
+
+ if (m0 == VOIDmode && m1 == VOIDmode)
+ return to_mode;
+ else if (m0 == VOIDmode || GET_MODE_SIZE (m0) < GET_MODE_SIZE (m1))
+ result = m1;
+ else
+ result = m0;
+
+ if (GET_MODE_SIZE (result) > GET_MODE_SIZE (to_mode))
+ return to_mode;
+
+ return result;
+}
+\f
+/* Find a widening optab even if it doesn't widen as much as we want.
+ E.g. if from_mode is HImode, and to_mode is DImode, and there is no
+ direct HI->SI insn, then return SI->DI, if that exists.
+ If PERMIT_NON_WIDENING is non-zero then this can be used with
+ non-widening optabs also. */
+
+enum insn_code
+find_widening_optab_handler_and_mode (optab op, enum machine_mode to_mode,
+ enum machine_mode from_mode,
+ int permit_non_widening,
+ enum machine_mode *found_mode)
+{
+ for (; (permit_non_widening || from_mode != to_mode)
+ && GET_MODE_SIZE (from_mode) <= GET_MODE_SIZE (to_mode)
+ && from_mode != VOIDmode;
+ from_mode = GET_MODE_WIDER_MODE (from_mode))
+ {
+ enum insn_code handler = widening_optab_handler (op, to_mode,
+ from_mode);
+
+ if (handler != CODE_FOR_nothing)
+ {
+ if (found_mode)
+ *found_mode = from_mode;
+ return handler;
+ }
+ }
+
+ return CODE_FOR_nothing;
+}
+\f
/* Widen OP to MODE and return the rtx for the widened operand. UNSIGNEDP
says whether OP is signed or unsigned. NO_EXTEND is nonzero if we need
not actually do a sign-extend or zero-extend, but can leave the
optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
if (ops->code == WIDEN_MULT_PLUS_EXPR
|| ops->code == WIDEN_MULT_MINUS_EXPR)
- icode = optab_handler (widen_pattern_optab,
- TYPE_MODE (TREE_TYPE (ops->op2)));
+ icode = find_widening_optab_handler (widen_pattern_optab,
+ TYPE_MODE (TREE_TYPE (ops->op2)),
+ tmode0, 0);
else
icode = optab_handler (widen_pattern_optab, tmode0);
gcc_assert (icode != CODE_FOR_nothing);
|| binoptab == umul_highpart_optab);
}
-/* X is to be used in mode MODE as an operand to BINOPTAB. If we're
+/* X is to be used in mode MODE as operand OPN to BINOPTAB. If we're
optimizing, and if the operand is a constant that costs more than
1 instruction, force the constant into a register and return that
register. Return X otherwise. UNSIGNEDP says whether X is unsigned. */
static rtx
avoid_expensive_constant (enum machine_mode mode, optab binoptab,
- rtx x, bool unsignedp)
+ int opn, rtx x, bool unsignedp)
{
bool speed = optimize_insn_for_speed_p ();
if (mode != VOIDmode
&& optimize
&& CONSTANT_P (x)
- && rtx_cost (x, binoptab->code, speed) > rtx_cost (x, SET, speed))
+ && rtx_cost (x, binoptab->code, opn, speed) > set_src_cost (x, speed))
{
if (CONST_INT_P (x))
{
rtx target, int unsignedp, enum optab_methods methods,
rtx last)
{
- enum insn_code icode = optab_handler (binoptab, mode);
+ enum machine_mode from_mode = widened_mode (mode, op0, op1);
+ enum insn_code icode = find_widening_optab_handler (binoptab, mode,
+ from_mode, 1);
enum machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
enum machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
enum machine_mode mode0, mode1, tmp_mode;
}
/* If we are optimizing, force expensive constants into a register. */
- xop0 = avoid_expensive_constant (xmode0, binoptab, xop0, unsignedp);
+ xop0 = avoid_expensive_constant (xmode0, binoptab, 0, xop0, unsignedp);
if (!shift_optab_p (binoptab))
- xop1 = avoid_expensive_constant (xmode1, binoptab, xop1, unsignedp);
+ xop1 = avoid_expensive_constant (xmode1, binoptab, 1, xop1, unsignedp);
/* In case the insn wants input operands in modes different from
those of the actual operands, convert the operands. It would
/* If we can do it with a three-operand insn, do so. */
if (methods != OPTAB_MUST_WIDEN
- && optab_handler (binoptab, mode) != CODE_FOR_nothing)
+ && find_widening_optab_handler (binoptab, mode,
+ widened_mode (mode, op0, op1), 1)
+ != CODE_FOR_nothing)
{
temp = expand_binop_directly (mode, binoptab, op0, op1, target,
unsignedp, methods, last);
{
optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab);
rtx newop1;
- unsigned int bits = GET_MODE_BITSIZE (mode);
+ unsigned int bits = GET_MODE_PRECISION (mode);
if (CONST_INT_P (op1))
newop1 = GEN_INT (bits - INTVAL (op1));
takes operands of this mode and makes a wider mode. */
if (binoptab == smul_optab
- && GET_MODE_WIDER_MODE (mode) != VOIDmode
- && (optab_handler ((unsignedp ? umul_widen_optab : smul_widen_optab),
- GET_MODE_WIDER_MODE (mode))
+ && GET_MODE_2XWIDER_MODE (mode) != VOIDmode
+ && (widening_optab_handler ((unsignedp ? umul_widen_optab
+ : smul_widen_optab),
+ GET_MODE_2XWIDER_MODE (mode), mode)
!= CODE_FOR_nothing))
{
- temp = expand_binop (GET_MODE_WIDER_MODE (mode),
+ temp = expand_binop (GET_MODE_2XWIDER_MODE (mode),
unsignedp ? umul_widen_optab : smul_widen_optab,
op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
if (temp != 0)
{
if (GET_MODE_CLASS (mode) == MODE_INT
- && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
- GET_MODE_BITSIZE (GET_MODE (temp))))
+ && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (temp)))
return gen_lowpart (mode, temp);
else
return convert_to_mode (mode, temp, unsignedp);
if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing
|| (binoptab == smul_optab
&& GET_MODE_WIDER_MODE (wider_mode) != VOIDmode
- && (optab_handler ((unsignedp ? umul_widen_optab
- : smul_widen_optab),
- GET_MODE_WIDER_MODE (wider_mode))
+ && (find_widening_optab_handler ((unsignedp
+ ? umul_widen_optab
+ : smul_widen_optab),
+ GET_MODE_WIDER_MODE (wider_mode),
+ mode, 0)
!= CODE_FOR_nothing)))
{
rtx xop0 = op0, xop1 = op1;
&& mclass == MODE_INT)
{
no_extend = 1;
- xop0 = avoid_expensive_constant (mode, binoptab,
+ xop0 = avoid_expensive_constant (mode, binoptab, 0,
xop0, unsignedp);
if (binoptab != ashl_optab)
- xop1 = avoid_expensive_constant (mode, binoptab,
+ xop1 = avoid_expensive_constant (mode, binoptab, 1,
xop1, unsignedp);
}
if (temp)
{
if (mclass != MODE_INT
- || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
- GET_MODE_BITSIZE (wider_mode)))
+ || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
{
if (target == 0)
target = gen_reg_rtx (mode);
/* If TARGET is the same as one of the operands, the REG_EQUAL note
won't be accurate, so use a new target. */
- if (target == 0 || target == op0 || target == op1)
+ if (target == 0
+ || target == op0
+ || target == op1
+ || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
start_sequence ();
&& mclass == MODE_INT
&& (CONST_INT_P (op1) || optimize_insn_for_speed_p ())
&& GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && GET_MODE_PRECISION (mode) == GET_MODE_BITSIZE (mode)
&& optab_handler (binoptab, word_mode) != CODE_FOR_nothing
&& optab_handler (ashl_optab, word_mode) != CODE_FOR_nothing
&& optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
/* 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)
+ if (target == 0
+ || target == op0
+ || target == op1
+ || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
start_sequence ();
if ((binoptab == rotl_optab || binoptab == rotr_optab)
&& mclass == MODE_INT
&& CONST_INT_P (op1)
- && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && GET_MODE_PRECISION (mode) == 2 * BITS_PER_WORD
&& optab_handler (ashl_optab, word_mode) != CODE_FOR_nothing
&& optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
{
opportunities, and second because if target and op0 happen to be MEMs
designating the same location, we would risk clobbering it too early
in the code sequence we generate below. */
- if (target == 0 || target == op0 || target == op1 || ! REG_P (target))
+ if (target == 0
+ || target == op0
+ || target == op1
+ || !REG_P (target)
+ || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
start_sequence ();
xtarget = gen_reg_rtx (mode);
- if (target == 0 || !REG_P (target))
+ if (target == 0 || !REG_P (target) || !valid_multiword_target_p (target))
target = xtarget;
/* Indicate for flow that the entire target reg is being set. */
&& optab_handler (add_optab, word_mode) != CODE_FOR_nothing)
{
rtx product = NULL_RTX;
-
- if (optab_handler (umul_widen_optab, mode) != CODE_FOR_nothing)
+ if (widening_optab_handler (umul_widen_optab, mode, word_mode)
+ != CODE_FOR_nothing)
{
product = expand_doubleword_mult (mode, op0, op1, target,
true, methods);
}
if (product == NULL_RTX
- && optab_handler (smul_widen_optab, mode) != CODE_FOR_nothing)
+ && widening_optab_handler (smul_widen_optab, mode, word_mode)
+ != CODE_FOR_nothing)
{
product = expand_doubleword_mult (mode, op0, op1, target,
false, methods);
wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
- if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing
+ if (find_widening_optab_handler (binoptab, wider_mode, mode, 1)
+ != CODE_FOR_nothing
|| (methods == OPTAB_LIB
&& optab_libfunc (binoptab, wider_mode)))
{
if (temp)
{
if (mclass != MODE_INT
- || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
- GET_MODE_BITSIZE (wider_mode)))
+ || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
{
if (target == 0)
target = gen_reg_rtx (mode);
rtx xop0 = op0, xop1 = op1;
/* If we are optimizing, force expensive constants into a register. */
- xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp);
- xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp);
+ xop0 = avoid_expensive_constant (mode0, binoptab, 0, xop0, unsignedp);
+ xop1 = avoid_expensive_constant (mode1, binoptab, 1, xop1, unsignedp);
create_fixed_operand (&ops[0], targ0);
create_convert_operand_from (&ops[1], op0, mode, unsignedp);
/* Try calculating
(clz:narrow x)
as
- (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)). */
+ (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)).
+
+ A similar operation can be used for clrsb. UNOPTAB says which operation
+ we are trying to expand. */
static rtx
-widen_clz (enum machine_mode mode, rtx op0, rtx target)
+widen_leading (enum machine_mode mode, rtx op0, rtx target, optab unoptab)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
if (CLASS_HAS_WIDER_MODES_P (mclass))
wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
- if (optab_handler (clz_optab, wider_mode) != CODE_FOR_nothing)
+ if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
{
rtx xop0, temp, last;
if (target == 0)
target = gen_reg_rtx (mode);
- xop0 = widen_operand (op0, wider_mode, mode, true, false);
- temp = expand_unop (wider_mode, clz_optab, xop0, NULL_RTX, true);
+ xop0 = widen_operand (op0, wider_mode, mode,
+ unoptab != clrsb_optab, false);
+ temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX,
+ unoptab != clrsb_optab);
if (temp != 0)
temp = expand_binop (wider_mode, sub_optab, temp,
- GEN_INT (GET_MODE_BITSIZE (wider_mode)
- - GET_MODE_BITSIZE (mode)),
+ GEN_INT (GET_MODE_PRECISION (wider_mode)
+ - GET_MODE_PRECISION (mode)),
target, true, OPTAB_DIRECT);
if (temp == 0)
delete_insns_since (last);
x = widen_operand (op0, wider_mode, mode, true, true);
x = expand_unop (wider_mode, bswap_optab, x, NULL_RTX, true);
+ gcc_assert (GET_MODE_PRECISION (wider_mode) == GET_MODE_BITSIZE (wider_mode)
+ && GET_MODE_PRECISION (mode) == GET_MODE_BITSIZE (mode));
if (x != 0)
x = expand_shift (RSHIFT_EXPR, wider_mode, x,
- size_int (GET_MODE_BITSIZE (wider_mode)
- - GET_MODE_BITSIZE (mode)),
+ GET_MODE_BITSIZE (wider_mode)
+ - GET_MODE_BITSIZE (mode),
NULL_RTX, true);
if (x != 0)
t0 = expand_unop (word_mode, bswap_optab,
operand_subword_force (op, 1, mode), NULL_RTX, true);
- if (target == 0)
+ if (target == 0 || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
if (REG_P (target))
emit_clobber (target);
}
/* Try calculating ctz(x) as K - clz(x & -x) ,
- where K is GET_MODE_BITSIZE(mode) - 1.
+ where K is GET_MODE_PRECISION(mode) - 1.
Both __builtin_ctz and __builtin_clz are undefined at zero, so we
don't have to worry about what the hardware does in that case. (If
if (temp)
temp = expand_unop_direct (mode, clz_optab, temp, NULL_RTX, true);
if (temp)
- temp = expand_binop (mode, sub_optab, GEN_INT (GET_MODE_BITSIZE (mode) - 1),
+ temp = expand_binop (mode, sub_optab, GEN_INT (GET_MODE_PRECISION (mode) - 1),
temp, target,
true, OPTAB_DIRECT);
if (temp == 0)
if (CLZ_DEFINED_VALUE_AT_ZERO (mode, val) == 2)
{
defined_at_zero = true;
- val = (GET_MODE_BITSIZE (mode) - 1) - val;
+ val = (GET_MODE_PRECISION (mode) - 1) - val;
}
}
else
if (code == ABS)
mask = double_int_not (mask);
- if (target == 0 || target == op0)
+ if (target == 0
+ || target == op0
+ || (nwords > 1 && !valid_multiword_target_p (target)))
target = gen_reg_rtx (mode);
if (nwords > 1)
/* Widening (or narrowing) clz needs special treatment. */
if (unoptab == clz_optab)
{
- temp = widen_clz (mode, op0, target);
+ temp = widen_leading (mode, op0, target, unoptab);
if (temp)
return temp;
return temp;
}
- goto try_libcall;
+ goto try_libcall;
+ }
+
+ if (unoptab == clrsb_optab)
+ {
+ temp = widen_leading (mode, op0, target, unoptab);
+ if (temp)
+ return temp;
+ goto try_libcall;
}
/* Widening (or narrowing) bswap needs special treatment. */
if (temp)
{
if (mclass != MODE_INT
- || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
- GET_MODE_BITSIZE (wider_mode)))
+ || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
{
if (target == 0)
target = gen_reg_rtx (mode);
int i;
rtx insns;
- if (target == 0 || target == op0)
+ if (target == 0 || target == op0 || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
start_sequence ();
/* All of these functions return small values. Thus we choose to
have them return something that isn't a double-word. */
if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab
- || unoptab == popcount_optab || unoptab == parity_optab)
+ || unoptab == clrsb_optab || unoptab == popcount_optab
+ || unoptab == parity_optab)
outmode
= GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node),
optab_libfunc (unoptab, mode)));
unsignedp);
/* If we are generating clz using wider mode, adjust the
- result. */
- if (unoptab == clz_optab && temp != 0)
+ result. Similarly for clrsb. */
+ if ((unoptab == clz_optab || unoptab == clrsb_optab)
+ && temp != 0)
temp = expand_binop (wider_mode, sub_optab, temp,
- GEN_INT (GET_MODE_BITSIZE (wider_mode)
- - GET_MODE_BITSIZE (mode)),
+ GEN_INT (GET_MODE_PRECISION (wider_mode)
+ - GET_MODE_PRECISION (mode)),
target, true, OPTAB_DIRECT);
if (temp)
false) >= 2)
{
rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
- size_int (GET_MODE_BITSIZE (mode) - 1),
+ GET_MODE_PRECISION (mode) - 1,
NULL_RTX, 0);
temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
false) >= 2)
{
rtx extended = expand_shift (RSHIFT_EXPR, mode, op0,
- size_int (GET_MODE_BITSIZE (mode) - 1),
+ GET_MODE_PRECISION (mode) - 1,
NULL_RTX, 0);
temp = expand_binop (mode, xor_optab, extended, op0, target, 0,
mask = double_int_setbit (double_int_zero, bitpos);
- if (target == 0 || target == op0 || target == op1)
+ if (target == 0
+ || target == op0
+ || target == op1
+ || (nwords > 1 && !valid_multiword_target_p (target)))
target = gen_reg_rtx (mode);
if (nwords > 1)
/* If we are optimizing, force expensive constants into a register. */
if (CONSTANT_P (x) && optimize
- && (rtx_cost (x, COMPARE, optimize_insn_for_speed_p ())
+ && (rtx_cost (x, COMPARE, 0, optimize_insn_for_speed_p ())
> COSTS_N_INSNS (1)))
x = force_reg (mode, x);
if (CONSTANT_P (y) && optimize
- && (rtx_cost (y, COMPARE, optimize_insn_for_speed_p ())
+ && (rtx_cost (y, COMPARE, 1, optimize_insn_for_speed_p ())
> COSTS_N_INSNS (1)))
y = force_reg (mode, y);
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. */
+ represent LTU.
+ The comparisons in the fixed-point helper library are always
+ biased. */
x = result;
y = const1_rtx;
- if (!TARGET_LIB_INT_CMP_BIASED)
+ if (!TARGET_LIB_INT_CMP_BIASED && !ALL_FIXED_POINT_MODE_P (mode))
{
if (unsignedp)
x = plus_constant (result, 1);
return CODE_FOR_nothing;
}
-static enum insn_code
+enum insn_code
can_float_p (enum machine_mode fltmode, enum machine_mode fixmode,
int unsignedp)
{
int doing_unsigned = unsignedp;
if (fmode != GET_MODE (to)
- && significand_size (fmode) < GET_MODE_BITSIZE (GET_MODE (from)))
+ && significand_size (fmode) < GET_MODE_PRECISION (GET_MODE (from)))
continue;
icode = can_float_p (fmode, imode, unsignedp);
for (fmode = GET_MODE (to); fmode != VOIDmode;
fmode = GET_MODE_WIDER_MODE (fmode))
- if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
+ if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
&& can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
break;
/* Avoid double-rounding when TO is narrower than FROM. */
if ((significand_size (fmode) + 1)
- < GET_MODE_BITSIZE (GET_MODE (from)))
+ < GET_MODE_PRECISION (GET_MODE (from)))
{
rtx temp1;
rtx neglabel = gen_label_rtx ();
emit_label (neglabel);
temp = expand_binop (imode, and_optab, from, const1_rtx,
NULL_RTX, 1, OPTAB_LIB_WIDEN);
- temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node,
- NULL_RTX, 1);
+ temp1 = expand_shift (RSHIFT_EXPR, imode, from, 1, NULL_RTX, 1);
temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1,
OPTAB_LIB_WIDEN);
expand_float (target, temp, 0);
0, label);
- real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)), fmode);
+ real_2expN (&offset, GET_MODE_PRECISION (GET_MODE (from)), fmode);
temp = expand_binop (fmode, add_optab, target,
CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode),
target, 0, OPTAB_LIB_WIDEN);
2^63. The subtraction of 2^63 should not generate any rounding as it
simply clears out that bit. The rest is trivial. */
- if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
+ if (unsignedp && GET_MODE_PRECISION (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
for (fmode = GET_MODE (from); fmode != VOIDmode;
fmode = GET_MODE_WIDER_MODE (fmode))
if (CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, &must_trunc)
&& (!DECIMAL_FLOAT_MODE_P (fmode)
- || GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to))))
+ || GET_MODE_BITSIZE (fmode) > GET_MODE_PRECISION (GET_MODE (to))))
{
int bitsize;
REAL_VALUE_TYPE offset;
rtx limit, lab1, lab2, insn;
- bitsize = GET_MODE_BITSIZE (GET_MODE (to));
+ bitsize = GET_MODE_PRECISION (GET_MODE (to));
real_2expN (&offset, bitsize - 1, fmode);
limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode);
lab1 = gen_label_rtx ();
unsigned opname_len = strlen (opname);
const char *mname = GET_MODE_NAME (mode);
unsigned mname_len = strlen (mname);
- char *libfunc_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
+ int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2;
+ int len = prefix_len + opname_len + mname_len + 1 + 1;
+ char *libfunc_name = XALLOCAVEC (char, len);
char *p;
const char *q;
p = libfunc_name;
*p++ = '_';
*p++ = '_';
+ if (targetm.libfunc_gnu_prefix)
+ {
+ *p++ = 'g';
+ *p++ = 'n';
+ *p++ = 'u';
+ *p++ = '_';
+ }
for (q = opname; *q; )
*p++ = *q++;
for (q = mname; *q; q++)
const char *fname, *tname;
const char *q;
+ int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2;
char *libfunc_name, *suffix;
char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix;
char *p;
mname_len = strlen (GET_MODE_NAME (tmode)) + strlen (GET_MODE_NAME (fmode));
- nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
+ nondec_name = XALLOCAVEC (char, prefix_len + opname_len + mname_len + 1 + 1);
nondec_name[0] = '_';
nondec_name[1] = '_';
- memcpy (&nondec_name[2], opname, opname_len);
- nondec_suffix = nondec_name + opname_len + 2;
+ if (targetm.libfunc_gnu_prefix)
+ {
+ nondec_name[2] = 'g';
+ nondec_name[3] = 'n';
+ nondec_name[4] = 'u';
+ nondec_name[5] = '_';
+ }
+
+ memcpy (&nondec_name[prefix_len], opname, opname_len);
+ nondec_suffix = nondec_name + opname_len + prefix_len;
dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1);
dec_name[0] = '_';
const char *fname, *tname;
const char *q;
+ int prefix_len = targetm.libfunc_gnu_prefix ? 6 : 2;
char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix;
char *libfunc_name, *suffix;
char *p;
nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1);
nondec_name[0] = '_';
nondec_name[1] = '_';
- memcpy (&nondec_name[2], opname, opname_len);
- nondec_suffix = nondec_name + opname_len + 2;
+ if (targetm.libfunc_gnu_prefix)
+ {
+ nondec_name[2] = 'g';
+ nondec_name[3] = 'n';
+ nondec_name[4] = 'u';
+ nondec_name[5] = '_';
+ }
+ memcpy (&nondec_name[prefix_len], opname, opname_len);
+ nondec_suffix = nondec_name + opname_len + prefix_len;
dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1);
dec_name[0] = '_';
init_optab (usashl_optab, US_ASHIFT);
init_optab (ashr_optab, ASHIFTRT);
init_optab (lshr_optab, LSHIFTRT);
+ init_optabv (vashl_optab, ASHIFT);
+ init_optabv (vashr_optab, ASHIFTRT);
+ init_optabv (vlshr_optab, LSHIFTRT);
init_optab (rotl_optab, ROTATE);
init_optab (rotr_optab, ROTATERT);
init_optab (smin_optab, SMIN);
init_optab (ffs_optab, FFS);
init_optab (clz_optab, CLZ);
init_optab (ctz_optab, CTZ);
+ init_optab (clrsb_optab, CLRSB);
init_optab (popcount_optab, POPCOUNT);
init_optab (parity_optab, PARITY);
init_optab (sqrt_optab, SQRT);
ctz_optab->libcall_basename = "ctz";
ctz_optab->libcall_suffix = '2';
ctz_optab->libcall_gen = gen_int_libfunc;
+ clrsb_optab->libcall_basename = "clrsb";
+ clrsb_optab->libcall_suffix = '2';
+ clrsb_optab->libcall_gen = gen_int_libfunc;
popcount_optab->libcall_basename = "popcount";
popcount_optab->libcall_suffix = '2';
popcount_optab->libcall_gen = gen_int_libfunc;
/* Explicitly initialize the bswap libfuncs since we need them to be
valid for things other than word_mode. */
- set_optab_libfunc (bswap_optab, SImode, "__bswapsi2");
- set_optab_libfunc (bswap_optab, DImode, "__bswapdi2");
+ if (targetm.libfunc_gnu_prefix)
+ {
+ set_optab_libfunc (bswap_optab, SImode, "__gnu_bswapsi2");
+ set_optab_libfunc (bswap_optab, DImode, "__gnu_bswapdi2");
+ }
+ else
+ {
+ set_optab_libfunc (bswap_optab, SImode, "__bswapsi2");
+ set_optab_libfunc (bswap_optab, DImode, "__bswapdi2");
+ }
/* 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. */
return gen_rtx_fmt_ee (rcode, VOIDmode, ops[0].value, ops[1].value);
}
-/* Return insn code for TYPE, the type of a VEC_COND_EXPR. */
+/* Return insn code for a conditional operator with a comparison in
+ mode CMODE, unsigned if UNS is true, resulting in a value of mode VMODE. */
static inline enum insn_code
-get_vcond_icode (tree type, enum machine_mode mode)
+get_vcond_icode (enum machine_mode vmode, enum machine_mode cmode, bool uns)
{
enum insn_code icode = CODE_FOR_nothing;
-
- if (TYPE_UNSIGNED (type))
- icode = direct_optab_handler (vcondu_optab, mode);
+ if (uns)
+ icode = convert_optab_handler (vcondu_optab, vmode, cmode);
else
- icode = direct_optab_handler (vcond_optab, mode);
+ icode = convert_optab_handler (vcond_optab, vmode, cmode);
return icode;
}
/* Return TRUE iff, appropriate vector insns are available
- for vector cond expr with type TYPE in VMODE mode. */
+ for vector cond expr with vector type VALUE_TYPE and a comparison
+ with operand vector types in CMP_OP_TYPE. */
bool
-expand_vec_cond_expr_p (tree type, enum machine_mode vmode)
-{
- if (get_vcond_icode (type, vmode) == CODE_FOR_nothing)
+expand_vec_cond_expr_p (tree value_type, tree cmp_op_type)
+{
+ enum machine_mode value_mode = TYPE_MODE (value_type);
+ enum machine_mode cmp_op_mode = TYPE_MODE (cmp_op_type);
+ if (GET_MODE_SIZE (value_mode) != GET_MODE_SIZE (cmp_op_mode)
+ || GET_MODE_NUNITS (value_mode) != GET_MODE_NUNITS (cmp_op_mode)
+ || get_vcond_icode (TYPE_MODE (value_type), TYPE_MODE (cmp_op_type),
+ TYPE_UNSIGNED (cmp_op_type)) == CODE_FOR_nothing)
return false;
return true;
}
enum insn_code icode;
rtx comparison, rtx_op1, rtx_op2;
enum machine_mode mode = TYPE_MODE (vec_cond_type);
- bool unsignedp = TYPE_UNSIGNED (vec_cond_type);
+ enum machine_mode cmp_op_mode;
+ bool unsignedp;
+
+ gcc_assert (COMPARISON_CLASS_P (op0));
+
+ unsignedp = TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0)));
+ cmp_op_mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (op0, 0)));
- icode = get_vcond_icode (vec_cond_type, mode);
+ gcc_assert (GET_MODE_SIZE (mode) == GET_MODE_SIZE (cmp_op_mode)
+ && GET_MODE_NUNITS (mode) == GET_MODE_NUNITS (cmp_op_mode));
+
+ icode = get_vcond_icode (mode, cmp_op_mode, unsignedp);
if (icode == CODE_FOR_nothing)
return 0;
(operand, insn_data[(int) icode].operand[opno].mode)));
}
\f
+/* TARGET is a target of a multiword operation that we are going to
+ implement as a series of word-mode operations. Return true if
+ TARGET is suitable for this purpose. */
+
+bool
+valid_multiword_target_p (rtx target)
+{
+ enum machine_mode mode;
+ int i;
+
+ mode = GET_MODE (target);
+ for (i = 0; i < GET_MODE_SIZE (mode); i += UNITS_PER_WORD)
+ if (!validate_subreg (word_mode, mode, target, i))
+ return false;
+ return true;
+}
+
+/* Like maybe_legitimize_operand, but do not change the code of the
+ current rtx value. */
+
+static bool
+maybe_legitimize_operand_same_code (enum insn_code icode, unsigned int opno,
+ struct expand_operand *op)
+{
+ /* See if the operand matches in its current form. */
+ if (insn_operand_matches (icode, opno, op->value))
+ return true;
+
+ /* If the operand is a memory whose address has no side effects,
+ try forcing the address into a register. The check for side
+ effects is important because force_reg cannot handle things
+ like auto-modified addresses. */
+ if (insn_data[(int) icode].operand[opno].allows_mem
+ && MEM_P (op->value)
+ && !side_effects_p (XEXP (op->value, 0)))
+ {
+ rtx addr, mem, last;
+
+ last = get_last_insn ();
+ addr = force_reg (Pmode, XEXP (op->value, 0));
+ mem = replace_equiv_address (op->value, addr);
+ if (insn_operand_matches (icode, opno, mem))
+ {
+ op->value = mem;
+ return true;
+ }
+ delete_insns_since (last);
+ }
+
+ return false;
+}
+
/* Try to make OP match operand OPNO of instruction ICODE. Return true
on success, storing the new operand value back in OP. */
enum machine_mode mode, imode;
bool old_volatile_ok, result;
- old_volatile_ok = volatile_ok;
mode = op->mode;
- result = false;
switch (op->type)
{
case EXPAND_FIXED:
+ old_volatile_ok = volatile_ok;
volatile_ok = true;
- break;
+ result = maybe_legitimize_operand_same_code (icode, opno, op);
+ volatile_ok = old_volatile_ok;
+ return result;
case EXPAND_OUTPUT:
gcc_assert (mode != VOIDmode);
- if (!op->value
- || op->value == const0_rtx
- || GET_MODE (op->value) != mode
- || !insn_operand_matches (icode, opno, op->value))
- op->value = gen_reg_rtx (mode);
+ if (op->value
+ && op->value != const0_rtx
+ && GET_MODE (op->value) == mode
+ && maybe_legitimize_operand_same_code (icode, opno, op))
+ return true;
+
+ op->value = gen_reg_rtx (mode);
break;
case EXPAND_INPUT:
gcc_assert (mode != VOIDmode);
gcc_assert (GET_MODE (op->value) == VOIDmode
|| GET_MODE (op->value) == mode);
- result = insn_operand_matches (icode, opno, op->value);
- if (!result)
- op->value = copy_to_mode_reg (mode, op->value);
+ if (maybe_legitimize_operand_same_code (icode, opno, op))
+ return true;
+
+ op->value = copy_to_mode_reg (mode, op->value);
break;
case EXPAND_CONVERT_TO:
goto input;
break;
}
- if (!result)
- result = insn_operand_matches (icode, opno, op->value);
- volatile_ok = old_volatile_ok;
- return result;
+ return insn_operand_matches (icode, opno, op->value);
}
/* Make OP describe an input operand that should have the same value