📄 optabs.c
字号:
res = expand_binop (submode, binoptab, imag0, imag1, imagr, unsignedp, methods); else if (imag0) res = imag0; else if (binoptab->code == MINUS) res = expand_unop (submode, neg_optab, imag1, imagr, unsignedp); else res = imag1; if (res != imagr) emit_move_insn (imagr, res); break; case MULT: /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */ res = expand_binop (submode, binoptab, real0, real1, realr, unsignedp, methods); if (imag0 && imag1) { rtx temp = expand_binop (submode, sub_optab, res, expand_binop (submode, binoptab, imag0, imag1, 0, unsignedp, methods), realr, unsignedp, methods); if (temp != realr) emit_move_insn (realr, temp); res = expand_binop (submode, add_optab, expand_binop (submode, binoptab, real0, imag1, 0, unsignedp, methods), expand_binop (submode, binoptab, real1, imag0, 0, unsignedp, methods), imagr, unsignedp, methods); if (res != imagr) emit_move_insn (imagr, res); } else { if (res != realr) emit_move_insn (realr, res); if (imag0) res = expand_binop (submode, binoptab, real1, imag0, imagr, unsignedp, methods); else res = expand_binop (submode, binoptab, real0, imag1, imagr, unsignedp, methods); if (res != imagr) emit_move_insn (imagr, res); } break; case DIV: /* (c+id)/(a+ib) == ((c+id)*(a-ib))/(a*a+b*b) */ if (! imag1) { /* Simply divide the real and imaginary parts by `a' */ res = expand_binop (submode, binoptab, real0, real1, realr, unsignedp, methods); if (res != realr) emit_move_insn (realr, res); res = expand_binop (submode, binoptab, imag0, real1, imagr, unsignedp, methods); if (res != imagr) emit_move_insn (imagr, res); } else /* Divisor is of complex type */ { /* X/(a+ib) */ rtx divisor; rtx real_t; rtx imag_t; optab mulopt = unsignedp ? umul_widen_optab : smul_optab; /* Divisor: c*c + d*d */ divisor = expand_binop (submode, add_optab, expand_binop (submode, mulopt, real1, real1, 0, unsignedp, methods), expand_binop (submode, mulopt, imag1, imag1, 0, unsignedp, methods), 0, unsignedp, methods); if (! imag0) /* ((a)(c-id))/divisor */ { /* (a+i0) / (c+id) = (ac/(cc+dd)) + i(-ad/(cc+dd)) */ /* Calculate the dividend */ real_t = expand_binop (submode, mulopt, real0, real1, 0, unsignedp, methods); imag_t = expand_unop (submode, neg_optab, expand_binop (submode, mulopt, real0, imag1, 0, unsignedp, methods), 0, unsignedp); } else /* ((a+ib)(c-id))/divider */ { /* Calculate the dividend */ real_t = expand_binop (submode, add_optab, expand_binop (submode, mulopt, real0, real1, 0, unsignedp, methods), expand_binop (submode, mulopt, imag0, imag1, 0, unsignedp, methods), 0, unsignedp, methods); imag_t = expand_binop (submode, sub_optab, expand_binop (submode, mulopt, imag0, real1, 0, unsignedp, methods), expand_binop (submode, mulopt, real0, imag1, 0, unsignedp, methods), 0, unsignedp, methods); } res = expand_binop (submode, binoptab, real_t, divisor, realr, unsignedp, methods); if (res != realr) emit_move_insn (realr, res); res = expand_binop (submode, binoptab, imag_t, divisor, imagr, unsignedp, methods); if (res != imagr) emit_move_insn (imagr, res); } break; default: abort (); } seq = get_insns (); end_sequence (); if (binoptab->code != UNKNOWN) equiv_value = gen_rtx (binoptab->code, mode, op0, op1); else equiv_value = 0; emit_no_conflict_block (seq, target, op0, op1, equiv_value); return target; } /* It can't be open-coded in this mode. Use a library call if one is available and caller says that's ok. */ if (binoptab->handlers[(int) mode].libfunc && (methods == OPTAB_LIB || methods == OPTAB_LIB_WIDEN)) { rtx insns; rtx funexp = binoptab->handlers[(int) mode].libfunc; rtx op1x = op1; enum machine_mode op1_mode = mode; start_sequence (); if (shift_op) { op1_mode = word_mode; /* Specify unsigned here, since negative shift counts are meaningless. */ op1x = convert_to_mode (word_mode, op1, 1); } /* Pass 1 for NO_QUEUE so we don't lose any increments if the libcall is cse'd or moved. */ emit_library_call (binoptab->handlers[(int) mode].libfunc, 1, mode, 2, op0, mode, op1x, op1_mode); insns = get_insns (); end_sequence (); target = gen_reg_rtx (mode); emit_libcall_block (insns, target, hard_libcall_value (mode), gen_rtx (binoptab->code, mode, op0, op1)); return target; } delete_insns_since (last); /* It can't be done in this mode. Can we do it in a wider mode? */ if (! (methods == OPTAB_WIDEN || methods == OPTAB_LIB_WIDEN || methods == OPTAB_MUST_WIDEN)) { /* Caller says, don't even try. */ delete_insns_since (entry_last); return 0; } /* Compute the value of METHODS to pass to recursive calls. Don't allow widening to be tried recursively. */ methods = (methods == OPTAB_LIB_WIDEN ? OPTAB_LIB : OPTAB_DIRECT); /* Look for a wider mode of the same class for which it appears we can do the operation. */ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) { for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; wider_mode = GET_MODE_WIDER_MODE (wider_mode)) { if ((binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) || (methods == OPTAB_LIB && binoptab->handlers[(int) wider_mode].libfunc)) { rtx xop0 = op0, xop1 = op1; int no_extend = 0; /* For certain integer operations, we need not actually extend the narrow operands, as long as we will truncate the results to the same narrowness. Don't do this when WIDER_MODE is wider than a word since a paradoxical SUBREG isn't valid for such modes. */ if ((binoptab == ior_optab || binoptab == and_optab || binoptab == xor_optab || binoptab == add_optab || binoptab == sub_optab || binoptab == smul_optab || binoptab == ashl_optab || binoptab == lshl_optab) && class == MODE_INT && GET_MODE_SIZE (wider_mode) <= UNITS_PER_WORD) no_extend = 1; /* If an operand is a constant integer, we might as well convert it since that is more efficient than using a SUBREG, unlike the case for other operands. Similarly for SUBREGs that were made due to promoted objects.*/ if (no_extend && GET_MODE (xop0) != VOIDmode && ! (GET_CODE (xop0) == SUBREG && SUBREG_PROMOTED_VAR_P (xop0))) xop0 = gen_rtx (SUBREG, wider_mode, force_reg (GET_MODE (xop0), xop0), 0); else xop0 = convert_to_mode (wider_mode, xop0, unsignedp); if (no_extend && GET_MODE (xop1) != VOIDmode && ! (GET_CODE (xop1) == SUBREG && SUBREG_PROMOTED_VAR_P (xop1))) xop1 = gen_rtx (SUBREG, wider_mode, force_reg (GET_MODE (xop1), xop1), 0); else xop1 = convert_to_mode (wider_mode, xop1, unsignedp); temp = expand_binop (wider_mode, binoptab, xop0, xop1, NULL_RTX, unsignedp, methods); if (temp) { if (class != MODE_INT) { if (target == 0) target = gen_reg_rtx (mode); convert_move (target, temp, 0); return target; } else return gen_lowpart (mode, temp); } else delete_insns_since (last); } } } delete_insns_since (entry_last); return 0;}/* Expand a binary operator which has both signed and unsigned forms. UOPTAB is the optab for unsigned operations, and SOPTAB is for signed operations. If we widen unsigned operands, we may use a signed wider operation instead of an unsigned wider operation, since the result would be the same. */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].libfunc = 0; temp = expand_binop (mode, &wide_soptab, op0, op1, target, unsignedp, OPTAB_WIDEN); /* For unsigned operands, try widening to an unsigned int. */ if (temp == 0 && unsignedp) temp = expand_binop (mode, uoptab, op0, op1, target, unsignedp, OPTAB_WIDEN); if (temp || methods == OPTAB_WIDEN) return temp; /* Use the right width lib call if that exists. */ temp = expand_binop (mode, direct_optab, op0, op1, target, unsignedp, OPTAB_LIB); if (temp || methods == OPTAB_LIB) return temp; /* Must widen and use a lib call, use either signed or unsigned. */ temp = expand_binop (mode, &wide_soptab, op0, op1, target, unsignedp, methods); if (temp != 0) return temp; if (unsignedp) return expand_binop (mode, uoptab, op0, op1, target, unsignedp, methods); return 0;}/* Generate code to perform an operation specified by BINOPTAB on operands OP0 and OP1, with two results to TARG1 and TARG2. We assume that the order of the operands for the instruction is TARG0, OP0, OP1, TARG1, which would fit a pattern like [(set TARG0 (operate OP0 OP1)) (set TARG1 (operate ...))]. Either TARG0 or TARG1 may be zero, but what that means is that that result is not actually wanted. We will generate it into a dummy pseudo-reg and discard it. They may not both be zero. Returns 1 if this operation can be performed; 0 if not. */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; rtx entry_last = get_last_insn (); rtx last; class = GET_MODE_CLASS (mode); op0 = protect_from_queue (op0, 0); op1 = protect_from_queue (op1, 0); if (flag_force_mem) { op0 = force_not_mem (op0); op1 = force_not_mem (op1); } /* If we are inside an appropriately-short loop and one operand is an expensive constant, force it into a register. */ if (CONSTANT_P (op0) && preserve_subexpressions_p () && rtx_cost (op0, binoptab->code) > 2) op0 = force_reg (mode, op0); if (CONSTANT_P (op1) && preserve_subexpressions_p () && rtx_cost (op1, binoptab->code) > 2) op1 = force_reg (mode, op1); if (targ0) targ0 = protect_from_queue (targ0, 1); else targ0 = gen_reg_rtx (mode); if (targ1) targ1 = protect_from_queue (targ1, 1); else targ1 = gen_reg_rtx (mode); /* Record where to go back to if we fail. */ last = get_last_insn (); if (binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { int icode = (int) binoptab->handlers[(int) mode].insn_code; enum machine_mode mode0 = insn_operand_mode[icode][1]; enum machine_mode mode1 = insn_operand_mode[icode][2]; rtx pat; rtx xop0 = op0, xop1 = op1; /* In case this insn wants input operands in modes different from the result, convert the operands. */ if (GET_MODE (op0) != VOIDmode && GET_MODE (op0) != mode0) xop0 = convert_to_mode (mode0, xop0, unsignedp); if (GET_MODE (op1) != VOIDmode && GET_MODE (op1) != mode1) xop1 = convert_to_mode (mode1, xop1, unsignedp); /* Now, if insn doesn't accept these operands, put them into pseudos. */ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)) xop0 = copy_to_mode_reg (mode0, xop0); if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)) xop1 = copy_to_mode_reg (mode1, xop1); /* We could handle this, but we should always be called with a pseudo for our targets and all insns should take them as outputs. */ if (! (*insn_operand_predicate[icode][0]) (targ0, mode) || ! (*insn_operand_predicate[icode][3]) (targ1, mode)) abort (); pat = GEN_FCN (icode) (targ0, xop0, xop1, targ1); if (pat) { emit_insn (pat); return 1; } else delete_insns_since (last); } /* It can't be done in this mode. Can we do it in a wider mode? */ if (class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) { for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; wider_mode = GET_MODE_WIDER_MODE (wider_mode)) { if (binoptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -