Mercurial > hg > CbC > CbC_gcc
diff gcc/optabs.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/optabs.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/optabs.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,7 +1,5 @@ /* Expand the basic unary and binary arithmetic operations, for GNU compiler. - Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - Free Software Foundation, Inc. + Copyright (C) 1987-2017 Free Software Foundation, Inc. This file is part of GCC. @@ -23,139 +21,36 @@ #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "memmodel.h" +#include "predict.h" +#include "tm_p.h" +#include "expmed.h" +#include "optabs.h" +#include "emit-rtl.h" +#include "recog.h" #include "diagnostic-core.h" /* Include insn-config.h before expr.h so that HAVE_conditional_move is properly defined. */ -#include "insn-config.h" -#include "rtl.h" -#include "tree.h" -#include "tm_p.h" -#include "flags.h" -#include "function.h" +#include "stor-layout.h" #include "except.h" +#include "dojump.h" +#include "explow.h" #include "expr.h" -#include "optabs.h" +#include "optabs-tree.h" #include "libfuncs.h" -#include "recog.h" -#include "reload.h" -#include "ggc.h" -#include "basic-block.h" -#include "target.h" - -struct target_optabs default_target_optabs; -struct target_libfuncs default_target_libfuncs; -#if SWITCHABLE_TARGET -struct target_optabs *this_target_optabs = &default_target_optabs; -struct target_libfuncs *this_target_libfuncs = &default_target_libfuncs; -#endif - -#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); + 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); - -/* Prefixes for the current version of decimal floating point (BID vs. DPD) */ -#if ENABLE_DECIMAL_BID_FORMAT -#define DECIMAL_PREFIX "bid_" -#else -#define DECIMAL_PREFIX "dpd_" -#endif - -/* Used for libfunc_hash. */ - -static hashval_t -hash_libfunc (const void *p) -{ - const struct libfunc_entry *const e = (const struct libfunc_entry *) p; - - return (((int) e->mode1 + (int) e->mode2 * NUM_MACHINE_MODES) - ^ e->optab); -} - -/* Used for libfunc_hash. */ - -static int -eq_libfunc (const void *p, const void *q) -{ - 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 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) -{ - struct libfunc_entry e; - struct libfunc_entry **slot; - - e.optab = (size_t) (optab - &convert_optab_table[0]); - e.mode1 = mode1; - e.mode2 = mode2; - slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &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; - } - return (*slot)->libfunc; -} - -/* Return libfunc corresponding operation defined by OPTAB in MODE. - Trigger lazy initialization if needed, return NULL if no libfunc is - available. */ -rtx -optab_libfunc (optab optab, enum machine_mode mode) -{ - struct libfunc_entry e; - struct libfunc_entry **slot; - - e.optab = (size_t) (optab - &optab_table[0]); - e.mode1 = mode; - e.mode2 = VOIDmode; - slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &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; - } - return (*slot)->libfunc; -} - /* Add a REG_EQUAL note to the last insn in INSNS. TARGET is being set to the result of operation CODE applied to OP0 (and OP1 if it is a binary @@ -163,14 +58,15 @@ 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)); @@ -190,7 +86,36 @@ 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; @@ -200,23 +125,33 @@ || ! 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) - note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0)); + switch (code) + { + case FFS: + case CLZ: + case CTZ: + case CLRSB: + case POPCOUNT: + case PARITY: + case BSWAP: + if (GET_MODE (op0) != VOIDmode && GET_MODE (target) != GET_MODE (op0)) + { + note = gen_rtx_fmt_e (code, GET_MODE (op0), copy_rtx (op0)); + if (GET_MODE_UNIT_SIZE (GET_MODE (op0)) + > GET_MODE_UNIT_SIZE (GET_MODE (target))) + note = simplify_gen_unary (TRUNCATE, GET_MODE (target), + note, GET_MODE (op0)); + else + note = simplify_gen_unary (ZERO_EXTEND, GET_MODE (target), + note, GET_MODE (op0)); + break; + } + /* FALLTHRU */ + default: + note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0)); + break; + } else note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1)); @@ -225,6 +160,30 @@ return 1; } +/* Given two input operands, OP0 and OP1, determine what the correct from_mode + for a widening operation would be. In most cases this would be OP0, but if + that's a constant it'll be VOIDmode, which isn't useful. */ + +static machine_mode +widened_mode (machine_mode to_mode, rtx op0, rtx op1) +{ + machine_mode m0 = GET_MODE (op0); + machine_mode m1 = GET_MODE (op1); + machine_mode result; + + if (m0 == VOIDmode && m1 == VOIDmode) + return to_mode; + else if (m0 == VOIDmode || GET_MODE_UNIT_SIZE (m0) < GET_MODE_UNIT_SIZE (m1)) + result = m1; + else + result = m0; + + if (GET_MODE_UNIT_SIZE (result) > GET_MODE_UNIT_SIZE (to_mode)) + return to_mode; + + return result; +} + /* Widen OP to MODE and return the rtx for the widened operand. UNSIGNEDP says whether OP is signed or unsigned. NO_EXTEND is nonzero if we need not actually do a sign-extend or zero-extend, but can leave the @@ -232,10 +191,11 @@ 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; + scalar_int_mode int_mode; /* If we don't have to extend and this is a constant, return it. */ if (no_extend && GET_MODE (op) == VOIDmode) @@ -245,238 +205,25 @@ extend since it will be more efficient to do so unless the signedness of a promoted object differs from our extension. */ if (! no_extend + || !is_a <scalar_int_mode> (mode, &int_mode) || (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); + if (GET_MODE_SIZE (int_mode) <= UNITS_PER_WORD) + return gen_lowpart (int_mode, force_reg (GET_MODE (op), op)); /* Otherwise, get an object of MODE, clobber it, and set the low-order part to OP. */ - result = gen_reg_rtx (mode); + result = gen_reg_rtx (int_mode); emit_clobber (result); emit_move_insn (gen_lowpart (GET_MODE (op), result), op); return result; } -/* Return the optab used for computing the operation given by the tree code, - CODE and the tree EXP. This function is not always usable (for example, it - cannot give complete results for multiplication or division) but probably - ought to be relied on more widely throughout the expander. */ -optab -optab_for_tree_code (enum tree_code code, const_tree type, - enum optab_subtype subtype) -{ - bool trapv; - switch (code) - { - case BIT_AND_EXPR: - return and_optab; - - case BIT_IOR_EXPR: - return ior_optab; - - case BIT_NOT_EXPR: - return one_cmpl_optab; - - case BIT_XOR_EXPR: - return xor_optab; - - case TRUNC_MOD_EXPR: - case CEIL_MOD_EXPR: - case FLOOR_MOD_EXPR: - case ROUND_MOD_EXPR: - return TYPE_UNSIGNED (type) ? umod_optab : smod_optab; - - case RDIV_EXPR: - case TRUNC_DIV_EXPR: - case CEIL_DIV_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; - 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; - - gcc_assert (subtype == optab_scalar); - } - if (TYPE_SATURATING(type)) - return TYPE_UNSIGNED(type) ? usashl_optab : ssashl_optab; - return ashl_optab; - - case RSHIFT_EXPR: - if (TREE_CODE (type) == VECTOR_TYPE) - { - if (subtype == optab_vector) - return TYPE_UNSIGNED (type) ? vlshr_optab : vashr_optab; - - gcc_assert (subtype == optab_scalar); - } - return TYPE_UNSIGNED (type) ? lshr_optab : ashr_optab; - - case LROTATE_EXPR: - if (TREE_CODE (type) == VECTOR_TYPE) - { - if (subtype == optab_vector) - return vrotl_optab; - - gcc_assert (subtype == optab_scalar); - } - return rotl_optab; - - case RROTATE_EXPR: - if (TREE_CODE (type) == VECTOR_TYPE) - { - if (subtype == optab_vector) - return vrotr_optab; - - gcc_assert (subtype == optab_scalar); - } - return rotr_optab; - - case MAX_EXPR: - return TYPE_UNSIGNED (type) ? umax_optab : smax_optab; - - case MIN_EXPR: - return TYPE_UNSIGNED (type) ? umin_optab : smin_optab; - - case REALIGN_LOAD_EXPR: - return vec_realign_load_optab; - - case WIDEN_SUM_EXPR: - return TYPE_UNSIGNED (type) ? usum_widen_optab : ssum_widen_optab; - - case DOT_PROD_EXPR: - return TYPE_UNSIGNED (type) ? udot_prod_optab : sdot_prod_optab; - - case WIDEN_MULT_PLUS_EXPR: - return (TYPE_UNSIGNED (type) - ? (TYPE_SATURATING (type) - ? usmadd_widen_optab : umadd_widen_optab) - : (TYPE_SATURATING (type) - ? ssmadd_widen_optab : smadd_widen_optab)); - - case WIDEN_MULT_MINUS_EXPR: - return (TYPE_UNSIGNED (type) - ? (TYPE_SATURATING (type) - ? usmsub_widen_optab : umsub_widen_optab) - : (TYPE_SATURATING (type) - ? ssmsub_widen_optab : smsub_widen_optab)); - - case FMA_EXPR: - return fma_optab; - - case REDUC_MAX_EXPR: - return TYPE_UNSIGNED (type) ? reduc_umax_optab : reduc_smax_optab; - - case REDUC_MIN_EXPR: - return TYPE_UNSIGNED (type) ? reduc_umin_optab : reduc_smin_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; - - case VEC_WIDEN_MULT_HI_EXPR: - return TYPE_UNSIGNED (type) ? - vec_widen_umult_hi_optab : vec_widen_smult_hi_optab; - - case VEC_WIDEN_MULT_LO_EXPR: - return TYPE_UNSIGNED (type) ? - vec_widen_umult_lo_optab : vec_widen_smult_lo_optab; - - case VEC_UNPACK_HI_EXPR: - return TYPE_UNSIGNED (type) ? - vec_unpacku_hi_optab : vec_unpacks_hi_optab; - - case VEC_UNPACK_LO_EXPR: - return TYPE_UNSIGNED (type) ? - vec_unpacku_lo_optab : vec_unpacks_lo_optab; - - case VEC_UNPACK_FLOAT_HI_EXPR: - /* The signedness is determined from input operand. */ - return TYPE_UNSIGNED (type) ? - vec_unpacku_float_hi_optab : vec_unpacks_float_hi_optab; - - case VEC_UNPACK_FLOAT_LO_EXPR: - /* The signedness is determined from input operand. */ - return TYPE_UNSIGNED (type) ? - vec_unpacku_float_lo_optab : vec_unpacks_float_lo_optab; - - case VEC_PACK_TRUNC_EXPR: - return vec_pack_trunc_optab; - - case VEC_PACK_SAT_EXPR: - return TYPE_UNSIGNED (type) ? vec_pack_usat_optab : vec_pack_ssat_optab; - - case VEC_PACK_FIX_TRUNC_EXPR: - /* The signedness is determined from output operand. */ - return TYPE_UNSIGNED (type) ? - vec_pack_ufix_trunc_optab : vec_pack_sfix_trunc_optab; - - default: - break; - } - - trapv = INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_TRAPS (type); - switch (code) - { - case POINTER_PLUS_EXPR: - case PLUS_EXPR: - 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; - return trapv ? subv_optab : sub_optab; - - case MULT_EXPR: - 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; - return trapv ? negv_optab : neg_optab; - - case ABS_EXPR: - return trapv ? absv_optab : abs_optab; - - case VEC_EXTRACT_EVEN_EXPR: - return vec_extract_even_optab; - - case VEC_EXTRACT_ODD_EXPR: - return vec_extract_odd_optab; - - case VEC_INTERLEAVE_HIGH_EXPR: - return vec_interleave_high_optab; - - case VEC_INTERLEAVE_LOW_EXPR: - return vec_interleave_low_optab; - - default: - return NULL; - } -} - - /* Expand vector widening operations. There are two different classes of operations handled here: @@ -501,15 +248,13 @@ expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op, rtx target, int unsignedp) { + 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; - int icode; - enum machine_mode xmode0, xmode1 = VOIDmode, wxmode = VOIDmode; - rtx temp; - rtx pat; - rtx xop0, xop1, wxop; + enum insn_code icode; int nops = TREE_CODE_LENGTH (ops->code); + int op; oprnd0 = ops->op0; tmode0 = TYPE_MODE (TREE_TYPE (oprnd0)); @@ -517,117 +262,39 @@ optab_for_tree_code (ops->code, TREE_TYPE (oprnd0), optab_default); if (ops->code == WIDEN_MULT_PLUS_EXPR || ops->code == WIDEN_MULT_MINUS_EXPR) - icode = (int) optab_handler (widen_pattern_optab, - TYPE_MODE (TREE_TYPE (ops->op2))); + icode = find_widening_optab_handler (widen_pattern_optab, + TYPE_MODE (TREE_TYPE (ops->op2)), + tmode0, 0); else - icode = (int) optab_handler (widen_pattern_optab, tmode0); + icode = optab_handler (widen_pattern_optab, tmode0); gcc_assert (icode != CODE_FOR_nothing); - xmode0 = insn_data[icode].operand[1].mode; if (nops >= 2) { oprnd1 = ops->op1; tmode1 = TYPE_MODE (TREE_TYPE (oprnd1)); - xmode1 = insn_data[icode].operand[2].mode; } /* The last operand is of a wider mode than the rest of the operands. */ if (nops == 2) - { - wmode = tmode1; - wxmode = xmode1; - } + wmode = tmode1; else if (nops == 3) { gcc_assert (tmode1 == tmode0); gcc_assert (op1); oprnd2 = ops->op2; wmode = TYPE_MODE (TREE_TYPE (oprnd2)); - wxmode = insn_data[icode].operand[3].mode; - } - - if (!wide_op) - wmode = wxmode = insn_data[icode].operand[0].mode; - - if (!target - || ! (*insn_data[icode].operand[0].predicate) (target, wmode)) - temp = gen_reg_rtx (wmode); - else - temp = target; - - xop0 = op0; - xop1 = op1; - wxop = wide_op; - - /* In case the insn wants input operands in modes different from - those of the actual operands, convert the operands. It would - seem that we don't need to convert CONST_INTs, but we do, so - that they're properly zero-extended, sign-extended or truncated - for their mode. */ - - if (GET_MODE (op0) != xmode0 && xmode0 != VOIDmode) - xop0 = convert_modes (xmode0, - GET_MODE (op0) != VOIDmode - ? GET_MODE (op0) - : tmode0, - xop0, unsignedp); - + } + + op = 0; + create_output_operand (&eops[op++], target, TYPE_MODE (ops->type)); + create_convert_operand_from (&eops[op++], op0, tmode0, unsignedp); if (op1) - if (GET_MODE (op1) != xmode1 && xmode1 != VOIDmode) - xop1 = convert_modes (xmode1, - GET_MODE (op1) != VOIDmode - ? GET_MODE (op1) - : tmode1, - xop1, unsignedp); - + create_convert_operand_from (&eops[op++], op1, tmode1, unsignedp); if (wide_op) - if (GET_MODE (wide_op) != wxmode && wxmode != VOIDmode) - wxop = convert_modes (wxmode, - GET_MODE (wide_op) != VOIDmode - ? GET_MODE (wide_op) - : wmode, - wxop, unsignedp); - - /* Now, if insn's predicates don't allow our operands, put them into - pseudo regs. */ - - if (! (*insn_data[icode].operand[1].predicate) (xop0, xmode0) - && xmode0 != VOIDmode) - xop0 = copy_to_mode_reg (xmode0, xop0); - - if (op1) - { - if (! (*insn_data[icode].operand[2].predicate) (xop1, xmode1) - && xmode1 != VOIDmode) - xop1 = copy_to_mode_reg (xmode1, xop1); - - if (wide_op) - { - if (! (*insn_data[icode].operand[3].predicate) (wxop, wxmode) - && wxmode != VOIDmode) - wxop = copy_to_mode_reg (wxmode, wxop); - - pat = GEN_FCN (icode) (temp, xop0, xop1, wxop); - } - else - pat = GEN_FCN (icode) (temp, xop0, xop1); - } - else - { - if (wide_op) - { - if (! (*insn_data[icode].operand[2].predicate) (wxop, wxmode) - && wxmode != VOIDmode) - wxop = copy_to_mode_reg (wxmode, wxop); - - pat = GEN_FCN (icode) (temp, xop0, wxop); - } - else - pat = GEN_FCN (icode) (temp, xop0); - } - - emit_insn (pat); - return temp; + create_convert_operand_from (&eops[op++], wide_op, wmode, unsignedp); + expand_insn (icode, op, eops); + return eops[0].value; } /* Generate code to perform an operation specified by TERNARY_OPTAB @@ -642,70 +309,20 @@ 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) { - int icode = (int) optab_handler (ternary_optab, mode); - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - enum machine_mode mode1 = insn_data[icode].operand[2].mode; - enum machine_mode mode2 = insn_data[icode].operand[3].mode; - rtx temp; - rtx pat; - rtx xop0 = op0, xop1 = op1, xop2 = op2; + struct expand_operand ops[4]; + enum insn_code icode = optab_handler (ternary_optab, mode); gcc_assert (optab_handler (ternary_optab, mode) != CODE_FOR_nothing); - if (!target || !insn_data[icode].operand[0].predicate (target, mode)) - temp = gen_reg_rtx (mode); - else - temp = target; - - /* In case the insn wants input operands in modes different from - those of the actual operands, convert the operands. It would - seem that we don't need to convert CONST_INTs, but we do, so - that they're properly zero-extended, sign-extended or truncated - for their mode. */ - - if (GET_MODE (op0) != mode0 && mode0 != VOIDmode) - xop0 = convert_modes (mode0, - GET_MODE (op0) != VOIDmode - ? GET_MODE (op0) - : mode, - xop0, unsignedp); - - if (GET_MODE (op1) != mode1 && mode1 != VOIDmode) - xop1 = convert_modes (mode1, - GET_MODE (op1) != VOIDmode - ? GET_MODE (op1) - : mode, - xop1, unsignedp); - - if (GET_MODE (op2) != mode2 && mode2 != VOIDmode) - xop2 = convert_modes (mode2, - GET_MODE (op2) != VOIDmode - ? GET_MODE (op2) - : mode, - xop2, unsignedp); - - /* Now, if insn's predicates don't allow our operands, put them into - pseudo regs. */ - - if (!insn_data[icode].operand[1].predicate (xop0, mode0) - && mode0 != VOIDmode) - xop0 = copy_to_mode_reg (mode0, xop0); - - if (!insn_data[icode].operand[2].predicate (xop1, mode1) - && mode1 != VOIDmode) - xop1 = copy_to_mode_reg (mode1, xop1); - - if (!insn_data[icode].operand[3].predicate (xop2, mode2) - && mode2 != VOIDmode) - xop2 = copy_to_mode_reg (mode2, xop2); - - pat = GEN_FCN (icode) (temp, xop0, xop1, xop2); - - emit_insn (pat); - return temp; + create_output_operand (&ops[0], target, mode); + create_convert_operand_from (&ops[1], op0, mode, unsignedp); + create_convert_operand_from (&ops[2], op1, mode, unsignedp); + create_convert_operand_from (&ops[3], op2, mode, unsignedp); + expand_insn (icode, 4, ops); + return ops[0].value; } @@ -713,15 +330,15 @@ calculated at compile time. The arguments and return value are otherwise the same as for expand_binop. */ -static rtx -simplify_expand_binop (enum machine_mode mode, optab binoptab, +rtx +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; } @@ -733,7 +350,7 @@ 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) { @@ -746,59 +363,40 @@ return true; } -/* Generate insns for VEC_LSHIFT_EXPR, VEC_RSHIFT_EXPR. */ - -rtx -expand_vec_shift_expr (sepops ops, rtx target) +/* 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 (machine_mode vmode, rtx op) { enum insn_code icode; - rtx rtx_op1, rtx_op2; - enum machine_mode mode1; - enum machine_mode mode2; - enum machine_mode mode = TYPE_MODE (ops->type); - tree vec_oprnd = ops->op0; - tree shift_oprnd = ops->op1; - optab shift_optab; - rtx pat; - - 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); - - mode1 = insn_data[icode].operand[1].mode; - mode2 = insn_data[icode].operand[2].mode; - - rtx_op1 = expand_normal (vec_oprnd); - if (!(*insn_data[icode].operand[1].predicate) (rtx_op1, mode1) - && mode1 != VOIDmode) - rtx_op1 = force_reg (mode1, rtx_op1); - - rtx_op2 = expand_normal (shift_oprnd); - if (!(*insn_data[icode].operand[2].predicate) (rtx_op2, mode2) - && mode2 != VOIDmode) - rtx_op2 = force_reg (mode2, rtx_op2); - - if (!target - || ! (*insn_data[icode].operand[0].predicate) (target, mode)) - target = gen_reg_rtx (mode); - - /* Emit instruction */ - pat = GEN_FCN (icode) (target, rtx_op1, rtx_op2); - gcc_assert (pat); - emit_insn (pat); - - return target; + rtvec vec; + rtx ret; + int i, n; + + gcc_checking_assert (VECTOR_MODE_P (vmode)); + + n = GET_MODE_NUNITS (vmode); + vec = rtvec_alloc (n); + for (i = 0; i < n; ++i) + RTVEC_ELT (vec, i) = op; + + if (CONSTANT_P (op)) + return gen_rtx_CONST_VECTOR (vmode, vec); + + /* ??? If the target doesn't have a vec_init, then we have no easy way + of performing this operation. Most of this sort of generic support + is hidden away in the vector lowering support in gimple. */ + icode = convert_optab_handler (vec_init_optab, vmode, + GET_MODE_INNER (vmode)); + if (icode == CODE_FOR_nothing) + return NULL; + + ret = gen_reg_rtx (vmode); + emit_insn (GEN_FCN (icode) (ret, gen_rtx_PARALLEL (vmode, vec))); + + return ret; } /* This subroutine of expand_doubleword_shift handles the cases in which @@ -837,7 +435,7 @@ value are the same as for the parent routine. */ static bool -expand_subword_shift (enum machine_mode op1_mode, optab binoptab, +expand_subword_shift (scalar_int_mode op1_mode, optab binoptab, rtx outof_input, rtx into_input, rtx op1, rtx outof_target, rtx into_target, int unsignedp, enum optab_methods methods, @@ -855,7 +453,8 @@ 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); } @@ -870,13 +469,15 @@ 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); } @@ -910,7 +511,6 @@ } -#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 @@ -918,7 +518,7 @@ 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 (scalar_int_mode op1_mode, optab binoptab, enum rtx_code cmp_code, rtx cmp1, rtx cmp2, rtx outof_input, rtx into_input, rtx subword_op1, rtx superword_op1, @@ -970,7 +570,6 @@ 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 @@ -1002,14 +601,13 @@ Return true if the shift could be successfully synthesized. */ static bool -expand_doubleword_shift (enum machine_mode op1_mode, optab binoptab, +expand_doubleword_shift (scalar_int_mode op1_mode, optab binoptab, rtx outof_input, rtx into_input, rtx op1, rtx outof_target, rtx into_target, int unsignedp, enum optab_methods methods, 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 @@ -1039,7 +637,7 @@ 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 @@ -1078,28 +676,28 @@ 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, - 0, 0, subword_label, -1); + 0, 0, subword_label, + profile_probability::uninitialized ()); OK_DEFER_POP; if (!expand_superword_shift (binoptab, outof_input, superword_op1, @@ -1107,7 +705,7 @@ unsignedp, methods)) return false; - emit_jump_insn (gen_jump (done_label)); + emit_jump_insn (targetm.gen_jump (done_label)); emit_barrier (); emit_label (subword_label); @@ -1177,7 +775,7 @@ 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); @@ -1285,11 +883,11 @@ 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); @@ -1323,7 +921,7 @@ static bool shift_optab_p (optab binoptab) { - switch (binoptab->code) + switch (optab_to_code (binoptab)) { case ASHIFT: case SS_ASHIFT: @@ -1344,28 +942,29 @@ 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 || binoptab == umul_highpart_optab); } -/* X is to be used in mode MODE as an operand to BINOPTAB. If we're +/* X is to be used in mode MODE as operand OPN to BINOPTAB. If we're optimizing, and if the operand is a constant that costs more than 1 instruction, force the constant into a register and return that register. Return X otherwise. UNSIGNEDP says whether X is unsigned. */ static rtx -avoid_expensive_constant (enum machine_mode mode, optab binoptab, - rtx x, bool unsignedp) +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, speed) > rtx_cost (x, SET, speed)) + && (rtx_cost (x, mode, optab_to_code (binoptab), opn, speed) + > set_src_cost (x, mode, speed))) { if (CONST_INT_P (x)) { @@ -1384,42 +983,40 @@ 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) { - int icode = (int) optab_handler (binoptab, mode); - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - enum machine_mode mode1 = insn_data[icode].operand[2].mode; - enum machine_mode tmp_mode; + machine_mode from_mode = widened_mode (mode, op0, op1); + enum insn_code icode = find_widening_optab_handler (binoptab, mode, + from_mode, 1); + machine_mode xmode0 = insn_data[(int) icode].operand[1].mode; + machine_mode xmode1 = insn_data[(int) icode].operand[2].mode; + machine_mode mode0, mode1, tmp_mode; + struct expand_operand ops[3]; bool commutative_p; - rtx pat; + rtx_insn *pat; rtx xop0 = op0, xop1 = op1; - rtx temp; - rtx swap; - - if (target) - temp = target; - else - temp = gen_reg_rtx (mode); + bool canonicalize_op1 = false; /* If it is a commutative operator and the modes would match if we would swap the operands, we can save the conversions. */ commutative_p = commutative_optab_p (binoptab); if (commutative_p - && GET_MODE (xop0) != mode0 && GET_MODE (xop1) != mode1 - && GET_MODE (xop0) == mode1 && GET_MODE (xop1) == mode1) - { - swap = xop0; - xop0 = xop1; - xop1 = swap; - } + && GET_MODE (xop0) != xmode0 && GET_MODE (xop1) != xmode1 + && GET_MODE (xop0) == xmode1 && GET_MODE (xop1) == xmode1) + std::swap (xop0, xop1); /* If we are optimizing, force expensive constants into a register. */ - xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp); + xop0 = avoid_expensive_constant (xmode0, binoptab, 0, xop0, unsignedp); if (!shift_optab_p (binoptab)) - xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp); + xop1 = avoid_expensive_constant (xmode1, binoptab, 1, xop1, unsignedp); + else + /* Shifts and rotates often use a different mode for op1 from op0; + for VOIDmode constants we don't know the mode, so force it + to be canonicalized using convert_modes. */ + canonicalize_op1 = true; /* In case the insn wants input operands in modes different from those of the actual operands, convert the operands. It would @@ -1427,19 +1024,20 @@ that they're properly zero-extended, sign-extended or truncated for their mode. */ - if (GET_MODE (xop0) != mode0 && mode0 != VOIDmode) - xop0 = convert_modes (mode0, - GET_MODE (xop0) != VOIDmode - ? GET_MODE (xop0) - : mode, - xop0, unsignedp); - - if (GET_MODE (xop1) != mode1 && mode1 != VOIDmode) - xop1 = convert_modes (mode1, - GET_MODE (xop1) != VOIDmode - ? GET_MODE (xop1) - : mode, - xop1, unsignedp); + mode0 = GET_MODE (xop0) != VOIDmode ? GET_MODE (xop0) : mode; + if (xmode0 != VOIDmode && xmode0 != mode0) + { + xop0 = convert_modes (xmode0, mode0, xop0, unsignedp); + mode0 = xmode0; + } + + mode1 = ((GET_MODE (xop1) != VOIDmode || canonicalize_op1) + ? GET_MODE (xop1) : mode); + if (xmode1 != VOIDmode && xmode1 != mode1) + { + xop1 = convert_modes (xmode1, mode1, xop1, unsignedp); + mode1 = xmode1; + } /* If operation is commutative, try to make the first operand a register. @@ -1447,23 +1045,11 @@ 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. */ - if (!insn_data[icode].operand[1].predicate (xop0, mode0) - && mode0 != VOIDmode) - xop0 = copy_to_mode_reg (mode0, xop0); - - if (!insn_data[icode].operand[2].predicate (xop1, mode1) - && mode1 != VOIDmode) - xop1 = copy_to_mode_reg (mode1, xop1); - if (binoptab == vec_pack_trunc_optab || binoptab == vec_pack_usat_optab || binoptab == vec_pack_ssat_optab @@ -1472,24 +1058,30 @@ { /* The mode of the result is different then the mode of the arguments. */ - tmp_mode = insn_data[icode].operand[0].mode; - if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode)) - return 0; + tmp_mode = insn_data[(int) icode].operand[0].mode; + if (VECTOR_MODE_P (mode) + && GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode)) + { + delete_insns_since (last); + return NULL_RTX; + } } else tmp_mode = mode; - if (!insn_data[icode].operand[0].predicate (temp, tmp_mode)) - temp = gen_reg_rtx (tmp_mode); - - pat = GEN_FCN (icode) (temp, xop0, xop1); + create_output_operand (&ops[0], target, tmp_mode); + create_input_operand (&ops[1], xop0, mode0); + create_input_operand (&ops[2], xop1, mode1); + pat = maybe_gen_insn (icode, 3, ops); if (pat) { /* If PAT is composed of more than one insn, try to add an appropriate 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, temp, binoptab->code, xop0, xop1)) + && ! add_equal_note (pat, ops[0].value, + optab_to_code (binoptab), + ops[1].value, ops[2].value)) { delete_insns_since (last); return expand_binop (mode, binoptab, op0, op1, NULL_RTX, @@ -1497,9 +1089,8 @@ } emit_insn (pat); - return temp; - } - + return ops[0].value; + } delete_insns_since (last); return NULL_RTX; } @@ -1516,18 +1107,19 @@ 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; + scalar_int_mode int_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); @@ -1539,6 +1131,16 @@ op1 = negate_rtx (mode, op1); binoptab = add_optab; } + /* For shifts, constant invalid op1 might be expanded from different + mode than MODE. As those are invalid, force them to a register + to avoid further problems during expansion. */ + else if (CONST_INT_P (op1) + && shift_optab_p (binoptab) + && UINTVAL (op1) >= GET_MODE_BITSIZE (GET_MODE_INNER (mode))) + { + op1 = gen_int_mode (INTVAL (op1), GET_MODE_INNER (mode)); + op1 = force_reg (GET_MODE_INNER (mode), op1); + } /* Record where to delete back to if we backtrack. */ last = get_last_insn (); @@ -1546,7 +1148,9 @@ /* If we can do it with a three-operand insn, do so. */ if (methods != OPTAB_MUST_WIDEN - && optab_handler (binoptab, mode) != CODE_FOR_nothing) + && find_widening_optab_handler (binoptab, mode, + widened_mode (mode, op0, op1), 1) + != CODE_FOR_nothing) { temp = expand_binop_directly (mode, binoptab, op0, op1, target, unsignedp, methods, last); @@ -1560,22 +1164,22 @@ && optab_handler (rotr_optab, mode) != CODE_FOR_nothing) || (binoptab == rotr_optab && optab_handler (rotl_optab, mode) != CODE_FOR_nothing)) - && mclass == MODE_INT) + && is_int_mode (mode, &int_mode)) { optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab); rtx newop1; - unsigned int bits = GET_MODE_BITSIZE (mode); + unsigned int bits = GET_MODE_PRECISION (int_mode); if (CONST_INT_P (op1)) newop1 = GEN_INT (bits - INTVAL (op1)); - else if (targetm.shift_truncation_mask (mode) == bits - 1) + else if (targetm.shift_truncation_mask (int_mode) == bits - 1) 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, + temp = expand_binop_directly (int_mode, otheroptab, op0, newop1, target, unsignedp, methods, last); if (temp) return temp; @@ -1585,42 +1189,81 @@ takes operands of this mode and makes a wider mode. */ if (binoptab == smul_optab - && GET_MODE_WIDER_MODE (mode) != VOIDmode - && (optab_handler ((unsignedp ? umul_widen_optab : smul_widen_optab), - GET_MODE_WIDER_MODE (mode)) - != CODE_FOR_nothing)) - { - temp = expand_binop (GET_MODE_WIDER_MODE (mode), + && GET_MODE_2XWIDER_MODE (mode).exists (&wider_mode) + && (convert_optab_handler ((unsignedp + ? umul_widen_optab + : smul_widen_optab), + wider_mode, mode) != CODE_FOR_nothing)) + { + temp = expand_binop (wider_mode, unsignedp ? umul_widen_optab : smul_widen_optab, op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT); if (temp != 0) { if (GET_MODE_CLASS (mode) == MODE_INT - && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), - GET_MODE_BITSIZE (GET_MODE (temp)))) + && TRULY_NOOP_TRUNCATION_MODES_P (mode, GET_MODE (temp))) return gen_lowpart (mode, temp); else return convert_to_mode (mode, temp, unsignedp); } } + /* If this is a vector shift by a scalar, see if we can do a vector + shift by a vector. If so, broadcast the scalar into a vector. */ + if (mclass == MODE_VECTOR_INT) + { + optab otheroptab = unknown_optab; + + if (binoptab == ashl_optab) + otheroptab = vashl_optab; + else if (binoptab == ashr_optab) + otheroptab = vashr_optab; + else if (binoptab == lshr_optab) + otheroptab = vlshr_optab; + else if (binoptab == rotl_optab) + otheroptab = vrotl_optab; + else if (binoptab == rotr_optab) + otheroptab = vrotr_optab; + + if (otheroptab && optab_handler (otheroptab, mode) != CODE_FOR_nothing) + { + /* The scalar may have been extended to be too wide. Truncate + it back to the proper size to fit in the broadcast vector. */ + scalar_mode inner_mode = GET_MODE_INNER (mode); + if (!CONST_INT_P (op1) + && (GET_MODE_BITSIZE (as_a <scalar_int_mode> (GET_MODE (op1))) + > GET_MODE_BITSIZE (inner_mode))) + op1 = force_reg (inner_mode, + simplify_gen_unary (TRUNCATE, inner_mode, op1, + GET_MODE (op1))); + rtx vop1 = expand_vector_broadcast (mode, op1); + if (vop1) + { + temp = expand_binop_directly (mode, otheroptab, op0, vop1, + target, unsignedp, methods, last); + if (temp) + return temp; + } + } + } + /* Look for a wider mode of the same class for which we think we can open-code the operation. Check for a widening multiply at the wider mode as well. */ if (CLASS_HAS_WIDER_MODES_P (mclass) && methods != OPTAB_DIRECT && methods != OPTAB_LIB) - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + FOR_EACH_WIDER_MODE (wider_mode, mode) { + machine_mode next_mode; if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing || (binoptab == smul_optab - && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode - && (optab_handler ((unsignedp ? umul_widen_optab - : smul_widen_optab), - GET_MODE_WIDER_MODE (wider_mode)) + && GET_MODE_WIDER_MODE (wider_mode).exists (&next_mode) + && (find_widening_optab_handler ((unsignedp + ? umul_widen_optab + : smul_widen_optab), + next_mode, mode, 0) != CODE_FOR_nothing))) { rtx xop0 = op0, xop1 = op1; @@ -1637,10 +1280,10 @@ && mclass == MODE_INT) { no_extend = 1; - xop0 = avoid_expensive_constant (mode, binoptab, + xop0 = avoid_expensive_constant (mode, binoptab, 0, xop0, unsignedp); if (binoptab != ashl_optab) - xop1 = avoid_expensive_constant (mode, binoptab, + xop1 = avoid_expensive_constant (mode, binoptab, 1, xop1, unsignedp); } @@ -1655,8 +1298,7 @@ if (temp) { if (mclass != MODE_INT - || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), - GET_MODE_BITSIZE (wider_mode))) + || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode)) { if (target == 0) target = gen_reg_rtx (mode); @@ -1677,35 +1319,34 @@ 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) - && mclass == MODE_INT - && GET_MODE_SIZE (mode) > UNITS_PER_WORD + && is_int_mode (mode, &int_mode) + && GET_MODE_SIZE (int_mode) > UNITS_PER_WORD && 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. */ - if (target == 0 || target == op0 || target == op1) - target = gen_reg_rtx (mode); + if (target == 0 + || target == op0 + || target == op1 + || !valid_multiword_target_p (target)) + target = gen_reg_rtx (int_mode); start_sequence (); /* Do the actual arithmetic. */ - for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) + for (i = 0; i < GET_MODE_BITSIZE (int_mode) / BITS_PER_WORD; i++) { - rtx target_piece = operand_subword (target, i, 1, mode); + rtx target_piece = operand_subword (target, i, 1, int_mode); rtx x = expand_binop (word_mode, binoptab, - operand_subword_force (op0, i, mode), - operand_subword_force (op1, i, mode), + operand_subword_force (op0, i, int_mode), + operand_subword_force (op1, i, int_mode), target_piece, unsignedp, next_methods); if (x == 0) @@ -1718,7 +1359,7 @@ insns = get_insns (); end_sequence (); - if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) + if (i == GET_MODE_BITSIZE (int_mode) / BITS_PER_WORD) { emit_insn (insns); return target; @@ -1728,19 +1369,22 @@ /* Synthesize double word shifts from single word shifts. */ if ((binoptab == lshr_optab || binoptab == ashl_optab || binoptab == ashr_optab) - && mclass == MODE_INT + && is_int_mode (mode, &int_mode) && (CONST_INT_P (op1) || optimize_insn_for_speed_p ()) - && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD + && GET_MODE_PRECISION (int_mode) == GET_MODE_BITSIZE (int_mode) && optab_handler (binoptab, word_mode) != CODE_FOR_nothing && optab_handler (ashl_optab, word_mode) != CODE_FOR_nothing && optab_handler (lshr_optab, word_mode) != CODE_FOR_nothing) { unsigned HOST_WIDE_INT shift_mask, double_shift_mask; - enum machine_mode op1_mode; - - double_shift_mask = targetm.shift_truncation_mask (mode); + scalar_int_mode op1_mode; + + double_shift_mask = targetm.shift_truncation_mask (int_mode); shift_mask = targetm.shift_truncation_mask (word_mode); - op1_mode = GET_MODE (op1) != VOIDmode ? GET_MODE (op1) : word_mode; + op1_mode = (GET_MODE (op1) != VOIDmode + ? as_a <scalar_int_mode> (GET_MODE (op1)) + : word_mode); /* Apply the truncation to constant shifts. */ if (double_shift_mask > 0 && CONST_INT_P (op1)) @@ -1755,15 +1399,18 @@ || (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; /* If TARGET is the same as one of the operands, the REG_EQUAL note won't be accurate, so use a new target. */ - if (target == 0 || target == op0 || target == op1) - target = gen_reg_rtx (mode); + if (target == 0 + || target == op0 + || target == op1 + || !valid_multiword_target_p (target)) + target = gen_reg_rtx (int_mode); start_sequence (); @@ -1775,11 +1422,11 @@ left_shift = binoptab == ashl_optab; outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; - outof_target = operand_subword (target, outof_word, 1, mode); - into_target = operand_subword (target, 1 - outof_word, 1, mode); - - outof_input = operand_subword_force (op0, outof_word, mode); - into_input = operand_subword_force (op0, 1 - outof_word, mode); + outof_target = operand_subword (target, outof_word, 1, int_mode); + into_target = operand_subword (target, 1 - outof_word, 1, int_mode); + + outof_input = operand_subword_force (op0, outof_word, int_mode); + into_input = operand_subword_force (op0, 1 - outof_word, int_mode); if (expand_doubleword_shift (op1_mode, binoptab, outof_input, into_input, op1, @@ -1798,13 +1445,13 @@ /* Synthesize double word rotates from single word shifts. */ if ((binoptab == rotl_optab || binoptab == rotr_optab) - && mclass == MODE_INT + && is_int_mode (mode, &int_mode) && CONST_INT_P (op1) - && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && GET_MODE_PRECISION (int_mode) == 2 * BITS_PER_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; @@ -1816,8 +1463,12 @@ opportunities, and second because if target and op0 happen to be MEMs designating the same location, we would risk clobbering it too early in the code sequence we generate below. */ - if (target == 0 || target == op0 || target == op1 || ! REG_P (target)) - target = gen_reg_rtx (mode); + if (target == 0 + || target == op0 + || target == op1 + || !REG_P (target) + || !valid_multiword_target_p (target)) + target = gen_reg_rtx (int_mode); start_sequence (); @@ -1831,11 +1482,11 @@ left_shift = (binoptab == rotl_optab); outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; - outof_target = operand_subword (target, outof_word, 1, mode); - into_target = operand_subword (target, 1 - outof_word, 1, mode); - - outof_input = operand_subword_force (op0, outof_word, mode); - into_input = operand_subword_force (op0, 1 - outof_word, mode); + outof_target = operand_subword (target, outof_word, 1, int_mode); + into_target = operand_subword (target, 1 - outof_word, 1, int_mode); + + outof_input = operand_subword_force (op0, outof_word, int_mode); + into_input = operand_subword_force (op0, 1 - outof_word, int_mode); if (shift_count == BITS_PER_WORD) { @@ -1911,13 +1562,13 @@ /* These can be done a word at a time by propagating carries. */ if ((binoptab == add_optab || binoptab == sub_optab) - && mclass == MODE_INT - && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD + && is_int_mode (mode, &int_mode) + && GET_MODE_SIZE (int_mode) >= 2 * UNITS_PER_WORD && optab_handler (binoptab, word_mode) != CODE_FOR_nothing) { unsigned int i; optab otheroptab = binoptab == add_optab ? sub_optab : add_optab; - const unsigned int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; + const unsigned int nwords = GET_MODE_BITSIZE (int_mode) / BITS_PER_WORD; rtx carry_in = NULL_RTX, carry_out = NULL_RTX; rtx xop0, xop1, xtarget; @@ -1931,12 +1582,12 @@ #endif /* Prepare the operands. */ - xop0 = force_reg (mode, op0); - xop1 = force_reg (mode, op1); - - xtarget = gen_reg_rtx (mode); - - if (target == 0 || !REG_P (target)) + xop0 = force_reg (int_mode, op0); + xop1 = force_reg (int_mode, op1); + + xtarget = gen_reg_rtx (int_mode); + + if (target == 0 || !REG_P (target) || !valid_multiword_target_p (target)) target = xtarget; /* Indicate for flow that the entire target reg is being set. */ @@ -1947,9 +1598,9 @@ for (i = 0; i < nwords; i++) { int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); - rtx target_piece = operand_subword (xtarget, index, 1, mode); - rtx op0_piece = operand_subword_force (xop0, index, mode); - rtx op1_piece = operand_subword_force (xop1, index, mode); + rtx target_piece = operand_subword (xtarget, index, 1, int_mode); + rtx op0_piece = operand_subword_force (xop0, index, int_mode); + rtx op1_piece = operand_subword_force (xop1, index, int_mode); rtx x; /* Main add/subtract of the input operands. */ @@ -2008,18 +1659,18 @@ carry_in = carry_out; } - if (i == GET_MODE_BITSIZE (mode) / (unsigned) BITS_PER_WORD) + if (i == GET_MODE_BITSIZE (int_mode) / (unsigned) BITS_PER_WORD) { - if (optab_handler (mov_optab, mode) != CODE_FOR_nothing + if (optab_handler (mov_optab, int_mode) != CODE_FOR_nothing || ! rtx_equal_p (target, xtarget)) { - rtx temp = emit_move_insn (target, xtarget); - - set_unique_reg_note (temp, - REG_EQUAL, - gen_rtx_fmt_ee (binoptab->code, mode, - copy_rtx (xop0), - copy_rtx (xop1))); + rtx_insn *temp = emit_move_insn (target, xtarget); + + set_dst_reg_note (temp, REG_EQUAL, + gen_rtx_fmt_ee (optab_to_code (binoptab), + int_mode, copy_rtx (xop0), + copy_rtx (xop1)), + target); } else target = xtarget; @@ -2037,25 +1688,26 @@ try using a signed widening multiply. */ if (binoptab == smul_optab - && mclass == MODE_INT - && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && is_int_mode (mode, &int_mode) + && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD && optab_handler (smul_optab, word_mode) != CODE_FOR_nothing && optab_handler (add_optab, word_mode) != CODE_FOR_nothing) { rtx product = NULL_RTX; - - if (optab_handler (umul_widen_optab, mode) != CODE_FOR_nothing) + if (widening_optab_handler (umul_widen_optab, int_mode, word_mode) + != CODE_FOR_nothing) { - product = expand_doubleword_mult (mode, op0, op1, target, + product = expand_doubleword_mult (int_mode, op0, op1, target, true, methods); if (!product) delete_insns_since (last); } if (product == NULL_RTX - && optab_handler (smul_widen_optab, mode) != CODE_FOR_nothing) + && (widening_optab_handler (smul_widen_optab, int_mode, word_mode) + != CODE_FOR_nothing)) { - product = expand_doubleword_mult (mode, op0, op1, target, + product = expand_doubleword_mult (int_mode, op0, op1, target, false, methods); if (!product) delete_insns_since (last); @@ -2063,14 +1715,16 @@ if (product != NULL_RTX) { - if (optab_handler (mov_optab, mode) != CODE_FOR_nothing) + if (optab_handler (mov_optab, int_mode) != CODE_FOR_nothing) { - temp = emit_move_insn (target ? target : product, product); - set_unique_reg_note (temp, - REG_EQUAL, - gen_rtx_fmt_ee (MULT, mode, - copy_rtx (op0), - copy_rtx (op1))); + rtx_insn *move = emit_move_insn (target ? target : product, + product); + set_dst_reg_note (move, + REG_EQUAL, + gen_rtx_fmt_ee (MULT, int_mode, + copy_rtx (op0), + copy_rtx (op1)), + target ? target : product); } return product; } @@ -2083,9 +1737,9 @@ 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 (); @@ -2105,15 +1759,18 @@ /* Pass 1 for NO_QUEUE so we don't lose any increments if the libcall is cse'd or moved. */ value = emit_library_call_value (libfunc, - NULL_RTX, LCT_CONST, mode, 2, + NULL_RTX, LCT_CONST, mode, op0, mode, op1x, op1_mode); insns = get_insns (); end_sequence (); + bool trapv = trapv_binoptab_p (binoptab); target = gen_reg_rtx (mode); - emit_libcall_block (insns, target, value, - gen_rtx_fmt_ee (binoptab->code, mode, op0, op1)); + emit_libcall_block_1 (insns, target, value, + trapv ? NULL_RTX + : gen_rtx_fmt_ee (optab_to_code (binoptab), + mode, op0, op1), trapv); return target; } @@ -2140,11 +1797,10 @@ if (CLASS_HAS_WIDER_MODES_P (mclass)) { - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + FOR_EACH_WIDER_MODE (wider_mode, mode) { - if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing + if (find_widening_optab_handler (binoptab, wider_mode, mode, 1) + != CODE_FOR_nothing || (methods == OPTAB_LIB && optab_libfunc (binoptab, wider_mode))) { @@ -2174,8 +1830,7 @@ if (temp) { if (mclass != MODE_INT - || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), - GET_MODE_BITSIZE (wider_mode))) + || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode)) { if (target == 0) target = gen_reg_rtx (mode); @@ -2203,13 +1858,13 @@ 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, @@ -2217,38 +1872,38 @@ 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; - - temp = expand_binop (mode, &wide_soptab, op0, op1, target, + /* 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, 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; } /* Generate code to perform an operation specified by UNOPPTAB @@ -2266,11 +1921,11 @@ 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); @@ -2284,41 +1939,21 @@ if (optab_handler (unoptab, mode) != CODE_FOR_nothing) { - int icode = (int) optab_handler (unoptab, mode); - enum machine_mode mode0 = insn_data[icode].operand[2].mode; - rtx pat; - rtx xop0 = op0; - - if (GET_MODE (xop0) != VOIDmode - && GET_MODE (xop0) != mode0) - xop0 = convert_to_mode (mode0, xop0, unsignedp); - - /* Now, if insn doesn't accept these operands, put them into pseudos. */ - if (!insn_data[icode].operand[2].predicate (xop0, mode0)) - xop0 = copy_to_mode_reg (mode0, xop0); - - /* We could handle this, but we should always be called with a pseudo - for our targets and all insns should take them as outputs. */ - gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode)); - gcc_assert (insn_data[icode].operand[1].predicate (targ1, mode)); - - pat = GEN_FCN (icode) (targ0, targ1, xop0); - if (pat) - { - emit_insn (pat); - return 1; - } - else - delete_insns_since (last); + struct expand_operand ops[3]; + enum insn_code icode = optab_handler (unoptab, mode); + + create_fixed_operand (&ops[0], targ0); + create_fixed_operand (&ops[1], targ1); + create_convert_operand_from (&ops[2], op0, mode, unsignedp); + if (maybe_expand_insn (icode, 3, ops)) + return 1; } /* It can't be done in this mode. Can we do it in a wider mode? */ if (CLASS_HAS_WIDER_MODES_P (mclass)) { - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + FOR_EACH_WIDER_MODE (wider_mode, mode) { if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing) { @@ -2358,11 +1993,11 @@ 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); @@ -2376,65 +2011,30 @@ if (optab_handler (binoptab, mode) != CODE_FOR_nothing) { - int icode = (int) optab_handler (binoptab, mode); - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - enum machine_mode mode1 = insn_data[icode].operand[2].mode; - rtx pat; + struct expand_operand ops[4]; + enum insn_code icode = optab_handler (binoptab, 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. */ - xop0 = avoid_expensive_constant (mode0, binoptab, xop0, unsignedp); - xop1 = avoid_expensive_constant (mode1, binoptab, xop1, unsignedp); - - /* In case the insn wants input operands in modes different from - those of the actual operands, convert the operands. It would - seem that we don't need to convert CONST_INTs, but we do, so - that they're properly zero-extended, sign-extended or truncated - for their mode. */ - - if (GET_MODE (op0) != mode0 && mode0 != VOIDmode) - xop0 = convert_modes (mode0, - GET_MODE (op0) != VOIDmode - ? GET_MODE (op0) - : mode, - xop0, unsignedp); - - if (GET_MODE (op1) != mode1 && mode1 != VOIDmode) - xop1 = convert_modes (mode1, - GET_MODE (op1) != VOIDmode - ? GET_MODE (op1) - : mode, - xop1, unsignedp); - - /* Now, if insn doesn't accept these operands, put them into pseudos. */ - if (!insn_data[icode].operand[1].predicate (xop0, mode0)) - xop0 = copy_to_mode_reg (mode0, xop0); - - if (!insn_data[icode].operand[2].predicate (xop1, mode1)) - xop1 = copy_to_mode_reg (mode1, xop1); - - /* We could handle this, but we should always be called with a pseudo - for our targets and all insns should take them as outputs. */ - gcc_assert (insn_data[icode].operand[0].predicate (targ0, mode)); - gcc_assert (insn_data[icode].operand[3].predicate (targ1, mode)); - - pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1); - if (pat) - { - emit_insn (pat); - return 1; - } - else - delete_insns_since (last); + xop0 = avoid_expensive_constant (mode0, binoptab, 0, xop0, unsignedp); + xop1 = avoid_expensive_constant (mode1, binoptab, 1, xop1, unsignedp); + + create_fixed_operand (&ops[0], targ0); + create_convert_operand_from (&ops[1], op0, mode, unsignedp); + create_convert_operand_from (&ops[2], op1, mode, unsignedp); + create_fixed_operand (&ops[3], targ1); + if (maybe_expand_insn (icode, 4, ops)) + return 1; + delete_insns_since (last); } /* It can't be done in this mode. Can we do it in a wider mode? */ if (CLASS_HAS_WIDER_MODES_P (mclass)) { - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + FOR_EACH_WIDER_MODE (wider_mode, mode) { if (optab_handler (binoptab, wider_mode) != CODE_FOR_nothing) { @@ -2473,10 +2073,10 @@ 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. */ @@ -2489,11 +2089,10 @@ /* The value returned by the library function will have twice as many bits as the nominal MODE. */ - libval_mode = smallest_mode_for_size (2 * GET_MODE_BITSIZE (mode), - MODE_INT); + libval_mode = smallest_int_mode_for_size (2 * GET_MODE_BITSIZE (mode)); start_sequence (); libval = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, - libval_mode, 2, + libval_mode, op0, mode, op1, mode); /* Get the part of VAL containing the value that we want. */ @@ -2513,10 +2112,10 @@ 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); @@ -2525,38 +2124,41 @@ /* Try calculating (clz:narrow x) as - (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)). */ + (clz:wide (zero_extend:wide x)) - ((width wide) - (width narrow)). + + A similar operation can be used for clrsb. UNOPTAB says which operation + we are trying to expand. */ static rtx -widen_clz (enum machine_mode mode, rtx op0, rtx target) +widen_leading (scalar_int_mode mode, rtx op0, rtx target, optab unoptab) { - enum mode_class mclass = GET_MODE_CLASS (mode); - if (CLASS_HAS_WIDER_MODES_P (mclass)) - { - enum machine_mode wider_mode; - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + opt_scalar_int_mode wider_mode_iter; + FOR_EACH_WIDER_MODE (wider_mode_iter, mode) + { + scalar_int_mode wider_mode = wider_mode_iter.require (); + if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing) { - if (optab_handler (clz_optab, wider_mode) != CODE_FOR_nothing) - { - rtx xop0, temp, last; - - last = get_last_insn (); - - if (target == 0) - target = gen_reg_rtx (mode); - xop0 = widen_operand (op0, wider_mode, mode, true, false); - temp = expand_unop (wider_mode, clz_optab, xop0, NULL_RTX, true); - if (temp != 0) - temp = expand_binop (wider_mode, sub_optab, temp, - GEN_INT (GET_MODE_BITSIZE (wider_mode) - - GET_MODE_BITSIZE (mode)), - target, true, OPTAB_DIRECT); - if (temp == 0) - delete_insns_since (last); - - return temp; - } + rtx xop0, temp; + rtx_insn *last; + + last = get_last_insn (); + + if (target == 0) + target = gen_reg_rtx (mode); + xop0 = widen_operand (op0, wider_mode, mode, + unoptab != clrsb_optab, false); + temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX, + unoptab != clrsb_optab); + if (temp != 0) + temp = expand_binop + (wider_mode, sub_optab, temp, + gen_int_mode (GET_MODE_PRECISION (wider_mode) + - GET_MODE_PRECISION (mode), + wider_mode), + target, true, OPTAB_DIRECT); + if (temp == 0) + delete_insns_since (last); + + return temp; } } return 0; @@ -2565,14 +2167,15 @@ /* 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 (scalar_int_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 @@ -2599,7 +2202,7 @@ if (temp != result) convert_move (result, temp, true); - emit_jump_insn (gen_jump (after_label)); + emit_jump_insn (targetm.gen_jump (after_label)); emit_barrier (); /* Else clz of the full value is clz of the low word plus the number @@ -2610,7 +2213,7 @@ 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; @@ -2632,37 +2235,89 @@ return 0; } +/* Try calculating popcount of a double-word quantity as two popcount's of + word-sized quantities and summing up the results. */ +static rtx +expand_doubleword_popcount (scalar_int_mode mode, rtx op0, rtx target) +{ + rtx t0, t1, t; + rtx_insn *seq; + + start_sequence (); + + t0 = expand_unop_direct (word_mode, popcount_optab, + operand_subword_force (op0, 0, mode), NULL_RTX, + true); + t1 = expand_unop_direct (word_mode, popcount_optab, + operand_subword_force (op0, 1, mode), NULL_RTX, + true); + if (!t0 || !t1) + { + end_sequence (); + return NULL_RTX; + } + + /* If we were not given a target, use a word_mode register, not a + 'mode' register. The result will fit, and nobody is expecting + anything bigger (the return type of __builtin_popcount* is int). */ + if (!target) + target = gen_reg_rtx (word_mode); + + t = expand_binop (word_mode, add_optab, t0, t1, target, 0, OPTAB_DIRECT); + + seq = get_insns (); + end_sequence (); + + add_equal_note (seq, t, POPCOUNT, op0, 0); + emit_insn (seq); + return t; +} + +/* Try calculating + (parity:wide x) + as + (parity:narrow (low (x) ^ high (x))) */ +static rtx +expand_doubleword_parity (scalar_int_mode mode, rtx op0, rtx target) +{ + rtx t = expand_binop (word_mode, xor_optab, + operand_subword_force (op0, 0, mode), + operand_subword_force (op0, 1, mode), + NULL_RTX, 0, OPTAB_DIRECT); + return expand_unop (word_mode, parity_optab, t, target, true); +} + /* Try calculating (bswap:narrow x) as (lshiftrt:wide (bswap:wide x) ((width wide) - (width narrow))). */ static rtx -widen_bswap (enum machine_mode mode, rtx op0, rtx target) +widen_bswap (scalar_int_mode mode, rtx op0, rtx target) { - enum mode_class mclass = GET_MODE_CLASS (mode); - enum machine_mode wider_mode; - rtx x, last; - - if (!CLASS_HAS_WIDER_MODES_P (mclass)) + rtx x; + rtx_insn *last; + opt_scalar_int_mode wider_mode_iter; + + FOR_EACH_WIDER_MODE (wider_mode_iter, mode) + if (optab_handler (bswap_optab, wider_mode_iter.require ()) + != CODE_FOR_nothing) + break; + + if (!wider_mode_iter.exists ()) return NULL_RTX; - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) - if (optab_handler (bswap_optab, wider_mode) != CODE_FOR_nothing) - goto found; - return NULL_RTX; - - found: + scalar_int_mode wider_mode = wider_mode_iter.require (); last = get_last_insn (); x = widen_operand (op0, wider_mode, mode, true, true); x = expand_unop (wider_mode, bswap_optab, x, NULL_RTX, true); + gcc_assert (GET_MODE_PRECISION (wider_mode) == GET_MODE_BITSIZE (wider_mode) + && GET_MODE_PRECISION (mode) == GET_MODE_BITSIZE (mode)); if (x != 0) x = expand_shift (RSHIFT_EXPR, wider_mode, x, - size_int (GET_MODE_BITSIZE (wider_mode) - - GET_MODE_BITSIZE (mode)), + GET_MODE_BITSIZE (wider_mode) + - GET_MODE_BITSIZE (mode), NULL_RTX, true); if (x != 0) @@ -2680,7 +2335,7 @@ /* 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; @@ -2689,7 +2344,7 @@ t0 = expand_unop (word_mode, bswap_optab, operand_subword_force (op, 1, mode), NULL_RTX, true); - if (target == 0) + if (target == 0 || !valid_multiword_target_p (target)) target = gen_reg_rtx (mode); if (REG_P (target)) emit_clobber (target); @@ -2702,41 +2357,47 @@ /* 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 (scalar_int_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; - for (wider_mode = mode; wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + opt_scalar_int_mode wider_mode_iter; + FOR_EACH_MODE_FROM (wider_mode_iter, mode) + { + scalar_int_mode wider_mode = wider_mode_iter.require (); + if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing) { - if (optab_handler (popcount_optab, wider_mode) != CODE_FOR_nothing) + rtx xop0, temp; + rtx_insn *last; + + last = get_last_insn (); + + if (target == 0 || GET_MODE (target) != wider_mode) + target = gen_reg_rtx (wider_mode); + + xop0 = widen_operand (op0, wider_mode, mode, true, false); + temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX, + true); + if (temp != 0) + temp = expand_binop (wider_mode, and_optab, temp, const1_rtx, + target, true, OPTAB_DIRECT); + + if (temp) { - rtx xop0, temp, last; - - last = get_last_insn (); - - if (target == 0) - target = gen_reg_rtx (mode); - xop0 = widen_operand (op0, wider_mode, mode, true, false); - temp = expand_unop (wider_mode, popcount_optab, xop0, NULL_RTX, - true); - if (temp != 0) - temp = expand_binop (wider_mode, and_optab, temp, const1_rtx, - target, true, OPTAB_DIRECT); - if (temp == 0) - delete_insns_since (last); - - return temp; + if (mclass != MODE_INT + || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode)) + return convert_to_mode (mode, temp, 0); + else + return gen_lowpart (mode, temp); } + else + delete_insns_since (last); } } return 0; } /* Try calculating ctz(x) as K - clz(x & -x) , - where K is GET_MODE_BITSIZE(mode) - 1. + where K is GET_MODE_PRECISION(mode) - 1. Both __builtin_ctz and __builtin_clz are undefined at zero, so we don't have to worry about what the hardware does in that case. (If @@ -2748,9 +2409,10 @@ less convenient for expand_ffs anyway. */ static rtx -expand_ctz (enum machine_mode mode, rtx op0, rtx target) +expand_ctz (scalar_int_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; @@ -2764,7 +2426,8 @@ if (temp) temp = expand_unop_direct (mode, clz_optab, temp, NULL_RTX, true); if (temp) - temp = expand_binop (mode, sub_optab, GEN_INT (GET_MODE_BITSIZE (mode) - 1), + temp = expand_binop (mode, sub_optab, + gen_int_mode (GET_MODE_PRECISION (mode) - 1, mode), temp, target, true, OPTAB_DIRECT); if (temp == 0) @@ -2789,11 +2452,12 @@ 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 (scalar_int_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) { @@ -2815,7 +2479,7 @@ if (CLZ_DEFINED_VALUE_AT_ZERO (mode, val) == 2) { defined_at_zero = true; - val = (GET_MODE_BITSIZE (mode) - 1) - val; + val = (GET_MODE_PRECISION (mode) - 1) - val; } } else @@ -2835,7 +2499,7 @@ 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); @@ -2845,7 +2509,7 @@ /* 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; @@ -2868,8 +2532,8 @@ 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); @@ -2886,14 +2550,14 @@ 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, scalar_float_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; + scalar_int_mode imode; + rtx temp; + rtx_insn *insns; /* The format has to have a simple sign bit. */ fmt = REAL_MODE_FORMAT (mode); @@ -2910,8 +2574,7 @@ if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) { - imode = int_mode_for_mode (mode); - if (imode == BLKmode) + if (!int_mode_for_mode (mode).exists (&imode)) return NULL_RTX; word = 0; nwords = 1; @@ -2928,11 +2591,13 @@ 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); - - if (target == 0 || target == op0) + mask = ~mask; + + if (target == 0 + || target == op0 + || (nwords > 1 && !valid_multiword_target_p (target))) target = gen_reg_rtx (mode); if (nwords > 1) @@ -2948,7 +2613,7 @@ { 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); @@ -2966,12 +2631,13 @@ { 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); - set_unique_reg_note (get_last_insn (), REG_EQUAL, - gen_rtx_fmt_e (code, mode, copy_rtx (op0))); + set_dst_reg_note (get_last_insn (), REG_EQUAL, + gen_rtx_fmt_e (code, mode, copy_rtx (op0)), + target); } return target; @@ -2980,39 +2646,25 @@ /* 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, - int unsignedp) +expand_unop_direct (machine_mode mode, optab unoptab, rtx op0, rtx target, + int unsignedp) { if (optab_handler (unoptab, mode) != CODE_FOR_nothing) { - int icode = (int) optab_handler (unoptab, mode); - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - rtx xop0 = op0; - rtx last = get_last_insn (); - rtx pat, temp; - - if (target) - temp = target; - else - temp = gen_reg_rtx (mode); - - if (GET_MODE (xop0) != VOIDmode - && GET_MODE (xop0) != mode0) - xop0 = convert_to_mode (mode0, xop0, unsignedp); - - /* Now, if insn doesn't accept our operand, put it into a pseudo. */ - - if (!insn_data[icode].operand[1].predicate (xop0, mode0)) - xop0 = copy_to_mode_reg (mode0, xop0); - - if (!insn_data[icode].operand[0].predicate (temp, mode)) - temp = gen_reg_rtx (mode); - - pat = GEN_FCN (icode) (temp, xop0); + struct expand_operand ops[2]; + enum insn_code icode = optab_handler (unoptab, mode); + 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); + pat = maybe_gen_insn (icode, 2, ops); if (pat) { if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX - && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX)) + && ! add_equal_note (pat, ops[0].value, + optab_to_code (unoptab), + ops[1].value, NULL_RTX)) { delete_insns_since (last); return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp); @@ -3020,10 +2672,8 @@ emit_insn (pat); - return temp; + return ops[0].value; } - else - delete_insns_since (last); } return 0; } @@ -3040,11 +2690,13 @@ 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; + scalar_int_mode int_mode; + scalar_float_mode float_mode; rtx temp; rtx libfunc; @@ -3057,48 +2709,128 @@ /* Widening (or narrowing) clz needs special treatment. */ if (unoptab == clz_optab) { - temp = widen_clz (mode, op0, target); - if (temp) - return temp; - - if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD - && optab_handler (unoptab, word_mode) != CODE_FOR_nothing) + if (is_a <scalar_int_mode> (mode, &int_mode)) { - temp = expand_doubleword_clz (mode, op0, target); + temp = widen_leading (int_mode, op0, target, unoptab); + if (temp) + return temp; + + if (GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD + && optab_handler (unoptab, word_mode) != CODE_FOR_nothing) + { + temp = expand_doubleword_clz (int_mode, op0, target); + if (temp) + return temp; + } + } + + goto try_libcall; + } + + if (unoptab == clrsb_optab) + { + if (is_a <scalar_int_mode> (mode, &int_mode)) + { + temp = widen_leading (int_mode, op0, target, unoptab); if (temp) return temp; } - - goto try_libcall; + goto try_libcall; + } + + if (unoptab == popcount_optab + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD + && optab_handler (unoptab, word_mode) != CODE_FOR_nothing + && optimize_insn_for_speed_p ()) + { + temp = expand_doubleword_popcount (int_mode, op0, target); + if (temp) + return temp; + } + + if (unoptab == parity_optab + && is_a <scalar_int_mode> (mode, &int_mode) + && GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD + && (optab_handler (unoptab, word_mode) != CODE_FOR_nothing + || optab_handler (popcount_optab, word_mode) != CODE_FOR_nothing) + && optimize_insn_for_speed_p ()) + { + temp = expand_doubleword_parity (int_mode, op0, target); + if (temp) + return temp; } /* Widening (or narrowing) bswap needs special treatment. */ if (unoptab == bswap_optab) { - temp = widen_bswap (mode, op0, target); - if (temp) - return temp; - - if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD - && optab_handler (unoptab, word_mode) != CODE_FOR_nothing) + /* HImode is special because in this mode BSWAP is equivalent to ROTATE + or ROTATERT. First try these directly; if this fails, then try the + obvious pair of shifts with allowed widening, as this will probably + be always more efficient than the other fallback methods. */ + if (mode == HImode) { - temp = expand_doubleword_bswap (mode, op0, target); + rtx_insn *last; + rtx temp1, temp2; + + if (optab_handler (rotl_optab, mode) != CODE_FOR_nothing) + { + temp = expand_binop (mode, rotl_optab, op0, GEN_INT (8), target, + unsignedp, OPTAB_DIRECT); + if (temp) + return temp; + } + + if (optab_handler (rotr_optab, mode) != CODE_FOR_nothing) + { + temp = expand_binop (mode, rotr_optab, op0, GEN_INT (8), target, + unsignedp, OPTAB_DIRECT); + if (temp) + return temp; + } + + last = get_last_insn (); + + temp1 = expand_binop (mode, ashl_optab, op0, GEN_INT (8), NULL_RTX, + unsignedp, OPTAB_WIDEN); + temp2 = expand_binop (mode, lshr_optab, op0, GEN_INT (8), NULL_RTX, + unsignedp, OPTAB_WIDEN); + if (temp1 && temp2) + { + temp = expand_binop (mode, ior_optab, temp1, temp2, target, + unsignedp, OPTAB_WIDEN); + if (temp) + return temp; + } + + delete_insns_since (last); + } + + if (is_a <scalar_int_mode> (mode, &int_mode)) + { + temp = widen_bswap (int_mode, op0, target); if (temp) return temp; + + if (GET_MODE_SIZE (int_mode) == 2 * UNITS_PER_WORD + && optab_handler (unoptab, word_mode) != CODE_FOR_nothing) + { + temp = expand_doubleword_bswap (mode, op0, target); + if (temp) + return temp; + } } goto try_libcall; } if (CLASS_HAS_WIDER_MODES_P (mclass)) - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + FOR_EACH_WIDER_MODE (wider_mode, mode) { 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 @@ -3115,8 +2847,7 @@ if (temp) { if (mclass != MODE_INT - || !TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), - GET_MODE_BITSIZE (wider_mode))) + || !TRULY_NOOP_TRUNCATION_MODES_P (mode, wider_mode)) { if (target == 0) target = gen_reg_rtx (mode); @@ -3133,24 +2864,24 @@ /* These can be done a word at a time. */ if (unoptab == one_cmpl_optab - && mclass == MODE_INT - && GET_MODE_SIZE (mode) > UNITS_PER_WORD + && is_int_mode (mode, &int_mode) + && GET_MODE_SIZE (int_mode) > UNITS_PER_WORD && optab_handler (unoptab, word_mode) != CODE_FOR_nothing) { int i; - rtx insns; - - if (target == 0 || target == op0) - target = gen_reg_rtx (mode); + rtx_insn *insns; + + if (target == 0 || target == op0 || !valid_multiword_target_p (target)) + target = gen_reg_rtx (int_mode); start_sequence (); /* Do the actual arithmetic. */ - for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) + for (i = 0; i < GET_MODE_BITSIZE (int_mode) / BITS_PER_WORD; i++) { - rtx target_piece = operand_subword (target, i, 1, mode); + rtx target_piece = operand_subword (target, i, 1, int_mode); rtx x = expand_unop (word_mode, unoptab, - operand_subword_force (op0, i, mode), + operand_subword_force (op0, i, int_mode), target_piece, unsignedp); if (target_piece != x) @@ -3164,12 +2895,12 @@ 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)) + if (is_a <scalar_float_mode> (mode, &float_mode)) { - temp = expand_absneg_bit (NEG, mode, op0, target); + temp = expand_absneg_bit (NEG, float_mode, op0, target); if (temp) return temp; } @@ -3188,25 +2919,25 @@ } /* Try calculating parity (x) as popcount (x) % 2. */ - if (unoptab == parity_optab) - { - temp = expand_parity (mode, op0, target); + if (unoptab == parity_optab && is_a <scalar_int_mode> (mode, &int_mode)) + { + temp = expand_parity (int_mode, op0, target); if (temp) return temp; } /* Try implementing ffs (x) in terms of clz (x). */ - if (unoptab == ffs_optab) - { - temp = expand_ffs (mode, op0, target); + if (unoptab == ffs_optab && is_a <scalar_int_mode> (mode, &int_mode)) + { + temp = expand_ffs (int_mode, op0, target); if (temp) return temp; } /* Try implementing ctz (x) in terms of clz (x). */ - if (unoptab == ctz_optab) - { - temp = expand_ctz (mode, op0, target); + if (unoptab == ctz_optab && is_a <scalar_int_mode> (mode, &int_mode)) + { + temp = expand_ctz (int_mode, op0, target); if (temp) return temp; } @@ -3216,15 +2947,16 @@ 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. */ if (unoptab == ffs_optab || unoptab == clz_optab || unoptab == ctz_optab - || unoptab == popcount_optab || unoptab == parity_optab) + || unoptab == clrsb_optab || unoptab == popcount_optab + || unoptab == parity_optab) outmode = GET_MODE (hard_libcall_value (TYPE_MODE (integer_type_node), optab_libfunc (unoptab, mode))); @@ -3234,17 +2966,24 @@ /* Pass 1 for NO_QUEUE so we don't lose any increments if the libcall is cse'd or moved. */ value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, outmode, - 1, op0, mode); + op0, mode); insns = get_insns (); end_sequence (); target = gen_reg_rtx (outmode); - eq_value = gen_rtx_fmt_e (unoptab->code, 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)) - eq_value = simplify_gen_unary (ZERO_EXTEND, outmode, eq_value, mode); - emit_libcall_block (insns, target, value, eq_value); + bool trapv = trapv_unoptab_p (unoptab); + if (trapv) + eq_value = NULL_RTX; + else + { + eq_value = gen_rtx_fmt_e (optab_to_code (unoptab), mode, op0); + if (GET_MODE_UNIT_SIZE (outmode) < GET_MODE_UNIT_SIZE (mode)) + eq_value = simplify_gen_unary (TRUNCATE, outmode, eq_value, mode); + else if (GET_MODE_UNIT_SIZE (outmode) > GET_MODE_UNIT_SIZE (mode)) + eq_value = simplify_gen_unary (ZERO_EXTEND, + outmode, eq_value, mode); + } + emit_libcall_block_1 (insns, target, value, eq_value, trapv); return target; } @@ -3253,35 +2992,58 @@ if (CLASS_HAS_WIDER_MODES_P (mclass)) { - for (wider_mode = GET_MODE_WIDER_MODE (mode); - wider_mode != VOIDmode; - wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + FOR_EACH_WIDER_MODE (wider_mode, mode) { if (optab_handler (unoptab, wider_mode) != CODE_FOR_nothing || 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 results to the same narrowness. */ - xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, (unoptab == neg_optab - || unoptab == one_cmpl_optab) + || unoptab == one_cmpl_optab + || unoptab == bswap_optab) && mclass == MODE_INT); temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX, unsignedp); /* If we are generating clz using wider mode, adjust the - result. */ - if (unoptab == clz_optab && temp != 0) - temp = expand_binop (wider_mode, sub_optab, temp, - GEN_INT (GET_MODE_BITSIZE (wider_mode) - - GET_MODE_BITSIZE (mode)), - target, true, OPTAB_DIRECT); + result. Similarly for clrsb. */ + if ((unoptab == clz_optab || unoptab == clrsb_optab) + && temp != 0) + { + scalar_int_mode wider_int_mode + = as_a <scalar_int_mode> (wider_mode); + int_mode = as_a <scalar_int_mode> (mode); + temp = expand_binop + (wider_mode, sub_optab, temp, + gen_int_mode (GET_MODE_PRECISION (wider_int_mode) + - GET_MODE_PRECISION (int_mode), + wider_int_mode), + target, true, OPTAB_DIRECT); + } + + /* Likewise for bswap. */ + if (unoptab == bswap_optab && temp != 0) + { + scalar_int_mode wider_int_mode + = as_a <scalar_int_mode> (wider_mode); + int_mode = as_a <scalar_int_mode> (mode); + gcc_assert (GET_MODE_PRECISION (wider_int_mode) + == GET_MODE_BITSIZE (wider_int_mode) + && GET_MODE_PRECISION (int_mode) + == GET_MODE_BITSIZE (int_mode)); + + temp = expand_shift (RSHIFT_EXPR, wider_int_mode, temp, + GET_MODE_BITSIZE (wider_int_mode) + - GET_MODE_BITSIZE (int_mode), + NULL_RTX, true); + } if (temp) { @@ -3303,7 +3065,7 @@ /* 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, @@ -3327,12 +3089,13 @@ */ 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. */ @@ -3342,9 +3105,10 @@ return temp; /* For floating point modes, try clearing the sign bit. */ - if (SCALAR_FLOAT_MODE_P (mode)) - { - temp = expand_absneg_bit (ABS, mode, op0, target); + scalar_float_mode float_mode; + if (is_a <scalar_float_mode> (mode, &float_mode)) + { + temp = expand_absneg_bit (ABS, float_mode, op0, target); if (temp) return temp; } @@ -3353,9 +3117,10 @@ if (optab_handler (smax_optab, mode) != CODE_FOR_nothing && !HONOR_SIGNED_ZEROS (mode)) { - rtx last = get_last_insn (); - - temp = expand_unop (mode, neg_optab, op0, NULL_RTX, 0); + rtx_insn *last = get_last_insn (); + + 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); @@ -3370,18 +3135,20 @@ value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)), where W is the width of MODE. */ - if (GET_MODE_CLASS (mode) == MODE_INT + scalar_int_mode int_mode; + if (is_int_mode (mode, &int_mode) && BRANCH_COST (optimize_insn_for_speed_p (), false) >= 2) { - rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), + rtx extended = expand_shift (RSHIFT_EXPR, int_mode, op0, + GET_MODE_PRECISION (int_mode) - 1, NULL_RTX, 0); - temp = expand_binop (mode, xor_optab, extended, op0, target, 0, + temp = expand_binop (int_mode, xor_optab, extended, op0, target, 0, OPTAB_LIB_WIDEN); if (temp != 0) - temp = expand_binop (mode, result_unsignedp ? sub_optab : subv_optab, + temp = expand_binop (int_mode, + result_unsignedp ? sub_optab : subv_optab, temp, extended, target, 0, OPTAB_LIB_WIDEN); if (temp != 0) @@ -3392,12 +3159,14 @@ } 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; - - if (! flag_trapv) + rtx temp; + rtx_code_label *op1; + + if (GET_MODE_CLASS (mode) != MODE_INT + || ! flag_trapv) result_unsignedp = 1; temp = expand_abs_nojump (mode, op0, target, result_unsignedp); @@ -3424,7 +3193,8 @@ 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, + profile_probability::uninitialized ()); op0 = expand_unop (mode, result_unsignedp ? neg_optab : negv_optab, target, target, 0); @@ -3444,7 +3214,7 @@ 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; @@ -3455,7 +3225,7 @@ /* 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) @@ -3471,15 +3241,16 @@ /* If this machine has expensive jumps, we can do one's complement absolute value of X as (((signed) x >> (W-1)) ^ x). */ - if (GET_MODE_CLASS (mode) == MODE_INT + scalar_int_mode int_mode; + if (is_int_mode (mode, &int_mode) && BRANCH_COST (optimize_insn_for_speed_p (), false) >= 2) { - rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), + rtx extended = expand_shift (RSHIFT_EXPR, int_mode, op0, + GET_MODE_PRECISION (int_mode) - 1, NULL_RTX, 0); - temp = expand_binop (mode, xor_optab, extended, op0, target, 0, + temp = expand_binop (int_mode, xor_optab, extended, op0, target, 0, OPTAB_LIB_WIDEN); if (temp != 0) @@ -3495,33 +3266,31 @@ 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 (scalar_float_mode mode, rtx op0, rtx op1, rtx target, int bitpos, bool op0_is_abs) { - enum machine_mode imode; - int icode; - rtx sign, label; + scalar_int_mode imode; + enum insn_code icode; + rtx sign; + rtx_code_label *label; if (target == op1) target = NULL_RTX; /* Check if the back end provides an insn that handles signbit for the argument's mode. */ - icode = (int) optab_handler (signbit_optab, mode); + icode = optab_handler (signbit_optab, mode); if (icode != CODE_FOR_nothing) { - imode = insn_data[icode].operand[0].mode; + imode = as_a <scalar_int_mode> (insn_data[(int) icode].operand[0].mode); sign = gen_reg_rtx (imode); emit_unop_insn (icode, sign, op1, UNKNOWN); } else { - double_int mask; - if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) { - imode = int_mode_for_mode (mode); - if (imode == BLKmode) + if (!int_mode_for_mode (mode).exists (&imode)) return NULL_RTX; op1 = gen_lowpart (imode, op1); } @@ -3538,10 +3307,9 @@ 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); } @@ -3563,7 +3331,7 @@ 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); @@ -3581,18 +3349,17 @@ 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 (scalar_float_mode mode, rtx op0, rtx op1, rtx target, int bitpos, bool op0_is_abs) { - enum machine_mode imode; - double_int mask; + scalar_int_mode imode; int word, nwords, i; - rtx temp, insns; + rtx temp; + rtx_insn *insns; if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) { - imode = int_mode_for_mode (mode); - if (imode == BLKmode) + if (!int_mode_for_mode (mode).exists (&imode)) return NULL_RTX; word = 0; nwords = 1; @@ -3609,9 +3376,12 @@ nwords = (GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD; } - mask = double_int_setbit (double_int_zero, bitpos); - - if (target == 0 || target == op0 || target == op1) + wide_int mask = wi::set_bit_in_zero (bitpos, GET_MODE_PRECISION (imode)); + + if (target == 0 + || target == op0 + || target == op1 + || (nwords > 1 && !valid_multiword_target_p (target))) target = gen_reg_rtx (mode); if (nwords > 1) @@ -3628,13 +3398,11 @@ 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, @@ -3654,14 +3422,13 @@ 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, @@ -3679,12 +3446,12 @@ rtx expand_copysign (rtx op0, rtx op1, rtx target) { - enum machine_mode mode = GET_MODE (op0); + scalar_float_mode mode; const struct real_format *fmt; bool op0_is_abs; rtx temp; - gcc_assert (SCALAR_FLOAT_MODE_P (mode)); + mode = as_a <scalar_float_mode> (GET_MODE (op0)); gcc_assert (GET_MODE (op1) == mode); /* First try to do it with a special instruction. */ @@ -3698,7 +3465,7 @@ 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); @@ -3706,7 +3473,7 @@ } 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))) { @@ -3731,37 +3498,26 @@ Return false if expansion failed. */ bool -maybe_emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code) +maybe_emit_unop_insn (enum insn_code icode, rtx target, rtx op0, + enum rtx_code code) { - rtx temp; - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - rtx pat; - rtx last = get_last_insn (); - - temp = target; - - /* Now, if insn does not accept our operands, put them into pseudos. */ - - if (!insn_data[icode].operand[1].predicate (op0, mode0)) - op0 = copy_to_mode_reg (mode0, op0); - - if (!insn_data[icode].operand[0].predicate (temp, GET_MODE (temp))) - temp = gen_reg_rtx (GET_MODE (temp)); - - pat = GEN_FCN (icode) (temp, op0); + struct expand_operand ops[2]; + rtx_insn *pat; + + create_output_operand (&ops[0], target, GET_MODE (target)); + create_input_operand (&ops[1], op0, GET_MODE (op0)); + pat = maybe_gen_insn (icode, 2, ops); if (!pat) - { - delete_insns_since (last); - return false; - } - - if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX && code != UNKNOWN) - add_equal_note (pat, temp, code, op0, NULL_RTX); + return false; + + 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); - if (temp != target) - emit_move_insn (target, temp); + if (ops[0].value != target) + emit_move_insn (target, ops[0].value); return true; } /* Generate an instruction whose insn-code is INSN_CODE, @@ -3771,7 +3527,7 @@ the value that is stored into TARGET. */ void -emit_unop_insn (int icode, rtx target, rtx op0, enum rtx_code code) +emit_unop_insn (enum insn_code icode, rtx target, rtx op0, enum rtx_code code) { bool ok = maybe_emit_unop_insn (icode, target, op0, code); gcc_assert (ok); @@ -3779,7 +3535,8 @@ struct no_conflict_data { - rtx target, first, insn; + rtx target; + rtx_insn *first, *insn; bool must_stay; }; @@ -3830,11 +3587,12 @@ an insn to move RESULT to TARGET. This last insn will have a REQ_EQUAL note with an operand of EQUIV. */ -void -emit_libcall_block (rtx insns, rtx target, rtx result, rtx equiv) +static void +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. */ @@ -3844,7 +3602,8 @@ /* If we're using non-call exceptions, a libcall corresponding to an operation that may trap may also trap. */ /* ??? See the comment in front of make_reg_eh_region_note. */ - if (cfun->can_throw_non_call_exceptions && may_trap_p (equiv)) + if (cfun->can_throw_non_call_exceptions + && (equiv_may_trap || may_trap_p (equiv))) { for (insn = insns; insn; insn = NEXT_INSN (insn)) if (CALL_P (insn)) @@ -3894,12 +3653,12 @@ 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); } @@ -3920,12 +3679,18 @@ } last = emit_move_insn (target, result); - if (optab_handler (mov_optab, GET_MODE (target)) != CODE_FOR_nothing) - set_unique_reg_note (last, REG_EQUAL, copy_rtx (equiv)); + if (equiv) + set_dst_reg_note (last, REG_EQUAL, copy_rtx (equiv), target); if (final_dest != target) emit_move_insn (final_dest, target); } + +void +emit_libcall_block (rtx_insn *insns, rtx target, rtx result, rtx equiv) +{ + emit_libcall_block_1 (insns, target, result, equiv, false); +} /* Nonzero if we can perform a comparison of mode MODE straightforwardly. PURPOSE describes how this comparison will be used. CODE is the rtx @@ -3936,28 +3701,28 @@ 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; test = gen_rtx_fmt_ee (code, mode, const0_rtx, const0_rtx); do { - int icode; + enum insn_code icode; if (purpose == ccp_jump && (icode = optab_handler (cbranch_optab, mode)) != CODE_FOR_nothing - && insn_data[icode].operand[0].predicate (test, mode)) + && insn_operand_matches (icode, 0, test)) return 1; if (purpose == ccp_store_flag && (icode = optab_handler (cstore_optab, mode)) != CODE_FOR_nothing - && insn_data[icode].operand[1].predicate (test, mode)) + && insn_operand_matches (icode, 1, test)) return 1; if (purpose == ccp_cmov && optab_handler (cmov_optab, mode) != CODE_FOR_nothing) return 1; - mode = GET_MODE_WIDER_MODE (mode); + mode = GET_MODE_WIDER_MODE (mode).else_void (); PUT_MODE (test, mode); } while (mode != VOIDmode); @@ -3966,13 +3731,17 @@ } /* This function is called when we are going to emit a compare instruction that - compares the values found in *PX and *PY, using the rtl operator COMPARISON. + compares the values found in X and Y, using the rtl operator COMPARISON. + + If they have mode BLKmode, then SIZE specifies the size of both operands. + + UNSIGNEDP nonzero says that the operands are unsigned; + this matters if they need to be widened (as given by METHODS). + + *PTEST is where the resulting comparison RTX is returned or NULL_RTX + if we failed to produce one. *PMODE is the mode of the inputs (in case they are const_int). - *PUNSIGNEDP nonzero says that the operands are unsigned; - this matters if they need to be widened (as given by METHODS). - - If they have mode BLKmode, then SIZE specifies the size of both operands. This function performs all the setup necessary so that the caller only has to emit a single comparison insn. This setup can involve doing a BLKmode @@ -3985,11 +3754,11 @@ 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. */ @@ -3998,16 +3767,16 @@ /* If we are optimizing, force expensive constants into a register. */ if (CONSTANT_P (x) && optimize - && (rtx_cost (x, COMPARE, optimize_insn_for_speed_p ()) + && (rtx_cost (x, mode, COMPARE, 0, optimize_insn_for_speed_p ()) > COSTS_N_INSNS (1))) x = force_reg (mode, x); if (CONSTANT_P (y) && optimize - && (rtx_cost (y, COMPARE, optimize_insn_for_speed_p ()) + && (rtx_cost (y, mode, COMPARE, 1, optimize_insn_for_speed_p ()) > 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. */ @@ -4024,10 +3793,8 @@ if (mode == BLKmode) { - enum machine_mode result_mode; + machine_mode result_mode; enum insn_code cmp_code; - tree length_type; - rtx libfunc; rtx result; rtx opalign = GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT); @@ -4036,10 +3803,10 @@ /* Try to use a memory block compare insn - either cmpstr or cmpmem will do. */ - for (cmp_mode = GET_CLASS_NARROWEST_MODE (MODE_INT); - cmp_mode != VOIDmode; - cmp_mode = GET_MODE_WIDER_MODE (cmp_mode)) + opt_scalar_int_mode cmp_mode_iter; + FOR_EACH_MODE_IN_CLASS (cmp_mode_iter, MODE_INT) { + scalar_int_mode cmp_mode = cmp_mode_iter.require (); cmp_code = direct_optab_handler (cmpmem_optab, cmp_mode); if (cmp_code == CODE_FOR_nothing) cmp_code = direct_optab_handler (cmpstr_optab, cmp_mode); @@ -4049,10 +3816,10 @@ continue; /* Must make sure the size fits the insn's mode. */ - if ((CONST_INT_P (size) - && INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode))) - || (GET_MODE_BITSIZE (GET_MODE (size)) - > GET_MODE_BITSIZE (cmp_mode))) + if (CONST_INT_P (size) + ? INTVAL (size) >= (1 << GET_MODE_BITSIZE (cmp_mode)) + : (GET_MODE_BITSIZE (as_a <scalar_int_mode> (GET_MODE (size))) + > GET_MODE_BITSIZE (cmp_mode))) continue; result_mode = insn_data[cmp_code].operand[0].mode; @@ -4068,23 +3835,14 @@ if (methods != OPTAB_LIB && methods != OPTAB_LIB_WIDEN) goto fail; - /* Otherwise call a library function, memcmp. */ - libfunc = memcmp_libfunc; - length_type = sizetype; - result_mode = TYPE_MODE (integer_type_node); - cmp_mode = TYPE_MODE (length_type); - size = convert_to_mode (TYPE_MODE (length_type), size, - TYPE_UNSIGNED (length_type)); - - result = emit_library_call_value (libfunc, 0, LCT_PURE, - result_mode, 3, - 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; + /* Otherwise call a library function. */ + result = emit_block_comp_via_libcall (XEXP (x, 0), XEXP (y, 0), size); + + x = result; + y = const0_rtx; + mode = TYPE_MODE (integer_type_node); + methods = OPTAB_LIB_WIDEN; + unsignedp = false; } /* Don't allow operands to the compare to trap, as that can put the @@ -4092,36 +3850,36 @@ if (cfun->can_throw_non_call_exceptions) { if (may_trap_p (x)) - x = force_reg (mode, x); + x = copy_to_reg (x); if (may_trap_p (y)) - y = force_reg (mode, y); + y = copy_to_reg (y); } 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; } mclass = GET_MODE_CLASS (mode); test = gen_rtx_fmt_ee (comparison, VOIDmode, x, y); - cmp_mode = mode; - do - { + FOR_EACH_MODE_FROM (cmp_mode, mode) + { enum insn_code icode; icode = optab_handler (cbranch_optab, cmp_mode); if (icode != CODE_FOR_nothing - && insn_data[icode].operand[0].predicate (test, VOIDmode)) + && 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 - && insn_data[icode].operand[1].predicate - (op0, insn_data[icode].operand[1].mode) - && insn_data[icode].operand[2].predicate - (op1, insn_data[icode].operand[2].mode)) + && insn_operand_matches (icode, 1, op0) + && insn_operand_matches (icode, 2, op1)) { XEXP (test, 0) = op0; XEXP (test, 1) = op1; @@ -4134,9 +3892,7 @@ if (methods == OPTAB_DIRECT || !CLASS_HAS_WIDER_MODES_P (mclass)) break; - cmp_mode = GET_MODE_WIDER_MODE (cmp_mode); - } - while (cmp_mode != VOIDmode); + } if (methods != OPTAB_LIB_WIDEN) goto fail; @@ -4144,6 +3900,7 @@ 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); @@ -4158,9 +3915,9 @@ 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, 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 @@ -4169,19 +3926,21 @@ result against 1 in the biased case, and zero in the unbiased case. For unsigned comparisons always compare against 1 after biasing the unbiased result by adding 1. This gives us a way to - represent LTU. */ + represent LTU. + The comparisons in the fixed-point helper library are always + biased. */ x = result; y = const1_rtx; - if (!TARGET_LIB_INT_CMP_BIASED) + if (!TARGET_LIB_INT_CMP_BIASED && !ALL_FIXED_POINT_MODE_P (mode)) { if (unsignedp) - x = plus_constant (result, 1); + 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); } @@ -4200,18 +3959,20 @@ that it is accepted by the operand predicate. Return the new value. */ rtx -prepare_operand (int 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_data[icode].operand[opnum].predicate - (x, insn_data[icode].operand[opnum].mode)) - { + 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[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; @@ -4221,19 +3982,29 @@ 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, + profile_probability 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; icode = optab_handler (cbranch_optab, optab_mode); gcc_assert (icode != CODE_FOR_nothing); - gcc_assert (insn_data[icode].operand[0].predicate (test, VOIDmode)); - emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), XEXP (test, 1), label)); + gcc_assert (insn_operand_matches (icode, 0, test)); + insn = emit_jump_insn (GEN_FCN (icode) (test, XEXP (test, 0), + XEXP (test, 1), label)); + if (prob.initialized_p () + && profile_status_for_fn (cfun) != PROFILE_ABSENT + && insn + && JUMP_P (insn) + && any_condjump_p (insn) + && !find_reg_note (insn, REG_BR_PROB, 0)) + add_reg_br_prob_note (insn, prob); } /* Generate code to compare X with Y so that the condition codes are @@ -4251,11 +4022,14 @@ 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, + profile_probability prob) { rtx op0 = x, op1 = y; rtx test; @@ -4279,7 +4053,7 @@ 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); } @@ -4288,37 +4062,35 @@ 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; 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 (); - - for (mode = orig_mode; - mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - { - if (code_to_optab[comparison] - && (libfunc = optab_libfunc (code_to_optab[comparison], mode))) + scalar_int_mode cmp_mode = targetm.libgcc_cmp_return_mode (); + + FOR_EACH_MODE_FROM (mode, orig_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; + std::swap (x, y); comparison = swapped; 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; @@ -4399,7 +4171,7 @@ start_sequence (); value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, - cmp_mode, 2, x, mode, y, mode); + cmp_mode, x, mode, y, mode); insns = get_insns (); end_sequence (); @@ -4421,15 +4193,17 @@ void emit_indirect_jump (rtx loc) { - if (!insn_data[(int) CODE_FOR_indirect_jump].operand[0].predicate - (loc, Pmode)) - loc = copy_to_mode_reg (Pmode, loc); - - emit_jump_insn (gen_indirect_jump (loc)); - emit_barrier (); + if (!targetm.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 (targetm.code_for_indirect_jump, 1, ops); + emit_barrier (); + } } -#ifdef HAVE_conditional_move /* Emit a conditional move instruction if the machine supports one for that condition and machine mode. @@ -4447,21 +4221,31 @@ 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, subtarget, comparison, insn; + rtx comparison; + rtx_insn *last; enum insn_code icode; enum rtx_code reversed; + /* If the two source operands are identical, that's just a move. */ + + if (rtx_equal_p (op2, op3)) + { + if (!target) + target = gen_reg_rtx (mode); + + emit_move_insn (target, op3); + return target; + } + /* 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); } @@ -4476,14 +4260,15 @@ if (cmode == VOIDmode) cmode = GET_MODE (op0); + enum rtx_code orig_code = code; + bool swapped = false; if (swap_commutative_operands_p (op2, op3) && ((reversed = reversed_comparison_code_parts (code, op0, op1, NULL)) != UNKNOWN)) { - tem = op2; - op2 = op3; - op3 = tem; + std::swap (op2, op3); code = reversed; + swapped = true; } if (mode == VOIDmode) @@ -4492,84 +4277,114 @@ icode = direct_optab_handler (movcc_optab, mode); if (icode == CODE_FOR_nothing) - return 0; + return NULL_RTX; if (!target) target = gen_reg_rtx (mode); - subtarget = target; - - /* If the insn doesn't accept these operands, put them in pseudos. */ - - if (!insn_data[icode].operand[0].predicate - (subtarget, insn_data[icode].operand[0].mode)) - subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode); - - if (!insn_data[icode].operand[2].predicate - (op2, insn_data[icode].operand[2].mode)) - op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2); - - if (!insn_data[icode].operand[3].predicate - (op3, insn_data[icode].operand[3].mode)) - op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3); - - /* Everything should now be in the suitable form. */ - - code = unsignedp ? unsigned_condition (code) : code; - comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1); - - /* We can get const0_rtx or const_true_rtx in some circumstances. Just - return NULL and let the caller figure out how best to deal with this - situation. */ - if (!COMPARISON_P (comparison)) - return NULL_RTX; - - do_pending_stack_adjust (); - start_sequence (); - prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1), - GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN, - &comparison, &cmode); - if (!comparison) - insn = NULL_RTX; + for (int pass = 0; ; pass++) + { + code = unsignedp ? unsigned_condition (code) : code; + comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1); + + /* We can get const0_rtx or const_true_rtx in some circumstances. Just + punt and let the caller figure out how best to deal with this + situation. */ + if (COMPARISON_P (comparison)) + { + 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); + if (comparison) + { + struct expand_operand ops[4]; + + create_output_operand (&ops[0], target, mode); + create_fixed_operand (&ops[1], comparison); + create_input_operand (&ops[2], op2, mode); + create_input_operand (&ops[3], op3, mode); + if (maybe_expand_insn (icode, 4, ops)) + { + if (ops[0].value != target) + convert_move (target, ops[0].value, false); + return target; + } + } + delete_insns_since (last); + restore_pending_stack_adjust (&save); + } + + if (pass == 1) + return NULL_RTX; + + /* If the preferred op2/op3 order is not usable, retry with other + operand order, perhaps it will expand successfully. */ + if (swapped) + code = orig_code; + else if ((reversed = reversed_comparison_code_parts (orig_code, op0, op1, + NULL)) + != UNKNOWN) + code = reversed; + else + return NULL_RTX; + std::swap (op2, op3); + } +} + + +/* Emit a conditional negate or bitwise complement using the + negcc or notcc optabs if available. Return NULL_RTX if such operations + are not available. Otherwise return the RTX holding the result. + TARGET is the desired destination of the result. COMP is the comparison + on which to negate. If COND is true move into TARGET the negation + or bitwise complement of OP1. Otherwise move OP2 into TARGET. + CODE is either NEG or NOT. MODE is the machine mode in which the + operation is performed. */ + +rtx +emit_conditional_neg_or_complement (rtx target, rtx_code code, + machine_mode mode, rtx cond, rtx op1, + rtx op2) +{ + optab op = unknown_optab; + if (code == NEG) + op = negcc_optab; + else if (code == NOT) + op = notcc_optab; else - insn = GEN_FCN (icode) (subtarget, comparison, op2, op3); - - /* If that failed, then give up. */ - if (insn == 0) - { - end_sequence (); - return 0; - } - - emit_insn (insn); - insn = get_insns (); - end_sequence (); - emit_insn (insn); - if (subtarget != target) - convert_move (target, subtarget, 0); - - return target; + gcc_unreachable (); + + insn_code icode = direct_optab_handler (op, mode); + + if (icode == CODE_FOR_nothing) + return NULL_RTX; + + if (!target) + target = gen_reg_rtx (mode); + + rtx_insn *last = get_last_insn (); + struct expand_operand ops[4]; + + create_output_operand (&ops[0], target, mode); + create_fixed_operand (&ops[1], cond); + create_input_operand (&ops[2], op1, mode); + create_input_operand (&ops[3], op2, mode); + + if (maybe_expand_insn (icode, 4, ops)) + { + if (ops[0].value != target) + convert_move (target, ops[0].value, false); + + return target; + } + delete_insns_since (last); + return NULL_RTX; } -/* Return nonzero if a conditional move of mode MODE is supported. - - This function is for combine so it can tell whether an insn that looks - like a conditional move is actually supported by the hardware. If we - guess wrong we lose a bit on optimization, but that's it. */ -/* ??? sparc64 supports conditionally moving integers values based on fp - comparisons, and vice versa. How do we handle them? */ - -int -can_conditionally_move_p (enum 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. @@ -4577,7 +4392,7 @@ 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. @@ -4586,21 +4401,19 @@ 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, subtarget, comparison, insn; + 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); } @@ -4615,16 +4428,6 @@ 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); @@ -4636,24 +4439,6 @@ if (!target) target = gen_reg_rtx (mode); - /* If the insn doesn't accept these operands, put them in pseudos. */ - - if (!insn_data[icode].operand[0].predicate - (target, insn_data[icode].operand[0].mode)) - subtarget = gen_reg_rtx (insn_data[icode].operand[0].mode); - else - subtarget = target; - - if (!insn_data[icode].operand[2].predicate - (op2, insn_data[icode].operand[2].mode)) - op2 = copy_to_mode_reg (insn_data[icode].operand[2].mode, op2); - - if (!insn_data[icode].operand[3].predicate - (op3, insn_data[icode].operand[3].mode)) - op3 = copy_to_mode_reg (insn_data[icode].operand[3].mode, op3); - - /* Everything should now be in the suitable form. */ - code = unsignedp ? unsigned_condition (code) : code; comparison = simplify_gen_relational (code, VOIDmode, cmode, op0, op1); @@ -4664,30 +4449,27 @@ return NULL_RTX; do_pending_stack_adjust (); - start_sequence (); + last = get_last_insn (); prepare_cmp_insn (XEXP (comparison, 0), XEXP (comparison, 1), GET_CODE (comparison), NULL_RTX, unsignedp, OPTAB_WIDEN, &comparison, &cmode); - if (!comparison) - insn = NULL_RTX; - else - insn = GEN_FCN (icode) (subtarget, comparison, op2, op3); - - /* If that failed, then give up. */ - if (insn == 0) - { - end_sequence (); - return 0; - } - - emit_insn (insn); - insn = get_insns (); - end_sequence (); - emit_insn (insn); - if (subtarget != target) - convert_move (target, subtarget, 0); - - return target; + if (comparison) + { + struct expand_operand ops[4]; + + create_output_operand (&ops[0], target, mode); + create_fixed_operand (&ops[1], comparison); + create_input_operand (&ops[2], op2, mode); + create_input_operand (&ops[3], op3, mode); + if (maybe_expand_insn (icode, 4, ops)) + { + if (ops[0].value != target) + convert_move (target, ops[0].value, false); + return target; + } + } + delete_insns_since (last); + return NULL_RTX; } /* These functions attempt to generate an insn body, rather than @@ -4696,17 +4478,14 @@ /* Generate and return an insn body to add Y to X. */ -rtx +rtx_insn * gen_add2_insn (rtx x, rtx y) { - int icode = (int) optab_handler (add_optab, GET_MODE (x)); - - gcc_assert (insn_data[icode].operand[0].predicate - (x, insn_data[icode].operand[0].mode)); - gcc_assert (insn_data[icode].operand[1].predicate - (x, insn_data[icode].operand[1].mode)); - gcc_assert (insn_data[icode].operand[2].predicate - (y, insn_data[icode].operand[2].mode)); + enum insn_code icode = optab_handler (add_optab, GET_MODE (x)); + + gcc_assert (insn_operand_matches (icode, 0, x)); + gcc_assert (insn_operand_matches (icode, 1, x)); + gcc_assert (insn_operand_matches (icode, 2, y)); return GEN_FCN (icode) (x, x, y); } @@ -4714,19 +4493,16 @@ /* Generate and return an insn body to add r1 and c, storing the result in r0. */ -rtx +rtx_insn * gen_add3_insn (rtx r0, rtx r1, rtx c) { - int icode = (int) optab_handler (add_optab, GET_MODE (r0)); + enum insn_code icode = optab_handler (add_optab, GET_MODE (r0)); if (icode == CODE_FOR_nothing - || !(insn_data[icode].operand[0].predicate - (r0, insn_data[icode].operand[0].mode)) - || !(insn_data[icode].operand[1].predicate - (r1, insn_data[icode].operand[1].mode)) - || !(insn_data[icode].operand[2].predicate - (c, insn_data[icode].operand[2].mode))) - return NULL_RTX; + || !insn_operand_matches (icode, 0, r0) + || !insn_operand_matches (icode, 1, r1) + || !insn_operand_matches (icode, 2, c)) + return NULL; return GEN_FCN (icode) (r0, r1, c); } @@ -4734,21 +4510,55 @@ int have_add2_insn (rtx x, rtx y) { - int icode; + enum insn_code icode; gcc_assert (GET_MODE (x) != VOIDmode); - icode = (int) optab_handler (add_optab, GET_MODE (x)); + icode = optab_handler (add_optab, GET_MODE (x)); if (icode == CODE_FOR_nothing) return 0; - if (!(insn_data[icode].operand[0].predicate - (x, insn_data[icode].operand[0].mode)) - || !(insn_data[icode].operand[1].predicate - (x, insn_data[icode].operand[1].mode)) - || !(insn_data[icode].operand[2].predicate - (y, insn_data[icode].operand[2].mode))) + if (!insn_operand_matches (icode, 0, x) + || !insn_operand_matches (icode, 1, x) + || !insn_operand_matches (icode, 2, y)) + return 0; + + return 1; +} + +/* Generate and return an insn body to add Y to X. */ + +rtx_insn * +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; @@ -4756,17 +4566,14 @@ /* Generate and return an insn body to subtract Y from X. */ -rtx +rtx_insn * gen_sub2_insn (rtx x, rtx y) { - int icode = (int) optab_handler (sub_optab, GET_MODE (x)); - - gcc_assert (insn_data[icode].operand[0].predicate - (x, insn_data[icode].operand[0].mode)); - gcc_assert (insn_data[icode].operand[1].predicate - (x, insn_data[icode].operand[1].mode)); - gcc_assert (insn_data[icode].operand[2].predicate - (y, insn_data[icode].operand[2].mode)); + enum insn_code icode = optab_handler (sub_optab, GET_MODE (x)); + + gcc_assert (insn_operand_matches (icode, 0, x)); + gcc_assert (insn_operand_matches (icode, 1, x)); + gcc_assert (insn_operand_matches (icode, 2, y)); return GEN_FCN (icode) (x, x, y); } @@ -4774,19 +4581,16 @@ /* Generate and return an insn body to subtract r1 and c, storing the result in r0. */ -rtx +rtx_insn * gen_sub3_insn (rtx r0, rtx r1, rtx c) { - int icode = (int) optab_handler (sub_optab, GET_MODE (r0)); + enum insn_code icode = optab_handler (sub_optab, GET_MODE (r0)); if (icode == CODE_FOR_nothing - || !(insn_data[icode].operand[0].predicate - (r0, insn_data[icode].operand[0].mode)) - || !(insn_data[icode].operand[1].predicate - (r1, insn_data[icode].operand[1].mode)) - || !(insn_data[icode].operand[2].predicate - (c, insn_data[icode].operand[2].mode))) - return NULL_RTX; + || !insn_operand_matches (icode, 0, r0) + || !insn_operand_matches (icode, 1, r1) + || !insn_operand_matches (icode, 2, c)) + return NULL; return GEN_FCN (icode) (r0, r1, c); } @@ -4794,120 +4598,34 @@ int have_sub2_insn (rtx x, rtx y) { - int icode; + enum insn_code icode; gcc_assert (GET_MODE (x) != VOIDmode); - icode = (int) optab_handler (sub_optab, GET_MODE (x)); + icode = optab_handler (sub_optab, GET_MODE (x)); if (icode == CODE_FOR_nothing) return 0; - if (!(insn_data[icode].operand[0].predicate - (x, insn_data[icode].operand[0].mode)) - || !(insn_data[icode].operand[1].predicate - (x, insn_data[icode].operand[1].mode)) - || !(insn_data[icode].operand[2].predicate - (y, insn_data[icode].operand[2].mode))) + if (!insn_operand_matches (icode, 0, x) + || !insn_operand_matches (icode, 1, x) + || !insn_operand_matches (icode, 2, y)) return 0; 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; -} -/* 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, - int unsignedp) -{ - convert_optab tab; -#ifdef HAVE_ptr_extend - if (unsignedp < 0) - return CODE_FOR_ptr_extend; -#endif - - tab = unsignedp ? zext_optab : sext_optab; - return convert_optab_handler (tab, to_mode, from_mode); -} - /* Generate the body of an insn to extend Y (with mode MFROM) 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) +rtx_insn * +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); } -/* can_fix_p and can_float_p say whether the target machine - can directly convert a given fixed point type to - a given floating point type, or vice versa. - The returned value is the CODE_FOR_... value to use, - or CODE_FOR_nothing if these modes cannot be directly converted. - - *TRUNCP_PTR is set to 1 if it is necessary to output - 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, - int unsignedp, int *truncp_ptr) -{ - convert_optab tab; - enum insn_code icode; - - tab = unsignedp ? ufixtrunc_optab : sfixtrunc_optab; - icode = convert_optab_handler (tab, fixmode, fltmode); - if (icode != CODE_FOR_nothing) - { - *truncp_ptr = 0; - return icode; - } - - /* FIXME: This requires a port to define both FIX and FTRUNC pattern - for this to work. We need to rework the fix* and ftrunc* patterns - and documentation. */ - tab = unsignedp ? ufix_optab : sfix_optab; - icode = convert_optab_handler (tab, fixmode, fltmode); - if (icode != CODE_FOR_nothing - && optab_handler (ftrunc_optab, fltmode) != CODE_FOR_nothing) - { - *truncp_ptr = 1; - return icode; - } - - *truncp_ptr = 0; - return CODE_FOR_nothing; -} - -static enum insn_code -can_float_p (enum machine_mode fltmode, enum machine_mode fixmode, - int unsignedp) -{ - convert_optab tab; - - tab = unsignedp ? ufloat_optab : sfloat_optab; - return convert_optab_handler (tab, fltmode, fixmode); -} - /* Generate code to convert FROM to floating point and store in TO. FROM must be fixed point and not VOIDmode. UNSIGNEDP nonzero means regard FROM as unsigned. @@ -4919,7 +4637,8 @@ { enum insn_code icode; rtx target = to; - enum machine_mode fmode, imode; + scalar_mode from_mode, to_mode; + machine_mode fmode, imode; bool can_do_signed = false; /* Crash now, because we won't be able to decide which mode to use. */ @@ -4930,15 +4649,14 @@ wider mode. If the integer mode is wider than the mode of FROM, we can do the conversion signed even if the input is unsigned. */ - for (fmode = GET_MODE (to); fmode != VOIDmode; - fmode = GET_MODE_WIDER_MODE (fmode)) - for (imode = GET_MODE (from); imode != VOIDmode; - imode = GET_MODE_WIDER_MODE (imode)) + FOR_EACH_MODE_FROM (fmode, GET_MODE (to)) + FOR_EACH_MODE_FROM (imode, GET_MODE (from)) { int doing_unsigned = unsignedp; if (fmode != GET_MODE (to) - && significand_size (fmode) < GET_MODE_BITSIZE (GET_MODE (from))) + && (significand_size (fmode) + < GET_MODE_UNIT_PRECISION (GET_MODE (from)))) continue; icode = can_float_p (fmode, imode, unsignedp); @@ -4970,9 +4688,13 @@ /* Unsigned integer, and no way to convert directly. Convert as signed, then unconditionally adjust the result. */ - if (unsignedp && can_do_signed) - { - rtx label = gen_label_rtx (); + if (unsignedp + && can_do_signed + && is_a <scalar_mode> (GET_MODE (to), &to_mode) + && is_a <scalar_mode> (GET_MODE (from), &from_mode)) + { + opt_scalar_mode fmode_iter; + rtx_code_label *label = gen_label_rtx (); rtx temp; REAL_VALUE_TYPE offset; @@ -4980,23 +4702,25 @@ least as wide as the target. Using FMODE will avoid rounding woes with unsigned values greater than the signed maximum value. */ - for (fmode = GET_MODE (to); fmode != VOIDmode; - fmode = GET_MODE_WIDER_MODE (fmode)) - if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode) - && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing) - break; - - if (fmode == VOIDmode) + FOR_EACH_MODE_FROM (fmode_iter, to_mode) + { + scalar_mode fmode = fmode_iter.require (); + if (GET_MODE_PRECISION (from_mode) < GET_MODE_BITSIZE (fmode) + && can_float_p (fmode, from_mode, 0) != CODE_FOR_nothing) + break; + } + + if (!fmode_iter.exists (&fmode)) { /* There is no such mode. Pretend the target is wide enough. */ - fmode = GET_MODE (to); + fmode = to_mode; /* Avoid double-rounding when TO is narrower than FROM. */ if ((significand_size (fmode) + 1) - < GET_MODE_BITSIZE (GET_MODE (from))) + < GET_MODE_PRECISION (from_mode)) { 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. */ @@ -5005,7 +4729,7 @@ || GET_MODE (target) != fmode) target = gen_reg_rtx (fmode); - imode = GET_MODE (from); + imode = from_mode; do_pending_stack_adjust (); /* Test whether the sign bit is set. */ @@ -5014,7 +4738,7 @@ /* The sign bit is not set. Convert as signed. */ expand_float (target, from, 0); - emit_jump_insn (gen_jump (label)); + emit_jump_insn (targetm.gen_jump (label)); emit_barrier (); /* The sign bit is set. @@ -5025,8 +4749,7 @@ emit_label (neglabel); temp = expand_binop (imode, and_optab, from, const1_rtx, NULL_RTX, 1, OPTAB_LIB_WIDEN); - temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node, - NULL_RTX, 1); + temp1 = expand_shift (RSHIFT_EXPR, imode, from, 1, NULL_RTX, 1); temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1, OPTAB_LIB_WIDEN); expand_float (target, temp, 0); @@ -5046,7 +4769,7 @@ /* If we are about to do some arithmetic to correct for an unsigned operand, do it in a pseudo-register. */ - if (GET_MODE (to) != fmode + if (to_mode != fmode || !REG_P (to) || REGNO (to) < FIRST_PSEUDO_REGISTER) target = gen_reg_rtx (fmode); @@ -5057,13 +4780,13 @@ correct its value by 2**bitwidth. */ do_pending_stack_adjust (); - emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from), + emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, from_mode, 0, label); - real_2expN (&offset, GET_MODE_BITSIZE (GET_MODE (from)), fmode); + real_2expN (&offset, GET_MODE_PRECISION (from_mode), fmode); temp = expand_binop (fmode, add_optab, target, - CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode), + const_double_from_real_value (offset, fmode), target, 0, OPTAB_LIB_WIDEN); if (temp != target) emit_move_insn (target, temp); @@ -5076,11 +4799,11 @@ /* 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)); @@ -5089,8 +4812,7 @@ start_sequence (); value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, - GET_MODE (to), 1, from, - GET_MODE (from)); + GET_MODE (to), from, GET_MODE (from)); insns = get_insns (); end_sequence (); @@ -5121,18 +4843,17 @@ { enum insn_code icode; rtx target = to; - enum machine_mode fmode, imode; - int must_trunc = 0; + machine_mode fmode, imode; + opt_scalar_mode fmode_iter; + bool must_trunc = false; /* We first try to find a pair of modes, one real and one integer, at least as wide as FROM and TO, respectively, in which we can open-code this conversion. If the integer mode is wider than the mode of TO, we can do the conversion either signed or unsigned. */ - for (fmode = GET_MODE (from); fmode != VOIDmode; - fmode = GET_MODE_WIDER_MODE (fmode)) - for (imode = GET_MODE (to); imode != VOIDmode; - imode = GET_MODE_WIDER_MODE (imode)) + FOR_EACH_MODE_FROM (fmode, GET_MODE (from)) + FOR_EACH_MODE_FROM (imode, GET_MODE (to)) { int doing_unsigned = unsignedp; @@ -5142,7 +4863,7 @@ 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); @@ -5191,73 +4912,80 @@ 2^63. The subtraction of 2^63 should not generate any rounding as it simply clears out that bit. The rest is trivial. */ - if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT) - for (fmode = GET_MODE (from); fmode != VOIDmode; - fmode = GET_MODE_WIDER_MODE (fmode)) - if (CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, &must_trunc) - && (!DECIMAL_FLOAT_MODE_P (fmode) - || GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to)))) - { - int bitsize; - REAL_VALUE_TYPE offset; - rtx limit, lab1, lab2, insn; - - bitsize = GET_MODE_BITSIZE (GET_MODE (to)); - real_2expN (&offset, bitsize - 1, fmode); - limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode); - lab1 = gen_label_rtx (); - lab2 = gen_label_rtx (); - - if (fmode != GET_MODE (from)) - from = convert_to_mode (fmode, from, 0); - - /* See if we need to do the subtraction. */ - do_pending_stack_adjust (); - emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, GET_MODE (from), - 0, lab1); - - /* If not, do the signed "fix" and branch around fixup code. */ - expand_fix (to, from, 0); - emit_jump_insn (gen_jump (lab2)); - emit_barrier (); - - /* Otherwise, subtract 2**(N-1), convert to signed number, - then add 2**(N-1). Do the addition using XOR since this - will often generate better code. */ - emit_label (lab1); - target = expand_binop (GET_MODE (from), sub_optab, from, limit, - NULL_RTX, 0, OPTAB_LIB_WIDEN); - expand_fix (to, target, 0); - target = expand_binop (GET_MODE (to), xor_optab, to, - gen_int_mode - ((HOST_WIDE_INT) 1 << (bitsize - 1), - GET_MODE (to)), - to, 1, OPTAB_LIB_WIDEN); - - if (target != to) - emit_move_insn (to, target); - - emit_label (lab2); - - if (optab_handler (mov_optab, GET_MODE (to)) != CODE_FOR_nothing) - { - /* Make a place for a REG_NOTE and add it. */ - insn = emit_move_insn (to, to); - set_unique_reg_note (insn, - REG_EQUAL, - gen_rtx_fmt_e (UNSIGNED_FIX, - GET_MODE (to), - copy_rtx (from))); - } - - return; - } + scalar_int_mode to_mode; + if (unsignedp + && is_a <scalar_int_mode> (GET_MODE (to), &to_mode) + && HWI_COMPUTABLE_MODE_P (to_mode)) + FOR_EACH_MODE_FROM (fmode_iter, as_a <scalar_mode> (GET_MODE (from))) + { + scalar_mode fmode = fmode_iter.require (); + if (CODE_FOR_nothing != can_fix_p (to_mode, fmode, + 0, &must_trunc) + && (!DECIMAL_FLOAT_MODE_P (fmode) + || (GET_MODE_BITSIZE (fmode) > GET_MODE_PRECISION (to_mode)))) + { + int bitsize; + REAL_VALUE_TYPE offset; + rtx limit; + rtx_code_label *lab1, *lab2; + rtx_insn *insn; + + bitsize = GET_MODE_PRECISION (to_mode); + real_2expN (&offset, bitsize - 1, fmode); + limit = const_double_from_real_value (offset, fmode); + lab1 = gen_label_rtx (); + lab2 = gen_label_rtx (); + + if (fmode != GET_MODE (from)) + from = convert_to_mode (fmode, from, 0); + + /* See if we need to do the subtraction. */ + do_pending_stack_adjust (); + emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, + GET_MODE (from), 0, lab1); + + /* If not, do the signed "fix" and branch around fixup code. */ + expand_fix (to, from, 0); + emit_jump_insn (targetm.gen_jump (lab2)); + emit_barrier (); + + /* Otherwise, subtract 2**(N-1), convert to signed number, + then add 2**(N-1). Do the addition using XOR since this + will often generate better code. */ + emit_label (lab1); + target = expand_binop (GET_MODE (from), sub_optab, from, limit, + NULL_RTX, 0, OPTAB_LIB_WIDEN); + expand_fix (to, target, 0); + target = expand_binop (to_mode, xor_optab, to, + gen_int_mode + (HOST_WIDE_INT_1 << (bitsize - 1), + to_mode), + to, 1, OPTAB_LIB_WIDEN); + + if (target != to) + emit_move_insn (to, target); + + emit_label (lab2); + + if (optab_handler (mov_optab, to_mode) != CODE_FOR_nothing) + { + /* Make a place for a REG_NOTE and add it. */ + insn = emit_move_insn (to, to); + set_dst_reg_note (insn, REG_EQUAL, + gen_rtx_fmt_e (UNSIGNED_FIX, to_mode, + copy_rtx (from)), + to); + } + + return; + } + } /* We can't do it with an insn, so use a library call. But first ensure that the mode of TO is at least as wide as SImode, since those are the only library calls we know about. */ - if (GET_MODE_SIZE (GET_MODE (to)) < GET_MODE_SIZE (SImode)) + if (GET_MODE_PRECISION (GET_MODE (to)) < GET_MODE_PRECISION (SImode)) { target = gen_reg_rtx (SImode); @@ -5265,7 +4993,7 @@ } else { - rtx insns; + rtx_insn *insns; rtx value; rtx libfunc; @@ -5276,8 +5004,7 @@ start_sequence (); value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, - GET_MODE (to), 1, from, - GET_MODE (from)); + GET_MODE (to), from, GET_MODE (from)); insns = get_insns (); end_sequence (); @@ -5295,6 +5022,33 @@ } } + +/* Promote integer arguments for a libcall if necessary. + emit_library_call_value cannot do the promotion because it does not + know if it should do a signed or unsigned promotion. This is because + there are no tree types defined for libcalls. */ + +static rtx +prepare_libcall_arg (rtx arg, int uintp) +{ + scalar_int_mode mode; + machine_mode arg_mode; + if (is_a <scalar_int_mode> (GET_MODE (arg), &mode)) + { + /* If we need to promote the integer function argument we need to do + it here instead of inside emit_library_call_value because in + emit_library_call_value we don't know if we should do a signed or + unsigned promotion. */ + + int unsigned_p = 0; + arg_mode = promote_function_mode (NULL_TREE, mode, + &unsigned_p, NULL_TREE, 0); + if (arg_mode != mode) + return convert_to_mode (arg_mode, arg, uintp); + } + return arg; +} + /* Generate code to convert FROM or TO a fixed-point. If UINTP is true, either TO or FROM is an unsigned integer. If SATP is true, we need to saturate the result. */ @@ -5302,12 +5056,13 @@ 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) @@ -5336,14 +5091,17 @@ libfunc = convert_optab_libfunc (tab, to_mode, from_mode); gcc_assert (libfunc); + from = prepare_libcall_arg (from, uintp); + from_mode = GET_MODE (from); + start_sequence (); value = emit_library_call_value (libfunc, NULL_RTX, LCT_CONST, to_mode, - 1, from, from_mode); + from, from_mode); insns = get_insns (); end_sequence (); 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 @@ -5355,22 +5113,20 @@ { 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 this conversion. If the integer mode is wider than the mode of TO, we can do the conversion either signed or unsigned. */ - for (fmode = GET_MODE (from); fmode != VOIDmode; - fmode = GET_MODE_WIDER_MODE (fmode)) - for (imode = GET_MODE (to); imode != VOIDmode; - imode = GET_MODE_WIDER_MODE (imode)) + FOR_EACH_MODE_FROM (fmode, GET_MODE (from)) + FOR_EACH_MODE_FROM (imode, GET_MODE (to)) { 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); @@ -5394,1181 +5150,11 @@ /* 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) -{ - return (code_to_optab[(int) code] != 0 - && (optab_handler (code_to_optab[(int) 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 - a mode name (downshifted to lowercase) followed by a single character - representing the number of operands for the given operation (which is - usually one of the characters '2', '3', or '4'). - - OPTABLE is the table in which libfunc fields are to be initialized. - OPNAME is the generic (string) name of the operation. - SUFFIX is the character which specifies the number of operands for - the given generic operation. - MODE is the mode to generate for. -*/ - -static void -gen_libfunc (optab optable, const char *opname, int suffix, enum machine_mode mode) -{ - unsigned opname_len = strlen (opname); - const char *mname = GET_MODE_NAME (mode); - unsigned mname_len = strlen (mname); - char *libfunc_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1); - char *p; - const char *q; - - p = libfunc_name; - *p++ = '_'; - *p++ = '_'; - for (q = opname; *q; ) - *p++ = *q++; - for (q = mname; *q; q++) - *p++ = TOLOWER (*q); - *p++ = suffix; - *p = '\0'; - - set_optab_libfunc (optable, mode, - ggc_alloc_string (libfunc_name, p - libfunc_name)); -} - -/* Like gen_libfunc, but verify that integer operation is involved. */ - -static void -gen_int_libfunc (optab optable, const char *opname, char suffix, - enum machine_mode mode) -{ - int maxsize = 2 * 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) - return; - gen_libfunc (optable, opname, suffix, mode); -} - -/* Like gen_libfunc, but verify that FP and set decimal prefix if needed. */ - -static void -gen_fp_libfunc (optab optable, const char *opname, char suffix, - enum machine_mode mode) -{ - char *dec_opname; - - if (GET_MODE_CLASS (mode) == MODE_FLOAT) - gen_libfunc (optable, opname, suffix, mode); - if (DECIMAL_FLOAT_MODE_P (mode)) - { - dec_opname = XALLOCAVEC (char, sizeof (DECIMAL_PREFIX) + strlen (opname)); - /* For BID support, change the name to have either a bid_ or dpd_ prefix - depending on the low level floating format used. */ - memcpy (dec_opname, DECIMAL_PREFIX, sizeof (DECIMAL_PREFIX) - 1); - strcpy (dec_opname + sizeof (DECIMAL_PREFIX) - 1, opname); - gen_libfunc (optable, dec_opname, suffix, mode); - } -} - -/* Like gen_libfunc, but verify that fixed-point operation is involved. */ - -static void -gen_fixed_libfunc (optab optable, const char *opname, char suffix, - enum machine_mode mode) -{ - if (!ALL_FIXED_POINT_MODE_P (mode)) - return; - gen_libfunc (optable, opname, suffix, mode); -} - -/* Like gen_libfunc, but verify that signed fixed-point operation is - involved. */ - -static void -gen_signed_fixed_libfunc (optab optable, const char *opname, char suffix, - enum machine_mode mode) -{ - if (!SIGNED_FIXED_POINT_MODE_P (mode)) - return; - gen_libfunc (optable, opname, suffix, mode); -} - -/* Like gen_libfunc, but verify that unsigned fixed-point operation is - involved. */ - -static void -gen_unsigned_fixed_libfunc (optab optable, const char *opname, char suffix, - enum machine_mode mode) -{ - if (!UNSIGNED_FIXED_POINT_MODE_P (mode)) - return; - gen_libfunc (optable, opname, suffix, mode); -} - -/* Like gen_libfunc, but verify that FP or INT operation is involved. */ - -static void -gen_int_fp_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT) - gen_fp_libfunc (optable, name, suffix, mode); - if (INTEGRAL_MODE_P (mode)) - gen_int_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 -gen_intv_fp_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT) - gen_fp_libfunc (optable, name, suffix, mode); - if (GET_MODE_CLASS (mode) == MODE_INT) - { - int len = strlen (name); - char *v_name = XALLOCAVEC (char, len + 2); - strcpy (v_name, name); - v_name[len] = 'v'; - v_name[len + 1] = 0; - gen_int_libfunc (optable, v_name, suffix, mode); - } -} - -/* Like gen_libfunc, but verify that FP or INT or FIXED operation is - involved. */ - -static void -gen_int_fp_fixed_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT) - gen_fp_libfunc (optable, name, suffix, mode); - if (INTEGRAL_MODE_P (mode)) - gen_int_libfunc (optable, name, suffix, mode); - if (ALL_FIXED_POINT_MODE_P (mode)) - gen_fixed_libfunc (optable, name, suffix, mode); -} - -/* Like gen_libfunc, but verify that FP or INT or signed FIXED operation is - involved. */ - -static void -gen_int_fp_signed_fixed_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (DECIMAL_FLOAT_MODE_P (mode) || GET_MODE_CLASS (mode) == MODE_FLOAT) - gen_fp_libfunc (optable, name, suffix, mode); - if (INTEGRAL_MODE_P (mode)) - gen_int_libfunc (optable, name, suffix, mode); - if (SIGNED_FIXED_POINT_MODE_P (mode)) - gen_signed_fixed_libfunc (optable, name, suffix, mode); -} - -/* Like gen_libfunc, but verify that INT or FIXED operation is - involved. */ - -static void -gen_int_fixed_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (INTEGRAL_MODE_P (mode)) - gen_int_libfunc (optable, name, suffix, mode); - if (ALL_FIXED_POINT_MODE_P (mode)) - gen_fixed_libfunc (optable, name, suffix, mode); -} - -/* Like gen_libfunc, but verify that INT or signed FIXED operation is - involved. */ - -static void -gen_int_signed_fixed_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (INTEGRAL_MODE_P (mode)) - gen_int_libfunc (optable, name, suffix, mode); - if (SIGNED_FIXED_POINT_MODE_P (mode)) - gen_signed_fixed_libfunc (optable, name, suffix, mode); -} - -/* Like gen_libfunc, but verify that INT or unsigned FIXED operation is - involved. */ - -static void -gen_int_unsigned_fixed_libfunc (optab optable, const char *name, char suffix, - enum machine_mode mode) -{ - if (INTEGRAL_MODE_P (mode)) - gen_int_libfunc (optable, name, suffix, mode); - if (UNSIGNED_FIXED_POINT_MODE_P (mode)) - gen_unsigned_fixed_libfunc (optable, name, suffix, mode); -} - -/* Initialize the libfunc fields of an entire group of entries of an - inter-mode-class conversion optab. The string formation rules are - similar to the ones for init_libfuncs, above, but instead of having - a mode name and an operand count these functions have two mode names - and no operand count. */ - -static void -gen_interclass_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - size_t opname_len = strlen (opname); - size_t mname_len = 0; - - const char *fname, *tname; - const char *q; - char *libfunc_name, *suffix; - char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix; - char *p; - - /* If this is a decimal conversion, add the current BID vs. DPD prefix that - depends on which underlying decimal floating point format is used. */ - const size_t dec_len = sizeof (DECIMAL_PREFIX) - 1; - - mname_len = strlen (GET_MODE_NAME (tmode)) + strlen (GET_MODE_NAME (fmode)); - - nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1); - nondec_name[0] = '_'; - nondec_name[1] = '_'; - memcpy (&nondec_name[2], opname, opname_len); - nondec_suffix = nondec_name + opname_len + 2; - - dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1); - dec_name[0] = '_'; - dec_name[1] = '_'; - memcpy (&dec_name[2], DECIMAL_PREFIX, dec_len); - memcpy (&dec_name[2+dec_len], opname, opname_len); - dec_suffix = dec_name + dec_len + opname_len + 2; - - fname = GET_MODE_NAME (fmode); - tname = GET_MODE_NAME (tmode); - - if (DECIMAL_FLOAT_MODE_P(fmode) || DECIMAL_FLOAT_MODE_P(tmode)) - { - libfunc_name = dec_name; - suffix = dec_suffix; - } - else - { - libfunc_name = nondec_name; - suffix = nondec_suffix; - } - - p = suffix; - for (q = fname; *q; p++, q++) - *p = TOLOWER (*q); - for (q = tname; *q; p++, q++) - *p = TOLOWER (*q); - - *p = '\0'; - - set_conv_libfunc (tab, tmode, fmode, - ggc_alloc_string (libfunc_name, p - libfunc_name)); -} - -/* Same as gen_interclass_conv_libfunc but verify that we are producing - int->fp conversion. */ - -static void -gen_int_to_fp_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (GET_MODE_CLASS (fmode) != MODE_INT) - return; - if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode)) - return; - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* ufloat_optab is special by using floatun for FP and floatuns decimal fp - naming scheme. */ - -static void -gen_ufloat_conv_libfunc (convert_optab tab, - const char *opname ATTRIBUTE_UNUSED, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (DECIMAL_FLOAT_MODE_P (tmode)) - gen_int_to_fp_conv_libfunc (tab, "floatuns", tmode, fmode); - else - gen_int_to_fp_conv_libfunc (tab, "floatun", tmode, fmode); -} - -/* Same as gen_interclass_conv_libfunc but verify that we are producing - fp->int conversion. */ - -static void -gen_int_to_fp_nondecimal_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (GET_MODE_CLASS (fmode) != MODE_INT) - return; - if (GET_MODE_CLASS (tmode) != MODE_FLOAT) - return; - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Same as gen_interclass_conv_libfunc but verify that we are producing - fp->int conversion with no decimal floating point involved. */ - -static void -gen_fp_to_int_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode)) - return; - if (GET_MODE_CLASS (tmode) != MODE_INT) - return; - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Initialize the libfunc fields of an of an intra-mode-class conversion optab. - The string formation rules are - similar to the ones for init_libfunc, above. */ - -static void -gen_intraclass_conv_libfunc (convert_optab tab, const char *opname, - enum machine_mode tmode, enum machine_mode fmode) -{ - size_t opname_len = strlen (opname); - size_t mname_len = 0; - - const char *fname, *tname; - const char *q; - char *nondec_name, *dec_name, *nondec_suffix, *dec_suffix; - char *libfunc_name, *suffix; - char *p; - - /* If this is a decimal conversion, add the current BID vs. DPD prefix that - depends on which underlying decimal floating point format is used. */ - const size_t dec_len = sizeof (DECIMAL_PREFIX) - 1; - - mname_len = strlen (GET_MODE_NAME (tmode)) + strlen (GET_MODE_NAME (fmode)); - - nondec_name = XALLOCAVEC (char, 2 + opname_len + mname_len + 1 + 1); - nondec_name[0] = '_'; - nondec_name[1] = '_'; - memcpy (&nondec_name[2], opname, opname_len); - nondec_suffix = nondec_name + opname_len + 2; - - dec_name = XALLOCAVEC (char, 2 + dec_len + opname_len + mname_len + 1 + 1); - dec_name[0] = '_'; - dec_name[1] = '_'; - memcpy (&dec_name[2], DECIMAL_PREFIX, dec_len); - memcpy (&dec_name[2 + dec_len], opname, opname_len); - dec_suffix = dec_name + dec_len + opname_len + 2; - - fname = GET_MODE_NAME (fmode); - tname = GET_MODE_NAME (tmode); - - if (DECIMAL_FLOAT_MODE_P(fmode) || DECIMAL_FLOAT_MODE_P(tmode)) - { - libfunc_name = dec_name; - suffix = dec_suffix; - } - else - { - libfunc_name = nondec_name; - suffix = nondec_suffix; - } - - p = suffix; - for (q = fname; *q; p++, q++) - *p = TOLOWER (*q); - for (q = tname; *q; p++, q++) - *p = TOLOWER (*q); - - *p++ = '2'; - *p = '\0'; - - set_conv_libfunc (tab, tmode, fmode, - ggc_alloc_string (libfunc_name, p - libfunc_name)); -} - -/* Pick proper libcall for trunc_optab. We need to chose if we do - truncation or extension and interclass or intraclass. */ - -static void -gen_trunc_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode)) - return; - if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode)) - return; - if (tmode == fmode) - return; - - if ((GET_MODE_CLASS (tmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (fmode)) - || (GET_MODE_CLASS (fmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (tmode))) - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); - - if (GET_MODE_PRECISION (fmode) <= GET_MODE_PRECISION (tmode)) - return; - - if ((GET_MODE_CLASS (tmode) == MODE_FLOAT - && GET_MODE_CLASS (fmode) == MODE_FLOAT) - || (DECIMAL_FLOAT_MODE_P (fmode) && DECIMAL_FLOAT_MODE_P (tmode))) - gen_intraclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Pick proper libcall for extend_optab. We need to chose if we do - truncation or extension and interclass or intraclass. */ - -static void -gen_extend_conv_libfunc (convert_optab tab, - const char *opname ATTRIBUTE_UNUSED, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (GET_MODE_CLASS (tmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (tmode)) - return; - if (GET_MODE_CLASS (fmode) != MODE_FLOAT && !DECIMAL_FLOAT_MODE_P (fmode)) - return; - if (tmode == fmode) - return; - - if ((GET_MODE_CLASS (tmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (fmode)) - || (GET_MODE_CLASS (fmode) == MODE_FLOAT && DECIMAL_FLOAT_MODE_P (tmode))) - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); - - if (GET_MODE_PRECISION (fmode) > GET_MODE_PRECISION (tmode)) - return; - - if ((GET_MODE_CLASS (tmode) == MODE_FLOAT - && GET_MODE_CLASS (fmode) == MODE_FLOAT) - || (DECIMAL_FLOAT_MODE_P (fmode) && DECIMAL_FLOAT_MODE_P (tmode))) - gen_intraclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Pick proper libcall for fract_optab. We need to chose if we do - interclass or intraclass. */ - -static void -gen_fract_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (tmode == fmode) - return; - if (!(ALL_FIXED_POINT_MODE_P (tmode) || ALL_FIXED_POINT_MODE_P (fmode))) - return; - - if (GET_MODE_CLASS (tmode) == GET_MODE_CLASS (fmode)) - gen_intraclass_conv_libfunc (tab, opname, tmode, fmode); - else - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Pick proper libcall for fractuns_optab. */ - -static void -gen_fractuns_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (tmode == fmode) - return; - /* One mode must be a fixed-point mode, and the other must be an integer - mode. */ - if (!((ALL_FIXED_POINT_MODE_P (tmode) && GET_MODE_CLASS (fmode) == MODE_INT) - || (ALL_FIXED_POINT_MODE_P (fmode) - && GET_MODE_CLASS (tmode) == MODE_INT))) - return; - - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Pick proper libcall for satfract_optab. We need to chose if we do - interclass or intraclass. */ - -static void -gen_satfract_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) -{ - if (tmode == fmode) - return; - /* TMODE must be a fixed-point mode. */ - if (!ALL_FIXED_POINT_MODE_P (tmode)) - return; - - if (GET_MODE_CLASS (tmode) == GET_MODE_CLASS (fmode)) - gen_intraclass_conv_libfunc (tab, opname, tmode, fmode); - else - gen_interclass_conv_libfunc (tab, opname, tmode, fmode); -} - -/* Pick proper libcall for satfractuns_optab. */ - -static void -gen_satfractuns_conv_libfunc (convert_optab tab, - const char *opname, - enum machine_mode tmode, - enum machine_mode fmode) +have_insn_for (enum rtx_code code, machine_mode mode) { - if (tmode == fmode) - return; - /* TMODE must be a fixed-point mode, and FMODE must be an integer mode. */ - if (!(ALL_FIXED_POINT_MODE_P (tmode) && GET_MODE_CLASS (fmode) == MODE_INT)) - 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) -{ - return IDENTIFIER_HASH_VALUE (DECL_NAME ((const_tree) entry)); -} - -static int -libfunc_decl_eq (const void *entry1, const void *entry2) -{ - return DECL_NAME ((const_tree) entry1) == (const_tree) entry2; -} - -/* Build a decl for a libfunc named NAME. */ - -tree -build_libfunc_function (const char *name) -{ - tree decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, - get_identifier (name), - build_function_type (integer_type_node, NULL_TREE)); - /* ??? We don't have any type information except for this is - a function. Pretend this is "int foo()". */ - DECL_ARTIFICIAL (decl) = 1; - DECL_EXTERNAL (decl) = 1; - TREE_PUBLIC (decl) = 1; - gcc_assert (DECL_ASSEMBLER_NAME (decl)); - - /* Zap the nonsensical SYMBOL_REF_DECL for this. What we're left with - are the flags assigned by targetm.encode_section_info. */ - SET_SYMBOL_REF_DECL (XEXP (DECL_RTL (decl), 0), NULL); - - return decl; -} - -rtx -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); - - /* 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; - if (decl == NULL) - { - /* Create a new decl, so that it can be passed to - targetm.encode_section_info. */ - decl = build_libfunc_function (name); - *slot = decl; - } - return XEXP (DECL_RTL (decl), 0); -} - -/* Adjust the assembler name of libfunc NAME to ASMSPEC. */ - -rtx -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); - gcc_assert (slot); - decl = (tree) *slot; - set_user_assembler_name (decl, asmspec); - return XEXP (DECL_RTL (decl), 0); -} - -/* 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) -{ - rtx val; - struct libfunc_entry e; - struct libfunc_entry **slot; - e.optab = (size_t) (optable - &optab_table[0]); - e.mode1 = mode; - e.mode2 = VOIDmode; - - if (name) - val = init_one_libfunc (name); - else - val = 0; - slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT); - if (*slot == NULL) - *slot = ggc_alloc_libfunc_entry (); - (*slot)->optab = (size_t) (optable - &optab_table[0]); - (*slot)->mode1 = mode; - (*slot)->mode2 = VOIDmode; - (*slot)->libfunc = val; -} - -/* Call this to reset the function entry for one conversion optab - (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) -{ - rtx val; - struct libfunc_entry e; - struct libfunc_entry **slot; - e.optab = (size_t) (optable - &convert_optab_table[0]); - e.mode1 = tmode; - e.mode2 = fmode; - - if (name) - val = init_one_libfunc (name); - else - val = 0; - slot = (struct libfunc_entry **) htab_find_slot (libfunc_hash, &e, INSERT); - if (*slot == NULL) - *slot = ggc_alloc_libfunc_entry (); - (*slot)->optab = (size_t) (optable - &convert_optab_table[0]); - (*slot)->mode1 = tmode; - (*slot)->mode2 = fmode; - (*slot)->libfunc = val; -} - -/* Call this to initialize the contents of the optabs - appropriately for the current target machine. */ - -void -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 (); - } - 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_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 (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_extract_even_optab, UNKNOWN); - init_optab (vec_extract_odd_optab, UNKNOWN); - init_optab (vec_interleave_high_optab, UNKNOWN); - init_optab (vec_interleave_low_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_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); - - /* 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; - 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; - - /* The ffs function operates on `int'. Fall back on it if we do not - have a libgcc2 function for that width. */ - if (INT_TYPE_SIZE < BITS_PER_WORD) - set_optab_libfunc (ffs_optab, mode_for_size (INT_TYPE_SIZE, MODE_INT, 0), - "ffs"); - - /* Explicitly initialize the bswap libfuncs since we need them to be - valid for things other than word_mode. */ - set_optab_libfunc (bswap_optab, SImode, "__bswapsi2"); - set_optab_libfunc (bswap_optab, DImode, "__bswapdi2"); - - /* 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"); - - abort_libfunc = init_one_libfunc ("abort"); - memcpy_libfunc = init_one_libfunc ("memcpy"); - memmove_libfunc = init_one_libfunc ("memmove"); - memcmp_libfunc = init_one_libfunc ("memcmp"); - memset_libfunc = init_one_libfunc ("memset"); - setbits_libfunc = init_one_libfunc ("__setbits"); - -#ifndef DONT_USE_BUILTIN_SETJMP - setjmp_libfunc = init_one_libfunc ("__builtin_setjmp"); - longjmp_libfunc = init_one_libfunc ("__builtin_longjmp"); -#else - setjmp_libfunc = init_one_libfunc ("setjmp"); - longjmp_libfunc = init_one_libfunc ("longjmp"); -#endif - unwind_sjlj_register_libfunc = init_one_libfunc ("_Unwind_SjLj_Register"); - unwind_sjlj_unregister_libfunc - = init_one_libfunc ("_Unwind_SjLj_Unregister"); - - /* For function entry/exit instrumentation. */ - profile_function_entry_libfunc - = init_one_libfunc ("__cyg_profile_func_enter"); - profile_function_exit_libfunc - = init_one_libfunc ("__cyg_profile_func_exit"); - - gcov_flush_libfunc = init_one_libfunc ("__gcov_flush"); - - /* Allow the target to add more libcalls or rename some, etc. */ - targetm.init_libfuncs (); + return (code_to_optab (code) + && (optab_handler (code_to_optab (code), mode) + != CODE_FOR_nothing)); } /* Print information about the current contents of the optabs on @@ -6577,45 +5163,35 @@ 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)); @@ -6623,16 +5199,15 @@ } } - /* Generate insns to trap with code TCODE if OP1 and OP2 satisfy condition CODE. Return 0 on failure. */ -rtx +rtx_insn * 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_insn *insn; rtx trap_rtx; if (mode == VOIDmode) @@ -6643,8 +5218,7 @@ return 0; /* Some targets only accept a zero trap code. */ - if (insn_data[icode].operand[3].predicate - && !insn_data[icode].operand[3].predicate (tcode, VOIDmode)) + if (!insn_operand_matches (icode, 3, tcode)) return 0; do_pending_stack_adjust (); @@ -6652,7 +5226,7 @@ prepare_cmp_insn (op1, op2, code, NULL_RTX, false, OPTAB_DIRECT, &trap_rtx, &mode); if (!trap_rtx) - insn = NULL_RTX; + insn = NULL; else insn = GEN_FCN (icode) (trap_rtx, XEXP (trap_rtx, 0), XEXP (trap_rtx, 1), tcode); @@ -6673,7 +5247,7 @@ /* 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; @@ -6723,70 +5297,322 @@ code = LTGT; break; + case BIT_AND_EXPR: + code = AND; + break; + + case BIT_IOR_EXPR: + code = IOR; + break; + default: gcc_unreachable (); } return code; } -/* Return comparison rtx for COND. Use UNSIGNEDP to select signed or - unsigned operators. Do not generate compare instruction. */ +/* Return a comparison rtx of mode CMP_MODE for COND. Use UNSIGNEDP to + select signed or unsigned operators. OPNO holds the index of the + first comparison operand for insn ICODE. Do not generate the + compare instruction itself. */ static rtx -vector_compare_rtx (tree cond, bool unsignedp, enum insn_code icode) +vector_compare_rtx (machine_mode cmp_mode, enum tree_code tcode, + tree t_op0, tree t_op1, bool unsignedp, + enum insn_code icode, unsigned int opno) { - enum rtx_code rcode; - tree t_op0, t_op1; + struct expand_operand ops[2]; rtx rtx_op0, rtx_op1; - - /* This is unlikely. While generating VEC_COND_EXPR, auto vectorizer - ensures that condition is a relational operation. */ - gcc_assert (COMPARISON_CLASS_P (cond)); - - rcode = get_rtx_code (TREE_CODE (cond), unsignedp); - t_op0 = TREE_OPERAND (cond, 0); - t_op1 = TREE_OPERAND (cond, 1); - - /* Expand operands. */ + machine_mode m0, m1; + enum rtx_code rcode = get_rtx_code (tcode, unsignedp); + + gcc_assert (TREE_CODE_CLASS (tcode) == tcc_comparison); + + /* 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); - - if (!insn_data[icode].operand[4].predicate (rtx_op0, GET_MODE (rtx_op0)) - && GET_MODE (rtx_op0) != VOIDmode) - rtx_op0 = force_reg (GET_MODE (rtx_op0), rtx_op0); - - if (!insn_data[icode].operand[5].predicate (rtx_op1, GET_MODE (rtx_op1)) - && GET_MODE (rtx_op1) != VOIDmode) - rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1); - - return gen_rtx_fmt_ee (rcode, VOIDmode, rtx_op0, rtx_op1); + m1 = GET_MODE (rtx_op1); + if (m1 == VOIDmode) + m1 = TYPE_MODE (TREE_TYPE (t_op1)); + + create_input_operand (&ops[0], rtx_op0, m0); + create_input_operand (&ops[1], rtx_op1, m1); + if (!maybe_legitimize_operands (icode, opno, 2, ops)) + gcc_unreachable (); + return gen_rtx_fmt_ee (rcode, cmp_mode, ops[0].value, ops[1].value); +} + +/* Checks if vec_perm mask SEL is a constant equivalent to a shift of the first + vec_perm operand, assuming the second operand is a constant vector of zeroes. + Return the shift distance in bits if so, or NULL_RTX if the vec_perm is not a + shift. */ +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_UNIT_BITSIZE (GET_MODE (sel)); + + if (GET_CODE (sel) != CONST_VECTOR) + return NULL_RTX; + + first = INTVAL (CONST_VECTOR_ELT (sel, 0)); + if (first >= nelt) + return NULL_RTX; + for (i = 1; i < nelt; i++) + { + int idx = INTVAL (CONST_VECTOR_ELT (sel, i)); + unsigned int expected = i + first; + /* Indices into the second vector are all equivalent. */ + if (idx < 0 || (MIN (nelt, (unsigned) idx) != MIN (nelt, expected))) + return NULL_RTX; + } + + return GEN_INT (first * bitsize); } -/* Return insn code for TYPE, the type of a VEC_COND_EXPR. */ - -static inline enum insn_code -get_vcond_icode (tree type, enum machine_mode mode) +/* 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) +{ + machine_mode tmode = GET_MODE (target); + machine_mode smode = GET_MODE (sel); + struct expand_operand ops[4]; + + create_output_operand (&ops[0], target, tmode); + create_input_operand (&ops[3], sel, smode); + + /* Make an effort to preserve v0 == v1. The target expander is able to + rely on this to determine if we're permuting a single input operand. */ + if (rtx_equal_p (v0, v1)) + { + if (!insn_operand_matches (icode, 1, v0)) + v0 = force_reg (tmode, v0); + gcc_checking_assert (insn_operand_matches (icode, 1, v0)); + gcc_checking_assert (insn_operand_matches (icode, 2, v0)); + + create_fixed_operand (&ops[1], v0); + create_fixed_operand (&ops[2], v0); + } + else + { + create_input_operand (&ops[1], v0, tmode); + create_input_operand (&ops[2], v1, tmode); + } + + if (maybe_expand_insn (icode, 4, ops)) + return ops[0].value; + return NULL_RTX; +} + +/* Generate instructions for vec_perm optab given its mode + and three operands. */ + +rtx +expand_vec_perm (machine_mode mode, rtx v0, rtx v1, rtx sel, rtx target) { - enum insn_code icode = CODE_FOR_nothing; - - if (TYPE_UNSIGNED (type)) - icode = direct_optab_handler (vcondu_optab, mode); - else - icode = direct_optab_handler (vcond_optab, mode); - return icode; + enum insn_code icode; + machine_mode qimode; + unsigned int i, w, e, u; + rtx tmp, sel_qi = NULL; + rtvec vec; + + if (!target || GET_MODE (target) != mode) + target = gen_reg_rtx (mode); + + w = GET_MODE_SIZE (mode); + e = GET_MODE_NUNITS (mode); + u = GET_MODE_UNIT_SIZE (mode); + + /* Set QIMODE to a different vector mode with byte elements. + If no such mode, or if MODE already has byte elements, use VOIDmode. */ + if (GET_MODE_INNER (mode) == QImode + || !mode_for_vector (QImode, w).exists (&qimode) + || !VECTOR_MODE_P (qimode)) + qimode = VOIDmode; + + /* If the input is a constant, expand it specially. */ + gcc_assert (GET_MODE_CLASS (GET_MODE (sel)) == MODE_VECTOR_INT); + if (GET_CODE (sel) == CONST_VECTOR) + { + /* See if this can be handled with a vec_shr. We only do this if the + second vector is all zeroes. */ + enum insn_code shift_code = optab_handler (vec_shr_optab, mode); + enum insn_code shift_code_qi = ((qimode != VOIDmode && qimode != mode) + ? optab_handler (vec_shr_optab, qimode) + : CODE_FOR_nothing); + rtx shift_amt = NULL_RTX; + if (v1 == CONST0_RTX (GET_MODE (v1)) + && (shift_code != CODE_FOR_nothing + || shift_code_qi != CODE_FOR_nothing)) + { + shift_amt = shift_amt_for_vec_perm_mask (sel); + if (shift_amt) + { + struct expand_operand ops[3]; + if (shift_code != CODE_FOR_nothing) + { + create_output_operand (&ops[0], target, mode); + create_input_operand (&ops[1], v0, mode); + create_convert_operand_from_type (&ops[2], shift_amt, + sizetype); + if (maybe_expand_insn (shift_code, 3, ops)) + return ops[0].value; + } + if (shift_code_qi != CODE_FOR_nothing) + { + tmp = gen_reg_rtx (qimode); + create_output_operand (&ops[0], tmp, qimode); + create_input_operand (&ops[1], gen_lowpart (qimode, v0), + qimode); + create_convert_operand_from_type (&ops[2], shift_amt, + sizetype); + if (maybe_expand_insn (shift_code_qi, 3, ops)) + return gen_lowpart (mode, ops[0].value); + } + } + } + + icode = direct_optab_handler (vec_perm_const_optab, mode); + if (icode != CODE_FOR_nothing) + { + tmp = expand_vec_perm_1 (icode, target, v0, v1, sel); + if (tmp) + return tmp; + } + + /* Fall back to a constant byte-based permutation. */ + if (qimode != VOIDmode) + { + vec = rtvec_alloc (w); + for (i = 0; i < e; ++i) + { + unsigned int j, this_e; + + this_e = INTVAL (CONST_VECTOR_ELT (sel, i)); + this_e &= 2 * e - 1; + this_e *= u; + + for (j = 0; j < u; ++j) + RTVEC_ELT (vec, i * u + j) = GEN_INT (this_e + j); + } + sel_qi = gen_rtx_CONST_VECTOR (qimode, vec); + + icode = direct_optab_handler (vec_perm_const_optab, qimode); + if (icode != CODE_FOR_nothing) + { + tmp = mode != qimode ? gen_reg_rtx (qimode) : target; + tmp = expand_vec_perm_1 (icode, tmp, gen_lowpart (qimode, v0), + gen_lowpart (qimode, v1), sel_qi); + if (tmp) + return gen_lowpart (mode, tmp); + } + } + } + + /* Otherwise expand as a fully variable permuation. */ + icode = direct_optab_handler (vec_perm_optab, mode); + if (icode != CODE_FOR_nothing) + { + tmp = expand_vec_perm_1 (icode, target, v0, v1, sel); + if (tmp) + return tmp; + } + + /* As a special case to aid several targets, lower the element-based + permutation to a byte-based permutation and try again. */ + if (qimode == VOIDmode) + return NULL_RTX; + icode = direct_optab_handler (vec_perm_optab, qimode); + if (icode == CODE_FOR_nothing) + return NULL_RTX; + + if (sel_qi == NULL) + { + /* Multiply each element by its byte size. */ + machine_mode selmode = GET_MODE (sel); + if (u == 2) + sel = expand_simple_binop (selmode, PLUS, sel, sel, + NULL, 0, OPTAB_DIRECT); + else + sel = expand_simple_binop (selmode, ASHIFT, sel, + GEN_INT (exact_log2 (u)), + NULL, 0, OPTAB_DIRECT); + gcc_assert (sel != NULL); + + /* Broadcast the low byte each element into each of its bytes. */ + vec = rtvec_alloc (w); + for (i = 0; i < w; ++i) + { + int this_e = i / u * u; + if (BYTES_BIG_ENDIAN) + this_e += u - 1; + RTVEC_ELT (vec, i) = GEN_INT (this_e); + } + tmp = gen_rtx_CONST_VECTOR (qimode, vec); + sel = gen_lowpart (qimode, sel); + sel = expand_vec_perm (qimode, sel, sel, tmp, NULL); + gcc_assert (sel != NULL); + + /* Add the byte offset to each byte element. */ + /* Note that the definition of the indicies here is memory ordering, + so there should be no difference between big and little endian. */ + vec = rtvec_alloc (w); + for (i = 0; i < w; ++i) + RTVEC_ELT (vec, i) = GEN_INT (i % u); + tmp = gen_rtx_CONST_VECTOR (qimode, vec); + sel_qi = expand_simple_binop (qimode, PLUS, sel, tmp, + sel, 0, OPTAB_DIRECT); + gcc_assert (sel_qi != NULL); + } + + 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); + return tmp; } -/* Return TRUE iff, appropriate vector insns are available - for vector cond expr with type TYPE in VMODE mode. */ - -bool -expand_vec_cond_expr_p (tree type, enum machine_mode vmode) +/* Generate insns for a VEC_COND_EXPR with mask, given its TYPE and its + three operands. */ + +rtx +expand_vec_cond_mask_expr (tree vec_cond_type, tree op0, tree op1, tree op2, + rtx target) { - if (get_vcond_icode (type, vmode) == CODE_FOR_nothing) - return false; - return true; + struct expand_operand ops[4]; + machine_mode mode = TYPE_MODE (vec_cond_type); + machine_mode mask_mode = TYPE_MODE (TREE_TYPE (op0)); + enum insn_code icode = get_vcond_mask_icode (mode, mask_mode); + rtx mask, rtx_op1, rtx_op2; + + if (icode == CODE_FOR_nothing) + return 0; + + mask = expand_normal (op0); + rtx_op1 = expand_normal (op1); + rtx_op2 = expand_normal (op2); + + mask = force_reg (mask_mode, mask); + rtx_op1 = force_reg (GET_MODE (rtx_op1), rtx_op1); + + create_output_operand (&ops[0], target, mode); + create_input_operand (&ops[1], rtx_op1, mode); + create_input_operand (&ops[2], rtx_op2, mode); + create_input_operand (&ops[3], mask, mask_mode); + expand_insn (icode, 4, ops); + + return ops[0].value; } /* Generate insns for a VEC_COND_EXPR, given its TYPE and its @@ -6796,91 +5622,181 @@ expand_vec_cond_expr (tree vec_cond_type, tree op0, tree op1, tree op2, rtx target) { + struct expand_operand ops[6]; enum insn_code icode; - rtx comparison, rtx_op1, rtx_op2, cc_op0, cc_op1; - enum machine_mode mode = TYPE_MODE (vec_cond_type); - bool unsignedp = TYPE_UNSIGNED (vec_cond_type); - - icode = get_vcond_icode (vec_cond_type, mode); + rtx comparison, rtx_op1, rtx_op2; + machine_mode mode = TYPE_MODE (vec_cond_type); + machine_mode cmp_op_mode; + bool unsignedp; + tree op0a, op0b; + enum tree_code tcode; + + if (COMPARISON_CLASS_P (op0)) + { + op0a = TREE_OPERAND (op0, 0); + op0b = TREE_OPERAND (op0, 1); + tcode = TREE_CODE (op0); + } + else + { + gcc_assert (VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (op0))); + if (get_vcond_mask_icode (mode, TYPE_MODE (TREE_TYPE (op0))) + != CODE_FOR_nothing) + return expand_vec_cond_mask_expr (vec_cond_type, op0, op1, + op2, target); + /* Fake op0 < 0. */ + else + { + gcc_assert (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (op0))) + == MODE_VECTOR_INT); + op0a = op0; + op0b = build_zero_cst (TREE_TYPE (op0)); + tcode = LT_EXPR; + } + } + cmp_op_mode = TYPE_MODE (TREE_TYPE (op0a)); + unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a)); + + + gcc_assert (GET_MODE_SIZE (mode) == GET_MODE_SIZE (cmp_op_mode) + && GET_MODE_NUNITS (mode) == GET_MODE_NUNITS (cmp_op_mode)); + + icode = get_vcond_icode (mode, cmp_op_mode, unsignedp); if (icode == CODE_FOR_nothing) - return 0; - - if (!target || !insn_data[icode].operand[0].predicate (target, mode)) - target = gen_reg_rtx (mode); - - /* Get comparison rtx. First expand both cond expr operands. */ - comparison = vector_compare_rtx (op0, - unsignedp, icode); - cc_op0 = XEXP (comparison, 0); - cc_op1 = XEXP (comparison, 1); - /* Expand both operands and force them in reg, if required. */ + { + if (tcode == EQ_EXPR || tcode == NE_EXPR) + icode = get_vcond_eq_icode (mode, cmp_op_mode); + if (icode == CODE_FOR_nothing) + return 0; + } + + comparison = vector_compare_rtx (VOIDmode, tcode, op0a, op0b, unsignedp, + icode, 4); rtx_op1 = expand_normal (op1); - if (!insn_data[icode].operand[1].predicate (rtx_op1, mode) - && mode != VOIDmode) - rtx_op1 = force_reg (mode, rtx_op1); - rtx_op2 = expand_normal (op2); - if (!insn_data[icode].operand[2].predicate (rtx_op2, mode) - && mode != VOIDmode) - rtx_op2 = force_reg (mode, rtx_op2); - - /* Emit instruction! */ - emit_insn (GEN_FCN (icode) (target, rtx_op1, rtx_op2, - comparison, cc_op0, cc_op1)); - - return target; + + create_output_operand (&ops[0], target, mode); + create_input_operand (&ops[1], rtx_op1, mode); + create_input_operand (&ops[2], rtx_op2, mode); + create_fixed_operand (&ops[3], comparison); + create_fixed_operand (&ops[4], XEXP (comparison, 0)); + create_fixed_operand (&ops[5], XEXP (comparison, 1)); + expand_insn (icode, 6, ops); + return ops[0].value; } - -/* This is an internal subroutine of the other compare_and_swap expanders. - MEM, OLD_VAL and NEW_VAL are as you'd expect for a compare-and-swap - operation. TARGET is an optional place to store the value result of - the operation. ICODE is the particular instruction to expand. Return - the result of the operation. */ - -static rtx -expand_val_compare_and_swap_1 (rtx mem, rtx old_val, rtx new_val, - rtx target, enum insn_code icode) -{ - enum machine_mode mode = GET_MODE (mem); - rtx insn; - - if (!target || !insn_data[icode].operand[0].predicate (target, mode)) - target = gen_reg_rtx (mode); - - if (GET_MODE (old_val) != VOIDmode && GET_MODE (old_val) != mode) - old_val = convert_modes (mode, GET_MODE (old_val), old_val, 1); - if (!insn_data[icode].operand[2].predicate (old_val, mode)) - old_val = force_reg (mode, old_val); - - if (GET_MODE (new_val) != VOIDmode && GET_MODE (new_val) != mode) - new_val = convert_modes (mode, GET_MODE (new_val), new_val, 1); - if (!insn_data[icode].operand[3].predicate (new_val, mode)) - new_val = force_reg (mode, new_val); - - insn = GEN_FCN (icode) (target, mem, old_val, new_val); - if (insn == NULL_RTX) - return NULL_RTX; - emit_insn (insn); - - return target; -} - -/* Expand a compare-and-swap operation and return its value. */ +/* Generate insns for a vector comparison into a mask. */ rtx -expand_val_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target) +expand_vec_cmp_expr (tree type, tree exp, rtx target) +{ + struct expand_operand ops[4]; + enum insn_code icode; + rtx comparison; + machine_mode mask_mode = TYPE_MODE (type); + machine_mode vmode; + bool unsignedp; + tree op0a, op0b; + enum tree_code tcode; + + op0a = TREE_OPERAND (exp, 0); + op0b = TREE_OPERAND (exp, 1); + tcode = TREE_CODE (exp); + + unsignedp = TYPE_UNSIGNED (TREE_TYPE (op0a)); + vmode = TYPE_MODE (TREE_TYPE (op0a)); + + icode = get_vec_cmp_icode (vmode, mask_mode, unsignedp); + if (icode == CODE_FOR_nothing) + { + if (tcode == EQ_EXPR || tcode == NE_EXPR) + icode = get_vec_cmp_eq_icode (vmode, mask_mode); + if (icode == CODE_FOR_nothing) + return 0; + } + + comparison = vector_compare_rtx (mask_mode, tcode, op0a, op0b, + unsignedp, icode, 2); + create_output_operand (&ops[0], target, mask_mode); + create_fixed_operand (&ops[1], comparison); + create_fixed_operand (&ops[2], XEXP (comparison, 0)); + create_fixed_operand (&ops[3], XEXP (comparison, 1)); + expand_insn (icode, 4, ops); + return ops[0].value; +} + +/* Expand a highpart multiply. */ + +rtx +expand_mult_highpart (machine_mode mode, rtx op0, rtx op1, + rtx target, bool uns_p) { - enum machine_mode mode = GET_MODE (mem); - enum insn_code icode - = direct_optab_handler (sync_compare_and_swap_optab, mode); - - if (icode == CODE_FOR_nothing) - return NULL_RTX; - - return expand_val_compare_and_swap_1 (mem, old_val, new_val, target, icode); + 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) + std::swap (tab1, tab2); + 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); } - + /* Helper function to find the MODE_CC set in a sync_compare_and_swap pattern. */ @@ -6896,58 +5812,6 @@ } } -/* Expand a compare-and-swap operation and store true into the result if - the operation was successful and false otherwise. Return the result. - Unlike other routines, TARGET is not optional. */ - -rtx -expand_bool_compare_and_swap (rtx mem, rtx old_val, rtx new_val, rtx target) -{ - enum machine_mode mode = GET_MODE (mem); - enum insn_code icode; - rtx subtarget, seq, cc_reg; - - /* If the target supports a compare-and-swap pattern that simultaneously - sets some flag for success, then use it. Otherwise use the regular - compare-and-swap and follow that immediately with a compare insn. */ - icode = direct_optab_handler (sync_compare_and_swap_optab, mode); - if (icode == CODE_FOR_nothing) - return NULL_RTX; - - do_pending_stack_adjust (); - do - { - start_sequence (); - subtarget = expand_val_compare_and_swap_1 (mem, old_val, new_val, - NULL_RTX, icode); - cc_reg = NULL_RTX; - if (subtarget == NULL_RTX) - { - end_sequence (); - return NULL_RTX; - } - - if (have_insn_for (COMPARE, CCmode)) - note_stores (PATTERN (get_last_insn ()), find_cc_set, &cc_reg); - seq = get_insns (); - end_sequence (); - - /* We might be comparing against an old value. Try again. :-( */ - if (!cc_reg && MEM_P (old_val)) - { - seq = NULL_RTX; - old_val = force_reg (mode, old_val); - } - } - while (!seq); - - emit_insn (seq); - if (cc_reg) - return emit_store_flag_force (target, EQ, cc_reg, const0_rtx, VOIDmode, 0, 1); - else - return emit_store_flag_force (target, EQ, subtarget, old_val, VOIDmode, 1, 1); -} - /* This is a helper function for the other atomic operations. This function emits a loop that contains SEQ that iterates until a compare-and-swap operation at the end succeeds. MEM is the memory to be modified. SEQ is @@ -6960,9 +5824,9 @@ static bool expand_compare_and_swap_loop (rtx mem, rtx old_reg, rtx new_reg, rtx seq) { - enum machine_mode mode = GET_MODE (mem); - enum insn_code icode; - rtx label, cmp_reg, subtarget, cc_reg; + machine_mode mode = GET_MODE (mem); + rtx_code_label *label; + rtx cmp_reg, success, oldval; /* The loop we want to generate looks like @@ -6970,8 +5834,8 @@ label: old_reg = cmp_reg; seq; - cmp_reg = compare-and-swap(mem, old_reg, new_reg) - if (cmp_reg != old_reg) + (success, cmp_reg) = compare-and-swap(mem, old_reg, new_reg) + if (success) goto label; Note that we only do the plain load from memory once. Subsequent @@ -6986,343 +5850,121 @@ if (seq) emit_insn (seq); - /* If the target supports a compare-and-swap pattern that simultaneously - sets some flag for success, then use it. Otherwise use the regular - compare-and-swap and follow that immediately with a compare insn. */ - icode = direct_optab_handler (sync_compare_and_swap_optab, mode); - if (icode == CODE_FOR_nothing) - return false; - - subtarget = expand_val_compare_and_swap_1 (mem, old_reg, new_reg, - cmp_reg, icode); - if (subtarget == NULL_RTX) + success = NULL_RTX; + oldval = cmp_reg; + if (!expand_atomic_compare_and_swap (&success, &oldval, mem, old_reg, + new_reg, false, MEMMODEL_SYNC_SEQ_CST, + MEMMODEL_RELAXED)) return false; - cc_reg = NULL_RTX; - if (have_insn_for (COMPARE, CCmode)) - note_stores (PATTERN (get_last_insn ()), find_cc_set, &cc_reg); - if (cc_reg) - { - cmp_reg = cc_reg; - old_reg = const0_rtx; - } - else - { - if (subtarget != cmp_reg) - emit_move_insn (cmp_reg, subtarget); - } - - /* ??? Mark this jump predicted not taken? */ - emit_cmp_and_jump_insns (cmp_reg, old_reg, NE, const0_rtx, GET_MODE (cmp_reg), 1, - label); + if (oldval != cmp_reg) + emit_move_insn (cmp_reg, oldval); + + /* Mark this jump predicted not taken. */ + emit_cmp_and_jump_insns (success, const0_rtx, EQ, const0_rtx, + GET_MODE (success), 1, label, + profile_probability::guessed_never ()); return true; } -/* This function generates the atomic operation MEM CODE= VAL. In this - case, we do not care about any resulting value. Returns NULL if we - cannot generate the operation. */ - -rtx -expand_sync_operation (rtx mem, rtx val, enum rtx_code code) + +/* This function tries to emit an atomic_exchange intruction. VAL is written + to *MEM using memory model MODEL. The previous contents of *MEM are returned, + using TARGET if possible. */ + +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; - rtx insn; - - /* Look to see if the target supports the operation directly. */ - switch (code) - { - case PLUS: - icode = direct_optab_handler (sync_add_optab, mode); - break; - case IOR: - icode = direct_optab_handler (sync_ior_optab, mode); - break; - case XOR: - icode = direct_optab_handler (sync_xor_optab, mode); - break; - case AND: - icode = direct_optab_handler (sync_and_optab, mode); - break; - case NOT: - icode = direct_optab_handler (sync_nand_optab, mode); - break; - - case MINUS: - icode = direct_optab_handler (sync_sub_optab, mode); - if (icode == CODE_FOR_nothing || CONST_INT_P (val)) - { - icode = direct_optab_handler (sync_add_optab, mode); - if (icode != CODE_FOR_nothing) - { - val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1); - code = PLUS; - } - } - break; - - default: - gcc_unreachable (); - } - - /* Generate the direct operation, if present. */ + + /* If the target supports the exchange directly, great. */ + icode = direct_optab_handler (atomic_exchange_optab, mode); if (icode != CODE_FOR_nothing) { - if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode) - val = convert_modes (mode, GET_MODE (val), val, 1); - if (!insn_data[icode].operand[1].predicate (val, mode)) - val = force_reg (mode, val); - - insn = GEN_FCN (icode) (mem, val); - if (insn) - { - emit_insn (insn); - return const0_rtx; - } - } - - /* Failing that, generate a compare-and-swap loop in which we perform the - operation with normal arithmetic instructions. */ - if (direct_optab_handler (sync_compare_and_swap_optab, mode) - != CODE_FOR_nothing) - { - rtx t0 = gen_reg_rtx (mode), t1; - - start_sequence (); - - t1 = t0; - if (code == NOT) - { - t1 = expand_simple_binop (mode, AND, t1, val, NULL_RTX, - true, OPTAB_LIB_WIDEN); - t1 = expand_simple_unop (mode, code, t1, NULL_RTX, true); - } - else - t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX, - true, OPTAB_LIB_WIDEN); - insn = get_insns (); - end_sequence (); - - if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn)) - return const0_rtx; + struct expand_operand ops[4]; + + create_output_operand (&ops[0], target, mode); + create_fixed_operand (&ops[1], mem); + create_input_operand (&ops[2], val, mode); + create_integer_operand (&ops[3], model); + if (maybe_expand_insn (icode, 4, ops)) + return ops[0].value; } return NULL_RTX; } -/* This function generates the atomic operation MEM CODE= VAL. In this - case, we do care about the resulting value: if AFTER is true then - return the value MEM holds after the operation, if AFTER is false - then return the value MEM holds before the operation. TARGET is an - optional place for the result value to be stored. */ - -rtx -expand_sync_fetch_operation (rtx mem, rtx val, enum rtx_code code, - bool after, rtx target) +/* This function tries to implement an atomic exchange operation using + __sync_lock_test_and_set. VAL is written to *MEM using memory model MODEL. + The previous contents of *MEM are returned, using TARGET if possible. + Since this instructionn is an acquire barrier only, stronger memory + models may require additional barriers to be emitted. */ + +static rtx +maybe_emit_sync_lock_test_and_set (rtx target, rtx mem, rtx val, + enum memmodel model) { - enum machine_mode mode = GET_MODE (mem); - enum insn_code old_code, new_code, icode; - bool compensate; - rtx insn; - - /* Look to see if the target supports the operation directly. */ - switch (code) - { - case PLUS: - old_code = direct_optab_handler (sync_old_add_optab, mode); - new_code = direct_optab_handler (sync_new_add_optab, mode); - break; - case IOR: - old_code = direct_optab_handler (sync_old_ior_optab, mode); - new_code = direct_optab_handler (sync_new_ior_optab, mode); - break; - case XOR: - old_code = direct_optab_handler (sync_old_xor_optab, mode); - new_code = direct_optab_handler (sync_new_xor_optab, mode); - break; - case AND: - old_code = direct_optab_handler (sync_old_and_optab, mode); - new_code = direct_optab_handler (sync_new_and_optab, mode); - break; - case NOT: - old_code = direct_optab_handler (sync_old_nand_optab, mode); - new_code = direct_optab_handler (sync_new_nand_optab, mode); - break; - - case MINUS: - old_code = direct_optab_handler (sync_old_sub_optab, mode); - new_code = direct_optab_handler (sync_new_sub_optab, mode); - if ((old_code == CODE_FOR_nothing && new_code == CODE_FOR_nothing) - || CONST_INT_P (val)) - { - old_code = direct_optab_handler (sync_old_add_optab, mode); - new_code = direct_optab_handler (sync_new_add_optab, mode); - if (old_code != CODE_FOR_nothing || new_code != CODE_FOR_nothing) - { - val = expand_simple_unop (mode, NEG, val, NULL_RTX, 1); - code = PLUS; - } - } - break; - - default: - gcc_unreachable (); - } - - /* If the target does supports the proper new/old operation, great. But - if we only support the opposite old/new operation, check to see if we - can compensate. In the case in which the old value is supported, then - we can always perform the operation again with normal arithmetic. In - the case in which the new value is supported, then we can only handle - this in the case the operation is reversible. */ - compensate = false; - if (after) - { - icode = new_code; - if (icode == CODE_FOR_nothing) - { - icode = old_code; - if (icode != CODE_FOR_nothing) - compensate = true; - } - } - else - { - icode = old_code; - if (icode == CODE_FOR_nothing - && (code == PLUS || code == MINUS || code == XOR)) + machine_mode mode = GET_MODE (mem); + enum insn_code icode; + rtx_insn *last_insn = get_last_insn (); + + icode = optab_handler (sync_lock_test_and_set_optab, mode); + + /* Legacy sync_lock_test_and_set is an acquire barrier. If the pattern + exists, and the memory model is stronger than acquire, add a release + barrier before the instruction. */ + + 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); + create_input_operand (&ops[2], val, mode); + if (maybe_expand_insn (icode, 3, ops)) + return ops[0].value; + } + + /* If an external test-and-set libcall is provided, use that instead of + any external compare-and-swap that we might get from the compare-and- + swap-loop expansion later. */ + if (!can_compare_and_swap_p (mode, false)) + { + rtx libfunc = optab_libfunc (sync_lock_test_and_set_optab, mode); + if (libfunc != NULL) { - icode = new_code; - if (icode != CODE_FOR_nothing) - compensate = true; + rtx addr; + + addr = convert_memory_address (ptr_mode, XEXP (mem, 0)); + return emit_library_call_value (libfunc, NULL_RTX, LCT_NORMAL, + mode, addr, ptr_mode, + val, mode); } } - /* If we found something supported, great. */ - if (icode != CODE_FOR_nothing) - { - if (!target || !insn_data[icode].operand[0].predicate (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 (!insn_data[icode].operand[2].predicate (val, mode)) - val = force_reg (mode, val); - - insn = GEN_FCN (icode) (target, mem, val); - if (insn) - { - emit_insn (insn); - - /* If we need to compensate for using an operation with the - wrong return value, do so now. */ - if (compensate) - { - if (!after) - { - if (code == PLUS) - code = MINUS; - else if (code == MINUS) - code = PLUS; - } - - if (code == NOT) - { - target = expand_simple_binop (mode, AND, target, val, - NULL_RTX, true, - OPTAB_LIB_WIDEN); - target = expand_simple_unop (mode, code, target, - NULL_RTX, true); - } - else - target = expand_simple_binop (mode, code, target, val, - NULL_RTX, true, - OPTAB_LIB_WIDEN); - } - - return target; - } - } - - /* Failing that, generate a compare-and-swap loop in which we perform the - operation with normal arithmetic instructions. */ - if (direct_optab_handler (sync_compare_and_swap_optab, mode) - != CODE_FOR_nothing) - { - rtx t0 = gen_reg_rtx (mode), t1; - - if (!target || !register_operand (target, mode)) - target = gen_reg_rtx (mode); - - start_sequence (); - - if (!after) - emit_move_insn (target, t0); - t1 = t0; - if (code == NOT) - { - t1 = expand_simple_binop (mode, AND, t1, val, NULL_RTX, - true, OPTAB_LIB_WIDEN); - t1 = expand_simple_unop (mode, code, t1, NULL_RTX, true); - } - else - t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX, - true, OPTAB_LIB_WIDEN); - if (after) - emit_move_insn (target, t1); - - insn = get_insns (); - end_sequence (); - - if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn)) - return target; - } - + /* If the test_and_set can't be emitted, eliminate any barrier that might + have been emitted. */ + delete_insns_since (last_insn); return NULL_RTX; } -/* This function expands a test-and-set operation. Ideally we atomically - store VAL in MEM and return the previous value in MEM. Some targets - may not support this operation and only support VAL with the constant 1; - in this case while the return value will be 0/1, but the exact value - stored in MEM is target defined. TARGET is an option place to stick - the return value. */ - -rtx -expand_sync_lock_test_and_set (rtx mem, rtx val, rtx target) +/* This function tries to implement an atomic exchange operation using a + compare_and_swap loop. VAL is written to *MEM. The previous contents of + *MEM are returned, using TARGET if possible. No memory model is required + since a compare_and_swap loop is seq-cst. */ + +static rtx +maybe_emit_compare_and_swap_exchange_loop (rtx target, rtx mem, rtx val) { - enum machine_mode mode = GET_MODE (mem); - enum insn_code icode; - rtx insn; - - /* If the target supports the test-and-set directly, great. */ - icode = direct_optab_handler (sync_lock_test_and_set_optab, mode); - if (icode != CODE_FOR_nothing) - { - if (!target || !insn_data[icode].operand[0].predicate (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 (!insn_data[icode].operand[2].predicate (val, mode)) - val = force_reg (mode, val); - - insn = GEN_FCN (icode) (target, mem, val); - if (insn) - { - emit_insn (insn); - return target; - } - } - - /* Otherwise, use a compare-and-swap loop for the exchange. */ - if (direct_optab_handler (sync_compare_and_swap_optab, mode) - != CODE_FOR_nothing) + 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; } @@ -7330,4 +5972,1200 @@ return NULL_RTX; } -#include "gt-optabs.h" +/* This function tries to implement an atomic test-and-set operation + using the atomic_test_and_set instruction pattern. A boolean value + is returned from the operation, using TARGET if possible. */ + +static rtx +maybe_emit_atomic_test_and_set (rtx target, rtx mem, enum memmodel model) +{ + machine_mode pat_bool_mode; + struct expand_operand ops[3]; + + if (!targetm.have_atomic_test_and_set ()) + return NULL_RTX; + + /* While we always get QImode from __atomic_test_and_set, we get + other memory modes from __sync_lock_test_and_set. Note that we + use no endian adjustment here. This matches the 4.6 behavior + in the Sparc backend. */ + enum insn_code icode = targetm.code_for_atomic_test_and_set; + gcc_checking_assert (insn_data[icode].operand[1].mode == QImode); + if (GET_MODE (mem) != QImode) + mem = adjust_address_nv (mem, QImode, 0); + + pat_bool_mode = insn_data[icode].operand[0].mode; + create_output_operand (&ops[0], target, pat_bool_mode); + create_fixed_operand (&ops[1], mem); + create_integer_operand (&ops[2], model); + + if (maybe_expand_insn (icode, 3, ops)) + return ops[0].value; + return NULL_RTX; +} + +/* This function expands the legacy _sync_lock test_and_set operation which is + generally an atomic exchange. Some limited targets only allow the + constant 1 to be stored. This is an ACQUIRE operation. + + TARGET is an optional place to stick the return value. + MEM is where VAL is stored. */ + +rtx +expand_sync_lock_test_and_set (rtx target, rtx mem, rtx val) +{ + rtx ret; + + /* Try an atomic_exchange first. */ + 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_SYNC_ACQUIRE); + if (ret) + return ret; + + ret = maybe_emit_compare_and_swap_exchange_loop (target, mem, val); + 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_SYNC_ACQUIRE); + + return ret; +} + +/* This function expands the atomic test_and_set operation: + atomically store a boolean TRUE into MEM and return the previous value. + + MEMMODEL is the memory model variant to use. + TARGET is an optional place to stick the return value. */ + +rtx +expand_atomic_test_and_set (rtx target, rtx mem, enum memmodel model) +{ + machine_mode mode = GET_MODE (mem); + rtx ret, trueval, subtarget; + + ret = maybe_emit_atomic_test_and_set (target, mem, model); + if (ret) + return ret; + + /* Be binary compatible with non-default settings of trueval, and different + cpu revisions. E.g. one revision may have atomic-test-and-set, but + another only has atomic-exchange. */ + if (targetm.atomic_test_and_set_trueval == 1) + { + trueval = const1_rtx; + subtarget = target ? target : gen_reg_rtx (mode); + } + else + { + trueval = gen_int_mode (targetm.atomic_test_and_set_trueval, mode); + subtarget = gen_reg_rtx (mode); + } + + /* Try the atomic-exchange optab... */ + ret = maybe_emit_atomic_exchange (subtarget, mem, trueval, model); + + /* ... then an atomic-compare-and-swap loop ... */ + if (!ret) + ret = maybe_emit_compare_and_swap_exchange_loop (subtarget, mem, trueval); + + /* ... before trying the vaguely defined legacy lock_test_and_set. */ + if (!ret) + ret = maybe_emit_sync_lock_test_and_set (subtarget, mem, trueval, model); + + /* Recall that the legacy lock_test_and_set optab was allowed to do magic + things with the value 1. Thus we try again without trueval. */ + if (!ret && targetm.atomic_test_and_set_trueval != 1) + ret = maybe_emit_sync_lock_test_and_set (subtarget, mem, const1_rtx, model); + + /* Failing all else, assume a single threaded environment and simply + perform the operation. */ + if (!ret) + { + /* 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; + } + + /* Recall that have to return a boolean value; rectify if trueval + is not exactly one. */ + if (targetm.atomic_test_and_set_trueval != 1) + ret = emit_store_flag_force (target, NE, ret, const0_rtx, mode, 0, 1); + + return ret; +} + +/* This function expands the atomic exchange operation: + atomically store VAL in MEM and return the previous value in MEM. + + MEMMODEL is the memory model variant to use. + TARGET is an optional place to stick the return value. */ + +rtx +expand_atomic_exchange (rtx target, rtx mem, rtx val, enum memmodel model) +{ + machine_mode mode = GET_MODE (mem); + rtx ret; + + /* If loads are not atomic for the required size and we are not called to + provide a __sync builtin, do not do anything so that we stay consistent + with atomic loads of the same size. */ + if (!can_atomic_load_p (mode) && !is_mm_sync (model)) + return NULL_RTX; + + ret = maybe_emit_atomic_exchange (target, mem, val, model); + + /* Next try a compare-and-swap loop for the exchange. */ + if (!ret) + ret = maybe_emit_compare_and_swap_exchange_loop (target, mem, val); + + return ret; +} + +/* This function expands the atomic compare exchange operation: + + *PTARGET_BOOL is an optional place to store the boolean success/failure. + *PTARGET_OVAL is an optional place to store the old value from memory. + Both target parameters may be NULL or const0_rtx to indicate that we do + not care about that return value. Both target parameters are updated on + success to the actual location of the corresponding result. + + MEMMODEL is the memory model variant to use. + + The return value of the function is true for success. */ + +bool +expand_atomic_compare_and_swap (rtx *ptarget_bool, rtx *ptarget_oval, + rtx mem, rtx expected, rtx desired, + bool is_weak, enum memmodel succ_model, + enum memmodel fail_model) +{ + machine_mode mode = GET_MODE (mem); + struct expand_operand ops[8]; + enum insn_code icode; + rtx target_oval, target_bool = NULL_RTX; + rtx libfunc; + + /* If loads are not atomic for the required size and we are not called to + provide a __sync builtin, do not do anything so that we stay consistent + with atomic loads of the same size. */ + if (!can_atomic_load_p (mode) && !is_mm_sync (succ_model)) + return false; + + /* Load expected into a register for the compare and swap. */ + if (MEM_P (expected)) + expected = copy_to_reg (expected); + + /* Make sure we always have some place to put the return oldval. + Further, make sure that place is distinct from the input expected, + just in case we need that path down below. */ + if (ptarget_oval && *ptarget_oval == const0_rtx) + ptarget_oval = NULL; + + if (ptarget_oval == NULL + || (target_oval = *ptarget_oval) == NULL + || reg_overlap_mentioned_p (expected, target_oval)) + target_oval = gen_reg_rtx (mode); + + icode = direct_optab_handler (atomic_compare_and_swap_optab, mode); + if (icode != CODE_FOR_nothing) + { + machine_mode bool_mode = insn_data[icode].operand[0].mode; + + if (ptarget_bool && *ptarget_bool == const0_rtx) + ptarget_bool = NULL; + + /* Make sure we always have a place for the bool operand. */ + if (ptarget_bool == NULL + || (target_bool = *ptarget_bool) == NULL + || GET_MODE (target_bool) != bool_mode) + target_bool = gen_reg_rtx (bool_mode); + + /* Emit the compare_and_swap. */ + create_output_operand (&ops[0], target_bool, bool_mode); + create_output_operand (&ops[1], target_oval, mode); + create_fixed_operand (&ops[2], mem); + 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); + 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 + which is always seq-cst. */ + icode = optab_handler (sync_compare_and_swap_optab, mode); + if (icode != CODE_FOR_nothing) + { + rtx cc_reg; + + create_output_operand (&ops[0], target_oval, mode); + create_fixed_operand (&ops[1], mem); + create_input_operand (&ops[2], expected, mode); + create_input_operand (&ops[3], desired, mode); + if (!maybe_expand_insn (icode, 4, ops)) + return false; + + target_oval = ops[0].value; + + /* If the caller isn't interested in the boolean return value, + skip the computation of it. */ + if (ptarget_bool == NULL) + goto success; + + /* Otherwise, work out if the compare-and-swap succeeded. */ + cc_reg = NULL_RTX; + if (have_insn_for (COMPARE, CCmode)) + note_stores (PATTERN (get_last_insn ()), find_cc_set, &cc_reg); + if (cc_reg) + { + target_bool = emit_store_flag_force (target_bool, EQ, cc_reg, + const0_rtx, VOIDmode, 0, 1); + goto success; + } + goto success_bool_from_val; + } + + /* Also check for library support for __sync_val_compare_and_swap. */ + libfunc = optab_libfunc (sync_compare_and_swap_optab, mode); + if (libfunc != NULL) + { + rtx addr = convert_memory_address (ptr_mode, XEXP (mem, 0)); + rtx target = emit_library_call_value (libfunc, NULL_RTX, LCT_NORMAL, + mode, addr, ptr_mode, + expected, mode, desired, mode); + emit_move_insn (target_oval, target); + + /* Compute the boolean return value only if requested. */ + if (ptarget_bool) + goto success_bool_from_val; + else + goto success; + } + + /* Failure. */ + return false; + + success_bool_from_val: + target_bool = emit_store_flag_force (target_bool, EQ, target_oval, + expected, VOIDmode, 1, 1); + success: + /* Make sure that the oval output winds up where the caller asked. */ + if (ptarget_oval) + *ptarget_oval = target_oval; + if (ptarget_bool) + *ptarget_bool = target_bool; + return true; +} + +/* Generate asm volatile("" : : : "memory") as the memory blockage. */ + +static void +expand_asm_memory_blockage (void) +{ + rtx asm_op, clob; + + asm_op = gen_rtx_ASM_OPERANDS (VOIDmode, "", "", 0, + rtvec_alloc (0), rtvec_alloc (0), + rtvec_alloc (0), UNKNOWN_LOCATION); + MEM_VOLATILE_P (asm_op) = 1; + + clob = gen_rtx_SCRATCH (VOIDmode); + clob = gen_rtx_MEM (BLKmode, clob); + clob = gen_rtx_CLOBBER (VOIDmode, clob); + + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, asm_op, clob))); +} + +/* Do not propagate memory accesses across this point. */ + +static void +expand_memory_blockage (void) +{ + if (targetm.have_memory_blockage ()) + emit_insn (targetm.gen_memory_blockage ()); + else + expand_asm_memory_blockage (); +} + +/* This routine will either emit the mem_thread_fence pattern or issue a + sync_synchronize to generate a fence for memory model MEMMODEL. */ + +void +expand_mem_thread_fence (enum memmodel model) +{ + if (is_mm_relaxed (model)) + return; + if (targetm.have_mem_thread_fence ()) + { + emit_insn (targetm.gen_mem_thread_fence (GEN_INT (model))); + expand_memory_blockage (); + } + else if (targetm.have_memory_barrier ()) + emit_insn (targetm.gen_memory_barrier ()); + else if (synchronize_libfunc != NULL_RTX) + emit_library_call (synchronize_libfunc, LCT_NORMAL, VOIDmode); + else + expand_memory_blockage (); +} + +/* Emit a signal fence with given memory model. */ + +void +expand_mem_signal_fence (enum memmodel model) +{ + /* No machine barrier is required to implement a signal fence, but + a compiler memory barrier must be issued, except for relaxed MM. */ + if (!is_mm_relaxed (model)) + expand_memory_blockage (); +} + +/* This function expands the atomic load operation: + return the atomically loaded value in MEM. + + MEMMODEL is the memory model variant to use. + TARGET is an option place to stick the return value. */ + +rtx +expand_atomic_load (rtx target, rtx mem, enum memmodel model) +{ + machine_mode mode = GET_MODE (mem); + enum insn_code icode; + + /* If the target supports the load directly, great. */ + icode = direct_optab_handler (atomic_load_optab, mode); + if (icode != CODE_FOR_nothing) + { + struct expand_operand ops[3]; + rtx_insn *last = get_last_insn (); + if (is_mm_seq_cst (model)) + expand_memory_blockage (); + + create_output_operand (&ops[0], target, mode); + create_fixed_operand (&ops[1], mem); + create_integer_operand (&ops[2], model); + if (maybe_expand_insn (icode, 3, ops)) + { + if (!is_mm_relaxed (model)) + expand_memory_blockage (); + return ops[0].value; + } + delete_insns_since (last); + } + + /* If the size of the object is greater than word size on this target, + then we assume that a load will not be atomic. We could try to + emulate a load with a compare-and-swap operation, but the store that + doing this could result in would be incorrect if this is a volatile + atomic load or targetting read-only-mapped memory. */ + if (GET_MODE_PRECISION (mode) > BITS_PER_WORD) + /* If there is no atomic load, leave the library call. */ + return NULL_RTX; + + /* Otherwise assume loads are atomic, and emit the proper barriers. */ + if (!target || target == const0_rtx) + target = gen_reg_rtx (mode); + + /* 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); + + /* Emit the appropriate barrier after the load. */ + expand_mem_thread_fence (model); + + return target; +} + +/* This function expands the atomic store operation: + Atomically store VAL in MEM. + MEMMODEL is the memory model variant to use. + USE_RELEASE is true if __sync_lock_release can be used as a fall back. + function returns const0_rtx if a pattern was emitted. */ + +rtx +expand_atomic_store (rtx mem, rtx val, enum memmodel model, bool use_release) +{ + machine_mode mode = GET_MODE (mem); + enum insn_code icode; + struct expand_operand ops[3]; + + /* If the target supports the store directly, great. */ + icode = direct_optab_handler (atomic_store_optab, mode); + if (icode != CODE_FOR_nothing) + { + rtx_insn *last = get_last_insn (); + if (!is_mm_relaxed (model)) + expand_memory_blockage (); + create_fixed_operand (&ops[0], mem); + create_input_operand (&ops[1], val, mode); + create_integer_operand (&ops[2], model); + if (maybe_expand_insn (icode, 3, ops)) + { + if (is_mm_seq_cst (model)) + expand_memory_blockage (); + return const0_rtx; + } + delete_insns_since (last); + } + + /* If using __sync_lock_release is a viable alternative, try it. + Note that this will not be set to true if we are expanding a generic + __atomic_store_n. */ + if (use_release) + { + icode = direct_optab_handler (sync_lock_release_optab, mode); + if (icode != CODE_FOR_nothing) + { + create_fixed_operand (&ops[0], mem); + create_input_operand (&ops[1], const0_rtx, mode); + if (maybe_expand_insn (icode, 2, ops)) + { + /* lock_release is only a release barrier. */ + 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. */ + if (GET_MODE_PRECISION (mode) > BITS_PER_WORD) + { + /* If loads are atomic or we are called to provide a __sync builtin, + we can try a atomic_exchange and throw away the result. Otherwise, + don't do anything so that we do not create an inconsistency between + loads and stores. */ + if (can_atomic_load_p (mode) || is_mm_sync (model)) + { + rtx target = maybe_emit_atomic_exchange (NULL_RTX, mem, val, model); + if (!target) + target = maybe_emit_compare_and_swap_exchange_loop (NULL_RTX, mem, + val); + if (target) + return const0_rtx; + } + return NULL_RTX; + } + + /* 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 store. */ + if (is_mm_seq_cst (model)) + expand_mem_thread_fence (model); + + return const0_rtx; +} + + +/* Structure containing the pointers and values required to process the + various forms of the atomic_fetch_op and atomic_op_fetch builtins. */ + +struct atomic_op_functions +{ + direct_optab mem_fetch_before; + direct_optab mem_fetch_after; + direct_optab mem_no_result; + optab fetch_before; + optab fetch_after; + direct_optab no_result; + enum rtx_code reverse_code; +}; + + +/* Fill in structure pointed to by OP with the various optab entries for an + operation of type CODE. */ + +static void +get_atomic_op_for_code (struct atomic_op_functions *op, enum rtx_code code) +{ + gcc_assert (op!= NULL); + + /* If SWITCHABLE_TARGET is defined, then subtargets can be switched + in the source code during compilation, and the optab entries are not + computable until runtime. Fill in the values at runtime. */ + switch (code) + { + case PLUS: + op->mem_fetch_before = atomic_fetch_add_optab; + op->mem_fetch_after = atomic_add_fetch_optab; + op->mem_no_result = atomic_add_optab; + op->fetch_before = sync_old_add_optab; + op->fetch_after = sync_new_add_optab; + op->no_result = sync_add_optab; + op->reverse_code = MINUS; + break; + case MINUS: + op->mem_fetch_before = atomic_fetch_sub_optab; + op->mem_fetch_after = atomic_sub_fetch_optab; + op->mem_no_result = atomic_sub_optab; + op->fetch_before = sync_old_sub_optab; + op->fetch_after = sync_new_sub_optab; + op->no_result = sync_sub_optab; + op->reverse_code = PLUS; + break; + case XOR: + op->mem_fetch_before = atomic_fetch_xor_optab; + op->mem_fetch_after = atomic_xor_fetch_optab; + op->mem_no_result = atomic_xor_optab; + op->fetch_before = sync_old_xor_optab; + op->fetch_after = sync_new_xor_optab; + op->no_result = sync_xor_optab; + op->reverse_code = XOR; + break; + case AND: + op->mem_fetch_before = atomic_fetch_and_optab; + op->mem_fetch_after = atomic_and_fetch_optab; + op->mem_no_result = atomic_and_optab; + op->fetch_before = sync_old_and_optab; + op->fetch_after = sync_new_and_optab; + op->no_result = sync_and_optab; + op->reverse_code = UNKNOWN; + break; + case IOR: + op->mem_fetch_before = atomic_fetch_or_optab; + op->mem_fetch_after = atomic_or_fetch_optab; + op->mem_no_result = atomic_or_optab; + op->fetch_before = sync_old_ior_optab; + op->fetch_after = sync_new_ior_optab; + op->no_result = sync_ior_optab; + op->reverse_code = UNKNOWN; + break; + case NOT: + op->mem_fetch_before = atomic_fetch_nand_optab; + op->mem_fetch_after = atomic_nand_fetch_optab; + op->mem_no_result = atomic_nand_optab; + op->fetch_before = sync_old_nand_optab; + op->fetch_after = sync_new_nand_optab; + op->no_result = sync_nand_optab; + op->reverse_code = UNKNOWN; + break; + default: + gcc_unreachable (); + } +} + +/* See if there is a more optimal way to implement the operation "*MEM CODE VAL" + using memory order MODEL. If AFTER is true the operation needs to return + the value of *MEM after the operation, otherwise the previous value. + TARGET is an optional place to place the result. The result is unused if + it is const0_rtx. + Return the result if there is a better sequence, otherwise NULL_RTX. */ + +static rtx +maybe_optimize_fetch_op (rtx target, rtx mem, rtx val, enum rtx_code code, + enum memmodel model, bool after) +{ + /* If the value is prefetched, or not used, it may be possible to replace + the sequence with a native exchange operation. */ + if (!after || target == const0_rtx) + { + /* fetch_and (&x, 0, m) can be replaced with exchange (&x, 0, m). */ + if (code == AND && val == const0_rtx) + { + if (target == const0_rtx) + target = gen_reg_rtx (GET_MODE (mem)); + return maybe_emit_atomic_exchange (target, mem, val, model); + } + + /* fetch_or (&x, -1, m) can be replaced with exchange (&x, -1, m). */ + if (code == IOR && val == constm1_rtx) + { + if (target == const0_rtx) + target = gen_reg_rtx (GET_MODE (mem)); + return maybe_emit_atomic_exchange (target, mem, val, model); + } + } + + return NULL_RTX; +} + +/* Try to emit an instruction for a specific operation varaition. + OPTAB contains the OP functions. + TARGET is an optional place to return the result. const0_rtx means unused. + MEM is the memory location to operate on. + VAL is the value to use in the operation. + USE_MEMMODEL is TRUE if the variation with a memory model should be tried. + MODEL is the memory model, if used. + AFTER is true if the returned result is the value after the operation. */ + +static rtx +maybe_emit_op (const struct atomic_op_functions *optab, rtx target, rtx mem, + rtx val, bool use_memmodel, enum memmodel model, bool after) +{ + machine_mode mode = GET_MODE (mem); + struct expand_operand ops[4]; + enum insn_code icode; + int op_counter = 0; + int num_ops; + + /* Check to see if there is a result returned. */ + if (target == const0_rtx) + { + if (use_memmodel) + { + icode = direct_optab_handler (optab->mem_no_result, mode); + create_integer_operand (&ops[2], model); + num_ops = 3; + } + else + { + icode = direct_optab_handler (optab->no_result, mode); + num_ops = 2; + } + } + /* Otherwise, we need to generate a result. */ + else + { + if (use_memmodel) + { + icode = direct_optab_handler (after ? optab->mem_fetch_after + : optab->mem_fetch_before, mode); + create_integer_operand (&ops[3], model); + num_ops = 4; + } + else + { + icode = optab_handler (after ? optab->fetch_after + : optab->fetch_before, mode); + num_ops = 3; + } + create_output_operand (&ops[op_counter++], target, mode); + } + if (icode == CODE_FOR_nothing) + return NULL_RTX; + + create_fixed_operand (&ops[op_counter++], mem); + /* VAL may have been promoted to a wider mode. Shrink it if so. */ + create_convert_operand_to (&ops[op_counter++], val, mode, true); + + if (maybe_expand_insn (icode, num_ops, ops)) + return (target == const0_rtx ? const0_rtx : ops[0].value); + + 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). + + 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) +{ + machine_mode mode = GET_MODE (mem); + struct atomic_op_functions optab; + rtx result; + bool unused_result = (target == const0_rtx); + + get_atomic_op_for_code (&optab, code); + + /* Check to see if there are any better instructions. */ + result = maybe_optimize_fetch_op (target, mem, val, code, model, after); + if (result) + return result; + + /* Check for the case where the result isn't used and try those patterns. */ + if (unused_result) + { + /* Try the memory model variant first. */ + result = maybe_emit_op (&optab, target, mem, val, true, model, true); + if (result) + return result; + + /* Next try the old style withuot a memory model. */ + result = maybe_emit_op (&optab, target, mem, val, false, model, true); + if (result) + return result; + + /* There is no no-result pattern, so try patterns with a result. */ + target = NULL_RTX; + } + + /* Try the __atomic version. */ + result = maybe_emit_op (&optab, target, mem, val, true, model, after); + if (result) + return result; + + /* Try the older __sync version. */ + result = maybe_emit_op (&optab, target, mem, val, false, model, after); + if (result) + return result; + + /* If the fetch value can be calculated from the other variation of fetch, + try that operation. */ + if (after || unused_result || optab.reverse_code != UNKNOWN) + { + /* Try the __atomic version, then the older __sync version. */ + result = maybe_emit_op (&optab, target, mem, val, true, model, !after); + if (!result) + result = maybe_emit_op (&optab, target, mem, val, false, model, !after); + + if (result) + { + /* If the result isn't used, no need to do compensation code. */ + if (unused_result) + return result; + + /* Issue compensation code. Fetch_after == fetch_before OP val. + Fetch_before == after REVERSE_OP val. */ + if (!after) + code = optab.reverse_code; + if (code == NOT) + { + result = expand_simple_binop (mode, AND, result, val, NULL_RTX, + true, OPTAB_LIB_WIDEN); + result = expand_simple_unop (mode, NOT, result, target, true); + } + else + result = expand_simple_binop (mode, code, result, val, target, + true, OPTAB_LIB_WIDEN); + return result; + } + } + + /* 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); + + /* If loads are not atomic for the required size and we are not called to + provide a __sync builtin, do not do anything so that we stay consistent + with atomic loads of the same size. */ + if (!can_atomic_load_p (mode) && !is_mm_sync (model)) + return NULL_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 + && (after || unused_result || optab.reverse_code != UNKNOWN)) + { + fixup = true; + if (!after) + code = optab.reverse_code; + libfunc = optab_libfunc (after ? optab.fetch_before + : optab.fetch_after, mode); + } + if (libfunc != NULL) + { + rtx addr = convert_memory_address (ptr_mode, XEXP (mem, 0)); + result = emit_library_call_value (libfunc, NULL, LCT_NORMAL, mode, + addr, ptr_mode, val, mode); + + if (!unused_result && fixup) + result = expand_simple_binop (mode, code, result, val, target, + 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 *insn; + rtx t0 = gen_reg_rtx (mode), t1; + + start_sequence (); + + /* If the result is used, get a register for it. */ + if (!unused_result) + { + if (!target || !register_operand (target, mode)) + target = gen_reg_rtx (mode); + /* If fetch_before, copy the value now. */ + if (!after) + emit_move_insn (target, t0); + } + else + target = const0_rtx; + + t1 = t0; + if (code == NOT) + { + t1 = expand_simple_binop (mode, AND, t1, val, NULL_RTX, + true, OPTAB_LIB_WIDEN); + t1 = expand_simple_unop (mode, code, t1, NULL_RTX, true); + } + else + t1 = expand_simple_binop (mode, code, t1, val, NULL_RTX, true, + OPTAB_LIB_WIDEN); + + /* For after, copy the value now. */ + if (!unused_result && after) + emit_move_insn (target, t1); + insn = get_insns (); + end_sequence (); + + if (t1 != NULL && expand_compare_and_swap_loop (mem, t0, t1, insn)) + return target; + } + + return NULL_RTX; +} + +/* Return true if OPERAND is suitable for operand number OPNO of + instruction ICODE. */ + +bool +insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand) +{ + return (!insn_data[(int) icode].operand[opno].predicate + || (insn_data[(int) icode].operand[opno].predicate + (operand, insn_data[(int) icode].operand[opno].mode))); +} + +/* TARGET is a target of a multiword operation that we are going to + implement as a series of word-mode operations. Return true if + TARGET is suitable for this purpose. */ + +bool +valid_multiword_target_p (rtx target) +{ + machine_mode mode; + int i; + + mode = GET_MODE (target); + for (i = 0; i < GET_MODE_SIZE (mode); i += UNITS_PER_WORD) + if (!validate_subreg (word_mode, mode, target, i)) + return false; + return true; +} + +/* Like maybe_legitimize_operand, but do not change the code of the + current rtx value. */ + +static bool +maybe_legitimize_operand_same_code (enum insn_code icode, unsigned int opno, + struct expand_operand *op) +{ + /* See if the operand matches in its current form. */ + if (insn_operand_matches (icode, opno, op->value)) + return true; + + /* If the operand is a memory whose address has no side effects, + try forcing the address into a non-virtual pseudo register. + The check for side effects is important because copy_to_mode_reg + cannot handle things like auto-modified addresses. */ + if (insn_data[(int) icode].operand[opno].allows_mem && MEM_P (op->value)) + { + rtx addr, mem; + + mem = op->value; + addr = XEXP (mem, 0); + if (!(REG_P (addr) && REGNO (addr) > LAST_VIRTUAL_REGISTER) + && !side_effects_p (addr)) + { + rtx_insn *last; + machine_mode mode; + + last = get_last_insn (); + mode = get_address_mode (mem); + mem = replace_equiv_address (mem, copy_to_mode_reg (mode, addr)); + if (insn_operand_matches (icode, opno, mem)) + { + op->value = mem; + return true; + } + delete_insns_since (last); + } + } + + return false; +} + +/* Try to make OP match operand OPNO of instruction ICODE. Return true + on success, storing the new operand value back in OP. */ + +static bool +maybe_legitimize_operand (enum insn_code icode, unsigned int opno, + struct expand_operand *op) +{ + machine_mode mode, imode; + bool old_volatile_ok, result; + + mode = op->mode; + switch (op->type) + { + case EXPAND_FIXED: + old_volatile_ok = volatile_ok; + volatile_ok = true; + result = maybe_legitimize_operand_same_code (icode, opno, op); + volatile_ok = old_volatile_ok; + return result; + + case EXPAND_OUTPUT: + gcc_assert (mode != VOIDmode); + if (op->value + && op->value != const0_rtx + && GET_MODE (op->value) == mode + && maybe_legitimize_operand_same_code (icode, opno, op)) + return true; + + op->value = gen_reg_rtx (mode); + op->target = 0; + break; + + case EXPAND_INPUT: + input: + gcc_assert (mode != VOIDmode); + gcc_assert (GET_MODE (op->value) == VOIDmode + || GET_MODE (op->value) == mode); + if (maybe_legitimize_operand_same_code (icode, opno, op)) + return true; + + op->value = copy_to_mode_reg (mode, op->value); + break; + + case EXPAND_CONVERT_TO: + gcc_assert (mode != VOIDmode); + op->value = convert_to_mode (mode, op->value, op->unsigned_p); + goto input; + + case EXPAND_CONVERT_FROM: + if (GET_MODE (op->value) != VOIDmode) + mode = GET_MODE (op->value); + else + /* The caller must tell us what mode this value has. */ + gcc_assert (mode != VOIDmode); + + imode = insn_data[(int) icode].operand[opno].mode; + if (imode != VOIDmode && imode != mode) + { + op->value = convert_modes (imode, mode, op->value, op->unsigned_p); + mode = imode; + } + goto input; + + case EXPAND_ADDRESS: + op->value = convert_memory_address (as_a <scalar_int_mode> (mode), + op->value); + goto input; + + case EXPAND_INTEGER: + mode = insn_data[(int) icode].operand[opno].mode; + if (mode != VOIDmode && const_int_operand (op->value, mode)) + goto input; + break; + } + return insn_operand_matches (icode, opno, op->value); +} + +/* Make OP describe an input operand that should have the same value + as VALUE, after any mode conversion that the target might request. + TYPE is the type of VALUE. */ + +void +create_convert_operand_from_type (struct expand_operand *op, + rtx value, tree type) +{ + create_convert_operand_from (op, value, TYPE_MODE (type), + TYPE_UNSIGNED (type)); +} + +/* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS) + of instruction ICODE. Return true on success, leaving the new operand + values in the OPS themselves. Emit no code on failure. */ + +bool +maybe_legitimize_operands (enum insn_code icode, unsigned int opno, + unsigned int nops, struct expand_operand *ops) +{ + rtx_insn *last; + unsigned int i; + + last = get_last_insn (); + for (i = 0; i < nops; i++) + if (!maybe_legitimize_operand (icode, opno + i, &ops[i])) + { + delete_insns_since (last); + return false; + } + return true; +} + +/* Try to generate instruction ICODE, using operands [OPS, OPS + NOPS) + as its operands. Return the instruction pattern on success, + and emit any necessary set-up code. Return null and emit no + code on failure. */ + +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; + + switch (nops) + { + case 1: + return GEN_FCN (icode) (ops[0].value); + case 2: + return GEN_FCN (icode) (ops[0].value, ops[1].value); + case 3: + return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value); + case 4: + return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value, + ops[3].value); + case 5: + return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value, + ops[3].value, ops[4].value); + case 6: + return GEN_FCN (icode) (ops[0].value, ops[1].value, ops[2].value, + ops[3].value, ops[4].value, ops[5].value); + case 7: + 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); + case 8: + 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 (); +} + +/* Try to emit instruction ICODE, using operands [OPS, OPS + NOPS) + as its operands. Return true on success and emit no code on failure. */ + +bool +maybe_expand_insn (enum insn_code icode, unsigned int nops, + struct expand_operand *ops) +{ + rtx_insn *pat = maybe_gen_insn (icode, nops, ops); + if (pat) + { + emit_insn (pat); + return true; + } + return false; +} + +/* Like maybe_expand_insn, but for jumps. */ + +bool +maybe_expand_jump_insn (enum insn_code icode, unsigned int nops, + struct expand_operand *ops) +{ + rtx_insn *pat = maybe_gen_insn (icode, nops, ops); + if (pat) + { + emit_jump_insn (pat); + return true; + } + return false; +} + +/* Emit instruction ICODE, using operands [OPS, OPS + NOPS) + as its operands. */ + +void +expand_insn (enum insn_code icode, unsigned int nops, + struct expand_operand *ops) +{ + if (!maybe_expand_insn (icode, nops, ops)) + gcc_unreachable (); +} + +/* Like expand_insn, but for jumps. */ + +void +expand_jump_insn (enum insn_code icode, unsigned int nops, + struct expand_operand *ops) +{ + if (!maybe_expand_jump_insn (icode, nops, ops)) + gcc_unreachable (); +}