]> gcc.gnu.org Git - gcc.git/commitdiff
MAINTAINERS: Resurrect the i860 maintainer.
authorJason Eckhardt <jle@rice.edu>
Sat, 23 Aug 2003 02:02:18 +0000 (02:02 +0000)
committerJason Eckhardt <jle@gcc.gnu.org>
Sat, 23 Aug 2003 02:02:18 +0000 (02:02 +0000)
2003-08-22  Jason Eckhardt  <jle@rice.edu>

ChangeLog
* MAINTAINERS: Resurrect the i860 maintainer.

gcc/ChangeLog:
* gcc/config.gcc (i860-*-sysv4*): Add target.
* config/i860/i860-protos.h: New.
* config/i860/i860.c: New.
* config/i860/i860.h: New.
* config/i860/i860.md: New.
* config/i860/sysv4.h: New.
* config/i860/varargs.asm: New.
* config/i860/x-sysv4: New.

From-SVN: r70719

ChangeLog
MAINTAINERS
gcc/ChangeLog
gcc/config.gcc
gcc/config/i860/i860-protos.h [new file with mode: 0644]
gcc/config/i860/i860.c [new file with mode: 0644]
gcc/config/i860/i860.h [new file with mode: 0644]
gcc/config/i860/i860.md [new file with mode: 0644]
gcc/config/i860/sysv4.h [new file with mode: 0644]
gcc/config/i860/varargs.asm [new file with mode: 0644]
gcc/config/i860/x-sysv4 [new file with mode: 0644]

index cc1f34cbfea944c696b48f5af37f8c7586d22848..a46f68d9e7b95e73a82b02cb13a41c614b7bab54 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2003-08-22  Jason Eckhardt  <jle@rice.edu>
+
+       * MAINTAINERS: Resurrect the i860 maintainer.
+
 2003-08-20  Geoffrey Keating  <geoffk@apple.com>
 
        PR 8180
index 63e6b1d24ca21f128f93c3da82890df1aaef38a2..1cdcb8b5a46f0a7502639b86d2bbe2cd5afb6f08 100644 (file)
@@ -49,6 +49,7 @@ h8 port                       Kazu Hirata             kazu@cs.umass.edu
 hppa port              Jeff Law                law@redhat.com
 hppa port              Dave Anglin             dave.anglin@nrc.ca
 i386 port              Richard Henderson       rth@redhat.com
+i860 port              Jason Eckhardt          jle@rice.edu
 i960 port              Jim Wilson              wilson@tuliptree.org
 ia64 port              Jim Wilson              wilson@tuliptree.org
 ip2k port              Denis Chertykov         denisc@overta.ru
index 0aa8268e3455168424c4a3b867392182859b0408..55fbc0ab134e593439481a7896059dfcc53e2093 100644 (file)
@@ -1,3 +1,14 @@
+2003-08-22  Jason Eckhardt  <jle@rice.edu>
+
+       * config.gcc (i860-*-sysv4*): Add target.
+       * config/i860/i860-protos.h: New.
+       * config/i860/i860.c: New.
+       * config/i860/i860.h: New.
+       * config/i860/i860.md: New.
+       * config/i860/sysv4.h: New.
+       * config/i860/varargs.asm: New.
+       * config/i860/x-sysv4: New.
+
 2003-08-22  Jason Eckhardt  <jle@rice.edu>
 
        * config/pa/pa.c: Replace 'GNU CC' with 'GCC'.
index 4dd37aa1dca49c57cb51adcc0867c2995fba6cd8..e98544d465f007627aea2e2cee91f795821c8b05 100644 (file)
@@ -1235,6 +1235,13 @@ i[34567]86-*-kaos*)
        tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h i386/i386elf.h kaos.h i386/kaos-i386.h"
        tmake_file="i386/t-i386elf t-svr4"
        ;;
+i860-*-sysv4*)
+        tm_file="${tm_file} elfos.h svr4.h i860/sysv4.h"
+        xm_defines="USG SVR3"
+        xmake_file=i860/x-sysv4
+        tmake_file=t-svr4
+        extra_parts="crtbegin.o crtend.o"
+        ;;
 i960-*-coff*)
        tm_file="${tm_file} dbxcoff.h i960/i960-coff.h libgloss.h"
        tmake_file=i960/t-960bare
diff --git a/gcc/config/i860/i860-protos.h b/gcc/config/i860/i860-protos.h
new file mode 100644 (file)
index 0000000..42cbe18
--- /dev/null
@@ -0,0 +1,61 @@
+/* Definitions of target machine for GNU compiler, for Intel 860.
+   Copyright (C) 2000 Free Software Foundation, Inc.
+   Hacked substantially by Ron Guilmette (rfg@monkeys.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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Declare things which are defined in i860.c but called from
+   insn-output.c.  */
+
+#ifdef RTX_CODE
+extern unsigned long sfmode_constant_to_ulong PARAMS ((rtx));
+extern const char *output_load PARAMS ((rtx *));
+extern const char *output_store PARAMS ((rtx *));
+extern const char *output_move_double PARAMS ((rtx *));
+extern const char *output_fp_move_double PARAMS ((rtx *));
+extern const char *output_block_move PARAMS ((rtx *));
+extern const char *output_delay_insn PARAMS ((rtx));
+#if 0
+extern const char *output_delayed_branch PARAMS ((const char *, rtx *, rtx));
+#endif
+extern void output_load_address PARAMS ((rtx *));
+extern int safe_insn_src_p PARAMS ((rtx, enum machine_mode));
+extern int operand_clobbered_before_used_after PARAMS ((rtx, rtx));
+extern int single_insn_src_p PARAMS ((rtx, enum machine_mode));
+extern int reg_or_0_operand PARAMS ((rtx, enum machine_mode));
+extern int arith_operand PARAMS ((rtx, enum machine_mode));
+extern int logic_operand PARAMS ((rtx, enum machine_mode));
+extern int shift_operand PARAMS ((rtx, enum machine_mode));
+extern int compare_operand PARAMS ((rtx, enum machine_mode));
+extern int bte_operand PARAMS ((rtx, enum machine_mode));
+extern int indexed_operand PARAMS ((rtx, enum machine_mode));
+extern int load_operand PARAMS ((rtx, enum machine_mode));
+extern int small_int PARAMS ((rtx, enum machine_mode));
+extern int logic_int PARAMS ((rtx, enum machine_mode));
+extern int call_insn_operand PARAMS ((rtx, enum machine_mode));
+extern rtx i860_saveregs PARAMS ((void));
+#ifdef TREE_CODE
+extern void i860_va_start PARAMS ((int, tree, rtx));
+extern rtx i860_va_arg PARAMS ((tree, tree));
+#endif /* TREE_CODE */
+#endif /* RTX_CODE */
+
+#ifdef TREE_CODE
+extern tree i860_build_va_list PARAMS ((void));
+#endif /* TREE_CODE */
diff --git a/gcc/config/i860/i860.c b/gcc/config/i860/i860.c
new file mode 100644 (file)
index 0000000..1849397
--- /dev/null
@@ -0,0 +1,2361 @@
+/* Subroutines for insn-output.c for Intel 860
+   Copyright (C) 1989, 1991, 1997, 1998, 1999, 2000, 2001, 2002
+   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 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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+
+#include "config.h"
+#include "system.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 "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+
+static rtx find_addr_reg PARAMS ((rtx));
+static int reg_clobbered_p PARAMS ((rtx, rtx));
+static const char *singlemove_string PARAMS ((rtx *));
+static const char *load_opcode PARAMS ((enum machine_mode, const char *, rtx));
+static const char *store_opcode PARAMS ((enum machine_mode, const char *, rtx));
+static void output_size_for_block_move PARAMS ((rtx, rtx, rtx));
+static void i860_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
+static void i860_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+
+#ifndef I860_REG_PREFIX
+#define I860_REG_PREFIX ""
+#endif
+
+const 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
+/* Initialize the GCC target structure.  */
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE i860_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE i860_output_function_epilogue
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\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 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 ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      return 1;
+
+    case SUBREG:
+      if (SUBREG_BYTE (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;
+    }
+}
+\f
+/* Return non-zero only if OP is a register of mode MODE,
+   or const0_rtx.  */
+int
+reg_or_0_operand (op, mode)
+     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.  */
+
+int
+arith_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.  */
+
+int
+logic_operand (op, mode)
+     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.  */
+
+int
+shift_operand (op, mode)
+     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.  */
+
+int
+compare_operand (op, mode)
+     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.  */
+
+int
+bte_operand (op, mode)
+     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.  */
+
+int
+indexed_operand (op, mode)
+     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.  */
+
+int
+load_operand (op, mode)
+     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.  */
+
+int
+small_int (op, mode)
+     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.  */
+
+int
+logic_int (op, mode)
+     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.  */
+
+int
+call_insn_operand (op, mode)
+     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;
+}
+\f
+/* Return the best assembler insn template
+   for moving operands[1] into operands[0] as a fullword.  */
+
+static const char *
+singlemove_string (operands)
+     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)
+                && (cc_prev_status.flags & CC_HI_R31_ADJ)
+                && cc_prev_status.mdep == XEXP (operands[1], 0)))
+           {
+             CC_STATUS_INIT;
+             output_asm_insn ("orh %h1,%?r0,%?r31", operands);
+           }
+         cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+         cc_status.mdep = XEXP (operands[1], 0);
+         return "ld.l %L1(%?r31),%0";
+       }
+      return "ld.l %m1,%0";
+    }
+ if (GET_CODE (operands[1]) == CONST_INT)
+   {
+     if (operands[1] == const0_rtx)
+      return "mov %?r0,%0";
+     if((INTVAL (operands[1]) & 0xffff0000) == 0)
+      return "or %L1,%?r0,%0";
+     if((INTVAL (operands[1]) & 0xffff8000) == 0xffff8000)
+      return "adds %1,%?r0,%0";
+     if((INTVAL (operands[1]) & 0x0000ffff) == 0)
+      return "orh %H1,%?r0,%0";
+
+     return "orh %H1,%?r0,%0\n\tor %L1,%0,%0";
+   }
+  return "mov %1,%0";
+}
+\f
+/* Output assembler code to perform a doubleword move insn
+   with operands OPERANDS.  */
+
+const char *
+output_move_double (operands)
+     rtx *operands;
+{
+  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
+  rtx latehalf[2];
+  rtx addreg0 = 0, addreg1 = 0;
+  int highest_first = 0;
+  int no_addreg1_decrement = 0;
+
+  /* First classify both operands.  */
+
+  if (REG_P (operands[0]))
+    optype0 = REGOP;
+  else if (offsettable_memref_p (operands[0]))
+    optype0 = OFFSOP;
+  else if (GET_CODE (operands[0]) == MEM)
+    optype0 = MEMOP;
+  else
+    optype0 = RNDOP;
+
+  if (REG_P (operands[1]))
+    optype1 = REGOP;
+  else if (CONSTANT_P (operands[1]))
+    optype1 = CNSTOP;
+  else if (offsettable_memref_p (operands[1]))
+    optype1 = OFFSOP;
+  else if (GET_CODE (operands[1]) == MEM)
+    optype1 = MEMOP;
+  else
+    optype1 = RNDOP;
+
+  /* Check for the cases that the operand constraints are not
+     supposed to allow to happen.  Abort if we get one,
+     because generating code for these cases is painful.  */
+
+  if (optype0 == RNDOP || optype1 == RNDOP)
+    abort ();
+
+  /* If an operand is an unoffsettable memory ref, find a register
+     we can increment temporarily to make it refer to the second word.  */
+
+  if (optype0 == MEMOP)
+    addreg0 = find_addr_reg (XEXP (operands[0], 0));
+
+  if (optype1 == MEMOP)
+    addreg1 = find_addr_reg (XEXP (operands[1], 0));
+
+/* ??? Perhaps in some cases move double words
+   if there is a spare pair of floating regs.  */
+
+  /* Ok, we can do one word at a time.
+     Normally we do the low-numbered word first,
+     but if either operand is autodecrementing then we
+     do the high-numbered word first.
+
+     In either case, set up in LATEHALF the operands to use
+     for the high-numbered word and in some cases alter the
+     operands in OPERANDS to be suitable for the low-numbered word.  */
+
+  if (optype0 == REGOP)
+    latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
+  else if (optype0 == OFFSOP)
+    latehalf[0] = adjust_address (operands[0], SImode, 4);
+  else
+    latehalf[0] = operands[0];
+
+  if (optype1 == REGOP)
+    latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
+  else if (optype1 == OFFSOP)
+    latehalf[1] = adjust_address (operands[1], SImode, 4);
+  else if (optype1 == CNSTOP)
+    {
+      if (GET_CODE (operands[1]) == CONST_DOUBLE)
+       split_double (operands[1], &operands[1], &latehalf[1]);
+      else if (CONSTANT_P (operands[1]))
+       latehalf[1] = const0_rtx;
+    }
+  else
+    latehalf[1] = operands[1];
+
+  /* If the first move would clobber the source of the second one,
+     do them in the other order.
+
+     RMS says "This happens only for registers;
+     such overlap can't happen in memory unless the user explicitly
+     sets it up, and that is an undefined circumstance."
+
+     but it happens on the sparc when loading parameter registers,
+     so I am going to define that circumstance, and make it work
+     as expected.  */
+
+  if (optype0 == REGOP && optype1 == REGOP
+      && REGNO (operands[0]) == REGNO (latehalf[1]))
+    {
+      CC_STATUS_PARTIAL_INIT;
+      /* Make any unoffsettable addresses point at high-numbered word.  */
+      if (addreg0)
+       output_asm_insn ("adds 0x4,%0,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("adds 0x4,%0,%0", &addreg1);
+
+      /* Do that word.  */
+      output_asm_insn (singlemove_string (latehalf), latehalf);
+
+      /* Undo the adds we just did.  */
+      if (addreg0)
+       output_asm_insn ("adds -0x4,%0,%0", &addreg0);
+      if (addreg1)
+       output_asm_insn ("adds -0x4,%0,%0", &addreg1);
+
+      /* Do low-numbered word.  */
+      return singlemove_string (operands);
+    }
+  else if (optype0 == REGOP && optype1 != REGOP
+          && reg_overlap_mentioned_p (operands[0], operands[1]))
+    {
+      /* If both halves of dest are used in the src memory address,
+        add the two regs and put them in the low reg (operands[0]).
+        Then it works to load latehalf first.  */
+      if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
+         && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+       {
+         rtx xops[2];
+         xops[0] = latehalf[0];
+         xops[1] = operands[0];
+         output_asm_insn ("adds %1,%0,%1", xops);
+         operands[1] = gen_rtx_MEM (DImode, operands[0]);
+         latehalf[1] = adjust_address (operands[1], SImode, 4);
+         addreg1 = 0;
+         highest_first = 1;
+       }
+      /* Only one register in the dest is used in the src memory address,
+        and this is the first register of the dest, so we want to do
+        the late half first here also.  */
+      else if (! reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
+       highest_first = 1;
+      /* Only one register in the dest is used in the src memory address,
+        and this is the second register of the dest, so we want to do
+        the late half last.  If addreg1 is set, and addreg1 is the same
+        register as latehalf, then we must suppress the trailing decrement,
+        because it would clobber the value just loaded.  */
+      else if (addreg1 && reg_mentioned_p (addreg1, latehalf[0]))
+       no_addreg1_decrement = 1;
+    }
+
+  /* Normal case: do the two words, low-numbered first.
+     Overlap case (highest_first set): do high-numbered word first.  */
+
+  if (! highest_first)
+    output_asm_insn (singlemove_string (operands), operands);
+
+  CC_STATUS_PARTIAL_INIT;
+  /* Make any unoffsettable addresses point at high-numbered word.  */
+  if (addreg0)
+    output_asm_insn ("adds 0x4,%0,%0", &addreg0);
+  if (addreg1)
+    output_asm_insn ("adds 0x4,%0,%0", &addreg1);
+
+  /* Do that word.  */
+  output_asm_insn (singlemove_string (latehalf), latehalf);
+
+  /* Undo the adds we just did.  */
+  if (addreg0)
+    output_asm_insn ("adds -0x4,%0,%0", &addreg0);
+  if (addreg1 && !no_addreg1_decrement)
+    output_asm_insn ("adds -0x4,%0,%0", &addreg1);
+
+  if (highest_first)
+    output_asm_insn (singlemove_string (operands), operands);
+
+  return "";
+}
+\f
+const char *
+output_fp_move_double (operands)
+     rtx *operands;
+{
+  /* If the source operand is any sort of zero, use f0 instead.  */
+
+  if (operands[1] == CONST0_RTX (GET_MODE (operands[1])))
+    operands[1] = gen_rtx_REG (DFmode, F0_REGNUM);
+
+  if (FP_REG_P (operands[0]))
+    {
+      if (FP_REG_P (operands[1]))
+       return "fmov.dd %1,%0";
+      if (GET_CODE (operands[1]) == REG)
+       {
+         output_asm_insn ("ixfr %1,%0", operands);
+         operands[0] = gen_rtx_REG (VOIDmode, REGNO (operands[0]) + 1);
+         operands[1] = gen_rtx_REG (VOIDmode, REGNO (operands[1]) + 1);
+         return "ixfr %1,%0";
+       }
+      if (operands[1] == CONST0_RTX (DFmode))
+       return "fmov.dd f0,%0";
+      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+       {
+         if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
+                && (cc_prev_status.flags & CC_HI_R31_ADJ)
+                && cc_prev_status.mdep == XEXP (operands[1], 0)))
+           {
+             CC_STATUS_INIT;
+             output_asm_insn ("orh %h1,%?r0,%?r31", operands);
+           }
+         cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+         cc_status.mdep = XEXP (operands[1], 0);
+         return "fld.d %L1(%?r31),%0";
+       }
+      return "fld.d %1,%0";
+    }
+  else if (FP_REG_P (operands[1]))
+    {
+      if (GET_CODE (operands[0]) == REG)
+       {
+         output_asm_insn ("fxfr %1,%0", operands);
+         operands[0] = gen_rtx_REG (VOIDmode, REGNO (operands[0]) + 1);
+         operands[1] = gen_rtx_REG (VOIDmode, REGNO (operands[1]) + 1);
+         return "fxfr %1,%0";
+       }
+      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 "fst.d %1,%L0(%?r31)";
+       }
+      return "fst.d %1,%0";
+    }
+  else
+    abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+\f
+/* Return a REG that occurs in ADDR with coefficient 1.
+   ADDR can be effectively incremented by incrementing REG.  */
+
+static rtx
+find_addr_reg (addr)
+     rtx addr;
+{
+  while (GET_CODE (addr) == PLUS)
+    {
+      if (GET_CODE (XEXP (addr, 0)) == REG)
+       addr = XEXP (addr, 0);
+      else if (GET_CODE (XEXP (addr, 1)) == REG)
+       addr = XEXP (addr, 1);
+      else if (CONSTANT_P (XEXP (addr, 0)))
+       addr = XEXP (addr, 1);
+      else if (CONSTANT_P (XEXP (addr, 1)))
+       addr = XEXP (addr, 0);
+      else
+       abort ();
+    }
+  if (GET_CODE (addr) == REG)
+    return addr;
+  abort ();
+  /* NOTREACHED */
+  return NULL;
+}
+
+/* Return a template for a load instruction with mode MODE and
+   arguments from the string ARGS.
+
+   This string is in static storage.   */
+
+static const char *
+load_opcode (mode, args, reg)
+     enum machine_mode mode;
+     const char *args;
+     rtx reg;
+{
+  static char buf[30];
+  const char *opcode;
+
+  switch (mode)
+    {
+    case QImode:
+      opcode = "ld.b";
+      break;
+
+    case HImode:
+      opcode = "ld.s";
+      break;
+
+    case SImode:
+    case SFmode:
+      if (FP_REG_P (reg))
+       opcode = "fld.l";
+      else
+       opcode = "ld.l";
+      break;
+
+    case DImode:
+      if (!FP_REG_P (reg))
+       abort ();
+    case DFmode:
+      opcode = "fld.d";
+      break;
+
+    default:
+      abort ();
+    }
+
+  sprintf (buf, "%s %s", opcode, args);
+  return buf;
+}
+
+/* Return a template for a store instruction with mode MODE and
+   arguments from the string ARGS.
+
+   This string is in static storage.   */
+
+static const char *
+store_opcode (mode, args, reg)
+     enum machine_mode mode;
+     const char *args;
+     rtx reg;
+{
+  static char buf[30];
+  const char *opcode;
+
+  switch (mode)
+    {
+    case QImode:
+      opcode = "st.b";
+      break;
+
+    case HImode:
+      opcode = "st.s";
+      break;
+
+    case SImode:
+    case SFmode:
+      if (FP_REG_P (reg))
+       opcode = "fst.l";
+      else
+       opcode = "st.l";
+      break;
+
+    case DImode:
+      if (!FP_REG_P (reg))
+       abort ();
+    case DFmode:
+      opcode = "fst.d";
+      break;
+
+    default:
+      abort ();
+    }
+
+  sprintf (buf, "%s %s", opcode, args);
+  return buf;
+}
+\f
+/* Output a store-in-memory whose operands are OPERANDS[0,1].
+   OPERANDS[0] is a MEM, and OPERANDS[1] is a reg or zero.
+
+   This function returns a template for an insn.
+   This is in static storage.
+
+   It may also output some insns directly.
+   It may alter the values of operands[0] and operands[1].  */
+
+const char *
+output_store (operands)
+     rtx *operands;
+{
+  enum machine_mode mode = GET_MODE (operands[0]);
+  rtx address = XEXP (operands[0], 0);
+
+  cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+  cc_status.mdep = address;
+
+  if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
+        && (cc_prev_status.flags & CC_HI_R31_ADJ)
+        && address == cc_prev_status.mdep))
+    {
+      CC_STATUS_INIT;
+      output_asm_insn ("orh %h0,%?r0,%?r31", operands);
+      cc_prev_status.mdep = address;
+    }
+
+  /* Store zero in two parts when appropriate.  */
+  if (mode == DFmode && operands[1] == CONST0_RTX (DFmode))
+    return store_opcode (DFmode, "%r1,%L0(%?r31)", operands[1]);
+
+  /* Code below isn't smart enough to move a doubleword in two parts,
+     so use output_move_double to do that in the cases that require it.  */
+  if ((mode == DImode || mode == DFmode)
+      && ! FP_REG_P (operands[1]))
+    return output_move_double (operands);
+
+  return store_opcode (mode, "%r1,%L0(%?r31)", operands[1]);
+}
+
+/* Output a load-from-memory whose operands are OPERANDS[0,1].
+   OPERANDS[0] is a reg, and OPERANDS[1] is a mem.
+
+   This function returns a template for an insn.
+   This is in static storage.
+
+   It may also output some insns directly.
+   It may alter the values of operands[0] and operands[1].  */
+
+const char *
+output_load (operands)
+     rtx *operands;
+{
+  enum machine_mode mode = GET_MODE (operands[0]);
+  rtx address = XEXP (operands[1], 0);
+
+  /* We don't bother trying to see if we know %hi(address).
+     This is because we are doing a load, and if we know the
+     %hi value, we probably also know that value in memory.  */
+  cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+  cc_status.mdep = address;
+
+  if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
+        && (cc_prev_status.flags & CC_HI_R31_ADJ)
+        && address == cc_prev_status.mdep
+        && cc_prev_status.mdep == cc_status.mdep))
+    {
+      CC_STATUS_INIT;
+      output_asm_insn ("orh %h1,%?r0,%?r31", operands);
+      cc_prev_status.mdep = address;
+    }
+
+  /* Code below isn't smart enough to move a doubleword in two parts,
+     so use output_move_double to do that in the cases that require it.  */
+  if ((mode == DImode || mode == DFmode)
+      && ! FP_REG_P (operands[0]))
+    return output_move_double (operands);
+
+  return load_opcode (mode, "%L1(%?r31),%0", operands[0]);
+}
+\f
+#if 0
+/* Load the address specified by OPERANDS[3] into the register
+   specified by OPERANDS[0].
+
+   OPERANDS[3] may be the result of a sum, hence it could either be:
+
+   (1) CONST
+   (2) REG
+   (2) REG + CONST_INT
+   (3) REG + REG + CONST_INT
+   (4) REG + REG  (special case of 3).
+
+   Note that (3) is not a legitimate address.
+   All cases are handled here.  */
+
+void
+output_load_address (operands)
+     rtx *operands;
+{
+  rtx base, offset;
+
+  if (CONSTANT_P (operands[3]))
+    {
+      output_asm_insn ("mov %3,%0", operands);
+      return;
+    }
+
+  if (REG_P (operands[3]))
+    {
+      if (REGNO (operands[0]) != REGNO (operands[3]))
+       output_asm_insn ("shl %?r0,%3,%0", operands);
+      return;
+    }
+
+  if (GET_CODE (operands[3]) != PLUS)
+    abort ();
+
+  base = XEXP (operands[3], 0);
+  offset = XEXP (operands[3], 1);
+
+  if (GET_CODE (base) == CONST_INT)
+    {
+      rtx tmp = base;
+      base = offset;
+      offset = tmp;
+    }
+
+  if (GET_CODE (offset) != CONST_INT)
+    {
+      /* Operand is (PLUS (REG) (REG)).  */
+      base = operands[3];
+      offset = const0_rtx;
+    }
+
+  if (REG_P (base))
+    {
+      operands[6] = base;
+      operands[7] = offset;
+      CC_STATUS_PARTIAL_INIT;
+      if (SMALL_INT (offset))
+       output_asm_insn ("adds %7,%6,%0", operands);
+      else
+       output_asm_insn ("mov %7,%0\n\tadds %0,%6,%0", operands);
+    }
+  else if (GET_CODE (base) == PLUS)
+    {
+      operands[6] = XEXP (base, 0);
+      operands[7] = XEXP (base, 1);
+      operands[8] = offset;
+
+      CC_STATUS_PARTIAL_INIT;
+      if (SMALL_INT (offset))
+       output_asm_insn ("adds %6,%7,%0\n\tadds %8,%0,%0", operands);
+      else
+       output_asm_insn ("mov %8,%0\n\tadds %0,%6,%0\n\tadds %0,%7,%0", operands);
+    }
+  else
+    abort ();
+}
+#endif
+
+/* Output code to place a size count SIZE in register REG.
+   Because block moves are pipelined, we don't include the
+   first element in the transfer of SIZE to REG.
+   For this, we subtract ALIGN.  (Actually, I think it is not
+   right to subtract on this machine, so right now we don't.)  */
+
+static void
+output_size_for_block_move (size, reg, align)
+     rtx size, reg, align;
+{
+  rtx xoperands[3];
+
+  xoperands[0] = reg;
+  xoperands[1] = size;
+  xoperands[2] = align;
+
+#if 1
+  cc_status.flags &= ~ CC_KNOW_HI_R31;
+  output_asm_insn (singlemove_string (xoperands), xoperands);
+#else
+  if (GET_CODE (size) == REG)
+    output_asm_insn ("sub %2,%1,%0", xoperands);
+  else
+    {
+      xoperands[1] = GEN_INT (INTVAL (size) - INTVAL (align));
+      cc_status.flags &= ~ CC_KNOW_HI_R31;
+      output_asm_insn ("mov %1,%0", xoperands);
+    }
+#endif
+}
+
+/* Emit code to perform a block move.
+
+   OPERANDS[0] is the destination.
+   OPERANDS[1] is the source.
+   OPERANDS[2] is the size.
+   OPERANDS[3] is the known safe alignment.
+   OPERANDS[4..6] are pseudos we can safely clobber as temps.  */
+
+const char *
+output_block_move (operands)
+     rtx *operands;
+{
+  /* A vector for our computed operands.  Note that load_output_address
+     makes use of (and can clobber) up to the 8th element of this vector.  */
+  rtx xoperands[10];
+#if 0
+  rtx zoperands[10];
+#endif
+  static int movstrsi_label = 0;
+  int i;
+  rtx temp1 = operands[4];
+  rtx alignrtx = operands[3];
+  int align = INTVAL (alignrtx);
+  int chunk_size;
+
+  xoperands[0] = operands[0];
+  xoperands[1] = operands[1];
+  xoperands[2] = temp1;
+
+  /* We can't move more than four bytes at a time
+     because we have only one register to move them through.  */
+  if (align > 4)
+    {
+      align = 4;
+      alignrtx = GEN_INT (4);
+    }
+
+  /* Recognize special cases of block moves.  These occur
+     when GNU C++ is forced to treat something as BLKmode
+     to keep it in memory, when its mode could be represented
+     with something smaller.
+
+     We cannot do this for global variables, since we don't know
+     what pages they don't cross.  Sigh.  */
+  if (GET_CODE (operands[2]) == CONST_INT
+      && ! CONSTANT_ADDRESS_P (operands[0])
+      && ! CONSTANT_ADDRESS_P (operands[1]))
+    {
+      int size = INTVAL (operands[2]);
+      rtx op0 = xoperands[0];
+      rtx op1 = xoperands[1];
+
+      if ((align & 3) == 0 && (size & 3) == 0 && (size >> 2) <= 16)
+       {
+         if (memory_address_p (SImode, plus_constant (op0, size))
+             && memory_address_p (SImode, plus_constant (op1, size)))
+           {
+             cc_status.flags &= ~CC_KNOW_HI_R31;
+             for (i = (size>>2)-1; i >= 0; i--)
+               {
+                 xoperands[0] = plus_constant (op0, i * 4);
+                 xoperands[1] = plus_constant (op1, i * 4);
+                 output_asm_insn ("ld.l %a1,%?r31\n\tst.l %?r31,%a0",
+                                  xoperands);
+               }
+             return "";
+           }
+       }
+      else if ((align & 1) == 0 && (size & 1) == 0 && (size >> 1) <= 16)
+       {
+         if (memory_address_p (HImode, plus_constant (op0, size))
+             && memory_address_p (HImode, plus_constant (op1, size)))
+           {
+             cc_status.flags &= ~CC_KNOW_HI_R31;
+             for (i = (size>>1)-1; i >= 0; i--)
+               {
+                 xoperands[0] = plus_constant (op0, i * 2);
+                 xoperands[1] = plus_constant (op1, i * 2);
+                 output_asm_insn ("ld.s %a1,%?r31\n\tst.s %?r31,%a0",
+                                  xoperands);
+               }
+             return "";
+           }
+       }
+      else if (size <= 16)
+       {
+         if (memory_address_p (QImode, plus_constant (op0, size))
+             && memory_address_p (QImode, plus_constant (op1, size)))
+           {
+             cc_status.flags &= ~CC_KNOW_HI_R31;
+             for (i = size-1; i >= 0; i--)
+               {
+                 xoperands[0] = plus_constant (op0, i);
+                 xoperands[1] = plus_constant (op1, i);
+                 output_asm_insn ("ld.b %a1,%?r31\n\tst.b %?r31,%a0",
+                                  xoperands);
+               }
+             return "";
+           }
+       }
+    }
+
+  /* Since we clobber untold things, nix the condition codes.  */
+  CC_STATUS_INIT;
+
+  /* This is the size of the transfer.
+     Either use the register which already contains the size,
+     or use a free register (used by no operands).  */
+  output_size_for_block_move (operands[2], operands[4], alignrtx);
+
+#if 0
+  /* Also emit code to decrement the size value by ALIGN.  */
+  zoperands[0] = operands[0];
+  zoperands[3] = plus_constant (operands[0], align);
+  output_load_address (zoperands);
+#endif
+
+  /* Generate number for unique label.  */
+
+  xoperands[3] = GEN_INT (movstrsi_label++);
+
+  /* Calculate the size of the chunks we will be trying to move first.  */
+
+#if 0
+  if ((align & 3) == 0)
+    chunk_size = 4;
+  else if ((align & 1) == 0)
+    chunk_size = 2;
+  else
+#endif
+    chunk_size = 1;
+
+  /* Copy the increment (negative) to a register for bla insn.  */
+
+  xoperands[4] = GEN_INT (- chunk_size);
+  xoperands[5] = operands[5];
+  output_asm_insn ("adds %4,%?r0,%5", xoperands);
+
+  /* Predecrement the loop counter.  This happens again also in the `bla'
+     instruction which precedes the loop, but we need to have it done
+     two times before we enter the loop because of the bizarre semantics
+     of the bla instruction.  */
+
+  output_asm_insn ("adds %5,%2,%2", xoperands);
+
+  /* Check for the case where the original count was less than or equal to
+     zero.  Avoid going through the loop at all if the original count was
+     indeed less than or equal to zero.  Note that we treat the count as
+     if it were a signed 32-bit quantity here, rather than an unsigned one,
+     even though we really shouldn't.  We have to do this because of the
+     semantics of the `ble' instruction, which assume that the count is
+     a signed 32-bit value.  Anyway, in practice it won't matter because
+     nobody is going to try to do a memcpy() of more than half of the
+     entire address space (i.e. 2 gigabytes) anyway.  */
+
+  output_asm_insn ("bc .Le%3", xoperands);
+
+  /* Make available a register which is a temporary.  */
+
+  xoperands[6] = operands[6];
+
+  /* Now the actual loop.
+     In xoperands, elements 1 and 0 are the input and output vectors.
+     Element 2 is the loop index.  Element 5 is the increment.  */
+
+  output_asm_insn ("subs %1,%5,%1", xoperands);
+  output_asm_insn ("bla %5,%2,.Lm%3", xoperands);
+  output_asm_insn ("adds %0,%2,%6", xoperands);
+  output_asm_insn ("\n.Lm%3:", xoperands);         /* Label for bla above.  */
+  output_asm_insn ("\n.Ls%3:",  xoperands);        /* Loop start label. */
+  output_asm_insn ("adds %5,%6,%6", xoperands);
+
+  /* NOTE:  The code here which is supposed to handle the cases where the
+     sources and destinations are known to start on a 4 or 2 byte boundary
+     are currently broken.  They fail to do anything about the overflow
+     bytes which might still need to be copied even after we have copied
+     some number of words or halfwords.  Thus, for now we use the lowest
+     common denominator, i.e. the code which just copies some number of
+     totally unaligned individual bytes.  (See the calculation of
+     chunk_size above.  */
+
+  if (chunk_size == 4)
+    {
+      output_asm_insn ("ld.l %2(%1),%?r31", xoperands);
+      output_asm_insn ("bla %5,%2,.Ls%3", xoperands);
+      output_asm_insn ("st.l %?r31,8(%6)", xoperands);
+    }
+  else if (chunk_size == 2)
+    {
+      output_asm_insn ("ld.s %2(%1),%?r31", xoperands);
+      output_asm_insn ("bla %5,%2,.Ls%3", xoperands);
+      output_asm_insn ("st.s %?r31,4(%6)", xoperands);
+    }
+  else /* chunk_size == 1 */
+    {
+      output_asm_insn ("ld.b %2(%1),%?r31", xoperands);
+      output_asm_insn ("bla %5,%2,.Ls%3", xoperands);
+      output_asm_insn ("st.b %?r31,2(%6)", xoperands);
+    }
+  output_asm_insn ("\n.Le%3:", xoperands);         /* Here if count <= 0.  */
+
+  return "";
+}
+\f
+#if 0
+/* Output a delayed branch insn with the delay insn in its
+   branch slot.  The delayed branch insn template is in TEMPLATE,
+   with operands OPERANDS.  The insn in its delay slot is INSN.
+
+   As a special case, since we know that all memory transfers are via
+   ld/st insns, if we see a (MEM (SYMBOL_REF ...)) we divide the memory
+   reference around the branch as
+
+       orh ha%x,%?r0,%?r31
+       b ...
+       ld/st l%x(%?r31),...
+
+   As another special case, we handle loading (SYMBOL_REF ...) and
+   other large constants around branches as well:
+
+       orh h%x,%?r0,%0
+       b ...
+       or l%x,%0,%1
+
+   */
+/* ??? Disabled because this re-recognition is incomplete and causes
+   constrain_operands to segfault.  Anyone who cares should fix up
+   the code to use the DBR pass.  */
+
+const char *
+output_delayed_branch (template, operands, insn)
+     const char *template;
+     rtx *operands;
+     rtx insn;
+{
+  rtx src = XVECEXP (PATTERN (insn), 0, 1);
+  rtx dest = XVECEXP (PATTERN (insn), 0, 0);
+
+  /* See if we are doing some branch together with setting some register
+     to some 32-bit value which does (or may) have some of the high-order
+     16 bits set.  If so, we need to set the register in two stages.  One
+     stage must be done before the branch, and the other one can be done
+     in the delay slot.  */
+
+  if ( (GET_CODE (src) == CONST_INT
+       && ((unsigned) INTVAL (src) & (unsigned) 0xffff0000) != (unsigned) 0)
+      || (GET_CODE (src) == SYMBOL_REF)
+      || (GET_CODE (src) == LABEL_REF)
+      || (GET_CODE (src) == CONST))
+    {
+      rtx xoperands[2];
+      xoperands[0] = dest;
+      xoperands[1] = src;
+
+      CC_STATUS_PARTIAL_INIT;
+      /* Output the `orh' insn.  */
+      output_asm_insn ("orh %H1,%?r0,%0", xoperands);
+
+      /* Output the branch instruction next.  */
+      output_asm_insn (template, operands);
+
+      /* Now output the `or' insn.  */
+      output_asm_insn ("or %L1,%0,%0", xoperands);
+    }
+  else if ((GET_CODE (src) == MEM
+           && CONSTANT_ADDRESS_P (XEXP (src, 0)))
+          || (GET_CODE (dest) == MEM
+              && CONSTANT_ADDRESS_P (XEXP (dest, 0))))
+    {
+      rtx xoperands[2];
+      const char *split_template;
+      xoperands[0] = dest;
+      xoperands[1] = src;
+
+      /* Output the `orh' insn.  */
+      if (GET_CODE (src) == MEM)
+       {
+         if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
+                && (cc_prev_status.flags & CC_HI_R31_ADJ)
+                && cc_prev_status.mdep == XEXP (operands[1], 0)))
+           {
+             CC_STATUS_INIT;
+             output_asm_insn ("orh %h1,%?r0,%?r31", xoperands);
+           }
+         split_template = load_opcode (GET_MODE (dest),
+                                       "%L1(%?r31),%0", dest);
+       }
+      else
+       {
+         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", xoperands);
+           }
+         split_template = store_opcode (GET_MODE (dest),
+                                        "%r1,%L0(%?r31)", src);
+       }
+
+      /* Output the branch instruction next.  */
+      output_asm_insn (template, operands);
+
+      /* Now output the load or store.
+        No need to do a CC_STATUS_INIT, because we are branching anyway.  */
+      output_asm_insn (split_template, xoperands);
+    }
+  else
+    {
+      int insn_code_number;
+      rtx pat = gen_rtx_SET (VOIDmode, dest, src);
+      rtx delay_insn = gen_rtx_INSN (VOIDmode, 0, 0, 0, pat, -1, 0, 0);
+      int i;
+
+      /* Output the branch instruction first.  */
+      output_asm_insn (template, operands);
+
+      /* Now recognize the insn which we put in its delay slot.
+        We must do this after outputting the branch insn,
+        since operands may just be a pointer to `recog_data.operand'.  */
+      INSN_CODE (delay_insn) = insn_code_number
+       = recog (pat, delay_insn, NULL);
+      if (insn_code_number == -1)
+       abort ();
+
+      for (i = 0; i < insn_data[insn_code_number].n_operands; i++)
+       {
+         if (GET_CODE (recog_data.operand[i]) == SUBREG)
+           alter_subreg (&recog_data.operand[i]);
+       }
+
+      insn_extract (delay_insn);
+      if (! constrain_operands (1))
+       fatal_insn_not_found (delay_insn);
+
+      template = get_insn_template (insn_code_number, delay_insn);
+      output_asm_insn (template, recog_data.operand);
+    }
+  CC_STATUS_INIT;
+  return "";
+}
+
+/* Output a newly constructed insn DELAY_INSN.  */
+const char *
+output_delay_insn (delay_insn)
+     rtx delay_insn;
+{
+  const char *template;
+  int insn_code_number;
+  int i;
+
+  /* Now recognize the insn which we put in its delay slot.
+     We must do this after outputting the branch insn,
+     since operands may just be a pointer to `recog_data.operand'.  */
+  insn_code_number = recog_memoized (delay_insn);
+  if (insn_code_number == -1)
+    abort ();
+
+  /* Extract the operands of this delay insn.  */
+  INSN_CODE (delay_insn) = insn_code_number;
+  insn_extract (delay_insn);
+
+  /* It is possible that this insn has not been properly scanned by final
+     yet.  If this insn's operands don't appear in the peephole's
+     actual operands, then they won't be fixed up by final, so we
+     make sure they get fixed up here.  -- This is a kludge.  */
+  for (i = 0; i < insn_data[insn_code_number].n_operands; i++)
+    {
+      if (GET_CODE (recog_data.operand[i]) == SUBREG)
+       alter_subreg (&recog_data.operand[i]);
+    }
+
+  if (! constrain_operands (1))
+    abort ();
+
+  cc_prev_status = cc_status;
+
+  /* Update `cc_status' for this instruction.
+     The instruction's output routine may change it further.
+     If the output routine for a jump insn needs to depend
+     on the cc status, it should look at cc_prev_status.  */
+
+  NOTICE_UPDATE_CC (PATTERN (delay_insn), delay_insn);
+
+  /* Now get the template for what this insn would
+     have been, without the branch.  */
+
+  template = get_insn_template (insn_code_number, delay_insn);
+  output_asm_insn (template, recog_data.operand);
+  return "";
+}
+#endif
+\f
+/* Special routine to convert an SFmode value represented as a
+   CONST_DOUBLE into its equivalent unsigned long bit pattern.
+   We convert the value from a double precision floating-point
+   value to single precision first, and thence to a bit-wise
+   equivalent unsigned long value.  This routine is used when
+   generating an immediate move of an SFmode value directly
+   into a general register because the svr4 assembler doesn't
+   grok floating literals in instruction operand contexts.  */
+
+unsigned long
+sfmode_constant_to_ulong (x)
+     rtx x;
+{
+  REAL_VALUE_TYPE d;
+  unsigned long l;
+
+  if (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != SFmode)
+    abort ();
+
+  REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+  REAL_VALUE_TO_TARGET_SINGLE (d, l);
+  return l;
+}
+\f
+/* This function generates the assembly code for function entry.
+
+   ASM_FILE is a stdio stream to output the code to.
+   SIZE is an int: how many units of temporary storage to allocate.
+
+   Refer to the array `regs_ever_live' to determine which registers
+   to save; `regs_ever_live[I]' is nonzero if register number I
+   is ever used in the function.  This macro is responsible for
+   knowing which registers should not be saved even if used.
+
+   NOTE: `frame_lower_bytes' is the count of bytes which will lie
+   between the new `fp' value and the new `sp' value after the
+   prologue is done.  `frame_upper_bytes' is the count of bytes
+   that will lie between the new `fp' and the *old* `sp' value
+   after the new `fp' is setup (in the prologue).  The upper
+   part of each frame always includes at least 2 words (8 bytes)
+   to hold the saved frame pointer and the saved return address.
+
+   The svr4 ABI for the i860 now requires that the values of the
+   stack pointer and frame pointer registers be kept aligned to
+   16-byte boundaries at all times.  We obey that restriction here.
+
+   The svr4 ABI for the i860 is entirely vague when it comes to specifying
+   exactly where the "preserved" registers should be saved.  The native
+   svr4 C compiler I now have doesn't help to clarify the requirements
+   very much because it is plainly out-of-date and non-ABI-compliant
+   (in at least one important way, i.e. how it generates function
+   epilogues).
+
+   The native svr4 C compiler saves the "preserved" registers (i.e.
+   r4-r15 and f2-f7) in the lower part of a frame (i.e. at negative
+   offsets from the frame pointer).
+
+   Previous versions of GCC also saved the "preserved" registers in the
+   "negative" part of the frame, but they saved them using positive
+   offsets from the (adjusted) stack pointer (after it had been adjusted
+   to allocate space for the new frame).  That's just plain wrong
+   because if the current function calls alloca(), the stack pointer
+   will get moved, and it will be impossible to restore the registers
+   properly again after that.
+
+   Both compilers handled parameter registers (i.e. r16-r27 and f8-f15)
+   by copying their values either into various "preserved" registers or
+   into stack slots in the lower part of the current frame (as seemed
+   appropriate, depending upon subsequent usage of these values).
+
+   Here we want to save the preserved registers at some offset from the
+   frame pointer register so as to avoid any possible problems arising
+   from calls to alloca().  We can either save them at small positive
+   offsets from the frame pointer, or at small negative offsets from
+   the frame pointer.  If we save them at small negative offsets from
+   the frame pointer (i.e. in the lower part of the frame) then we
+   must tell the rest of GCC (via STARTING_FRAME_OFFSET) exactly how
+   many bytes of space we plan to use in the lower part of the frame
+   for this purpose.  Since other parts of the compiler reference the
+   value of STARTING_FRAME_OFFSET long before final() calls this function,
+   we would have to go ahead and assume the worst-case storage requirements
+   for saving all of the "preserved" registers (and use that number, i.e.
+   `80', to define STARTING_FRAME_OFFSET) if we wanted to save them in
+   the lower part of the frame.  That could potentially be very wasteful,
+   and that wastefulness could really hamper people compiling for embedded
+   i860 targets with very tight limits on stack space.  Thus, we choose
+   here to save the preserved registers in the upper part of the
+   frame, so that we can decide at the very last minute how much (or how
+   little) space we must allocate for this purpose.
+
+   To satisfy the needs of the svr4 ABI "tdesc" scheme, preserved
+   registers must always be saved so that the saved values of registers
+   with higher numbers are at higher addresses.  We obey that restriction
+   here.
+
+   There are two somewhat different ways that you can generate prologues
+   here... i.e. pedantically ABI-compliant, and the "other" way.  The
+   "other" way is more consistent with what is currently generated by the
+   "native" svr4 C compiler for the i860.  That's important if you want
+   to use the current (as of 8/91) incarnation of svr4 SDB for the i860.
+   The SVR4 SDB for the i860 insists on having function prologues be
+   non-ABI-compliant!
+
+   To get fully ABI-compliant prologues, define I860_STRICT_ABI_PROLOGUES
+   in the i860svr4.h file.  (By default this is *not* defined).
+
+   The differences between the ABI-compliant and non-ABI-compliant prologues
+   are that (a) the ABI version seems to require the use of *signed*
+   (rather than unsigned) adds and subtracts, and (b) the ordering of
+   the various steps (e.g. saving preserved registers, saving the
+   return address, setting up the new frame pointer value) is different.
+
+   For strict ABI compliance, it seems to be the case that the very last
+   thing that is supposed to happen in the prologue is getting the frame
+   pointer set to its new value (but only after everything else has
+   already been properly setup).  We do that here, but only if the symbol
+   I860_STRICT_ABI_PROLOGUES is defined.
+*/
+
+#ifndef STACK_ALIGNMENT
+#define STACK_ALIGNMENT        16
+#endif
+
+const char *current_function_original_name;
+
+static int must_preserve_r1;
+static unsigned must_preserve_bytes;
+
+static void
+i860_output_function_prologue (asm_file, local_bytes)
+     register FILE *asm_file;
+     register HOST_WIDE_INT local_bytes;
+{
+  register HOST_WIDE_INT frame_lower_bytes;
+  register HOST_WIDE_INT frame_upper_bytes;
+  register HOST_WIDE_INT total_fsize;
+  register unsigned preserved_reg_bytes = 0;
+  register unsigned i;
+  register unsigned preserved_so_far = 0;
+
+  must_preserve_r1 = (optimize < 2 || ! leaf_function_p ());
+  must_preserve_bytes = 4 + (must_preserve_r1 ? 4 : 0);
+
+  /* Count registers that need preserving.  Ignore r0.  It never needs
+     preserving.  */
+
+  for (i = 1; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      if (regs_ever_live[i] && ! call_used_regs[i])
+        preserved_reg_bytes += 4;
+    }
+
+  /* Round-up the frame_lower_bytes so that it's a multiple of 16. */
+
+  frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT;
+
+  /* The upper part of each frame will contain the saved fp,
+     the saved r1, and stack slots for all of the other "preserved"
+     registers that we find we will need to save & restore. */
+
+  frame_upper_bytes = must_preserve_bytes + preserved_reg_bytes;
+
+  /* Round-up the frame_upper_bytes so that it's a multiple of 16. */
+
+  frame_upper_bytes
+    = (frame_upper_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT;
+
+  total_fsize = frame_upper_bytes + frame_lower_bytes;
+
+#ifndef I860_STRICT_ABI_PROLOGUES
+
+  /* There are two kinds of function prologues.
+     You use the "small" version if the total frame size is
+     small enough so that it can fit into an immediate 16-bit
+     value in one instruction.  Otherwise, you use the "large"
+     version of the function prologue.  */
+
+  if (total_fsize > 0x7fff)
+    {
+      /* Adjust the stack pointer.  The ABI sez to do this using `adds',
+        but the native C compiler on svr4 uses `addu'.  */
+
+      fprintf (asm_file, "\taddu -%d,%ssp,%ssp\n",
+       frame_upper_bytes, i860_reg_prefix, i860_reg_prefix);
+
+      /* Save the old frame pointer.  */
+
+      fprintf (asm_file, "\tst.l %sfp,0(%ssp)\n",
+       i860_reg_prefix, i860_reg_prefix);
+
+      /* Setup the new frame pointer.  The ABI sez to do this after
+        preserving registers (using adds), but that's not what the
+        native C compiler on svr4 does.  */
+
+      fprintf (asm_file, "\taddu 0,%ssp,%sfp\n",
+       i860_reg_prefix, i860_reg_prefix);
+
+      /* Get the value of frame_lower_bytes into r31.  */
+
+      fprintf (asm_file, "\torh %d,%sr0,%sr31\n",
+       frame_lower_bytes >> 16, i860_reg_prefix, i860_reg_prefix);
+      fprintf (asm_file, "\tor %d,%sr31,%sr31\n",
+       frame_lower_bytes & 0xffff, i860_reg_prefix, i860_reg_prefix);
+
+      /* Now re-adjust the stack pointer using the value in r31.
+        The ABI sez to do this with `subs' but SDB may prefer `subu'.  */
+
+      fprintf (asm_file, "\tsubu %ssp,%sr31,%ssp\n",
+       i860_reg_prefix, i860_reg_prefix, i860_reg_prefix);
+
+      /* Preserve registers.  The ABI sez to do this before setting
+        up the new frame pointer, but that's not what the native
+        C compiler on svr4 does.  */
+
+      for (i = 1; i < 32; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tst.l %s%s,%d(%sfp)\n",
+           i860_reg_prefix, reg_names[i],
+           must_preserve_bytes  + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      for (i = 32; i < 64; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tfst.l %s%s,%d(%sfp)\n",
+           i860_reg_prefix, reg_names[i],
+           must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      /* Save the return address.  */
+
+      if (must_preserve_r1)
+        fprintf (asm_file, "\tst.l %sr1,4(%sfp)\n",
+         i860_reg_prefix, i860_reg_prefix);
+    }
+  else
+    {
+      /* Adjust the stack pointer.  The ABI sez to do this using `adds',
+        but the native C compiler on svr4 uses `addu'.  */
+
+      fprintf (asm_file, "\taddu -%d,%ssp,%ssp\n",
+       total_fsize, i860_reg_prefix, i860_reg_prefix);
+
+      /* Save the old frame pointer.  */
+
+      fprintf (asm_file, "\tst.l %sfp,%d(%ssp)\n",
+       i860_reg_prefix, frame_lower_bytes, i860_reg_prefix);
+
+      /* Setup the new frame pointer.  The ABI sez to do this after
+        preserving registers and after saving the return address,
+       (and its saz to do this using adds), but that's not what the
+        native C compiler on svr4 does.  */
+
+      fprintf (asm_file, "\taddu %d,%ssp,%sfp\n",
+       frame_lower_bytes, i860_reg_prefix, i860_reg_prefix);
+
+      /* Preserve registers.  The ABI sez to do this before setting
+        up the new frame pointer, but that's not what the native
+        compiler on svr4 does.  */
+
+      for (i = 1; i < 32; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tst.l %s%s,%d(%sfp)\n",
+           i860_reg_prefix, reg_names[i],
+           must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      for (i = 32; i < 64; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tfst.l %s%s,%d(%sfp)\n",
+           i860_reg_prefix, reg_names[i],
+           must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      /* Save the return address.  The ABI sez to do this earlier,
+        and also via an offset from %sp, but the native C compiler
+        on svr4 does it later (i.e. now) and uses an offset from
+        %fp.  */
+
+      if (must_preserve_r1)
+        fprintf (asm_file, "\tst.l %sr1,4(%sfp)\n",
+         i860_reg_prefix, i860_reg_prefix);
+    }
+
+#else /* defined(I860_STRICT_ABI_PROLOGUES) */
+
+  /* There are two kinds of function prologues.
+     You use the "small" version if the total frame size is
+     small enough so that it can fit into an immediate 16-bit
+     value in one instruction.  Otherwise, you use the "large"
+     version of the function prologue.  */
+
+  if (total_fsize > 0x7fff)
+    {
+      /* Adjust the stack pointer (thereby allocating a new frame).  */
+
+      fprintf (asm_file, "\tadds -%d,%ssp,%ssp\n",
+       frame_upper_bytes, i860_reg_prefix, i860_reg_prefix);
+
+      /* Save the caller's frame pointer.  */
+
+      fprintf (asm_file, "\tst.l %sfp,0(%ssp)\n",
+       i860_reg_prefix, i860_reg_prefix);
+
+      /* Save return address.  */
+
+      if (must_preserve_r1)
+        fprintf (asm_file, "\tst.l %sr1,4(%ssp)\n",
+         i860_reg_prefix, i860_reg_prefix);
+
+      /* Get the value of frame_lower_bytes into r31 for later use.  */
+
+      fprintf (asm_file, "\torh %d,%sr0,%sr31\n",
+       frame_lower_bytes >> 16, i860_reg_prefix, i860_reg_prefix);
+      fprintf (asm_file, "\tor %d,%sr31,%sr31\n",
+       frame_lower_bytes & 0xffff, i860_reg_prefix, i860_reg_prefix);
+
+      /* Now re-adjust the stack pointer using the value in r31.  */
+
+      fprintf (asm_file, "\tsubs %ssp,%sr31,%ssp\n",
+       i860_reg_prefix, i860_reg_prefix, i860_reg_prefix);
+
+      /* Pre-compute value to be used as the new frame pointer.  */
+
+      fprintf (asm_file, "\tadds %ssp,%sr31,%sr31\n",
+       i860_reg_prefix, i860_reg_prefix, i860_reg_prefix);
+
+      /* Preserve registers.  */
+
+      for (i = 1; i < 32; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tst.l %s%s,%d(%sr31)\n",
+           i860_reg_prefix, reg_names[i],
+           must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      for (i = 32; i < 64; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tfst.l %s%s,%d(%sr31)\n",
+           i860_reg_prefix, reg_names[i],
+           must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      /* Actually set the new value of the frame pointer.  */
+
+      fprintf (asm_file, "\tmov %sr31,%sfp\n",
+       i860_reg_prefix, i860_reg_prefix);
+    }
+  else
+    {
+      /* Adjust the stack pointer.  */
+
+      fprintf (asm_file, "\tadds -%d,%ssp,%ssp\n",
+       total_fsize, i860_reg_prefix, i860_reg_prefix);
+
+      /* Save the caller's frame pointer.  */
+
+      fprintf (asm_file, "\tst.l %sfp,%d(%ssp)\n",
+       i860_reg_prefix, frame_lower_bytes, i860_reg_prefix);
+
+      /* Save the return address.  */
+
+      if (must_preserve_r1)
+        fprintf (asm_file, "\tst.l %sr1,%d(%ssp)\n",
+         i860_reg_prefix, frame_lower_bytes + 4, i860_reg_prefix);
+
+      /* Preserve registers.  */
+
+      for (i = 1; i < 32; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tst.l %s%s,%d(%ssp)\n",
+           i860_reg_prefix, reg_names[i],
+           frame_lower_bytes + must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      for (i = 32; i < 64; i++)
+        if (regs_ever_live[i] && ! call_used_regs[i])
+          fprintf (asm_file, "\tfst.l %s%s,%d(%ssp)\n",
+           i860_reg_prefix, reg_names[i],
+           frame_lower_bytes + must_preserve_bytes + (4 * preserved_so_far++),
+           i860_reg_prefix);
+
+      /* Setup the new frame pointer.  */
+
+      fprintf (asm_file, "\tadds %d,%ssp,%sfp\n",
+       frame_lower_bytes, i860_reg_prefix, i860_reg_prefix);
+    }
+#endif /* defined(I860_STRICT_ABI_PROLOGUES) */
+
+#ifdef ASM_OUTPUT_PROLOGUE_SUFFIX
+  ASM_OUTPUT_PROLOGUE_SUFFIX (asm_file);
+#endif /* defined(ASM_OUTPUT_PROLOGUE_SUFFIX) */
+}
+\f
+/* This function generates the assembly code for function exit.
+
+   ASM_FILE is a stdio stream to output the code to.
+   SIZE is an int: how many units of temporary storage to allocate.
+
+   The function epilogue should not depend on the current stack pointer!
+   It should use the frame pointer only.  This is mandatory because
+   of alloca; we also take advantage of it to omit stack adjustments
+   before returning.
+
+   Note that when we go to restore the preserved register values we must
+   not try to address their slots by using offsets from the stack pointer.
+   That's because the stack pointer may have been moved during the function
+   execution due to a call to alloca().  Rather, we must restore all
+   preserved registers via offsets from the frame pointer value.
+
+   Note also that when the current frame is being "popped" (by adjusting
+   the value of the stack pointer) on function exit, we must (for the
+   sake of alloca) set the new value of the stack pointer based upon
+   the current value of the frame pointer.  We can't just add what we
+   believe to be the (static) frame size to the stack pointer because
+   if we did that, and alloca() had been called during this function,
+   we would end up returning *without* having fully deallocated all of
+   the space grabbed by alloca.  If that happened, and a function
+   containing one or more alloca() calls was called over and over again,
+   then the stack would grow without limit!
+
+   Finally note that the epilogues generated here are completely ABI
+   compliant.  They go out of their way to insure that the value in
+   the frame pointer register is never less than the value in the stack
+   pointer register.  It's not clear why this relationship needs to be
+   maintained at all times, but maintaining it only costs one extra
+   instruction, so what the hell.
+*/
+
+/* This corresponds to a version 4 TDESC structure. Lower numbered
+   versions successively omit the last word of the structure. We
+   don't try to handle version 5 here. */
+
+typedef struct TDESC_flags {
+       int version:4;
+       int reg_packing:1;
+       int callable_block:1;
+       int reserved:4;
+       int fregs:6;    /* fp regs 2-7 */
+       int iregs:16;   /* regs 0-15 */
+} TDESC_flags;
+
+typedef struct TDESC {
+       TDESC_flags flags;
+       int integer_reg_offset;         /* same as must_preserve_bytes */
+       int floating_point_reg_offset;
+       unsigned int positive_frame_size;       /* same as frame_upper_bytes */
+       unsigned int negative_frame_size;       /* same as frame_lower_bytes */
+} TDESC;
+
+static void
+i860_output_function_epilogue (asm_file, local_bytes)
+     register FILE *asm_file;
+     register HOST_WIDE_INT local_bytes;
+{
+  register HOST_WIDE_INT frame_upper_bytes;
+  register HOST_WIDE_INT frame_lower_bytes;
+  register HOST_WIDE_INT preserved_reg_bytes = 0;
+  register unsigned i;
+  register unsigned restored_so_far = 0;
+  register unsigned int_restored;
+  register unsigned mask;
+  unsigned intflags=0;
+  register TDESC_flags *flags = (TDESC_flags *) &intflags;
+#ifdef OUTPUT_TDESC    /* Output an ABI-compliant TDESC entry */
+  const char *long_op = integer_asm_op (4, TRUE);
+#endif
+
+  flags->version = 4;
+  flags->reg_packing = 1;
+  flags->iregs = 8;    /* old fp always gets saved */
+
+  /* Round-up the frame_lower_bytes so that it's a multiple of 16. */
+
+  frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT;
+
+  /* Count the number of registers that were preserved in the prologue.
+     Ignore r0.  It is never preserved.  */
+
+  for (i = 1; i < FIRST_PSEUDO_REGISTER; i++)
+    {
+      if (regs_ever_live[i] && ! call_used_regs[i])
+        preserved_reg_bytes += 4;
+    }
+
+  /* The upper part of each frame will contain only saved fp,
+     the saved r1, and stack slots for all of the other "preserved"
+     registers that we find we will need to save & restore. */
+
+  frame_upper_bytes = must_preserve_bytes + preserved_reg_bytes;
+
+  /* Round-up frame_upper_bytes so that t is a multiple of 16. */
+
+  frame_upper_bytes
+    = (frame_upper_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT;
+
+  /* Restore all of the "preserved" registers that need restoring.  */
+
+  mask = 2;
+
+  for (i = 1; i < 32; i++, mask<<=1)
+    if (regs_ever_live[i] && ! call_used_regs[i]) {
+      fprintf (asm_file, "\tld.l %d(%sfp),%s%s\n",
+       must_preserve_bytes + (4 * restored_so_far++),
+       i860_reg_prefix, i860_reg_prefix, reg_names[i]);
+      if (i > 3 && i < 16)
+       flags->iregs |= mask;
+    }
+
+  int_restored = restored_so_far;
+  mask = 1;
+
+  for (i = 32; i < 64; i++) {
+    if (regs_ever_live[i] && ! call_used_regs[i]) {
+      fprintf (asm_file, "\tfld.l %d(%sfp),%s%s\n",
+       must_preserve_bytes + (4 * restored_so_far++),
+       i860_reg_prefix, i860_reg_prefix, reg_names[i]);
+      if (i > 33 && i < 40)
+       flags->fregs |= mask;
+    }
+    if (i > 33 && i < 40)
+      mask<<=1;
+  }
+
+  /* Get the value we plan to use to restore the stack pointer into r31.  */
+
+  fprintf (asm_file, "\tadds %d,%sfp,%sr31\n",
+    frame_upper_bytes, i860_reg_prefix, i860_reg_prefix);
+
+  /* Restore the return address and the old frame pointer.  */
+
+  if (must_preserve_r1) {
+    fprintf (asm_file, "\tld.l 4(%sfp),%sr1\n",
+      i860_reg_prefix, i860_reg_prefix);
+    flags->iregs |= 2;
+  }
+
+  fprintf (asm_file, "\tld.l 0(%sfp),%sfp\n",
+    i860_reg_prefix, i860_reg_prefix);
+
+  /* Return and restore the old stack pointer value.  */
+
+  fprintf (asm_file, "\tbri %sr1\n\tmov %sr31,%ssp\n",
+    i860_reg_prefix, i860_reg_prefix, i860_reg_prefix);
+
+#ifdef OUTPUT_TDESC    /* Output an ABI-compliant TDESC entry */
+  if (! frame_lower_bytes) {
+    flags->version--;
+    if (! frame_upper_bytes) {
+      flags->version--;
+      if (restored_so_far == int_restored)     /* No FP saves */
+       flags->version--;
+    }
+  }
+  assemble_name(asm_file,current_function_original_name);
+  fputs(".TDESC:\n", asm_file);
+  fprintf(asm_file, "%s 0x%0x\n", long_op, intflags);
+  fprintf(asm_file, "%s %d\n", long_op,
+       int_restored ? must_preserve_bytes : 0);
+  if (flags->version > 1) {
+    fprintf(asm_file, "%s %d\n", long_op,
+       (restored_so_far == int_restored) ? 0 : must_preserve_bytes +
+         (4 * int_restored));
+    if (flags->version > 2) {
+      fprintf(asm_file, "%s %d\n", long_op, frame_upper_bytes);
+      if (flags->version > 3)
+       fprintf(asm_file, "%s %d\n", long_op, frame_lower_bytes);
+    }
+  }
+  tdesc_section();
+  fprintf(asm_file, "%s ", long_op);
+  assemble_name(asm_file, current_function_original_name);
+  fprintf(asm_file, "\n%s ", long_op);
+  assemble_name(asm_file, current_function_original_name);
+  fputs(".TDESC\n", asm_file);
+  text_section();
+#endif
+}
+\f
+
+/* Expand a library call to __builtin_saveregs.  */
+rtx
+i860_saveregs ()
+{
+  rtx fn = gen_rtx_SYMBOL_REF (Pmode, "__builtin_saveregs");
+  rtx save = gen_reg_rtx (Pmode);
+  rtx valreg = LIBCALL_VALUE (Pmode);
+  rtx ret;
+
+  /* The return value register overlaps the first argument register.
+     Save and restore it around the call.  */
+  emit_move_insn (save, valreg);
+  ret = emit_library_call_value (fn, NULL_RTX, 1, Pmode, 0);
+  if (GET_CODE (ret) != REG || REGNO (ret) < FIRST_PSEUDO_REGISTER)
+    ret = copy_to_reg (ret);
+  emit_move_insn (valreg, save);
+
+  return ret;
+}
+
+tree
+i860_build_va_list ()
+{
+  tree field_ireg_used, field_freg_used, field_reg_base, field_mem_ptr;
+  tree record;
+
+  record = make_node (RECORD_TYPE);
+
+  field_ireg_used = build_decl (FIELD_DECL, get_identifier ("__ireg_used"),
+                               unsigned_type_node);
+  field_freg_used = build_decl (FIELD_DECL, get_identifier ("__freg_used"),
+                               unsigned_type_node);
+  field_reg_base = build_decl (FIELD_DECL, get_identifier ("__reg_base"),
+                              ptr_type_node);
+  field_mem_ptr = build_decl (FIELD_DECL, get_identifier ("__mem_ptr"),
+                             ptr_type_node);
+
+  DECL_FIELD_CONTEXT (field_ireg_used) = record;
+  DECL_FIELD_CONTEXT (field_freg_used) = record;
+  DECL_FIELD_CONTEXT (field_reg_base) = record;
+  DECL_FIELD_CONTEXT (field_mem_ptr) = record;
+
+#ifdef I860_SVR4_VA_LIST
+  TYPE_FIELDS (record) = field_ireg_used;
+  TREE_CHAIN (field_ireg_used) = field_freg_used;
+  TREE_CHAIN (field_freg_used) = field_reg_base;
+  TREE_CHAIN (field_reg_base) = field_mem_ptr;
+#else
+  TYPE_FIELDS (record) = field_reg_base;
+  TREE_CHAIN (field_reg_base) = field_mem_ptr;
+  TREE_CHAIN (field_mem_ptr) = field_ireg_used;
+  TREE_CHAIN (field_ireg_used) = field_freg_used;
+#endif
+
+  layout_type (record);
+  return record;
+}
+
+void
+i860_va_start (stdarg_p, valist, nextarg)
+     int stdarg_p;
+     tree valist;
+     rtx nextarg;
+{
+  tree saveregs, t;
+
+  saveregs = make_tree (build_pointer_type (va_list_type_node),
+                       expand_builtin_saveregs ());
+  saveregs = build1 (INDIRECT_REF, va_list_type_node, saveregs);
+
+  if (stdarg_p)
+    {
+      tree field_ireg_used, field_freg_used, field_reg_base, field_mem_ptr;
+      tree ireg_used, freg_used, reg_base, mem_ptr;
+
+#ifdef I860_SVR4_VA_LIST
+      field_ireg_used = TYPE_FIELDS (va_list_type_node);
+      field_freg_used = TREE_CHAIN (field_ireg_used);
+      field_reg_base = TREE_CHAIN (field_freg_used);
+      field_mem_ptr = TREE_CHAIN (field_reg_base);
+#else
+      field_reg_base = TYPE_FIELDS (va_list_type_node);
+      field_mem_ptr = TREE_CHAIN (field_reg_base);
+      field_ireg_used = TREE_CHAIN (field_mem_ptr);
+      field_freg_used = TREE_CHAIN (field_ireg_used);
+#endif
+
+      ireg_used = build (COMPONENT_REF, TREE_TYPE (field_ireg_used),
+                        valist, field_ireg_used);
+      freg_used = build (COMPONENT_REF, TREE_TYPE (field_freg_used),
+                        valist, field_freg_used);
+      reg_base = build (COMPONENT_REF, TREE_TYPE (field_reg_base),
+                       valist, field_reg_base);
+      mem_ptr = build (COMPONENT_REF, TREE_TYPE (field_mem_ptr),
+                      valist, field_mem_ptr);
+
+      t = build_int_2 (current_function_args_info.ints, 0);
+      t = build (MODIFY_EXPR, TREE_TYPE (ireg_used), ireg_used, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      
+      t = build_int_2 (ROUNDUP (current_function_args_info.floats, 8), 0);
+      t = build (MODIFY_EXPR, TREE_TYPE (freg_used), freg_used, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      
+      t = build (COMPONENT_REF, TREE_TYPE (field_reg_base),
+                saveregs, field_reg_base);
+      t = build (MODIFY_EXPR, TREE_TYPE (reg_base), reg_base, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      t = make_tree (ptr_type_node, nextarg);
+      t = build (MODIFY_EXPR, TREE_TYPE (mem_ptr), mem_ptr, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+  else
+    {
+      t = build (MODIFY_EXPR, va_list_type_node, valist, saveregs);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+}
+
+#define NUM_PARM_FREGS 8
+#define NUM_PARM_IREGS 12
+#ifdef I860_SVR4_VARARGS
+#define FREG_OFFSET 0
+#define IREG_OFFSET (NUM_PARM_FREGS * UNITS_PER_WORD)
+#else
+#define FREG_OFFSET (NUM_PARM_IREGS * UNITS_PER_WORD)
+#define IREG_OFFSET 0
+#endif
+
+rtx
+i860_va_arg (valist, type)
+     tree valist, type;
+{
+  tree field_ireg_used, field_freg_used, field_reg_base, field_mem_ptr;
+  tree type_ptr_node, t;
+  rtx lab_over = NULL_RTX;
+  rtx ret, val;
+  HOST_WIDE_INT align;
+
+#ifdef I860_SVR4_VA_LIST
+  field_ireg_used = TYPE_FIELDS (va_list_type_node);
+  field_freg_used = TREE_CHAIN (field_ireg_used);
+  field_reg_base = TREE_CHAIN (field_freg_used);
+  field_mem_ptr = TREE_CHAIN (field_reg_base);
+#else
+  field_reg_base = TYPE_FIELDS (va_list_type_node);
+  field_mem_ptr = TREE_CHAIN (field_reg_base);
+  field_ireg_used = TREE_CHAIN (field_mem_ptr);
+  field_freg_used = TREE_CHAIN (field_ireg_used);
+#endif
+
+  field_ireg_used = build (COMPONENT_REF, TREE_TYPE (field_ireg_used),
+                          valist, field_ireg_used);
+  field_freg_used = build (COMPONENT_REF, TREE_TYPE (field_freg_used),
+                          valist, field_freg_used);
+  field_reg_base = build (COMPONENT_REF, TREE_TYPE (field_reg_base),
+                         valist, field_reg_base);
+  field_mem_ptr = build (COMPONENT_REF, TREE_TYPE (field_mem_ptr),
+                        valist, field_mem_ptr);
+
+  ret = gen_reg_rtx (Pmode);
+  type_ptr_node = build_pointer_type (type);
+
+  if (! AGGREGATE_TYPE_P (type))
+    {
+      int nparm, incr, ofs;
+      tree field;
+      rtx lab_false;
+
+      if (FLOAT_TYPE_P (type))
+       {
+         field = field_freg_used;
+         nparm = NUM_PARM_FREGS;
+         incr = 2;
+         ofs = FREG_OFFSET;
+       }
+      else
+       {
+         field = field_ireg_used;
+         nparm = NUM_PARM_IREGS;
+         incr = int_size_in_bytes (type) / UNITS_PER_WORD;
+         ofs = IREG_OFFSET;
+       }
+
+      lab_false = gen_label_rtx ();
+      lab_over = gen_label_rtx ();
+
+      emit_cmp_and_jump_insns (expand_expr (field, NULL_RTX, 0, 0),
+                              GEN_INT (nparm - incr), GT, const0_rtx,
+                              TYPE_MODE (TREE_TYPE (field)),
+                              TREE_UNSIGNED (field), lab_false);
+
+      t = fold (build (POSTINCREMENT_EXPR, TREE_TYPE (field), field,
+                      build_int_2 (incr, 0)));
+      TREE_SIDE_EFFECTS (t) = 1;
+
+      t = fold (build (MULT_EXPR, TREE_TYPE (field), field,
+                      build_int_2 (UNITS_PER_WORD, 0)));
+      TREE_SIDE_EFFECTS (t) = 1;
+      
+      t = fold (build (PLUS_EXPR, ptr_type_node, field_reg_base,
+                      fold (build (PLUS_EXPR, TREE_TYPE (field), t,
+                                   build_int_2 (ofs, 0)))));
+      TREE_SIDE_EFFECTS (t) = 1;
+      
+      val = expand_expr (t, ret, VOIDmode, EXPAND_NORMAL);
+      if (val != ret)
+       emit_move_insn (ret, val);
+
+      emit_jump_insn (gen_jump (lab_over));
+      emit_barrier ();
+      emit_label (lab_false);
+    }
+
+  align = TYPE_ALIGN (type);
+  if (align < BITS_PER_WORD)
+    align = BITS_PER_WORD;
+  align /= BITS_PER_UNIT;
+
+  t = build (PLUS_EXPR, ptr_type_node, field_mem_ptr,
+            build_int_2 (align - 1, 0));
+  t = build (BIT_AND_EXPR, ptr_type_node, t, build_int_2 (-align, -1));
+
+  val = expand_expr (t, ret, VOIDmode, EXPAND_NORMAL);
+  if (val != ret)
+    emit_move_insn (ret, val);
+
+  t = fold (build (PLUS_EXPR, ptr_type_node,
+                  make_tree (ptr_type_node, ret),
+                  build_int_2 (int_size_in_bytes (type), 0)));
+  t = build (MODIFY_EXPR, ptr_type_node, field_mem_ptr, t);
+  TREE_SIDE_EFFECTS (t) = 1;
+  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+  if (lab_over)
+    emit_label (lab_over);
+
+  return ret;
+}
diff --git a/gcc/config/i860/i860.h b/gcc/config/i860/i860.h
new file mode 100644 (file)
index 0000000..f9fd62c
--- /dev/null
@@ -0,0 +1,1319 @@
+/* Definitions of target machine for GNU compiler, for Intel 860.
+   Copyright (C) 1989, 1991, 1993, 1995, 1996, 1997, 1998, 1999, 2000,
+   2001, 2002 Free Software Foundation, Inc.
+   Hacked substantially by Ron Guilmette (rfg@monkeys.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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+
+/* Note that some other tm.h files include this one and then override
+   many of the definitions that relate to assembler syntax.  */
+
+
+/* Names to predefine in the preprocessor for this target machine.  */
+
+#define CPP_PREDEFINES "-Di860 -Dunix -Asystem=unix -Asystem=svr4 -Acpu=i860 -Amachine=i860"
+
+/* Print subsidiary information on the compiler version in use.  */
+#define TARGET_VERSION fprintf (stderr, " (i860)");
+
+/* Run-time compilation parameters selecting different hardware subsets
+   or supersets.
+
+   On the i860, we have one: TARGET_XP.  This option allows gcc to generate
+   additional instructions available only on the newer i860 XP (but not on
+   the older i860 XR).
+*/
+
+extern int target_flags;
+
+/* Nonzero if we should generate code to use the fpu.  */
+#define TARGET_XP (target_flags & 1)
+
+/* Macro to define tables used to set the flags.
+   This is a list in braces of pairs in braces,
+   each pair being { "NAME", VALUE }
+   where VALUE is the bits to set or minus the bits to clear.
+   An empty string NAME is used to identify the default VALUE.  */
+
+#define TARGET_SWITCHES  \
+  { {"xp", 1, N_("Generate code which uses the FPU")},                 \
+    {"noxp", -1, N_("Do not generate code which uses the FPU")},       \
+    {"xr", -1, N_("Do not generate code which uses the FPU")},         \
+    { "", TARGET_DEFAULT, NULL}}
+
+#define TARGET_DEFAULT 0
+\f
+/* target machine storage layout */
+
+/* Define this if most significant bit is lowest numbered
+   in instructions that operate on numbered bit-fields.
+   This is a moot question on the i860 due to the lack of bit-field insns.  */
+#define BITS_BIG_ENDIAN 0
+
+/* Define this if most significant byte of a word is the lowest numbered.  */
+/* That is not true on i860 in the mode we will use.  */
+#define BYTES_BIG_ENDIAN 0
+
+/* Define this if most significant word of a multiword number is the lowest
+   numbered.  */
+/* For the i860 this goes with BYTES_BIG_ENDIAN.  */
+/* NOTE: GCC probably cannot support a big-endian i860
+   because GCC fundamentally assumes that the order of words
+   in memory as the same as the order in registers.
+   That's not true for the big-endian i860.
+   The big-endian i860 isn't important enough to
+   justify the trouble of changing this assumption.  */
+#define WORDS_BIG_ENDIAN 0
+
+/* Width of a word, in units (bytes).  */
+#define UNITS_PER_WORD 4
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list.  */
+#define PARM_BOUNDARY 32
+
+/* Boundary (in *bits*) on which stack pointer should be aligned.  */
+#define STACK_BOUNDARY 128
+
+/* Allocation boundary (in *bits*) for the code of a function.  */
+#define FUNCTION_BOUNDARY 64
+
+/* Alignment of field after `int : 0' in a structure.  */
+#define EMPTY_FIELD_BOUNDARY 32
+
+/* Every structure's size must be a multiple of this.  */
+#define STRUCTURE_SIZE_BOUNDARY 8
+
+/* Minimum size in bits of the largest boundary to which any
+   and all fundamental data types supported by the hardware
+   might need to be aligned. No data type wants to be aligned
+   rounder than this.  The i860 supports 128-bit (long double)
+   floating point quantities, and the System V Release 4 i860
+   ABI requires these to be aligned to 16-byte (128-bit)
+   boundaries.  */
+#define BIGGEST_ALIGNMENT 128
+
+/* Set this nonzero if move instructions will actually fail to work
+   when given unaligned data.  */
+#define STRICT_ALIGNMENT 1
+
+/* If bit field type is int, don't let it cross an int,
+   and give entire struct the alignment of an int.  */
+#define PCC_BITFIELD_TYPE_MATTERS 1
+\f
+/* Standard register usage.  */
+
+/* Number of actual hardware registers.
+   The hardware registers are assigned numbers for the compiler
+   from 0 to just below FIRST_PSEUDO_REGISTER.
+   All registers that the compiler knows about must be given numbers,
+   even those that are not normally considered general registers.
+
+   i860 has 32 fullword registers and 32 floating point registers.  */
+
+#define FIRST_PSEUDO_REGISTER 64
+
+/* 1 for registers that have pervasive standard uses
+   and are not available for the register allocator.
+   On the i860, this includes the always-0 registers
+   and fp, sp, arg pointer, and the return address.
+   Also r31, used for special purposes for constant addresses.  */
+#define FIXED_REGISTERS  \
+ {1, 1, 1, 1, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 1,      \
+  1, 1, 0, 0, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 0}
+
+/* 1 for registers not available across function calls.
+   These must include the FIXED_REGISTERS and also any
+   registers that can be used without being saved.
+   On the i860, these are r0-r3, r16-r31, f0, f1, and f16-f31.  */
+#define CALL_USED_REGISTERS  \
+ {1, 1, 1, 1, 0, 0, 0, 0,      \
+  0, 0, 0, 0, 0, 0, 0, 0,      \
+  1, 1, 1, 1, 1, 1, 1, 1,      \
+  1, 1, 1, 1, 1, 1, 1, 1,      \
+  1, 1, 0, 0, 0, 0, 0, 0,      \
+  1, 1, 1, 1, 1, 1, 1, 1,      \
+  1, 1, 1, 1, 1, 1, 1, 1,      \
+  1, 1, 1, 1, 1, 1, 1, 1}
+
+/* Try to get a non-preserved register before trying to get one we will
+   have to preserve.  Try to get an FP register only *after* trying to
+   get a general register, because it is relatively expensive to move
+   into or out of an FP register.  */
+
+#define REG_ALLOC_ORDER                        \
+ {31, 30, 29, 28, 27, 26, 25, 24,      \
+  23, 22, 21, 20, 19, 18, 17, 16,      \
+  15, 14, 13, 12, 11, 10,  9,  8,      \
+   7,  6,  5,  4,  3,  2,  1,  0,      \
+  63, 62, 61, 60, 59, 58, 57, 56,      \
+  55, 54, 53, 52, 51, 50, 49, 48,      \
+  47, 46, 45, 44, 43, 42, 41, 40,      \
+  39, 38, 37, 36, 35, 34, 33, 32}
+
+/* Return number of consecutive hard regs needed starting at reg REGNO
+   to hold something of mode MODE.
+   This is ordinarily the length in words of a value of mode MODE
+   but can be less for certain modes in special long registers.
+
+   On the i860, all registers hold 32 bits worth.  */
+#define HARD_REGNO_NREGS(REGNO, MODE)   \
+  (((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+#define REGNO_MODE_ALIGNED(REGNO, MODE) \
+  (((REGNO) % ((GET_MODE_UNIT_SIZE (MODE) + 3) / 4)) == 0)
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
+
+   On the i860, we allow anything to go into any registers, but we require
+   any sort of value going into the FP registers to be properly aligned
+   (based on its size) within the FP register set.
+*/
+#define HARD_REGNO_MODE_OK(REGNO, MODE)                                        \
+  (((REGNO) < 32)                                                      \
+   || (MODE) == VOIDmode || (MODE) == BLKmode                          \
+   || REGNO_MODE_ALIGNED (REGNO, MODE))
+
+/* Value is 1 if it is a good idea to tie two pseudo registers
+   when one has mode MODE1 and one has mode MODE2.
+   If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
+   for any hard reg, then this must be 0 for correct output.  */
+/* I think that is not always true; alignment restrictions for doubles
+   should not prevent tying them with singles.  So try allowing that.
+   On the other hand, don't let fixed and floating be tied;
+   this restriction is not necessary, but may make better code.  */
+#define MODES_TIEABLE_P(MODE1, MODE2) \
+  ((GET_MODE_CLASS (MODE1) == MODE_FLOAT               \
+    || GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT)   \
+   == (GET_MODE_CLASS (MODE2) == MODE_FLOAT            \
+       || GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT))
+
+/* Specify the registers used for certain standard purposes.
+   The values of these macros are register numbers.  */
+
+/* i860 pc isn't overloaded on a register that the compiler knows about.  */
+/* #define PC_REGNUM  */
+
+/* Register to use for pushing function arguments.  */
+#define STACK_POINTER_REGNUM 2
+
+/* Base register for access to local variables of the function.  */
+#define FRAME_POINTER_REGNUM 3
+
+/* Value should be nonzero if functions must have frame pointers.
+   Zero means the frame pointer need not be set up (and parms
+   may be accessed via the stack pointer) in functions that seem suitable.
+   This is computed in `reload', in reload1.c.  */
+#define FRAME_POINTER_REQUIRED 1
+
+/* Base register for access to arguments of the function.  */
+#define ARG_POINTER_REGNUM 28
+
+/* Register in which static-chain is passed to a function.  */
+#define STATIC_CHAIN_REGNUM 29
+
+/* Register in which address to store a structure value
+   is passed to a function.  */
+#define STRUCT_VALUE_REGNUM 16
+
+/* Register to use when a source of a floating-point zero is needed.  */
+#define F0_REGNUM      32
+\f
+/* Define the classes of registers for register constraints in the
+   machine description.  Also define ranges of constants.
+
+   One of the classes must always be named ALL_REGS and include all hard regs.
+   If there is more than one class, another class must be named NO_REGS
+   and contain no registers.
+
+   The name GENERAL_REGS must be the name of a class (or an alias for
+   another name such as ALL_REGS).  This is the class of registers
+   that is allowed by "g" or "r" in a register constraint.
+   Also, registers outside this class are allocated only when
+   instructions express preferences for them.
+
+   The classes must be numbered in nondecreasing order; that is,
+   a larger-numbered class must never be contained completely
+   in a smaller-numbered class.
+
+   For any two classes, it is very desirable that there be another
+   class that represents their union.  */
+   
+/* The i860 has two kinds of registers, hence four classes.  */
+
+enum reg_class { NO_REGS, GENERAL_REGS, FP_REGS, ALL_REGS, LIM_REG_CLASSES };
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+/* Give names of register classes as strings for dump file.   */
+
+#define REG_CLASS_NAMES \
+ {"NO_REGS", "GENERAL_REGS", "FP_REGS", "ALL_REGS" }
+
+/* Define which registers fit in which classes.
+   This is an initializer for a vector of HARD_REG_SET
+   of length N_REG_CLASSES.  */
+
+#define REG_CLASS_CONTENTS     \
+ {{0, 0}, {0xffffffff, 0},     \
+  {0, 0xffffffff}, {0xffffffff, 0xffffffff}}
+
+/* The same information, inverted:
+   Return the class number of the smallest class containing
+   reg number REGNO.  This could be a conditional expression
+   or could index an array.  */
+
+#define REGNO_REG_CLASS(REGNO) \
+ ((REGNO) >= 32 ? FP_REGS : GENERAL_REGS)
+
+/* The class value for index registers, and the one for base regs.  */
+#define INDEX_REG_CLASS GENERAL_REGS
+#define BASE_REG_CLASS GENERAL_REGS
+
+/* Get reg_class from a letter such as appears in the machine description.  */
+
+#define REG_CLASS_FROM_LETTER(C) \
+  ((C) == 'f' ? FP_REGS : NO_REGS)
+
+/* The letters I, J, K, L and M in a register constraint string
+   can be used to stand for particular ranges of immediate operands.
+   This macro defines what the ranges are.
+   C is the letter, and VALUE is a constant value.
+   Return 1 if VALUE is in the range specified by C.
+
+   For the i860, `I' is used for the range of constants 
+   an add/subtract insn can actually contain.
+   But not including -0x8000, since we need
+   to negate the constant sometimes.
+   `J' is used for the range which is just zero (since that is R0).
+   `K' is used for the range allowed in bte.
+   `L' is used for the range allowed in logical insns.  */
+
+#define SMALL_INT(X) ((unsigned) (INTVAL (X) + 0x7fff) < 0xffff)
+
+#define LOGIC_INT(X) ((unsigned) INTVAL (X) < 0x10000)
+
+#define SMALL_INTVAL(X) ((unsigned) ((X) + 0x7fff) < 0xffff)
+
+#define LOGIC_INTVAL(X) ((unsigned) (X) < 0x10000)
+
+#define CONST_OK_FOR_LETTER_P(VALUE, C)  \
+  ((C) == 'I' ? ((unsigned) (VALUE) + 0x7fff) < 0xffff \
+   : (C) == 'J' ? (VALUE) == 0                         \
+   : (C) == 'K' ? (unsigned) (VALUE) < 0x20    \
+   : (C) == 'L' ? (unsigned) (VALUE) < 0x10000 \
+   : 0)
+
+/* Return non-zero if the given VALUE is acceptable for the
+   constraint letter C.  For the i860, constraint letter 'G'
+   permits only a floating-point zero value.  */
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C)         \
+  ((C) == 'G' && CONST_DOUBLE_LOW ((VALUE)) == 0       \
+   && CONST_DOUBLE_HIGH ((VALUE)) == 0)
+
+/* Given an rtx X being reloaded into a reg required to be
+   in class CLASS, return the class of reg to actually use.
+   In general this is just CLASS; but on some machines
+   in some cases it is preferable to use a more restrictive class.
+
+   If we are trying to put an integer constant into some register, prefer an
+   integer register to an FP register.  If we are trying to put a 
+   non-zero floating-point constant into some register, use an integer
+   register if the constant is SFmode and GENERAL_REGS is one of our options.
+   Otherwise, put the constant into memory.
+
+   When reloading something smaller than a word, use a general reg
+   rather than an FP reg.  */
+
+#define PREFERRED_RELOAD_CLASS(X,CLASS)  \
+  ((CLASS) == ALL_REGS && GET_CODE (X) == CONST_INT ? GENERAL_REGS     \
+   : ((GET_MODE (X) == HImode || GET_MODE (X) == QImode)               \
+      && (CLASS) == ALL_REGS)                                          \
+   ? GENERAL_REGS                                                      \
+   : (GET_CODE (X) == CONST_DOUBLE                                     \
+      && GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT                   \
+      && ! CONST_DOUBLE_OK_FOR_LETTER_P (X, 'G'))                      \
+   ? ((CLASS) == ALL_REGS && GET_MODE (X) == SFmode ? GENERAL_REGS     \
+      : (CLASS) == GENERAL_REGS && GET_MODE (X) == SFmode ? (CLASS)    \
+      : NO_REGS)                                                       \
+   : (CLASS))
+
+/* Return the register class of a scratch register needed to copy IN into
+   a register in CLASS in MODE.  If it can be done directly, NO_REGS is
+   returned.  */
+
+#define SECONDARY_INPUT_RELOAD_CLASS(CLASS,MODE,IN) \
+  ((CLASS) == FP_REGS && CONSTANT_P (IN) ? GENERAL_REGS : NO_REGS)
+
+/* Return the maximum number of consecutive registers
+   needed to represent mode MODE in a register of class CLASS.  */
+/* On the i860, this is the size of MODE in words.  */
+#define CLASS_MAX_NREGS(CLASS, MODE)   \
+  ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+\f
+/* Stack layout; function entry, exit and calling.  */
+
+/* Define this if pushing a word on the stack
+   makes the stack pointer a smaller address.  */
+#define STACK_GROWS_DOWNWARD
+
+/* Define this if the nominal address of the stack frame
+   is at the high-address end of the local variables;
+   that is, each additional local variable allocated
+   goes at a more negative offset in the frame.  */
+#define FRAME_GROWS_DOWNWARD
+
+/* Offset within stack frame to start allocating local variables at.
+   If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
+   first local allocated.  Otherwise, it is the offset to the BEGINNING
+   of the first local allocated.  */
+#define STARTING_FRAME_OFFSET 0
+
+/* If we generate an insn to push BYTES bytes,
+   this says how many the stack pointer really advances by.
+   On the i860, don't define this because there are no push insns.  */
+/*  #define PUSH_ROUNDING(BYTES) */
+
+/* Offset of first parameter from the argument pointer register value.  */
+#define FIRST_PARM_OFFSET(FNDECL) 0
+
+/* Value is the number of bytes of arguments automatically
+   popped when returning from a subroutine call.
+   FUNDECL is the declaration node of the function (as a tree),
+   FUNTYPE is the data type of the function (as a tree),
+   or for a library call it is an identifier node for the subroutine name.
+   SIZE is the number of bytes of arguments passed on the stack.  */
+
+#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
+
+/* Define how to find the value returned by a function.
+   VALTYPE is the data type of the value (as a tree).
+   If the precise function being called is known, FUNC is its FUNCTION_DECL;
+   otherwise, FUNC is 0.  */
+
+/* On the i860, the value register depends on the mode.  */
+
+#define FUNCTION_VALUE(VALTYPE, FUNC)  \
+  gen_rtx_REG (TYPE_MODE (VALTYPE),                            \
+              (GET_MODE_CLASS (TYPE_MODE (VALTYPE)) == MODE_FLOAT      \
+               ? 40 : 16))
+
+/* Define how to find the value returned by a library function
+   assuming the value has mode MODE.  */
+
+#define LIBCALL_VALUE(MODE)                            \
+  gen_rtx_REG (MODE,                                   \
+              (GET_MODE_CLASS ((MODE)) == MODE_FLOAT   \
+               ? 40 : 16))
+
+/* 1 if N is a possible register number for a function value
+   as seen by the caller.  */
+
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == 40 || (N) == 16)
+
+/* 1 if N is a possible register number for function argument passing.
+   On the i860, these are r16-r27 and f8-f15.  */
+
+#define FUNCTION_ARG_REGNO_P(N)                \
+  (((N) < 28 && (N) > 15) || ((N) < 48 && (N) >= 40))
+\f
+/* Define a data type for recording info about an argument list
+   during the scan of that argument list.  This data type should
+   hold all necessary information about the function itself
+   and about the args processed so far, enough to enable macros
+   such as FUNCTION_ARG to determine where the next arg should go.
+
+   On the i860, we must count separately the number of general registers used
+   and the number of float registers used.  */
+
+struct cumulative_args { int ints, floats; };
+#define CUMULATIVE_ARGS struct cumulative_args
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.
+
+   On the i860, the general-reg offset normally starts at 0,
+   but starts at 4 bytes
+   when the function gets a structure-value-address as an
+   invisible first argument.  */
+
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT)      \
+ ((CUM).ints = ((FNTYPE) != 0 && aggregate_value_p (TREE_TYPE ((FNTYPE))) \
+               ? 4 : 0),                       \
+  (CUM).floats = 0)
+
+/* Machine-specific subroutines of the following macros.  */
+#define CEILING(X,Y)  (((X) + (Y) - 1) / (Y))
+#define ROUNDUP(X,Y)  (CEILING ((X), (Y)) * (Y))
+
+/* Update the data in CUM to advance over an argument
+   of mode MODE and data type TYPE.
+   (TYPE is null for libcalls where that information may not be available.)
+   Floats, and doubleword ints, are returned in f regs;
+   other ints, in r regs.
+   Aggregates, even short ones, are passed in memory.  */
+
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)           \
+ ((TYPE) != 0 && (TREE_CODE ((TYPE)) == RECORD_TYPE            \
+                 || TREE_CODE ((TYPE)) == UNION_TYPE)          \
+  ? 0                                                          \
+  : GET_MODE_CLASS ((MODE)) == MODE_FLOAT || (MODE) == DImode  \
+  ? ((CUM).floats = (ROUNDUP ((CUM).floats, GET_MODE_SIZE ((MODE)))    \
+                    + ROUNDUP (GET_MODE_SIZE (MODE), 4)))      \
+  : GET_MODE_CLASS ((MODE)) == MODE_INT                                \
+  ? ((CUM).ints = (ROUNDUP ((CUM).ints, GET_MODE_SIZE ((MODE))) \
+                  + ROUNDUP (GET_MODE_SIZE (MODE), 4)))        \
+  : 0)
+
+/* Determine where to put an argument to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).  */
+
+/* On the i860, the first 12 words of integer arguments go in r16-r27,
+   and the first 8 words of floating arguments go in f8-f15.
+   DImode values are treated as floats.  */
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED)           \
+ ((TYPE) != 0 && (TREE_CODE ((TYPE)) == RECORD_TYPE    \
+                 || TREE_CODE ((TYPE)) == UNION_TYPE)  \
+  ? 0                                                  \
+  : GET_MODE_CLASS ((MODE)) == MODE_FLOAT || (MODE) == DImode  \
+  ? (ROUNDUP ((CUM).floats, GET_MODE_SIZE ((MODE))) < 32       \
+     ? gen_rtx_REG ((MODE),                            \
+                   40 + (ROUNDUP ((CUM).floats,        \
+                                  GET_MODE_SIZE ((MODE)))      \
+                         / 4))                         \
+     : 0)                                              \
+  : GET_MODE_CLASS ((MODE)) == MODE_INT                        \
+  ? (ROUNDUP ((CUM).ints, GET_MODE_SIZE ((MODE))) < 48 \
+     ? gen_rtx_REG ((MODE),                            \
+                   16 + (ROUNDUP ((CUM).ints,          \
+                                  GET_MODE_SIZE ((MODE)))      \
+                         / 4))                         \
+     : 0)                                              \
+  : 0)
+
+/* For an arg passed partly in registers and partly in memory,
+   this is the number of registers used.
+   For args passed entirely in registers or entirely in memory, zero.  */
+
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0
+
+/* If defined, a C expression that gives the alignment boundary, in
+   bits, of an argument with the specified mode and type.  If it is
+   not defined,  `PARM_BOUNDARY' is used for all arguments.  */
+
+#define FUNCTION_ARG_BOUNDARY(MODE, TYPE)                      \
+  (((TYPE) != 0)                                               \
+   ? ((TYPE_ALIGN(TYPE) <= PARM_BOUNDARY)                      \
+      ? PARM_BOUNDARY                                          \
+      : TYPE_ALIGN(TYPE))                                      \
+   : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY)              \
+      ? PARM_BOUNDARY                                          \
+      : GET_MODE_ALIGNMENT(MODE)))
+
+/* Output a no-op just before the beginning of the function,
+   to ensure that there does not appear to be a delayed branch there.
+   Such a thing would confuse interrupt recovery.  */
+#define ASM_OUTPUT_FUNCTION_PREFIX(FILE,NAME) \
+  fprintf (FILE, "\tnop\n")
+
+/* Output assembler code to FILE to increment profiler label # LABELNO
+   for profiling a function entry.  */
+
+#define FUNCTION_PROFILER(FILE, LABELNO)  \
+   abort ();
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+   the stack pointer does not matter.  The value is tested only in
+   functions that have frame pointers.
+   No definition is equivalent to always zero.  */
+
+#define EXIT_IGNORE_STACK 1
+
+/* Generate necessary RTL for __builtin_saveregs().  */
+#define EXPAND_BUILTIN_SAVEREGS() \
+  i860_saveregs()
+
+/* Define the `__builtin_va_list' type for the ABI.  */
+#define BUILD_VA_LIST_TYPE(VALIST) \
+  (VALIST) = i860_build_va_list ()
+
+/* Implement `va_start' for varargs and stdarg.  */
+#define EXPAND_BUILTIN_VA_START(stdarg, valist, nextarg) \
+  i860_va_start (stdarg, valist, nextarg)
+
+/* Implement `va_arg'.  */
+#define EXPAND_BUILTIN_VA_ARG(valist, type) \
+  i860_va_arg (valist, type)
+
+/* Store in the variable DEPTH the initial difference between the
+   frame pointer reg contents and the stack pointer reg contents,
+   as of the start of the function body.  This depends on the layout
+   of the fixed parts of the stack frame and on how registers are saved.
+
+   On the i860, FRAME_POINTER_REQUIRED is always 1, so the definition of this
+   macro doesn't matter.  But it must be defined.  */
+
+#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \
+  do { (DEPTH) = 0; } while (0)
+
+/* Output assembler code for a block containing the constant parts
+   of a trampoline, leaving space for the variable parts.  */
+
+/* On the i860, the trampoline contains five instructions:
+     orh #TOP_OF_FUNCTION,r0,r31
+     or #BOTTOM_OF_FUNCTION,r31,r31
+     orh #TOP_OF_STATIC,r0,r29
+     bri r31
+     or #BOTTOM_OF_STATIC,r29,r29  */
+#define TRAMPOLINE_TEMPLATE(FILE)                                      \
+{                                                                      \
+  assemble_aligned_integer (UNITS_PER_WORD, GEN_INT (0xec1f0000));     \
+  assemble_aligned_integer (UNITS_PER_WORD, GEN_INT (0xe7ff0000));     \
+  assemble_aligned_integer (UNITS_PER_WORD, GEN_INT (0xec1d0000));     \
+  assemble_aligned_integer (UNITS_PER_WORD, GEN_INT (0x4000f800));     \
+  assemble_aligned_integer (UNITS_PER_WORD, GEN_INT (0xe7bd0000));     \
+}
+
+/* Length in units of the trampoline for entering a nested function.  */
+
+#define TRAMPOLINE_SIZE 20
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+   FNADDR is an RTX for the address of the function's pure code.
+   CXT is an RTX for the static chain value for the function.
+
+   Store hi function at +0, low function at +4,
+   hi static at +8, low static at +16  */
+
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT)                      \
+{                                                                      \
+  rtx cxt = force_reg (Pmode, CXT);                                    \
+  rtx fn = force_reg (Pmode, FNADDR);                                  \
+  rtx hi_cxt = expand_shift (RSHIFT_EXPR, SImode, cxt,                 \
+                            size_int (16), 0, 0);                      \
+  rtx hi_fn = expand_shift (RSHIFT_EXPR, SImode, fn,                   \
+                           size_int (16), 0, 0);                       \
+  emit_move_insn (gen_rtx_MEM (HImode, plus_constant (TRAMP, 16)),     \
+                 gen_lowpart (HImode, cxt));                           \
+  emit_move_insn (gen_rtx_MEM (HImode, plus_constant (TRAMP, 4)),      \
+                 gen_lowpart (HImode, fn));                            \
+  emit_move_insn (gen_rtx_MEM (HImode, plus_constant (TRAMP, 8)),      \
+                 gen_lowpart (HImode, hi_cxt));                        \
+  emit_move_insn (gen_rtx_MEM (HImode, plus_constant (TRAMP, 0)),      \
+                 gen_lowpart (HImode, hi_fn));                         \
+}
+\f
+/* Addressing modes, and classification of registers for them.  */
+
+/* #define HAVE_POST_INCREMENT 0 */
+/* #define HAVE_POST_DECREMENT 0 */
+
+/* #define HAVE_PRE_DECREMENT 0 */
+/* #define HAVE_PRE_INCREMENT 0 */
+
+/* Macros to check register numbers against specific register classes.  */
+
+/* These assume that REGNO is a hard or pseudo reg number.
+   They give nonzero only if REGNO is a hard reg of the suitable class
+   or a pseudo reg currently allocated to a suitable hard reg.
+   Since they use reg_renumber, they are safe only once reg_renumber
+   has been allocated, which happens in local-alloc.c.  */
+
+#define REGNO_OK_FOR_INDEX_P(REGNO) \
+((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32)
+#define REGNO_OK_FOR_BASE_P(REGNO) \
+((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32)
+#define REGNO_OK_FOR_FP_P(REGNO) \
+(((REGNO) ^ 0x20) < 32 || (unsigned) (reg_renumber[REGNO] ^ 0x20) < 32)
+
+/* Now macros that check whether X is a register and also,
+   strictly, whether it is in a specified class.
+
+   These macros are specific to the i860, and may be used only
+   in code for printing assembler insns and in conditions for
+   define_optimization.  */
+
+/* 1 if X is an fp register.  */
+
+#define FP_REG_P(X) (REG_P (X) && REGNO_OK_FOR_FP_P (REGNO (X)))
+\f
+/* Maximum number of registers that can appear in a valid memory address.  */
+
+#define MAX_REGS_PER_ADDRESS 2
+
+/* Recognize any constant value that is a valid address.  */
+
+#define CONSTANT_ADDRESS_P(X)   \
+  (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF             \
+   || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST               \
+   || GET_CODE (X) == HIGH)
+
+/* Nonzero if the constant value X is a legitimate general operand.
+   It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.
+
+   On the Sparc, this is anything but a CONST_DOUBLE.
+   Let's try permitting CONST_DOUBLEs and see what happens.  */
+
+#define LEGITIMATE_CONSTANT_P(X) 1
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
+   and check its validity for a certain class.
+   We have two alternate definitions for each of them.
+   The usual definition accepts all pseudo regs; the other rejects
+   them unless they have been allocated suitable hard regs.
+   The symbol REG_OK_STRICT causes the latter definition to be used.
+
+   Most source files want to accept pseudo regs in the hope that
+   they will get allocated to the class that the insn wants them to be in.
+   Source files for reload pass need to be strict.
+   After reload, it makes no difference, since pseudo regs have
+   been eliminated by then.  */
+
+#ifndef REG_OK_STRICT
+
+/* Nonzero if X is a hard reg that can be used as an index
+   or if it is a pseudo reg.  */
+#define REG_OK_FOR_INDEX_P(X) (((unsigned) REGNO (X)) - 32 >= 14)
+/* Nonzero if X is a hard reg that can be used as a base reg
+   or if it is a pseudo reg.  */
+#define REG_OK_FOR_BASE_P(X) (((unsigned) REGNO (X)) - 32 >= 14)
+
+#else
+
+/* Nonzero if X is a hard reg that can be used as an index.  */
+#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X))
+/* Nonzero if X is a hard reg that can be used as a base reg.  */
+#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X))
+
+#endif
+\f
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression
+   that is a valid memory address for an instruction.
+   The MODE argument is the machine mode for the MEM expression
+   that wants to use this address.
+
+   On the i860, the actual addresses must be REG+REG or REG+SMALLINT.
+   But we can treat a SYMBOL_REF as legitimate if it is part of this
+   function's constant-pool, because such addresses can actually
+   be output as REG+SMALLINT.
+
+   The displacement in an address must be a multiple of the alignment.
+
+   Try making SYMBOL_REF (and other things which are CONSTANT_ADDRESS_P)
+   a legitimate address, regardless.  Because the only insns which can use
+   memory are load or store insns, the added hair in the machine description
+   is not that bad.  It should also speed up the compiler by halving the number
+   of insns it must manage for each (MEM (SYMBOL_REF ...)) involved.  */
+
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR)                \
+{ if (GET_CODE (X) == REG)                             \
+    { if (REG_OK_FOR_BASE_P (X)) goto ADDR; }          \
+  else if (GET_CODE (X) == PLUS)                       \
+    {                                                  \
+      if (GET_CODE (XEXP (X, 0)) == REG                        \
+         && REG_OK_FOR_BASE_P (XEXP (X, 0)))           \
+       {                                               \
+         if (GET_CODE (XEXP (X, 1)) == CONST_INT       \
+             && INTVAL (XEXP (X, 1)) >= -0x8000        \
+             && INTVAL (XEXP (X, 1)) < 0x8000          \
+             && (INTVAL (XEXP (X, 1)) & (GET_MODE_SIZE (MODE) - 1)) == 0) \
+           goto ADDR;                                  \
+       }                                               \
+      else if (GET_CODE (XEXP (X, 1)) == REG           \
+         && REG_OK_FOR_BASE_P (XEXP (X, 1)))           \
+       {                                               \
+         if (GET_CODE (XEXP (X, 0)) == CONST_INT       \
+             && INTVAL (XEXP (X, 0)) >= -0x8000        \
+             && INTVAL (XEXP (X, 0)) < 0x8000          \
+             && (INTVAL (XEXP (X, 0)) & (GET_MODE_SIZE (MODE) - 1)) == 0) \
+           goto ADDR;                                  \
+       }                                               \
+    }                                                  \
+  else if (CONSTANT_ADDRESS_P (X))                     \
+    goto ADDR;                                         \
+}
+\f
+/* Try machine-dependent ways of modifying an illegitimate address
+   to be legitimate.  If we find one, return the new, valid address.
+   This macro is used in only one place: `memory_address' in explow.c.
+
+   OLDX is the address as it was before break_out_memory_refs was called.
+   In some cases it is useful to look at this to decide what needs to be done.
+
+   MODE and WIN are passed so that this macro can use
+   GO_IF_LEGITIMATE_ADDRESS.
+
+   It is always safe for this macro to do nothing.  It exists to recognize
+   opportunities to optimize the output.  */
+
+/* On the i860, change COMPLICATED + CONSTANT to REG+CONSTANT.
+   Also change a symbolic constant to a REG,
+   though that may not be necessary.  */
+
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN)    \
+{ if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == MULT)  \
+    (X) = gen_rtx_PLUS (SImode, XEXP (X, 1),                   \
+                       force_operand (XEXP (X, 0), 0));        \
+  if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == MULT)  \
+    (X) = gen_rtx_PLUS (SImode, XEXP (X, 0),                   \
+                       force_operand (XEXP (X, 1), 0));        \
+  if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == PLUS)  \
+    (X) = gen_rtx_PLUS (SImode, XEXP (X, 1),                   \
+                       force_operand (XEXP (X, 0), 0));        \
+  if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == PLUS)  \
+    (X) = gen_rtx_PLUS (SImode, XEXP (X, 0),                   \
+                       force_operand (XEXP (X, 1), 0));        \
+  if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) != REG    \
+      && GET_CODE (XEXP (X, 0)) != CONST_INT)                  \
+    (X) = gen_rtx_PLUS (SImode, XEXP (X, 1),                   \
+                       copy_to_mode_reg (SImode, XEXP (X, 0))); \
+  if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) != REG    \
+      && GET_CODE (XEXP (X, 1)) != CONST_INT)                  \
+    (X) = gen_rtx_PLUS (SImode, XEXP (X, 0),                   \
+                       copy_to_mode_reg (SImode, XEXP (X, 1))); \
+  if (GET_CODE (x) == SYMBOL_REF)                              \
+    (X) = copy_to_reg (X);                                     \
+  if (GET_CODE (x) == CONST)                                   \
+    (X) = copy_to_reg (X);                                     \
+  if (memory_address_p (MODE, X))                              \
+    goto WIN; }
+
+/* Go to LABEL if ADDR (a legitimate address expression)
+   has an effect that depends on the machine mode it is used for.
+   On the i860 this is never true.
+   There are some addresses that are invalid in wide modes
+   but valid for narrower modes, but they shouldn't affect
+   the places that use this macro.  */
+
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)
+\f
+/* Specify the machine mode that this machine uses
+   for the index in the tablejump instruction.  */
+#define CASE_VECTOR_MODE SImode
+
+/* Define as C expression which evaluates to nonzero if the tablejump
+   instruction expects the table to contain offsets from the address of the
+   table.
+   Do not define this if the table should contain absolute addresses. */
+/* #define CASE_VECTOR_PC_RELATIVE 1 */
+
+/* Must pass floats to libgcc functions as doubles.  */
+#define LIBGCC_NEEDS_DOUBLE 1
+
+#define DIVSI3_LIBCALL "*.div"
+#define UDIVSI3_LIBCALL "*.udiv"
+#define REMSI3_LIBCALL "*.rem"
+#define UREMSI3_LIBCALL "*.urem"
+
+/* Define this as 1 if `char' should by default be signed; else as 0.  */
+#define DEFAULT_SIGNED_CHAR 1
+
+/* Max number of bytes we can move from memory to memory
+   in one reasonably fast instruction.  */
+#define MOVE_MAX 16
+
+/* Nonzero if access to memory by bytes is slow and undesirable.  */
+#define SLOW_BYTE_ACCESS 0
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+   is done just by pretending it is already truncated.  */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+/* Value is 1 if it generates better code to perform an unsigned comparison
+   on the given literal integer value in the given mode when we are only
+   looking for an equal/non-equal result.  */
+/* For the i860, if the immediate value has its high-order 27 bits zero,
+   then we want to engineer an unsigned comparison for EQ/NE because
+   such values can fit in the 5-bit immediate field of a bte or btne
+   instruction (which gets zero extended before comparing).  For all
+   other immediate values on the i860, we will use signed compares
+   because that avoids the need for doing explicit xor's to zero_extend
+   the non-constant operand in cases where it was (mem:QI ...) or a
+   (mem:HI ...) which always gets automatically sign-extended by the
+   hardware upon loading.  */
+
+#define LITERAL_COMPARE_BETTER_UNSIGNED(intval, mode)                   \
+  (((unsigned) (intval) & 0x1f) == (unsigned) (intval))
+
+/* Specify the machine mode that pointers have.
+   After generation of rtl, the compiler makes no further distinction
+   between pointers and any other objects of this machine mode.  */
+#define Pmode SImode
+
+/* A function address in a call instruction
+   is a byte address (for indexing purposes)
+   so give the MEM rtx a byte's mode.  */
+#define FUNCTION_MODE SImode
+
+/* Define this if addresses of constant functions
+   shouldn't be put through pseudo regs where they can be cse'd.
+   Desirable on machines where ordinary constants are expensive
+   but a CALL with constant address is cheap.  */
+#define NO_FUNCTION_CSE
+
+/* Compute the cost of computing a constant rtl expression RTX
+   whose rtx-code is CODE.  The body of this macro is a portion
+   of a switch statement.  If the code is computed here,
+   return it with a return statement.  Otherwise, break from the switch.  */
+
+#define CONST_COSTS(RTX,CODE, OUTER_CODE)                      \
+  case CONST_INT:                                              \
+    if (INTVAL (RTX) == 0)                                     \
+      return 0;                                                        \
+    if (INTVAL (RTX) < 0x2000 && INTVAL (RTX) >= -0x2000) return 1; \
+  case CONST:                                                  \
+  case LABEL_REF:                                              \
+  case SYMBOL_REF:                                             \
+    return 4;                                                  \
+  case CONST_DOUBLE:                                           \
+    return 6;
+
+/* Specify the cost of a branch insn; roughly the number of extra insns that
+   should be added to avoid a branch.
+
+   Set this to 3 on the i860 since branches may often take three cycles.  */
+
+#define BRANCH_COST 3
+\f
+/* Tell final.c how to eliminate redundant test instructions.  */
+
+/* Here we define machine-dependent flags and fields in cc_status
+   (see `conditions.h').  */
+
+/* This holds the value sourcing h%r31.  We keep this info
+   around so that mem/mem ops, such as increment and decrement,
+   etc, can be performed reasonably.  */
+#define CC_STATUS_MDEP rtx
+
+#define CC_STATUS_MDEP_INIT (cc_status.mdep = 0)
+
+#define CC_NEGATED     01000
+
+/* We use this macro in those places in the i860.md file where we would
+   normally just do a CC_STATUS_INIT (for other machines).  This macro
+   differs from CC_STATUS_INIT in that it doesn't mess with the special
+   bits or fields which describe what is currently in the special r31
+   scratch register, but it does clear out everything that actually
+   relates to the condition code bit of the i860.  */
+
+#define CC_STATUS_PARTIAL_INIT                                         \
+ (cc_status.flags &= (CC_KNOW_HI_R31 | CC_HI_R31_ADJ),                 \
+  cc_status.value1 = 0,                                                        \
+  cc_status.value2 = 0)
+
+/* Nonzero if we know the value of h%r31.  */
+#define CC_KNOW_HI_R31 0100000
+
+/* Nonzero if h%r31 is actually ha%something, rather than h%something.  */
+#define CC_HI_R31_ADJ 0200000
+
+/* Store in cc_status the expressions
+   that the condition codes will describe
+   after execution of an instruction whose pattern is EXP.
+   Do not alter them if the instruction would not alter the cc's.  */
+
+/* On the i860, only compare insns set a useful condition code.  */
+
+#define NOTICE_UPDATE_CC(EXP, INSN) \
+{ cc_status.flags &= (CC_KNOW_HI_R31 | CC_HI_R31_ADJ); \
+  cc_status.value1 = 0; cc_status.value2 = 0; }
+\f
+/* Control the assembler format that we output.  */
+
+/* Assembler pseudos to introduce constants of various size.  */
+
+#define ASM_DOUBLE "\t.double"
+
+/* Output at beginning of assembler file.  */
+/* The .file command should always begin the output.  */
+
+#define ASM_FILE_START(FILE)
+#if 0
+#define ASM_FILE_START(FILE)                                   \
+  do { output_file_directive ((FILE), main_input_filename);    \
+       if (optimize) ASM_FILE_START_1 (FILE);                  \
+     } while (0)
+#endif
+
+#define ASM_FILE_START_1(FILE)
+
+/* Output to assembler file text saying following lines
+   may contain character constants, extra white space, comments, etc.  */
+
+#define ASM_APP_ON ""
+
+/* Output to assembler file text saying following lines
+   no longer contain unusual constructs.  */
+
+#define ASM_APP_OFF ""
+
+/* Output before read-only data.  */
+
+#define TEXT_SECTION_ASM_OP "\t.text"
+
+/* Output before writable data.  */
+
+#define DATA_SECTION_ASM_OP "\t.data"
+
+/* How to refer to registers in assembler output.
+   This sequence is indexed by compiler's hard-register-number (see above).  */
+
+#define REGISTER_NAMES \
+{"r0", "r1", "sp", "fp", "r4", "r5", "r6", "r7", "r8", "r9",           \
+ "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", \
+ "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \
+ "r30", "r31",                                                         \
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9",           \
+ "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", \
+ "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", \
+ "f30", "f31" }
+
+/* This is how to output the definition of a user-level label named NAME,
+   such as the label on a static function or variable NAME.  */
+
+#define ASM_OUTPUT_LABEL(FILE,NAME)    \
+  do { assemble_name (FILE, NAME); fputs (":\n", FILE); } while (0)
+
+/* This is how to output a command to make the user-level label named NAME
+   defined for reference from other files.  */
+
+#define ASM_GLOBALIZE_LABEL(FILE,NAME)                                 \
+  do { fputs (".globl ", FILE);                                        \
+       assemble_name (FILE, NAME);                                     \
+       fputs ("\n", FILE);                                             \
+  } while (0)
+
+/* The prefix to add to user-visible assembler symbols.
+
+   This definition is overridden in i860v4.h because under System V
+   Release 4, user-level symbols are *not* prefixed with underscores in
+   the generated assembly code.  */
+
+#define USER_LABEL_PREFIX "_"
+
+/* This is how to output an internal numbered label where
+   PREFIX is the class of label and NUM is the number within the class.  */
+
+#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM)     \
+  fprintf (FILE, ".%s%d:\n", PREFIX, NUM)
+
+/* This is how to output an internal numbered label which
+   labels a jump table.  */
+
+#undef ASM_OUTPUT_CASE_LABEL
+#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, JUMPTABLE)            \
+do { ASM_OUTPUT_ALIGN ((FILE), 2);                                     \
+     ASM_OUTPUT_INTERNAL_LABEL ((FILE), PREFIX, NUM);                  \
+   } while (0)
+
+/* Output at the end of a jump table.  */
+
+#define ASM_OUTPUT_CASE_END(FILE,NUM,INSN)     \
+  fprintf (FILE, ".text\n")
+
+/* This is how to store into the string LABEL
+   the symbol_ref name of an internal numbered label where
+   PREFIX is the class of label and NUM is the number within the class.
+   This is suitable for output with `assemble_name'.  */
+
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM)  \
+  sprintf (LABEL, "*.%s%d", PREFIX, NUM)
+
+/* This is how to output code to push a register on the stack.
+   It need not be very fast code.  */
+
+#define ASM_OUTPUT_REG_PUSH(FILE,REGNO)                                        \
+  fprintf (FILE, "\taddu -16,%ssp,%ssp\n\t%sst.l %s%s,0(%ssp)\n",      \
+       i860_reg_prefix, i860_reg_prefix,                               \
+       ((REGNO) < 32 ? "" : "f"),                                      \
+       i860_reg_prefix, reg_names[REGNO],                              \
+       i860_reg_prefix)
+
+/* This is how to output an insn to pop a register from the stack.
+   It need not be very fast code.  */
+
+#define ASM_OUTPUT_REG_POP(FILE,REGNO)                                 \
+  fprintf (FILE, "\t%sld.l 0(%ssp),%s%s\n\taddu 16,%ssp,%ssp\n",       \
+       ((REGNO) < 32 ? "" : "f"),                                      \
+       i860_reg_prefix,                                                \
+       i860_reg_prefix, reg_names[REGNO],                              \
+       i860_reg_prefix, i860_reg_prefix)
+
+/* This is how to output an element of a case-vector that is absolute.  */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE)  \
+  fprintf (FILE, "\t.long .L%d\n", VALUE)
+
+/* This is how to output an element of a case-vector that is relative.
+   (The i860 does not use such vectors,
+   but we must define this macro anyway.)  */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL)  \
+  fprintf (FILE, "\t.word .L%d-.L%d\n", VALUE, REL)
+
+/* This is how to output an assembler line
+   that says to advance the location counter
+   to a multiple of 2**LOG bytes.  */
+
+#define ASM_OUTPUT_ALIGN(FILE,LOG)     \
+  if ((LOG) != 0)                      \
+    fprintf (FILE, "\t.align %d\n", 1 << (LOG))
+
+#define ASM_OUTPUT_SKIP(FILE,SIZE)  \
+  fprintf (FILE, "\t.blkb %u\n", (SIZE))
+
+/* This says how to output an assembler line
+   to define a global common symbol.  */
+
+#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED)  \
+( fputs (".comm ", (FILE)),                    \
+  assemble_name ((FILE), (NAME)),              \
+  fprintf ((FILE), ",%u\n", (ROUNDED)))
+
+/* This says how to output an assembler line
+   to define a local common symbol.  */
+
+#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED)  \
+( fputs (".lcomm ", (FILE)),                   \
+  assemble_name ((FILE), (NAME)),              \
+  fprintf ((FILE), ",%u\n", (ROUNDED)))
+
+/* Store in OUTPUT a string (made with alloca) containing
+   an assembler-name for a local static variable named NAME.
+   LABELNO is an integer which is different for each call.  */
+
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
+( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10),   \
+  sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO)))
+
+/* Print operand X (an rtx) in assembler syntax to file FILE.
+   CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
+   For `%' followed by punctuation, CODE is the punctuation and X is null.
+
+   In the following comments, the term "constant address" is used frequently.
+   For an exact definition of what constitutes a "constant address" see the
+   output_addr_const routine in final.c
+
+   On the i860, the following target-specific special codes are recognized:
+
+       `r'     The operand can be anything, but if it is an immediate zero
+               value (either integer or floating point) then it will be
+               represented as `r0' or as `f0' (respectively).
+
+       `m'     The operand is a memory ref (to a constant address) but print
+               its address as a constant.
+
+       `L'     The operand is a numeric constant, a constant address, or
+               a memory ref to a constant address.  Print the correct
+               notation to yield the low part of the given value or
+               address or the low part of the address of the referred
+               to memory object.
+
+       `H'     The operand is a numeric constant, a constant address, or
+               a memory ref to a constant address.  Print the correct
+               notation to yield the high part of the given value or
+               address or the high part of the address of the referred
+               to memory object.
+
+       `h'     The operand is a numeric constant, a constant address, or
+               a memory ref to a constant address.  Either print the
+               correct notation to yield the plain high part of the
+               given value or address (or the plain high part of the
+               address of the memory object) or else print the correct
+               notation to yield the "adjusted" high part of the given
+               address (or of the address of the referred to memory object).
+
+               The choice of what to print depends upon whether the address
+               in question is relocatable or not.  If it is relocatable,
+               print the notation to get the adjusted high part.  Otherwise
+               just print the notation to get the plain high part.  Note
+               that "adjusted" high parts are generally used *only* when
+               the next following instruction uses the low part of the
+               address as an offset, as in `offset(reg)'.
+
+       `R'     The operand is a floating-pointer register.  Print the
+               name of the next following (32-bit) floating-point register.
+               (This is used when moving a value into just the most
+               significant part of a floating-point register pair.)
+
+       `?'     (takes no operand) Substitute the value of i860_reg_prefix
+               at this point.  The value of i860_reg_prefix is typically
+               a null string for most i860 targets, but for System V
+               Release 4 the i860 assembler syntax requires that all
+               names of registers be prefixed with a percent-sign, so
+               for SVR4, the value of i860_reg_prefix is initialized to
+               "%" in i860.c.
+*/
+
+extern const char *i860_reg_prefix;
+
+#define PRINT_OPERAND_PUNCT_VALID_P(CODE) ((CODE) == '?')
+
+/* The following macro definition is overridden in i860v4.h
+   because the svr4 i860 assembler required a different syntax
+   for getting parts of constant/relocatable values.  */
+
+#define PRINT_OPERAND_PART(FILE, X, PART_CODE)                         \
+  do { fprintf (FILE, "%s%%", PART_CODE);                              \
+       output_address (X);                                             \
+  } while (0)
+
+#define OPERAND_LOW_PART       "l"
+#define OPERAND_HIGH_PART      "h"
+/* NOTE: All documentation available for the i860 sez that you must
+   use "ha" to get the relocated high part of a relocatable, but
+   reality sez different.  */
+#define OPERAND_HIGH_ADJ_PART  "ha"
+
+#define PRINT_OPERAND(FILE, X, CODE)                                   \
+{ if ((CODE) == '?')                                                   \
+    fprintf (FILE, "%s", i860_reg_prefix);                             \
+  else if (CODE == 'R')                                                        \
+    fprintf (FILE, "%s%s", i860_reg_prefix, reg_names[REGNO (X) + 1]); \
+  else if (GET_CODE (X) == REG)                                                \
+    fprintf (FILE, "%s%s", i860_reg_prefix, reg_names[REGNO (X)]);     \
+  else if ((CODE) == 'm')                                              \
+    output_address (XEXP (X, 0));                                      \
+  else if ((CODE) == 'L')                                              \
+    {                                                                  \
+      if (GET_CODE (X) == MEM)                                         \
+       PRINT_OPERAND_PART (FILE, XEXP (X, 0), OPERAND_LOW_PART);       \
+      else                                                             \
+       PRINT_OPERAND_PART (FILE, X, OPERAND_LOW_PART);                 \
+    }                                                                  \
+  else if ((CODE) == 'H')                                              \
+    {                                                                  \
+      if (GET_CODE (X) == MEM)                                         \
+       PRINT_OPERAND_PART (FILE, XEXP (X, 0), OPERAND_HIGH_PART);      \
+      else                                                             \
+       PRINT_OPERAND_PART (FILE, X, OPERAND_HIGH_PART);                \
+    }                                                                  \
+  else if ((CODE) == 'h')                                              \
+    {                                                                  \
+      if (GET_CODE (X) == MEM)                                         \
+       PRINT_OPERAND_PART (FILE, XEXP (X, 0), OPERAND_HIGH_ADJ_PART);  \
+      else                                                             \
+       PRINT_OPERAND_PART (FILE, X, OPERAND_HIGH_ADJ_PART);            \
+    }                                                                  \
+  else if (GET_CODE (X) == MEM)                                                \
+    output_address (XEXP (X, 0));                                      \
+  else if ((CODE) == 'r' && (X) == const0_rtx)                         \
+    fprintf (FILE, "%sr0", i860_reg_prefix);                           \
+  else if ((CODE) == 'r' && (X) == CONST0_RTX (GET_MODE (X)))          \
+    fprintf (FILE, "%sf0", i860_reg_prefix);                           \
+  else if (GET_CODE (X) == CONST_DOUBLE)                               \
+    fprintf (FILE, "0x%lx", sfmode_constant_to_ulong (X));             \
+  else                                                                 \
+    output_addr_const (FILE, X); }
+\f
+/* Print a memory address as an operand to reference that memory location.  */
+
+#define PRINT_OPERAND_ADDRESS(FILE, ADDR)  \
+{ register rtx addr = ADDR;                                    \
+  if (GET_CODE (addr) == REG)                                  \
+    {                                                          \
+      fprintf (FILE, "0(%s%s)",                                        \
+       i860_reg_prefix, reg_names[REGNO (addr)]);              \
+    }                                                          \
+  else if (GET_CODE (addr) == CONST_DOUBLE                     \
+            && GET_MODE (addr) == SFmode)                      \
+    fprintf (FILE, "0x%lx", sfmode_constant_to_ulong (addr));  \
+  else if (GET_CODE (addr) == PLUS)                            \
+    {                                                          \
+      if ((GET_CODE (XEXP (addr, 0)) == CONST_INT)             \
+         && (GET_CODE (XEXP (addr, 1)) == REG))                \
+       fprintf (FILE, "%d(%s%s)", INTVAL (XEXP (addr, 0)),     \
+           i860_reg_prefix, reg_names[REGNO (XEXP (addr, 1))]);\
+      else if ((GET_CODE (XEXP (addr, 1)) == CONST_INT)                \
+         && (GET_CODE (XEXP (addr, 0)) == REG))                \
+       fprintf (FILE, "%d(%s%s)", INTVAL (XEXP (addr, 1)),     \
+           i860_reg_prefix, reg_names[REGNO (XEXP (addr, 0))]);\
+      else if ((GET_CODE (XEXP (addr, 0)) == REG)              \
+         && (GET_CODE (XEXP (addr, 1)) == REG))                \
+       fprintf (FILE, "%s%s(%s%s)",                            \
+           i860_reg_prefix, reg_names[REGNO (XEXP (addr, 0))], \
+           i860_reg_prefix, reg_names[REGNO (XEXP (addr, 1))]);\
+      else                                                     \
+       output_addr_const (FILE, addr);                         \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      output_addr_const (FILE, addr);                          \
+    }                                                          \
+}
+
+/* Optionally define this if you have added predicates to
+   `MACHINE.c'.  This macro is called within an initializer of an
+   array of structures.  The first field in the structure is the
+   name of a predicate and the second field is an array of rtl
+   codes.  For each predicate, list all rtl codes that can be in
+   expressions matched by the predicate.  The list should have a
+   trailing comma.  Here is an example of two entries in the list
+   for a typical RISC machine:
+
+   #define PREDICATE_CODES \
+     {"gen_reg_rtx_operand", {SUBREG, REG}},  \
+     {"reg_or_short_cint_operand", {SUBREG, REG, CONST_INT}},
+
+   Defining this macro does not affect the generated code (however,
+   incorrect definitions that omit an rtl code that may be matched
+   by the predicate can cause the compiler to malfunction). 
+   Instead, it allows the table built by `genrecog' to be more
+   compact and efficient, thus speeding up the compiler.  The most
+   important predicates to include in the list specified by this
+   macro are thoses used in the most insn patterns.  */
+
+#define PREDICATE_CODES                                                        \
+   {"reg_or_0_operand",                {REG, SUBREG, CONST_INT}},              \
+   {"arith_operand",           {REG, SUBREG, CONST_INT}},              \
+   {"logic_operand",           {REG, SUBREG, CONST_INT}},              \
+   {"shift_operand",           {REG, SUBREG, CONST_INT}},              \
+   {"compare_operand",         {REG, SUBREG, CONST_INT}},              \
+   {"arith_const_operand",     {CONST_INT}},                           \
+   {"logic_const_operand",     {CONST_INT}},                           \
+   {"bte_operand",             {REG, SUBREG, CONST_INT}},              \
+   {"indexed_operand",         {MEM}},                                 \
+   {"load_operand",            {MEM}},                                 \
+   {"small_int",               {CONST_INT}},                           \
+   {"logic_int",               {CONST_INT}},                           \
+   {"call_insn_operand",       {MEM}},
+
+/* Define the information needed to generate branch insns.  This is stored
+   from the compare operation.  Note that we can't use "rtx" here since it
+   hasn't been defined!  */
+
+extern struct rtx_def *i860_compare_op0, *i860_compare_op1;
diff --git a/gcc/config/i860/i860.md b/gcc/config/i860/i860.md
new file mode 100644 (file)
index 0000000..b866c91
--- /dev/null
@@ -0,0 +1,2327 @@
+;;- Machine description for Intel 860 chip for GNU C compiler
+;;  Copyright (C) 1989, 1990, 1997, 1998, 1999, 2000
+;;  Free Software Foundation, Inc.
+
+;; 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, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+
+;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
+
+;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
+;;- updates for most instructions.
+
+;;- Operand classes for the register allocator:
+\f
+/* Bit-test instructions.  */
+
+(define_insn ""
+  [(set (cc0) (eq (and:SI (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "logic_operand" "rL"))
+                 (const_int 0)))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"and %1,%0,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0) (ne (and:SI (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "logic_operand" "rL"))
+                 (const_int 0)))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"and %1,%0,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0) (eq (and:SI (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "immediate_operand" "i"))
+                 (const_int 0)))]
+  "GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 0xffff) == 0"
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"andh %H1,%0,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0) (ne (and:SI (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "immediate_operand" "i"))
+                 (const_int 0)))]
+  "GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 0xffff) == 0"
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"andh %H1,%0,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0) (eq (ashiftrt:SI
+                  (sign_extend:SI
+                   (ashift:QI (match_operand:QI 0 "register_operand" "r")
+                              (match_operand:QI 1 "logic_int" "n")))
+                  (match_operand:SI 2 "logic_int" "n"))
+                 (const_int 0)))]
+  ""
+  "*
+{
+  int width = 8 - INTVAL (operands[2]);
+  int pos = 8 - width - INTVAL (operands[1]);
+
+  CC_STATUS_PARTIAL_INIT;
+  operands[2] = GEN_INT (~((-1) << width) << pos);
+  return \"and %2,%0,%?r0\";
+}")
+\f
+;; -------------------------------------------------------------------------
+;; SImode signed integer comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn "cmpeqsi"
+  [(set (cc0) (eq (match_operand:SI 0 "logic_operand" "r,rL")
+                 (match_operand:SI 1 "logic_operand" "L,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[0]))
+    return \"xor %1,%0,%?r0\";
+  else
+    return \"xor %0,%1,%?r0\";
+}")
+
+(define_insn "cmpnesi"
+  [(set (cc0) (ne (match_operand:SI 0 "logic_operand" "r,rL")
+                 (match_operand:SI 1 "logic_operand" "L,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  if (REG_P (operands[0]))
+    return \"xor %1,%0,%?r0\";
+  else
+    return \"xor %0,%1,%?r0\";
+}")
+
+(define_insn "cmpltsi"
+  [(set (cc0) (lt (match_operand:SI 0 "arith_operand" "r,rI")
+                 (match_operand:SI 1 "arith_operand" "I,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[1]))
+    return \"subs %0,%1,%?r0\";
+  else
+    {
+      cc_status.flags |= CC_REVERSED;
+      operands[1] = GEN_INT (- INTVAL (operands[1]));
+      return \"adds %1,%0,%?r0\";
+    }
+}")
+
+(define_insn "cmpgtsi"
+  [(set (cc0) (gt (match_operand:SI 0 "arith_operand" "r,rI")
+                 (match_operand:SI 1 "arith_operand" "I,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[0]))
+    return \"subs %1,%0,%?r0\";
+  else
+    {
+      cc_status.flags |= CC_REVERSED;
+      operands[0] = GEN_INT (- INTVAL (operands[0]));
+      return \"adds %0,%1,%?r0\";
+    }
+}")
+
+(define_insn "cmplesi"
+  [(set (cc0) (le (match_operand:SI 0 "arith_operand" "r,rI")
+                 (match_operand:SI 1 "arith_operand" "I,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  if (REG_P (operands[0]))
+    return \"subs %1,%0,%?r0\";
+  else
+    {
+      cc_status.flags |= CC_REVERSED;
+      operands[0] = GEN_INT (- INTVAL (operands[0]));
+      return \"adds %0,%1,%?r0\";
+    }
+}")
+
+(define_insn "cmpgesi"
+  [(set (cc0) (ge (match_operand:SI 0 "arith_operand" "r,rI")
+                 (match_operand:SI 1 "arith_operand" "I,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  if (REG_P (operands[1]))
+    return \"subs %0,%1,%?r0\";
+  else
+    {
+      cc_status.flags |= CC_REVERSED;
+      operands[1] = GEN_INT (- INTVAL (operands[1]));
+      return \"adds %1,%0,%?r0\";
+    }
+}")
+
+;; -------------------------------------------------------------------------
+;; SImode unsigned integer comparisons
+;; -------------------------------------------------------------------------
+
+;; WARNING!  There is a small i860 hardware limitation (bug?) which we
+;; may run up against (if we are not careful) when we are trying to do
+;; unsigned comparisons like (x >= 0), (x < 0), (0 <= x), and (0 > x).
+;; Specifically, we must avoid using an `addu' instruction to perform
+;; such comparisons because the result (in the CC bit register) will
+;; come out wrong.  (This fact is documented in a footnote on page 7-10
+;; of the 1991 version of the i860 Microprocessor Family Programmer's
+;; Reference Manual).  Note that unsigned comparisons of this sort are
+;; always redundant anyway, because an unsigned quantity can never be
+;; less than zero.  When we see cases like this, we generate an
+;; `or K,%r0,%r0' instruction instead (where K is a constant 0 or -1)
+;; so as to get the CC bit register set properly for any subsequent
+;; conditional jump instruction.
+
+(define_insn "cmpgeusi"
+  [(set (cc0) (geu (match_operand:SI 0 "arith_operand" "r,rI")
+                  (match_operand:SI 1 "arith_operand" "I,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[1]))
+    return \"subu %0,%1,%?r0\";
+  else
+    {
+      if (INTVAL (operands[1]) == 0)
+       return \"or 0,%?r0,%?r0\";
+      else
+       {
+         cc_status.flags |= CC_REVERSED;
+         operands[1] = GEN_INT (- INTVAL (operands[1]));
+         return \"addu %1,%0,%?r0\";
+       }
+    }
+}")
+
+(define_insn "cmpleusi"
+  [(set (cc0) (leu (match_operand:SI 0 "arith_operand" "r,rI")
+                  (match_operand:SI 1 "arith_operand" "I,r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[0]))
+    return \"subu %1,%0,%?r0\";
+  else
+    {
+      if (INTVAL (operands[0]) == 0)
+       return \"or 0,%?r0,%?r0\";
+      else
+       {
+         cc_status.flags |= CC_REVERSED;
+         operands[0] = GEN_INT (- INTVAL (operands[0]));
+         return \"addu %0,%1,%?r0\";
+       }
+    }
+}")
+
+;; -------------------------------------------------------------------------
+;; SFmode floating-point comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn "cmpeqsf"
+  [(set (cc0) (eq (match_operand:SF 0 "reg_or_0_operand" "fG")
+                 (match_operand:SF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"pfeq.ss %r0,%r1,%?f0\";
+}")
+
+(define_insn "cmpnesf"
+  [(set (cc0) (ne (match_operand:SF 0 "reg_or_0_operand" "fG")
+                 (match_operand:SF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"pfeq.ss %r1,%r0,%?f0\";
+}")
+
+;; NOTE:  The i860 Programmer's Reference Manual says that when we are
+;; doing (A < B) or (A > B) comparisons, we have to use pfgt for these
+;; in order to be IEEE compliant (in case a trap occurs during these
+;; operations).  Conversely, for (A <= B) or (A >= B) comparisons, we
+;; must use pfle to be IEEE compliant.
+
+(define_insn "cmpltsf"
+  [(set (cc0) (lt (match_operand:SF 0 "reg_or_0_operand" "fG")
+                 (match_operand:SF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"pfgt.ss %r1,%r0,%?f0\";
+}")
+
+(define_insn "cmpgtsf"
+  [(set (cc0) (gt (match_operand:SF 0 "reg_or_0_operand" "fG")
+                 (match_operand:SF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"pfgt.ss %r0,%r1,%?f0\";
+}")
+
+;; NOTE:  The pfle opcode doesn't do what you think it does.  It is
+;; bass-ackwards.  It *clears* the CC flag if the first operand is
+;; less than or equal to the second.  Thus, we have to set CC_NEGATED
+;; for the following two patterns.
+
+(define_insn "cmplesf"
+  [(set (cc0) (le (match_operand:SF 0 "reg_or_0_operand" "fG")
+                 (match_operand:SF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"pfle.ss %r0,%r1,%?f0\";
+}")
+
+(define_insn "cmpgesf"
+  [(set (cc0) (ge (match_operand:SF 0 "reg_or_0_operand" "fG")
+                 (match_operand:SF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"pfle.ss %r1,%r0,%?f0\";
+}")
+
+;; -------------------------------------------------------------------------
+;; DFmode floating-point comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn "cmpeqdf"
+  [(set (cc0) (eq (match_operand:DF 0 "reg_or_0_operand" "fG")
+                 (match_operand:DF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"pfeq.dd %r0,%r1,%?f0\";
+}")
+
+(define_insn "cmpnedf"
+  [(set (cc0) (ne (match_operand:DF 0 "reg_or_0_operand" "fG")
+                 (match_operand:DF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"pfeq.dd %r1,%r0,%?f0\";
+}")
+
+;; NOTE:  The i860 Programmer's Reference Manual says that when we are
+;; doing (A < B) or (A > B) comparisons, we have to use pfgt for these
+;; in order to be IEEE compliant (in case a trap occurs during these
+;; operations).  Conversely, for (A <= B) or (A >= B) comparisons, we
+;; must use pfle to be IEEE compliant.
+
+(define_insn "cmpltdf"
+  [(set (cc0) (lt (match_operand:DF 0 "reg_or_0_operand" "fG")
+                 (match_operand:DF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"pfgt.dd %r1,%r0,%?f0\";
+}")
+
+(define_insn "cmpgtdf"
+  [(set (cc0) (gt (match_operand:DF 0 "reg_or_0_operand" "fG")
+                 (match_operand:DF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"pfgt.dd %r0,%r1,%?f0\";
+}")
+
+;; NOTE:  The pfle opcode doesn't do what you think it does.  It is
+;; bass-ackwards.  It *clears* the CC flag if the first operand is
+;; less than or equal to the second.  Thus, we have to set CC_NEGATED
+;; for the following two patterns.
+
+(define_insn "cmpledf"
+  [(set (cc0) (le (match_operand:DF 0 "reg_or_0_operand" "fG")
+                 (match_operand:DF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"pfle.dd %r0,%r1,%?f0\";
+}")
+
+(define_insn "cmpgedf"
+  [(set (cc0) (ge (match_operand:DF 0 "reg_or_0_operand" "fG")
+                 (match_operand:DF 1 "reg_or_0_operand" "fG")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  cc_status.flags |= CC_NEGATED;
+  return \"pfle.dd %r1,%r0,%?f0\";
+}")
+
+;; ------------------------------------------------------------------------
+;; Integer EQ/NE comparisons against constant values which will fit in the
+;; 16-bit immediate field of an instruction.  These are made by combining.
+;; ------------------------------------------------------------------------
+
+(define_insn ""
+  [(set (cc0) (eq (zero_extend:SI (match_operand:HI 0 "load_operand" "m"))
+                 (match_operand:SI 1 "small_int" "I")))]
+  "INTVAL (operands[1]) >= 0"
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"ld.s %0,%?r31\;xor %1,%?r31,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0) (eq (match_operand:SI 0 "small_int" "I")
+                 (zero_extend:SI (match_operand:HI 1 "load_operand" "m"))))]
+  "INTVAL (operands[0]) >= 0"
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"ld.s %1,%?r31\;xor %0,%?r31,%?r0\";
+}")
+\f
+;; ------------------------------------------------------------------------
+;; Define the real conditional branch instructions.
+;; ------------------------------------------------------------------------
+
+(define_insn "cbranch"
+  [(set (pc) (if_then_else (eq (cc0) (const_int 0))
+                          (label_ref (match_operand 0 "" ""))
+                          (pc)))]
+  ""
+  "*
+{
+  if ((cc_prev_status.flags & CC_NEGATED) == 0)
+    return \"bnc %l0\";
+  else
+    return \"bc %l0\";
+}")
+
+(define_insn "flipped_cbranch"
+  [(set (pc) (if_then_else (ne (cc0)
+                              (const_int 0))
+                          (pc)
+                          (label_ref (match_operand 0 "" ""))))]
+  ""
+  "*
+{
+  if ((cc_prev_status.flags & CC_NEGATED) == 0)
+    return \"bnc %l0\";
+  else
+    return \"bc %l0\";
+}")
+
+(define_insn "inverse_cbranch"
+  [(set (pc) (if_then_else (eq (cc0)
+                              (const_int 0))
+                          (pc)
+                          (label_ref (match_operand 0 "" ""))))]
+  ""
+  "*
+{
+  if ((cc_prev_status.flags & CC_NEGATED) == 0)
+    return \"bc %l0\";
+  else
+    return \"bnc %l0\";
+}")
+
+
+(define_insn "flipped_inverse_cbranch"
+  [(set (pc) (if_then_else (ne (cc0)
+                              (const_int 0))
+                          (label_ref (match_operand 0 "" ""))
+                          (pc)))]
+  ""
+  "*
+{
+  if ((cc_prev_status.flags & CC_NEGATED) == 0)
+    return \"bc %l0\";
+  else
+    return \"bnc %l0\";
+}")
+
+;; Simple BTE/BTNE compare-and-branch insns made by combining.
+;; Note that it is wrong to add similar patterns for QI or HImode
+;; because bte/btne always compare the whole register.
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (eq (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "bte_operand" "rK"))
+                     (label_ref (match_operand 2 "" ""))
+                     (pc)))]
+  ""
+  "bte %1,%0,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (ne (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "bte_operand" "rK"))
+                     (label_ref (match_operand 2 "" ""))
+                     (pc)))]
+  ""
+  "btne %1,%0,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (eq (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "bte_operand" "rK"))
+                     (pc)
+                     (label_ref (match_operand 2 "" ""))))]
+  ""
+  "btne %1,%0,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (ne (match_operand:SI 0 "register_operand" "r")
+                         (match_operand:SI 1 "bte_operand" "rK"))
+                     (pc)
+                     (label_ref (match_operand 2 "" ""))))]
+  ""
+  "bte %1,%0,%2")
+
+;; Load byte/halfword, zero-extend, & compare-and-branch insns.
+;; These are made by combining.
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (eq (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (label_ref (match_operand 2 "" ""))
+                      (pc)))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.b %0,%3;bte %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (ne (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (label_ref (match_operand 2 "" ""))
+                      (pc)))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.b %0,%3;btne %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (eq (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (pc)
+                      (label_ref (match_operand 2 "" ""))))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.b %0,%3;btne %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (ne (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (pc)
+                      (label_ref (match_operand 2 "" ""))))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.b %0,%3;bte %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (eq (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (label_ref (match_operand 2 "" ""))
+                      (pc)))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.s %0,%3;bte %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (ne (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (label_ref (match_operand 2 "" ""))
+                      (pc)))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.s %0,%3;btne %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (eq (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (pc)
+                      (label_ref (match_operand 2 "" ""))))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.s %0,%3;btne %1,%3,%2")
+
+(define_insn ""
+  [(set (pc)
+        (if_then_else (ne (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
+                          (match_operand:SI 1 "bte_operand" "K"))
+                      (pc)
+                      (label_ref (match_operand 2 "" ""))))
+   (match_scratch:SI 3 "=r")]
+  ""
+  "ld.s %0,%3;bte %1,%3,%2")
+
+\f
+;; Generation of conditionals.
+
+;; We save the compare operands in the cmpxx patterns and use then when
+;; we generate the branch.
+
+(define_expand "cmpsi"
+  [(set (cc0) (compare (match_operand:SI 0 "register_operand" "")
+                      (match_operand:SI 1 "compare_operand" "")))]
+  ""
+  "
+{ i860_compare_op0 = operands[0];
+  i860_compare_op1 = operands[1];
+  DONE;
+}")
+
+(define_expand "cmpsf"
+  [(set (cc0) (compare (match_operand:SF 0 "register_operand" "")
+                      (match_operand:SF 1 "register_operand" "")))]
+  ""
+  "
+{ i860_compare_op0 = operands[0];
+  i860_compare_op1 = operands[1];
+  DONE;
+}")
+
+(define_expand "cmpdf"
+  [(set (cc0) (compare (match_operand:DF 0 "register_operand" "")
+                      (match_operand:DF 1 "register_operand" "")))]
+  ""
+  "
+{ i860_compare_op0 = operands[0];
+  i860_compare_op1 = operands[1];
+  DONE;
+}")
+
+;; These are the standard-named conditional branch patterns.
+;; Detailed comments are found in the first one only.
+
+(define_expand "beq"
+  [(set (pc)
+       (if_then_else (eq (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  /* Emit a single-condition compare insn according to
+     the type of operands and the condition to be tested.  */
+
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
+    emit_insn (gen_cmpeqsi (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == SFmode)
+    emit_insn (gen_cmpeqsf (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == DFmode)
+    emit_insn (gen_cmpeqdf (i860_compare_op0, i860_compare_op1));
+  else
+    abort ();
+
+  /* Emit branch-if-true.  */
+
+  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+  DONE;
+}")
+
+(define_expand "bne"
+  [(set (pc)
+       (if_then_else (ne (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
+    emit_insn (gen_cmpeqsi (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == SFmode)
+    emit_insn (gen_cmpeqsf (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == DFmode)
+    emit_insn (gen_cmpeqdf (i860_compare_op0, i860_compare_op1));
+  else
+    abort ();
+
+  emit_jump_insn (gen_flipped_cbranch (operands[0]));
+
+  DONE;
+}")
+
+(define_expand "bgt"
+  [(set (pc)
+       (if_then_else (gt (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
+    emit_insn (gen_cmpgtsi (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == SFmode)
+    emit_insn (gen_cmpgtsf (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == DFmode)
+    emit_insn (gen_cmpgtdf (i860_compare_op0, i860_compare_op1));
+  else
+    abort ();
+
+  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+  DONE;
+}")
+
+(define_expand "blt"
+  [(set (pc)
+       (if_then_else (lt (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
+    emit_insn (gen_cmpltsi (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == SFmode)
+    emit_insn (gen_cmpltsf (i860_compare_op0, i860_compare_op1));
+  else if (GET_MODE (i860_compare_op0) == DFmode)
+    emit_insn (gen_cmpltdf (i860_compare_op0, i860_compare_op1));
+  else
+    abort ();
+
+  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+  DONE;
+}")
+
+(define_expand "ble"
+  [(set (pc)
+       (if_then_else (le (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
+    {
+      emit_insn (gen_cmpgtsi (i860_compare_op0, i860_compare_op1));
+      emit_jump_insn (gen_flipped_cbranch (operands[0]));
+    }
+  else
+    {
+      if (GET_MODE (i860_compare_op0) == SFmode)
+       emit_insn (gen_cmplesf (i860_compare_op0, i860_compare_op1));
+      else if (GET_MODE (i860_compare_op0) == DFmode)
+       emit_insn (gen_cmpledf (i860_compare_op0, i860_compare_op1));
+      else
+       abort ();
+      emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+    }
+  DONE;
+}")
+
+(define_expand "bge"
+  [(set (pc)
+       (if_then_else (ge (cc0)
+                         (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
+    {
+      emit_insn (gen_cmpltsi (i860_compare_op0, i860_compare_op1));
+      emit_jump_insn (gen_flipped_cbranch (operands[0]));
+    }
+  else
+    {
+      if (GET_MODE (i860_compare_op0) == SFmode)
+       emit_insn (gen_cmpgesf (i860_compare_op0, i860_compare_op1));
+      else if (GET_MODE (i860_compare_op0) == DFmode)
+       emit_insn (gen_cmpgedf (i860_compare_op0, i860_compare_op1));
+      else
+       abort ();
+      emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+    }
+  DONE;
+}")
+
+(define_expand "bgtu"
+  [(set (pc)
+       (if_then_else (gtu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
+    abort ();
+
+  emit_insn (gen_cmpleusi (i860_compare_op0, i860_compare_op1));
+  emit_jump_insn (gen_flipped_cbranch (operands[0]));
+  DONE;
+}")
+
+(define_expand "bltu"
+  [(set (pc)
+       (if_then_else (ltu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
+    abort ();
+
+  emit_insn (gen_cmpgeusi (i860_compare_op0, i860_compare_op1));
+  emit_jump_insn (gen_flipped_cbranch (operands[0]));
+  DONE;
+}")
+
+(define_expand "bgeu"
+  [(set (pc)
+       (if_then_else (geu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
+    abort ();
+
+  emit_insn (gen_cmpgeusi (i860_compare_op0, i860_compare_op1));
+  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+  DONE;
+}")
+
+(define_expand "bleu"
+  [(set (pc)
+       (if_then_else (leu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
+    abort ();
+
+  emit_insn (gen_cmpleusi (i860_compare_op0, i860_compare_op1));
+  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
+  DONE;
+}")
+\f
+;; Move instructions
+
+;; Note that source operands for `mov' pseudo-instructions are no longer
+;; allowed (by the svr4 assembler) to be "big" things, i.e. constants that
+;; won't fit in 16-bits.  (This includes any sort of a relocatable address
+;; also.)  Thus, we must use an explicit orh/or pair of instructions if
+;; the source operand is something "big".
+
+(define_insn "movsi"
+  [(set (match_operand:SI 0 "general_operand" "=r,m,f")
+       (match_operand:SI 1 "general_operand" "rmif,rfJ,rmfJ"))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM)
+    {
+      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+       return output_store (operands);
+      if (FP_REG_P (operands[1]))
+       return \"fst.l %1,%0\";
+      return \"st.l %r1,%0\";
+    }
+  if (GET_CODE (operands[1]) == MEM)
+    {
+      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+       return output_load (operands);
+      if (FP_REG_P (operands[0]))
+       return \"fld.l %1,%0\";
+      return \"ld.l %1,%0\";
+    }
+  if (FP_REG_P (operands[1]) && FP_REG_P (operands[0]))
+    return \"fmov.ss %1,%0\";
+  if (FP_REG_P (operands[1]))
+    return \"fxfr %1,%0\";
+  if (FP_REG_P (operands[0]) && operands[1] == const0_rtx)
+    return \"fmov.ss %?f0,%0\";
+  if (FP_REG_P (operands[0]))
+    return \"ixfr %1,%0\";
+
+  if (GET_CODE (operands[1]) == REG)
+    return \"shl %?r0,%1,%0\";
+
+  CC_STATUS_PARTIAL_INIT;
+
+  if (GET_CODE (operands[1]) == CONST_INT)
+    {
+      if((INTVAL (operands[1]) & 0xffff0000) == 0)
+        return \"or %L1,%?r0,%0\";
+      if((INTVAL (operands[1]) & 0x0000ffff) == 0)
+        return \"orh %H1,%?r0,%0\";
+    }
+  return \"orh %H1,%?r0,%0\;or %L1,%0,%0\";
+}")
+(define_insn "movhi"
+  [(set (match_operand:HI 0 "general_operand" "=r,m,!*f,!r")
+       (match_operand:HI 1 "general_operand" "rmi,rJ,rJ*f,*f"))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM)
+    {
+      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+       return output_store (operands);
+      return \"st.s %r1,%0\";
+    }
+  if (GET_CODE (operands[1]) == MEM)
+    {
+      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+       return output_load (operands);
+      return \"ld.s %1,%0\";
+    }
+  if (FP_REG_P (operands[1]) && FP_REG_P (operands[0]))
+    return \"fmov.ss %1,%0\";
+  if (FP_REG_P (operands[1]))
+    return \"fxfr %1,%0\";
+  if (FP_REG_P (operands[0]) && operands[1] == const0_rtx)
+    return \"fmov.ss %?f0,%0\";
+  if (FP_REG_P (operands[0]))
+    return \"ixfr %1,%0\";
+
+  if (GET_CODE (operands[1]) == REG)
+    return \"shl %?r0,%1,%0\";
+
+  CC_STATUS_PARTIAL_INIT;
+
+  return \"or %L1,%?r0,%0\";
+}")
+
+(define_insn "movqi"
+  [(set (match_operand:QI 0 "general_operand" "=r,m,!*f,!r")
+       (match_operand:QI 1 "general_operand" "rmi,rJ,rJ*f,*f"))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM)
+    {
+      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+       return output_store (operands);
+      return \"st.b %r1,%0\";
+    }
+  if (GET_CODE (operands[1]) == MEM)
+    {
+      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+       return output_load (operands);
+      return \"ld.b %1,%0\";
+    }
+  if (FP_REG_P (operands[1]) && FP_REG_P (operands[0]))
+    return \"fmov.ss %1,%0\";
+  if (FP_REG_P (operands[1]))
+    return \"fxfr %1,%0\";
+  if (FP_REG_P (operands[0]) && operands[1] == const0_rtx)
+    return \"fmov.ss %?f0,%0\";
+  if (FP_REG_P (operands[0]))
+    return \"ixfr %1,%0\";
+
+  if (GET_CODE (operands[1]) == REG)
+    return \"shl %?r0,%1,%0\";
+
+  CC_STATUS_PARTIAL_INIT;
+
+  return \"or %L1,%?r0,%0\";
+}")
+
+;; The definition of this insn does not really explain what it does,
+;; but it should suffice
+;; that anything generated as this insn will be recognized as one
+;; and that it won't successfully combine with anything.
+(define_expand "movstrsi"
+  [(parallel [(set (match_operand:BLK 0 "general_operand" "")
+                  (match_operand:BLK 1 "general_operand" ""))
+             (use (match_operand:SI 2 "nonmemory_operand" ""))
+             (use (match_operand:SI 3 "immediate_operand" ""))
+             (clobber (match_dup 4))
+             (clobber (match_dup 5))
+             (clobber (match_dup 6))
+             (clobber (match_dup 7))
+             (clobber (match_dup 8))])]
+  ""
+  "
+{
+  operands[4] = gen_reg_rtx (SImode);
+  operands[5] = gen_reg_rtx (SImode);
+  operands[6] = gen_reg_rtx (SImode);
+  operands[7] = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
+  operands[8] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
+
+  operands[0] = replace_equiv_address (operands[0], operands[7]);
+  operands[1] = replace_equiv_address (operands[1], operands[8]);
+}")
+
+(define_insn ""
+  [(set (mem:BLK (match_operand:SI 0 "register_operand" "r"))
+       (mem:BLK (match_operand:SI 1 "register_operand" "r")))
+   (use (match_operand:SI 2 "general_operand" "rn"))
+   (use (match_operand:SI 3 "immediate_operand" "i"))
+   (clobber (match_operand:SI 4 "register_operand" "=r"))
+   (clobber (match_operand:SI 5 "register_operand" "=r"))
+   (clobber (match_operand:SI 6 "register_operand" "=r"))
+   (clobber (match_dup 0))
+   (clobber (match_dup 1))]
+  ""
+  "* return output_block_move (operands);")
+\f
+;; Floating point move insns
+
+;; This pattern forces (set (reg:DF ...) (const_double ...))
+;; to be reloaded by putting the constant into memory.
+;; It must come before the more general movdf pattern.
+(define_insn ""
+  [(set (match_operand:DF 0 "general_operand" "=r,f,o")
+       (match_operand:DF 1 "" "mG,m,G"))]
+  "GET_CODE (operands[1]) == CONST_DOUBLE"
+  "*
+{
+  if (FP_REG_P (operands[0]) || operands[1] == CONST0_RTX (DFmode))
+    return output_fp_move_double (operands);
+  return output_move_double (operands);
+}")
+
+(define_insn "movdf"
+  [(set (match_operand:DF 0 "general_operand" "=*rm,*r,?f,?*rm")
+       (match_operand:DF 1 "general_operand" "*r,m,*rfmG,f"))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM
+      && CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+    return output_store (operands);
+  if (GET_CODE (operands[1]) == MEM
+      && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    return output_load (operands);
+
+  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
+    return output_fp_move_double (operands);
+  return output_move_double (operands);
+}")
+
+(define_insn "movdi"
+  [(set (match_operand:DI 0 "general_operand" "=rm,r,?f,?rm")
+       (match_operand:DI 1 "general_operand" "r,miF,rfmG,f"))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM
+      && CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+    return output_store (operands);
+  if (GET_CODE (operands[1]) == MEM
+      && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    return output_load (operands);
+
+  /* ??? How can we have a DFmode arg here with DImode above? */
+  if (FP_REG_P (operands[0]) && operands[1] == CONST0_RTX (DFmode))
+    return \"fmov.dd %?f0,%0\";
+
+  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
+    return output_fp_move_double (operands);
+  return output_move_double (operands);
+}")
+
+;; The alternative m/r is separate from m/f
+;; The first alternative is separate from the second for the same reason.
+(define_insn "movsf"
+  [(set (match_operand:SF 0 "general_operand" "=*rf,*rf,*r,m,m")
+       (match_operand:SF 1 "general_operand" "*r,fmG,F,*r,f"))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == MEM
+      && CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
+    return output_store (operands);
+  if (GET_CODE (operands[1]) == MEM
+      && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    return output_load (operands);
+  if (FP_REG_P (operands[0]))
+    {
+      if (FP_REG_P (operands[1]))
+       return \"fmov.ss %1,%0\";
+      if (GET_CODE (operands[1]) == REG)
+       return \"ixfr %1,%0\";
+      if (operands[1] == CONST0_RTX (SFmode))
+        return \"fmov.ss %?f0,%0\";
+      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+       {
+         if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
+                && (cc_prev_status.flags & CC_HI_R31_ADJ)
+                && cc_prev_status.mdep == XEXP(operands[1],0)))
+           {
+             CC_STATUS_INIT;
+             cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+             cc_status.mdep = XEXP (operands[1], 0);
+             return \"orh %h1,%?r0,%?r31\;fld.l %L1(%?r31),%0\";
+           }
+         return \"fld.l %L1(%?r31),%0\";
+       }
+      return \"fld.l %1,%0\";
+    }
+  if (FP_REG_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE)
+    {
+      if (GET_CODE (operands[0]) == REG && FP_REG_P (operands[1]))
+       return \"fxfr %1,%0\";
+      if (GET_CODE (operands[0]) == REG)
+       {
+         CC_STATUS_PARTIAL_INIT;
+         if (GET_CODE (operands[1]) == CONST_DOUBLE)
+           {
+             register unsigned long ul;
+
+              ul = sfmode_constant_to_ulong (operands[1]);
+             if ((ul & 0x0000ffff) == 0)
+               return \"orh %H1,%?r0,%0\";
+             if ((ul & 0xffff0000) == 0)
+               return \"or %L1,%?r0,%0\";
+           }
+          return \"orh %H1,%?r0,%0\;or %L1,%0,%0\";
+       }
+      /* Now operand 0 must be memory.
+         If operand 1 is CONST_DOUBLE, its value must be 0.  */
+      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)
+                && XEXP (operands[0], 0) == cc_prev_status.mdep))
+           {
+             CC_STATUS_INIT;
+             cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+             cc_status.mdep = XEXP (operands[0], 0);
+             output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
+           }
+         return \"fst.l %r1,%L0(%?r31)\";
+       }
+      return \"fst.l %r1,%0\";
+    }
+  if (GET_CODE (operands[0]) == MEM)
+    return \"st.l %r1,%0\";
+  if (GET_CODE (operands[1]) == MEM)
+    return \"ld.l %1,%0\";
+  if (operands[1] == CONST0_RTX (SFmode))
+    return \"shl %?r0,%?r0,%0\";
+  return \"mov %1,%0\";
+}")
+\f
+;; Special load insns for REG+REG addresses.
+;; Such addresses are not "legitimate" because st rejects them.
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=rf")
+       (match_operand:DF 1 "indexed_operand" "m"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[0]))
+    return output_fp_move_double (operands);
+  return output_move_double (operands);
+}")
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=rf")
+       (match_operand:SF 1 "indexed_operand" "m"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[0]))
+    return \"fld.l %1,%0\";
+  return \"ld.l %1,%0\";
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=rf")
+       (match_operand:SI 1 "indexed_operand" "m"))]
+  ""
+  "*
+{
+  if (FP_REG_P (operands[0]))
+    return \"fld.l %1,%0\";
+  return \"ld.l %1,%0\";
+}")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (match_operand:HI 1 "indexed_operand" "m"))]
+  ""
+  "ld.s %1,%0")
+
+(define_insn ""
+  [(set (match_operand:QI 0 "register_operand" "=r")
+       (match_operand:QI 1 "indexed_operand" "m"))]
+  ""
+  "ld.b %1,%0")
+
+;; Likewise for floating-point store insns.
+
+(define_insn ""
+  [(set (match_operand:DF 0 "indexed_operand" "=m")
+       (match_operand:DF 1 "register_operand" "f"))]
+  ""
+  "fst.d %1,%0")
+
+(define_insn ""
+  [(set (match_operand:SF 0 "indexed_operand" "=m")
+       (match_operand:SF 1 "register_operand" "f"))]
+  ""
+  "fst.l %1,%0")
+\f
+;;- truncation instructions
+(define_insn "truncsiqi2"
+  [(set (match_operand:QI 0 "general_operand" "=g")
+       (truncate:QI
+        (match_operand:SI 1 "register_operand" "r")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == 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)
+              && XEXP (operands[0], 0) == cc_prev_status.mdep))
+         {
+           CC_STATUS_INIT;
+           cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+           cc_status.mdep = XEXP (operands[0], 0);
+           output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
+         }
+       return \"st.b %1,%L0(%?r31)\";
+      }
+    else
+      return \"st.b %1,%0\";
+  }
+  return \"shl %?r0,%1,%0\";
+}")
+
+(define_insn "trunchiqi2"
+  [(set (match_operand:QI 0 "general_operand" "=g")
+       (truncate:QI
+        (match_operand:HI 1 "register_operand" "r")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == 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)
+              && XEXP (operands[0], 0) == cc_prev_status.mdep))
+         {
+           CC_STATUS_INIT;
+           cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+           cc_status.mdep = XEXP (operands[0], 0);
+           output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
+         }
+       return \"st.b %1,%L0(%?r31)\";
+      }
+    else
+      return \"st.b %1,%0\";
+  }
+  return \"shl %?r0,%1,%0\";
+}")
+
+(define_insn "truncsihi2"
+  [(set (match_operand:HI 0 "general_operand" "=g")
+       (truncate:HI
+        (match_operand:SI 1 "register_operand" "r")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == 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)
+              && XEXP (operands[0], 0) == cc_prev_status.mdep))
+         {
+           CC_STATUS_INIT;
+           cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+           cc_status.mdep = XEXP (operands[0], 0);
+           output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
+         }
+       return \"st.s %1,%L0(%?r31)\";
+      }
+    else
+      return \"st.s %1,%0\";
+  }
+  return \"shl %?r0,%1,%0\";
+}")
+\f
+;;- zero extension instructions
+
+(define_insn "zero_extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (zero_extend:SI
+        (match_operand:HI 1 "register_operand" "r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"and 0xffff,%1,%0\";
+}")
+
+(define_insn "zero_extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (zero_extend:HI
+        (match_operand:QI 1 "register_operand" "r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"and 0xff,%1,%0\";
+}")
+
+(define_insn "zero_extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (zero_extend:SI
+        (match_operand:QI 1 "register_operand" "r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"and 0xff,%1,%0\";
+}")
+\f
+;; Sign extension instructions.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (sign_extend:SI
+        (match_operand:HI 1 "indexed_operand" "m")))]
+  ""
+  "ld.s %1,%0")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (sign_extend:HI
+        (match_operand:QI 1 "indexed_operand" "m")))]
+  ""
+  "ld.b %1,%0")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (sign_extend:SI
+        (match_operand:QI 1 "indexed_operand" "m")))]
+  ""
+  "ld.b %1,%0")
+
+(define_insn "extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (sign_extend:SI
+        (match_operand:HI 1 "nonimmediate_operand" "mr")))]
+  ""
+  "*
+{
+  if (REG_P (operands[1]))
+    return \"shl 16,%1,%0\;shra 16,%0,%0\";
+  if (GET_CODE (operands[1]) == CONST_INT)
+    abort ();
+  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    {
+      CC_STATUS_INIT;
+      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+      cc_status.mdep = XEXP (operands[1], 0);
+      return \"orh %h1,%?r0,%?r31\;ld.s %L1(%?r31),%0\";
+    }
+  else
+    return \"ld.s %1,%0\";
+}")
+
+(define_insn "extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (sign_extend:HI
+        (match_operand:QI 1 "nonimmediate_operand" "mr")))]
+  ""
+  "*
+{
+  if (REG_P (operands[1]))
+    return \"shl 24,%1,%0\;shra 24,%0,%0\";
+  if (GET_CODE (operands[1]) == CONST_INT)
+    abort ();
+  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    {
+      CC_STATUS_INIT;
+      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+      cc_status.mdep = XEXP (operands[1], 0);
+      return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\";
+    }
+  else
+    return \"ld.b %1,%0\";
+}")
+
+(define_insn "extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (sign_extend:SI
+        (match_operand:QI 1 "nonimmediate_operand" "mr")))]
+  ""
+  "*
+{
+  if (REG_P (operands[1]))
+    return \"shl 24,%1,%0\;shra 24,%0,%0\";
+  if (GET_CODE (operands[1]) == CONST_INT)
+    abort ();
+  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    {
+      CC_STATUS_INIT;
+      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+      cc_status.mdep = XEXP (operands[1], 0);
+      return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\";
+    }
+  else
+    return \"ld.b %1,%0\";
+}")
+
+;; Signed bitfield extractions come out looking like
+;;     (shiftrt (sign_extend (shift <Y> <C1>)) <C2>)
+;; which we expand poorly as four shift insns.
+;; These patterns yield two shifts:
+;;     (shiftrt (shift <Y> <C3>) <C4>)
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashiftrt:SI
+        (sign_extend:SI
+         (match_operand:QI 1 "register_operand" "r"))
+        (match_operand:SI 2 "logic_int" "n")))]
+  "INTVAL (operands[2]) < 8"
+  "*
+{
+  return \"shl 24,%1,%0\;shra 24+%2,%0,%0\";
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashiftrt:SI
+        (sign_extend:SI
+         (subreg:QI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+                               (match_operand:SI 2 "logic_int" "n")) 0))
+        (match_operand:SI 3 "logic_int" "n")))]
+  "INTVAL (operands[3]) < 8"
+  "*
+{
+  return \"shl 0x18+%2,%1,%0\;shra 0x18+%3,%0,%0\";
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashiftrt:SI
+        (sign_extend:SI
+         (ashift:QI (match_operand:QI 1 "register_operand" "r")
+                    (match_operand:QI 2 "logic_int" "n")))
+        (match_operand:SI 3 "logic_int" "n")))]
+  "INTVAL (operands[3]) < 8"
+  "*
+{
+  return \"shl 0x18+%2,%1,%0\;shra 0x18+%3,%0,%0\";
+}")
+\f
+;; Special patterns for optimizing bit-field instructions.
+
+;; First two patterns are for bitfields that came from memory
+;; testing only the high bit.  They work with old combiner.
+
+(define_insn ""
+  [(set (cc0)
+       (eq (zero_extend:SI (subreg:QI (lshiftrt:SI (match_operand:SI 0 "register_operand" "r")
+                                                   (const_int 7)) 0))
+           (const_int 0)))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"and 128,%0,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0)
+       (eq (sign_extend:SI (subreg:QI (ashiftrt:SI (match_operand:SI 0 "register_operand" "r")
+                                                   (const_int 7)) 0))
+           (const_int 0)))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"and 128,%0,%?r0\";
+}")
+
+;; next two patterns are good for bitfields coming from memory
+;; (via pseudo-register) or from a register, though this optimization
+;; is only good for values contained wholly within the bottom 13 bits
+(define_insn ""
+  [(set (cc0)
+       (eq 
+        (and:SI (lshiftrt:SI (match_operand:SI 0 "register_operand" "r")
+                             (match_operand:SI 1 "logic_int" "n"))
+                (match_operand:SI 2 "logic_int" "n"))
+        (const_int 0)))]
+  "LOGIC_INTVAL (INTVAL (operands[2]) << INTVAL (operands[1]))"
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  operands[2] = GEN_INT (INTVAL (operands[2]) << INTVAL (operands[1]));
+  return \"and %2,%0,%?r0\";
+}")
+
+(define_insn ""
+  [(set (cc0)
+       (eq 
+        (and:SI (ashiftrt:SI (match_operand:SI 0 "register_operand" "r")
+                             (match_operand:SI 1 "logic_int" "n"))
+                (match_operand:SI 2 "logic_int" "n"))
+        (const_int 0)))]
+  "LOGIC_INTVAL (INTVAL (operands[2]) << INTVAL (operands[1]))"
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  operands[2] = GEN_INT (INTVAL (operands[2]) << INTVAL (operands[1]));
+  return \"and %2,%0,%?r0\";
+}")
+\f
+;; Conversions between float and double.
+
+(define_insn "extendsfdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (float_extend:DF
+        (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fmov.sd %1,%0")
+
+(define_insn "truncdfsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (float_truncate:SF
+        (match_operand:DF 1 "register_operand" "f")))]
+  ""
+  "fmov.ds %1,%0")
+\f
+;; Conversion between fixed point and floating point.
+;; Note that among the fix-to-float insns
+;; the ones that start with SImode come first.
+;; That is so that an operand that is a CONST_INT
+;; (and therefore lacks a specific machine mode).
+;; will be recognized as SImode (which is always valid)
+;; rather than as QImode or HImode.
+
+;; This pattern forces (set (reg:SF ...) (float:SF (const_int ...)))
+;; to be reloaded by putting the constant into memory.
+;; It must come before the more general floatsisf2 pattern.
+(define_expand "floatsidf2"
+  [(set (match_dup 2) (match_dup 3))
+   (set (match_dup 4) (xor:SI (match_operand:SI 1 "register_operand" "")
+                             (const_int -2147483648)))
+   (set (match_dup 5) (match_dup 3))
+   (set (subreg:SI (match_dup 5) 0) (match_dup 4))
+   (set (match_operand:DF 0 "register_operand" "")
+       (minus:DF (match_dup 5) (match_dup 2)))]
+  ""
+  "
+{
+  REAL_VALUE_TYPE d;
+  /* 4503601774854144 is  (1 << 30) * ((1 << 22) + (1 << 1)).  */
+  d = REAL_VALUE_ATOF (\"4503601774854144\", DFmode);
+  operands[2] = gen_reg_rtx (DFmode);
+  operands[3] = CONST_DOUBLE_FROM_REAL_VALUE (d, DFmode);
+  operands[4] = gen_reg_rtx (SImode);
+  operands[5] = gen_reg_rtx (DFmode);
+}")
+\f
+;; Floating to fixed conversion.
+
+(define_expand "fix_truncdfsi2"
+  ;; This first insn produces a double-word value
+  ;; in which only the low word is valid.
+  [(set (match_dup 2)
+       (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
+   (set (match_operand:SI 0 "register_operand" "=f")
+       (subreg:SI (match_dup 2) 0))]
+  ""
+  "
+{
+  operands[2] = gen_reg_rtx (DImode);
+}")
+
+;; Recognize the first insn generated above.
+;; This RTL looks like a fix_truncdfdi2 insn,
+;; but we don't call it that, because only 32 bits
+;; of the result are valid.
+;; This pattern will work for the intended purposes 
+;; as long as we do not have any fixdfdi2 or fix_truncdfdi2.
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=f")
+       (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))]
+  ""
+  "ftrunc.dd %1,%0")
+
+(define_expand "fix_truncsfsi2"
+  ;; This first insn produces a double-word value
+  ;; in which only the low word is valid.
+  [(set (match_dup 2)
+       (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))
+   (set (match_operand:SI 0 "register_operand" "=f")
+       (subreg:SI (match_dup 2) 0))]
+  ""
+  "
+{
+  operands[2] = gen_reg_rtx (DImode);
+}")
+
+;; Recognize the first insn generated above.
+;; This RTL looks like a fix_truncsfdi2 insn,
+;; but we don't call it that, because only 32 bits
+;; of the result are valid.
+;; This pattern will work for the intended purposes 
+;; as long as we do not have any fixsfdi2 or fix_truncsfdi2.
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=f")
+       (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))]
+  ""
+  "ftrunc.sd %1,%0")
+\f
+;;- arithmetic instructions
+
+(define_insn "addsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r,*f")
+       (plus:SI (match_operand:SI 1 "nonmemory_operand" "%r,*f")
+                (match_operand:SI 2 "arith_operand" "rI,*f")))]
+  ""
+  "*
+{
+  if (which_alternative == 1)
+    return \"fiadd.ss %2,%1,%0\";
+  CC_STATUS_PARTIAL_INIT;
+  return \"addu %2,%1,%0\";
+}")
+
+(define_insn "adddi3"
+  [(set (match_operand:DI 0 "register_operand" "=f")
+       (plus:DI (match_operand:DI 1 "register_operand" "%f")
+                (match_operand:DI 2 "register_operand" "f")))]
+  ""
+  "fiadd.dd %1,%2,%0")
+
+(define_insn "subsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,*f")
+       (minus:SI (match_operand:SI 1 "register_operand" "r,I,*f")
+                 (match_operand:SI 2 "arith_operand" "rI,r,*f")))]
+  ""
+  "*
+{
+  if (which_alternative == 2)
+    return \"fisub.ss %1,%2,%0\";
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[2]))
+    return \"subu %1,%2,%0\";
+  operands[2] = GEN_INT (- INTVAL (operands[2]));
+  return \"addu %2,%1,%0\";
+}")
+
+(define_insn "subdi3"
+  [(set (match_operand:DI 0 "register_operand" "=f")
+       (minus:DI (match_operand:DI 1 "register_operand" "f")
+                 (match_operand:DI 2 "register_operand" "f")))]
+  ""
+  "fisub.dd %1,%2,%0")
+
+(define_expand "mulsi3"
+  [(set (subreg:SI (match_dup 4) 0) (match_operand:SI 1 "general_operand" ""))
+   (set (subreg:SI (match_dup 5) 0) (match_operand:SI 2 "general_operand" ""))
+   (clobber (match_dup 3))
+   (set (subreg:SI (match_dup 3) 0)
+       (mult:SI (subreg:SI (match_dup 4) 0) (subreg:SI (match_dup 5) 0)))
+   (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 0))]
+  ""
+  "
+{
+  if (WORDS_BIG_ENDIAN)
+    emit_insn (gen_mulsi3_big (operands[0], operands[1], operands[2]));
+  else
+    emit_insn (gen_mulsi3_little (operands[0], operands[1], operands[2]));
+  DONE;
+}")
+
+(define_expand "mulsi3_little"
+  [(set (subreg:SI (match_dup 4) 0) (match_operand:SI 1 "general_operand" ""))
+   (set (subreg:SI (match_dup 5) 0) (match_operand:SI 2 "general_operand" ""))
+   (clobber (match_dup 3))
+   (set (subreg:SI (match_dup 3) 0)
+       (mult:SI (subreg:SI (match_dup 4) 0) (subreg:SI (match_dup 5) 0)))
+   (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 0))]
+  "! WORDS_BIG_ENDIAN"
+  "
+{
+  operands[3] = gen_reg_rtx (DImode);
+  operands[4] = gen_reg_rtx (DImode);
+  operands[5] = gen_reg_rtx (DImode);
+}")
+
+(define_expand "mulsi3_big"
+  [(set (subreg:SI (match_dup 4) 4) (match_operand:SI 1 "general_operand" ""))
+   (set (subreg:SI (match_dup 5) 4) (match_operand:SI 2 "general_operand" ""))
+   (clobber (match_dup 3))
+   (set (subreg:SI (match_dup 3) 4)
+       (mult:SI (subreg:SI (match_dup 4) 4) (subreg:SI (match_dup 5) 4)))
+   (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 4))]
+  "WORDS_BIG_ENDIAN"
+  "
+{
+  operands[3] = gen_reg_rtx (DImode);
+  operands[4] = gen_reg_rtx (DImode);
+  operands[5] = gen_reg_rtx (DImode);
+}")
+
+(define_insn ""
+  [(set (subreg:SI (match_operand:DI 0 "register_operand" "=f") 0)
+       (mult:SI (subreg:SI (match_operand:DI 1 "register_operand" "f") 0)
+                (subreg:SI (match_operand:DI 2 "register_operand" "f") 0)))]
+  "! WORDS_BIG_ENDIAN"
+  "fmlow.dd %2,%1,%0")
+
+(define_insn ""
+  [(set (subreg:SI (match_operand:DI 0 "register_operand" "=f") 4)
+       (mult:SI (subreg:SI (match_operand:DI 1 "register_operand" "f") 4)
+                (subreg:SI (match_operand:DI 2 "register_operand" "f") 4)))]
+  "WORDS_BIG_ENDIAN"
+  "fmlow.dd %2,%1,%0")
+\f
+;;- and instructions (with compliment also)                       
+(define_insn "andsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (and:SI (match_operand:SI 1 "nonmemory_operand" "%r")
+               (match_operand:SI 2 "nonmemory_operand" "rL")))]
+  ""
+  "*
+{
+  rtx xop[3];
+
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[2]) || LOGIC_INT (operands[2]))
+    return \"and %2,%1,%0\";
+  if ((INTVAL (operands[2]) & 0xffff) == 0)
+    {
+      operands[2]
+       = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[2]) >> 16);
+      return \"andh %2,%1,%0\";
+    }
+  xop[0] = operands[0];
+  xop[1] = operands[1];
+  xop[2] = GEN_INT (~INTVAL (operands[2]) & 0xffff);
+  output_asm_insn (\"andnot %2,%1,%0\", xop);
+  operands[2] = GEN_INT (~(unsigned HOST_WIDE_INT) INTVAL (operands[2]) >> 16);
+  return \"andnoth %2,%0,%0\";
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (and:SI (not:SI (match_operand:SI 1 "register_operand" "rn"))
+               (match_operand:SI 2 "register_operand" "r")))]
+  ""
+  "*
+{
+  rtx xop[3];
+
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[1]) || LOGIC_INT (operands[1]))
+    return \"andnot %1,%2,%0\";
+  if ((INTVAL (operands[1]) & 0xffff) == 0)
+    {
+      operands[1]
+       = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[1]) >> 16);
+      return \"andnoth %1,%2,%0\";
+    }
+  xop[0] = operands[0];
+  xop[1] = GEN_INT (INTVAL (operands[1]) & 0xffff);
+  xop[2] = operands[2];
+  output_asm_insn (\"andnot %1,%2,%0\", xop);
+  operands[1] = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[1]) >> 16);
+  return \"andnoth %1,%0,%0\";
+}")
+
+(define_insn "iorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ior:SI (match_operand:SI 1 "nonmemory_operand" "%r")
+               (match_operand:SI 2 "nonmemory_operand" "rL")))]
+  ""
+  "*
+{
+  rtx xop[3];
+
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[2]) || LOGIC_INT (operands[2]))
+    return \"or %2,%1,%0\";
+  if ((INTVAL (operands[2]) & 0xffff) == 0)
+    {
+      operands[2]
+       = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[2]) >> 16);
+      return \"orh %2,%1,%0\";
+    }
+  xop[0] = operands[0];
+  xop[1] = operands[1];
+  xop[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+  output_asm_insn (\"or %2,%1,%0\", xop);
+  operands[2] = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[2]) >> 16);
+  return \"orh %2,%0,%0\";
+}")
+
+(define_insn "xorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (xor:SI (match_operand:SI 1 "nonmemory_operand" "%r")
+               (match_operand:SI 2 "nonmemory_operand" "rL")))]
+  ""
+  "*
+{
+  rtx xop[3];
+
+  CC_STATUS_PARTIAL_INIT;
+  if (REG_P (operands[2]) || LOGIC_INT (operands[2]))
+    return \"xor %2,%1,%0\";
+  if ((INTVAL (operands[2]) & 0xffff) == 0)
+    {
+      operands[2]
+       = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[2]) >> 16);
+      return \"xorh %2,%1,%0\";
+    }
+  xop[0] = operands[0];
+  xop[1] = operands[1];
+  xop[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+  output_asm_insn (\"xor %2,%1,%0\", xop);
+  operands[2] = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (operands[2]) >> 16);
+  return \"xorh %2,%0,%0\";
+}")
+
+;(The i860 instruction set doesn't allow an immediate second operand in
+; a subtraction.)
+(define_insn "negsi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (neg:SI (match_operand:SI 1 "arith_operand" "r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"subu %?r0,%1,%0\";
+}")
+
+(define_insn "one_cmplsi2"
+  [(set (match_operand:SI 0 "general_operand" "=r")
+       (not:SI (match_operand:SI 1 "arith_operand" "r")))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  return \"subu -1,%1,%0\";
+}")
+\f
+;; Floating point arithmetic instructions.
+
+(define_insn "adddf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (plus:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "fadd.dd %1,%2,%0")
+
+(define_insn "addsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (plus:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fadd.ss %1,%2,%0")
+
+(define_insn "subdf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (minus:DF (match_operand:DF 1 "register_operand" "f")
+                 (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "fsub.dd %1,%2,%0")
+
+(define_insn "subsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (minus:SF (match_operand:SF 1 "register_operand" "f")
+                 (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fsub.ss %1,%2,%0")
+
+(define_insn "muldf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (mult:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  ""
+  "fmul.dd %1,%2,%0")
+
+(define_insn "mulsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (mult:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
+  ""
+  "fmul.ss %1,%2,%0")
+
+(define_insn "negdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (neg:DF (match_operand:DF 1 "register_operand" "f")))]
+  ""
+  "fsub.dd %?f0,%1,%0")
+
+(define_insn "negsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (neg:SF (match_operand:SF 1 "register_operand" "f")))]
+  ""
+  "fsub.ss %?f0,%1,%0")
+\f
+(define_insn "divdf3"
+  [(set (match_operand:DF 0 "register_operand" "=&f")
+       (div:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))
+   (clobber (match_scratch:DF 3 "=&f"))
+   (clobber (match_scratch:DF 4 "=&f"))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (((cc_prev_status.flags & CC_KNOW_HI_R31) == 0)
+      || (cc_prev_status.flags & CC_HI_R31_ADJ)
+      || (cc_prev_status.mdep != CONST2_RTX (SFmode)))
+    {
+      cc_status.flags |= CC_KNOW_HI_R31;
+      cc_status.flags &= ~CC_HI_R31_ADJ;
+      cc_status.mdep = CONST2_RTX (SFmode); 
+      return \"frcp.dd %2,%3\;fmul.dd %2,%3,%0\;fmov.dd %?f0,%4\;\\
+orh 0x4000,%?r0,%?r31\;ixfr %?r31,%R4\;fsub.dd %4,%0,%0\;\\
+fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\
+fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\
+fmul.dd %3,%1,%3\;fmul.dd %0,%3,%0\";
+    }
+  else
+    return \"frcp.dd %2,%3\;fmul.dd %2,%3,%0\;fmov.dd %?f0,%4\;\\
+ixfr %?r31,%R4\;fsub.dd %4,%0,%0\;\\
+fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\
+fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\
+fmul.dd %3,%1,%3\;fmul.dd %0,%3,%0\";
+}")
+
+(define_insn "divsf3"
+  [(set (match_operand:SF 0 "register_operand" "=&f")
+       (div:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))
+   (clobber (match_scratch:SF 3 "=&f"))
+   (clobber (match_scratch:SF 4 "=&f"))]
+  ""
+  "*
+{
+  CC_STATUS_PARTIAL_INIT;
+  if (((cc_prev_status.flags & CC_KNOW_HI_R31) == 0)
+      || (cc_prev_status.flags & CC_HI_R31_ADJ)
+      || (cc_prev_status.mdep != CONST2_RTX (SFmode)))
+    {
+      cc_status.flags |= CC_KNOW_HI_R31;
+      cc_status.flags &= ~CC_HI_R31_ADJ;
+      cc_status.mdep = CONST2_RTX (SFmode);
+      output_asm_insn (\"orh 0x4000,%?r0,%?r31\", operands);
+    }
+  return \"ixfr %?r31,%4\;frcp.ss %2,%0\;\\
+fmul.ss %2,%0,%3\;fsub.ss %4,%3,%3\;fmul.ss %0,%3,%0\;\\
+fmul.ss %2,%0,%3\;fsub.ss %4,%3,%3\;\\
+fmul.ss %1,%0,%4\;fmul.ss %3,%4,%0\";
+}")
+\f
+;; Shift instructions
+
+;; Optimized special case of shifting.
+;; Must precede the general case.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashiftrt:SI (match_operand:SI 1 "memory_operand" "m")
+                    (const_int 24)))]
+  ""
+  "*
+{
+  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
+    {
+      CC_STATUS_INIT;
+      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
+      cc_status.mdep = XEXP (operands[1], 0);
+      return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\";
+    }
+  return \"ld.b %1,%0\";
+}")
+
+\f
+;;- arithmetic shift instructions
+(define_insn "ashlsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashift:SI (match_operand:SI 1 "register_operand" "r")
+                  (match_operand:SI 2 "shift_operand" "rn")))]
+  ""
+  "*
+{
+  return \"shl %2,%1,%0\";
+}")
+
+(define_insn "ashlhi3"
+  [(set (match_operand:HI 0 "register_operand" "=r")
+       (ashift:HI (match_operand:HI 1 "register_operand" "r")
+                  (match_operand:HI 2 "shift_operand" "rn")))]
+  ""
+  "*
+{
+  return \"shl %2,%1,%0\";
+}")
+
+(define_insn "ashlqi3"
+  [(set (match_operand:QI 0 "register_operand" "=r")
+       (ashift:QI (match_operand:QI 1 "register_operand" "r")
+                  (match_operand:QI 2 "shift_operand" "rn")))]
+  ""
+  "*
+{
+  return \"shl %2,%1,%0\";
+}")
+
+(define_insn "ashrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
+                    (match_operand:SI 2 "shift_operand" "rn")))]
+  ""
+  "*
+{
+  return \"shra %2,%1,%0\";
+}")
+
+(define_insn "lshrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
+                    (match_operand:SI 2 "shift_operand" "rn")))]
+  ""
+  "*
+{
+  return \"shr %2,%1,%0\";
+}")
+\f
+;; Unconditional and other jump instructions
+
+(define_insn "jump"
+  [(set (pc) (label_ref (match_operand 0 "" "")))]
+  ""
+  "*
+{
+  return \"br %l0\;nop\";
+}")
+
+;; Here are two simple peepholes which fill the delay slot of
+;; an unconditional branch.
+;
+;; ??? All disabled, because output_delayed_branch is a crock
+;; that will reliably segfault.  This should be using the dbr
+;; pass in any case.  Anyone who cares is welcome to fix it.
+;
+;(define_peephole
+;  [(set (match_operand:SI 0 "register_operand" "=rf")
+;      (match_operand:SI 1 "single_insn_src_p" "gfG"))
+;   (set (pc) (label_ref (match_operand 2 "" "")))]
+;  ""
+;  "* return output_delayed_branch (\"br %l2\", operands, insn);")
+;
+;(define_peephole
+;  [(set (match_operand:SI 0 "memory_operand" "=m")
+;      (match_operand:SI 1 "reg_or_0_operand" "rfJ"))
+;   (set (pc) (label_ref (match_operand 2 "" "")))]
+;  ""
+;  "* return output_delayed_branch (\"br %l2\", operands, insn);")
+
+(define_insn "tablejump"
+  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
+   (use (label_ref (match_operand 1 "" "")))]
+  ""
+  "bri %0\;nop")
+
+;(define_peephole
+;  [(set (match_operand:SI 0 "memory_operand" "=m")
+;      (match_operand:SI 1 "reg_or_0_operand" "rfJ"))
+;   (set (pc) (match_operand:SI 2 "register_operand" "r"))
+;   (use (label_ref (match_operand 3 "" "")))]
+;  ""
+;  "* return output_delayed_branch (\"bri %2\", operands, insn);")
+
+;;- jump to subroutine
+(define_expand "call"
+  [(call (match_operand:SI 0 "memory_operand" "m")
+        (match_operand 1 "" "i"))]
+  ;; operand[2] is next_arg_register
+  ""
+  "
+{
+  /* Make sure the address is just one reg and will stay that way.  */
+  if (! call_insn_operand (operands[0], QImode))
+    operands[0]
+      = replace_equiv_address (operands[0],
+                              copy_to_mode_reg (Pmode,
+                                                XEXP (operands[0], 0)));
+  if (INTVAL (operands[1]) > 0)
+    {
+      emit_move_insn (arg_pointer_rtx, stack_pointer_rtx);
+      emit_insn (gen_rtx_USE (VOIDmode, arg_pointer_rtx));
+    }
+}")
+
+;;- jump to subroutine
+(define_insn ""
+  [(call (match_operand:SI 0 "call_insn_operand" "m")
+        (match_operand 1 "" "i"))]
+  ;; operand[2] is next_arg_register
+  ""
+  "*
+{
+  /* strip the MEM.  */
+  operands[0] = XEXP (operands[0], 0);
+  CC_STATUS_INIT;
+  if (GET_CODE (operands[0]) == REG)
+    return \"calli %0\;nop\";
+  return \"call %0\;nop\";
+}")
+
+;(define_peephole
+;  [(set (match_operand:SI 0 "register_operand" "=rf")
+;      (match_operand:SI 1 "single_insn_src_p" "gfG"))
+;   (call (match_operand:SI 2 "memory_operand" "m")
+;       (match_operand 3 "" "i"))]
+;  ;;- Don't use operand 1 for most machines.
+;  "! reg_mentioned_p (operands[0], operands[2])"
+;  "*
+;{
+;  /* strip the MEM.  */
+;  operands[2] = XEXP (operands[2], 0);
+;  if (GET_CODE (operands[2]) == REG)
+;    return output_delayed_branch (\"calli %2\", operands, insn);
+;  return output_delayed_branch (\"call %2\", operands, insn);
+;}")
+
+;(define_peephole
+;  [(set (match_operand:SI 0 "memory_operand" "=m")
+;      (match_operand:SI 1 "reg_or_0_operand" "rfJ"))
+;   (call (match_operand:SI 2 "call_insn_operand" "m")
+;       (match_operand 3 "" "i"))]
+;  ;;- Don't use operand 1 for most machines.
+;  ""
+;  "*
+;{
+;  /* strip the MEM.  */
+;  operands[2] = XEXP (operands[2], 0);
+;  if (GET_CODE (operands[2]) == REG)
+;    return output_delayed_branch (\"calli %2\", operands, insn);
+;  return output_delayed_branch (\"call %2\", operands, insn);
+;}")
+
+(define_expand "call_value"
+  [(set (match_operand 0 "register_operand" "=rf")
+       (call (match_operand:SI 1 "memory_operand" "m")
+             (match_operand 2 "" "i")))]
+  ;; operand 3 is next_arg_register
+  ""
+  "
+{
+  /* Make sure the address is just one reg and will stay that way.  */
+  if (! call_insn_operand (operands[1], QImode))
+    operands[1]
+      = replace_equiv_address (operands[1],
+                              copy_to_mode_reg (Pmode,
+                                                XEXP (operands[1], 0)));
+  if (INTVAL (operands[2]) > 0)
+    {
+      emit_move_insn (arg_pointer_rtx, stack_pointer_rtx);
+      emit_insn (gen_rtx_USE (VOIDmode, arg_pointer_rtx));
+    }
+}")
+
+(define_insn ""
+  [(set (match_operand 0 "register_operand" "=rf")
+       (call (match_operand:SI 1 "call_insn_operand" "m")
+             (match_operand 2 "" "i")))]
+  ;; operand 3 is next_arg_register
+  ""
+  "*
+{
+  /* strip the MEM.  */
+  operands[1] = XEXP (operands[1], 0);
+  CC_STATUS_INIT;
+  if (GET_CODE (operands[1]) == REG)
+    return \"calli %1\;nop\";
+  return \"call %1\;nop\";
+}")
+
+;(define_peephole
+;  [(set (match_operand:SI 0 "register_operand" "=rf")
+;      (match_operand:SI 1 "single_insn_src_p" "gfG"))
+;   (set (match_operand 2 "" "=rf")
+;      (call (match_operand:SI 3 "call_insn_operand" "m")
+;            (match_operand 4 "" "i")))]
+;  ;;- Don't use operand 4 for most machines.
+;  "! reg_mentioned_p (operands[0], operands[3])"
+;  "*
+;{
+;  /* strip the MEM.  */
+;  operands[3] = XEXP (operands[3], 0);
+;  if (GET_CODE (operands[3]) == REG)
+;    return output_delayed_branch (\"calli %3\", operands, insn);
+;  return output_delayed_branch (\"call %3\", operands, insn);
+;}")
+
+;(define_peephole
+;  [(set (match_operand:SI 0 "memory_operand" "=m")
+;      (match_operand:SI 1 "reg_or_0_operand" "rJf"))
+;   (set (match_operand 2 "" "=rf")
+;      (call (match_operand:SI 3 "call_insn_operand" "m")
+;            (match_operand 4 "" "i")))]
+;  ;;- Don't use operand 4 for most machines.
+;  ""
+;  "*
+;{
+;  /* strip the MEM.  */
+;  operands[3] = XEXP (operands[3], 0);
+;  if (GET_CODE (operands[3]) == REG)
+;    return output_delayed_branch (\"calli %3\", operands, insn);
+;  return output_delayed_branch (\"call %3\", operands, insn);
+;}")
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+  [(parallel [(call (match_operand 0 "" "")
+                   (const_int 0))
+             (match_operand 1 "" "")
+             (match_operand 2 "" "")])]
+  ""
+  "
+{
+  int i;
+
+  emit_call_insn (GEN_CALL (operands[0], const0_rtx, NULL, const0_rtx));
+
+  for (i = 0; i < XVECLEN (operands[2], 0); i++)
+    {
+      rtx set = XVECEXP (operands[2], 0, i);
+      emit_move_insn (SET_DEST (set), SET_SRC (set));
+    }
+
+  /* The optimizer does not know that the call sets the function value
+     registers we stored in the result block.  We avoid problems by
+     claiming that all hard registers are used and clobbered at this
+     point.  */
+  emit_insn (gen_blockage ());
+
+  DONE;
+}")
+
+;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
+;; all of memory.  This blocks insns from being moved across this point.
+
+(define_insn "blockage"
+  [(unspec_volatile [(const_int 0)] 0)]
+  ""
+  "")
+\f
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop")
+
+(define_insn "indirect_jump"
+  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
+  ""
+  "bri %0")
+\f
+;;
+;; A special insn that does the work to get setup just
+;; before a table jump.
+;;
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
+                        (label_ref (match_operand 2 "" "")))))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+  return \"orh %H2,%?r0,%?r31\;or %L2,%?r31,%?r31\;ld.l %?r31(%1),%0\";
+}")
+  
+;(define_peephole
+;  [(set (match_operand:SI 0 "register_operand" "=rf")
+;      (match_operand:SI 1 "single_insn_src_p" "gfG"))
+;   (set (pc) (match_operand:SI 2 "register_operand" "r"))
+;   (use (label_ref (match_operand 3 "" "")))]
+;  "REGNO (operands[0]) != REGNO (operands[2])"
+;  "* return output_delayed_branch (\"bri %2\", operands, insn);")
diff --git a/gcc/config/i860/sysv4.h b/gcc/config/i860/sysv4.h
new file mode 100644 (file)
index 0000000..753807f
--- /dev/null
@@ -0,0 +1,143 @@
+/* Target definitions for GNU compiler for Intel 80860 running System V.4
+   Copyright (C) 1991, 1996, 2000, 2002 Free Software Foundation, Inc.
+   Contributed by Ron Guilmette (rfg@monkeys.com).
+
+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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#undef TARGET_VERSION
+#define TARGET_VERSION fprintf (stderr, " (i860 System V Release 4)");
+
+/* Provide a set of pre-definitions and pre-assertions appropriate for
+   the i860 running svr4.  Note that the symbol `__svr4__' MUST BE
+   DEFINED!  It is needed so that the va_list struct in va-i860.h
+   will get correctly defined for the svr4 (ABI compliant) case rather
+   than for the previous (svr3, svr2, ...) case.  It also needs to be
+   defined so that the correct (svr4) version of __builtin_saveregs
+   will be selected when we are building gnulib2.c.
+   __svr4__ is our extension.  */
+
+#define CPP_PREDEFINES \
+  "-Di860 -Dunix -DSVR4 -D__svr4__ -Asystem=unix -Asystem=svr4 -Acpu=i860 -Amachine=i860"
+
+/* For the benefit of i860_va_arg, flag it this way too.  */
+
+#define I860_SVR4_VA_LIST 1
+
+/* The prefix to be used in assembler output for all names of registers.
+   This string gets prepended to all i860 register names (svr4 only).  */
+
+#define I860_REG_PREFIX        "%"
+
+#define ASM_COMMENT_START "#"
+
+#undef TYPE_OPERAND_FMT
+#define TYPE_OPERAND_FMT      "\"%s\""
+
+/* The following macro definition overrides the one in i860.h
+   because the svr4 i860 assembler requires a different syntax
+   for getting parts of constant/relocatable values.  */
+
+#undef PRINT_OPERAND_PART
+#define PRINT_OPERAND_PART(FILE, X, PART_CODE)                         \
+  do { fprintf (FILE, "[");                                            \
+       output_address (X);                                             \
+       fprintf (FILE, "]@%s", PART_CODE);                              \
+  } while (0)
+
+#undef ASM_FILE_START
+#define ASM_FILE_START(FILE)                                           \
+  do { output_file_directive (FILE, main_input_filename);              \
+       fprintf (FILE, "\t.version\t\"01.01\"\n");                      \
+  } while (0)
+
+/* Output the special word the svr4 SDB wants to see just before
+   the first word of each function's prologue code.  */
+
+extern const char *current_function_original_name;
+
+/* This special macro is used to output a magic word just before the
+   first word of each function.  On some versions of UNIX running on
+   the i860, this word can be any word that looks like a NOP, however
+   under svr4, this neds to be an `shr r0,r0,r0' instruction in which
+   the normally unused low-order bits contain the length of the function
+   prologue code (in bytes).  This is needed to make the svr4 SDB debugger
+   happy.  */
+
+#undef ASM_OUTPUT_FUNCTION_PREFIX
+#define ASM_OUTPUT_FUNCTION_PREFIX(FILE, FNNAME)                       \
+  do { ASM_OUTPUT_ALIGN (FILE, 2);                                     \
+       fprintf ((FILE), "\t.long\t.ep.");                              \
+       assemble_name (FILE, FNNAME);                                   \
+       fprintf (FILE, "-");                                            \
+       assemble_name (FILE, FNNAME);                                   \
+       fprintf (FILE, "+0xc8000000\n");                                \
+       current_function_original_name = (FNNAME);                      \
+  } while (0)
+
+/* Output the special label that must go just after each function's
+   prologue code to support svr4 SDB.  */
+
+#define ASM_OUTPUT_PROLOGUE_SUFFIX(FILE)                               \
+  do { fprintf (FILE, ".ep.");                                         \
+       assemble_name (FILE, current_function_original_name);           \
+       fprintf (FILE, ":\n");                                          \
+  } while (0)
+
+/* Define the pseudo-ops used to switch to the .ctors and .dtors sections.
+   Note that we want to give these sections the SHF_WRITE attribute
+   because these sections will actually contain data (i.e. tables of
+   addresses of functions in the current root executable or shared library
+   file) and, in the case of a shared library, the relocatable addresses
+   will have to be properly resolved/relocated (and then written into) by
+   the dynamic linker when it actually attaches the given shared library
+   to the executing process.  (Note that on SVR4, you may wish to use the
+   `-z text' option to the ELF linker, when building a shared library, as
+   an additional check that you are doing everything right.  But if you do
+   use the `-z text' option when building a shared library, you will get
+   errors unless the .ctors and .dtors sections are marked as writable
+   via the SHF_WRITE attribute.)  */
+#undef CTORS_SECTION_ASM_OP
+#define CTORS_SECTION_ASM_OP   "\t.section\t.ctors,\"aw\""
+#undef DTORS_SECTION_ASM_OP
+#define DTORS_SECTION_ASM_OP   "\t.section\t.dtors,\"aw\""
+
+/* Add definitions to support the .tdesc section as specified in the svr4
+   ABI for the i860.  */
+
+#define TDESC_SECTION_ASM_OP    "\t.section\t.tdesc"
+
+#undef EXTRA_SECTIONS
+#define EXTRA_SECTIONS in_tdesc
+
+#undef EXTRA_SECTION_FUNCTIONS
+#define EXTRA_SECTION_FUNCTIONS                                                \
+  TDESC_SECTION_FUNCTION
+
+#define TDESC_SECTION_FUNCTION                                         \
+void                                                                   \
+tdesc_section ()                                                       \
+{                                                                      \
+  if (in_section != in_tdesc)                                          \
+    {                                                                  \
+      fprintf (asm_out_file, "%s\n", TDESC_SECTION_ASM_OP);            \
+      in_section = in_tdesc;                                           \
+    }                                                                  \
+}
+
diff --git a/gcc/config/i860/varargs.asm b/gcc/config/i860/varargs.asm
new file mode 100644 (file)
index 0000000..8f87006
--- /dev/null
@@ -0,0 +1,201 @@
+/* Special varargs support for i860.
+   Copyright (C) 2001  Free Software Foundation, Inc.
+
+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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#if defined(__svr4__) || defined(__alliant__)
+       .text
+       .align  4
+
+/* The Alliant needs the added underscore.  */
+       .globl  __builtin_saveregs
+__builtin_saveregs:
+       .globl  ___builtin_saveregs
+___builtin_saveregs:
+
+       andnot  0x0f,%sp,%sp    /* round down to 16-byte boundary */
+       adds    -96,%sp,%sp  /* allocate stack space for reg save
+                          area and also for a new va_list
+                          structure */
+       /* Save all argument registers in the arg reg save area.  The
+          arg reg save area must have the following layout (according
+          to the svr4 ABI):
+
+       struct {
+         union  {
+           float freg[8];
+           double dreg[4];
+         } float_regs;
+         long  ireg[12];
+       };
+       */
+
+       fst.q   %f8,  0(%sp) /* save floating regs (f8-f15)  */
+       fst.q   %f12,16(%sp) 
+
+       st.l    %r16,32(%sp) /* save integer regs (r16-r27) */
+       st.l    %r17,36(%sp) 
+       st.l    %r18,40(%sp)
+       st.l    %r19,44(%sp)
+       st.l    %r20,48(%sp)
+       st.l    %r21,52(%sp)
+       st.l    %r22,56(%sp)
+       st.l    %r23,60(%sp)
+       st.l    %r24,64(%sp)
+       st.l    %r25,68(%sp)
+       st.l    %r26,72(%sp)
+       st.l    %r27,76(%sp)
+
+       adds    80,%sp,%r16  /* compute the address of the new
+                          va_list structure.  Put in into
+                          r16 so that it will be returned
+                          to the caller.  */
+
+       /* Initialize all fields of the new va_list structure.  This
+          structure looks like:
+
+       typedef struct {
+           unsigned long       ireg_used;
+           unsigned long       freg_used;
+           long        *reg_base;
+           long        *mem_ptr;
+       } va_list;
+       */
+
+       st.l    %r0, 0(%r16) /* nfixed */
+       st.l    %r0, 4(%r16) /* nfloating */
+       st.l    %sp, 8(%r16) /* __va_ctl points to __va_struct.  */
+       bri     %r1     /* delayed return */
+       st.l    %r28,12(%r16) /* pointer to overflow args */
+
+#else /* not __svr4__ */
+#if defined(__PARAGON__)
+       /*
+        *      we'll use SVR4-ish varargs but need SVR3.2 assembler syntax,
+        *      and we stand a better chance of hooking into libraries
+        *      compiled by PGI.  [andyp@ssd.intel.com]
+        */
+       .text
+       .align  4
+       .globl  __builtin_saveregs
+__builtin_saveregs:
+       .globl  ___builtin_saveregs
+___builtin_saveregs:
+
+       andnot  0x0f,sp,sp      /* round down to 16-byte boundary */
+       adds    -96,sp,sp       /* allocate stack space for reg save
+                          area and also for a new va_list
+                          structure */
+       /* Save all argument registers in the arg reg save area.  The
+          arg reg save area must have the following layout (according
+          to the svr4 ABI):
+
+       struct {
+         union  {
+           float freg[8];
+           double dreg[4];
+         } float_regs;
+         long  ireg[12];
+       };
+       */
+
+       fst.q   f8,  0(sp)
+       fst.q   f12,16(sp) 
+       st.l    r16,32(sp)
+       st.l    r17,36(sp) 
+       st.l    r18,40(sp)
+       st.l    r19,44(sp)
+       st.l    r20,48(sp)
+       st.l    r21,52(sp)
+       st.l    r22,56(sp)
+       st.l    r23,60(sp)
+       st.l    r24,64(sp)
+       st.l    r25,68(sp)
+       st.l    r26,72(sp)
+       st.l    r27,76(sp)
+
+       adds    80,sp,r16  /* compute the address of the new
+                          va_list structure.  Put in into
+                          r16 so that it will be returned
+                          to the caller.  */
+
+       /* Initialize all fields of the new va_list structure.  This
+          structure looks like:
+
+       typedef struct {
+           unsigned long       ireg_used;
+           unsigned long       freg_used;
+           long        *reg_base;
+           long        *mem_ptr;
+       } va_list;
+       */
+
+       st.l    r0, 0(r16) /* nfixed */
+       st.l    r0, 4(r16) /* nfloating */
+       st.l    sp, 8(r16) /* __va_ctl points to __va_struct.  */
+       bri     r1      /* delayed return */
+       st.l    r28,12(r16) /* pointer to overflow args */
+#else /* not __PARAGON__ */
+       .text
+       .align  4
+
+       .globl  ___builtin_saveregs
+___builtin_saveregs:
+       mov     sp,r30
+       andnot  0x0f,sp,sp
+       adds    -96,sp,sp  /* allocate sufficient space on the stack */
+
+/* Fill in the __va_struct.  */
+       st.l    r16, 0(sp) /* save integer regs (r16-r27) */
+       st.l    r17, 4(sp) /* int       fixed[12] */
+       st.l    r18, 8(sp)
+       st.l    r19,12(sp)
+       st.l    r20,16(sp)
+       st.l    r21,20(sp)
+       st.l    r22,24(sp)
+       st.l    r23,28(sp)
+       st.l    r24,32(sp)
+       st.l    r25,36(sp)
+       st.l    r26,40(sp)
+       st.l    r27,44(sp)
+
+       fst.q   f8, 48(sp) /* save floating regs (f8-f15) */
+       fst.q   f12,64(sp) /* int floating[8] */
+
+/* Fill in the __va_ctl.  */
+       st.l    sp, 80(sp) /* __va_ctl points to __va_struct.  */
+       st.l    r28,84(sp) /* pointer to more args */
+       st.l    r0, 88(sp) /* nfixed */
+       st.l    r0, 92(sp) /* nfloating */
+
+       adds    80,sp,r16  /* return address of the __va_ctl.  */
+       bri     r1
+       mov     r30,sp
+               /* recover stack and pass address to start 
+                  of data.  */
+#endif /* not __PARAGON__ */
+#endif /* not __svr4__ */
diff --git a/gcc/config/i860/x-sysv4 b/gcc/config/i860/x-sysv4
new file mode 100644 (file)
index 0000000..c01fb88
--- /dev/null
@@ -0,0 +1,44 @@
+# The svr4 reference port for the i860 contains an alloca.o routine
+# in /usr/ucblib/libucb.a, but we can't just try to get that by
+# setting CLIB to /usr/ucblib/libucb.a because (unfortunately)
+# there are a lot of other routines in libucb.a which are supposed
+# to be the Berkeley versions of library routines normally found in
+# libc.a and many of these Berkeley versions are badly broken.  Thus,
+# if we try to link programs with libucb.a before libc.a, those
+# programs tend to crash.
+
+# Also, the alloca() routine supplied in early version of svr4 for
+# the i860 is non-ABI compliant.  It doesn't keep the stack aligned
+# to a 16-byte boundary as the ABI requires.
+
+# More importantly however, even a fully ABI compliant alloca() routine
+# would fail to work correctly with some versions of the native svr4 C
+# compiler currently being distributed for the i860 (as of 1/29/92).
+# The problem is that the native C compiler generates non-ABI-compliant
+# function epilogues which cut back the stack (upon function exit) in
+# an incorrect manner.  Specifically, they cut back the stack by adding
+# the nominal *static* frame size (determined statically at compile-time)
+# to the stack pointer rather than setting the stack pointer based upon
+# the current value of the frame pointer (as called for in the i860 ABI).
+# This can cause serious trouble in cases where you repeatedly call a
+# routine which itself calls alloca().  In such cases, the stack will
+# grow continuously until you finally run out of swap space or exceed
+# the system's process size limit.  To avoid this problem (which can
+# arise when a stage1 gcc is being used to build a stage2 gcc) you
+# *must* link in the C language version of alloca() which is supplied
+# with gcc to your stage1 version of gcc.  The following definition
+# forces that to happen.
+
+ALLOCA=alloca.o
+
+# We build all stages *without* shared libraries because that may make
+# debugging the compiler easier (until there is a GDB which supports
+# both Dwarf *and* svr4 shared libraries).
+
+# Note that the native C compiler for the svr4 reference port on the
+# i860 recognizes a special -gg option.  Using that option causes *full*
+# Dwarf debugging information to be generated, whereas using only -g
+# causes only limited Dwarf debugging information to be generated.
+# (This is an undocumented feature of the native svr4 C compiler.)
+
+CCLIBFLAGS=-Bstatic -dn -gg
This page took 0.229233 seconds and 5 git commands to generate.