/* 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,
- 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 1987-2015 Free Software Foundation, Inc.
This file is part of GCC.
is properly defined. */
#include "insn-config.h"
#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
#include "tree.h"
+#include "tree-hasher.h"
+#include "stor-layout.h"
+#include "stringpool.h"
+#include "varasm.h"
#include "tm_p.h"
#include "flags.h"
+#include "hard-reg-set.h"
#include "function.h"
#include "except.h"
+#include "hashtab.h"
+#include "statistics.h"
+#include "real.h"
+#include "fixed-value.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "stmt.h"
#include "expr.h"
+#include "insn-codes.h"
#include "optabs.h"
#include "libfuncs.h"
#include "recog.h"
#include "reload.h"
#include "ggc.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
#include "basic-block.h"
#include "target.h"
struct target_optabs default_target_optabs;
struct target_libfuncs default_target_libfuncs;
+struct target_optabs *this_fn_optabs = &default_target_optabs;
#if SWITCHABLE_TARGET
struct target_optabs *this_target_optabs = &default_target_optabs;
struct target_libfuncs *this_target_libfuncs = &default_target_libfuncs;
#define libfunc_hash \
(this_target_libfuncs->x_libfunc_hash)
-/* Contains the optab used for each rtx code. */
-optab code_to_optab[NUM_RTX_CODE + 1];
-
static void prepare_float_lib_cmp (rtx, rtx, enum rtx_code, rtx *,
- enum machine_mode *);
-static rtx expand_unop_direct (enum machine_mode, optab, rtx, rtx, int);
-static void emit_libcall_block_1 (rtx, rtx, rtx, rtx, bool);
+ machine_mode *);
+static rtx expand_unop_direct (machine_mode, optab, rtx, rtx, int);
+static void emit_libcall_block_1 (rtx_insn *, rtx, rtx, rtx, bool);
/* Debug facility for use in GDB. */
void debug_optab_libfuncs (void);
\f
/* Used for libfunc_hash. */
-static hashval_t
-hash_libfunc (const void *p)
+hashval_t
+libfunc_hasher::hash (libfunc_entry *e)
{
- const struct libfunc_entry *const e = (const struct libfunc_entry *) p;
-
- return (((int) e->mode1 + (int) e->mode2 * NUM_MACHINE_MODES)
- ^ e->optab);
+ return ((e->mode1 + e->mode2 * NUM_MACHINE_MODES) ^ e->op);
}
/* Used for libfunc_hash. */
-static int
-eq_libfunc (const void *p, const void *q)
+bool
+libfunc_hasher::equal (libfunc_entry *e1, libfunc_entry *e2)
{
- const struct libfunc_entry *const e1 = (const struct libfunc_entry *) p;
- const struct libfunc_entry *const e2 = (const struct libfunc_entry *) q;
-
- return (e1->optab == e2->optab
- && e1->mode1 == e2->mode1
- && e1->mode2 == e2->mode2);
+ return e1->op == e2->op && e1->mode1 == e2->mode1 && e1->mode2 == e2->mode2;
}
/* Return libfunc corresponding operation defined by OPTAB converting
from MODE2 to MODE1. Trigger lazy initialization if needed, return NULL
if no libfunc is available. */
rtx
-convert_optab_libfunc (convert_optab optab, enum machine_mode mode1,
- enum machine_mode mode2)
+convert_optab_libfunc (convert_optab optab, machine_mode mode1,
+ machine_mode mode2)
{
struct libfunc_entry e;
struct libfunc_entry **slot;
- e.optab = (size_t) (optab - &convert_optab_table[0]);
+ /* ??? This ought to be an assert, but not all of the places
+ that we expand optabs know about the optabs that got moved
+ to being direct. */
+ if (!(optab >= FIRST_CONV_OPTAB && optab <= LAST_CONVLIB_OPTAB))
+ return NULL_RTX;
+
+ e.op = optab;
e.mode1 = mode1;
e.mode2 = mode2;
- slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, NO_INSERT);
+ slot = libfunc_hash->find_slot (&e, NO_INSERT);
if (!slot)
{
- if (optab->libcall_gen)
- {
- optab->libcall_gen (optab, optab->libcall_basename, mode1, mode2);
- slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, NO_INSERT);
- if (slot)
- return (*slot)->libfunc;
- else
- return NULL;
- }
- return NULL;
+ const struct convert_optab_libcall_d *d
+ = &convlib_def[optab - FIRST_CONV_OPTAB];
+
+ if (d->libcall_gen == NULL)
+ return NULL;
+
+ d->libcall_gen (optab, d->libcall_basename, mode1, mode2);
+ slot = libfunc_hash->find_slot (&e, NO_INSERT);
+ if (!slot)
+ return NULL;
}
return (*slot)->libfunc;
}
Trigger lazy initialization if needed, return NULL if no libfunc is
available. */
rtx
-optab_libfunc (optab optab, enum machine_mode mode)
+optab_libfunc (optab optab, machine_mode mode)
{
struct libfunc_entry e;
struct libfunc_entry **slot;
- e.optab = (size_t) (optab - &optab_table[0]);
+ /* ??? This ought to be an assert, but not all of the places
+ that we expand optabs know about the optabs that got moved
+ to being direct. */
+ if (!(optab >= FIRST_NORM_OPTAB && optab <= LAST_NORMLIB_OPTAB))
+ return NULL_RTX;
+
+ e.op = optab;
e.mode1 = mode;
e.mode2 = VOIDmode;
- slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, NO_INSERT);
+ slot = libfunc_hash->find_slot (&e, NO_INSERT);
if (!slot)
{
- if (optab->libcall_gen)
- {
- optab->libcall_gen (optab, optab->libcall_basename,
- optab->libcall_suffix, mode);
- slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash,
- &e, NO_INSERT);
- if (slot)
- return (*slot)->libfunc;
- else
- return NULL;
- }
- return NULL;
+ const struct optab_libcall_d *d
+ = &normlib_def[optab - FIRST_NORM_OPTAB];
+
+ if (d->libcall_gen == NULL)
+ return NULL;
+
+ d->libcall_gen (optab, d->libcall_basename, d->libcall_suffix, mode);
+ slot = libfunc_hash->find_slot (&e, NO_INSERT);
+ if (!slot)
+ return NULL;
}
return (*slot)->libfunc;
}
If the last insn does not set TARGET, don't do anything, but return 1.
- If a previous insn sets TARGET and TARGET is one of OP0 or OP1,
- don't add the REG_EQUAL note but return 0. Our caller can then try
- again, ensuring that TARGET is not one of the operands. */
+ If the last insn or a previous insn sets TARGET and TARGET is one of OP0
+ or OP1, don't add the REG_EQUAL note but return 0. Our caller can then
+ try again, ensuring that TARGET is not one of the operands. */
static int
-add_equal_note (rtx insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
+add_equal_note (rtx_insn *insns, rtx target, enum rtx_code code, rtx op0, rtx op1)
{
- rtx last_insn, insn, set;
+ rtx_insn *last_insn;
+ rtx set;
rtx note;
gcc_assert (insns && INSN_P (insns) && NEXT_INSN (insns));
last_insn = NEXT_INSN (last_insn))
;
- set = single_set (last_insn);
+ /* If TARGET is in OP0 or OP1, punt. We'd end up with a note referencing
+ a value changing in the insn, so the note would be invalid for CSE. */
+ if (reg_overlap_mentioned_p (target, op0)
+ || (op1 && reg_overlap_mentioned_p (target, op1)))
+ {
+ if (MEM_P (target)
+ && (rtx_equal_p (target, op0)
+ || (op1 && rtx_equal_p (target, op1))))
+ {
+ /* For MEM target, with MEM = MEM op X, prefer no REG_EQUAL note
+ over expanding it as temp = MEM op X, MEM = temp. If the target
+ supports MEM = MEM op X instructions, it is sometimes too hard
+ to reconstruct that form later, especially if X is also a memory,
+ and due to multiple occurrences of addresses the address might
+ be forced into register unnecessarily.
+ Note that not emitting the REG_EQUIV note might inhibit
+ CSE in some cases. */
+ set = single_set (last_insn);
+ if (set
+ && GET_CODE (SET_SRC (set)) == code
+ && MEM_P (SET_DEST (set))
+ && (rtx_equal_p (SET_DEST (set), XEXP (SET_SRC (set), 0))
+ || (op1 && rtx_equal_p (SET_DEST (set),
+ XEXP (SET_SRC (set), 1)))))
+ return 1;
+ }
+ return 0;
+ }
+
+ set = set_for_reg_notes (last_insn);
if (set == NULL_RTX)
return 1;
|| ! rtx_equal_p (XEXP (SET_DEST (set), 0), target)))
return 1;
- /* If TARGET is in OP0 or OP1, check if anything in SEQ sets TARGET
- besides the last insn. */
- if (reg_overlap_mentioned_p (target, op0)
- || (op1 && reg_overlap_mentioned_p (target, op1)))
- {
- insn = PREV_INSN (last_insn);
- while (insn != NULL_RTX)
- {
- if (reg_set_p (target, insn))
- return 0;
-
- insn = PREV_INSN (insn);
- }
- }
-
if (GET_RTX_CLASS (code) == RTX_UNARY)
switch (code)
{
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)
+static machine_mode
+widened_mode (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;
+ machine_mode m0 = GET_MODE (op0);
+ machine_mode m1 = GET_MODE (op1);
+ machine_mode result;
if (m0 == VOIDmode && m1 == VOIDmode)
return to_mode;
return result;
}
\f
+/* Like optab_handler, but for widening_operations that have a
+ TO_MODE and a FROM_MODE. */
+
+enum insn_code
+widening_optab_handler (optab op, machine_mode to_mode,
+ machine_mode from_mode)
+{
+ unsigned scode = (op << 16) | to_mode;
+ if (to_mode != from_mode && from_mode != VOIDmode)
+ {
+ /* ??? Why does find_widening_optab_handler_and_mode attempt to
+ widen things that can't be widened? E.g. add_optab... */
+ if (op > LAST_CONV_OPTAB)
+ return CODE_FOR_nothing;
+ scode |= from_mode << 8;
+ }
+ return raw_optab_handler (scode);
+}
+
/* 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.
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,
+find_widening_optab_handler_and_mode (optab op, machine_mode to_mode,
+ machine_mode from_mode,
int permit_non_widening,
- enum machine_mode *found_mode)
+ machine_mode *found_mode)
{
for (; (permit_non_widening || from_mode != to_mode)
&& GET_MODE_SIZE (from_mode) <= GET_MODE_SIZE (to_mode)
of logical operations, but not right shifts. */
static rtx
-widen_operand (rtx op, enum machine_mode mode, enum machine_mode oldmode,
+widen_operand (rtx op, machine_mode mode, machine_mode oldmode,
int unsignedp, int no_extend)
{
rtx result;
a promoted object differs from our extension. */
if (! no_extend
|| (GET_CODE (op) == SUBREG && SUBREG_PROMOTED_VAR_P (op)
- && SUBREG_PROMOTED_UNSIGNED_P (op) == unsignedp))
+ && SUBREG_CHECK_PROMOTED_SIGN (op, unsignedp)))
return convert_modes (mode, oldmode, op, unsignedp);
- /* If MODE is no wider than a single word, we return a paradoxical
+ /* If MODE is no wider than a single word, we return a lowpart or paradoxical
SUBREG. */
if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
- return gen_rtx_SUBREG (mode, force_reg (GET_MODE (op), op), 0);
+ return gen_lowpart (mode, force_reg (GET_MODE (op), op));
/* Otherwise, get an object of MODE, clobber it, and set the low-order
part to OP. */
case BIT_XOR_EXPR:
return xor_optab;
+ case MULT_HIGHPART_EXPR:
+ return TYPE_UNSIGNED (type) ? umul_highpart_optab : smul_highpart_optab;
+
case TRUNC_MOD_EXPR:
case CEIL_MOD_EXPR:
case FLOOR_MOD_EXPR:
case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR:
- if (TYPE_SATURATING(type))
- return TYPE_UNSIGNED(type) ? usdiv_optab : ssdiv_optab;
+ if (TYPE_SATURATING (type))
+ return TYPE_UNSIGNED (type) ? usdiv_optab : ssdiv_optab;
return TYPE_UNSIGNED (type) ? udiv_optab : sdiv_optab;
case LSHIFT_EXPR:
if (TREE_CODE (type) == VECTOR_TYPE)
{
if (subtype == optab_vector)
- return TYPE_SATURATING (type) ? NULL : vashl_optab;
+ return TYPE_SATURATING (type) ? unknown_optab : vashl_optab;
gcc_assert (subtype == optab_scalar);
}
- if (TYPE_SATURATING(type))
- return TYPE_UNSIGNED(type) ? usashl_optab : ssashl_optab;
+ if (TYPE_SATURATING (type))
+ return TYPE_UNSIGNED (type) ? usashl_optab : ssashl_optab;
return ashl_optab;
case RSHIFT_EXPR:
case DOT_PROD_EXPR:
return TYPE_UNSIGNED (type) ? udot_prod_optab : sdot_prod_optab;
+ case SAD_EXPR:
+ return TYPE_UNSIGNED (type) ? usad_optab : ssad_optab;
+
case WIDEN_MULT_PLUS_EXPR:
return (TYPE_UNSIGNED (type)
? (TYPE_SATURATING (type)
return fma_optab;
case REDUC_MAX_EXPR:
- return TYPE_UNSIGNED (type) ? reduc_umax_optab : reduc_smax_optab;
+ return TYPE_UNSIGNED (type)
+ ? reduc_umax_scal_optab : reduc_smax_scal_optab;
case REDUC_MIN_EXPR:
- return TYPE_UNSIGNED (type) ? reduc_umin_optab : reduc_smin_optab;
+ return TYPE_UNSIGNED (type)
+ ? reduc_umin_scal_optab : reduc_smin_scal_optab;
case REDUC_PLUS_EXPR:
- return TYPE_UNSIGNED (type) ? reduc_uplus_optab : reduc_splus_optab;
-
- case VEC_LSHIFT_EXPR:
- return vec_shl_optab;
-
- case VEC_RSHIFT_EXPR:
- return vec_shr_optab;
+ return reduc_plus_scal_optab;
case VEC_WIDEN_MULT_HI_EXPR:
return TYPE_UNSIGNED (type) ?
return TYPE_UNSIGNED (type) ?
vec_widen_umult_lo_optab : vec_widen_smult_lo_optab;
+ case VEC_WIDEN_MULT_EVEN_EXPR:
+ return TYPE_UNSIGNED (type) ?
+ vec_widen_umult_even_optab : vec_widen_smult_even_optab;
+
+ case VEC_WIDEN_MULT_ODD_EXPR:
+ return TYPE_UNSIGNED (type) ?
+ vec_widen_umult_odd_optab : vec_widen_smult_odd_optab;
+
case VEC_WIDEN_LSHIFT_HI_EXPR:
return TYPE_UNSIGNED (type) ?
vec_widen_ushiftl_hi_optab : vec_widen_sshiftl_hi_optab;
{
case POINTER_PLUS_EXPR:
case PLUS_EXPR:
- if (TYPE_SATURATING(type))
- return TYPE_UNSIGNED(type) ? usadd_optab : ssadd_optab;
+ if (TYPE_SATURATING (type))
+ return TYPE_UNSIGNED (type) ? usadd_optab : ssadd_optab;
return trapv ? addv_optab : add_optab;
case MINUS_EXPR:
- if (TYPE_SATURATING(type))
- return TYPE_UNSIGNED(type) ? ussub_optab : sssub_optab;
+ if (TYPE_SATURATING (type))
+ return TYPE_UNSIGNED (type) ? ussub_optab : sssub_optab;
return trapv ? subv_optab : sub_optab;
case MULT_EXPR:
- if (TYPE_SATURATING(type))
- return TYPE_UNSIGNED(type) ? usmul_optab : ssmul_optab;
+ if (TYPE_SATURATING (type))
+ return TYPE_UNSIGNED (type) ? usmul_optab : ssmul_optab;
return trapv ? smulv_optab : smul_optab;
case NEGATE_EXPR:
- if (TYPE_SATURATING(type))
- return TYPE_UNSIGNED(type) ? usneg_optab : ssneg_optab;
+ if (TYPE_SATURATING (type))
+ return TYPE_UNSIGNED (type) ? usneg_optab : ssneg_optab;
return trapv ? negv_optab : neg_optab;
case ABS_EXPR:
return trapv ? absv_optab : abs_optab;
default:
- return NULL;
+ return unknown_optab;
+ }
+}
+
+/* Given optab UNOPTAB that reduces a vector to a scalar, find instead the old
+ optab that produces a vector with the reduction result in one element,
+ for a tree with type TYPE. */
+
+optab
+scalar_reduc_to_vector (optab unoptab, const_tree type)
+{
+ switch (unoptab)
+ {
+ case reduc_plus_scal_optab:
+ return TYPE_UNSIGNED (type) ? reduc_uplus_optab : reduc_splus_optab;
+
+ case reduc_smin_scal_optab: return reduc_smin_optab;
+ case reduc_umin_scal_optab: return reduc_umin_optab;
+ case reduc_smax_scal_optab: return reduc_smax_optab;
+ case reduc_umax_scal_optab: return reduc_umax_optab;
+ default: return unknown_optab;
}
}
-\f
/* Expand vector widening operations.
{
struct expand_operand eops[4];
tree oprnd0, oprnd1, oprnd2;
- enum machine_mode wmode = VOIDmode, tmode0, tmode1 = VOIDmode;
+ machine_mode wmode = VOIDmode, tmode0, tmode1 = VOIDmode;
optab widen_pattern_optab;
enum insn_code icode;
int nops = TREE_CODE_LENGTH (ops->code);
this may or may not be TARGET. */
rtx
-expand_ternary_op (enum machine_mode mode, optab ternary_optab, rtx op0,
+expand_ternary_op (machine_mode mode, optab ternary_optab, rtx op0,
rtx op1, rtx op2, rtx target, int unsignedp)
{
struct expand_operand ops[4];
otherwise the same as for expand_binop. */
rtx
-simplify_expand_binop (enum machine_mode mode, optab binoptab,
+simplify_expand_binop (machine_mode mode, optab binoptab,
rtx op0, rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
{
if (CONSTANT_P (op0) && CONSTANT_P (op1))
{
- rtx x = simplify_binary_operation (binoptab->code, mode, op0, op1);
-
+ rtx x = simplify_binary_operation (optab_to_code (binoptab),
+ mode, op0, op1);
if (x)
return x;
}
Return true if the expansion succeeded. */
bool
-force_expand_binop (enum machine_mode mode, optab binoptab,
+force_expand_binop (machine_mode mode, optab binoptab,
rtx op0, rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
{
return true;
}
-/* Generate insns for VEC_LSHIFT_EXPR, VEC_RSHIFT_EXPR. */
-
-rtx
-expand_vec_shift_expr (sepops ops, rtx target)
-{
- struct expand_operand eops[3];
- enum insn_code icode;
- rtx rtx_op1, rtx_op2;
- enum machine_mode mode = TYPE_MODE (ops->type);
- tree vec_oprnd = ops->op0;
- tree shift_oprnd = ops->op1;
- optab shift_optab;
-
- switch (ops->code)
- {
- case VEC_RSHIFT_EXPR:
- shift_optab = vec_shr_optab;
- break;
- case VEC_LSHIFT_EXPR:
- shift_optab = vec_shl_optab;
- break;
- default:
- gcc_unreachable ();
- }
-
- icode = optab_handler (shift_optab, mode);
- gcc_assert (icode != CODE_FOR_nothing);
-
- rtx_op1 = expand_normal (vec_oprnd);
- rtx_op2 = expand_normal (shift_oprnd);
-
- create_output_operand (&eops[0], target, mode);
- create_input_operand (&eops[1], rtx_op1, GET_MODE (rtx_op1));
- create_convert_operand_from_type (&eops[2], rtx_op2, TREE_TYPE (shift_oprnd));
- expand_insn (icode, 3, eops);
-
- return eops[0].value;
-}
-
/* Create a new vector value in VMODE with all elements set to OP. The
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
-expand_vector_broadcast (enum machine_mode vmode, rtx op)
+expand_vector_broadcast (machine_mode vmode, rtx op)
{
enum insn_code icode;
rtvec vec;
value are the same as for the parent routine. */
static bool
-expand_subword_shift (enum machine_mode op1_mode, optab binoptab,
+expand_subword_shift (machine_mode op1_mode, optab binoptab,
rtx outof_input, rtx into_input, rtx op1,
rtx outof_target, rtx into_target,
int unsignedp, enum optab_methods methods,
if (CONSTANT_P (op1) || shift_mask >= BITS_PER_WORD)
{
carries = outof_input;
- tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+ tmp = immed_wide_int_const (wi::shwi (BITS_PER_WORD,
+ op1_mode), op1_mode);
tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
0, true, methods);
}
outof_input, const1_rtx, 0, unsignedp, methods);
if (shift_mask == BITS_PER_WORD - 1)
{
- tmp = immed_double_const (-1, -1, op1_mode);
+ tmp = immed_wide_int_const
+ (wi::minus_one (GET_MODE_PRECISION (op1_mode)), op1_mode);
tmp = simplify_expand_binop (op1_mode, xor_optab, op1, tmp,
0, true, methods);
}
else
{
- tmp = immed_double_const (BITS_PER_WORD - 1, 0, op1_mode);
+ tmp = immed_wide_int_const (wi::shwi (BITS_PER_WORD - 1,
+ op1_mode), op1_mode);
tmp = simplify_expand_binop (op1_mode, sub_optab, tmp, op1,
0, true, methods);
}
}
-#ifdef HAVE_conditional_move
/* Try implementing expand_doubleword_shift using conditional moves.
The shift is by < BITS_PER_WORD if (CMP_CODE CMP1 CMP2) is true,
otherwise it is by >= BITS_PER_WORD. SUBWORD_OP1 and SUPERWORD_OP1
arguments are the same as the parent routine. */
static bool
-expand_doubleword_shift_condmove (enum machine_mode op1_mode, optab binoptab,
+expand_doubleword_shift_condmove (machine_mode op1_mode, optab binoptab,
enum rtx_code cmp_code, rtx cmp1, rtx cmp2,
rtx outof_input, rtx into_input,
rtx subword_op1, rtx superword_op1,
return true;
}
-#endif
/* Expand a doubleword shift (ashl, ashr or lshr) using word-mode shifts.
OUTOF_INPUT and INTO_INPUT are the two word-sized halves of the first
Return true if the shift could be successfully synthesized. */
static bool
-expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab,
+expand_doubleword_shift (machine_mode op1_mode, optab binoptab,
rtx outof_input, rtx into_input, rtx op1,
rtx outof_target, rtx into_target,
int unsignedp, enum optab_methods methods,
unsigned HOST_WIDE_INT shift_mask)
{
rtx superword_op1, tmp, cmp1, cmp2;
- rtx subword_label, done_label;
enum rtx_code cmp_code;
/* See if word-mode shifts by BITS_PER_WORD...BITS_PER_WORD * 2 - 1 will
is true when the effective shift value is less than BITS_PER_WORD.
Set SUPERWORD_OP1 to the shift count that should be used to shift
OUTOF_INPUT into INTO_TARGET when the condition is false. */
- tmp = immed_double_const (BITS_PER_WORD, 0, op1_mode);
+ tmp = immed_wide_int_const (wi::shwi (BITS_PER_WORD, op1_mode), op1_mode);
if (!CONSTANT_P (op1) && shift_mask == BITS_PER_WORD - 1)
{
/* Set CMP1 to OP1 & BITS_PER_WORD. The result is zero iff OP1
unsignedp, methods, shift_mask);
}
-#ifdef HAVE_conditional_move
/* Try using conditional moves to generate straight-line code. */
- {
- rtx start = get_last_insn ();
- if (expand_doubleword_shift_condmove (op1_mode, binoptab,
- cmp_code, cmp1, cmp2,
- outof_input, into_input,
- op1, superword_op1,
- outof_target, into_target,
- unsignedp, methods, shift_mask))
- return true;
- delete_insns_since (start);
- }
-#endif
+ if (HAVE_conditional_move)
+ {
+ rtx_insn *start = get_last_insn ();
+ if (expand_doubleword_shift_condmove (op1_mode, binoptab,
+ cmp_code, cmp1, cmp2,
+ outof_input, into_input,
+ op1, superword_op1,
+ outof_target, into_target,
+ unsignedp, methods, shift_mask))
+ return true;
+ delete_insns_since (start);
+ }
/* As a last resort, use branches to select the correct alternative. */
- subword_label = gen_label_rtx ();
- done_label = gen_label_rtx ();
+ rtx_code_label *subword_label = gen_label_rtx ();
+ rtx_code_label *done_label = gen_label_rtx ();
NO_DEFER_POP;
do_compare_rtx_and_jump (cmp1, cmp2, cmp_code, false, op1_mode,
the 0 or -1. */
static rtx
-expand_doubleword_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+expand_doubleword_mult (machine_mode mode, rtx op0, rtx op1, rtx target,
bool umulp, enum optab_methods methods)
{
int low = (WORDS_BIG_ENDIAN ? 1 : 0);
the operation to perform, not an optab pointer. All other
arguments are the same. */
rtx
-expand_simple_binop (enum machine_mode mode, enum rtx_code code, rtx op0,
+expand_simple_binop (machine_mode mode, enum rtx_code code, rtx op0,
rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
{
- optab binop = code_to_optab[(int) code];
+ optab binop = code_to_optab (code);
gcc_assert (binop);
return expand_binop (mode, binop, op0, op1, target, unsignedp, methods);
static bool
shift_optab_p (optab binoptab)
{
- switch (binoptab->code)
+ switch (optab_to_code (binoptab))
{
case ASHIFT:
case SS_ASHIFT:
static bool
commutative_optab_p (optab binoptab)
{
- return (GET_RTX_CLASS (binoptab->code) == RTX_COMM_ARITH
+ return (GET_RTX_CLASS (optab_to_code (binoptab)) == RTX_COMM_ARITH
|| binoptab == smul_widen_optab
|| binoptab == umul_widen_optab
|| binoptab == smul_highpart_optab
register. Return X otherwise. UNSIGNEDP says whether X is unsigned. */
static rtx
-avoid_expensive_constant (enum machine_mode mode, optab binoptab,
+avoid_expensive_constant (machine_mode mode, optab binoptab,
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, opn, speed) > set_src_cost (x, speed))
+ && (rtx_cost (x, optab_to_code (binoptab), opn, speed)
+ > set_src_cost (x, speed)))
{
if (CONST_INT_P (x))
{
is an insn that directly implements the indicated operation.
Returns null if this is not possible. */
static rtx
-expand_binop_directly (enum machine_mode mode, optab binoptab,
+expand_binop_directly (machine_mode mode, optab binoptab,
rtx op0, rtx op1,
rtx target, int unsignedp, enum optab_methods methods,
- rtx last)
+ rtx_insn *last)
{
- enum machine_mode from_mode = widened_mode (mode, op0, op1);
+ 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;
+ 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;
struct expand_operand ops[3];
bool commutative_p;
- rtx pat;
+ rtx_insn *pat;
rtx xop0 = op0, xop1 = op1;
- rtx swap;
/* If it is a commutative operator and the modes would match
if we would swap the operands, we can save the conversions. */
if (commutative_p
&& GET_MODE (xop0) != xmode0 && GET_MODE (xop1) != xmode1
&& GET_MODE (xop0) == xmode1 && GET_MODE (xop1) == xmode1)
- {
- swap = xop0;
- xop0 = xop1;
- xop1 = swap;
- }
+ std::swap (xop0, xop1);
/* If we are optimizing, force expensive constants into a register. */
xop0 = avoid_expensive_constant (xmode0, binoptab, 0, xop0, unsignedp);
Also try to make the last operand a constant. */
if (commutative_p
&& swap_commutative_operands_with_target (target, xop0, xop1))
- {
- swap = xop1;
- xop1 = xop0;
- xop0 = swap;
- }
+ std::swap (xop0, xop1);
/* Now, if insn's predicates don't allow our operands, put them into
pseudo regs. */
REG_EQUAL note to it. If we can't because TEMP conflicts with an
operand, call expand_binop again, this time without a target. */
if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
- && ! add_equal_note (pat, ops[0].value, binoptab->code,
+ && ! add_equal_note (pat, ops[0].value,
+ optab_to_code (binoptab),
ops[1].value, ops[2].value))
{
delete_insns_since (last);
this may or may not be TARGET. */
rtx
-expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
+expand_binop (machine_mode mode, optab binoptab, rtx op0, rtx op1,
rtx target, int unsignedp, enum optab_methods methods)
{
enum optab_methods next_methods
= (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN
? OPTAB_WIDEN : methods);
enum mode_class mclass;
- enum machine_mode wider_mode;
+ machine_mode wider_mode;
rtx libfunc;
rtx temp;
- rtx entry_last = get_last_insn ();
- rtx last;
+ rtx_insn *entry_last = get_last_insn ();
+ rtx_insn *last;
mclass = GET_MODE_CLASS (mode);
newop1 = negate_rtx (GET_MODE (op1), op1);
else
newop1 = expand_binop (GET_MODE (op1), sub_optab,
- GEN_INT (bits), op1,
+ gen_int_mode (bits, GET_MODE (op1)), op1,
NULL_RTX, unsignedp, OPTAB_DIRECT);
temp = expand_binop_directly (mode, otheroptab, op0, newop1,
shift by a vector. If so, broadcast the scalar into a vector. */
if (mclass == MODE_VECTOR_INT)
{
- optab otheroptab = NULL;
+ optab otheroptab = unknown_optab;
if (binoptab == ashl_optab)
otheroptab = vashl_optab;
Also try to make the last operand a constant. */
if (commutative_optab_p (binoptab)
&& swap_commutative_operands_with_target (target, op0, op1))
- {
- temp = op1;
- op1 = op0;
- op0 = temp;
- }
+ std::swap (op0, op1);
/* These can be done a word at a time. */
if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab)
&& optab_handler (binoptab, word_mode) != CODE_FOR_nothing)
{
int i;
- rtx insns;
+ rtx_insn *insns;
/* If TARGET is the same as one of the operands, the REG_EQUAL note
won't be accurate, so use a new target. */
&& optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
{
unsigned HOST_WIDE_INT shift_mask, double_shift_mask;
- enum machine_mode op1_mode;
+ machine_mode op1_mode;
double_shift_mask = targetm.shift_truncation_mask (mode);
shift_mask = targetm.shift_truncation_mask (word_mode);
|| (shift_mask == BITS_PER_WORD - 1
&& double_shift_mask == BITS_PER_WORD * 2 - 1))
{
- rtx insns;
+ rtx_insn *insns;
rtx into_target, outof_target;
rtx into_input, outof_input;
int left_shift, outof_word;
&& optab_handler (ashl_optab, word_mode) != CODE_FOR_nothing
&& optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing)
{
- rtx insns;
+ rtx_insn *insns;
rtx into_target, outof_target;
rtx into_input, outof_input;
rtx inter;
rtx temp = emit_move_insn (target, xtarget);
set_dst_reg_note (temp, REG_EQUAL,
- gen_rtx_fmt_ee (binoptab->code, mode,
- copy_rtx (xop0),
+ gen_rtx_fmt_ee (optab_to_code (binoptab),
+ mode, copy_rtx (xop0),
copy_rtx (xop1)),
target);
}
if (libfunc
&& (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN))
{
- rtx insns;
+ rtx_insn *insns;
rtx op1x = op1;
- enum machine_mode op1_mode = mode;
+ machine_mode op1_mode = mode;
rtx value;
start_sequence ();
target = gen_reg_rtx (mode);
emit_libcall_block_1 (insns, target, value,
- gen_rtx_fmt_ee (binoptab->code, mode, op0, op1),
+ gen_rtx_fmt_ee (optab_to_code (binoptab),
+ mode, op0, op1),
trapv_binoptab_p (binoptab));
return target;
of an unsigned wider operation, since the result would be the same. */
rtx
-sign_expand_binop (enum machine_mode mode, optab uoptab, optab soptab,
+sign_expand_binop (machine_mode mode, optab uoptab, optab soptab,
rtx op0, rtx op1, rtx target, int unsignedp,
enum optab_methods methods)
{
rtx temp;
optab direct_optab = unsignedp ? uoptab : soptab;
- struct optab_d wide_soptab;
+ bool save_enable;
/* Do it without widening, if possible. */
temp = expand_binop (mode, direct_optab, op0, op1, target,
if (temp || methods == OPTAB_DIRECT)
return temp;
- /* Try widening to a signed int. Make a fake signed optab that
- hides any signed insn for direct use. */
- wide_soptab = *soptab;
- set_optab_handler (&wide_soptab, mode, CODE_FOR_nothing);
- /* We don't want to generate new hash table entries from this fake
- optab. */
- wide_soptab.libcall_gen = NULL;
+ /* Try widening to a signed int. Disable any direct use of any
+ signed insn in the current mode. */
+ save_enable = swap_optab_enable (soptab, mode, false);
- temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+ temp = expand_binop (mode, soptab, op0, op1, target,
unsignedp, OPTAB_WIDEN);
/* For unsigned operands, try widening to an unsigned int. */
- if (temp == 0 && unsignedp)
+ if (!temp && unsignedp)
temp = expand_binop (mode, uoptab, op0, op1, target,
unsignedp, OPTAB_WIDEN);
if (temp || methods == OPTAB_WIDEN)
- return temp;
+ goto egress;
/* Use the right width libcall if that exists. */
- temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB);
+ temp = expand_binop (mode, direct_optab, op0, op1, target,
+ unsignedp, OPTAB_LIB);
if (temp || methods == OPTAB_LIB)
- return temp;
+ goto egress;
/* Must widen and use a libcall, use either signed or unsigned. */
- temp = expand_binop (mode, &wide_soptab, op0, op1, target,
+ temp = expand_binop (mode, soptab, op0, op1, target,
unsignedp, methods);
- if (temp != 0)
- return temp;
- if (unsignedp)
- return expand_binop (mode, uoptab, op0, op1, target,
+ if (!temp && unsignedp)
+ temp = expand_binop (mode, uoptab, op0, op1, target,
unsignedp, methods);
- return 0;
+
+ egress:
+ /* Undo the fiddling above. */
+ if (save_enable)
+ swap_optab_enable (soptab, mode, true);
+ return temp;
}
\f
/* Generate code to perform an operation specified by UNOPPTAB
expand_twoval_unop (optab unoptab, rtx op0, rtx targ0, rtx targ1,
int unsignedp)
{
- enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
+ machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
enum mode_class mclass;
- enum machine_mode wider_mode;
- rtx entry_last = get_last_insn ();
- rtx last;
+ machine_mode wider_mode;
+ rtx_insn *entry_last = get_last_insn ();
+ rtx_insn *last;
mclass = GET_MODE_CLASS (mode);
expand_twoval_binop (optab binoptab, rtx op0, rtx op1, rtx targ0, rtx targ1,
int unsignedp)
{
- enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
+ machine_mode mode = GET_MODE (targ0 ? targ0 : targ1);
enum mode_class mclass;
- enum machine_mode wider_mode;
- rtx entry_last = get_last_insn ();
- rtx last;
+ machine_mode wider_mode;
+ rtx_insn *entry_last = get_last_insn ();
+ rtx_insn *last;
mclass = GET_MODE_CLASS (mode);
{
struct expand_operand ops[4];
enum insn_code icode = optab_handler (binoptab, mode);
- enum machine_mode mode0 = insn_data[icode].operand[1].mode;
- enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+ machine_mode mode0 = insn_data[icode].operand[1].mode;
+ machine_mode mode1 = insn_data[icode].operand[2].mode;
rtx xop0 = op0, xop1 = op1;
/* If we are optimizing, force expensive constants into a register. */
expand_twoval_binop_libfunc (optab binoptab, rtx op0, rtx op1,
rtx targ0, rtx targ1, enum rtx_code code)
{
- enum machine_mode mode;
- enum machine_mode libval_mode;
+ machine_mode mode;
+ machine_mode libval_mode;
rtx libval;
- rtx insns;
+ rtx_insn *insns;
rtx libfunc;
/* Exactly one of TARG0 or TARG1 should be non-NULL. */
the operation to perform, not an optab pointer. All other
arguments are the same. */
rtx
-expand_simple_unop (enum machine_mode mode, enum rtx_code code, rtx op0,
+expand_simple_unop (machine_mode mode, enum rtx_code code, rtx op0,
rtx target, int unsignedp)
{
- optab unop = code_to_optab[(int) code];
+ optab unop = code_to_optab (code);
gcc_assert (unop);
return expand_unop (mode, unop, op0, target, unsignedp);
A similar operation can be used for clrsb. UNOPTAB says which operation
we are trying to expand. */
static rtx
-widen_leading (enum machine_mode mode, rtx op0, rtx target, optab unoptab)
+widen_leading (machine_mode mode, rtx op0, rtx target, optab unoptab)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
if (CLASS_HAS_WIDER_MODES_P (mclass))
{
- enum machine_mode wider_mode;
+ machine_mode wider_mode;
for (wider_mode = GET_MODE_WIDER_MODE (mode);
wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
{
- rtx xop0, temp, last;
+ rtx xop0, temp;
+ rtx_insn *last;
last = get_last_insn ();
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_PRECISION (wider_mode)
- - GET_MODE_PRECISION (mode)),
- target, true, OPTAB_DIRECT);
+ 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);
/* 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 (enum machine_mode mode, rtx op0, rtx target)
+expand_doubleword_clz (machine_mode mode, rtx op0, rtx target)
{
rtx xop0 = force_reg (mode, op0);
rtx subhi = gen_highpart (word_mode, xop0);
rtx sublo = gen_lowpart (word_mode, xop0);
- rtx hi0_label = gen_label_rtx ();
- rtx after_label = gen_label_rtx ();
- rtx seq, temp, result;
+ rtx_code_label *hi0_label = gen_label_rtx ();
+ rtx_code_label *after_label = gen_label_rtx ();
+ rtx_insn *seq;
+ rtx temp, result;
/* If we were not given a target, use a word_mode register, not a
'mode' register. The result will fit, and nobody is expecting
if (!temp)
goto fail;
temp = expand_binop (word_mode, add_optab, temp,
- GEN_INT (GET_MODE_BITSIZE (word_mode)),
+ gen_int_mode (GET_MODE_BITSIZE (word_mode), word_mode),
result, true, OPTAB_DIRECT);
if (!temp)
goto fail;
as
(lshiftrt:wide (bswap:wide x) ((width wide) - (width narrow))). */
static rtx
-widen_bswap (enum machine_mode mode, rtx op0, rtx target)
+widen_bswap (machine_mode mode, rtx op0, rtx target)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
- enum machine_mode wider_mode;
- rtx x, last;
+ machine_mode wider_mode;
+ rtx x;
+ rtx_insn *last;
if (!CLASS_HAS_WIDER_MODES_P (mclass))
return NULL_RTX;
/* Try calculating bswap as two bswaps of two word-sized operands. */
static rtx
-expand_doubleword_bswap (enum machine_mode mode, rtx op, rtx target)
+expand_doubleword_bswap (machine_mode mode, rtx op, rtx target)
{
rtx t0, t1;
/* Try calculating (parity x) as (and (popcount x) 1), where
popcount can also be done in a wider mode. */
static rtx
-expand_parity (enum machine_mode mode, rtx op0, rtx target)
+expand_parity (machine_mode mode, rtx op0, rtx target)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
if (CLASS_HAS_WIDER_MODES_P (mclass))
{
- enum machine_mode wider_mode;
+ machine_mode wider_mode;
for (wider_mode = mode; wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
{
if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing)
{
- rtx xop0, temp, last;
+ rtx xop0, temp;
+ rtx_insn *last;
last = get_last_insn ();
less convenient for expand_ffs anyway. */
static rtx
-expand_ctz (enum machine_mode mode, rtx op0, rtx target)
+expand_ctz (machine_mode mode, rtx op0, rtx target)
{
- rtx seq, temp;
+ rtx_insn *seq;
+ rtx temp;
if (optab_handler (clz_optab, mode) == CODE_FOR_nothing)
return 0;
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_PRECISION (mode) - 1),
+ temp = expand_binop (mode, sub_optab,
+ gen_int_mode (GET_MODE_PRECISION (mode) - 1, mode),
temp, target,
true, OPTAB_DIRECT);
if (temp == 0)
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 (enum machine_mode mode, rtx op0, rtx target)
+expand_ffs (machine_mode mode, rtx op0, rtx target)
{
HOST_WIDE_INT val = 0;
bool defined_at_zero = false;
- rtx temp, seq;
+ rtx temp;
+ rtx_insn *seq;
if (optab_handler (ctz_optab, mode) != CODE_FOR_nothing)
{
the operation sets condition codes that can be recycled for this.
(This is true on i386, for instance.) */
- rtx nonzero_label = gen_label_rtx ();
+ rtx_code_label *nonzero_label = gen_label_rtx ();
emit_cmp_and_jump_insns (op0, CONST0_RTX (mode), NE, 0,
mode, true, nonzero_label);
/* temp now has a value in the range -1..bitsize-1. ffs is supposed
to produce a value in the range 0..bitsize. */
- temp = expand_binop (mode, add_optab, temp, GEN_INT (1),
+ temp = expand_binop (mode, add_optab, temp, gen_int_mode (1, mode),
target, false, OPTAB_DIRECT);
if (!temp)
goto fail;
register will work around the situation. */
static rtx
-lowpart_subreg_maybe_copy (enum machine_mode omode, rtx val,
- enum machine_mode imode)
+lowpart_subreg_maybe_copy (machine_mode omode, rtx val,
+ machine_mode imode)
{
rtx ret;
ret = lowpart_subreg (omode, val, imode);
logical operation on the sign bit. */
static rtx
-expand_absneg_bit (enum rtx_code code, enum machine_mode mode,
+expand_absneg_bit (enum rtx_code code, machine_mode mode,
rtx op0, rtx target)
{
const struct real_format *fmt;
int bitpos, word, nwords, i;
- enum machine_mode imode;
- double_int mask;
- rtx temp, insns;
+ machine_mode imode;
+ rtx temp;
+ rtx_insn *insns;
/* The format has to have a simple sign bit. */
fmt = REAL_MODE_FORMAT (mode);
nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD;
}
- mask = double_int_setbit (double_int_zero, bitpos);
+ wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (imode));
if (code == ABS)
- mask = double_int_not (mask);
+ mask = ~mask;
if (target == 0
|| target == op0
{
temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
op0_piece,
- immed_double_int_const (mask, imode),
+ immed_wide_int_const (mask, imode),
targ_piece, 1, OPTAB_LIB_WIDEN);
if (temp != targ_piece)
emit_move_insn (targ_piece, temp);
{
temp = expand_binop (imode, code == ABS ? and_optab : xor_optab,
gen_lowpart (imode, op0),
- immed_double_int_const (mask, imode),
+ immed_wide_int_const (mask, imode),
gen_lowpart (imode, target), 1, OPTAB_LIB_WIDEN);
target = lowpart_subreg_maybe_copy (mode, temp, imode);
/* As expand_unop, but will fail rather than attempt the operation in a
different mode or with a libcall. */
static rtx
-expand_unop_direct (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
+expand_unop_direct (machine_mode mode, optab unoptab, rtx op0, rtx target,
int unsignedp)
{
if (optab_handler (unoptab, mode) != CODE_FOR_nothing)
{
struct expand_operand ops[2];
enum insn_code icode = optab_handler (unoptab, mode);
- rtx last = get_last_insn ();
- rtx pat;
+ rtx_insn *last = get_last_insn ();
+ rtx_insn *pat;
create_output_operand (&ops[0], target, mode);
create_convert_operand_from (&ops[1], op0, mode, unsignedp);
if (pat)
{
if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
- && ! add_equal_note (pat, ops[0].value, unoptab->code,
+ && ! add_equal_note (pat, ops[0].value,
+ optab_to_code (unoptab),
ops[1].value, NULL_RTX))
{
delete_insns_since (last);
this may or may not be TARGET. */
rtx
-expand_unop (enum machine_mode mode, optab unoptab, rtx op0, rtx target,
+expand_unop (machine_mode mode, optab unoptab, rtx op0, rtx target,
int unsignedp)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
- enum machine_mode wider_mode;
+ machine_mode wider_mode;
rtx temp;
rtx libfunc;
be always more efficient than the other fallback methods. */
if (mode == HImode)
{
- rtx last, temp1, temp2;
+ rtx_insn *last;
+ rtx temp1, temp2;
if (optab_handler (rotl_optab, mode) != CODE_FOR_nothing)
{
if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing)
{
rtx xop0 = op0;
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
/* For certain operations, we need not actually extend
the narrow operand, as long as we will truncate the
&& optab_handler (unoptab, word_mode) != CODE_FOR_nothing)
{
int i;
- rtx insns;
+ rtx_insn *insns;
if (target == 0 || target == op0 || !valid_multiword_target_p (target))
target = gen_reg_rtx (mode);
return target;
}
- if (unoptab->code == NEG)
+ if (optab_to_code (unoptab) == NEG)
{
/* Try negating floating point values by flipping the sign bit. */
if (SCALAR_FLOAT_MODE_P (mode))
libfunc = optab_libfunc (unoptab, mode);
if (libfunc)
{
- rtx insns;
+ rtx_insn *insns;
rtx value;
rtx eq_value;
- enum machine_mode outmode = mode;
+ machine_mode outmode = mode;
/* All of these functions return small values. Thus we choose to
have them return something that isn't a double-word. */
end_sequence ();
target = gen_reg_rtx (outmode);
- eq_value = gen_rtx_fmt_e (unoptab->code, mode, op0);
+ eq_value = gen_rtx_fmt_e (optab_to_code (unoptab), mode, op0);
if (GET_MODE_SIZE (outmode) < GET_MODE_SIZE (mode))
eq_value = simplify_gen_unary (TRUNCATE, outmode, eq_value, mode);
else if (GET_MODE_SIZE (outmode) > GET_MODE_SIZE (mode))
|| optab_libfunc (unoptab, wider_mode))
{
rtx xop0 = op0;
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
/* For certain operations, we need not actually extend
the narrow operand, as long as we will truncate the
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_PRECISION (wider_mode)
- - GET_MODE_PRECISION (mode)),
- target, true, OPTAB_DIRECT);
+ 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);
/* Likewise for bswap. */
if (unoptab == bswap_optab && temp != 0)
/* One final attempt at implementing negation via subtraction,
this time allowing widening of the operand. */
- if (unoptab->code == NEG && !HONOR_SIGNED_ZEROS (mode))
+ if (optab_to_code (unoptab) == NEG && !HONOR_SIGNED_ZEROS (mode))
{
rtx temp;
temp = expand_binop (mode,
*/
rtx
-expand_abs_nojump (enum machine_mode mode, rtx op0, rtx target,
+expand_abs_nojump (machine_mode mode, rtx op0, rtx target,
int result_unsignedp)
{
rtx temp;
- if (! flag_trapv)
+ if (GET_MODE_CLASS (mode) != MODE_INT
+ || ! flag_trapv)
result_unsignedp = 1;
/* First try to do it with a special abs instruction. */
if (optab_handler (smax_optab, mode) != CODE_FOR_nothing
&& !HONOR_SIGNED_ZEROS (mode))
{
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
- temp = expand_unop (mode, neg_optab, op0, NULL_RTX, 0);
+ temp = expand_unop (mode, result_unsignedp ? neg_optab : negv_optab,
+ op0, NULL_RTX, 0);
if (temp != 0)
temp = expand_binop (mode, smax_optab, op0, temp, target, 0,
OPTAB_WIDEN);
}
rtx
-expand_abs (enum machine_mode mode, rtx op0, rtx target,
+expand_abs (machine_mode mode, rtx op0, rtx target,
int result_unsignedp, int safe)
{
- rtx temp, op1;
+ rtx temp;
+ rtx_code_label *op1;
- if (! flag_trapv)
+ if (GET_MODE_CLASS (mode) != MODE_INT
+ || ! flag_trapv)
result_unsignedp = 1;
temp = expand_abs_nojump (mode, op0, target, result_unsignedp);
NO_DEFER_POP;
do_compare_rtx_and_jump (target, CONST0_RTX (mode), GE, 0, mode,
- NULL_RTX, NULL_RTX, op1, -1);
+ NULL_RTX, NULL, op1, -1);
op0 = expand_unop (mode, result_unsignedp ? neg_optab : negv_optab,
target, target, 0);
different but can be deduced from MODE. */
rtx
-expand_one_cmpl_abs_nojump (enum machine_mode mode, rtx op0, rtx target)
+expand_one_cmpl_abs_nojump (machine_mode mode, rtx op0, rtx target)
{
rtx temp;
/* If we have a MAX insn, we can do this as MAX (x, ~x). */
if (optab_handler (smax_optab, mode) != CODE_FOR_nothing)
{
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
temp = expand_unop (mode, one_cmpl_optab, op0, NULL_RTX, 0);
if (temp != 0)
and not playing with subregs so much, will help the register allocator. */
static rtx
-expand_copysign_absneg (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+expand_copysign_absneg (machine_mode mode, rtx op0, rtx op1, rtx target,
int bitpos, bool op0_is_abs)
{
- enum machine_mode imode;
+ machine_mode imode;
enum insn_code icode;
- rtx sign, label;
+ rtx sign;
+ rtx_code_label *label;
if (target == op1)
target = NULL_RTX;
}
else
{
- double_int mask;
-
if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
{
imode = int_mode_for_mode (mode);
op1 = operand_subword_force (op1, word, mode);
}
- mask = double_int_setbit (double_int_zero, bitpos);
-
+ wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (imode));
sign = expand_binop (imode, and_optab, op1,
- immed_double_int_const (mask, imode),
+ immed_wide_int_const (mask, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
}
label = gen_label_rtx ();
emit_cmp_and_jump_insns (sign, const0_rtx, EQ, NULL_RTX, imode, 1, label);
- if (GET_CODE (op0) == CONST_DOUBLE)
+ if (CONST_DOUBLE_AS_FLOAT_P (op0))
op0 = simplify_unary_operation (NEG, mode, op0, mode);
else
op0 = expand_unop (mode, neg_optab, op0, target, 0);
is true if op0 is known to have its sign bit clear. */
static rtx
-expand_copysign_bit (enum machine_mode mode, rtx op0, rtx op1, rtx target,
+expand_copysign_bit (machine_mode mode, rtx op0, rtx op1, rtx target,
int bitpos, bool op0_is_abs)
{
- enum machine_mode imode;
- double_int mask;
+ machine_mode imode;
int word, nwords, i;
- rtx temp, insns;
+ rtx temp;
+ rtx_insn *insns;
if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD)
{
nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD;
}
- mask = double_int_setbit (double_int_zero, bitpos);
+ wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (imode));
if (target == 0
|| target == op0
if (!op0_is_abs)
op0_piece
= expand_binop (imode, and_optab, op0_piece,
- immed_double_int_const (double_int_not (mask),
- imode),
+ immed_wide_int_const (~mask, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
-
op1 = expand_binop (imode, and_optab,
operand_subword_force (op1, i, mode),
- immed_double_int_const (mask, imode),
+ immed_wide_int_const (mask, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
temp = expand_binop (imode, ior_optab, op0_piece, op1,
else
{
op1 = expand_binop (imode, and_optab, gen_lowpart (imode, op1),
- immed_double_int_const (mask, imode),
+ immed_wide_int_const (mask, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
op0 = gen_lowpart (imode, op0);
if (!op0_is_abs)
op0 = expand_binop (imode, and_optab, op0,
- immed_double_int_const (double_int_not (mask),
- imode),
+ immed_wide_int_const (~mask, imode),
NULL_RTX, 1, OPTAB_LIB_WIDEN);
temp = expand_binop (imode, ior_optab, op0, op1,
rtx
expand_copysign (rtx op0, rtx op1, rtx target)
{
- enum machine_mode mode = GET_MODE (op0);
+ machine_mode mode = GET_MODE (op0);
const struct real_format *fmt;
bool op0_is_abs;
rtx temp;
return NULL_RTX;
op0_is_abs = false;
- if (GET_CODE (op0) == CONST_DOUBLE)
+ if (CONST_DOUBLE_AS_FLOAT_P (op0))
{
if (real_isneg (CONST_DOUBLE_REAL_VALUE (op0)))
op0 = simplify_unary_operation (ABS, mode, op0, mode);
}
if (fmt->signbit_ro >= 0
- && (GET_CODE (op0) == CONST_DOUBLE
+ && (CONST_DOUBLE_AS_FLOAT_P (op0)
|| (optab_handler (neg_optab, mode) != CODE_FOR_nothing
&& optab_handler (abs_optab, mode) != CODE_FOR_nothing)))
{
enum rtx_code code)
{
struct expand_operand ops[2];
- rtx pat;
+ rtx_insn *pat;
create_output_operand (&ops[0], target, GET_MODE (target));
create_input_operand (&ops[1], op0, GET_MODE (op0));
if (!pat)
return false;
- if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN)
+ if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
+ && code != UNKNOWN)
add_equal_note (pat, ops[0].value, code, ops[1].value, NULL_RTX);
emit_insn (pat);
\f
struct no_conflict_data
{
- rtx target, first, insn;
+ rtx target;
+ rtx_insn *first, *insn;
bool must_stay;
};
note with an operand of EQUIV. */
static void
-emit_libcall_block_1 (rtx insns, rtx target, rtx result, rtx equiv,
+emit_libcall_block_1 (rtx_insn *insns, rtx target, rtx result, rtx equiv,
bool equiv_may_trap)
{
rtx final_dest = target;
- rtx next, last, insn;
+ rtx_insn *next, *last, *insn;
/* If this is a reg with REG_USERVAR_P set, then it could possibly turn
into a MEM later. Protect the libcall block from this change. */
if (! data.must_stay)
{
if (PREV_INSN (insn))
- NEXT_INSN (PREV_INSN (insn)) = next;
+ SET_NEXT_INSN (PREV_INSN (insn)) = next;
else
insns = next;
if (next)
- PREV_INSN (next) = PREV_INSN (insn);
+ SET_PREV_INSN (next) = PREV_INSN (insn);
add_insn (insn);
}
void
emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv)
{
- emit_libcall_block_1 (insns, target, result, equiv, false);
+ emit_libcall_block_1 (safe_as_a <rtx_insn *> (insns),
+ target, result, equiv, false);
}
\f
/* Nonzero if we can perform a comparison of mode MODE straightforwardly.
required to implement all (or any) of the unordered bcc operations. */
int
-can_compare_p (enum rtx_code code, enum machine_mode mode,
+can_compare_p (enum rtx_code code, machine_mode mode,
enum can_compare_purpose purpose)
{
rtx test;
static void
prepare_cmp_insn (rtx x, rtx y, enum rtx_code comparison, rtx size,
int unsignedp, enum optab_methods methods,
- rtx *ptest, enum machine_mode *pmode)
+ rtx *ptest, machine_mode *pmode)
{
- enum machine_mode mode = *pmode;
+ machine_mode mode = *pmode;
rtx libfunc, test;
- enum machine_mode cmp_mode;
+ machine_mode cmp_mode;
enum mode_class mclass;
/* The other methods are not needed. */
> COSTS_N_INSNS (1)))
y = force_reg (mode, y);
-#ifdef HAVE_cc0
+#if HAVE_cc0
/* Make sure if we have a canonical comparison. The RTL
documentation states that canonical comparisons are required only
for targets which have cc0. */
if (mode == BLKmode)
{
- enum machine_mode result_mode;
+ machine_mode result_mode;
enum insn_code cmp_code;
tree length_type;
rtx libfunc;
XEXP (x, 0), Pmode,
XEXP (y, 0), Pmode,
size, cmp_mode);
-
- *ptest = gen_rtx_fmt_ee (comparison, VOIDmode, result, const0_rtx);
- *pmode = result_mode;
- return;
+ x = result;
+ y = const0_rtx;
+ mode = result_mode;
+ methods = OPTAB_LIB_WIDEN;
+ unsignedp = false;
}
/* Don't allow operands to the compare to trap, as that can put the
if (GET_MODE_CLASS (mode) == MODE_CC)
{
- gcc_assert (can_compare_p (comparison, CCmode, ccp_jump));
- *ptest = gen_rtx_fmt_ee (comparison, VOIDmode, x, y);
+ enum insn_code icode = optab_handler (cbranch_optab, CCmode);
+ test = gen_rtx_fmt_ee (comparison, VOIDmode, x, y);
+ gcc_assert (icode != CODE_FOR_nothing
+ && insn_operand_matches (icode, 0, test));
+ *ptest = test;
return;
}
if (icode != CODE_FOR_nothing
&& insn_operand_matches (icode, 0, test))
{
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
rtx op0 = prepare_operand (icode, x, 1, mode, cmp_mode, unsignedp);
rtx op1 = prepare_operand (icode, y, 2, mode, cmp_mode, unsignedp);
if (op0 && op1
if (!SCALAR_FLOAT_MODE_P (mode))
{
rtx result;
+ machine_mode ret_mode;
/* Handle a libcall just for the mode we are using. */
libfunc = optab_libfunc (cmp_optab, mode);
libfunc = ulibfunc;
}
+ ret_mode = targetm.libgcc_cmp_return_mode ();
result = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST,
- targetm.libgcc_cmp_return_mode (),
- 2, x, mode, y, mode);
+ ret_mode, 2, x, mode, y, mode);
/* There are two kinds of comparison routines. Biased routines
return 0/1/2, and unbiased routines return -1/0/1. Other parts
if (!TARGET_LIB_INT_CMP_BIASED && !ALL_FIXED_POINT_MODE_P (mode))
{
if (unsignedp)
- x = plus_constant (result, 1);
+ x = plus_constant (ret_mode, result, 1);
else
y = const0_rtx;
}
- *pmode = word_mode;
+ *pmode = ret_mode;
prepare_cmp_insn (x, y, comparison, NULL_RTX, unsignedp, methods,
ptest, pmode);
}
that it is accepted by the operand predicate. Return the new value. */
rtx
-prepare_operand (enum insn_code icode, rtx x, int opnum, enum machine_mode mode,
- enum machine_mode wider_mode, int unsignedp)
+prepare_operand (enum insn_code icode, rtx x, int opnum, machine_mode mode,
+ machine_mode wider_mode, int unsignedp)
{
if (mode != wider_mode)
x = convert_modes (wider_mode, mode, x, unsignedp);
if (!insn_operand_matches (icode, opnum, x))
{
+ machine_mode op_mode = insn_data[(int) icode].operand[opnum].mode;
if (reload_completed)
return NULL_RTX;
- x = copy_to_mode_reg (insn_data[(int) icode].operand[opnum].mode, x);
+ if (GET_MODE (x) != op_mode && GET_MODE (x) != VOIDmode)
+ return NULL_RTX;
+ x = copy_to_mode_reg (op_mode, x);
}
return x;
we can do the branch. */
static void
-emit_cmp_and_jump_insn_1 (rtx test, enum machine_mode mode, rtx label)
+emit_cmp_and_jump_insn_1 (rtx test, machine_mode mode, rtx label, int prob)
{
- enum machine_mode optab_mode;
+ machine_mode optab_mode;
enum mode_class mclass;
enum insn_code icode;
+ rtx_insn *insn;
mclass = GET_MODE_CLASS (mode);
optab_mode = (mclass == MODE_CC) ? CCmode : mode;
gcc_assert (icode != CODE_FOR_nothing);
gcc_assert (insn_operand_matches (icode, 0, test));
- emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), XEXP (test, 1), label));
+ insn = emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0),
+ XEXP (test, 1), label));
+ if (prob != -1
+ && profile_status_for_fn (cfun) != PROFILE_ABSENT
+ && insn
+ && JUMP_P (insn)
+ && any_condjump_p (insn)
+ && !find_reg_note (insn, REG_BR_PROB, 0))
+ add_int_reg_note (insn, REG_BR_PROB, prob);
}
/* Generate code to compare X with Y so that the condition codes are
COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.).
It will be potentially converted into an unsigned variant based on
- UNSIGNEDP to select a proper jump instruction. */
+ UNSIGNEDP to select a proper jump instruction.
+
+ PROB is the probability of jumping to LABEL. */
void
emit_cmp_and_jump_insns (rtx x, rtx y, enum rtx_code comparison, rtx size,
- enum machine_mode mode, int unsignedp, rtx label)
+ machine_mode mode, int unsignedp, rtx label,
+ int prob)
{
rtx op0 = x, op1 = y;
rtx test;
prepare_cmp_insn (op0, op1, comparison, size, unsignedp, OPTAB_LIB_WIDEN,
&test, &mode);
- emit_cmp_and_jump_insn_1 (test, mode, label);
+ emit_cmp_and_jump_insn_1 (test, mode, label, prob);
}
\f
static void
prepare_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison,
- rtx *ptest, enum machine_mode *pmode)
+ rtx *ptest, machine_mode *pmode)
{
enum rtx_code swapped = swap_condition (comparison);
enum rtx_code reversed = reverse_condition_maybe_unordered (comparison);
- enum machine_mode orig_mode = GET_MODE (x);
- enum machine_mode mode, cmp_mode;
+ machine_mode orig_mode = GET_MODE (x);
+ machine_mode mode, cmp_mode;
rtx true_rtx, false_rtx;
- rtx value, target, insns, equiv;
+ rtx value, target, equiv;
+ rtx_insn *insns;
rtx libfunc = 0;
bool reversed_p = false;
cmp_mode = targetm.libgcc_cmp_return_mode ();
mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
{
- if (code_to_optab[comparison]
- && (libfunc = optab_libfunc (code_to_optab[comparison], mode)))
+ if (code_to_optab (comparison)
+ && (libfunc = optab_libfunc (code_to_optab (comparison), mode)))
break;
- if (code_to_optab[swapped]
- && (libfunc = optab_libfunc (code_to_optab[swapped], mode)))
+ if (code_to_optab (swapped)
+ && (libfunc = optab_libfunc (code_to_optab (swapped), mode)))
{
rtx tmp;
tmp = x; x = y; y = tmp;
break;
}
- if (code_to_optab[reversed]
- && (libfunc = optab_libfunc (code_to_optab[reversed], mode)))
+ if (code_to_optab (reversed)
+ && (libfunc = optab_libfunc (code_to_optab (reversed), mode)))
{
comparison = reversed;
reversed_p = true;
/* Generate code to indirectly jump to a location given in the rtx LOC. */
void
-emit_indirect_jump (rtx loc)
+emit_indirect_jump (rtx loc ATTRIBUTE_UNUSED)
{
+#ifndef HAVE_indirect_jump
+ sorry ("indirect jumps are not available on this target");
+#else
struct expand_operand ops[1];
-
create_address_operand (&ops[0], loc);
expand_jump_insn (CODE_FOR_indirect_jump, 1, ops);
emit_barrier ();
+#endif
}
\f
-#ifdef HAVE_conditional_move
/* Emit a conditional move instruction if the machine supports one for that
condition and machine mode.
rtx
emit_conditional_move (rtx target, enum rtx_code code, rtx op0, rtx op1,
- enum machine_mode cmode, rtx op2, rtx op3,
- enum machine_mode mode, int unsignedp)
+ machine_mode cmode, rtx op2, rtx op3,
+ machine_mode mode, int unsignedp)
{
- rtx tem, comparison, last;
+ rtx comparison;
+ rtx_insn *last;
enum insn_code icode;
enum rtx_code reversed;
if (swap_commutative_operands_p (op0, op1))
{
- tem = op0;
- op0 = op1;
- op1 = tem;
+ std::swap (op0, op1);
code = swap_condition (code);
}
&& ((reversed = reversed_comparison_code_parts (code, op0, op1, NULL))
!= UNKNOWN))
{
- tem = op2;
- op2 = op3;
- op3 = tem;
+ std::swap (op2, op3);
code = reversed;
}
if (!COMPARISON_P (comparison))
return NULL_RTX;
- do_pending_stack_adjust ();
+ saved_pending_stack_adjust save;
+ save_pending_stack_adjust (&save);
last = get_last_insn ();
+ do_pending_stack_adjust ();
prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1),
GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN,
&comparison, &cmode);
}
}
delete_insns_since (last);
+ restore_pending_stack_adjust (&save);
return NULL_RTX;
}
comparisons, and vice versa. How do we handle them? */
int
-can_conditionally_move_p (enum machine_mode mode)
+can_conditionally_move_p (machine_mode mode)
{
if (direct_optab_handler (movcc_optab, mode) != CODE_FOR_nothing)
return 1;
return 0;
}
-#endif /* HAVE_conditional_move */
-
/* Emit a conditional addition instruction if the machine supports one for that
condition and machine mode.
the mode to use should they be constants. If it is VOIDmode, they cannot
both be constants.
- OP2 should be stored in TARGET if the comparison is true, otherwise OP2+OP3
+ OP2 should be stored in TARGET if the comparison is false, otherwise OP2+OP3
should be stored there. MODE is the mode to use should they be constants.
If it is VOIDmode, they cannot both be constants.
rtx
emit_conditional_add (rtx target, enum rtx_code code, rtx op0, rtx op1,
- enum machine_mode cmode, rtx op2, rtx op3,
- enum machine_mode mode, int unsignedp)
+ machine_mode cmode, rtx op2, rtx op3,
+ machine_mode mode, int unsignedp)
{
- rtx tem, comparison, last;
+ rtx comparison;
+ rtx_insn *last;
enum insn_code icode;
- enum rtx_code reversed;
/* If one operand is constant, make it the second one. Only do this
if the other operand is not constant as well. */
if (swap_commutative_operands_p (op0, op1))
{
- tem = op0;
- op0 = op1;
- op1 = tem;
+ std::swap (op0, op1);
code = swap_condition (code);
}
if (cmode == VOIDmode)
cmode = GET_MODE (op0);
- if (swap_commutative_operands_p (op2, op3)
- && ((reversed = reversed_comparison_code_parts (code, op0, op1, NULL))
- != UNKNOWN))
- {
- tem = op2;
- op2 = op3;
- op3 = tem;
- code = reversed;
- }
-
if (mode == VOIDmode)
mode = GET_MODE (op2);
return 1;
}
+/* Generate and return an insn body to add Y to X. */
+
+rtx
+gen_addptr3_insn (rtx x, rtx y, rtx z)
+{
+ enum insn_code icode = optab_handler (addptr3_optab, GET_MODE (x));
+
+ gcc_assert (insn_operand_matches (icode, 0, x));
+ gcc_assert (insn_operand_matches (icode, 1, y));
+ gcc_assert (insn_operand_matches (icode, 2, z));
+
+ return GEN_FCN (icode) (x, y, z);
+}
+
+/* Return true if the target implements an addptr pattern and X, Y,
+ and Z are valid for the pattern predicates. */
+
+int
+have_addptr3_insn (rtx x, rtx y, rtx z)
+{
+ enum insn_code icode;
+
+ gcc_assert (GET_MODE (x) != VOIDmode);
+
+ icode = optab_handler (addptr3_optab, GET_MODE (x));
+
+ if (icode == CODE_FOR_nothing)
+ return 0;
+
+ if (!insn_operand_matches (icode, 0, x)
+ || !insn_operand_matches (icode, 1, y)
+ || !insn_operand_matches (icode, 2, z))
+ return 0;
+
+ return 1;
+}
+
/* Generate and return an insn body to subtract Y from X. */
rtx
return 1;
}
-
-/* Generate the body of an instruction to copy Y into X.
- It may be a list of insns, if one insn isn't enough. */
-
-rtx
-gen_move_insn (rtx x, rtx y)
-{
- rtx seq;
-
- start_sequence ();
- emit_move_insn_1 (x, y);
- seq = get_insns ();
- end_sequence ();
- return seq;
-}
\f
/* Return the insn code used to extend FROM_MODE to TO_MODE.
UNSIGNEDP specifies zero-extension instead of sign-extension. If
no such operation exists, CODE_FOR_nothing will be returned. */
enum insn_code
-can_extend_p (enum machine_mode to_mode, enum machine_mode from_mode,
+can_extend_p (machine_mode to_mode, machine_mode from_mode,
int unsignedp)
{
convert_optab tab;
into X (with mode MTO). Do zero-extension if UNSIGNEDP is nonzero. */
rtx
-gen_extend_insn (rtx x, rtx y, enum machine_mode mto,
- enum machine_mode mfrom, int unsignedp)
+gen_extend_insn (rtx x, rtx y, machine_mode mto,
+ machine_mode mfrom, int unsignedp)
{
enum insn_code icode = can_extend_p (mto, mfrom, unsignedp);
return GEN_FCN (icode) (x, y);
an explicit FTRUNC insn before the fix insn; otherwise 0. */
static enum insn_code
-can_fix_p (enum machine_mode fixmode, enum machine_mode fltmode,
+can_fix_p (machine_mode fixmode, machine_mode fltmode,
int unsignedp, int *truncp_ptr)
{
convert_optab tab;
}
enum insn_code
-can_float_p (enum machine_mode fltmode, enum machine_mode fixmode,
+can_float_p (machine_mode fltmode, machine_mode fixmode,
int unsignedp)
{
convert_optab tab;
tree vectype_out, tree vectype_in,
tree *decl, enum tree_code *code1)
{
- enum machine_mode m1,m2;
+ machine_mode m1,m2;
int truncp;
m1 = TYPE_MODE (vectype_out);
{
enum insn_code icode;
rtx target = to;
- enum machine_mode fmode, imode;
+ machine_mode fmode, imode;
bool can_do_signed = false;
/* Crash now, because we won't be able to decide which mode to use. */
then unconditionally adjust the result. */
if (unsignedp && can_do_signed)
{
- rtx label = gen_label_rtx ();
+ rtx_code_label *label = gen_label_rtx ();
rtx temp;
REAL_VALUE_TYPE offset;
< GET_MODE_PRECISION (GET_MODE (from)))
{
rtx temp1;
- rtx neglabel = gen_label_rtx ();
+ rtx_code_label *neglabel = gen_label_rtx ();
/* Don't use TARGET if it isn't a register, is a hard register,
or is the wrong mode. */
/* No hardware instruction available; call a library routine. */
{
rtx libfunc;
- rtx insns;
+ rtx_insn *insns;
rtx value;
convert_optab tab = unsignedp ? ufloat_optab : sfloat_optab;
- if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
+ if (GET_MODE_PRECISION (GET_MODE (from)) < GET_MODE_PRECISION (SImode))
from = convert_to_mode (SImode, from, unsignedp);
libfunc = convert_optab_libfunc (tab, GET_MODE (to), GET_MODE (from));
{
enum insn_code icode;
rtx target = to;
- enum machine_mode fmode, imode;
+ machine_mode fmode, imode;
int must_trunc = 0;
/* We first try to find a pair of modes, one real and one integer, at
if (icode != CODE_FOR_nothing)
{
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
if (fmode != GET_MODE (from))
from = convert_to_mode (fmode, from, 0);
{
int bitsize;
REAL_VALUE_TYPE offset;
- rtx limit, lab1, lab2, insn;
+ rtx limit;
+ rtx_code_label *lab1, *lab2;
+ rtx_insn *insn;
bitsize = GET_MODE_PRECISION (GET_MODE (to));
real_2expN (&offset, bitsize - 1, fmode);
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_SIZE (GET_MODE (to)) < GET_MODE_SIZE (SImode))
+ if (GET_MODE_PRECISION (GET_MODE (to)) < GET_MODE_PRECISION (SImode))
{
target = gen_reg_rtx (SImode);
}
else
{
- rtx insns;
+ rtx_insn *insns;
rtx value;
rtx libfunc;
void
expand_fixed_convert (rtx to, rtx from, int uintp, int satp)
{
- enum machine_mode to_mode = GET_MODE (to);
- enum machine_mode from_mode = GET_MODE (from);
+ machine_mode to_mode = GET_MODE (to);
+ machine_mode from_mode = GET_MODE (from);
convert_optab tab;
enum rtx_code this_code;
enum insn_code code;
- rtx insns, value;
+ rtx_insn *insns;
+ rtx value;
rtx libfunc;
if (to_mode == from_mode)
end_sequence ();
emit_libcall_block (insns, to, value,
- gen_rtx_fmt_e (tab->code, to_mode, from));
+ gen_rtx_fmt_e (optab_to_code (tab), to_mode, from));
}
/* Generate code to convert FROM to fixed point and store in TO. FROM
{
enum insn_code icode;
rtx target = to;
- enum machine_mode fmode, imode;
+ machine_mode fmode, imode;
/* We first try to find a pair of modes, one real and one integer, at
least as wide as FROM and TO, respectively, in which we can open-code
icode = convert_optab_handler (tab, imode, fmode);
if (icode != CODE_FOR_nothing)
{
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
if (fmode != GET_MODE (from))
from = convert_to_mode (fmode, from, 0);
/* Report whether we have an instruction to perform the operation
specified by CODE on operands of mode MODE. */
int
-have_insn_for (enum rtx_code code, enum machine_mode mode)
+have_insn_for (enum rtx_code code, machine_mode mode)
{
- return (code_to_optab[(int) code] != 0
- && (optab_handler (code_to_optab[(int) code], mode)
+ return (code_to_optab (code)
+ && (optab_handler (code_to_optab (code), mode)
!= CODE_FOR_nothing));
}
-/* Set all insn_code fields to CODE_FOR_nothing. */
-
-static void
-init_insn_codes (void)
-{
- memset (optab_table, 0, sizeof (optab_table));
- memset (convert_optab_table, 0, sizeof (convert_optab_table));
- memset (direct_optab_table, 0, sizeof (direct_optab_table));
-}
-
-/* Initialize OP's code to CODE, and write it into the code_to_optab table. */
-static inline void
-init_optab (optab op, enum rtx_code code)
-{
- op->code = code;
- code_to_optab[(int) code] = op;
-}
-
-/* Same, but fill in its code as CODE, and do _not_ write it into
- the code_to_optab table. */
-static inline void
-init_optabv (optab op, enum rtx_code code)
-{
- op->code = code;
-}
-
-/* Conversion optabs never go in the code_to_optab table. */
-static void
-init_convert_optab (convert_optab op, enum rtx_code code)
-{
- op->code = code;
-}
-
/* Initialize the libfunc fields of an entire group of entries in some
optab. Each entry is set equal to a string consisting of a leading
pair of underscores followed by a generic operation name followed by
*/
static void
-gen_libfunc (optab optable, const char *opname, int suffix, enum machine_mode mode)
+gen_libfunc (optab optable, const char *opname, int suffix,
+ machine_mode mode)
{
unsigned opname_len = strlen (opname);
const char *mname = GET_MODE_NAME (mode);
/* Like gen_libfunc, but verify that integer operation is involved. */
-static void
+void
gen_int_libfunc (optab optable, const char *opname, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
int maxsize = 2 * BITS_PER_WORD;
+ int minsize = BITS_PER_WORD;
if (GET_MODE_CLASS (mode) != MODE_INT)
return;
if (maxsize < LONG_LONG_TYPE_SIZE)
maxsize = LONG_LONG_TYPE_SIZE;
- if (GET_MODE_CLASS (mode) != MODE_INT
- || mode < word_mode || GET_MODE_BITSIZE (mode) > maxsize)
+ if (minsize > INT_TYPE_SIZE
+ && (trapv_binoptab_p (optable)
+ || trapv_unoptab_p (optable)))
+ minsize = INT_TYPE_SIZE;
+ if (GET_MODE_BITSIZE (mode) < minsize
+ || GET_MODE_BITSIZE (mode) > maxsize)
return;
gen_libfunc (optable, opname, suffix, mode);
}
/* Like gen_libfunc, but verify that FP and set decimal prefix if needed. */
-static void
+void
gen_fp_libfunc (optab optable, const char *opname, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
char *dec_opname;
/* Like gen_libfunc, but verify that fixed-point operation is involved. */
-static void
+void
gen_fixed_libfunc (optab optable, const char *opname, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (!ALL_FIXED_POINT_MODE_P (mode))
return;
/* Like gen_libfunc, but verify that signed fixed-point operation is
involved. */
-static void
+void
gen_signed_fixed_libfunc (optab optable, const char *opname, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (!SIGNED_FIXED_POINT_MODE_P (mode))
return;
/* Like gen_libfunc, but verify that unsigned fixed-point operation is
involved. */
-static void
+void
gen_unsigned_fixed_libfunc (optab optable, const char *opname, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (!UNSIGNED_FIXED_POINT_MODE_P (mode))
return;
/* Like gen_libfunc, but verify that FP or INT operation is involved. */
-static void
+void
gen_int_fp_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
gen_fp_libfunc (optable, name, suffix, mode);
/* Like gen_libfunc, but verify that FP or INT operation is involved
and add 'v' suffix for integer operation. */
-static void
+void
gen_intv_fp_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
gen_fp_libfunc (optable, name, suffix, mode);
/* Like gen_libfunc, but verify that FP or INT or FIXED operation is
involved. */
-static void
+void
gen_int_fp_fixed_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
gen_fp_libfunc (optable, name, suffix, mode);
/* Like gen_libfunc, but verify that FP or INT or signed FIXED operation is
involved. */
-static void
+void
gen_int_fp_signed_fixed_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT)
gen_fp_libfunc (optable, name, suffix, mode);
/* Like gen_libfunc, but verify that INT or FIXED operation is
involved. */
-static void
+void
gen_int_fixed_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (INTEGRAL_MODE_P (mode))
gen_int_libfunc (optable, name, suffix, mode);
/* Like gen_libfunc, but verify that INT or signed FIXED operation is
involved. */
-static void
+void
gen_int_signed_fixed_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (INTEGRAL_MODE_P (mode))
gen_int_libfunc (optable, name, suffix, mode);
/* Like gen_libfunc, but verify that INT or unsigned FIXED operation is
involved. */
-static void
+void
gen_int_unsigned_fixed_libfunc (optab optable, const char *name, char suffix,
- enum machine_mode mode)
+ machine_mode mode)
{
if (INTEGRAL_MODE_P (mode))
gen_int_libfunc (optable, name, suffix, mode);
a mode name and an operand count these functions have two mode names
and no operand count. */
-static void
+void
gen_interclass_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
size_t opname_len = strlen (opname);
size_t mname_len = 0;
fname = GET_MODE_NAME (fmode);
tname = GET_MODE_NAME (tmode);
- if (DECIMAL_FLOAT_MODE_P(fmode) || DECIMAL_FLOAT_MODE_P(tmode))
+ if (DECIMAL_FLOAT_MODE_P (fmode) || DECIMAL_FLOAT_MODE_P (tmode))
{
libfunc_name = dec_name;
suffix = dec_suffix;
/* Same as gen_interclass_conv_libfunc but verify that we are producing
int->fp conversion. */
-static void
+void
gen_int_to_fp_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (GET_MODE_CLASS (fmode) != MODE_INT)
return;
/* ufloat_optab is special by using floatun for FP and floatuns decimal fp
naming scheme. */
-static void
+void
gen_ufloat_conv_libfunc (convert_optab tab,
const char *opname ATTRIBUTE_UNUSED,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (DECIMAL_FLOAT_MODE_P (tmode))
gen_int_to_fp_conv_libfunc (tab, "floatuns", tmode, fmode);
/* Same as gen_interclass_conv_libfunc but verify that we are producing
fp->int conversion. */
-static void
+void
gen_int_to_fp_nondecimal_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (GET_MODE_CLASS (fmode) != MODE_INT)
return;
/* Same as gen_interclass_conv_libfunc but verify that we are producing
fp->int conversion with no decimal floating point involved. */
-static void
+void
gen_fp_to_int_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode))
return;
The string formation rules are
similar to the ones for init_libfunc, above. */
-static void
+void
gen_intraclass_conv_libfunc (convert_optab tab, const char *opname,
- enum machine_mode tmode, enum machine_mode fmode)
+ machine_mode tmode, machine_mode fmode)
{
size_t opname_len = strlen (opname);
size_t mname_len = 0;
fname = GET_MODE_NAME (fmode);
tname = GET_MODE_NAME (tmode);
- if (DECIMAL_FLOAT_MODE_P(fmode) || DECIMAL_FLOAT_MODE_P(tmode))
+ if (DECIMAL_FLOAT_MODE_P (fmode) || DECIMAL_FLOAT_MODE_P (tmode))
{
libfunc_name = dec_name;
suffix = dec_suffix;
/* Pick proper libcall for trunc_optab. We need to chose if we do
truncation or extension and interclass or intraclass. */
-static void
+void
gen_trunc_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode))
return;
/* Pick proper libcall for extend_optab. We need to chose if we do
truncation or extension and interclass or intraclass. */
-static void
+void
gen_extend_conv_libfunc (convert_optab tab,
const char *opname ATTRIBUTE_UNUSED,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode))
return;
/* Pick proper libcall for fract_optab. We need to chose if we do
interclass or intraclass. */
-static void
+void
gen_fract_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (tmode == fmode)
return;
/* Pick proper libcall for fractuns_optab. */
-static void
+void
gen_fractuns_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (tmode == fmode)
return;
/* Pick proper libcall for satfract_optab. We need to chose if we do
interclass or intraclass. */
-static void
+void
gen_satfract_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (tmode == fmode)
return;
/* Pick proper libcall for satfractuns_optab. */
-static void
+void
gen_satfractuns_conv_libfunc (convert_optab tab,
const char *opname,
- enum machine_mode tmode,
- enum machine_mode fmode)
+ machine_mode tmode,
+ machine_mode fmode)
{
if (tmode == fmode)
return;
gen_interclass_conv_libfunc (tab, opname, tmode, fmode);
}
-/* A table of previously-created libfuncs, hashed by name. */
-static GTY ((param_is (union tree_node))) htab_t libfunc_decls;
-
/* Hashtable callbacks for libfunc_decls. */
-static hashval_t
-libfunc_decl_hash (const void *entry)
+struct libfunc_decl_hasher : ggc_hasher<tree>
{
- return IDENTIFIER_HASH_VALUE (DECL_NAME ((const_tree) entry));
-}
+ static hashval_t
+ hash (tree entry)
+ {
+ return IDENTIFIER_HASH_VALUE (DECL_NAME (entry));
+ }
-static int
-libfunc_decl_eq (const void *entry1, const void *entry2)
-{
- return DECL_NAME ((const_tree) entry1) == (const_tree) entry2;
-}
+ static bool
+ equal (tree decl, tree name)
+ {
+ return DECL_NAME (decl) == name;
+ }
+};
+
+/* A table of previously-created libfuncs, hashed by name. */
+static GTY (()) hash_table<libfunc_decl_hasher> *libfunc_decls;
/* Build a decl for a libfunc named NAME. */
init_one_libfunc (const char *name)
{
tree id, decl;
- void **slot;
hashval_t hash;
if (libfunc_decls == NULL)
- libfunc_decls = htab_create_ggc (37, libfunc_decl_hash,
- libfunc_decl_eq, NULL);
+ libfunc_decls = hash_table<libfunc_decl_hasher>::create_ggc (37);
/* See if we have already created a libfunc decl for this function. */
id = get_identifier (name);
hash = IDENTIFIER_HASH_VALUE (id);
- slot = htab_find_slot_with_hash (libfunc_decls, id, hash, INSERT);
- decl = (tree) *slot;
+ tree *slot = libfunc_decls->find_slot_with_hash (id, hash, INSERT);
+ decl = *slot;
if (decl == NULL)
{
/* Create a new decl, so that it can be passed to
set_user_assembler_libfunc (const char *name, const char *asmspec)
{
tree id, decl;
- void **slot;
hashval_t hash;
id = get_identifier (name);
hash = IDENTIFIER_HASH_VALUE (id);
- slot = htab_find_slot_with_hash (libfunc_decls, id, hash, NO_INSERT);
+ tree *slot = libfunc_decls->find_slot_with_hash (id, hash, NO_INSERT);
gcc_assert (slot);
decl = (tree) *slot;
set_user_assembler_name (decl, asmspec);
/* Call this to reset the function entry for one optab (OPTABLE) in mode
MODE to NAME, which should be either 0 or a string constant. */
void
-set_optab_libfunc (optab optable, enum machine_mode mode, const char *name)
+set_optab_libfunc (optab op, machine_mode mode, const char *name)
{
rtx val;
struct libfunc_entry e;
struct libfunc_entry **slot;
- e.optab = (size_t) (optable - &optab_table[0]);
+
+ e.op = op;
e.mode1 = mode;
e.mode2 = VOIDmode;
val = init_one_libfunc (name);
else
val = 0;
- slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT);
+ slot = libfunc_hash->find_slot (&e, INSERT);
if (*slot == NULL)
- *slot = ggc_alloc_libfunc_entry ();
- (*slot)->optab = (size_t) (optable - &optab_table[0]);
+ *slot = ggc_alloc<libfunc_entry> ();
+ (*slot)->op = op;
(*slot)->mode1 = mode;
(*slot)->mode2 = VOIDmode;
(*slot)->libfunc = val;
(OPTABLE) from mode FMODE to mode TMODE to NAME, which should be
either 0 or a string constant. */
void
-set_conv_libfunc (convert_optab optable, enum machine_mode tmode,
- enum machine_mode fmode, const char *name)
+set_conv_libfunc (convert_optab optab, machine_mode tmode,
+ machine_mode fmode, const char *name)
{
rtx val;
struct libfunc_entry e;
struct libfunc_entry **slot;
- e.optab = (size_t) (optable - &convert_optab_table[0]);
+
+ e.op = optab;
e.mode1 = tmode;
e.mode2 = fmode;
val = init_one_libfunc (name);
else
val = 0;
- slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT);
+ slot = libfunc_hash->find_slot (&e, INSERT);
if (*slot == NULL)
- *slot = ggc_alloc_libfunc_entry ();
- (*slot)->optab = (size_t) (optable - &convert_optab_table[0]);
+ *slot = ggc_alloc<libfunc_entry> ();
+ (*slot)->op = optab;
(*slot)->mode1 = tmode;
(*slot)->mode2 = fmode;
(*slot)->libfunc = val;
init_optabs (void)
{
if (libfunc_hash)
- {
- htab_empty (libfunc_hash);
- /* We statically initialize the insn_codes with the equivalent of
- CODE_FOR_nothing. Repeat the process if reinitialising. */
- init_insn_codes ();
- }
+ libfunc_hash->empty ();
else
- libfunc_hash = htab_create_ggc (10, hash_libfunc, eq_libfunc, NULL);
-
- init_optab (add_optab, PLUS);
- init_optabv (addv_optab, PLUS);
- init_optab (sub_optab, MINUS);
- init_optabv (subv_optab, MINUS);
- init_optab (ssadd_optab, SS_PLUS);
- init_optab (usadd_optab, US_PLUS);
- init_optab (sssub_optab, SS_MINUS);
- init_optab (ussub_optab, US_MINUS);
- init_optab (smul_optab, MULT);
- init_optab (ssmul_optab, SS_MULT);
- init_optab (usmul_optab, US_MULT);
- init_optabv (smulv_optab, MULT);
- init_optab (smul_highpart_optab, UNKNOWN);
- init_optab (umul_highpart_optab, UNKNOWN);
- init_optab (smul_widen_optab, UNKNOWN);
- init_optab (umul_widen_optab, UNKNOWN);
- init_optab (usmul_widen_optab, UNKNOWN);
- init_optab (smadd_widen_optab, UNKNOWN);
- init_optab (umadd_widen_optab, UNKNOWN);
- init_optab (ssmadd_widen_optab, UNKNOWN);
- init_optab (usmadd_widen_optab, UNKNOWN);
- init_optab (smsub_widen_optab, UNKNOWN);
- init_optab (umsub_widen_optab, UNKNOWN);
- init_optab (ssmsub_widen_optab, UNKNOWN);
- init_optab (usmsub_widen_optab, UNKNOWN);
- init_optab (sdiv_optab, DIV);
- init_optab (ssdiv_optab, SS_DIV);
- init_optab (usdiv_optab, US_DIV);
- init_optabv (sdivv_optab, DIV);
- init_optab (sdivmod_optab, UNKNOWN);
- init_optab (udiv_optab, UDIV);
- init_optab (udivmod_optab, UNKNOWN);
- init_optab (smod_optab, MOD);
- init_optab (umod_optab, UMOD);
- init_optab (fmod_optab, UNKNOWN);
- init_optab (remainder_optab, UNKNOWN);
- init_optab (ftrunc_optab, UNKNOWN);
- init_optab (and_optab, AND);
- init_optab (ior_optab, IOR);
- init_optab (xor_optab, XOR);
- init_optab (ashl_optab, ASHIFT);
- init_optab (ssashl_optab, SS_ASHIFT);
- 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 (smax_optab, SMAX);
- init_optab (umin_optab, UMIN);
- init_optab (umax_optab, UMAX);
- init_optab (pow_optab, UNKNOWN);
- init_optab (atan2_optab, UNKNOWN);
- init_optab (fma_optab, FMA);
- init_optab (fms_optab, UNKNOWN);
- init_optab (fnma_optab, UNKNOWN);
- init_optab (fnms_optab, UNKNOWN);
-
- /* These three have codes assigned exclusively for the sake of
- have_insn_for. */
- init_optab (mov_optab, SET);
- init_optab (movstrict_optab, STRICT_LOW_PART);
- init_optab (cbranch_optab, COMPARE);
-
- init_optab (cmov_optab, UNKNOWN);
- init_optab (cstore_optab, UNKNOWN);
- init_optab (ctrap_optab, UNKNOWN);
-
- init_optab (storent_optab, UNKNOWN);
-
- init_optab (cmp_optab, UNKNOWN);
- init_optab (ucmp_optab, UNKNOWN);
-
- init_optab (eq_optab, EQ);
- init_optab (ne_optab, NE);
- init_optab (gt_optab, GT);
- init_optab (ge_optab, GE);
- init_optab (lt_optab, LT);
- init_optab (le_optab, LE);
- init_optab (unord_optab, UNORDERED);
-
- init_optab (neg_optab, NEG);
- init_optab (ssneg_optab, SS_NEG);
- init_optab (usneg_optab, US_NEG);
- init_optabv (negv_optab, NEG);
- init_optab (abs_optab, ABS);
- init_optabv (absv_optab, ABS);
- init_optab (addcc_optab, UNKNOWN);
- init_optab (one_cmpl_optab, NOT);
- init_optab (bswap_optab, BSWAP);
- 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);
- init_optab (floor_optab, UNKNOWN);
- init_optab (ceil_optab, UNKNOWN);
- init_optab (round_optab, UNKNOWN);
- init_optab (btrunc_optab, UNKNOWN);
- init_optab (nearbyint_optab, UNKNOWN);
- init_optab (rint_optab, UNKNOWN);
- init_optab (sincos_optab, UNKNOWN);
- init_optab (sin_optab, UNKNOWN);
- init_optab (asin_optab, UNKNOWN);
- init_optab (cos_optab, UNKNOWN);
- init_optab (acos_optab, UNKNOWN);
- init_optab (exp_optab, UNKNOWN);
- init_optab (exp10_optab, UNKNOWN);
- init_optab (exp2_optab, UNKNOWN);
- init_optab (expm1_optab, UNKNOWN);
- init_optab (ldexp_optab, UNKNOWN);
- init_optab (scalb_optab, UNKNOWN);
- init_optab (significand_optab, UNKNOWN);
- init_optab (logb_optab, UNKNOWN);
- init_optab (ilogb_optab, UNKNOWN);
- init_optab (log_optab, UNKNOWN);
- init_optab (log10_optab, UNKNOWN);
- init_optab (log2_optab, UNKNOWN);
- init_optab (log1p_optab, UNKNOWN);
- init_optab (tan_optab, UNKNOWN);
- init_optab (atan_optab, UNKNOWN);
- init_optab (copysign_optab, UNKNOWN);
- init_optab (signbit_optab, UNKNOWN);
-
- init_optab (isinf_optab, UNKNOWN);
-
- init_optab (strlen_optab, UNKNOWN);
- init_optab (push_optab, UNKNOWN);
-
- init_optab (reduc_smax_optab, UNKNOWN);
- init_optab (reduc_umax_optab, UNKNOWN);
- init_optab (reduc_smin_optab, UNKNOWN);
- init_optab (reduc_umin_optab, UNKNOWN);
- init_optab (reduc_splus_optab, UNKNOWN);
- init_optab (reduc_uplus_optab, UNKNOWN);
-
- init_optab (ssum_widen_optab, UNKNOWN);
- init_optab (usum_widen_optab, UNKNOWN);
- init_optab (sdot_prod_optab, UNKNOWN);
- init_optab (udot_prod_optab, UNKNOWN);
-
- init_optab (vec_extract_optab, UNKNOWN);
- init_optab (vec_set_optab, UNKNOWN);
- init_optab (vec_init_optab, UNKNOWN);
- init_optab (vec_shl_optab, UNKNOWN);
- init_optab (vec_shr_optab, UNKNOWN);
- init_optab (vec_realign_load_optab, UNKNOWN);
- init_optab (movmisalign_optab, UNKNOWN);
- init_optab (vec_widen_umult_hi_optab, UNKNOWN);
- init_optab (vec_widen_umult_lo_optab, UNKNOWN);
- init_optab (vec_widen_smult_hi_optab, UNKNOWN);
- init_optab (vec_widen_smult_lo_optab, UNKNOWN);
- init_optab (vec_widen_ushiftl_hi_optab, UNKNOWN);
- init_optab (vec_widen_ushiftl_lo_optab, UNKNOWN);
- init_optab (vec_widen_sshiftl_hi_optab, UNKNOWN);
- init_optab (vec_widen_sshiftl_lo_optab, UNKNOWN);
- init_optab (vec_unpacks_hi_optab, UNKNOWN);
- init_optab (vec_unpacks_lo_optab, UNKNOWN);
- init_optab (vec_unpacku_hi_optab, UNKNOWN);
- init_optab (vec_unpacku_lo_optab, UNKNOWN);
- init_optab (vec_unpacks_float_hi_optab, UNKNOWN);
- init_optab (vec_unpacks_float_lo_optab, UNKNOWN);
- init_optab (vec_unpacku_float_hi_optab, UNKNOWN);
- init_optab (vec_unpacku_float_lo_optab, UNKNOWN);
- init_optab (vec_pack_trunc_optab, UNKNOWN);
- init_optab (vec_pack_usat_optab, UNKNOWN);
- init_optab (vec_pack_ssat_optab, UNKNOWN);
- init_optab (vec_pack_ufix_trunc_optab, UNKNOWN);
- init_optab (vec_pack_sfix_trunc_optab, UNKNOWN);
-
- init_optab (powi_optab, UNKNOWN);
-
- /* Conversions. */
- init_convert_optab (sext_optab, SIGN_EXTEND);
- init_convert_optab (zext_optab, ZERO_EXTEND);
- init_convert_optab (trunc_optab, TRUNCATE);
- init_convert_optab (sfix_optab, FIX);
- init_convert_optab (ufix_optab, UNSIGNED_FIX);
- init_convert_optab (sfixtrunc_optab, UNKNOWN);
- init_convert_optab (ufixtrunc_optab, UNKNOWN);
- init_convert_optab (sfloat_optab, FLOAT);
- init_convert_optab (ufloat_optab, UNSIGNED_FLOAT);
- init_convert_optab (lrint_optab, UNKNOWN);
- init_convert_optab (lround_optab, UNKNOWN);
- init_convert_optab (lfloor_optab, UNKNOWN);
- init_convert_optab (lceil_optab, UNKNOWN);
-
- init_convert_optab (fract_optab, FRACT_CONVERT);
- init_convert_optab (fractuns_optab, UNSIGNED_FRACT_CONVERT);
- init_convert_optab (satfract_optab, SAT_FRACT);
- init_convert_optab (satfractuns_optab, UNSIGNED_SAT_FRACT);
+ libfunc_hash = hash_table<libfunc_hasher>::create_ggc (10);
/* Fill in the optabs with the insns we support. */
- init_all_optabs ();
-
- /* Initialize the optabs with the names of the library functions. */
- add_optab->libcall_basename = "add";
- add_optab->libcall_suffix = '3';
- add_optab->libcall_gen = gen_int_fp_fixed_libfunc;
- addv_optab->libcall_basename = "add";
- addv_optab->libcall_suffix = '3';
- addv_optab->libcall_gen = gen_intv_fp_libfunc;
- ssadd_optab->libcall_basename = "ssadd";
- ssadd_optab->libcall_suffix = '3';
- ssadd_optab->libcall_gen = gen_signed_fixed_libfunc;
- usadd_optab->libcall_basename = "usadd";
- usadd_optab->libcall_suffix = '3';
- usadd_optab->libcall_gen = gen_unsigned_fixed_libfunc;
- sub_optab->libcall_basename = "sub";
- sub_optab->libcall_suffix = '3';
- sub_optab->libcall_gen = gen_int_fp_fixed_libfunc;
- subv_optab->libcall_basename = "sub";
- subv_optab->libcall_suffix = '3';
- subv_optab->libcall_gen = gen_intv_fp_libfunc;
- sssub_optab->libcall_basename = "sssub";
- sssub_optab->libcall_suffix = '3';
- sssub_optab->libcall_gen = gen_signed_fixed_libfunc;
- ussub_optab->libcall_basename = "ussub";
- ussub_optab->libcall_suffix = '3';
- ussub_optab->libcall_gen = gen_unsigned_fixed_libfunc;
- smul_optab->libcall_basename = "mul";
- smul_optab->libcall_suffix = '3';
- smul_optab->libcall_gen = gen_int_fp_fixed_libfunc;
- smulv_optab->libcall_basename = "mul";
- smulv_optab->libcall_suffix = '3';
- smulv_optab->libcall_gen = gen_intv_fp_libfunc;
- ssmul_optab->libcall_basename = "ssmul";
- ssmul_optab->libcall_suffix = '3';
- ssmul_optab->libcall_gen = gen_signed_fixed_libfunc;
- usmul_optab->libcall_basename = "usmul";
- usmul_optab->libcall_suffix = '3';
- usmul_optab->libcall_gen = gen_unsigned_fixed_libfunc;
- sdiv_optab->libcall_basename = "div";
- sdiv_optab->libcall_suffix = '3';
- sdiv_optab->libcall_gen = gen_int_fp_signed_fixed_libfunc;
- sdivv_optab->libcall_basename = "divv";
- sdivv_optab->libcall_suffix = '3';
- sdivv_optab->libcall_gen = gen_int_libfunc;
- ssdiv_optab->libcall_basename = "ssdiv";
- ssdiv_optab->libcall_suffix = '3';
- ssdiv_optab->libcall_gen = gen_signed_fixed_libfunc;
- udiv_optab->libcall_basename = "udiv";
- udiv_optab->libcall_suffix = '3';
- udiv_optab->libcall_gen = gen_int_unsigned_fixed_libfunc;
- usdiv_optab->libcall_basename = "usdiv";
- usdiv_optab->libcall_suffix = '3';
- usdiv_optab->libcall_gen = gen_unsigned_fixed_libfunc;
- sdivmod_optab->libcall_basename = "divmod";
- sdivmod_optab->libcall_suffix = '4';
- sdivmod_optab->libcall_gen = gen_int_libfunc;
- udivmod_optab->libcall_basename = "udivmod";
- udivmod_optab->libcall_suffix = '4';
- udivmod_optab->libcall_gen = gen_int_libfunc;
- smod_optab->libcall_basename = "mod";
- smod_optab->libcall_suffix = '3';
- smod_optab->libcall_gen = gen_int_libfunc;
- umod_optab->libcall_basename = "umod";
- umod_optab->libcall_suffix = '3';
- umod_optab->libcall_gen = gen_int_libfunc;
- ftrunc_optab->libcall_basename = "ftrunc";
- ftrunc_optab->libcall_suffix = '2';
- ftrunc_optab->libcall_gen = gen_fp_libfunc;
- and_optab->libcall_basename = "and";
- and_optab->libcall_suffix = '3';
- and_optab->libcall_gen = gen_int_libfunc;
- ior_optab->libcall_basename = "ior";
- ior_optab->libcall_suffix = '3';
- ior_optab->libcall_gen = gen_int_libfunc;
- xor_optab->libcall_basename = "xor";
- xor_optab->libcall_suffix = '3';
- xor_optab->libcall_gen = gen_int_libfunc;
- ashl_optab->libcall_basename = "ashl";
- ashl_optab->libcall_suffix = '3';
- ashl_optab->libcall_gen = gen_int_fixed_libfunc;
- ssashl_optab->libcall_basename = "ssashl";
- ssashl_optab->libcall_suffix = '3';
- ssashl_optab->libcall_gen = gen_signed_fixed_libfunc;
- usashl_optab->libcall_basename = "usashl";
- usashl_optab->libcall_suffix = '3';
- usashl_optab->libcall_gen = gen_unsigned_fixed_libfunc;
- ashr_optab->libcall_basename = "ashr";
- ashr_optab->libcall_suffix = '3';
- ashr_optab->libcall_gen = gen_int_signed_fixed_libfunc;
- lshr_optab->libcall_basename = "lshr";
- lshr_optab->libcall_suffix = '3';
- lshr_optab->libcall_gen = gen_int_unsigned_fixed_libfunc;
- smin_optab->libcall_basename = "min";
- smin_optab->libcall_suffix = '3';
- smin_optab->libcall_gen = gen_int_fp_libfunc;
- smax_optab->libcall_basename = "max";
- smax_optab->libcall_suffix = '3';
- smax_optab->libcall_gen = gen_int_fp_libfunc;
- umin_optab->libcall_basename = "umin";
- umin_optab->libcall_suffix = '3';
- umin_optab->libcall_gen = gen_int_libfunc;
- umax_optab->libcall_basename = "umax";
- umax_optab->libcall_suffix = '3';
- umax_optab->libcall_gen = gen_int_libfunc;
- neg_optab->libcall_basename = "neg";
- neg_optab->libcall_suffix = '2';
- neg_optab->libcall_gen = gen_int_fp_fixed_libfunc;
- ssneg_optab->libcall_basename = "ssneg";
- ssneg_optab->libcall_suffix = '2';
- ssneg_optab->libcall_gen = gen_signed_fixed_libfunc;
- usneg_optab->libcall_basename = "usneg";
- usneg_optab->libcall_suffix = '2';
- usneg_optab->libcall_gen = gen_unsigned_fixed_libfunc;
- negv_optab->libcall_basename = "neg";
- negv_optab->libcall_suffix = '2';
- negv_optab->libcall_gen = gen_intv_fp_libfunc;
- one_cmpl_optab->libcall_basename = "one_cmpl";
- one_cmpl_optab->libcall_suffix = '2';
- one_cmpl_optab->libcall_gen = gen_int_libfunc;
- ffs_optab->libcall_basename = "ffs";
- ffs_optab->libcall_suffix = '2';
- ffs_optab->libcall_gen = gen_int_libfunc;
- clz_optab->libcall_basename = "clz";
- clz_optab->libcall_suffix = '2';
- clz_optab->libcall_gen = gen_int_libfunc;
- 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;
- parity_optab->libcall_basename = "parity";
- parity_optab->libcall_suffix = '2';
- parity_optab->libcall_gen = gen_int_libfunc;
-
- /* Comparison libcalls for integers MUST come in pairs,
- signed/unsigned. */
- cmp_optab->libcall_basename = "cmp";
- cmp_optab->libcall_suffix = '2';
- cmp_optab->libcall_gen = gen_int_fp_fixed_libfunc;
- ucmp_optab->libcall_basename = "ucmp";
- ucmp_optab->libcall_suffix = '2';
- ucmp_optab->libcall_gen = gen_int_libfunc;
-
- /* EQ etc are floating point only. */
- eq_optab->libcall_basename = "eq";
- eq_optab->libcall_suffix = '2';
- eq_optab->libcall_gen = gen_fp_libfunc;
- ne_optab->libcall_basename = "ne";
- ne_optab->libcall_suffix = '2';
- ne_optab->libcall_gen = gen_fp_libfunc;
- gt_optab->libcall_basename = "gt";
- gt_optab->libcall_suffix = '2';
- gt_optab->libcall_gen = gen_fp_libfunc;
- ge_optab->libcall_basename = "ge";
- ge_optab->libcall_suffix = '2';
- ge_optab->libcall_gen = gen_fp_libfunc;
- lt_optab->libcall_basename = "lt";
- lt_optab->libcall_suffix = '2';
- lt_optab->libcall_gen = gen_fp_libfunc;
- le_optab->libcall_basename = "le";
- le_optab->libcall_suffix = '2';
- le_optab->libcall_gen = gen_fp_libfunc;
- unord_optab->libcall_basename = "unord";
- unord_optab->libcall_suffix = '2';
- unord_optab->libcall_gen = gen_fp_libfunc;
-
- powi_optab->libcall_basename = "powi";
- powi_optab->libcall_suffix = '2';
- powi_optab->libcall_gen = gen_fp_libfunc;
-
- /* Conversions. */
- sfloat_optab->libcall_basename = "float";
- sfloat_optab->libcall_gen = gen_int_to_fp_conv_libfunc;
- ufloat_optab->libcall_gen = gen_ufloat_conv_libfunc;
- sfix_optab->libcall_basename = "fix";
- sfix_optab->libcall_gen = gen_fp_to_int_conv_libfunc;
- ufix_optab->libcall_basename = "fixuns";
- ufix_optab->libcall_gen = gen_fp_to_int_conv_libfunc;
- lrint_optab->libcall_basename = "lrint";
- lrint_optab->libcall_gen = gen_int_to_fp_nondecimal_conv_libfunc;
- lround_optab->libcall_basename = "lround";
- lround_optab->libcall_gen = gen_int_to_fp_nondecimal_conv_libfunc;
- lfloor_optab->libcall_basename = "lfloor";
- lfloor_optab->libcall_gen = gen_int_to_fp_nondecimal_conv_libfunc;
- lceil_optab->libcall_basename = "lceil";
- lceil_optab->libcall_gen = gen_int_to_fp_nondecimal_conv_libfunc;
-
- /* trunc_optab is also used for FLOAT_EXTEND. */
- sext_optab->libcall_basename = "extend";
- sext_optab->libcall_gen = gen_extend_conv_libfunc;
- trunc_optab->libcall_basename = "trunc";
- trunc_optab->libcall_gen = gen_trunc_conv_libfunc;
-
- /* Conversions for fixed-point modes and other modes. */
- fract_optab->libcall_basename = "fract";
- fract_optab->libcall_gen = gen_fract_conv_libfunc;
- satfract_optab->libcall_basename = "satfract";
- satfract_optab->libcall_gen = gen_satfract_conv_libfunc;
- fractuns_optab->libcall_basename = "fractuns";
- fractuns_optab->libcall_gen = gen_fractuns_conv_libfunc;
- satfractuns_optab->libcall_basename = "satfractuns";
- satfractuns_optab->libcall_gen = gen_satfractuns_conv_libfunc;
+ init_all_optabs (this_fn_optabs);
/* The ffs function operates on `int'. Fall back on it if we do not
have a libgcc2 function for that width. */
/* Use cabs for double complex abs, since systems generally have cabs.
Don't define any libcall for float complex, so that cabs will be used. */
if (complex_double_type_node)
- set_optab_libfunc (abs_optab, TYPE_MODE (complex_double_type_node), "cabs");
+ set_optab_libfunc (abs_optab, TYPE_MODE (complex_double_type_node),
+ "cabs");
abort_libfunc = init_one_libfunc ("abort");
memcpy_libfunc = init_one_libfunc ("memcpy");
targetm.init_libfuncs ();
}
+/* Use the current target and options to initialize
+ TREE_OPTIMIZATION_OPTABS (OPTNODE). */
+
+void
+init_tree_optimization_optabs (tree optnode)
+{
+ /* Quick exit if we have already computed optabs for this target. */
+ if (TREE_OPTIMIZATION_BASE_OPTABS (optnode) == this_target_optabs)
+ return;
+
+ /* Forget any previous information and set up for the current target. */
+ TREE_OPTIMIZATION_BASE_OPTABS (optnode) = this_target_optabs;
+ struct target_optabs *tmp_optabs = (struct target_optabs *)
+ TREE_OPTIMIZATION_OPTABS (optnode);
+ if (tmp_optabs)
+ memset (tmp_optabs, 0, sizeof (struct target_optabs));
+ else
+ tmp_optabs = ggc_alloc<target_optabs> ();
+
+ /* Generate a new set of optabs into tmp_optabs. */
+ init_all_optabs (tmp_optabs);
+
+ /* If the optabs changed, record it. */
+ if (memcmp (tmp_optabs, this_target_optabs, sizeof (struct target_optabs)))
+ TREE_OPTIMIZATION_OPTABS (optnode) = tmp_optabs;
+ else
+ {
+ TREE_OPTIMIZATION_OPTABS (optnode) = NULL;
+ ggc_free (tmp_optabs);
+ }
+}
+
/* A helper function for init_sync_libfuncs. Using the basename BASE,
install libfuncs into TAB for BASE_N for 1 <= N <= MAX. */
static void
init_sync_libfuncs_1 (optab tab, const char *base, int max)
{
- enum machine_mode mode;
+ machine_mode mode;
char buf[64];
size_t len = strlen (base);
int i;
DEBUG_FUNCTION void
debug_optab_libfuncs (void)
{
- int i;
- int j;
- int k;
+ int i, j, k;
/* Dump the arithmetic optabs. */
- for (i = 0; i != (int) OTI_MAX; i++)
+ for (i = FIRST_NORM_OPTAB; i <= LAST_NORMLIB_OPTAB; ++i)
for (j = 0; j < NUM_MACHINE_MODES; ++j)
{
- optab o;
- rtx l;
-
- o = &optab_table[i];
- l = optab_libfunc (o, (enum machine_mode) j);
+ rtx l = optab_libfunc ((optab) i, (machine_mode) j);
if (l)
{
gcc_assert (GET_CODE (l) == SYMBOL_REF);
fprintf (stderr, "%s\t%s:\t%s\n",
- GET_RTX_NAME (o->code),
+ GET_RTX_NAME (optab_to_code ((optab) i)),
GET_MODE_NAME (j),
XSTR (l, 0));
}
}
/* Dump the conversion optabs. */
- for (i = 0; i < (int) COI_MAX; ++i)
+ for (i = FIRST_CONV_OPTAB; i <= LAST_CONVLIB_OPTAB; ++i)
for (j = 0; j < NUM_MACHINE_MODES; ++j)
for (k = 0; k < NUM_MACHINE_MODES; ++k)
{
- convert_optab o;
- rtx l;
-
- o = &convert_optab_table[i];
- l = convert_optab_libfunc (o, (enum machine_mode) j,
- (enum machine_mode) k);
+ rtx l = convert_optab_libfunc ((optab) i, (machine_mode) j,
+ (machine_mode) k);
if (l)
{
gcc_assert (GET_CODE (l) == SYMBOL_REF);
fprintf (stderr, "%s\t%s\t%s:\t%s\n",
- GET_RTX_NAME (o->code),
+ GET_RTX_NAME (optab_to_code ((optab) i)),
GET_MODE_NAME (j),
GET_MODE_NAME (k),
XSTR (l, 0));
rtx
gen_cond_trap (enum rtx_code code, rtx op1, rtx op2, rtx tcode)
{
- enum machine_mode mode = GET_MODE (op1);
+ machine_mode mode = GET_MODE (op1);
enum insn_code icode;
rtx insn;
rtx trap_rtx;
/* Return rtx code for TCODE. Use UNSIGNEDP to select signed
or unsigned operation code. */
-static enum rtx_code
+enum rtx_code
get_rtx_code (enum tree_code tcode, bool unsignedp)
{
enum rtx_code code;
code = LTGT;
break;
+ case BIT_AND_EXPR:
+ code = AND;
+ break;
+
+ case BIT_IOR_EXPR:
+ code = IOR;
+ break;
+
default:
gcc_unreachable ();
}
unsigned operators. Do not generate compare instruction. */
static rtx
-vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode)
+vector_compare_rtx (enum tree_code tcode, tree t_op0, tree t_op1,
+ bool unsignedp, enum insn_code icode)
{
struct expand_operand ops[2];
- enum rtx_code rcode;
- tree t_op0, t_op1;
rtx rtx_op0, rtx_op1;
+ machine_mode m0, m1;
+ enum rtx_code rcode = get_rtx_code (tcode, unsignedp);
- /* This is unlikely. While generating VEC_COND_EXPR, auto vectorizer
- ensures that condition is a relational operation. */
- gcc_assert (COMPARISON_CLASS_P (cond));
+ gcc_assert (TREE_CODE_CLASS (tcode) == tcc_comparison);
- rcode = get_rtx_code (TREE_CODE (cond), unsignedp);
- t_op0 = TREE_OPERAND (cond, 0);
- t_op1 = TREE_OPERAND (cond, 1);
-
- /* Expand operands. */
+ /* Expand operands. For vector types with scalar modes, e.g. where int64x1_t
+ has mode DImode, this can produce a constant RTX of mode VOIDmode; in such
+ cases, use the original mode. */
rtx_op0 = expand_expr (t_op0, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op0)),
EXPAND_STACK_PARM);
+ m0 = GET_MODE (rtx_op0);
+ if (m0 == VOIDmode)
+ m0 = TYPE_MODE (TREE_TYPE (t_op0));
+
rtx_op1 = expand_expr (t_op1, NULL_RTX, TYPE_MODE (TREE_TYPE (t_op1)),
EXPAND_STACK_PARM);
+ m1 = GET_MODE (rtx_op1);
+ if (m1 == VOIDmode)
+ m1 = TYPE_MODE (TREE_TYPE (t_op1));
- create_input_operand (&ops[0], rtx_op0, GET_MODE (rtx_op0));
- create_input_operand (&ops[1], rtx_op1, GET_MODE (rtx_op1));
+ create_input_operand (&ops[0], rtx_op0, m0);
+ create_input_operand (&ops[1], rtx_op1, m1);
if (!maybe_legitimize_operands (icode, 4, 2, ops))
gcc_unreachable ();
return gen_rtx_fmt_ee (rcode, VOIDmode, ops[0].value, ops[1].value);
}
-/* Return true if VEC_PERM_EXPR can be expanded using SIMD extensions
- of the CPU. SEL may be NULL, which stands for an unknown constant. */
+/* Return true if VEC_PERM_EXPR of arbitrary input vectors can be expanded using
+ SIMD extensions of the CPU. SEL may be NULL, which stands for an unknown
+ constant. Note that additional permutations representing whole-vector shifts
+ may also be handled via the vec_shr optab, but only where the second input
+ vector is entirely constant zeroes; this case is not dealt with here. */
bool
-can_vec_perm_p (enum machine_mode mode, bool variable,
+can_vec_perm_p (machine_mode mode, bool variable,
const unsigned char *sel)
{
- enum machine_mode qimode;
+ machine_mode qimode;
/* If the target doesn't implement a vector mode for the vector type,
then no operations are supported. */
return true;
}
+/* 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. */
+static rtx
+shift_amt_for_vec_perm_mask (rtx sel)
+{
+ unsigned int i, first, nelt = GET_MODE_NUNITS (GET_MODE (sel));
+ unsigned int bitsize = GET_MODE_BITSIZE (GET_MODE_INNER (GET_MODE (sel)));
+
+ if (GET_CODE (sel) != CONST_VECTOR)
+ return NULL_RTX;
+
+ first = INTVAL (CONST_VECTOR_ELT (sel, 0));
+ if (first >= 2*nelt)
+ return NULL_RTX;
+ for (i = 1; i < nelt; i++)
+ {
+ int idx = INTVAL (CONST_VECTOR_ELT (sel, i));
+ unsigned int expected = (i + first) & (2 * nelt - 1);
+ /* Indices into the second vector are all equivalent. */
+ if (idx < 0 || (MIN (nelt, (unsigned) idx) != MIN (nelt, expected)))
+ return NULL_RTX;
+ }
+
+ return GEN_INT (first * bitsize);
+}
+
/* A subroutine of expand_vec_perm for expanding one vec_perm insn. */
static rtx
expand_vec_perm_1 (enum insn_code icode, rtx target,
rtx v0, rtx v1, rtx sel)
{
- enum machine_mode tmode = GET_MODE (target);
- enum machine_mode smode = GET_MODE (sel);
+ machine_mode tmode = GET_MODE (target);
+ machine_mode smode = GET_MODE (sel);
struct expand_operand ops[4];
create_output_operand (&ops[0], target, tmode);
else
{
create_input_operand (&ops[1], v0, tmode);
+ /* 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, GET_MODE (v0));
+ if (v1 == CONST0_RTX (GET_MODE (v1)) && shift_code)
+ if (rtx shift_amt = shift_amt_for_vec_perm_mask (sel))
+ {
+ create_convert_operand_from_type (&ops[2], shift_amt,
+ sizetype_tab[(int) stk_sizetype]);
+ if (maybe_expand_insn (shift_code, 3, ops))
+ return ops[0].value;
+ }
create_input_operand (&ops[2], v1, tmode);
}
and three operands. */
rtx
-expand_vec_perm (enum machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
+expand_vec_perm (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target)
{
enum insn_code icode;
- enum machine_mode qimode;
+ machine_mode qimode;
unsigned int i, w, e, u;
rtx tmp, sel_qi = NULL;
rtvec vec;
icode = direct_optab_handler (vec_perm_const_optab, qimode);
if (icode != CODE_FOR_nothing)
{
- tmp = expand_vec_perm_1 (icode, gen_lowpart (qimode, target),
- gen_lowpart (qimode, v0),
+ 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);
if (sel_qi == NULL)
{
/* Multiply each element by its byte size. */
- enum machine_mode selmode = GET_MODE (sel);
+ machine_mode selmode = GET_MODE (sel);
if (u == 2)
sel = expand_simple_binop (selmode, PLUS, sel, sel,
- sel, 0, OPTAB_DIRECT);
+ NULL, 0, OPTAB_DIRECT);
else
sel = expand_simple_binop (selmode, ASHIFT, sel,
GEN_INT (exact_log2 (u)),
- sel, 0, OPTAB_DIRECT);
+ NULL, 0, OPTAB_DIRECT);
gcc_assert (sel != NULL);
/* Broadcast the low byte each element into each of its bytes. */
gcc_assert (sel_qi != NULL);
}
- tmp = expand_vec_perm_1 (icode, gen_lowpart (qimode, target),
- gen_lowpart (qimode, v0),
+ 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)
tmp = gen_lowpart (mode, tmp);
mode CMODE, unsigned if UNS is true, resulting in a value of mode VMODE. */
static inline enum insn_code
-get_vcond_icode (enum machine_mode vmode, enum machine_mode cmode, bool uns)
+get_vcond_icode (machine_mode vmode, machine_mode cmode, bool uns)
{
enum insn_code icode = CODE_FOR_nothing;
if (uns)
bool
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);
+ machine_mode value_mode = TYPE_MODE (value_type);
+ 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),
struct expand_operand ops[6];
enum insn_code icode;
rtx comparison, rtx_op1, rtx_op2;
- enum machine_mode mode = TYPE_MODE (vec_cond_type);
- enum machine_mode cmp_op_mode;
+ machine_mode mode = TYPE_MODE (vec_cond_type);
+ machine_mode cmp_op_mode;
bool unsignedp;
+ tree op0a, op0b;
+ enum tree_code tcode;
- gcc_assert (COMPARISON_CLASS_P (op0));
+ if (COMPARISON_CLASS_P (op0))
+ {
+ op0a = TREE_OPERAND (op0, 0);
+ op0b = TREE_OPERAND (op0, 1);
+ tcode = TREE_CODE (op0);
+ }
+ else
+ {
+ /* Fake op0 < 0. */
+ gcc_assert (!TYPE_UNSIGNED (TREE_TYPE (op0)));
+ op0a = op0;
+ op0b = build_zero_cst (TREE_TYPE (op0));
+ tcode = LT_EXPR;
+ }
+ unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a));
+ cmp_op_mode = TYPE_MODE (TREE_TYPE (op0a));
- unsignedp = TYPE_UNSIGNED (TREE_TYPE (TREE_OPERAND (op0, 0)));
- cmp_op_mode = TYPE_MODE (TREE_TYPE (TREE_OPERAND (op0, 0)));
gcc_assert (GET_MODE_SIZE (mode) == GET_MODE_SIZE (cmp_op_mode)
&& GET_MODE_NUNITS (mode) == GET_MODE_NUNITS (cmp_op_mode));
if (icode == CODE_FOR_nothing)
return 0;
- comparison = vector_compare_rtx (op0, unsignedp, icode);
+ comparison = vector_compare_rtx (tcode, op0a, op0b, unsignedp, icode);
rtx_op1 = expand_normal (op1);
rtx_op2 = expand_normal (op2);
return ops[0].value;
}
+/* Return non-zero if a highpart multiply is supported of can be synthisized.
+ For the benefit of expand_mult_highpart, the return value is 1 for direct,
+ 2 for even/odd widening, and 3 for hi/lo widening. */
+
+int
+can_mult_highpart_p (machine_mode mode, bool uns_p)
+{
+ optab op;
+ unsigned char *sel;
+ unsigned i, nunits;
+
+ op = uns_p ? umul_highpart_optab : smul_highpart_optab;
+ if (optab_handler (op, mode) != CODE_FOR_nothing)
+ return 1;
+
+ /* If the mode is an integral vector, synth from widening operations. */
+ if (GET_MODE_CLASS (mode) != MODE_VECTOR_INT)
+ return 0;
+
+ nunits = GET_MODE_NUNITS (mode);
+ sel = XALLOCAVEC (unsigned char, nunits);
+
+ op = uns_p ? vec_widen_umult_even_optab : vec_widen_smult_even_optab;
+ if (optab_handler (op, mode) != CODE_FOR_nothing)
+ {
+ op = uns_p ? vec_widen_umult_odd_optab : vec_widen_smult_odd_optab;
+ if (optab_handler (op, mode) != CODE_FOR_nothing)
+ {
+ for (i = 0; i < nunits; ++i)
+ sel[i] = !BYTES_BIG_ENDIAN + (i & ~1) + ((i & 1) ? nunits : 0);
+ if (can_vec_perm_p (mode, false, sel))
+ return 2;
+ }
+ }
+
+ op = uns_p ? vec_widen_umult_hi_optab : vec_widen_smult_hi_optab;
+ if (optab_handler (op, mode) != CODE_FOR_nothing)
+ {
+ op = uns_p ? vec_widen_umult_lo_optab : vec_widen_smult_lo_optab;
+ if (optab_handler (op, mode) != CODE_FOR_nothing)
+ {
+ for (i = 0; i < nunits; ++i)
+ sel[i] = 2 * i + (BYTES_BIG_ENDIAN ? 0 : 1);
+ if (can_vec_perm_p (mode, false, sel))
+ return 3;
+ }
+ }
+
+ return 0;
+}
+
+/* Expand a highpart multiply. */
+
+rtx
+expand_mult_highpart (machine_mode mode, rtx op0, rtx op1,
+ rtx target, bool uns_p)
+{
+ struct expand_operand eops[3];
+ enum insn_code icode;
+ int method, i, nunits;
+ machine_mode wmode;
+ rtx m1, m2, perm;
+ optab tab1, tab2;
+ rtvec v;
+
+ method = can_mult_highpart_p (mode, uns_p);
+ switch (method)
+ {
+ case 0:
+ return NULL_RTX;
+ case 1:
+ tab1 = uns_p ? umul_highpart_optab : smul_highpart_optab;
+ return expand_binop (mode, tab1, op0, op1, target, uns_p,
+ OPTAB_LIB_WIDEN);
+ case 2:
+ tab1 = uns_p ? vec_widen_umult_even_optab : vec_widen_smult_even_optab;
+ tab2 = uns_p ? vec_widen_umult_odd_optab : vec_widen_smult_odd_optab;
+ break;
+ case 3:
+ tab1 = uns_p ? vec_widen_umult_lo_optab : vec_widen_smult_lo_optab;
+ tab2 = uns_p ? vec_widen_umult_hi_optab : vec_widen_smult_hi_optab;
+ if (BYTES_BIG_ENDIAN)
+ {
+ optab t = tab1;
+ tab1 = tab2;
+ tab2 = t;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ 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));
+
+ create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode);
+ create_input_operand (&eops[1], op0, mode);
+ create_input_operand (&eops[2], op1, mode);
+ expand_insn (icode, 3, eops);
+ m1 = gen_lowpart (mode, eops[0].value);
+
+ create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode);
+ create_input_operand (&eops[1], op0, mode);
+ create_input_operand (&eops[2], op1, mode);
+ expand_insn (optab_handler (tab2, mode), 3, eops);
+ m2 = gen_lowpart (mode, eops[0].value);
+
+ v = rtvec_alloc (nunits);
+ if (method == 2)
+ {
+ for (i = 0; i < nunits; ++i)
+ RTVEC_ELT (v, i) = GEN_INT (!BYTES_BIG_ENDIAN + (i & ~1)
+ + ((i & 1) ? nunits : 0));
+ }
+ else
+ {
+ for (i = 0; i < nunits; ++i)
+ RTVEC_ELT (v, i) = GEN_INT (2 * i + (BYTES_BIG_ENDIAN ? 0 : 1));
+ }
+ perm = gen_rtx_CONST_VECTOR (mode, v);
+
+ return expand_vec_perm (mode, m1, m2, perm, target);
+}
+
+/* Return true if target supports vector masked load/store for mode. */
+bool
+can_vec_mask_load_store_p (machine_mode mode, bool is_load)
+{
+ optab op = is_load ? maskload_optab : maskstore_optab;
+ machine_mode vmode;
+ unsigned int vector_sizes;
+
+ /* If mode is vector mode, check it directly. */
+ if (VECTOR_MODE_P (mode))
+ return optab_handler (op, mode) != CODE_FOR_nothing;
+
+ /* Otherwise, return true if there is some vector mode with
+ the mask load/store supported. */
+
+ /* See if there is any chance the mask load or store might be
+ vectorized. If not, punt. */
+ vmode = targetm.vectorize.preferred_simd_mode (mode);
+ if (!VECTOR_MODE_P (vmode))
+ return false;
+
+ if (optab_handler (op, vmode) != CODE_FOR_nothing)
+ return true;
+
+ vector_sizes = targetm.vectorize.autovectorize_vector_sizes ();
+ while (vector_sizes != 0)
+ {
+ unsigned int cur = 1 << floor_log2 (vector_sizes);
+ vector_sizes &= ~cur;
+ if (cur <= GET_MODE_SIZE (mode))
+ continue;
+ vmode = mode_for_vector (mode, cur / GET_MODE_SIZE (mode));
+ if (VECTOR_MODE_P (vmode)
+ && optab_handler (op, vmode) != CODE_FOR_nothing)
+ return true;
+ }
+ return false;
+}
\f
/* Return true if there is a compare_and_swap pattern. */
bool
-can_compare_and_swap_p (enum machine_mode mode, bool allow_libcall)
+can_compare_and_swap_p (machine_mode mode, bool allow_libcall)
{
enum insn_code icode;
/* Return true if an atomic exchange can be performed. */
bool
-can_atomic_exchange_p (enum machine_mode mode, bool allow_libcall)
+can_atomic_exchange_p (machine_mode mode, bool allow_libcall)
{
enum insn_code icode;
static bool
expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq)
{
- enum machine_mode mode = GET_MODE (mem);
- rtx label, cmp_reg, success, oldval;
+ machine_mode mode = GET_MODE (mem);
+ rtx_code_label *label;
+ rtx cmp_reg, success, oldval;
/* The loop we want to generate looks like
success = NULL_RTX;
oldval = cmp_reg;
if (!expand_atomic_compare_and_swap (&success, &oldval, mem, old_reg,
- new_reg, false, MEMMODEL_SEQ_CST,
+ new_reg, false, MEMMODEL_SYNC_SEQ_CST,
MEMMODEL_RELAXED))
return false;
if (oldval != cmp_reg)
emit_move_insn (cmp_reg, oldval);
- /* ??? Mark this jump predicted not taken? */
+ /* Mark this jump predicted not taken. */
emit_cmp_and_jump_insns (success, const0_rtx, EQ, const0_rtx,
- GET_MODE (success), 1, label);
+ GET_MODE (success), 1, label, 0);
return true;
}
static rtx
maybe_emit_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
enum insn_code icode;
/* If the target supports the exchange directly, great. */
create_output_operand (&ops[0], target, mode);
create_fixed_operand (&ops[1], mem);
- /* VAL may have been promoted to a wider mode. Shrink it if so. */
- create_convert_operand_to (&ops[2], val, mode, true);
+ create_input_operand (&ops[2], val, mode);
create_integer_operand (&ops[3], model);
if (maybe_expand_insn (icode, 4, ops))
return ops[0].value;
maybe_emit_sync_lock_test_and_set (rtx target, rtx mem, rtx val,
enum memmodel model)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
enum insn_code icode;
- rtx last_insn = get_last_insn ();
+ rtx_insn *last_insn = get_last_insn ();
icode = optab_handler (sync_lock_test_and_set_optab, mode);
exists, and the memory model is stronger than acquire, add a release
barrier before the instruction. */
- if (model == MEMMODEL_SEQ_CST
- || model == MEMMODEL_RELEASE
- || model == MEMMODEL_ACQ_REL)
+ if (is_mm_seq_cst (model) || is_mm_release (model) || is_mm_acq_rel (model))
expand_mem_thread_fence (model);
if (icode != CODE_FOR_nothing)
struct expand_operand ops[3];
create_output_operand (&ops[0], target, mode);
create_fixed_operand (&ops[1], mem);
- /* VAL may have been promoted to a wider mode. Shrink it if so. */
- create_convert_operand_to (&ops[2], val, mode, true);
+ create_input_operand (&ops[2], val, mode);
if (maybe_expand_insn (icode, 3, ops))
return ops[0].value;
}
static rtx
maybe_emit_compare_and_swap_exchange_loop (rtx target, rtx mem, rtx val)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
if (can_compare_and_swap_p (mode, true))
{
if (!target || !register_operand (target, mode))
target = gen_reg_rtx (mode);
- if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
- val = convert_modes (mode, GET_MODE (val), val, 1);
if (expand_compare_and_swap_loop (mem, target, val, NULL_RTX))
return target;
}
static rtx
maybe_emit_atomic_test_and_set (rtx target, rtx mem, enum memmodel model)
{
- enum machine_mode pat_bool_mode;
+ machine_mode pat_bool_mode;
struct expand_operand ops[3];
if (!HAVE_atomic_test_and_set)
rtx ret;
/* Try an atomic_exchange first. */
- ret = maybe_emit_atomic_exchange (target, mem, val, MEMMODEL_ACQUIRE);
+ ret = maybe_emit_atomic_exchange (target, mem, val, MEMMODEL_SYNC_ACQUIRE);
if (ret)
return ret;
- ret = maybe_emit_sync_lock_test_and_set (target, mem, val, MEMMODEL_ACQUIRE);
+ ret = maybe_emit_sync_lock_test_and_set (target, mem, val,
+ MEMMODEL_SYNC_ACQUIRE);
if (ret)
return ret;
/* If there are no other options, try atomic_test_and_set if the value
being stored is 1. */
if (val == const1_rtx)
- ret = maybe_emit_atomic_test_and_set (target, mem, MEMMODEL_ACQUIRE);
+ ret = maybe_emit_atomic_test_and_set (target, mem, MEMMODEL_SYNC_ACQUIRE);
return ret;
}
rtx
expand_atomic_test_and_set (rtx target, rtx mem, enum memmodel model)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
rtx ret, trueval, subtarget;
ret = maybe_emit_atomic_test_and_set (target, mem, model);
perform the operation. */
if (!ret)
{
- emit_move_insn (subtarget, mem);
+ /* If the result is ignored skip the move to target. */
+ if (subtarget != const0_rtx)
+ emit_move_insn (subtarget, mem);
+
emit_move_insn (mem, trueval);
ret = subtarget;
}
bool is_weak, enum memmodel succ_model,
enum memmodel fail_model)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
struct expand_operand ops[8];
enum insn_code icode;
rtx target_oval, target_bool = NULL_RTX;
icode = direct_optab_handler (atomic_compare_and_swap_optab, mode);
if (icode != CODE_FOR_nothing)
{
- enum machine_mode bool_mode = insn_data[icode].operand[0].mode;
+ machine_mode bool_mode = insn_data[icode].operand[0].mode;
/* Make sure we always have a place for the bool operand. */
if (ptarget_bool == NULL
create_output_operand (&ops[0], target_bool, bool_mode);
create_output_operand (&ops[1], target_oval, mode);
create_fixed_operand (&ops[2], mem);
- create_convert_operand_to (&ops[3], expected, mode, true);
- create_convert_operand_to (&ops[4], desired, mode, true);
+ create_input_operand (&ops[3], expected, mode);
+ create_input_operand (&ops[4], desired, mode);
create_integer_operand (&ops[5], is_weak);
create_integer_operand (&ops[6], succ_model);
create_integer_operand (&ops[7], fail_model);
- expand_insn (icode, 8, ops);
-
- /* Return success/failure. */
- target_bool = ops[0].value;
- target_oval = ops[1].value;
- goto success;
+ if (maybe_expand_insn (icode, 8, ops))
+ {
+ /* Return success/failure. */
+ target_bool = ops[0].value;
+ target_oval = ops[1].value;
+ goto success;
+ }
}
/* Otherwise fall back to the original __sync_val_compare_and_swap
create_output_operand (&ops[0], target_oval, mode);
create_fixed_operand (&ops[1], mem);
- create_convert_operand_to (&ops[2], expected, mode, true);
- create_convert_operand_to (&ops[3], desired, mode, true);
+ create_input_operand (&ops[2], expected, mode);
+ create_input_operand (&ops[3], desired, mode);
if (!maybe_expand_insn (icode, 4, ops))
return false;
/* This routine will either emit the mem_thread_fence pattern or issue a
sync_synchronize to generate a fence for memory model MEMMODEL. */
-#ifndef HAVE_mem_thread_fence
-# define HAVE_mem_thread_fence 0
-# define gen_mem_thread_fence(x) (gcc_unreachable (), NULL_RTX)
-#endif
-#ifndef HAVE_memory_barrier
-# define HAVE_memory_barrier 0
-# define gen_memory_barrier() (gcc_unreachable (), NULL_RTX)
-#endif
-
void
expand_mem_thread_fence (enum memmodel model)
{
if (HAVE_mem_thread_fence)
emit_insn (gen_mem_thread_fence (GEN_INT (model)));
- else if (model != MEMMODEL_RELAXED)
+ else if (!is_mm_relaxed (model))
{
if (HAVE_memory_barrier)
emit_insn (gen_memory_barrier ());
/* This routine will either emit the mem_signal_fence pattern or issue a
sync_synchronize to generate a fence for memory model MEMMODEL. */
-#ifndef HAVE_mem_signal_fence
-# define HAVE_mem_signal_fence 0
-# define gen_mem_signal_fence(x) (gcc_unreachable (), NULL_RTX)
-#endif
-
void
expand_mem_signal_fence (enum memmodel model)
{
if (HAVE_mem_signal_fence)
emit_insn (gen_mem_signal_fence (GEN_INT (model)));
- else if (model != MEMMODEL_RELAXED)
+ 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
rtx
expand_atomic_load (rtx target, rtx mem, enum memmodel model)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
enum insn_code icode;
/* If the target supports the load directly, great. */
if (!target || target == const0_rtx)
target = gen_reg_rtx (mode);
- /* Emit the appropriate barrier before the load. */
- expand_mem_thread_fence (model);
+ /* For SEQ_CST, emit a barrier before the load. */
+ if (is_mm_seq_cst (model))
+ expand_mem_thread_fence (model);
emit_move_insn (target, mem);
- /* For SEQ_CST, also emit a barrier after the load. */
- if (model == MEMMODEL_SEQ_CST)
- expand_mem_thread_fence (model);
+ /* Emit the appropriate barrier after the load. */
+ expand_mem_thread_fence (model);
return target;
}
rtx
expand_atomic_store (rtx mem, rtx val, enum memmodel model, bool use_release)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
enum insn_code icode;
struct expand_operand ops[3];
if (maybe_expand_insn (icode, 2, ops))
{
/* lock_release is only a release barrier. */
- if (model == MEMMODEL_SEQ_CST)
+ if (is_mm_seq_cst (model))
expand_mem_thread_fence (model);
return const0_rtx;
}
/* If the size of the object is greater than word size on this target,
a default store will not be atomic, Try a mem_exchange and throw away
the result. If that doesn't work, don't do anything. */
- if (GET_MODE_PRECISION(mode) > BITS_PER_WORD)
+ if (GET_MODE_PRECISION (mode) > BITS_PER_WORD)
{
rtx target = maybe_emit_atomic_exchange (NULL_RTX, mem, val, model);
if (!target)
return NULL_RTX;
}
- /* If there is no mem_store, default to a move with barriers */
- if (model == MEMMODEL_SEQ_CST || model == MEMMODEL_RELEASE)
- expand_mem_thread_fence (model);
+ /* Otherwise assume stores are atomic, and emit the proper barriers. */
+ expand_mem_thread_fence (model);
emit_move_insn (mem, val);
- /* For SEQ_CST, also emit a barrier after the load. */
- if (model == MEMMODEL_SEQ_CST)
+ /* For SEQ_CST, also emit a barrier after the store. */
+ if (is_mm_seq_cst (model))
expand_mem_thread_fence (model);
return const0_rtx;
maybe_emit_op (const struct atomic_op_functions *optab, rtx target, rtx mem,
rtx val, bool use_memmodel, enum memmodel model, bool after)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
struct expand_operand ops[4];
enum insn_code icode;
int op_counter = 0;
CODE is the operation being performed (OP)
MEMMODEL is the memory model variant to use.
AFTER is true to return the result of the operation (OP_fetch).
- AFTER is false to return the value before the operation (fetch_OP). */
-rtx
-expand_atomic_fetch_op (rtx target, rtx mem, rtx val, enum rtx_code code,
- enum memmodel model, bool after)
+ AFTER is false to return the value before the operation (fetch_OP).
+
+ This function will *only* generate instructions if there is a direct
+ optab. No compare and swap loops or libcalls will be generated. */
+
+static rtx
+expand_atomic_fetch_op_no_fallback (rtx target, rtx mem, rtx val,
+ enum rtx_code code, enum memmodel model,
+ bool after)
{
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
struct atomic_op_functions optab;
rtx result;
bool unused_result = (target == const0_rtx);
}
}
+ /* No direct opcode can be generated. */
+ return NULL_RTX;
+}
+
+
+
+/* This function expands an atomic fetch_OP or OP_fetch operation:
+ TARGET is an option place to stick the return value. const0_rtx indicates
+ the result is unused.
+ atomically fetch MEM, perform the operation with VAL and return it to MEM.
+ CODE is the operation being performed (OP)
+ MEMMODEL is the memory model variant to use.
+ AFTER is true to return the result of the operation (OP_fetch).
+ AFTER is false to return the value before the operation (fetch_OP). */
+rtx
+expand_atomic_fetch_op (rtx target, rtx mem, rtx val, enum rtx_code code,
+ enum memmodel model, bool after)
+{
+ machine_mode mode = GET_MODE (mem);
+ rtx result;
+ bool unused_result = (target == const0_rtx);
+
+ result = expand_atomic_fetch_op_no_fallback (target, mem, val, code, model,
+ after);
+
+ if (result)
+ return result;
+
+ /* Add/sub can be implemented by doing the reverse operation with -(val). */
+ if (code == PLUS || code == MINUS)
+ {
+ rtx tmp;
+ enum rtx_code reverse = (code == PLUS ? MINUS : PLUS);
+
+ start_sequence ();
+ tmp = expand_simple_unop (mode, NEG, val, NULL_RTX, true);
+ result = expand_atomic_fetch_op_no_fallback (target, mem, tmp, reverse,
+ model, after);
+ if (result)
+ {
+ /* PLUS worked so emit the insns and return. */
+ tmp = get_insns ();
+ end_sequence ();
+ emit_insn (tmp);
+ return result;
+ }
+
+ /* PLUS did not work, so throw away the negation code and continue. */
+ end_sequence ();
+ }
+
/* Try the __sync libcalls only if we can't do compare-and-swap inline. */
if (!can_compare_and_swap_p (mode, false))
{
rtx libfunc;
bool fixup = false;
+ enum rtx_code orig_code = code;
+ struct atomic_op_functions optab;
+ get_atomic_op_for_code (&optab, code);
libfunc = optab_libfunc (after ? optab.fetch_after
: optab.fetch_before, mode);
if (libfunc == NULL
true, OPTAB_LIB_WIDEN);
return result;
}
+
+ /* We need the original code for any further attempts. */
+ code = orig_code;
}
/* If nothing else has succeeded, default to a compare and swap loop. */
if (can_compare_and_swap_p (mode, true))
{
- rtx insn;
+ rtx_insn *insn;
rtx t0 = gen_reg_rtx (mode), t1;
start_sequence ();
bool
valid_multiword_target_p (rtx target)
{
- enum machine_mode mode;
+ machine_mode mode;
int i;
mode = GET_MODE (target);
if (!(REG_P (addr) && REGNO (addr) > LAST_VIRTUAL_REGISTER)
&& !side_effects_p (addr))
{
- rtx last;
- enum machine_mode mode;
+ rtx_insn *last;
+ machine_mode mode;
last = get_last_insn ();
- mode = targetm.addr_space.address_mode (MEM_ADDR_SPACE (mem));
+ mode = get_address_mode (mem);
mem = replace_equiv_address (mem, copy_to_mode_reg (mode, addr));
if (insn_operand_matches (icode, opno, mem))
{
maybe_legitimize_operand (enum insn_code icode, unsigned int opno,
struct expand_operand *op)
{
- enum machine_mode mode, imode;
+ machine_mode mode, imode;
bool old_volatile_ok, result;
mode = op->mode;
maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
unsigned int nops, struct expand_operand *ops)
{
- rtx last;
+ rtx_insn *last;
unsigned int i;
last = get_last_insn ();
and emit any necessary set-up code. Return null and emit no
code on failure. */
-rtx
+rtx_insn *
maybe_gen_insn (enum insn_code icode, unsigned int nops,
struct expand_operand *ops)
{
gcc_assert (nops == (unsigned int) insn_data[(int) icode].n_generator_args);
if (!maybe_legitimize_operands (icode, 0, nops, ops))
- return NULL_RTX;
+ return NULL;
switch (nops)
{
return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
ops[3].value, ops[4].value, ops[5].value,
ops[6].value, ops[7].value);
+ case 9:
+ return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value,
+ ops[3].value, ops[4].value, ops[5].value,
+ ops[6].value, ops[7].value, ops[8].value);
}
gcc_unreachable ();
}
gcc_unreachable ();
}
+/* Reduce conditional compilation elsewhere. */
+#ifndef HAVE_insv
+#define HAVE_insv 0
+#define CODE_FOR_insv CODE_FOR_nothing
+#endif
+#ifndef HAVE_extv
+#define HAVE_extv 0
+#define CODE_FOR_extv CODE_FOR_nothing
+#endif
+#ifndef HAVE_extzv
+#define HAVE_extzv 0
+#define CODE_FOR_extzv CODE_FOR_nothing
+#endif
+
+/* Enumerates the possible types of structure operand to an
+ extraction_insn. */
+enum extraction_type { ET_unaligned_mem, ET_reg };
+
+/* Check whether insv, extv or extzv pattern ICODE can be used for an
+ insertion or extraction of type TYPE on a structure of mode MODE.
+ Return true if so and fill in *INSN accordingly. STRUCT_OP is the
+ operand number of the structure (the first sign_extract or zero_extract
+ operand) and FIELD_OP is the operand number of the field (the other
+ side of the set from the sign_extract or zero_extract). */
+
+static bool
+get_traditional_extraction_insn (extraction_insn *insn,
+ enum extraction_type type,
+ machine_mode mode,
+ enum insn_code icode,
+ int struct_op, int field_op)
+{
+ const struct insn_data_d *data = &insn_data[icode];
+
+ machine_mode struct_mode = data->operand[struct_op].mode;
+ if (struct_mode == VOIDmode)
+ struct_mode = word_mode;
+ if (mode != struct_mode)
+ return false;
+
+ machine_mode field_mode = data->operand[field_op].mode;
+ if (field_mode == VOIDmode)
+ field_mode = word_mode;
+
+ machine_mode pos_mode = data->operand[struct_op + 2].mode;
+ if (pos_mode == VOIDmode)
+ pos_mode = word_mode;
+
+ insn->icode = icode;
+ insn->field_mode = field_mode;
+ insn->struct_mode = (type == ET_unaligned_mem ? byte_mode : struct_mode);
+ insn->pos_mode = pos_mode;
+ return true;
+}
+
+/* Return true if an optab exists to perform an insertion or extraction
+ of type TYPE in mode MODE. Describe the instruction in *INSN if so.
+
+ REG_OPTAB is the optab to use for register structures and
+ MISALIGN_OPTAB is the optab to use for misaligned memory structures.
+ POS_OP is the operand number of the bit position. */
+
+static bool
+get_optab_extraction_insn (struct extraction_insn *insn,
+ enum extraction_type type,
+ machine_mode mode, direct_optab reg_optab,
+ direct_optab misalign_optab, int pos_op)
+{
+ direct_optab optab = (type == ET_unaligned_mem ? misalign_optab : reg_optab);
+ enum insn_code icode = direct_optab_handler (optab, mode);
+ if (icode == CODE_FOR_nothing)
+ return false;
+
+ const struct insn_data_d *data = &insn_data[icode];
+
+ insn->icode = icode;
+ insn->field_mode = mode;
+ insn->struct_mode = (type == ET_unaligned_mem ? BLKmode : mode);
+ insn->pos_mode = data->operand[pos_op].mode;
+ if (insn->pos_mode == VOIDmode)
+ insn->pos_mode = word_mode;
+ return true;
+}
+
+/* Return true if an instruction exists to perform an insertion or
+ extraction (PATTERN says which) of type TYPE in mode MODE.
+ Describe the instruction in *INSN if so. */
+
+static bool
+get_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ enum extraction_type type,
+ machine_mode mode)
+{
+ switch (pattern)
+ {
+ case EP_insv:
+ if (HAVE_insv
+ && get_traditional_extraction_insn (insn, type, mode,
+ CODE_FOR_insv, 0, 3))
+ return true;
+ return get_optab_extraction_insn (insn, type, mode, insv_optab,
+ insvmisalign_optab, 2);
+
+ case EP_extv:
+ if (HAVE_extv
+ && get_traditional_extraction_insn (insn, type, mode,
+ CODE_FOR_extv, 1, 0))
+ return true;
+ return get_optab_extraction_insn (insn, type, mode, extv_optab,
+ extvmisalign_optab, 3);
+
+ case EP_extzv:
+ if (HAVE_extzv
+ && get_traditional_extraction_insn (insn, type, mode,
+ CODE_FOR_extzv, 1, 0))
+ return true;
+ return get_optab_extraction_insn (insn, type, mode, extzv_optab,
+ extzvmisalign_optab, 3);
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return true if an instruction exists to access a field of mode
+ FIELDMODE in a structure that has STRUCT_BITS significant bits.
+ Describe the "best" such instruction in *INSN if so. PATTERN and
+ TYPE describe the type of insertion or extraction we want to perform.
+
+ For an insertion, the number of significant structure bits includes
+ all bits of the target. For an extraction, it need only include the
+ most significant bit of the field. Larger widths are acceptable
+ in both cases. */
+
+static bool
+get_best_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ enum extraction_type type,
+ unsigned HOST_WIDE_INT struct_bits,
+ machine_mode field_mode)
+{
+ machine_mode mode = smallest_mode_for_size (struct_bits, MODE_INT);
+ while (mode != VOIDmode)
+ {
+ if (get_extraction_insn (insn, pattern, type, mode))
+ {
+ while (mode != VOIDmode
+ && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (field_mode)
+ && !TRULY_NOOP_TRUNCATION_MODES_P (insn->field_mode,
+ field_mode))
+ {
+ get_extraction_insn (insn, pattern, type, mode);
+ mode = GET_MODE_WIDER_MODE (mode);
+ }
+ return true;
+ }
+ mode = GET_MODE_WIDER_MODE (mode);
+ }
+ return false;
+}
+
+/* Return true if an instruction exists to access a field of mode
+ FIELDMODE in a register structure that has STRUCT_BITS significant bits.
+ Describe the "best" such instruction in *INSN if so. PATTERN describes
+ the type of insertion or extraction we want to perform.
+
+ For an insertion, the number of significant structure bits includes
+ all bits of the target. For an extraction, it need only include the
+ most significant bit of the field. Larger widths are acceptable
+ in both cases. */
+
+bool
+get_best_reg_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ unsigned HOST_WIDE_INT struct_bits,
+ machine_mode field_mode)
+{
+ return get_best_extraction_insn (insn, pattern, ET_reg, struct_bits,
+ field_mode);
+}
+
+/* Return true if an instruction exists to access a field of BITSIZE
+ bits starting BITNUM bits into a memory structure. Describe the
+ "best" such instruction in *INSN if so. PATTERN describes the type
+ of insertion or extraction we want to perform and FIELDMODE is the
+ natural mode of the extracted field.
+
+ The instructions considered here only access bytes that overlap
+ the bitfield; they do not touch any surrounding bytes. */
+
+bool
+get_best_mem_extraction_insn (extraction_insn *insn,
+ enum extraction_pattern pattern,
+ HOST_WIDE_INT bitsize, HOST_WIDE_INT bitnum,
+ machine_mode field_mode)
+{
+ unsigned HOST_WIDE_INT struct_bits = (bitnum % BITS_PER_UNIT
+ + bitsize
+ + BITS_PER_UNIT - 1);
+ struct_bits -= struct_bits % BITS_PER_UNIT;
+ return get_best_extraction_insn (insn, pattern, ET_unaligned_mem,
+ struct_bits, field_mode);
+}
+
+/* Determine whether "1 << x" is relatively cheap in word_mode. */
+
+bool
+lshift_cheap_p (bool speed_p)
+{
+ /* FIXME: This should be made target dependent via this "this_target"
+ mechanism, similar to e.g. can_copy_init_p in gcse.c. */
+ static bool init[2] = { false, false };
+ static bool cheap[2] = { true, true };
+
+ /* If the targer has no lshift in word_mode, the operation will most
+ probably not be cheap. ??? Does GCC even work for such targets? */
+ if (optab_handler (ashl_optab, word_mode) == CODE_FOR_nothing)
+ return false;
+
+ if (!init[speed_p])
+ {
+ rtx reg = gen_raw_REG (word_mode, 10000);
+ int cost = set_src_cost (gen_rtx_ASHIFT (word_mode, const1_rtx, reg),
+ speed_p);
+ cheap[speed_p] = cost < COSTS_N_INSNS (3);
+ init[speed_p] = true;
+ }
+
+ return cheap[speed_p];
+}
+
#include "gt-optabs.h"