📄 optabs.c
字号:
{ register rtx tmp; tmp = op0; op0 = op1; op1 = tmp; tmp = xop0; xop0 = xop1; xop1 = tmp; } } /* 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 && mode0 != VOIDmode) xop0 = convert_to_mode (mode0, xop0, unsignedp); if (GET_MODE (xop1) != VOIDmode && GET_MODE (xop1) != mode1 && mode1 != VOIDmode) xop1 = convert_to_mode (mode1, xop1, unsignedp); /* Now, if insn's predicates don't allow our operands, put them into pseudo regs. */ if (! (*insn_operand_predicate[icode][1]) (xop0, mode0) && mode0 != VOIDmode) xop0 = copy_to_mode_reg (mode0, xop0); if (! (*insn_operand_predicate[icode][2]) (xop1, mode1) && mode1 != VOIDmode) xop1 = copy_to_mode_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) { /* If PAT is a multi-insn sequence, try to add an appropriate REG_EQUAL note to it. If we can't because TEMP conflicts with an operand, call ourselves again, this time without a target. */ if (GET_CODE (pat) == SEQUENCE && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1)) { delete_insns_since (last); return expand_binop (mode, binoptab, op0, op1, NULL_RTX, unsignedp, methods); } emit_insn (pat); return temp; } else delete_insns_since (last); } /* If this is a multiply, see if we can do a widening operation that takes operands of this mode and makes a wider mode. */ if (binoptab == smul_optab && GET_MODE_WIDER_MODE (mode) != VOIDmode && (((unsignedp ? umul_widen_optab : smul_widen_optab) ->handlers[(int) GET_MODE_WIDER_MODE (mode)].insn_code) != CODE_FOR_nothing)) { temp = expand_binop (GET_MODE_WIDER_MODE (mode), unsignedp ? umul_widen_optab : smul_widen_optab, op0, op1, NULL_RTX, unsignedp, OPTAB_DIRECT); if (temp != 0) { if (GET_MODE_CLASS (mode) == MODE_INT) return gen_lowpart (mode, temp); else return convert_to_mode (mode, temp, unsignedp); } } /* Look for a wider mode of the same class for which we think we can open-code the operation. Check for a widening multiply at the wider mode as well. */ if ((class == MODE_INT || class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT) && methods != OPTAB_DIRECT && methods != OPTAB_LIB) 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 || (binoptab == smul_optab && GET_MODE_WIDER_MODE (wider_mode) != VOIDmode && (((unsignedp ? umul_widen_optab : smul_widen_optab) ->handlers[(int) GET_MODE_WIDER_MODE (wider_mode)].insn_code) != 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. */ if ((binoptab == ior_optab || binoptab == and_optab || binoptab == xor_optab || binoptab == add_optab || binoptab == sub_optab || binoptab == smul_optab || binoptab == ashl_optab) && class == MODE_INT) no_extend = 1; xop0 = widen_operand (xop0, wider_mode, mode, unsignedp, no_extend); /* The second operand of a shift must always be extended. */ xop1 = widen_operand (xop1, wider_mode, mode, unsignedp, no_extend && binoptab != ashl_optab); 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, next_methods); if (x == 0) break; if (target_piece != x) emit_move_insn (target_piece, x); } insns = get_insns (); end_sequence (); if (i == GET_MODE_BITSIZE (mode) / BITS_PER_WORD) { if (binoptab->code != UNKNOWN) equiv_value = gen_rtx (binoptab->code, mode, copy_rtx (op0), copy_rtx (op1)); else equiv_value = 0; emit_no_conflict_block (insns, target, op0, op1, equiv_value); return target; } } /* Synthesize double word shifts from single word shifts. */ if ((binoptab == lshr_optab || binoptab == ashl_optab || binoptab == ashr_optab) && class == MODE_INT && GET_CODE (op1) == CONST_INT && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD && binoptab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) { rtx insns, inter, equiv_value; rtx into_target, outof_target; rtx into_input, outof_input; int shift_count, left_shift, outof_word; /* 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 (); shift_count = INTVAL (op1); /* OUTOF_* is the word we are shifting bits away from, and INTO_* is the word that we are shifting bits towards, thus they differ depending on the direction of the shift and WORDS_BIG_ENDIAN. */ left_shift = binoptab == ashl_optab; outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; outof_target = operand_subword (target, outof_word, 1, mode); into_target = operand_subword (target, 1 - outof_word, 1, mode); outof_input = operand_subword_force (op0, outof_word, mode); into_input = operand_subword_force (op0, 1 - outof_word, mode); if (shift_count >= BITS_PER_WORD) { inter = expand_binop (word_mode, binoptab, outof_input, GEN_INT (shift_count - BITS_PER_WORD), into_target, unsignedp, next_methods); if (inter != 0 && inter != into_target) emit_move_insn (into_target, inter); /* For a signed right shift, we must fill the word we are shifting out of with copies of the sign bit. Otherwise it is zeroed. */ if (inter != 0 && binoptab != ashr_optab) inter = CONST0_RTX (word_mode); else if (inter != 0) inter = expand_binop (word_mode, binoptab, outof_input, GEN_INT (BITS_PER_WORD - 1), outof_target, unsignedp, next_methods); if (inter != 0 && inter != outof_target) emit_move_insn (outof_target, inter); } else { rtx carries; optab reverse_unsigned_shift, unsigned_shift; /* For a shift of less then BITS_PER_WORD, to compute the carry, we must do a logical shift in the opposite direction of the desired shift. */ reverse_unsigned_shift = (left_shift ? lshr_optab : ashl_optab); /* For a shift of less than BITS_PER_WORD, to compute the word shifted towards, we need to unsigned shift the orig value of that word. */ unsigned_shift = (left_shift ? ashl_optab : lshr_optab); carries = expand_binop (word_mode, reverse_unsigned_shift, outof_input, GEN_INT (BITS_PER_WORD - shift_count), 0, unsignedp, next_methods); if (carries == 0) inter = 0; else inter = expand_binop (word_mode, unsigned_shift, into_input, op1, 0, unsignedp, next_methods); if (inter != 0) inter = expand_binop (word_mode, ior_optab, carries, inter, into_target, unsignedp, next_methods); if (inter != 0 && inter != into_target) emit_move_insn (into_target, inter); if (inter != 0) inter = expand_binop (word_mode, binoptab, outof_input, op1, outof_target, unsignedp, next_methods); if (inter != 0 && inter != outof_target) emit_move_insn (outof_target, inter); } insns = get_insns (); end_sequence (); if (inter != 0) { 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; } } /* Synthesize double word rotates from single word shifts. */ if ((binoptab == rotl_optab || binoptab == rotr_optab) && class == MODE_INT && GET_CODE (op1) == CONST_INT && GET_MODE_SIZE (mode) == 2 * UNITS_PER_WORD && ashl_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing && lshr_optab->handlers[(int) word_mode].insn_code != CODE_FOR_nothing) { rtx insns, equiv_value; rtx into_target, outof_target; rtx into_input, outof_input; rtx inter; int shift_count, left_shift, outof_word; /* 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 (); shift_count = INTVAL (op1); /* OUTOF_* is the word we are shifting bits away from, and INTO_* is the word that we are shifting bits towards, thus they differ depending on the direction of the shift and WORDS_BIG_ENDIAN. */ left_shift = (binoptab == rotl_optab); outof_word = left_shift ^ ! WORDS_BIG_ENDIAN; outof_target = operand_subword (target, outof_word, 1, mode); into_target = operand_subword (target, 1 - outof_word, 1, mode); outof_input = operand_subword_force (op0, outof_word, mode); into_input = operand_subword_force (op0, 1 - outof_word, mode); if (shift_count == BITS_PER_WORD) { /* This is just a word swap. */ emit_move_insn (outof_target, into_input); emit_move_insn (into_target, outof_input); inter = const0_rtx; } else { rtx into_temp1, into_temp2, outof_temp1, outof_temp2; rtx first_shift_count, second_shift_count; optab reverse_unsigned_shift, unsigned_shift; reverse_unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD) ? lshr_optab : ashl_optab); unsigned_shift = (left_shift ^ (shift_count < BITS_PER_WORD) ? ashl_optab : lshr_optab); if (shift_count > BITS_PER_WORD) { first_shift_count = GEN_INT (shift_count - BITS_PER_WORD); second_shift_count = GEN_INT (2*BITS_PER_WORD - shift_count); } else { first_shift_count = GEN_INT (BITS_PER_WORD - shift_count); second_shift_count = GEN_INT (shift_count); } into_temp1 = expand_binop (word_mode, unsigned_shift, outof_input, first_shift_count, NULL_RTX, unsignedp, next_methods); into_temp2 = expand_binop (word_mode, reverse_unsigned_shift, into_input, second_shift_count, into_target, unsignedp, next_methods); if (into_temp1 != 0 && into_temp2 != 0) inter = expand_binop (word_mode, ior_optab, into_temp1, into_temp2, into_target, unsignedp, next_methods); else inter = 0; if (inter != 0 && inter != into_target) emit_move_insn (into_target, inter); outof_temp1 = expand_binop (word_mode, unsigned_shift, into_input, first_shift_count, NULL_RTX, unsignedp, next_methods); outof_temp2 = expand_binop (word_mode, reverse_unsigned_shift, outof_input, second_shift_count, outof_target, unsignedp, next_methods); if (inter != 0 && outof_temp1 != 0 && outof_temp2 != 0) inter = expand_binop (word_mode, ior_optab, outof_temp1, outof_temp2, outof_target, unsignedp, next_methods); if (inter != 0 && inter != outof_target) emit_move_insn (outof_target, inter); } insns = get_insns (); end_sequence (); if (inter != 0) { if (binoptab->code != UNKNOWN) equiv_value = gen_rtx (binoptab->code, mode, op0, op1); else equiv_value = 0; /* We can't make this a no conflict block if this is a word swap, because the word swap case fails if the input and output values are in the same register. */ if (shift_count != BITS_PER_WORD) emit_no_conflict_block (insns, target, op0, op1, equiv_value); else emit_insns (insns); 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -