+/* Subroutines for insn-output.c for Intel 860
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ Derived from sparc.c.
+
+ Written by Richard Stallman (rms@ai.mit.edu).
+
+ Hacked substantially by Ron Guilmette (rfg@ncd.com) to cater
+ to the whims of the System V Release 4 assembler.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, 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 of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "config.h"
+#include "flags.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "recog.h"
+#include "insn-attr.h"
+
+#include <stdio.h>
+
+static rtx find_addr_reg ();
+
+#ifndef I860_REG_PREFIX
+#define I860_REG_PREFIX ""
+#endif
+
+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;
+\f
+/* Return non-zero if this pattern, can be evaluated safely, even if it
+ was not asked for. */
+int
+safe_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 int
+reg_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. */
+int
+operand_clobbered_before_used_after (op, 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 non-zero if this pattern, as a source to a "SET",
+ is known to yield an instruction of unit size. */
+int
+single_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;
+ }
+}