📄 optabs.c
字号:
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); /* Indicate for flow that the entire target reg is being set. */ if (GET_CODE (target) == REG) emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); /* 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, next_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 == 0) 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, next_methods); if (x == 0) break; else 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, next_methods); if (carry_out == 0) break; } } carry_in = carry_out; } if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) { rtx temp = emit_move_insn (target, target); REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL, gen_rtx (binoptab->code, mode, copy_rtx (xop0), copy_rtx (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 && GET_CODE (target) != REG)) 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, next_methods); if (op0_xhigh) op0_xhigh = expand_binop (word_mode, add_optab, op0_high, op0_xhigh, op0_xhigh, 0, next_methods); else { op0_xhigh = expand_binop (word_mode, ashr_optab, op0_low, wordm1, NULL_RTX, 0, next_methods); if (op0_xhigh) op0_xhigh = expand_binop (word_mode, sub_optab, op0_high, op0_xhigh, op0_xhigh, 0, next_methods); } op1_xhigh = expand_binop (word_mode, lshr_optab, op1_low, wordm1, NULL_RTX, 1, next_methods); if (op1_xhigh) op1_xhigh = expand_binop (word_mode, add_optab, op1_high, op1_xhigh, op1_xhigh, 0, next_methods); else { op1_xhigh = expand_binop (word_mode, ashr_optab, op1_low, wordm1, NULL_RTX, 0, next_methods); if (op1_xhigh) op1_xhigh = expand_binop (word_mode, sub_optab, op1_high, op1_xhigh, op1_xhigh, 0, next_methods); } } /* 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_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 != 0) temp = expand_binop (word_mode, add_optab, temp, product_high, product_high, 0, next_methods); if (temp != 0 && temp != product_high) emit_move_insn (product_high, temp); if (temp != 0) temp = expand_binop (word_mode, binoptab, op1_low, op0_xhigh, NULL_RTX, 0, OPTAB_DIRECT); if (temp != 0) temp = expand_binop (word_mode, add_optab, temp, product_high, product_high, 0, next_methods); if (temp != 0 && temp != product_high) emit_move_insn (product_high, temp); if (temp != 0) { temp = emit_move_insn (product, product); REG_NOTES (temp) = gen_rtx (EXPR_LIST, REG_EQUAL, gen_rtx (MULT, mode, copy_rtx (op0), copy_rtx (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 = 0, imag0 = 0; rtx real1 = 0, imag1 = 0; rtx realr, imagr, res; rtx seq; rtx equiv_value; int ok = 0; /* 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 == 0 || real1 == 0 || ! (imag0 != 0|| imag1 != 0)) abort (); switch (binoptab->code) { case PLUS: /* (a+ib) + (c+id) = (a+c) + i(b+d) */ case MINUS: /* (a+ib) - (c+id) = (a-c) + i(b-d) */ res = expand_binop (submode, binoptab, real0, real1, realr, unsignedp, methods); if (res == 0) break; else if (res != realr) emit_move_insn (realr, res); if (imag0 && imag1) 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 == 0) break; else if (res != imagr) emit_move_insn (imagr, res); ok = 1; break; case MULT: /* (a+ib) * (c+id) = (ac-bd) + i(ad+cb) */ if (imag0 && imag1) { rtx temp1, temp2; /* Don't fetch these from memory more than once. */ real0 = force_reg (submode, real0); real1 = force_reg (submode, real1); imag0 = force_reg (submode, imag0); imag1 = force_reg (submode, imag1); temp1 = expand_binop (submode, binoptab, real0, real1, NULL_RTX, unsignedp, methods); temp2 = expand_binop (submode, binoptab, imag0, imag1, NULL_RTX, unsignedp, methods); if (temp1 == 0 || temp2 == 0) break; res = expand_binop (submode, sub_optab, temp1, temp2, realr, unsignedp, methods); if (res == 0) break; else if (res != realr) emit_move_insn (realr, res); temp1 = expand_binop (submode, binoptab, real0, imag1, NULL_RTX, unsignedp, methods); temp2 = expand_binop (submode, binoptab, real1, imag0, NULL_RTX, unsignedp, methods); if (temp1 == 0 || temp2 == 0) break; res = expand_binop (submode, add_optab, temp1, temp2, imagr, unsignedp, methods); if (res == 0) break; else if (res != imagr) emit_move_insn (imagr, res); ok = 1; } else { /* Don't fetch these from memory more than once. */ real0 = force_reg (submode, real0); real1 = force_reg (submode, real1); res = expand_binop (submode, binoptab, real0, real1, realr, unsignedp, methods); if (res == 0) break; else if (res != realr) emit_move_insn (realr, res); if (imag0 != 0) res = expand_binop (submode, binoptab, real1, imag0, imagr, unsignedp, methods); else res = expand_binop (submode, binoptab, real0, imag1, imagr, unsignedp, methods); if (res == 0) break; else if (res != imagr) emit_move_insn (imagr, res); ok = 1; } break; case DIV: /* (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd)) */ if (imag1 == 0) { /* (a+ib) / (c+i0) = (a/c) + i(b/c) */ /* Don't fetch these from memory more than once. */ real1 = force_reg (submode, real1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -