📄 optabs.c
字号:
return target; } /* It can't be done in this mode. Can we do it in a wider mode? */ for (wider_mode = GET_MODE_WIDER_MODE (mode); wider_mode != VOIDmode; wider_mode = GET_MODE_WIDER_MODE (wider_mode)) { if ((abs_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing) || abs_optab->handlers[(int) wider_mode].libfunc) { rtx xop0 = op0; xop0 = convert_to_mode (wider_mode, xop0, unsignedp); temp = expand_complex_abs (wider_mode, xop0, NULL_RTX, unsignedp); if (temp) { if (class != MODE_COMPLEX_INT) { if (target == 0) target = gen_reg_rtx (submode); convert_move (target, temp, 0); return target; } else return gen_lowpart (submode, temp); } else delete_insns_since (last); } } delete_insns_since (entry_last); return 0;}/* Generate an instruction whose insn-code is INSN_CODE, with two operands: an output TARGET and an input OP0. TARGET *must* be nonzero, and the output is always stored there. CODE is an rtx code such that (CODE OP0) is an rtx that describes the value that is stored into TARGET. */voidemit_unop_insn (icode, target, op0, code) int icode; rtx target; rtx op0; enum rtx_code code;{ register rtx temp; enum machine_mode mode0 = insn_operand_mode[icode][1]; rtx pat; temp = target = protect_from_queue (target, 1); op0 = protect_from_queue (op0, 0); if (flag_force_mem) op0 = force_not_mem (op0); /* Now, if insn does not accept our operands, put them into pseudos. */ if (! (*insn_operand_predicate[icode][1]) (op0, mode0)) op0 = copy_to_mode_reg (mode0, op0); if (! (*insn_operand_predicate[icode][0]) (temp, GET_MODE (temp)) || (flag_force_mem && GET_CODE (temp) == MEM)) temp = gen_reg_rtx (GET_MODE (temp)); pat = GEN_FCN (icode) (temp, op0); if (GET_CODE (pat) == SEQUENCE && code != UNKNOWN) add_equal_note (pat, temp, code, op0, NULL_RTX); emit_insn (pat); if (temp != target) emit_move_insn (target, temp);}/* Emit code to perform a series of operations on a multi-word quantity, one word at a time. Such a block is preceded by a CLOBBER of the output, consists of multiple insns, each setting one word of the output, and followed by a SET copying the output to itself. Each of the insns setting words of the output receives a REG_NO_CONFLICT note indicating that it doesn't conflict with the (also multi-word) inputs. The entire block is surrounded by REG_LIBCALL and REG_RETVAL notes. INSNS is a block of code generated to perform the operation, not including the CLOBBER and final copy. All insns that compute intermediate values are first emitted, followed by the block as described above. Only INSNs are allowed in the block; no library calls or jumps may be present. TARGET, OP0, and OP1 are the output and inputs of the operations, respectively. OP1 may be zero for a unary operation. EQUIV, if non-zero, is an expression to be placed into a REG_EQUAL note on the last insn. If TARGET is not a register, INSNS is simply emitted with no special processing. The final insn emitted is returned. */rtxemit_no_conflict_block (insns, target, op0, op1, equiv) rtx insns; rtx target; rtx op0, op1; rtx equiv;{ rtx prev, next, first, last, insn; if (GET_CODE (target) != REG || reload_in_progress) return emit_insns (insns); /* First emit all insns that do not store into words of the output and remove these from the list. */ for (insn = insns; insn; insn = next) { rtx set = 0; int i; next = NEXT_INSN (insn); if (GET_CODE (insn) != INSN) abort (); if (GET_CODE (PATTERN (insn)) == SET) set = PATTERN (insn); else if (GET_CODE (PATTERN (insn)) == PARALLEL) { for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET) { set = XVECEXP (PATTERN (insn), 0, i); break; } } if (set == 0) abort (); if (! reg_overlap_mentioned_p (target, SET_DEST (set))) { if (PREV_INSN (insn)) NEXT_INSN (PREV_INSN (insn)) = next; else insns = next; if (next) PREV_INSN (next) = PREV_INSN (insn); add_insn (insn); } } prev = get_last_insn (); /* Now write the CLOBBER of the output, followed by the setting of each of the words, followed by the final copy. */ if (target != op0 && target != op1) emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); for (insn = insns; insn; insn = next) { next = NEXT_INSN (insn); add_insn (insn); if (op1 && GET_CODE (op1) == REG) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op1, REG_NOTES (insn)); if (op0 && GET_CODE (op0) == REG) REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_NO_CONFLICT, op0, REG_NOTES (insn)); } last = emit_move_insn (target, target); if (equiv) REG_NOTES (last) = gen_rtx (EXPR_LIST, REG_EQUAL, equiv, REG_NOTES (last)); if (prev == 0) first = get_insns (); else first = NEXT_INSN (prev); /* Encapsulate the block so it gets manipulated as a unit. */ REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, REG_NOTES (first)); REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last)); return last;}/* Emit code to make a call to a constant function or a library call. INSNS is a list containing all insns emitted in the call. These insns leave the result in RESULT. Our block is to copy RESULT to TARGET, which is logically equivalent to EQUIV. We first emit any insns that set a pseudo on the assumption that these are loading constants into registers; doing so allows them to be safely cse'ed between blocks. Then we emit all the other insns in the block, followed by an insn to move RESULT to TARGET. This last insn will have a REQ_EQUAL note with an operand of EQUIV. Moving assignments to pseudos outside of the block is done to improve the generated code, but is not required to generate correct code, hence being unable to move an assignment is not grounds for not making a libcall block. There are two reasons why it is safe to leave these insns inside the block: First, we know that these pseudos cannot be used in generated RTL outside the block since they are created for temporary purposes within the block. Second, CSE will not record the values of anything set inside a libcall block, so we know they must be dead at the end of the block. Except for the first group of insns (the ones setting pseudos), the block is delimited by REG_RETVAL and REG_LIBCALL notes. */voidemit_libcall_block (insns, target, result, equiv) rtx insns; rtx target; rtx result; rtx equiv;{ rtx prev, next, first, last, insn; /* First emit all insns that set pseudos. Remove them from the list as we go. Avoid insns that set pseudo which were referenced in previous insns. These can be generated by move_by_pieces, for example, to update an address. */ for (insn = insns; insn; insn = next) { rtx set = single_set (insn); next = NEXT_INSN (insn); if (set != 0 && GET_CODE (SET_DEST (set)) == REG && REGNO (SET_DEST (set)) >= FIRST_PSEUDO_REGISTER && (insn == insns || (! reg_mentioned_p (SET_DEST (set), PATTERN (insns)) && ! reg_used_between_p (SET_DEST (set), insns, insn)))) { if (PREV_INSN (insn)) NEXT_INSN (PREV_INSN (insn)) = next; else insns = next; if (next) PREV_INSN (next) = PREV_INSN (insn); add_insn (insn); } } prev = get_last_insn (); /* Write the remaining insns followed by the final copy. */ for (insn = insns; insn; insn = next) { next = NEXT_INSN (insn); add_insn (insn); } last = emit_move_insn (target, result); REG_NOTES (last) = gen_rtx (EXPR_LIST, REG_EQUAL, equiv, REG_NOTES (last)); if (prev == 0) first = get_insns (); else first = NEXT_INSN (prev); /* Encapsulate the block so it gets manipulated as a unit. */ REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, REG_NOTES (first)); REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, REG_NOTES (last));}/* Generate code to store zero in X. */voidemit_clr_insn (x) rtx x;{ emit_move_insn (x, const0_rtx);}/* Generate code to store 1 in X assuming it contains zero beforehand. */voidemit_0_to_1_insn (x) rtx x;{ emit_move_insn (x, const1_rtx);}/* Generate code to compare X with Y so that the condition codes are set. MODE is the mode of the inputs (in case they are const_int). UNSIGNEDP nonzero says that X and Y are unsigned; this matters if they need to be widened. If they have mode BLKmode, then SIZE specifies the size of both X and Y, and ALIGN specifies the known shared alignment of X and Y. COMPARISON is the rtl operator to compare with (EQ, NE, GT, etc.). It is ignored for fixed-point and block comparisons; it is used only for floating-point comparisons. */voidemit_cmp_insn (x, y, comparison, size, mode, unsignedp, align) rtx x, y; enum rtx_code comparison; rtx size; enum machine_mode mode; int unsignedp; int align;{ enum mode_class class; enum machine_mode wider_mode; class = GET_MODE_CLASS (mode); /* They could both be VOIDmode if both args are immediate constants, but we should fold that at an earlier stage. With no special code here, this will call abort, reminding the programmer to implement such folding. */ if (mode != BLKmode && flag_force_mem) { x = force_not_mem (x); y = force_not_mem (y); } /* If we are inside an appropriately-short loop and one operand is an expensive constant, force it into a register. */ if (CONSTANT_P (x) && preserve_subexpressions_p () && rtx_cost (x, COMPARE) > 2) x = force_reg (mode, x); if (CONSTANT_P (y) && preserve_subexpressions_p () && rtx_cost (y, COMPARE) > 2) y = force_reg (mode, y); /* Don't let both operands fail to indicate the mode. */ if (GET_MODE (x) == VOIDmode && GET_MODE (y) == VOIDmode) x = force_reg (mode, x); /* Handle all BLKmode compares. */ if (mode == BLKmode) { emit_queue (); x = protect_from_queue (x, 0); y = protect_from_queue (y, 0); if (size == 0) abort ();#ifdef HAVE_cmpstrqi if (HAVE_cmpstrqi && GET_CODE (size) == CONST_INT && INTVAL (size) < (1 << GET_MODE_BITSIZE (QImode))) { enum machine_mode result_mode = insn_operand_mode[(int) CODE_FOR_cmpstrqi][0]; rtx result = gen_reg_rtx (result_mode); emit_insn (gen_cmpstrqi (result, x, y, size, GEN_INT (align))); emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, result_mode, 0, 0); } else#endif#ifdef HAVE_cmpstrhi if (HAVE_cmpstrhi && GET_CODE (size) == CONST_INT && INTVAL (size) < (1 << GET_MODE_BITSIZE (HImode))) { enum machine_mode result_mode = insn_operand_mode[(int) CODE_FOR_cmpstrhi][0]; rtx result = gen_reg_rtx (result_mode); emit_insn (gen_cmpstrhi (result, x, y, size, GEN_INT (align))); emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, result_mode, 0, 0); } else#endif#ifdef HAVE_cmpstrsi if (HAVE_cmpstrsi) { enum machine_mode result_mode = insn_operand_mode[(int) CODE_FOR_cmpstrsi][0]; rtx result = gen_reg_rtx (result_mode); size = protect_from_queue (size, 0); emit_insn (gen_cmpstrsi (result, x, y, convert_to_mode (SImode, size, 1), GEN_INT (align))); emit_cmp_insn (result, const0_rtx, comparison, NULL_RTX, result_mode, 0, 0); } else#endif {#ifdef TARGET_MEM_FUNCTIONS emit_library_call (memcmp_libfunc, 0, TYPE_MODE (integer_type_node), 3, XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, size, Pmode);#else emit_library_call (bcmp_libfunc, 0, TYPE_MODE (integer_type_node), 3, XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, size, Pmode);#endif emit_cmp_insn (hard_libcall_value (TYPE_MODE (integer_type_node)), const0_rtx, comparison, NULL_RTX, TYPE_MODE (integer_type_node), 0, 0); } return; } /* Handle some compares against zero. */ if (y == CONST0_RTX (mode) && tst_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { int icode = (int) tst_optab->handlers[(int) mode].insn_code; emit_queue (); x = protect_from_queue (x, 0); y = protect_from_queue (y, 0); /* Now, if insn does accept t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -