OSDN Git Service

Merge in gcc2-ss-010999
[pf3gnuchains/gcc-fork.git] / gcc / config / i860 / i860.c
index 51175c7..d55e912 100644 (file)
@@ -1,10 +1,10 @@
 /* Subroutines for insn-output.c for Intel 860
-   Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+   Copyright (C) 1989, 91, 97, 98, 1999 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,12 +21,15 @@ 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"
@@ -36,8 +39,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "output.h"
 #include "recog.h"
 #include "insn-attr.h"
-
-#include <stdio.h>
+#include "function.h"
+#include "expr.h"
 
 static rtx find_addr_reg ();
 
@@ -99,7 +102,6 @@ safe_insn_src_p (op, mode)
     case AND:
     case IOR:
     case XOR:
-    case LSHIFT:
     case ASHIFT:
     case ASHIFTRT:
     case LSHIFTRT:
@@ -313,7 +315,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,10 +325,9 @@ 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:
@@ -351,145 +352,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.  */
@@ -607,6 +469,26 @@ logic_int (op, mode)
 {
   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;
+{
+  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.  */
@@ -640,7 +522,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 +551,14 @@ 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 "mov %1,%0";
 }
@@ -689,6 +573,8 @@ output_move_double (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 +627,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 +683,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,9 +731,12 @@ 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
@@ -833,7 +747,7 @@ output_fp_move_double (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 +756,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 +782,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)))
@@ -1196,14 +1110,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);
     }
@@ -1242,7 +1155,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 +1240,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,7 +1255,7 @@ 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);
 
@@ -1412,6 +1325,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,6 +1346,9 @@ 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 *
 output_delayed_branch (template, operands, insn)
@@ -1514,8 +1431,8 @@ 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.  */
@@ -1524,7 +1441,8 @@ output_delayed_branch (template, operands, insn)
       /* 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_operand'.  */
-      INSN_CODE (delay_insn) = insn_code_number = recog (pat, delay_insn);
+      INSN_CODE (delay_insn) = insn_code_number
+       = recog (pat, delay_insn, NULL_PTR);
       if (insn_code_number == -1)
        abort ();
 
@@ -1535,7 +1453,7 @@ output_delayed_branch (template, operands, insn)
        }
 
       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];
@@ -1578,7 +1496,7 @@ output_delay_insn (delay_insn)
     }
 
 #ifdef REGISTER_CONSTRAINTS
-  if (! constrain_operands (insn_code_number))
+  if (! constrain_operands (1))
     abort ();
 #endif
 
@@ -1600,6 +1518,7 @@ output_delay_insn (delay_insn)
   output_asm_insn (template, recog_operand);
   return "";
 }
+#endif
 \f
 /* Special routine to convert an SFmode value represented as a
    CONST_DOUBLE into its equivalent unsigned long bit pattern.
@@ -1621,7 +1540,7 @@ sfmode_constant_to_ulong (x)
     abort ();
 
 #if TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT
-# error IEEE emulation needed
+ error IEEE emulation needed
 #endif
   REAL_VALUE_FROM_CONST_DOUBLE (d, x);
   u2.f = d;
@@ -2037,15 +1956,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.  */
@@ -2069,17 +2022,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;
+    }
 
-  for (i = 32; i < 64; i++)
-    if (regs_ever_live[i] && ! call_used_regs[i])
+  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.  */
 
@@ -2088,9 +2055,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);
@@ -2099,4 +2068,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;
 }