--- /dev/null
+/* Expand the basic unary and binary arithmetic operations, for GNU compiler.
+ Copyright (C) 1987-1991 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, 675 Mass Ave, Cambridge, MA 02139, 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"
+
+/* 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_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 lshl_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 cmp_optab;
+optab ucmp_optab; /* Used only for libcalls for unsigned comparisons. */
+optab tst_optab;
+
+/* SYMBOL_REF rtx's for the library functions that are called
+ implicitly and not via optabs. */
+
+rtx extendsfdf2_libfunc;
+rtx truncdfsf2_libfunc;
+rtx memcpy_libfunc;
+rtx bcopy_libfunc;
+rtx memcmp_libfunc;
+rtx bcmp_libfunc;
+rtx memset_libfunc;
+rtx bzero_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;
+
+/* 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];
+
+static void emit_float_lib_cmp ();
+\f
+/* 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), op0);
+ else
+ note = gen_rtx (code, GET_MODE (target), op0, 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;
+}
+\f
+/* 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 mode_class class;
+ enum machine_mode wider_mode;
+ enum machine_mode submode = mode_for_size (BITS_PER_WORD, MODE_INT, 0);
+ register rtx temp;
+ int commutative_op = 0;
+ int shift_op = (binoptab->code == ASHIFT
+ || binoptab->code == ASHIFTRT
+ || binoptab->code == LSHIFT
+ || binoptab->code == LSHIFTRT
+ || binoptab->code == ROTATE
+ || binoptab->code == ROTATERT);
+ 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 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) > 2)
+ op0 = force_reg (mode, op0);
+
+ if (CONSTANT_P (op1) && preserve_subexpressions_p () && rtx_cost (op1) > 2)
+ op1 = force_reg ((shift_op
+ ? mode_for_size (BITS_PER_WORD, MODE_INT, 0)
+ : mode),
+ op1);
+
+#if 0 /* Turned off because it seems to be a kludgy method. */
+ /* If subtracting integer from pointer, and the pointer has a special mode,
+ then change it to an add. We use the add insn of Pmode for combining
+ integers with pointers, and the sub insn to subtract two pointers. */
+
+ if (binoptab == sub_optab
+ && GET_MODE (op0) == Pmode && GET_MODE (op1) != Pmode)
+ {
+ op1 = negate_rtx (GET_MODE(op1), op1);
+ binoptab = add_optab;
+ }
+#endif /* 0 */
+
+ /* 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)
+ {
+ 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)
+ xop0 = convert_to_mode (mode0, xop0, unsignedp);
+
+ if (GET_MODE (xop1) != VOIDmode
+ && GET_MODE (xop1) != mode1)
+ 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))
+ xop0 = copy_to_mode_reg (mode0, xop0);
+
+ if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+ 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, 0, unsignedp,
+ methods);
+ }
+
+ emit_insn (pat);
+ return 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) submode].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 (submode, binoptab,
+ operand_subword_force (op0, i, mode),
+ operand_subword_force (op1, i, mode),
+ target_piece, unsignedp, methods);
+ if (target_piece != x)
+ emit_move_insn (target_piece, x);
+ }
+
+ insns = get_insns ();
+ end_sequence ();
+
+ 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;
+ }
+
+ /* 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) submode].insn_code != CODE_FOR_nothing)
+ {
+ int i;
+ rtx carry_tmp = gen_reg_rtx (submode);
+ optab otheroptab = binoptab == add_optab ? sub_optab : add_optab;
+ int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD;
+ rtx carry_in, carry_out;
+
+ /* 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. */
+ op0 = force_reg (mode, op0);
+ op1 = force_reg (mode, op1);
+
+ if (target == 0 || GET_CODE (target) != REG
+ || target == op0 || target == op1)
+ target = gen_reg_rtx (mode);
+
+ /* 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 (op0, index, mode);
+ rtx op1_piece = operand_subword_force (op1, index, mode);
+ rtx x;
+
+ /* Main add/subtract of the input operands. */
+ x = expand_binop (submode, binoptab,
+ op0_piece, op1_piece,
+ target_piece, unsignedp, methods);
+ if (x == 0)
+ break;
+
+ if (i + 1 < nwords)
+ {
+ /* Store carry from main add/subtract. */
+ carry_out = gen_reg_rtx (submode);
+ carry_out = emit_store_flag (carry_out,
+ binoptab == add_optab ? LTU : GTU,
+ x, op0_piece,
+ submode, 1, normalizep);
+ if (!carry_out)
+ break;
+ }
+
+ if (i > 0)
+ {
+ /* Add/subtract previous carry to main result. */
+ x = expand_binop (submode,
+ normalizep == 1 ? binoptab : otheroptab,
+ x, carry_in,
+ target_piece, 1, methods);
+ 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,
+ submode, 1, normalizep);
+ /* Logical-ior the two poss. carry together. */
+ carry_out = expand_binop (submode, ior_optab,
+ carry_out, carry_tmp,
+ carry_out, 0, methods);
+ if (!carry_out)
+ break;
+ }
+ }
+
+ carry_in = carry_out;
+ }
+
+ if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD)
+ {
+ rtx temp;
+
+ temp = emit_move_insn (target, target);
+ REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (binoptab->code, mode, op0, op1),
+ 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) submode].insn_code != CODE_FOR_nothing
+ && add_optab->handlers[(int) submode].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;
+
+ /* 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_rtx (CONST_INT, VOIDmode, BITS_PER_WORD - 1);
+ product = expand_binop (mode, smul_widen_optab, op0_low, op1_low,
+ target, 1, OPTAB_DIRECT);
+ op0_xhigh = expand_binop (submode, lshr_optab, op0_low, wordm1,
+ 0, 1, OPTAB_DIRECT);
+ if (op0_xhigh)
+ op0_xhigh = expand_binop (submode, add_optab, op0_high, op0_xhigh,
+ op0_xhigh, 0, OPTAB_DIRECT);
+ else
+ {
+ op0_xhigh = expand_binop (submode, ashr_optab, op0_low, wordm1,
+ 0, 0, OPTAB_DIRECT);
+ if (op0_xhigh)
+ op0_xhigh = expand_binop (submode, sub_optab, op0_high,
+ op0_xhigh, op0_xhigh, 0,
+ OPTAB_DIRECT);
+ }
+
+ op1_xhigh = expand_binop (submode, lshr_optab, op1_low, wordm1,
+ 0, 1, OPTAB_DIRECT);
+ if (op1_xhigh)
+ op1_xhigh = expand_binop (SImode, add_optab, op1_high, op1_xhigh,
+ op1_xhigh, 0, OPTAB_DIRECT);
+ else
+ {
+ op1_xhigh = expand_binop (submode, ashr_optab, op1_low, wordm1,
+ 0, 0, OPTAB_DIRECT);
+ if (op1_xhigh)
+ op1_xhigh = expand_binop (SImode, sub_optab, op1_high,
+ op1_xhigh, op1_xhigh, 0,
+ OPTAB_DIRECT);
+ }
+ }
+
+ /* 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_piece;
+ rtx product_high = operand_subword (product, high, 1, mode);
+ rtx temp = expand_binop (submode, binoptab, op0_low, op1_xhigh, 0,
+ 0, OPTAB_DIRECT);
+
+ if (temp)
+ {
+ product_piece = expand_binop (submode, add_optab, temp,
+ product_high, product_high,
+ 0, OPTAB_LIB_WIDEN);
+ if (product_piece != product_high)
+ emit_move_insn (product_high, product_piece);
+
+ temp = expand_binop (submode, binoptab, op1_low, op0_xhigh, 0,
+ 0, OPTAB_DIRECT);
+
+ product_piece = expand_binop (submode, add_optab, temp,
+ product_high, product_high,
+ 0, OPTAB_LIB_WIDEN);
+ if (product_piece != product_high)
+ emit_move_insn (product_high, product_piece);
+
+ temp = emit_move_insn (product, product);
+ REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL,
+ gen_rtx (MULT, mode, op0, 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);
+ }
+
+ /* 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;
+
+ start_sequence ();
+
+ /* Pass 1 for NO_QUEUE so we don't lose any increments
+ if the libcall is cse'd or moved. */
+ emit_library_call (binoptab->handlers[(int) mode].libfunc,
+ 1, mode, 2, op0, mode, op1,
+ (shift_op
+ ? mode_for_size (BITS_PER_WORD, MODE_INT, 0)
+ : mode));
+
+ insns = get_insns ();
+ end_sequence ();
+
+ target = gen_reg_rtx (mode);
+ emit_libcall_block (insns, target, hard_libcall_value (mode),
+ 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))
+ return 0; /* Caller says, don't even try. */
+
+ /* 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);
+
+ /* Widening is now independent of specific machine modes.
+ It is assumed that widening may be performed to any
+ higher numbered mode in the same mode class. */
+
+ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT)
+ {
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ ((int) wider_mode < (int) MAX_MACHINE_MODE
+ && GET_MODE_CLASS (wider_mode) == class);
+ 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 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 || binoptab == lshl_optab)
+ no_extend = 1;
+
+ if (GET_MODE (xop0) != VOIDmode
+ && GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+ {
+ if (no_extend)
+ {
+ temp = force_reg (GET_MODE (xop0), xop0);
+ xop0 = gen_rtx (SUBREG, wider_mode, temp, 0);
+ }
+ else
+ {
+ temp = gen_reg_rtx (wider_mode);
+ convert_move (temp, xop0, unsignedp);
+ xop0 = temp;
+ }
+ }
+ if (GET_MODE (xop1) != VOIDmode
+ && GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+ {
+ if (no_extend)
+ {
+ temp = force_reg (GET_MODE (xop1), xop1);
+ xop1 = gen_rtx (SUBREG, wider_mode, temp, 0);
+ }
+ else
+ {
+ temp = gen_reg_rtx (wider_mode);
+ convert_move (temp, xop1, unsignedp);
+ xop1 = temp;
+ }
+ }
+
+ temp = expand_binop (wider_mode, binoptab, xop0, xop1, 0,
+ unsignedp, methods);
+ if (temp)
+ {
+ if (class == MODE_FLOAT)
+ {
+ 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);
+ }
+ }
+ }
+
+ return 0;
+}
+\f
+/* 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;
+}
+\f
+/* 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 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) > 2)
+ op0 = force_reg (mode, op0);
+
+ if (CONSTANT_P (op1) && preserve_subexpressions_p () && rtx_cost (op1) > 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);
+ GET_MODE_CLASS (wider_mode) == class;
+ 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_to_mode (wider_mode, op0,
+ unsignedp),
+ convert_to_mode (wider_mode, op1,
+ unsignedp),
+ t0, t1, unsignedp))
+ {
+ convert_move (targ0, t0, unsignedp);
+ convert_move (targ1, t1, unsignedp);
+ return 1;
+ }
+ else
+ delete_insns_since (last);
+ }
+ }
+ }
+
+ return 0;
+}
+\f
+/* 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;
+ enum machine_mode submode = mode_for_size (BITS_PER_WORD, MODE_INT, 0);
+ 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, 0))
+ {
+ delete_insns_since (last);
+ return expand_unop (mode, unoptab, op0, 0, unsignedp);
+ }
+
+ emit_insn (pat);
+
+ return 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) submode].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 (submode, 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, 0,
+ gen_rtx (unoptab->code, mode, op0));
+ return target;
+ }
+
+ if (unoptab->handlers[(int) mode].libfunc)
+ {
+ rtx insns;
+ rtx funexp = unoptab->handlers[(int) mode].libfunc;
+
+ start_sequence ();
+
+ /* Pass 1 for NO_QUEUE so we don't lose any increments
+ if the libcall is cse'd or moved. */
+ emit_library_call (unoptab->handlers[(int) mode].libfunc,
+ 1, mode, 1, op0, mode);
+ insns = get_insns ();
+ end_sequence ();
+
+ target = gen_reg_rtx (mode);
+ emit_libcall_block (insns, target, hard_libcall_value (mode),
+ 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);
+ GET_MODE_CLASS (wider_mode) == class;
+ 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)
+ {
+ if (GET_MODE (op0) != VOIDmode
+ && GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+ {
+ temp = gen_reg_rtx (wider_mode);
+ convert_move (temp, op0, unsignedp);
+ op0 = temp;
+ }
+
+ target = expand_unop (wider_mode, unoptab, op0, 0, unsignedp);
+ if (class == MODE_FLOAT)
+ {
+ if (target == 0)
+ target = gen_reg_rtx (mode);
+ convert_move (target, temp, 0);
+ return target;
+ }
+ else
+ return gen_lowpart (mode, target);
+ }
+ }
+ }
+
+ return 0;
+}
+\f
+/* 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, 0);
+
+ emit_insn (pat);
+
+ if (temp != target)
+ emit_move_insn (target, temp);
+}
+\f
+/* Emit code to perform a series of operations on a multi-word quantity, one
+ word at a time.
+
+ Such a block is preceeded 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. Only
+ INSNs are allowed in the block; no library calls or jumps may be
+ present.
+
+ 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.
+
+ 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);
+
+ /* 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 (insn) != INSN)
+ abort ();
+
+ 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));
+ }
+
+ last = emit_move_insn (target, target);
+ if (equiv)
+ REG_NOTES (last) = gen_rtx (EXPR_LIST, REG_EQUAL, 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));
+
+ return last;
+}
+\f
+/* 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.
+
+ 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. */
+
+ 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)
+ {
+ 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, 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));
+}
+\f
+/* 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;
+ 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) > 2)
+ x = force_reg (mode, x);
+
+ if (CONSTANT_P (y) && preserve_subexpressions_p () && rtx_cost (y) > 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_rtx (CONST_INT, VOIDmode, align)));
+ emit_cmp_insn (result, const0_rtx, comparison, 0, 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_rtx (CONST_INT, VOIDmode, align)));
+ emit_cmp_insn (result, const0_rtx, comparison, 0, 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);
+ emit_insn (gen_cmpstrsi (result, x, y,
+ convert_to_mode (SImode, size, 1),
+ gen_rtx (CONST_INT, VOIDmode, align)));
+ emit_cmp_insn (result, const0_rtx, comparison, 0, 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, 0,
+ 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);
+ GET_MODE_CLASS (wider_mode) == class;
+ wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ {
+ if (cmp_optab->handlers[(int) wider_mode].insn_code
+ != CODE_FOR_nothing)
+ {
+ x = convert_to_mode (wider_mode, x, unsignedp);
+ y = convert_to_mode (wider_mode, y, unsignedp);
+ emit_cmp_insn (x, y, comparison, 0,
+ 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, 0,
+ SImode, 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 (SImode), const1_rtx,
+ comparison, 0, SImode, 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;
+}
+\f
+/* Emit a library call comparison between floating point X and Y.
+ COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). */
+
+static void
+emit_float_lib_cmp (x, y, comparison)
+ rtx x, y;
+ enum rtx_code comparison;
+{
+ enum machine_mode mode = GET_MODE (x);
+ rtx libfunc;
+
+ 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
+ {
+ enum machine_mode wider_mode;
+
+ for (wider_mode = GET_MODE_WIDER_MODE (mode);
+ GET_MODE_CLASS (wider_mode) == MODE_FLOAT;
+ 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 = convert_to_mode (wider_mode, x, 0);
+ y = convert_to_mode (wider_mode, y, 0);
+ emit_float_lib_cmp (x, y, comparison);
+ return;
+ }
+ }
+ abort ();
+ }
+
+ emit_library_call (libfunc, 0,
+ SImode, 2, x, mode, y, mode);
+
+ emit_cmp_insn (hard_libcall_value (SImode), const0_rtx, comparison,
+ 0, SImode, 0, 0);
+}
+\f
+/* 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, VOIDmode)))
+ loc = copy_to_mode_reg (insn_operand_mode[(int)CODE_FOR_indirect_jump][0],
+ loc);
+
+ emit_jump_insn (gen_indirect_jump (loc));
+}
+\f
+/* 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. */
+
+rtx
+gen_move_insn (x, y)
+ rtx x, y;
+{
+ register enum machine_mode mode = GET_MODE (x);
+ enum insn_code insn_code;
+
+ 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 (insn_code == CODE_FOR_nothing)
+ {
+ enum machine_mode tmode = VOIDmode;
+ rtx x1 = x, y1 = y;
+
+ if (GET_MODE_CLASS (mode) == MODE_CC && mode != CCmode
+ && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing)
+ tmode = CCmode;
+ else if (GET_MODE_CLASS (mode) == MODE_CC)
+ 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));
+}
+\f
+/* Tables of patterns for extending one integer mode to another. */
+static enum insn_code zero_extend_codes[MAX_MACHINE_MODE][MAX_MACHINE_MODE];
+static enum insn_code sign_extend_codes[MAX_MACHINE_MODE][MAX_MACHINE_MODE];
+
+/* Return nonzero if it's possible to extend FROM_MODE to TO_MODE.
+ UNSIGNEDP specifies zero-extension instead of sign-extension.
+
+ Actually, the value is the instruction code for the extension pattern. */
+
+int
+can_extend_p (to_mode, from_mode, unsignedp)
+ enum machine_mode to_mode, from_mode;
+ int unsignedp;
+{
+ return ((unsignedp ? zero_extend_codes : sign_extend_codes)
+ [(int) to_mode][(int) from_mode]);
+}
+
+/* Generate the body of an insn to extend Y (with mode MFROM)
+ into X (with mode MTO). Do zero-extension if UNSIGNEDP is nonzero. */
+
+rtx
+gen_extend_insn (x, y, mto, mfrom, unsignedp)
+ rtx x, y;
+ enum machine_mode mto, mfrom;
+ int unsignedp;
+{
+ return (GEN_FCN ((unsignedp ? zero_extend_codes : sign_extend_codes)
+ [(int)mto][(int)mfrom])
+ (x, y));
+}
+
+static void
+init_extends ()
+{
+ bzero (sign_extend_codes, sizeof sign_extend_codes);
+ bzero (zero_extend_codes, sizeof zero_extend_codes);
+
+#ifdef HAVE_extendditi2
+ if (HAVE_extendditi2)
+ sign_extend_codes[(int) TImode][(int) DImode] = CODE_FOR_extendditi2;
+#endif
+#ifdef HAVE_extendsiti2
+ if (HAVE_extendsiti2)
+ sign_extend_codes[(int) TImode][(int) SImode] = CODE_FOR_extendsiti2;
+#endif
+#ifdef HAVE_extendhiti2
+ if (HAVE_extendhiti2)
+ sign_extend_codes[(int) TImode][(int) HImode] = CODE_FOR_extendhiti2;
+#endif
+#ifdef HAVE_extendqiti2
+ if (HAVE_extendqiti2)
+ sign_extend_codes[(int) TImode][(int) QImode] = CODE_FOR_extendqiti2;
+#endif
+#ifdef HAVE_extendsidi2
+ if (HAVE_extendsidi2)
+ sign_extend_codes[(int) DImode][(int) SImode] = CODE_FOR_extendsidi2;
+#endif
+#ifdef HAVE_extendhidi2
+ if (HAVE_extendhidi2)
+ sign_extend_codes[(int) DImode][(int) HImode] = CODE_FOR_extendhidi2;
+#endif
+#ifdef HAVE_extendqidi2
+ if (HAVE_extendqidi2)
+ sign_extend_codes[(int) DImode][(int) QImode] = CODE_FOR_extendqidi2;
+#endif
+#ifdef HAVE_extendhisi2
+ if (HAVE_extendhisi2)
+ sign_extend_codes[(int) SImode][(int) HImode] = CODE_FOR_extendhisi2;
+#endif
+#ifdef HAVE_extendqisi2
+ if (HAVE_extendqisi2)
+ sign_extend_codes[(int) SImode][(int) QImode] = CODE_FOR_extendqisi2;
+#endif
+#ifdef HAVE_extendqihi2
+ if (HAVE_extendqihi2)
+ sign_extend_codes[(int) HImode][(int) QImode] = CODE_FOR_extendqihi2;
+#endif
+
+#ifdef HAVE_zero_extendditi2
+ if (HAVE_zero_extendsiti2)
+ zero_extend_codes[(int) TImode][(int) DImode] = CODE_FOR_zero_extendditi2;
+#endif
+#ifdef HAVE_zero_extendsiti2
+ if (HAVE_zero_extendsiti2)
+ zero_extend_codes[(int) TImode][(int) SImode] = CODE_FOR_zero_extendsiti2;
+#endif
+#ifdef HAVE_zero_extendhiti2
+ if (HAVE_zero_extendhiti2)
+ zero_extend_codes[(int) TImode][(int) HImode] = CODE_FOR_zero_extendhiti2;
+#endif
+#ifdef HAVE_zero_extendqiti2
+ if (HAVE_zero_extendqiti2)
+ zero_extend_codes[(int) TImode][(int) QImode] = CODE_FOR_zero_extendqiti2;
+#endif
+#ifdef HAVE_zero_extendsidi2
+ if (HAVE_zero_extendsidi2)
+ zero_extend_codes[(int) DImode][(int) SImode] = CODE_FOR_zero_extendsidi2;
+#endif
+#ifdef HAVE_zero_extendhidi2
+ if (HAVE_zero_extendhidi2)
+ zero_extend_codes[(int) DImode][(int) HImode] = CODE_FOR_zero_extendhidi2;
+#endif
+#ifdef HAVE_zero_extendqidi2
+ if (HAVE_zero_extendqidi2)
+ zero_extend_codes[(int) DImode][(int) QImode] = CODE_FOR_zero_extendqidi2;
+#endif
+#ifdef HAVE_zero_extendhisi2
+ if (HAVE_zero_extendhisi2)
+ zero_extend_codes[(int) SImode][(int) HImode] = CODE_FOR_zero_extendhisi2;
+#endif
+#ifdef HAVE_zero_extendqisi2
+ if (HAVE_zero_extendqisi2)
+ zero_extend_codes[(int) SImode][(int) QImode] = CODE_FOR_zero_extendqisi2;
+#endif
+#ifdef HAVE_zero_extendqihi2
+ if (HAVE_zero_extendqihi2)
+ zero_extend_codes[(int) HImode][(int) QImode] = CODE_FOR_zero_extendqihi2;
+#endif
+}
+\f
+/* 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. */
+
+static enum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+static enum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+static enum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2];
+
+/* *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];
+}
+
+void
+init_fixtab ()
+{
+ enum insn_code *p;
+ 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;
+
+#ifdef HAVE_fixsfqi2
+ if (HAVE_fixsfqi2)
+ fixtab[(int) SFmode][(int) QImode][0] = CODE_FOR_fixsfqi2;
+#endif
+#ifdef HAVE_fixsfhi2
+ if (HAVE_fixsfhi2)
+ fixtab[(int) SFmode][(int) HImode][0] = CODE_FOR_fixsfhi2;
+#endif
+#ifdef HAVE_fixsfsi2
+ if (HAVE_fixsfsi2)
+ fixtab[(int) SFmode][(int) SImode][0] = CODE_FOR_fixsfsi2;
+#endif
+#ifdef HAVE_fixsfdi2
+ if (HAVE_fixsfdi2)
+ fixtab[(int) SFmode][(int) DImode][0] = CODE_FOR_fixsfdi2;
+#endif
+
+#ifdef HAVE_fixdfqi2
+ if (HAVE_fixdfqi2)
+ fixtab[(int) DFmode][(int) QImode][0] = CODE_FOR_fixdfqi2;
+#endif
+#ifdef HAVE_fixdfhi2
+ if (HAVE_fixdfhi2)
+ fixtab[(int) DFmode][(int) HImode][0] = CODE_FOR_fixdfhi2;
+#endif
+#ifdef HAVE_fixdfsi2
+ if (HAVE_fixdfsi2)
+ fixtab[(int) DFmode][(int) SImode][0] = CODE_FOR_fixdfsi2;
+#endif
+#ifdef HAVE_fixdfdi2
+ if (HAVE_fixdfdi2)
+ fixtab[(int) DFmode][(int) DImode][0] = CODE_FOR_fixdfdi2;
+#endif
+#ifdef HAVE_fixdfti2
+ if (HAVE_fixdfti2)
+ fixtab[(int) DFmode][(int) TImode][0] = CODE_FOR_fixdfti2;
+#endif
+
+#ifdef HAVE_fixtfqi2
+ if (HAVE_fixtfqi2)
+ fixtab[(int) TFmode][(int) QImode][0] = CODE_FOR_fixtfqi2;
+#endif
+#ifdef HAVE_fixtfhi2
+ if (HAVE_fixtfhi2)
+ fixtab[(int) TFmode][(int) HImode][0] = CODE_FOR_fixtfhi2;
+#endif
+#ifdef HAVE_fixtfsi2
+ if (HAVE_fixtfsi2)
+ fixtab[(int) TFmode][(int) SImode][0] = CODE_FOR_fixtfsi2;
+#endif
+#ifdef HAVE_fixtfdi2
+ if (HAVE_fixtfdi2)
+ fixtab[(int) TFmode][(int) DImode][0] = CODE_FOR_fixtfdi2;
+#endif
+#ifdef HAVE_fixtfti2
+ if (HAVE_fixtfti2)
+ fixtab[(int) TFmode][(int) TImode][0] = CODE_FOR_fixtfti2;
+#endif
+
+#ifdef HAVE_fixunssfqi2
+ if (HAVE_fixunssfqi2)
+ fixtab[(int) SFmode][(int) QImode][1] = CODE_FOR_fixunssfqi2;
+#endif
+#ifdef HAVE_fixunssfhi2
+ if (HAVE_fixunssfhi2)
+ fixtab[(int) SFmode][(int) HImode][1] = CODE_FOR_fixunssfhi2;
+#endif
+#ifdef HAVE_fixunssfsi2
+ if (HAVE_fixunssfsi2)
+ fixtab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixunssfsi2;
+#endif
+#ifdef HAVE_fixunssfdi2
+ if (HAVE_fixunssfdi2)
+ fixtab[(int) SFmode][(int) DImode][1] = CODE_FOR_fixunssfdi2;
+#endif
+
+#ifdef HAVE_fixunsdfqi2
+ if (HAVE_fixunsdfqi2)
+ fixtab[(int) DFmode][(int) QImode][1] = CODE_FOR_fixunsdfqi2;
+#endif
+#ifdef HAVE_fixunsdfhi2
+ if (HAVE_fixunsdfhi2)
+ fixtab[(int) DFmode][(int) HImode][1] = CODE_FOR_fixunsdfhi2;
+#endif
+#ifdef HAVE_fixunsdfsi2
+ if (HAVE_fixunsdfsi2)
+ fixtab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixunsdfsi2;
+#endif
+#ifdef HAVE_fixunsdfdi2
+ if (HAVE_fixunsdfdi2)
+ fixtab[(int) DFmode][(int) DImode][1] = CODE_FOR_fixunsdfdi2;
+#endif
+#ifdef HAVE_fixunsdfti2
+ if (HAVE_fixunsdfti2)
+ fixtab[(int) DFmode][(int) TImode][1] = CODE_FOR_fixunsdfti2;
+#endif
+
+#ifdef HAVE_fixunstfqi2
+ if (HAVE_fixunstfqi2)
+ fixtab[(int) TFmode][(int) QImode][1] = CODE_FOR_fixunstfqi2;
+#endif
+#ifdef HAVE_fixunstfhi2
+ if (HAVE_fixunstfhi2)
+ fixtab[(int) TFmode][(int) HImode][1] = CODE_FOR_fixunstfhi2;
+#endif
+#ifdef HAVE_fixunstfsi2
+ if (HAVE_fixunstfsi2)
+ fixtab[(int) TFmode][(int) SImode][1] = CODE_FOR_fixunstfsi2;
+#endif
+#ifdef HAVE_fixunstfdi2
+ if (HAVE_fixunstfdi2)
+ fixtab[(int) TFmode][(int) DImode][1] = CODE_FOR_fixunstfdi2;
+#endif
+#ifdef HAVE_fixunstfti2
+ if (HAVE_fixunstfti2)
+ fixtab[(int) TFmode][(int) TImode][1] = CODE_FOR_fixunstfti2;
+#endif
+
+#ifdef HAVE_fix_truncsfqi2
+ if (HAVE_fix_truncsfqi2)
+ fixtrunctab[(int) SFmode][(int) QImode][0] = CODE_FOR_fix_truncsfqi2;
+#endif
+#ifdef HAVE_fix_truncsfhi2
+ if (HAVE_fix_truncsfhi2)
+ fixtrunctab[(int) SFmode][(int) HImode][0] = CODE_FOR_fix_truncsfhi2;
+#endif
+#ifdef HAVE_fix_truncsfsi2
+ if (HAVE_fix_truncsfsi2)
+ fixtrunctab[(int) SFmode][(int) SImode][0] = CODE_FOR_fix_truncsfsi2;
+#endif
+#ifdef HAVE_fix_truncsfdi2
+ if (HAVE_fix_truncsfdi2)
+ fixtrunctab[(int) SFmode][(int) DImode][0] = CODE_FOR_fix_truncsfdi2;
+#endif
+
+#ifdef HAVE_fix_truncdfqi2
+ if (HAVE_fix_truncdfsi2)
+ fixtrunctab[(int) DFmode][(int) QImode][0] = CODE_FOR_fix_truncdfqi2;
+#endif
+#ifdef HAVE_fix_truncdfhi2
+ if (HAVE_fix_truncdfhi2)
+ fixtrunctab[(int) DFmode][(int) HImode][0] = CODE_FOR_fix_truncdfhi2;
+#endif
+#ifdef HAVE_fix_truncdfsi2
+ if (HAVE_fix_truncdfsi2)
+ fixtrunctab[(int) DFmode][(int) SImode][0] = CODE_FOR_fix_truncdfsi2;
+#endif
+#ifdef HAVE_fix_truncdfdi2
+ if (HAVE_fix_truncdfdi2)
+ fixtrunctab[(int) DFmode][(int) DImode][0] = CODE_FOR_fix_truncdfdi2;
+#endif
+#ifdef HAVE_fix_truncdfti2
+ if (HAVE_fix_truncdfti2)
+ fixtrunctab[(int) DFmode][(int) TImode][0] = CODE_FOR_fix_truncdfti2;
+#endif
+
+#ifdef HAVE_fix_trunctfqi2
+ if (HAVE_fix_trunctfqi2)
+ fixtrunctab[(int) TFmode][(int) QImode][0] = CODE_FOR_fix_trunctfqi2;
+#endif
+#ifdef HAVE_fix_trunctfhi2
+ if (HAVE_fix_trunctfhi2)
+ fixtrunctab[(int) TFmode][(int) HImode][0] = CODE_FOR_fix_trunctfhi2;
+#endif
+#ifdef HAVE_fix_trunctfsi2
+ if (HAVE_fix_trunctfsi2)
+ fixtrunctab[(int) TFmode][(int) SImode][0] = CODE_FOR_fix_trunctfsi2;
+#endif
+#ifdef HAVE_fix_trunctfdi2
+ if (HAVE_fix_trunctfdi2)
+ fixtrunctab[(int) TFmode][(int) DImode][0] = CODE_FOR_fix_trunctfdi2;
+#endif
+#ifdef HAVE_fix_trunctfti2
+ if (HAVE_fix_trunctfti2)
+ fixtrunctab[(int) TFmode][(int) TImode][0] = CODE_FOR_fix_trunctfti2;
+#endif
+
+#ifdef HAVE_fixuns_truncsfqi2
+ if (HAVE_fixuns_truncsfqi2)
+ fixtrunctab[(int) SFmode][(int) QImode][1] = CODE_FOR_fixuns_truncsfqi2;
+#endif
+#ifdef HAVE_fixuns_truncsfhi2
+ if (HAVE_fixuns_truncsfhi2)
+ fixtrunctab[(int) SFmode][(int) HImode][1] = CODE_FOR_fixuns_truncsfhi2;
+#endif
+#ifdef HAVE_fixuns_truncsfsi2
+ if (HAVE_fixuns_truncsfsi2)
+ fixtrunctab[(int) SFmode][(int) SImode][1] = CODE_FOR_fixuns_truncsfsi2;
+#endif
+#ifdef HAVE_fixuns_truncsfdi2
+ if (HAVE_fixuns_truncsfdi2)
+ fixtrunctab[(int) SFmode][(int) DImode][1] = CODE_FOR_fixuns_truncsfdi2;
+#endif
+
+#ifdef HAVE_fixuns_truncdfqi2
+ if (HAVE_fixuns_truncdfqi2)
+ fixtrunctab[(int) DFmode][(int) QImode][1] = CODE_FOR_fixuns_truncdfqi2;
+#endif
+#ifdef HAVE_fixuns_truncdfhi2
+ if (HAVE_fixuns_truncdfhi2)
+ fixtrunctab[(int) DFmode][(int) HImode][1] = CODE_FOR_fixuns_truncdfhi2;
+#endif
+#ifdef HAVE_fixuns_truncdfsi2
+ if (HAVE_fixuns_truncdfsi2)
+ fixtrunctab[(int) DFmode][(int) SImode][1] = CODE_FOR_fixuns_truncdfsi2;
+#endif
+#ifdef HAVE_fixuns_truncdfdi2
+ if (HAVE_fixuns_truncdfdi2)
+ fixtrunctab[(int) DFmode][(int) DImode][1] = CODE_FOR_fixuns_truncdfdi2;
+#endif
+#ifdef HAVE_fixuns_truncdfti2
+ if (HAVE_fixuns_truncdfti2)
+ fixtrunctab[(int) DFmode][(int) TImode][1] = CODE_FOR_fixuns_truncdfti2;
+#endif
+
+#ifdef HAVE_fixuns_trunctfqi2
+ if (HAVE_fixuns_trunctfqi2)
+ fixtrunctab[(int) TFmode][(int) QImode][1] = CODE_FOR_fixuns_trunctfqi2;
+#endif
+#ifdef HAVE_fixuns_trunctfhi2
+ if (HAVE_fixuns_trunctfhi2)
+ fixtrunctab[(int) TFmode][(int) HImode][1] = CODE_FOR_fixuns_trunctfhi2;
+#endif
+#ifdef HAVE_fixuns_trunctfsi2
+ if (HAVE_fixuns_trunctfsi2)
+ fixtrunctab[(int) TFmode][(int) SImode][1] = CODE_FOR_fixuns_trunctfsi2;
+#endif
+#ifdef HAVE_fixuns_trunctfdi2
+ if (HAVE_fixuns_trunctfdi2)
+ fixtrunctab[(int) TFmode][(int) DImode][1] = CODE_FOR_fixuns_trunctfdi2;
+#endif
+#ifdef HAVE_fixuns_trunctfti2
+ if (HAVE_fixuns_trunctfti2)
+ fixtrunctab[(int) TFmode][(int) TImode][1] = CODE_FOR_fixuns_trunctfti2;
+#endif
+
+#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. */
+ {
+ int i;
+ int j;
+ 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
+}
+
+void
+init_floattab ()
+{
+ enum insn_code *p;
+ for (p = floattab[0][0];
+ p < floattab[0][0] + sizeof floattab / sizeof (floattab[0][0][0]);
+ p++)
+ *p = CODE_FOR_nothing;
+
+#ifdef HAVE_floatqisf2
+ if (HAVE_floatqisf2)
+ floattab[(int) SFmode][(int) QImode][0] = CODE_FOR_floatqisf2;
+#endif
+#ifdef HAVE_floathisf2
+ if (HAVE_floathisf2)
+ floattab[(int) SFmode][(int) HImode][0] = CODE_FOR_floathisf2;
+#endif
+#ifdef HAVE_floatsisf2
+ if (HAVE_floatsisf2)
+ floattab[(int) SFmode][(int) SImode][0] = CODE_FOR_floatsisf2;
+#endif
+#ifdef HAVE_floatdisf2
+ if (HAVE_floatdisf2)
+ floattab[(int) SFmode][(int) DImode][0] = CODE_FOR_floatdisf2;
+#endif
+#ifdef HAVE_floattisf2
+ if (HAVE_floattisf2)
+ floattab[(int) SFmode][(int) TImode][0] = CODE_FOR_floattisf2;
+#endif
+
+#ifdef HAVE_floatqidf2
+ if (HAVE_floatqidf2)
+ floattab[(int) DFmode][(int) QImode][0] = CODE_FOR_floatqidf2;
+#endif
+#ifdef HAVE_floathidf2
+ if (HAVE_floathidf2)
+ floattab[(int) DFmode][(int) HImode][0] = CODE_FOR_floathidf2;
+#endif
+#ifdef HAVE_floatsidf2
+ if (HAVE_floatsidf2)
+ floattab[(int) DFmode][(int) SImode][0] = CODE_FOR_floatsidf2;
+#endif
+#ifdef HAVE_floatdidf2
+ if (HAVE_floatdidf2)
+ floattab[(int) DFmode][(int) DImode][0] = CODE_FOR_floatdidf2;
+#endif
+#ifdef HAVE_floattidf2
+ if (HAVE_floattidf2)
+ floattab[(int) DFmode][(int) TImode][0] = CODE_FOR_floattidf2;
+#endif
+
+#ifdef HAVE_floatqitf2
+ if (HAVE_floatqitf2)
+ floattab[(int) TFmode][(int) QImode][0] = CODE_FOR_floatqitf2;
+#endif
+#ifdef HAVE_floathitf2
+ if (HAVE_floathitf2)
+ floattab[(int) TFmode][(int) HImode][0] = CODE_FOR_floathitf2;
+#endif
+#ifdef HAVE_floatsitf2
+ if (HAVE_floatsitf2)
+ floattab[(int) TFmode][(int) SImode][0] = CODE_FOR_floatsitf2;
+#endif
+#ifdef HAVE_floatditf2
+ if (HAVE_floatditf2)
+ floattab[(int) TFmode][(int) DImode][0] = CODE_FOR_floatditf2;
+#endif
+#ifdef HAVE_floattitf2
+ if (HAVE_floattitf2)
+ floattab[(int) TFmode][(int) TImode][0] = CODE_FOR_floattitf2;
+#endif
+
+#ifdef HAVE_floatunsqisf2
+ if (HAVE_floatunsqisf2)
+ floattab[(int) SFmode][(int) QImode][1] = CODE_FOR_floatunsqisf2;
+#endif
+#ifdef HAVE_floatunshisf2
+ if (HAVE_floatunshisf2)
+ floattab[(int) SFmode][(int) HImode][1] = CODE_FOR_floatunshisf2;
+#endif
+#ifdef HAVE_floatunssisf2
+ if (HAVE_floatunssisf2)
+ floattab[(int) SFmode][(int) SImode][1] = CODE_FOR_floatunssisf2;
+#endif
+#ifdef HAVE_floatunsdisf2
+ if (HAVE_floatunsdisf2)
+ floattab[(int) SFmode][(int) DImode][1] = CODE_FOR_floatunsdisf2;
+#endif
+#ifdef HAVE_floatunstisf2
+ if (HAVE_floatunstisf2)
+ floattab[(int) SFmode][(int) TImode][1] = CODE_FOR_floatunstisf2;
+#endif
+
+#ifdef HAVE_floatunsqidf2
+ if (HAVE_floatunsqidf2)
+ floattab[(int) DFmode][(int) QImode][1] = CODE_FOR_floatunsqidf2;
+#endif
+#ifdef HAVE_floatunshidf2
+ if (HAVE_floatunshidf2)
+ floattab[(int) DFmode][(int) HImode][1] = CODE_FOR_floatunshidf2;
+#endif
+#ifdef HAVE_floatunssidf2
+ if (HAVE_floatunssidf2)
+ floattab[(int) DFmode][(int) SImode][1] = CODE_FOR_floatunssidf2;
+#endif
+#ifdef HAVE_floatunsdidf2
+ if (HAVE_floatunsdidf2)
+ floattab[(int) DFmode][(int) DImode][1] = CODE_FOR_floatunsdidf2;
+#endif
+#ifdef HAVE_floatunstidf2
+ if (HAVE_floatunstidf2)
+ floattab[(int) DFmode][(int) TImode][1] = CODE_FOR_floatunstidf2;
+#endif
+
+#ifdef HAVE_floatunsqitf2
+ if (HAVE_floatunsqitf2)
+ floattab[(int) TFmode][(int) QImode][1] = CODE_FOR_floatunsqitf2;
+#endif
+#ifdef HAVE_floatunshitf2
+ if (HAVE_floatunshitf2)
+ floattab[(int) TFmode][(int) HImode][1] = CODE_FOR_floatunshitf2;
+#endif
+#ifdef HAVE_floatunssitf2
+ if (HAVE_floatunssitf2)
+ floattab[(int) TFmode][(int) SImode][1] = CODE_FOR_floatunssitf2;
+#endif
+#ifdef HAVE_floatunsditf2
+ if (HAVE_floatunsditf2)
+ floattab[(int) TFmode][(int) DImode][1] = CODE_FOR_floatunsditf2;
+#endif
+#ifdef HAVE_floatunstitf2
+ if (HAVE_floatunstitf2)
+ floattab[(int) TFmode][(int) TImode][1] = CODE_FOR_floatunstitf2;
+#endif
+}
+\f
+/* Generate code to convert FROM to floating point
+ and store in TO. FROM must be fixed point.
+ 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;
+
+ /* 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);
+
+ if (imode != GET_MODE (from))
+ from = convert_to_mode (imode, from, unsignedp);
+ else
+ from = protect_from_queue (from, 0);
+
+ 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);
+
+ /* If we are about to do some arithmetic to correct for an
+ unsigned operand, do it in a pseudo-register. */
+
+ if (GET_CODE (to) != REG || REGNO (to) <= LAST_VIRTUAL_REGISTER)
+ target = gen_reg_rtx (GET_MODE (to));
+
+ /* 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, 0, 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 (GET_MODE (to), add_optab, target,
+ immed_real_const_1 (offset, GET_MODE (to)),
+ target, 0, OPTAB_LIB_WIDEN);
+ if (temp != target)
+ emit_move_insn (target, temp);
+ do_pending_stack_adjust ();
+ emit_label (label);
+ }
+ else
+#endif
+
+ /* No hardware instruction available; call a library
+ to convert from SImode or DImode into SFmode or DFmode. */
+ {
+ char *fnname;
+ rtx insns;
+
+ to = protect_from_queue (to, 1);
+
+ if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode))
+ from = convert_to_mode (SImode, from, unsignedp);
+ else
+ from = protect_from_queue (from, 0);
+
+ if (flag_force_mem)
+ from = force_not_mem (from);
+
+ if (GET_MODE (to) == SFmode)
+ {
+ if (GET_MODE (from) == SImode)
+ fnname = "__floatsisf";
+ else if (GET_MODE (from) == DImode)
+ fnname = "__floatdisf";
+ else
+ abort ();
+ }
+ else if (GET_MODE (to) == DFmode)
+ {
+ if (GET_MODE (from) == SImode)
+ fnname = "__floatsidf";
+ else if (GET_MODE (from) == DImode)
+ fnname = "__floatdidf";
+ else
+ abort ();
+ }
+ else
+ abort ();
+
+ start_sequence ();
+
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, fnname),
+ 0, GET_MODE (to), 1, from, GET_MODE (from));
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_libcall_block (insns, target, hard_libcall_value (GET_MODE (to)),
+ gen_rtx (FLOAT, GET_MODE (to), from));
+ }
+
+ /* 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);
+ }
+}
+\f
+/* 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;
+ char *fnname = 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);
+
+ if (fmode != GET_MODE (from))
+ from = convert_to_mode (fmode, from, 0);
+ else
+ from = protect_from_queue (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 inetger mode. */
+
+ if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_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 = GET_MODE_BITSIZE (GET_MODE (to));
+ REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1);
+ rtx limit = immed_real_const_1 (offset, fmode);
+ rtx lab1 = gen_label_rtx ();
+ rtx lab2 = gen_label_rtx ();
+ rtx insn;
+
+ 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, 0, 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,
+ 0, 0, OPTAB_LIB_WIDEN);
+ expand_fix (to, target, 0);
+ target = expand_binop (GET_MODE (to), xor_optab, to,
+ gen_rtx (CONST_INT, VOIDmode,
+ 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),
+ 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)
+ fnname = unsignedp ? "__fixunssfsi" : "__fixsfsi";
+ else if (GET_MODE (to) == DImode)
+ fnname = unsignedp ? "__fixunssfdi" : "__fixsfdi";
+ else
+ abort ();
+ }
+ else if (GET_MODE (from) == DFmode)
+ {
+ if (GET_MODE (to) == SImode)
+ fnname = unsignedp ? "__fixunsdfsi" : "__fixdfsi";
+ else if (GET_MODE (to) == DImode)
+ fnname = unsignedp ? "__fixunsdfdi" : "__fixdfdi";
+ else
+ abort ();
+ }
+ else
+ abort ();
+
+ if (fnname)
+ {
+ rtx insns;
+
+ to = protect_from_queue (to, 1);
+ from = protect_from_queue (from, 0);
+
+ if (flag_force_mem)
+ from = force_not_mem (from);
+
+ start_sequence ();
+
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, fnname),
+ 0, GET_MODE (to), 1, from, GET_MODE (from));
+ insns = get_insns ();
+ end_sequence ();
+
+ emit_libcall_block (insns, target, hard_libcall_value (GET_MODE (to)),
+ gen_rtx (unsignedp ? FIX : UNSIGNED_FIX,
+ GET_MODE (to), from));
+ }
+
+ if (GET_MODE (to) == GET_MODE (target))
+ emit_move_insn (to, target);
+ else
+ convert_move (to, target, 0);
+}
+\f
+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;
+ }
+ return op;
+}
+
+/* Call this once to initialize the contents of the optabs
+ appropriately for the current target machine. */
+
+void
+init_optabs ()
+{
+ int i;
+
+ init_fixtab ();
+ init_floattab ();
+ init_extends ();
+
+ add_optab = init_optab (PLUS);
+ sub_optab = init_optab (MINUS);
+ smul_optab = init_optab (MULT);
+ 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);
+ lshl_optab = init_optab (LSHIFT);
+ 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);
+
+#ifdef HAVE_addqi3
+ if (HAVE_addqi3)
+ add_optab->handlers[(int) QImode].insn_code = CODE_FOR_addqi3;
+#endif
+#ifdef HAVE_addhi3
+ if (HAVE_addhi3)
+ add_optab->handlers[(int) HImode].insn_code = CODE_FOR_addhi3;
+#endif
+#ifdef HAVE_addpsi3
+ if (HAVE_addpsi3)
+ add_optab->handlers[(int) PSImode].insn_code = CODE_FOR_addpsi3;
+#endif
+#ifdef HAVE_addsi3
+ if (HAVE_addsi3)
+ add_optab->handlers[(int) SImode].insn_code = CODE_FOR_addsi3;
+#endif
+#ifdef HAVE_adddi3
+ if (HAVE_adddi3)
+ add_optab->handlers[(int) DImode].insn_code = CODE_FOR_adddi3;
+#endif
+#ifdef HAVE_addti3
+ if (HAVE_addti3)
+ add_optab->handlers[(int) TImode].insn_code = CODE_FOR_addti3;
+#endif
+#ifdef HAVE_addsf3
+ if (HAVE_addsf3)
+ add_optab->handlers[(int) SFmode].insn_code = CODE_FOR_addsf3;
+#endif
+#ifdef HAVE_adddf3
+ if (HAVE_adddf3)
+ add_optab->handlers[(int) DFmode].insn_code = CODE_FOR_adddf3;
+#endif
+#ifdef HAVE_addtf3
+ if (HAVE_addtf3)
+ add_optab->handlers[(int) TFmode].insn_code = CODE_FOR_addtf3;
+#endif
+ add_optab->handlers[(int) SFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__addsf3");
+ add_optab->handlers[(int) DFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__adddf3");
+
+#ifdef HAVE_subqi3
+ if (HAVE_subqi3)
+ sub_optab->handlers[(int) QImode].insn_code = CODE_FOR_subqi3;
+#endif
+#ifdef HAVE_subhi3
+ if (HAVE_subhi3)
+ sub_optab->handlers[(int) HImode].insn_code = CODE_FOR_subhi3;
+#endif
+#ifdef HAVE_subpsi3
+ if (HAVE_subpsi3)
+ sub_optab->handlers[(int) PSImode].insn_code = CODE_FOR_subpsi3;
+#endif
+#ifdef HAVE_subsi3
+ if (HAVE_subsi3)
+ sub_optab->handlers[(int) SImode].insn_code = CODE_FOR_subsi3;
+#endif
+#ifdef HAVE_subdi3
+ if (HAVE_subdi3)
+ sub_optab->handlers[(int) DImode].insn_code = CODE_FOR_subdi3;
+#endif
+#ifdef HAVE_subti3
+ if (HAVE_subti3)
+ sub_optab->handlers[(int) Imode].insn_code = CODE_FOR_subti3;
+#endif
+#ifdef HAVE_subsf3
+ if (HAVE_subsf3)
+ sub_optab->handlers[(int) SFmode].insn_code = CODE_FOR_subsf3;
+#endif
+#ifdef HAVE_subdf3
+ if (HAVE_subdf3)
+ sub_optab->handlers[(int) DFmode].insn_code = CODE_FOR_subdf3;
+#endif
+#ifdef HAVE_subtf3
+ if (HAVE_subtf3)
+ sub_optab->handlers[(int) TFmode].insn_code = CODE_FOR_subtf3;
+#endif
+ sub_optab->handlers[(int) SFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__subsf3");
+ sub_optab->handlers[(int) DFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__subdf3");
+
+#ifdef HAVE_mulqi3
+ if (HAVE_mulqi3)
+ smul_optab->handlers[(int) QImode].insn_code = CODE_FOR_mulqi3;
+#endif
+#ifdef HAVE_mulhi3
+ if (HAVE_mulhi3)
+ smul_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulhi3;
+#endif
+#ifdef HAVE_mulpsi3
+ if (HAVE_mulpsi3)
+ smul_optab->handlers[(int) PSImode].insn_code = CODE_FOR_mulpsi3;
+#endif
+#ifdef HAVE_mulsi3
+ if (HAVE_mulsi3)
+ smul_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulsi3;
+#endif
+#ifdef HAVE_muldi3
+ if (HAVE_muldi3)
+ smul_optab->handlers[(int) DImode].insn_code = CODE_FOR_muldi3;
+#endif
+#ifdef HAVE_multi3
+ if (HAVE_multi3)
+ smul_optab->handlers[(int) TImode].insn_code = CODE_FOR_multi3;
+#endif
+#ifdef HAVE_mulsf3
+ if (HAVE_mulsf3)
+ smul_optab->handlers[(int) SFmode].insn_code = CODE_FOR_mulsf3;
+#endif
+#ifdef HAVE_muldf3
+ if (HAVE_muldf3)
+ smul_optab->handlers[(int) DFmode].insn_code = CODE_FOR_muldf3;
+#endif
+#ifdef HAVE_multf3
+ if (HAVE_multf3)
+ smul_optab->handlers[(int) TFmode].insn_code = CODE_FOR_multf3;
+#endif
+
+#ifdef MULSI3_LIBCALL
+ smul_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MULSI3_LIBCALL);
+#else
+ smul_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__mulsi3");
+#endif
+#ifdef MULDI3_LIBCALL
+ smul_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MULDI3_LIBCALL);
+#else
+ smul_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__muldi3");
+#endif
+ smul_optab->handlers[(int) SFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__mulsf3");
+ smul_optab->handlers[(int) DFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__muldf3");
+
+#ifdef HAVE_mulqihi3
+ if (HAVE_mulqihi3)
+ smul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_mulqihi3;
+#endif
+#ifdef HAVE_mulhisi3
+ if (HAVE_mulhisi3)
+ smul_widen_optab->handlers[(int) SImode].insn_code = CODE_FOR_mulhisi3;
+#endif
+#ifdef HAVE_mulsidi3
+ if (HAVE_mulsidi3)
+ smul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_mulsidi3;
+#endif
+#ifdef HAVE_mulditi3
+ if (HAVE_mulditi3)
+ smul_widen_optab->handlers[(int) TImode].insn_code = CODE_FOR_mulditi3;
+#endif
+
+#ifdef HAVE_umulqihi3
+ if (HAVE_umulqihi3)
+ umul_widen_optab->handlers[(int) HImode].insn_code = CODE_FOR_umulqihi3;
+#endif
+#ifdef HAVE_umulhisi3
+ if (HAVE_umulhisi3)
+ umul_widen_optab->handlers[(int) SImode].insn_code = CODE_FOR_umulhisi3;
+#endif
+#ifdef HAVE_umulsidi3
+ if (HAVE_umulsidi3)
+ umul_widen_optab->handlers[(int) DImode].insn_code = CODE_FOR_umulsidi3;
+#endif
+#ifdef HAVE_umulditi3
+ if (HAVE_umulditi3)
+ umul_widen_optab->handlers[(int) TImode].insn_code = CODE_FOR_umulditi3;
+#endif
+
+#ifdef HAVE_divqi3
+ if (HAVE_divqi3)
+ sdiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_divqi3;
+#endif
+#ifdef HAVE_divhi3
+ if (HAVE_divhi3)
+ sdiv_optab->handlers[(int) HImode].insn_code = CODE_FOR_divhi3;
+#endif
+#ifdef HAVE_divpsi3
+ if (HAVE_divpsi3)
+ sdiv_optab->handlers[(int) PSImode].insn_code = CODE_FOR_divpsi3;
+#endif
+#ifdef HAVE_divsi3
+ if (HAVE_divsi3)
+ sdiv_optab->handlers[(int) SImode].insn_code = CODE_FOR_divsi3;
+#endif
+#ifdef HAVE_divdi3
+ if (HAVE_divdi3)
+ sdiv_optab->handlers[(int) DImode].insn_code = CODE_FOR_divdi3;
+#endif
+#ifdef HAVE_divti3
+ if (HAVE_divti3)
+ sdiv_optab->handlers[(int) TImode].insn_code = CODE_FOR_divti3;
+#endif
+
+#ifdef DIVSI3_LIBCALL
+ sdiv_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, DIVSI3_LIBCALL);
+#else
+ sdiv_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__divsi3");
+#endif
+#ifdef DIVDI3_LIBCALL
+ sdiv_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, DIVDI3_LIBCALL);
+#else
+ sdiv_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__divdi3");
+#endif
+
+#ifdef HAVE_udivqi3
+ if (HAVE_udivqi3)
+ udiv_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivqi3;
+#endif
+#ifdef HAVE_udivhi3
+ if (HAVE_udivhi3)
+ udiv_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivhi3;
+#endif
+#ifdef HAVE_udivpsi3
+ if (HAVE_udivpsi3)
+ udiv_optab->handlers[(int) PSImode].insn_code = CODE_FOR_udivpsi3;
+#endif
+#ifdef HAVE_udivsi3
+ if (HAVE_udivsi3)
+ udiv_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivsi3;
+#endif
+#ifdef HAVE_udivdi3
+ if (HAVE_udivdi3)
+ udiv_optab->handlers[(int) DImode].insn_code = CODE_FOR_udivdi3;
+#endif
+#ifdef HAVE_udivti3
+ if (HAVE_udivti3)
+ udiv_optab->handlers[(int) TImode].insn_code = CODE_FOR_udivti3;
+#endif
+
+#ifdef UDIVSI3_LIBCALL
+ udiv_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UDIVSI3_LIBCALL);
+#else
+ udiv_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__udivsi3");
+#endif
+#ifdef UDIVDI3_LIBCALL
+ udiv_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UDIVDI3_LIBCALL);
+#else
+ udiv_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__udivdi3");
+#endif
+
+#ifdef HAVE_divmodqi4
+ if (HAVE_divmodqi4)
+ sdivmod_optab->handlers[(int) QImode].insn_code = CODE_FOR_divmodqi4;
+#endif
+#ifdef HAVE_divmodhi4
+ if (HAVE_divmodhi4)
+ sdivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_divmodhi4;
+#endif
+#ifdef HAVE_divmodsi4
+ if (HAVE_divmodsi4)
+ sdivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_divmodsi4;
+#endif
+#ifdef HAVE_divmoddi4
+ if (HAVE_divmoddi4)
+ sdivmod_optab->handlers[(int) DImode].insn_code = CODE_FOR_divmoddi4;
+#endif
+#ifdef HAVE_divmodti4
+ if (HAVE_divmodti4)
+ sdivmod_optab->handlers[(int) TImode].insn_code = CODE_FOR_divmodti4;
+#endif
+
+#ifdef HAVE_udivmodqi4
+ if (HAVE_udivmodqi4)
+ udivmod_optab->handlers[(int) QImode].insn_code = CODE_FOR_udivmodqi4;
+#endif
+#ifdef HAVE_udivmodhi4
+ if (HAVE_udivmodhi4)
+ udivmod_optab->handlers[(int) HImode].insn_code = CODE_FOR_udivmodhi4;
+#endif
+#ifdef HAVE_udivmodsi4
+ if (HAVE_udivmodsi4)
+ udivmod_optab->handlers[(int) SImode].insn_code = CODE_FOR_udivmodsi4;
+#endif
+#ifdef HAVE_udivmoddi4
+ if (HAVE_udivmoddi4)
+ udivmod_optab->handlers[(int) DImode].insn_code = CODE_FOR_udivmoddi4;
+#endif
+#ifdef HAVE_udivmodti4
+ if (HAVE_udivmodti4)
+ udivmod_optab->handlers[(int) TImode].insn_code = CODE_FOR_udivmodti4;
+#endif
+
+#ifdef HAVE_modqi3
+ if (HAVE_modqi3)
+ smod_optab->handlers[(int) QImode].insn_code = CODE_FOR_modqi3;
+#endif
+#ifdef HAVE_modhi3
+ if (HAVE_modhi3)
+ smod_optab->handlers[(int) HImode].insn_code = CODE_FOR_modhi3;
+#endif
+#ifdef HAVE_modpsi3
+ if (HAVE_modpsi3)
+ smod_optab->handlers[(int) PSImode].insn_code = CODE_FOR_modpsi3;
+#endif
+#ifdef HAVE_modsi3
+ if (HAVE_modsi3)
+ smod_optab->handlers[(int) SImode].insn_code = CODE_FOR_modsi3;
+#endif
+#ifdef HAVE_moddi3
+ if (HAVE_moddi3)
+ smod_optab->handlers[(int) DImode].insn_code = CODE_FOR_moddi3;
+#endif
+#ifdef HAVE_modti3
+ if (HAVE_modti3)
+ smod_optab->handlers[(int) TImode].insn_code = CODE_FOR_modti3;
+#endif
+
+#ifdef MODSI3_LIBCALL
+ smod_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MODSI3_LIBCALL);
+#else
+ smod_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__modsi3");
+#endif
+#ifdef MODDI3_LIBCALL
+ smod_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, MODDI3_LIBCALL);
+#else
+ smod_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__moddi3");
+#endif
+
+#ifdef HAVE_umodqi3
+ if (HAVE_umodqi3)
+ umod_optab->handlers[(int) QImode].insn_code = CODE_FOR_umodqi3;
+#endif
+#ifdef HAVE_umodhi3
+ if (HAVE_umodhi3)
+ umod_optab->handlers[(int) HImode].insn_code = CODE_FOR_umodhi3;
+#endif
+#ifdef HAVE_umodpsi3
+ if (HAVE_umodpsi3)
+ umod_optab->handlers[(int) PSImode].insn_code = CODE_FOR_umodpsi3;
+#endif
+#ifdef HAVE_umodsi3
+ if (HAVE_umodsi3)
+ umod_optab->handlers[(int) SImode].insn_code = CODE_FOR_umodsi3;
+#endif
+#ifdef HAVE_umoddi3
+ if (HAVE_umoddi3)
+ umod_optab->handlers[(int) DImode].insn_code = CODE_FOR_umoddi3;
+#endif
+#ifdef HAVE_umodti3
+ if (HAVE_umodti3)
+ umod_optab->handlers[(int) TImode].insn_code = CODE_FOR_umodti3;
+#endif
+
+#ifdef UMODSI3_LIBCALL
+ umod_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UMODSI3_LIBCALL);
+#else
+ umod_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__umodsi3");
+#endif
+#ifdef UMODDI3_LIBCALL
+ umod_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, UMODDI3_LIBCALL);
+#else
+ umod_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__umoddi3");
+#endif
+
+#ifdef HAVE_divsf3
+ if (HAVE_divsf3)
+ flodiv_optab->handlers[(int) SFmode].insn_code = CODE_FOR_divsf3;
+#endif
+#ifdef HAVE_divdf3
+ if (HAVE_divdf3)
+ flodiv_optab->handlers[(int) DFmode].insn_code = CODE_FOR_divdf3;
+#endif
+#ifdef HAVE_divtf3
+ if (HAVE_divtf3)
+ flodiv_optab->handlers[(int) TFmode].insn_code = CODE_FOR_divtf3;
+#endif
+ flodiv_optab->handlers[(int) SFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__divsf3");
+ flodiv_optab->handlers[(int) DFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__divdf3");
+
+#ifdef HAVE_ftruncsf2
+ if (HAVE_ftruncsf2)
+ ftrunc_optab->handlers[(int) SFmode].insn_code = CODE_FOR_ftruncsf2;
+#endif
+#ifdef HAVE_ftruncdf2
+ if (HAVE_ftruncdf2)
+ ftrunc_optab->handlers[(int) DFmode].insn_code = CODE_FOR_ftruncdf2;
+#endif
+#ifdef HAVE_ftrunctf2
+ if (HAVE_ftrunctf2)
+ ftrunc_optab->handlers[(int) TFmode].insn_code = CODE_FOR_ftrunctf2;
+#endif
+
+#ifdef HAVE_andqi3
+ if (HAVE_andqi3)
+ and_optab->handlers[(int) QImode].insn_code = CODE_FOR_andqi3;
+#endif
+#ifdef HAVE_andhi3
+ if (HAVE_andhi3)
+ and_optab->handlers[(int) HImode].insn_code = CODE_FOR_andhi3;
+#endif
+#ifdef HAVE_andpsi3
+ if (HAVE_andpsi3)
+ and_optab->handlers[(int) PSImode].insn_code = CODE_FOR_andpsi3;
+#endif
+#ifdef HAVE_andsi3
+ if (HAVE_andsi3)
+ and_optab->handlers[(int) SImode].insn_code = CODE_FOR_andsi3;
+#endif
+#ifdef HAVE_anddi3
+ if (HAVE_anddi3)
+ and_optab->handlers[(int) DImode].insn_code = CODE_FOR_anddi3;
+#endif
+#ifdef HAVE_andti3
+ if (HAVE_andti3)
+ and_optab->handlers[(int) TImode].insn_code = CODE_FOR_andti3;
+#endif
+
+#ifdef HAVE_iorqi3
+ if (HAVE_iorqi3)
+ ior_optab->handlers[(int) QImode].insn_code = CODE_FOR_iorqi3;
+#endif
+#ifdef HAVE_iorhi3
+ if (HAVE_iorhi3)
+ ior_optab->handlers[(int) HImode].insn_code = CODE_FOR_iorhi3;
+#endif
+#ifdef HAVE_iorpsi3
+ if (HAVE_iorpsi3)
+ ior_optab->handlers[(int) PSImode].insn_code = CODE_FOR_iorpsi3;
+#endif
+#ifdef HAVE_iorsi3
+ if (HAVE_iorsi3)
+ ior_optab->handlers[(int) SImode].insn_code = CODE_FOR_iorsi3;
+#endif
+#ifdef HAVE_iordi3
+ if (HAVE_iordi3)
+ ior_optab->handlers[(int) DImode].insn_code = CODE_FOR_iordi3;
+#endif
+#ifdef HAVE_iorti3
+ if (HAVE_iorti3)
+ ior_optab->handlers[(int) TImode].insn_code = CODE_FOR_iorti3;
+#endif
+
+#ifdef HAVE_xorqi3
+ if (HAVE_xorqi3)
+ xor_optab->handlers[(int) QImode].insn_code = CODE_FOR_xorqi3;
+#endif
+#ifdef HAVE_xorhi3
+ if (HAVE_xorhi3)
+ xor_optab->handlers[(int) HImode].insn_code = CODE_FOR_xorhi3;
+#endif
+#ifdef HAVE_xorpsi3
+ if (HAVE_xorpsi3)
+ xor_optab->handlers[(int) PSImode].insn_code = CODE_FOR_xorpsi3;
+#endif
+#ifdef HAVE_xorsi3
+ if (HAVE_xorsi3)
+ xor_optab->handlers[(int) SImode].insn_code = CODE_FOR_xorsi3;
+#endif
+#ifdef HAVE_xordi3
+ if (HAVE_xordi3)
+ xor_optab->handlers[(int) DImode].insn_code = CODE_FOR_xordi3;
+#endif
+#ifdef HAVE_xorti3
+ if (HAVE_xorti3)
+ xor_optab->handlers[(int) TImode].insn_code = CODE_FOR_xorti3;
+#endif
+
+#ifdef HAVE_ashlqi3
+ if (HAVE_ashlqi3)
+ ashl_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashlqi3;
+#endif
+#ifdef HAVE_ashlhi3
+ if (HAVE_ashlhi3)
+ ashl_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashlhi3;
+#endif
+#ifdef HAVE_ashlpsi3
+ if (HAVE_ashlpsi3)
+ ashl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_ashlpsi3;
+#endif
+#ifdef HAVE_ashlsi3
+ if (HAVE_ashlsi3)
+ ashl_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashlsi3;
+#endif
+#ifdef HAVE_ashldi3
+ if (HAVE_ashldi3)
+ ashl_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashldi3;
+#endif
+#ifdef HAVE_ashlti3
+ if (HAVE_ashlti3)
+ ashl_optab->handlers[(int) TImode].insn_code = CODE_FOR_ashlti3;
+#endif
+ ashl_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__ashlsi3");
+ ashl_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__ashldi3");
+
+#ifdef HAVE_ashrqi3
+ if (HAVE_ashrqi3)
+ ashr_optab->handlers[(int) QImode].insn_code = CODE_FOR_ashrqi3;
+#endif
+#ifdef HAVE_ashrhi3
+ if (HAVE_ashrhi3)
+ ashr_optab->handlers[(int) HImode].insn_code = CODE_FOR_ashrhi3;
+#endif
+#ifdef HAVE_ashrpsi3
+ if (HAVE_ashrpsi3)
+ ashr_optab->handlers[(int) PSImode].insn_code = CODE_FOR_ashrpsi3;
+#endif
+#ifdef HAVE_ashrsi3
+ if (HAVE_ashrsi3)
+ ashr_optab->handlers[(int) SImode].insn_code = CODE_FOR_ashrsi3;
+#endif
+#ifdef HAVE_ashrdi3
+ if (HAVE_ashrdi3)
+ ashr_optab->handlers[(int) DImode].insn_code = CODE_FOR_ashrdi3;
+#endif
+#ifdef HAVE_ashrti3
+ if (HAVE_ashrti3)
+ ashr_optab->handlers[(int) TImode].insn_code = CODE_FOR_ashrti3;
+#endif
+ ashr_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__ashrsi3");
+ ashr_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__ashrdi3");
+
+#ifdef HAVE_lshlqi3
+ if (HAVE_lshlqi3)
+ lshl_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshlqi3;
+#endif
+#ifdef HAVE_lshlhi3
+ if (HAVE_lshlhi3)
+ lshl_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshlhi3;
+#endif
+#ifdef HAVE_lshlpsi3
+ if (HAVE_lshlpsi3)
+ lshl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_lshlpsi3;
+#endif
+#ifdef HAVE_lshlsi3
+ if (HAVE_lshlsi3)
+ lshl_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshlsi3;
+#endif
+#ifdef HAVE_lshldi3
+ if (HAVE_lshldi3)
+ lshl_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshldi3;
+#endif
+#ifdef HAVE_lshlti3
+ if (HAVE_lshlti3)
+ lshl_optab->handlers[(int) TImode].insn_code = CODE_FOR_lshlti3;
+#endif
+ lshl_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__lshlsi3");
+ lshl_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__lshldi3");
+
+#ifdef HAVE_lshrqi3
+ if (HAVE_lshrqi3)
+ lshr_optab->handlers[(int) QImode].insn_code = CODE_FOR_lshrqi3;
+#endif
+#ifdef HAVE_lshrhi3
+ if (HAVE_lshrhi3)
+ lshr_optab->handlers[(int) HImode].insn_code = CODE_FOR_lshrhi3;
+#endif
+#ifdef HAVE_lshrpsi3
+ if (HAVE_lshrpsi3)
+ lshr_optab->handlers[(int) PSImode].insn_code = CODE_FOR_lshrpsi3;
+#endif
+#ifdef HAVE_lshrsi3
+ if (HAVE_lshrsi3)
+ lshr_optab->handlers[(int) SImode].insn_code = CODE_FOR_lshrsi3;
+#endif
+#ifdef HAVE_lshrdi3
+ if (HAVE_lshrdi3)
+ lshr_optab->handlers[(int) DImode].insn_code = CODE_FOR_lshrdi3;
+#endif
+#ifdef HAVE_lshrti3
+ if (HAVE_lshrti3)
+ lshr_optab->handlers[(int) TImode].insn_code = CODE_FOR_lshrti3;
+#endif
+ lshr_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__lshrsi3");
+ lshr_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__lshrdi3");
+
+#ifdef HAVE_rotlqi3
+ if (HAVE_rotlqi3)
+ rotl_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotlqi3;
+#endif
+#ifdef HAVE_rotlhi3
+ if (HAVE_rotlhi3)
+ rotl_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotlhi3;
+#endif
+#ifdef HAVE_rotlpsi3
+ if (HAVE_rotlpsi3)
+ rotl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_rotlpsi3;
+#endif
+#ifdef HAVE_rotlsi3
+ if (HAVE_rotlsi3)
+ rotl_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotlsi3;
+#endif
+#ifdef HAVE_rotldi3
+ if (HAVE_rotldi3)
+ rotl_optab->handlers[(int) DImode].insn_code = CODE_FOR_rotldi3;
+#endif
+#ifdef HAVE_rotlti3
+ if (HAVE_rotlti3)
+ rotl_optab->handlers[(int) TImode].insn_code = CODE_FOR_rotlti3;
+#endif
+ rotl_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__rotlsi3");
+ rotl_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__rotldi3");
+
+#ifdef HAVE_rotrqi3
+ if (HAVE_rotrqi3)
+ rotr_optab->handlers[(int) QImode].insn_code = CODE_FOR_rotrqi3;
+#endif
+#ifdef HAVE_rotrhi3
+ if (HAVE_rotrhi3)
+ rotr_optab->handlers[(int) HImode].insn_code = CODE_FOR_rotrhi3;
+#endif
+#ifdef HAVE_rotrpsi3
+ if (HAVE_rotrpsi3)
+ rotr_optab->handlers[(int) PSImode].insn_code = CODE_FOR_rotrpsi3;
+#endif
+#ifdef HAVE_rotrsi3
+ if (HAVE_rotrsi3)
+ rotr_optab->handlers[(int) SImode].insn_code = CODE_FOR_rotrsi3;
+#endif
+#ifdef HAVE_rotrdi3
+ if (HAVE_rotrdi3)
+ rotr_optab->handlers[(int) DImode].insn_code = CODE_FOR_rotrdi3;
+#endif
+#ifdef HAVE_rotrti3
+ if (HAVE_rotrti3)
+ rotr_optab->handlers[(int) TImode].insn_code = CODE_FOR_rotrti3;
+#endif
+ rotr_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__rotrsi3");
+ rotr_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__rotrdi3");
+
+#ifdef HAVE_sminqi3
+ if (HAVE_sminqi3)
+ smin_optab->handlers[(int) QImode].insn_code = CODE_FOR_sminqi3;
+#endif
+#ifdef HAVE_sminhi3
+ if (HAVE_sminhi3)
+ smin_optab->handlers[(int) HImode].insn_code = CODE_FOR_sminhi3;
+#endif
+#ifdef HAVE_sminsi3
+ if (HAVE_sminsi3)
+ smin_optab->handlers[(int) SImode].insn_code = CODE_FOR_sminsi3;
+#endif
+#ifdef HAVE_smindi3
+ if (HAVE_smindi3)
+ smin_optab->handlers[(int) DImode].insn_code = CODE_FOR_smindi3;
+#endif
+#ifdef HAVE_sminti3
+ if (HAVE_sminti3)
+ smin_optab->handlers[(int) TImode].insn_code = CODE_FOR_sminti3;
+#endif
+#ifdef HAVE_sminsf3
+ if (HAVE_sminsf3)
+ smin_optab->handlers[(int) SFmode].insn_code = CODE_FOR_sminsf3;
+#endif
+#ifdef HAVE_smindf3
+ if (HAVE_smindf3)
+ smin_optab->handlers[(int) DFmode].insn_code = CODE_FOR_smindf3;
+#endif
+#ifdef HAVE_smintf3
+ if (HAVE_smintf3)
+ smin_optab->handlers[(int) TFmode].insn_code = CODE_FOR_smintf3;
+#endif
+
+#ifdef HAVE_smaxqi3
+ if (HAVE_smaxqi3)
+ smax_optab->handlers[(int) QImode].insn_code = CODE_FOR_smaxqi3;
+#endif
+#ifdef HAVE_smaxhi3
+ if (HAVE_smaxhi3)
+ smax_optab->handlers[(int) HImode].insn_code = CODE_FOR_smaxhi3;
+#endif
+#ifdef HAVE_smaxsi3
+ if (HAVE_smaxsi3)
+ smax_optab->handlers[(int) SImode].insn_code = CODE_FOR_smaxsi3;
+#endif
+#ifdef HAVE_smaxdi3
+ if (HAVE_smaxdi3)
+ smax_optab->handlers[(int) DImode].insn_code = CODE_FOR_smaxdi3;
+#endif
+#ifdef HAVE_smaxti3
+ if (HAVE_smaxti3)
+ smax_optab->handlers[(int) TImode].insn_code = CODE_FOR_smaxti3;
+#endif
+#ifdef HAVE_smaxsf3
+ if (HAVE_smaxsf3)
+ smax_optab->handlers[(int) SFmode].insn_code = CODE_FOR_smaxsf3;
+#endif
+#ifdef HAVE_smaxdf3
+ if (HAVE_smaxdf3)
+ smax_optab->handlers[(int) DFmode].insn_code = CODE_FOR_smaxdf3;
+#endif
+#ifdef HAVE_smaxtf3
+ if (HAVE_smaxtf3)
+ smax_optab->handlers[(int) TFmode].insn_code = CODE_FOR_smaxtf3;
+#endif
+
+#ifdef HAVE_uminqi3
+ if (HAVE_uminqi3)
+ umin_optab->handlers[(int) QImode].insn_code = CODE_FOR_uminqi3;
+#endif
+#ifdef HAVE_uminhi3
+ if (HAVE_uminhi3)
+ umin_optab->handlers[(int) HImode].insn_code = CODE_FOR_uminhi3;
+#endif
+#ifdef HAVE_uminsi3
+ if (HAVE_uminsi3)
+ umin_optab->handlers[(int) SImode].insn_code = CODE_FOR_uminsi3;
+#endif
+#ifdef HAVE_umindi3
+ if (HAVE_umindi3)
+ umin_optab->handlers[(int) DImode].insn_code = CODE_FOR_umindi3;
+#endif
+#ifdef HAVE_uminti3
+ if (HAVE_uminti3)
+ umin_optab->handlers[(int) TImode].insn_code = CODE_FOR_uminti3;
+#endif
+
+#ifdef HAVE_umaxqi3
+ if (HAVE_umaxqi3)
+ umax_optab->handlers[(int) QImode].insn_code = CODE_FOR_umaxqi3;
+#endif
+#ifdef HAVE_umaxhi3
+ if (HAVE_umaxhi3)
+ umax_optab->handlers[(int) HImode].insn_code = CODE_FOR_umaxhi3;
+#endif
+#ifdef HAVE_umaxsi3
+ if (HAVE_umaxsi3)
+ umax_optab->handlers[(int) SImode].insn_code = CODE_FOR_umaxsi3;
+#endif
+#ifdef HAVE_umaxdi3
+ if (HAVE_umaxdi3)
+ umax_optab->handlers[(int) DImode].insn_code = CODE_FOR_umaxdi3;
+#endif
+#ifdef HAVE_umaxti3
+ if (HAVE_umaxti3)
+ umax_optab->handlers[(int) TImode].insn_code = CODE_FOR_umaxti3;
+#endif
+
+#ifdef HAVE_negqi2
+ if (HAVE_negqi2)
+ neg_optab->handlers[(int) QImode].insn_code = CODE_FOR_negqi2;
+#endif
+#ifdef HAVE_neghi2
+ if (HAVE_neghi2)
+ neg_optab->handlers[(int) HImode].insn_code = CODE_FOR_neghi2;
+#endif
+#ifdef HAVE_negpsi2
+ if (HAVE_negpsi2)
+ neg_optab->handlers[(int) PSImode].insn_code = CODE_FOR_negpsi2;
+#endif
+#ifdef HAVE_negsi2
+ if (HAVE_negsi2)
+ neg_optab->handlers[(int) SImode].insn_code = CODE_FOR_negsi2;
+#endif
+#ifdef HAVE_negdi2
+ if (HAVE_negdi2)
+ neg_optab->handlers[(int) DImode].insn_code = CODE_FOR_negdi2;
+#endif
+#ifdef HAVE_negti2
+ if (HAVE_negti2)
+ neg_optab->handlers[(int) TImode].insn_code = CODE_FOR_negti2;
+#endif
+#ifdef HAVE_negsf2
+ if (HAVE_negsf2)
+ neg_optab->handlers[(int) SFmode].insn_code = CODE_FOR_negsf2;
+#endif
+#ifdef HAVE_negdf2
+ if (HAVE_negdf2)
+ neg_optab->handlers[(int) DFmode].insn_code = CODE_FOR_negdf2;
+#endif
+#ifdef HAVE_negtf2
+ if (HAVE_negtf2)
+ neg_optab->handlers[(int) TFmode].insn_code = CODE_FOR_negtf2;
+#endif
+ neg_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__negsi2");
+ neg_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__negdi2");
+ neg_optab->handlers[(int) SFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__negsf2");
+ neg_optab->handlers[(int) DFmode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__negdf2");
+
+#ifdef HAVE_absqi2
+ if (HAVE_absqi2)
+ abs_optab->handlers[(int) QImode].insn_code = CODE_FOR_absqi2;
+#endif
+#ifdef HAVE_abshi2
+ if (HAVE_abshi2)
+ abs_optab->handlers[(int) HImode].insn_code = CODE_FOR_abshi2;
+#endif
+#ifdef HAVE_abspsi2
+ if (HAVE_abspsi2)
+ abs_optab->handlers[(int) PSImode].insn_code = CODE_FOR_abspsi2;
+#endif
+#ifdef HAVE_abssi2
+ if (HAVE_abssi2)
+ abs_optab->handlers[(int) SImode].insn_code = CODE_FOR_abssi2;
+#endif
+#ifdef HAVE_absdi2
+ if (HAVE_absdi2)
+ abs_optab->handlers[(int) DImode].insn_code = CODE_FOR_absdi2;
+#endif
+#ifdef HAVE_absti2
+ if (HAVE_absti2)
+ abs_optab->handlers[(int) TImode].insn_code = CODE_FOR_absti2;
+#endif
+#ifdef HAVE_abssf2
+ if (HAVE_abssf2)
+ abs_optab->handlers[(int) SFmode].insn_code = CODE_FOR_abssf2;
+#endif
+#ifdef HAVE_absdf2
+ if (HAVE_absdf2)
+ abs_optab->handlers[(int) DFmode].insn_code = CODE_FOR_absdf2;
+#endif
+#ifdef HAVE_abstf2
+ if (HAVE_abstf2)
+ abs_optab->handlers[(int) TFmode].insn_code = CODE_FOR_abstf2;
+#endif
+ /* No library calls here! If there is no abs instruction,
+ expand_expr will generate a conditional negation. */
+
+#ifdef HAVE_one_cmplqi2
+ if (HAVE_one_cmplqi2)
+ one_cmpl_optab->handlers[(int) QImode].insn_code = CODE_FOR_one_cmplqi2;
+#endif
+#ifdef HAVE_one_cmplhi2
+ if (HAVE_one_cmplhi2)
+ one_cmpl_optab->handlers[(int) HImode].insn_code = CODE_FOR_one_cmplhi2;
+#endif
+#ifdef HAVE_one_cmplpsi2
+ if (HAVE_one_cmplpsi2)
+ one_cmpl_optab->handlers[(int) PSImode].insn_code = CODE_FOR_one_cmplpsi2;
+#endif
+#ifdef HAVE_one_cmplsi2
+ if (HAVE_one_cmplsi2)
+ one_cmpl_optab->handlers[(int) SImode].insn_code = CODE_FOR_one_cmplsi2;
+#endif
+#ifdef HAVE_one_cmpldi2
+ if (HAVE_one_cmpldi2)
+ one_cmpl_optab->handlers[(int) DImode].insn_code = CODE_FOR_one_cmpldi2;
+#endif
+#ifdef HAVE_one_cmplti2
+ if (HAVE_one_cmplti2)
+ one_cmpl_optab->handlers[(int) TImode].insn_code = CODE_FOR_one_cmplti2;
+#endif
+ one_cmpl_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__one_cmplsi2");
+
+#ifdef HAVE_ffsqi2
+ if (HAVE_ffsqi2)
+ ffs_optab->handlers[(int) QImode].insn_code = CODE_FOR_ffsqi2;
+#endif
+#ifdef HAVE_ffshi2
+ if (HAVE_ffshi2)
+ ffs_optab->handlers[(int) HImode].insn_code = CODE_FOR_ffshi2;
+#endif
+#ifdef HAVE_ffspsi2
+ if (HAVE_ffspsi2)
+ ffs_optab->handlers[(int) PSImode].insn_code = CODE_FOR_ffspsi2;
+#endif
+#ifdef HAVE_ffssi2
+ if (HAVE_ffssi2)
+ ffs_optab->handlers[(int) SImode].insn_code = CODE_FOR_ffssi2;
+#endif
+#ifdef HAVE_ffsdi2
+ if (HAVE_ffsdi2)
+ ffs_optab->handlers[(int) DImode].insn_code = CODE_FOR_ffsdi2;
+#endif
+#ifdef HAVE_ffsti2
+ if (HAVE_ffsti2)
+ ffs_optab->handlers[(int) TImode].insn_code = CODE_FOR_ffsti2;
+#endif
+ ffs_optab->handlers[(int) SImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "ffs");
+
+#ifdef HAVE_movqi
+ if (HAVE_movqi)
+ mov_optab->handlers[(int) QImode].insn_code = CODE_FOR_movqi;
+#endif
+#ifdef HAVE_movhi
+ if (HAVE_movhi)
+ mov_optab->handlers[(int) HImode].insn_code = CODE_FOR_movhi;
+#endif
+#ifdef HAVE_movpsi
+ if (HAVE_movpsi)
+ mov_optab->handlers[(int) PSImode].insn_code = CODE_FOR_movpsi;
+#endif
+#ifdef HAVE_movsi
+ if (HAVE_movsi)
+ mov_optab->handlers[(int) SImode].insn_code = CODE_FOR_movsi;
+#endif
+#ifdef HAVE_movdi
+ if (HAVE_movdi)
+ mov_optab->handlers[(int) DImode].insn_code = CODE_FOR_movdi;
+#endif
+#ifdef HAVE_movti
+ if (HAVE_movti)
+ mov_optab->handlers[(int) TImode].insn_code = CODE_FOR_movti;
+#endif
+#ifdef HAVE_movsf
+ if (HAVE_movsf)
+ mov_optab->handlers[(int) SFmode].insn_code = CODE_FOR_movsf;
+#endif
+#ifdef HAVE_movdf
+ if (HAVE_movdf)
+ mov_optab->handlers[(int) DFmode].insn_code = CODE_FOR_movdf;
+#endif
+#ifdef HAVE_movtf
+ if (HAVE_movtf)
+ mov_optab->handlers[(int) TFmode].insn_code = CODE_FOR_movtf;
+#endif
+#ifdef HAVE_movcc
+ if (HAVE_movcc)
+ mov_optab->handlers[(int) CCmode].insn_code = CODE_FOR_movcc;
+#endif
+
+#ifdef EXTRA_CC_MODES
+ init_mov_optab ();
+#endif
+
+#ifdef HAVE_movstrictqi
+ if (HAVE_movstrictqi)
+ movstrict_optab->handlers[(int) QImode].insn_code = CODE_FOR_movstrictqi;
+#endif
+#ifdef HAVE_movstricthi
+ if (HAVE_movstricthi)
+ movstrict_optab->handlers[(int) HImode].insn_code = CODE_FOR_movstricthi;
+#endif
+#ifdef HAVE_movstrictpsi
+ if (HAVE_movstrictpsi)
+ movstrict_optab->handlers[(int) PSImode].insn_code = CODE_FOR_movstrictpsi;
+#endif
+#ifdef HAVE_movstrictsi
+ if (HAVE_movstrictsi)
+ movstrict_optab->handlers[(int) SImode].insn_code = CODE_FOR_movstrictsi;
+#endif
+#ifdef HAVE_movstrictdi
+ if (HAVE_movstrictdi)
+ movstrict_optab->handlers[(int) DImode].insn_code = CODE_FOR_movstrictdi;
+#endif
+#ifdef HAVE_movstrictti
+ if (HAVE_movstrictti)
+ movstrict_optab->handlers[(int) TImode].insn_code = CODE_FOR_movstrictti;
+#endif
+
+#ifdef HAVE_cmpqi
+ if (HAVE_cmpqi)
+ cmp_optab->handlers[(int) QImode].insn_code = CODE_FOR_cmpqi;
+#endif
+#ifdef HAVE_cmphi
+ if (HAVE_cmphi)
+ cmp_optab->handlers[(int) HImode].insn_code = CODE_FOR_cmphi;
+#endif
+#ifdef HAVE_cmppsi
+ if (HAVE_cmppsi)
+ cmp_optab->handlers[(int) PSImode].insn_code = CODE_FOR_cmppsi;
+#endif
+#ifdef HAVE_cmpsi
+ if (HAVE_cmpsi)
+ cmp_optab->handlers[(int) SImode].insn_code = CODE_FOR_cmpsi;
+#endif
+#ifdef HAVE_cmpdi
+ if (HAVE_cmpdi)
+ cmp_optab->handlers[(int) DImode].insn_code = CODE_FOR_cmpdi;
+#endif
+#ifdef HAVE_cmpti
+ if (HAVE_cmpti)
+ cmp_optab->handlers[(int) TImode].insn_code = CODE_FOR_cmpti;
+#endif
+#ifdef HAVE_cmpsf
+ if (HAVE_cmpsf)
+ cmp_optab->handlers[(int) SFmode].insn_code = CODE_FOR_cmpsf;
+#endif
+#ifdef HAVE_cmpdf
+ if (HAVE_cmpdf)
+ cmp_optab->handlers[(int) DFmode].insn_code = CODE_FOR_cmpdf;
+#endif
+#ifdef HAVE_cmptf
+ if (HAVE_cmptf)
+ cmp_optab->handlers[(int) TFmode].insn_code = CODE_FOR_cmptf;
+#endif
+#ifdef HAVE_tstqi
+ if (HAVE_tstqi)
+ tst_optab->handlers[(int) QImode].insn_code = CODE_FOR_tstqi;
+#endif
+#ifdef HAVE_tsthi
+ if (HAVE_tsthi)
+ tst_optab->handlers[(int) HImode].insn_code = CODE_FOR_tsthi;
+#endif
+#ifdef HAVE_tstpsi
+ if (HAVE_tstpsi)
+ tst_optab->handlers[(int) PSImode].insn_code = CODE_FOR_tstpsi;
+#endif
+#ifdef HAVE_tstsi
+ if (HAVE_tstsi)
+ tst_optab->handlers[(int) SImode].insn_code = CODE_FOR_tstsi;
+#endif
+#ifdef HAVE_tstdi
+ if (HAVE_tstdi)
+ tst_optab->handlers[(int) DImode].insn_code = CODE_FOR_tstdi;
+#endif
+#ifdef HAVE_tstti
+ if (HAVE_tstti)
+ tst_optab->handlers[(int) TImode].insn_code = CODE_FOR_tstti;
+#endif
+#ifdef HAVE_tstsf
+ if (HAVE_tstsf)
+ tst_optab->handlers[(int) SFmode].insn_code = CODE_FOR_tstsf;
+#endif
+#ifdef HAVE_tstdf
+ if (HAVE_tstdf)
+ tst_optab->handlers[(int) DFmode].insn_code = CODE_FOR_tstdf;
+#endif
+#ifdef HAVE_tsttf
+ if (HAVE_tsttf)
+ tst_optab->handlers[(int) TFmode].insn_code = CODE_FOR_tsttf;
+#endif
+ /* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */
+ cmp_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__cmpdi2");
+ ucmp_optab->handlers[(int) DImode].libfunc
+ = gen_rtx (SYMBOL_REF, Pmode, "__ucmpdi2");
+
+#ifdef HAVE_beq
+ if (HAVE_beq)
+ bcc_gen_fctn[(int) EQ] = gen_beq;
+#endif
+#ifdef HAVE_bne
+ if (HAVE_bne)
+ bcc_gen_fctn[(int) NE] = gen_bne;
+#endif
+#ifdef HAVE_bgt
+ if (HAVE_bgt)
+ bcc_gen_fctn[(int) GT] = gen_bgt;
+#endif
+#ifdef HAVE_bge
+ if (HAVE_bge)
+ bcc_gen_fctn[(int) GE] = gen_bge;
+#endif
+#ifdef HAVE_bgtu
+ if (HAVE_bgtu)
+ bcc_gen_fctn[(int) GTU] = gen_bgtu;
+#endif
+#ifdef HAVE_bgeu
+ if (HAVE_bgeu)
+ bcc_gen_fctn[(int) GEU] = gen_bgeu;
+#endif
+#ifdef HAVE_blt
+ if (HAVE_blt)
+ bcc_gen_fctn[(int) LT] = gen_blt;
+#endif
+#ifdef HAVE_ble
+ if (HAVE_ble)
+ bcc_gen_fctn[(int) LE] = gen_ble;
+#endif
+#ifdef HAVE_bltu
+ if (HAVE_bltu)
+ bcc_gen_fctn[(int) LTU] = gen_bltu;
+#endif
+#ifdef HAVE_bleu
+ if (HAVE_bleu)
+ bcc_gen_fctn[(int) LEU] = gen_bleu;
+#endif
+
+ for (i = 0; i < NUM_RTX_CODE; i++)
+ setcc_gen_code[i] = CODE_FOR_nothing;
+
+#ifdef HAVE_seq
+ if (HAVE_seq)
+ setcc_gen_code[(int) EQ] = CODE_FOR_seq;
+#endif
+#ifdef HAVE_sne
+ if (HAVE_sne)
+ setcc_gen_code[(int) NE] = CODE_FOR_sne;
+#endif
+#ifdef HAVE_sgt
+ if (HAVE_sgt)
+ setcc_gen_code[(int) GT] = CODE_FOR_sgt;
+#endif
+#ifdef HAVE_sge
+ if (HAVE_sge)
+ setcc_gen_code[(int) GE] = CODE_FOR_sge;
+#endif
+#ifdef HAVE_sgtu
+ if (HAVE_sgtu)
+ setcc_gen_code[(int) GTU] = CODE_FOR_sgtu;
+#endif
+#ifdef HAVE_sgeu
+ if (HAVE_sgeu)
+ setcc_gen_code[(int) GEU] = CODE_FOR_sgeu;
+#endif
+#ifdef HAVE_slt
+ if (HAVE_slt)
+ setcc_gen_code[(int) LT] = CODE_FOR_slt;
+#endif
+#ifdef HAVE_sle
+ if (HAVE_sle)
+ setcc_gen_code[(int) LE] = CODE_FOR_sle;
+#endif
+#ifdef HAVE_sltu
+ if (HAVE_sltu)
+ setcc_gen_code[(int) LTU] = CODE_FOR_sltu;
+#endif
+#ifdef HAVE_sleu
+ if (HAVE_sleu)
+ setcc_gen_code[(int) LEU] = CODE_FOR_sleu;
+#endif
+
+ extendsfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__extendsfdf2");
+ truncdfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__truncdfsf2");
+ 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, "bcmp");
+ memset_libfunc = gen_rtx (SYMBOL_REF, Pmode, "memset");
+ bzero_libfunc = gen_rtx (SYMBOL_REF, Pmode, "bzero");
+ 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");
+}