/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
- Copyright (C) 1987-2017 Free Software Foundation, Inc.
+ Copyright (C) 1987-2019 Free Software Foundation, Inc.
This file is part of GCC.
#include "emit-rtl.h"
#include "recog.h"
#include "diagnostic-core.h"
+#include "rtx-vector-builder.h"
/* Include insn-config.h before expr.h so that HAVE_conditional_move
is properly defined. */
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)))
+ if (GET_MODE_UNIT_SIZE (GET_MODE (op0))
+ > GET_MODE_UNIT_SIZE (GET_MODE (target)))
note = simplify_gen_unary (TRUNCATE, GET_MODE (target),
note, GET_MODE (op0));
else
if (m0 == VOIDmode && m1 == VOIDmode)
return to_mode;
- else if (m0 == VOIDmode || GET_MODE_SIZE (m0) < GET_MODE_SIZE (m1))
+ else if (m0 == VOIDmode || GET_MODE_UNIT_SIZE (m0) < GET_MODE_UNIT_SIZE (m1))
result = m1;
else
result = m0;
- if (GET_MODE_SIZE (result) > GET_MODE_SIZE (to_mode))
+ if (GET_MODE_UNIT_SIZE (result) > GET_MODE_UNIT_SIZE (to_mode))
return to_mode;
return result;
enum insn_code icode;
int nops = TREE_CODE_LENGTH (ops->code);
int op;
+ bool sbool = false;
oprnd0 = ops->op0;
tmode0 = TYPE_MODE (TREE_TYPE (oprnd0));
- widen_pattern_optab =
- optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default);
+ if (ops->code == VEC_UNPACK_FIX_TRUNC_HI_EXPR
+ || ops->code == VEC_UNPACK_FIX_TRUNC_LO_EXPR)
+ /* The sign is from the result type rather than operand's type
+ for these ops. */
+ widen_pattern_optab
+ = optab_for_tree_code (ops->code, ops->type, optab_default);
+ else if ((ops->code == VEC_UNPACK_HI_EXPR
+ || ops->code == VEC_UNPACK_LO_EXPR)
+ && VECTOR_BOOLEAN_TYPE_P (ops->type)
+ && VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (oprnd0))
+ && TYPE_MODE (ops->type) == TYPE_MODE (TREE_TYPE (oprnd0))
+ && SCALAR_INT_MODE_P (TYPE_MODE (ops->type)))
+ {
+ /* For VEC_UNPACK_{LO,HI}_EXPR if the mode of op0 and result is
+ the same scalar mode for VECTOR_BOOLEAN_TYPE_P vectors, use
+ vec_unpacks_sbool_{lo,hi}_optab, so that we can pass in
+ the pattern number of elements in the wider vector. */
+ widen_pattern_optab
+ = (ops->code == VEC_UNPACK_HI_EXPR
+ ? vec_unpacks_sbool_hi_optab : vec_unpacks_sbool_lo_optab);
+ sbool = true;
+ }
+ else
+ widen_pattern_optab
+ = 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 = find_widening_optab_handler (widen_pattern_optab,
TYPE_MODE (TREE_TYPE (ops->op2)),
- tmode0, 0);
+ tmode0);
else
icode = optab_handler (widen_pattern_optab, tmode0);
gcc_assert (icode != CODE_FOR_nothing);
oprnd1 = ops->op1;
tmode1 = TYPE_MODE (TREE_TYPE (oprnd1));
}
+ else if (sbool)
+ {
+ nops = 2;
+ op1 = GEN_INT (TYPE_VECTOR_SUBPARTS (TREE_TYPE (oprnd0)).to_constant ());
+ tmode1 = tmode0;
+ }
/* The last operand is of a wider mode than the rest of the operands. */
if (nops == 2)
mode of OP must be the element mode of VMODE. If OP is a constant,
then the return value will be a constant. */
-static rtx
+rtx
expand_vector_broadcast (machine_mode vmode, rtx op)
{
- enum insn_code icode;
+ int n;
rtvec vec;
- rtx ret;
- int i, n;
gcc_checking_assert (VECTOR_MODE_P (vmode));
- n = GET_MODE_NUNITS (vmode);
- vec = rtvec_alloc (n);
- for (i = 0; i < n; ++i)
- RTVEC_ELT (vec, i) = op;
+ if (valid_for_const_vector_p (vmode, op))
+ return gen_const_vec_duplicate (vmode, op);
- if (CONSTANT_P (op))
- return gen_rtx_CONST_VECTOR (vmode, vec);
+ insn_code icode = optab_handler (vec_duplicate_optab, vmode);
+ if (icode != CODE_FOR_nothing)
+ {
+ struct expand_operand ops[2];
+ create_output_operand (&ops[0], NULL_RTX, vmode);
+ create_input_operand (&ops[1], op, GET_MODE (op));
+ expand_insn (icode, 2, ops);
+ return ops[0].value;
+ }
+
+ if (!GET_MODE_NUNITS (vmode).is_constant (&n))
+ return NULL;
/* ??? If the target doesn't have a vec_init, then we have no easy way
of performing this operation. Most of this sort of generic support
if (icode == CODE_FOR_nothing)
return NULL;
- ret = gen_reg_rtx (vmode);
+ vec = rtvec_alloc (n);
+ for (int i = 0; i < n; ++i)
+ RTVEC_ELT (vec, i) = op;
+ rtx ret = gen_reg_rtx (vmode);
emit_insn (GEN_FCN (icode) (ret, gen_rtx_PARALLEL (vmode, vec)));
return ret;
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),
+ if (!force_expand_binop (word_mode, binoptab, outof_input,
+ gen_int_shift_amount (word_mode,
+ BITS_PER_WORD - 1),
outof_target, unsignedp, methods))
return false;
}
value are the same as for the parent routine. */
static bool
-expand_subword_shift (machine_mode op1_mode, optab binoptab,
+expand_subword_shift (scalar_int_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,
arguments are the same as the parent routine. */
static bool
-expand_doubleword_shift_condmove (machine_mode op1_mode, optab binoptab,
+expand_doubleword_shift_condmove (scalar_int_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,
Return true if the shift could be successfully synthesized. */
static bool
-expand_doubleword_shift (machine_mode op1_mode, optab binoptab,
+expand_doubleword_shift (scalar_int_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,
{
int low = (WORDS_BIG_ENDIAN ? 1 : 0);
int high = (WORDS_BIG_ENDIAN ? 0 : 1);
- rtx wordm1 = umulp ? NULL_RTX : GEN_INT (BITS_PER_WORD - 1);
+ rtx wordm1 = (umulp ? NULL_RTX
+ : gen_int_shift_amount (word_mode, BITS_PER_WORD - 1));
rtx product, adjust, product_high, temp;
rtx op0_high = operand_subword_force (op0, high, mode);
if (target && !REG_P (target))
target = NULL_RTX;
+ /* *_widen_optab needs to determine operand mode, make sure at least
+ one operand has non-VOID mode. */
+ if (GET_MODE (op0_low) == VOIDmode && GET_MODE (op1_low) == VOIDmode)
+ op0_low = force_reg (word_mode, op0_low);
+
if (umulp)
product = expand_binop (mode, umul_widen_optab, op0_low, op1_low,
target, 1, OPTAB_DIRECT);
}
/* Helper function for expand_binop: handle the case where there
- is an insn that directly implements the indicated operation.
+ is an insn ICODE that directly implements the indicated operation.
Returns null if this is not possible. */
static rtx
-expand_binop_directly (machine_mode mode, optab binoptab,
+expand_binop_directly (enum insn_code icode, machine_mode mode, optab binoptab,
rtx op0, rtx op1,
rtx target, int unsignedp, enum optab_methods methods,
rtx_insn *last)
{
- machine_mode from_mode = widened_mode (mode, op0, op1);
- enum insn_code icode = find_widening_optab_handler (binoptab, mode,
- from_mode, 1);
machine_mode xmode0 = insn_data[(int) icode].operand[1].mode;
machine_mode xmode1 = insn_data[(int) icode].operand[2].mode;
machine_mode mode0, mode1, tmp_mode;
|| binoptab == vec_pack_usat_optab
|| binoptab == vec_pack_ssat_optab
|| binoptab == vec_pack_ufix_trunc_optab
- || binoptab == vec_pack_sfix_trunc_optab)
+ || binoptab == vec_pack_sfix_trunc_optab
+ || binoptab == vec_packu_float_optab
+ || binoptab == vec_packs_float_optab)
{
/* The mode of the result is different then the mode of the
arguments. */
tmp_mode = insn_data[(int) icode].operand[0].mode;
if (VECTOR_MODE_P (mode)
- && GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
+ && maybe_ne (GET_MODE_NUNITS (tmp_mode), 2 * GET_MODE_NUNITS (mode)))
{
delete_insns_since (last);
return NULL_RTX;
= (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
? OPTAB_WIDEN : methods);
enum mode_class mclass;
+ enum insn_code icode;
machine_mode wider_mode;
scalar_int_mode int_mode;
rtx libfunc;
/* If we can do it with a three-operand insn, do so. */
- if (methods != OPTAB_MUST_WIDEN
- && find_widening_optab_handler (binoptab, mode,
- widened_mode (mode, op0, op1), 1)
- != CODE_FOR_nothing)
+ if (methods != OPTAB_MUST_WIDEN)
{
- temp = expand_binop_directly (mode, binoptab, op0, op1, target,
- unsignedp, methods, last);
- if (temp)
- return temp;
+ if (convert_optab_p (binoptab))
+ {
+ machine_mode from_mode = widened_mode (mode, op0, op1);
+ icode = find_widening_optab_handler (binoptab, mode, from_mode);
+ }
+ else
+ icode = optab_handler (binoptab, mode);
+ if (icode != CODE_FOR_nothing)
+ {
+ temp = expand_binop_directly (icode, mode, binoptab, op0, op1,
+ target, unsignedp, methods, last);
+ if (temp)
+ return temp;
+ }
}
/* If we were trying to rotate, and that didn't work, try rotating
the other direction before falling back to shifts and bitwise-or. */
if (((binoptab == rotl_optab
- && optab_handler (rotr_optab, mode) != CODE_FOR_nothing)
+ && (icode = optab_handler (rotr_optab, mode)) != CODE_FOR_nothing)
|| (binoptab == rotr_optab
- && optab_handler (rotl_optab, mode) != CODE_FOR_nothing))
+ && (icode = optab_handler (rotl_optab, mode)) != CODE_FOR_nothing))
&& is_int_mode (mode, &int_mode))
{
optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab);
unsigned int bits = GET_MODE_PRECISION (int_mode);
if (CONST_INT_P (op1))
- newop1 = GEN_INT (bits - INTVAL (op1));
+ newop1 = gen_int_shift_amount (int_mode, bits - INTVAL (op1));
else if (targetm.shift_truncation_mask (int_mode) == bits - 1)
newop1 = negate_rtx (GET_MODE (op1), op1);
else
gen_int_mode (bits, GET_MODE (op1)), op1,
NULL_RTX, unsignedp, OPTAB_DIRECT);
- temp = expand_binop_directly (int_mode, otheroptab, op0, newop1,
+ temp = expand_binop_directly (icode, int_mode, otheroptab, op0, newop1,
target, unsignedp, methods, last);
if (temp)
return temp;
: smul_widen_optab),
wider_mode, mode) != CODE_FOR_nothing))
{
+ /* *_widen_optab needs to determine operand mode, make sure at least
+ one operand has non-VOID mode. */
+ if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
+ op0 = force_reg (mode, op0);
temp = expand_binop (wider_mode,
unsignedp ? umul_widen_optab : smul_widen_optab,
op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT);
else if (binoptab == rotr_optab)
otheroptab = vrotr_optab;
- if (otheroptab && optab_handler (otheroptab, mode) != CODE_FOR_nothing)
+ if (otheroptab
+ && (icode = optab_handler (otheroptab, mode)) != CODE_FOR_nothing)
{
/* The scalar may have been extended to be too wide. Truncate
it back to the proper size to fit in the broadcast vector. */
- machine_mode inner_mode = GET_MODE_INNER (mode);
+ scalar_mode inner_mode = GET_MODE_INNER (mode);
if (!CONST_INT_P (op1)
- && (GET_MODE_BITSIZE (inner_mode)
- < GET_MODE_BITSIZE (GET_MODE (op1))))
+ && (GET_MODE_BITSIZE (as_a <scalar_int_mode> (GET_MODE (op1)))
+ > GET_MODE_BITSIZE (inner_mode)))
op1 = force_reg (inner_mode,
simplify_gen_unary (TRUNCATE, inner_mode, op1,
GET_MODE (op1)));
rtx vop1 = expand_vector_broadcast (mode, op1);
if (vop1)
{
- temp = expand_binop_directly (mode, otheroptab, op0, vop1,
+ temp = expand_binop_directly (icode, mode, otheroptab, op0, vop1,
target, unsignedp, methods, last);
if (temp)
return temp;
&& (find_widening_optab_handler ((unsignedp
? umul_widen_optab
: smul_widen_optab),
- next_mode, mode, 0)
+ next_mode, mode)
!= CODE_FOR_nothing)))
{
rtx xop0 = op0, xop1 = op1;
start_sequence ();
/* Do the actual arithmetic. */
+ machine_mode op0_mode = GET_MODE (op0);
+ machine_mode op1_mode = GET_MODE (op1);
+ if (op0_mode == VOIDmode)
+ op0_mode = int_mode;
+ if (op1_mode == VOIDmode)
+ op1_mode = int_mode;
for (i = 0; i < GET_MODE_BITSIZE (int_mode) / BITS_PER_WORD; i++)
{
rtx target_piece = operand_subword (target, i, 1, int_mode);
rtx x = expand_binop (word_mode, binoptab,
- operand_subword_force (op0, i, int_mode),
- operand_subword_force (op1, i, int_mode),
+ operand_subword_force (op0, i, op0_mode),
+ operand_subword_force (op1, i, op1_mode),
target_piece, unsignedp, next_methods);
if (x == 0)
&& optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
{
unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
- machine_mode op1_mode;
+ scalar_int_mode op1_mode;
double_shift_mask = targetm.shift_truncation_mask (int_mode);
shift_mask = targetm.shift_truncation_mask (word_mode);
- op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode;
+ op1_mode = (GET_MODE (op1) != VOIDmode
+ ? as_a <scalar_int_mode> (GET_MODE (op1))
+ : word_mode);
/* Apply the truncation to constant shifts. */
if (double_shift_mask > 0 && CONST_INT_P (op1))
- op1 = GEN_INT (INTVAL (op1) & double_shift_mask);
+ op1 = gen_int_mode (INTVAL (op1) & double_shift_mask, op1_mode);
if (op1 == CONST0_RTX (op1_mode))
return op0;
else
{
rtx into_temp1, into_temp2, outof_temp1, outof_temp2;
- rtx first_shift_count, second_shift_count;
+ HOST_WIDE_INT first_shift_count, second_shift_count;
optab reverse_unsigned_shift, unsigned_shift;
reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD)
if (shift_count > BITS_PER_WORD)
{
- first_shift_count = GEN_INT (shift_count - BITS_PER_WORD);
- second_shift_count = GEN_INT (2 * BITS_PER_WORD - shift_count);
+ first_shift_count = shift_count - BITS_PER_WORD;
+ second_shift_count = 2 * BITS_PER_WORD - shift_count;
}
else
{
- first_shift_count = GEN_INT (BITS_PER_WORD - shift_count);
- second_shift_count = GEN_INT (shift_count);
+ first_shift_count = BITS_PER_WORD - shift_count;
+ second_shift_count = shift_count;
}
+ rtx first_shift_count_rtx
+ = gen_int_shift_amount (word_mode, first_shift_count);
+ rtx second_shift_count_rtx
+ = gen_int_shift_amount (word_mode, second_shift_count);
into_temp1 = expand_binop (word_mode, unsigned_shift,
- outof_input, first_shift_count,
+ outof_input, first_shift_count_rtx,
NULL_RTX, unsignedp, next_methods);
into_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
- into_input, second_shift_count,
+ into_input, second_shift_count_rtx,
NULL_RTX, unsignedp, next_methods);
if (into_temp1 != 0 && into_temp2 != 0)
emit_move_insn (into_target, inter);
outof_temp1 = expand_binop (word_mode, unsigned_shift,
- into_input, first_shift_count,
+ into_input, first_shift_count_rtx,
NULL_RTX, unsignedp, next_methods);
outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift,
- outof_input, second_shift_count,
+ outof_input, second_shift_count_rtx,
NULL_RTX, unsignedp, next_methods);
if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0)
&& optab_handler (add_optab, word_mode) != CODE_FOR_nothing)
{
rtx product = NULL_RTX;
- if (widening_optab_handler (umul_widen_optab, int_mode, word_mode)
+ if (convert_optab_handler (umul_widen_optab, int_mode, word_mode)
!= CODE_FOR_nothing)
{
product = expand_doubleword_mult (int_mode, op0, op1, target,
}
if (product == NULL_RTX
- && (widening_optab_handler (smul_widen_optab, int_mode, word_mode)
+ && (convert_optab_handler (smul_widen_optab, int_mode, word_mode)
!= CODE_FOR_nothing))
{
product = expand_doubleword_mult (int_mode, op0, op1, target,
/* 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 (libfunc,
- NULL_RTX, LCT_CONST, mode, 2,
+ NULL_RTX, LCT_CONST, mode,
op0, mode, op1x, op1_mode);
insns = get_insns ();
if (CLASS_HAS_WIDER_MODES_P (mclass))
{
+ /* This code doesn't make sense for conversion optabs, since we
+ wouldn't then want to extend the operands to be the same size
+ as the result. */
+ gcc_assert (!convert_optab_p (binoptab));
FOR_EACH_WIDER_MODE (wider_mode, mode)
{
- if (find_widening_optab_handler (binoptab, wider_mode, mode, 1)
- != CODE_FOR_nothing
+ if (optab_handler (binoptab, wider_mode)
|| (methods == OPTAB_LIB
&& optab_libfunc (binoptab, wider_mode)))
{
libval_mode = smallest_int_mode_for_size (2 * GET_MODE_BITSIZE (mode));
start_sequence ();
libval = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
- libval_mode, 2,
+ libval_mode,
op0, mode,
op1, mode);
/* Get the part of VAL containing the value that we want. */
A similar operation can be used for clrsb. UNOPTAB says which operation
we are trying to expand. */
static rtx
-widen_leading (machine_mode mode, rtx op0, rtx target, optab unoptab)
+widen_leading (scalar_int_mode mode, rtx op0, rtx target, optab unoptab)
{
- enum mode_class mclass = GET_MODE_CLASS (mode);
- if (CLASS_HAS_WIDER_MODES_P (mclass))
+ opt_scalar_int_mode wider_mode_iter;
+ FOR_EACH_WIDER_MODE (wider_mode_iter, mode)
{
- machine_mode wider_mode;
- FOR_EACH_WIDER_MODE (wider_mode, mode)
+ scalar_int_mode wider_mode = wider_mode_iter.require ();
+ if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
{
- if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
- {
- rtx xop0, temp;
- rtx_insn *last;
+ rtx xop0, temp;
+ rtx_insn *last;
- last = get_last_insn ();
+ last = get_last_insn ();
- if (target == 0)
- target = gen_reg_rtx (mode);
- 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_mode (GET_MODE_PRECISION (wider_mode)
- - GET_MODE_PRECISION (mode),
- wider_mode),
- target, true, OPTAB_DIRECT);
- if (temp == 0)
- delete_insns_since (last);
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ 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_mode (GET_MODE_PRECISION (wider_mode)
+ - GET_MODE_PRECISION (mode),
+ wider_mode),
+ target, true, OPTAB_DIRECT);
+ if (temp == 0)
+ delete_insns_since (last);
- return temp;
- }
+ return temp;
}
}
return 0;
/* Try calculating clz of a double-word quantity as two clz's of word-sized
quantities, choosing which based on whether the high word is nonzero. */
static rtx
-expand_doubleword_clz (machine_mode mode, rtx op0, rtx target)
+expand_doubleword_clz (scalar_int_mode mode, rtx op0, rtx target)
{
rtx xop0 = force_reg (mode, op0);
rtx subhi = gen_highpart (word_mode, xop0);
/* Try calculating popcount of a double-word quantity as two popcount's of
word-sized quantities and summing up the results. */
static rtx
-expand_doubleword_popcount (machine_mode mode, rtx op0, rtx target)
+expand_doubleword_popcount (scalar_int_mode mode, rtx op0, rtx target)
{
rtx t0, t1, t;
rtx_insn *seq;
as
(parity:narrow (low (x) ^ high (x))) */
static rtx
-expand_doubleword_parity (machine_mode mode, rtx op0, rtx target)
+expand_doubleword_parity (scalar_int_mode mode, rtx op0, rtx target)
{
rtx t = expand_binop (word_mode, xor_optab,
operand_subword_force (op0, 0, mode),
as
(lshiftrt:wide (bswap:wide x) ((width wide) - (width narrow))). */
static rtx
-widen_bswap (machine_mode mode, rtx op0, rtx target)
+widen_bswap (scalar_int_mode mode, rtx op0, rtx target)
{
- enum mode_class mclass = GET_MODE_CLASS (mode);
- machine_mode wider_mode;
rtx x;
rtx_insn *last;
+ opt_scalar_int_mode wider_mode_iter;
- if (!CLASS_HAS_WIDER_MODES_P (mclass))
- return NULL_RTX;
+ FOR_EACH_WIDER_MODE (wider_mode_iter, mode)
+ if (optab_handler (bswap_optab, wider_mode_iter.require ())
+ != CODE_FOR_nothing)
+ break;
- FOR_EACH_WIDER_MODE (wider_mode, mode)
- if (optab_handler (bswap_optab, wider_mode) != CODE_FOR_nothing)
- goto found;
- return NULL_RTX;
+ if (!wider_mode_iter.exists ())
+ return NULL_RTX;
- found:
+ scalar_int_mode wider_mode = wider_mode_iter.require ();
last = get_last_insn ();
x = widen_operand (op0, wider_mode, mode, true, true);
/* Try calculating (parity x) as (and (popcount x) 1), where
popcount can also be done in a wider mode. */
static rtx
-expand_parity (machine_mode mode, rtx op0, rtx target)
+expand_parity (scalar_int_mode mode, rtx op0, rtx target)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
- if (CLASS_HAS_WIDER_MODES_P (mclass))
+ opt_scalar_int_mode wider_mode_iter;
+ FOR_EACH_MODE_FROM (wider_mode_iter, mode)
{
- machine_mode wider_mode;
- FOR_EACH_MODE_FROM (wider_mode, mode)
+ scalar_int_mode wider_mode = wider_mode_iter.require ();
+ if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing)
{
- if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing)
- {
- rtx xop0, temp;
- rtx_insn *last;
+ rtx xop0, temp;
+ rtx_insn *last;
- last = get_last_insn ();
+ last = get_last_insn ();
- if (target == 0 || GET_MODE (target) != wider_mode)
- target = gen_reg_rtx (wider_mode);
+ if (target == 0 || GET_MODE (target) != wider_mode)
+ target = gen_reg_rtx (wider_mode);
- xop0 = widen_operand (op0, wider_mode, mode, true, false);
- temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
- true);
- if (temp != 0)
- temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
- target, true, OPTAB_DIRECT);
+ xop0 = widen_operand (op0, wider_mode, mode, true, false);
+ temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX,
+ true);
+ if (temp != 0)
+ temp = expand_binop (wider_mode, and_optab, temp, const1_rtx,
+ target, true, OPTAB_DIRECT);
- if (temp)
- {
- if (mclass != MODE_INT
- || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
- return convert_to_mode (mode, temp, 0);
- else
- return gen_lowpart (mode, temp);
- }
+ if (temp)
+ {
+ if (mclass != MODE_INT
+ || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode))
+ return convert_to_mode (mode, temp, 0);
else
- delete_insns_since (last);
+ return gen_lowpart (mode, temp);
}
+ else
+ delete_insns_since (last);
}
}
return 0;
less convenient for expand_ffs anyway. */
static rtx
-expand_ctz (machine_mode mode, rtx op0, rtx target)
+expand_ctz (scalar_int_mode mode, rtx op0, rtx target)
{
rtx_insn *seq;
rtx temp;
may have an undefined value in that case. If they do not give us a
convenient value, we have to generate a test and branch. */
static rtx
-expand_ffs (machine_mode mode, rtx op0, rtx target)
+expand_ffs (scalar_int_mode mode, rtx op0, rtx target)
{
HOST_WIDE_INT val = 0;
bool defined_at_zero = false;
{
const struct real_format *fmt;
int bitpos, word, nwords, i;
- machine_mode imode;
+ scalar_int_mode imode;
rtx temp;
rtx_insn *insns;
/* Widening (or narrowing) clz needs special treatment. */
if (unoptab == clz_optab)
{
- temp = widen_leading (mode, op0, target, unoptab);
- if (temp)
- return temp;
-
- if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
- && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+ if (is_a <scalar_int_mode> (mode, &int_mode))
{
- temp = expand_doubleword_clz (mode, op0, target);
+ temp = widen_leading (int_mode, op0, target, unoptab);
if (temp)
return temp;
+
+ if (GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
+ && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+ {
+ temp = expand_doubleword_clz (int_mode, op0, target);
+ if (temp)
+ return temp;
+ }
}
goto try_libcall;
if (unoptab == clrsb_optab)
{
- temp = widen_leading (mode, op0, target, unoptab);
- if (temp)
- return temp;
+ if (is_a <scalar_int_mode> (mode, &int_mode))
+ {
+ temp = widen_leading (int_mode, op0, target, unoptab);
+ if (temp)
+ return temp;
+ }
goto try_libcall;
}
if (unoptab == popcount_optab
- && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
&& optab_handler (unoptab, word_mode) != CODE_FOR_nothing
&& optimize_insn_for_speed_p ())
{
- temp = expand_doubleword_popcount (mode, op0, target);
+ temp = expand_doubleword_popcount (int_mode, op0, target);
if (temp)
return temp;
}
if (unoptab == parity_optab
- && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
&& (optab_handler (unoptab, word_mode) != CODE_FOR_nothing
|| optab_handler (popcount_optab, word_mode) != CODE_FOR_nothing)
&& optimize_insn_for_speed_p ())
{
- temp = expand_doubleword_parity (mode, op0, target);
+ temp = expand_doubleword_parity (int_mode, op0, target);
if (temp)
return temp;
}
if (optab_handler (rotl_optab, mode) != CODE_FOR_nothing)
{
- temp = expand_binop (mode, rotl_optab, op0, GEN_INT (8), target,
- unsignedp, OPTAB_DIRECT);
+ temp = expand_binop (mode, rotl_optab, op0,
+ gen_int_shift_amount (mode, 8),
+ target, unsignedp, OPTAB_DIRECT);
if (temp)
return temp;
}
if (optab_handler (rotr_optab, mode) != CODE_FOR_nothing)
{
- temp = expand_binop (mode, rotr_optab, op0, GEN_INT (8), target,
- unsignedp, OPTAB_DIRECT);
+ temp = expand_binop (mode, rotr_optab, op0,
+ gen_int_shift_amount (mode, 8),
+ target, unsignedp, OPTAB_DIRECT);
if (temp)
return temp;
}
last = get_last_insn ();
- temp1 = expand_binop (mode, ashl_optab, op0, GEN_INT (8), NULL_RTX,
+ temp1 = expand_binop (mode, ashl_optab, op0,
+ gen_int_shift_amount (mode, 8), NULL_RTX,
unsignedp, OPTAB_WIDEN);
- temp2 = expand_binop (mode, lshr_optab, op0, GEN_INT (8), NULL_RTX,
+ temp2 = expand_binop (mode, lshr_optab, op0,
+ gen_int_shift_amount (mode, 8), NULL_RTX,
unsignedp, OPTAB_WIDEN);
if (temp1 && temp2)
{
delete_insns_since (last);
}
- temp = widen_bswap (mode, op0, target);
- if (temp)
- return temp;
-
- if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD
- && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+ if (is_a <scalar_int_mode> (mode, &int_mode))
{
- temp = expand_doubleword_bswap (mode, op0, target);
+ temp = widen_bswap (int_mode, op0, target);
if (temp)
return temp;
+
+ if (GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD
+ && optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
+ {
+ temp = expand_doubleword_bswap (mode, op0, target);
+ if (temp)
+ return temp;
+ }
}
goto try_libcall;
}
/* Try calculating parity (x) as popcount (x) % 2. */
- if (unoptab == parity_optab)
+ if (unoptab == parity_optab && is_a <scalar_int_mode> (mode, &int_mode))
{
- temp = expand_parity (mode, op0, target);
+ temp = expand_parity (int_mode, op0, target);
if (temp)
return temp;
}
/* Try implementing ffs (x) in terms of clz (x). */
- if (unoptab == ffs_optab)
+ if (unoptab == ffs_optab && is_a <scalar_int_mode> (mode, &int_mode))
{
- temp = expand_ffs (mode, op0, target);
+ temp = expand_ffs (int_mode, op0, target);
if (temp)
return temp;
}
/* Try implementing ctz (x) in terms of clz (x). */
- if (unoptab == ctz_optab)
+ if (unoptab == ctz_optab && is_a <scalar_int_mode> (mode, &int_mode))
{
- temp = expand_ctz (mode, op0, target);
+ temp = expand_ctz (int_mode, op0, target);
if (temp)
return temp;
}
/* 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 (libfunc, NULL_RTX, LCT_CONST, outmode,
- 1, op0, mode);
+ op0, mode);
insns = get_insns ();
end_sequence ();
else
{
eq_value = gen_rtx_fmt_e (optab_to_code (unoptab), mode, op0);
- if (GET_MODE_SIZE (outmode) < GET_MODE_SIZE (mode))
+ if (GET_MODE_UNIT_SIZE (outmode) < GET_MODE_UNIT_SIZE (mode))
eq_value = simplify_gen_unary (TRUNCATE, outmode, eq_value, mode);
- else if (GET_MODE_SIZE (outmode) > GET_MODE_SIZE (mode))
+ else if (GET_MODE_UNIT_SIZE (outmode) > GET_MODE_UNIT_SIZE (mode))
eq_value = simplify_gen_unary (ZERO_EXTEND,
outmode, eq_value, mode);
}
result. Similarly for clrsb. */
if ((unoptab == clz_optab || unoptab == clrsb_optab)
&& temp != 0)
- temp = expand_binop
- (wider_mode, sub_optab, temp,
- gen_int_mode (GET_MODE_PRECISION (wider_mode)
- - GET_MODE_PRECISION (mode),
- wider_mode),
- target, true, OPTAB_DIRECT);
+ {
+ scalar_int_mode wider_int_mode
+ = as_a <scalar_int_mode> (wider_mode);
+ int_mode = as_a <scalar_int_mode> (mode);
+ temp = expand_binop
+ (wider_mode, sub_optab, temp,
+ gen_int_mode (GET_MODE_PRECISION (wider_int_mode)
+ - GET_MODE_PRECISION (int_mode),
+ wider_int_mode),
+ target, true, OPTAB_DIRECT);
+ }
/* Likewise for bswap. */
if (unoptab == bswap_optab && temp != 0)
{
- gcc_assert (GET_MODE_PRECISION (wider_mode)
- == GET_MODE_BITSIZE (wider_mode)
- && GET_MODE_PRECISION (mode)
- == GET_MODE_BITSIZE (mode));
-
- temp = expand_shift (RSHIFT_EXPR, wider_mode, temp,
- GET_MODE_BITSIZE (wider_mode)
- - GET_MODE_BITSIZE (mode),
+ scalar_int_mode wider_int_mode
+ = as_a <scalar_int_mode> (wider_mode);
+ int_mode = as_a <scalar_int_mode> (mode);
+ gcc_assert (GET_MODE_PRECISION (wider_int_mode)
+ == GET_MODE_BITSIZE (wider_int_mode)
+ && GET_MODE_PRECISION (int_mode)
+ == GET_MODE_BITSIZE (int_mode));
+
+ temp = expand_shift (RSHIFT_EXPR, wider_int_mode, temp,
+ GET_MODE_BITSIZE (wider_int_mode)
+ - GET_MODE_BITSIZE (int_mode),
NULL_RTX, true);
}
expand_copysign_absneg (scalar_float_mode mode, rtx op0, rtx op1, rtx target,
int bitpos, bool op0_is_abs)
{
- machine_mode imode;
+ scalar_int_mode imode;
enum insn_code icode;
rtx sign;
rtx_code_label *label;
icode = optab_handler (signbit_optab, mode);
if (icode != CODE_FOR_nothing)
{
- imode = insn_data[(int) icode].operand[0].mode;
+ imode = as_a <scalar_int_mode> (insn_data[(int) icode].operand[0].mode);
sign = gen_reg_rtx (imode);
emit_unop_insn (icode, sign, op1, UNKNOWN);
}
gcc_assert (methods == OPTAB_DIRECT || methods == OPTAB_WIDEN
|| methods == OPTAB_LIB_WIDEN);
+ if (CONST_SCALAR_INT_P (y))
+ canonicalize_comparison (mode, &comparison, &y);
+
/* If we are optimizing, force expensive constants into a register. */
if (CONSTANT_P (x) && optimize
&& (rtx_cost (x, mode, COMPARE, 0, optimize_insn_for_speed_p ())
/* Try to use a memory block compare insn - either cmpstr
or cmpmem will do. */
- FOR_EACH_MODE_IN_CLASS (cmp_mode, MODE_INT)
+ opt_scalar_int_mode cmp_mode_iter;
+ FOR_EACH_MODE_IN_CLASS (cmp_mode_iter, MODE_INT)
{
+ scalar_int_mode cmp_mode = cmp_mode_iter.require ();
cmp_code = direct_optab_handler (cmpmem_optab, cmp_mode);
if (cmp_code == CODE_FOR_nothing)
cmp_code = direct_optab_handler (cmpstr_optab, cmp_mode);
continue;
/* Must make sure the size fits the insn's mode. */
- if ((CONST_INT_P (size)
- && INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode)))
- || (GET_MODE_BITSIZE (GET_MODE (size))
- > GET_MODE_BITSIZE (cmp_mode)))
+ if (CONST_INT_P (size)
+ ? INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode))
+ : (GET_MODE_BITSIZE (as_a <scalar_int_mode> (GET_MODE (size)))
+ > GET_MODE_BITSIZE (cmp_mode)))
continue;
result_mode = insn_data[cmp_code].operand[0].mode;
if (methods != OPTAB_LIB_WIDEN)
goto fail;
- if (!SCALAR_FLOAT_MODE_P (mode))
+ if (SCALAR_FLOAT_MODE_P (mode))
+ {
+ /* Small trick if UNORDERED isn't implemented by the hardware. */
+ if (comparison == UNORDERED && rtx_equal_p (x, y))
+ {
+ prepare_cmp_insn (x, y, UNLT, NULL_RTX, unsignedp, OPTAB_WIDEN,
+ ptest, pmode);
+ if (*ptest)
+ return;
+ }
+
+ prepare_float_lib_cmp (x, y, comparison, ptest, pmode);
+ }
+ else
{
rtx result;
machine_mode ret_mode;
ret_mode = targetm.libgcc_cmp_return_mode ();
result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
- ret_mode, 2, x, mode, y, mode);
+ ret_mode, 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
prepare_cmp_insn (x, y, comparison, NULL_RTX, unsignedp, methods,
ptest, pmode);
}
- else
- prepare_float_lib_cmp (x, y, comparison, ptest, pmode);
return;
enum rtx_code swapped = swap_condition (comparison);
enum rtx_code reversed = reverse_condition_maybe_unordered (comparison);
machine_mode orig_mode = GET_MODE (x);
- machine_mode mode, cmp_mode;
+ machine_mode mode;
rtx true_rtx, false_rtx;
rtx value, target, equiv;
rtx_insn *insns;
rtx libfunc = 0;
bool reversed_p = false;
- cmp_mode = targetm.libgcc_cmp_return_mode ();
+ scalar_int_mode cmp_mode = targetm.libgcc_cmp_return_mode ();
FOR_EACH_MODE_FROM (mode, orig_mode)
{
start_sequence ();
value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
- cmp_mode, 2, x, mode, y, mode);
+ cmp_mode, x, mode, y, mode);
insns = get_insns ();
end_sequence ();
save_pending_stack_adjust (&save);
last = get_last_insn ();
do_pending_stack_adjust ();
+ machine_mode cmpmode = cmode;
prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
GET_CODE (comparison), NULL_RTX, unsignedp,
- OPTAB_WIDEN, &comparison, &cmode);
+ OPTAB_WIDEN, &comparison, &cmpmode);
if (comparison)
{
struct expand_operand ops[4];
{
enum insn_code icode;
rtx target = to;
+ scalar_mode from_mode, to_mode;
machine_mode fmode, imode;
bool can_do_signed = false;
int doing_unsigned = unsignedp;
if (fmode != GET_MODE (to)
- && significand_size (fmode) < GET_MODE_PRECISION (GET_MODE (from)))
+ && (significand_size (fmode)
+ < GET_MODE_UNIT_PRECISION (GET_MODE (from))))
continue;
icode = can_float_p (fmode, imode, unsignedp);
/* Unsigned integer, and no way to convert directly. Convert as signed,
then unconditionally adjust the result. */
- if (unsignedp && can_do_signed)
+ if (unsignedp
+ && can_do_signed
+ && is_a <scalar_mode> (GET_MODE (to), &to_mode)
+ && is_a <scalar_mode> (GET_MODE (from), &from_mode))
{
+ opt_scalar_mode fmode_iter;
rtx_code_label *label = gen_label_rtx ();
rtx temp;
REAL_VALUE_TYPE offset;
least as wide as the target. Using FMODE will avoid rounding woes
with unsigned values greater than the signed maximum value. */
- FOR_EACH_MODE_FROM (fmode, GET_MODE (to))
- if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_BITSIZE (fmode)
- && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing)
- break;
+ FOR_EACH_MODE_FROM (fmode_iter, to_mode)
+ {
+ scalar_mode fmode = fmode_iter.require ();
+ if (GET_MODE_PRECISION (from_mode) < GET_MODE_BITSIZE (fmode)
+ && can_float_p (fmode, from_mode, 0) != CODE_FOR_nothing)
+ break;
+ }
- if (fmode == VOIDmode)
+ if (!fmode_iter.exists (&fmode))
{
/* There is no such mode. Pretend the target is wide enough. */
- fmode = GET_MODE (to);
+ fmode = to_mode;
/* Avoid double-rounding when TO is narrower than FROM. */
if ((significand_size (fmode) + 1)
- < GET_MODE_PRECISION (GET_MODE (from)))
+ < GET_MODE_PRECISION (from_mode))
{
rtx temp1;
rtx_code_label *neglabel = gen_label_rtx ();
|| GET_MODE (target) != fmode)
target = gen_reg_rtx (fmode);
- imode = GET_MODE (from);
+ imode = from_mode;
do_pending_stack_adjust ();
/* Test whether the sign bit is set. */
/* If we are about to do some arithmetic to correct for an
unsigned operand, do it in a pseudo-register. */
- if (GET_MODE (to) != fmode
+ if (to_mode != fmode
|| !REG_P (to) || REGNO (to) < FIRST_PSEUDO_REGISTER)
target = gen_reg_rtx (fmode);
correct its value by 2**bitwidth. */
do_pending_stack_adjust ();
- emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from),
+ emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, from_mode,
0, label);
- real_2expN (&offset, GET_MODE_PRECISION (GET_MODE (from)), fmode);
+ real_2expN (&offset, GET_MODE_PRECISION (from_mode), fmode);
temp = expand_binop (fmode, add_optab, target,
const_double_from_real_value (offset, fmode),
target, 0, OPTAB_LIB_WIDEN);
rtx value;
convert_optab tab = unsignedp ? ufloat_optab : sfloat_optab;
- if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_PRECISION (SImode))
+ if (is_narrower_int_mode (GET_MODE (from), SImode))
from = convert_to_mode (SImode, from, unsignedp);
libfunc = convert_optab_libfunc (tab, GET_MODE (to), GET_MODE (from));
start_sequence ();
value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
- GET_MODE (to), 1, from,
- GET_MODE (from));
+ GET_MODE (to), from, GET_MODE (from));
insns = get_insns ();
end_sequence ();
enum insn_code icode;
rtx target = to;
machine_mode fmode, imode;
+ opt_scalar_mode fmode_iter;
bool must_trunc = false;
/* We first try to find a pair of modes, one real and one integer, at
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_PRECISION (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT)
- FOR_EACH_MODE_FROM (fmode, GET_MODE (from))
- 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_PRECISION (GET_MODE (to))))
- {
- int bitsize;
- REAL_VALUE_TYPE offset;
- rtx limit;
- rtx_code_label *lab1, *lab2;
- rtx_insn *insn;
-
- 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 ();
- lab2 = gen_label_rtx ();
-
- if (fmode != GET_MODE (from))
- from = convert_to_mode (fmode, from, 0);
-
- /* See if we need to do the subtraction. */
- do_pending_stack_adjust ();
- emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, GET_MODE (from),
- 0, lab1);
-
- /* If not, do the signed "fix" and branch around fixup code. */
- expand_fix (to, from, 0);
- emit_jump_insn (targetm.gen_jump (lab2));
- emit_barrier ();
-
- /* Otherwise, subtract 2**(N-1), convert to signed number,
- then add 2**(N-1). Do the addition using XOR since this
- will often generate better code. */
- emit_label (lab1);
- target = expand_binop (GET_MODE (from), sub_optab, from, limit,
- NULL_RTX, 0, OPTAB_LIB_WIDEN);
- expand_fix (to, target, 0);
- target = expand_binop (GET_MODE (to), xor_optab, to,
- gen_int_mode
- (HOST_WIDE_INT_1 << (bitsize - 1),
- GET_MODE (to)),
- to, 1, OPTAB_LIB_WIDEN);
-
- if (target != to)
- emit_move_insn (to, target);
-
- emit_label (lab2);
-
- if (optab_handler (mov_optab, GET_MODE (to)) != CODE_FOR_nothing)
- {
- /* Make a place for a REG_NOTE and add it. */
- insn = emit_move_insn (to, to);
- set_dst_reg_note (insn, REG_EQUAL,
- gen_rtx_fmt_e (UNSIGNED_FIX, GET_MODE (to),
- copy_rtx (from)),
- to);
- }
+ scalar_int_mode to_mode;
+ if (unsignedp
+ && is_a <scalar_int_mode> (GET_MODE (to), &to_mode)
+ && HWI_COMPUTABLE_MODE_P (to_mode))
+ FOR_EACH_MODE_FROM (fmode_iter, as_a <scalar_mode> (GET_MODE (from)))
+ {
+ scalar_mode fmode = fmode_iter.require ();
+ if (CODE_FOR_nothing != can_fix_p (to_mode, fmode,
+ 0, &must_trunc)
+ && (!DECIMAL_FLOAT_MODE_P (fmode)
+ || (GET_MODE_BITSIZE (fmode) > GET_MODE_PRECISION (to_mode))))
+ {
+ int bitsize;
+ REAL_VALUE_TYPE offset;
+ rtx limit;
+ rtx_code_label *lab1, *lab2;
+ rtx_insn *insn;
+
+ bitsize = GET_MODE_PRECISION (to_mode);
+ real_2expN (&offset, bitsize - 1, fmode);
+ limit = const_double_from_real_value (offset, fmode);
+ lab1 = gen_label_rtx ();
+ lab2 = gen_label_rtx ();
- return;
- }
+ if (fmode != GET_MODE (from))
+ from = convert_to_mode (fmode, from, 0);
+
+ /* See if we need to do the subtraction. */
+ do_pending_stack_adjust ();
+ emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX,
+ GET_MODE (from), 0, lab1);
+
+ /* If not, do the signed "fix" and branch around fixup code. */
+ expand_fix (to, from, 0);
+ emit_jump_insn (targetm.gen_jump (lab2));
+ emit_barrier ();
+
+ /* Otherwise, subtract 2**(N-1), convert to signed number,
+ then add 2**(N-1). Do the addition using XOR since this
+ will often generate better code. */
+ emit_label (lab1);
+ target = expand_binop (GET_MODE (from), sub_optab, from, limit,
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ expand_fix (to, target, 0);
+ target = expand_binop (to_mode, xor_optab, to,
+ gen_int_mode
+ (HOST_WIDE_INT_1 << (bitsize - 1),
+ to_mode),
+ to, 1, OPTAB_LIB_WIDEN);
+
+ if (target != to)
+ emit_move_insn (to, target);
+
+ emit_label (lab2);
+
+ if (optab_handler (mov_optab, to_mode) != CODE_FOR_nothing)
+ {
+ /* Make a place for a REG_NOTE and add it. */
+ insn = emit_move_insn (to, to);
+ set_dst_reg_note (insn, REG_EQUAL,
+ gen_rtx_fmt_e (UNSIGNED_FIX, to_mode,
+ copy_rtx (from)),
+ to);
+ }
+
+ return;
+ }
+ }
/* We can't do it with an insn, so use a library call. But first ensure
that the mode of TO is at least as wide as SImode, since those are the
only library calls we know about. */
- if (GET_MODE_PRECISION (GET_MODE (to)) < GET_MODE_PRECISION (SImode))
+ if (is_narrower_int_mode (GET_MODE (to), SImode))
{
target = gen_reg_rtx (SImode);
start_sequence ();
value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
- GET_MODE (to), 1, from,
- GET_MODE (from));
+ GET_MODE (to), from, GET_MODE (from));
insns = get_insns ();
end_sequence ();
start_sequence ();
value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, to_mode,
- 1, from, from_mode);
+ from, from_mode);
insns = get_insns ();
end_sequence ();
return gen_rtx_fmt_ee (rcode, cmp_mode, ops[0].value, ops[1].value);
}
-/* Checks if vec_perm mask SEL is a constant equivalent to a shift of the first
- vec_perm operand, assuming the second operand is a constant vector of zeroes.
- Return the shift distance in bits if so, or NULL_RTX if the vec_perm is not a
- shift. */
+/* Check if vec_perm mask SEL is a constant equivalent to a shift of
+ the first vec_perm operand, assuming the second operand is a constant
+ vector of zeros. Return the shift distance in bits if so, or NULL_RTX
+ if the vec_perm is not a shift. MODE is the mode of the value being
+ shifted. */
static rtx
-shift_amt_for_vec_perm_mask (rtx sel)
+shift_amt_for_vec_perm_mask (machine_mode mode, const vec_perm_indices &sel)
{
- unsigned int i, first, nelt = GET_MODE_NUNITS (GET_MODE (sel));
- unsigned int bitsize = GET_MODE_UNIT_BITSIZE (GET_MODE (sel));
-
- if (GET_CODE (sel) != CONST_VECTOR)
+ unsigned int bitsize = GET_MODE_UNIT_BITSIZE (mode);
+ poly_int64 first = sel[0];
+ if (maybe_ge (sel[0], GET_MODE_NUNITS (mode)))
return NULL_RTX;
- first = INTVAL (CONST_VECTOR_ELT (sel, 0));
- if (first >= nelt)
- return NULL_RTX;
- for (i = 1; i < nelt; i++)
+ if (!sel.series_p (0, 1, first, 1))
{
- int idx = INTVAL (CONST_VECTOR_ELT (sel, i));
- unsigned int expected = i + first;
- /* Indices into the second vector are all equivalent. */
- if (idx < 0 || (MIN (nelt, (unsigned) idx) != MIN (nelt, expected)))
+ unsigned int nelt;
+ if (!GET_MODE_NUNITS (mode).is_constant (&nelt))
return NULL_RTX;
+ for (unsigned int i = 1; i < nelt; i++)
+ {
+ poly_int64 expected = i + first;
+ /* Indices into the second vector are all equivalent. */
+ if (maybe_lt (sel[i], nelt)
+ ? maybe_ne (sel[i], expected)
+ : maybe_lt (expected, nelt))
+ return NULL_RTX;
+ }
}
- return GEN_INT (first * bitsize);
+ return gen_int_shift_amount (mode, first * bitsize);
}
-/* A subroutine of expand_vec_perm for expanding one vec_perm insn. */
+/* A subroutine of expand_vec_perm_var for expanding one vec_perm insn. */
static rtx
expand_vec_perm_1 (enum insn_code icode, rtx target,
machine_mode smode = GET_MODE (sel);
struct expand_operand ops[4];
+ gcc_assert (GET_MODE_CLASS (smode) == MODE_VECTOR_INT
+ || mode_for_int_vector (tmode).require () == smode);
create_output_operand (&ops[0], target, tmode);
create_input_operand (&ops[3], sel, smode);
return NULL_RTX;
}
-/* Generate instructions for vec_perm optab given its mode
- and three operands. */
+/* Implement a permutation of vectors v0 and v1 using the permutation
+ vector in SEL and return the result. Use TARGET to hold the result
+ if nonnull and convenient.
+
+ MODE is the mode of the vectors being permuted (V0 and V1). SEL_MODE
+ is the TYPE_MODE associated with SEL, or BLKmode if SEL isn't known
+ to have a particular mode. */
rtx
-expand_vec_perm (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
+expand_vec_perm_const (machine_mode mode, rtx v0, rtx v1,
+ const vec_perm_builder &sel, machine_mode sel_mode,
+ rtx target)
{
- enum insn_code icode;
- machine_mode qimode;
- unsigned int i, w, e, u;
- rtx tmp, sel_qi = NULL;
- rtvec vec;
-
- if (!target || GET_MODE (target) != mode)
+ if (!target || !register_operand (target, mode))
target = gen_reg_rtx (mode);
- w = GET_MODE_SIZE (mode);
- e = GET_MODE_NUNITS (mode);
- u = GET_MODE_UNIT_SIZE (mode);
-
/* Set QIMODE to a different vector mode with byte elements.
If no such mode, or if MODE already has byte elements, use VOIDmode. */
- qimode = VOIDmode;
- if (GET_MODE_INNER (mode) != QImode)
- {
- qimode = mode_for_vector (QImode, w);
- if (!VECTOR_MODE_P (qimode))
- qimode = VOIDmode;
- }
-
- /* If the input is a constant, expand it specially. */
- gcc_assert (GET_MODE_CLASS (GET_MODE (sel)) == MODE_VECTOR_INT);
- if (GET_CODE (sel) == CONST_VECTOR)
- {
- /* See if this can be handled with a vec_shr. We only do this if the
- second vector is all zeroes. */
- enum insn_code shift_code = optab_handler (vec_shr_optab, mode);
- enum insn_code shift_code_qi = ((qimode != VOIDmode && qimode != mode)
- ? optab_handler (vec_shr_optab, qimode)
- : CODE_FOR_nothing);
- rtx shift_amt = NULL_RTX;
- if (v1 == CONST0_RTX (GET_MODE (v1))
- && (shift_code != CODE_FOR_nothing
- || shift_code_qi != CODE_FOR_nothing))
+ machine_mode qimode;
+ if (!qimode_for_vec_perm (mode).exists (&qimode))
+ qimode = VOIDmode;
+
+ rtx_insn *last = get_last_insn ();
+
+ bool single_arg_p = rtx_equal_p (v0, v1);
+ /* Always specify two input vectors here and leave the target to handle
+ cases in which the inputs are equal. Not all backends can cope with
+ the single-input representation when testing for a double-input
+ target instruction. */
+ vec_perm_indices indices (sel, 2, GET_MODE_NUNITS (mode));
+
+ /* See if this can be handled with a vec_shr. We only do this if the
+ second vector is all zeroes. */
+ insn_code shift_code = optab_handler (vec_shr_optab, mode);
+ insn_code shift_code_qi = ((qimode != VOIDmode && qimode != mode)
+ ? optab_handler (vec_shr_optab, qimode)
+ : CODE_FOR_nothing);
+
+ if (v1 == CONST0_RTX (GET_MODE (v1))
+ && (shift_code != CODE_FOR_nothing
+ || shift_code_qi != CODE_FOR_nothing))
+ {
+ rtx shift_amt = shift_amt_for_vec_perm_mask (mode, indices);
+ if (shift_amt)
{
- shift_amt = shift_amt_for_vec_perm_mask (sel);
- if (shift_amt)
+ struct expand_operand ops[3];
+ if (shift_code != CODE_FOR_nothing)
{
- struct expand_operand ops[3];
- if (shift_code != CODE_FOR_nothing)
- {
- create_output_operand (&ops[0], target, mode);
- create_input_operand (&ops[1], v0, mode);
- create_convert_operand_from_type (&ops[2], shift_amt,
- sizetype);
- if (maybe_expand_insn (shift_code, 3, ops))
- return ops[0].value;
- }
- if (shift_code_qi != CODE_FOR_nothing)
- {
- tmp = gen_reg_rtx (qimode);
- create_output_operand (&ops[0], tmp, qimode);
- create_input_operand (&ops[1], gen_lowpart (qimode, v0),
- qimode);
- create_convert_operand_from_type (&ops[2], shift_amt,
- sizetype);
- if (maybe_expand_insn (shift_code_qi, 3, ops))
- return gen_lowpart (mode, ops[0].value);
- }
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], v0, mode);
+ create_convert_operand_from_type (&ops[2], shift_amt, sizetype);
+ if (maybe_expand_insn (shift_code, 3, ops))
+ return ops[0].value;
+ }
+ if (shift_code_qi != CODE_FOR_nothing)
+ {
+ rtx tmp = gen_reg_rtx (qimode);
+ create_output_operand (&ops[0], tmp, qimode);
+ create_input_operand (&ops[1], gen_lowpart (qimode, v0), qimode);
+ create_convert_operand_from_type (&ops[2], shift_amt, sizetype);
+ if (maybe_expand_insn (shift_code_qi, 3, ops))
+ return gen_lowpart (mode, ops[0].value);
}
}
+ }
+
+ if (targetm.vectorize.vec_perm_const != NULL)
+ {
+ v0 = force_reg (mode, v0);
+ if (single_arg_p)
+ v1 = v0;
+ else
+ v1 = force_reg (mode, v1);
+
+ if (targetm.vectorize.vec_perm_const (mode, target, v0, v1, indices))
+ return target;
+ }
+
+ /* Fall back to a constant byte-based permutation. */
+ vec_perm_indices qimode_indices;
+ rtx target_qi = NULL_RTX, v0_qi = NULL_RTX, v1_qi = NULL_RTX;
+ if (qimode != VOIDmode)
+ {
+ qimode_indices.new_expanded_vector (indices, GET_MODE_UNIT_SIZE (mode));
+ target_qi = gen_reg_rtx (qimode);
+ v0_qi = gen_lowpart (qimode, v0);
+ v1_qi = gen_lowpart (qimode, v1);
+ if (targetm.vectorize.vec_perm_const != NULL
+ && targetm.vectorize.vec_perm_const (qimode, target_qi, v0_qi,
+ v1_qi, qimode_indices))
+ return gen_lowpart (mode, target_qi);
+ }
+
+ /* Otherwise expand as a fully variable permuation. */
+
+ /* The optabs are only defined for selectors with the same width
+ as the values being permuted. */
+ machine_mode required_sel_mode;
+ if (!mode_for_int_vector (mode).exists (&required_sel_mode)
+ || !VECTOR_MODE_P (required_sel_mode))
+ {
+ delete_insns_since (last);
+ return NULL_RTX;
+ }
+
+ /* We know that it is semantically valid to treat SEL as having SEL_MODE.
+ If that isn't the mode we want then we need to prove that using
+ REQUIRED_SEL_MODE is OK. */
+ if (sel_mode != required_sel_mode)
+ {
+ if (!selector_fits_mode_p (required_sel_mode, indices))
+ {
+ delete_insns_since (last);
+ return NULL_RTX;
+ }
+ sel_mode = required_sel_mode;
+ }
- icode = direct_optab_handler (vec_perm_const_optab, mode);
+ insn_code icode = direct_optab_handler (vec_perm_optab, mode);
+ if (icode != CODE_FOR_nothing)
+ {
+ rtx sel_rtx = vec_perm_indices_to_rtx (sel_mode, indices);
+ rtx tmp = expand_vec_perm_1 (icode, target, v0, v1, sel_rtx);
+ if (tmp)
+ return tmp;
+ }
+
+ if (qimode != VOIDmode
+ && selector_fits_mode_p (qimode, qimode_indices))
+ {
+ icode = direct_optab_handler (vec_perm_optab, qimode);
if (icode != CODE_FOR_nothing)
{
- tmp = expand_vec_perm_1 (icode, target, v0, v1, sel);
+ rtx sel_qi = vec_perm_indices_to_rtx (qimode, qimode_indices);
+ rtx tmp = expand_vec_perm_1 (icode, target_qi, v0_qi, v1_qi, sel_qi);
if (tmp)
- return tmp;
+ return gen_lowpart (mode, tmp);
}
+ }
- /* Fall back to a constant byte-based permutation. */
- if (qimode != VOIDmode)
- {
- vec = rtvec_alloc (w);
- for (i = 0; i < e; ++i)
- {
- unsigned int j, this_e;
+ delete_insns_since (last);
+ return NULL_RTX;
+}
- this_e = INTVAL (CONST_VECTOR_ELT (sel, i));
- this_e &= 2 * e - 1;
- this_e *= u;
+/* Implement a permutation of vectors v0 and v1 using the permutation
+ vector in SEL and return the result. Use TARGET to hold the result
+ if nonnull and convenient.
- for (j = 0; j < u; ++j)
- RTVEC_ELT (vec, i * u + j) = GEN_INT (this_e + j);
- }
- sel_qi = gen_rtx_CONST_VECTOR (qimode, vec);
+ MODE is the mode of the vectors being permuted (V0 and V1).
+ SEL must have the integer equivalent of MODE and is known to be
+ unsuitable for permutes with a constant permutation vector. */
- icode = direct_optab_handler (vec_perm_const_optab, qimode);
- if (icode != CODE_FOR_nothing)
- {
- tmp = mode != qimode ? gen_reg_rtx (qimode) : target;
- tmp = expand_vec_perm_1 (icode, tmp, gen_lowpart (qimode, v0),
- gen_lowpart (qimode, v1), sel_qi);
- if (tmp)
- return gen_lowpart (mode, tmp);
- }
- }
- }
+rtx
+expand_vec_perm_var (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
+{
+ enum insn_code icode;
+ unsigned int i, u;
+ rtx tmp, sel_qi;
+
+ u = GET_MODE_UNIT_SIZE (mode);
+
+ if (!target || GET_MODE (target) != mode)
+ target = gen_reg_rtx (mode);
- /* Otherwise expand as a fully variable permuation. */
icode = direct_optab_handler (vec_perm_optab, mode);
if (icode != CODE_FOR_nothing)
{
/* As a special case to aid several targets, lower the element-based
permutation to a byte-based permutation and try again. */
- if (qimode == VOIDmode)
+ machine_mode qimode;
+ if (!qimode_for_vec_perm (mode).exists (&qimode)
+ || maybe_gt (GET_MODE_NUNITS (qimode), GET_MODE_MASK (QImode) + 1))
return NULL_RTX;
icode = direct_optab_handler (vec_perm_optab, qimode);
if (icode == CODE_FOR_nothing)
return NULL_RTX;
- if (sel_qi == NULL)
- {
- /* Multiply each element by its byte size. */
- machine_mode selmode = GET_MODE (sel);
- if (u == 2)
- sel = expand_simple_binop (selmode, PLUS, sel, sel,
- NULL, 0, OPTAB_DIRECT);
- else
- sel = expand_simple_binop (selmode, ASHIFT, sel,
- GEN_INT (exact_log2 (u)),
- NULL, 0, OPTAB_DIRECT);
- gcc_assert (sel != NULL);
-
- /* Broadcast the low byte each element into each of its bytes. */
- vec = rtvec_alloc (w);
- for (i = 0; i < w; ++i)
- {
- int this_e = i / u * u;
- if (BYTES_BIG_ENDIAN)
- this_e += u - 1;
- RTVEC_ELT (vec, i) = GEN_INT (this_e);
- }
- tmp = gen_rtx_CONST_VECTOR (qimode, vec);
- sel = gen_lowpart (qimode, sel);
- sel = expand_vec_perm (qimode, sel, sel, tmp, NULL);
- gcc_assert (sel != NULL);
-
- /* Add the byte offset to each byte element. */
- /* Note that the definition of the indicies here is memory ordering,
- so there should be no difference between big and little endian. */
- vec = rtvec_alloc (w);
- for (i = 0; i < w; ++i)
- RTVEC_ELT (vec, i) = GEN_INT (i % u);
- tmp = gen_rtx_CONST_VECTOR (qimode, vec);
- sel_qi = expand_simple_binop (qimode, PLUS, sel, tmp,
- sel, 0, OPTAB_DIRECT);
- gcc_assert (sel_qi != NULL);
- }
+ /* Multiply each element by its byte size. */
+ machine_mode selmode = GET_MODE (sel);
+ if (u == 2)
+ sel = expand_simple_binop (selmode, PLUS, sel, sel,
+ NULL, 0, OPTAB_DIRECT);
+ else
+ sel = expand_simple_binop (selmode, ASHIFT, sel,
+ gen_int_shift_amount (selmode, exact_log2 (u)),
+ NULL, 0, OPTAB_DIRECT);
+ gcc_assert (sel != NULL);
+
+ /* Broadcast the low byte each element into each of its bytes.
+ The encoding has U interleaved stepped patterns, one for each
+ byte of an element. */
+ vec_perm_builder const_sel (GET_MODE_SIZE (mode), u, 3);
+ unsigned int low_byte_in_u = BYTES_BIG_ENDIAN ? u - 1 : 0;
+ for (i = 0; i < 3; ++i)
+ for (unsigned int j = 0; j < u; ++j)
+ const_sel.quick_push (i * u + low_byte_in_u);
+ sel = gen_lowpart (qimode, sel);
+ sel = expand_vec_perm_const (qimode, sel, sel, const_sel, qimode, NULL);
+ gcc_assert (sel != NULL);
+
+ /* Add the byte offset to each byte element. */
+ /* Note that the definition of the indicies here is memory ordering,
+ so there should be no difference between big and little endian. */
+ rtx_vector_builder byte_indices (qimode, u, 1);
+ for (i = 0; i < u; ++i)
+ byte_indices.quick_push (GEN_INT (i));
+ tmp = byte_indices.build ();
+ sel_qi = expand_simple_binop (qimode, PLUS, sel, tmp,
+ sel, 0, OPTAB_DIRECT);
+ gcc_assert (sel_qi != NULL);
tmp = mode != qimode ? gen_reg_rtx (qimode) : target;
tmp = expand_vec_perm_1 (icode, tmp, gen_lowpart (qimode, v0),
unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a));
- gcc_assert (GET_MODE_SIZE (mode) == GET_MODE_SIZE (cmp_op_mode)
- && GET_MODE_NUNITS (mode) == GET_MODE_NUNITS (cmp_op_mode));
+ gcc_assert (known_eq (GET_MODE_SIZE (mode), GET_MODE_SIZE (cmp_op_mode))
+ && known_eq (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 ops[0].value;
}
+/* Generate VEC_SERIES_EXPR <OP0, OP1>, returning a value of mode VMODE.
+ Use TARGET for the result if nonnull and convenient. */
+
+rtx
+expand_vec_series_expr (machine_mode vmode, rtx op0, rtx op1, rtx target)
+{
+ struct expand_operand ops[3];
+ enum insn_code icode;
+ machine_mode emode = GET_MODE_INNER (vmode);
+
+ icode = direct_optab_handler (vec_series_optab, vmode);
+ gcc_assert (icode != CODE_FOR_nothing);
+
+ create_output_operand (&ops[0], target, vmode);
+ create_input_operand (&ops[1], op0, emode);
+ create_input_operand (&ops[2], op1, emode);
+
+ expand_insn (icode, 3, ops);
+ return ops[0].value;
+}
+
/* Generate insns for a vector comparison into a mask. */
rtx
{
struct expand_operand eops[3];
enum insn_code icode;
- int method, i, nunits;
+ int method, i;
machine_mode wmode;
- rtx m1, m2, perm;
+ rtx m1, m2;
optab tab1, tab2;
- rtvec v;
method = can_mult_highpart_p (mode, uns_p);
switch (method)
}
icode = optab_handler (tab1, mode);
- nunits = GET_MODE_NUNITS (mode);
wmode = insn_data[icode].operand[0].mode;
- gcc_checking_assert (2 * GET_MODE_NUNITS (wmode) == nunits);
- gcc_checking_assert (GET_MODE_SIZE (wmode) == GET_MODE_SIZE (mode));
+ gcc_checking_assert (known_eq (2 * GET_MODE_NUNITS (wmode),
+ GET_MODE_NUNITS (mode)));
+ gcc_checking_assert (known_eq (GET_MODE_SIZE (wmode), GET_MODE_SIZE (mode)));
create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode);
create_input_operand (&eops[1], op0, mode);
expand_insn (optab_handler (tab2, mode), 3, eops);
m2 = gen_lowpart (mode, eops[0].value);
- v = rtvec_alloc (nunits);
+ vec_perm_builder sel;
if (method == 2)
{
- for (i = 0; i < nunits; ++i)
- RTVEC_ELT (v, i) = GEN_INT (!BYTES_BIG_ENDIAN + (i & ~1)
- + ((i & 1) ? nunits : 0));
+ /* The encoding has 2 interleaved stepped patterns. */
+ sel.new_vector (GET_MODE_NUNITS (mode), 2, 3);
+ for (i = 0; i < 6; ++i)
+ sel.quick_push (!BYTES_BIG_ENDIAN + (i & ~1)
+ + ((i & 1) ? GET_MODE_NUNITS (mode) : 0));
}
else
{
- for (i = 0; i < nunits; ++i)
- RTVEC_ELT (v, i) = GEN_INT (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1));
+ /* The encoding has a single interleaved stepped pattern. */
+ sel.new_vector (GET_MODE_NUNITS (mode), 1, 3);
+ for (i = 0; i < 3; ++i)
+ sel.quick_push (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1));
}
- perm = gen_rtx_CONST_VECTOR (mode, v);
- return expand_vec_perm (mode, m1, m2, perm, target);
+ return expand_vec_perm_const (mode, m1, m2, sel, BLKmode, target);
}
\f
/* Helper function to find the MODE_CC set in a sync_compare_and_swap
addr = convert_memory_address (ptr_mode, XEXP (mem, 0));
return emit_library_call_value (libfunc, NULL_RTX, LCT_NORMAL,
- mode, 2, addr, ptr_mode,
+ mode, addr, ptr_mode,
val, mode);
}
}
{
rtx addr = convert_memory_address (ptr_mode, XEXP (mem, 0));
rtx target = emit_library_call_value (libfunc, NULL_RTX, LCT_NORMAL,
- mode, 3, addr, ptr_mode,
+ mode, addr, ptr_mode,
expected, mode, desired, mode);
emit_move_insn (target_oval, target);
return true;
}
-/* Generate asm volatile("" : : : "memory") as the memory barrier. */
+/* Generate asm volatile("" : : : "memory") as the memory blockage. */
static void
-expand_asm_memory_barrier (void)
+expand_asm_memory_blockage (void)
{
rtx asm_op, clob;
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, asm_op, clob)));
}
+/* Do not propagate memory accesses across this point. */
+
+static void
+expand_memory_blockage (void)
+{
+ if (targetm.have_memory_blockage ())
+ emit_insn (targetm.gen_memory_blockage ());
+ else
+ expand_asm_memory_blockage ();
+}
+
/* This routine will either emit the mem_thread_fence pattern or issue a
sync_synchronize to generate a fence for memory model MEMMODEL. */
if (targetm.have_mem_thread_fence ())
{
emit_insn (targetm.gen_mem_thread_fence (GEN_INT (model)));
- expand_asm_memory_barrier ();
+ expand_memory_blockage ();
}
else if (targetm.have_memory_barrier ())
emit_insn (targetm.gen_memory_barrier ());
else if (synchronize_libfunc != NULL_RTX)
- emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode, 0);
+ emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode);
else
- expand_asm_memory_barrier ();
+ expand_memory_blockage ();
}
-/* This routine will either emit the mem_signal_fence pattern or issue a
- sync_synchronize to generate a fence for memory model MEMMODEL. */
+/* Emit a signal fence with given memory model. */
void
expand_mem_signal_fence (enum memmodel model)
{
- if (targetm.have_mem_signal_fence ())
- emit_insn (targetm.gen_mem_signal_fence (GEN_INT (model)));
- else if (!is_mm_relaxed (model))
- {
- /* By default targets are coherent between a thread and the signal
- handler running on the same thread. Thus this really becomes a
- compiler barrier, in that stores must not be sunk past
- (or raised above) a given point. */
- expand_asm_memory_barrier ();
- }
+ /* No machine barrier is required to implement a signal fence, but
+ a compiler memory barrier must be issued, except for relaxed MM. */
+ if (!is_mm_relaxed (model))
+ expand_memory_blockage ();
}
/* This function expands the atomic load operation:
if (icode != CODE_FOR_nothing)
{
struct expand_operand ops[3];
+ rtx_insn *last = get_last_insn ();
+ if (is_mm_seq_cst (model))
+ expand_memory_blockage ();
create_output_operand (&ops[0], target, mode);
create_fixed_operand (&ops[1], mem);
create_integer_operand (&ops[2], model);
if (maybe_expand_insn (icode, 3, ops))
- return ops[0].value;
+ {
+ if (!is_mm_relaxed (model))
+ expand_memory_blockage ();
+ return ops[0].value;
+ }
+ delete_insns_since (last);
}
/* If the size of the object is greater than word size on this target,
emulate a load with a compare-and-swap operation, but the store that
doing this could result in would be incorrect if this is a volatile
atomic load or targetting read-only-mapped memory. */
- if (GET_MODE_PRECISION (mode) > BITS_PER_WORD)
+ if (maybe_gt (GET_MODE_PRECISION (mode), BITS_PER_WORD))
/* If there is no atomic load, leave the library call. */
return NULL_RTX;
icode = direct_optab_handler (atomic_store_optab, mode);
if (icode != CODE_FOR_nothing)
{
+ rtx_insn *last = get_last_insn ();
+ if (!is_mm_relaxed (model))
+ expand_memory_blockage ();
create_fixed_operand (&ops[0], mem);
create_input_operand (&ops[1], val, mode);
create_integer_operand (&ops[2], model);
if (maybe_expand_insn (icode, 3, ops))
- return const0_rtx;
+ {
+ if (is_mm_seq_cst (model))
+ expand_memory_blockage ();
+ return const0_rtx;
+ }
+ delete_insns_since (last);
}
/* If using __sync_lock_release is a viable alternative, try it.
/* If the size of the object is greater than word size on this target,
a default store will not be atomic. */
- if (GET_MODE_PRECISION (mode) > BITS_PER_WORD)
+ if (maybe_gt (GET_MODE_PRECISION (mode), BITS_PER_WORD))
{
/* If loads are atomic or we are called to provide a __sync builtin,
we can try a atomic_exchange and throw away the result. Otherwise,
{
rtx addr = convert_memory_address (ptr_mode, XEXP (mem, 0));
result = emit_library_call_value (libfunc, NULL, LCT_NORMAL, mode,
- 2, addr, ptr_mode, val, mode);
+ addr, ptr_mode, val, mode);
if (!unused_result && fixup)
result = expand_simple_binop (mode, code, result, val, target,
valid_multiword_target_p (rtx target)
{
machine_mode mode;
- int i;
+ int i, size;
mode = GET_MODE (target);
- for (i = 0; i < GET_MODE_SIZE (mode); i += UNITS_PER_WORD)
+ if (!GET_MODE_SIZE (mode).is_constant (&size))
+ return false;
+ for (i = 0; i < size; i += UNITS_PER_WORD)
if (!validate_subreg (word_mode, mode, target, i))
return false;
return true;
}
+/* Make OP describe an input operand that has value INTVAL and that has
+ no inherent mode. This function should only be used for operands that
+ are always expand-time constants. The backend may request that INTVAL
+ be copied into a different kind of rtx, but it must specify the mode
+ of that rtx if so. */
+
+void
+create_integer_operand (struct expand_operand *op, poly_int64 intval)
+{
+ create_expand_operand (op, EXPAND_INTEGER,
+ gen_int_mode (intval, MAX_MODE_INT),
+ VOIDmode, false, intval);
+}
+
/* Like maybe_legitimize_operand, but do not change the code of the
current rtx value. */
goto input;
case EXPAND_ADDRESS:
- gcc_assert (mode != VOIDmode);
- op->value = convert_memory_address (mode, op->value);
+ op->value = convert_memory_address (as_a <scalar_int_mode> (mode),
+ op->value);
goto input;
case EXPAND_INTEGER:
mode = insn_data[(int) icode].operand[opno].mode;
- if (mode != VOIDmode && const_int_operand (op->value, mode))
- goto input;
+ if (mode != VOIDmode
+ && known_eq (trunc_int_for_mode (op->int_value, mode),
+ op->int_value))
+ {
+ op->value = gen_int_mode (op->int_value, mode);
+ goto input;
+ }
break;
}
return insn_operand_matches (icode, opno, op->value);
TYPE_UNSIGNED (type));
}
+/* Return true if the requirements on operands OP1 and OP2 of instruction
+ ICODE are similar enough for the result of legitimizing OP1 to be
+ reusable for OP2. OPNO1 and OPNO2 are the operand numbers associated
+ with OP1 and OP2 respectively. */
+
+static inline bool
+can_reuse_operands_p (enum insn_code icode,
+ unsigned int opno1, unsigned int opno2,
+ const struct expand_operand *op1,
+ const struct expand_operand *op2)
+{
+ /* Check requirements that are common to all types. */
+ if (op1->type != op2->type
+ || op1->mode != op2->mode
+ || (insn_data[(int) icode].operand[opno1].mode
+ != insn_data[(int) icode].operand[opno2].mode))
+ return false;
+
+ /* Check the requirements for specific types. */
+ switch (op1->type)
+ {
+ case EXPAND_OUTPUT:
+ /* Outputs must remain distinct. */
+ return false;
+
+ case EXPAND_FIXED:
+ case EXPAND_INPUT:
+ case EXPAND_ADDRESS:
+ case EXPAND_INTEGER:
+ return true;
+
+ case EXPAND_CONVERT_TO:
+ case EXPAND_CONVERT_FROM:
+ return op1->unsigned_p == op2->unsigned_p;
+ }
+ gcc_unreachable ();
+}
+
/* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS)
of instruction ICODE. Return true on success, leaving the new operand
values in the OPS themselves. Emit no code on failure. */
maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
unsigned int nops, struct expand_operand *ops)
{
- rtx_insn *last;
- unsigned int i;
+ rtx_insn *last = get_last_insn ();
+ rtx *orig_values = XALLOCAVEC (rtx, nops);
+ for (unsigned int i = 0; i < nops; i++)
+ {
+ orig_values[i] = ops[i].value;
+
+ /* First try reusing the result of an earlier legitimization.
+ This avoids duplicate rtl and ensures that tied operands
+ remain tied.
+
+ This search is linear, but NOPS is bounded at compile time
+ to a small number (current a single digit). */
+ unsigned int j = 0;
+ for (; j < i; ++j)
+ if (can_reuse_operands_p (icode, opno + j, opno + i, &ops[j], &ops[i])
+ && rtx_equal_p (orig_values[j], orig_values[i])
+ && ops[j].value
+ && insn_operand_matches (icode, opno + i, ops[j].value))
+ {
+ ops[i].value = copy_rtx (ops[j].value);
+ break;
+ }
- last = get_last_insn ();
- for (i = 0; i < nops; i++)
- if (!maybe_legitimize_operand (icode, opno + i, &ops[i]))
- {
- delete_insns_since (last);
- return false;
- }
+ /* Otherwise try legitimizing the operand on its own. */
+ if (j == i && !maybe_legitimize_operand (icode, opno + i, &ops[i]))
+ {
+ delete_insns_since (last);
+ return false;
+ }
+ }
return true;
}