OSDN Git Service

try to fix NT trampolines
[pf3gnuchains/gcc-fork.git] / gcc / config / rs6000 / rs6000.c
index 62fe8cf..20c72d2 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines used for code generation on IBM RS/6000.
-   Copyright (C) 1991, 1993 Free Software Foundation, Inc.
-   Contributed by Richard Kenner (kenner@nyu.edu)
+   Copyright (C) 1991, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+   Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
 This file is part of GNU CC.
 
@@ -16,9 +16,11 @@ 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 <stdio.h>
+#include <ctype.h>
 #include "config.h"
 #include "rtl.h"
 #include "regs.h"
@@ -35,6 +37,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "obstack.h"
 #include "tree.h"
 
+#ifndef TARGET_NO_PROTOTYPE
+#define TARGET_NO_PROTOTYPE 0
+#endif
+
 extern char *language_string;
 extern int profile_block_flag;
 
@@ -44,7 +50,13 @@ extern int profile_block_flag;
 /* Target cpu type */
 
 enum processor_type rs6000_cpu;
-char *rs6000_cpu_string;
+struct rs6000_cpu_select rs6000_select[3] =
+{
+  /* switch    name,                   tune    arch */
+  { (char *)0, "--with-cpu=",          1,      1 },
+  { (char *)0, "-mcpu=",               1,      1 },
+  { (char *)0, "-mtune=",              1,      0 },
+};
 
 /* Set to non-zero by "fix" operation to indicate that itrunc and
    uitrunc must be defined.  */
@@ -55,25 +67,145 @@ int rs6000_trunc_used;
 
 static int trunc_defined;
 
+/* Set to non-zero once AIX common-mode calls have been defined.  */
+static int common_mode_defined;
 /* Save information from a "cmpxx" operation until the branch or scc is
    emitted.  */
 
 rtx rs6000_compare_op0, rs6000_compare_op1;
 int rs6000_compare_fp_p;
+
+#ifdef USING_SVR4_H
+/* Label number of label created for -mrelocatable, to call to so we can
+   get the address of the GOT section */
+int rs6000_pic_labelno;
+#endif
+
+/* Whether a System V.4 varargs area was created.  */
+int rs6000_sysv_varargs_p;
+
+/* Whether we need to save the TOC register.  */
+int rs6000_save_toc_p;
+
+/* ABI enumeration available for subtarget to use.  */
+enum rs6000_abi rs6000_current_abi;
+
+/* Temporary memory used to convert integer -> float */
+static rtx stack_temps[NUM_MACHINE_MODES];
+
+\f
+/* Print the options used in the assembly file.  */
+
+extern char *version_string, *language_string;
+
+struct asm_option
+{
+  char *string;
+  int *variable;
+  int on_value;
+};
+
+#define MAX_LINE 79
+
+static int
+output_option (file, type, name, pos)
+     FILE *file;
+     char *type;
+     char *name;
+     int pos;
+{
+  int type_len = strlen (type);
+  int name_len = strlen (name);
+
+  if (1 + type_len + name_len + pos > MAX_LINE)
+    {
+      fprintf (file, "\n # %s%s", type, name);
+      return 3 + type_len + name_len;
+    }
+  fprintf (file, " %s%s", type, name);
+  return pos + 1 + type_len + name_len;
+}
+
+static struct { char *name; int value; } m_options[] = TARGET_SWITCHES;
+
+void
+output_options (file, f_options, f_len, W_options, W_len)
+     FILE *file;
+     struct asm_option *f_options;
+     int f_len;
+     struct asm_option *W_options;
+     int W_len;
+{
+  int j;
+  int flags = target_flags;
+  int pos = 32767;
+
+  fprintf (file, " # %s %s", language_string, version_string);
+
+  if (optimize)
+    {
+      char opt_string[20];
+      sprintf (opt_string, "%d", optimize);
+      pos = output_option (file, "-O", opt_string, pos);
+    }
+
+  if (profile_flag)
+    pos = output_option (file, "-p", "", pos);
+
+  if (profile_block_flag)
+    pos = output_option (file, "-a", "", pos);
+
+  if (inhibit_warnings)
+    pos = output_option (file, "-w", "", pos);
+
+  for (j = 0; j < f_len; j++)
+    {
+      if (*f_options[j].variable == f_options[j].on_value)
+       pos = output_option (file, "-f", f_options[j].string, pos);
+    }
+
+  for (j = 0; j < W_len; j++)
+    {
+      if (*W_options[j].variable == W_options[j].on_value)
+       pos = output_option (file, "-W", W_options[j].string, pos);
+    }
+
+  for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++)
+    {
+      if (m_options[j].name[0] != '\0'
+         && m_options[j].value > 0
+         && ((m_options[j].value & flags) == m_options[j].value))
+       {
+         pos = output_option (file, "-m", m_options[j].name, pos);
+         flags &= ~ m_options[j].value;
+       }
+    }
+
+  for (j = 0; j < sizeof (rs6000_select) / sizeof(rs6000_select[0]); j++)
+    if (rs6000_select[j].string != (char *)0)
+      pos = output_option (file, rs6000_select[j].name, rs6000_select[j].string, pos);
+
+  fputs ("\n\n", file);
+}
+
 \f
 /* Override command line options.  Mostly we process the processor
    type and sometimes adjust other TARGET_ options.  */
 
 void
-rs6000_override_options ()
+rs6000_override_options (default_cpu)
+     char *default_cpu;
 {
-  int i;
+  int i, j;
+  struct rs6000_cpu_select *ptr;
 
   /* Simplify the entries below by making a mask for any POWER
      variant and any PowerPC variant.  */
 
-#define POWER_MASKS (MASK_POWER | MASK_POWER2)
-#define POWERPC_MASKS (MASK_POWERPC | MASK_POWERPCSQR | MASK_POWERPC64)
+#define POWER_MASKS (MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING)
+#define POWERPC_MASKS (MASK_POWERPC | MASK_PPC_GPOPT \
+                      | MASK_PPC_GFXOPT | MASK_POWERPC64)
+#define POWERPC_OPT_MASKS (MASK_PPC_GPOPT | MASK_PPC_GFXOPT)
 
   static struct ptt
     {
@@ -82,87 +214,177 @@ rs6000_override_options ()
       int target_enable;       /* Target flags to enable.  */
       int target_disable;      /* Target flags to disable.  */
     } processor_target_table[]
-      = {{"all", PROCESSOR_DEFAULT, 0, POWER_MASKS | POWERPC_MASKS},
-        {"rios", PROCESSOR_RIOS1, MASK_POWER, MASK_POWER2 | POWERPC_MASKS},
-        {"rios1", PROCESSOR_RIOS1, MASK_POWER, MASK_POWER2 | POWERPC_MASKS},
-        {"rios2", PROCESSOR_RIOS2, MASK_POWER | MASK_POWER2 , POWERPC_MASKS},
+      = {{"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS,
+           POWER_MASKS | POWERPC_MASKS},
+        {"power", PROCESSOR_POWER,
+           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"power2", PROCESSOR_POWER,
+           MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
+           POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"powerpc", PROCESSOR_POWERPC,
+           MASK_POWERPC | MASK_NEW_MNEMONICS,
+           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+        {"rios", PROCESSOR_RIOS1,
+           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"rios1", PROCESSOR_RIOS1,
+           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"rsc", PROCESSOR_PPC601,
+           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"rsc1", PROCESSOR_PPC601,
+           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
+           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"rios2", PROCESSOR_RIOS2,
+           MASK_POWER | MASK_MULTIPLE | MASK_STRING | MASK_POWER2,
+           POWERPC_MASKS | MASK_NEW_MNEMONICS},
+        {"403", PROCESSOR_PPC403,
+           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+        {"505", PROCESSOR_MPCCORE,
+           MASK_POWERPC | MASK_NEW_MNEMONICS,
+           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
         {"601", PROCESSOR_PPC601,
-           MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS,
-           MASK_POWER2 | MASK_POWERPCSQR | MASK_POWERPC64},
-        {"mpc601", PROCESSOR_PPC601,
-           MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS,
-           MASK_POWER2 | MASK_POWERPCSQR | MASK_POWERPC64},
-        {"ppc601", PROCESSOR_PPC601,
-           MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS,
-           MASK_POWER2 | MASK_POWERPCSQR | MASK_POWERPC64},
+           MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS | MASK_MULTIPLE | MASK_STRING,
+           MASK_POWER2 | POWERPC_OPT_MASKS | MASK_POWERPC64},
+        {"602", PROCESSOR_PPC602,
+           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
         {"603", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_POWERPCSQR | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_POWERPC64},
-        {"mpc603", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_POWERPCSQR | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_POWERPC64},
-        {"ppc603", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_POWERPCSQR | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_POWERPC64},
+           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+        {"603e", PROCESSOR_PPC603,
+           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
         {"604", PROCESSOR_PPC604,
-           MASK_POWERPC | MASK_POWERPCSQR | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_POWERPC64},
-        {"mpc604", PROCESSOR_PPC604,
-           MASK_POWERPC | MASK_POWERPCSQR | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_POWERPC64},
-        {"ppc604", PROCESSOR_PPC604,
-           MASK_POWERPC | MASK_POWERPCSQR | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_POWERPC64},
+           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
         {"620", PROCESSOR_PPC620,
-           (MASK_POWERPC | MASK_POWERPCSQR | MASK_POWERPC64
-            | MASK_NEW_MNEMONICS),
-             POWER_MASKS},
-        {"mpc620", PROCESSOR_PPC620,
-           (MASK_POWERPC | MASK_POWERPCSQR | MASK_POWERPC64
-            | MASK_NEW_MNEMONICS),
-             POWER_MASKS},
-        {"ppc620", PROCESSOR_PPC620,
-           (MASK_POWERPC | MASK_POWERPCSQR | MASK_POWERPC64
-            | MASK_NEW_MNEMONICS),
-             POWER_MASKS}};
+           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+        {"821", PROCESSOR_MPCCORE,
+           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+        {"860", PROCESSOR_MPCCORE,
+           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
+           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64}};
 
   int ptt_size = sizeof (processor_target_table) / sizeof (struct ptt);
 
+  int multiple = TARGET_MULTIPLE;      /* save current -mmultiple/-mno-multiple status */
+  int string   = TARGET_STRING;                /* save current -mstring/-mno-string status */
+
   profile_block_flag = 0;
 
   /* Identify the processor type */
-  if (rs6000_cpu_string == 0)
-    rs6000_cpu = PROCESSOR_DEFAULT;
-  else
+  rs6000_select[0].string = default_cpu;
+  rs6000_cpu = PROCESSOR_DEFAULT;
+
+  for (i = 0; i < sizeof (rs6000_select) / sizeof (rs6000_select[0]); i++)
     {
-      for (i = 0; i < ptt_size; i++)
-       if (! strcmp (rs6000_cpu_string, processor_target_table[i].name))
-         {
-           rs6000_cpu = processor_target_table[i].processor;
-           target_flags |= processor_target_table[i].target_enable;
-           target_flags &= ~processor_target_table[i].target_disable;
-           break;
-         }
+      ptr = &rs6000_select[i];
+      if (ptr->string != (char *)0 && ptr->string[0] != '\0')
+       {
+         for (j = 0; j < ptt_size; j++)
+           if (! strcmp (ptr->string, processor_target_table[j].name))
+             {
+               if (ptr->set_tune_p)
+                 rs6000_cpu = processor_target_table[j].processor;
+
+               if (ptr->set_arch_p)
+                 {
+                   target_flags |= processor_target_table[j].target_enable;
+                   target_flags &= ~processor_target_table[j].target_disable;
+                 }
+               break;
+             }
+
+         if (i == ptt_size)
+           error ("bad value (%s) for %s switch", ptr->string, ptr->name);
+       }
+    }
+
+  /* If -mmultiple or -mno-multiple was explicitly used, don't
+     override with the processor default */
+  if (TARGET_MULTIPLE_SET)
+    target_flags = (target_flags & ~MASK_MULTIPLE) | multiple;
+
+  /* If -mstring or -mno-string was explicitly used, don't
+     override with the processor default */
+  if (TARGET_STRING_SET)
+    target_flags = (target_flags & ~MASK_STRING) | string;
+
+  /* Don't allow -mmultiple or -mstring on little endian systems, because the
+     hardware doesn't support the instructions used in little endian mode */
+  if (!BYTES_BIG_ENDIAN)
+    {
+      if (TARGET_MULTIPLE)
+       {
+         target_flags &= ~MASK_MULTIPLE;
+         if (TARGET_MULTIPLE_SET)
+           warning ("-mmultiple is not supported on little endian systems");
+       }
 
-      if (i == ptt_size)
+      if (TARGET_STRING)
        {
-         error ("bad value (%s) for -mcpu= switch", rs6000_cpu_string);
-         rs6000_cpu_string = "default";
-         rs6000_cpu = PROCESSOR_DEFAULT;
+         target_flags &= ~MASK_STRING;
+         if (TARGET_STRING_SET)
+           warning ("-mstring is not supported on little endian systems");
        }
     }
+
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+  SUBTARGET_OVERRIDE_OPTIONS;
+#endif
+}
+\f
+/* Create a CONST_DOUBLE from a string.  */
+
+struct rtx_def *
+rs6000_float_const (string, mode)
+     char *string;
+     enum machine_mode mode;
+{
+  REAL_VALUE_TYPE value = REAL_VALUE_ATOF (string, mode);
+  return immed_real_const_1 (value, mode);
+}
+
+\f
+/* Create a CONST_DOUBLE like immed_double_const, except reverse the
+   two parts of the constant if the target is little endian.  */
+
+struct rtx_def *
+rs6000_immed_double_const (i0, i1, mode)
+     HOST_WIDE_INT i0, i1;
+     enum machine_mode mode;
+{
+  if (! WORDS_BIG_ENDIAN)
+    return immed_double_const (i1, i0, mode);
+
+  return immed_double_const (i0, i1, mode);
 }
+
 \f
 /* Return non-zero if this function is known to have a null epilogue.  */
 
 int
 direct_return ()
 {
-  return (reload_completed
-         && first_reg_to_save () == 32
-         && first_fp_reg_to_save () == 64
-         && ! regs_ever_live[65]
-         && ! rs6000_pushes_stack ());
+  if (reload_completed)
+    {
+      rs6000_stack_t *info = rs6000_stack_info ();
+
+      if (info->first_gp_reg_save == 32
+         && info->first_fp_reg_save == 64
+         && !info->lr_save_p
+         && !info->cr_save_p
+         && !info->push_p)
+       return 1;
+    }
+
+  return 0;
 }
 
 /* Returns 1 always.  */
@@ -175,6 +397,23 @@ any_operand (op, mode)
   return 1;
 }
 
+/* Returns 1 if op is the count register */
+int count_register_operand(op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != REG)
+    return 0;
+
+  if (REGNO (op) == COUNT_REGISTER_REGNUM)
+    return 1;
+
+  if (REGNO (op) > FIRST_PSEUDO_REGISTER)
+    return 1;
+
+  return 0;
+}
+
 /* Return 1 if OP is a constant that can fit in a D field.  */
 
 int
@@ -285,37 +524,86 @@ reg_or_cint_operand (op, mode)
      return GET_CODE (op) == CONST_INT || gpc_reg_operand (op, mode);
 }
 
-/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a
-   register with one instruction per word.  For SFmode, this means  that
-   the low 16-bits are zero.  For DFmode, it means the low 16-bits of
-   the first word are zero and the high 16 bits of the second word
-   are zero (usually all bits in the low-order word will be zero).
-
-   We only do this if we can safely read CONST_DOUBLE_{LOW,HIGH}.  */
+/* Return 1 if the operand is a CONST_DOUBLE and it can be put into a register
+   with one instruction per word.  We only do this if we can safely read
+   CONST_DOUBLE_{LOW,HIGH}.  */
 
 int
 easy_fp_constant (op, mode)
      register rtx op;
      register enum machine_mode mode;
 {
-  rtx low, high;
-
   if (GET_CODE (op) != CONST_DOUBLE
       || GET_MODE (op) != mode
       || GET_MODE_CLASS (mode) != MODE_FLOAT)
     return 0;
 
-  high = operand_subword (op, 0, 0, mode);
-  low = operand_subword (op, 1, 0, mode);
+  /* Consider all constants with -msoft-float to be easy */
+  if (TARGET_SOFT_FLOAT)
+    return 1;
+
+  if (mode == DFmode)
+    {
+      long k[2];
+      REAL_VALUE_TYPE rv;
+
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+      REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
+
+      return (((unsigned) (k[0] + 0x8000) < 0x10000 || (k[0] & 0xffff) == 0)
+             && ((unsigned) (k[1] + 0x8000) < 0x10000 || (k[1] & 0xffff) == 0));
+    }
+  else
+    {
+      long l;
+      REAL_VALUE_TYPE rv;
+
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+      REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+
+      return ((unsigned) (l + 0x8000) < 0x10000 || (l & 0xffff) == 0);
+    }
+}
+
+/* Return 1 if the operand is in volatile memory.  Note that during the
+   RTL generation phase, memory_operand does not return TRUE for
+   volatile memory references.  So this function allows us to
+   recognize volatile references where its safe.  */
+
+int
+volatile_mem_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != MEM)
+    return 0;
+
+  if (!MEM_VOLATILE_P (op))
+    return 0;
 
-  if (high == 0 || GET_CODE (high) != CONST_INT || (INTVAL (high) & 0xffff))
+  if (mode != GET_MODE (op))
     return 0;
 
-  return (mode == SFmode
-         || (low != 0 && GET_CODE (low) == CONST_INT
-             && (INTVAL (low) & 0xffff0000) == 0));
+  if (reload_completed)
+    return memory_operand (op, mode);
+
+  if (reload_in_progress)
+    return strict_memory_address_p (mode, XEXP (op, 0));
+
+  return memory_address_p (mode, XEXP (op, 0));
 }
-      
+
+/* Return 1 if the operand is an offsettable memory address.  */
+
+int
+offsettable_addr_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  return offsettable_address_p (reload_completed | reload_in_progress,
+                               mode, op);
+}
+
 /* Return 1 if the operand is either a floating-point register, a pseudo
    register, or memory.  */
 
@@ -325,6 +613,7 @@ fp_reg_or_mem_operand (op, mode)
      enum machine_mode mode;
 {
   return (memory_operand (op, mode)
+         || volatile_mem_operand (op, mode)
          || (register_operand (op, mode)
              && (GET_CODE (op) != REG
                  || REGNO (op) >= FIRST_PSEUDO_REGISTER
@@ -459,7 +748,29 @@ reg_or_mem_operand (op, mode)
      register rtx op;
      register enum machine_mode mode;
 {
-  return gpc_reg_operand (op, mode) || memory_operand (op, mode);
+  return (gpc_reg_operand (op, mode)
+         || memory_operand (op, mode)
+         || volatile_mem_operand (op, mode));
+}
+
+/* Return 1 if the operand is a general register or memory operand without
+   pre-inc or pre_dec which produces invalid form of PowerPC lwa
+   instruction.  */
+
+int
+lwa_operand (op, mode)
+     register rtx op;
+     register enum machine_mode mode;
+{
+  rtx inner = op;
+
+  if (reload_completed && GET_CODE (inner) == SUBREG)
+    inner = SUBREG_REG (inner);
+    
+  return gpc_reg_operand (inner, mode)
+    || (memory_operand (inner, mode)
+       && GET_CODE (XEXP (inner, 0)) != PRE_INC
+       && GET_CODE (XEXP (inner, 0)) != PRE_DEC);
 }
 
 /* Return 1 if the operand, used inside a MEM, is a valid first argument
@@ -522,144 +833,877 @@ input_operand (op, mode)
   if (register_operand (op, mode))
     return 1;
 
-  /* For HImode and QImode, any constant is valid. */
-  if ((mode == HImode || mode == QImode)
-      && GET_CODE (op) == CONST_INT)
+  /* For integer modes, any constant is ok.  */
+  if (GET_CODE (op) == CONST_INT)
+    return 1;
+
+  /* A SYMBOL_REF referring to the TOC is valid.  */
+  if (LEGITIMATE_CONSTANT_POOL_ADDRESS_P (op))
+    return 1;
+
+  /* Windows NT allows SYMBOL_REFs and LABEL_REFs against the TOC
+     directly in the instruction stream */
+  if (DEFAULT_ABI == ABI_NT
+      && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF))
+    return 1;
+
+  /* V.4 allows SYMBOL_REFs and CONSTs that are in the small data region
+     to be valid.  */
+  if (DEFAULT_ABI == ABI_V4
+      && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST)
+      && small_data_operand (op, Pmode))
     return 1;
 
-  /* Otherwise, we will be doing this SET with an add, so anything valid
-     for an add will be valid.  */
-  return add_operand (op, mode);
+  return 0;
 }
-\f
-/* Return 1 if OP is a load multiple operation.  It is known to be a
-   PARALLEL and the first section will be tested.  */
+
+/* Return 1 for an operand in small memory on V.4/eabi */
 
 int
-load_multiple_operation (op, mode)
+small_data_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  int count = XVECLEN (op, 0);
-  int dest_regno;
-  rtx src_addr;
-  int i;
+  rtx sym_ref, const_part;
 
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
+#ifdef TARGET_SDATA
+  if (!TARGET_SDATA)
     return 0;
+#endif
 
-  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
-  src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+  if (DEFAULT_ABI != ABI_V4)
+    return 0;
 
-  for (i = 1; i < count; i++)
-    {
-      rtx elt = XVECEXP (op, 0, i);
+  if (GET_CODE (op) == SYMBOL_REF)
+    sym_ref = op;
 
-      if (GET_CODE (elt) != SET
-         || GET_CODE (SET_DEST (elt)) != REG
-         || GET_MODE (SET_DEST (elt)) != SImode
-         || REGNO (SET_DEST (elt)) != dest_regno + i
-         || GET_CODE (SET_SRC (elt)) != MEM
-         || GET_MODE (SET_SRC (elt)) != SImode
-         || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
-         || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
-         || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
-         || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
-       return 0;
-    }
+  else if (GET_CODE (op) != CONST
+          || GET_CODE (XEXP (op, 0)) != PLUS
+          || GET_CODE (XEXP (XEXP (op, 0), 0)) != SYMBOL_REF
+          || GET_CODE (XEXP (XEXP (op, 0), 1)) != CONST_INT)
+    return 0;
+
+  else
+    sym_ref = XEXP (XEXP (op, 0), 0);
+
+  if (*XSTR (sym_ref, 0) != '@')
+    return 0;
 
   return 1;
 }
 
-/* Similar, but tests for store multiple.  Here, the second vector element
-   is a CLOBBER.  It will be tested later.  */
+\f
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.
 
-int
-store_multiple_operation (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  int count = XVECLEN (op, 0) - 1;
-  int src_regno;
-  rtx dest_addr;
-  int i;
+   For incoming args we set the number of arguments in the prototype large
+   so we never return an EXPR_LIST.  */
 
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
-    return 0;
+void
+init_cumulative_args (cum, fntype, libname, incoming)
+     CUMULATIVE_ARGS *cum;
+     tree fntype;
+     rtx libname;
+     int incoming;
+{
+  static CUMULATIVE_ARGS zero_cumulative;
+  enum rs6000_abi abi = DEFAULT_ABI;
 
-  src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
-  dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+  *cum = zero_cumulative;
+  cum->words = 0;
+  cum->fregno = FP_ARG_MIN_REG;
+  cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+  cum->call_cookie = CALL_NORMAL;
 
-  for (i = 1; i < count; i++)
+  if (incoming)
     {
-      rtx elt = XVECEXP (op, 0, i + 1);
-
-      if (GET_CODE (elt) != SET
-         || GET_CODE (SET_SRC (elt)) != REG
-         || GET_MODE (SET_SRC (elt)) != SImode
-         || REGNO (SET_SRC (elt)) != src_regno + i
-         || GET_CODE (SET_DEST (elt)) != MEM
-         || GET_MODE (SET_DEST (elt)) != SImode
-         || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
-         || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
-         || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
-         || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
-       return 0;
+      cum->nargs_prototype = 1000;             /* don't return an EXPR_LIST */
+      if (abi == ABI_V4)
+       cum->varargs_offset = RS6000_VARARGS_OFFSET;
     }
 
-  return 1;
-}
-\f
-/* Return 1 if OP is a comparison operation that is valid for a branch insn.
-   We only check the opcode against the mode of the CC value here.  */
+  else if (cum->prototype)
+    cum->nargs_prototype = (list_length (TYPE_ARG_TYPES (fntype)) - 1
+                           + (TYPE_MODE (TREE_TYPE (fntype)) == BLKmode
+                              || RETURN_IN_MEMORY (TREE_TYPE (fntype))));
 
-int
-branch_comparison_operator (op, mode)
-     register rtx op;
-     enum machine_mode mode;
-{
-  enum rtx_code code = GET_CODE (op);
-  enum machine_mode cc_mode;
+  else
+    cum->nargs_prototype = 0;
 
-  if (GET_RTX_CLASS (code) != '<')
-    return 0;
+  cum->orig_nargs = cum->nargs_prototype;
 
-  cc_mode = GET_MODE (XEXP (op, 0));
-  if (GET_MODE_CLASS (cc_mode) != MODE_CC)
-    return 0;
+  /* Check for DLL import functions */
+  if (abi == ABI_NT
+      && fntype
+      && lookup_attribute ("dllimport", TYPE_ATTRIBUTES (fntype)))
+    cum->call_cookie = CALL_NT_DLLIMPORT;
 
-  if ((code == GT || code == LT || code == GE || code == LE)
-      && cc_mode == CCUNSmode)
-    return 0;
+  if (TARGET_DEBUG_ARG)
+    {
+      fprintf (stderr, "\ninit_cumulative_args:");
+      if (fntype)
+       {
+         tree ret_type = TREE_TYPE (fntype);
+         fprintf (stderr, " ret code = %s,",
+                  tree_code_name[ (int)TREE_CODE (ret_type) ]);
+       }
 
-  if ((code == GTU || code == LTU || code == GEU || code == LEU)
-      && (cc_mode != CCUNSmode))
-    return 0;
+      if (abi == ABI_V4 && incoming)
+       fprintf (stderr, " varargs = %d, ", cum->varargs_offset);
 
-  return 1;
+      if (cum->call_cookie == CALL_NT_DLLIMPORT)
+       fprintf (stderr, " dllimport,");
+
+      fprintf (stderr, " proto = %d, nargs = %d\n",
+              cum->prototype, cum->nargs_prototype);
+    }
 }
+\f
+/* If defined, a C expression that gives the alignment boundary, in bits,
+   of an argument with the specified mode and type.  If it is not defined, 
+   PARM_BOUNDARY is used for all arguments.
+   
+   Windows NT wants anything >= 8 bytes to be double word aligned.
 
-/* Return 1 if OP is a comparison operation that is valid for an scc insn.
-   We check the opcode against the mode of the CC value and disallow EQ or
-   NE comparisons for integers.  */
+   V.4 wants long longs to be double word aligned.  */
 
 int
-scc_comparison_operator (op, mode)
-     register rtx op;
+function_arg_boundary (mode, type)
      enum machine_mode mode;
+     tree type;
 {
-  enum rtx_code code = GET_CODE (op);
-  enum machine_mode cc_mode;
+  if (DEFAULT_ABI == ABI_V4 && mode == DImode)
+    return 64;
 
-  if (GET_MODE (op) != mode && mode != VOIDmode)
+  if (DEFAULT_ABI != ABI_NT || TARGET_64BIT)
+    return PARM_BOUNDARY;
+
+  if (mode != BLKmode)
+    return (GET_MODE_SIZE (mode)) >= 8 ? 64 : 32;
+
+  return (int_size_in_bytes (type) >= 8) ? 64 : 32;
+}
+\f
+/* Update the data in CUM to advance over an argument
+   of mode MODE and data type TYPE.
+   (TYPE is null for libcalls where that information may not be available.)  */
+
+void
+function_arg_advance (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  int align = ((cum->words & 1) != 0 && function_arg_boundary (mode, type) == 64) ? 1 : 0;
+  cum->words += align;
+  cum->nargs_prototype--;
+
+  if (DEFAULT_ABI == ABI_V4)
+    {
+      /* Long longs must not be split between registers and stack */
+      if ((GET_MODE_CLASS (mode) != MODE_FLOAT || TARGET_SOFT_FLOAT)
+         && type && !AGGREGATE_TYPE_P (type)
+         && cum->words < GP_ARG_NUM_REG
+         && cum->words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG)
+       {
+         cum->words = GP_ARG_NUM_REG;
+       }
+
+      /* Aggregates get passed as pointers */
+      if (type && AGGREGATE_TYPE_P (type))
+       cum->words++;
+
+      /* Floats go in registers, & don't occupy space in the GP registers
+        like they do for AIX unless software floating point.  */
+      else if (GET_MODE_CLASS (mode) == MODE_FLOAT
+              && TARGET_HARD_FLOAT
+              && cum->fregno <= FP_ARG_V4_MAX_REG)
+       cum->fregno++;
+
+      else
+       cum->words += RS6000_ARG_SIZE (mode, type, 1);
+    }
+  else
+    if (named)
+      {
+       cum->words += RS6000_ARG_SIZE (mode, type, named);
+       if (GET_MODE_CLASS (mode) == MODE_FLOAT && TARGET_HARD_FLOAT)
+         cum->fregno++;
+      }
+
+  if (TARGET_DEBUG_ARG)
+    fprintf (stderr,
+            "function_adv: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n",
+            cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align);
+}
+\f
+/* Determine where to put an argument to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).
+
+   On RS/6000 the first eight words of non-FP are normally in registers
+   and the rest are pushed.  Under AIX, the first 13 FP args are in registers.
+   Under V.4, the first 8 FP args are in registers.
+
+   If this is floating-point and no prototype is specified, we use
+   both an FP and integer register (or possibly FP reg and stack).  Library
+   functions (when TYPE is zero) always have the proper types for args,
+   so we can pass the FP value just in one register.  emit_library_function
+   doesn't support EXPR_LIST anyway.  */
+
+struct rtx_def *
+function_arg (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  int align = ((cum->words & 1) != 0 && function_arg_boundary (mode, type) == 64) ? 1 : 0;
+  int align_words = cum->words + align;
+
+  if (TARGET_DEBUG_ARG)
+    fprintf (stderr,
+            "function_arg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, named = %d, align = %d\n",
+            cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), named, align);
+
+  /* Return a marker to indicate whether CR1 needs to set or clear the bit that V.4
+     uses to say fp args were passed in registers.  Assume that we don't need the
+     marker for software floating point, or compiler generated library calls.  */
+  if (mode == VOIDmode)
+    {
+      enum rs6000_abi abi = DEFAULT_ABI;
+
+      if (abi == ABI_V4
+         && TARGET_HARD_FLOAT
+         && cum->nargs_prototype < 0
+         && type && (cum->prototype || TARGET_NO_PROTOTYPE))
+       {
+         if (cum->call_cookie != CALL_NORMAL)
+           abort ();
+
+         return GEN_INT ((cum->fregno == FP_ARG_MIN_REG)
+                         ? CALL_V4_SET_FP_ARGS
+                         : CALL_V4_CLEAR_FP_ARGS);
+       }
+
+      return GEN_INT (cum->call_cookie);
+    }
+
+  if (!named)
+    {
+      if (DEFAULT_ABI != ABI_V4)
+       return NULL_RTX;
+    }
+
+  if (type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+    return NULL_RTX;
+
+  if (USE_FP_FOR_ARG_P (*cum, mode, type))
+    {
+      if ((cum->nargs_prototype > 0)
+         || (DEFAULT_ABI == ABI_V4)    /* V.4 never passes FP values in GP registers */
+         || !type)
+       return gen_rtx (REG, mode, cum->fregno);
+
+      return gen_rtx (EXPR_LIST, VOIDmode,
+                     ((align_words < GP_ARG_NUM_REG)
+                      ? gen_rtx (REG, mode, GP_ARG_MIN_REG + align_words)
+                      : NULL_RTX),
+                     gen_rtx (REG, mode, cum->fregno));
+    }
+
+  /* Long longs won't be split between register and stack */
+  else if (DEFAULT_ABI == ABI_V4 &&
+          align_words + RS6000_ARG_SIZE (mode, type, named) > GP_ARG_NUM_REG)
+    {
+      return NULL_RTX;
+    }
+
+  else if (align_words < GP_ARG_NUM_REG)
+    return gen_rtx (REG, mode, GP_ARG_MIN_REG + align_words);
+
+  return NULL_RTX;
+}
+\f
+/* For an arg passed partly in registers and partly in memory,
+   this is the number of registers used.
+   For args passed entirely in registers or entirely in memory, zero.  */
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  if (! named)
+    return 0;
+
+  if (DEFAULT_ABI == ABI_V4)
+    return 0;
+
+  if (USE_FP_FOR_ARG_P (*cum, mode, type))
+    {
+      if (cum->nargs_prototype >= 0)
+       return 0;
+    }
+
+  if (cum->words < GP_ARG_NUM_REG
+      && GP_ARG_NUM_REG < (cum->words + RS6000_ARG_SIZE (mode, type, named)))
+    {
+      int ret = GP_ARG_NUM_REG - cum->words;
+      if (ret && TARGET_DEBUG_ARG)
+       fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
+
+      return ret;
+    }
+
+  return 0;
+}
+\f
+/* A C expression that indicates when an argument must be passed by
+   reference.  If nonzero for an argument, a copy of that argument is
+   made in memory and a pointer to the argument is passed instead of
+   the argument itself.  The pointer is passed in whatever way is
+   appropriate for passing a pointer to that type.
+
+   Under V.4, structures and unions are passed by reference.  */
+
+int
+function_arg_pass_by_reference (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  if (DEFAULT_ABI == ABI_V4 && type && AGGREGATE_TYPE_P (type))
+    {
+      if (TARGET_DEBUG_ARG)
+       fprintf (stderr, "function_arg_pass_by_reference: aggregate\n");
+
+      return 1;
+    }
+
+  return 0;
+}
+
+\f
+/* Perform any needed actions needed for a function that is receiving a
+   variable number of arguments. 
+
+   CUM is as above.
+
+   MODE and TYPE are the mode and type of the current parameter.
+
+   PRETEND_SIZE is a variable that should be set to the amount of stack
+   that must be pushed by the prolog to pretend that our caller pushed
+   it.
+
+   Normally, this macro will push all remaining incoming registers on the
+   stack and set PRETEND_SIZE to the length of the registers pushed.  */
+
+void
+setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int *pretend_size;
+     int no_rtl;
+
+{
+  rtx save_area = virtual_incoming_args_rtx;
+  int reg_size = (TARGET_64BIT) ? 8 : 4;
+
+  if (TARGET_DEBUG_ARG)
+    fprintf (stderr,
+            "setup_vararg: words = %2d, fregno = %2d, nargs = %4d, proto = %d, mode = %4s, no_rtl= %d\n",
+            cum->words, cum->fregno, cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode), no_rtl);
+
+  if (DEFAULT_ABI == ABI_V4 && !no_rtl)
+    {
+      rs6000_sysv_varargs_p = 1;
+      save_area = plus_constant (frame_pointer_rtx, RS6000_VARARGS_OFFSET);
+    }
+
+  if (cum->words < 8)
+    {
+      int first_reg_offset = cum->words;
+
+      if (MUST_PASS_IN_STACK (mode, type))
+       first_reg_offset += RS6000_ARG_SIZE (TYPE_MODE (type), type, 1);
+
+      if (first_reg_offset > GP_ARG_NUM_REG)
+       first_reg_offset = GP_ARG_NUM_REG;
+
+      if (!no_rtl && first_reg_offset != GP_ARG_NUM_REG)
+       move_block_from_reg
+         (GP_ARG_MIN_REG + first_reg_offset,
+          gen_rtx (MEM, BLKmode,
+                   plus_constant (save_area, first_reg_offset * reg_size)),
+          GP_ARG_NUM_REG - first_reg_offset,
+          (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD);
+
+      *pretend_size = (GP_ARG_NUM_REG - first_reg_offset) * UNITS_PER_WORD;
+    }
+
+  /* Save FP registers if needed.  */
+  if (DEFAULT_ABI == ABI_V4 && TARGET_HARD_FLOAT && !no_rtl)
+    {
+      int fregno     = cum->fregno;
+      int num_fp_reg = FP_ARG_V4_MAX_REG + 1 - fregno;
+
+      if (num_fp_reg >= 0)
+       {
+         rtx cr1 = gen_rtx (REG, CCmode, 69);
+         rtx lab = gen_label_rtx ();
+         int off = (GP_ARG_NUM_REG * reg_size) + ((fregno - FP_ARG_MIN_REG) * 8);
+
+         emit_jump_insn (gen_rtx (SET, VOIDmode,
+                                  pc_rtx,
+                                  gen_rtx (IF_THEN_ELSE, VOIDmode,
+                                           gen_rtx (NE, VOIDmode, cr1, const0_rtx),
+                                           gen_rtx (LABEL_REF, VOIDmode, lab),
+                                           pc_rtx)));
+
+         while ( num_fp_reg-- >= 0)
+           {
+             emit_move_insn (gen_rtx (MEM, DFmode, plus_constant (save_area, off)),
+                             gen_rtx (REG, DFmode, fregno++));
+             off += 8;
+           }
+
+         emit_label (lab);
+       }
+    }
+}
+\f
+/* If defined, is a C expression that produces the machine-specific
+   code for a call to `__builtin_saveregs'.  This code will be moved
+   to the very beginning of the function, before any parameter access
+   are made.  The return value of this function should be an RTX that
+   contains the value to use as the return of `__builtin_saveregs'.
+
+   The argument ARGS is a `tree_list' containing the arguments that
+   were passed to `__builtin_saveregs'.
+
+   If this macro is not defined, the compiler will output an ordinary
+   call to the library function `__builtin_saveregs'.
+   
+   On the Power/PowerPC return the address of the area on the stack
+   used to hold arguments.  Under AIX, this includes the 8 word register
+   save area.  Under V.4 this does not.  */
+
+struct rtx_def *
+expand_builtin_saveregs (args)
+     tree args;
+{
+  return virtual_incoming_args_rtx;
+}
+
+\f
+/* Allocate a stack temp.  Only allocate one stack temp per type for a
+   function.  */
+
+struct rtx_def *
+rs6000_stack_temp (mode, size)
+     enum machine_mode mode;
+     int size;
+{
+  rtx temp = stack_temps[ (int)mode ];
+  rtx addr;
+
+  if (temp == NULL_RTX)
+    {
+      temp = assign_stack_local (mode, size, 0);
+      addr = XEXP (temp, 0);
+
+      if ((size > 4 && !offsettable_address_p (0, mode, addr))
+         || (size <= 4 && !memory_address_p (mode, addr)))
+       {
+         XEXP (temp, 0) = copy_addr_to_reg (addr);
+       }
+
+      stack_temps[ (int)mode ] = temp;
+    }
+
+  return temp;
+}
+
+\f
+/* Generate a memory reference for expand_block_move, copying volatile,
+   and other bits from an original memory reference.  */
+
+static rtx
+expand_block_move_mem (mode, addr, orig_mem)
+     enum machine_mode mode;
+     rtx addr;
+     rtx orig_mem;
+{
+  rtx mem = gen_rtx (MEM, mode, addr);
+
+  RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (orig_mem);
+  MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (orig_mem);
+  MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (orig_mem);
+#ifdef MEM_UNALIGNED_P
+  MEM_UNALIGNED_P (mem) = MEM_UNALIGNED_P (orig_mem);
+#endif
+  return mem;
+}
+
+/* Expand a block move operation, and return 1 if successful.  Return 0
+   if we should let the compiler generate normal code.
+
+   operands[0] is the destination
+   operands[1] is the source
+   operands[2] is the length
+   operands[3] is the alignment */
+
+#define MAX_MOVE_REG 4
+
+int
+expand_block_move (operands)
+     rtx operands[];
+{
+  rtx orig_dest = operands[0];
+  rtx orig_src = operands[1];
+  rtx bytes_rtx        = operands[2];
+  rtx align_rtx = operands[3];
+  int constp   = (GET_CODE (bytes_rtx) == CONST_INT);
+  int align    = XINT (align_rtx, 0);
+  int bytes;
+  int offset;
+  int num_reg;
+  int i;
+  rtx src_reg;
+  rtx dest_reg;
+  rtx src_addr;
+  rtx dest_addr;
+  rtx tmp_reg;
+  rtx stores[MAX_MOVE_REG];
+  int move_bytes;
+
+  /* If this is not a fixed size move, just call memcpy */
+  if (!constp)
+    return 0;
+
+  /* Anything to move? */
+  bytes = INTVAL (bytes_rtx);
+  if (bytes <= 0)
+    return 1;
+
+  /* Don't support real large moves.  If string instructions are not used,
+     then don't generate more than 8 loads.  */
+  if (TARGET_STRING)
+    {
+      if (bytes > 4*8)
+       return 0;
+    }
+  else if (!STRICT_ALIGNMENT)
+    {
+      if (bytes > 4*8)
+       return 0;
+    }
+  else if (bytes > 8*align)
+    return 0;
+
+  /* Move the address into scratch registers.  */
+  dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
+  src_reg  = copy_addr_to_reg (XEXP (orig_src,  0));
+
+  if (TARGET_STRING)   /* string instructions are available */
+    {
+      for ( ; bytes > 0; bytes -= move_bytes)
+       {
+         if (bytes > 24                /* move up to 32 bytes at a time */
+             && !fixed_regs[5]
+             && !fixed_regs[6]
+             && !fixed_regs[7]
+             && !fixed_regs[8]
+             && !fixed_regs[9]
+             && !fixed_regs[10]
+             && !fixed_regs[11]
+             && !fixed_regs[12])
+           {
+             move_bytes = (bytes > 32) ? 32 : bytes;
+             emit_insn (gen_movstrsi_8reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+                                           expand_block_move_mem (BLKmode, src_reg, orig_src),
+                                           GEN_INT ((move_bytes == 32) ? 0 : move_bytes),
+                                           align_rtx));
+           }
+         else if (bytes > 16   /* move up to 24 bytes at a time */
+                  && !fixed_regs[7]
+                  && !fixed_regs[8]
+                  && !fixed_regs[9]
+                  && !fixed_regs[10]
+                  && !fixed_regs[11]
+                  && !fixed_regs[12])
+           {
+             move_bytes = (bytes > 24) ? 24 : bytes;
+             emit_insn (gen_movstrsi_6reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+                                           expand_block_move_mem (BLKmode, src_reg, orig_src),
+                                           GEN_INT (move_bytes),
+                                           align_rtx));
+           }
+         else if (bytes > 8    /* move up to 16 bytes at a time */
+                  && !fixed_regs[9]
+                  && !fixed_regs[10]
+                  && !fixed_regs[11]
+                  && !fixed_regs[12])
+           {
+             move_bytes = (bytes > 16) ? 16 : bytes;
+             emit_insn (gen_movstrsi_4reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+                                           expand_block_move_mem (BLKmode, src_reg, orig_src),
+                                           GEN_INT (move_bytes),
+                                           align_rtx));
+           }
+         else if (bytes > 4 && !TARGET_64BIT)
+           {                   /* move up to 8 bytes at a time */
+             move_bytes = (bytes > 8) ? 8 : bytes;
+             emit_insn (gen_movstrsi_2reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+                                           expand_block_move_mem (BLKmode, src_reg, orig_src),
+                                           GEN_INT (move_bytes),
+                                           align_rtx));
+           }
+         else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT))
+           {                   /* move 4 bytes */
+             move_bytes = 4;
+             tmp_reg = gen_reg_rtx (SImode);
+             emit_move_insn (tmp_reg, expand_block_move_mem (SImode, src_reg, orig_src));
+             emit_move_insn (expand_block_move_mem (SImode, dest_reg, orig_dest), tmp_reg);
+           }
+         else if (bytes == 2 && (align >= 2 || !STRICT_ALIGNMENT))
+           {                   /* move 2 bytes */
+             move_bytes = 2;
+             tmp_reg = gen_reg_rtx (HImode);
+             emit_move_insn (tmp_reg, expand_block_move_mem (HImode, src_reg, orig_src));
+             emit_move_insn (expand_block_move_mem (HImode, dest_reg, orig_dest), tmp_reg);
+           }
+         else if (bytes == 1)  /* move 1 byte */
+           {
+             move_bytes = 1;
+             tmp_reg = gen_reg_rtx (QImode);
+             emit_move_insn (tmp_reg, expand_block_move_mem (QImode, src_reg, orig_src));
+             emit_move_insn (expand_block_move_mem (QImode, dest_reg, orig_dest), tmp_reg);
+           }
+         else
+           {                   /* move up to 4 bytes at a time */
+             move_bytes = (bytes > 4) ? 4 : bytes;
+             emit_insn (gen_movstrsi_1reg (expand_block_move_mem (BLKmode, dest_reg, orig_dest),
+                                           expand_block_move_mem (BLKmode, src_reg, orig_src),
+                                           GEN_INT (move_bytes),
+                                           align_rtx));
+           }
+
+         if (bytes > move_bytes)
+           {
+             emit_insn (gen_addsi3 (src_reg, src_reg, GEN_INT (move_bytes)));
+             emit_insn (gen_addsi3 (dest_reg, dest_reg, GEN_INT (move_bytes)));
+           }
+       }
+    }
+
+  else                 /* string instructions not available */
+    {
+      num_reg = offset = 0;
+      for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes))
+       {
+         /* Calculate the correct offset for src/dest */
+         if (offset == 0)
+           {
+             src_addr  = src_reg;
+             dest_addr = dest_reg;
+           }
+         else
+           {
+             src_addr  = gen_rtx (PLUS, Pmode, src_reg,  GEN_INT (offset));
+             dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
+           }
+
+         /* Generate the appropriate load and store, saving the stores for later */
+         if (bytes >= 8 && TARGET_64BIT && (align >= 8 || !STRICT_ALIGNMENT))
+           {
+             move_bytes = 8;
+             tmp_reg = gen_reg_rtx (DImode);
+             emit_insn (gen_movdi (tmp_reg, expand_block_move_mem (DImode, src_addr, orig_src)));
+             stores[ num_reg++ ] = gen_movdi (expand_block_move_mem (DImode, dest_addr, orig_dest), tmp_reg);
+           }
+         else if (bytes >= 4 && (align >= 4 || !STRICT_ALIGNMENT))
+           {
+             move_bytes = 4;
+             tmp_reg = gen_reg_rtx (SImode);
+             emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (SImode, src_addr, orig_src)));
+             stores[ num_reg++ ] = gen_movsi (expand_block_move_mem (SImode, dest_addr, orig_dest), tmp_reg);
+           }
+         else if (bytes >= 2 && (align >= 2 || !STRICT_ALIGNMENT))
+           {
+             move_bytes = 2;
+             tmp_reg = gen_reg_rtx (HImode);
+             emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (HImode, src_addr, orig_src)));
+             stores[ num_reg++ ] = gen_movhi (expand_block_move_mem (HImode, dest_addr, orig_dest), tmp_reg);
+           }
+         else
+           {
+             move_bytes = 1;
+             tmp_reg = gen_reg_rtx (QImode);
+             emit_insn (gen_movsi (tmp_reg, expand_block_move_mem (QImode, src_addr, orig_src)));
+             stores[ num_reg++ ] = gen_movqi (expand_block_move_mem (QImode, dest_addr, orig_dest), tmp_reg);
+           }
+
+         if (num_reg >= MAX_MOVE_REG)
+           {
+             for (i = 0; i < num_reg; i++)
+               emit_insn (stores[i]);
+             num_reg = 0;
+           }
+       }
+
+      for (i = 0; i < num_reg; i++)
+       emit_insn (stores[i]);
+    }
+
+  return 1;
+}
+
+\f
+/* Return 1 if OP is a load multiple operation.  It is known to be a
+   PARALLEL and the first section will be tested.  */
+
+int
+load_multiple_operation (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  int count = XVECLEN (op, 0);
+  int dest_regno;
+  rtx src_addr;
+  int i;
+
+  /* Perform a quick check so we don't blow up below.  */
+  if (count <= 1
+      || GET_CODE (XVECEXP (op, 0, 0)) != SET
+      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
+      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
+    return 0;
+
+  dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
+  src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+
+  for (i = 1; i < count; i++)
+    {
+      rtx elt = XVECEXP (op, 0, i);
+
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_DEST (elt)) != REG
+         || GET_MODE (SET_DEST (elt)) != SImode
+         || REGNO (SET_DEST (elt)) != dest_regno + i
+         || GET_CODE (SET_SRC (elt)) != MEM
+         || GET_MODE (SET_SRC (elt)) != SImode
+         || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
+         || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+         || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
+         || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != i * 4)
+       return 0;
+    }
+
+  return 1;
+}
+
+/* Similar, but tests for store multiple.  Here, the second vector element
+   is a CLOBBER.  It will be tested later.  */
+
+int
+store_multiple_operation (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  int count = XVECLEN (op, 0) - 1;
+  int src_regno;
+  rtx dest_addr;
+  int i;
+
+  /* Perform a quick check so we don't blow up below.  */
+  if (count <= 1
+      || GET_CODE (XVECEXP (op, 0, 0)) != SET
+      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
+      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
+    return 0;
+
+  src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
+  dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+
+  for (i = 1; i < count; i++)
+    {
+      rtx elt = XVECEXP (op, 0, i + 1);
+
+      if (GET_CODE (elt) != SET
+         || GET_CODE (SET_SRC (elt)) != REG
+         || GET_MODE (SET_SRC (elt)) != SImode
+         || REGNO (SET_SRC (elt)) != src_regno + i
+         || GET_CODE (SET_DEST (elt)) != MEM
+         || GET_MODE (SET_DEST (elt)) != SImode
+         || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
+         || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
+         || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
+         || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != i * 4)
+       return 0;
+    }
+
+  return 1;
+}
+\f
+/* Return 1 if OP is a comparison operation that is valid for a branch insn.
+   We only check the opcode against the mode of the CC value here.  */
+
+int
+branch_comparison_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  enum rtx_code code = GET_CODE (op);
+  enum machine_mode cc_mode;
+
+  if (GET_RTX_CLASS (code) != '<')
+    return 0;
+
+  cc_mode = GET_MODE (XEXP (op, 0));
+  if (GET_MODE_CLASS (cc_mode) != MODE_CC)
+    return 0;
+
+  if ((code == GT || code == LT || code == GE || code == LE)
+      && cc_mode == CCUNSmode)
+    return 0;
+
+  if ((code == GTU || code == LTU || code == GEU || code == LEU)
+      && (cc_mode != CCUNSmode))
+    return 0;
+
+  return 1;
+}
+
+/* Return 1 if OP is a comparison operation that is valid for an scc insn.
+   We check the opcode against the mode of the CC value and disallow EQ or
+   NE comparisons for integers.  */
+
+int
+scc_comparison_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  enum rtx_code code = GET_CODE (op);
+  enum machine_mode cc_mode;
+
+  if (GET_MODE (op) != mode && mode != VOIDmode)
     return 0;
 
   if (GET_RTX_CLASS (code) != '<')
@@ -713,6 +1757,78 @@ includes_rshift_p (shiftop, andop)
 
   return (INTVAL (andop) & ~ shift_mask) == 0;
 }
+
+/* Return 1 if REGNO (reg1) == REGNO (reg2) - 1 making them candidates
+   for lfq and stfq insns.
+
+   Note reg1 and reg2 *must* be hard registers.  To be sure we will
+   abort if we are passed pseudo registers.  */
+
+int
+registers_ok_for_quad_peep (reg1, reg2)
+     rtx reg1, reg2;
+{
+  /* We might have been passed a SUBREG.  */
+  if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG) 
+    return 0;
+
+  return (REGNO (reg1) == REGNO (reg2) - 1);
+}
+
+/* Return 1 if addr1 and addr2 are suitable for lfq or stfq insn.  addr1 and
+   addr2 must be in consecutive memory locations (addr2 == addr1 + 8).  */
+
+int
+addrs_ok_for_quad_peep (addr1, addr2)
+     register rtx addr1;
+     register rtx addr2;
+{
+  int reg1;
+  int offset1;
+
+  /* Extract an offset (if used) from the first addr.  */
+  if (GET_CODE (addr1) == PLUS)
+    {
+      /* If not a REG, return zero.  */
+      if (GET_CODE (XEXP (addr1, 0)) != REG)
+       return 0;
+      else
+       {
+          reg1 = REGNO (XEXP (addr1, 0));
+         /* The offset must be constant!  */
+         if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
+            return 0;
+          offset1 = INTVAL (XEXP (addr1, 1));
+       }
+    }
+  else if (GET_CODE (addr1) != REG)
+    return 0;
+  else
+    {
+      reg1 = REGNO (addr1);
+      /* This was a simple (mem (reg)) expression.  Offset is 0.  */
+      offset1 = 0;
+    }
+
+  /* Make sure the second address is a (mem (plus (reg) (const_int).  */
+  if (GET_CODE (addr2) != PLUS)
+    return 0;
+
+  if (GET_CODE (XEXP (addr2, 0)) != REG
+      || GET_CODE (XEXP (addr2, 1)) != CONST_INT)
+    return 0;
+
+  if (reg1 != REGNO (XEXP (addr2, 0)))
+    return 0;
+
+  /* The offset for the second addr must be 8 more than the first addr.  */
+  if (INTVAL (XEXP (addr2, 1)) != offset1 + 8)
+    return 0;
+
+  /* All the tests passed.  addr1 and addr2 are valid for lfq or stfq
+     instructions.  */
+  return 1;
+}
 \f
 /* Return the register class of a scratch register needed to copy IN into
    or out of a register in CLASS in MODE.  If it can be done directly,
@@ -827,9 +1943,14 @@ print_operand (file, x, code)
   switch (code)
     {
     case '.':
-      /* Write out the bit number for "cror" after a call.   This differs
-        between AIX 3.2 and earlier versions.  */
-      fprintf (file, "%d", RS6000_CROR_BIT_NUMBER);
+      /* Write out an instruction after the call which may be replaced
+        with glue code by the loader.  This depends on the AIX version.  */
+      asm_fprintf (file, RS6000_CALL_GLUE);
+      return;
+
+    case '*':
+      /* Write the register number of the TOC register.  */
+      fputs (TARGET_MINIMAL_TOC ? reg_names[30] : reg_names[2], file);
       return;
 
     case 'A':
@@ -893,7 +2014,7 @@ print_operand (file, x, code)
        output_operand_lossage ("invalid %%E value");
 
       fprintf(file, "%d", 4 * (REGNO (x) - 68) + 3);
-      break;
+      return;
 
     case 'f':
       /* X is a CR register.  Print the shift count needed to move it
@@ -934,11 +2055,12 @@ print_operand (file, x, code)
       return;
 
     case 'H':
-      /* X must be a constant.  Output the low order 5 bits plus 24.  */
-      if (! INT_P (x))
-       output_operand_lossage ("invalid %%H value");
-
-      fprintf (file, "%d", (INT_LOWPART (x) + 24) & 31);
+      /* If constant, output low-order six bits.  Otherwise,
+        write normally. */
+      if (INT_P (x))
+       fprintf (file, "%d", INT_LOWPART (x) & 63);
+      else
+       print_operand (file, x, 0);
       return;
 
     case 'I':
@@ -990,6 +2112,8 @@ print_operand (file, x, code)
            output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4));
          else
            output_address (plus_constant (XEXP (x, 0), 4));
+         if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+           fprintf (file, "@sda21(%s)", reg_names[0]);
        }
       return;
                            
@@ -1005,7 +2129,7 @@ print_operand (file, x, code)
         the left.  */
       if (val < 0 && (val & 1) == 0)
        {
-         fprintf (file, "0");
+         putc ('0', file);
          return;
        }
       else if (val >= 0)
@@ -1115,14 +2239,6 @@ print_operand (file, x, code)
       fprintf (file, "%d", (32 - INT_LOWPART (x)) & 31);
       return;
 
-    case 'S':
-      /* Low 5 bits of 31 - value */
-      if (! INT_P (x))
-       output_operand_lossage ("invalid %%S value");
-
-      fprintf (file, "%d", (31 - INT_LOWPART (x)) & 31);
-      return;
-
     case 't':
       /* Write 12 if this jump operation will branch if true, 4 otherwise. 
         All floating-point operations except NE branch true and integer
@@ -1208,17 +2324,40 @@ print_operand (file, x, code)
            output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8));
          else
            output_address (plus_constant (XEXP (x, 0), 8));
+         if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+           fprintf (file, "@sda21(%s)", reg_names[0]);
        }
       return;
                            
     case 'z':
       /* X is a SYMBOL_REF.  Write out the name preceded by a
         period and without any trailing data in brackets.  Used for function
-        names.  */
+        names.  If we are configured for System V (or the embedded ABI) on
+        the PowerPC, do not emit the period, since those systems do not use
+        TOCs and the like.  */
       if (GET_CODE (x) != SYMBOL_REF)
        abort ();
 
-      putc ('.', file);
+      if (XSTR (x, 0)[0] != '.')
+       {
+         switch (DEFAULT_ABI)
+           {
+           default:
+             abort ();
+
+           case ABI_AIX:
+             putc ('.', file);
+             break;
+
+           case ABI_V4:
+           case ABI_AIX_NODESC:
+             break;
+
+           case ABI_NT:
+             fputs ("..", file);
+             break;
+           }
+       }
       RS6000_OUTPUT_BASENAME (file, XSTR (x, 0));
       return;
 
@@ -1233,6 +2372,8 @@ print_operand (file, x, code)
            output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12));
          else
            output_address (plus_constant (XEXP (x, 0), 12));
+         if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+           fprintf (file, "@sda21(%s)", reg_names[0]);
        }
       return;
                            
@@ -1254,7 +2395,7 @@ print_operand (file, x, code)
        }
       else
        output_addr_const (file, x);
-      break;
+      return;
 
     default:
       output_operand_lossage ("invalid %%xn code");
@@ -1269,26 +2410,37 @@ print_operand_address (file, x)
      register rtx x;
 {
   if (GET_CODE (x) == REG)
-    fprintf (file, "0(%d)", REGNO (x));
+    fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
   else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST)
     {
       output_addr_const (file, x);
-      /* When TARGET_MINIMAL_TOC, use the indirected toc table pointer instead
-        of the toc pointer.  */
-      if (TARGET_MINIMAL_TOC)
-       fprintf (file, "(30)");
+      if (DEFAULT_ABI == ABI_V4 && small_data_operand (x, GET_MODE (x)))
+       fprintf (file, "@sda21(%s)", reg_names[0]);
+
+#ifdef TARGET_NO_TOC
+      else if (TARGET_NO_TOC)
+       ;
+#endif
       else
-       fprintf (file, "(2)");
+       fprintf (file, "(%s)", reg_names[ TARGET_MINIMAL_TOC ? 30 : 2 ]);
     }
   else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG)
     {
       if (REGNO (XEXP (x, 0)) == 0)
-       fprintf (file, "%d,%d", REGNO (XEXP (x, 1)), REGNO (XEXP (x, 0)));
+       fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 1)) ],
+                reg_names[ REGNO (XEXP (x, 0)) ]);
       else
-       fprintf (file, "%d,%d", REGNO (XEXP (x, 0)), REGNO (XEXP (x, 1)));
+       fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 0)) ],
+                reg_names[ REGNO (XEXP (x, 1)) ]);
     }
   else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
-    fprintf (file, "%d(%d)", INTVAL (XEXP (x, 1)), REGNO (XEXP (x, 0)));
+    fprintf (file, "%d(%s)", INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]);
+  else if (TARGET_ELF && !TARGET_64BIT && GET_CODE (x) == LO_SUM
+          && GET_CODE (XEXP (x, 0)) == REG && CONSTANT_P (XEXP (x, 1)))
+    {
+      output_addr_const (file, XEXP (x, 1));
+      fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+    }
   else
     abort ();
 }
@@ -1314,7 +2466,7 @@ first_reg_to_save ()
      to 23 to do this.  Don't use the frame pointer in reg 31.
 
      For now, save enough room for all of the parameter registers.  */
-  if (profile_flag)
+  if (DEFAULT_ABI == ABI_AIX && profile_flag)
     if (first_reg > 23)
       first_reg = 23;
 
@@ -1336,31 +2488,6 @@ first_fp_reg_to_save ()
   return first_reg;
 }
 
-/* Return 1 if we need to save CR.  */
-
-int
-must_save_cr ()
-{
-  return regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72];
-}
-
-/* Compute the size of the save area in the stack, including the space for
-   the fixed area.  */
-
-int
-rs6000_sa_size ()
-{
-  int size;
-
-  /* We have the six fixed words, plus the size of the register save 
-     areas, rounded to a double-word.  */
-  size = 6 + (32 - first_reg_to_save ()) + (64 - first_fp_reg_to_save ()) * 2;
-  if (size & 1)
-    size++;
-
-  return size * 4;
-}
-
 /* Return non-zero if this function makes calls.  */
 
 int
@@ -1379,121 +2506,756 @@ rs6000_makes_calls ()
   return 0;
 }
 
-/* Return non-zero if this function needs to push space on the stack.  */
+\f
+/* Calculate the stack information for the current function.  This is
+   complicated by having two separate calling sequences, the AIX calling
+   sequence and the V.4 calling sequence.
+
+   AIX stack frames look like:
+
+       SP----> +---------------------------------------+
+               | back chain to caller                  | 0
+               +---------------------------------------+
+               | saved CR                              | 4
+               +---------------------------------------+
+               | saved LR                              | 8
+               +---------------------------------------+
+               | reserved for compilers                | 12
+               +---------------------------------------+
+               | reserved for binders                  | 16
+               +---------------------------------------+
+               | saved TOC pointer                     | 20
+               +---------------------------------------+
+               | Parameter save area (P)               | 24
+               +---------------------------------------+
+               | Alloca space (A)                      | 24+P
+               +---------------------------------------+
+               | Local variable space (L)              | 24+P+A
+               +---------------------------------------+
+               | Save area for GP registers (G)        | 24+P+A+L
+               +---------------------------------------+
+               | Save area for FP registers (F)        | 24+P+A+L+G
+               +---------------------------------------+
+       old SP->| back chain to caller's caller         |
+               +---------------------------------------+
+
+   V.4 stack frames look like:
+
+       SP----> +---------------------------------------+
+               | back chain to caller                  | 0
+               +---------------------------------------+
+               | caller's saved LR                     | 4
+               +---------------------------------------+
+               | Parameter save area (P)               | 8
+               +---------------------------------------+
+               | Alloca space (A)                      | 8+P
+               +---------------------------------------+
+               | Varargs save area (V)                 | 8+P+A
+               +---------------------------------------+
+               | Local variable space (L)              | 8+P+A+V
+               +---------------------------------------+
+               | saved CR (C)                          | 8+P+A+V+L
+               +---------------------------------------+
+               | Save area for GP registers (G)        | 8+P+A+V+L+C
+               +---------------------------------------+
+               | Save area for FP registers (F)        | 8+P+A+V+L+C+G
+               +---------------------------------------+
+       old SP->| back chain to caller's caller         |
+               +---------------------------------------+
+
+
+   A PowerPC Windows/NT frame looks like:
+
+       SP----> +---------------------------------------+
+               | back chain to caller                  | 0
+               +---------------------------------------+
+               | reserved                              | 4
+               +---------------------------------------+
+               | reserved                              | 8
+               +---------------------------------------+
+               | reserved                              | 12
+               +---------------------------------------+
+               | reserved                              | 16
+               +---------------------------------------+
+               | reserved                              | 20
+               +---------------------------------------+
+               | Parameter save area (P)               | 24
+               +---------------------------------------+
+               | Alloca space (A)                      | 24+P
+               +---------------------------------------+
+               | Local variable space (L)              | 24+P+A
+               +---------------------------------------+
+               | Save area for FP registers (F)        | 24+P+A+L
+               +---------------------------------------+
+               | Possible alignment area (X)           | 24+P+A+L+F
+               +---------------------------------------+
+               | Save area for GP registers (G)        | 24+P+A+L+F+X
+               +---------------------------------------+
+               | Save area for CR (C)                  | 24+P+A+L+F+X+G
+               +---------------------------------------+
+               | Save area for TOC (T)                 | 24+P+A+L+F+X+G+C
+               +---------------------------------------+
+               | Save area for LR (R)                  | 24+P+A+L+F+X+G+C+T
+               +---------------------------------------+
+       old SP->| back chain to caller's caller         |
+               +---------------------------------------+
+
+   For NT, there is no specific order to save the registers, but in
+   order to support __builtin_return_address, the save area for the
+   link register needs to be in a known place, so we use -4 off of the
+   old SP.  To support calls through pointers, we also allocate a
+   fixed slot to store the TOC, -8 off the old SP.  */
+
+rs6000_stack_t *
+rs6000_stack_info ()
+{
+  static rs6000_stack_t info, zero_info;
+  rs6000_stack_t *info_ptr = &info;
+  int reg_size = TARGET_64BIT ? 8 : 4;
+  enum rs6000_abi abi;
+  int total_raw_size;
 
-int
-rs6000_pushes_stack ()
+  /* Zero all fields portably */
+  info = zero_info;
+
+  /* Select which calling sequence */
+  info_ptr->abi = abi = DEFAULT_ABI;
+
+  /* Calculate which registers need to be saved & save area size */
+  info_ptr->first_gp_reg_save = first_reg_to_save ();
+  info_ptr->gp_size = reg_size * (32 - info_ptr->first_gp_reg_save);
+
+  info_ptr->first_fp_reg_save = first_fp_reg_to_save ();
+  info_ptr->fp_size = 8 * (64 - info_ptr->first_fp_reg_save);
+
+  /* Does this function call anything? */
+  info_ptr->calls_p = rs6000_makes_calls ();
+
+  /* Do we need to allocate space to save the toc? */
+  if (rs6000_save_toc_p)
+    {
+      info_ptr->toc_save_p = 1;
+      info_ptr->toc_size = reg_size;
+    }
+
+  /* If this is main and we need to call a function to set things up,
+     save main's arguments around the call.  */
+  if (strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)), "main") == 0)
+    {
+      info_ptr->main_p = 1;
+
+#ifdef NAME__MAIN
+      info_ptr->calls_p = 1;
+
+      if (DECL_ARGUMENTS (current_function_decl))
+       {
+         int i;
+         tree arg;
+
+         info_ptr->main_save_p = 1;
+         info_ptr->main_size = 0;
+
+         for ((i = 0), (arg = DECL_ARGUMENTS (current_function_decl));
+              arg != NULL_TREE && i < 8;
+              (arg = TREE_CHAIN (arg)), i++)
+           {
+             info_ptr->main_size += reg_size;
+           }
+       }
+#endif
+    }
+
+  /* Determine if we need to save the link register */
+  if (regs_ever_live[65] || profile_flag
+#ifdef TARGET_RELOCATABLE
+      || (TARGET_RELOCATABLE && (get_pool_size () != 0))
+#endif
+      || (info_ptr->first_fp_reg_save != 64
+         && !FP_SAVE_INLINE (info_ptr->first_fp_reg_save))
+      || (abi == ABI_V4 && current_function_calls_alloca)
+      || info_ptr->calls_p)
+    {
+      info_ptr->lr_save_p = 1;
+      regs_ever_live[65] = 1;
+      if (abi == ABI_NT)
+       info_ptr->lr_size = reg_size;
+    }
+
+  /* Determine if we need to save the condition code registers */
+  if (regs_ever_live[70] || regs_ever_live[71] || regs_ever_live[72])
+    {
+      info_ptr->cr_save_p = 1;
+      if (abi == ABI_V4 || abi == ABI_NT)
+       info_ptr->cr_size = reg_size;
+    }
+
+  /* Determine various sizes */
+  info_ptr->reg_size     = reg_size;
+  info_ptr->fixed_size   = RS6000_SAVE_AREA;
+  info_ptr->varargs_size = RS6000_VARARGS_AREA;
+  info_ptr->vars_size    = ALIGN (get_frame_size (), 8);
+  info_ptr->parm_size    = ALIGN (current_function_outgoing_args_size, 8);
+  info_ptr->save_size    = ALIGN (info_ptr->fp_size
+                                 + info_ptr->gp_size
+                                 + info_ptr->cr_size
+                                 + info_ptr->lr_size
+                                 + info_ptr->toc_size
+                                 + info_ptr->main_size, 8);
+
+  total_raw_size        = (info_ptr->vars_size
+                           + info_ptr->parm_size
+                           + info_ptr->save_size
+                           + info_ptr->varargs_size
+                           + info_ptr->fixed_size);
+
+  info_ptr->total_size   = ALIGN (total_raw_size, STACK_BOUNDARY / BITS_PER_UNIT);
+
+  /* Determine if we need to allocate any stack frame.
+     For AIX We need to push the stack if a frame pointer is needed (because
+     the stack might be dynamically adjusted), if we are debugging, if the
+     total stack size is more than 220 bytes, or if we make calls.
+
+     For V.4 we don't have the stack cushion that AIX uses, but assume that
+     the debugger can handle stackless frames.  */
+
+  if (info_ptr->calls_p)
+    info_ptr->push_p = 1;
+
+  else if (abi == ABI_V4 || abi == ABI_NT)
+    info_ptr->push_p = (total_raw_size > info_ptr->fixed_size
+                       || info_ptr->lr_save_p);
+
+  else
+    info_ptr->push_p = (frame_pointer_needed
+                       || write_symbols != NO_DEBUG
+                       || info_ptr->total_size > 220);
+
+  /* Calculate the offsets */
+  switch (abi)
+    {
+    case ABI_NONE:
+    default:
+      abort ();
+
+    case ABI_AIX:
+    case ABI_AIX_NODESC:
+      info_ptr->fp_save_offset   = - info_ptr->fp_size;
+      info_ptr->gp_save_offset   = info_ptr->fp_save_offset - info_ptr->gp_size;
+      info_ptr->main_save_offset = info_ptr->gp_save_offset - info_ptr->main_size;
+      info_ptr->cr_save_offset   = 4;
+      info_ptr->lr_save_offset   = 8;
+      break;
+
+    case ABI_V4:
+      info_ptr->fp_save_offset   = - info_ptr->fp_size;
+      info_ptr->gp_save_offset   = info_ptr->fp_save_offset - info_ptr->gp_size;
+      info_ptr->cr_save_offset   = info_ptr->gp_save_offset - reg_size;
+      info_ptr->toc_save_offset  = info_ptr->cr_save_offset - info_ptr->cr_size;
+      info_ptr->main_save_offset = info_ptr->toc_save_offset - info_ptr->toc_size;
+      info_ptr->lr_save_offset   = reg_size;
+      break;
+
+    case ABI_NT:
+      info_ptr->lr_save_offset    = -4;
+      info_ptr->toc_save_offset   = info_ptr->lr_save_offset - info_ptr->lr_size;
+      info_ptr->cr_save_offset    = info_ptr->toc_save_offset - info_ptr->toc_size;
+      info_ptr->gp_save_offset    = info_ptr->cr_save_offset - info_ptr->cr_size - info_ptr->gp_size + reg_size;
+      info_ptr->fp_save_offset    = info_ptr->gp_save_offset - info_ptr->fp_size;
+      if (info_ptr->fp_size && ((- info_ptr->fp_save_offset) % 8) != 0)
+       info_ptr->fp_save_offset -= 4;
+
+      info_ptr->main_save_offset = info_ptr->fp_save_offset - info_ptr->main_size;
+      break;
+    }
+
+  /* Zero offsets if we're not saving those registers */
+  if (!info_ptr->fp_size)
+    info_ptr->fp_save_offset = 0;
+
+  if (!info_ptr->gp_size)
+    info_ptr->gp_save_offset = 0;
+
+  if (!info_ptr->lr_save_p)
+    info_ptr->lr_save_offset = 0;
+
+  if (!info_ptr->cr_save_p)
+    info_ptr->cr_save_offset = 0;
+
+  if (!info_ptr->toc_save_p)
+    info_ptr->toc_save_offset = 0;
+
+  if (!info_ptr->main_save_p)
+    info_ptr->main_save_offset = 0;
+
+  return info_ptr;
+}
+
+void
+debug_stack_info (info)
+     rs6000_stack_t *info;
 {
-  int total_size = (rs6000_sa_size () + get_frame_size ()
-                   + current_function_outgoing_args_size);
+  char *abi_string;
+
+  if (!info)
+    info = rs6000_stack_info ();
+
+  fprintf (stderr, "\nStack information for function %s:\n",
+          ((current_function_decl && DECL_NAME (current_function_decl))
+           ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
+           : "<unknown>"));
+
+  switch (info->abi)
+    {
+    default:            abi_string = "Unknown";        break;
+    case ABI_NONE:      abi_string = "NONE";           break;
+    case ABI_AIX:       abi_string = "AIX";            break;
+    case ABI_AIX_NODESC: abi_string = "AIX";           break;
+    case ABI_V4:        abi_string = "V.4";            break;
+    case ABI_NT:        abi_string = "NT";             break;
+    }
+
+  fprintf (stderr, "\tABI                 = %5s\n", abi_string);
+
+  if (info->first_gp_reg_save != 32)
+    fprintf (stderr, "\tfirst_gp_reg_save   = %5d\n", info->first_gp_reg_save);
+
+  if (info->first_fp_reg_save != 64)
+    fprintf (stderr, "\tfirst_fp_reg_save   = %5d\n", info->first_fp_reg_save);
+
+  if (info->lr_save_p)
+    fprintf (stderr, "\tlr_save_p           = %5d\n", info->lr_save_p);
+
+  if (info->cr_save_p)
+    fprintf (stderr, "\tcr_save_p           = %5d\n", info->cr_save_p);
+
+  if (info->toc_save_p)
+    fprintf (stderr, "\ttoc_save_p          = %5d\n", info->toc_save_p);
+
+  if (info->push_p)
+    fprintf (stderr, "\tpush_p              = %5d\n", info->push_p);
+
+  if (info->calls_p)
+    fprintf (stderr, "\tcalls_p             = %5d\n", info->calls_p);
+
+  if (info->main_p)
+    fprintf (stderr, "\tmain_p              = %5d\n", info->main_p);
+
+  if (info->main_save_p)
+    fprintf (stderr, "\tmain_save_p         = %5d\n", info->main_save_p);
+
+  if (info->gp_save_offset)
+    fprintf (stderr, "\tgp_save_offset      = %5d\n", info->gp_save_offset);
+
+  if (info->fp_save_offset)
+    fprintf (stderr, "\tfp_save_offset      = %5d\n", info->fp_save_offset);
+
+  if (info->lr_save_offset)
+    fprintf (stderr, "\tlr_save_offset      = %5d\n", info->lr_save_offset);
+
+  if (info->cr_save_offset)
+    fprintf (stderr, "\tcr_save_offset      = %5d\n", info->cr_save_offset);
+
+  if (info->toc_save_offset)
+    fprintf (stderr, "\ttoc_save_offset     = %5d\n", info->toc_save_offset);
 
-  /* We need to push the stack if a frame pointer is needed (because the
-     stack might be dynamically adjusted), if we are debugging, if the
-     total stack size is more than 220 bytes, or if we make calls.  */
+  if (info->varargs_save_offset)
+    fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset);
 
-  return (frame_pointer_needed || write_symbols != NO_DEBUG
-         || total_size > 220
-         || rs6000_makes_calls ());
+  if (info->main_save_offset)
+    fprintf (stderr, "\tmain_save_offset    = %5d\n", info->main_save_offset);
+
+  if (info->total_size)
+    fprintf (stderr, "\ttotal_size          = %5d\n", info->total_size);
+
+  if (info->varargs_size)
+    fprintf (stderr, "\tvarargs_size        = %5d\n", info->varargs_size);
+
+  if (info->vars_size)
+    fprintf (stderr, "\tvars_size           = %5d\n", info->vars_size);
+
+  if (info->parm_size)
+    fprintf (stderr, "\tparm_size           = %5d\n", info->parm_size);
+
+  if (info->fixed_size)
+    fprintf (stderr, "\tfixed_size          = %5d\n", info->fixed_size);
+
+  if (info->gp_size)
+    fprintf (stderr, "\tgp_size             = %5d\n", info->gp_size);
+
+  if (info->fp_size)
+    fprintf (stderr, "\tfp_size             = %5d\n", info->fp_size);
+
+ if (info->lr_size)
+    fprintf (stderr, "\tlr_size             = %5d\n", info->cr_size);
+
+  if (info->cr_size)
+    fprintf (stderr, "\tcr_size             = %5d\n", info->cr_size);
+
+ if (info->toc_size)
+    fprintf (stderr, "\ttoc_size            = %5d\n", info->toc_size);
+
+ if (info->main_size)
+    fprintf (stderr, "\tmain_size           = %5d\n", info->main_size);
+
+  if (info->save_size)
+    fprintf (stderr, "\tsave_size           = %5d\n", info->save_size);
+
+  if (info->reg_size != 4)
+    fprintf (stderr, "\treg_size            = %5d\n", info->reg_size);
+
+  fprintf (stderr, "\n");
 }
 
+\f
 /* Write function prologue.  */
-
 void
 output_prolog (file, size)
      FILE *file;
      int size;
 {
-  int first_reg = first_reg_to_save ();
-  int must_push = rs6000_pushes_stack ();
-  int first_fp_reg = first_fp_reg_to_save ();
-  int basic_size = rs6000_sa_size ();
-  int total_size = (basic_size + size + current_function_outgoing_args_size);
+  rs6000_stack_t *info = rs6000_stack_info ();
+  int reg_size = info->reg_size;
+  char *store_reg;
+  char *load_reg;
+  int sp_reg = 1;
+  int sp_offset = 0;
+
+  if (TARGET_32BIT)
+    {
+      store_reg = "\t{st|stw} %s,%d(%s)\n";
+      load_reg = "\t{l|lwz} %s,%d(%s)\n";
+    }
+  else
+    {
+      store_reg = "\tstd %s,%d(%s)\n";
+      load_reg = "\tlld %s,%d(%s)\n";
+    }
 
-  /* Round size to multiple of 8 bytes.  */
-  total_size = (total_size + 7) & ~7;
+  if (TARGET_DEBUG_STACK)
+    debug_stack_info (info);
 
   /* Write .extern for any function we will call to save and restore fp
      values.  */
-  if (first_fp_reg < 62)
-    fprintf (file, "\t.extern ._savef%d\n\t.extern ._restf%d\n",
-            first_fp_reg - 32, first_fp_reg - 32);
+  if (info->first_fp_reg_save < 64 && !FP_SAVE_INLINE (info->first_fp_reg_save))
+    fprintf (file, "\t.extern %s%d%s\n\t.extern %s%d%s\n",
+            SAVE_FP_PREFIX, info->first_fp_reg_save - 32, SAVE_FP_SUFFIX,
+            RESTORE_FP_PREFIX, info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
 
   /* Write .extern for truncation routines, if needed.  */
   if (rs6000_trunc_used && ! trunc_defined)
     {
-      fprintf (file, "\t.extern .itrunc\n\t.extern .uitrunc\n");
+      fprintf (file, "\t.extern .%s\n\t.extern .%s\n",
+              RS6000_ITRUNC, RS6000_UITRUNC);
       trunc_defined = 1;
     }
 
-  /* If we have to call a function to save fpr's, or if we are doing profiling,
-     then we will be using LR.  */
-  if (first_fp_reg < 62 || profile_flag)
-    regs_ever_live[65] = 1;
+  /* Write .extern for AIX common mode routines, if needed.  */
+  if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined)
+    {
+      fputs ("\t.extern __mulh\n", file);
+      fputs ("\t.extern __mull\n", file);
+      fputs ("\t.extern __divss\n", file);
+      fputs ("\t.extern __divus\n", file);
+      fputs ("\t.extern __quoss\n", file);
+      fputs ("\t.extern __quous\n", file);
+      common_mode_defined = 1;
+    }
+
+  /* For V.4, update stack before we do any saving and set back pointer.  */
+  if (info->push_p && DEFAULT_ABI == ABI_V4)
+    {
+      if (info->total_size < 32767)
+       {
+         asm_fprintf (file,
+                      (TARGET_32BIT) ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n",
+                      reg_names[1], - info->total_size, reg_names[1]);
+         sp_offset = info->total_size;
+       }
+      else
+       {
+         int neg_size = - info->total_size;
+         sp_reg = 12;
+         asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]);
+         asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+                      reg_names[0], (neg_size >> 16) & 0xffff,
+                      reg_names[0], reg_names[0], neg_size & 0xffff);
+         asm_fprintf (file,
+                      (TARGET_32BIT) ? "\t{stux|stwux} %s,%s,%s\n" : "\tstdux %s,%s,%s\n",
+                      reg_names[1], reg_names[1], reg_names[0]);
+       }
+    }
 
   /* If we use the link register, get it into r0.  */
-  if (regs_ever_live[65])
-    asm_fprintf (file, "\tmflr 0\n");
+  if (info->lr_save_p)
+    asm_fprintf (file, "\tmflr %s\n", reg_names[0]);
 
   /* If we need to save CR, put it into r12.  */
-  if (must_save_cr ())
-    asm_fprintf (file, "\tmfcr 12\n");
+  if (info->cr_save_p && sp_reg != 12)
+    asm_fprintf (file, "\tmfcr %s\n", reg_names[12]);
 
   /* Do any required saving of fpr's.  If only one or two to save, do it
      ourself.  Otherwise, call function.  Note that since they are statically
      linked, we do not need a nop following them.  */
-  if (first_fp_reg == 62)
-    asm_fprintf (file, "\tstfd 30,-16(1)\n\tstfd 31,-8(1)\n");
-  else if (first_fp_reg == 63)
-    asm_fprintf (file, "\tstfd 31,-8(1)\n");
-  else if (first_fp_reg != 64)
-    asm_fprintf (file, "\tbl ._savef%d\n", first_fp_reg - 32);
+  if (FP_SAVE_INLINE (info->first_fp_reg_save))
+    {
+      int regno = info->first_fp_reg_save;
+      int loc   = info->fp_save_offset + sp_offset;
+
+      for ( ; regno < 64; regno++, loc += 8)
+       asm_fprintf (file, "\tstfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]);
+    }
+  else if (info->first_fp_reg_save != 64)
+    asm_fprintf (file, "\tbl %s%d%s\n", SAVE_FP_PREFIX,
+                info->first_fp_reg_save - 32, SAVE_FP_SUFFIX);
 
   /* Now save gpr's.  */
-  if (! TARGET_POWER || first_reg == 31)
+  if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT)
+    {
+      int regno    = info->first_gp_reg_save;
+      int loc      = info->gp_save_offset + sp_offset;
+
+      for ( ; regno < 32; regno++, loc += reg_size)
+       asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]);
+    }
+
+  else if (info->first_gp_reg_save != 32)
+    asm_fprintf (file, "\t{stm|stmw} %s,%d(%s)\n",
+                reg_names[info->first_gp_reg_save],
+                info->gp_save_offset + sp_offset,
+                reg_names[sp_reg]);
+
+  /* Save main's arguments if we need to call a function */
+#ifdef NAME__MAIN
+  if (info->main_save_p)
+    {
+      int regno;
+      int loc = info->main_save_offset;
+      int size = info->main_size;
+
+      for (regno = 3; size > 0; regno++, loc -= reg_size, size -= reg_size)
+       asm_fprintf (file, store_reg, reg_names[regno], loc, reg_names[sp_reg]);
+    }
+#endif
+
+  /* Save lr if we used it.  */
+  if (info->lr_save_p)
+    asm_fprintf (file, store_reg, reg_names[0], info->lr_save_offset + sp_offset,
+                reg_names[sp_reg]);
+
+  /* Save CR if we use any that must be preserved.  */
+  if (info->cr_save_p)
+    {
+      if (sp_reg == 12)        /* If r12 is used to hold the original sp, copy cr now */
+       {
+         asm_fprintf (file, "\tmfcr %s\n", reg_names[0]);
+         asm_fprintf (file, store_reg, reg_names[0],
+                      info->cr_save_offset + sp_offset,
+                      reg_names[sp_reg]);
+       }
+      else
+       asm_fprintf (file, store_reg, reg_names[12], info->cr_save_offset + sp_offset,
+                    reg_names[sp_reg]);
+    }
+
+  if (info->toc_save_p)
+    asm_fprintf (file, store_reg, reg_names[2], info->toc_save_offset + sp_offset,
+                reg_names[sp_reg]);
+
+  /* NT needs us to probe the stack frame every 4k pages for large frames, so
+     do it here.  */
+  if (DEFAULT_ABI == ABI_NT && info->total_size > 4096)
+    {
+      if (info->total_size < 32768)
+       {
+         int probe_offset = 4096;
+         while (probe_offset < info->total_size)
+           {
+             asm_fprintf (file, "\t{l|lwz} %s,%d(%s)\n", reg_names[0], -probe_offset, reg_names[1]);
+             probe_offset += 4096;
+           }
+       }
+      else
+       {
+         int probe_iterations = info->total_size / 4096;
+         static int probe_labelno = 0;
+         char buf[256];
+
+         if (probe_iterations < 32768)
+           asm_fprintf (file, "\tli %s,%d\n", reg_names[12], probe_iterations);
+         else
+           {
+             asm_fprintf (file, "\tlis %s,%d\n", reg_names[12], probe_iterations >> 16);
+             if (probe_iterations & 0xffff)
+               asm_fprintf (file, "\tori %s,%s,%d\n", reg_names[12], reg_names[12],
+                            probe_iterations & 0xffff);
+           }
+         asm_fprintf (file, "\tmtctr %s\n", reg_names[12]);
+         asm_fprintf (file, "\tmr %s,%s\n", reg_names[12], reg_names[1]);
+         ASM_OUTPUT_INTERNAL_LABEL (file, "LCprobe", probe_labelno);
+         asm_fprintf (file, "\t{lu|lwzu} %s,-4096(%s)\n", reg_names[0], reg_names[12]);
+         ASM_GENERATE_INTERNAL_LABEL (buf, "LCprobe", probe_labelno);
+         fputs ("\tbdnz ", file);
+         assemble_name (file, buf);
+         fputs ("\n", file);
+       }
+    }
+
+  /* Update stack and set back pointer and we have already done so for V.4.  */
+  if (info->push_p && DEFAULT_ABI != ABI_V4)
+    {
+      if (info->total_size < 32767)
+       asm_fprintf (file,
+                    (TARGET_32BIT) ? "\t{stu|stwu} %s,%d(%s)\n" : "\tstdu %s,%d(%s)\n",
+                    reg_names[1], - info->total_size, reg_names[1]);
+      else
+       {
+         int neg_size = - info->total_size;
+         asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+                      reg_names[0], (neg_size >> 16) & 0xffff,
+                      reg_names[0], reg_names[0], neg_size & 0xffff);
+         asm_fprintf (file,
+                      (TARGET_32BIT) ? "\t{stux|stwux} %s,%s,%s\n" : "\tstdux %s,%s,%s\n",
+                      reg_names[1], reg_names[1], reg_names[0]);
+       }
+    }
+
+  /* Set frame pointer, if needed.  */
+  if (frame_pointer_needed)
+    asm_fprintf (file, "\tmr %s,%s\n", reg_names[31], reg_names[1]);
+
+#ifdef NAME__MAIN
+  /* If we need to call a function to set things up for main, do so now
+     before dealing with the TOC.  */
+  if (info->main_p)
     {
-      int regno, loc;
+      char *prefix = "";
+
+      switch (DEFAULT_ABI)
+       {
+       case ABI_AIX:   prefix = ".";   break;
+       case ABI_NT:    prefix = "..";  break;
+       }
+
+      fprintf (file, "\tbl %s%s\n", prefix, NAME__MAIN);
+#ifdef RS6000_CALL_GLUE2
+      fprintf (file, "\t%s%s%s\n", RS6000_CALL_GLUE2, prefix, NAME_MAIN);
+#else
+#ifdef RS6000_CALL_GLUE
+      if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_NT)
+       fprintf (file, "\t%s\n", RS6000_CALL_GLUE);
+#endif
+#endif
+
+      if (info->main_save_p)
+       {
+         int regno;
+         int loc;
+         int size = info->main_size;
+
+         if (info->total_size < 32767)
+           {
+             loc = info->total_size + info->main_save_offset;
+             for (regno = 3; size > 0; regno++, size -= reg_size, loc -= reg_size)
+               asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[1]);
+           }
+         else
+           {                   /* for large AIX/NT frames, reg 0 above contains -frame size */
+                               /* for V.4, we need to reload -frame size */
+             loc = info->main_save_offset;
+             if (DEFAULT_ABI == ABI_V4 && info->total_size > 32767)
+               {
+                 int neg_size = - info->total_size;
+                 asm_fprintf (file, "\t{liu|lis} %s,%d\n\t{oril|ori} %s,%s,%d\n",
+                              reg_names[0], (neg_size >> 16) & 0xffff,
+                              reg_names[0], reg_names[0], neg_size & 0xffff);
+               }
+
+             asm_fprintf (file, "\t{sf|subf} %s,%s,%s\n", reg_names[0], reg_names[0],
+                          reg_names[1]);
 
-      for (regno = first_reg,
-          loc = - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8;
-          regno < 32;
-          regno++, loc += 4)
-       asm_fprintf (file, "\t{st|stw} %d,%d(1)\n", regno, loc);
+             for (regno = 3; size > 0; regno++, size -= reg_size, loc -= reg_size)
+               asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[0]);
+           }
+       }
     }
+#endif
+
+
+  /* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the
+     TOC_TABLE address into register 30.  */
+  if (TARGET_TOC && TARGET_MINIMAL_TOC && get_pool_size () != 0)
+    {
+      char buf[256];
 
-  else if (first_reg != 32)
-    asm_fprintf (file, "\t{stm|stmw} %d,%d(1)\n", first_reg,
-            - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+#ifdef TARGET_RELOCATABLE
+      if (TARGET_RELOCATABLE)
+       {
+         ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
+         fputs ("\tbl ", file);
+         assemble_name (file, buf);
+         putc ('\n', file);
+
+         ASM_OUTPUT_INTERNAL_LABEL (file, "LCF", rs6000_pic_labelno);
+         fprintf (file, "\tmflr %s\n", reg_names[30]);
+
+         asm_fprintf (file, (TARGET_32BIT) ? "\t{l|lwz}" : "\tld");
+         fprintf (file, " %s,(", reg_names[0]);
+         ASM_GENERATE_INTERNAL_LABEL (buf, "LCL", rs6000_pic_labelno);
+         assemble_name (file, buf);
+         putc ('-', file);
+         ASM_GENERATE_INTERNAL_LABEL (buf, "LCF", rs6000_pic_labelno);
+         assemble_name (file, buf);
+         fprintf (file, ")(%s)\n", reg_names[30]);
+         asm_fprintf (file, "\t{cax|add} %s,%s,%s\n",
+                      reg_names[30], reg_names[0], reg_names[30]);
+         rs6000_pic_labelno++;
+       }
+      else
+#endif
 
-  /* Save lr if we used it.  */
-  if (regs_ever_live[65])
-    asm_fprintf (file, "\t{st|stw} 0,8(1)\n");
+       switch (DEFAULT_ABI)
+         {
+         case ABI_V4:
+         case ABI_AIX_NODESC:
+           if (TARGET_32BIT)
+             {
+               ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 1);
+               asm_fprintf (file, "\t{cau|addis} %s,%s,", reg_names[30], reg_names[0]);
+               assemble_name (file, buf);
+               asm_fprintf (file, "@ha\n");
+               if (TARGET_NEW_MNEMONICS)
+                 {
+                   asm_fprintf (file, "\taddi %s,%s,", reg_names[30], reg_names[30]);
+                   assemble_name (file, buf);
+                   asm_fprintf (file, "@l\n");
+                 }
+               else
+                 {
+                   asm_fprintf (file, "\tcal %s,", reg_names[30]);
+                   assemble_name (file, buf);
+                   asm_fprintf (file, "@l(%s)\n", reg_names[30]);
+                 }
+             }
+           else
+             abort ();
 
-  /* Save CR if we use any that must be preserved.  */
-  if (must_save_cr ())
-    asm_fprintf (file, "\t{st|stw} 12,4(1)\n");
+         break;
 
-  /* Update stack and set back pointer.  */
-  if (must_push)
-    {
-      if (total_size < 32767)
-       asm_fprintf (file, "\t{stu|stwu} 1,%d(1)\n", - total_size);
-      else
-       {
-         asm_fprintf (file, "\t{cau|addis} 0,0,%d\n\t{oril|ori} 0,0,%d\n",
-                  (total_size >> 16) & 0xffff, total_size & 0xffff);
-         asm_fprintf (file, "\t{sf|subfc} 12,0,1\n");
-         asm_fprintf (file, "\t{st|stw} 1,0(12)\n\t{oril|ori} 1,12,0\n");
+       case ABI_NT:
+       case ABI_AIX:
+         ASM_GENERATE_INTERNAL_LABEL (buf, "LCTOC", 0);
+         asm_fprintf (file, "\t{l|lwz} %s,", reg_names[30]);
+         assemble_name (file, buf);
+         asm_fprintf (file, "(%s)\n", reg_names[2]);
+         break;
        }
     }
 
-  /* Set frame pointer, if needed.  */
-  if (frame_pointer_needed)
-    asm_fprintf (file, "\t{oril|ori} 31,1,0\n");
-
-  /* If TARGET_MINIMAL_TOC, and the constant pool is needed, then load the
-     TOC_TABLE address into register 30.  */
-  if (TARGET_MINIMAL_TOC && get_pool_size () != 0)
-    asm_fprintf (file, "\t{l|lwz} 30,LCTOC..0(2)\n");
+  if (DEFAULT_ABI == ABI_NT)
+    {
+      assemble_name (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+      fputs (".b:\n", file);
+    }
 }
 
 /* Write function epilogue.  */
@@ -1503,15 +3265,16 @@ output_epilog (file, size)
      FILE *file;
      int size;
 {
-  int first_reg = first_reg_to_save ();
-  int must_push = rs6000_pushes_stack ();
-  int first_fp_reg = first_fp_reg_to_save ();
-  int basic_size = rs6000_sa_size ();
-  int total_size = (basic_size + size + current_function_outgoing_args_size);
+  rs6000_stack_t *info = rs6000_stack_info ();
+  char *load_reg = (TARGET_32BIT) ? "\t{l|lwz} %s,%d(%s)\n" : "\tld %s,%d(%s)\n";
   rtx insn = get_last_insn ();
+  int sp_reg = 1;
+  int sp_offset = 0;
+  int i;
 
-  /* Round size to multiple of 8 bytes.  */
-  total_size = (total_size + 7) & ~7;
+  /* Forget about any temporaries created */
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    stack_temps[i] = NULL_RTX;
 
   /* If the last insn was a BARRIER, we don't have to write anything except
      the trace table.  */
@@ -1523,232 +3286,298 @@ output_epilog (file, size)
         frame, restore the old stack pointer using the backchain.  Otherwise,
         we know what size to update it with.  */
       if (frame_pointer_needed || current_function_calls_alloca
-         || total_size > 32767)
-       asm_fprintf (file, "\t{l|lwz} 1,0(1)\n");
-      else if (must_push)
-       asm_fprintf (file, "\t{ai|addic} 1,1,%d\n", total_size);
+         || info->total_size > 32767)
+       {
+         /* Under V.4, don't reset the stack pointer until after we're done
+            loading the saved registers.  */
+         if (DEFAULT_ABI == ABI_V4)
+           sp_reg = 11;
+
+         asm_fprintf (file, load_reg, reg_names[sp_reg], 0, reg_names[1]);
+       }
+      else if (info->push_p)
+       {
+         if (DEFAULT_ABI == ABI_V4)
+           sp_offset = info->total_size;
+         else if (TARGET_NEW_MNEMONICS)
+           asm_fprintf (file, "\taddi %s,%s,%d\n", reg_names[1], reg_names[1], info->total_size);
+         else
+           asm_fprintf (file, "\tcal %s,%d(%s)\n", reg_names[1], info->total_size, reg_names[1]);
+       }
 
       /* Get the old lr if we saved it.  */
-      if (regs_ever_live[65])
-       asm_fprintf (file, "\t{l|lwz} 0,8(1)\n");
+      if (info->lr_save_p)
+       asm_fprintf (file, load_reg, reg_names[0], info->lr_save_offset + sp_offset, reg_names[sp_reg]);
 
       /* Get the old cr if we saved it.  */
-      if (must_save_cr ())
-       asm_fprintf (file, "\t{l|lwz} 12,4(1)\n");
+      if (info->cr_save_p)
+       asm_fprintf (file, load_reg, reg_names[12], info->cr_save_offset + sp_offset, reg_names[sp_reg]);
 
       /* Set LR here to try to overlap restores below.  */
-      if (regs_ever_live[65])
-       asm_fprintf (file, "\tmtlr 0\n");
+      if (info->lr_save_p)
+       asm_fprintf (file, "\tmtlr %s\n", reg_names[0]);
 
       /* Restore gpr's.  */
-      if (! TARGET_POWER || first_reg == 31)
+      if (! TARGET_MULTIPLE || info->first_gp_reg_save == 31 || TARGET_64BIT)
        {
-         int regno, loc;
+         int regno    = info->first_gp_reg_save;
+         int loc      = info->gp_save_offset + sp_offset;
+         int reg_size = (TARGET_32BIT) ? 4 : 8;
 
-         for (regno = first_reg,
-              loc = - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8;
-              regno < 32;
-              regno++, loc += 4)
-           asm_fprintf (file, "\t{l|lwz} %d,%d(1)\n", regno, loc);
+         for ( ; regno < 32; regno++, loc += reg_size)
+           asm_fprintf (file, load_reg, reg_names[regno], loc, reg_names[sp_reg]);
        }
 
-      else if (first_reg != 32)
-       asm_fprintf (file, "\t{lm|lmw} %d,%d(1)\n", first_reg,
-            - (32 - first_reg) * 4 - (64 - first_fp_reg) * 8);
+      else if (info->first_gp_reg_save != 32)
+       asm_fprintf (file, "\t{lm|lmw} %s,%d(%s)\n",
+                    reg_names[info->first_gp_reg_save],
+                    info->gp_save_offset + sp_offset,
+                    reg_names[sp_reg]);
 
       /* Restore fpr's if we can do it without calling a function.  */
-      if (first_fp_reg == 62)
-       asm_fprintf (file, "\tlfd 30,-16(1)\n\tlfd 31,-8(1)\n");
-      else if (first_fp_reg == 63)
-       asm_fprintf (file, "\tlfd 31,-8(1)\n");
+      if (FP_SAVE_INLINE (info->first_fp_reg_save))
+       {
+         int regno = info->first_fp_reg_save;
+         int loc   = info->fp_save_offset + sp_offset;
+
+         for ( ; regno < 64; regno++, loc += 8)
+           asm_fprintf (file, "\tlfd %s,%d(%s)\n", reg_names[regno], loc, reg_names[sp_reg]);
+       }
 
       /* If we saved cr, restore it here.  Just those of cr2, cr3, and cr4
         that were used.  */
-      if (must_save_cr ())
-       asm_fprintf (file, "\tmtcrf %d,12\n",
+      if (info->cr_save_p)
+       asm_fprintf (file, "\tmtcrf %d,%s\n",
                     (regs_ever_live[70] != 0) * 0x20
                     + (regs_ever_live[71] != 0) * 0x10
-                    + (regs_ever_live[72] != 0) * 0x8);
+                    + (regs_ever_live[72] != 0) * 0x8, reg_names[12]);
+
+      /* If this is V.4, unwind the stack pointer after all of the loads have been done */
+      if (sp_offset)
+       {
+         if (TARGET_NEW_MNEMONICS)
+           asm_fprintf (file, "\taddi %s,%s,%d\n", reg_names[1], reg_names[1], sp_offset);
+         else
+           asm_fprintf (file, "\tcal %s,%d(%s)\n", reg_names[1], sp_offset, reg_names[1]);
+       }
+      else if (sp_reg != 1)
+       asm_fprintf (file, "\tmr %s,%s\n", reg_names[1], reg_names[sp_reg]);
 
       /* If we have to restore more than two FP registers, branch to the
         restore function.  It will return to our caller.  */
-      if (first_fp_reg < 62)
-       asm_fprintf (file, "\tb ._restf%d\n", first_fp_reg - 32);
+      if (info->first_fp_reg_save != 64 && !FP_SAVE_INLINE (info->first_fp_reg_save))
+       asm_fprintf (file, "\tb %s%d%s\n", RESTORE_FP_PREFIX,
+                    info->first_fp_reg_save - 32, RESTORE_FP_SUFFIX);
       else
        asm_fprintf (file, "\t{br|blr}\n");
     }
 
   /* Output a traceback table here.  See /usr/include/sys/debug.h for info
-     on its format.  */
-  {
-    char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
-    int fixed_parms, float_parms, parm_info;
-    int i;
-
-    /* Need label immediately before tbtab, so we can compute its offset
-       from the function start.  */
-    if (*fname == '*')
-      ++fname;
-    fprintf (file, "LT..");
-    ASM_OUTPUT_LABEL (file, fname);
-
-    /* The .tbtab pseudo-op can only be used for the first eight
-       expressions, since it can't handle the possibly variable length
-       fields that follow.  However, if you omit the optional fields,
-       the assembler outputs zeros for all optional fields anyways, giving each
-       variable length field is minimum length (as defined in sys/debug.h).
-       Thus we can not use the .tbtab pseudo-op at all.  */
-
-    /* An all-zero word flags the start of the tbtab, for debuggers that have
-       to find it by searching forward from the entry point or from the
-       current pc.  */
-    fprintf (file, "\t.long 0\n");
-
-    /* Tbtab format type.  Use format type 0.  */
-    fprintf (file, "\t.byte 0,");
-
-    /* Language type.  Unfortunately, there doesn't seem to be any official way
-       to get this info, so we use language_string.  C is 0.  C++ is 9.
-       No number defined for Obj-C, so use the value for C for now.  */
-    if (! strcmp (language_string, "GNU C")
-       || ! strcmp (language_string, "GNU Obj-C"))
-      i = 0;
-    else if (! strcmp (language_string, "GNU F77"))
-      i = 1;
-    else if (! strcmp (language_string, "GNU Ada"))
-      i = 3;
-    else if (! strcmp (language_string, "GNU PASCAL"))
-      i = 2;
-    else if (! strcmp (language_string, "GNU C++"))
-      i = 9;
-    else
-      abort ();
-    fprintf (file, "%d,", i);
-
-    /* 8 single bit fields: global linkage (not set for C extern linkage,
-       apparently a PL/I convention?), out-of-line epilogue/prologue, offset
-       from start of procedure stored in tbtab, internal function, function
-       has controlled storage, function has no toc, function uses fp,
-       function logs/aborts fp operations.  */
-    /* Assume that fp operations are used if any fp reg must be saved.  */
-    fprintf (file, "%d,", (1 << 5) | ((first_fp_reg != 64) << 1));
-
-    /* 6 bitfields: function is interrupt handler, name present in proc table,
-       function calls alloca, on condition directives (controls stack walks,
-       3 bits), saves condition reg, saves link reg.  */
-    /* The `function calls alloca' bit seems to be set whenever reg 31 is
-       set up as a frame pointer, even when there is no alloca call.  */
-    fprintf (file, "%d,",
-            ((1 << 6) | (frame_pointer_needed << 5)
-             | (must_save_cr () << 1) | (regs_ever_live[65])));
-
-    /* 3 bitfields: saves backchain, spare bit, number of fpr saved
-       (6 bits).  */
-    fprintf (file, "%d,",
-            (must_push << 7) | (64 - first_fp_reg_to_save ()));
-
-    /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits).  */
-    fprintf (file, "%d,", (32 - first_reg_to_save ()));
-
-    {
-      /* Compute the parameter info from the function decl argument list.  */
-      tree decl;
-      int next_parm_info_bit;
-
-      next_parm_info_bit = 31;
-      parm_info = 0;
-      fixed_parms = 0;
-      float_parms = 0;
-
-      for (decl = DECL_ARGUMENTS (current_function_decl);
-          decl; decl = TREE_CHAIN (decl))
-       {
-         rtx parameter = DECL_INCOMING_RTL (decl);
-         enum machine_mode mode = GET_MODE (parameter);
-
-         if (GET_CODE (parameter) == REG)
-           {
-             if (GET_MODE_CLASS (mode) == MODE_FLOAT)
-               {
-                 int bits;
-
-                 float_parms++;
+     on its format.
+
+     We don't output a traceback table if -finhibit-size-directive was
+     used.  The documentation for -finhibit-size-directive reads
+     ``don't output a @code{.size} assembler directive, or anything
+     else that would cause trouble if the function is split in the
+     middle, and the two halves are placed at locations far apart in
+     memory.''  The traceback table has this property, since it
+     includes the offset from the start of the function to the
+     traceback table itself.
+
+     System V.4 Powerpc's (and the embedded ABI derived from it) use a
+     different traceback table.  */
+  if (DEFAULT_ABI == ABI_AIX && ! flag_inhibit_size_directive)
+    {
+      char *fname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+      int fixed_parms, float_parms, parm_info;
+      int i;
+
+      while (*fname == '.')    /* V.4 encodes . in the name */
+       fname++;
+
+      /* Need label immediately before tbtab, so we can compute its offset
+        from the function start.  */
+      if (*fname == '*')
+       ++fname;
+      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT");
+      ASM_OUTPUT_LABEL (file, fname);
+
+      /* The .tbtab pseudo-op can only be used for the first eight
+        expressions, since it can't handle the possibly variable
+        length fields that follow.  However, if you omit the optional
+        fields, the assembler outputs zeros for all optional fields
+        anyways, giving each variable length field is minimum length
+        (as defined in sys/debug.h).  Thus we can not use the .tbtab
+        pseudo-op at all.  */
+
+      /* An all-zero word flags the start of the tbtab, for debuggers
+        that have to find it by searching forward from the entry
+        point or from the current pc.  */
+      fputs ("\t.long 0\n", file);
+
+      /* Tbtab format type.  Use format type 0.  */
+      fputs ("\t.byte 0,", file);
+
+      /* Language type.  Unfortunately, there doesn't seem to be any
+        official way to get this info, so we use language_string.  C
+        is 0.  C++ is 9.  No number defined for Obj-C, so use the
+        value for C for now.  */
+      if (! strcmp (language_string, "GNU C")
+         || ! strcmp (language_string, "GNU Obj-C"))
+       i = 0;
+      else if (! strcmp (language_string, "GNU F77"))
+       i = 1;
+      else if (! strcmp (language_string, "GNU Ada"))
+       i = 3;
+      else if (! strcmp (language_string, "GNU PASCAL"))
+       i = 2;
+      else if (! strcmp (language_string, "GNU C++"))
+       i = 9;
+      else
+       abort ();
+      fprintf (file, "%d,", i);
+
+      /* 8 single bit fields: global linkage (not set for C extern linkage,
+        apparently a PL/I convention?), out-of-line epilogue/prologue, offset
+        from start of procedure stored in tbtab, internal function, function
+        has controlled storage, function has no toc, function uses fp,
+        function logs/aborts fp operations.  */
+      /* Assume that fp operations are used if any fp reg must be saved.  */
+      fprintf (file, "%d,", (1 << 5) | ((info->first_fp_reg_save != 64) << 1));
+
+      /* 6 bitfields: function is interrupt handler, name present in
+        proc table, function calls alloca, on condition directives
+        (controls stack walks, 3 bits), saves condition reg, saves
+        link reg.  */
+      /* The `function calls alloca' bit seems to be set whenever reg 31 is
+        set up as a frame pointer, even when there is no alloca call.  */
+      fprintf (file, "%d,",
+              ((1 << 6) | (frame_pointer_needed << 5)
+               | (info->cr_save_p << 1) | (info->lr_save_p)));
+
+      /* 3 bitfields: saves backchain, spare bit, number of fpr saved
+        (6 bits).  */
+      fprintf (file, "%d,",
+              (info->push_p << 7) | (64 - info->first_fp_reg_save));
+
+      /* 2 bitfields: spare bits (2 bits), number of gpr saved (6 bits).  */
+      fprintf (file, "%d,", (32 - first_reg_to_save ()));
+
+      {
+       /* Compute the parameter info from the function decl argument
+          list.  */
+       tree decl;
+       int next_parm_info_bit;
+
+       next_parm_info_bit = 31;
+       parm_info = 0;
+       fixed_parms = 0;
+       float_parms = 0;
+
+       for (decl = DECL_ARGUMENTS (current_function_decl);
+            decl; decl = TREE_CHAIN (decl))
+         {
+           rtx parameter = DECL_INCOMING_RTL (decl);
+           enum machine_mode mode = GET_MODE (parameter);
+
+           if (GET_CODE (parameter) == REG)
+             {
+               if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+                 {
+                   int bits;
+
+                   float_parms++;
+
+                   if (mode == SFmode)
+                     bits = 0x2;
+                   else if (mode == DFmode)
+                     bits = 0x3;
+                   else
+                     abort ();
+
+                   /* If only one bit will fit, don't or in this entry.  */
+                   if (next_parm_info_bit > 0)
+                     parm_info |= (bits << (next_parm_info_bit - 1));
+                   next_parm_info_bit -= 2;
+                 }
+               else
+                 {
+                   fixed_parms += ((GET_MODE_SIZE (mode)
+                                    + (UNITS_PER_WORD - 1))
+                                   / UNITS_PER_WORD);
+                   next_parm_info_bit -= 1;
+                 }
+             }
+         }
+      }
+
+      /* Number of fixed point parameters.  */
+      /* This is actually the number of words of fixed point parameters; thus
+        an 8 byte struct counts as 2; and thus the maximum value is 8.  */
+      fprintf (file, "%d,", fixed_parms);
+
+      /* 2 bitfields: number of floating point parameters (7 bits), parameters
+        all on stack.  */
+      /* This is actually the number of fp registers that hold parameters;
+        and thus the maximum value is 13.  */
+      /* Set parameters on stack bit if parameters are not in their original
+        registers, regardless of whether they are on the stack?  Xlc
+        seems to set the bit when not optimizing.  */
+      fprintf (file, "%d\n", ((float_parms << 1) | (! optimize)));
+
+      /* Optional fields follow.  Some are variable length.  */
+
+      /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float,
+        11 double float.  */
+      /* There is an entry for each parameter in a register, in the order that
+        they occur in the parameter list.  Any intervening arguments on the
+        stack are ignored.  If the list overflows a long (max possible length
+        34 bits) then completely leave off all elements that don't fit.  */
+      /* Only emit this long if there was at least one parameter.  */
+      if (fixed_parms || float_parms)
+       fprintf (file, "\t.long %d\n", parm_info);
+
+      /* Offset from start of code to tb table.  */
+      fputs ("\t.long ", file);
+      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LT");
+      RS6000_OUTPUT_BASENAME (file, fname);
+      fputs ("-.", file);
+      RS6000_OUTPUT_BASENAME (file, fname);
+      putc ('\n', file);
+
+      /* Interrupt handler mask.  */
+      /* Omit this long, since we never set the interrupt handler bit
+        above.  */
+
+      /* Number of CTL (controlled storage) anchors.  */
+      /* Omit this long, since the has_ctl bit is never set above.  */
+
+      /* Displacement into stack of each CTL anchor.  */
+      /* Omit this list of longs, because there are no CTL anchors.  */
+
+      /* Length of function name.  */
+      fprintf (file, "\t.short %d\n", strlen (fname));
+
+      /* Function name.  */
+      assemble_string (fname, strlen (fname));
+
+      /* Register for alloca automatic storage; this is always reg 31.
+        Only emit this if the alloca bit was set above.  */
+      if (frame_pointer_needed)
+       fputs ("\t.byte 31\n", file);
+    }
 
-                 if (mode == SFmode)
-                   bits = 0x2;
-                 else if (mode == DFmode)
-                   bits = 0x3;
-                 else
-                   abort ();
+  /* Reset varargs and save TOC indicator */
+  rs6000_sysv_varargs_p = 0;
+  rs6000_save_toc_p = 0;
 
-                 /* If only one bit will fit, don't or in this entry.  */
-                 if (next_parm_info_bit > 0)
-                   parm_info |= (bits << (next_parm_info_bit - 1));
-                 next_parm_info_bit -= 2;
-               }
-             else
-               {
-                 fixed_parms += ((GET_MODE_SIZE (mode) + (UNITS_PER_WORD - 1))
-                                 / UNITS_PER_WORD);
-                 next_parm_info_bit -= 1;
-               }
-           }
-       }
+  if (DEFAULT_ABI == ABI_NT)
+    {
+      RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+      fputs (".e:\nFE_MOT_RESVD..", file);
+      RS6000_OUTPUT_BASENAME (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0));
+      fputs (":\n", file);
     }
-
-    /* Number of fixed point parameters.  */
-    /* This is actually the number of words of fixed point parameters; thus
-       an 8 byte struct counts as 2; and thus the maximum value is 8.  */
-    fprintf (file, "%d,", fixed_parms);
-
-    /* 2 bitfields: number of floating point parameters (7 bits), parameters
-       all on stack.  */
-    /* This is actually the number of fp registers that hold parameters;
-       and thus the maximum value is 13.  */
-    /* Set parameters on stack bit if parameters are not in their original
-       registers, regardless of whether they are on the stack?  Xlc
-       seems to set the bit when not optimizing.  */
-    fprintf (file, "%d\n", ((float_parms << 1) | (! optimize)));
-
-    /* Optional fields follow.  Some are variable length.  */
-
-    /* Parameter types, left adjusted bit fields: 0 fixed, 10 single float,
-       11 double float.  */
-    /* There is an entry for each parameter in a register, in the order that
-       they occur in the parameter list.  Any intervening arguments on the
-       stack are ignored.  If the list overflows a long (max possible length
-       34 bits) then completely leave off all elements that don't fit.  */
-    /* Only emit this long if there was at least one parameter.  */
-    if (fixed_parms || float_parms)
-      fprintf (file, "\t.long %d\n", parm_info);
-
-    /* Offset from start of code to tb table.  */
-    fprintf (file, "\t.long LT..");
-    RS6000_OUTPUT_BASENAME (file, fname);
-    fprintf (file, "-.");
-    RS6000_OUTPUT_BASENAME (file, fname);
-    fprintf (file, "\n");
-
-    /* Interrupt handler mask.  */
-    /* Omit this long, since we never set the interrupt handler bit above.  */
-
-    /* Number of CTL (controlled storage) anchors.  */
-    /* Omit this long, since the has_ctl bit is never set above.  */
-
-    /* Displacement into stack of each CTL anchor.  */
-    /* Omit this list of longs, because there are no CTL anchors.  */
-
-    /* Length of function name.  */
-    fprintf (file, "\t.short %d\n", strlen (fname));
-
-    /* Function name.  */
-    assemble_string (fname, strlen (fname));
-
-    /* Register for alloca automatic storage; this is always reg 31.
-       Only emit this if the alloca bit was set above.  */
-    if (frame_pointer_needed)
-      fprintf (file, "\t.byte 31\n");
-  }
 }
 \f
 /* Output a TOC entry.  We derive the entry name from what is
@@ -1762,41 +3591,96 @@ output_toc (file, x, labelno)
 {
   char buf[256];
   char *name = buf;
+  char *real_name;
   rtx base = x;
   int offset = 0;
 
-  ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno);
+  if (TARGET_NO_TOC)
+    abort ();
+
+  /* if we're going to put a double constant in the TOC, make sure it's
+     aligned properly when strict alignment is on. */
+  if (GET_CODE (x) == CONST_DOUBLE
+      && STRICT_ALIGNMENT
+      && GET_MODE (x) == DFmode
+      && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC)) {
+    ASM_OUTPUT_ALIGN (file, 3);
+  }
+
+
+  if (TARGET_ELF && TARGET_MINIMAL_TOC)
+    {
+      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LC");
+      fprintf (file, "%d = .-", labelno);
+      ASM_OUTPUT_INTERNAL_LABEL_PREFIX (file, "LCTOC");
+      fputs ("1\n", file);
+    }
+  else
+    ASM_OUTPUT_INTERNAL_LABEL (file, "LC", labelno);
 
   /* Handle FP constants specially.  Note that if we have a minimal
      TOC, things we put here aren't actually in the TOC, so we can allow
      FP constants.  */
-  if (GET_CODE (x) == CONST_DOUBLE
-      && GET_MODE (x) == DFmode
-      && TARGET_FLOAT_FORMAT == HOST_FLOAT_FORMAT
-      && BITS_PER_WORD == HOST_BITS_PER_INT
+  if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode
       && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
     {
+      REAL_VALUE_TYPE rv;
+      long k[2];
+
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+      REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
       if (TARGET_MINIMAL_TOC)
-       fprintf (file, "\t.long %d\n\t.long %d\n",
-                CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x));
+       fprintf (file, "\t.long %ld\n\t.long %ld\n", k[0], k[1]);
       else
-       fprintf (file, "\t.tc FD_%x_%x[TC],%d,%d\n",
-                CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x),
-                CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x));
+       fprintf (file, "\t.tc FD_%lx_%lx[TC],%ld,%ld\n",
+                k[0], k[1], k[0], k[1]);
       return;
     }
   else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode
           && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
     {
-      rtx val = operand_subword (x, 0, 0, SFmode);
+      REAL_VALUE_TYPE rv;
+      long l;
 
-      if (val == 0 || GET_CODE (val) != CONST_INT)
-       abort ();
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+      REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+
+      if (TARGET_MINIMAL_TOC)
+       fprintf (file, "\t.long %d\n", l);
+      else
+       fprintf (file, "\t.tc FS_%x[TC],%d\n", l, l);
+      return;
+    }
+  else if (GET_MODE (x) == DImode
+          && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+          && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
+    {
+      HOST_WIDE_INT low;
+      HOST_WIDE_INT high;
+
+      if (GET_CODE (x) == CONST_DOUBLE)
+       {
+         low = CONST_DOUBLE_LOW (x);
+         high = CONST_DOUBLE_HIGH (x);
+       }
+      else
+#if HOST_BITS_PER_WIDE_INT == 32
+       {
+         low = INTVAL (x);
+         high = (low < 0) ? ~0 : 0;
+       }
+#else
+       {
+          low = INTVAL (x) & 0xffffffff;
+          high = (HOST_WIDE_INT) INTVAL (x) >> 32;
+       }
+#endif
 
       if (TARGET_MINIMAL_TOC)
-       fprintf (file, "\t.long %d\n", INTVAL (val));
+       fprintf (file, "\t.long %ld\n\t.long %ld\n", high, low);
       else
-       fprintf (file, "\t.tc FS_%x[TC],%d\n", INTVAL (val), INTVAL (val));
+       fprintf (file, "\t.tc ID_%lx_%lx[TC],%ld,%ld\n",
+                high, low, high, low);
       return;
     }
 
@@ -1816,21 +3700,21 @@ output_toc (file, x, labelno)
     abort ();
 
   if (TARGET_MINIMAL_TOC)
-    fprintf (file, "\t.long ");
+    fputs ("\t.long ", file);
   else
     {
-      fprintf (file, "\t.tc ");
-      RS6000_OUTPUT_BASENAME (file, name);
+      STRIP_NAME_ENCODING (real_name, name);
+      fprintf (file, "\t.tc %s", real_name);
 
       if (offset < 0)
        fprintf (file, ".N%d", - offset);
       else if (offset)
        fprintf (file, ".P%d", offset);
 
-      fprintf (file, "[TC],");
+      fputs ("[TC],", file);
     }
   output_addr_const (file, x);
-  fprintf (file, "\n");
+  putc ('\n', file);
 }
 \f
 /* Output an assembler pseudo-op to write an ASCII string of N characters
@@ -1968,11 +3852,29 @@ output_function_profiler (file, labelno)
   /* The last used parameter register.  */
   int last_parm_reg;
   int i, j;
+  char buf[100];
+
+  if (DEFAULT_ABI != ABI_AIX)
+    abort ();
 
   /* Set up a TOC entry for the profiler label.  */
   toc_section ();
-  fprintf (file, "LPC..%d:\n\t.tc\tLP..%d[TC],LP..%d\n",
-          labelno, labelno, labelno);
+  ASM_OUTPUT_INTERNAL_LABEL (file, "LPC", labelno);
+  ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
+  if (TARGET_MINIMAL_TOC)
+    {
+      fputs ("\t.long ", file);
+      assemble_name (file, buf);
+      putc ('\n', file);
+    }
+  else
+    {
+      fputs ("\t.tc\t", file);
+      assemble_name (file, buf);
+      fputs ("[TC],", file);
+      assemble_name (file, buf);
+      putc ('\n', file);
+    }
   text_section ();
 
   /* Figure out last used parameter register.  The proper thing to do is
@@ -1988,14 +3890,494 @@ output_function_profiler (file, labelno)
      it might be set up as the frame pointer.  */
 
   for (i = 3, j = 30; i <= last_parm_reg; i++, j--)
-    fprintf (file, "\tai %d,%d,0\n", j, i);
+    asm_fprintf (file, "\tmr %d,%d\n", j, i);
 
   /* Load location address into r3, and call mcount.  */
 
-  fprintf (file, "\tl 3,LPC..%d(2)\n\tbl .mcount\n", labelno);
+  ASM_GENERATE_INTERNAL_LABEL (buf, "LPC", labelno);
+  asm_fprintf (file, "\t{l|lwz} %s,", reg_names[3]);
+  assemble_name (file, buf);
+  asm_fprintf (file, "(%s)\n\tbl .mcount\n", reg_names[2]);
 
   /* Restore parameter registers.  */
 
   for (i = 3, j = 30; i <= last_parm_reg; i++, j--)
-    fprintf (file, "\tai %d,%d,0\n", i, j);
+    asm_fprintf (file, "\tmr %d,%d\n", i, j);
+}
+
+/* Adjust the cost of a scheduling dependency.  Return the new cost of
+   a dependency LINK or INSN on DEP_INSN.  COST is the current cost.  */
+
+int
+rs6000_adjust_cost (insn, link, dep_insn, cost)
+     rtx insn;
+     rtx link;
+     rtx dep_insn;
+     int cost;
+{
+  if (! recog_memoized (insn))
+    return 0;
+
+  if (REG_NOTE_KIND (link) != 0)
+    return 0;
+
+  if (REG_NOTE_KIND (link) == 0)
+    {
+      /* Data dependency; DEP_INSN writes a register that INSN reads some
+        cycles later.  */
+
+      /* Tell the first scheduling pass about the latency between a mtctr
+        and bctr (and mtlr and br/blr).  The first scheduling pass will not
+        know about this latency since the mtctr instruction, which has the
+        latency associated to it, will be generated by reload.  */
+      if (get_attr_type (insn) == TYPE_JMPREG)
+       return TARGET_POWER ? 5 : 4;
+
+      /* Fall out to return default cost.  */
+    }
+
+  return cost;
+}
+
+/* Return how many instructions the machine can issue per cycle */
+int get_issue_rate()
+{
+  switch (rs6000_cpu_attr) {
+  case CPU_RIOS1:
+    return 3;       /* ? */
+  case CPU_RIOS2:
+    return 4;
+  case CPU_PPC601:
+    return 3;       /* ? */
+  case CPU_PPC602:
+    return 1; 
+  case CPU_PPC603:
+    return 2; 
+  case CPU_PPC604:
+    return 4;
+  case CPU_PPC620:
+    return 4;
+  default:
+    return 1;
+  }
+}
+
+\f
+/* Output insns to flush the {data|instruction} caches after building a
+   trampoline. */
+
+static void
+rs6000_sync_trampoline (addr)
+     rtx addr;
+{
+  enum machine_mode pmode = Pmode;
+  rtx reg = gen_reg_rtx (pmode);
+  rtx mem2;
+  rtx mem1;
+  int size = rs6000_trampoline_size ();
+  rtx (*sub_fcn) PROTO ((rtx, rtx, rtx));
+  rtx (*cmp_fcn) PROTO ((rtx, rtx));
+  rtx label;
+
+  if (TARGET_32BIT)
+    {
+      sub_fcn = gen_subsi3;
+      cmp_fcn = gen_cmpsi;
+    }
+  else
+    {
+      sub_fcn = gen_subdi3;
+      cmp_fcn = gen_cmpdi;
+    }
+
+  addr = force_reg (pmode, addr);
+  mem2 = gen_rtx (MEM, pmode, gen_rtx (PLUS, pmode, addr, reg));
+  mem1 = gen_rtx (MEM, pmode, addr);
+
+  /* Issue a loop of dcbst's to flush the data cache */
+  emit_move_insn (reg, GEN_INT (size-4));
+  label = gen_label_rtx ();
+  emit_label (label);
+  emit_insn (gen_dcbst (mem2, addr, reg));
+  emit_insn ((*sub_fcn) (reg, reg, GEN_INT (4)));
+  emit_insn ((*cmp_fcn) (reg, const0_rtx));
+  emit_jump_insn (gen_bgt (label));
+
+  /* Issue a sync after the dcbst's to let things settle down */
+  emit_insn (gen_sync (mem1));
+
+  /* Issue a loop of icbi's to flush the instruction cache */
+  emit_move_insn (reg, GEN_INT (size-4));
+  label = gen_label_rtx ();
+  emit_label (label);
+  emit_insn (gen_icbi (mem2, addr, reg));
+  emit_insn ((*sub_fcn) (reg, reg, GEN_INT (4)));
+  emit_insn ((*cmp_fcn) (reg, const0_rtx));
+  emit_jump_insn (gen_bgt (label));
+
+  /* Issue a sync after the icbi's to let things settle down */
+  emit_insn (gen_sync (mem1));
+
+  /* Finally issue an isync to synchronize the icache */
+  emit_insn (gen_isync (mem1));
+}
+
+\f
+/* Output assembler code for a block containing the constant parts
+   of a trampoline, leaving space for the variable parts.
+
+   The trampoline should set the static chain pointer to value placed
+   into the trampoline and should branch to the specified routine.  */
+
+void
+rs6000_trampoline_template (file)
+     FILE *file;
+{
+  char *sc = reg_names[STATIC_CHAIN_REGNUM];
+  char *r0 = reg_names[0];
+  char *r2 = reg_names[2];
+
+  switch (DEFAULT_ABI)
+    {
+    default:
+      abort ();
+
+    /* Under AIX, this is not code at all, but merely a data area,
+       since that is the way all functions are called.  The first word is
+       the address of the function, the second word is the TOC pointer (r2),
+       and the third word is the static chain value.  */
+    case ABI_AIX:
+      fprintf (file, "\t.long %s\n", (TARGET_32BIT) ? "0,0,0" : "0,0,0,0,0,0");
+      break;
+
+
+    /* V.4/eabi function pointers are just a single pointer, so we need to
+       do the full gory code to load up the static chain.  */
+    case ABI_V4:
+    case ABI_AIX_NODESC:
+      if (STATIC_CHAIN_REGNUM == 0 || !TARGET_NEW_MNEMONICS)
+       abort ();
+
+      if (TARGET_32BIT)
+       {
+         fprintf (file, "\tmflr %s\n", r0);            /* offset  0 */
+         fprintf (file, "\tbl .LTRAMP1\n");            /* offset  4 */
+         fprintf (file, "\t.long 0,0\n");              /* offset  8 */
+         fprintf (file, ".LTRAMP1:\n");
+         fprintf (file, "\tmflr %s\n", sc);            /* offset 20 */
+         fprintf (file, "\tmtlr %s\n", r0);            /* offset 24 */
+         fprintf (file, "\tlwz %s,0(%s)\n", r0, sc);   /* offset 28 */
+         fprintf (file, "\tlwz %s,4(%s)\n", sc, sc);   /* offset 32 */
+         fprintf (file, "\tmtctr %s\n", r0);           /* offset 36 */
+         fprintf (file, "\tbctr\n");                   /* offset 40 */
+       }
+      else
+       {
+         fprintf (file, "\tmflr %s\n", r0);            /* offset  0 */
+         fprintf (file, "\tbl .LTRAMP1\n");            /* offset  4 */
+         fprintf (file, "\t.long 0,0,0,0\n");          /* offset  8 */
+         fprintf (file, ".LTRAMP1:\n");
+         fprintf (file, "\tmflr %s\n", sc);            /* offset 28 */
+         fprintf (file, "\tmtlr %s\n", r0);            /* offset 32 */
+         fprintf (file, "\tld %s,0(%s)\n", r0, sc);    /* offset 36 */
+         fprintf (file, "\tld %s,8(%s)\n", sc, sc);    /* offset 40 */
+         fprintf (file, "\tmtctr %s\n", r0);           /* offset 44 */
+         fprintf (file, "\tbctr\n");                   /* offset 48 */
+       }
+      break;
+
+  /* NT function pointers point to a two word area (real address, TOC)
+     which unfortunately does not include a static chain field.  So we
+     use the function field to point to ..LTRAMP1 and the toc field
+     to point to the whole table.  */
+    case ABI_NT:
+      if (STATIC_CHAIN_REGNUM == 0
+         || STATIC_CHAIN_REGNUM == 2
+         || TARGET_64BIT
+         || !TARGET_NEW_MNEMONICS)
+       abort ();
+
+      fprintf (file, "\t.ualong 0\n");                 /* offset  0 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset  4 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset  8 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset 12 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset 16 */
+      fprintf (file, "..LTRAMP1:\n");                  /* offset 20 */
+      fprintf (file, "\tlwz %s,8(%s)\n", r0, r2);      /* offset 24 */
+      fprintf (file, "\tlwz %s,12(%s)\n", sc, r2);     /* offset 28 */
+      fprintf (file, "\tmtctr %s\n", r0);              /* offset 32 */
+      fprintf (file, "\tlwz %s,16(%s)\n", r2, r2);     /* offset 36 */
+      fprintf (file, "\tbctr\n");                      /* offset 40 */
+      break;
+    }
+
+  return;
+}
+
+/* Length in units of the trampoline for entering a nested function.  */
+
+int
+rs6000_trampoline_size ()
+{
+  int ret = 0;
+
+  switch (DEFAULT_ABI)
+    {
+    default:
+      abort ();
+
+    case ABI_AIX:
+      ret = (TARGET_32BIT) ? 12 : 24;
+      break;
+
+    case ABI_V4:
+    case ABI_AIX_NODESC:
+      ret = (TARGET_32BIT) ? 40 : 48;
+      break;
+
+    case ABI_NT:
+      ret = 20;
+      break;
+    }
+
+  return ret;
+}
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+   FNADDR is an RTX for the address of the function's pure code.
+   CXT is an RTX for the static chain value for the function.  */
+
+void
+rs6000_initialize_trampoline (addr, fnaddr, cxt)
+     rtx addr;
+     rtx fnaddr;
+     rtx cxt;
+{
+  rtx reg, reg2, reg3;
+  enum machine_mode pmode = Pmode;
+
+  switch (DEFAULT_ABI)
+    {
+    default:
+      abort ();
+
+#define MEM_PLUS(addr,offset) gen_rtx (MEM, pmode, memory_address (pmode, plus_constant (addr, offset)))
+
+    /* Under AIX, just build the 3 word function descriptor */
+    case ABI_AIX:
+      emit_move_insn (gen_rtx (MEM, pmode, memory_address (pmode, addr)),
+                     gen_rtx (MEM, pmode, memory_address (pmode, fnaddr)));
+
+      emit_move_insn (MEM_PLUS (addr, 4), MEM_PLUS (fnaddr, 4));
+      emit_move_insn (MEM_PLUS (addr, 8), force_reg (pmode, cxt));
+      break;
+
+    /* Under V.4/eabi, update the two words after the bl to have the real
+       function address and the static chain.  */
+    case ABI_V4:
+    case ABI_AIX_NODESC:
+      reg = gen_reg_rtx (pmode);
+
+      emit_move_insn (reg, fnaddr);
+      emit_move_insn (MEM_PLUS (addr, 8), reg);
+      emit_move_insn (MEM_PLUS (addr, (TARGET_64BIT ? 16 : 12)), cxt);
+
+      rs6000_sync_trampoline (addr);
+      break;
+
+    /* Under NT, update the first word to point to the ..LTRAMP1 header,
+       second word will point to the whole trampoline, third-fifth words
+       will then have the real address, static chain, and toc value.  */
+    case ABI_NT:
+      addr = force_reg (pmode, addr);
+      reg = gen_reg_rtx (pmode);
+      reg2 = gen_reg_rtx (pmode);
+      emit_move_insn (reg, gen_rtx (SYMBOL_REF, pmode, "..LTRAMP1"));
+      emit_move_insn (reg2, fnaddr);
+      reg3 = force_reg (pmode, cxt);
+      emit_move_insn (MEM_PLUS (addr, 4), addr);
+      emit_move_insn (gen_rtx (MEM, pmode, addr), reg);
+      emit_move_insn (MEM_PLUS (addr, 8), reg2);
+      emit_move_insn (MEM_PLUS (addr, 12), reg3);
+      emit_move_insn (MEM_PLUS (addr, 16), gen_rtx (REG, pmode, 2));
+      break;
+    }
+
+  return;
+}
+
+\f
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+   with arguments ARGS is a valid machine specific attribute for DECL.
+   The attributes in ATTRIBUTES have previously been assigned to DECL.  */
+
+int
+rs6000_valid_decl_attribute_p (decl, attributes, identifier, args)
+     tree decl;
+     tree attributes;
+     tree identifier;
+     tree args;
+{
+  return 0;
+}
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+   with arguments ARGS is a valid machine specific attribute for TYPE.
+   The attributes in ATTRIBUTES have previously been assigned to TYPE.  */
+
+int
+rs6000_valid_type_attribute_p (type, attributes, identifier, args)
+     tree type;
+     tree attributes;
+     tree identifier;
+     tree args;
+{
+  if (TREE_CODE (type) != FUNCTION_TYPE
+      && TREE_CODE (type) != FIELD_DECL
+      && TREE_CODE (type) != TYPE_DECL)
+    return 0;
+
+  if (DEFAULT_ABI == ABI_NT)
+    {
+      /* Stdcall attribute says callee is responsible for popping arguments
+        if they are not variable.  */
+      if (is_attribute_p ("stdcall", identifier))
+       return (args == NULL_TREE);
+
+      /* Cdecl attribute says the callee is a normal C declaration */
+      if (is_attribute_p ("cdecl", identifier))
+       return (args == NULL_TREE);
+
+      /* Dllimport attribute says says the caller is to call the function
+        indirectly through a __imp_<name> pointer.  */
+      if (is_attribute_p ("dllimport", identifier))
+       return (args == NULL_TREE);
+
+      /* Dllexport attribute says says the callee is to create a __imp_<name>
+        pointer.  */
+      if (is_attribute_p ("dllexport", identifier))
+       return (args == NULL_TREE);
+    }
+
+  return 0;
+}
+
+/* If defined, a C expression whose value is zero if the attributes on
+   TYPE1 and TYPE2 are incompatible, one if they are compatible, and
+   two if they are nearly compatible (which causes a warning to be
+   generated).  */
+
+int
+rs6000_comp_type_attributes (type1, type2)
+     tree type1;
+     tree type2;
+{
+  return 1;
+}
+
+/* If defined, a C statement that assigns default attributes to newly
+   defined TYPE.  */
+
+void
+rs6000_set_default_type_attributes (type)
+     tree type;
+{
+}
+
+/* Return a dll import reference corresponding to to a call's SYMBOL_REF */
+struct rtx_def *
+rs6000_dll_import_ref (call_ref)
+     rtx call_ref;
+{
+  char *call_name;
+  int len;
+  char *p;
+  rtx reg1, reg2;
+  tree node;
+
+  if (GET_CODE (call_ref) != SYMBOL_REF)
+    abort ();
+
+  call_name = XSTR (call_ref, 0);
+  len = sizeof ("__imp_") + strlen (call_name);
+  p = alloca (len);
+  reg2 = gen_reg_rtx (Pmode);
+
+  strcpy (p, "__imp_");
+  strcat (p, call_name);
+  node = get_identifier (p);
+
+  reg1 = force_reg (Pmode, gen_rtx (SYMBOL_REF, VOIDmode, IDENTIFIER_POINTER (node)));
+  emit_move_insn (reg2, gen_rtx (MEM, Pmode, reg1));
+
+  return reg2;
+}
+
+\f
+/* A C statement or statements to switch to the appropriate section
+   for output of RTX in mode MODE.  You can assume that RTX is some
+   kind of constant in RTL.  The argument MODE is redundant except in
+   the case of a `const_int' rtx.  Select the section by calling
+   `text_section' or one of the alternatives for other sections.
+
+   Do not define this macro if you put all constants in the read-only
+   data section.  */
+
+#ifdef USING_SVR4_H
+
+void
+rs6000_select_rtx_section (mode, x)
+     enum machine_mode mode;
+     rtx x;
+{
+  if (ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (x))
+    toc_section ();
+  else
+    const_section ();
+}
+
+/* A C statement or statements to switch to the appropriate
+   section for output of DECL.  DECL is either a `VAR_DECL' node
+   or a constant of some sort.  RELOC indicates whether forming
+   the initial value of DECL requires link-time relocations.  */
+
+void
+rs6000_select_section (decl, reloc)
+     tree decl;
+     int reloc;
+{
+  int size = int_size_in_bytes (TREE_TYPE (decl));
+
+  if (TREE_CODE (decl) == STRING_CST)
+    {
+      if (! flag_writable_strings)
+       const_section ();
+      else
+       data_section ();
+    }
+  else if (TREE_CODE (decl) == VAR_DECL)
+    {
+      if ((flag_pic && reloc)
+         || !TREE_READONLY (decl)
+         || TREE_SIDE_EFFECTS (decl)
+         || !DECL_INITIAL (decl)
+         || (DECL_INITIAL (decl) != error_mark_node
+             && !TREE_CONSTANT (DECL_INITIAL (decl))))
+       {
+         if (TARGET_SDATA && (size > 0) && (size <= g_switch_value))
+           sdata_section ();
+         else
+           data_section ();
+       }
+      else
+       {
+         if (TARGET_SDATA && (size > 0) && (size <= g_switch_value))
+           sdata2_section ();
+         else
+           const_section ();
+       }
+    }
+  else
+    const_section ();
 }
+#endif /* USING_SVR4_H */