📄 out-i860.c
字号:
/* Subroutines for insn-output.c for Intel 860 Copyright (C) 1989 Free Software Foundation, Inc. Derived from out-sparc.c.This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 1, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *//* Global variables for machine-dependend things. *//* This should go away if we pass floats to regs via the stack instead of the frame, and if we learn how to renumber all the registers when we don't do a save (hard!). */extern int frame_pointer_needed;static rtx find_addr_reg ();/* Return non-zero only if OP is a register of mode MODE, or const0_rtx. */intreg_or_0_operand (op, mode) rtx op; enum machine_mode mode;{ return (op == const0_rtx || register_operand (op, mode) || op == CONST0_RTX (mode));}/* Return non-zero if this pattern, can be evaluated safely, even if it was not asked for. */intsafe_insn_src_p (op, mode) rtx op; enum machine_mode mode;{ /* Just experimenting. */ /* No floating point src is safe if it contains an arithmetic operation, since that operation may trap. */ switch (GET_CODE (op)) { case CONST_INT: case LABEL_REF: case SYMBOL_REF: case CONST: return 1; case REG: return 1; case MEM: return CONSTANT_ADDRESS_P (XEXP (op, 0)); /* We never need to negate or complement constants. */ case NEG: return (mode != SFmode && mode != DFmode); case NOT: case ZERO_EXTEND: return 1; case EQ: case NE: case LT: case GT: case LE: case GE: case LTU: case GTU: case LEU: case GEU: case MINUS: case PLUS: return (mode != SFmode && mode != DFmode); case AND: case IOR: case XOR: case LSHIFT: case ASHIFT: case ASHIFTRT: case LSHIFTRT: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) return 0; return 1; default: return 0; }}/* Return 1 if REG is clobbered in IN. Return 2 if REG is used in IN. Return 3 if REG is both used and clobbered in IN. Return 0 if neither. */static intreg_clobbered_p (reg, in) rtx reg; rtx in;{ register enum rtx_code code; if (in == 0) return 0; code = GET_CODE (in); if (code == SET || code == CLOBBER) { rtx dest = SET_DEST (in); int set = 0; int used = 0; while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG || GET_CODE (dest) == SIGN_EXTRACT || GET_CODE (dest) == ZERO_EXTRACT) dest = XEXP (dest, 0); if (dest == reg) set = 1; else if (GET_CODE (dest) == REG && refers_to_regno_p (REGNO (reg), REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), SET_DEST (in), 0)) { set = 1; /* Anything that sets just part of the register is considered using as well as setting it. But note that a straight SUBREG of a single-word value clobbers the entire value. */ if (dest != SET_DEST (in) && ! (GET_CODE (SET_DEST (in)) == SUBREG || UNITS_PER_WORD >= GET_MODE_SIZE (GET_MODE (dest)))) used = 1; } if (code == SET) { if (set) used = refers_to_regno_p (REGNO (reg), REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), SET_SRC (in), 0); else used = refers_to_regno_p (REGNO (reg), REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), in, 0); } return set + used * 2; } if (refers_to_regno_p (REGNO (reg), REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), in, 0)) return 2; return 0;}/* Return non-zero if OP can be written to without screwing up GCC's model of what's going on. It is assumed that this operand appears in the dest position of a SET insn in a conditional branch's delay slot. AFTER is the label to start looking from. */intoperand_clobbered_before_used_after (op, after) rtx op; rtx after;{ extern char call_used_regs[]; /* Just experimenting. */ if (GET_CODE (op) == CC0) return 1; if (GET_CODE (op) == REG) { rtx insn; if (op == stack_pointer_rtx) return 0; /* Scan forward from the label, to see if the value of OP is clobbered before the first use. */ for (insn = NEXT_INSN (after); insn; insn = NEXT_INSN (insn)) { if (GET_CODE (insn) == NOTE) continue; if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN) { switch (reg_clobbered_p (op, PATTERN (insn))) { default: return 0; case 1: return 1; case 0: break; } } /* If we reach another label without clobbering OP, then we cannot safely write it here. */ else if (GET_CODE (insn) == CODE_LABEL) return 0; if (GET_CODE (insn) == JUMP_INSN) { if (condjump_p (insn)) return 0; /* This is a jump insn which has already been mangled. We can't tell what it does. */ if (GET_CODE (PATTERN (insn)) == PARALLEL) return 0; if (! JUMP_LABEL (insn)) return 0; /* Keep following jumps. */ insn = JUMP_LABEL (insn); } } return 1; } /* In both of these cases, the first insn executed for this op will be a orh whatever%h,r0,r31, which is tolerable. */ if (GET_CODE (op) == MEM) return (CONSTANT_ADDRESS_P (XEXP (op, 0))); return 0;}/* Return non-zero if this pattern, as a source to a "SET", is known to yield an instruction of unit size. */intsingle_insn_src_p (op, mode) rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case CONST_INT: /* This is not always a single insn src, technically, but output_delayed_branch knows how to deal with it. */ return 1; case SYMBOL_REF: case CONST: /* This is not a single insn src, technically, but output_delayed_branch knows how to deal with it. */ return 1; case REG: return 1; case MEM: return 1; /* We never need to negate or complement constants. */ case NEG: return (mode != DFmode); case NOT: case ZERO_EXTEND: return 1; case PLUS: case MINUS: /* Detect cases that require multiple instructions. */ if (CONSTANT_P (XEXP (op, 1)) && !(GET_CODE (XEXP (op, 1)) == CONST_INT && SMALL_INT (XEXP (op, 1)))) return 0; case EQ: case NE: case LT: case GT: case LE: case GE: case LTU: case GTU: case LEU: case GEU: /* Not doing floating point, since they probably take longer than the branch slot they might fill. */ return (mode != SFmode && mode != DFmode); case AND: if (GET_CODE (XEXP (op, 1)) == NOT) { rtx arg = XEXP (XEXP (op, 1), 0); if (CONSTANT_P (arg) && !(GET_CODE (arg) == CONST_INT && (SMALL_INT (arg) || INTVAL (arg) & 0xffff == 0))) return 0; } case IOR: case XOR: /* Both small and round numbers take one instruction; others take two. */ if (CONSTANT_P (XEXP (op, 1)) && !(GET_CODE (XEXP (op, 1)) == CONST_INT && (SMALL_INT (XEXP (op, 1)) || INTVAL (XEXP (op, 1)) & 0xffff == 0))) return 0; case LSHIFT: case ASHIFT: case ASHIFTRT: case LSHIFTRT: return 1; case SUBREG: if (SUBREG_WORD (op) != 0) return 0; return single_insn_src_p (SUBREG_REG (op), mode); /* Not doing floating point, since they probably take longer than the branch slot they might fill. */ case FLOAT_EXTEND: case FLOAT_TRUNCATE: case FLOAT: case FIX: case UNSIGNED_FLOAT: case UNSIGNED_FIX: return 0; default: return 0; }}/* Nonzero only if this *really* is a single insn operand. */intstrict_single_insn_op_p (op, mode) rtx op; enum machine_mode mode;{ if (mode == VOIDmode) mode = GET_MODE (op); switch (GET_CODE (op)) { case CC0: return 1; case CONST_INT: if (SMALL_INT (op)) return 1; /* We can put this set insn into delay slot, because this is one insn; `orh'. */ if ((INTVAL (op) & 0xffff) == 0) return 1; return 0; case SYMBOL_REF: return 0; case REG:#if 0 /* This loses when moving an freg to a general reg. */ return HARD_REGNO_NREGS (REGNO (op), mode) == 1;#endif return (mode != DFmode && mode != DImode); case MEM: if (! CONSTANT_ADDRESS_P (XEXP (op, 0))) return (mode != DFmode && mode != DImode); return 0; /* We never need to negate or complement constants. */ case NEG: return (mode != DFmode); case NOT: case ZERO_EXTEND: return 1; case PLUS: case MINUS: /* Detect cases that require multiple instructions. */ if (CONSTANT_P (XEXP (op, 1)) && !(GET_CODE (XEXP (op, 1)) == CONST_INT && SMALL_INT (XEXP (op, 1)))) return 0; case EQ: case NE: case LT: case GT: case LE: case GE: case LTU: case GTU: case LEU: case GEU: return 1; case AND: if (GET_CODE (XEXP (op, 1)) == NOT) { rtx arg = XEXP (XEXP (op, 1), 0); if (CONSTANT_P (arg) && !(GET_CODE (arg) == CONST_INT && (SMALL_INT (arg) || INTVAL (arg) & 0xffff == 0))) return 0; } case IOR: case XOR: /* Both small and round numbers take one instruction; others take two. */ if (CONSTANT_P (XEXP (op, 1)) && !(GET_CODE (XEXP (op, 1)) == CONST_INT && (SMALL_INT (XEXP (op, 1)) || INTVAL (XEXP (op, 1)) & 0xffff == 0))) return 0; case LSHIFT: case ASHIFT: case ASHIFTRT: case LSHIFTRT: return 1; case SUBREG: if (SUBREG_WORD (op) != 0) return 0; return strict_single_insn_op_p (SUBREG_REG (op), mode); case SIGN_EXTEND: if (GET_CODE (XEXP (op, 0)) == MEM && ! CONSTANT_ADDRESS_P (XEXP (XEXP (op, 0), 0))) return 1; return 0; /* Not doing floating point, since they probably take longer than the branch slot they might fill. */ case FLOAT_EXTEND: case FLOAT_TRUNCATE: case FLOAT: case FIX: case UNSIGNED_FLOAT: case UNSIGNED_FIX: return 0; default: return 0; }}/* Return truth value of whether OP is a relational operator. */intrelop (op, mode) rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case EQ: case NE: case GT: case GE: case LT: case LE: case GTU: case GEU: case LTU: case LEU: return 1; } return 0;}/* Return truth value of whether OP can be used as an operands in a three address add/subtract insn (such as add %o1,7,%l2) of mode MODE. */intarith_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && SMALL_INT (op)));}/* Return 1 if OP is a valid first operand for a logical insn of mode MODE. */intlogic_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && LOGIC_INT (op)));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -