Makefile.in (TARGET_H, [...]): New.
[gcc.git] / gcc / config / i860 / i860.c
index 2b1828dd9bae9124e09495ba261a833a4a16eee5..08e099b2eb43b77a4cac1ba696b4aba646a5d49a 100644 (file)
@@ -1,10 +1,11 @@
 /* Subroutines for insn-output.c for Intel 860
-   Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1991, 1997, 1998, 1999, 2000
+   Free Software Foundation, Inc.
    Derived from sparc.c.
 
    Written by Richard Stallman (rms@ai.mit.edu).
 
-   Hacked substantially by Ron Guilmette (rfg@ncd.com) to cater
+   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.
@@ -21,36 +22,50 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+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 "insn-flags.h"
 #include "output.h"
 #include "recog.h"
 #include "insn-attr.h"
-
-#include <stdio.h>
-
-static rtx find_addr_reg ();
+#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));
 
 #ifndef I860_REG_PREFIX
 #define I860_REG_PREFIX ""
 #endif
 
-char *i860_reg_prefix = I860_REG_PREFIX;
+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.  */
+
+struct gcc_target target = TARGET_INITIALIZER;
+\f
 /* Return non-zero if this pattern, can be evaluated safely, even if it
    was not asked for.  */
 int
@@ -99,7 +114,6 @@ safe_insn_src_p (op, mode)
     case AND:
     case IOR:
     case XOR:
-    case LSHIFT:
     case ASHIFT:
     case ASHIFTRT:
     case LSHIFTRT:
@@ -313,7 +327,7 @@ single_insn_src_p (op, mode)
          if (CONSTANT_P (arg)
              && !(GET_CODE (arg) == CONST_INT
                   && (SMALL_INT (arg)
-                      || INTVAL (arg) & 0xffff == 0)))
+                      || (INTVAL (arg) & 0xffff) == 0)))
            return 0;
        }
     case IOR:
@@ -323,17 +337,16 @@ single_insn_src_p (op, mode)
       if (CONSTANT_P (XEXP (op, 1))
          && !(GET_CODE (XEXP (op, 1)) == CONST_INT
               && (SMALL_INT (XEXP (op, 1))
-                  || INTVAL (XEXP (op, 1)) & 0xffff == 0)))
+                  || (INTVAL (XEXP (op, 1)) & 0xffff) == 0)))
        return 0;
 
-    case LSHIFT:
     case ASHIFT:
     case ASHIFTRT:
     case LSHIFTRT:
       return 1;
 
     case SUBREG:
-      if (SUBREG_WORD (op) != 0)
+      if (SUBREG_BYTE (op) != 0)
        return 0;
       return single_insn_src_p (SUBREG_REG (op), mode);
 
@@ -351,145 +364,6 @@ single_insn_src_p (op, mode)
       return 0;
     }
 }
-
-/* Nonzero only if this *really* is a single insn operand.  */
-int
-strict_single_insn_op_p (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  if (mode == VOIDmode)
-    mode = GET_MODE (op);
-
-  switch (GET_CODE (op))
-    {
-    case CC0:
-      return 1;
-
-    case CONST_INT:
-      if (SMALL_INT (op))
-       return 1;
-      /* We can put this set insn into delay slot, because this is one
-        insn; `orh'.  */
-      if ((INTVAL (op) & 0xffff) == 0)
-       return 1;
-      return 0;
-
-    case SYMBOL_REF:
-      return 0;
-
-    case REG:
-#if 0
-      /* This loses when moving an freg to a general reg.  */
-      return HARD_REGNO_NREGS (REGNO (op), mode) == 1;
-#endif
-      return (mode != DFmode && mode != DImode);
-
-    case MEM:
-      if (! CONSTANT_ADDRESS_P (XEXP (op, 0)))
-       return (mode != DFmode && mode != DImode);
-      return 0;
-
-      /* We never need to negate or complement constants.  */
-    case NEG:
-      return (mode != DFmode);
-    case NOT:
-    case ZERO_EXTEND:
-      return 1;
-
-    case PLUS:
-    case MINUS:
-      /* Detect cases that require multiple instructions.  */
-      if (CONSTANT_P (XEXP (op, 1))
-         && !(GET_CODE (XEXP (op, 1)) == CONST_INT
-              && SMALL_INT (XEXP (op, 1))))
-       return 0;
-    case EQ:
-    case NE:
-    case LT:
-    case GT:
-    case LE:
-    case GE:
-    case LTU:
-    case GTU:
-    case LEU:
-    case GEU:
-      return 1;
-
-    case AND:
-      if (GET_CODE (XEXP (op, 1)) == NOT)
-       {
-         rtx arg = XEXP (XEXP (op, 1), 0);
-         if (CONSTANT_P (arg)
-             && !(GET_CODE (arg) == CONST_INT
-                  && (SMALL_INT (arg)
-                      || INTVAL (arg) & 0xffff == 0)))
-           return 0;
-       }
-    case IOR:
-    case XOR:
-      /* Both small and round numbers take one instruction;
-        others take two.  */
-      if (CONSTANT_P (XEXP (op, 1))
-         && !(GET_CODE (XEXP (op, 1)) == CONST_INT
-              && (SMALL_INT (XEXP (op, 1))
-                  || INTVAL (XEXP (op, 1)) & 0xffff == 0)))
-       return 0;
-
-    case LSHIFT:
-    case ASHIFT:
-    case ASHIFTRT:
-    case LSHIFTRT:
-      return 1;
-
-    case SUBREG:
-      if (SUBREG_WORD (op) != 0)
-       return 0;
-      return strict_single_insn_op_p (SUBREG_REG (op), mode);
-
-    case SIGN_EXTEND:
-      if (GET_CODE (XEXP (op, 0)) == MEM
-         && ! CONSTANT_ADDRESS_P (XEXP (XEXP (op, 0), 0)))
-       return 1;
-      return 0;
-
-      /* Not doing floating point, since they probably
-        take longer than the branch slot they might fill.  */
-    case FLOAT_EXTEND:
-    case FLOAT_TRUNCATE:
-    case FLOAT:
-    case FIX:
-    case UNSIGNED_FLOAT:
-    case UNSIGNED_FIX:
-      return 0;
-
-    default:
-      return 0;
-    }
-}
-\f
-/* Return truth value of whether OP is a relational operator.  */
-int
-relop (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  switch (GET_CODE (op))
-    {
-    case EQ:
-    case NE:
-    case GT:
-    case GE:
-    case LT:
-    case LE:
-    case GTU:
-    case GEU:
-    case LTU:
-    case LEU:
-      return 1;
-    }
-  return 0;
-}
 \f
 /* Return non-zero only if OP is a register of mode MODE,
    or const0_rtx.  */
@@ -592,7 +466,7 @@ load_operand (op, mode)
 int
 small_int (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
 }
@@ -603,15 +477,35 @@ small_int (op, mode)
 int
 logic_int (op, mode)
      rtx op;
-     enum machine_mode mode;
+     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 char *
+static const char *
 singlemove_string (operands)
      rtx *operands;
 {
@@ -640,7 +534,7 @@ singlemove_string (operands)
          rtx xoperands[2];
 
          cc_status.flags &= ~CC_F0_IS_0;
-         xoperands[0] = gen_rtx (REG, SFmode, 32);
+         xoperands[0] = gen_rtx_REG (SFmode, 32);
          xoperands[1] = operands[1];
          output_asm_insn (singlemove_string (xoperands), xoperands);
          xoperands[1] = xoperands[0];
@@ -669,12 +563,16 @@ singlemove_string (operands)
     }
  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";
-     if (operands[1] == const0_rtx)
-      return "mov %?r0,%0";
+
+     return "orh %H1,%?r0,%0\n\tor %L1,%0,%0";
    }
   return "mov %1,%0";
 }
@@ -682,13 +580,15 @@ singlemove_string (operands)
 /* Output assembler code to perform a doubleword move insn
    with operands OPERANDS.  */
 
-char *
+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.  */
 
@@ -741,14 +641,14 @@ output_move_double (operands)
      operands in OPERANDS to be suitable for the low-numbered word.  */
 
   if (optype0 == REGOP)
-    latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+    latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
   else if (optype0 == OFFSOP)
     latehalf[0] = adj_offsettable_operand (operands[0], 4);
   else
     latehalf[0] = operands[0];
 
   if (optype1 == REGOP)
-    latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+    latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
   else if (optype1 == OFFSOP)
     latehalf[1] = adj_offsettable_operand (operands[1], 4);
   else if (optype1 == CNSTOP)
@@ -797,15 +697,40 @@ output_move_double (operands)
   else if (optype0 == REGOP && optype1 != REGOP
           && reg_overlap_mentioned_p (operands[0], operands[1]))
     {
-      /* Do the late half first.  */
-      output_asm_insn (singlemove_string (latehalf), latehalf);
-      /* Then clobber.  */
-      return singlemove_string (operands);
+      /* 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] = adj_offsettable_operand (operands[1], 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.  */
+  /* Normal case: do the two words, low-numbered first.
+     Overlap case (highest_first set): do high-numbered word first.  */
 
-  output_asm_insn (singlemove_string (operands), operands);
+  if (! highest_first)
+    output_asm_insn (singlemove_string (operands), operands);
 
   CC_STATUS_PARTIAL_INIT;
   /* Make any unoffsettable addresses point at high-numbered word.  */
@@ -820,20 +745,23 @@ output_move_double (operands)
   /* Undo the adds we just did.  */
   if (addreg0)
     output_asm_insn ("adds -0x4,%0,%0", &addreg0);
-  if (addreg1)
+  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
-char *
+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);
+    operands[1] = gen_rtx_REG (DFmode, F0_REGNUM);
 
   if (FP_REG_P (operands[0]))
     {
@@ -842,8 +770,8 @@ output_fp_move_double (operands)
       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);
+         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))
@@ -868,8 +796,8 @@ output_fp_move_double (operands)
       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);
+         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)))
@@ -925,14 +853,14 @@ find_addr_reg (addr)
 
    This string is in static storage.   */
 
-static char *
+static const char *
 load_opcode (mode, args, reg)
      enum machine_mode mode;
-     char *args;
+     const char *args;
      rtx reg;
 {
   static char buf[30];
-  char *opcode;
+  const char *opcode;
 
   switch (mode)
     {
@@ -972,14 +900,14 @@ load_opcode (mode, args, reg)
 
    This string is in static storage.   */
 
-static char *
+static const char *
 store_opcode (mode, args, reg)
      enum machine_mode mode;
-     char *args;
+     const char *args;
      rtx reg;
 {
   static char buf[30];
-  char *opcode;
+  const char *opcode;
 
   switch (mode)
     {
@@ -1023,13 +951,12 @@ store_opcode (mode, args, reg)
    It may also output some insns directly.
    It may alter the values of operands[0] and operands[1].  */
 
-char *
+const char *
 output_store (operands)
      rtx *operands;
 {
   enum machine_mode mode = GET_MODE (operands[0]);
   rtx address = XEXP (operands[0], 0);
-  char *string;
 
   cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
   cc_status.mdep = address;
@@ -1065,7 +992,7 @@ output_store (operands)
    It may also output some insns directly.
    It may alter the values of operands[0] and operands[1].  */
 
-char *
+const char *
 output_load (operands)
      rtx *operands;
 {
@@ -1196,14 +1123,13 @@ output_size_for_block_move (size, reg, align)
 
 #if 1
   cc_status.flags &= ~ CC_KNOW_HI_R31;
-  output_asm_insn ("mov %1,%0", xoperands);
+  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_rtx (CONST_INT, VOIDmode, INTVAL (size) - INTVAL (align));
+      xoperands[1] = GEN_INT (INTVAL (size) - INTVAL (align));
       cc_status.flags &= ~ CC_KNOW_HI_R31;
       output_asm_insn ("mov %1,%0", xoperands);
     }
@@ -1218,16 +1144,18 @@ output_size_for_block_move (size, reg, align)
    OPERANDS[3] is the known safe alignment.
    OPERANDS[4..6] are pseudos we can safely clobber as temps.  */
 
-char *
+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, j;
+  int i;
   rtx temp1 = operands[4];
   rtx alignrtx = operands[3];
   int align = INTVAL (alignrtx);
@@ -1242,7 +1170,7 @@ output_block_move (operands)
   if (align > 4)
     {
       align = 4;
-      alignrtx = gen_rtx (CONST_INT, VOIDmode, 4);
+      alignrtx = GEN_INT (4);
     }
 
   /* Recognize special cases of block moves.  These occur
@@ -1327,7 +1255,7 @@ output_block_move (operands)
 
   /* Generate number for unique label.  */
 
-  xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++);
+  xoperands[3] = GEN_INT (movstrsi_label++);
 
   /* Calculate the size of the chunks we will be trying to move first.  */
 
@@ -1342,13 +1270,13 @@ output_block_move (operands)
 
   /* Copy the increment (negative) to a register for bla insn.  */
 
-  xoperands[4] = gen_rtx (CONST_INT, VOIDmode, - chunk_size);
+  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 precceds the loop, but we need to have it done
-     two times before we enter the loop because of the bizzare semantics
+     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);
@@ -1412,6 +1340,7 @@ output_block_move (operands)
   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.
@@ -1432,10 +1361,13 @@ output_block_move (operands)
        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.  */
 
-char *
+const char *
 output_delayed_branch (template, operands, insn)
-     char *template;
+     const char *template;
      rtx *operands;
      rtx insn;
 {
@@ -1474,7 +1406,7 @@ output_delayed_branch (template, operands, insn)
               && CONSTANT_ADDRESS_P (XEXP (dest, 0))))
     {
       rtx xoperands[2];
-      char *split_template;
+      const char *split_template;
       xoperands[0] = dest;
       xoperands[1] = src;
 
@@ -1514,51 +1446,50 @@ output_delayed_branch (template, operands, insn)
   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);
+      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 outputing the branch insn,
-        since operands may just be a pointer to `recog_operand'.  */
-      INSN_CODE (delay_insn) = insn_code_number = recog (pat, delay_insn);
+        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_n_operands[insn_code_number]; i++)
+      for (i = 0; i < insn_data[insn_code_number].n_operands; i++)
        {
-         if (GET_CODE (recog_operand[i]) == SUBREG)
-           recog_operand[i] = alter_subreg (recog_operand[i]);
+         if (GET_CODE (recog_data.operand[i]) == SUBREG)
+           recog_data.operand[i] = alter_subreg (recog_data.operand[i]);
        }
 
       insn_extract (delay_insn);
-      if (! constrain_operands (insn_code_number, 1))
+      if (! constrain_operands (1))
        fatal_insn_not_found (delay_insn);
 
-      template = insn_template[insn_code_number];
-      if (template == 0)
-       template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn);
-      output_asm_insn (template, recog_operand);
+      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.  */
-char *
+const char *
 output_delay_insn (delay_insn)
      rtx delay_insn;
 {
-  char *template;
+  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 outputing the branch insn,
-     since operands may just be a pointer to `recog_operand'.  */
+     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 ();
@@ -1567,20 +1498,18 @@ output_delay_insn (delay_insn)
   INSN_CODE (delay_insn) = insn_code_number;
   insn_extract (delay_insn);
 
-  /* It is possible that this insn has not been properly scaned by final
+  /* 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_n_operands[insn_code_number]; i++)
+  for (i = 0; i < insn_data[insn_code_number].n_operands; i++)
     {
-      if (GET_CODE (recog_operand[i]) == SUBREG)
-       recog_operand[i] = alter_subreg (recog_operand[i]);
+      if (GET_CODE (recog_data.operand[i]) == SUBREG)
+       recog_data.operand[i] = alter_subreg (recog_data.operand[i]);
     }
 
-#ifdef REGISTER_CONSTRAINTS
-  if (! constrain_operands (insn_code_number))
+  if (! constrain_operands (1))
     abort ();
-#endif
 
   cc_prev_status = cc_status;
 
@@ -1594,12 +1523,11 @@ output_delay_insn (delay_insn)
   /* Now get the template for what this insn would
      have been, without the branch.  */
 
-  template = insn_template[insn_code_number];
-  if (template == 0)
-    template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn);
-  output_asm_insn (template, recog_operand);
+  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.
@@ -1614,21 +1542,17 @@ unsigned long
 sfmode_constant_to_ulong (x)
      rtx x;
 {
-  union { double d; unsigned long i[2]; } u;
+  REAL_VALUE_TYPE d;
   union { float f; unsigned long i; } u2;
 
   if (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != SFmode)
     abort ();
 
-#ifndef HOST_WORDS_BIG_ENDIAN
-  u.i[0] = CONST_DOUBLE_LOW (x);
-  u.i[1] = CONST_DOUBLE_HIGH (x);
-#else
-  u.i[0] = CONST_DOUBLE_HIGH (x);
-  u.i[1] = CONST_DOUBLE_LOW (x);
+#if TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT
+ error IEEE emulation needed
 #endif
-
-  u2.f = u.d;
+  REAL_VALUE_FROM_CONST_DOUBLE (d, x);
+  u2.f = d;
   return u2.i;
 }
 \f
@@ -1667,7 +1591,7 @@ sfmode_constant_to_ulong (x)
    offsets from the frame pointer).
 
    Previous versions of GCC also saved the "preserved" registers in the
-   "nagative" part of the frame, but they saved them using positive
+   "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
@@ -1706,7 +1630,7 @@ sfmode_constant_to_ulong (x)
 
    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 consistant with what is currently generated by 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
@@ -1733,7 +1657,6 @@ sfmode_constant_to_ulong (x)
 #endif
 
 extern char call_used_regs[];
-extern int leaf_function_p ();
 
 char *current_function_original_name;
 
@@ -2041,15 +1964,49 @@ function_prologue (asm_file, local_bytes)
    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;
+
 void
 function_epilogue (asm_file, local_bytes)
      register FILE *asm_file;
      register unsigned local_bytes;
 {
   register unsigned frame_upper_bytes;
+  register unsigned frame_lower_bytes;
   register unsigned 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;
+
+  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.  */
@@ -2073,17 +2030,31 @@ function_epilogue (asm_file, local_bytes)
 
   /* Restore all of the "preserved" registers that need restoring.  */
 
-  for (i = 1; i < 32; i++)
-    if (regs_ever_live[i] && ! call_used_regs[i])
+  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])
+  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.  */
 
@@ -2092,9 +2063,11 @@ function_epilogue (asm_file, local_bytes)
 
   /* Restore the return address and the old frame pointer.  */
 
-  if (must_preserve_r1)
+  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);
@@ -2103,4 +2076,284 @@ function_epilogue (asm_file, local_bytes)
 
   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", ASM_LONG, intflags);
+  fprintf(asm_file, "%s %d\n", ASM_LONG,
+       int_restored ? must_preserve_bytes : 0);
+  if (flags->version > 1) {
+    fprintf(asm_file, "%s %d\n", ASM_LONG,
+       (restored_so_far == int_restored) ? 0 : must_preserve_bytes +
+         (4 * int_restored));
+    if (flags->version > 2) {
+      fprintf(asm_file, "%s %d\n", ASM_LONG, frame_upper_bytes);
+      if (flags->version > 3)
+       fprintf(asm_file, "%s %d\n", ASM_LONG, frame_lower_bytes);
+    }
+  }
+  tdesc_section();
+  fprintf(asm_file, "%s ", ASM_LONG);
+  assemble_name(asm_file, current_function_original_name);
+  fprintf(asm_file, "\n%s ", ASM_LONG);
+  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), 0, 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;
 }