📄 i860.c
字号:
/* Subroutines for insn-output.c for Intel i860 Copyright (C) 1989, 1991, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Derived from sparc.c. Written by Richard Stallman (rms@ai.mit.edu). Hacked substantially by Ron Guilmette (rfg@netcom.com) to cater to the whims of the System V Release 4 assembler.This file is part of GCC.GCC 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 2, or (at your option)any later version.GCC 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 GCC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "config.h"#include "system.h"#include "coretypes.h"#include "tm.h"#include "flags.h"#include "rtl.h"#include "tree.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "output.h"#include "recog.h"#include "insn-attr.h"#include "function.h"#include "expr.h"#include "optabs.h"#include "toplev.h"#include "tm_p.h"#include "target.h"#include "target-def.h"#include "langhooks.h"#include "tree-gimple.h"static rtx find_addr_reg (rtx);#ifndef I860_REG_PREFIX#define I860_REG_PREFIX ""#endifconst char *i860_reg_prefix = I860_REG_PREFIX;/* Save information from a "cmpxx" operation until the branch is emitted. */rtx i860_compare_op0, i860_compare_op1;/* Return nonzero if this pattern, can be evaluated safely, even if it was not asked for. */intsafe_insn_src_p (rtx op, enum machine_mode mode){ /* Just experimenting. */ /* No floating point source 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 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 none of the above. */static intreg_clobbered_p (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) == 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 nonzero 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 (rtx op, rtx after){ /* 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 nonzero only if OP is a register of mode MODE, or const0_rtx. */intreg_or_0_operand (rtx op, enum machine_mode mode){ return (op == const0_rtx || register_operand (op, mode) || op == CONST0_RTX (mode));}/* 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 (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 (rtx op, enum machine_mode mode){ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && LOGIC_INT (op)));}/* Return 1 if OP is a valid first operand for a shift insn of mode MODE. */intshift_operand (rtx op, enum machine_mode mode){ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT));}/* Return 1 if OP is a valid first operand for either a logical insn or an add insn of mode MODE. */intcompare_operand (rtx op, enum machine_mode mode){ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && SMALL_INT (op) && LOGIC_INT (op)));}/* Return truth value of whether OP can be used as the 5-bit immediate operand of a bte or btne insn. */intbte_operand (rtx op, enum machine_mode mode){ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x20));}/* Return 1 if OP is an indexed memory reference of mode MODE. */intindexed_operand (rtx op, enum machine_mode mode){ return (GET_CODE (op) == MEM && GET_MODE (op) == mode && GET_CODE (XEXP (op, 0)) == PLUS && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (XEXP (op, 0), 0), SImode) && register_operand (XEXP (XEXP (op, 0), 1), SImode));}/* Return 1 if OP is a suitable source operand for a load insn with mode MODE. */intload_operand (rtx op, enum machine_mode mode){ return (memory_operand (op, mode) || indexed_operand (op, mode));}/* Return truth value of whether OP is an integer which fits the range constraining immediate operands in add/subtract insns. */intsmall_int (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED){ return (GET_CODE (op) == CONST_INT && SMALL_INT (op));}/* Return truth value of whether OP is an integer which fits the range constraining immediate operands in logic insns. */intlogic_int (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED){ return (GET_CODE (op) == CONST_INT && LOGIC_INT (op));}/* Test for a valid operand for a call instruction. Don't allow the arg pointer register or virtual regs since they may change into reg + const, which the patterns can't handle yet. */intcall_insn_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED){ if (GET_CODE (op) == MEM && (CONSTANT_ADDRESS_P (XEXP (op, 0)) || (GET_CODE (XEXP (op, 0)) == REG && XEXP (op, 0) != arg_pointer_rtx && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) return 1; return 0;}/* Return the best assembler insn template for moving operands[1] into operands[0] as a fullword. */static const char *singlemove_string (rtx *operands){ if (GET_CODE (operands[0]) == MEM) { if (GET_CODE (operands[1]) != MEM) if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) { if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) && (cc_prev_status.flags & CC_HI_R31_ADJ) && cc_prev_status.mdep == XEXP (operands[0], 0))) { CC_STATUS_INIT; output_asm_insn ("orh %h0,%?r0,%?r31", operands); } cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; cc_status.mdep = XEXP (operands[0], 0); return "st.l %r1,%L0(%?r31)"; } else return "st.l %r1,%0"; else abort ();#if 0 { rtx xoperands[2]; cc_status.flags &= ~CC_F0_IS_0; xoperands[0] = gen_rtx_REG (SFmode, 32); xoperands[1] = operands[1]; output_asm_insn (singlemove_string (xoperands), xoperands); xoperands[1] = xoperands[0]; xoperands[0] = operands[0]; output_asm_insn (singlemove_string (xoperands), xoperands); return ""; }#endif } if (GET_CODE (operands[1]) == MEM) { if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) { if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -