📄 optabs.c
字号:
!= CODE_FOR_nothing))) { rtx xop0 = op0, xop1 = op1; int no_extend = 0; /* For certain integer operations, we need not actually extend the narrow operands, as long as we will truncate the results to the same narrowness. 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, OPTAB_DIRECT); if (temp) { if (class != MODE_INT) { if (target == 0) target = gen_reg_rtx (mode); convert_move (target, temp, 0); return target; } else return gen_lowpart (mode, temp); } else delete_insns_since (last); } } /* These can be done a word at a time. */ if ((binoptab == and_optab || binoptab == ior_optab || binoptab == xor_optab) && class == MODE_INT && GET_MODE_SIZE (mode) > UNITS_PER_WORD && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) { int i; rtx insns; rtx equiv_value; /* If TARGET is the same as one of the operands, the REG_EQUAL note won't be accurate, so use a new target. */ if (target == 0 || target == op0 || target == op1) target = gen_reg_rtx (mode); start_sequence (); /* Do the actual arithmetic. */ for (i = 0; i < GET_MODE_BITSIZE (mode) / BITS_PER_WORD; i++) { rtx target_piece = operand_subword (target, i, 1, mode); rtx x = expand_binop (word_mode, binoptab, operand_subword_force (op0, i, mode), operand_subword_force (op1, i, mode), target_piece, unsignedp, methods); if (target_piece != x) emit_move_insn (target_piece, x); } insns = get_insns (); end_sequence (); if (binoptab->code != UNKNOWN) equiv_value = gen_rtx (binoptab->code, mode, op0, op1); else equiv_value = 0; emit_no_conflict_block (insns, target, op0, op1, equiv_value); return target; } /* These can be done a word at a time by propagating carries. */ if ((binoptab == add_optab || binoptab == sub_optab) && class == MODE_INT && GET_MODE_SIZE (mode) >= 2 * UNITS_PER_WORD && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) { int i; rtx carry_tmp = gen_reg_rtx (word_mode); optab otheroptab = binoptab == add_optab ? sub_optab : add_optab; int nwords = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; rtx carry_in, carry_out; rtx xop0, xop1; /* We can handle either a 1 or -1 value for the carry. If STORE_FLAG value is one of those, use it. Otherwise, use 1 since it is the one easiest to get. */#if STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1 int normalizep = STORE_FLAG_VALUE;#else int normalizep = 1;#endif /* Prepare the operands. */ xop0 = force_reg (mode, op0); xop1 = force_reg (mode, op1); if (target == 0 || GET_CODE (target) != REG || target == xop0 || target == xop1) target = gen_reg_rtx (mode); /* Do the actual arithmetic. */ for (i = 0; i < nwords; i++) { int index = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i); rtx target_piece = operand_subword (target, index, 1, mode); rtx op0_piece = operand_subword_force (xop0, index, mode); rtx op1_piece = operand_subword_force (xop1, index, mode); rtx x; /* Main add/subtract of the input operands. */ x = expand_binop (word_mode, binoptab, op0_piece, op1_piece, target_piece, unsignedp, methods); if (x == 0) break; if (i + 1 < nwords) { /* Store carry from main add/subtract. */ carry_out = gen_reg_rtx (word_mode); carry_out = emit_store_flag (carry_out, binoptab == add_optab ? LTU : GTU, x, op0_piece, word_mode, 1, normalizep); if (!carry_out) break; } if (i > 0) { /* Add/subtract previous carry to main result. */ x = expand_binop (word_mode, normalizep == 1 ? binoptab : otheroptab, x, carry_in, target_piece, 1, methods); if (target_piece != x) emit_move_insn (target_piece, x); if (i + 1 < nwords) { /* THIS CODE HAS NOT BEEN TESTED. */ /* Get out carry from adding/subtracting carry in. */ carry_tmp = emit_store_flag (carry_tmp, binoptab == add_optab ? LTU : GTU, x, carry_in, word_mode, 1, normalizep); /* Logical-ior the two poss. carry together. */ carry_out = expand_binop (word_mode, ior_optab, carry_out, carry_tmp, carry_out, 0, methods); if (!carry_out) break; } } carry_in = carry_out; } if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) { rtx temp; temp = emit_move_insn (target, target); REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL, gen_rtx (binoptab->code, mode, xop0, xop1), REG_NOTES (temp)); return target; } else delete_insns_since (last); } /* If we want to multiply two two-word values and have normal and widening multiplies of single-word values, we can do this with three smaller multiplications. Note that we do not make a REG_NO_CONFLICT block here because we are not operating on one word at a time. The multiplication proceeds as follows: _______________________ [__op0_high_|__op0_low__] _______________________ * [__op1_high_|__op1_low__] _______________________________________________ _______________________ (1) [__op0_low__*__op1_low__] _______________________ (2a) [__op0_low__*__op1_high_] _______________________ (2b) [__op0_high_*__op1_low__] _______________________ (3) [__op0_high_*__op1_high_] This gives a 4-word result. Since we are only interested in the lower 2 words, partial result (3) and the upper words of (2a) and (2b) don't need to be calculated. Hence (2a) and (2b) can be calculated using non-widening multiplication. (1), however, needs to be calculated with an unsigned widening multiplication. If this operation is not directly supported we try using a signed widening multiplication and adjust the result. This adjustment works as follows: If both operands are positive then no adjustment is needed. If the operands have different signs, for example op0_low < 0 and op1_low >= 0, the instruction treats the most significant bit of op0_low as a sign bit instead of a bit with significance 2**(BITS_PER_WORD-1), i.e. the instruction multiplies op1_low with 2**BITS_PER_WORD - op0_low, and two's complements the result. Conclusion: We need to add op1_low * 2**BITS_PER_WORD to the result. Similarly, if both operands are negative, we need to add (op0_low + op1_low) * 2**BITS_PER_WORD. We use a trick to adjust quickly. We logically shift op0_low right (op1_low) BITS_PER_WORD-1 steps to get 0 or 1, and add this to op0_high (op1_high) before it is used to calculate 2b (2a). If no logical shift exists, we do an arithmetic right shift and subtract the 0 or -1. */ if (binoptab == smul_optab && class == MODE_INT && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD && smul_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && add_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && ((umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) || (smul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing))) { int low = (WORDS_BIG_ENDIAN ? 1 : 0); int high = (WORDS_BIG_ENDIAN ? 0 : 1); rtx op0_high = operand_subword_force (op0, high, mode); rtx op0_low = operand_subword_force (op0, low, mode); rtx op1_high = operand_subword_force (op1, high, mode); rtx op1_low = operand_subword_force (op1, low, mode); rtx product = 0; rtx op0_xhigh; rtx op1_xhigh; /* If the target is the same as one of the inputs, don't use it. This prevents problems with the REG_EQUAL note. */ if (target == op0 || target == op1) target = 0; /* Multiply the two lower words to get a double-word product. If unsigned widening multiplication is available, use that; otherwise use the signed form and compensate. */ if (umul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { product = expand_binop (mode, umul_widen_optab, op0_low, op1_low, target, 1, OPTAB_DIRECT); /* If we didn't succeed, delete everything we did so far. */ if (product == 0) delete_insns_since (last); else op0_xhigh = op0_high, op1_xhigh = op1_high; } if (product == 0 && smul_widen_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { rtx wordm1 = GEN_INT (BITS_PER_WORD - 1); product = expand_binop (mode, smul_widen_optab, op0_low, op1_low, target, 1, OPTAB_DIRECT); op0_xhigh = expand_binop (word_mode, lshr_optab, op0_low, wordm1, NULL_RTX, 1, OPTAB_DIRECT); if (op0_xhigh) op0_xhigh = expand_binop (word_mode, add_optab, op0_high, op0_xhigh, op0_xhigh, 0, OPTAB_DIRECT); else { op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1, NULL_RTX, 0, OPTAB_DIRECT); if (op0_xhigh) op0_xhigh = expand_binop (word_mode, sub_optab, op0_high, op0_xhigh, op0_xhigh, 0, OPTAB_DIRECT); } op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1, NULL_RTX, 1, OPTAB_DIRECT); if (op1_xhigh) op1_xhigh = expand_binop (word_mode, add_optab, op1_high, op1_xhigh, op1_xhigh, 0, OPTAB_DIRECT); else { op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1, NULL_RTX, 0, OPTAB_DIRECT); if (op1_xhigh) op1_xhigh = expand_binop (word_mode, sub_optab, op1_high, op1_xhigh, op1_xhigh, 0, OPTAB_DIRECT); } } /* If we have been able to directly compute the product of the low-order words of the operands and perform any required adjustments of the operands, we proceed by trying two more multiplications and then computing the appropriate sum. We have checked above that the required addition is provided. Full-word addition will normally always succeed, especially if it is provided at all, so we don't worry about its failure. The multiplication may well fail, however, so we do handle that. */ if (product && op0_xhigh && op1_xhigh) { rtx product_piece; rtx product_high = operand_subword (product, high, 1, mode); rtx temp = expand_binop (word_mode, binoptab, op0_low, op1_xhigh, NULL_RTX, 0, OPTAB_DIRECT); if (temp) { product_piece = expand_binop (word_mode, add_optab, temp, product_high, product_high, 0, OPTAB_LIB_WIDEN); if (product_piece != product_high) emit_move_insn (product_high, product_piece); temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, NULL_RTX, 0, OPTAB_DIRECT); product_piece = expand_binop (word_mode, add_optab, temp, product_high, product_high, 0, OPTAB_LIB_WIDEN); if (product_piece != product_high) emit_move_insn (product_high, product_piece); temp = emit_move_insn (product, product); REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL, gen_rtx (MULT, mode, op0, op1), REG_NOTES (temp)); return product; } } /* If we get here, we couldn't do it for some reason even though we originally thought we could. Delete anything we've emitted in trying to do it. */ delete_insns_since (last); } /* We need to open-code the complex type operations: '+, -, * and /' */ /* At this point we allow operations between two similar complex numbers, and also if one of the operands is not a complex number but rather of MODE_FLOAT or MODE_INT. However, the caller must make sure that the MODE of the non-complex operand matches the SUBMODE of the complex operand. */ if (class == MODE_COMPLEX_FLOAT || class == MODE_COMPLEX_INT) { rtx real0 = (rtx) 0; rtx imag0 = (rtx) 0; rtx real1 = (rtx) 0; rtx imag1 = (rtx) 0; rtx realr; rtx imagr; rtx res; rtx seq; rtx equiv_value; /* Find the correct mode for the real and imaginary parts */ enum machine_mode submode = mode_for_size (GET_MODE_UNIT_SIZE (mode) * BITS_PER_UNIT, class == MODE_COMPLEX_INT ? MODE_INT : MODE_FLOAT, 0); if (submode == BLKmode) abort (); if (! target) target = gen_reg_rtx (mode); start_sequence (); realr = gen_realpart (submode, target); imagr = gen_imagpart (submode, target); if (GET_MODE (op0) == mode) { real0 = gen_realpart (submode, op0); imag0 = gen_imagpart (submode, op0); } else real0 = op0; if (GET_MODE (op1) == mode) { real1 = gen_realpart (submode, op1); imag1 = gen_imagpart (submode, op1); } else real1 = op1; if (! real0 || ! real1 || ! (imag0 || imag1)) abort (); switch (binoptab->code) { case PLUS: case MINUS: res = expand_binop (submode, binoptab, real0, real1, realr, unsignedp, methods); if (res != realr) emit_move_insn (realr, res); if (imag0 && imag1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -