📄 optabs.c
字号:
/* Expand the basic unary and binary arithmetic operations, for GNU compiler. Copyright (C) 1987, 1988 Free Software Foundation, Inc.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 1, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */#include "config.h"#include "rtl.h"#include "tree.h"#include "flags.h"#include "insn-flags.h"#include "insn-codes.h"#include "expr.h"#include "insn-config.h"#include "recog.h"/* In ANSI C we could write MODE + 1, but traditional C compilers seem to reject it. */#define INC_MODE(MODE) (enum machine_mode) ((int)(MODE) + 1)/* Each optab contains info on how this target machine can perform a particular operation for all sizes and kinds of operands. The operation to be performed is often specified by passing one of these optabs as an argument. See expr.h for documentation of these optabs. */optab add_optab;optab sub_optab;optab smul_optab;optab umul_optab;optab smul_widen_optab;optab umul_widen_optab;optab sdiv_optab;optab sdivmod_optab;optab udiv_optab;optab udivmod_optab;optab smod_optab;optab umod_optab;optab flodiv_optab;optab ftrunc_optab;optab and_optab;optab andcb_optab;optab ior_optab;optab xor_optab;optab ashl_optab;optab lshr_optab;optab lshl_optab;optab ashr_optab;optab rotl_optab;optab rotr_optab;optab mov_optab;optab movstrict_optab;optab neg_optab;optab abs_optab;optab one_cmpl_optab;optab ffs_optab;optab cmp_optab;optab ucmp_optab; /* Used only for libcalls for unsigned comparisons. */optab tst_optab;/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) gives the gen_function to make a branch to test that condition. */rtxfun bcc_gen_fctn[NUM_RTX_CODE];/* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) gives the gen_function to make a store-condition insn to test that condition. */rtxfun setcc_gen_fctn[NUM_RTX_CODE];/* Generate code to perform an operation specified by BINOPTAB on operands OP0 and OP1, with result having machine-mode MODE. UNSIGNEDP is for the case where we have to widen the operands to perform the operation. It says to use zero-extension. If TARGET is nonzero, the value is generated there, if it is convenient to do so. In all cases an rtx is returned for the locus of the value; this may or may not be TARGET. */rtxexpand_binop (mode, binoptab, op0, op1, target, unsignedp, methods) enum machine_mode mode; optab binoptab; rtx op0, op1; rtx target; int unsignedp; enum optab_methods methods;{ enum mode_class class; enum machine_mode wider_mode; register rtx temp; rtx last; class = GET_MODE_CLASS (mode); op0 = protect_from_queue (op0, 0); op1 = protect_from_queue (op1, 0); if (target) target = protect_from_queue (target, 1);#if 0 /* We may get better code by generating the result in a register when the target is not one of the operands. */ if (target && ! rtx_equal_p (target, op1) && ! rtx_equal_p (target, op0)) target_is_not_an_operand = 1;#endif if (flag_force_mem) { op0 = force_not_mem (op0); op1 = force_not_mem (op1); } /* Record where to delete back to if we backtrack. */ last = get_last_insn (); /* If operation is commutative, try to make the first operand a register. Even better, try to make it the same as the target. Also try to make the last operand a constant. */ if (binoptab == add_optab || binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab || binoptab == smul_optab || binoptab == umul_optab || binoptab == smul_widen_optab || binoptab == umul_widen_optab) { if (((target == 0 || GET_CODE (target) == REG) ? ((GET_CODE (op1) == REG && GET_CODE (op0) != REG) || target == op1) : rtx_equal_p (op1, target)) || GET_CODE (op0) == CONST_INT) { temp = op1; op1 = op0; op0 = temp; } } /* If we can do it with a three-operand insn, do so. */ if (methods != OPTAB_MUST_WIDEN && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { int icode = (int) binoptab->handlers[(int) mode].insn_code; enum machine_mode mode0 = insn_operand_mode[icode][1]; enum machine_mode mode1 = insn_operand_mode[icode][2]; rtx pat; rtx xop0 = op0, xop1 = op1; if (target) temp = target; else temp = gen_reg_rtx (mode); /* In case the insn wants input operands in modes different from the result, convert the operands. */ if (GET_MODE (op0) != VOIDmode && GET_MODE (op0) != mode0) xop0 = convert_to_mode (mode0, xop0, unsignedp); if (GET_MODE (xop1) != VOIDmode && GET_MODE (xop1) != mode1) xop1 = convert_to_mode (mode1, xop1, unsignedp); /* Now, if insn requires register operands, put operands into regs. */ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) xop0 = force_reg (mode0, xop0); if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)) xop1 = force_reg (mode1, xop1); if (! (*insn_operand_predicate[icode][0]) (temp, mode)) temp = gen_reg_rtx (mode); pat = GEN_FCN (icode) (temp, xop0, xop1); if (pat) { emit_insn (pat); return temp; } else delete_insns_since (last); } /* It can't be open-coded in this mode. Use a library call if one is available and caller says that's ok. */ if (binoptab->handlers[(int) mode].lib_call && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN)) { rtx insn_before, insn_first, insn_last; rtx funexp = gen_rtx (SYMBOL_REF, Pmode, binoptab->handlers[(int) mode].lib_call); /* Pass the address through a pseudoreg, if desired, before the "beginning" of the library call. So this insn isn't "part of" the library call, in case that is deleted, or cse'd. */#ifndef NO_FUNCTION_CSE if (! flag_no_function_cse) funexp = copy_to_mode_reg (Pmode, funexp);#endif insn_before = get_last_insn (); /* Cannot pass FUNEXP since emit_library_call insists on getting a SYMBOL_REF. But cse will make this SYMBOL_REF be replaced with the copy we made just above. */ /* Pass 1 for NO_QUEUE so we don't lose any increments if the libcall is cse'd or moved. */ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, binoptab->handlers[(int) mode].lib_call), 1, mode, 2, op0, mode, op1, mode); target = hard_libcall_value (mode); temp = copy_to_reg (target); if (insn_before == 0) insn_first = get_insns (); else insn_first = NEXT_INSN (insn_before); insn_last = get_last_insn (); REG_NOTES (insn_last) = gen_rtx (EXPR_LIST, REG_EQUAL, gen_rtx (binoptab->code, mode, op0, op1), gen_rtx (INSN_LIST, REG_RETVAL, insn_first, REG_NOTES (insn_last))); REG_NOTES (insn_first) = gen_rtx (INSN_LIST, REG_LIBCALL, insn_last, REG_NOTES (insn_first)); return temp; } delete_insns_since (last); /* It can't be done in this mode. Can we do it in a wider mode? */ if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN || methods == OPTAB_MUST_WIDEN)) return 0; /* Caller says, don't even try. */ /* Compute the value of METHODS to pass to recursive calls. Don't allow widening to be tried recursively. */ methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT); /* Widening is now independent of specific machine modes. It is assumed that widening may be performed to any higher numbered mode in the same mode class. */ if (class == MODE_INT || class == MODE_FLOAT) { for (wider_mode = INC_MODE (mode); ((int) wider_mode < (int) MAX_MACHINE_MODE && GET_MODE_CLASS (wider_mode) == class); wider_mode = INC_MODE (wider_mode)) { if ((binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) || (methods == OPTAB_LIB && binoptab->handlers[(int) wider_mode].lib_call)) { rtx xop0 = op0, xop1 = op1; int no_extend = 0; /* For certain operations, we need not actually extend the narrow operands, as long as we will truncate the results to the same narrowness. */ if (binoptab == ior_optab || binoptab == and_optab || binoptab == xor_optab || binoptab == andcb_optab || binoptab == add_optab || binoptab == sub_optab || binoptab == smul_optab || binoptab == umul_optab || binoptab == ashl_optab || binoptab == lshl_optab) no_extend = 1; if (GET_MODE (xop0) != VOIDmode) { if (no_extend) { temp = force_reg (GET_MODE (xop0), xop0); xop0 = gen_rtx (SUBREG, wider_mode, temp, 0); } else { temp = gen_reg_rtx (wider_mode); convert_move (temp, xop0, unsignedp); xop0 = temp; } } if (GET_MODE (xop1) != VOIDmode) { if (no_extend) { temp = force_reg (GET_MODE (xop1), xop1); xop1 = gen_rtx (SUBREG, wider_mode, temp, 0); } else { temp = gen_reg_rtx (wider_mode); convert_move (temp, xop1, unsignedp); xop1 = temp; } } temp = expand_binop (wider_mode, binoptab, xop0, xop1, 0, unsignedp, methods); if (temp) { if (class == MODE_FLOAT) { if (target == 0) target = gen_reg_rtx (mode); convert_move (target, temp, 0); return target; } else return gen_lowpart (mode, temp); } else delete_insns_since (last); } } } return 0;}/* Expand a binary operator which has both signed and unsigned forms. UOPTAB is the optab for unsigned operations, and SOPTAB is for signed operations. If we widen unsigned operands, we may use a signed wider operation instead of an unsigned wider operation, since the result would be the same. */rtxsign_expand_binop (mode, uoptab, soptab, op0, op1, target, unsignedp, methods) enum machine_mode mode; optab uoptab, soptab; rtx op0, op1, target; int unsignedp; enum optab_methods methods;{ register rtx temp; optab direct_optab = unsignedp ? uoptab : soptab; struct optab wide_soptab; /* Do it without widening, if possible. */ temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_DIRECT); if (temp || methods == OPTAB_DIRECT) return temp; /* Try widening to a signed int. Make a fake signed optab that hides any signed insn for direct use. */ wide_soptab = *soptab; wide_soptab.handlers[(int) mode].insn_code = CODE_FOR_nothing; wide_soptab.handlers[(int) mode].lib_call = 0; temp = expand_binop (mode, &wide_soptab, op0, op1, target, unsignedp, OPTAB_WIDEN); /* For unsigned operands, try widening to an unsigned int. */ if (temp == 0 && unsignedp) temp = expand_binop (mode, uoptab, op0, op1, target, unsignedp, OPTAB_WIDEN); if (temp || methods == OPTAB_WIDEN) return temp; /* Use the right width lib call if that exists. */ temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB); if (temp || methods == OPTAB_LIB) return temp; /* Must widen and use a lib call, use either signed or unsigned. */ temp = expand_binop (mode, &wide_soptab, op0, op1, target, unsignedp, methods); if (temp != 0) return temp; if (unsignedp) return expand_binop (mode, uoptab, op0, op1, target, unsignedp, methods); return 0;}/* Generate code to perform an operation specified by BINOPTAB on operands OP0 and OP1, with two results to TARG1 and TARG2. We assume that the order of the operands for the instruction is TARG0, OP0, OP1, TARG1, which would fit a pattern like [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. Either TARG0 or TARG1 may be zero, but what that means is that that result is not actually wanted. We will generate it into a dummy pseudo-reg and discard it. They may not both be zero. Returns 1 if this operation can be performed; 0 if not. */intexpand_twoval_binop (binoptab, op0, op1, targ0, targ1, unsignedp) optab binoptab; rtx op0, op1; rtx targ0, targ1; int unsignedp;{ enum machine_mode mode = GET_MODE (targ0 ? targ0 : targ1); enum mode_class class; enum machine_mode wider_mode; class = GET_MODE_CLASS (mode); op0 = protect_from_queue (op0, 0); op1 = protect_from_queue (op1, 0); if (flag_force_mem) { op0 = force_not_mem (op0); op1 = force_not_mem (op1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -