optabs.c revision 52284
118334Speter/* Expand the basic unary and binary arithmetic operations, for GNU compiler. 252284Sobrien Copyright (C) 1987, 88, 92-98, 1999 Free Software Foundation, Inc. 318334Speter 418334SpeterThis file is part of GNU CC. 518334Speter 618334SpeterGNU CC is free software; you can redistribute it and/or modify 718334Speterit under the terms of the GNU General Public License as published by 818334Speterthe Free Software Foundation; either version 2, or (at your option) 918334Speterany later version. 1018334Speter 1118334SpeterGNU CC is distributed in the hope that it will be useful, 1218334Speterbut WITHOUT ANY WARRANTY; without even the implied warranty of 1318334SpeterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1418334SpeterGNU General Public License for more details. 1518334Speter 1618334SpeterYou should have received a copy of the GNU General Public License 1718334Speteralong with GNU CC; see the file COPYING. If not, write to 1818334Speterthe Free Software Foundation, 59 Temple Place - Suite 330, 1918334SpeterBoston, MA 02111-1307, USA. */ 2018334Speter 2118334Speter 2218334Speter#include "config.h" 2350397Sobrien#include "system.h" 2452284Sobrien#include "toplev.h" 2552284Sobrien 2652284Sobrien/* Include insn-config.h before expr.h so that HAVE_conditional_move 2752284Sobrien is properly defined. */ 2852284Sobrien#include "insn-config.h" 2918334Speter#include "rtl.h" 3018334Speter#include "tree.h" 3118334Speter#include "flags.h" 3218334Speter#include "insn-flags.h" 3318334Speter#include "insn-codes.h" 3418334Speter#include "expr.h" 3518334Speter#include "recog.h" 3618334Speter#include "reload.h" 3718334Speter 3818334Speter/* Each optab contains info on how this target machine 3918334Speter can perform a particular operation 4018334Speter for all sizes and kinds of operands. 4118334Speter 4218334Speter The operation to be performed is often specified 4318334Speter by passing one of these optabs as an argument. 4418334Speter 4518334Speter See expr.h for documentation of these optabs. */ 4618334Speter 4718334Speteroptab add_optab; 4818334Speteroptab sub_optab; 4918334Speteroptab smul_optab; 5018334Speteroptab smul_highpart_optab; 5118334Speteroptab umul_highpart_optab; 5218334Speteroptab smul_widen_optab; 5318334Speteroptab umul_widen_optab; 5418334Speteroptab sdiv_optab; 5518334Speteroptab sdivmod_optab; 5618334Speteroptab udiv_optab; 5718334Speteroptab udivmod_optab; 5818334Speteroptab smod_optab; 5918334Speteroptab umod_optab; 6018334Speteroptab flodiv_optab; 6118334Speteroptab ftrunc_optab; 6218334Speteroptab and_optab; 6318334Speteroptab ior_optab; 6418334Speteroptab xor_optab; 6518334Speteroptab ashl_optab; 6618334Speteroptab lshr_optab; 6718334Speteroptab ashr_optab; 6818334Speteroptab rotl_optab; 6918334Speteroptab rotr_optab; 7018334Speteroptab smin_optab; 7118334Speteroptab smax_optab; 7218334Speteroptab umin_optab; 7318334Speteroptab umax_optab; 7418334Speter 7518334Speteroptab mov_optab; 7618334Speteroptab movstrict_optab; 7718334Speter 7818334Speteroptab neg_optab; 7918334Speteroptab abs_optab; 8018334Speteroptab one_cmpl_optab; 8118334Speteroptab ffs_optab; 8218334Speteroptab sqrt_optab; 8318334Speteroptab sin_optab; 8418334Speteroptab cos_optab; 8518334Speter 8618334Speteroptab cmp_optab; 8718334Speteroptab ucmp_optab; /* Used only for libcalls for unsigned comparisons. */ 8818334Speteroptab tst_optab; 8918334Speter 9018334Speteroptab strlen_optab; 9118334Speter 9218334Speter/* Tables of patterns for extending one integer mode to another. */ 9318334Speterenum insn_code extendtab[MAX_MACHINE_MODE][MAX_MACHINE_MODE][2]; 9418334Speter 9550397Sobrien/* Tables of patterns for converting between fixed and floating point. */ 9618334Speterenum insn_code fixtab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2]; 9718334Speterenum insn_code fixtrunctab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2]; 9818334Speterenum insn_code floattab[NUM_MACHINE_MODES][NUM_MACHINE_MODES][2]; 9918334Speter 10018334Speter/* Contains the optab used for each rtx code. */ 10118334Speteroptab code_to_optab[NUM_RTX_CODE + 1]; 10218334Speter 10318334Speter/* SYMBOL_REF rtx's for the library functions that are called 10418334Speter implicitly and not via optabs. */ 10518334Speter 10618334Speterrtx extendsfdf2_libfunc; 10718334Speterrtx extendsfxf2_libfunc; 10818334Speterrtx extendsftf2_libfunc; 10918334Speterrtx extenddfxf2_libfunc; 11018334Speterrtx extenddftf2_libfunc; 11118334Speter 11218334Speterrtx truncdfsf2_libfunc; 11318334Speterrtx truncxfsf2_libfunc; 11418334Speterrtx trunctfsf2_libfunc; 11518334Speterrtx truncxfdf2_libfunc; 11618334Speterrtx trunctfdf2_libfunc; 11718334Speter 11818334Speterrtx memcpy_libfunc; 11918334Speterrtx bcopy_libfunc; 12018334Speterrtx memcmp_libfunc; 12118334Speterrtx bcmp_libfunc; 12218334Speterrtx memset_libfunc; 12318334Speterrtx bzero_libfunc; 12418334Speter 12550397Sobrienrtx throw_libfunc; 12652284Sobrienrtx rethrow_libfunc; 12750397Sobrienrtx sjthrow_libfunc; 12850397Sobrienrtx sjpopnthrow_libfunc; 12950397Sobrienrtx terminate_libfunc; 13050397Sobrienrtx setjmp_libfunc; 13150397Sobrienrtx longjmp_libfunc; 13252284Sobrienrtx eh_rtime_match_libfunc; 13350397Sobrien 13418334Speterrtx eqhf2_libfunc; 13518334Speterrtx nehf2_libfunc; 13618334Speterrtx gthf2_libfunc; 13718334Speterrtx gehf2_libfunc; 13818334Speterrtx lthf2_libfunc; 13918334Speterrtx lehf2_libfunc; 14018334Speter 14118334Speterrtx eqsf2_libfunc; 14218334Speterrtx nesf2_libfunc; 14318334Speterrtx gtsf2_libfunc; 14418334Speterrtx gesf2_libfunc; 14518334Speterrtx ltsf2_libfunc; 14618334Speterrtx lesf2_libfunc; 14718334Speter 14818334Speterrtx eqdf2_libfunc; 14918334Speterrtx nedf2_libfunc; 15018334Speterrtx gtdf2_libfunc; 15118334Speterrtx gedf2_libfunc; 15218334Speterrtx ltdf2_libfunc; 15318334Speterrtx ledf2_libfunc; 15418334Speter 15518334Speterrtx eqxf2_libfunc; 15618334Speterrtx nexf2_libfunc; 15718334Speterrtx gtxf2_libfunc; 15818334Speterrtx gexf2_libfunc; 15918334Speterrtx ltxf2_libfunc; 16018334Speterrtx lexf2_libfunc; 16118334Speter 16218334Speterrtx eqtf2_libfunc; 16318334Speterrtx netf2_libfunc; 16418334Speterrtx gttf2_libfunc; 16518334Speterrtx getf2_libfunc; 16618334Speterrtx lttf2_libfunc; 16718334Speterrtx letf2_libfunc; 16818334Speter 16918334Speterrtx floatsisf_libfunc; 17018334Speterrtx floatdisf_libfunc; 17118334Speterrtx floattisf_libfunc; 17218334Speter 17318334Speterrtx floatsidf_libfunc; 17418334Speterrtx floatdidf_libfunc; 17518334Speterrtx floattidf_libfunc; 17618334Speter 17718334Speterrtx floatsixf_libfunc; 17818334Speterrtx floatdixf_libfunc; 17918334Speterrtx floattixf_libfunc; 18018334Speter 18118334Speterrtx floatsitf_libfunc; 18218334Speterrtx floatditf_libfunc; 18318334Speterrtx floattitf_libfunc; 18418334Speter 18518334Speterrtx fixsfsi_libfunc; 18618334Speterrtx fixsfdi_libfunc; 18718334Speterrtx fixsfti_libfunc; 18818334Speter 18918334Speterrtx fixdfsi_libfunc; 19018334Speterrtx fixdfdi_libfunc; 19118334Speterrtx fixdfti_libfunc; 19218334Speter 19318334Speterrtx fixxfsi_libfunc; 19418334Speterrtx fixxfdi_libfunc; 19518334Speterrtx fixxfti_libfunc; 19618334Speter 19718334Speterrtx fixtfsi_libfunc; 19818334Speterrtx fixtfdi_libfunc; 19918334Speterrtx fixtfti_libfunc; 20018334Speter 20118334Speterrtx fixunssfsi_libfunc; 20218334Speterrtx fixunssfdi_libfunc; 20318334Speterrtx fixunssfti_libfunc; 20418334Speter 20518334Speterrtx fixunsdfsi_libfunc; 20618334Speterrtx fixunsdfdi_libfunc; 20718334Speterrtx fixunsdfti_libfunc; 20818334Speter 20918334Speterrtx fixunsxfsi_libfunc; 21018334Speterrtx fixunsxfdi_libfunc; 21118334Speterrtx fixunsxfti_libfunc; 21218334Speter 21318334Speterrtx fixunstfsi_libfunc; 21418334Speterrtx fixunstfdi_libfunc; 21518334Speterrtx fixunstfti_libfunc; 21618334Speter 21750397Sobrienrtx chkr_check_addr_libfunc; 21850397Sobrienrtx chkr_set_right_libfunc; 21950397Sobrienrtx chkr_copy_bitmap_libfunc; 22050397Sobrienrtx chkr_check_exec_libfunc; 22150397Sobrienrtx chkr_check_str_libfunc; 22250397Sobrien 22352284Sobrienrtx profile_function_entry_libfunc; 22452284Sobrienrtx profile_function_exit_libfunc; 22552284Sobrien 22618334Speter/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) 22718334Speter gives the gen_function to make a branch to test that condition. */ 22818334Speter 22918334Speterrtxfun bcc_gen_fctn[NUM_RTX_CODE]; 23018334Speter 23118334Speter/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) 23218334Speter gives the insn code to make a store-condition insn 23318334Speter to test that condition. */ 23418334Speter 23518334Speterenum insn_code setcc_gen_code[NUM_RTX_CODE]; 23618334Speter 23718334Speter#ifdef HAVE_conditional_move 23818334Speter/* Indexed by the machine mode, gives the insn code to make a conditional 23918334Speter move insn. This is not indexed by the rtx-code like bcc_gen_fctn and 24018334Speter setcc_gen_code to cut down on the number of named patterns. Consider a day 24118334Speter when a lot more rtx codes are conditional (eg: for the ARM). */ 24218334Speter 24318334Speterenum insn_code movcc_gen_code[NUM_MACHINE_MODES]; 24418334Speter#endif 24518334Speter 24618334Speterstatic int add_equal_note PROTO((rtx, rtx, enum rtx_code, rtx, rtx)); 24718334Speterstatic rtx widen_operand PROTO((rtx, enum machine_mode, 24818334Speter enum machine_mode, int, int)); 24952284Sobrienstatic int expand_cmplxdiv_straight PROTO((rtx, rtx, rtx, rtx, 25052284Sobrien rtx, rtx, enum machine_mode, 25152284Sobrien int, enum optab_methods, 25252284Sobrien enum mode_class, optab)); 25352284Sobrienstatic int expand_cmplxdiv_wide PROTO((rtx, rtx, rtx, rtx, 25452284Sobrien rtx, rtx, enum machine_mode, 25552284Sobrien int, enum optab_methods, 25652284Sobrien enum mode_class, optab)); 25718334Speterstatic enum insn_code can_fix_p PROTO((enum machine_mode, enum machine_mode, 25818334Speter int, int *)); 25918334Speterstatic enum insn_code can_float_p PROTO((enum machine_mode, enum machine_mode, 26018334Speter int)); 26118334Speterstatic rtx ftruncify PROTO((rtx)); 26218334Speterstatic optab init_optab PROTO((enum rtx_code)); 26352284Sobrienstatic void init_libfuncs PROTO((optab, int, int, const char *, int)); 26452284Sobrienstatic void init_integral_libfuncs PROTO((optab, const char *, int)); 26552284Sobrienstatic void init_floating_libfuncs PROTO((optab, const char *, int)); 26650397Sobrien#ifdef HAVE_conditional_trap 26750397Sobrienstatic void init_traps PROTO((void)); 26850397Sobrien#endif 26918334Speter 27018334Speter/* Add a REG_EQUAL note to the last insn in SEQ. TARGET is being set to 27118334Speter the result of operation CODE applied to OP0 (and OP1 if it is a binary 27218334Speter operation). 27318334Speter 27418334Speter If the last insn does not set TARGET, don't do anything, but return 1. 27518334Speter 27618334Speter If a previous insn sets TARGET and TARGET is one of OP0 or OP1, 27718334Speter don't add the REG_EQUAL note but return 0. Our caller can then try 27818334Speter again, ensuring that TARGET is not one of the operands. */ 27918334Speter 28018334Speterstatic int 28118334Speteradd_equal_note (seq, target, code, op0, op1) 28218334Speter rtx seq; 28318334Speter rtx target; 28418334Speter enum rtx_code code; 28518334Speter rtx op0, op1; 28618334Speter{ 28718334Speter rtx set; 28818334Speter int i; 28918334Speter rtx note; 29018334Speter 29118334Speter if ((GET_RTX_CLASS (code) != '1' && GET_RTX_CLASS (code) != '2' 29218334Speter && GET_RTX_CLASS (code) != 'c' && GET_RTX_CLASS (code) != '<') 29318334Speter || GET_CODE (seq) != SEQUENCE 29418334Speter || (set = single_set (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1))) == 0 29518334Speter || GET_CODE (target) == ZERO_EXTRACT 29618334Speter || (! rtx_equal_p (SET_DEST (set), target) 29718334Speter /* For a STRICT_LOW_PART, the REG_NOTE applies to what is inside the 29818334Speter SUBREG. */ 29918334Speter && (GET_CODE (SET_DEST (set)) != STRICT_LOW_PART 30018334Speter || ! rtx_equal_p (SUBREG_REG (XEXP (SET_DEST (set), 0)), 30118334Speter target)))) 30218334Speter return 1; 30318334Speter 30418334Speter /* If TARGET is in OP0 or OP1, check if anything in SEQ sets TARGET 30518334Speter besides the last insn. */ 30618334Speter if (reg_overlap_mentioned_p (target, op0) 30718334Speter || (op1 && reg_overlap_mentioned_p (target, op1))) 30818334Speter for (i = XVECLEN (seq, 0) - 2; i >= 0; i--) 30918334Speter if (reg_set_p (target, XVECEXP (seq, 0, i))) 31018334Speter return 0; 31118334Speter 31218334Speter if (GET_RTX_CLASS (code) == '1') 31350397Sobrien note = gen_rtx_fmt_e (code, GET_MODE (target), copy_rtx (op0)); 31418334Speter else 31550397Sobrien note = gen_rtx_fmt_ee (code, GET_MODE (target), copy_rtx (op0), copy_rtx (op1)); 31618334Speter 31752284Sobrien set_unique_reg_note (XVECEXP (seq, 0, XVECLEN (seq, 0) - 1), REG_EQUAL, note); 31818334Speter 31918334Speter return 1; 32018334Speter} 32118334Speter 32218334Speter/* Widen OP to MODE and return the rtx for the widened operand. UNSIGNEDP 32318334Speter says whether OP is signed or unsigned. NO_EXTEND is nonzero if we need 32418334Speter not actually do a sign-extend or zero-extend, but can leave the 32518334Speter higher-order bits of the result rtx undefined, for example, in the case 32618334Speter of logical operations, but not right shifts. */ 32718334Speter 32818334Speterstatic rtx 32918334Speterwiden_operand (op, mode, oldmode, unsignedp, no_extend) 33018334Speter rtx op; 33118334Speter enum machine_mode mode, oldmode; 33218334Speter int unsignedp; 33318334Speter int no_extend; 33418334Speter{ 33518334Speter rtx result; 33618334Speter 33718334Speter /* If we must extend do so. If OP is either a constant or a SUBREG 33818334Speter for a promoted object, also extend since it will be more efficient to 33918334Speter do so. */ 34018334Speter if (! no_extend 34118334Speter || GET_MODE (op) == VOIDmode 34218334Speter || (GET_CODE (op) == SUBREG && SUBREG_PROMOTED_VAR_P (op))) 34318334Speter return convert_modes (mode, oldmode, op, unsignedp); 34418334Speter 34518334Speter /* If MODE is no wider than a single word, we return a paradoxical 34618334Speter SUBREG. */ 34718334Speter if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD) 34850397Sobrien return gen_rtx_SUBREG (mode, force_reg (GET_MODE (op), op), 0); 34918334Speter 35018334Speter /* Otherwise, get an object of MODE, clobber it, and set the low-order 35118334Speter part to OP. */ 35218334Speter 35318334Speter result = gen_reg_rtx (mode); 35450397Sobrien emit_insn (gen_rtx_CLOBBER (VOIDmode, result)); 35518334Speter emit_move_insn (gen_lowpart (GET_MODE (op), result), op); 35618334Speter return result; 35718334Speter} 35818334Speter 35952284Sobrien/* Generate code to perform a straightforward complex divide. */ 36052284Sobrien 36152284Sobrienstatic int 36252284Sobrienexpand_cmplxdiv_straight (real0, real1, imag0, imag1, realr, imagr, submode, 36352284Sobrien unsignedp, methods, class, binoptab) 36452284Sobrien rtx real0, real1, imag0, imag1, realr, imagr; 36552284Sobrien enum machine_mode submode; 36652284Sobrien int unsignedp; 36752284Sobrien enum optab_methods methods; 36852284Sobrien enum mode_class class; 36952284Sobrien optab binoptab; 37052284Sobrien{ 37152284Sobrien rtx divisor; 37252284Sobrien rtx real_t, imag_t; 37352284Sobrien rtx temp1, temp2; 37452284Sobrien rtx res; 37552284Sobrien 37652284Sobrien /* Don't fetch these from memory more than once. */ 37752284Sobrien real0 = force_reg (submode, real0); 37852284Sobrien real1 = force_reg (submode, real1); 37952284Sobrien 38052284Sobrien if (imag0 != 0) 38152284Sobrien imag0 = force_reg (submode, imag0); 38252284Sobrien 38352284Sobrien imag1 = force_reg (submode, imag1); 38452284Sobrien 38552284Sobrien /* Divisor: c*c + d*d. */ 38652284Sobrien temp1 = expand_binop (submode, smul_optab, real1, real1, 38752284Sobrien NULL_RTX, unsignedp, methods); 38852284Sobrien 38952284Sobrien temp2 = expand_binop (submode, smul_optab, imag1, imag1, 39052284Sobrien NULL_RTX, unsignedp, methods); 39152284Sobrien 39252284Sobrien if (temp1 == 0 || temp2 == 0) 39352284Sobrien return 0; 39452284Sobrien 39552284Sobrien divisor = expand_binop (submode, add_optab, temp1, temp2, 39652284Sobrien NULL_RTX, unsignedp, methods); 39752284Sobrien if (divisor == 0) 39852284Sobrien return 0; 39952284Sobrien 40052284Sobrien if (imag0 == 0) 40152284Sobrien { 40252284Sobrien /* Mathematically, ((a)(c-id))/divisor. */ 40352284Sobrien /* Computationally, (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)). */ 40452284Sobrien 40552284Sobrien /* Calculate the dividend. */ 40652284Sobrien real_t = expand_binop (submode, smul_optab, real0, real1, 40752284Sobrien NULL_RTX, unsignedp, methods); 40852284Sobrien 40952284Sobrien imag_t = expand_binop (submode, smul_optab, real0, imag1, 41052284Sobrien NULL_RTX, unsignedp, methods); 41152284Sobrien 41252284Sobrien if (real_t == 0 || imag_t == 0) 41352284Sobrien return 0; 41452284Sobrien 41552284Sobrien imag_t = expand_unop (submode, neg_optab, imag_t, 41652284Sobrien NULL_RTX, unsignedp); 41752284Sobrien } 41852284Sobrien else 41952284Sobrien { 42052284Sobrien /* Mathematically, ((a+ib)(c-id))/divider. */ 42152284Sobrien /* Calculate the dividend. */ 42252284Sobrien temp1 = expand_binop (submode, smul_optab, real0, real1, 42352284Sobrien NULL_RTX, unsignedp, methods); 42452284Sobrien 42552284Sobrien temp2 = expand_binop (submode, smul_optab, imag0, imag1, 42652284Sobrien NULL_RTX, unsignedp, methods); 42752284Sobrien 42852284Sobrien if (temp1 == 0 || temp2 == 0) 42952284Sobrien return 0; 43052284Sobrien 43152284Sobrien real_t = expand_binop (submode, add_optab, temp1, temp2, 43252284Sobrien NULL_RTX, unsignedp, methods); 43352284Sobrien 43452284Sobrien temp1 = expand_binop (submode, smul_optab, imag0, real1, 43552284Sobrien NULL_RTX, unsignedp, methods); 43652284Sobrien 43752284Sobrien temp2 = expand_binop (submode, smul_optab, real0, imag1, 43852284Sobrien NULL_RTX, unsignedp, methods); 43952284Sobrien 44052284Sobrien if (temp1 == 0 || temp2 == 0) 44152284Sobrien return 0; 44252284Sobrien 44352284Sobrien imag_t = expand_binop (submode, sub_optab, temp1, temp2, 44452284Sobrien NULL_RTX, unsignedp, methods); 44552284Sobrien 44652284Sobrien if (real_t == 0 || imag_t == 0) 44752284Sobrien return 0; 44852284Sobrien } 44952284Sobrien 45052284Sobrien if (class == MODE_COMPLEX_FLOAT) 45152284Sobrien res = expand_binop (submode, binoptab, real_t, divisor, 45252284Sobrien realr, unsignedp, methods); 45352284Sobrien else 45452284Sobrien res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 45552284Sobrien real_t, divisor, realr, unsignedp); 45652284Sobrien 45752284Sobrien if (res == 0) 45852284Sobrien return 0; 45952284Sobrien 46052284Sobrien if (res != realr) 46152284Sobrien emit_move_insn (realr, res); 46252284Sobrien 46352284Sobrien if (class == MODE_COMPLEX_FLOAT) 46452284Sobrien res = expand_binop (submode, binoptab, imag_t, divisor, 46552284Sobrien imagr, unsignedp, methods); 46652284Sobrien else 46752284Sobrien res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 46852284Sobrien imag_t, divisor, imagr, unsignedp); 46952284Sobrien 47052284Sobrien if (res == 0) 47152284Sobrien return 0; 47252284Sobrien 47352284Sobrien if (res != imagr) 47452284Sobrien emit_move_insn (imagr, res); 47552284Sobrien 47652284Sobrien return 1; 47752284Sobrien} 47852284Sobrien 47952284Sobrien/* Generate code to perform a wide-input-range-acceptable complex divide. */ 48052284Sobrien 48152284Sobrienstatic int 48252284Sobrienexpand_cmplxdiv_wide (real0, real1, imag0, imag1, realr, imagr, submode, 48352284Sobrien unsignedp, methods, class, binoptab) 48452284Sobrien rtx real0, real1, imag0, imag1, realr, imagr; 48552284Sobrien enum machine_mode submode; 48652284Sobrien int unsignedp; 48752284Sobrien enum optab_methods methods; 48852284Sobrien enum mode_class class; 48952284Sobrien optab binoptab; 49052284Sobrien{ 49152284Sobrien rtx ratio, divisor; 49252284Sobrien rtx real_t, imag_t; 49352284Sobrien rtx temp1, temp2, lab1, lab2; 49452284Sobrien enum machine_mode mode; 49552284Sobrien int align; 49652284Sobrien rtx res; 49752284Sobrien 49852284Sobrien /* Don't fetch these from memory more than once. */ 49952284Sobrien real0 = force_reg (submode, real0); 50052284Sobrien real1 = force_reg (submode, real1); 50152284Sobrien 50252284Sobrien if (imag0 != 0) 50352284Sobrien imag0 = force_reg (submode, imag0); 50452284Sobrien 50552284Sobrien imag1 = force_reg (submode, imag1); 50652284Sobrien 50752284Sobrien /* XXX What's an "unsigned" complex number? */ 50852284Sobrien if (unsignedp) 50952284Sobrien { 51052284Sobrien temp1 = real1; 51152284Sobrien temp2 = imag1; 51252284Sobrien } 51352284Sobrien else 51452284Sobrien { 51552284Sobrien temp1 = expand_abs (submode, real1, NULL_RTX, 1); 51652284Sobrien temp2 = expand_abs (submode, imag1, NULL_RTX, 1); 51752284Sobrien } 51852284Sobrien 51952284Sobrien if (temp1 == 0 || temp2 == 0) 52052284Sobrien return 0; 52152284Sobrien 52252284Sobrien mode = GET_MODE (temp1); 52352284Sobrien align = GET_MODE_ALIGNMENT (mode); 52452284Sobrien lab1 = gen_label_rtx (); 52552284Sobrien emit_cmp_and_jump_insns (temp1, temp2, LT, NULL_RTX, 52652284Sobrien mode, unsignedp, align, lab1); 52752284Sobrien 52852284Sobrien /* |c| >= |d|; use ratio d/c to scale dividend and divisor. */ 52952284Sobrien 53052284Sobrien if (class == MODE_COMPLEX_FLOAT) 53152284Sobrien ratio = expand_binop (submode, binoptab, imag1, real1, 53252284Sobrien NULL_RTX, unsignedp, methods); 53352284Sobrien else 53452284Sobrien ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode, 53552284Sobrien imag1, real1, NULL_RTX, unsignedp); 53652284Sobrien 53752284Sobrien if (ratio == 0) 53852284Sobrien return 0; 53952284Sobrien 54052284Sobrien /* Calculate divisor. */ 54152284Sobrien 54252284Sobrien temp1 = expand_binop (submode, smul_optab, imag1, ratio, 54352284Sobrien NULL_RTX, unsignedp, methods); 54452284Sobrien 54552284Sobrien if (temp1 == 0) 54652284Sobrien return 0; 54752284Sobrien 54852284Sobrien divisor = expand_binop (submode, add_optab, temp1, real1, 54952284Sobrien NULL_RTX, unsignedp, methods); 55052284Sobrien 55152284Sobrien if (divisor == 0) 55252284Sobrien return 0; 55352284Sobrien 55452284Sobrien /* Calculate dividend. */ 55552284Sobrien 55652284Sobrien if (imag0 == 0) 55752284Sobrien { 55852284Sobrien real_t = real0; 55952284Sobrien 56052284Sobrien /* Compute a / (c+id) as a / (c+d(d/c)) + i (-a(d/c)) / (c+d(d/c)). */ 56152284Sobrien 56252284Sobrien imag_t = expand_binop (submode, smul_optab, real0, ratio, 56352284Sobrien NULL_RTX, unsignedp, methods); 56452284Sobrien 56552284Sobrien if (imag_t == 0) 56652284Sobrien return 0; 56752284Sobrien 56852284Sobrien imag_t = expand_unop (submode, neg_optab, imag_t, 56952284Sobrien NULL_RTX, unsignedp); 57052284Sobrien 57152284Sobrien if (real_t == 0 || imag_t == 0) 57252284Sobrien return 0; 57352284Sobrien } 57452284Sobrien else 57552284Sobrien { 57652284Sobrien /* Compute (a+ib)/(c+id) as 57752284Sobrien (a+b(d/c))/(c+d(d/c) + i(b-a(d/c))/(c+d(d/c)). */ 57852284Sobrien 57952284Sobrien temp1 = expand_binop (submode, smul_optab, imag0, ratio, 58052284Sobrien NULL_RTX, unsignedp, methods); 58152284Sobrien 58252284Sobrien if (temp1 == 0) 58352284Sobrien return 0; 58452284Sobrien 58552284Sobrien real_t = expand_binop (submode, add_optab, temp1, real0, 58652284Sobrien NULL_RTX, unsignedp, methods); 58752284Sobrien 58852284Sobrien temp1 = expand_binop (submode, smul_optab, real0, ratio, 58952284Sobrien NULL_RTX, unsignedp, methods); 59052284Sobrien 59152284Sobrien if (temp1 == 0) 59252284Sobrien return 0; 59352284Sobrien 59452284Sobrien imag_t = expand_binop (submode, sub_optab, imag0, temp1, 59552284Sobrien NULL_RTX, unsignedp, methods); 59652284Sobrien 59752284Sobrien if (real_t == 0 || imag_t == 0) 59852284Sobrien return 0; 59952284Sobrien } 60052284Sobrien 60152284Sobrien if (class == MODE_COMPLEX_FLOAT) 60252284Sobrien res = expand_binop (submode, binoptab, real_t, divisor, 60352284Sobrien realr, unsignedp, methods); 60452284Sobrien else 60552284Sobrien res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 60652284Sobrien real_t, divisor, realr, unsignedp); 60752284Sobrien 60852284Sobrien if (res == 0) 60952284Sobrien return 0; 61052284Sobrien 61152284Sobrien if (res != realr) 61252284Sobrien emit_move_insn (realr, res); 61352284Sobrien 61452284Sobrien if (class == MODE_COMPLEX_FLOAT) 61552284Sobrien res = expand_binop (submode, binoptab, imag_t, divisor, 61652284Sobrien imagr, unsignedp, methods); 61752284Sobrien else 61852284Sobrien res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 61952284Sobrien imag_t, divisor, imagr, unsignedp); 62052284Sobrien 62152284Sobrien if (res == 0) 62252284Sobrien return 0; 62352284Sobrien 62452284Sobrien if (res != imagr) 62552284Sobrien emit_move_insn (imagr, res); 62652284Sobrien 62752284Sobrien lab2 = gen_label_rtx (); 62852284Sobrien emit_jump_insn (gen_jump (lab2)); 62952284Sobrien emit_barrier (); 63052284Sobrien 63152284Sobrien emit_label (lab1); 63252284Sobrien 63352284Sobrien /* |d| > |c|; use ratio c/d to scale dividend and divisor. */ 63452284Sobrien 63552284Sobrien if (class == MODE_COMPLEX_FLOAT) 63652284Sobrien ratio = expand_binop (submode, binoptab, real1, imag1, 63752284Sobrien NULL_RTX, unsignedp, methods); 63852284Sobrien else 63952284Sobrien ratio = expand_divmod (0, TRUNC_DIV_EXPR, submode, 64052284Sobrien real1, imag1, NULL_RTX, unsignedp); 64152284Sobrien 64252284Sobrien if (ratio == 0) 64352284Sobrien return 0; 64452284Sobrien 64552284Sobrien /* Calculate divisor. */ 64652284Sobrien 64752284Sobrien temp1 = expand_binop (submode, smul_optab, real1, ratio, 64852284Sobrien NULL_RTX, unsignedp, methods); 64952284Sobrien 65052284Sobrien if (temp1 == 0) 65152284Sobrien return 0; 65252284Sobrien 65352284Sobrien divisor = expand_binop (submode, add_optab, temp1, imag1, 65452284Sobrien NULL_RTX, unsignedp, methods); 65552284Sobrien 65652284Sobrien if (divisor == 0) 65752284Sobrien return 0; 65852284Sobrien 65952284Sobrien /* Calculate dividend. */ 66052284Sobrien 66152284Sobrien if (imag0 == 0) 66252284Sobrien { 66352284Sobrien /* Compute a / (c+id) as a(c/d) / (c(c/d)+d) + i (-a) / (c(c/d)+d). */ 66452284Sobrien 66552284Sobrien real_t = expand_binop (submode, smul_optab, real0, ratio, 66652284Sobrien NULL_RTX, unsignedp, methods); 66752284Sobrien 66852284Sobrien imag_t = expand_unop (submode, neg_optab, real0, 66952284Sobrien NULL_RTX, unsignedp); 67052284Sobrien 67152284Sobrien if (real_t == 0 || imag_t == 0) 67252284Sobrien return 0; 67352284Sobrien } 67452284Sobrien else 67552284Sobrien { 67652284Sobrien /* Compute (a+ib)/(c+id) as 67752284Sobrien (a(c/d)+b)/(c(c/d)+d) + i (b(c/d)-a)/(c(c/d)+d). */ 67852284Sobrien 67952284Sobrien temp1 = expand_binop (submode, smul_optab, real0, ratio, 68052284Sobrien NULL_RTX, unsignedp, methods); 68152284Sobrien 68252284Sobrien if (temp1 == 0) 68352284Sobrien return 0; 68452284Sobrien 68552284Sobrien real_t = expand_binop (submode, add_optab, temp1, imag0, 68652284Sobrien NULL_RTX, unsignedp, methods); 68752284Sobrien 68852284Sobrien temp1 = expand_binop (submode, smul_optab, imag0, ratio, 68952284Sobrien NULL_RTX, unsignedp, methods); 69052284Sobrien 69152284Sobrien if (temp1 == 0) 69252284Sobrien return 0; 69352284Sobrien 69452284Sobrien imag_t = expand_binop (submode, sub_optab, temp1, real0, 69552284Sobrien NULL_RTX, unsignedp, methods); 69652284Sobrien 69752284Sobrien if (real_t == 0 || imag_t == 0) 69852284Sobrien return 0; 69952284Sobrien } 70052284Sobrien 70152284Sobrien if (class == MODE_COMPLEX_FLOAT) 70252284Sobrien res = expand_binop (submode, binoptab, real_t, divisor, 70352284Sobrien realr, unsignedp, methods); 70452284Sobrien else 70552284Sobrien res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 70652284Sobrien real_t, divisor, realr, unsignedp); 70752284Sobrien 70852284Sobrien if (res == 0) 70952284Sobrien return 0; 71052284Sobrien 71152284Sobrien if (res != realr) 71252284Sobrien emit_move_insn (realr, res); 71352284Sobrien 71452284Sobrien if (class == MODE_COMPLEX_FLOAT) 71552284Sobrien res = expand_binop (submode, binoptab, imag_t, divisor, 71652284Sobrien imagr, unsignedp, methods); 71752284Sobrien else 71852284Sobrien res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 71952284Sobrien imag_t, divisor, imagr, unsignedp); 72052284Sobrien 72152284Sobrien if (res == 0) 72252284Sobrien return 0; 72352284Sobrien 72452284Sobrien if (res != imagr) 72552284Sobrien emit_move_insn (imagr, res); 72652284Sobrien 72752284Sobrien emit_label (lab2); 72852284Sobrien 72952284Sobrien return 1; 73052284Sobrien} 73152284Sobrien 73218334Speter/* Generate code to perform an operation specified by BINOPTAB 73318334Speter on operands OP0 and OP1, with result having machine-mode MODE. 73418334Speter 73518334Speter UNSIGNEDP is for the case where we have to widen the operands 73618334Speter to perform the operation. It says to use zero-extension. 73718334Speter 73818334Speter If TARGET is nonzero, the value 73918334Speter is generated there, if it is convenient to do so. 74018334Speter In all cases an rtx is returned for the locus of the value; 74118334Speter this may or may not be TARGET. */ 74218334Speter 74318334Speterrtx 74418334Speterexpand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) 74518334Speter enum machine_mode mode; 74618334Speter optab binoptab; 74718334Speter rtx op0, op1; 74818334Speter rtx target; 74918334Speter int unsignedp; 75018334Speter enum optab_methods methods; 75118334Speter{ 75218334Speter enum optab_methods next_methods 75318334Speter = (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN 75418334Speter ? OPTAB_WIDEN : methods); 75518334Speter enum mode_class class; 75618334Speter enum machine_mode wider_mode; 75718334Speter register rtx temp; 75818334Speter int commutative_op = 0; 75918334Speter int shift_op = (binoptab->code == ASHIFT 76018334Speter || binoptab->code == ASHIFTRT 76118334Speter || binoptab->code == LSHIFTRT 76218334Speter || binoptab->code == ROTATE 76318334Speter || binoptab->code == ROTATERT); 76418334Speter rtx entry_last = get_last_insn (); 76518334Speter rtx last; 76618334Speter 76718334Speter class = GET_MODE_CLASS (mode); 76818334Speter 76918334Speter op0 = protect_from_queue (op0, 0); 77018334Speter op1 = protect_from_queue (op1, 0); 77118334Speter if (target) 77218334Speter target = protect_from_queue (target, 1); 77318334Speter 77418334Speter if (flag_force_mem) 77518334Speter { 77618334Speter op0 = force_not_mem (op0); 77718334Speter op1 = force_not_mem (op1); 77818334Speter } 77918334Speter 78018334Speter /* If subtracting an integer constant, convert this into an addition of 78118334Speter the negated constant. */ 78218334Speter 78318334Speter if (binoptab == sub_optab && GET_CODE (op1) == CONST_INT) 78418334Speter { 78518334Speter op1 = negate_rtx (mode, op1); 78618334Speter binoptab = add_optab; 78718334Speter } 78818334Speter 78918334Speter /* If we are inside an appropriately-short loop and one operand is an 79018334Speter expensive constant, force it into a register. */ 79118334Speter if (CONSTANT_P (op0) && preserve_subexpressions_p () 79218334Speter && rtx_cost (op0, binoptab->code) > 2) 79318334Speter op0 = force_reg (mode, op0); 79418334Speter 79518334Speter if (CONSTANT_P (op1) && preserve_subexpressions_p () 79618334Speter && ! shift_op && rtx_cost (op1, binoptab->code) > 2) 79718334Speter op1 = force_reg (mode, op1); 79818334Speter 79918334Speter /* Record where to delete back to if we backtrack. */ 80018334Speter last = get_last_insn (); 80118334Speter 80218334Speter /* If operation is commutative, 80318334Speter try to make the first operand a register. 80418334Speter Even better, try to make it the same as the target. 80518334Speter Also try to make the last operand a constant. */ 80618334Speter if (GET_RTX_CLASS (binoptab->code) == 'c' 80718334Speter || binoptab == smul_widen_optab 80818334Speter || binoptab == umul_widen_optab 80918334Speter || binoptab == smul_highpart_optab 81018334Speter || binoptab == umul_highpart_optab) 81118334Speter { 81218334Speter commutative_op = 1; 81318334Speter 81418334Speter if (((target == 0 || GET_CODE (target) == REG) 81518334Speter ? ((GET_CODE (op1) == REG 81618334Speter && GET_CODE (op0) != REG) 81718334Speter || target == op1) 81818334Speter : rtx_equal_p (op1, target)) 81918334Speter || GET_CODE (op0) == CONST_INT) 82018334Speter { 82118334Speter temp = op1; 82218334Speter op1 = op0; 82318334Speter op0 = temp; 82418334Speter } 82518334Speter } 82618334Speter 82718334Speter /* If we can do it with a three-operand insn, do so. */ 82818334Speter 82918334Speter if (methods != OPTAB_MUST_WIDEN 83018334Speter && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 83118334Speter { 83218334Speter int icode = (int) binoptab->handlers[(int) mode].insn_code; 83318334Speter enum machine_mode mode0 = insn_operand_mode[icode][1]; 83418334Speter enum machine_mode mode1 = insn_operand_mode[icode][2]; 83518334Speter rtx pat; 83618334Speter rtx xop0 = op0, xop1 = op1; 83718334Speter 83818334Speter if (target) 83918334Speter temp = target; 84018334Speter else 84118334Speter temp = gen_reg_rtx (mode); 84218334Speter 84318334Speter /* If it is a commutative operator and the modes would match 84450397Sobrien if we would swap the operands, we can save the conversions. */ 84518334Speter if (commutative_op) 84618334Speter { 84718334Speter if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1 84818334Speter && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0) 84918334Speter { 85018334Speter register rtx tmp; 85118334Speter 85218334Speter tmp = op0; op0 = op1; op1 = tmp; 85318334Speter tmp = xop0; xop0 = xop1; xop1 = tmp; 85418334Speter } 85518334Speter } 85618334Speter 85718334Speter /* In case the insn wants input operands in modes different from 85818334Speter the result, convert the operands. */ 85918334Speter 86018334Speter if (GET_MODE (op0) != VOIDmode 86118334Speter && GET_MODE (op0) != mode0 86218334Speter && mode0 != VOIDmode) 86318334Speter xop0 = convert_to_mode (mode0, xop0, unsignedp); 86418334Speter 86518334Speter if (GET_MODE (xop1) != VOIDmode 86618334Speter && GET_MODE (xop1) != mode1 86718334Speter && mode1 != VOIDmode) 86818334Speter xop1 = convert_to_mode (mode1, xop1, unsignedp); 86918334Speter 87018334Speter /* Now, if insn's predicates don't allow our operands, put them into 87118334Speter pseudo regs. */ 87218334Speter 87318334Speter if (! (*insn_operand_predicate[icode][1]) (xop0, mode0) 87418334Speter && mode0 != VOIDmode) 87518334Speter xop0 = copy_to_mode_reg (mode0, xop0); 87618334Speter 87718334Speter if (! (*insn_operand_predicate[icode][2]) (xop1, mode1) 87818334Speter && mode1 != VOIDmode) 87918334Speter xop1 = copy_to_mode_reg (mode1, xop1); 88018334Speter 88118334Speter if (! (*insn_operand_predicate[icode][0]) (temp, mode)) 88218334Speter temp = gen_reg_rtx (mode); 88318334Speter 88418334Speter pat = GEN_FCN (icode) (temp, xop0, xop1); 88518334Speter if (pat) 88618334Speter { 88718334Speter /* If PAT is a multi-insn sequence, try to add an appropriate 88818334Speter REG_EQUAL note to it. If we can't because TEMP conflicts with an 88918334Speter operand, call ourselves again, this time without a target. */ 89018334Speter if (GET_CODE (pat) == SEQUENCE 89118334Speter && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1)) 89218334Speter { 89318334Speter delete_insns_since (last); 89418334Speter return expand_binop (mode, binoptab, op0, op1, NULL_RTX, 89518334Speter unsignedp, methods); 89618334Speter } 89718334Speter 89818334Speter emit_insn (pat); 89918334Speter return temp; 90018334Speter } 90118334Speter else 90218334Speter delete_insns_since (last); 90318334Speter } 90418334Speter 90518334Speter /* If this is a multiply, see if we can do a widening operation that 90618334Speter takes operands of this mode and makes a wider mode. */ 90718334Speter 90818334Speter if (binoptab == smul_optab && GET_MODE_WIDER_MODE (mode) != VOIDmode 90918334Speter && (((unsignedp ? umul_widen_optab : smul_widen_optab) 91018334Speter ->handlers[(int) GET_MODE_WIDER_MODE (mode)].insn_code) 91118334Speter != CODE_FOR_nothing)) 91218334Speter { 91318334Speter temp = expand_binop (GET_MODE_WIDER_MODE (mode), 91418334Speter unsignedp ? umul_widen_optab : smul_widen_optab, 91518334Speter op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT); 91618334Speter 91718334Speter if (temp != 0) 91818334Speter { 91918334Speter if (GET_MODE_CLASS (mode) == MODE_INT) 92018334Speter return gen_lowpart (mode, temp); 92118334Speter else 92218334Speter return convert_to_mode (mode, temp, unsignedp); 92318334Speter } 92418334Speter } 92518334Speter 92618334Speter /* Look for a wider mode of the same class for which we think we 92718334Speter can open-code the operation. Check for a widening multiply at the 92818334Speter wider mode as well. */ 92918334Speter 93018334Speter if ((class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) 93118334Speter && methods != OPTAB_DIRECT && methods != OPTAB_LIB) 93218334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 93318334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 93418334Speter { 93518334Speter if (binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing 93618334Speter || (binoptab == smul_optab 93718334Speter && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode 93818334Speter && (((unsignedp ? umul_widen_optab : smul_widen_optab) 93918334Speter ->handlers[(int) GET_MODE_WIDER_MODE (wider_mode)].insn_code) 94018334Speter != CODE_FOR_nothing))) 94118334Speter { 94218334Speter rtx xop0 = op0, xop1 = op1; 94318334Speter int no_extend = 0; 94418334Speter 94518334Speter /* For certain integer operations, we need not actually extend 94618334Speter the narrow operands, as long as we will truncate 94718334Speter the results to the same narrowness. */ 94818334Speter 94918334Speter if ((binoptab == ior_optab || binoptab == and_optab 95018334Speter || binoptab == xor_optab 95118334Speter || binoptab == add_optab || binoptab == sub_optab 95218334Speter || binoptab == smul_optab || binoptab == ashl_optab) 95318334Speter && class == MODE_INT) 95418334Speter no_extend = 1; 95518334Speter 95618334Speter xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, no_extend); 95718334Speter 95818334Speter /* The second operand of a shift must always be extended. */ 95918334Speter xop1 = widen_operand (xop1, wider_mode, mode, unsignedp, 96018334Speter no_extend && binoptab != ashl_optab); 96118334Speter 96218334Speter temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX, 96318334Speter unsignedp, OPTAB_DIRECT); 96418334Speter if (temp) 96518334Speter { 96618334Speter if (class != MODE_INT) 96718334Speter { 96818334Speter if (target == 0) 96918334Speter target = gen_reg_rtx (mode); 97018334Speter convert_move (target, temp, 0); 97118334Speter return target; 97218334Speter } 97318334Speter else 97418334Speter return gen_lowpart (mode, temp); 97518334Speter } 97618334Speter else 97718334Speter delete_insns_since (last); 97818334Speter } 97918334Speter } 98018334Speter 98118334Speter /* These can be done a word at a time. */ 98218334Speter if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab) 98318334Speter && class == MODE_INT 98418334Speter && GET_MODE_SIZE (mode) > UNITS_PER_WORD 98518334Speter && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) 98618334Speter { 98718334Speter int i; 98818334Speter rtx insns; 98918334Speter rtx equiv_value; 99018334Speter 99118334Speter /* If TARGET is the same as one of the operands, the REG_EQUAL note 99218334Speter won't be accurate, so use a new target. */ 99318334Speter if (target == 0 || target == op0 || target == op1) 99418334Speter target = gen_reg_rtx (mode); 99518334Speter 99618334Speter start_sequence (); 99718334Speter 99818334Speter /* Do the actual arithmetic. */ 99918334Speter for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) 100018334Speter { 100118334Speter rtx target_piece = operand_subword (target, i, 1, mode); 100218334Speter rtx x = expand_binop (word_mode, binoptab, 100318334Speter operand_subword_force (op0, i, mode), 100418334Speter operand_subword_force (op1, i, mode), 100518334Speter target_piece, unsignedp, next_methods); 100618334Speter 100718334Speter if (x == 0) 100818334Speter break; 100918334Speter 101018334Speter if (target_piece != x) 101118334Speter emit_move_insn (target_piece, x); 101218334Speter } 101318334Speter 101418334Speter insns = get_insns (); 101518334Speter end_sequence (); 101618334Speter 101718334Speter if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) 101818334Speter { 101918334Speter if (binoptab->code != UNKNOWN) 102018334Speter equiv_value 102150397Sobrien = gen_rtx_fmt_ee (binoptab->code, mode, 102250397Sobrien copy_rtx (op0), copy_rtx (op1)); 102318334Speter else 102418334Speter equiv_value = 0; 102518334Speter 102618334Speter emit_no_conflict_block (insns, target, op0, op1, equiv_value); 102718334Speter return target; 102818334Speter } 102918334Speter } 103018334Speter 103118334Speter /* Synthesize double word shifts from single word shifts. */ 103218334Speter if ((binoptab == lshr_optab || binoptab == ashl_optab 103318334Speter || binoptab == ashr_optab) 103418334Speter && class == MODE_INT 103518334Speter && GET_CODE (op1) == CONST_INT 103618334Speter && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD 103718334Speter && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing 103818334Speter && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing 103918334Speter && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) 104018334Speter { 104118334Speter rtx insns, inter, equiv_value; 104218334Speter rtx into_target, outof_target; 104318334Speter rtx into_input, outof_input; 104418334Speter int shift_count, left_shift, outof_word; 104518334Speter 104618334Speter /* If TARGET is the same as one of the operands, the REG_EQUAL note 104718334Speter won't be accurate, so use a new target. */ 104818334Speter if (target == 0 || target == op0 || target == op1) 104918334Speter target = gen_reg_rtx (mode); 105018334Speter 105118334Speter start_sequence (); 105218334Speter 105318334Speter shift_count = INTVAL (op1); 105418334Speter 105518334Speter /* OUTOF_* is the word we are shifting bits away from, and 105618334Speter INTO_* is the word that we are shifting bits towards, thus 105718334Speter they differ depending on the direction of the shift and 105818334Speter WORDS_BIG_ENDIAN. */ 105918334Speter 106018334Speter left_shift = binoptab == ashl_optab; 106118334Speter outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; 106218334Speter 106318334Speter outof_target = operand_subword (target, outof_word, 1, mode); 106418334Speter into_target = operand_subword (target, 1 - outof_word, 1, mode); 106518334Speter 106618334Speter outof_input = operand_subword_force (op0, outof_word, mode); 106718334Speter into_input = operand_subword_force (op0, 1 - outof_word, mode); 106818334Speter 106918334Speter if (shift_count >= BITS_PER_WORD) 107018334Speter { 107118334Speter inter = expand_binop (word_mode, binoptab, 107218334Speter outof_input, 107318334Speter GEN_INT (shift_count - BITS_PER_WORD), 107418334Speter into_target, unsignedp, next_methods); 107518334Speter 107618334Speter if (inter != 0 && inter != into_target) 107718334Speter emit_move_insn (into_target, inter); 107818334Speter 107918334Speter /* For a signed right shift, we must fill the word we are shifting 108018334Speter out of with copies of the sign bit. Otherwise it is zeroed. */ 108118334Speter if (inter != 0 && binoptab != ashr_optab) 108218334Speter inter = CONST0_RTX (word_mode); 108318334Speter else if (inter != 0) 108418334Speter inter = expand_binop (word_mode, binoptab, 108518334Speter outof_input, 108618334Speter GEN_INT (BITS_PER_WORD - 1), 108718334Speter outof_target, unsignedp, next_methods); 108818334Speter 108918334Speter if (inter != 0 && inter != outof_target) 109018334Speter emit_move_insn (outof_target, inter); 109118334Speter } 109218334Speter else 109318334Speter { 109418334Speter rtx carries; 109518334Speter optab reverse_unsigned_shift, unsigned_shift; 109618334Speter 109718334Speter /* For a shift of less then BITS_PER_WORD, to compute the carry, 109818334Speter we must do a logical shift in the opposite direction of the 109918334Speter desired shift. */ 110018334Speter 110118334Speter reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab); 110218334Speter 110318334Speter /* For a shift of less than BITS_PER_WORD, to compute the word 110418334Speter shifted towards, we need to unsigned shift the orig value of 110518334Speter that word. */ 110618334Speter 110718334Speter unsigned_shift = (left_shift ? ashl_optab : lshr_optab); 110818334Speter 110918334Speter carries = expand_binop (word_mode, reverse_unsigned_shift, 111018334Speter outof_input, 111118334Speter GEN_INT (BITS_PER_WORD - shift_count), 111218334Speter 0, unsignedp, next_methods); 111318334Speter 111418334Speter if (carries == 0) 111518334Speter inter = 0; 111618334Speter else 111718334Speter inter = expand_binop (word_mode, unsigned_shift, into_input, 111818334Speter op1, 0, unsignedp, next_methods); 111918334Speter 112018334Speter if (inter != 0) 112118334Speter inter = expand_binop (word_mode, ior_optab, carries, inter, 112218334Speter into_target, unsignedp, next_methods); 112318334Speter 112418334Speter if (inter != 0 && inter != into_target) 112518334Speter emit_move_insn (into_target, inter); 112618334Speter 112718334Speter if (inter != 0) 112818334Speter inter = expand_binop (word_mode, binoptab, outof_input, 112918334Speter op1, outof_target, unsignedp, next_methods); 113018334Speter 113118334Speter if (inter != 0 && inter != outof_target) 113218334Speter emit_move_insn (outof_target, inter); 113318334Speter } 113418334Speter 113518334Speter insns = get_insns (); 113618334Speter end_sequence (); 113718334Speter 113818334Speter if (inter != 0) 113918334Speter { 114018334Speter if (binoptab->code != UNKNOWN) 114150397Sobrien equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1); 114218334Speter else 114318334Speter equiv_value = 0; 114418334Speter 114518334Speter emit_no_conflict_block (insns, target, op0, op1, equiv_value); 114618334Speter return target; 114718334Speter } 114818334Speter } 114918334Speter 115018334Speter /* Synthesize double word rotates from single word shifts. */ 115118334Speter if ((binoptab == rotl_optab || binoptab == rotr_optab) 115218334Speter && class == MODE_INT 115318334Speter && GET_CODE (op1) == CONST_INT 115418334Speter && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD 115518334Speter && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing 115618334Speter && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) 115718334Speter { 115818334Speter rtx insns, equiv_value; 115918334Speter rtx into_target, outof_target; 116018334Speter rtx into_input, outof_input; 116118334Speter rtx inter; 116218334Speter int shift_count, left_shift, outof_word; 116318334Speter 116418334Speter /* If TARGET is the same as one of the operands, the REG_EQUAL note 116518334Speter won't be accurate, so use a new target. */ 116618334Speter if (target == 0 || target == op0 || target == op1) 116718334Speter target = gen_reg_rtx (mode); 116818334Speter 116918334Speter start_sequence (); 117018334Speter 117118334Speter shift_count = INTVAL (op1); 117218334Speter 117318334Speter /* OUTOF_* is the word we are shifting bits away from, and 117418334Speter INTO_* is the word that we are shifting bits towards, thus 117518334Speter they differ depending on the direction of the shift and 117618334Speter WORDS_BIG_ENDIAN. */ 117718334Speter 117818334Speter left_shift = (binoptab == rotl_optab); 117918334Speter outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; 118018334Speter 118118334Speter outof_target = operand_subword (target, outof_word, 1, mode); 118218334Speter into_target = operand_subword (target, 1 - outof_word, 1, mode); 118318334Speter 118418334Speter outof_input = operand_subword_force (op0, outof_word, mode); 118518334Speter into_input = operand_subword_force (op0, 1 - outof_word, mode); 118618334Speter 118718334Speter if (shift_count == BITS_PER_WORD) 118818334Speter { 118918334Speter /* This is just a word swap. */ 119018334Speter emit_move_insn (outof_target, into_input); 119118334Speter emit_move_insn (into_target, outof_input); 119218334Speter inter = const0_rtx; 119318334Speter } 119418334Speter else 119518334Speter { 119618334Speter rtx into_temp1, into_temp2, outof_temp1, outof_temp2; 119718334Speter rtx first_shift_count, second_shift_count; 119818334Speter optab reverse_unsigned_shift, unsigned_shift; 119918334Speter 120018334Speter reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD) 120118334Speter ? lshr_optab : ashl_optab); 120218334Speter 120318334Speter unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD) 120418334Speter ? ashl_optab : lshr_optab); 120518334Speter 120618334Speter if (shift_count > BITS_PER_WORD) 120718334Speter { 120818334Speter first_shift_count = GEN_INT (shift_count - BITS_PER_WORD); 120918334Speter second_shift_count = GEN_INT (2*BITS_PER_WORD - shift_count); 121018334Speter } 121118334Speter else 121218334Speter { 121318334Speter first_shift_count = GEN_INT (BITS_PER_WORD - shift_count); 121418334Speter second_shift_count = GEN_INT (shift_count); 121518334Speter } 121618334Speter 121718334Speter into_temp1 = expand_binop (word_mode, unsigned_shift, 121818334Speter outof_input, first_shift_count, 121918334Speter NULL_RTX, unsignedp, next_methods); 122018334Speter into_temp2 = expand_binop (word_mode, reverse_unsigned_shift, 122118334Speter into_input, second_shift_count, 122218334Speter into_target, unsignedp, next_methods); 122318334Speter 122418334Speter if (into_temp1 != 0 && into_temp2 != 0) 122518334Speter inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2, 122618334Speter into_target, unsignedp, next_methods); 122718334Speter else 122818334Speter inter = 0; 122918334Speter 123018334Speter if (inter != 0 && inter != into_target) 123118334Speter emit_move_insn (into_target, inter); 123218334Speter 123318334Speter outof_temp1 = expand_binop (word_mode, unsigned_shift, 123418334Speter into_input, first_shift_count, 123518334Speter NULL_RTX, unsignedp, next_methods); 123618334Speter outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift, 123718334Speter outof_input, second_shift_count, 123818334Speter outof_target, unsignedp, next_methods); 123918334Speter 124018334Speter if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0) 124118334Speter inter = expand_binop (word_mode, ior_optab, 124218334Speter outof_temp1, outof_temp2, 124318334Speter outof_target, unsignedp, next_methods); 124418334Speter 124518334Speter if (inter != 0 && inter != outof_target) 124618334Speter emit_move_insn (outof_target, inter); 124718334Speter } 124818334Speter 124918334Speter insns = get_insns (); 125018334Speter end_sequence (); 125118334Speter 125218334Speter if (inter != 0) 125318334Speter { 125418334Speter if (binoptab->code != UNKNOWN) 125550397Sobrien equiv_value = gen_rtx_fmt_ee (binoptab->code, mode, op0, op1); 125618334Speter else 125718334Speter equiv_value = 0; 125818334Speter 125918334Speter /* We can't make this a no conflict block if this is a word swap, 126018334Speter because the word swap case fails if the input and output values 126118334Speter are in the same register. */ 126218334Speter if (shift_count != BITS_PER_WORD) 126318334Speter emit_no_conflict_block (insns, target, op0, op1, equiv_value); 126418334Speter else 126518334Speter emit_insns (insns); 126618334Speter 126718334Speter 126818334Speter return target; 126918334Speter } 127018334Speter } 127118334Speter 127218334Speter /* These can be done a word at a time by propagating carries. */ 127318334Speter if ((binoptab == add_optab || binoptab == sub_optab) 127418334Speter && class == MODE_INT 127518334Speter && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD 127618334Speter && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) 127718334Speter { 127818334Speter int i; 127918334Speter rtx carry_tmp = gen_reg_rtx (word_mode); 128018334Speter optab otheroptab = binoptab == add_optab ? sub_optab : add_optab; 128118334Speter int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; 128252284Sobrien rtx carry_in = NULL_RTX, carry_out = NULL_RTX; 128318334Speter rtx xop0, xop1; 128418334Speter 128518334Speter /* We can handle either a 1 or -1 value for the carry. If STORE_FLAG 128618334Speter value is one of those, use it. Otherwise, use 1 since it is the 128718334Speter one easiest to get. */ 128818334Speter#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1 128918334Speter int normalizep = STORE_FLAG_VALUE; 129018334Speter#else 129118334Speter int normalizep = 1; 129218334Speter#endif 129318334Speter 129418334Speter /* Prepare the operands. */ 129518334Speter xop0 = force_reg (mode, op0); 129618334Speter xop1 = force_reg (mode, op1); 129718334Speter 129818334Speter if (target == 0 || GET_CODE (target) != REG 129918334Speter || target == xop0 || target == xop1) 130018334Speter target = gen_reg_rtx (mode); 130118334Speter 130218334Speter /* Indicate for flow that the entire target reg is being set. */ 130318334Speter if (GET_CODE (target) == REG) 130450397Sobrien emit_insn (gen_rtx_CLOBBER (VOIDmode, target)); 130518334Speter 130618334Speter /* Do the actual arithmetic. */ 130718334Speter for (i = 0; i < nwords; i++) 130818334Speter { 130918334Speter int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); 131018334Speter rtx target_piece = operand_subword (target, index, 1, mode); 131118334Speter rtx op0_piece = operand_subword_force (xop0, index, mode); 131218334Speter rtx op1_piece = operand_subword_force (xop1, index, mode); 131318334Speter rtx x; 131418334Speter 131518334Speter /* Main add/subtract of the input operands. */ 131618334Speter x = expand_binop (word_mode, binoptab, 131718334Speter op0_piece, op1_piece, 131818334Speter target_piece, unsignedp, next_methods); 131918334Speter if (x == 0) 132018334Speter break; 132118334Speter 132218334Speter if (i + 1 < nwords) 132318334Speter { 132418334Speter /* Store carry from main add/subtract. */ 132518334Speter carry_out = gen_reg_rtx (word_mode); 132650397Sobrien carry_out = emit_store_flag_force (carry_out, 132750397Sobrien (binoptab == add_optab 132850397Sobrien ? LTU : GTU), 132950397Sobrien x, op0_piece, 133050397Sobrien word_mode, 1, normalizep); 133118334Speter } 133218334Speter 133318334Speter if (i > 0) 133418334Speter { 133518334Speter /* Add/subtract previous carry to main result. */ 133618334Speter x = expand_binop (word_mode, 133718334Speter normalizep == 1 ? binoptab : otheroptab, 133818334Speter x, carry_in, 133918334Speter target_piece, 1, next_methods); 134018334Speter if (x == 0) 134118334Speter break; 134218334Speter else if (target_piece != x) 134318334Speter emit_move_insn (target_piece, x); 134418334Speter 134518334Speter if (i + 1 < nwords) 134618334Speter { 134718334Speter /* THIS CODE HAS NOT BEEN TESTED. */ 134818334Speter /* Get out carry from adding/subtracting carry in. */ 134950397Sobrien carry_tmp = emit_store_flag_force (carry_tmp, 135050397Sobrien binoptab == add_optab 135150397Sobrien ? LTU : GTU, 135250397Sobrien x, carry_in, 135350397Sobrien word_mode, 1, normalizep); 135418334Speter 135518334Speter /* Logical-ior the two poss. carry together. */ 135618334Speter carry_out = expand_binop (word_mode, ior_optab, 135718334Speter carry_out, carry_tmp, 135818334Speter carry_out, 0, next_methods); 135918334Speter if (carry_out == 0) 136018334Speter break; 136118334Speter } 136218334Speter } 136318334Speter 136418334Speter carry_in = carry_out; 136518334Speter } 136618334Speter 136718334Speter if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) 136818334Speter { 136950397Sobrien if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 137050397Sobrien { 137150397Sobrien rtx temp = emit_move_insn (target, target); 137218334Speter 137352284Sobrien set_unique_reg_note (temp, 137452284Sobrien REG_EQUAL, 137552284Sobrien gen_rtx_fmt_ee (binoptab->code, mode, 137652284Sobrien copy_rtx (xop0), 137752284Sobrien copy_rtx (xop1))); 137850397Sobrien } 137918334Speter return target; 138018334Speter } 138118334Speter else 138218334Speter delete_insns_since (last); 138318334Speter } 138418334Speter 138518334Speter /* If we want to multiply two two-word values and have normal and widening 138618334Speter multiplies of single-word values, we can do this with three smaller 138718334Speter multiplications. Note that we do not make a REG_NO_CONFLICT block here 138818334Speter because we are not operating on one word at a time. 138918334Speter 139018334Speter The multiplication proceeds as follows: 139118334Speter _______________________ 139218334Speter [__op0_high_|__op0_low__] 139318334Speter _______________________ 139418334Speter * [__op1_high_|__op1_low__] 139518334Speter _______________________________________________ 139618334Speter _______________________ 139718334Speter (1) [__op0_low__*__op1_low__] 139818334Speter _______________________ 139918334Speter (2a) [__op0_low__*__op1_high_] 140018334Speter _______________________ 140118334Speter (2b) [__op0_high_*__op1_low__] 140218334Speter _______________________ 140318334Speter (3) [__op0_high_*__op1_high_] 140418334Speter 140518334Speter 140618334Speter This gives a 4-word result. Since we are only interested in the 140718334Speter lower 2 words, partial result (3) and the upper words of (2a) and 140818334Speter (2b) don't need to be calculated. Hence (2a) and (2b) can be 140918334Speter calculated using non-widening multiplication. 141018334Speter 141118334Speter (1), however, needs to be calculated with an unsigned widening 141218334Speter multiplication. If this operation is not directly supported we 141318334Speter try using a signed widening multiplication and adjust the result. 141418334Speter This adjustment works as follows: 141518334Speter 141618334Speter If both operands are positive then no adjustment is needed. 141718334Speter 141818334Speter If the operands have different signs, for example op0_low < 0 and 141918334Speter op1_low >= 0, the instruction treats the most significant bit of 142018334Speter op0_low as a sign bit instead of a bit with significance 142118334Speter 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low 142218334Speter with 2**BITS_PER_WORD - op0_low, and two's complements the 142318334Speter result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to 142418334Speter the result. 142518334Speter 142618334Speter Similarly, if both operands are negative, we need to add 142718334Speter (op0_low + op1_low) * 2**BITS_PER_WORD. 142818334Speter 142918334Speter We use a trick to adjust quickly. We logically shift op0_low right 143018334Speter (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to 143118334Speter op0_high (op1_high) before it is used to calculate 2b (2a). If no 143218334Speter logical shift exists, we do an arithmetic right shift and subtract 143318334Speter the 0 or -1. */ 143418334Speter 143518334Speter if (binoptab == smul_optab 143618334Speter && class == MODE_INT 143718334Speter && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD 143818334Speter && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing 143918334Speter && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing 144018334Speter && ((umul_widen_optab->handlers[(int) mode].insn_code 144118334Speter != CODE_FOR_nothing) 144218334Speter || (smul_widen_optab->handlers[(int) mode].insn_code 144318334Speter != CODE_FOR_nothing))) 144418334Speter { 144518334Speter int low = (WORDS_BIG_ENDIAN ? 1 : 0); 144618334Speter int high = (WORDS_BIG_ENDIAN ? 0 : 1); 144718334Speter rtx op0_high = operand_subword_force (op0, high, mode); 144818334Speter rtx op0_low = operand_subword_force (op0, low, mode); 144918334Speter rtx op1_high = operand_subword_force (op1, high, mode); 145018334Speter rtx op1_low = operand_subword_force (op1, low, mode); 145118334Speter rtx product = 0; 145252284Sobrien rtx op0_xhigh = NULL_RTX; 145352284Sobrien rtx op1_xhigh = NULL_RTX; 145418334Speter 145518334Speter /* If the target is the same as one of the inputs, don't use it. This 145618334Speter prevents problems with the REG_EQUAL note. */ 145718334Speter if (target == op0 || target == op1 145818334Speter || (target != 0 && GET_CODE (target) != REG)) 145918334Speter target = 0; 146018334Speter 146118334Speter /* Multiply the two lower words to get a double-word product. 146218334Speter If unsigned widening multiplication is available, use that; 146318334Speter otherwise use the signed form and compensate. */ 146418334Speter 146518334Speter if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 146618334Speter { 146718334Speter product = expand_binop (mode, umul_widen_optab, op0_low, op1_low, 146818334Speter target, 1, OPTAB_DIRECT); 146918334Speter 147018334Speter /* If we didn't succeed, delete everything we did so far. */ 147118334Speter if (product == 0) 147218334Speter delete_insns_since (last); 147318334Speter else 147418334Speter op0_xhigh = op0_high, op1_xhigh = op1_high; 147518334Speter } 147618334Speter 147718334Speter if (product == 0 147818334Speter && smul_widen_optab->handlers[(int) mode].insn_code 147918334Speter != CODE_FOR_nothing) 148018334Speter { 148118334Speter rtx wordm1 = GEN_INT (BITS_PER_WORD - 1); 148218334Speter product = expand_binop (mode, smul_widen_optab, op0_low, op1_low, 148318334Speter target, 1, OPTAB_DIRECT); 148418334Speter op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1, 148518334Speter NULL_RTX, 1, next_methods); 148618334Speter if (op0_xhigh) 148718334Speter op0_xhigh = expand_binop (word_mode, add_optab, op0_high, 148818334Speter op0_xhigh, op0_xhigh, 0, next_methods); 148918334Speter else 149018334Speter { 149118334Speter op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1, 149218334Speter NULL_RTX, 0, next_methods); 149318334Speter if (op0_xhigh) 149418334Speter op0_xhigh = expand_binop (word_mode, sub_optab, op0_high, 149518334Speter op0_xhigh, op0_xhigh, 0, 149618334Speter next_methods); 149718334Speter } 149818334Speter 149918334Speter op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1, 150018334Speter NULL_RTX, 1, next_methods); 150118334Speter if (op1_xhigh) 150218334Speter op1_xhigh = expand_binop (word_mode, add_optab, op1_high, 150318334Speter op1_xhigh, op1_xhigh, 0, next_methods); 150418334Speter else 150518334Speter { 150618334Speter op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1, 150718334Speter NULL_RTX, 0, next_methods); 150818334Speter if (op1_xhigh) 150918334Speter op1_xhigh = expand_binop (word_mode, sub_optab, op1_high, 151018334Speter op1_xhigh, op1_xhigh, 0, 151118334Speter next_methods); 151218334Speter } 151318334Speter } 151418334Speter 151518334Speter /* If we have been able to directly compute the product of the 151618334Speter low-order words of the operands and perform any required adjustments 151718334Speter of the operands, we proceed by trying two more multiplications 151818334Speter and then computing the appropriate sum. 151918334Speter 152018334Speter We have checked above that the required addition is provided. 152118334Speter Full-word addition will normally always succeed, especially if 152218334Speter it is provided at all, so we don't worry about its failure. The 152318334Speter multiplication may well fail, however, so we do handle that. */ 152418334Speter 152518334Speter if (product && op0_xhigh && op1_xhigh) 152618334Speter { 152718334Speter rtx product_high = operand_subword (product, high, 1, mode); 152818334Speter rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh, 152918334Speter NULL_RTX, 0, OPTAB_DIRECT); 153018334Speter 153118334Speter if (temp != 0) 153218334Speter temp = expand_binop (word_mode, add_optab, temp, product_high, 153318334Speter product_high, 0, next_methods); 153418334Speter 153518334Speter if (temp != 0 && temp != product_high) 153618334Speter emit_move_insn (product_high, temp); 153718334Speter 153818334Speter if (temp != 0) 153918334Speter temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, 154018334Speter NULL_RTX, 0, OPTAB_DIRECT); 154118334Speter 154218334Speter if (temp != 0) 154318334Speter temp = expand_binop (word_mode, add_optab, temp, 154418334Speter product_high, product_high, 154518334Speter 0, next_methods); 154618334Speter 154718334Speter if (temp != 0 && temp != product_high) 154818334Speter emit_move_insn (product_high, temp); 154918334Speter 155018334Speter if (temp != 0) 155118334Speter { 155250397Sobrien if (mov_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 155350397Sobrien { 155450397Sobrien temp = emit_move_insn (product, product); 155552284Sobrien set_unique_reg_note (temp, 155652284Sobrien REG_EQUAL, 155752284Sobrien gen_rtx_fmt_ee (MULT, mode, 155852284Sobrien copy_rtx (op0), 155952284Sobrien copy_rtx (op1))); 156050397Sobrien } 156118334Speter return product; 156218334Speter } 156318334Speter } 156418334Speter 156518334Speter /* If we get here, we couldn't do it for some reason even though we 156618334Speter originally thought we could. Delete anything we've emitted in 156718334Speter trying to do it. */ 156818334Speter 156918334Speter delete_insns_since (last); 157018334Speter } 157118334Speter 157218334Speter /* We need to open-code the complex type operations: '+, -, * and /' */ 157318334Speter 157418334Speter /* At this point we allow operations between two similar complex 157518334Speter numbers, and also if one of the operands is not a complex number 157618334Speter but rather of MODE_FLOAT or MODE_INT. However, the caller 157718334Speter must make sure that the MODE of the non-complex operand matches 157818334Speter the SUBMODE of the complex operand. */ 157918334Speter 158018334Speter if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) 158118334Speter { 158218334Speter rtx real0 = 0, imag0 = 0; 158318334Speter rtx real1 = 0, imag1 = 0; 158418334Speter rtx realr, imagr, res; 158518334Speter rtx seq; 158618334Speter rtx equiv_value; 158718334Speter int ok = 0; 158818334Speter 158918334Speter /* Find the correct mode for the real and imaginary parts */ 159018334Speter enum machine_mode submode 159118334Speter = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, 159218334Speter class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, 159318334Speter 0); 159418334Speter 159518334Speter if (submode == BLKmode) 159618334Speter abort (); 159718334Speter 159818334Speter if (! target) 159918334Speter target = gen_reg_rtx (mode); 160018334Speter 160118334Speter start_sequence (); 160218334Speter 160352284Sobrien realr = gen_realpart (submode, target); 160418334Speter imagr = gen_imagpart (submode, target); 160518334Speter 160618334Speter if (GET_MODE (op0) == mode) 160718334Speter { 160852284Sobrien real0 = gen_realpart (submode, op0); 160918334Speter imag0 = gen_imagpart (submode, op0); 161018334Speter } 161118334Speter else 161218334Speter real0 = op0; 161318334Speter 161418334Speter if (GET_MODE (op1) == mode) 161518334Speter { 161652284Sobrien real1 = gen_realpart (submode, op1); 161718334Speter imag1 = gen_imagpart (submode, op1); 161818334Speter } 161918334Speter else 162018334Speter real1 = op1; 162118334Speter 162218334Speter if (real0 == 0 || real1 == 0 || ! (imag0 != 0|| imag1 != 0)) 162318334Speter abort (); 162418334Speter 162518334Speter switch (binoptab->code) 162618334Speter { 162718334Speter case PLUS: 162818334Speter /* (a+ib) + (c+id) = (a+c) + i(b+d) */ 162918334Speter case MINUS: 163018334Speter /* (a+ib) - (c+id) = (a-c) + i(b-d) */ 163118334Speter res = expand_binop (submode, binoptab, real0, real1, 163218334Speter realr, unsignedp, methods); 163318334Speter 163418334Speter if (res == 0) 163518334Speter break; 163618334Speter else if (res != realr) 163718334Speter emit_move_insn (realr, res); 163818334Speter 163918334Speter if (imag0 && imag1) 164018334Speter res = expand_binop (submode, binoptab, imag0, imag1, 164118334Speter imagr, unsignedp, methods); 164218334Speter else if (imag0) 164318334Speter res = imag0; 164418334Speter else if (binoptab->code == MINUS) 164518334Speter res = expand_unop (submode, neg_optab, imag1, imagr, unsignedp); 164618334Speter else 164718334Speter res = imag1; 164818334Speter 164918334Speter if (res == 0) 165018334Speter break; 165118334Speter else if (res != imagr) 165218334Speter emit_move_insn (imagr, res); 165318334Speter 165418334Speter ok = 1; 165518334Speter break; 165618334Speter 165718334Speter case MULT: 165818334Speter /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */ 165918334Speter 166018334Speter if (imag0 && imag1) 166118334Speter { 166218334Speter rtx temp1, temp2; 166318334Speter 166418334Speter /* Don't fetch these from memory more than once. */ 166518334Speter real0 = force_reg (submode, real0); 166618334Speter real1 = force_reg (submode, real1); 166718334Speter imag0 = force_reg (submode, imag0); 166818334Speter imag1 = force_reg (submode, imag1); 166918334Speter 167018334Speter temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX, 167118334Speter unsignedp, methods); 167218334Speter 167318334Speter temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX, 167418334Speter unsignedp, methods); 167518334Speter 167618334Speter if (temp1 == 0 || temp2 == 0) 167718334Speter break; 167818334Speter 167918334Speter res = expand_binop (submode, sub_optab, temp1, temp2, 168018334Speter realr, unsignedp, methods); 168118334Speter 168218334Speter if (res == 0) 168318334Speter break; 168418334Speter else if (res != realr) 168518334Speter emit_move_insn (realr, res); 168618334Speter 168718334Speter temp1 = expand_binop (submode, binoptab, real0, imag1, 168818334Speter NULL_RTX, unsignedp, methods); 168918334Speter 169018334Speter temp2 = expand_binop (submode, binoptab, real1, imag0, 169118334Speter NULL_RTX, unsignedp, methods); 169218334Speter 169318334Speter if (temp1 == 0 || temp2 == 0) 169418334Speter break; 169518334Speter 169618334Speter res = expand_binop (submode, add_optab, temp1, temp2, 169718334Speter imagr, unsignedp, methods); 169818334Speter 169918334Speter if (res == 0) 170018334Speter break; 170118334Speter else if (res != imagr) 170218334Speter emit_move_insn (imagr, res); 170318334Speter 170418334Speter ok = 1; 170518334Speter } 170618334Speter else 170718334Speter { 170818334Speter /* Don't fetch these from memory more than once. */ 170918334Speter real0 = force_reg (submode, real0); 171018334Speter real1 = force_reg (submode, real1); 171118334Speter 171218334Speter res = expand_binop (submode, binoptab, real0, real1, 171318334Speter realr, unsignedp, methods); 171418334Speter if (res == 0) 171518334Speter break; 171618334Speter else if (res != realr) 171718334Speter emit_move_insn (realr, res); 171818334Speter 171918334Speter if (imag0 != 0) 172018334Speter res = expand_binop (submode, binoptab, 172118334Speter real1, imag0, imagr, unsignedp, methods); 172218334Speter else 172318334Speter res = expand_binop (submode, binoptab, 172418334Speter real0, imag1, imagr, unsignedp, methods); 172518334Speter 172618334Speter if (res == 0) 172718334Speter break; 172818334Speter else if (res != imagr) 172918334Speter emit_move_insn (imagr, res); 173018334Speter 173118334Speter ok = 1; 173218334Speter } 173318334Speter break; 173418334Speter 173518334Speter case DIV: 173618334Speter /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */ 173718334Speter 173818334Speter if (imag1 == 0) 173918334Speter { 174018334Speter /* (a+ib) / (c+i0) = (a/c) + i(b/c) */ 174118334Speter 174218334Speter /* Don't fetch these from memory more than once. */ 174318334Speter real1 = force_reg (submode, real1); 174418334Speter 174518334Speter /* Simply divide the real and imaginary parts by `c' */ 174618334Speter if (class == MODE_COMPLEX_FLOAT) 174718334Speter res = expand_binop (submode, binoptab, real0, real1, 174818334Speter realr, unsignedp, methods); 174918334Speter else 175018334Speter res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 175118334Speter real0, real1, realr, unsignedp); 175218334Speter 175318334Speter if (res == 0) 175418334Speter break; 175518334Speter else if (res != realr) 175618334Speter emit_move_insn (realr, res); 175718334Speter 175818334Speter if (class == MODE_COMPLEX_FLOAT) 175918334Speter res = expand_binop (submode, binoptab, imag0, real1, 176018334Speter imagr, unsignedp, methods); 176118334Speter else 176218334Speter res = expand_divmod (0, TRUNC_DIV_EXPR, submode, 176318334Speter imag0, real1, imagr, unsignedp); 176418334Speter 176518334Speter if (res == 0) 176618334Speter break; 176718334Speter else if (res != imagr) 176818334Speter emit_move_insn (imagr, res); 176918334Speter 177018334Speter ok = 1; 177118334Speter } 177218334Speter else 177318334Speter { 177452284Sobrien switch (flag_complex_divide_method) 177518334Speter { 177652284Sobrien case 0: 177752284Sobrien ok = expand_cmplxdiv_straight (real0, real1, imag0, imag1, 177852284Sobrien realr, imagr, submode, 177952284Sobrien unsignedp, methods, 178052284Sobrien class, binoptab); 178152284Sobrien break; 178218334Speter 178352284Sobrien case 1: 178452284Sobrien ok = expand_cmplxdiv_wide (real0, real1, imag0, imag1, 178552284Sobrien realr, imagr, submode, 178652284Sobrien unsignedp, methods, 178752284Sobrien class, binoptab); 178852284Sobrien break; 178918334Speter 179052284Sobrien default: 179152284Sobrien abort (); 179218334Speter } 179318334Speter } 179418334Speter break; 179518334Speter 179618334Speter default: 179718334Speter abort (); 179818334Speter } 179918334Speter 180018334Speter seq = get_insns (); 180118334Speter end_sequence (); 180218334Speter 180318334Speter if (ok) 180418334Speter { 180518334Speter if (binoptab->code != UNKNOWN) 180618334Speter equiv_value 180750397Sobrien = gen_rtx_fmt_ee (binoptab->code, mode, 180850397Sobrien copy_rtx (op0), copy_rtx (op1)); 180918334Speter else 181018334Speter equiv_value = 0; 181118334Speter 181218334Speter emit_no_conflict_block (seq, target, op0, op1, equiv_value); 181318334Speter 181418334Speter return target; 181518334Speter } 181618334Speter } 181718334Speter 181818334Speter /* It can't be open-coded in this mode. 181918334Speter Use a library call if one is available and caller says that's ok. */ 182018334Speter 182118334Speter if (binoptab->handlers[(int) mode].libfunc 182218334Speter && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN)) 182318334Speter { 182418334Speter rtx insns; 182518334Speter rtx op1x = op1; 182618334Speter enum machine_mode op1_mode = mode; 182718334Speter rtx value; 182818334Speter 182918334Speter start_sequence (); 183018334Speter 183118334Speter if (shift_op) 183218334Speter { 183318334Speter op1_mode = word_mode; 183418334Speter /* Specify unsigned here, 183518334Speter since negative shift counts are meaningless. */ 183618334Speter op1x = convert_to_mode (word_mode, op1, 1); 183718334Speter } 183818334Speter 183918334Speter if (GET_MODE (op0) != VOIDmode 184018334Speter && GET_MODE (op0) != mode) 184118334Speter op0 = convert_to_mode (mode, op0, unsignedp); 184218334Speter 184318334Speter /* Pass 1 for NO_QUEUE so we don't lose any increments 184418334Speter if the libcall is cse'd or moved. */ 184518334Speter value = emit_library_call_value (binoptab->handlers[(int) mode].libfunc, 184618334Speter NULL_RTX, 1, mode, 2, 184718334Speter op0, mode, op1x, op1_mode); 184818334Speter 184918334Speter insns = get_insns (); 185018334Speter end_sequence (); 185118334Speter 185218334Speter target = gen_reg_rtx (mode); 185318334Speter emit_libcall_block (insns, target, value, 185450397Sobrien gen_rtx_fmt_ee (binoptab->code, mode, op0, op1)); 185518334Speter 185618334Speter return target; 185718334Speter } 185818334Speter 185918334Speter delete_insns_since (last); 186018334Speter 186118334Speter /* It can't be done in this mode. Can we do it in a wider mode? */ 186218334Speter 186318334Speter if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN 186418334Speter || methods == OPTAB_MUST_WIDEN)) 186518334Speter { 186618334Speter /* Caller says, don't even try. */ 186718334Speter delete_insns_since (entry_last); 186818334Speter return 0; 186918334Speter } 187018334Speter 187118334Speter /* Compute the value of METHODS to pass to recursive calls. 187218334Speter Don't allow widening to be tried recursively. */ 187318334Speter 187418334Speter methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT); 187518334Speter 187618334Speter /* Look for a wider mode of the same class for which it appears we can do 187718334Speter the operation. */ 187818334Speter 187918334Speter if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) 188018334Speter { 188118334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 188218334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 188318334Speter { 188418334Speter if ((binoptab->handlers[(int) wider_mode].insn_code 188518334Speter != CODE_FOR_nothing) 188618334Speter || (methods == OPTAB_LIB 188718334Speter && binoptab->handlers[(int) wider_mode].libfunc)) 188818334Speter { 188918334Speter rtx xop0 = op0, xop1 = op1; 189018334Speter int no_extend = 0; 189118334Speter 189218334Speter /* For certain integer operations, we need not actually extend 189318334Speter the narrow operands, as long as we will truncate 189418334Speter the results to the same narrowness. */ 189518334Speter 189618334Speter if ((binoptab == ior_optab || binoptab == and_optab 189718334Speter || binoptab == xor_optab 189818334Speter || binoptab == add_optab || binoptab == sub_optab 189918334Speter || binoptab == smul_optab || binoptab == ashl_optab) 190018334Speter && class == MODE_INT) 190118334Speter no_extend = 1; 190218334Speter 190318334Speter xop0 = widen_operand (xop0, wider_mode, mode, 190418334Speter unsignedp, no_extend); 190518334Speter 190618334Speter /* The second operand of a shift must always be extended. */ 190718334Speter xop1 = widen_operand (xop1, wider_mode, mode, unsignedp, 190818334Speter no_extend && binoptab != ashl_optab); 190918334Speter 191018334Speter temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX, 191118334Speter unsignedp, methods); 191218334Speter if (temp) 191318334Speter { 191418334Speter if (class != MODE_INT) 191518334Speter { 191618334Speter if (target == 0) 191718334Speter target = gen_reg_rtx (mode); 191818334Speter convert_move (target, temp, 0); 191918334Speter return target; 192018334Speter } 192118334Speter else 192218334Speter return gen_lowpart (mode, temp); 192318334Speter } 192418334Speter else 192518334Speter delete_insns_since (last); 192618334Speter } 192718334Speter } 192818334Speter } 192918334Speter 193018334Speter delete_insns_since (entry_last); 193118334Speter return 0; 193218334Speter} 193318334Speter 193418334Speter/* Expand a binary operator which has both signed and unsigned forms. 193518334Speter UOPTAB is the optab for unsigned operations, and SOPTAB is for 193618334Speter signed operations. 193718334Speter 193818334Speter If we widen unsigned operands, we may use a signed wider operation instead 193918334Speter of an unsigned wider operation, since the result would be the same. */ 194018334Speter 194118334Speterrtx 194218334Spetersign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods) 194318334Speter enum machine_mode mode; 194418334Speter optab uoptab, soptab; 194518334Speter rtx op0, op1, target; 194618334Speter int unsignedp; 194718334Speter enum optab_methods methods; 194818334Speter{ 194918334Speter register rtx temp; 195018334Speter optab direct_optab = unsignedp ? uoptab : soptab; 195118334Speter struct optab wide_soptab; 195218334Speter 195318334Speter /* Do it without widening, if possible. */ 195418334Speter temp = expand_binop (mode, direct_optab, op0, op1, target, 195518334Speter unsignedp, OPTAB_DIRECT); 195618334Speter if (temp || methods == OPTAB_DIRECT) 195718334Speter return temp; 195818334Speter 195918334Speter /* Try widening to a signed int. Make a fake signed optab that 196018334Speter hides any signed insn for direct use. */ 196118334Speter wide_soptab = *soptab; 196218334Speter wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing; 196318334Speter wide_soptab.handlers[(int) mode].libfunc = 0; 196418334Speter 196518334Speter temp = expand_binop (mode, &wide_soptab, op0, op1, target, 196618334Speter unsignedp, OPTAB_WIDEN); 196718334Speter 196818334Speter /* For unsigned operands, try widening to an unsigned int. */ 196918334Speter if (temp == 0 && unsignedp) 197018334Speter temp = expand_binop (mode, uoptab, op0, op1, target, 197118334Speter unsignedp, OPTAB_WIDEN); 197218334Speter if (temp || methods == OPTAB_WIDEN) 197318334Speter return temp; 197418334Speter 197518334Speter /* Use the right width lib call if that exists. */ 197618334Speter temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB); 197718334Speter if (temp || methods == OPTAB_LIB) 197818334Speter return temp; 197918334Speter 198018334Speter /* Must widen and use a lib call, use either signed or unsigned. */ 198118334Speter temp = expand_binop (mode, &wide_soptab, op0, op1, target, 198218334Speter unsignedp, methods); 198318334Speter if (temp != 0) 198418334Speter return temp; 198518334Speter if (unsignedp) 198618334Speter return expand_binop (mode, uoptab, op0, op1, target, 198718334Speter unsignedp, methods); 198818334Speter return 0; 198918334Speter} 199018334Speter 199118334Speter/* Generate code to perform an operation specified by BINOPTAB 199218334Speter on operands OP0 and OP1, with two results to TARG1 and TARG2. 199318334Speter We assume that the order of the operands for the instruction 199418334Speter is TARG0, OP0, OP1, TARG1, which would fit a pattern like 199518334Speter [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. 199618334Speter 199718334Speter Either TARG0 or TARG1 may be zero, but what that means is that 199850397Sobrien the result is not actually wanted. We will generate it into 199918334Speter a dummy pseudo-reg and discard it. They may not both be zero. 200018334Speter 200118334Speter Returns 1 if this operation can be performed; 0 if not. */ 200218334Speter 200318334Speterint 200418334Speterexpand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp) 200518334Speter optab binoptab; 200618334Speter rtx op0, op1; 200718334Speter rtx targ0, targ1; 200818334Speter int unsignedp; 200918334Speter{ 201018334Speter enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1); 201118334Speter enum mode_class class; 201218334Speter enum machine_mode wider_mode; 201318334Speter rtx entry_last = get_last_insn (); 201418334Speter rtx last; 201518334Speter 201618334Speter class = GET_MODE_CLASS (mode); 201718334Speter 201818334Speter op0 = protect_from_queue (op0, 0); 201918334Speter op1 = protect_from_queue (op1, 0); 202018334Speter 202118334Speter if (flag_force_mem) 202218334Speter { 202318334Speter op0 = force_not_mem (op0); 202418334Speter op1 = force_not_mem (op1); 202518334Speter } 202618334Speter 202718334Speter /* If we are inside an appropriately-short loop and one operand is an 202818334Speter expensive constant, force it into a register. */ 202918334Speter if (CONSTANT_P (op0) && preserve_subexpressions_p () 203018334Speter && rtx_cost (op0, binoptab->code) > 2) 203118334Speter op0 = force_reg (mode, op0); 203218334Speter 203318334Speter if (CONSTANT_P (op1) && preserve_subexpressions_p () 203418334Speter && rtx_cost (op1, binoptab->code) > 2) 203518334Speter op1 = force_reg (mode, op1); 203618334Speter 203718334Speter if (targ0) 203818334Speter targ0 = protect_from_queue (targ0, 1); 203918334Speter else 204018334Speter targ0 = gen_reg_rtx (mode); 204118334Speter if (targ1) 204218334Speter targ1 = protect_from_queue (targ1, 1); 204318334Speter else 204418334Speter targ1 = gen_reg_rtx (mode); 204518334Speter 204618334Speter /* Record where to go back to if we fail. */ 204718334Speter last = get_last_insn (); 204818334Speter 204918334Speter if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 205018334Speter { 205118334Speter int icode = (int) binoptab->handlers[(int) mode].insn_code; 205218334Speter enum machine_mode mode0 = insn_operand_mode[icode][1]; 205318334Speter enum machine_mode mode1 = insn_operand_mode[icode][2]; 205418334Speter rtx pat; 205518334Speter rtx xop0 = op0, xop1 = op1; 205618334Speter 205718334Speter /* In case this insn wants input operands in modes different from the 205818334Speter result, convert the operands. */ 205918334Speter if (GET_MODE (op0) != VOIDmode && GET_MODE (op0) != mode0) 206018334Speter xop0 = convert_to_mode (mode0, xop0, unsignedp); 206118334Speter 206218334Speter if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode1) 206318334Speter xop1 = convert_to_mode (mode1, xop1, unsignedp); 206418334Speter 206518334Speter /* Now, if insn doesn't accept these operands, put them into pseudos. */ 206618334Speter if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) 206718334Speter xop0 = copy_to_mode_reg (mode0, xop0); 206818334Speter 206918334Speter if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)) 207018334Speter xop1 = copy_to_mode_reg (mode1, xop1); 207118334Speter 207218334Speter /* We could handle this, but we should always be called with a pseudo 207318334Speter for our targets and all insns should take them as outputs. */ 207418334Speter if (! (*insn_operand_predicate[icode][0]) (targ0, mode) 207518334Speter || ! (*insn_operand_predicate[icode][3]) (targ1, mode)) 207618334Speter abort (); 207718334Speter 207818334Speter pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1); 207918334Speter if (pat) 208018334Speter { 208118334Speter emit_insn (pat); 208218334Speter return 1; 208318334Speter } 208418334Speter else 208518334Speter delete_insns_since (last); 208618334Speter } 208718334Speter 208818334Speter /* It can't be done in this mode. Can we do it in a wider mode? */ 208918334Speter 209018334Speter if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) 209118334Speter { 209218334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 209318334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 209418334Speter { 209518334Speter if (binoptab->handlers[(int) wider_mode].insn_code 209618334Speter != CODE_FOR_nothing) 209718334Speter { 209818334Speter register rtx t0 = gen_reg_rtx (wider_mode); 209918334Speter register rtx t1 = gen_reg_rtx (wider_mode); 210018334Speter 210118334Speter if (expand_twoval_binop (binoptab, 210218334Speter convert_modes (wider_mode, mode, op0, 210318334Speter unsignedp), 210418334Speter convert_modes (wider_mode, mode, op1, 210518334Speter unsignedp), 210618334Speter t0, t1, unsignedp)) 210718334Speter { 210818334Speter convert_move (targ0, t0, unsignedp); 210918334Speter convert_move (targ1, t1, unsignedp); 211018334Speter return 1; 211118334Speter } 211218334Speter else 211318334Speter delete_insns_since (last); 211418334Speter } 211518334Speter } 211618334Speter } 211718334Speter 211818334Speter delete_insns_since (entry_last); 211918334Speter return 0; 212018334Speter} 212118334Speter 212218334Speter/* Generate code to perform an operation specified by UNOPTAB 212318334Speter on operand OP0, with result having machine-mode MODE. 212418334Speter 212518334Speter UNSIGNEDP is for the case where we have to widen the operands 212618334Speter to perform the operation. It says to use zero-extension. 212718334Speter 212818334Speter If TARGET is nonzero, the value 212918334Speter is generated there, if it is convenient to do so. 213018334Speter In all cases an rtx is returned for the locus of the value; 213118334Speter this may or may not be TARGET. */ 213218334Speter 213318334Speterrtx 213418334Speterexpand_unop (mode, unoptab, op0, target, unsignedp) 213518334Speter enum machine_mode mode; 213618334Speter optab unoptab; 213718334Speter rtx op0; 213818334Speter rtx target; 213918334Speter int unsignedp; 214018334Speter{ 214118334Speter enum mode_class class; 214218334Speter enum machine_mode wider_mode; 214318334Speter register rtx temp; 214418334Speter rtx last = get_last_insn (); 214518334Speter rtx pat; 214618334Speter 214718334Speter class = GET_MODE_CLASS (mode); 214818334Speter 214918334Speter op0 = protect_from_queue (op0, 0); 215018334Speter 215118334Speter if (flag_force_mem) 215218334Speter { 215318334Speter op0 = force_not_mem (op0); 215418334Speter } 215518334Speter 215618334Speter if (target) 215718334Speter target = protect_from_queue (target, 1); 215818334Speter 215918334Speter if (unoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 216018334Speter { 216118334Speter int icode = (int) unoptab->handlers[(int) mode].insn_code; 216218334Speter enum machine_mode mode0 = insn_operand_mode[icode][1]; 216318334Speter rtx xop0 = op0; 216418334Speter 216518334Speter if (target) 216618334Speter temp = target; 216718334Speter else 216818334Speter temp = gen_reg_rtx (mode); 216918334Speter 217018334Speter if (GET_MODE (xop0) != VOIDmode 217118334Speter && GET_MODE (xop0) != mode0) 217218334Speter xop0 = convert_to_mode (mode0, xop0, unsignedp); 217318334Speter 217418334Speter /* Now, if insn doesn't accept our operand, put it into a pseudo. */ 217518334Speter 217618334Speter if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) 217718334Speter xop0 = copy_to_mode_reg (mode0, xop0); 217818334Speter 217918334Speter if (! (*insn_operand_predicate[icode][0]) (temp, mode)) 218018334Speter temp = gen_reg_rtx (mode); 218118334Speter 218218334Speter pat = GEN_FCN (icode) (temp, xop0); 218318334Speter if (pat) 218418334Speter { 218518334Speter if (GET_CODE (pat) == SEQUENCE 218618334Speter && ! add_equal_note (pat, temp, unoptab->code, xop0, NULL_RTX)) 218718334Speter { 218818334Speter delete_insns_since (last); 218918334Speter return expand_unop (mode, unoptab, op0, NULL_RTX, unsignedp); 219018334Speter } 219118334Speter 219218334Speter emit_insn (pat); 219318334Speter 219418334Speter return temp; 219518334Speter } 219618334Speter else 219718334Speter delete_insns_since (last); 219818334Speter } 219918334Speter 220018334Speter /* It can't be done in this mode. Can we open-code it in a wider mode? */ 220118334Speter 220218334Speter if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) 220318334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 220418334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 220518334Speter { 220618334Speter if (unoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) 220718334Speter { 220818334Speter rtx xop0 = op0; 220918334Speter 221018334Speter /* For certain operations, we need not actually extend 221118334Speter the narrow operand, as long as we will truncate the 221218334Speter results to the same narrowness. */ 221318334Speter 221418334Speter xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, 221518334Speter (unoptab == neg_optab 221618334Speter || unoptab == one_cmpl_optab) 221718334Speter && class == MODE_INT); 221818334Speter 221918334Speter temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX, 222018334Speter unsignedp); 222118334Speter 222218334Speter if (temp) 222318334Speter { 222418334Speter if (class != MODE_INT) 222518334Speter { 222618334Speter if (target == 0) 222718334Speter target = gen_reg_rtx (mode); 222818334Speter convert_move (target, temp, 0); 222918334Speter return target; 223018334Speter } 223118334Speter else 223218334Speter return gen_lowpart (mode, temp); 223318334Speter } 223418334Speter else 223518334Speter delete_insns_since (last); 223618334Speter } 223718334Speter } 223818334Speter 223918334Speter /* These can be done a word at a time. */ 224018334Speter if (unoptab == one_cmpl_optab 224118334Speter && class == MODE_INT 224218334Speter && GET_MODE_SIZE (mode) > UNITS_PER_WORD 224318334Speter && unoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) 224418334Speter { 224518334Speter int i; 224618334Speter rtx insns; 224718334Speter 224818334Speter if (target == 0 || target == op0) 224918334Speter target = gen_reg_rtx (mode); 225018334Speter 225118334Speter start_sequence (); 225218334Speter 225318334Speter /* Do the actual arithmetic. */ 225418334Speter for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) 225518334Speter { 225618334Speter rtx target_piece = operand_subword (target, i, 1, mode); 225718334Speter rtx x = expand_unop (word_mode, unoptab, 225818334Speter operand_subword_force (op0, i, mode), 225918334Speter target_piece, unsignedp); 226018334Speter if (target_piece != x) 226118334Speter emit_move_insn (target_piece, x); 226218334Speter } 226318334Speter 226418334Speter insns = get_insns (); 226518334Speter end_sequence (); 226618334Speter 226718334Speter emit_no_conflict_block (insns, target, op0, NULL_RTX, 226850397Sobrien gen_rtx_fmt_e (unoptab->code, mode, 226950397Sobrien copy_rtx (op0))); 227018334Speter return target; 227118334Speter } 227218334Speter 227318334Speter /* Open-code the complex negation operation. */ 227418334Speter else if (unoptab == neg_optab 227518334Speter && (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT)) 227618334Speter { 227718334Speter rtx target_piece; 227818334Speter rtx x; 227918334Speter rtx seq; 228018334Speter 228118334Speter /* Find the correct mode for the real and imaginary parts */ 228218334Speter enum machine_mode submode 228318334Speter = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, 228418334Speter class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, 228518334Speter 0); 228618334Speter 228718334Speter if (submode == BLKmode) 228818334Speter abort (); 228918334Speter 229018334Speter if (target == 0) 229118334Speter target = gen_reg_rtx (mode); 229218334Speter 229318334Speter start_sequence (); 229418334Speter 229518334Speter target_piece = gen_imagpart (submode, target); 229618334Speter x = expand_unop (submode, unoptab, 229718334Speter gen_imagpart (submode, op0), 229818334Speter target_piece, unsignedp); 229918334Speter if (target_piece != x) 230018334Speter emit_move_insn (target_piece, x); 230118334Speter 230218334Speter target_piece = gen_realpart (submode, target); 230318334Speter x = expand_unop (submode, unoptab, 230418334Speter gen_realpart (submode, op0), 230518334Speter target_piece, unsignedp); 230618334Speter if (target_piece != x) 230718334Speter emit_move_insn (target_piece, x); 230818334Speter 230918334Speter seq = get_insns (); 231018334Speter end_sequence (); 231118334Speter 231218334Speter emit_no_conflict_block (seq, target, op0, 0, 231350397Sobrien gen_rtx_fmt_e (unoptab->code, mode, 231450397Sobrien copy_rtx (op0))); 231518334Speter return target; 231618334Speter } 231718334Speter 231818334Speter /* Now try a library call in this mode. */ 231918334Speter if (unoptab->handlers[(int) mode].libfunc) 232018334Speter { 232118334Speter rtx insns; 232218334Speter rtx value; 232318334Speter 232418334Speter start_sequence (); 232518334Speter 232618334Speter /* Pass 1 for NO_QUEUE so we don't lose any increments 232718334Speter if the libcall is cse'd or moved. */ 232818334Speter value = emit_library_call_value (unoptab->handlers[(int) mode].libfunc, 232918334Speter NULL_RTX, 1, mode, 1, op0, mode); 233018334Speter insns = get_insns (); 233118334Speter end_sequence (); 233218334Speter 233318334Speter target = gen_reg_rtx (mode); 233418334Speter emit_libcall_block (insns, target, value, 233550397Sobrien gen_rtx_fmt_e (unoptab->code, mode, op0)); 233618334Speter 233718334Speter return target; 233818334Speter } 233918334Speter 234018334Speter /* It can't be done in this mode. Can we do it in a wider mode? */ 234118334Speter 234218334Speter if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) 234318334Speter { 234418334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 234518334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 234618334Speter { 234718334Speter if ((unoptab->handlers[(int) wider_mode].insn_code 234818334Speter != CODE_FOR_nothing) 234918334Speter || unoptab->handlers[(int) wider_mode].libfunc) 235018334Speter { 235118334Speter rtx xop0 = op0; 235218334Speter 235318334Speter /* For certain operations, we need not actually extend 235418334Speter the narrow operand, as long as we will truncate the 235518334Speter results to the same narrowness. */ 235618334Speter 235718334Speter xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, 235818334Speter (unoptab == neg_optab 235918334Speter || unoptab == one_cmpl_optab) 236018334Speter && class == MODE_INT); 236118334Speter 236218334Speter temp = expand_unop (wider_mode, unoptab, xop0, NULL_RTX, 236318334Speter unsignedp); 236418334Speter 236518334Speter if (temp) 236618334Speter { 236718334Speter if (class != MODE_INT) 236818334Speter { 236918334Speter if (target == 0) 237018334Speter target = gen_reg_rtx (mode); 237118334Speter convert_move (target, temp, 0); 237218334Speter return target; 237318334Speter } 237418334Speter else 237518334Speter return gen_lowpart (mode, temp); 237618334Speter } 237718334Speter else 237818334Speter delete_insns_since (last); 237918334Speter } 238018334Speter } 238118334Speter } 238218334Speter 238318334Speter /* If there is no negate operation, try doing a subtract from zero. 238418334Speter The US Software GOFAST library needs this. */ 238518334Speter if (unoptab == neg_optab) 238618334Speter { 238718334Speter rtx temp; 238818334Speter temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0, 238918334Speter target, unsignedp, OPTAB_LIB_WIDEN); 239018334Speter if (temp) 239118334Speter return temp; 239218334Speter } 239318334Speter 239418334Speter return 0; 239518334Speter} 239618334Speter 239718334Speter/* Emit code to compute the absolute value of OP0, with result to 239818334Speter TARGET if convenient. (TARGET may be 0.) The return value says 239918334Speter where the result actually is to be found. 240018334Speter 240118334Speter MODE is the mode of the operand; the mode of the result is 240218334Speter different but can be deduced from MODE. 240318334Speter 240452284Sobrien */ 240518334Speter 240618334Speterrtx 240752284Sobrienexpand_abs (mode, op0, target, safe) 240818334Speter enum machine_mode mode; 240918334Speter rtx op0; 241018334Speter rtx target; 241118334Speter int safe; 241218334Speter{ 241318334Speter rtx temp, op1; 241418334Speter 241518334Speter /* First try to do it with a special abs instruction. */ 241618334Speter temp = expand_unop (mode, abs_optab, op0, target, 0); 241718334Speter if (temp != 0) 241818334Speter return temp; 241918334Speter 242018334Speter /* If this machine has expensive jumps, we can do integer absolute 242118334Speter value of X as (((signed) x >> (W-1)) ^ x) - ((signed) x >> (W-1)), 242218334Speter where W is the width of MODE. */ 242318334Speter 242418334Speter if (GET_MODE_CLASS (mode) == MODE_INT && BRANCH_COST >= 2) 242518334Speter { 242618334Speter rtx extended = expand_shift (RSHIFT_EXPR, mode, op0, 242718334Speter size_int (GET_MODE_BITSIZE (mode) - 1), 242818334Speter NULL_RTX, 0); 242918334Speter 243018334Speter temp = expand_binop (mode, xor_optab, extended, op0, target, 0, 243118334Speter OPTAB_LIB_WIDEN); 243218334Speter if (temp != 0) 243318334Speter temp = expand_binop (mode, sub_optab, temp, extended, target, 0, 243418334Speter OPTAB_LIB_WIDEN); 243518334Speter 243618334Speter if (temp != 0) 243718334Speter return temp; 243818334Speter } 243918334Speter 244018334Speter /* If that does not win, use conditional jump and negate. */ 244150397Sobrien 244250397Sobrien /* It is safe to use the target if it is the same 244350397Sobrien as the source if this is also a pseudo register */ 244450397Sobrien if (op0 == target && GET_CODE (op0) == REG 244550397Sobrien && REGNO (op0) >= FIRST_PSEUDO_REGISTER) 244650397Sobrien safe = 1; 244750397Sobrien 244818334Speter op1 = gen_label_rtx (); 244918334Speter if (target == 0 || ! safe 245018334Speter || GET_MODE (target) != mode 245118334Speter || (GET_CODE (target) == MEM && MEM_VOLATILE_P (target)) 245218334Speter || (GET_CODE (target) == REG 245318334Speter && REGNO (target) < FIRST_PSEUDO_REGISTER)) 245418334Speter target = gen_reg_rtx (mode); 245518334Speter 245618334Speter emit_move_insn (target, op0); 245718334Speter NO_DEFER_POP; 245818334Speter 245918334Speter /* If this mode is an integer too wide to compare properly, 246018334Speter compare word by word. Rely on CSE to optimize constant cases. */ 246118334Speter if (GET_MODE_CLASS (mode) == MODE_INT && ! can_compare_p (mode)) 246218334Speter do_jump_by_parts_greater_rtx (mode, 0, target, const0_rtx, 246318334Speter NULL_RTX, op1); 246418334Speter else 246518334Speter { 246618334Speter temp = compare_from_rtx (target, CONST0_RTX (mode), GE, 0, mode, 246718334Speter NULL_RTX, 0); 246818334Speter if (temp == const1_rtx) 246918334Speter return target; 247018334Speter else if (temp != const0_rtx) 247118334Speter { 247218334Speter if (bcc_gen_fctn[(int) GET_CODE (temp)] != 0) 247318334Speter emit_jump_insn ((*bcc_gen_fctn[(int) GET_CODE (temp)]) (op1)); 247418334Speter else 247518334Speter abort (); 247618334Speter } 247718334Speter } 247818334Speter 247918334Speter op0 = expand_unop (mode, neg_optab, target, target, 0); 248018334Speter if (op0 != target) 248118334Speter emit_move_insn (target, op0); 248218334Speter emit_label (op1); 248318334Speter OK_DEFER_POP; 248418334Speter return target; 248518334Speter} 248618334Speter 248718334Speter/* Emit code to compute the absolute value of OP0, with result to 248818334Speter TARGET if convenient. (TARGET may be 0.) The return value says 248918334Speter where the result actually is to be found. 249018334Speter 249118334Speter MODE is the mode of the operand; the mode of the result is 249218334Speter different but can be deduced from MODE. 249318334Speter 249418334Speter UNSIGNEDP is relevant for complex integer modes. */ 249518334Speter 249618334Speterrtx 249718334Speterexpand_complex_abs (mode, op0, target, unsignedp) 249818334Speter enum machine_mode mode; 249918334Speter rtx op0; 250018334Speter rtx target; 250118334Speter int unsignedp; 250218334Speter{ 250318334Speter enum mode_class class = GET_MODE_CLASS (mode); 250418334Speter enum machine_mode wider_mode; 250518334Speter register rtx temp; 250618334Speter rtx entry_last = get_last_insn (); 250718334Speter rtx last; 250818334Speter rtx pat; 250918334Speter 251018334Speter /* Find the correct mode for the real and imaginary parts. */ 251118334Speter enum machine_mode submode 251218334Speter = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, 251318334Speter class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, 251418334Speter 0); 251518334Speter 251618334Speter if (submode == BLKmode) 251718334Speter abort (); 251818334Speter 251918334Speter op0 = protect_from_queue (op0, 0); 252018334Speter 252118334Speter if (flag_force_mem) 252218334Speter { 252318334Speter op0 = force_not_mem (op0); 252418334Speter } 252518334Speter 252618334Speter last = get_last_insn (); 252718334Speter 252818334Speter if (target) 252918334Speter target = protect_from_queue (target, 1); 253018334Speter 253118334Speter if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 253218334Speter { 253318334Speter int icode = (int) abs_optab->handlers[(int) mode].insn_code; 253418334Speter enum machine_mode mode0 = insn_operand_mode[icode][1]; 253518334Speter rtx xop0 = op0; 253618334Speter 253718334Speter if (target) 253818334Speter temp = target; 253918334Speter else 254018334Speter temp = gen_reg_rtx (submode); 254118334Speter 254218334Speter if (GET_MODE (xop0) != VOIDmode 254318334Speter && GET_MODE (xop0) != mode0) 254418334Speter xop0 = convert_to_mode (mode0, xop0, unsignedp); 254518334Speter 254618334Speter /* Now, if insn doesn't accept our operand, put it into a pseudo. */ 254718334Speter 254818334Speter if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) 254918334Speter xop0 = copy_to_mode_reg (mode0, xop0); 255018334Speter 255118334Speter if (! (*insn_operand_predicate[icode][0]) (temp, submode)) 255218334Speter temp = gen_reg_rtx (submode); 255318334Speter 255418334Speter pat = GEN_FCN (icode) (temp, xop0); 255518334Speter if (pat) 255618334Speter { 255718334Speter if (GET_CODE (pat) == SEQUENCE 255818334Speter && ! add_equal_note (pat, temp, abs_optab->code, xop0, NULL_RTX)) 255918334Speter { 256018334Speter delete_insns_since (last); 256118334Speter return expand_unop (mode, abs_optab, op0, NULL_RTX, unsignedp); 256218334Speter } 256318334Speter 256418334Speter emit_insn (pat); 256518334Speter 256618334Speter return temp; 256718334Speter } 256818334Speter else 256918334Speter delete_insns_since (last); 257018334Speter } 257118334Speter 257218334Speter /* It can't be done in this mode. Can we open-code it in a wider mode? */ 257318334Speter 257418334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 257518334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 257618334Speter { 257718334Speter if (abs_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) 257818334Speter { 257918334Speter rtx xop0 = op0; 258018334Speter 258118334Speter xop0 = convert_modes (wider_mode, mode, xop0, unsignedp); 258218334Speter temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); 258318334Speter 258418334Speter if (temp) 258518334Speter { 258618334Speter if (class != MODE_COMPLEX_INT) 258718334Speter { 258818334Speter if (target == 0) 258918334Speter target = gen_reg_rtx (submode); 259018334Speter convert_move (target, temp, 0); 259118334Speter return target; 259218334Speter } 259318334Speter else 259418334Speter return gen_lowpart (submode, temp); 259518334Speter } 259618334Speter else 259718334Speter delete_insns_since (last); 259818334Speter } 259918334Speter } 260018334Speter 260118334Speter /* Open-code the complex absolute-value operation 260218334Speter if we can open-code sqrt. Otherwise it's not worth while. */ 260318334Speter if (sqrt_optab->handlers[(int) submode].insn_code != CODE_FOR_nothing) 260418334Speter { 260518334Speter rtx real, imag, total; 260618334Speter 260718334Speter real = gen_realpart (submode, op0); 260818334Speter imag = gen_imagpart (submode, op0); 260918334Speter 261018334Speter /* Square both parts. */ 261118334Speter real = expand_mult (submode, real, real, NULL_RTX, 0); 261218334Speter imag = expand_mult (submode, imag, imag, NULL_RTX, 0); 261318334Speter 261418334Speter /* Sum the parts. */ 261518334Speter total = expand_binop (submode, add_optab, real, imag, NULL_RTX, 261618334Speter 0, OPTAB_LIB_WIDEN); 261718334Speter 261818334Speter /* Get sqrt in TARGET. Set TARGET to where the result is. */ 261918334Speter target = expand_unop (submode, sqrt_optab, total, target, 0); 262018334Speter if (target == 0) 262118334Speter delete_insns_since (last); 262218334Speter else 262318334Speter return target; 262418334Speter } 262518334Speter 262618334Speter /* Now try a library call in this mode. */ 262718334Speter if (abs_optab->handlers[(int) mode].libfunc) 262818334Speter { 262918334Speter rtx insns; 263018334Speter rtx value; 263118334Speter 263218334Speter start_sequence (); 263318334Speter 263418334Speter /* Pass 1 for NO_QUEUE so we don't lose any increments 263518334Speter if the libcall is cse'd or moved. */ 263618334Speter value = emit_library_call_value (abs_optab->handlers[(int) mode].libfunc, 263718334Speter NULL_RTX, 1, submode, 1, op0, mode); 263818334Speter insns = get_insns (); 263918334Speter end_sequence (); 264018334Speter 264118334Speter target = gen_reg_rtx (submode); 264218334Speter emit_libcall_block (insns, target, value, 264350397Sobrien gen_rtx_fmt_e (abs_optab->code, mode, op0)); 264418334Speter 264518334Speter return target; 264618334Speter } 264718334Speter 264818334Speter /* It can't be done in this mode. Can we do it in a wider mode? */ 264918334Speter 265018334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 265118334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 265218334Speter { 265318334Speter if ((abs_optab->handlers[(int) wider_mode].insn_code 265418334Speter != CODE_FOR_nothing) 265518334Speter || abs_optab->handlers[(int) wider_mode].libfunc) 265618334Speter { 265718334Speter rtx xop0 = op0; 265818334Speter 265918334Speter xop0 = convert_modes (wider_mode, mode, xop0, unsignedp); 266018334Speter 266118334Speter temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); 266218334Speter 266318334Speter if (temp) 266418334Speter { 266518334Speter if (class != MODE_COMPLEX_INT) 266618334Speter { 266718334Speter if (target == 0) 266818334Speter target = gen_reg_rtx (submode); 266918334Speter convert_move (target, temp, 0); 267018334Speter return target; 267118334Speter } 267218334Speter else 267318334Speter return gen_lowpart (submode, temp); 267418334Speter } 267518334Speter else 267618334Speter delete_insns_since (last); 267718334Speter } 267818334Speter } 267918334Speter 268018334Speter delete_insns_since (entry_last); 268118334Speter return 0; 268218334Speter} 268318334Speter 268418334Speter/* Generate an instruction whose insn-code is INSN_CODE, 268518334Speter with two operands: an output TARGET and an input OP0. 268618334Speter TARGET *must* be nonzero, and the output is always stored there. 268718334Speter CODE is an rtx code such that (CODE OP0) is an rtx that describes 268818334Speter the value that is stored into TARGET. */ 268918334Speter 269018334Spetervoid 269118334Speteremit_unop_insn (icode, target, op0, code) 269218334Speter int icode; 269318334Speter rtx target; 269418334Speter rtx op0; 269518334Speter enum rtx_code code; 269618334Speter{ 269718334Speter register rtx temp; 269818334Speter enum machine_mode mode0 = insn_operand_mode[icode][1]; 269918334Speter rtx pat; 270018334Speter 270118334Speter temp = target = protect_from_queue (target, 1); 270218334Speter 270318334Speter op0 = protect_from_queue (op0, 0); 270418334Speter 270550397Sobrien /* Sign and zero extension from memory is often done specially on 270650397Sobrien RISC machines, so forcing into a register here can pessimize 270750397Sobrien code. */ 270850397Sobrien if (flag_force_mem && code != SIGN_EXTEND && code != ZERO_EXTEND) 270918334Speter op0 = force_not_mem (op0); 271018334Speter 271118334Speter /* Now, if insn does not accept our operands, put them into pseudos. */ 271218334Speter 271318334Speter if (! (*insn_operand_predicate[icode][1]) (op0, mode0)) 271418334Speter op0 = copy_to_mode_reg (mode0, op0); 271518334Speter 271618334Speter if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp)) 271718334Speter || (flag_force_mem && GET_CODE (temp) == MEM)) 271818334Speter temp = gen_reg_rtx (GET_MODE (temp)); 271918334Speter 272018334Speter pat = GEN_FCN (icode) (temp, op0); 272118334Speter 272218334Speter if (GET_CODE (pat) == SEQUENCE && code != UNKNOWN) 272318334Speter add_equal_note (pat, temp, code, op0, NULL_RTX); 272418334Speter 272518334Speter emit_insn (pat); 272618334Speter 272718334Speter if (temp != target) 272818334Speter emit_move_insn (target, temp); 272918334Speter} 273018334Speter 273118334Speter/* Emit code to perform a series of operations on a multi-word quantity, one 273218334Speter word at a time. 273318334Speter 273418334Speter Such a block is preceded by a CLOBBER of the output, consists of multiple 273518334Speter insns, each setting one word of the output, and followed by a SET copying 273618334Speter the output to itself. 273718334Speter 273818334Speter Each of the insns setting words of the output receives a REG_NO_CONFLICT 273918334Speter note indicating that it doesn't conflict with the (also multi-word) 274018334Speter inputs. The entire block is surrounded by REG_LIBCALL and REG_RETVAL 274118334Speter notes. 274218334Speter 274318334Speter INSNS is a block of code generated to perform the operation, not including 274418334Speter the CLOBBER and final copy. All insns that compute intermediate values 274518334Speter are first emitted, followed by the block as described above. 274618334Speter 274718334Speter TARGET, OP0, and OP1 are the output and inputs of the operations, 274818334Speter respectively. OP1 may be zero for a unary operation. 274918334Speter 275018334Speter EQUIV, if non-zero, is an expression to be placed into a REG_EQUAL note 275118334Speter on the last insn. 275218334Speter 275318334Speter If TARGET is not a register, INSNS is simply emitted with no special 275418334Speter processing. Likewise if anything in INSNS is not an INSN or if 275518334Speter there is a libcall block inside INSNS. 275618334Speter 275718334Speter The final insn emitted is returned. */ 275818334Speter 275918334Speterrtx 276018334Speteremit_no_conflict_block (insns, target, op0, op1, equiv) 276118334Speter rtx insns; 276218334Speter rtx target; 276318334Speter rtx op0, op1; 276418334Speter rtx equiv; 276518334Speter{ 276618334Speter rtx prev, next, first, last, insn; 276718334Speter 276818334Speter if (GET_CODE (target) != REG || reload_in_progress) 276918334Speter return emit_insns (insns); 277018334Speter else 277118334Speter for (insn = insns; insn; insn = NEXT_INSN (insn)) 277218334Speter if (GET_CODE (insn) != INSN 277318334Speter || find_reg_note (insn, REG_LIBCALL, NULL_RTX)) 277418334Speter return emit_insns (insns); 277518334Speter 277618334Speter /* First emit all insns that do not store into words of the output and remove 277718334Speter these from the list. */ 277818334Speter for (insn = insns; insn; insn = next) 277918334Speter { 278018334Speter rtx set = 0; 278118334Speter int i; 278218334Speter 278318334Speter next = NEXT_INSN (insn); 278418334Speter 278518334Speter if (GET_CODE (PATTERN (insn)) == SET) 278618334Speter set = PATTERN (insn); 278718334Speter else if (GET_CODE (PATTERN (insn)) == PARALLEL) 278818334Speter { 278918334Speter for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) 279018334Speter if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) 279118334Speter { 279218334Speter set = XVECEXP (PATTERN (insn), 0, i); 279318334Speter break; 279418334Speter } 279518334Speter } 279618334Speter 279718334Speter if (set == 0) 279818334Speter abort (); 279918334Speter 280018334Speter if (! reg_overlap_mentioned_p (target, SET_DEST (set))) 280118334Speter { 280218334Speter if (PREV_INSN (insn)) 280318334Speter NEXT_INSN (PREV_INSN (insn)) = next; 280418334Speter else 280518334Speter insns = next; 280618334Speter 280718334Speter if (next) 280818334Speter PREV_INSN (next) = PREV_INSN (insn); 280918334Speter 281018334Speter add_insn (insn); 281118334Speter } 281218334Speter } 281318334Speter 281418334Speter prev = get_last_insn (); 281518334Speter 281618334Speter /* Now write the CLOBBER of the output, followed by the setting of each 281718334Speter of the words, followed by the final copy. */ 281818334Speter if (target != op0 && target != op1) 281950397Sobrien emit_insn (gen_rtx_CLOBBER (VOIDmode, target)); 282018334Speter 282118334Speter for (insn = insns; insn; insn = next) 282218334Speter { 282318334Speter next = NEXT_INSN (insn); 282418334Speter add_insn (insn); 282518334Speter 282618334Speter if (op1 && GET_CODE (op1) == REG) 282750397Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op1, 282850397Sobrien REG_NOTES (insn)); 282918334Speter 283018334Speter if (op0 && GET_CODE (op0) == REG) 283150397Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_NO_CONFLICT, op0, 283250397Sobrien REG_NOTES (insn)); 283318334Speter } 283418334Speter 283518334Speter if (mov_optab->handlers[(int) GET_MODE (target)].insn_code 283618334Speter != CODE_FOR_nothing) 283718334Speter { 283818334Speter last = emit_move_insn (target, target); 283918334Speter if (equiv) 284052284Sobrien set_unique_reg_note (last, REG_EQUAL, equiv); 284118334Speter } 284218334Speter else 284318334Speter last = get_last_insn (); 284418334Speter 284518334Speter if (prev == 0) 284618334Speter first = get_insns (); 284718334Speter else 284818334Speter first = NEXT_INSN (prev); 284918334Speter 285018334Speter /* Encapsulate the block so it gets manipulated as a unit. */ 285150397Sobrien REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last, 285250397Sobrien REG_NOTES (first)); 285350397Sobrien REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, REG_NOTES (last)); 285418334Speter 285518334Speter return last; 285618334Speter} 285718334Speter 285818334Speter/* Emit code to make a call to a constant function or a library call. 285918334Speter 286018334Speter INSNS is a list containing all insns emitted in the call. 286118334Speter These insns leave the result in RESULT. Our block is to copy RESULT 286218334Speter to TARGET, which is logically equivalent to EQUIV. 286318334Speter 286418334Speter We first emit any insns that set a pseudo on the assumption that these are 286518334Speter loading constants into registers; doing so allows them to be safely cse'ed 286618334Speter between blocks. Then we emit all the other insns in the block, followed by 286718334Speter an insn to move RESULT to TARGET. This last insn will have a REQ_EQUAL 286818334Speter note with an operand of EQUIV. 286918334Speter 287018334Speter Moving assignments to pseudos outside of the block is done to improve 287118334Speter the generated code, but is not required to generate correct code, 287218334Speter hence being unable to move an assignment is not grounds for not making 287318334Speter a libcall block. There are two reasons why it is safe to leave these 287418334Speter insns inside the block: First, we know that these pseudos cannot be 287518334Speter used in generated RTL outside the block since they are created for 287618334Speter temporary purposes within the block. Second, CSE will not record the 287718334Speter values of anything set inside a libcall block, so we know they must 287818334Speter be dead at the end of the block. 287918334Speter 288018334Speter Except for the first group of insns (the ones setting pseudos), the 288118334Speter block is delimited by REG_RETVAL and REG_LIBCALL notes. */ 288218334Speter 288318334Spetervoid 288418334Speteremit_libcall_block (insns, target, result, equiv) 288518334Speter rtx insns; 288618334Speter rtx target; 288718334Speter rtx result; 288818334Speter rtx equiv; 288918334Speter{ 289018334Speter rtx prev, next, first, last, insn; 289118334Speter 289252284Sobrien /* look for any CALL_INSNs in this sequence, and attach a REG_EH_REGION 289352284Sobrien reg note to indicate that this call cannot throw. (Unless there is 289452284Sobrien already a REG_EH_REGION note.) */ 289552284Sobrien 289652284Sobrien for (insn = insns; insn; insn = NEXT_INSN (insn)) 289752284Sobrien { 289852284Sobrien if (GET_CODE (insn) == CALL_INSN) 289952284Sobrien { 290052284Sobrien rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); 290152284Sobrien if (note == NULL_RTX) 290252284Sobrien REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_REGION, GEN_INT (-1), 290352284Sobrien REG_NOTES (insn)); 290452284Sobrien } 290552284Sobrien } 290652284Sobrien 290718334Speter /* First emit all insns that set pseudos. Remove them from the list as 290818334Speter we go. Avoid insns that set pseudos which were referenced in previous 290918334Speter insns. These can be generated by move_by_pieces, for example, 291018334Speter to update an address. Similarly, avoid insns that reference things 291118334Speter set in previous insns. */ 291218334Speter 291318334Speter for (insn = insns; insn; insn = next) 291418334Speter { 291518334Speter rtx set = single_set (insn); 291618334Speter 291718334Speter next = NEXT_INSN (insn); 291818334Speter 291918334Speter if (set != 0 && GET_CODE (SET_DEST (set)) == REG 292018334Speter && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER 292118334Speter && (insn == insns 292218334Speter || (! reg_mentioned_p (SET_DEST (set), PATTERN (insns)) 292318334Speter && ! reg_used_between_p (SET_DEST (set), insns, insn) 292418334Speter && ! modified_in_p (SET_SRC (set), insns) 292518334Speter && ! modified_between_p (SET_SRC (set), insns, insn)))) 292618334Speter { 292718334Speter if (PREV_INSN (insn)) 292818334Speter NEXT_INSN (PREV_INSN (insn)) = next; 292918334Speter else 293018334Speter insns = next; 293118334Speter 293218334Speter if (next) 293318334Speter PREV_INSN (next) = PREV_INSN (insn); 293418334Speter 293518334Speter add_insn (insn); 293618334Speter } 293718334Speter } 293818334Speter 293918334Speter prev = get_last_insn (); 294018334Speter 294118334Speter /* Write the remaining insns followed by the final copy. */ 294218334Speter 294318334Speter for (insn = insns; insn; insn = next) 294418334Speter { 294518334Speter next = NEXT_INSN (insn); 294618334Speter 294718334Speter add_insn (insn); 294818334Speter } 294918334Speter 295018334Speter last = emit_move_insn (target, result); 295150397Sobrien if (mov_optab->handlers[(int) GET_MODE (target)].insn_code 295250397Sobrien != CODE_FOR_nothing) 295352284Sobrien set_unique_reg_note (last, REG_EQUAL, copy_rtx (equiv)); 295418334Speter 295518334Speter if (prev == 0) 295618334Speter first = get_insns (); 295718334Speter else 295818334Speter first = NEXT_INSN (prev); 295918334Speter 296018334Speter /* Encapsulate the block so it gets manipulated as a unit. */ 296150397Sobrien REG_NOTES (first) = gen_rtx_INSN_LIST (REG_LIBCALL, last, 296250397Sobrien REG_NOTES (first)); 296350397Sobrien REG_NOTES (last) = gen_rtx_INSN_LIST (REG_RETVAL, first, REG_NOTES (last)); 296418334Speter} 296518334Speter 296618334Speter/* Generate code to store zero in X. */ 296718334Speter 296818334Spetervoid 296918334Speteremit_clr_insn (x) 297018334Speter rtx x; 297118334Speter{ 297218334Speter emit_move_insn (x, const0_rtx); 297318334Speter} 297418334Speter 297518334Speter/* Generate code to store 1 in X 297618334Speter assuming it contains zero beforehand. */ 297718334Speter 297818334Spetervoid 297918334Speteremit_0_to_1_insn (x) 298018334Speter rtx x; 298118334Speter{ 298218334Speter emit_move_insn (x, const1_rtx); 298318334Speter} 298418334Speter 298518334Speter/* Generate code to compare X with Y 298618334Speter so that the condition codes are set. 298718334Speter 298818334Speter MODE is the mode of the inputs (in case they are const_int). 298918334Speter UNSIGNEDP nonzero says that X and Y are unsigned; 299018334Speter this matters if they need to be widened. 299118334Speter 299218334Speter If they have mode BLKmode, then SIZE specifies the size of both X and Y, 299318334Speter and ALIGN specifies the known shared alignment of X and Y. 299418334Speter 299518334Speter COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). 299618334Speter It is ignored for fixed-point and block comparisons; 299718334Speter it is used only for floating-point comparisons. */ 299818334Speter 299918334Spetervoid 300018334Speteremit_cmp_insn (x, y, comparison, size, mode, unsignedp, align) 300118334Speter rtx x, y; 300218334Speter enum rtx_code comparison; 300318334Speter rtx size; 300418334Speter enum machine_mode mode; 300518334Speter int unsignedp; 300618334Speter int align; 300718334Speter{ 300818334Speter enum mode_class class; 300918334Speter enum machine_mode wider_mode; 301018334Speter 301118334Speter class = GET_MODE_CLASS (mode); 301218334Speter 301318334Speter /* They could both be VOIDmode if both args are immediate constants, 301418334Speter but we should fold that at an earlier stage. 301518334Speter With no special code here, this will call abort, 301618334Speter reminding the programmer to implement such folding. */ 301718334Speter 301818334Speter if (mode != BLKmode && flag_force_mem) 301918334Speter { 302018334Speter x = force_not_mem (x); 302118334Speter y = force_not_mem (y); 302218334Speter } 302318334Speter 302418334Speter /* If we are inside an appropriately-short loop and one operand is an 302518334Speter expensive constant, force it into a register. */ 302618334Speter if (CONSTANT_P (x) && preserve_subexpressions_p () && rtx_cost (x, COMPARE) > 2) 302718334Speter x = force_reg (mode, x); 302818334Speter 302918334Speter if (CONSTANT_P (y) && preserve_subexpressions_p () && rtx_cost (y, COMPARE) > 2) 303018334Speter y = force_reg (mode, y); 303118334Speter 303252284Sobrien#ifdef HAVE_cc0 303352284Sobrien /* Abort if we have a non-canonical comparison. The RTL documentation 303452284Sobrien states that canonical comparisons are required only for targets which 303552284Sobrien have cc0. */ 303652284Sobrien if (CONSTANT_P (x) && ! CONSTANT_P (y)) 303752284Sobrien abort(); 303852284Sobrien#endif 303952284Sobrien 304018334Speter /* Don't let both operands fail to indicate the mode. */ 304118334Speter if (GET_MODE (x) == VOIDmode && GET_MODE (y) == VOIDmode) 304218334Speter x = force_reg (mode, x); 304318334Speter 304418334Speter /* Handle all BLKmode compares. */ 304518334Speter 304618334Speter if (mode == BLKmode) 304718334Speter { 304818334Speter emit_queue (); 304918334Speter x = protect_from_queue (x, 0); 305018334Speter y = protect_from_queue (y, 0); 305118334Speter 305218334Speter if (size == 0) 305318334Speter abort (); 305418334Speter#ifdef HAVE_cmpstrqi 305518334Speter if (HAVE_cmpstrqi 305618334Speter && GET_CODE (size) == CONST_INT 305718334Speter && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode))) 305818334Speter { 305918334Speter enum machine_mode result_mode 306018334Speter = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0]; 306118334Speter rtx result = gen_reg_rtx (result_mode); 306218334Speter emit_insn (gen_cmpstrqi (result, x, y, size, GEN_INT (align))); 306318334Speter emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, 306418334Speter result_mode, 0, 0); 306518334Speter } 306618334Speter else 306718334Speter#endif 306818334Speter#ifdef HAVE_cmpstrhi 306918334Speter if (HAVE_cmpstrhi 307018334Speter && GET_CODE (size) == CONST_INT 307118334Speter && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode))) 307218334Speter { 307318334Speter enum machine_mode result_mode 307418334Speter = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0]; 307518334Speter rtx result = gen_reg_rtx (result_mode); 307618334Speter emit_insn (gen_cmpstrhi (result, x, y, size, GEN_INT (align))); 307718334Speter emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, 307818334Speter result_mode, 0, 0); 307918334Speter } 308018334Speter else 308118334Speter#endif 308218334Speter#ifdef HAVE_cmpstrsi 308318334Speter if (HAVE_cmpstrsi) 308418334Speter { 308518334Speter enum machine_mode result_mode 308618334Speter = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0]; 308718334Speter rtx result = gen_reg_rtx (result_mode); 308818334Speter size = protect_from_queue (size, 0); 308918334Speter emit_insn (gen_cmpstrsi (result, x, y, 309018334Speter convert_to_mode (SImode, size, 1), 309118334Speter GEN_INT (align))); 309218334Speter emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, 309318334Speter result_mode, 0, 0); 309418334Speter } 309518334Speter else 309618334Speter#endif 309718334Speter { 309850397Sobrien rtx result; 309950397Sobrien 310018334Speter#ifdef TARGET_MEM_FUNCTIONS 310118334Speter emit_library_call (memcmp_libfunc, 0, 310218334Speter TYPE_MODE (integer_type_node), 3, 310318334Speter XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, 310450397Sobrien convert_to_mode (TYPE_MODE (sizetype), size, 310550397Sobrien TREE_UNSIGNED (sizetype)), 310650397Sobrien TYPE_MODE (sizetype)); 310718334Speter#else 310818334Speter emit_library_call (bcmp_libfunc, 0, 310918334Speter TYPE_MODE (integer_type_node), 3, 311018334Speter XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, 311150397Sobrien convert_to_mode (TYPE_MODE (integer_type_node), 311250397Sobrien size, 311350397Sobrien TREE_UNSIGNED (integer_type_node)), 311450397Sobrien TYPE_MODE (integer_type_node)); 311518334Speter#endif 311650397Sobrien 311750397Sobrien /* Immediately move the result of the libcall into a pseudo 311850397Sobrien register so reload doesn't clobber the value if it needs 311950397Sobrien the return register for a spill reg. */ 312050397Sobrien result = gen_reg_rtx (TYPE_MODE (integer_type_node)); 312150397Sobrien emit_move_insn (result, 312250397Sobrien hard_libcall_value (TYPE_MODE (integer_type_node))); 312350397Sobrien emit_cmp_insn (result, 312418334Speter const0_rtx, comparison, NULL_RTX, 312518334Speter TYPE_MODE (integer_type_node), 0, 0); 312618334Speter } 312718334Speter return; 312818334Speter } 312918334Speter 313018334Speter /* Handle some compares against zero. */ 313118334Speter 313218334Speter if (y == CONST0_RTX (mode) 313318334Speter && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 313418334Speter { 313518334Speter int icode = (int) tst_optab->handlers[(int) mode].insn_code; 313618334Speter 313718334Speter emit_queue (); 313818334Speter x = protect_from_queue (x, 0); 313918334Speter y = protect_from_queue (y, 0); 314018334Speter 314118334Speter /* Now, if insn does accept these operands, put them into pseudos. */ 314218334Speter if (! (*insn_operand_predicate[icode][0]) 314318334Speter (x, insn_operand_mode[icode][0])) 314418334Speter x = copy_to_mode_reg (insn_operand_mode[icode][0], x); 314518334Speter 314618334Speter emit_insn (GEN_FCN (icode) (x)); 314718334Speter return; 314818334Speter } 314918334Speter 315018334Speter /* Handle compares for which there is a directly suitable insn. */ 315118334Speter 315218334Speter if (cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 315318334Speter { 315418334Speter int icode = (int) cmp_optab->handlers[(int) mode].insn_code; 315518334Speter 315618334Speter emit_queue (); 315718334Speter x = protect_from_queue (x, 0); 315818334Speter y = protect_from_queue (y, 0); 315918334Speter 316018334Speter /* Now, if insn doesn't accept these operands, put them into pseudos. */ 316118334Speter if (! (*insn_operand_predicate[icode][0]) 316218334Speter (x, insn_operand_mode[icode][0])) 316318334Speter x = copy_to_mode_reg (insn_operand_mode[icode][0], x); 316418334Speter 316518334Speter if (! (*insn_operand_predicate[icode][1]) 316618334Speter (y, insn_operand_mode[icode][1])) 316718334Speter y = copy_to_mode_reg (insn_operand_mode[icode][1], y); 316818334Speter 316918334Speter emit_insn (GEN_FCN (icode) (x, y)); 317018334Speter return; 317118334Speter } 317218334Speter 317318334Speter /* Try widening if we can find a direct insn that way. */ 317418334Speter 317518334Speter if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) 317618334Speter { 317718334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 317818334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 317918334Speter { 318018334Speter if (cmp_optab->handlers[(int) wider_mode].insn_code 318118334Speter != CODE_FOR_nothing) 318218334Speter { 318318334Speter x = protect_from_queue (x, 0); 318418334Speter y = protect_from_queue (y, 0); 318518334Speter x = convert_modes (wider_mode, mode, x, unsignedp); 318618334Speter y = convert_modes (wider_mode, mode, y, unsignedp); 318718334Speter emit_cmp_insn (x, y, comparison, NULL_RTX, 318818334Speter wider_mode, unsignedp, align); 318918334Speter return; 319018334Speter } 319118334Speter } 319218334Speter } 319318334Speter 319418334Speter /* Handle a lib call just for the mode we are using. */ 319518334Speter 319618334Speter if (cmp_optab->handlers[(int) mode].libfunc 319718334Speter && class != MODE_FLOAT) 319818334Speter { 319918334Speter rtx libfunc = cmp_optab->handlers[(int) mode].libfunc; 320050397Sobrien rtx result; 320150397Sobrien 320218334Speter /* If we want unsigned, and this mode has a distinct unsigned 320318334Speter comparison routine, use that. */ 320418334Speter if (unsignedp && ucmp_optab->handlers[(int) mode].libfunc) 320518334Speter libfunc = ucmp_optab->handlers[(int) mode].libfunc; 320618334Speter 320718334Speter emit_library_call (libfunc, 1, 320818334Speter word_mode, 2, x, mode, y, mode); 320918334Speter 321050397Sobrien /* Immediately move the result of the libcall into a pseudo 321150397Sobrien register so reload doesn't clobber the value if it needs 321250397Sobrien the return register for a spill reg. */ 321350397Sobrien result = gen_reg_rtx (word_mode); 321450397Sobrien emit_move_insn (result, hard_libcall_value (word_mode)); 321550397Sobrien 321618334Speter /* Integer comparison returns a result that must be compared against 1, 321718334Speter so that even if we do an unsigned compare afterward, 321818334Speter there is still a value that can represent the result "less than". */ 321950397Sobrien emit_cmp_insn (result, const1_rtx, 322018334Speter comparison, NULL_RTX, word_mode, unsignedp, 0); 322118334Speter return; 322218334Speter } 322318334Speter 322418334Speter if (class == MODE_FLOAT) 322518334Speter emit_float_lib_cmp (x, y, comparison); 322618334Speter 322718334Speter else 322818334Speter abort (); 322918334Speter} 323018334Speter 323152284Sobrien/* Generate code to compare X with Y so that the condition codes are 323252284Sobrien set and to jump to LABEL if the condition is true. If X is a 323352284Sobrien constant and Y is not a constant, then the comparison is swapped to 323452284Sobrien ensure that the comparison RTL has the canonical form. 323552284Sobrien 323652284Sobrien UNSIGNEDP nonzero says that X and Y are unsigned; this matters if they 323752284Sobrien need to be widened by emit_cmp_insn. UNSIGNEDP is also used to select 323852284Sobrien the proper branch condition code. 323952284Sobrien 324052284Sobrien If X and Y have mode BLKmode, then SIZE specifies the size of both X and Y, 324152284Sobrien and ALIGN specifies the known shared alignment of X and Y. 324252284Sobrien 324352284Sobrien MODE is the mode of the inputs (in case they are const_int). 324452284Sobrien 324552284Sobrien COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). It will 324652284Sobrien be passed unchanged to emit_cmp_insn, then potentially converted into an 324752284Sobrien unsigned variant based on UNSIGNEDP to select a proper jump instruction. */ 324852284Sobrien 324952284Sobrienvoid 325052284Sobrienemit_cmp_and_jump_insns (x, y, comparison, size, mode, unsignedp, align, label) 325152284Sobrien rtx x, y; 325252284Sobrien enum rtx_code comparison; 325352284Sobrien rtx size; 325452284Sobrien enum machine_mode mode; 325552284Sobrien int unsignedp; 325652284Sobrien int align; 325752284Sobrien rtx label; 325852284Sobrien{ 325952284Sobrien rtx op0; 326052284Sobrien rtx op1; 326152284Sobrien 326252284Sobrien if (CONSTANT_P (x)) 326352284Sobrien { 326452284Sobrien /* Swap operands and condition to ensure canonical RTL. */ 326552284Sobrien op0 = y; 326652284Sobrien op1 = x; 326752284Sobrien comparison = swap_condition (comparison); 326852284Sobrien } 326952284Sobrien else 327052284Sobrien { 327152284Sobrien op0 = x; 327252284Sobrien op1 = y; 327352284Sobrien } 327452284Sobrien 327552284Sobrien#ifdef HAVE_cc0 327652284Sobrien /* If OP0 is still a constant, then both X and Y must be constants. Force 327752284Sobrien X into a register to avoid aborting in emit_cmp_insn due to non-canonical 327852284Sobrien RTL. */ 327952284Sobrien if (CONSTANT_P (op0)) 328052284Sobrien op0 = force_reg (mode, op0); 328152284Sobrien#endif 328252284Sobrien 328352284Sobrien emit_cmp_insn (op0, op1, comparison, size, mode, unsignedp, align); 328452284Sobrien 328552284Sobrien if (unsignedp) 328652284Sobrien comparison = unsigned_condition (comparison); 328752284Sobrien emit_jump_insn ((*bcc_gen_fctn[(int) comparison]) (label)); 328852284Sobrien} 328952284Sobrien 329052284Sobrien 329118334Speter/* Nonzero if a compare of mode MODE can be done straightforwardly 329218334Speter (without splitting it into pieces). */ 329318334Speter 329418334Speterint 329518334Spetercan_compare_p (mode) 329618334Speter enum machine_mode mode; 329718334Speter{ 329818334Speter do 329918334Speter { 330018334Speter if (cmp_optab->handlers[(int)mode].insn_code != CODE_FOR_nothing) 330118334Speter return 1; 330218334Speter mode = GET_MODE_WIDER_MODE (mode); 330318334Speter } while (mode != VOIDmode); 330418334Speter 330518334Speter return 0; 330618334Speter} 330718334Speter 330818334Speter/* Emit a library call comparison between floating point X and Y. 330918334Speter COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). */ 331018334Speter 331118334Spetervoid 331218334Speteremit_float_lib_cmp (x, y, comparison) 331318334Speter rtx x, y; 331418334Speter enum rtx_code comparison; 331518334Speter{ 331618334Speter enum machine_mode mode = GET_MODE (x); 331718334Speter rtx libfunc = 0; 331850397Sobrien rtx result; 331918334Speter 332018334Speter if (mode == HFmode) 332118334Speter switch (comparison) 332218334Speter { 332318334Speter case EQ: 332418334Speter libfunc = eqhf2_libfunc; 332518334Speter break; 332618334Speter 332718334Speter case NE: 332818334Speter libfunc = nehf2_libfunc; 332918334Speter break; 333018334Speter 333118334Speter case GT: 333218334Speter libfunc = gthf2_libfunc; 333318334Speter break; 333418334Speter 333518334Speter case GE: 333618334Speter libfunc = gehf2_libfunc; 333718334Speter break; 333818334Speter 333918334Speter case LT: 334018334Speter libfunc = lthf2_libfunc; 334118334Speter break; 334218334Speter 334318334Speter case LE: 334418334Speter libfunc = lehf2_libfunc; 334518334Speter break; 334650397Sobrien 334750397Sobrien default: 334850397Sobrien break; 334918334Speter } 335018334Speter else if (mode == SFmode) 335118334Speter switch (comparison) 335218334Speter { 335318334Speter case EQ: 335418334Speter libfunc = eqsf2_libfunc; 335518334Speter break; 335618334Speter 335718334Speter case NE: 335818334Speter libfunc = nesf2_libfunc; 335918334Speter break; 336018334Speter 336118334Speter case GT: 336218334Speter libfunc = gtsf2_libfunc; 336318334Speter break; 336418334Speter 336518334Speter case GE: 336618334Speter libfunc = gesf2_libfunc; 336718334Speter break; 336818334Speter 336918334Speter case LT: 337018334Speter libfunc = ltsf2_libfunc; 337118334Speter break; 337218334Speter 337318334Speter case LE: 337418334Speter libfunc = lesf2_libfunc; 337518334Speter break; 337650397Sobrien 337750397Sobrien default: 337850397Sobrien break; 337918334Speter } 338018334Speter else if (mode == DFmode) 338118334Speter switch (comparison) 338218334Speter { 338318334Speter case EQ: 338418334Speter libfunc = eqdf2_libfunc; 338518334Speter break; 338618334Speter 338718334Speter case NE: 338818334Speter libfunc = nedf2_libfunc; 338918334Speter break; 339018334Speter 339118334Speter case GT: 339218334Speter libfunc = gtdf2_libfunc; 339318334Speter break; 339418334Speter 339518334Speter case GE: 339618334Speter libfunc = gedf2_libfunc; 339718334Speter break; 339818334Speter 339918334Speter case LT: 340018334Speter libfunc = ltdf2_libfunc; 340118334Speter break; 340218334Speter 340318334Speter case LE: 340418334Speter libfunc = ledf2_libfunc; 340518334Speter break; 340650397Sobrien 340750397Sobrien default: 340850397Sobrien break; 340918334Speter } 341018334Speter else if (mode == XFmode) 341118334Speter switch (comparison) 341218334Speter { 341318334Speter case EQ: 341418334Speter libfunc = eqxf2_libfunc; 341518334Speter break; 341618334Speter 341718334Speter case NE: 341818334Speter libfunc = nexf2_libfunc; 341918334Speter break; 342018334Speter 342118334Speter case GT: 342218334Speter libfunc = gtxf2_libfunc; 342318334Speter break; 342418334Speter 342518334Speter case GE: 342618334Speter libfunc = gexf2_libfunc; 342718334Speter break; 342818334Speter 342918334Speter case LT: 343018334Speter libfunc = ltxf2_libfunc; 343118334Speter break; 343218334Speter 343318334Speter case LE: 343418334Speter libfunc = lexf2_libfunc; 343518334Speter break; 343650397Sobrien 343750397Sobrien default: 343850397Sobrien break; 343918334Speter } 344018334Speter else if (mode == TFmode) 344118334Speter switch (comparison) 344218334Speter { 344318334Speter case EQ: 344418334Speter libfunc = eqtf2_libfunc; 344518334Speter break; 344618334Speter 344718334Speter case NE: 344818334Speter libfunc = netf2_libfunc; 344918334Speter break; 345018334Speter 345118334Speter case GT: 345218334Speter libfunc = gttf2_libfunc; 345318334Speter break; 345418334Speter 345518334Speter case GE: 345618334Speter libfunc = getf2_libfunc; 345718334Speter break; 345818334Speter 345918334Speter case LT: 346018334Speter libfunc = lttf2_libfunc; 346118334Speter break; 346218334Speter 346318334Speter case LE: 346418334Speter libfunc = letf2_libfunc; 346518334Speter break; 346650397Sobrien 346750397Sobrien default: 346850397Sobrien break; 346918334Speter } 347018334Speter else 347118334Speter { 347218334Speter enum machine_mode wider_mode; 347318334Speter 347418334Speter for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; 347518334Speter wider_mode = GET_MODE_WIDER_MODE (wider_mode)) 347618334Speter { 347718334Speter if ((cmp_optab->handlers[(int) wider_mode].insn_code 347818334Speter != CODE_FOR_nothing) 347918334Speter || (cmp_optab->handlers[(int) wider_mode].libfunc != 0)) 348018334Speter { 348118334Speter x = protect_from_queue (x, 0); 348218334Speter y = protect_from_queue (y, 0); 348318334Speter x = convert_to_mode (wider_mode, x, 0); 348418334Speter y = convert_to_mode (wider_mode, y, 0); 348518334Speter emit_float_lib_cmp (x, y, comparison); 348618334Speter return; 348718334Speter } 348818334Speter } 348918334Speter abort (); 349018334Speter } 349118334Speter 349218334Speter if (libfunc == 0) 349318334Speter abort (); 349418334Speter 349518334Speter emit_library_call (libfunc, 1, 349618334Speter word_mode, 2, x, mode, y, mode); 349718334Speter 349850397Sobrien /* Immediately move the result of the libcall into a pseudo 349950397Sobrien register so reload doesn't clobber the value if it needs 350050397Sobrien the return register for a spill reg. */ 350150397Sobrien result = gen_reg_rtx (word_mode); 350250397Sobrien emit_move_insn (result, hard_libcall_value (word_mode)); 350350397Sobrien 350450397Sobrien emit_cmp_insn (result, const0_rtx, comparison, 350518334Speter NULL_RTX, word_mode, 0, 0); 350618334Speter} 350718334Speter 350818334Speter/* Generate code to indirectly jump to a location given in the rtx LOC. */ 350918334Speter 351018334Spetervoid 351118334Speteremit_indirect_jump (loc) 351218334Speter rtx loc; 351318334Speter{ 351418334Speter if (! ((*insn_operand_predicate[(int)CODE_FOR_indirect_jump][0]) 351518334Speter (loc, Pmode))) 351618334Speter loc = copy_to_mode_reg (Pmode, loc); 351718334Speter 351818334Speter emit_jump_insn (gen_indirect_jump (loc)); 351918334Speter emit_barrier (); 352018334Speter} 352118334Speter 352218334Speter#ifdef HAVE_conditional_move 352318334Speter 352418334Speter/* Emit a conditional move instruction if the machine supports one for that 352518334Speter condition and machine mode. 352618334Speter 352718334Speter OP0 and OP1 are the operands that should be compared using CODE. CMODE is 352818334Speter the mode to use should they be constants. If it is VOIDmode, they cannot 352918334Speter both be constants. 353018334Speter 353118334Speter OP2 should be stored in TARGET if the comparison is true, otherwise OP3 353218334Speter should be stored there. MODE is the mode to use should they be constants. 353318334Speter If it is VOIDmode, they cannot both be constants. 353418334Speter 353518334Speter The result is either TARGET (perhaps modified) or NULL_RTX if the operation 353618334Speter is not supported. */ 353718334Speter 353818334Speterrtx 353918334Speteremit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode, 354018334Speter unsignedp) 354118334Speter rtx target; 354218334Speter enum rtx_code code; 354318334Speter rtx op0, op1; 354418334Speter enum machine_mode cmode; 354518334Speter rtx op2, op3; 354618334Speter enum machine_mode mode; 354718334Speter int unsignedp; 354818334Speter{ 354918334Speter rtx tem, subtarget, comparison, insn; 355018334Speter enum insn_code icode; 355118334Speter 355218334Speter /* If one operand is constant, make it the second one. Only do this 355318334Speter if the other operand is not constant as well. */ 355418334Speter 355518334Speter if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) 355618334Speter || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) 355718334Speter { 355818334Speter tem = op0; 355918334Speter op0 = op1; 356018334Speter op1 = tem; 356118334Speter code = swap_condition (code); 356218334Speter } 356318334Speter 356418334Speter if (cmode == VOIDmode) 356518334Speter cmode = GET_MODE (op0); 356618334Speter 356750397Sobrien if (((CONSTANT_P (op2) && ! CONSTANT_P (op3)) 356850397Sobrien || (GET_CODE (op2) == CONST_INT && GET_CODE (op3) != CONST_INT)) 356950397Sobrien && (GET_MODE_CLASS (GET_MODE (op1)) != MODE_FLOAT 357050397Sobrien || TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT || flag_fast_math)) 357118334Speter { 357218334Speter tem = op2; 357318334Speter op2 = op3; 357418334Speter op3 = tem; 357518334Speter code = reverse_condition (code); 357618334Speter } 357718334Speter 357818334Speter if (mode == VOIDmode) 357918334Speter mode = GET_MODE (op2); 358018334Speter 358118334Speter icode = movcc_gen_code[mode]; 358218334Speter 358318334Speter if (icode == CODE_FOR_nothing) 358418334Speter return 0; 358518334Speter 358618334Speter if (flag_force_mem) 358718334Speter { 358818334Speter op2 = force_not_mem (op2); 358918334Speter op3 = force_not_mem (op3); 359018334Speter } 359118334Speter 359218334Speter if (target) 359318334Speter target = protect_from_queue (target, 1); 359418334Speter else 359518334Speter target = gen_reg_rtx (mode); 359618334Speter 359718334Speter subtarget = target; 359818334Speter 359918334Speter emit_queue (); 360018334Speter 360118334Speter op2 = protect_from_queue (op2, 0); 360218334Speter op3 = protect_from_queue (op3, 0); 360318334Speter 360418334Speter /* If the insn doesn't accept these operands, put them in pseudos. */ 360518334Speter 360618334Speter if (! (*insn_operand_predicate[icode][0]) 360718334Speter (subtarget, insn_operand_mode[icode][0])) 360818334Speter subtarget = gen_reg_rtx (insn_operand_mode[icode][0]); 360918334Speter 361018334Speter if (! (*insn_operand_predicate[icode][2]) 361118334Speter (op2, insn_operand_mode[icode][2])) 361218334Speter op2 = copy_to_mode_reg (insn_operand_mode[icode][2], op2); 361318334Speter 361418334Speter if (! (*insn_operand_predicate[icode][3]) 361518334Speter (op3, insn_operand_mode[icode][3])) 361618334Speter op3 = copy_to_mode_reg (insn_operand_mode[icode][3], op3); 361718334Speter 361818334Speter /* Everything should now be in the suitable form, so emit the compare insn 361918334Speter and then the conditional move. */ 362018334Speter 362118334Speter comparison 362218334Speter = compare_from_rtx (op0, op1, code, unsignedp, cmode, NULL_RTX, 0); 362318334Speter 362418334Speter /* ??? Watch for const0_rtx (nop) and const_true_rtx (unconditional)? */ 362518334Speter if (GET_CODE (comparison) != code) 362618334Speter /* This shouldn't happen. */ 362718334Speter abort (); 362818334Speter 362918334Speter insn = GEN_FCN (icode) (subtarget, comparison, op2, op3); 363018334Speter 363118334Speter /* If that failed, then give up. */ 363218334Speter if (insn == 0) 363318334Speter return 0; 363418334Speter 363518334Speter emit_insn (insn); 363618334Speter 363718334Speter if (subtarget != target) 363818334Speter convert_move (target, subtarget, 0); 363918334Speter 364018334Speter return target; 364118334Speter} 364218334Speter 364318334Speter/* Return non-zero if a conditional move of mode MODE is supported. 364418334Speter 364518334Speter This function is for combine so it can tell whether an insn that looks 364618334Speter like a conditional move is actually supported by the hardware. If we 364718334Speter guess wrong we lose a bit on optimization, but that's it. */ 364818334Speter/* ??? sparc64 supports conditionally moving integers values based on fp 364918334Speter comparisons, and vice versa. How do we handle them? */ 365018334Speter 365118334Speterint 365218334Spetercan_conditionally_move_p (mode) 365318334Speter enum machine_mode mode; 365418334Speter{ 365518334Speter if (movcc_gen_code[mode] != CODE_FOR_nothing) 365618334Speter return 1; 365718334Speter 365818334Speter return 0; 365918334Speter} 366018334Speter 366118334Speter#endif /* HAVE_conditional_move */ 366218334Speter 366318334Speter/* These three functions generate an insn body and return it 366418334Speter rather than emitting the insn. 366518334Speter 366618334Speter They do not protect from queued increments, 366718334Speter because they may be used 1) in protect_from_queue itself 366818334Speter and 2) in other passes where there is no queue. */ 366918334Speter 367018334Speter/* Generate and return an insn body to add Y to X. */ 367118334Speter 367218334Speterrtx 367318334Spetergen_add2_insn (x, y) 367418334Speter rtx x, y; 367518334Speter{ 367618334Speter int icode = (int) add_optab->handlers[(int) GET_MODE (x)].insn_code; 367718334Speter 367818334Speter if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0]) 367918334Speter || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1]) 368018334Speter || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2])) 368118334Speter abort (); 368218334Speter 368318334Speter return (GEN_FCN (icode) (x, x, y)); 368418334Speter} 368518334Speter 368618334Speterint 368718334Speterhave_add2_insn (mode) 368818334Speter enum machine_mode mode; 368918334Speter{ 369018334Speter return add_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing; 369118334Speter} 369218334Speter 369318334Speter/* Generate and return an insn body to subtract Y from X. */ 369418334Speter 369518334Speterrtx 369618334Spetergen_sub2_insn (x, y) 369718334Speter rtx x, y; 369818334Speter{ 369918334Speter int icode = (int) sub_optab->handlers[(int) GET_MODE (x)].insn_code; 370018334Speter 370118334Speter if (! (*insn_operand_predicate[icode][0]) (x, insn_operand_mode[icode][0]) 370218334Speter || ! (*insn_operand_predicate[icode][1]) (x, insn_operand_mode[icode][1]) 370318334Speter || ! (*insn_operand_predicate[icode][2]) (y, insn_operand_mode[icode][2])) 370418334Speter abort (); 370518334Speter 370618334Speter return (GEN_FCN (icode) (x, x, y)); 370718334Speter} 370818334Speter 370918334Speterint 371018334Speterhave_sub2_insn (mode) 371118334Speter enum machine_mode mode; 371218334Speter{ 371318334Speter return sub_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing; 371418334Speter} 371518334Speter 371618334Speter/* Generate the body of an instruction to copy Y into X. 371718334Speter It may be a SEQUENCE, if one insn isn't enough. */ 371818334Speter 371918334Speterrtx 372018334Spetergen_move_insn (x, y) 372118334Speter rtx x, y; 372218334Speter{ 372318334Speter register enum machine_mode mode = GET_MODE (x); 372418334Speter enum insn_code insn_code; 372518334Speter rtx seq; 372618334Speter 372718334Speter if (mode == VOIDmode) 372818334Speter mode = GET_MODE (y); 372918334Speter 373018334Speter insn_code = mov_optab->handlers[(int) mode].insn_code; 373118334Speter 373218334Speter /* Handle MODE_CC modes: If we don't have a special move insn for this mode, 373318334Speter find a mode to do it in. If we have a movcc, use it. Otherwise, 373418334Speter find the MODE_INT mode of the same width. */ 373518334Speter 373618334Speter if (GET_MODE_CLASS (mode) == MODE_CC && insn_code == CODE_FOR_nothing) 373718334Speter { 373818334Speter enum machine_mode tmode = VOIDmode; 373918334Speter rtx x1 = x, y1 = y; 374018334Speter 374118334Speter if (mode != CCmode 374218334Speter && mov_optab->handlers[(int) CCmode].insn_code != CODE_FOR_nothing) 374318334Speter tmode = CCmode; 374418334Speter else 374518334Speter for (tmode = QImode; tmode != VOIDmode; 374618334Speter tmode = GET_MODE_WIDER_MODE (tmode)) 374718334Speter if (GET_MODE_SIZE (tmode) == GET_MODE_SIZE (mode)) 374818334Speter break; 374918334Speter 375018334Speter if (tmode == VOIDmode) 375118334Speter abort (); 375218334Speter 375318334Speter /* Get X and Y in TMODE. We can't use gen_lowpart here because it 375418334Speter may call change_address which is not appropriate if we were 375518334Speter called when a reload was in progress. We don't have to worry 375618334Speter about changing the address since the size in bytes is supposed to 375718334Speter be the same. Copy the MEM to change the mode and move any 375818334Speter substitutions from the old MEM to the new one. */ 375918334Speter 376018334Speter if (reload_in_progress) 376118334Speter { 376218334Speter x = gen_lowpart_common (tmode, x1); 376318334Speter if (x == 0 && GET_CODE (x1) == MEM) 376418334Speter { 376550397Sobrien x = gen_rtx_MEM (tmode, XEXP (x1, 0)); 376618334Speter RTX_UNCHANGING_P (x) = RTX_UNCHANGING_P (x1); 376752284Sobrien MEM_COPY_ATTRIBUTES (x, x1); 376818334Speter copy_replacements (x1, x); 376918334Speter } 377018334Speter 377118334Speter y = gen_lowpart_common (tmode, y1); 377218334Speter if (y == 0 && GET_CODE (y1) == MEM) 377318334Speter { 377450397Sobrien y = gen_rtx_MEM (tmode, XEXP (y1, 0)); 377518334Speter RTX_UNCHANGING_P (y) = RTX_UNCHANGING_P (y1); 377652284Sobrien MEM_COPY_ATTRIBUTES (y, y1); 377718334Speter copy_replacements (y1, y); 377818334Speter } 377918334Speter } 378018334Speter else 378118334Speter { 378218334Speter x = gen_lowpart (tmode, x); 378318334Speter y = gen_lowpart (tmode, y); 378418334Speter } 378518334Speter 378618334Speter insn_code = mov_optab->handlers[(int) tmode].insn_code; 378718334Speter return (GEN_FCN (insn_code) (x, y)); 378818334Speter } 378918334Speter 379018334Speter start_sequence (); 379118334Speter emit_move_insn_1 (x, y); 379218334Speter seq = gen_sequence (); 379318334Speter end_sequence (); 379418334Speter return seq; 379518334Speter} 379618334Speter 379718334Speter/* Return the insn code used to extend FROM_MODE to TO_MODE. 379818334Speter UNSIGNEDP specifies zero-extension instead of sign-extension. If 379918334Speter no such operation exists, CODE_FOR_nothing will be returned. */ 380018334Speter 380118334Speterenum insn_code 380218334Spetercan_extend_p (to_mode, from_mode, unsignedp) 380318334Speter enum machine_mode to_mode, from_mode; 380418334Speter int unsignedp; 380518334Speter{ 380618334Speter return extendtab[(int) to_mode][(int) from_mode][unsignedp]; 380718334Speter} 380818334Speter 380918334Speter/* Generate the body of an insn to extend Y (with mode MFROM) 381018334Speter into X (with mode MTO). Do zero-extension if UNSIGNEDP is nonzero. */ 381118334Speter 381218334Speterrtx 381318334Spetergen_extend_insn (x, y, mto, mfrom, unsignedp) 381418334Speter rtx x, y; 381518334Speter enum machine_mode mto, mfrom; 381618334Speter int unsignedp; 381718334Speter{ 381818334Speter return (GEN_FCN (extendtab[(int) mto][(int) mfrom][unsignedp]) (x, y)); 381918334Speter} 382018334Speter 382118334Speter/* can_fix_p and can_float_p say whether the target machine 382218334Speter can directly convert a given fixed point type to 382318334Speter a given floating point type, or vice versa. 382418334Speter The returned value is the CODE_FOR_... value to use, 382518334Speter or CODE_FOR_nothing if these modes cannot be directly converted. 382618334Speter 382718334Speter *TRUNCP_PTR is set to 1 if it is necessary to output 382818334Speter an explicit FTRUNC insn before the fix insn; otherwise 0. */ 382918334Speter 383018334Speterstatic enum insn_code 383118334Spetercan_fix_p (fixmode, fltmode, unsignedp, truncp_ptr) 383218334Speter enum machine_mode fltmode, fixmode; 383318334Speter int unsignedp; 383418334Speter int *truncp_ptr; 383518334Speter{ 383618334Speter *truncp_ptr = 0; 383718334Speter if (fixtrunctab[(int) fltmode][(int) fixmode][unsignedp] != CODE_FOR_nothing) 383818334Speter return fixtrunctab[(int) fltmode][(int) fixmode][unsignedp]; 383918334Speter 384018334Speter if (ftrunc_optab->handlers[(int) fltmode].insn_code != CODE_FOR_nothing) 384118334Speter { 384218334Speter *truncp_ptr = 1; 384318334Speter return fixtab[(int) fltmode][(int) fixmode][unsignedp]; 384418334Speter } 384518334Speter return CODE_FOR_nothing; 384618334Speter} 384718334Speter 384818334Speterstatic enum insn_code 384918334Spetercan_float_p (fltmode, fixmode, unsignedp) 385018334Speter enum machine_mode fixmode, fltmode; 385118334Speter int unsignedp; 385218334Speter{ 385318334Speter return floattab[(int) fltmode][(int) fixmode][unsignedp]; 385418334Speter} 385518334Speter 385618334Speter/* Generate code to convert FROM to floating point 385718334Speter and store in TO. FROM must be fixed point and not VOIDmode. 385818334Speter UNSIGNEDP nonzero means regard FROM as unsigned. 385918334Speter Normally this is done by correcting the final value 386018334Speter if it is negative. */ 386118334Speter 386218334Spetervoid 386318334Speterexpand_float (to, from, unsignedp) 386418334Speter rtx to, from; 386518334Speter int unsignedp; 386618334Speter{ 386718334Speter enum insn_code icode; 386818334Speter register rtx target = to; 386918334Speter enum machine_mode fmode, imode; 387018334Speter 387118334Speter /* Crash now, because we won't be able to decide which mode to use. */ 387218334Speter if (GET_MODE (from) == VOIDmode) 387318334Speter abort (); 387418334Speter 387518334Speter /* Look for an insn to do the conversion. Do it in the specified 387618334Speter modes if possible; otherwise convert either input, output or both to 387718334Speter wider mode. If the integer mode is wider than the mode of FROM, 387818334Speter we can do the conversion signed even if the input is unsigned. */ 387918334Speter 388018334Speter for (imode = GET_MODE (from); imode != VOIDmode; 388118334Speter imode = GET_MODE_WIDER_MODE (imode)) 388218334Speter for (fmode = GET_MODE (to); fmode != VOIDmode; 388318334Speter fmode = GET_MODE_WIDER_MODE (fmode)) 388418334Speter { 388518334Speter int doing_unsigned = unsignedp; 388618334Speter 388718334Speter icode = can_float_p (fmode, imode, unsignedp); 388818334Speter if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp) 388918334Speter icode = can_float_p (fmode, imode, 0), doing_unsigned = 0; 389018334Speter 389118334Speter if (icode != CODE_FOR_nothing) 389218334Speter { 389318334Speter to = protect_from_queue (to, 1); 389418334Speter from = protect_from_queue (from, 0); 389518334Speter 389618334Speter if (imode != GET_MODE (from)) 389718334Speter from = convert_to_mode (imode, from, unsignedp); 389818334Speter 389918334Speter if (fmode != GET_MODE (to)) 390018334Speter target = gen_reg_rtx (fmode); 390118334Speter 390218334Speter emit_unop_insn (icode, target, from, 390318334Speter doing_unsigned ? UNSIGNED_FLOAT : FLOAT); 390418334Speter 390518334Speter if (target != to) 390618334Speter convert_move (to, target, 0); 390718334Speter return; 390818334Speter } 390918334Speter } 391018334Speter 391118334Speter#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) 391218334Speter 391318334Speter /* Unsigned integer, and no way to convert directly. 391418334Speter Convert as signed, then conditionally adjust the result. */ 391518334Speter if (unsignedp) 391618334Speter { 391718334Speter rtx label = gen_label_rtx (); 391818334Speter rtx temp; 391918334Speter REAL_VALUE_TYPE offset; 392018334Speter 392118334Speter emit_queue (); 392218334Speter 392318334Speter to = protect_from_queue (to, 1); 392418334Speter from = protect_from_queue (from, 0); 392518334Speter 392618334Speter if (flag_force_mem) 392718334Speter from = force_not_mem (from); 392818334Speter 392918334Speter /* Look for a usable floating mode FMODE wider than the source and at 393018334Speter least as wide as the target. Using FMODE will avoid rounding woes 393118334Speter with unsigned values greater than the signed maximum value. */ 393218334Speter 393318334Speter for (fmode = GET_MODE (to); fmode != VOIDmode; 393418334Speter fmode = GET_MODE_WIDER_MODE (fmode)) 393518334Speter if (GET_MODE_BITSIZE (GET_MODE (from)) < GET_MODE_BITSIZE (fmode) 393618334Speter && can_float_p (fmode, GET_MODE (from), 0) != CODE_FOR_nothing) 393718334Speter break; 393818334Speter 393918334Speter if (fmode == VOIDmode) 394018334Speter { 394118334Speter /* There is no such mode. Pretend the target is wide enough. */ 394218334Speter fmode = GET_MODE (to); 394318334Speter 394450397Sobrien /* Avoid double-rounding when TO is narrower than FROM. */ 394518334Speter if ((significand_size (fmode) + 1) 394618334Speter < GET_MODE_BITSIZE (GET_MODE (from))) 394718334Speter { 394818334Speter rtx temp1; 394918334Speter rtx neglabel = gen_label_rtx (); 395018334Speter 395118334Speter /* Don't use TARGET if it isn't a register, is a hard register, 395218334Speter or is the wrong mode. */ 395318334Speter if (GET_CODE (target) != REG 395418334Speter || REGNO (target) < FIRST_PSEUDO_REGISTER 395518334Speter || GET_MODE (target) != fmode) 395618334Speter target = gen_reg_rtx (fmode); 395718334Speter 395818334Speter imode = GET_MODE (from); 395918334Speter do_pending_stack_adjust (); 396018334Speter 396118334Speter /* Test whether the sign bit is set. */ 396218334Speter emit_cmp_insn (from, const0_rtx, GE, NULL_RTX, imode, 0, 0); 396318334Speter emit_jump_insn (gen_blt (neglabel)); 396418334Speter 396518334Speter /* The sign bit is not set. Convert as signed. */ 396618334Speter expand_float (target, from, 0); 396718334Speter emit_jump_insn (gen_jump (label)); 396818334Speter emit_barrier (); 396918334Speter 397018334Speter /* The sign bit is set. 397118334Speter Convert to a usable (positive signed) value by shifting right 397218334Speter one bit, while remembering if a nonzero bit was shifted 397318334Speter out; i.e., compute (from & 1) | (from >> 1). */ 397418334Speter 397518334Speter emit_label (neglabel); 397618334Speter temp = expand_binop (imode, and_optab, from, const1_rtx, 397718334Speter NULL_RTX, 1, OPTAB_LIB_WIDEN); 397818334Speter temp1 = expand_shift (RSHIFT_EXPR, imode, from, integer_one_node, 397918334Speter NULL_RTX, 1); 398018334Speter temp = expand_binop (imode, ior_optab, temp, temp1, temp, 1, 398118334Speter OPTAB_LIB_WIDEN); 398218334Speter expand_float (target, temp, 0); 398318334Speter 398418334Speter /* Multiply by 2 to undo the shift above. */ 398518334Speter temp = expand_binop (fmode, add_optab, target, target, 398618334Speter target, 0, OPTAB_LIB_WIDEN); 398718334Speter if (temp != target) 398818334Speter emit_move_insn (target, temp); 398918334Speter 399018334Speter do_pending_stack_adjust (); 399118334Speter emit_label (label); 399218334Speter goto done; 399318334Speter } 399418334Speter } 399518334Speter 399618334Speter /* If we are about to do some arithmetic to correct for an 399718334Speter unsigned operand, do it in a pseudo-register. */ 399818334Speter 399918334Speter if (GET_MODE (to) != fmode 400018334Speter || GET_CODE (to) != REG || REGNO (to) < FIRST_PSEUDO_REGISTER) 400118334Speter target = gen_reg_rtx (fmode); 400218334Speter 400318334Speter /* Convert as signed integer to floating. */ 400418334Speter expand_float (target, from, 0); 400518334Speter 400618334Speter /* If FROM is negative (and therefore TO is negative), 400718334Speter correct its value by 2**bitwidth. */ 400818334Speter 400918334Speter do_pending_stack_adjust (); 401052284Sobrien emit_cmp_and_jump_insns (from, const0_rtx, GE, NULL_RTX, GET_MODE (from), 401152284Sobrien 0, 0, label); 401218334Speter 401318334Speter /* On SCO 3.2.1, ldexp rejects values outside [0.5, 1). 401418334Speter Rather than setting up a dconst_dot_5, let's hope SCO 401518334Speter fixes the bug. */ 401618334Speter offset = REAL_VALUE_LDEXP (dconst1, GET_MODE_BITSIZE (GET_MODE (from))); 401718334Speter temp = expand_binop (fmode, add_optab, target, 401818334Speter CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode), 401918334Speter target, 0, OPTAB_LIB_WIDEN); 402018334Speter if (temp != target) 402118334Speter emit_move_insn (target, temp); 402218334Speter 402318334Speter do_pending_stack_adjust (); 402418334Speter emit_label (label); 402518334Speter goto done; 402618334Speter } 402718334Speter#endif 402818334Speter 402918334Speter /* No hardware instruction available; call a library routine to convert from 403018334Speter SImode, DImode, or TImode into SFmode, DFmode, XFmode, or TFmode. */ 403118334Speter { 403218334Speter rtx libfcn; 403318334Speter rtx insns; 403418334Speter rtx value; 403518334Speter 403618334Speter to = protect_from_queue (to, 1); 403718334Speter from = protect_from_queue (from, 0); 403818334Speter 403918334Speter if (GET_MODE_SIZE (GET_MODE (from)) < GET_MODE_SIZE (SImode)) 404018334Speter from = convert_to_mode (SImode, from, unsignedp); 404118334Speter 404218334Speter if (flag_force_mem) 404318334Speter from = force_not_mem (from); 404418334Speter 404518334Speter if (GET_MODE (to) == SFmode) 404618334Speter { 404718334Speter if (GET_MODE (from) == SImode) 404818334Speter libfcn = floatsisf_libfunc; 404918334Speter else if (GET_MODE (from) == DImode) 405018334Speter libfcn = floatdisf_libfunc; 405118334Speter else if (GET_MODE (from) == TImode) 405218334Speter libfcn = floattisf_libfunc; 405318334Speter else 405418334Speter abort (); 405518334Speter } 405618334Speter else if (GET_MODE (to) == DFmode) 405718334Speter { 405818334Speter if (GET_MODE (from) == SImode) 405918334Speter libfcn = floatsidf_libfunc; 406018334Speter else if (GET_MODE (from) == DImode) 406118334Speter libfcn = floatdidf_libfunc; 406218334Speter else if (GET_MODE (from) == TImode) 406318334Speter libfcn = floattidf_libfunc; 406418334Speter else 406518334Speter abort (); 406618334Speter } 406718334Speter else if (GET_MODE (to) == XFmode) 406818334Speter { 406918334Speter if (GET_MODE (from) == SImode) 407018334Speter libfcn = floatsixf_libfunc; 407118334Speter else if (GET_MODE (from) == DImode) 407218334Speter libfcn = floatdixf_libfunc; 407318334Speter else if (GET_MODE (from) == TImode) 407418334Speter libfcn = floattixf_libfunc; 407518334Speter else 407618334Speter abort (); 407718334Speter } 407818334Speter else if (GET_MODE (to) == TFmode) 407918334Speter { 408018334Speter if (GET_MODE (from) == SImode) 408118334Speter libfcn = floatsitf_libfunc; 408218334Speter else if (GET_MODE (from) == DImode) 408318334Speter libfcn = floatditf_libfunc; 408418334Speter else if (GET_MODE (from) == TImode) 408518334Speter libfcn = floattitf_libfunc; 408618334Speter else 408718334Speter abort (); 408818334Speter } 408918334Speter else 409018334Speter abort (); 409118334Speter 409218334Speter start_sequence (); 409318334Speter 409418334Speter value = emit_library_call_value (libfcn, NULL_RTX, 1, 409518334Speter GET_MODE (to), 409618334Speter 1, from, GET_MODE (from)); 409718334Speter insns = get_insns (); 409818334Speter end_sequence (); 409918334Speter 410018334Speter emit_libcall_block (insns, target, value, 410150397Sobrien gen_rtx_FLOAT (GET_MODE (to), from)); 410218334Speter } 410318334Speter 410418334Speter done: 410518334Speter 410618334Speter /* Copy result to requested destination 410718334Speter if we have been computing in a temp location. */ 410818334Speter 410918334Speter if (target != to) 411018334Speter { 411118334Speter if (GET_MODE (target) == GET_MODE (to)) 411218334Speter emit_move_insn (to, target); 411318334Speter else 411418334Speter convert_move (to, target, 0); 411518334Speter } 411618334Speter} 411718334Speter 411818334Speter/* expand_fix: generate code to convert FROM to fixed point 411918334Speter and store in TO. FROM must be floating point. */ 412018334Speter 412118334Speterstatic rtx 412218334Speterftruncify (x) 412318334Speter rtx x; 412418334Speter{ 412518334Speter rtx temp = gen_reg_rtx (GET_MODE (x)); 412618334Speter return expand_unop (GET_MODE (x), ftrunc_optab, x, temp, 0); 412718334Speter} 412818334Speter 412918334Spetervoid 413018334Speterexpand_fix (to, from, unsignedp) 413118334Speter register rtx to, from; 413218334Speter int unsignedp; 413318334Speter{ 413418334Speter enum insn_code icode; 413518334Speter register rtx target = to; 413618334Speter enum machine_mode fmode, imode; 413718334Speter int must_trunc = 0; 413818334Speter rtx libfcn = 0; 413918334Speter 414018334Speter /* We first try to find a pair of modes, one real and one integer, at 414118334Speter least as wide as FROM and TO, respectively, in which we can open-code 414218334Speter this conversion. If the integer mode is wider than the mode of TO, 414318334Speter we can do the conversion either signed or unsigned. */ 414418334Speter 414518334Speter for (imode = GET_MODE (to); imode != VOIDmode; 414618334Speter imode = GET_MODE_WIDER_MODE (imode)) 414718334Speter for (fmode = GET_MODE (from); fmode != VOIDmode; 414818334Speter fmode = GET_MODE_WIDER_MODE (fmode)) 414918334Speter { 415018334Speter int doing_unsigned = unsignedp; 415118334Speter 415218334Speter icode = can_fix_p (imode, fmode, unsignedp, &must_trunc); 415318334Speter if (icode == CODE_FOR_nothing && imode != GET_MODE (to) && unsignedp) 415418334Speter icode = can_fix_p (imode, fmode, 0, &must_trunc), doing_unsigned = 0; 415518334Speter 415618334Speter if (icode != CODE_FOR_nothing) 415718334Speter { 415818334Speter to = protect_from_queue (to, 1); 415918334Speter from = protect_from_queue (from, 0); 416018334Speter 416118334Speter if (fmode != GET_MODE (from)) 416218334Speter from = convert_to_mode (fmode, from, 0); 416318334Speter 416418334Speter if (must_trunc) 416518334Speter from = ftruncify (from); 416618334Speter 416718334Speter if (imode != GET_MODE (to)) 416818334Speter target = gen_reg_rtx (imode); 416918334Speter 417018334Speter emit_unop_insn (icode, target, from, 417118334Speter doing_unsigned ? UNSIGNED_FIX : FIX); 417218334Speter if (target != to) 417318334Speter convert_move (to, target, unsignedp); 417418334Speter return; 417518334Speter } 417618334Speter } 417718334Speter 417818334Speter#if !defined (REAL_IS_NOT_DOUBLE) || defined (REAL_ARITHMETIC) 417918334Speter /* For an unsigned conversion, there is one more way to do it. 418018334Speter If we have a signed conversion, we generate code that compares 418118334Speter the real value to the largest representable positive number. If if 418218334Speter is smaller, the conversion is done normally. Otherwise, subtract 418318334Speter one plus the highest signed number, convert, and add it back. 418418334Speter 418518334Speter We only need to check all real modes, since we know we didn't find 418618334Speter anything with a wider integer mode. */ 418718334Speter 418818334Speter if (unsignedp && GET_MODE_BITSIZE (GET_MODE (to)) <= HOST_BITS_PER_WIDE_INT) 418918334Speter for (fmode = GET_MODE (from); fmode != VOIDmode; 419018334Speter fmode = GET_MODE_WIDER_MODE (fmode)) 419118334Speter /* Make sure we won't lose significant bits doing this. */ 419218334Speter if (GET_MODE_BITSIZE (fmode) > GET_MODE_BITSIZE (GET_MODE (to)) 419318334Speter && CODE_FOR_nothing != can_fix_p (GET_MODE (to), fmode, 0, 419418334Speter &must_trunc)) 419518334Speter { 419618334Speter int bitsize; 419718334Speter REAL_VALUE_TYPE offset; 419818334Speter rtx limit, lab1, lab2, insn; 419918334Speter 420018334Speter bitsize = GET_MODE_BITSIZE (GET_MODE (to)); 420118334Speter offset = REAL_VALUE_LDEXP (dconst1, bitsize - 1); 420218334Speter limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, fmode); 420318334Speter lab1 = gen_label_rtx (); 420418334Speter lab2 = gen_label_rtx (); 420518334Speter 420618334Speter emit_queue (); 420718334Speter to = protect_from_queue (to, 1); 420818334Speter from = protect_from_queue (from, 0); 420918334Speter 421018334Speter if (flag_force_mem) 421118334Speter from = force_not_mem (from); 421218334Speter 421318334Speter if (fmode != GET_MODE (from)) 421418334Speter from = convert_to_mode (fmode, from, 0); 421518334Speter 421618334Speter /* See if we need to do the subtraction. */ 421718334Speter do_pending_stack_adjust (); 421852284Sobrien emit_cmp_and_jump_insns (from, limit, GE, NULL_RTX, GET_MODE (from), 421952284Sobrien 0, 0, lab1); 422018334Speter 422118334Speter /* If not, do the signed "fix" and branch around fixup code. */ 422218334Speter expand_fix (to, from, 0); 422318334Speter emit_jump_insn (gen_jump (lab2)); 422418334Speter emit_barrier (); 422518334Speter 422618334Speter /* Otherwise, subtract 2**(N-1), convert to signed number, 422718334Speter then add 2**(N-1). Do the addition using XOR since this 422818334Speter will often generate better code. */ 422918334Speter emit_label (lab1); 423018334Speter target = expand_binop (GET_MODE (from), sub_optab, from, limit, 423118334Speter NULL_RTX, 0, OPTAB_LIB_WIDEN); 423218334Speter expand_fix (to, target, 0); 423318334Speter target = expand_binop (GET_MODE (to), xor_optab, to, 423418334Speter GEN_INT ((HOST_WIDE_INT) 1 << (bitsize - 1)), 423518334Speter to, 1, OPTAB_LIB_WIDEN); 423618334Speter 423718334Speter if (target != to) 423818334Speter emit_move_insn (to, target); 423918334Speter 424018334Speter emit_label (lab2); 424118334Speter 424250397Sobrien if (mov_optab->handlers[(int) GET_MODE (to)].insn_code 424350397Sobrien != CODE_FOR_nothing) 424450397Sobrien { 424550397Sobrien /* Make a place for a REG_NOTE and add it. */ 424650397Sobrien insn = emit_move_insn (to, to); 424752284Sobrien set_unique_reg_note (insn, 424852284Sobrien REG_EQUAL, 424952284Sobrien gen_rtx_fmt_e (UNSIGNED_FIX, 425052284Sobrien GET_MODE (to), 425152284Sobrien copy_rtx (from))); 425250397Sobrien } 425318334Speter return; 425418334Speter } 425518334Speter#endif 425618334Speter 425718334Speter /* We can't do it with an insn, so use a library call. But first ensure 425818334Speter that the mode of TO is at least as wide as SImode, since those are the 425918334Speter only library calls we know about. */ 426018334Speter 426118334Speter if (GET_MODE_SIZE (GET_MODE (to)) < GET_MODE_SIZE (SImode)) 426218334Speter { 426318334Speter target = gen_reg_rtx (SImode); 426418334Speter 426518334Speter expand_fix (target, from, unsignedp); 426618334Speter } 426718334Speter else if (GET_MODE (from) == SFmode) 426818334Speter { 426918334Speter if (GET_MODE (to) == SImode) 427018334Speter libfcn = unsignedp ? fixunssfsi_libfunc : fixsfsi_libfunc; 427118334Speter else if (GET_MODE (to) == DImode) 427218334Speter libfcn = unsignedp ? fixunssfdi_libfunc : fixsfdi_libfunc; 427318334Speter else if (GET_MODE (to) == TImode) 427418334Speter libfcn = unsignedp ? fixunssfti_libfunc : fixsfti_libfunc; 427518334Speter else 427618334Speter abort (); 427718334Speter } 427818334Speter else if (GET_MODE (from) == DFmode) 427918334Speter { 428018334Speter if (GET_MODE (to) == SImode) 428118334Speter libfcn = unsignedp ? fixunsdfsi_libfunc : fixdfsi_libfunc; 428218334Speter else if (GET_MODE (to) == DImode) 428318334Speter libfcn = unsignedp ? fixunsdfdi_libfunc : fixdfdi_libfunc; 428418334Speter else if (GET_MODE (to) == TImode) 428518334Speter libfcn = unsignedp ? fixunsdfti_libfunc : fixdfti_libfunc; 428618334Speter else 428718334Speter abort (); 428818334Speter } 428918334Speter else if (GET_MODE (from) == XFmode) 429018334Speter { 429118334Speter if (GET_MODE (to) == SImode) 429218334Speter libfcn = unsignedp ? fixunsxfsi_libfunc : fixxfsi_libfunc; 429318334Speter else if (GET_MODE (to) == DImode) 429418334Speter libfcn = unsignedp ? fixunsxfdi_libfunc : fixxfdi_libfunc; 429518334Speter else if (GET_MODE (to) == TImode) 429618334Speter libfcn = unsignedp ? fixunsxfti_libfunc : fixxfti_libfunc; 429718334Speter else 429818334Speter abort (); 429918334Speter } 430018334Speter else if (GET_MODE (from) == TFmode) 430118334Speter { 430218334Speter if (GET_MODE (to) == SImode) 430318334Speter libfcn = unsignedp ? fixunstfsi_libfunc : fixtfsi_libfunc; 430418334Speter else if (GET_MODE (to) == DImode) 430518334Speter libfcn = unsignedp ? fixunstfdi_libfunc : fixtfdi_libfunc; 430618334Speter else if (GET_MODE (to) == TImode) 430718334Speter libfcn = unsignedp ? fixunstfti_libfunc : fixtfti_libfunc; 430818334Speter else 430918334Speter abort (); 431018334Speter } 431118334Speter else 431218334Speter abort (); 431318334Speter 431418334Speter if (libfcn) 431518334Speter { 431618334Speter rtx insns; 431718334Speter rtx value; 431818334Speter 431918334Speter to = protect_from_queue (to, 1); 432018334Speter from = protect_from_queue (from, 0); 432118334Speter 432218334Speter if (flag_force_mem) 432318334Speter from = force_not_mem (from); 432418334Speter 432518334Speter start_sequence (); 432618334Speter 432718334Speter value = emit_library_call_value (libfcn, NULL_RTX, 1, GET_MODE (to), 432818334Speter 432918334Speter 1, from, GET_MODE (from)); 433018334Speter insns = get_insns (); 433118334Speter end_sequence (); 433218334Speter 433318334Speter emit_libcall_block (insns, target, value, 433450397Sobrien gen_rtx_fmt_e (unsignedp ? UNSIGNED_FIX : FIX, 433550397Sobrien GET_MODE (to), from)); 433618334Speter } 433718334Speter 433850397Sobrien if (target != to) 433950397Sobrien { 434050397Sobrien if (GET_MODE (to) == GET_MODE (target)) 434150397Sobrien emit_move_insn (to, target); 434250397Sobrien else 434350397Sobrien convert_move (to, target, 0); 434450397Sobrien } 434518334Speter} 434618334Speter 434718334Speterstatic optab 434818334Speterinit_optab (code) 434918334Speter enum rtx_code code; 435018334Speter{ 435118334Speter int i; 435218334Speter optab op = (optab) xmalloc (sizeof (struct optab)); 435318334Speter op->code = code; 435418334Speter for (i = 0; i < NUM_MACHINE_MODES; i++) 435518334Speter { 435618334Speter op->handlers[i].insn_code = CODE_FOR_nothing; 435718334Speter op->handlers[i].libfunc = 0; 435818334Speter } 435918334Speter 436018334Speter if (code != UNKNOWN) 436118334Speter code_to_optab[(int) code] = op; 436218334Speter 436318334Speter return op; 436418334Speter} 436518334Speter 436618334Speter/* Initialize the libfunc fields of an entire group of entries in some 436718334Speter optab. Each entry is set equal to a string consisting of a leading 436818334Speter pair of underscores followed by a generic operation name followed by 436918334Speter a mode name (downshifted to lower case) followed by a single character 437018334Speter representing the number of operands for the given operation (which is 437118334Speter usually one of the characters '2', '3', or '4'). 437218334Speter 437318334Speter OPTABLE is the table in which libfunc fields are to be initialized. 437418334Speter FIRST_MODE is the first machine mode index in the given optab to 437518334Speter initialize. 437618334Speter LAST_MODE is the last machine mode index in the given optab to 437718334Speter initialize. 437818334Speter OPNAME is the generic (string) name of the operation. 437918334Speter SUFFIX is the character which specifies the number of operands for 438018334Speter the given generic operation. 438118334Speter*/ 438218334Speter 438318334Speterstatic void 438418334Speterinit_libfuncs (optable, first_mode, last_mode, opname, suffix) 438518334Speter register optab optable; 438618334Speter register int first_mode; 438718334Speter register int last_mode; 438852284Sobrien register const char *opname; 438918334Speter register int suffix; 439018334Speter{ 439118334Speter register int mode; 439218334Speter register unsigned opname_len = strlen (opname); 439318334Speter 439418334Speter for (mode = first_mode; (int) mode <= (int) last_mode; 439518334Speter mode = (enum machine_mode) ((int) mode + 1)) 439618334Speter { 439718334Speter register char *mname = mode_name[(int) mode]; 439818334Speter register unsigned mname_len = strlen (mname); 439918334Speter register char *libfunc_name 440018334Speter = (char *) xmalloc (2 + opname_len + mname_len + 1 + 1); 440118334Speter register char *p; 440252284Sobrien register const char *q; 440318334Speter 440418334Speter p = libfunc_name; 440518334Speter *p++ = '_'; 440618334Speter *p++ = '_'; 440718334Speter for (q = opname; *q; ) 440818334Speter *p++ = *q++; 440918334Speter for (q = mname; *q; q++) 441052284Sobrien *p++ = tolower ((unsigned char)*q); 441118334Speter *p++ = suffix; 441218334Speter *p++ = '\0'; 441318334Speter optable->handlers[(int) mode].libfunc 441450397Sobrien = gen_rtx_SYMBOL_REF (Pmode, libfunc_name); 441518334Speter } 441618334Speter} 441718334Speter 441818334Speter/* Initialize the libfunc fields of an entire group of entries in some 441918334Speter optab which correspond to all integer mode operations. The parameters 442018334Speter have the same meaning as similarly named ones for the `init_libfuncs' 442118334Speter routine. (See above). */ 442218334Speter 442318334Speterstatic void 442418334Speterinit_integral_libfuncs (optable, opname, suffix) 442518334Speter register optab optable; 442652284Sobrien register const char *opname; 442718334Speter register int suffix; 442818334Speter{ 442918334Speter init_libfuncs (optable, SImode, TImode, opname, suffix); 443018334Speter} 443118334Speter 443218334Speter/* Initialize the libfunc fields of an entire group of entries in some 443318334Speter optab which correspond to all real mode operations. The parameters 443418334Speter have the same meaning as similarly named ones for the `init_libfuncs' 443518334Speter routine. (See above). */ 443618334Speter 443718334Speterstatic void 443818334Speterinit_floating_libfuncs (optable, opname, suffix) 443918334Speter register optab optable; 444052284Sobrien register const char *opname; 444118334Speter register int suffix; 444218334Speter{ 444318334Speter init_libfuncs (optable, SFmode, TFmode, opname, suffix); 444418334Speter} 444518334Speter 444618334Speter 444718334Speter/* Call this once to initialize the contents of the optabs 444818334Speter appropriately for the current target machine. */ 444918334Speter 445018334Spetervoid 445118334Speterinit_optabs () 445218334Speter{ 445350397Sobrien int i; 445450397Sobrien#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC 445550397Sobrien int j; 445650397Sobrien#endif 445750397Sobrien 445818334Speter enum insn_code *p; 445918334Speter 446018334Speter /* Start by initializing all tables to contain CODE_FOR_nothing. */ 446118334Speter 446218334Speter for (p = fixtab[0][0]; 446318334Speter p < fixtab[0][0] + sizeof fixtab / sizeof (fixtab[0][0][0]); 446418334Speter p++) 446518334Speter *p = CODE_FOR_nothing; 446618334Speter 446718334Speter for (p = fixtrunctab[0][0]; 446818334Speter p < fixtrunctab[0][0] + sizeof fixtrunctab / sizeof (fixtrunctab[0][0][0]); 446918334Speter p++) 447018334Speter *p = CODE_FOR_nothing; 447118334Speter 447218334Speter for (p = floattab[0][0]; 447318334Speter p < floattab[0][0] + sizeof floattab / sizeof (floattab[0][0][0]); 447418334Speter p++) 447518334Speter *p = CODE_FOR_nothing; 447618334Speter 447718334Speter for (p = extendtab[0][0]; 447818334Speter p < extendtab[0][0] + sizeof extendtab / sizeof extendtab[0][0][0]; 447918334Speter p++) 448018334Speter *p = CODE_FOR_nothing; 448118334Speter 448218334Speter for (i = 0; i < NUM_RTX_CODE; i++) 448318334Speter setcc_gen_code[i] = CODE_FOR_nothing; 448418334Speter 448518334Speter#ifdef HAVE_conditional_move 448618334Speter for (i = 0; i < NUM_MACHINE_MODES; i++) 448718334Speter movcc_gen_code[i] = CODE_FOR_nothing; 448818334Speter#endif 448918334Speter 449018334Speter add_optab = init_optab (PLUS); 449118334Speter sub_optab = init_optab (MINUS); 449218334Speter smul_optab = init_optab (MULT); 449318334Speter smul_highpart_optab = init_optab (UNKNOWN); 449418334Speter umul_highpart_optab = init_optab (UNKNOWN); 449518334Speter smul_widen_optab = init_optab (UNKNOWN); 449618334Speter umul_widen_optab = init_optab (UNKNOWN); 449718334Speter sdiv_optab = init_optab (DIV); 449818334Speter sdivmod_optab = init_optab (UNKNOWN); 449918334Speter udiv_optab = init_optab (UDIV); 450018334Speter udivmod_optab = init_optab (UNKNOWN); 450118334Speter smod_optab = init_optab (MOD); 450218334Speter umod_optab = init_optab (UMOD); 450318334Speter flodiv_optab = init_optab (DIV); 450418334Speter ftrunc_optab = init_optab (UNKNOWN); 450518334Speter and_optab = init_optab (AND); 450618334Speter ior_optab = init_optab (IOR); 450718334Speter xor_optab = init_optab (XOR); 450818334Speter ashl_optab = init_optab (ASHIFT); 450918334Speter ashr_optab = init_optab (ASHIFTRT); 451018334Speter lshr_optab = init_optab (LSHIFTRT); 451118334Speter rotl_optab = init_optab (ROTATE); 451218334Speter rotr_optab = init_optab (ROTATERT); 451318334Speter smin_optab = init_optab (SMIN); 451418334Speter smax_optab = init_optab (SMAX); 451518334Speter umin_optab = init_optab (UMIN); 451618334Speter umax_optab = init_optab (UMAX); 451718334Speter mov_optab = init_optab (UNKNOWN); 451818334Speter movstrict_optab = init_optab (UNKNOWN); 451918334Speter cmp_optab = init_optab (UNKNOWN); 452018334Speter ucmp_optab = init_optab (UNKNOWN); 452118334Speter tst_optab = init_optab (UNKNOWN); 452218334Speter neg_optab = init_optab (NEG); 452318334Speter abs_optab = init_optab (ABS); 452418334Speter one_cmpl_optab = init_optab (NOT); 452518334Speter ffs_optab = init_optab (FFS); 452618334Speter sqrt_optab = init_optab (SQRT); 452718334Speter sin_optab = init_optab (UNKNOWN); 452818334Speter cos_optab = init_optab (UNKNOWN); 452918334Speter strlen_optab = init_optab (UNKNOWN); 453018334Speter 453118334Speter for (i = 0; i < NUM_MACHINE_MODES; i++) 453218334Speter { 453318334Speter movstr_optab[i] = CODE_FOR_nothing; 453450397Sobrien clrstr_optab[i] = CODE_FOR_nothing; 453518334Speter 453618334Speter#ifdef HAVE_SECONDARY_RELOADS 453718334Speter reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing; 453818334Speter#endif 453918334Speter } 454018334Speter 454118334Speter /* Fill in the optabs with the insns we support. */ 454218334Speter init_all_optabs (); 454318334Speter 454418334Speter#ifdef FIXUNS_TRUNC_LIKE_FIX_TRUNC 454518334Speter /* This flag says the same insns that convert to a signed fixnum 454618334Speter also convert validly to an unsigned one. */ 454718334Speter for (i = 0; i < NUM_MACHINE_MODES; i++) 454818334Speter for (j = 0; j < NUM_MACHINE_MODES; j++) 454918334Speter fixtrunctab[i][j][1] = fixtrunctab[i][j][0]; 455018334Speter#endif 455118334Speter 455218334Speter#ifdef EXTRA_CC_MODES 455318334Speter init_mov_optab (); 455418334Speter#endif 455518334Speter 455618334Speter /* Initialize the optabs with the names of the library functions. */ 455718334Speter init_integral_libfuncs (add_optab, "add", '3'); 455818334Speter init_floating_libfuncs (add_optab, "add", '3'); 455918334Speter init_integral_libfuncs (sub_optab, "sub", '3'); 456018334Speter init_floating_libfuncs (sub_optab, "sub", '3'); 456118334Speter init_integral_libfuncs (smul_optab, "mul", '3'); 456218334Speter init_floating_libfuncs (smul_optab, "mul", '3'); 456318334Speter init_integral_libfuncs (sdiv_optab, "div", '3'); 456418334Speter init_integral_libfuncs (udiv_optab, "udiv", '3'); 456518334Speter init_integral_libfuncs (sdivmod_optab, "divmod", '4'); 456618334Speter init_integral_libfuncs (udivmod_optab, "udivmod", '4'); 456718334Speter init_integral_libfuncs (smod_optab, "mod", '3'); 456818334Speter init_integral_libfuncs (umod_optab, "umod", '3'); 456918334Speter init_floating_libfuncs (flodiv_optab, "div", '3'); 457018334Speter init_floating_libfuncs (ftrunc_optab, "ftrunc", '2'); 457118334Speter init_integral_libfuncs (and_optab, "and", '3'); 457218334Speter init_integral_libfuncs (ior_optab, "ior", '3'); 457318334Speter init_integral_libfuncs (xor_optab, "xor", '3'); 457418334Speter init_integral_libfuncs (ashl_optab, "ashl", '3'); 457518334Speter init_integral_libfuncs (ashr_optab, "ashr", '3'); 457618334Speter init_integral_libfuncs (lshr_optab, "lshr", '3'); 457718334Speter init_integral_libfuncs (smin_optab, "min", '3'); 457818334Speter init_floating_libfuncs (smin_optab, "min", '3'); 457918334Speter init_integral_libfuncs (smax_optab, "max", '3'); 458018334Speter init_floating_libfuncs (smax_optab, "max", '3'); 458118334Speter init_integral_libfuncs (umin_optab, "umin", '3'); 458218334Speter init_integral_libfuncs (umax_optab, "umax", '3'); 458318334Speter init_integral_libfuncs (neg_optab, "neg", '2'); 458418334Speter init_floating_libfuncs (neg_optab, "neg", '2'); 458518334Speter init_integral_libfuncs (one_cmpl_optab, "one_cmpl", '2'); 458618334Speter init_integral_libfuncs (ffs_optab, "ffs", '2'); 458718334Speter 458818334Speter /* Comparison libcalls for integers MUST come in pairs, signed/unsigned. */ 458918334Speter init_integral_libfuncs (cmp_optab, "cmp", '2'); 459018334Speter init_integral_libfuncs (ucmp_optab, "ucmp", '2'); 459118334Speter init_floating_libfuncs (cmp_optab, "cmp", '2'); 459218334Speter 459318334Speter#ifdef MULSI3_LIBCALL 459418334Speter smul_optab->handlers[(int) SImode].libfunc 459550397Sobrien = gen_rtx_SYMBOL_REF (Pmode, MULSI3_LIBCALL); 459618334Speter#endif 459718334Speter#ifdef MULDI3_LIBCALL 459818334Speter smul_optab->handlers[(int) DImode].libfunc 459950397Sobrien = gen_rtx_SYMBOL_REF (Pmode, MULDI3_LIBCALL); 460018334Speter#endif 460118334Speter 460218334Speter#ifdef DIVSI3_LIBCALL 460318334Speter sdiv_optab->handlers[(int) SImode].libfunc 460450397Sobrien = gen_rtx_SYMBOL_REF (Pmode, DIVSI3_LIBCALL); 460518334Speter#endif 460618334Speter#ifdef DIVDI3_LIBCALL 460718334Speter sdiv_optab->handlers[(int) DImode].libfunc 460850397Sobrien = gen_rtx_SYMBOL_REF (Pmode, DIVDI3_LIBCALL); 460918334Speter#endif 461018334Speter 461118334Speter#ifdef UDIVSI3_LIBCALL 461218334Speter udiv_optab->handlers[(int) SImode].libfunc 461350397Sobrien = gen_rtx_SYMBOL_REF (Pmode, UDIVSI3_LIBCALL); 461418334Speter#endif 461518334Speter#ifdef UDIVDI3_LIBCALL 461618334Speter udiv_optab->handlers[(int) DImode].libfunc 461750397Sobrien = gen_rtx_SYMBOL_REF (Pmode, UDIVDI3_LIBCALL); 461818334Speter#endif 461918334Speter 462018334Speter#ifdef MODSI3_LIBCALL 462118334Speter smod_optab->handlers[(int) SImode].libfunc 462250397Sobrien = gen_rtx_SYMBOL_REF (Pmode, MODSI3_LIBCALL); 462318334Speter#endif 462418334Speter#ifdef MODDI3_LIBCALL 462518334Speter smod_optab->handlers[(int) DImode].libfunc 462650397Sobrien = gen_rtx_SYMBOL_REF (Pmode, MODDI3_LIBCALL); 462718334Speter#endif 462818334Speter 462918334Speter#ifdef UMODSI3_LIBCALL 463018334Speter umod_optab->handlers[(int) SImode].libfunc 463150397Sobrien = gen_rtx_SYMBOL_REF (Pmode, UMODSI3_LIBCALL); 463218334Speter#endif 463318334Speter#ifdef UMODDI3_LIBCALL 463418334Speter umod_optab->handlers[(int) DImode].libfunc 463550397Sobrien = gen_rtx_SYMBOL_REF (Pmode, UMODDI3_LIBCALL); 463618334Speter#endif 463718334Speter 463818334Speter /* Use cabs for DC complex abs, since systems generally have cabs. 463918334Speter Don't define any libcall for SCmode, so that cabs will be used. */ 464018334Speter abs_optab->handlers[(int) DCmode].libfunc 464150397Sobrien = gen_rtx_SYMBOL_REF (Pmode, "cabs"); 464218334Speter 464318334Speter /* The ffs function operates on `int'. */ 464418334Speter#ifndef INT_TYPE_SIZE 464518334Speter#define INT_TYPE_SIZE BITS_PER_WORD 464618334Speter#endif 464718334Speter ffs_optab->handlers[(int) mode_for_size (INT_TYPE_SIZE, MODE_INT, 0)] .libfunc 464850397Sobrien = gen_rtx_SYMBOL_REF (Pmode, "ffs"); 464918334Speter 465050397Sobrien extendsfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extendsfdf2"); 465150397Sobrien extendsfxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extendsfxf2"); 465250397Sobrien extendsftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extendsftf2"); 465350397Sobrien extenddfxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extenddfxf2"); 465450397Sobrien extenddftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__extenddftf2"); 465518334Speter 465650397Sobrien truncdfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__truncdfsf2"); 465750397Sobrien truncxfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__truncxfsf2"); 465850397Sobrien trunctfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__trunctfsf2"); 465950397Sobrien truncxfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__truncxfdf2"); 466050397Sobrien trunctfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__trunctfdf2"); 466118334Speter 466250397Sobrien memcpy_libfunc = gen_rtx_SYMBOL_REF (Pmode, "memcpy"); 466350397Sobrien bcopy_libfunc = gen_rtx_SYMBOL_REF (Pmode, "bcopy"); 466450397Sobrien memcmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "memcmp"); 466550397Sobrien bcmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gcc_bcmp"); 466650397Sobrien memset_libfunc = gen_rtx_SYMBOL_REF (Pmode, "memset"); 466750397Sobrien bzero_libfunc = gen_rtx_SYMBOL_REF (Pmode, "bzero"); 466818334Speter 466950397Sobrien throw_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__throw"); 467052284Sobrien rethrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__rethrow"); 467150397Sobrien sjthrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__sjthrow"); 467250397Sobrien sjpopnthrow_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__sjpopnthrow"); 467350397Sobrien terminate_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__terminate"); 467452284Sobrien eh_rtime_match_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eh_rtime_match"); 467550397Sobrien#ifndef DONT_USE_BUILTIN_SETJMP 467650397Sobrien setjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__builtin_setjmp"); 467750397Sobrien longjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__builtin_longjmp"); 467850397Sobrien#else 467950397Sobrien setjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "setjmp"); 468050397Sobrien longjmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "longjmp"); 468150397Sobrien#endif 468218334Speter 468350397Sobrien eqhf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqhf2"); 468450397Sobrien nehf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nehf2"); 468550397Sobrien gthf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gthf2"); 468650397Sobrien gehf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gehf2"); 468750397Sobrien lthf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lthf2"); 468850397Sobrien lehf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lehf2"); 468918334Speter 469050397Sobrien eqsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqsf2"); 469150397Sobrien nesf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nesf2"); 469250397Sobrien gtsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gtsf2"); 469350397Sobrien gesf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gesf2"); 469450397Sobrien ltsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ltsf2"); 469550397Sobrien lesf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lesf2"); 469618334Speter 469750397Sobrien eqdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqdf2"); 469850397Sobrien nedf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nedf2"); 469950397Sobrien gtdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gtdf2"); 470050397Sobrien gedf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gedf2"); 470150397Sobrien ltdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ltdf2"); 470250397Sobrien ledf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ledf2"); 470318334Speter 470450397Sobrien eqxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqxf2"); 470550397Sobrien nexf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__nexf2"); 470650397Sobrien gtxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gtxf2"); 470750397Sobrien gexf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gexf2"); 470850397Sobrien ltxf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__ltxf2"); 470950397Sobrien lexf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lexf2"); 471018334Speter 471150397Sobrien eqtf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__eqtf2"); 471250397Sobrien netf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__netf2"); 471350397Sobrien gttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__gttf2"); 471450397Sobrien getf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__getf2"); 471550397Sobrien lttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__lttf2"); 471650397Sobrien letf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__letf2"); 471718334Speter 471850397Sobrien floatsisf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsisf"); 471950397Sobrien floatdisf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatdisf"); 472050397Sobrien floattisf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattisf"); 472118334Speter 472250397Sobrien floatsidf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsidf"); 472350397Sobrien floatdidf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatdidf"); 472450397Sobrien floattidf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattidf"); 472518334Speter 472650397Sobrien floatsixf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsixf"); 472750397Sobrien floatdixf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatdixf"); 472850397Sobrien floattixf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattixf"); 472918334Speter 473050397Sobrien floatsitf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatsitf"); 473150397Sobrien floatditf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floatditf"); 473250397Sobrien floattitf_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__floattitf"); 473318334Speter 473450397Sobrien fixsfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixsfsi"); 473550397Sobrien fixsfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixsfdi"); 473650397Sobrien fixsfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixsfti"); 473718334Speter 473850397Sobrien fixdfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixdfsi"); 473950397Sobrien fixdfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixdfdi"); 474050397Sobrien fixdfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixdfti"); 474118334Speter 474250397Sobrien fixxfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixxfsi"); 474350397Sobrien fixxfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixxfdi"); 474450397Sobrien fixxfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixxfti"); 474518334Speter 474650397Sobrien fixtfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixtfsi"); 474750397Sobrien fixtfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixtfdi"); 474850397Sobrien fixtfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixtfti"); 474918334Speter 475050397Sobrien fixunssfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunssfsi"); 475150397Sobrien fixunssfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunssfdi"); 475250397Sobrien fixunssfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunssfti"); 475318334Speter 475450397Sobrien fixunsdfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsdfsi"); 475550397Sobrien fixunsdfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsdfdi"); 475650397Sobrien fixunsdfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsdfti"); 475718334Speter 475850397Sobrien fixunsxfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsxfsi"); 475950397Sobrien fixunsxfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsxfdi"); 476050397Sobrien fixunsxfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunsxfti"); 476118334Speter 476250397Sobrien fixunstfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfsi"); 476350397Sobrien fixunstfdi_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfdi"); 476450397Sobrien fixunstfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfti"); 476550397Sobrien 476650397Sobrien /* For check-memory-usage. */ 476752284Sobrien chkr_check_addr_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_addr"); 476852284Sobrien chkr_set_right_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_set_right"); 476952284Sobrien chkr_copy_bitmap_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_copy_bitmap"); 477052284Sobrien chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_exec"); 477152284Sobrien chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_str"); 477250397Sobrien 477352284Sobrien /* For function entry/exit instrumentation. */ 477452284Sobrien profile_function_entry_libfunc 477552284Sobrien = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_enter"); 477652284Sobrien profile_function_exit_libfunc 477752284Sobrien = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_exit"); 477852284Sobrien 477950397Sobrien#ifdef HAVE_conditional_trap 478050397Sobrien init_traps (); 478150397Sobrien#endif 478250397Sobrien 478318334Speter#ifdef INIT_TARGET_OPTABS 478418334Speter /* Allow the target to add more libcalls or rename some, etc. */ 478518334Speter INIT_TARGET_OPTABS; 478618334Speter#endif 478718334Speter} 478818334Speter 478918334Speter#ifdef BROKEN_LDEXP 479018334Speter 479150397Sobrien/* SCO 3.2 apparently has a broken ldexp. */ 479218334Speter 479318334Speterdouble 479418334Speterldexp(x,n) 479518334Speter double x; 479618334Speter int n; 479718334Speter{ 479818334Speter if (n > 0) 479918334Speter while (n--) 480018334Speter x *= 2; 480118334Speter 480218334Speter return x; 480318334Speter} 480418334Speter#endif /* BROKEN_LDEXP */ 480550397Sobrien 480650397Sobrien#ifdef HAVE_conditional_trap 480750397Sobrien/* The insn generating function can not take an rtx_code argument. 480850397Sobrien TRAP_RTX is used as an rtx argument. Its code is replaced with 480950397Sobrien the code to be used in the trap insn and all other fields are 481050397Sobrien ignored. 481150397Sobrien 481250397Sobrien ??? Will need to change to support garbage collection. */ 481350397Sobrienstatic rtx trap_rtx; 481450397Sobrien 481550397Sobrienstatic void 481650397Sobrieninit_traps () 481750397Sobrien{ 481850397Sobrien if (HAVE_conditional_trap) 481950397Sobrien trap_rtx = gen_rtx_fmt_ee (EQ, VOIDmode, NULL_RTX, NULL_RTX); 482050397Sobrien} 482150397Sobrien#endif 482250397Sobrien 482350397Sobrien/* Generate insns to trap with code TCODE if OP1 and OP2 satisfy condition 482450397Sobrien CODE. Return 0 on failure. */ 482550397Sobrien 482650397Sobrienrtx 482750397Sobriengen_cond_trap (code, op1, op2, tcode) 482852284Sobrien enum rtx_code code ATTRIBUTE_UNUSED; 482952284Sobrien rtx op1, op2 ATTRIBUTE_UNUSED, tcode ATTRIBUTE_UNUSED; 483050397Sobrien{ 483150397Sobrien enum machine_mode mode = GET_MODE (op1); 483250397Sobrien 483350397Sobrien if (mode == VOIDmode) 483450397Sobrien return 0; 483550397Sobrien 483650397Sobrien#ifdef HAVE_conditional_trap 483750397Sobrien if (HAVE_conditional_trap 483850397Sobrien && cmp_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) 483950397Sobrien { 484050397Sobrien rtx insn; 484150397Sobrien emit_insn (GEN_FCN (cmp_optab->handlers[(int) mode].insn_code) (op1, op2)); 484250397Sobrien PUT_CODE (trap_rtx, code); 484350397Sobrien insn = gen_conditional_trap (trap_rtx, tcode); 484450397Sobrien if (insn) 484550397Sobrien return insn; 484650397Sobrien } 484750397Sobrien#endif 484850397Sobrien 484950397Sobrien return 0; 485050397Sobrien} 4851