OSDN Git Service

* a29k.h, alpha.h, arc.h, arm.h, avr.h, clipper.h, convex.h,
[pf3gnuchains/gcc-fork.git] / gcc / config / i960 / i960.c
index 170d8cd..6f29fdc 100644 (file)
@@ -1,5 +1,6 @@
 /* Subroutines used for code generation on intel 80960.
-   Copyright (C) 1992, 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1992, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+   Free Software Foundation, Inc.
    Contributed by Steven McGeady, Intel Corp.
    Additional Work by Glenn Colon-Bonet, Jonathan Shapiro, Andy Wilson
    Converted to GCC 2.0 by Jim Wilson and Michael Tiemann, Cygnus Support.
@@ -21,27 +22,33 @@ 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 <stdio.h>
-
 #include "config.h"
+#include "system.h"
+#include <math.h>
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
 #include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
-#include "insn-flags.h"
 #include "output.h"
 #include "insn-attr.h"
 #include "flags.h"
 #include "tree.h"
-#include "insn-codes.h"
-#include "assert.h"
 #include "expr.h"
 #include "except.h"
 #include "function.h"
 #include "recog.h"
-#include <math.h>
+#include "toplev.h"
+#include "cpplib.h"
+#include "c-pragma.h"
+#include "c-lex.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+
+static void i960_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
+static void i960_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
 
 /* Save the operands last given to a compare for use when we
    generate a scc or bcc insn.  */
@@ -51,8 +58,8 @@ rtx i960_compare_op0, i960_compare_op1;
 /* Used to implement #pragma align/noalign.  Initialized by OVERRIDE_OPTIONS
    macro in i960.h.  */
 
-static int i960_maxbitalignment;
-static int i960_last_maxbitalignment;
+int i960_maxbitalignment;
+int i960_last_maxbitalignment;
 
 /* Used to implement switching between MEM and ALU insn types, for better
    C series performance.  */
@@ -84,90 +91,18 @@ static int ret_label = 0;
 ((TYPE_ARG_TYPES (TREE_TYPE (FNDECL)) != 0                                                   \
   && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (FNDECL)))) != void_type_node))    \
  || current_function_varargs)
+\f
+/* Initialize the GCC target structure.  */
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
 
-/* Handle pragmas for compatibility with Intel's compilers.  */
-
-/* ??? This is incomplete, since it does not handle all pragmas that the
-   intel compilers understand.  */
-
-int
-process_pragma (finput, t)
-     FILE *finput;
-     tree t;
-{
-  int i;
-  register int c;
-  register char *pname;
-
-  if (TREE_CODE (t) != IDENTIFIER_NODE)
-    return 0;
-
-  pname = IDENTIFIER_POINTER (t);
-
-  if (strcmp (pname, "align") == 0)
-    {
-      char buf[20];
-      char *s = buf;
-      int align;
-
-      do {
-       c = getc (finput);
-      } while (c == ' ' || c == '\t');
-
-      if (c == '(')
-       c = getc (finput);
-      while (c >= '0' && c <= '9')
-       {
-         if (s < buf + sizeof buf - 1)
-           *s++ = c;
-         c = getc (finput);
-       }
-      *s = '\0';
-
-      align = atoi (buf);
-      switch (align)
-       {
-       case 0:
-         /* Return to last alignment.  */
-         align = i960_last_maxbitalignment / 8;
-         /* Fall through.  */
-       case 16:
-       case 8:
-       case 4:
-       case 2:
-       case 1:
-         i960_last_maxbitalignment = i960_maxbitalignment;
-         i960_maxbitalignment = align * 8;
-         break;
-
-       default:
-         /* Silently ignore bad values.  */
-         break;
-       }
-
-      /* NOTE: ic960 R3.0 pragma align definition:
-
-        #pragma align [(size)] | (identifier=size[,...])
-        #pragma noalign [(identifier)[,...]]
-
-        (all parens are optional)
-
-        - size is [1,2,4,8,16]
-        - noalign means size==1
-        - applies only to component elements of a struct (and union?)
-        - identifier applies to structure tag (only)
-        - missing identifier means next struct
-
-        - alignment rules for bitfields need more investigation  */
-
-      return 1;
-    }
-
-  /* Should be pragma 'far' or equivalent for callx/balx here.  */
-
-  return 0;
-}
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE i960_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE i960_output_function_epilogue
 
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
 /* Initialize variables before compiling any files.  */
 
 void
@@ -241,7 +176,7 @@ fp_arith_operand (op, mode)
   return (register_operand (op, mode) || fp_literal (op, mode));
 }
 
-/* Return true is OP is a register or a valid signed integer literal.  */
+/* Return true if OP is a register or a valid signed integer literal.  */
 
 int
 signed_arith_operand (op, mode)
@@ -251,13 +186,13 @@ signed_arith_operand (op, mode)
   return (register_operand (op, mode) || signed_literal (op, mode));
 }
 
-/* Return truth value of whether OP is a integer which fits the
+/* Return truth value of whether OP is an integer which fits the
    range constraining immediate operands in three-address insns.  */
 
 int
 literal (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   return ((GET_CODE (op) == CONST_INT) && INTVAL(op) >= 0 && INTVAL(op) < 32);
 }
@@ -297,7 +232,7 @@ fp_literal(op, mode)
 int
 signed_literal(op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   return ((GET_CODE (op) == CONST_INT) && INTVAL(op) > -32 && INTVAL(op) < 32);
 }
@@ -308,7 +243,7 @@ signed_literal(op, mode)
 int
 symbolic_memory_operand (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   if (GET_CODE (op) == SUBREG)
     op = SUBREG_REG (op);
@@ -324,7 +259,7 @@ symbolic_memory_operand (op, mode)
 int
 eq_or_neq (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   return (GET_CODE (op) == EQ || GET_CODE (op) == NE);
 }
@@ -346,7 +281,7 @@ arith32_operand (op, mode)
 int
 power2_operand (op,mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   if (GET_CODE (op) != CONST_INT)
     return 0;
@@ -360,7 +295,7 @@ power2_operand (op,mode)
 int
 cmplpower2_operand (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   if (GET_CODE (op) != CONST_INT)
     return 0;
@@ -398,7 +333,7 @@ int
 is_mask (val)
      unsigned int val;
 {
-  register int start, end, i;
+  register int start, end = 0, i;
 
   start = -1;
   for (i = 0; val != 0; val >>= 1, i++)
@@ -478,7 +413,7 @@ bitstr (val, s, e)
 enum machine_mode
 select_cc_mode (op, x)
      RTX_CODE op;
-     rtx x;
+     rtx x ATTRIBUTE_UNUSED;
 {
   if (op == GTU || op == LTU || op == GEU || op == LEU)
     return CC_UNSmode;
@@ -506,9 +441,9 @@ gen_compare_reg (code, x, y)
        y = force_reg (SImode, y);
     }
 
-  cc_reg = gen_rtx (REG, ccmode, 36);
-  emit_insn (gen_rtx (SET, VOIDmode, cc_reg,
-                     gen_rtx (COMPARE, ccmode, x, y)));
+  cc_reg = gen_rtx_REG (ccmode, 36);
+  emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
+                         gen_rtx_COMPARE (ccmode, x, y)));
 
   return cc_reg;
 }
@@ -528,6 +463,12 @@ i960_address_cost (x)
   if (GET_CODE (x) == REG)
     return 1;
 #endif
+  /* This is a MEMA operand -- it's free.  */
+  if (GET_CODE (x) == CONST_INT
+      && INTVAL (x) >= 0
+      && INTVAL (x) < 4096)
+    return 0;
+
   if (GET_CODE (x) == PLUS)
     {
       rtx base = XEXP (x, 0);
@@ -577,8 +518,12 @@ emit_move_sequence (operands, mode)
      enum machine_mode mode;
 {
   /* We can only store registers to memory.  */
-
-  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG)
+  
+  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG
+      && (operands[1] != const0_rtx || current_function_args_size
+         || current_function_varargs || current_function_stdarg
+         || rtx_equal_function_value_matters))
+    /* Here we use the same test as movsi+1 pattern -- see i960.md.  */
     operands[1] = force_reg (mode, operands[1]);
 
   /* Storing multi-word values in unaligned hard registers to memory may
@@ -586,18 +531,23 @@ emit_move_sequence (operands, mode)
      adding 4 to the memory address may not yield a valid insn.  */
   /* ??? We don't always need the scratch, but that would complicate things.
      Maybe later.  */
+  /* ??? We must also handle stores to pseudos here, because the pseudo may be
+     replaced with a MEM later.  This would be cleaner if we didn't have
+     a separate pattern for unaligned DImode/TImode stores.  */
   if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
-      && GET_CODE (operands[0]) == MEM
+      && (GET_CODE (operands[0]) == MEM
+         || (GET_CODE (operands[0]) == REG
+             && REGNO (operands[0]) >= FIRST_PSEUDO_REGISTER))
       && GET_CODE (operands[1]) == REG
       && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
       && ! HARD_REGNO_MODE_OK (REGNO (operands[1]), mode))
     {
-      emit_insn (gen_rtx (PARALLEL, VOIDmode,
-                         gen_rtvec (2,
-                                    gen_rtx (SET, VOIDmode,
-                                             operands[0], operands[1]),
-                                    gen_rtx (CLOBBER, VOIDmode,
-                                             gen_rtx (SCRATCH, Pmode)))));
+      emit_insn (gen_rtx_PARALLEL
+                (VOIDmode,
+                 gen_rtvec (2,
+                            gen_rtx_SET (VOIDmode, operands[0], operands[1]),
+                            gen_rtx_CLOBBER (VOIDmode,
+                                             gen_rtx_SCRATCH (Pmode)))));
       return 1;
     }
 
@@ -606,7 +556,7 @@ emit_move_sequence (operands, mode)
 
 /* Output assembler to move a double word value.  */
 
-char *
+const char *
 i960_output_move_double (dst, src)
      rtx dst, src;
 {
@@ -648,10 +598,12 @@ i960_output_move_double (dst, src)
             edge conditions.  */
          operands[0] = dst;
          operands[1] = src;
-         operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 1);
-         operands[3] = gen_rtx (MEM, word_mode, operands[2]);
-         operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD);
-         output_asm_insn ("lda %1,%2\n\tld     %3,%0\n\tld     %4,%D0", operands);
+         operands[2] = gen_rtx_REG (Pmode, REGNO (dst) + 1);
+         operands[3] = gen_rtx_MEM (word_mode, operands[2]);
+         operands[4] = adjust_address (operands[3], word_mode,
+                                       UNITS_PER_WORD);
+         output_asm_insn
+           ("lda       %1,%2\n\tld     %3,%0\n\tld     %4,%D0", operands);
          return "";
        }
       else
@@ -662,8 +614,13 @@ i960_output_move_double (dst, src)
     {
       if (REGNO (src) & 1)
        {
-         /* This is handled by emit_move_sequence so we shouldn't get here.  */
-         abort ();
+         operands[0] = dst;
+         operands[1] = adjust_address (dst, word_mode, UNITS_PER_WORD);
+         if (! memory_address_p (word_mode, XEXP (operands[1], 0)))
+           abort ();
+         operands[2] = src;
+         output_asm_insn ("st  %2,%0\n\tst     %D2,%1", operands);
+         return "";
        }
       return "stl      %1,%0";
     }
@@ -671,9 +628,25 @@ i960_output_move_double (dst, src)
     abort ();
 }
 
+/* Output assembler to move a double word zero.  */
+
+const char *
+i960_output_move_double_zero (dst)
+     rtx dst;
+{
+  rtx operands[2];
+
+  operands[0] = dst;
+    {
+      operands[1] = adjust_address (dst, word_mode, 4);
+      output_asm_insn ("st     g14,%0\n\tst    g14,%1", operands);
+    }
+  return "";
+}
+
 /* Output assembler to move a quad word value.  */
 
-char *
+const char *
 i960_output_move_quad (dst, src)
      rtx dst, src;
 {
@@ -717,11 +690,14 @@ i960_output_move_quad (dst, src)
             edge conditions.  */
          operands[0] = dst;
          operands[1] = src;
-         operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 3);
-         operands[3] = gen_rtx (MEM, word_mode, operands[2]);
-         operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD);
-         operands[5] = adj_offsettable_operand (operands[4], UNITS_PER_WORD);
-         operands[6] = adj_offsettable_operand (operands[5], UNITS_PER_WORD);
+         operands[2] = gen_rtx_REG (Pmode, REGNO (dst) + 3);
+         operands[3] = gen_rtx_MEM (word_mode, operands[2]);
+         operands[4]
+           = adjust_address (operands[3], word_mode, UNITS_PER_WORD);
+         operands[5]
+           = adjust_address (operands[4], word_mode, UNITS_PER_WORD);
+         operands[6]
+           = adjust_address (operands[5], word_mode, UNITS_PER_WORD);
          output_asm_insn ("lda %1,%2\n\tld     %3,%0\n\tld     %4,%D0\n\tld    %5,%E0\n\tld    %6,%F0", operands);
          return "";
        }
@@ -733,19 +709,45 @@ i960_output_move_quad (dst, src)
     {
       if (REGNO (src) & 3)
        {
-         /* This is handled by emit_move_sequence so we shouldn't get here.  */
-         abort ();
+         operands[0] = dst;
+         operands[1] = adjust_address (dst, word_mode, UNITS_PER_WORD);
+         operands[2] = adjust_address (dst, word_mode, 2 * UNITS_PER_WORD);
+         operands[3] = adjust_address (dst, word_mode, 3 * UNITS_PER_WORD);
+         if (! memory_address_p (word_mode, XEXP (operands[3], 0)))
+           abort ();
+         operands[4] = src;
+         output_asm_insn ("st  %4,%0\n\tst     %D4,%1\n\tst    %E4,%2\n\tst    %F4,%3", operands);
+         return "";
        }
       return "stq      %1,%0";
     }
   else
     abort ();
 }
+
+/* Output assembler to move a quad word zero.  */
+
+const char *
+i960_output_move_quad_zero (dst)
+     rtx dst;
+{
+  rtx operands[4];
+
+  operands[0] = dst;
+    {
+      operands[1] = adjust_address (dst, word_mode, 4);
+      operands[2] = adjust_address (dst, word_mode, 8);
+      operands[3] = adjust_address (dst, word_mode, 12);
+      output_asm_insn ("st     g14,%0\n\tst    g14,%1\n\tst    g14,%2\n\tst    g14,%3", operands);
+    }
+  return "";
+}
+
 \f
 /* Emit insns to load a constant to non-floating point registers.
    Uses several strategies to try to use as few insns as possible.  */
 
-char *
+const char *
 i960_output_ldconst (dst, src)
      register rtx dst, src;
 {
@@ -781,7 +783,7 @@ i960_output_ldconst (dst, src)
 
       for (i = 0; i < 3; i++)
        {
-         operands[0] = gen_rtx (REG, SImode, REGNO (dst) + i);
+         operands[0] = gen_rtx_REG (SImode, REGNO (dst) + i);
          operands[1] = GEN_INT (value_long[i]);
          output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
                           operands);
@@ -800,11 +802,11 @@ i960_output_ldconst (dst, src)
 
       output_asm_insn ("# ldconst      %1,%0",operands);
 
-      operands[0] = gen_rtx (REG, SImode, REGNO (dst));
+      operands[0] = gen_rtx_REG (SImode, REGNO (dst));
       operands[1] = first;
       output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
                      operands);
-      operands[0] = gen_rtx (REG, SImode, REGNO (dst) + 1);
+      operands[0] = gen_rtx_REG (SImode, REGNO (dst) + 1);
       operands[1] = second;
       output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
                      operands);
@@ -819,8 +821,8 @@ i960_output_ldconst (dst, src)
       REAL_VALUE_TO_TARGET_SINGLE (d, value);
 
       output_asm_insn ("# ldconst      %1,%0",operands);
-      operands[0] = gen_rtx (REG, SImode, REGNO (dst));
-      operands[1] = gen_rtx (CONST_INT, VOIDmode, value);
+      operands[0] = gen_rtx_REG (SImode, REGNO (dst));
+      operands[1] = GEN_INT (value);
       output_asm_insn (i960_output_ldconst (operands[0], operands[1]),
                      operands);
       return "";
@@ -855,7 +857,7 @@ i960_output_ldconst (dst, src)
        return "movl    %1,%0";
 
       /* Output the upper half with a recursive call.  */
-      xoperands[0] = gen_rtx (REG, SImode, REGNO (dst) + 1);
+      xoperands[0] = gen_rtx_REG (SImode, REGNO (dst) + 1);
       xoperands[1] = upperhalf;
       output_asm_insn (i960_output_ldconst (xoperands[0], xoperands[1]),
                       xoperands);
@@ -891,7 +893,7 @@ i960_output_ldconst (dst, src)
        {
          if (i960_last_insn_type == I_TYPE_REG && TARGET_C_SERIES)
            return "lda %1,%0";
-         operands[1] = gen_rtx (CONST_INT, VOIDmode, rsrc1 - 31);
+         operands[1] = GEN_INT (rsrc1 - 31);
          output_asm_insn ("addo\t31,%1,%0\t# ldconst %3,%0", operands);
          return "";
        }
@@ -902,7 +904,7 @@ i960_output_ldconst (dst, src)
       if (rsrc1 >= -31)
        {
          /* return 'sub -(%1),0,%0' */
-         operands[1] = gen_rtx (CONST_INT, VOIDmode, - rsrc1);
+         operands[1] = GEN_INT (- rsrc1);
          output_asm_insn ("subo\t%1,0,%0\t# ldconst %3,%0", operands);
          return "";
        }
@@ -910,7 +912,7 @@ i960_output_ldconst (dst, src)
       /* ldconst       -32             ->      not     31,X  */
       if (rsrc1 == -32)
        {
-         operands[1] = gen_rtx (CONST_INT, VOIDmode, ~rsrc1);
+         operands[1] = GEN_INT (~rsrc1);
          output_asm_insn ("not\t%1,%0  # ldconst %3,%0", operands);
          return "";
        }
@@ -919,7 +921,7 @@ i960_output_ldconst (dst, src)
   /* If const is a single bit.  */
   if (bitpos (rsrc1) >= 0)
     {
-      operands[1] = gen_rtx (CONST_INT, VOIDmode, bitpos (rsrc1));
+      operands[1] = GEN_INT (bitpos (rsrc1));
       output_asm_insn ("setbit\t%1,0,%0\t# ldconst %3,%0", operands);
       return "";
     }
@@ -932,8 +934,8 @@ i960_output_ldconst (dst, src)
       if (bitstr (rsrc1, &s, &e) < 6)
        {
          rsrc2 = ((unsigned int) rsrc1) >> s;
-         operands[1] = gen_rtx (CONST_INT, VOIDmode, rsrc2);
-         operands[2] = gen_rtx (CONST_INT, VOIDmode, s);
+         operands[1] = GEN_INT (rsrc2);
+         operands[2] = GEN_INT (s);
          output_asm_insn ("shlo\t%2,%1,%0\t# ldconst %3,%0", operands);
          return "";
        }
@@ -999,7 +1001,7 @@ i960_bypass (insn, op1, op2, cmpbr_flag)
 void
 i960_function_name_declare (file, name, fndecl)
      FILE *file;
-     char *name;
+     const char *name;
      tree fndecl;
 {
   register int i, j;
@@ -1032,7 +1034,7 @@ i960_function_name_declare (file, name, fndecl)
       leaf_proc_ok = 0;
     }
       
-  /* See if caller passes in an address to return value. */
+  /* See if caller passes in an address to return value.  */
 
   if (aggregate_value_p (DECL_RESULT (fndecl)))
     {
@@ -1134,7 +1136,7 @@ i960_function_name_declare (file, name, fndecl)
       assemble_name (file, name);
       fprintf (file, ",%s.lf\n", (name[0] == '*' ? &name[1] : name));
       ASM_OUTPUT_LABEL (file, name);
-      fprintf (file, "\tlda    LR%d,g14\n", ret_label);
+      fprintf (file, "\tlda    Li960R%d,g14\n", ret_label);
       fprintf (file, "%s.lf:\n", (name[0] == '*' ? &name[1] : name));
       fprintf (file, "\tmov    g14,g%d\n", i960_leaf_ret_reg);
 
@@ -1173,37 +1175,143 @@ compute_frame_size (size)
   return actual_fsize;
 }
 
+/* Here register group is range of registers which can be moved by
+   one i960 instruction.  */
+
+struct reg_group
+{
+  char start_reg;
+  char length;
+};
+
+static int i960_form_reg_groups PARAMS ((int, int, int *, int, struct reg_group *));
+static int i960_reg_group_compare PARAMS ((const void *, const void *));
+static int i960_split_reg_group PARAMS ((struct reg_group *, int, int));
+static void i960_arg_size_and_align PARAMS ((enum machine_mode, tree, int *, int *));
+
+/* The following functions forms the biggest as possible register
+   groups with registers in STATE.  REGS contain states of the
+   registers in range [start, finish_reg).  The function returns the
+   number of groups formed.  */
+static int
+i960_form_reg_groups (start_reg, finish_reg, regs, state, reg_groups)
+     int start_reg;
+     int finish_reg;
+     int *regs;
+     int state;
+     struct reg_group *reg_groups;
+{
+  int i;
+  int nw = 0;
+
+  for (i = start_reg; i < finish_reg; )
+    {
+      if (regs [i] != state)
+       {
+         i++;
+         continue;
+       }
+      else if (i % 2 != 0 || regs [i + 1] != state)
+       reg_groups [nw].length = 1;
+      else if (i % 4 != 0 || regs [i + 2] != state)
+       reg_groups [nw].length = 2;
+      else if (regs [i + 3] != state)
+       reg_groups [nw].length = 3;
+      else
+       reg_groups [nw].length = 4;
+      reg_groups [nw].start_reg = i;
+      i += reg_groups [nw].length;
+      nw++;
+    }
+  return nw;
+}
+
+/* We sort register winodws in descending order by length.  */
+static int
+i960_reg_group_compare (group1, group2)
+     const void *group1;
+     const void *group2;
+{
+  const struct reg_group *w1 = group1;
+  const struct reg_group *w2 = group2;
+
+  if (w1->length > w2->length)
+    return -1;
+  else if (w1->length < w2->length)
+    return 1;
+  else
+    return 0;
+}
+
+/* Split the first register group in REG_GROUPS on subgroups one of
+   which will contain SUBGROUP_LENGTH registers.  The function
+   returns new number of winodws.  */
+static int
+i960_split_reg_group (reg_groups, nw, subgroup_length)
+     struct reg_group *reg_groups;
+     int nw;
+     int subgroup_length;
+{
+  if (subgroup_length < reg_groups->length - subgroup_length)
+    /* This guarantees correct alignments of the two subgroups for
+       i960 (see spliting for the group length 2, 3, 4).  More
+       generalized algorithm would require splitting the group more
+       two subgroups.  */
+    subgroup_length = reg_groups->length - subgroup_length;
+  /* More generalized algorithm would require to try merging
+     subgroups here.  But in case i960 it always results in failure
+     because of register group alignment.  */
+  reg_groups[nw].length = reg_groups->length - subgroup_length;
+  reg_groups[nw].start_reg = reg_groups->start_reg + subgroup_length;
+  nw++;
+  reg_groups->length = subgroup_length;
+  qsort (reg_groups, nw, sizeof (struct reg_group), i960_reg_group_compare);
+  return nw;
+}
+
 /* Output code for the function prologue.  */
 
-void
-i960_function_prologue (file, size)
+static void
+i960_output_function_prologue (file, size)
      FILE *file;
-     unsigned int size;
+     HOST_WIDE_INT size;
 {
   register int i, j, nr;
-  int n_iregs = 0;
-  int rsize = 0;
-  int actual_fsize, offset;
+  int n_saved_regs = 0;
+  int n_remaining_saved_regs;
+  HOST_WIDE_INT lvar_size;
+  HOST_WIDE_INT actual_fsize, offset;
+  int gnw, lnw;
+  struct reg_group *g, *l;
   char tmpstr[1000];
   /* -1 if reg must be saved on proc entry, 0 if available, 1 if saved
      somewhere.  */
   int regs[FIRST_PSEUDO_REGISTER];
+  /* All global registers (which must be saved) divided by groups.  */
+  struct reg_group global_reg_groups [16];
+  /* All local registers (which are available) divided by groups.  */
+  struct reg_group local_reg_groups [16];
+
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     if (regs_ever_live[i]
-       && ((! call_used_regs[i]) || (i > 7 && i < 12)))
+       && ((! call_used_regs[i]) || (i > 7 && i < 12))
+       /* No need to save the static chain pointer.  */
+       && ! (i == STATIC_CHAIN_REGNUM && current_function_needs_context))
       {
        regs[i] = -1;
         /* Count global registers that need saving.  */
        if (i < 16)
-         n_iregs++;
+         n_saved_regs++;
       }
     else
       regs[i] = 0;
 
+  n_remaining_saved_regs = n_saved_regs;
+
   epilogue_string[0] = '\0';
 
-  if (profile_flag || profile_block_flag)
+  if (current_function_profile)
     {
       /* When profiling, we may use registers 20 to 27 to save arguments, so
         they can't be used here for saving globals.  J is the number of
@@ -1215,69 +1323,48 @@ i960_function_prologue (file, size)
        regs[i] = -1;
     }
 
-  /* First look for local registers to save globals in.  */
-  for (i = 0; i < 16; i++)
+  gnw = i960_form_reg_groups (0, 16, regs, -1, global_reg_groups);
+  lnw = i960_form_reg_groups (19, 32, regs, 0, local_reg_groups);
+  qsort (global_reg_groups, gnw, sizeof (struct reg_group),
+        i960_reg_group_compare);
+  qsort (local_reg_groups, lnw, sizeof (struct reg_group),
+        i960_reg_group_compare);
+  for (g = global_reg_groups, l = local_reg_groups; lnw != 0 && gnw != 0;)
     {
-      if (regs[i] == 0)
-       continue;
-
-      /* Start at r4, not r3.  */
-      for (j = 20; j < 32; j++)
+      if (g->length == l->length)
        {
-         if (regs[j] != 0)
-           continue;
-
-         regs[i] = 1;
-         regs[j] = -1;
-         regs_ever_live[j] = 1;
-         nr = 1;
-         if (i <= 14 && i % 2 == 0 && j <= 30 && j % 2 == 0
-             && regs[i+1] != 0 && regs[j+1] == 0)
-           {
-             nr = 2;
-             regs[i+1] = 1;
-             regs[j+1] = -1;
-             regs_ever_live[j+1] = 1;
-           }
-         if (nr == 2 && i <= 12 && i % 4 == 0 && j <= 28 && j % 4 == 0
-             && regs[i+2] != 0 && regs[j+2] == 0)
-           {
-             nr = 3;
-             regs[i+2] = 1;
-             regs[j+2] = -1;
-             regs_ever_live[j+2] = 1;
-           }
-         if (nr == 3 && regs[i+3] != 0 && regs[j+3] == 0)
-           {
-             nr = 4;
-             regs[i+3] = 1;
-             regs[j+3] = -1;
-             regs_ever_live[j+3] = 1;
-           }
-
          fprintf (file, "\tmov%s       %s,%s\n",
-                  ((nr == 4) ? "q" :
-                   (nr == 3) ? "t" :
-                   (nr == 2) ? "l" : ""),
-                  reg_names[i], reg_names[j]);
+                  ((g->length == 4) ? "q" :
+                   (g->length == 3) ? "t" :
+                   (g->length == 2) ? "l" : ""),
+                  reg_names[(unsigned char) g->start_reg],
+                  reg_names[(unsigned char) l->start_reg]);
          sprintf (tmpstr, "\tmov%s     %s,%s\n",
-                  ((nr == 4) ? "q" :
-                   (nr == 3) ? "t" :
-                   (nr == 2) ? "l" : ""),
-                  reg_names[j], reg_names[i]);
+                  ((g->length == 4) ? "q" :
+                   (g->length == 3) ? "t" :
+                   (g->length == 2) ? "l" : ""),
+                  reg_names[(unsigned char) l->start_reg],
+                  reg_names[(unsigned char) g->start_reg]);
          strcat (epilogue_string, tmpstr);
-
-         n_iregs -= nr;
-         i += nr-1;
-         break;
+         n_remaining_saved_regs -= g->length;
+         for (i = 0; i < g->length; i++)
+           {
+             regs [i + g->start_reg] = 1;
+             regs [i + l->start_reg] = -1;
+             regs_ever_live [i + l->start_reg] = 1;
+           }
+         g++;
+         l++;
+         gnw--;
+         lnw--;
        }
+      else if (g->length > l->length)
+       gnw = i960_split_reg_group (g, gnw, l->length);
+      else
+       lnw = i960_split_reg_group (l, lnw, g->length);
     }
 
-  /* N_iregs is now the number of global registers that haven't been saved
-     yet.  */
-
-  rsize = (n_iregs * 4);
-  actual_fsize = compute_frame_size (size) + rsize;
+  actual_fsize = compute_frame_size (size) + 4 * n_remaining_saved_regs;
 #if 0
   /* ??? The 1.2.1 compiler does this also.  This is meant to round the frame
      size up to the nearest multiple of 16.  I don't know whether this is
@@ -1290,6 +1377,34 @@ i960_function_prologue (file, size)
   actual_fsize = (actual_fsize + 15) & ~0xF;
 #endif
 
+  /* Check stack limit if necessary.  */
+  if (current_function_limit_stack)
+    {
+      rtx min_stack = stack_limit_rtx;
+      if (actual_fsize != 0)
+       min_stack = plus_constant (stack_limit_rtx, -actual_fsize);
+
+      /* Now, emulate a little bit of reload.  We want to turn 'min_stack'
+        into an arith_operand.  Use register 20 as the temporary.  */
+      if (legitimate_address_p (Pmode, min_stack, 1) 
+         && !arith_operand (min_stack, Pmode))
+       {
+         rtx tmp = gen_rtx_MEM (Pmode, min_stack);
+         fputs ("\tlda\t", file);
+         i960_print_operand (file, tmp, 0);
+         fputs (",r4\n", file);
+         min_stack = gen_rtx_REG (Pmode, 20);
+       }
+      if (arith_operand (min_stack, Pmode))
+       {
+         fputs ("\tcmpo\tsp,", file);
+         i960_print_operand (file, min_stack, 0);
+         fputs ("\n\tfaultge.f\n", file);
+       }
+      else
+       warning ("stack limit expression is not supported");
+    }
+
   /* Allocate space for register save and locals.  */
   if (actual_fsize > 0)
     {
@@ -1301,9 +1416,12 @@ i960_function_prologue (file, size)
 
   /* Take hardware register save area created by the call instruction
      into account, but store them before the argument block area.  */
-  offset = 64 + actual_fsize - compute_frame_size (0) - rsize;
+  lvar_size = actual_fsize - compute_frame_size (0) - n_remaining_saved_regs * 4;
+  offset = STARTING_FRAME_OFFSET + lvar_size;
   /* Save registers on stack if needed.  */
-  for (i = 0, j = n_iregs; j > 0 && i < 16; i++)
+  /* ??? Is it worth to use the same algorithm as one for saving
+     global registers in local registers? */
+  for (i = 0, j = n_remaining_saved_regs; j > 0 && i < 16; i++)
     {
       if (regs[i] != -1)
        continue;
@@ -1336,17 +1454,17 @@ i960_function_prologue (file, size)
       offset += nr * 4;
     }
 
-  if (actual_fsize == 0 && size == 0 && rsize == 0)
+  if (actual_fsize == 0)
     return;
 
   fprintf (file, "\t#Prologue stats:\n");
   fprintf (file, "\t#  Total Frame Size: %d bytes\n", actual_fsize);
 
-  if (size)
-    fprintf (file, "\t#  Local Variable Size: %d bytes\n", size);
-  if (rsize)
+  if (lvar_size)
+    fprintf (file, "\t#  Local Variable Size: %d bytes\n", lvar_size);
+  if (n_saved_regs)
     fprintf (file, "\t#  Register Save Size: %d regs, %d bytes\n",
-            n_iregs, rsize);
+            n_saved_regs, n_saved_regs * 4);
   fprintf (file, "\t#End Prologue#\n");
 }
 
@@ -1431,14 +1549,14 @@ output_function_profiler (file, labelno)
 
 /* Output code for the function epilogue.  */
 
-void
-i960_function_epilogue (file, size)
+static void
+i960_output_function_epilogue (file, size)
      FILE *file;
-     unsigned int size;
+     HOST_WIDE_INT size ATTRIBUTE_UNUSED;
 {
   if (i960_leaf_ret_reg >= 0)
     {
-      fprintf (file, "LR%d:    ret\n", ret_label);
+      fprintf (file, "Li960R%d:        ret\n", ret_label);
       return;
     }
 
@@ -1468,11 +1586,11 @@ i960_function_epilogue (file, size)
            }
          break;
        }
-      fprintf (file, "LR%d:    ret\n", ret_label);
+      fprintf (file, "Li960R%d:        ret\n", ret_label);
       return;
     }
 
-  fprintf (file, "LR%d:\n", ret_label);
+  fprintf (file, "Li960R%d:\n", ret_label);
 
   fprintf (file, "\t#EPILOGUE#\n");
 
@@ -1494,7 +1612,7 @@ i960_function_epilogue (file, size)
 
 /* Output code for a call insn.  */
 
-char *
+const char *
 i960_output_call_insn (target, argsize_rtx, arg_pointer, insn)
      register rtx target, argsize_rtx, arg_pointer, insn;
 {
@@ -1546,7 +1664,7 @@ i960_output_call_insn (target, argsize_rtx, arg_pointer, insn)
 
 /* Output code for a return insn.  */
 
-char *
+const char *
 i960_output_ret_insn (insn)
      register rtx insn;
 {
@@ -1557,7 +1675,7 @@ i960_output_ret_insn (insn)
       if (! TARGET_CODE_ALIGN && next_real_insn (insn) == 0)
        return "";
 
-      sprintf (lbuf, "b        LR%d", ret_label);
+      sprintf (lbuf, "b        Li960R%d", ret_label);
       return lbuf;
     }
 
@@ -1575,49 +1693,15 @@ i960_output_ret_insn (insn)
   return "ret";
 }
 \f
-#if 0
-/* Return a character string representing the branch prediction
-   opcode to be tacked on an instruction.  This must at least
-   return a null string.  */
-
-char *
-i960_br_predict_opcode (lab_ref, insn)
-     rtx lab_ref, insn;
-{
-  if (TARGET_BRANCH_PREDICT)
-    {
-      unsigned long label_uid;
-      
-      if (GET_CODE (lab_ref) == CODE_LABEL)
-       label_uid = INSN_UID (lab_ref);
-      else if (GET_CODE (lab_ref) == LABEL_REF)
-       label_uid = INSN_UID (XEXP (lab_ref, 0));
-      else
-       return ".f";
-
-      /* If not optimizing, then the insn_addresses array will not be
-        valid.  In this case, always return ".t" since most branches
-        are taken.  If optimizing, return .t for backward branches
-        and .f for forward branches.  */
-      if (! optimize
-         || insn_addresses[label_uid] < insn_addresses[INSN_UID (insn)])
-       return ".t";
-      return ".f";
-    }
-    
-  return "";
-}
-#endif
-
 /* Print the operand represented by rtx X formatted by code CODE.  */
 
 void
 i960_print_operand (file, x, code)
      FILE *file;
      rtx x;
-     char code;
+     int code;
 {
-  enum rtx_code rtxcode = GET_CODE (x);
+  enum rtx_code rtxcode = x ? GET_CODE (x) : NIL;
 
   if (rtxcode == REG)
     {
@@ -1738,6 +1822,19 @@ i960_print_operand (file, x, code)
       else abort ();
       break;
 
+    case '+':
+      /* For conditional branches, substitute ".t" or ".f".  */
+      if (TARGET_BRANCH_PREDICT)
+       {
+         x = find_reg_note (current_output_insn, REG_BR_PROB, 0);
+         if (x)
+           {
+             int pred_val = INTVAL (XEXP (x, 0));
+             fputs ((pred_val < REG_BR_PROB_BASE / 2 ? ".f" : ".t"), file);
+           }
+       }
+      break;
+
     case 0:
       output_addr_const (file, x);
       break;
@@ -1892,7 +1989,7 @@ i960_print_operand_addr (file, addr)
 
 int
 legitimate_address_p (mode, addr, strict)
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
      register rtx addr;
      int strict;
 {
@@ -1982,8 +2079,8 @@ legitimate_address_p (mode, addr, strict)
 rtx
 legitimize_address (x, oldx, mode)
      register rtx x;
-     register rtx oldx;
-     enum machine_mode mode;
+     register rtx oldx ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 { 
   if (GET_CODE (x) == SYMBOL_REF)
     {
@@ -2000,9 +2097,9 @@ legitimize_address (x, oldx, mode)
      similar optimizations.  */
   if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT
       && GET_CODE (XEXP (x, 1)) == PLUS)
-    x = gen_rtx (PLUS, Pmode,
-                gen_rtx (PLUS, Pmode, XEXP (x, 0), XEXP (XEXP (x, 1), 0)),
-                XEXP (XEXP (x, 1), 1));
+    x = gen_rtx_PLUS (Pmode,
+                     gen_rtx_PLUS (Pmode, XEXP (x, 0), XEXP (XEXP (x, 1), 0)),
+                     XEXP (XEXP (x, 1), 1));
 
   /* Canonicalize (plus (plus (mult (reg) (const)) (plus (reg) (const))) const)
      into (plus (plus (mult (reg) (const)) (reg)) (const)).  */
@@ -2024,13 +2121,13 @@ legitimize_address (x, oldx, mode)
          other = XEXP (x, 1);
        }
       else
-       constant = 0;
+       constant = 0, other = 0;
 
       if (constant)
-       x = gen_rtx (PLUS, Pmode,
-                    gen_rtx (PLUS, Pmode, XEXP (XEXP (x, 0), 0),
-                             XEXP (XEXP (XEXP (x, 0), 1), 0)),
-                    plus_constant (other, INTVAL (constant)));
+       x = gen_rtx_PLUS (Pmode,
+                         gen_rtx_PLUS (Pmode, XEXP (XEXP (x, 0), 0),
+                                       XEXP (XEXP (XEXP (x, 0), 1), 0)),
+                         plus_constant (other, INTVAL (constant)));
     }
 
   return x;
@@ -2038,7 +2135,7 @@ legitimize_address (x, oldx, mode)
 \f
 #if 0
 /* Return the most stringent alignment that we are willing to consider
-   objects of size SIZE and known alignment ALIGN as having. */
+   objects of size SIZE and known alignment ALIGN as having.  */
    
 int
 i960_alignment (size, align)
@@ -2059,40 +2156,57 @@ i960_alignment (size, align)
 }
 #endif
 \f
-/* Modes for condition codes.  */
-#define C_MODES                \
-  ((1 << (int) CCmode) | (1 << (int) CC_UNSmode) | (1<< (int) CC_CHKmode))
 
-/* Modes for single-word (and smaller) quantities.  */
-#define S_MODES                                                \
- (~C_MODES                                             \
-  & ~ ((1 << (int) DImode) | (1 << (int) TImode)       \
-       | (1 << (int) DFmode) | (1 << (int) XFmode)))
-
-/* Modes for double-word (and smaller) quantities.  */
-#define D_MODES                                        \
-  (~C_MODES                                    \
-   & ~ ((1 << (int) TImode) | (1 << (int) XFmode)))
+int
+hard_regno_mode_ok (regno, mode)
+     int regno;
+     enum machine_mode mode;
+{
+  if (regno < 32)
+    {
+      switch (mode)
+       {
+       case CCmode: case CC_UNSmode: case CC_CHKmode:
+         return 0;
 
-/* Modes for quad-word quantities.  */
-#define T_MODES (~C_MODES)
+       case DImode: case DFmode:
+         return (regno & 1) == 0;
 
-/* Modes for single-float quantities.  */
-#define SF_MODES ((1 << (int) SFmode))
+       case TImode: case XFmode:
+         return (regno & 3) == 0;
 
-/* Modes for double-float quantities.  */
-#define DF_MODES (SF_MODES | (1 << (int) DFmode) | (1 << (int) SCmode))
+       default:
+         return 1;
+       }
+    }
+  else if (regno >= 32 && regno < 36)
+    {
+      switch (mode)
+       {
+       case SFmode: case DFmode: case XFmode:
+       case SCmode: case DCmode:
+         return 1;
 
-/* Modes for quad-float quantities.  */
-#define XF_MODES (DF_MODES | (1 << (int) XFmode) | (1 << (int) DCmode))
+       default:
+         return 0;
+       }
+    }
+  else if (regno == 36)
+    {
+      switch (mode)
+       {
+       case CCmode: case CC_UNSmode: case CC_CHKmode:
+         return 1;
 
-unsigned int hard_regno_mode_ok[FIRST_PSEUDO_REGISTER] = {
-  T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
-  T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
-  T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
-  T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
+       default:
+         return 0;
+       }
+    }
+  else if (regno == 37)
+    return 0;
 
-  XF_MODES, XF_MODES, XF_MODES, XF_MODES, C_MODES};
+  abort ();
+}
 
 \f
 /* Return the minimum alignment of an expression rtx X in bytes.  This takes
@@ -2133,7 +2247,7 @@ i960_expr_alignment (x, size)
 
     case SYMBOL_REF:
       /* If this is a valid program, objects are guaranteed to be
-        correctly aligned for whatever size the reference actually is. */
+        correctly aligned for whatever size the reference actually is.  */
       align = i960_object_bytes_bitalign (size) / BITS_PER_UNIT;
       break;
 
@@ -2143,7 +2257,7 @@ i960_expr_alignment (x, size)
       break;
 
     case ASHIFT:
-      align = i960_expr_alignment (XEXP (x, 0));
+      align = i960_expr_alignment (XEXP (x, 0), size);
 
       if (GET_CODE (XEXP (x, 1)) == CONST_INT)
        {
@@ -2158,6 +2272,8 @@ i960_expr_alignment (x, size)
 
       align = MIN (align, 16);
       break;
+    default:
+      break;
     }
 
   return align;
@@ -2237,7 +2353,8 @@ i960_arg_size_and_align (mode, type, size_out, align_out)
   else if (mode == VOIDmode)
     {
       /* End of parm list.  */
-      assert (type != 0 && TYPE_MODE (type) == VOIDmode);
+      if (type == 0 || TYPE_MODE (type) != VOIDmode)
+       abort ();
       size = 1;
     }
   else
@@ -2277,7 +2394,7 @@ i960_function_arg_advance (cum, mode, type, named)
      CUMULATIVE_ARGS *cum;
      enum machine_mode mode;
      tree type;
-     int named;
+     int named ATTRIBUTE_UNUSED;
 {
   int size, align;
 
@@ -2304,11 +2421,14 @@ i960_function_arg (cum, mode, type, named)
      CUMULATIVE_ARGS *cum;
      enum machine_mode mode;
      tree type;
-     int named;
+     int named ATTRIBUTE_UNUSED;
 {
   rtx ret;
   int size, align;
 
+  if (mode == VOIDmode)
+    return 0;
+
   i960_arg_size_and_align (mode, type, &size, &align);
 
   if (size > 4 || cum->ca_nstackparms != 0
@@ -2321,60 +2441,12 @@ i960_function_arg (cum, mode, type, named)
   else
     {
       cum->ca_nregparms = ROUND_PARM (cum->ca_nregparms, align);
-      ret = gen_rtx (REG, mode, cum->ca_nregparms);
+      ret = gen_rtx_REG (mode, cum->ca_nregparms);
     }
 
   return ret;
 }
 \f
-/* Floating-point support.  */
-
-void
-i960_output_long_double (file, value)
-     FILE *file;
-     REAL_VALUE_TYPE value;
-{
-  long value_long[3];
-  char dstr[30];
-
-  REAL_VALUE_TO_TARGET_LONG_DOUBLE (value, value_long);
-  REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr);
-
-  fprintf (file,
-          "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n\t.word\t0x%08lx\n",
-          value_long[0], dstr, value_long[1], value_long[2]);
-  fprintf (file, "\t.word\t0x0\n");
-}
-
-void
-i960_output_double (file, value)
-     FILE *file;
-     REAL_VALUE_TYPE value;
-{
-  long value_long[2];
-  char dstr[30];
-
-  REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
-  REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr);
-
-  fprintf (file, "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n",
-          value_long[0], dstr, value_long[1]);
-}
-  
-void
-i960_output_float (file, value)
-     FILE *file;
-     REAL_VALUE_TYPE value;
-{
-  long value_long;
-  char dstr[30];
-
-  REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
-  REAL_VALUE_TO_DECIMAL (value, "%.12g", dstr);
-
-  fprintf (file, "\t.word\t0x%08lx\t\t# %s (float)\n", value_long, dstr);
-}
-\f
 /* Return the number of bits that an object of size N bytes is aligned to.  */
 
 int
@@ -2401,7 +2473,7 @@ i960_round_align (align, tsize)
 {
   int new_align;
 
-  if (TREE_CODE (tsize) != INTEGER_CST)
+  if (! tsize || TREE_CODE (tsize) != INTEGER_CST)
     return align;
 
   new_align = i960_object_bytes_bitalign (TREE_INT_CST_LOW (tsize)
@@ -2423,9 +2495,9 @@ i960_round_align (align, tsize)
 void
 i960_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
      CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int *pretend_size;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     tree type ATTRIBUTE_UNUSED;
+     int *pretend_size ATTRIBUTE_UNUSED;
      int no_rtl;
 {
   /* Note: for a varargs fn with only a va_alist argument, this is 0.  */
@@ -2439,7 +2511,7 @@ i960_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
 
      If there are no stack arguments but there are exactly NPARM_REGS
      registers, either there were no extra arguments or the caller
-     allocated an argument block. */
+     allocated an argument block.  */
 
   if (cum->ca_nstackparms == 0 && first_reg < NPARM_REGS && !no_rtl)
     {
@@ -2453,25 +2525,126 @@ i960_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
         va_start assumes it.  */
       emit_insn (gen_cmpsi (arg_pointer_rtx, const0_rtx));
       emit_jump_insn (gen_bne (label));
-      emit_insn (gen_rtx (SET, VOIDmode, arg_pointer_rtx,
-                         stack_pointer_rtx));
-      emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx,
-                         memory_address (SImode,
-                                         plus_constant (stack_pointer_rtx,
-                                                        48))));
+      emit_insn (gen_rtx_SET (VOIDmode, arg_pointer_rtx,
+                             stack_pointer_rtx));
+      emit_insn (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                             memory_address (SImode,
+                                             plus_constant (stack_pointer_rtx,
+                                                            48))));
       emit_label (label);
 
       /* ??? Note that we unnecessarily store one extra register for stdarg
         fns.  We could optimize this, but it's kept as for now.  */
-      regblock = gen_rtx (MEM, BLKmode,
-                         plus_constant (arg_pointer_rtx,
-                                        first_reg * 4));
+      regblock = gen_rtx_MEM (BLKmode,
+                             plus_constant (arg_pointer_rtx, first_reg * 4));
+      set_mem_alias_set (regblock, get_varargs_alias_set ());
+      set_mem_align (regblock, BITS_PER_WORD);
       move_block_from_reg (first_reg, regblock,
                           NPARM_REGS - first_reg,
                           (NPARM_REGS - first_reg) * UNITS_PER_WORD);
     }
 }
 
+/* Define the `__builtin_va_list' type for the ABI.  */
+
+tree
+i960_build_va_list ()
+{
+  return build_array_type (unsigned_type_node,
+                          build_index_type (size_one_node));
+}
+
+/* Implement `va_start' for varargs and stdarg.  */
+
+void
+i960_va_start (stdarg_p, valist, nextarg)
+     int stdarg_p ATTRIBUTE_UNUSED;
+     tree valist;
+     rtx nextarg ATTRIBUTE_UNUSED;
+{
+  tree s, t, base, num;
+
+  /* The array type always decays to a pointer before we get here, so we
+     can't use ARRAY_REF.  */
+  base = build1 (INDIRECT_REF, unsigned_type_node, valist);
+  num = build1 (INDIRECT_REF, unsigned_type_node,
+               build (PLUS_EXPR, unsigned_type_node, valist,
+                      TYPE_SIZE_UNIT (TREE_TYPE (valist))));
+
+  s = make_tree (unsigned_type_node, arg_pointer_rtx);
+  t = build (MODIFY_EXPR, unsigned_type_node, base, s);
+  TREE_SIDE_EFFECTS (t) = 1;
+  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+  s = build_int_2 ((current_function_args_info.ca_nregparms
+                   + current_function_args_info.ca_nstackparms) * 4, 0);
+  t = build (MODIFY_EXPR, unsigned_type_node, num, s);
+  TREE_SIDE_EFFECTS (t) = 1;
+  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+}
+
+/* Implement `va_arg'.  */
+
+rtx
+i960_va_arg (valist, type)
+     tree valist, type;
+{
+  HOST_WIDE_INT siz, ali;
+  tree base, num, pad, next, this, t1, t2, int48;
+  rtx addr_rtx;
+
+  /* The array type always decays to a pointer before we get here, so we
+     can't use ARRAY_REF.  */
+  base = build1 (INDIRECT_REF, unsigned_type_node, valist);
+  num = build1 (INDIRECT_REF, unsigned_type_node,
+               build (PLUS_EXPR, unsigned_type_node, valist,
+                      TYPE_SIZE_UNIT (TREE_TYPE (valist))));
+
+  /* Round up sizeof(type) to a word.  */
+  siz = (int_size_in_bytes (type) + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+
+  /* Round up alignment to a word.  */
+  ali = TYPE_ALIGN (type);
+  if (ali < BITS_PER_WORD)
+    ali = BITS_PER_WORD;
+  ali /= BITS_PER_UNIT;
+
+  /* Align NUM appropriate for the argument.  */
+  pad = fold (build (PLUS_EXPR, unsigned_type_node, num, 
+                     build_int_2 (ali - 1, 0)));
+  pad = fold (build (BIT_AND_EXPR, unsigned_type_node, pad,
+                     build_int_2 (-ali, -1)));
+  pad = save_expr (pad);
+
+  /* Increment VPAD past this argument.  */
+  next = fold (build (PLUS_EXPR, unsigned_type_node, pad,
+                     build_int_2 (siz, 0)));
+  next = save_expr (next);
+
+  /* Find the offset for the current argument.  Mind peculiar overflow
+     from registers to stack.  */
+  int48 = build_int_2 (48, 0);
+  if (siz > 16)
+    t2 = integer_one_node;
+  else
+    t2 = fold (build (GT_EXPR, integer_type_node, next, int48));
+  t1 = fold (build (LE_EXPR, integer_type_node, num, int48));
+  t1 = fold (build (TRUTH_AND_EXPR, integer_type_node, t1, t2));
+  this = fold (build (COND_EXPR, unsigned_type_node, t1, int48, pad));
+
+  /* Find the address for the current argument.  */
+  t1 = fold (build (PLUS_EXPR, unsigned_type_node, base, this));
+  t1 = build1 (NOP_EXPR, ptr_type_node, t1);
+  addr_rtx = expand_expr (t1, NULL_RTX, Pmode, EXPAND_NORMAL);
+
+  /* Increment NUM.  */
+  t1 = build (MODIFY_EXPR, unsigned_type_node, num, next);
+  TREE_SIDE_EFFECTS (t1) = 1;
+  expand_expr (t1, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  
+  return addr_rtx;
+}
+
 /* Calculate the final size of the reg parm stack space for the current
    function, based on how many bytes would be allocated on the stack.  */
 
@@ -2548,7 +2721,7 @@ secondary_reload_class (class, mode, in)
 
 void
 i960_scan_opcode (p)
-     char *p;
+     const char *p;
 {
   switch (*p)
     {