diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-20 01:06:22 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-20 01:06:22 +0000 |
commit | c482518380683ee38d14024c1e362a0d681cf967 (patch) | |
tree | e69b4f6d3fee3aced20a41f3fdf543fc1c77fb5d /gnu/usr.bin/gcc/optabs.c | |
parent | 76a62188d0db49c65b696d474c855a799fd96dce (diff) |
FSF GCC version 2.7.2
Diffstat (limited to 'gnu/usr.bin/gcc/optabs.c')
-rw-r--r-- | gnu/usr.bin/gcc/optabs.c | 4320 |
1 files changed, 4320 insertions, 0 deletions
diff --git a/gnu/usr.bin/gcc/optabs.c b/gnu/usr.bin/gcc/optabs.c new file mode 100644 index 00000000000..2732ebed107 --- /dev/null +++ b/gnu/usr.bin/gcc/optabs.c @@ -0,0 +1,4320 @@ +/* Expand the basic unary and binary arithmetic operations, for GNU compiler. + Copyright (C) 1987, 88, 92, 93, 94, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "insn-flags.h" +#include "insn-codes.h" +#include "expr.h" +#include "insn-config.h" +#include "recog.h" +#include "reload.h" +#include <ctype.h> + +/* Each optab contains info on how this target machine + can perform a particular operation + for all sizes and kinds of operands. + + The operation to be performed is often specified + by passing one of these optabs as an argument. + + See expr.h for documentation of these optabs. */ + +optab add_optab; +optab sub_optab; +optab smul_optab; +optab smul_highpart_optab; +optab umul_highpart_optab; +optab smul_widen_optab; +optab umul_widen_optab; +optab sdiv_optab; +optab sdivmod_optab; +optab udiv_optab; +optab udivmod_optab; +optab smod_optab; +optab umod_optab; +optab flodiv_optab; +optab ftrunc_optab; +optab and_optab; +optab ior_optab; +optab xor_optab; +optab ashl_optab; +optab lshr_optab; +optab ashr_optab; +optab rotl_optab; +optab rotr_optab; +optab smin_optab; +optab smax_optab; +optab umin_optab; +optab umax_optab; + +optab mov_optab; +optab movstrict_optab; + +optab neg_optab; +optab abs_optab; +optab one_cmpl_optab; +optab ffs_optab; +optab sqrt_optab; +optab sin_optab; +optab cos_optab; + +optab cmp_optab; +optab ucmp_optab; /* Used only for libcalls for unsigned comparisons. */ +optab tst_optab; + +optab strlen_optab; + +/* Tables of patterns for extending one integer mode to another. */ +enum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2]; + +/* Tables of patterns for converting between fixed and floating point. */ +enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2]; +enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2]; +enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2]; + +/* Contains the optab used for each rtx code. */ +optab code_to_optab[NUM_RTX_CODE + 1]; + +/* SYMBOL_REF rtx's for the library functions that are called + implicitly and not via optabs. */ + +rtx extendsfdf2_libfunc; +rtx extendsfxf2_libfunc; +rtx extendsftf2_libfunc; +rtx extenddfxf2_libfunc; +rtx extenddftf2_libfunc; + +rtx truncdfsf2_libfunc; +rtx truncxfsf2_libfunc; +rtx trunctfsf2_libfunc; +rtx truncxfdf2_libfunc; +rtx trunctfdf2_libfunc; + +rtx memcpy_libfunc; +rtx bcopy_libfunc; +rtx memcmp_libfunc; +rtx bcmp_libfunc; +rtx memset_libfunc; +rtx bzero_libfunc; + +rtx eqhf2_libfunc; +rtx nehf2_libfunc; +rtx gthf2_libfunc; +rtx gehf2_libfunc; +rtx lthf2_libfunc; +rtx lehf2_libfunc; + +rtx eqsf2_libfunc; +rtx nesf2_libfunc; +rtx gtsf2_libfunc; +rtx gesf2_libfunc; +rtx ltsf2_libfunc; +rtx lesf2_libfunc; + +rtx eqdf2_libfunc; +rtx nedf2_libfunc; +rtx gtdf2_libfunc; +rtx gedf2_libfunc; +rtx ltdf2_libfunc; +rtx ledf2_libfunc; + +rtx eqxf2_libfunc; +rtx nexf2_libfunc; +rtx gtxf2_libfunc; +rtx gexf2_libfunc; +rtx ltxf2_libfunc; +rtx lexf2_libfunc; + +rtx eqtf2_libfunc; +rtx netf2_libfunc; +rtx gttf2_libfunc; +rtx getf2_libfunc; +rtx lttf2_libfunc; +rtx letf2_libfunc; + +rtx floatsisf_libfunc; +rtx floatdisf_libfunc; +rtx floattisf_libfunc; + +rtx floatsidf_libfunc; +rtx floatdidf_libfunc; +rtx floattidf_libfunc; + +rtx floatsixf_libfunc; +rtx floatdixf_libfunc; +rtx floattixf_libfunc; + +rtx floatsitf_libfunc; +rtx floatditf_libfunc; +rtx floattitf_libfunc; + +rtx fixsfsi_libfunc; +rtx fixsfdi_libfunc; +rtx fixsfti_libfunc; + +rtx fixdfsi_libfunc; +rtx fixdfdi_libfunc; +rtx fixdfti_libfunc; + +rtx fixxfsi_libfunc; +rtx fixxfdi_libfunc; +rtx fixxfti_libfunc; + +rtx fixtfsi_libfunc; +rtx fixtfdi_libfunc; +rtx fixtfti_libfunc; + +rtx fixunssfsi_libfunc; +rtx fixunssfdi_libfunc; +rtx fixunssfti_libfunc; + +rtx fixunsdfsi_libfunc; +rtx fixunsdfdi_libfunc; +rtx fixunsdfti_libfunc; + +rtx fixunsxfsi_libfunc; +rtx fixunsxfdi_libfunc; +rtx fixunsxfti_libfunc; + +rtx fixunstfsi_libfunc; +rtx fixunstfdi_libfunc; +rtx fixunstfti_libfunc; + +/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) + gives the gen_function to make a branch to test that condition. */ + +rtxfun bcc_gen_fctn[NUM_RTX_CODE]; + +/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) + gives the insn code to make a store-condition insn + to test that condition. */ + +enum insn_code setcc_gen_code[NUM_RTX_CODE]; + +#ifdef HAVE_conditional_move +/* Indexed by the machine mode, gives the insn code to make a conditional + move insn. This is not indexed by the rtx-code like bcc_gen_fctn and + setcc_gen_code to cut down on the number of named patterns. Consider a day + when a lot more rtx codes are conditional (eg: for the ARM). */ + +enum insn_code movcc_gen_code[NUM_MACHINE_MODES]; +#endif + +static int add_equal_note PROTO((rtx, rtx, enum rtx_code, rtx, rtx)); +static rtx widen_operand PROTO((rtx, enum machine_mode, + enum machine_mode, int, int)); +static enum insn_code can_fix_p PROTO((enum machine_mode, enum machine_mode, + int, int *)); +static enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode, + int)); +static rtx ftruncify PROTO((rtx)); +static optab init_optab PROTO((enum rtx_code)); +static void init_libfuncs PROTO((optab, int, int, char *, int)); +static void init_integral_libfuncs PROTO((optab, char *, int)); +static void init_floating_libfuncs PROTO((optab, char *, int)); +static void init_complex_libfuncs PROTO((optab, char *, int)); + +/* Add a REG_EQUAL note to the last insn in SEQ. TARGET is being set to + the result of operation CODE applied to OP0 (and OP1 if it is a binary + operation). + + 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. */ + +static int +add_equal_note (seq, target, code, op0, op1) + rtx seq; + rtx target; + enum rtx_code code; + rtx op0, op1; +{ + rtx set; + int i; + rtx note; + + if ((GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2' + && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<') + || GET_CODE (seq) != SEQUENCE + || (set = single_set (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))) == 0 + || GET_CODE (target) == ZERO_EXTRACT + || (! rtx_equal_p (SET_DEST (set), target) + /* For a STRICT_LOW_PART, the REG_NOTE applies to what is inside the + SUBREG. */ + && (GET_CODE (SET_DEST (set)) != STRICT_LOW_PART + || ! rtx_equal_p (SUBREG_REG (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))) + for (i = XVECLEN (seq, 0) - 2; i >= 0; i--) + if (reg_set_p (target, XVECEXP (seq, 0, i))) + return 0; + + if (GET_RTX_CLASS (code) == '1') + note = gen_rtx (code, GET_MODE (target), copy_rtx (op0)); + else + note = gen_rtx (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1)); + + REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1)) + = gen_rtx (EXPR_LIST, REG_EQUAL, note, + REG_NOTES (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))); + + return 1; +} + +/* 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 + higher-order bits of the result rtx undefined, for example, in the case + of logical operations, but not right shifts. */ + +static rtx +widen_operand (op, mode, oldmode, unsignedp, no_extend) + rtx op; + enum machine_mode mode, oldmode; + int unsignedp; + int no_extend; +{ + rtx result; + + /* If we must extend do so. If OP is either a constant or a SUBREG + for a promoted object, also extend since it will be more efficient to + do so. */ + if (! no_extend + || GET_MODE (op) == VOIDmode + || (GET_CODE (op) == SUBREG && SUBREG_PROMOTED_VAR_P (op))) + return convert_modes (mode, oldmode, op, unsignedp); + + /* If MODE is no wider than a single word, we return a paradoxical + SUBREG. */ + if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) + return gen_rtx (SUBREG, mode, force_reg (GET_MODE (op), op), 0); + + /* Otherwise, get an object of MODE, clobber it, and set the low-order + part to OP. */ + + result = gen_reg_rtx (mode); + emit_insn (gen_rtx (CLOBBER, VOIDmode, result)); + emit_move_insn (gen_lowpart (GET_MODE (op), result), op); + return result; +} + +/* Generate code to perform an operation specified by BINOPTAB + on operands OP0 and OP1, with result having machine-mode MODE. + + UNSIGNEDP is for the case where we have to widen the operands + to perform the operation. It says to use zero-extension. + + If TARGET is nonzero, the value + is generated there, if it is convenient to do so. + In all cases an rtx is returned for the locus of the value; + this may or may not be TARGET. */ + +rtx +expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) + enum machine_mode mode; + optab binoptab; + rtx op0, 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 class; + enum machine_mode wider_mode; + register rtx temp; + int commutative_op = 0; + int shift_op = (binoptab->code == ASHIFT + || binoptab->code == ASHIFTRT + || binoptab->code == LSHIFTRT + || binoptab->code == ROTATE + || binoptab->code == ROTATERT); + rtx entry_last = get_last_insn (); + rtx last; + + class = GET_MODE_CLASS (mode); + + op0 = protect_from_queue (op0, 0); + op1 = protect_from_queue (op1, 0); + if (target) + target = protect_from_queue (target, 1); + + if (flag_force_mem) + { + op0 = force_not_mem (op0); + op1 = force_not_mem (op1); + } + + /* If subtracting an integer constant, convert this into an addition of + the negated constant. */ + + if (binoptab == sub_optab && GET_CODE (op1) == CONST_INT) + { + op1 = negate_rtx (mode, op1); + binoptab = add_optab; + } + + /* If we are inside an appropriately-short loop and one operand is an + expensive constant, force it into a register. */ + if (CONSTANT_P (op0) && preserve_subexpressions_p () + && rtx_cost (op0, binoptab->code) > 2) + op0 = force_reg (mode, op0); + + if (CONSTANT_P (op1) && preserve_subexpressions_p () + && ! shift_op && rtx_cost (op1, binoptab->code) > 2) + op1 = force_reg (mode, op1); + + /* Record where to delete back to if we backtrack. */ + last = get_last_insn (); + + /* If operation is commutative, + try to make the first operand a register. + Even better, try to make it the same as the target. + Also try to make the last operand a constant. */ + if (GET_RTX_CLASS (binoptab->code) == 'c' + || binoptab == smul_widen_optab + || binoptab == umul_widen_optab + || binoptab == smul_highpart_optab + || binoptab == umul_highpart_optab) + { + commutative_op = 1; + + if (((target == 0 || GET_CODE (target) == REG) + ? ((GET_CODE (op1) == REG + && GET_CODE (op0) != REG) + || target == op1) + : rtx_equal_p (op1, target)) + || GET_CODE (op0) == CONST_INT) + { + temp = op1; + op1 = op0; + op0 = temp; + } + } + + /* If we can do it with a three-operand insn, do so. */ + + if (methods != OPTAB_MUST_WIDEN + && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) binoptab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_operand_mode[icode][1]; + enum machine_mode mode1 = insn_operand_mode[icode][2]; + rtx pat; + rtx xop0 = op0, xop1 = op1; + + if (target) + temp = target; + else + temp = gen_reg_rtx (mode); + + /* If it is a commutative operator and the modes would match + if we would swap the operands, we can save the conversions. */ + if (commutative_op) + { + if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1 + && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0) + { + register rtx tmp; + + tmp = op0; op0 = op1; op1 = tmp; + tmp = xop0; xop0 = xop1; xop1 = tmp; + } + } + + /* In case the insn wants input operands in modes different from + the result, convert the operands. */ + + if (GET_MODE (op0) != VOIDmode + && GET_MODE (op0) != mode0 + && mode0 != VOIDmode) + xop0 = convert_to_mode (mode0, xop0, unsignedp); + + if (GET_MODE (xop1) != VOIDmode + && GET_MODE (xop1) != mode1 + && mode1 != VOIDmode) + xop1 = convert_to_mode (mode1, xop1, unsignedp); + + /* Now, if insn's predicates don't allow our operands, put them into + pseudo regs. */ + + if (! (*insn_operand_predicate[icode][1]) (xop0, mode0) + && mode0 != VOIDmode) + xop0 = copy_to_mode_reg (mode0, xop0); + + if (! (*insn_operand_predicate[icode][2]) (xop1, mode1) + && mode1 != VOIDmode) + xop1 = copy_to_mode_reg (mode1, xop1); + + if (! (*insn_operand_predicate[icode][0]) (temp, mode)) + temp = gen_reg_rtx (mode); + + pat = GEN_FCN (icode) (temp, xop0, xop1); + if (pat) + { + /* If PAT is a multi-insn sequence, try to add an appropriate + REG_EQUAL note to it. If we can't because TEMP conflicts with an + operand, call ourselves again, this time without a target. */ + if (GET_CODE (pat) == SEQUENCE + && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1)) + { + delete_insns_since (last); + return expand_binop (mode, binoptab, op0, op1, NULL_RTX, + unsignedp, methods); + } + + emit_insn (pat); + return temp; + } + else + delete_insns_since (last); + } + + /* If this is a multiply, see if we can do a widening operation that + takes operands of this mode and makes a wider mode. */ + + if (binoptab == smul_optab && GET_MODE_WIDER_MODE (mode) != VOIDmode + && (((unsignedp ? umul_widen_optab : smul_widen_optab) + ->handlers[(int) GET_MODE_WIDER_MODE (mode)].insn_code) + != CODE_FOR_nothing)) + { + temp = expand_binop (GET_MODE_WIDER_MODE (mode), + unsignedp ? umul_widen_optab : smul_widen_optab, + op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT); + + if (temp != 0) + { + if (GET_MODE_CLASS (mode) == MODE_INT) + return gen_lowpart (mode, temp); + else + return convert_to_mode (mode, temp, unsignedp); + } + } + + /* 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 == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + && 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)) + { + if (binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing + || (binoptab == smul_optab + && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode + && (((unsignedp ? umul_widen_optab : smul_widen_optab) + ->handlers[(int) GET_MODE_WIDER_MODE (wider_mode)].insn_code) + != CODE_FOR_nothing))) + { + rtx xop0 = op0, xop1 = op1; + int no_extend = 0; + + /* For certain integer operations, we need not actually extend + the narrow operands, as long as we will truncate + the results to the same narrowness. */ + + if ((binoptab == ior_optab || binoptab == and_optab + || binoptab == xor_optab + || binoptab == add_optab || binoptab == sub_optab + || binoptab == smul_optab || binoptab == ashl_optab) + && class == MODE_INT) + no_extend = 1; + + xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, no_extend); + + /* The second operand of a shift must always be extended. */ + xop1 = widen_operand (xop1, wider_mode, mode, unsignedp, + no_extend && binoptab != ashl_optab); + + temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX, + unsignedp, OPTAB_DIRECT); + if (temp) + { + if (class != MODE_INT) + { + if (target == 0) + target = gen_reg_rtx (mode); + convert_move (target, temp, 0); + return target; + } + else + return gen_lowpart (mode, temp); + } + else + delete_insns_since (last); + } + } + + /* These can be done a word at a time. */ + if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab) + && class == MODE_INT + && GET_MODE_SIZE (mode) > UNITS_PER_WORD + && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) + { + int i; + rtx insns; + rtx equiv_value; + + /* 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); + + start_sequence (); + + /* Do the actual arithmetic. */ + for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) + { + rtx target_piece = operand_subword (target, i, 1, mode); + rtx x = expand_binop (word_mode, binoptab, + operand_subword_force (op0, i, mode), + operand_subword_force (op1, i, mode), + target_piece, unsignedp, next_methods); + + if (x == 0) + break; + + if (target_piece != x) + emit_move_insn (target_piece, x); + } + + insns = get_insns (); + end_sequence (); + + if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) + { + if (binoptab->code != UNKNOWN) + equiv_value + = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1)); + else + equiv_value = 0; + + emit_no_conflict_block (insns, target, op0, op1, equiv_value); + return target; + } + } + + /* Synthesize double word shifts from single word shifts. */ + if ((binoptab == lshr_optab || binoptab == ashl_optab + || binoptab == ashr_optab) + && class == MODE_INT + && GET_CODE (op1) == CONST_INT + && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing + && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing + && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) + { + rtx insns, inter, equiv_value; + rtx into_target, outof_target; + rtx into_input, outof_input; + int shift_count, 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); + + start_sequence (); + + shift_count = INTVAL (op1); + + /* OUTOF_* is the word we are shifting bits away from, and + INTO_* is the word that we are shifting bits towards, thus + they differ depending on the direction of the shift and + WORDS_BIG_ENDIAN. */ + + 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); + + if (shift_count >= BITS_PER_WORD) + { + inter = expand_binop (word_mode, binoptab, + outof_input, + GEN_INT (shift_count - BITS_PER_WORD), + into_target, unsignedp, next_methods); + + if (inter != 0 && inter != into_target) + emit_move_insn (into_target, inter); + + /* For a signed right shift, we must fill the word we are shifting + out of with copies of the sign bit. Otherwise it is zeroed. */ + if (inter != 0 && binoptab != ashr_optab) + inter = CONST0_RTX (word_mode); + else if (inter != 0) + inter = expand_binop (word_mode, binoptab, + outof_input, + GEN_INT (BITS_PER_WORD - 1), + outof_target, unsignedp, next_methods); + + if (inter != 0 && inter != outof_target) + emit_move_insn (outof_target, inter); + } + else + { + rtx carries; + optab reverse_unsigned_shift, unsigned_shift; + + /* For a shift of less then BITS_PER_WORD, to compute the carry, + we must do a logical shift in the opposite direction of the + desired shift. */ + + reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab); + + /* For a shift of less than BITS_PER_WORD, to compute the word + shifted towards, we need to unsigned shift the orig value of + that word. */ + + unsigned_shift = (left_shift ? ashl_optab : lshr_optab); + + carries = expand_binop (word_mode, reverse_unsigned_shift, + outof_input, + GEN_INT (BITS_PER_WORD - shift_count), + 0, unsignedp, next_methods); + + if (carries == 0) + inter = 0; + else + inter = expand_binop (word_mode, unsigned_shift, into_input, + op1, 0, unsignedp, next_methods); + + if (inter != 0) + inter = expand_binop (word_mode, ior_optab, carries, inter, + into_target, unsignedp, next_methods); + + if (inter != 0 && inter != into_target) + emit_move_insn (into_target, inter); + + if (inter != 0) + inter = expand_binop (word_mode, binoptab, outof_input, + op1, outof_target, unsignedp, next_methods); + + if (inter != 0 && inter != outof_target) + emit_move_insn (outof_target, inter); + } + + insns = get_insns (); + end_sequence (); + + if (inter != 0) + { + if (binoptab->code != UNKNOWN) + equiv_value = gen_rtx (binoptab->code, mode, op0, op1); + else + equiv_value = 0; + + emit_no_conflict_block (insns, target, op0, op1, equiv_value); + return target; + } + } + + /* Synthesize double word rotates from single word shifts. */ + if ((binoptab == rotl_optab || binoptab == rotr_optab) + && class == MODE_INT + && GET_CODE (op1) == CONST_INT + && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing + && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) + { + rtx insns, equiv_value; + rtx into_target, outof_target; + rtx into_input, outof_input; + rtx inter; + int shift_count, 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); + + start_sequence (); + + shift_count = INTVAL (op1); + + /* OUTOF_* is the word we are shifting bits away from, and + INTO_* is the word that we are shifting bits towards, thus + they differ depending on the direction of the shift and + WORDS_BIG_ENDIAN. */ + + 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); + + if (shift_count == BITS_PER_WORD) + { + /* This is just a word swap. */ + emit_move_insn (outof_target, into_input); + emit_move_insn (into_target, outof_input); + inter = const0_rtx; + } + else + { + rtx into_temp1, into_temp2, outof_temp1, outof_temp2; + rtx first_shift_count, second_shift_count; + optab reverse_unsigned_shift, unsigned_shift; + + reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD) + ? lshr_optab : ashl_optab); + + unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD) + ? ashl_optab : lshr_optab); + + if (shift_count > BITS_PER_WORD) + { + first_shift_count = GEN_INT (shift_count - BITS_PER_WORD); + second_shift_count = GEN_INT (2*BITS_PER_WORD - shift_count); + } + else + { + first_shift_count = GEN_INT (BITS_PER_WORD - shift_count); + second_shift_count = GEN_INT (shift_count); + } + + into_temp1 = expand_binop (word_mode, unsigned_shift, + outof_input, first_shift_count, + NULL_RTX, unsignedp, next_methods); + into_temp2 = expand_binop (word_mode, reverse_unsigned_shift, + into_input, second_shift_count, + into_target, unsignedp, next_methods); + + if (into_temp1 != 0 && into_temp2 != 0) + inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2, + into_target, unsignedp, next_methods); + else + inter = 0; + + if (inter != 0 && inter != into_target) + emit_move_insn (into_target, inter); + + outof_temp1 = expand_binop (word_mode, unsigned_shift, + into_input, first_shift_count, + NULL_RTX, unsignedp, next_methods); + outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift, + outof_input, second_shift_count, + outof_target, unsignedp, next_methods); + + if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0) + inter = expand_binop (word_mode, ior_optab, + outof_temp1, outof_temp2, + outof_target, unsignedp, next_methods); + + if (inter != 0 && inter != outof_target) + emit_move_insn (outof_target, inter); + } + + insns = get_insns (); + end_sequence (); + + if (inter != 0) + { + if (binoptab->code != UNKNOWN) + equiv_value = gen_rtx (binoptab->code, mode, op0, op1); + else + equiv_value = 0; + + /* We can't make this a no conflict block if this is a word swap, + because the word swap case fails if the input and output values + are in the same register. */ + if (shift_count != BITS_PER_WORD) + emit_no_conflict_block (insns, target, op0, op1, equiv_value); + else + emit_insns (insns); + + + return target; + } + } + + /* These can be done a word at a time by propagating carries. */ + if ((binoptab == add_optab || binoptab == sub_optab) + && class == MODE_INT + && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD + && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) + { + int i; + rtx carry_tmp = gen_reg_rtx (word_mode); + optab otheroptab = binoptab == add_optab ? sub_optab : add_optab; + int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; + rtx carry_in, carry_out; + rtx xop0, xop1; + + /* We can handle either a 1 or -1 value for the carry. If STORE_FLAG + value is one of those, use it. Otherwise, use 1 since it is the + one easiest to get. */ +#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1 + int normalizep = STORE_FLAG_VALUE; +#else + int normalizep = 1; +#endif + + /* Prepare the operands. */ + xop0 = force_reg (mode, op0); + xop1 = force_reg (mode, op1); + + if (target == 0 || GET_CODE (target) != REG + || target == xop0 || target == xop1) + target = gen_reg_rtx (mode); + + /* Indicate for flow that the entire target reg is being set. */ + if (GET_CODE (target) == REG) + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + /* Do the actual arithmetic. */ + for (i = 0; i < nwords; i++) + { + int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); + rtx target_piece = operand_subword (target, index, 1, mode); + rtx op0_piece = operand_subword_force (xop0, index, mode); + rtx op1_piece = operand_subword_force (xop1, index, mode); + rtx x; + + /* Main add/subtract of the input operands. */ + x = expand_binop (word_mode, binoptab, + op0_piece, op1_piece, + target_piece, unsignedp, next_methods); + if (x == 0) + break; + + if (i + 1 < nwords) + { + /* Store carry from main add/subtract. */ + carry_out = gen_reg_rtx (word_mode); + carry_out = emit_store_flag (carry_out, + binoptab == add_optab ? LTU : GTU, + x, op0_piece, + word_mode, 1, normalizep); + if (carry_out == 0) + break; + } + + if (i > 0) + { + /* Add/subtract previous carry to main result. */ + x = expand_binop (word_mode, + normalizep == 1 ? binoptab : otheroptab, + x, carry_in, + target_piece, 1, next_methods); + if (x == 0) + break; + else if (target_piece != x) + emit_move_insn (target_piece, x); + + if (i + 1 < nwords) + { + /* THIS CODE HAS NOT BEEN TESTED. */ + /* Get out carry from adding/subtracting carry in. */ + carry_tmp = emit_store_flag (carry_tmp, + binoptab == add_optab + ? LTU : GTU, + x, carry_in, + word_mode, 1, normalizep); + + /* Logical-ior the two poss. carry together. */ + carry_out = expand_binop (word_mode, ior_optab, + carry_out, carry_tmp, + carry_out, 0, next_methods); + if (carry_out == 0) + break; + } + } + + carry_in = carry_out; + } + + if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) + { + rtx temp = emit_move_insn (target, target); + + REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx (binoptab->code, mode, + copy_rtx (xop0), + copy_rtx (xop1)), + REG_NOTES (temp)); + return target; + } + else + delete_insns_since (last); + } + + /* If we want to multiply two two-word values and have normal and widening + multiplies of single-word values, we can do this with three smaller + multiplications. Note that we do not make a REG_NO_CONFLICT block here + because we are not operating on one word at a time. + + The multiplication proceeds as follows: + _______________________ + [__op0_high_|__op0_low__] + _______________________ + * [__op1_high_|__op1_low__] + _______________________________________________ + _______________________ + (1) [__op0_low__*__op1_low__] + _______________________ + (2a) [__op0_low__*__op1_high_] + _______________________ + (2b) [__op0_high_*__op1_low__] + _______________________ + (3) [__op0_high_*__op1_high_] + + + This gives a 4-word result. Since we are only interested in the + lower 2 words, partial result (3) and the upper words of (2a) and + (2b) don't need to be calculated. Hence (2a) and (2b) can be + calculated using non-widening multiplication. + + (1), however, needs to be calculated with an unsigned widening + multiplication. If this operation is not directly supported we + try using a signed widening multiplication and adjust the result. + This adjustment works as follows: + + If both operands are positive then no adjustment is needed. + + If the operands have different signs, for example op0_low < 0 and + op1_low >= 0, the instruction treats the most significant bit of + op0_low as a sign bit instead of a bit with significance + 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low + with 2**BITS_PER_WORD - op0_low, and two's complements the + result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to + the result. + + Similarly, if both operands are negative, we need to add + (op0_low + op1_low) * 2**BITS_PER_WORD. + + We use a trick to adjust quickly. We logically shift op0_low right + (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to + op0_high (op1_high) before it is used to calculate 2b (2a). If no + logical shift exists, we do an arithmetic right shift and subtract + the 0 or -1. */ + + if (binoptab == smul_optab + && class == MODE_INT + && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD + && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing + && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing + && ((umul_widen_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing) + || (smul_widen_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing))) + { + int low = (WORDS_BIG_ENDIAN ? 1 : 0); + int high = (WORDS_BIG_ENDIAN ? 0 : 1); + rtx op0_high = operand_subword_force (op0, high, mode); + rtx op0_low = operand_subword_force (op0, low, mode); + rtx op1_high = operand_subword_force (op1, high, mode); + rtx op1_low = operand_subword_force (op1, low, mode); + rtx product = 0; + rtx op0_xhigh; + rtx op1_xhigh; + + /* If the target is the same as one of the inputs, don't use it. This + prevents problems with the REG_EQUAL note. */ + if (target == op0 || target == op1 + || (target != 0 && GET_CODE (target) != REG)) + target = 0; + + /* Multiply the two lower words to get a double-word product. + If unsigned widening multiplication is available, use that; + otherwise use the signed form and compensate. */ + + if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + product = expand_binop (mode, umul_widen_optab, op0_low, op1_low, + target, 1, OPTAB_DIRECT); + + /* If we didn't succeed, delete everything we did so far. */ + if (product == 0) + delete_insns_since (last); + else + op0_xhigh = op0_high, op1_xhigh = op1_high; + } + + if (product == 0 + && smul_widen_optab->handlers[(int) mode].insn_code + != CODE_FOR_nothing) + { + rtx wordm1 = GEN_INT (BITS_PER_WORD - 1); + product = expand_binop (mode, smul_widen_optab, op0_low, op1_low, + target, 1, OPTAB_DIRECT); + op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1, + NULL_RTX, 1, next_methods); + if (op0_xhigh) + op0_xhigh = expand_binop (word_mode, add_optab, op0_high, + op0_xhigh, op0_xhigh, 0, next_methods); + else + { + op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1, + NULL_RTX, 0, next_methods); + if (op0_xhigh) + op0_xhigh = expand_binop (word_mode, sub_optab, op0_high, + op0_xhigh, op0_xhigh, 0, + next_methods); + } + + op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1, + NULL_RTX, 1, next_methods); + if (op1_xhigh) + op1_xhigh = expand_binop (word_mode, add_optab, op1_high, + op1_xhigh, op1_xhigh, 0, next_methods); + else + { + op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1, + NULL_RTX, 0, next_methods); + if (op1_xhigh) + op1_xhigh = expand_binop (word_mode, sub_optab, op1_high, + op1_xhigh, op1_xhigh, 0, + next_methods); + } + } + + /* If we have been able to directly compute the product of the + low-order words of the operands and perform any required adjustments + of the operands, we proceed by trying two more multiplications + and then computing the appropriate sum. + + We have checked above that the required addition is provided. + Full-word addition will normally always succeed, especially if + it is provided at all, so we don't worry about its failure. The + multiplication may well fail, however, so we do handle that. */ + + if (product && op0_xhigh && op1_xhigh) + { + rtx product_high = operand_subword (product, high, 1, mode); + rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh, + NULL_RTX, 0, OPTAB_DIRECT); + + if (temp != 0) + temp = expand_binop (word_mode, add_optab, temp, product_high, + product_high, 0, next_methods); + + if (temp != 0 && temp != product_high) + emit_move_insn (product_high, temp); + + if (temp != 0) + temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, + NULL_RTX, 0, OPTAB_DIRECT); + + if (temp != 0) + temp = expand_binop (word_mode, add_optab, temp, + product_high, product_high, + 0, next_methods); + + if (temp != 0 && temp != product_high) + emit_move_insn (product_high, temp); + + if (temp != 0) + { + temp = emit_move_insn (product, product); + REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx (MULT, mode, copy_rtx (op0), + copy_rtx (op1)), + REG_NOTES (temp)); + + return product; + } + } + + /* If we get here, we couldn't do it for some reason even though we + originally thought we could. Delete anything we've emitted in + trying to do it. */ + + delete_insns_since (last); + } + + /* We need to open-code the complex type operations: '+, -, * and /' */ + + /* At this point we allow operations between two similar complex + numbers, and also if one of the operands is not a complex number + but rather of MODE_FLOAT or MODE_INT. However, the caller + must make sure that the MODE of the non-complex operand matches + the SUBMODE of the complex operand. */ + + if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) + { + rtx real0 = 0, imag0 = 0; + rtx real1 = 0, imag1 = 0; + rtx realr, imagr, res; + rtx seq; + rtx equiv_value; + int ok = 0; + + /* Find the correct mode for the real and imaginary parts */ + enum machine_mode submode + = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, + class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, + 0); + + if (submode == BLKmode) + abort (); + + if (! target) + target = gen_reg_rtx (mode); + + start_sequence (); + + realr = gen_realpart (submode, target); + imagr = gen_imagpart (submode, target); + + if (GET_MODE (op0) == mode) + { + real0 = gen_realpart (submode, op0); + imag0 = gen_imagpart (submode, op0); + } + else + real0 = op0; + + if (GET_MODE (op1) == mode) + { + real1 = gen_realpart (submode, op1); + imag1 = gen_imagpart (submode, op1); + } + else + real1 = op1; + + if (real0 == 0 || real1 == 0 || ! (imag0 != 0|| imag1 != 0)) + abort (); + + switch (binoptab->code) + { + case PLUS: + /* (a+ib) + (c+id) = (a+c) + i(b+d) */ + case MINUS: + /* (a+ib) - (c+id) = (a-c) + i(b-d) */ + res = expand_binop (submode, binoptab, real0, real1, + realr, unsignedp, methods); + + if (res == 0) + break; + else if (res != realr) + emit_move_insn (realr, res); + + if (imag0 && imag1) + res = expand_binop (submode, binoptab, imag0, imag1, + imagr, unsignedp, methods); + else if (imag0) + res = imag0; + else if (binoptab->code == MINUS) + res = expand_unop (submode, neg_optab, imag1, imagr, unsignedp); + else + res = imag1; + + if (res == 0) + break; + else if (res != imagr) + emit_move_insn (imagr, res); + + ok = 1; + break; + + case MULT: + /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */ + + if (imag0 && imag1) + { + rtx temp1, temp2; + + /* Don't fetch these from memory more than once. */ + real0 = force_reg (submode, real0); + real1 = force_reg (submode, real1); + imag0 = force_reg (submode, imag0); + imag1 = force_reg (submode, imag1); + + temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX, + unsignedp, methods); + + temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX, + unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + break; + + res = expand_binop (submode, sub_optab, temp1, temp2, + realr, unsignedp, methods); + + if (res == 0) + break; + else if (res != realr) + emit_move_insn (realr, res); + + temp1 = expand_binop (submode, binoptab, real0, imag1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, binoptab, real1, imag0, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + break; + + res = expand_binop (submode, add_optab, temp1, temp2, + imagr, unsignedp, methods); + + if (res == 0) + break; + else if (res != imagr) + emit_move_insn (imagr, res); + + ok = 1; + } + else + { + /* Don't fetch these from memory more than once. */ + real0 = force_reg (submode, real0); + real1 = force_reg (submode, real1); + + res = expand_binop (submode, binoptab, real0, real1, + realr, unsignedp, methods); + if (res == 0) + break; + else if (res != realr) + emit_move_insn (realr, res); + + if (imag0 != 0) + res = expand_binop (submode, binoptab, + real1, imag0, imagr, unsignedp, methods); + else + res = expand_binop (submode, binoptab, + real0, imag1, imagr, unsignedp, methods); + + if (res == 0) + break; + else if (res != imagr) + emit_move_insn (imagr, res); + + ok = 1; + } + break; + + case DIV: + /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */ + + if (imag1 == 0) + { + /* (a+ib) / (c+i0) = (a/c) + i(b/c) */ + + /* Don't fetch these from memory more than once. */ + real1 = force_reg (submode, real1); + + /* Simply divide the real and imaginary parts by `c' */ + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, real0, real1, + realr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + real0, real1, realr, unsignedp); + + if (res == 0) + break; + else if (res != realr) + emit_move_insn (realr, res); + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, imag0, real1, + imagr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + imag0, real1, imagr, unsignedp); + + if (res == 0) + break; + else if (res != imagr) + emit_move_insn (imagr, res); + + ok = 1; + } + else + { + /* Divisor is of complex type: + X/(a+ib) */ + rtx divisor; + rtx real_t, imag_t; + rtx lhs, rhs; + rtx temp1, temp2; + + /* Don't fetch these from memory more than once. */ + real0 = force_reg (submode, real0); + real1 = force_reg (submode, real1); + + if (imag0 != 0) + imag0 = force_reg (submode, imag0); + + imag1 = force_reg (submode, imag1); + + /* Divisor: c*c + d*d */ + temp1 = expand_binop (submode, smul_optab, real1, real1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, smul_optab, imag1, imag1, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + break; + + divisor = expand_binop (submode, add_optab, temp1, temp2, + NULL_RTX, unsignedp, methods); + if (divisor == 0) + break; + + if (imag0 == 0) + { + /* ((a)(c-id))/divisor */ + /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */ + + /* Calculate the dividend */ + real_t = expand_binop (submode, smul_optab, real0, real1, + NULL_RTX, unsignedp, methods); + + imag_t = expand_binop (submode, smul_optab, real0, imag1, + NULL_RTX, unsignedp, methods); + + if (real_t == 0 || imag_t == 0) + break; + + imag_t = expand_unop (submode, neg_optab, imag_t, + NULL_RTX, unsignedp); + } + else + { + /* ((a+ib)(c-id))/divider */ + /* Calculate the dividend */ + temp1 = expand_binop (submode, smul_optab, real0, real1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, smul_optab, imag0, imag1, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + break; + + real_t = expand_binop (submode, add_optab, temp1, temp2, + NULL_RTX, unsignedp, methods); + + temp1 = expand_binop (submode, smul_optab, imag0, real1, + NULL_RTX, unsignedp, methods); + + temp2 = expand_binop (submode, smul_optab, real0, imag1, + NULL_RTX, unsignedp, methods); + + if (temp1 == 0 || temp2 == 0) + break; + + imag_t = expand_binop (submode, sub_optab, temp1, temp2, + NULL_RTX, unsignedp, methods); + + if (real_t == 0 || imag_t == 0) + break; + } + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, real_t, divisor, + realr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + real_t, divisor, realr, unsignedp); + + if (res == 0) + break; + else if (res != realr) + emit_move_insn (realr, res); + + if (class == MODE_COMPLEX_FLOAT) + res = expand_binop (submode, binoptab, imag_t, divisor, + imagr, unsignedp, methods); + else + res = expand_divmod (0, TRUNC_DIV_EXPR, submode, + imag_t, divisor, imagr, unsignedp); + + if (res == 0) + break; + else if (res != imagr) + emit_move_insn (imagr, res); + + ok = 1; + } + break; + + default: + abort (); + } + + seq = get_insns (); + end_sequence (); + + if (ok) + { + if (binoptab->code != UNKNOWN) + equiv_value + = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1)); + else + equiv_value = 0; + + emit_no_conflict_block (seq, target, op0, op1, equiv_value); + + return target; + } + } + + /* It can't be open-coded in this mode. + Use a library call if one is available and caller says that's ok. */ + + if (binoptab->handlers[(int) mode].libfunc + && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN)) + { + rtx insns; + rtx funexp = binoptab->handlers[(int) mode].libfunc; + rtx op1x = op1; + enum machine_mode op1_mode = mode; + rtx value; + + start_sequence (); + + if (shift_op) + { + op1_mode = word_mode; + /* Specify unsigned here, + since negative shift counts are meaningless. */ + op1x = convert_to_mode (word_mode, op1, 1); + } + + if (GET_MODE (op0) != VOIDmode + && GET_MODE (op0) != mode) + op0 = convert_to_mode (mode, op0, unsignedp); + + /* 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 (binoptab->handlers[(int) mode].libfunc, + NULL_RTX, 1, mode, 2, + op0, mode, op1x, op1_mode); + + insns = get_insns (); + end_sequence (); + + target = gen_reg_rtx (mode); + emit_libcall_block (insns, target, value, + gen_rtx (binoptab->code, mode, op0, op1)); + + return target; + } + + delete_insns_since (last); + + /* It can't be done in this mode. Can we do it in a wider mode? */ + + if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN + || methods == OPTAB_MUST_WIDEN)) + { + /* Caller says, don't even try. */ + delete_insns_since (entry_last); + return 0; + } + + /* Compute the value of METHODS to pass to recursive calls. + Don't allow widening to be tried recursively. */ + + methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT); + + /* Look for a wider mode of the same class for which it appears we can do + the operation. */ + + if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + { + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if ((binoptab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + || (methods == OPTAB_LIB + && binoptab->handlers[(int) wider_mode].libfunc)) + { + rtx xop0 = op0, xop1 = op1; + int no_extend = 0; + + /* For certain integer operations, we need not actually extend + the narrow operands, as long as we will truncate + the results to the same narrowness. */ + + if ((binoptab == ior_optab || binoptab == and_optab + || binoptab == xor_optab + || binoptab == add_optab || binoptab == sub_optab + || binoptab == smul_optab || binoptab == ashl_optab) + && class == MODE_INT) + no_extend = 1; + + xop0 = widen_operand (xop0, wider_mode, mode, + unsignedp, no_extend); + + /* The second operand of a shift must always be extended. */ + xop1 = widen_operand (xop1, wider_mode, mode, unsignedp, + no_extend && binoptab != ashl_optab); + + temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX, + unsignedp, methods); + if (temp) + { + if (class != MODE_INT) + { + if (target == 0) + target = gen_reg_rtx (mode); + convert_move (target, temp, 0); + return target; + } + else + return gen_lowpart (mode, temp); + } + else + delete_insns_since (last); + } + } + } + + delete_insns_since (entry_last); + return 0; +} + +/* Expand a binary operator which has both signed and unsigned forms. + UOPTAB is the optab for unsigned operations, and SOPTAB is for + signed operations. + + If we widen unsigned operands, we may use a signed wider operation instead + of an unsigned wider operation, since the result would be the same. */ + +rtx +sign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods) + enum machine_mode mode; + optab uoptab, soptab; + rtx op0, op1, target; + int unsignedp; + enum optab_methods methods; +{ + register rtx temp; + optab direct_optab = unsignedp ? uoptab : soptab; + struct optab wide_soptab; + + /* Do it without widening, if possible. */ + temp = expand_binop (mode, direct_optab, op0, op1, target, + unsignedp, OPTAB_DIRECT); + 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; + wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing; + wide_soptab.handlers[(int) mode].libfunc = 0; + + temp = expand_binop (mode, &wide_soptab, op0, op1, target, + unsignedp, OPTAB_WIDEN); + + /* For unsigned operands, try widening to an unsigned int. */ + if (temp == 0 && unsignedp) + temp = expand_binop (mode, uoptab, op0, op1, target, + unsignedp, OPTAB_WIDEN); + if (temp || methods == OPTAB_WIDEN) + return temp; + + /* Use the right width lib call if that exists. */ + temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB); + if (temp || methods == OPTAB_LIB) + return temp; + + /* Must widen and use a lib call, use either signed or unsigned. */ + temp = expand_binop (mode, &wide_soptab, op0, op1, target, + unsignedp, methods); + if (temp != 0) + return temp; + if (unsignedp) + return expand_binop (mode, uoptab, op0, op1, target, + unsignedp, methods); + return 0; +} + +/* Generate code to perform an operation specified by BINOPTAB + on operands OP0 and OP1, with two results to TARG1 and TARG2. + We assume that the order of the operands for the instruction + is TARG0, OP0, OP1, TARG1, which would fit a pattern like + [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. + + Either TARG0 or TARG1 may be zero, but what that means is that + that result is not actually wanted. We will generate it into + a dummy pseudo-reg and discard it. They may not both be zero. + + Returns 1 if this operation can be performed; 0 if not. */ + +int +expand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp) + optab binoptab; + rtx op0, op1; + rtx targ0, targ1; + int unsignedp; +{ + enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1); + enum mode_class class; + enum machine_mode wider_mode; + rtx entry_last = get_last_insn (); + rtx last; + + class = GET_MODE_CLASS (mode); + + op0 = protect_from_queue (op0, 0); + op1 = protect_from_queue (op1, 0); + + if (flag_force_mem) + { + op0 = force_not_mem (op0); + op1 = force_not_mem (op1); + } + + /* If we are inside an appropriately-short loop and one operand is an + expensive constant, force it into a register. */ + if (CONSTANT_P (op0) && preserve_subexpressions_p () + && rtx_cost (op0, binoptab->code) > 2) + op0 = force_reg (mode, op0); + + if (CONSTANT_P (op1) && preserve_subexpressions_p () + && rtx_cost (op1, binoptab->code) > 2) + op1 = force_reg (mode, op1); + + if (targ0) + targ0 = protect_from_queue (targ0, 1); + else + targ0 = gen_reg_rtx (mode); + if (targ1) + targ1 = protect_from_queue (targ1, 1); + else + targ1 = gen_reg_rtx (mode); + + /* Record where to go back to if we fail. */ + last = get_last_insn (); + + if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) binoptab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_operand_mode[icode][1]; + enum machine_mode mode1 = insn_operand_mode[icode][2]; + rtx pat; + rtx xop0 = op0, xop1 = op1; + + /* In case this insn wants input operands in modes different from the + result, convert the operands. */ + if (GET_MODE (op0) != VOIDmode && GET_MODE (op0) != mode0) + xop0 = convert_to_mode (mode0, xop0, unsignedp); + + if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode1) + xop1 = convert_to_mode (mode1, xop1, unsignedp); + + /* Now, if insn doesn't accept these operands, put them into pseudos. */ + if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) + xop0 = copy_to_mode_reg (mode0, xop0); + + if (! (*insn_operand_predicate[icode][2]) (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. */ + if (! (*insn_operand_predicate[icode][0]) (targ0, mode) + || ! (*insn_operand_predicate[icode][3]) (targ1, mode)) + abort (); + + pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1); + if (pat) + { + emit_insn (pat); + return 1; + } + else + delete_insns_since (last); + } + + /* It can't be done in this mode. Can we do it in a wider mode? */ + + if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + { + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if (binoptab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + { + register rtx t0 = gen_reg_rtx (wider_mode); + register rtx t1 = gen_reg_rtx (wider_mode); + + if (expand_twoval_binop (binoptab, + convert_modes (wider_mode, mode, op0, + unsignedp), + convert_modes (wider_mode, mode, op1, + unsignedp), + t0, t1, unsignedp)) + { + convert_move (targ0, t0, unsignedp); + convert_move (targ1, t1, unsignedp); + return 1; + } + else + delete_insns_since (last); + } + } + } + + delete_insns_since (entry_last); + return 0; +} + +/* Generate code to perform an operation specified by UNOPTAB + on operand OP0, with result having machine-mode MODE. + + UNSIGNEDP is for the case where we have to widen the operands + to perform the operation. It says to use zero-extension. + + If TARGET is nonzero, the value + is generated there, if it is convenient to do so. + In all cases an rtx is returned for the locus of the value; + this may or may not be TARGET. */ + +rtx +expand_unop (mode, unoptab, op0, target, unsignedp) + enum machine_mode mode; + optab unoptab; + rtx op0; + rtx target; + int unsignedp; +{ + enum mode_class class; + enum machine_mode wider_mode; + register rtx temp; + rtx last = get_last_insn (); + rtx pat; + + class = GET_MODE_CLASS (mode); + + op0 = protect_from_queue (op0, 0); + + if (flag_force_mem) + { + op0 = force_not_mem (op0); + } + + if (target) + target = protect_from_queue (target, 1); + + if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) unoptab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_operand_mode[icode][1]; + rtx xop0 = op0; + + 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_operand_predicate[icode][1]) (xop0, mode0)) + xop0 = copy_to_mode_reg (mode0, xop0); + + if (! (*insn_operand_predicate[icode][0]) (temp, mode)) + temp = gen_reg_rtx (mode); + + pat = GEN_FCN (icode) (temp, xop0); + if (pat) + { + if (GET_CODE (pat) == SEQUENCE + && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX)) + { + delete_insns_since (last); + return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp); + } + + emit_insn (pat); + + return temp; + } + else + delete_insns_since (last); + } + + /* It can't be done in this mode. Can we open-code it in a wider mode? */ + + if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if (unoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) + { + rtx xop0 = op0; + + /* 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) + && class == MODE_INT); + + temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX, + unsignedp); + + if (temp) + { + if (class != MODE_INT) + { + if (target == 0) + target = gen_reg_rtx (mode); + convert_move (target, temp, 0); + return target; + } + else + return gen_lowpart (mode, temp); + } + else + delete_insns_since (last); + } + } + + /* These can be done a word at a time. */ + if (unoptab == one_cmpl_optab + && class == MODE_INT + && GET_MODE_SIZE (mode) > UNITS_PER_WORD + && unoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) + { + int i; + rtx insns; + + if (target == 0 || target == op0) + target = gen_reg_rtx (mode); + + start_sequence (); + + /* Do the actual arithmetic. */ + for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) + { + rtx target_piece = operand_subword (target, i, 1, mode); + rtx x = expand_unop (word_mode, unoptab, + operand_subword_force (op0, i, mode), + target_piece, unsignedp); + if (target_piece != x) + emit_move_insn (target_piece, x); + } + + insns = get_insns (); + end_sequence (); + + emit_no_conflict_block (insns, target, op0, NULL_RTX, + gen_rtx (unoptab->code, mode, copy_rtx (op0))); + return target; + } + + /* Open-code the complex negation operation. */ + else if (unoptab == neg_optab + && (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)) + { + rtx target_piece; + rtx x; + rtx seq; + + /* Find the correct mode for the real and imaginary parts */ + enum machine_mode submode + = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, + class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, + 0); + + if (submode == BLKmode) + abort (); + + if (target == 0) + target = gen_reg_rtx (mode); + + start_sequence (); + + target_piece = gen_imagpart (submode, target); + x = expand_unop (submode, unoptab, + gen_imagpart (submode, op0), + target_piece, unsignedp); + if (target_piece != x) + emit_move_insn (target_piece, x); + + target_piece = gen_realpart (submode, target); + x = expand_unop (submode, unoptab, + gen_realpart (submode, op0), + target_piece, unsignedp); + if (target_piece != x) + emit_move_insn (target_piece, x); + + seq = get_insns (); + end_sequence (); + + emit_no_conflict_block (seq, target, op0, 0, + gen_rtx (unoptab->code, mode, copy_rtx (op0))); + return target; + } + + /* Now try a library call in this mode. */ + if (unoptab->handlers[(int) mode].libfunc) + { + rtx insns; + rtx funexp = unoptab->handlers[(int) mode].libfunc; + rtx value; + + start_sequence (); + + /* 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 (unoptab->handlers[(int) mode].libfunc, + NULL_RTX, 1, mode, 1, op0, mode); + insns = get_insns (); + end_sequence (); + + target = gen_reg_rtx (mode); + emit_libcall_block (insns, target, value, + gen_rtx (unoptab->code, mode, op0)); + + return target; + } + + /* It can't be done in this mode. Can we do it in a wider mode? */ + + if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + { + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if ((unoptab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + || unoptab->handlers[(int) wider_mode].libfunc) + { + rtx xop0 = op0; + + /* 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) + && class == MODE_INT); + + temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX, + unsignedp); + + if (temp) + { + if (class != MODE_INT) + { + if (target == 0) + target = gen_reg_rtx (mode); + convert_move (target, temp, 0); + return target; + } + else + return gen_lowpart (mode, temp); + } + else + delete_insns_since (last); + } + } + } + + /* If there is no negate operation, try doing a subtract from zero. + The US Software GOFAST library needs this. */ + if (unoptab == neg_optab) + { + rtx temp; + temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0, + target, unsignedp, OPTAB_LIB_WIDEN); + if (temp) + return temp; + } + + return 0; +} + +/* Emit code to compute the absolute value of OP0, with result to + TARGET if convenient. (TARGET may be 0.) The return value says + where the result actually is to be found. + + MODE is the mode of the operand; the mode of the result is + different but can be deduced from MODE. + + UNSIGNEDP is relevant if extension is needed. */ + +rtx +expand_abs (mode, op0, target, unsignedp, safe) + enum machine_mode mode; + rtx op0; + rtx target; + int unsignedp; + int safe; +{ + rtx temp, op1; + + /* First try to do it with a special abs instruction. */ + temp = expand_unop (mode, abs_optab, op0, target, 0); + if (temp != 0) + return temp; + + /* If this machine has expensive jumps, we can do integer absolute + 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 && BRANCH_COST >= 2) + { + rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, + size_int (GET_MODE_BITSIZE (mode) - 1), + NULL_RTX, 0); + + temp = expand_binop (mode, xor_optab, extended, op0, target, 0, + OPTAB_LIB_WIDEN); + if (temp != 0) + temp = expand_binop (mode, sub_optab, temp, extended, target, 0, + OPTAB_LIB_WIDEN); + + if (temp != 0) + return temp; + } + + /* If that does not win, use conditional jump and negate. */ + op1 = gen_label_rtx (); + if (target == 0 || ! safe + || GET_MODE (target) != mode + || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target)) + || (GET_CODE (target) == REG + && REGNO (target) < FIRST_PSEUDO_REGISTER)) + target = gen_reg_rtx (mode); + + emit_move_insn (target, op0); + NO_DEFER_POP; + + /* If this mode is an integer too wide to compare properly, + compare word by word. Rely on CSE to optimize constant cases. */ + if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode)) + do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx, + NULL_RTX, op1); + else + { + temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode, + NULL_RTX, 0); + if (temp == const1_rtx) + return target; + else if (temp != const0_rtx) + { + if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0) + emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1)); + else + abort (); + } + } + + op0 = expand_unop (mode, neg_optab, target, target, 0); + if (op0 != target) + emit_move_insn (target, op0); + emit_label (op1); + OK_DEFER_POP; + return target; +} + +/* Emit code to compute the absolute value of OP0, with result to + TARGET if convenient. (TARGET may be 0.) The return value says + where the result actually is to be found. + + MODE is the mode of the operand; the mode of the result is + different but can be deduced from MODE. + + UNSIGNEDP is relevant for complex integer modes. */ + +rtx +expand_complex_abs (mode, op0, target, unsignedp) + enum machine_mode mode; + rtx op0; + rtx target; + int unsignedp; +{ + enum mode_class class = GET_MODE_CLASS (mode); + enum machine_mode wider_mode; + register rtx temp; + rtx entry_last = get_last_insn (); + rtx last; + rtx pat; + + /* Find the correct mode for the real and imaginary parts. */ + enum machine_mode submode + = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, + class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, + 0); + + if (submode == BLKmode) + abort (); + + op0 = protect_from_queue (op0, 0); + + if (flag_force_mem) + { + op0 = force_not_mem (op0); + } + + last = get_last_insn (); + + if (target) + target = protect_from_queue (target, 1); + + if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) abs_optab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_operand_mode[icode][1]; + rtx xop0 = op0; + + if (target) + temp = target; + else + temp = gen_reg_rtx (submode); + + 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_operand_predicate[icode][1]) (xop0, mode0)) + xop0 = copy_to_mode_reg (mode0, xop0); + + if (! (*insn_operand_predicate[icode][0]) (temp, submode)) + temp = gen_reg_rtx (submode); + + pat = GEN_FCN (icode) (temp, xop0); + if (pat) + { + if (GET_CODE (pat) == SEQUENCE + && ! add_equal_note (pat, temp, abs_optab->code, xop0, NULL_RTX)) + { + delete_insns_since (last); + return expand_unop (mode, abs_optab, op0, NULL_RTX, unsignedp); + } + + emit_insn (pat); + + return temp; + } + else + delete_insns_since (last); + } + + /* It can't be done in this mode. Can we open-code it in a wider mode? */ + + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if (abs_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) + { + rtx xop0 = op0; + + xop0 = convert_modes (wider_mode, mode, xop0, unsignedp); + temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); + + if (temp) + { + if (class != MODE_COMPLEX_INT) + { + if (target == 0) + target = gen_reg_rtx (submode); + convert_move (target, temp, 0); + return target; + } + else + return gen_lowpart (submode, temp); + } + else + delete_insns_since (last); + } + } + + /* Open-code the complex absolute-value operation + if we can open-code sqrt. Otherwise it's not worth while. */ + if (sqrt_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing) + { + rtx real, imag, total; + + real = gen_realpart (submode, op0); + imag = gen_imagpart (submode, op0); + + /* Square both parts. */ + real = expand_mult (submode, real, real, NULL_RTX, 0); + imag = expand_mult (submode, imag, imag, NULL_RTX, 0); + + /* Sum the parts. */ + total = expand_binop (submode, add_optab, real, imag, NULL_RTX, + 0, OPTAB_LIB_WIDEN); + + /* Get sqrt in TARGET. Set TARGET to where the result is. */ + target = expand_unop (submode, sqrt_optab, total, target, 0); + if (target == 0) + delete_insns_since (last); + else + return target; + } + + /* Now try a library call in this mode. */ + if (abs_optab->handlers[(int) mode].libfunc) + { + rtx insns; + rtx funexp = abs_optab->handlers[(int) mode].libfunc; + rtx value; + + start_sequence (); + + /* 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 (abs_optab->handlers[(int) mode].libfunc, + NULL_RTX, 1, submode, 1, op0, mode); + insns = get_insns (); + end_sequence (); + + target = gen_reg_rtx (submode); + emit_libcall_block (insns, target, value, + gen_rtx (abs_optab->code, mode, op0)); + + return target; + } + + /* It can't be done in this mode. Can we do it in a wider mode? */ + + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if ((abs_optab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + || abs_optab->handlers[(int) wider_mode].libfunc) + { + rtx xop0 = op0; + + xop0 = convert_modes (wider_mode, mode, xop0, unsignedp); + + temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); + + if (temp) + { + if (class != MODE_COMPLEX_INT) + { + if (target == 0) + target = gen_reg_rtx (submode); + convert_move (target, temp, 0); + return target; + } + else + return gen_lowpart (submode, temp); + } + else + delete_insns_since (last); + } + } + + delete_insns_since (entry_last); + return 0; +} + +/* Generate an instruction whose insn-code is INSN_CODE, + with two operands: an output TARGET and an input OP0. + TARGET *must* be nonzero, and the output is always stored there. + CODE is an rtx code such that (CODE OP0) is an rtx that describes + the value that is stored into TARGET. */ + +void +emit_unop_insn (icode, target, op0, code) + int icode; + rtx target; + rtx op0; + enum rtx_code code; +{ + register rtx temp; + enum machine_mode mode0 = insn_operand_mode[icode][1]; + rtx pat; + + temp = target = protect_from_queue (target, 1); + + op0 = protect_from_queue (op0, 0); + + if (flag_force_mem) + op0 = force_not_mem (op0); + + /* Now, if insn does not accept our operands, put them into pseudos. */ + + if (! (*insn_operand_predicate[icode][1]) (op0, mode0)) + op0 = copy_to_mode_reg (mode0, op0); + + if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp)) + || (flag_force_mem && GET_CODE (temp) == MEM)) + temp = gen_reg_rtx (GET_MODE (temp)); + + pat = GEN_FCN (icode) (temp, op0); + + if (GET_CODE (pat) == SEQUENCE && code != UNKNOWN) + add_equal_note (pat, temp, code, op0, NULL_RTX); + + emit_insn (pat); + + if (temp != target) + emit_move_insn (target, temp); +} + +/* Emit code to perform a series of operations on a multi-word quantity, one + word at a time. + + Such a block is preceded by a CLOBBER of the output, consists of multiple + insns, each setting one word of the output, and followed by a SET copying + the output to itself. + + Each of the insns setting words of the output receives a REG_NO_CONFLICT + note indicating that it doesn't conflict with the (also multi-word) + inputs. The entire block is surrounded by REG_LIBCALL and REG_RETVAL + notes. + + INSNS is a block of code generated to perform the operation, not including + the CLOBBER and final copy. All insns that compute intermediate values + are first emitted, followed by the block as described above. + + TARGET, OP0, and OP1 are the output and inputs of the operations, + respectively. OP1 may be zero for a unary operation. + + EQUIV, if non-zero, is an expression to be placed into a REG_EQUAL note + on the last insn. + + If TARGET is not a register, INSNS is simply emitted with no special + processing. Likewise if anything in INSNS is not an INSN or if + there is a libcall block inside INSNS. + + The final insn emitted is returned. */ + +rtx +emit_no_conflict_block (insns, target, op0, op1, equiv) + rtx insns; + rtx target; + rtx op0, op1; + rtx equiv; +{ + rtx prev, next, first, last, insn; + + if (GET_CODE (target) != REG || reload_in_progress) + return emit_insns (insns); + else + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) != INSN + || find_reg_note (insn, REG_LIBCALL, NULL_RTX)) + return emit_insns (insns); + + /* First emit all insns that do not store into words of the output and remove + these from the list. */ + for (insn = insns; insn; insn = next) + { + rtx set = 0; + int i; + + next = NEXT_INSN (insn); + + if (GET_CODE (PATTERN (insn)) == SET) + set = PATTERN (insn); + else if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) + { + set = XVECEXP (PATTERN (insn), 0, i); + break; + } + } + + if (set == 0) + abort (); + + if (! reg_overlap_mentioned_p (target, SET_DEST (set))) + { + if (PREV_INSN (insn)) + NEXT_INSN (PREV_INSN (insn)) = next; + else + insns = next; + + if (next) + PREV_INSN (next) = PREV_INSN (insn); + + add_insn (insn); + } + } + + prev = get_last_insn (); + + /* Now write the CLOBBER of the output, followed by the setting of each + of the words, followed by the final copy. */ + if (target != op0 && target != op1) + emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); + + for (insn = insns; insn; insn = next) + { + next = NEXT_INSN (insn); + add_insn (insn); + + if (op1 && GET_CODE (op1) == REG) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op1, + REG_NOTES (insn)); + + if (op0 && GET_CODE (op0) == REG) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op0, + REG_NOTES (insn)); + } + + if (mov_optab->handlers[(int) GET_MODE (target)].insn_code + != CODE_FOR_nothing) + { + last = emit_move_insn (target, target); + if (equiv) + REG_NOTES (last) + = gen_rtx (EXPR_LIST, REG_EQUAL, equiv, REG_NOTES (last)); + } + else + last = get_last_insn (); + + if (prev == 0) + first = get_insns (); + else + first = NEXT_INSN (prev); + + /* Encapsulate the block so it gets manipulated as a unit. */ + REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, + REG_NOTES (first)); + REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last)); + + return last; +} + +/* Emit code to make a call to a constant function or a library call. + + INSNS is a list containing all insns emitted in the call. + These insns leave the result in RESULT. Our block is to copy RESULT + to TARGET, which is logically equivalent to EQUIV. + + We first emit any insns that set a pseudo on the assumption that these are + loading constants into registers; doing so allows them to be safely cse'ed + between blocks. Then we emit all the other insns in the block, followed by + an insn to move RESULT to TARGET. This last insn will have a REQ_EQUAL + note with an operand of EQUIV. + + Moving assignments to pseudos outside of the block is done to improve + the generated code, but is not required to generate correct code, + hence being unable to move an assignment is not grounds for not making + a libcall block. There are two reasons why it is safe to leave these + insns inside the block: First, we know that these pseudos cannot be + used in generated RTL outside the block since they are created for + temporary purposes within the block. Second, CSE will not record the + values of anything set inside a libcall block, so we know they must + be dead at the end of the block. + + Except for the first group of insns (the ones setting pseudos), the + block is delimited by REG_RETVAL and REG_LIBCALL notes. */ + +void +emit_libcall_block (insns, target, result, equiv) + rtx insns; + rtx target; + rtx result; + rtx equiv; +{ + rtx prev, next, first, last, insn; + + /* First emit all insns that set pseudos. Remove them from the list as + we go. Avoid insns that set pseudos which were referenced in previous + insns. These can be generated by move_by_pieces, for example, + to update an address. Similarly, avoid insns that reference things + set in previous insns. */ + + for (insn = insns; insn; insn = next) + { + rtx set = single_set (insn); + + next = NEXT_INSN (insn); + + if (set != 0 && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER + && (insn == insns + || (! reg_mentioned_p (SET_DEST (set), PATTERN (insns)) + && ! reg_used_between_p (SET_DEST (set), insns, insn) + && ! modified_in_p (SET_SRC (set), insns) + && ! modified_between_p (SET_SRC (set), insns, insn)))) + { + if (PREV_INSN (insn)) + NEXT_INSN (PREV_INSN (insn)) = next; + else + insns = next; + + if (next) + PREV_INSN (next) = PREV_INSN (insn); + + add_insn (insn); + } + } + + prev = get_last_insn (); + + /* Write the remaining insns followed by the final copy. */ + + for (insn = insns; insn; insn = next) + { + next = NEXT_INSN (insn); + + add_insn (insn); + } + + last = emit_move_insn (target, result); + REG_NOTES (last) = gen_rtx (EXPR_LIST, + REG_EQUAL, copy_rtx (equiv), REG_NOTES (last)); + + if (prev == 0) + first = get_insns (); + else + first = NEXT_INSN (prev); + + /* Encapsulate the block so it gets manipulated as a unit. */ + REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, + REG_NOTES (first)); + REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last)); +} + +/* Generate code to store zero in X. */ + +void +emit_clr_insn (x) + rtx x; +{ + emit_move_insn (x, const0_rtx); +} + +/* Generate code to store 1 in X + assuming it contains zero beforehand. */ + +void +emit_0_to_1_insn (x) + rtx x; +{ + emit_move_insn (x, const1_rtx); +} + +/* Generate code to compare X with Y + so that the condition codes are set. + + MODE is the mode of the inputs (in case they are const_int). + UNSIGNEDP nonzero says that X and Y are unsigned; + this matters if they need to be widened. + + If they have mode BLKmode, then SIZE specifies the size of both X and Y, + and ALIGN specifies the known shared alignment of X and Y. + + COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). + It is ignored for fixed-point and block comparisons; + it is used only for floating-point comparisons. */ + +void +emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align) + rtx x, y; + enum rtx_code comparison; + rtx size; + enum machine_mode mode; + int unsignedp; + int align; +{ + enum mode_class class; + enum machine_mode wider_mode; + + class = GET_MODE_CLASS (mode); + + /* They could both be VOIDmode if both args are immediate constants, + but we should fold that at an earlier stage. + With no special code here, this will call abort, + reminding the programmer to implement such folding. */ + + if (mode != BLKmode && flag_force_mem) + { + x = force_not_mem (x); + y = force_not_mem (y); + } + + /* If we are inside an appropriately-short loop and one operand is an + expensive constant, force it into a register. */ + if (CONSTANT_P (x) && preserve_subexpressions_p () && rtx_cost (x, COMPARE) > 2) + x = force_reg (mode, x); + + if (CONSTANT_P (y) && preserve_subexpressions_p () && rtx_cost (y, COMPARE) > 2) + y = force_reg (mode, y); + + /* Don't let both operands fail to indicate the mode. */ + if (GET_MODE (x) == VOIDmode && GET_MODE (y) == VOIDmode) + x = force_reg (mode, x); + + /* Handle all BLKmode compares. */ + + if (mode == BLKmode) + { + emit_queue (); + x = protect_from_queue (x, 0); + y = protect_from_queue (y, 0); + + if (size == 0) + abort (); +#ifdef HAVE_cmpstrqi + if (HAVE_cmpstrqi + && GET_CODE (size) == CONST_INT + && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode))) + { + enum machine_mode result_mode + = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0]; + rtx result = gen_reg_rtx (result_mode); + emit_insn (gen_cmpstrqi (result, x, y, size, GEN_INT (align))); + emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, + result_mode, 0, 0); + } + else +#endif +#ifdef HAVE_cmpstrhi + if (HAVE_cmpstrhi + && GET_CODE (size) == CONST_INT + && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode))) + { + enum machine_mode result_mode + = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0]; + rtx result = gen_reg_rtx (result_mode); + emit_insn (gen_cmpstrhi (result, x, y, size, GEN_INT (align))); + emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, + result_mode, 0, 0); + } + else +#endif +#ifdef HAVE_cmpstrsi + if (HAVE_cmpstrsi) + { + enum machine_mode result_mode + = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0]; + rtx result = gen_reg_rtx (result_mode); + size = protect_from_queue (size, 0); + emit_insn (gen_cmpstrsi (result, x, y, + convert_to_mode (SImode, size, 1), + GEN_INT (align))); + emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, + result_mode, 0, 0); + } + else +#endif + { +#ifdef TARGET_MEM_FUNCTIONS + emit_library_call (memcmp_libfunc, 0, + TYPE_MODE (integer_type_node), 3, + XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, + size, Pmode); +#else + emit_library_call (bcmp_libfunc, 0, + TYPE_MODE (integer_type_node), 3, + XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, + size, Pmode); +#endif + emit_cmp_insn (hard_libcall_value (TYPE_MODE (integer_type_node)), + const0_rtx, comparison, NULL_RTX, + TYPE_MODE (integer_type_node), 0, 0); + } + return; + } + + /* Handle some compares against zero. */ + + if (y == CONST0_RTX (mode) + && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) tst_optab->handlers[(int) mode].insn_code; + + emit_queue (); + x = protect_from_queue (x, 0); + y = protect_from_queue (y, 0); + + /* Now, if insn does accept these operands, put them into pseudos. */ + if (! (*insn_operand_predicate[icode][0]) + (x, insn_operand_mode[icode][0])) + x = copy_to_mode_reg (insn_operand_mode[icode][0], x); + + emit_insn (GEN_FCN (icode) (x)); + return; + } + + /* Handle compares for which there is a directly suitable insn. */ + + if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + { + int icode = (int) cmp_optab->handlers[(int) mode].insn_code; + + emit_queue (); + x = protect_from_queue (x, 0); + y = protect_from_queue (y, 0); + + /* Now, if insn doesn't accept these operands, put them into pseudos. */ + if (! (*insn_operand_predicate[icode][0]) + (x, insn_operand_mode[icode][0])) + x = copy_to_mode_reg (insn_operand_mode[icode][0], x); + + if (! (*insn_operand_predicate[icode][1]) + (y, insn_operand_mode[icode][1])) + y = copy_to_mode_reg (insn_operand_mode[icode][1], y); + + emit_insn (GEN_FCN (icode) (x, y)); + return; + } + + /* Try widening if we can find a direct insn that way. */ + + if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) + { + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if (cmp_optab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + { + x = protect_from_queue (x, 0); + y = protect_from_queue (y, 0); + x = convert_modes (wider_mode, mode, x, unsignedp); + y = convert_modes (wider_mode, mode, y, unsignedp); + emit_cmp_insn (x, y, comparison, NULL_RTX, + wider_mode, unsignedp, align); + return; + } + } + } + + /* Handle a lib call just for the mode we are using. */ + + if (cmp_optab->handlers[(int) mode].libfunc + && class != MODE_FLOAT) + { + rtx libfunc = cmp_optab->handlers[(int) mode].libfunc; + /* If we want unsigned, and this mode has a distinct unsigned + comparison routine, use that. */ + if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc) + libfunc = ucmp_optab->handlers[(int) mode].libfunc; + + emit_library_call (libfunc, 1, + word_mode, 2, x, mode, y, mode); + + /* Integer comparison returns a result that must be compared against 1, + so that even if we do an unsigned compare afterward, + there is still a value that can represent the result "less than". */ + + emit_cmp_insn (hard_libcall_value (word_mode), const1_rtx, + comparison, NULL_RTX, word_mode, unsignedp, 0); + return; + } + + if (class == MODE_FLOAT) + emit_float_lib_cmp (x, y, comparison); + + else + abort (); +} + +/* Nonzero if a compare of mode MODE can be done straightforwardly + (without splitting it into pieces). */ + +int +can_compare_p (mode) + enum machine_mode mode; +{ + do + { + if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing) + return 1; + mode = GET_MODE_WIDER_MODE (mode); + } while (mode != VOIDmode); + + return 0; +} + +/* Emit a library call comparison between floating point X and Y. + COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). */ + +void +emit_float_lib_cmp (x, y, comparison) + rtx x, y; + enum rtx_code comparison; +{ + enum machine_mode mode = GET_MODE (x); + rtx libfunc = 0; + + if (mode == HFmode) + switch (comparison) + { + case EQ: + libfunc = eqhf2_libfunc; + break; + + case NE: + libfunc = nehf2_libfunc; + break; + + case GT: + libfunc = gthf2_libfunc; + break; + + case GE: + libfunc = gehf2_libfunc; + break; + + case LT: + libfunc = lthf2_libfunc; + break; + + case LE: + libfunc = lehf2_libfunc; + break; + } + else if (mode == SFmode) + switch (comparison) + { + case EQ: + libfunc = eqsf2_libfunc; + break; + + case NE: + libfunc = nesf2_libfunc; + break; + + case GT: + libfunc = gtsf2_libfunc; + break; + + case GE: + libfunc = gesf2_libfunc; + break; + + case LT: + libfunc = ltsf2_libfunc; + break; + + case LE: + libfunc = lesf2_libfunc; + break; + } + else if (mode == DFmode) + switch (comparison) + { + case EQ: + libfunc = eqdf2_libfunc; + break; + + case NE: + libfunc = nedf2_libfunc; + break; + + case GT: + libfunc = gtdf2_libfunc; + break; + + case GE: + libfunc = gedf2_libfunc; + break; + + case LT: + libfunc = ltdf2_libfunc; + break; + + case LE: + libfunc = ledf2_libfunc; + break; + } + else if (mode == XFmode) + switch (comparison) + { + case EQ: + libfunc = eqxf2_libfunc; + break; + + case NE: + libfunc = nexf2_libfunc; + break; + + case GT: + libfunc = gtxf2_libfunc; + break; + + case GE: + libfunc = gexf2_libfunc; + break; + + case LT: + libfunc = ltxf2_libfunc; + break; + + case LE: + libfunc = lexf2_libfunc; + break; + } + else if (mode == TFmode) + switch (comparison) + { + case EQ: + libfunc = eqtf2_libfunc; + break; + + case NE: + libfunc = netf2_libfunc; + break; + + case GT: + libfunc = gttf2_libfunc; + break; + + case GE: + libfunc = getf2_libfunc; + break; + + case LT: + libfunc = lttf2_libfunc; + break; + + case LE: + libfunc = letf2_libfunc; + break; + } + else + { + enum machine_mode wider_mode; + + for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; + wider_mode = GET_MODE_WIDER_MODE (wider_mode)) + { + if ((cmp_optab->handlers[(int) wider_mode].insn_code + != CODE_FOR_nothing) + || (cmp_optab->handlers[(int) wider_mode].libfunc != 0)) + { + x = protect_from_queue (x, 0); + y = protect_from_queue (y, 0); + x = convert_to_mode (wider_mode, x, 0); + y = convert_to_mode (wider_mode, y, 0); + emit_float_lib_cmp (x, y, comparison); + return; + } + } + abort (); + } + + if (libfunc == 0) + abort (); + + emit_library_call (libfunc, 1, + word_mode, 2, x, mode, y, mode); + + emit_cmp_insn (hard_libcall_value (word_mode), const0_rtx, comparison, + NULL_RTX, word_mode, 0, 0); +} + +/* Generate code to indirectly jump to a location given in the rtx LOC. */ + +void +emit_indirect_jump (loc) + rtx loc; +{ + if (! ((*insn_operand_predicate[(int)CODE_FOR_indirect_jump][0]) + (loc, Pmode))) + loc = copy_to_mode_reg (Pmode, loc); + + emit_jump_insn (gen_indirect_jump (loc)); + emit_barrier (); +} + +#ifdef HAVE_conditional_move + +/* Emit a conditional move instruction if the machine supports one for that + condition and machine mode. + + OP0 and OP1 are the operands that should be compared using CODE. CMODE is + 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 OP3 + should be stored there. MODE is the mode to use should they be constants. + If it is VOIDmode, they cannot both be constants. + + The result is either TARGET (perhaps modified) or NULL_RTX if the operation + is not supported. */ + +rtx +emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode, + unsignedp) + rtx target; + enum rtx_code code; + rtx op0, op1; + enum machine_mode cmode; + rtx op2, op3; + enum machine_mode mode; + int unsignedp; +{ + rtx tem, subtarget, comparison, insn; + enum insn_code icode; + + /* If one operand is constant, make it the second one. Only do this + if the other operand is not constant as well. */ + + if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) + || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) + { + tem = op0; + op0 = op1; + op1 = tem; + code = swap_condition (code); + } + + if (cmode == VOIDmode) + cmode = GET_MODE (op0); + + if ((CONSTANT_P (op2) && ! CONSTANT_P (op3)) + || (GET_CODE (op2) == CONST_INT && GET_CODE (op3) != CONST_INT)) + { + tem = op2; + op2 = op3; + op3 = tem; + /* ??? This may not be appropriate (consider IEEE). Perhaps we should + call can_reverse_comparison_p here and bail out if necessary. + It's not clear whether we need to do this canonicalization though. */ + code = reverse_condition (code); + } + + if (mode == VOIDmode) + mode = GET_MODE (op2); + + icode = movcc_gen_code[mode]; + + if (icode == CODE_FOR_nothing) + return 0; + + if (flag_force_mem) + { + op2 = force_not_mem (op2); + op3 = force_not_mem (op3); + } + + if (target) + target = protect_from_queue (target, 1); + else + target = gen_reg_rtx (mode); + + subtarget = target; + + emit_queue (); + + op2 = protect_from_queue (op2, 0); + op3 = protect_from_queue (op3, 0); + + /* If the insn doesn't accept these operands, put them in pseudos. */ + + if (! (*insn_operand_predicate[icode][0]) + (subtarget, insn_operand_mode[icode][0])) + subtarget = gen_reg_rtx (insn_operand_mode[icode][0]); + + if (! (*insn_operand_predicate[icode][2]) + (op2, insn_operand_mode[icode][2])) + op2 = copy_to_mode_reg (insn_operand_mode[icode][2], op2); + + if (! (*insn_operand_predicate[icode][3]) + (op3, insn_operand_mode[icode][3])) + op3 = copy_to_mode_reg (insn_operand_mode[icode][3], op3); + + /* Everything should now be in the suitable form, so emit the compare insn + and then the conditional move. */ + + comparison + = compare_from_rtx (op0, op1, code, unsignedp, cmode, NULL_RTX, 0); + + /* ??? Watch for const0_rtx (nop) and const_true_rtx (unconditional)? */ + if (GET_CODE (comparison) != code) + /* This shouldn't happen. */ + abort (); + + insn = GEN_FCN (icode) (subtarget, comparison, op2, op3); + + /* If that failed, then give up. */ + if (insn == 0) + return 0; + + emit_insn (insn); + + if (subtarget != target) + convert_move (target, subtarget, 0); + + return target; +} + +/* Return non-zero 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 (mode) + enum machine_mode mode; +{ + if (movcc_gen_code[mode] != CODE_FOR_nothing) + return 1; + + return 0; +} + +#endif /* HAVE_conditional_move */ + +/* These three functions generate an insn body and return it + rather than emitting the insn. + + They do not protect from queued increments, + because they may be used 1) in protect_from_queue itself + and 2) in other passes where there is no queue. */ + +/* Generate and return an insn body to add Y to X. */ + +rtx +gen_add2_insn (x, y) + rtx x, y; +{ + int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; + + if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0]) + || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1]) + || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2])) + abort (); + + return (GEN_FCN (icode) (x, x, y)); +} + +int +have_add2_insn (mode) + enum machine_mode mode; +{ + return add_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing; +} + +/* Generate and return an insn body to subtract Y from X. */ + +rtx +gen_sub2_insn (x, y) + rtx x, y; +{ + int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; + + if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0]) + || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1]) + || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2])) + abort (); + + return (GEN_FCN (icode) (x, x, y)); +} + +int +have_sub2_insn (mode) + enum machine_mode mode; +{ + return sub_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing; +} + +/* Generate the body of an instruction to copy Y into X. + It may be a SEQUENCE, if one insn isn't enough. */ + +rtx +gen_move_insn (x, y) + rtx x, y; +{ + register enum machine_mode mode = GET_MODE (x); + enum insn_code insn_code; + rtx seq; + + if (mode == VOIDmode) + mode = GET_MODE (y); + + insn_code = mov_optab->handlers[(int) mode].insn_code; + + /* Handle MODE_CC modes: If we don't have a special move insn for this mode, + find a mode to do it in. If we have a movcc, use it. Otherwise, + find the MODE_INT mode of the same width. */ + + if (GET_MODE_CLASS (mode) == MODE_CC && insn_code == CODE_FOR_nothing) + { + enum machine_mode tmode = VOIDmode; + rtx x1 = x, y1 = y; + + if (mode != CCmode + && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing) + tmode = CCmode; + else + for (tmode = QImode; tmode != VOIDmode; + tmode = GET_MODE_WIDER_MODE (tmode)) + if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode)) + break; + + if (tmode == VOIDmode) + abort (); + + /* Get X and Y in TMODE. We can't use gen_lowpart here because it + may call change_address which is not appropriate if we were + called when a reload was in progress. We don't have to worry + about changing the address since the size in bytes is supposed to + be the same. Copy the MEM to change the mode and move any + substitutions from the old MEM to the new one. */ + + if (reload_in_progress) + { + x = gen_lowpart_common (tmode, x1); + if (x == 0 && GET_CODE (x1) == MEM) + { + x = gen_rtx (MEM, tmode, XEXP (x1, 0)); + RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (x1); + MEM_IN_STRUCT_P (x) = MEM_IN_STRUCT_P (x1); + MEM_VOLATILE_P (x) = MEM_VOLATILE_P (x1); + copy_replacements (x1, x); + } + + y = gen_lowpart_common (tmode, y1); + if (y == 0 && GET_CODE (y1) == MEM) + { + y = gen_rtx (MEM, tmode, XEXP (y1, 0)); + RTX_UNCHANGING_P (y) = RTX_UNCHANGING_P (y1); + MEM_IN_STRUCT_P (y) = MEM_IN_STRUCT_P (y1); + MEM_VOLATILE_P (y) = MEM_VOLATILE_P (y1); + copy_replacements (y1, y); + } + } + else + { + x = gen_lowpart (tmode, x); + y = gen_lowpart (tmode, y); + } + + insn_code = mov_optab->handlers[(int) tmode].insn_code; + return (GEN_FCN (insn_code) (x, y)); + } + + start_sequence (); + emit_move_insn_1 (x, y); + seq = gen_sequence (); + 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 (to_mode, from_mode, unsignedp) + enum machine_mode to_mode, from_mode; + int unsignedp; +{ + return extendtab[(int) to_mode][(int) from_mode][unsignedp]; +} + +/* 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 (x, y, mto, mfrom, unsignedp) + rtx x, y; + enum machine_mode mto, mfrom; + int unsignedp; +{ + return (GEN_FCN (extendtab[(int) mto][(int) mfrom][unsignedp]) (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 (fixmode, fltmode, unsignedp, truncp_ptr) + enum machine_mode fltmode, fixmode; + int unsignedp; + int *truncp_ptr; +{ + *truncp_ptr = 0; + if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp] != CODE_FOR_nothing) + return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp]; + + if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing) + { + *truncp_ptr = 1; + return fixtab[(int) fltmode][(int) fixmode][unsignedp]; + } + return CODE_FOR_nothing; +} + +static enum insn_code +can_float_p (fltmode, fixmode, unsignedp) + enum machine_mode fixmode, fltmode; + int unsignedp; +{ + return floattab[(int) fltmode][(int) fixmode][unsignedp]; +} + +/* 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. + Normally this is done by correcting the final value + if it is negative. */ + +void +expand_float (to, from, unsignedp) + rtx to, from; + int unsignedp; +{ + enum insn_code icode; + register rtx target = to; + enum machine_mode fmode, imode; + + /* Crash now, because we won't be able to decide which mode to use. */ + if (GET_MODE (from) == VOIDmode) + abort (); + + /* Look for an insn to do the conversion. Do it in the specified + modes if possible; otherwise convert either input, output or both to + 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 (imode = GET_MODE (from); imode != VOIDmode; + imode = GET_MODE_WIDER_MODE (imode)) + for (fmode = GET_MODE (to); fmode != VOIDmode; + fmode = GET_MODE_WIDER_MODE (fmode)) + { + int doing_unsigned = unsignedp; + + icode = can_float_p (fmode, imode, unsignedp); + if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp) + icode = can_float_p (fmode, imode, 0), doing_unsigned = 0; + + if (icode != CODE_FOR_nothing) + { + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (imode != GET_MODE (from)) + from = convert_to_mode (imode, from, unsignedp); + + if (fmode != GET_MODE (to)) + target = gen_reg_rtx (fmode); + + emit_unop_insn (icode, target, from, + doing_unsigned ? UNSIGNED_FLOAT : FLOAT); + + if (target != to) + convert_move (to, target, 0); + return; + } + } + +#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + + /* Unsigned integer, and no way to convert directly. + Convert as signed, then conditionally adjust the result. */ + if (unsignedp) + { + rtx label = gen_label_rtx (); + rtx temp; + REAL_VALUE_TYPE offset; + + emit_queue (); + + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (flag_force_mem) + from = force_not_mem (from); + + /* Look for a usable floating mode FMODE wider than the source and at + 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) + { + /* There is no such mode. Pretend the target is wide enough. */ + fmode = GET_MODE (to); + + /* Avoid double-rounding when TO is narrower than FROM. */ + if ((significand_size (fmode) + 1) + < GET_MODE_BITSIZE (GET_MODE (from))) + { + rtx temp1; + rtx neglabel = gen_label_rtx (); + + /* Don't use TARGET if it isn't a register, is a hard register, + or is the wrong mode. */ + if (GET_CODE (target) != REG + || REGNO (target) < FIRST_PSEUDO_REGISTER + || GET_MODE (target) != fmode) + target = gen_reg_rtx (fmode); + + imode = GET_MODE (from); + do_pending_stack_adjust (); + + /* Test whether the sign bit is set. */ + emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, imode, 0, 0); + emit_jump_insn (gen_blt (neglabel)); + + /* The sign bit is not set. Convert as signed. */ + expand_float (target, from, 0); + emit_jump_insn (gen_jump (label)); + emit_barrier (); + + /* The sign bit is set. + Convert to a usable (positive signed) value by shifting right + one bit, while remembering if a nonzero bit was shifted + out; i.e., compute (from & 1) | (from >> 1). */ + + 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); + temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1, + OPTAB_LIB_WIDEN); + expand_float (target, temp, 0); + + /* Multiply by 2 to undo the shift above. */ + temp = expand_binop (fmode, add_optab, target, target, + target, 0, OPTAB_LIB_WIDEN); + if (temp != target) + emit_move_insn (target, temp); + + do_pending_stack_adjust (); + emit_label (label); + goto done; + } + } + + /* 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 + || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER) + target = gen_reg_rtx (fmode); + + /* Convert as signed integer to floating. */ + expand_float (target, from, 0); + + /* If FROM is negative (and therefore TO is negative), + correct its value by 2**bitwidth. */ + + do_pending_stack_adjust (); + emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, GET_MODE (from), 0, 0); + emit_jump_insn (gen_bge (label)); + + /* On SCO 3.2.1, ldexp rejects values outside [0.5, 1). + Rather than setting up a dconst_dot_5, let's hope SCO + fixes the bug. */ + offset = REAL_VALUE_LDEXP (dconst1, GET_MODE_BITSIZE (GET_MODE (from))); + temp = expand_binop (fmode, add_optab, target, + CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode), + target, 0, OPTAB_LIB_WIDEN); + if (temp != target) + emit_move_insn (target, temp); + + do_pending_stack_adjust (); + emit_label (label); + goto done; + } +#endif + + /* No hardware instruction available; call a library routine to convert from + SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode. */ + { + rtx libfcn; + rtx insns; + rtx value; + + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode)) + from = convert_to_mode (SImode, from, unsignedp); + + if (flag_force_mem) + from = force_not_mem (from); + + if (GET_MODE (to) == SFmode) + { + if (GET_MODE (from) == SImode) + libfcn = floatsisf_libfunc; + else if (GET_MODE (from) == DImode) + libfcn = floatdisf_libfunc; + else if (GET_MODE (from) == TImode) + libfcn = floattisf_libfunc; + else + abort (); + } + else if (GET_MODE (to) == DFmode) + { + if (GET_MODE (from) == SImode) + libfcn = floatsidf_libfunc; + else if (GET_MODE (from) == DImode) + libfcn = floatdidf_libfunc; + else if (GET_MODE (from) == TImode) + libfcn = floattidf_libfunc; + else + abort (); + } + else if (GET_MODE (to) == XFmode) + { + if (GET_MODE (from) == SImode) + libfcn = floatsixf_libfunc; + else if (GET_MODE (from) == DImode) + libfcn = floatdixf_libfunc; + else if (GET_MODE (from) == TImode) + libfcn = floattixf_libfunc; + else + abort (); + } + else if (GET_MODE (to) == TFmode) + { + if (GET_MODE (from) == SImode) + libfcn = floatsitf_libfunc; + else if (GET_MODE (from) == DImode) + libfcn = floatditf_libfunc; + else if (GET_MODE (from) == TImode) + libfcn = floattitf_libfunc; + else + abort (); + } + else + abort (); + + start_sequence (); + + value = emit_library_call_value (libfcn, NULL_RTX, 1, + GET_MODE (to), + 1, from, GET_MODE (from)); + insns = get_insns (); + end_sequence (); + + emit_libcall_block (insns, target, value, + gen_rtx (FLOAT, GET_MODE (to), from)); + } + + done: + + /* Copy result to requested destination + if we have been computing in a temp location. */ + + if (target != to) + { + if (GET_MODE (target) == GET_MODE (to)) + emit_move_insn (to, target); + else + convert_move (to, target, 0); + } +} + +/* expand_fix: generate code to convert FROM to fixed point + and store in TO. FROM must be floating point. */ + +static rtx +ftruncify (x) + rtx x; +{ + rtx temp = gen_reg_rtx (GET_MODE (x)); + return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0); +} + +void +expand_fix (to, from, unsignedp) + register rtx to, from; + int unsignedp; +{ + enum insn_code icode; + register rtx target = to; + enum machine_mode fmode, imode; + int must_trunc = 0; + rtx libfcn = 0; + + /* 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 (imode = GET_MODE (to); imode != VOIDmode; + imode = GET_MODE_WIDER_MODE (imode)) + for (fmode = GET_MODE (from); fmode != VOIDmode; + fmode = GET_MODE_WIDER_MODE (fmode)) + { + int doing_unsigned = unsignedp; + + icode = can_fix_p (imode, fmode, unsignedp, &must_trunc); + if (icode == CODE_FOR_nothing && imode != GET_MODE (to) && unsignedp) + icode = can_fix_p (imode, fmode, 0, &must_trunc), doing_unsigned = 0; + + if (icode != CODE_FOR_nothing) + { + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (fmode != GET_MODE (from)) + from = convert_to_mode (fmode, from, 0); + + if (must_trunc) + from = ftruncify (from); + + if (imode != GET_MODE (to)) + target = gen_reg_rtx (imode); + + emit_unop_insn (icode, target, from, + doing_unsigned ? UNSIGNED_FIX : FIX); + if (target != to) + convert_move (to, target, unsignedp); + return; + } + } + +#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) + /* For an unsigned conversion, there is one more way to do it. + If we have a signed conversion, we generate code that compares + the real value to the largest representable positive number. If if + is smaller, the conversion is done normally. Otherwise, subtract + one plus the highest signed number, convert, and add it back. + + We only need to check all real modes, since we know we didn't find + anything with a wider integer mode. */ + + 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)) + /* Make sure we won't lose significant bits doing this. */ + if (GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to)) + && CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, + &must_trunc)) + { + int bitsize; + REAL_VALUE_TYPE offset; + rtx limit, lab1, lab2, insn; + + bitsize = GET_MODE_BITSIZE (GET_MODE (to)); + offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1); + limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode); + lab1 = gen_label_rtx (); + lab2 = gen_label_rtx (); + + emit_queue (); + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (flag_force_mem) + from = force_not_mem (from); + + 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_insn (from, limit, GE, NULL_RTX, GET_MODE (from), 0, 0); + emit_jump_insn (gen_bge (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 ((HOST_WIDE_INT) 1 << (bitsize - 1)), + to, 1, OPTAB_LIB_WIDEN); + + if (target != to) + emit_move_insn (to, target); + + emit_label (lab2); + + /* Make a place for a REG_NOTE and add it. */ + insn = emit_move_insn (to, to); + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx (UNSIGNED_FIX, GET_MODE (to), + copy_rtx (from)), + REG_NOTES (insn)); + + return; + } +#endif + + /* 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)) + { + target = gen_reg_rtx (SImode); + + expand_fix (target, from, unsignedp); + } + else if (GET_MODE (from) == SFmode) + { + if (GET_MODE (to) == SImode) + libfcn = unsignedp ? fixunssfsi_libfunc : fixsfsi_libfunc; + else if (GET_MODE (to) == DImode) + libfcn = unsignedp ? fixunssfdi_libfunc : fixsfdi_libfunc; + else if (GET_MODE (to) == TImode) + libfcn = unsignedp ? fixunssfti_libfunc : fixsfti_libfunc; + else + abort (); + } + else if (GET_MODE (from) == DFmode) + { + if (GET_MODE (to) == SImode) + libfcn = unsignedp ? fixunsdfsi_libfunc : fixdfsi_libfunc; + else if (GET_MODE (to) == DImode) + libfcn = unsignedp ? fixunsdfdi_libfunc : fixdfdi_libfunc; + else if (GET_MODE (to) == TImode) + libfcn = unsignedp ? fixunsdfti_libfunc : fixdfti_libfunc; + else + abort (); + } + else if (GET_MODE (from) == XFmode) + { + if (GET_MODE (to) == SImode) + libfcn = unsignedp ? fixunsxfsi_libfunc : fixxfsi_libfunc; + else if (GET_MODE (to) == DImode) + libfcn = unsignedp ? fixunsxfdi_libfunc : fixxfdi_libfunc; + else if (GET_MODE (to) == TImode) + libfcn = unsignedp ? fixunsxfti_libfunc : fixxfti_libfunc; + else + abort (); + } + else if (GET_MODE (from) == TFmode) + { + if (GET_MODE (to) == SImode) + libfcn = unsignedp ? fixunstfsi_libfunc : fixtfsi_libfunc; + else if (GET_MODE (to) == DImode) + libfcn = unsignedp ? fixunstfdi_libfunc : fixtfdi_libfunc; + else if (GET_MODE (to) == TImode) + libfcn = unsignedp ? fixunstfti_libfunc : fixtfti_libfunc; + else + abort (); + } + else + abort (); + + if (libfcn) + { + rtx insns; + rtx value; + + to = protect_from_queue (to, 1); + from = protect_from_queue (from, 0); + + if (flag_force_mem) + from = force_not_mem (from); + + start_sequence (); + + value = emit_library_call_value (libfcn, NULL_RTX, 1, GET_MODE (to), + + 1, from, GET_MODE (from)); + insns = get_insns (); + end_sequence (); + + emit_libcall_block (insns, target, value, + gen_rtx (unsignedp ? UNSIGNED_FIX : FIX, + GET_MODE (to), from)); + } + + if (GET_MODE (to) == GET_MODE (target)) + emit_move_insn (to, target); + else + convert_move (to, target, 0); +} + +static optab +init_optab (code) + enum rtx_code code; +{ + int i; + optab op = (optab) xmalloc (sizeof (struct optab)); + op->code = code; + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + op->handlers[i].insn_code = CODE_FOR_nothing; + op->handlers[i].libfunc = 0; + } + + if (code != UNKNOWN) + code_to_optab[(int) code] = op; + + return op; +} + +/* 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 lower case) 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. + FIRST_MODE is the first machine mode index in the given optab to + initialize. + LAST_MODE is the last machine mode index in the given optab to + initialize. + OPNAME is the generic (string) name of the operation. + SUFFIX is the character which specifies the number of operands for + the given generic operation. +*/ + +static void +init_libfuncs (optable, first_mode, last_mode, opname, suffix) + register optab optable; + register int first_mode; + register int last_mode; + register char *opname; + register int suffix; +{ + register int mode; + register unsigned opname_len = strlen (opname); + + for (mode = first_mode; (int) mode <= (int) last_mode; + mode = (enum machine_mode) ((int) mode + 1)) + { + register char *mname = mode_name[(int) mode]; + register unsigned mname_len = strlen (mname); + register char *libfunc_name + = (char *) xmalloc (2 + opname_len + mname_len + 1 + 1); + register char *p; + register 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'; + optable->handlers[(int) mode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, libfunc_name); + } +} + +/* Initialize the libfunc fields of an entire group of entries in some + optab which correspond to all integer mode operations. The parameters + have the same meaning as similarly named ones for the `init_libfuncs' + routine. (See above). */ + +static void +init_integral_libfuncs (optable, opname, suffix) + register optab optable; + register char *opname; + register int suffix; +{ + init_libfuncs (optable, SImode, TImode, opname, suffix); +} + +/* Initialize the libfunc fields of an entire group of entries in some + optab which correspond to all real mode operations. The parameters + have the same meaning as similarly named ones for the `init_libfuncs' + routine. (See above). */ + +static void +init_floating_libfuncs (optable, opname, suffix) + register optab optable; + register char *opname; + register int suffix; +{ + init_libfuncs (optable, SFmode, TFmode, opname, suffix); +} + +/* Initialize the libfunc fields of an entire group of entries in some + optab which correspond to all complex floating modes. The parameters + have the same meaning as similarly named ones for the `init_libfuncs' + routine. (See above). */ + +static void +init_complex_libfuncs (optable, opname, suffix) + register optab optable; + register char *opname; + register int suffix; +{ + init_libfuncs (optable, SCmode, TCmode, opname, suffix); +} + +/* Call this once to initialize the contents of the optabs + appropriately for the current target machine. */ + +void +init_optabs () +{ + int i, j; + enum insn_code *p; + + /* Start by initializing all tables to contain CODE_FOR_nothing. */ + + for (p = fixtab[0][0]; + p < fixtab[0][0] + sizeof fixtab / sizeof (fixtab[0][0][0]); + p++) + *p = CODE_FOR_nothing; + + for (p = fixtrunctab[0][0]; + p < fixtrunctab[0][0] + sizeof fixtrunctab / sizeof (fixtrunctab[0][0][0]); + p++) + *p = CODE_FOR_nothing; + + for (p = floattab[0][0]; + p < floattab[0][0] + sizeof floattab / sizeof (floattab[0][0][0]); + p++) + *p = CODE_FOR_nothing; + + for (p = extendtab[0][0]; + p < extendtab[0][0] + sizeof extendtab / sizeof extendtab[0][0][0]; + p++) + *p = CODE_FOR_nothing; + + for (i = 0; i < NUM_RTX_CODE; i++) + setcc_gen_code[i] = CODE_FOR_nothing; + +#ifdef HAVE_conditional_move + for (i = 0; i < NUM_MACHINE_MODES; i++) + movcc_gen_code[i] = CODE_FOR_nothing; +#endif + + add_optab = init_optab (PLUS); + sub_optab = init_optab (MINUS); + smul_optab = init_optab (MULT); + smul_highpart_optab = init_optab (UNKNOWN); + umul_highpart_optab = init_optab (UNKNOWN); + smul_widen_optab = init_optab (UNKNOWN); + umul_widen_optab = init_optab (UNKNOWN); + sdiv_optab = init_optab (DIV); + sdivmod_optab = init_optab (UNKNOWN); + udiv_optab = init_optab (UDIV); + udivmod_optab = init_optab (UNKNOWN); + smod_optab = init_optab (MOD); + umod_optab = init_optab (UMOD); + flodiv_optab = init_optab (DIV); + ftrunc_optab = init_optab (UNKNOWN); + and_optab = init_optab (AND); + ior_optab = init_optab (IOR); + xor_optab = init_optab (XOR); + ashl_optab = init_optab (ASHIFT); + ashr_optab = init_optab (ASHIFTRT); + lshr_optab = init_optab (LSHIFTRT); + rotl_optab = init_optab (ROTATE); + rotr_optab = init_optab (ROTATERT); + smin_optab = init_optab (SMIN); + smax_optab = init_optab (SMAX); + umin_optab = init_optab (UMIN); + umax_optab = init_optab (UMAX); + mov_optab = init_optab (UNKNOWN); + movstrict_optab = init_optab (UNKNOWN); + cmp_optab = init_optab (UNKNOWN); + ucmp_optab = init_optab (UNKNOWN); + tst_optab = init_optab (UNKNOWN); + neg_optab = init_optab (NEG); + abs_optab = init_optab (ABS); + one_cmpl_optab = init_optab (NOT); + ffs_optab = init_optab (FFS); + sqrt_optab = init_optab (SQRT); + sin_optab = init_optab (UNKNOWN); + cos_optab = init_optab (UNKNOWN); + strlen_optab = init_optab (UNKNOWN); + + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + movstr_optab[i] = CODE_FOR_nothing; + +#ifdef HAVE_SECONDARY_RELOADS + reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing; +#endif + } + + /* Fill in the optabs with the insns we support. */ + init_all_optabs (); + +#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC + /* This flag says the same insns that convert to a signed fixnum + also convert validly to an unsigned one. */ + for (i = 0; i < NUM_MACHINE_MODES; i++) + for (j = 0; j < NUM_MACHINE_MODES; j++) + fixtrunctab[i][j][1] = fixtrunctab[i][j][0]; +#endif + +#ifdef EXTRA_CC_MODES + init_mov_optab (); +#endif + + /* Initialize the optabs with the names of the library functions. */ + init_integral_libfuncs (add_optab, "add", '3'); + init_floating_libfuncs (add_optab, "add", '3'); + init_integral_libfuncs (sub_optab, "sub", '3'); + init_floating_libfuncs (sub_optab, "sub", '3'); + init_integral_libfuncs (smul_optab, "mul", '3'); + init_floating_libfuncs (smul_optab, "mul", '3'); + init_integral_libfuncs (sdiv_optab, "div", '3'); + init_integral_libfuncs (udiv_optab, "udiv", '3'); + init_integral_libfuncs (sdivmod_optab, "divmod", '4'); + init_integral_libfuncs (udivmod_optab, "udivmod", '4'); + init_integral_libfuncs (smod_optab, "mod", '3'); + init_integral_libfuncs (umod_optab, "umod", '3'); + init_floating_libfuncs (flodiv_optab, "div", '3'); + init_floating_libfuncs (ftrunc_optab, "ftrunc", '2'); + init_integral_libfuncs (and_optab, "and", '3'); + init_integral_libfuncs (ior_optab, "ior", '3'); + init_integral_libfuncs (xor_optab, "xor", '3'); + init_integral_libfuncs (ashl_optab, "ashl", '3'); + init_integral_libfuncs (ashr_optab, "ashr", '3'); + init_integral_libfuncs (lshr_optab, "lshr", '3'); + init_integral_libfuncs (smin_optab, "min", '3'); + init_floating_libfuncs (smin_optab, "min", '3'); + init_integral_libfuncs (smax_optab, "max", '3'); + init_floating_libfuncs (smax_optab, "max", '3'); + init_integral_libfuncs (umin_optab, "umin", '3'); + init_integral_libfuncs (umax_optab, "umax", '3'); + init_integral_libfuncs (neg_optab, "neg", '2'); + init_floating_libfuncs (neg_optab, "neg", '2'); + init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2'); + init_integral_libfuncs (ffs_optab, "ffs", '2'); + + /* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */ + init_integral_libfuncs (cmp_optab, "cmp", '2'); + init_integral_libfuncs (ucmp_optab, "ucmp", '2'); + init_floating_libfuncs (cmp_optab, "cmp", '2'); + +#ifdef MULSI3_LIBCALL + smul_optab->handlers[(int) SImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, MULSI3_LIBCALL); +#endif +#ifdef MULDI3_LIBCALL + smul_optab->handlers[(int) DImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, MULDI3_LIBCALL); +#endif + +#ifdef DIVSI3_LIBCALL + sdiv_optab->handlers[(int) SImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, DIVSI3_LIBCALL); +#endif +#ifdef DIVDI3_LIBCALL + sdiv_optab->handlers[(int) DImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, DIVDI3_LIBCALL); +#endif + +#ifdef UDIVSI3_LIBCALL + udiv_optab->handlers[(int) SImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, UDIVSI3_LIBCALL); +#endif +#ifdef UDIVDI3_LIBCALL + udiv_optab->handlers[(int) DImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, UDIVDI3_LIBCALL); +#endif + +#ifdef MODSI3_LIBCALL + smod_optab->handlers[(int) SImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, MODSI3_LIBCALL); +#endif +#ifdef MODDI3_LIBCALL + smod_optab->handlers[(int) DImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, MODDI3_LIBCALL); +#endif + +#ifdef UMODSI3_LIBCALL + umod_optab->handlers[(int) SImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, UMODSI3_LIBCALL); +#endif +#ifdef UMODDI3_LIBCALL + umod_optab->handlers[(int) DImode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, UMODDI3_LIBCALL); +#endif + + /* Use cabs for DC complex abs, since systems generally have cabs. + Don't define any libcall for SCmode, so that cabs will be used. */ + abs_optab->handlers[(int) DCmode].libfunc + = gen_rtx (SYMBOL_REF, Pmode, "cabs"); + + /* The ffs function operates on `int'. */ +#ifndef INT_TYPE_SIZE +#define INT_TYPE_SIZE BITS_PER_WORD +#endif + ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)] .libfunc + = gen_rtx (SYMBOL_REF, Pmode, "ffs"); + + extendsfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfdf2"); + extendsfxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfxf2"); + extendsftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsftf2"); + extenddfxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extenddfxf2"); + extenddftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extenddftf2"); + + truncdfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncdfsf2"); + truncxfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncxfsf2"); + trunctfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__trunctfsf2"); + truncxfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncxfdf2"); + trunctfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__trunctfdf2"); + + memcpy_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memcpy"); + bcopy_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bcopy"); + memcmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memcmp"); + bcmp_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gcc_bcmp"); + memset_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memset"); + bzero_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bzero"); + + eqhf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqhf2"); + nehf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nehf2"); + gthf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gthf2"); + gehf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gehf2"); + lthf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lthf2"); + lehf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lehf2"); + + eqsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqsf2"); + nesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nesf2"); + gtsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtsf2"); + gesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gesf2"); + ltsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltsf2"); + lesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lesf2"); + + eqdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqdf2"); + nedf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nedf2"); + gtdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtdf2"); + gedf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gedf2"); + ltdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltdf2"); + ledf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ledf2"); + + eqxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqxf2"); + nexf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__nexf2"); + gtxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gtxf2"); + gexf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gexf2"); + ltxf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__ltxf2"); + lexf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lexf2"); + + eqtf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__eqtf2"); + netf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__netf2"); + gttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__gttf2"); + getf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__getf2"); + lttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lttf2"); + letf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__letf2"); + + floatsisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsisf"); + floatdisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdisf"); + floattisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattisf"); + + floatsidf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsidf"); + floatdidf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdidf"); + floattidf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattidf"); + + floatsixf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsixf"); + floatdixf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdixf"); + floattixf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattixf"); + + floatsitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsitf"); + floatditf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatditf"); + floattitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattitf"); + + fixsfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixsfsi"); + fixsfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixsfdi"); + fixsfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixsfti"); + + fixdfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixdfsi"); + fixdfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixdfdi"); + fixdfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixdfti"); + + fixxfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixxfsi"); + fixxfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixxfdi"); + fixxfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixxfti"); + + fixtfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixtfsi"); + fixtfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixtfdi"); + fixtfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixtfti"); + + fixunssfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunssfsi"); + fixunssfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunssfdi"); + fixunssfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunssfti"); + + fixunsdfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsdfsi"); + fixunsdfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsdfdi"); + fixunsdfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsdfti"); + + fixunsxfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsxfsi"); + fixunsxfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsxfdi"); + fixunsxfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunsxfti"); + + fixunstfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfsi"); + fixunstfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfdi"); + fixunstfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfti"); + +#ifdef INIT_TARGET_OPTABS + /* Allow the target to add more libcalls or rename some, etc. */ + INIT_TARGET_OPTABS; +#endif +} + +#ifdef BROKEN_LDEXP + +/* SCO 3.2 apparently has a broken ldexp. */ + +double +ldexp(x,n) + double x; + int n; +{ + if (n > 0) + while (n--) + x *= 2; + + return x; +} +#endif /* BROKEN_LDEXP */ |