OSDN Git Service

* 1750a.h (datalbl, jmplbl): Declare array size explicitly.
[pf3gnuchains/gcc-fork.git] / gcc / config / ns32k / ns32k.c
index 1c759ae..7c3afe2 100644 (file)
@@ -1,5 +1,6 @@
 /* Subroutines for assembler code output on the NS32000.
-   Copyright (C) 1988, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+   Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -18,102 +19,505 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
-/* Some output-actions in ns32k.md need these.  */
 #include "config.h"
-#include <stdio.h>
+#include "system.h"
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
 #include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
-#include "insn-flags.h"
 #include "output.h"
 #include "insn-attr.h"
+#include "tree.h"
+#include "function.h"
+#include "expr.h"
+#include "flags.h"
+#include "recog.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+#include "toplev.h"
 
 #ifdef OSF_OS
 int ns32k_num_files = 0;
 #endif
 
-void
-trace (s, s1, s2)
-     char *s, *s1, *s2;
+/* This duplicates reg_class_contents in reg_class.c, but maybe that isn't
+   initialized in time. Also this is more convenient as an array of ints.
+   We know that HARD_REG_SET fits in an unsigned int */
+
+unsigned int ns32k_reg_class_contents[N_REG_CLASSES][1] = REG_CLASS_CONTENTS;
+
+enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
 {
-  fprintf (stderr, s, s1, s2);
-}
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  FLOAT_REG0, LONG_FLOAT_REG0, FLOAT_REGS, FLOAT_REGS,
+  FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FRAME_POINTER_REG, STACK_POINTER_REG
+};
+
+static const char *const ns32k_out_reg_names[] = OUTPUT_REGISTER_NAMES;
+
+static rtx gen_indexed_expr PARAMS ((rtx, rtx, rtx));
+static const char *singlemove_string PARAMS ((rtx *));
+static void move_tail PARAMS ((rtx[], int, int));
+static tree ns32k_handle_fntype_attribute PARAMS ((tree *, tree, tree, int, bool *));
+const struct attribute_spec ns32k_attribute_table[];
+static void ns32k_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
+static void ns32k_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+\f
+/* Initialize the GCC target structure.  */
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE ns32k_attribute_table
 
-/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ 
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
 
-int
-hard_regno_mode_ok (regno, mode)
-     int regno;
-     enum machine_mode mode;
+#ifdef ENCORE_ASM
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.double\t"
+#endif
+
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE ns32k_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE ns32k_output_function_epilogue
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+/* Generate the assembly code for function entry.  FILE is a stdio
+   stream to output the code to.  SIZE is an int: how many units of
+   temporary storage to allocate.
+
+   Refer to the array `regs_ever_live' to determine which registers to
+   save; `regs_ever_live[I]' is nonzero if register number I is ever
+   used in the function.  This function is responsible for knowing
+   which registers should not be saved even if used.  */
+
+/*
+ * The function prologue for the ns32k is fairly simple.
+ * If a frame pointer is needed (decided in reload.c ?) then
+ * we need assembler of the form
+ *
+ *  # Save the oldframe pointer, set the new frame pointer, make space
+ *  # on the stack and save any general purpose registers necessary
+ *
+ *  enter [<general purpose regs to save>], <local stack space>
+ *
+ *  movf  fn, tos    # Save any floating point registers necessary
+ *  .
+ *  .
+ *
+ * If a frame pointer is not needed we need assembler of the form
+ *
+ *  # Make space on the stack
+ *
+ *  adjspd <local stack space + 4>
+ *
+ *  # Save any general purpose registers necessary
+ *
+ *  save [<general purpose regs to save>]
+ *
+ *  movf  fn, tos    # Save any floating point registers necessary
+ *  .
+ *  .
+ */
+
+#if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
+
+#if defined(IMMEDIATE_PREFIX) && IMMEDIATE_PREFIX
+#define ADJSP(FILE, N) \
+        fprintf (FILE, "\tadjspd %c%d\n", IMMEDIATE_PREFIX, (N))
+#else
+#define ADJSP(FILE, N) \
+        fprintf (FILE, "\tadjspd %d\n", (N))
+#endif
+
+static void
+ns32k_output_function_prologue (file, size)
+     FILE *file;
+     HOST_WIDE_INT size;
 {
-  switch (mode)
-    {
-    case QImode:
-    case HImode:
-    case PSImode:
-    case SImode:
-    case PDImode:
-    case VOIDmode:
-    case BLKmode:
-      if (regno < 8 || regno == 16 || regno == 17)
-       return 1;
+  register int regno, g_regs_used = 0;
+  int used_regs_buf[8], *bufp = used_regs_buf;
+  int used_fregs_buf[17], *fbufp = used_fregs_buf;
+
+  for (regno = R0_REGNUM; regno < F0_REGNUM; regno++)
+    if (regs_ever_live[regno]
+       && ! call_used_regs[regno])
+      {
+        *bufp++ = regno; g_regs_used++;
+      }
+  *bufp = -1;
+
+  for (; regno < FRAME_POINTER_REGNUM; regno++)
+    if (regs_ever_live[regno] && !call_used_regs[regno])
+      {
+        *fbufp++ = regno;
+      }
+  *fbufp = -1;
+
+  bufp = used_regs_buf;
+  if (frame_pointer_needed)
+    fprintf (file, "\tenter [");
+  else
+    {
+      if (size)
+        ADJSP (file, size + 4);
+      if (g_regs_used && g_regs_used > 4)
+        fprintf (file, "\tsave [");
       else
-       return 0;
+       {
+         while (*bufp >= 0)
+            fprintf (file, "\tmovd r%d,tos\n", *bufp++);
+         g_regs_used = 0;
+       }
+    }
 
-    case DImode:
-      if (regno < 8 && (regno & 1) == 0)
-       return 1;
+  while (*bufp >= 0)
+    {
+      fprintf (file, "r%d", *bufp++);
+      if (*bufp >= 0)
+       fputc (',', file);
+    }
+
+  if (frame_pointer_needed)
+    fprintf (file, "],%d\n", size);
+  else if (g_regs_used)
+    fprintf (file, "]\n");
+
+  fbufp = used_fregs_buf;
+  while (*fbufp >= 0)
+    {
+      if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
+       fprintf (file, "\tmovf %s,tos\n", ns32k_out_reg_names[*fbufp++]);
       else
-       return 0;
+       {
+         fprintf (file, "\tmovl %s,tos\n",
+                  ns32k_out_reg_names[fbufp[0]]);
+         fbufp += 2;
+       }
+    }
 
-    case SFmode:
-    case SCmode:
-      if (TARGET_32081)
+  if (flag_pic && current_function_uses_pic_offset_table)
+    {
+      fprintf (file, "\tsprd sb,tos\n");
+      if (TARGET_REGPARM)
        {
-         if (regno < 16)
-           return 1;
-         else
-           return 0;
+         fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),tos\n");
+         fprintf (file, "\tlprd sb,tos\n");
        }
       else
        {
-         if (regno < 8)
-           return 1;
-         else 
-           return 0;
+         fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),r0\n");
+         fprintf (file, "\tlprd sb,r0\n");
        }
+    }
+}
 
-    case DFmode:
-    case DCmode:
-      if ((regno & 1) == 0)
-       {       
-         if (TARGET_32081)
-           {
-             if (regno < 16)
-               return 1;
-             else
-               return 0;
-           }
-         else
-           {
-             if (regno < 8)
-               return 1;
-             else
-               return 0;
-           }
+#else /* MERLIN_TARGET || UTEK_ASM  */
+
+/* This differs from the standard one above in printing a bitmask
+   rather than a register list in the enter or save instruction.  */
+
+static void
+ns32k_output_function_prologue (file, size)
+     FILE *file;
+     HOST_WIDE_INT size;
+{
+  register int regno, g_regs_used = 0;
+  int used_regs_buf[8], *bufp = used_regs_buf;
+  int used_fregs_buf[8], *fbufp = used_fregs_buf;
+
+  for (regno = 0; regno < 8; regno++)
+    if (regs_ever_live[regno]
+       && ! call_used_regs[regno])
+      {
+       *bufp++ = regno; g_regs_used++;
+      }
+  *bufp = -1;
+
+  for (; regno < 16; regno++)
+    if (regs_ever_live[regno] && !call_used_regs[regno]) {
+      *fbufp++ = regno;
+    }
+  *fbufp = -1;
+
+  bufp = used_regs_buf;
+  if (frame_pointer_needed)
+    fprintf (file, "\tenter ");
+  else if (g_regs_used)
+    fprintf (file, "\tsave ");
+
+  if (frame_pointer_needed || g_regs_used)
+    {
+      char mask = 0;
+      while (*bufp >= 0)
+       mask |= 1 << *bufp++;
+      fprintf (file, "$0x%x", (int) mask & 0xff);
+    }
+
+  if (frame_pointer_needed)
+#ifdef UTEK_ASM
+    fprintf (file, ",$%d\n", size);
+#else
+    fprintf (file, ",%d\n", size);
+#endif
+  else if (g_regs_used)
+    fprintf (file, "\n");
+
+  fbufp = used_fregs_buf;
+  while (*fbufp >= 0)
+    {
+      if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
+       fprintf (file, "\tmovf f%d,tos\n", *fbufp++ - 8);
+      else
+       {
+         fprintf (file, "\tmovl f%d,tos\n", fbufp[0] - 8);
+         fbufp += 2;
        }
+    }
+}
+
+#endif /* MERLIN_TARGET || UTEK_ASM  */
+
+/* This function generates the assembly code for function exit,
+   on machines that need it.
+
+   The function epilogue should not depend on the current stack pointer,
+   if EXIT_IGNORE_STACK is nonzero.  That doesn't apply here.
+
+   If a frame pointer is needed (decided in reload.c ?) then
+   we need assembler of the form
+
+    movf  tos, fn      # Restore any saved floating point registers
+    .
+    .
+
+    # Restore any saved general purpose registers, restore the stack
+    # pointer from the frame pointer, restore the old frame pointer.
+    exit [<general purpose regs to save>]
+
+   If a frame pointer is not needed we need assembler of the form
+    # Restore any general purpose registers saved
+
+    movf  tos, fn      # Restore any saved floating point registers
+    .
+    .
+    .
+    restore [<general purpose regs to save>]
+
+    # reclaim space allocated on stack
+
+    adjspd <-(local stack space + 4)> */
+
+#if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
+
+static void
+ns32k_output_function_epilogue (file, size)
+     FILE *file;
+     HOST_WIDE_INT size;
+{
+  register int regno, g_regs_used = 0, f_regs_used = 0;
+  int used_regs_buf[8], *bufp = used_regs_buf;
+  int used_fregs_buf[17], *fbufp = used_fregs_buf;
+
+  if (flag_pic && current_function_uses_pic_offset_table)
+    fprintf (file, "\tlprd sb,tos\n");
+
+  *fbufp++ = -2;
+  for (regno = F0_REGNUM; regno < FRAME_POINTER_REGNUM; regno++)
+    if (regs_ever_live[regno] && !call_used_regs[regno])
+      {
+       *fbufp++ = regno; f_regs_used++;
+      }
+  fbufp--;
+
+  for (regno = 0; regno < F0_REGNUM; regno++)
+    if (regs_ever_live[regno]
+       && ! call_used_regs[regno])
+      {
+        *bufp++ = regno; g_regs_used++;
+      }
+
+  while (fbufp > used_fregs_buf)
+    {
+      if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
+       {
+         fprintf (file, "\tmovl tos,%s\n",
+                  ns32k_out_reg_names[fbufp[-1]]);
+         fbufp -= 2;
+       }
+      else fprintf (file, "\tmovf tos,%s\n", ns32k_out_reg_names[*fbufp--]);
+    }
+
+  if (frame_pointer_needed)
+    fprintf (file, "\texit [");
+  else
+    {
+      if (g_regs_used && g_regs_used > 4)
+        fprintf (file, "\trestore [");
       else
-       return 0;
+        {
+         while (bufp > used_regs_buf)
+            fprintf (file, "\tmovd tos,r%d\n", *--bufp);
+         g_regs_used = 0;
+        }
+    }
+
+  while (bufp > used_regs_buf)
+    {
+      fprintf (file, "r%d", *--bufp);
+      if (bufp > used_regs_buf)
+       fputc (',', file);
+    }
+
+  if (g_regs_used || frame_pointer_needed)
+    fprintf (file, "]\n");
+
+  if (size && !frame_pointer_needed)
+    ADJSP (file, -(size + 4));
+
+  if (current_function_pops_args)
+    fprintf (file, "\tret %d\n", current_function_pops_args);
+  else
+    fprintf (file, "\tret 0\n");
+}
+
+#else /* MERLIN_TARGET || UTEK_ASM  */
+
+/* This differs from the standard one above in printing a bitmask
+   rather than a register list in the exit or restore instruction.  */
+
+static void
+ns32k_output_function_epilogue (file, size)
+     FILE *file;
+     HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+{
+  register int regno, g_regs_used = 0, f_regs_used = 0;
+  int used_regs_buf[8], *bufp = used_regs_buf;
+  int used_fregs_buf[8], *fbufp = used_fregs_buf;
+
+  *fbufp++ = -2;
+  for (regno = 8; regno < 16; regno++)
+    if (regs_ever_live[regno] && !call_used_regs[regno]) {
+      *fbufp++ = regno; f_regs_used++;
     }
+  fbufp--;
 
-  /* Used to abort here, but simply saying "no" handles TImode
-     much better.  */
+  for (regno = 0; regno < 8; regno++)
+    if (regs_ever_live[regno]
+       && ! call_used_regs[regno])
+      {
+       *bufp++ = regno; g_regs_used++;
+      }
+
+  while (fbufp > used_fregs_buf)
+    {
+      if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
+       {
+         fprintf (file, "\tmovl tos,f%d\n", fbufp[-1] - 8);
+         fbufp -= 2;
+       }
+      else fprintf (file, "\tmovf tos,f%d\n", *fbufp-- - 8);
+    }
+
+  if (frame_pointer_needed)
+    fprintf (file, "\texit ");
+  else if (g_regs_used)
+    fprintf (file, "\trestore ");
+
+  if (g_regs_used || frame_pointer_needed)
+    {
+      char mask = 0;
+
+      while (bufp > used_regs_buf)
+       {
+         /* Utek assembler takes care of reversing this */
+         mask |= 1 << *--bufp;
+       }
+      fprintf (file, "$0x%x\n", (int) mask & 0xff);
+    }
+
+#ifdef UTEK_ASM
+  if (current_function_pops_args)
+    fprintf (file, "\tret $%d\n", current_function_pops_args);
+  else
+    fprintf (file, "\tret $0\n");
+#else
+  if (current_function_pops_args)
+    fprintf (file, "\tret %d\n", current_function_pops_args);
+  else
+    fprintf (file, "\tret 0\n");
+#endif
+}
+
+#endif /* MERLIN_TARGET || UTEK_ASM  */
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ 
+int
+hard_regno_mode_ok (regno, mode)
+     int regno;
+     enum machine_mode mode;
+{
+  int size = GET_MODE_UNIT_SIZE (mode);
+
+  if (FLOAT_MODE_P (mode))
+    {
+      if (size == UNITS_PER_WORD && regno < L1_REGNUM)
+       return 1;
+      if (size == UNITS_PER_WORD * 2
+         && (((regno & 1) == 0 && regno < FRAME_POINTER_REGNUM)))
+       return 1;
+      return 0;
+    }
+  if (size == UNITS_PER_WORD * 2
+      && (regno & 1) == 0 && regno < F0_REGNUM)
+    return 1;
+  if (size <= UNITS_PER_WORD
+      && (regno < F0_REGNUM || regno == FRAME_POINTER_REGNUM
+         || regno == STACK_POINTER_REGNUM))
+    return 1;
   return 0;
 }
 
+int register_move_cost (CLASS1, CLASS2)
+     enum reg_class CLASS1;
+     enum reg_class CLASS2;
+{
+  if (CLASS1 == NO_REGS || CLASS2 == NO_REGS)
+    return 2;
+  if ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
+   || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)))
+    return 8;
+  if (((CLASS1) == STACK_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
+      || ((CLASS2) == STACK_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
+    return 6;
+  if (((CLASS1) == FRAME_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
+      || ((CLASS2) == FRAME_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
+    return 6;
+  return 2;
+}
+
+#if 0
+/* We made the insn definitions copy from floating point to general
+  registers via the stack. */
+int secondary_memory_needed (CLASS1, CLASS2, M)
+     enum reg_class CLASS1;
+     enum reg_class CLASS2;
+     enum machine_mode M;
+{
+  int ret = ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
+   || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)));
+  return ret;
+}
+#endif
+    
+
 /* ADDRESS_COST calls this.  This function is not optimal
    for the 32032 & 32332, but it probably is better than
    the default. */
@@ -124,30 +528,38 @@ calc_address_cost (operand)
 {
   int i;
   int cost = 0;
-  
   if (GET_CODE (operand) == MEM)
     cost += 3;
   if (GET_CODE (operand) == MULT)
     cost += 2;
-#if 0
-  if (GET_CODE (operand) == REG)
-    cost += 1;                 /* not really, but the documentation
-                                  says different amount of registers
-                                  shouldn't return the same costs */
-#endif
   switch (GET_CODE (operand))
     {
     case REG:
-    case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case SYMBOL_REF:
-    case LABEL_REF:
+      cost += 1;
+      break;
     case POST_DEC:
     case PRE_DEC:
       break;
-    case MULT:
+    case CONST_INT:
+      if (INTVAL (operand) <= 7 && INTVAL (operand) >= -8)
+       break;
+      if (INTVAL (operand) < 0x2000 && INTVAL (operand) >= -0x2000)
+       {
+         cost +=1;
+         break;
+       }
+    case CONST:
+    case LABEL_REF:
+    case SYMBOL_REF:
+      cost +=3;
+      break;
+    case CONST_DOUBLE:
+      cost += 5;
+      break;
     case MEM:
+      cost += calc_address_cost (XEXP (operand, 0));
+      break;
+    case MULT:
     case PLUS:
       for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++)
        {
@@ -166,7 +578,7 @@ calc_address_cost (operand)
 enum reg_class
 secondary_reload_class (class, mode, in)
      enum reg_class class;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
      rtx in;
 {
   int regno = true_regnum (in);
@@ -174,30 +586,19 @@ secondary_reload_class (class, mode, in)
   if (regno >= FIRST_PSEUDO_REGISTER)
     regno = -1;
 
-  /* We can place anything into GENERAL_REGS and can put GENERAL_REGS
-     into anything.  */
-  if (class == GENERAL_REGS || (regno >= 0 && regno < 8))
-    return NO_REGS;
-
-  /* Constants, memory, and FP registers can go into FP registers.  */
-  if ((regno == -1 || (regno >= 8 && regno < 16)) && (class == FLOAT_REGS))
-    return NO_REGS;
-
-#if 0 /* This isn't strictly true (can't move fp to sp or vice versa),
-        so it's cleaner to use PREFERRED_RELOAD_CLASS
-        to make the right things happen.  */
-  if (regno >= 16 && class == GEN_AND_MEM_REGS)
+  if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM)
+      || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM))
+    return GENERAL_REGS;
+  else
     return NO_REGS;
-#endif
-
-  /* Otherwise, we need GENERAL_REGS. */
-  return GENERAL_REGS;
 }
+
 /* Generate the rtx that comes from an address expression in the md file */
 /* The expression to be build is BASE[INDEX:SCALE].  To recognize this,
    scale must be converted from an exponent (from ASHIFT) to a
    multiplier (for MULT). */
-rtx
+
+static rtx
 gen_indexed_expr (base, index, scale)
      rtx base, index, scale;
 {
@@ -206,26 +607,13 @@ gen_indexed_expr (base, index, scale)
   /* This generates an invalid addressing mode, if BASE is
      fp or sp.  This is handled by PRINT_OPERAND_ADDRESS.  */
   if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT)
-    base = gen_rtx (MEM, SImode, base);
-  addr = gen_rtx (MULT, SImode, index,
-                 GEN_INT (1 << INTVAL (scale)));
-  addr = gen_rtx (PLUS, SImode, base, addr);
+    base = gen_rtx_MEM (SImode, base);
+  addr = gen_rtx_MULT (SImode, index,
+                      GEN_INT (1 << INTVAL (scale)));
+  addr = gen_rtx_PLUS (SImode, base, addr);
   return addr;
 }
 
-/* Return 1 if OP is a valid operand of mode MODE.  This
-   predicate rejects operands which do not have a mode
-   (such as CONST_INT which are VOIDmode).  */
-int
-reg_or_mem_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
-{
-  return (GET_MODE (op) == mode
-         && (GET_CODE (op) == REG
-             || GET_CODE (op) == SUBREG
-             || GET_CODE (op) == MEM));
-}
 \f
 /* Split one or more DImode RTL references into pairs of SImode
    references.  The RTL can be REG, offsettable MEM, integer constant, or
@@ -243,8 +631,8 @@ split_di (operands, num, lo_half, hi_half)
     {
       if (GET_CODE (operands[num]) == REG)
        {
-         lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]));
-         hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1);
+         lo_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]));
+         hi_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]) + 1);
        }
       else if (CONSTANT_P (operands[num]))
        {
@@ -253,17 +641,17 @@ split_di (operands, num, lo_half, hi_half)
       else if (offsettable_memref_p (operands[num]))
        {
          lo_half[num] = operands[num];
-         hi_half[num] = adj_offsettable_operand (operands[num], 4);
+         hi_half[num] = adjust_address (operands[num], SImode, 4);
        }
       else
-       abort();
+       abort ();
     }
 }
 \f
 /* Return the best assembler insn template
    for moving operands[1] into operands[0] as a fullword.  */
 
-static char *
+static const char *
 singlemove_string (operands)
      rtx *operands;
 {
@@ -274,7 +662,7 @@ singlemove_string (operands)
   return "movd %1,%0";
 }
 
-char *
+const char *
 output_move_double (operands)
      rtx *operands;
 {
@@ -321,16 +709,16 @@ output_move_double (operands)
      operands in OPERANDS to be suitable for the low-numbered word.  */
 
   if (optype0 == REGOP)
-    latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+    latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
   else if (optype0 == OFFSOP)
-    latehalf[0] = adj_offsettable_operand (operands[0], 4);
+    latehalf[0] = adjust_address (operands[0], SImode, 4);
   else
     latehalf[0] = operands[0];
 
   if (optype1 == REGOP)
-    latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+    latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
   else if (optype1 == OFFSOP)
-    latehalf[1] = adj_offsettable_operand (operands[1], 4);
+    latehalf[1] = adjust_address (operands[1], SImode, 4);
   else if (optype1 == CNSTOP)
     split_double (operands[1], &operands[1], &latehalf[1]);
   else
@@ -379,8 +767,8 @@ output_move_double (operands)
          xops[0] = XEXP (operands[1], 0);
          xops[1] = operands[0];
          output_asm_insn ("addr %a0,%1", xops);
-         operands[1] = gen_rtx (MEM, DImode, operands[0]);
-         latehalf[1] = adj_offsettable_operand (operands[1], 4);
+         operands[1] = gen_rtx_MEM (DImode, operands[0]);
+         latehalf[1] = adjust_address (operands[1], SImode, 4);
          /* The first half has the overlap, Do the late half first.  */
          output_asm_insn (singlemove_string (latehalf), latehalf);
          /* Then clobber.  */
@@ -404,27 +792,157 @@ output_move_double (operands)
   return singlemove_string (operands);
 }
 
-int
-check_reg (oper, reg)
-     rtx oper;
-     int reg;
+\f
+#define MAX_UNALIGNED_COPY (32)
+/* Expand string/block move operations.
+
+   operands[0] is the pointer to the destination.
+   operands[1] is the pointer to the source.
+   operands[2] is the number of bytes to move.
+   operands[3] is the alignment.  */
+
+static void
+move_tail (operands, bytes, offset)
+     rtx operands[];
+     int bytes;
+     int offset;
 {
-  register int i;
+  if (bytes & 2)
+    {
+      emit_move_insn (adjust_address (operands[0], HImode, offset),
+                     adjust_address (operands[1], HImode, offset));
+      offset += 2;
+    }
+  if (bytes & 1)
+    emit_move_insn (adjust_address (operands[0], QImode, offset),
+                   adjust_address (operands[1], QImode, offset));
+}
 
-  if (oper == 0)
-    return 0;
-  switch (GET_CODE(oper))
+void
+expand_block_move (operands)
+     rtx operands[];
+{
+  rtx bytes_rtx        = operands[2];
+  rtx align_rtx = operands[3];
+  int constp   = (GET_CODE (bytes_rtx) == CONST_INT);
+  int bytes    = (constp ? INTVAL (bytes_rtx) : 0);
+  int align    = INTVAL (align_rtx);
+  rtx src_reg = gen_rtx_REG (Pmode, 1);
+  rtx dest_reg = gen_rtx_REG (Pmode, 2);
+  rtx count_reg = gen_rtx_REG (SImode, 0);
+
+  if (constp && bytes <= 0)
+    return;
+
+  if (constp && bytes < 20)
     {
-    case REG:
-      return (REGNO(oper) == reg) ? 1 : 0;
-    case MEM:
-      return check_reg(XEXP(oper, 0), reg);
-    case PLUS:
-    case MULT:
-      return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg);
+      int words = bytes >> 2;
+
+      if (words)
+       {
+         if (words < 3 || flag_unroll_loops)
+           {
+             int offset = 0;
+
+             for (; words; words--, offset += 4)
+               emit_move_insn (adjust_address (operands[0], SImode, offset),
+                               adjust_address (operands[1], SImode, offset));
+           }
+         else
+           {
+             /* Use movmd. It is slower than multiple movd's but more
+                compact. It is also slower than movsd for large copies
+                but causes less registers reloading so is better than movsd
+                for small copies. */
+             rtx src, dest;
+             dest = copy_addr_to_reg (XEXP (operands[0], 0));
+             src = copy_addr_to_reg (XEXP (operands[1], 0));
+           
+             emit_insn (gen_movstrsi2(dest, src, GEN_INT (words)));
+           }
+       }
+      move_tail (operands, bytes & 3, bytes & ~3);
+      return;
+    }
+
+  if (align > UNITS_PER_WORD)
+    align = UNITS_PER_WORD;
+
+  /* Move the address into scratch registers.  */
+  emit_insn (gen_rtx_CLOBBER (VOIDmode, dest_reg));
+  emit_move_insn (dest_reg, XEXP (operands[0], 0));
+  operands[0] = gen_rtx_MEM (SImode, dest_reg);
+  emit_insn (gen_rtx_CLOBBER (VOIDmode, src_reg));
+  emit_move_insn (src_reg, XEXP (operands[1], 0));
+  operands[1] = gen_rtx_MEM (SImode, src_reg);
+  emit_insn (gen_rtx_CLOBBER (VOIDmode, count_reg));
+
+  if (constp && (align == UNITS_PER_WORD || bytes < MAX_UNALIGNED_COPY))
+    {
+      /* constant no of bytes and aligned or small enough copy to not bother
+       * aligning. Emit insns to copy by words.
+       */
+      if (bytes >> 2)
+       {
+         emit_move_insn (count_reg, GEN_INT (bytes >> 2));
+         emit_insn (gen_movstrsi1 (GEN_INT (4)));
+       }
+      /* insns to copy rest */
+      move_tail (operands, bytes & 3, 0);
+    }
+  else if (align == UNITS_PER_WORD)
+    {
+      /* insns to copy by words */
+      emit_insn (gen_lshrsi3 (count_reg, bytes_rtx, GEN_INT (2)));
+      emit_insn (gen_movstrsi1 (GEN_INT (4)));
+      if (constp)
+       {
+         move_tail (operands, bytes & 3, 0);
+       }
+      else
+       {
+         /* insns to copy rest */
+         emit_insn (gen_andsi3 (count_reg, bytes_rtx, GEN_INT (3)));
+         emit_insn (gen_movstrsi1 (const1_rtx));
+       }
+    }
+  else
+    {
+      /* Not aligned and we may have a lot to copy so it is worth
+       * aligning.
+       */
+      rtx aligned_label = gen_label_rtx ();
+      rtx bytes_reg;
+
+      bytes_reg = copy_to_mode_reg (SImode, bytes_rtx);
+      if (!constp)
+       {
+         /* Emit insns to test and skip over the alignment if it is
+          * not worth it. This doubles as a test to ensure that the alignment
+          * operation can't copy too many bytes
+          */
+         emit_insn (gen_cmpsi (bytes_reg, GEN_INT (MAX_UNALIGNED_COPY)));
+         emit_jump_insn (gen_blt (aligned_label));
+       }
+
+      /* Emit insns to do alignment at run time */
+      emit_insn (gen_negsi2 (count_reg, src_reg));
+      emit_insn (gen_andsi3 (count_reg, count_reg, GEN_INT (3)));
+      emit_insn (gen_subsi3 (bytes_reg, bytes_reg, count_reg));
+      emit_insn (gen_movstrsi1 (const1_rtx));
+      if (!constp)
+       emit_label (aligned_label);
+
+      /* insns to copy by words */
+      emit_insn (gen_lshrsi3 (count_reg, bytes_reg, GEN_INT (2)));
+      emit_insn (gen_movstrsi1 (GEN_INT (4)));
+
+      /* insns to copy rest */
+      emit_insn (gen_andsi3 (count_reg, bytes_reg, GEN_INT (3)));
+      emit_insn (gen_movstrsi1 (const1_rtx));
     }
-  return 0;
 }
+\f
 
 /* Returns 1 if OP contains a global symbol reference */
 
@@ -433,7 +951,7 @@ global_symbolic_reference_mentioned_p (op, f)
      rtx op;
      int f;
 {
-  register char *fmt;
+  register const char *fmt;
   register int i;
 
   if (GET_CODE (op) == SYMBOL_REF)
@@ -466,25 +984,137 @@ global_symbolic_reference_mentioned_p (op, f)
 }
 
 \f
+/* Returns 1 if OP contains a symbol reference */
+
+int
+symbolic_reference_mentioned_p (op)
+     rtx op;
+{
+  register const char *fmt;
+  register int i;
+
+  if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
+    return 1;
+
+  fmt = GET_RTX_FORMAT (GET_CODE (op));
+  for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+       {
+         register int j;
+
+         for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+           if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+             return 1;
+       }
+      else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
+       return 1;
+    }
+
+  return 0;
+}
+\f
+/* Table of machine-specific attributes.  */
+
+const struct attribute_spec ns32k_attribute_table[] =
+{
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  /* Stdcall attribute says callee is responsible for popping arguments
+     if they are not variable.  */
+  { "stdcall", 0, 0, false, true,  true,  ns32k_handle_fntype_attribute },
+  /* Cdecl attribute says the callee is a normal C declaration */
+  { "cdecl",   0, 0, false, true,  true,  ns32k_handle_fntype_attribute },
+  { NULL,      0, 0, false, false, false, NULL }
+};
+
+/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
+   arguments as in struct attribute_spec.handler.  */
+static tree
+ns32k_handle_fntype_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name;
+     tree args ATTRIBUTE_UNUSED;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  if (TREE_CODE (*node) != FUNCTION_TYPE
+      && TREE_CODE (*node) != FIELD_DECL
+      && TREE_CODE (*node) != TYPE_DECL)
+    {
+      warning ("`%s' attribute only applies to functions",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+\f
+/* Value is the number of bytes of arguments automatically
+   popped when returning from a subroutine call.
+   FUNDECL is the declaration node of the function (as a tree),
+   FUNTYPE is the data type of the function (as a tree),
+   or for a library call it is an identifier node for the subroutine name.
+   SIZE is the number of bytes of arguments passed on the stack.
+
+   On the ns32k, the RET insn may be used to pop them if the number
+     of args is fixed, but if the number is variable then the caller
+     must pop them all.  RET can't be used for library calls now
+     because the library is compiled with the Unix compiler.
+   Use of RET is a selectable option, since it is incompatible with
+   standard Unix calling sequences.  If the option is not selected,
+   the caller must always pop the args.
+
+   The attribute stdcall is equivalent to RET on a per module basis.  */
+
+int
+ns32k_return_pops_args (fundecl, funtype, size)
+     tree fundecl ATTRIBUTE_UNUSED;
+     tree funtype;
+     int size;
+{
+  int rtd = TARGET_RTD;
+
+  if (TREE_CODE (funtype) == IDENTIFIER_NODE)
+    return rtd ? size : 0;
+
+  /* Cdecl functions override -mrtd, and never pop the stack */
+  if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype)))
+    return 0;
+
+  /* Stdcall functions will pop the stack if not variable args */
+  if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)))
+    rtd = 1;
+
+  if (rtd)
+    {
+      if (TYPE_ARG_TYPES (funtype) == NULL_TREE
+         || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
+       return size;
+    }
+
+  return 0;
+}
+\f
 /* PRINT_OPERAND is defined to call this function,
    which is easier to debug than putting all the code in
    a macro definition in ns32k.h.  */
 
+/* XXX time 12% of cpu time is in fprintf for non optimizing */
 void
 print_operand (file, x, code)
      FILE *file;
      rtx x;
-     char code;
+     int code;
 {
   if (code == '$')
     PUT_IMMEDIATE_PREFIX (file);
   else if (code == '?')
     PUT_EXTERNAL_PREFIX (file);
   else if (GET_CODE (x) == REG)
-    fprintf (file, "%s", reg_names[REGNO (x)]);
+    fprintf (file, "%s", ns32k_out_reg_names[REGNO (x)]);
   else if (GET_CODE (x) == MEM)
     {
-      rtx tmp = XEXP (x, 0);
       output_address (XEXP (x, 0));
     }
   else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode)
@@ -493,9 +1123,9 @@ print_operand (file, x, code)
        { 
          union { double d; int i[2]; } u;
          u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x);
-         PUT_IMMEDIATE_PREFIX(file);
+         PUT_IMMEDIATE_PREFIX (file);
 #ifdef SEQUENT_ASM
-         /* Sequent likes it's floating point constants as integers */
+         /* Sequent likes its floating point constants as integers */
          fprintf (file, "0Dx%08x%08x", u.i[1], u.i[0]);
 #else
 #ifdef ENCORE_ASM
@@ -519,7 +1149,7 @@ print_operand (file, x, code)
          {
            union { float f; long l; } uu;
            uu.f = u.d;
-           fprintf (file, "0Fx%08x", uu.l);
+           fprintf (file, "0Fx%08lx", uu.l);
          }
 #else
          fprintf (file, "0f%.20e", u.d); 
@@ -528,11 +1158,30 @@ print_operand (file, x, code)
     }
   else
     {
+      if (flag_pic
+          && GET_CODE (x) == CONST
+          && symbolic_reference_mentioned_p (x))
+        {
+         fprintf (stderr, "illegal constant for pic-mode: \n");
+         print_rtl (stderr, x);
+          fprintf (stderr, "\nGET_CODE (x) == %d, CONST == %d, symbolic_reference_mentioned_p (x) == %d\n",
+                 GET_CODE (x), CONST, symbolic_reference_mentioned_p (x));
+         abort ();
+       }
+      else if (flag_pic
+               && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF))
+       {
+         output_addr_const (file, x);
+         fprintf (file, "(sb)");
+       }
+      else
+        {
 #ifdef NO_IMMEDIATE_PREFIX_IF_SYMBOLIC
-      if (GET_CODE (x) == CONST_INT)
+          if (GET_CODE (x) == CONST_INT)
 #endif
-       PUT_IMMEDIATE_PREFIX (file);
-      output_addr_const (file, x);
+           PUT_IMMEDIATE_PREFIX (file);
+          output_addr_const (file, x);
+       }
     }
 }
 \f
@@ -545,11 +1194,12 @@ print_operand (file, x, code)
    figure out how it worked.
    90-11-25 Tatu Yl|nen <ylo@cs.hut.fi> */
 
+void
 print_operand_address (file, addr)
      register FILE *file;
      register rtx addr;
 {
-  static char scales[] = { 'b', 'w', 'd', 0, 'q', };
+  static const char scales[] = { 'b', 'w', 'd', 0, 'q', };
   rtx offset, base, indexexp, tmp;
   int scale;
   extern int flag_pic;
@@ -597,7 +1247,7 @@ print_operand_address (file, addr)
            base = tmp;
          break;
        case REG:
-         if (REGNO (tmp) < 8)
+         if (REGNO (tmp) < F0_REGNUM)
            if (base)
              {
                indexexp = tmp;
@@ -677,7 +1327,7 @@ print_operand_address (file, addr)
        case CONST_INT:
        case LABEL_REF:
          if (offset)
-           offset = gen_rtx (PLUS, SImode, tmp, offset);
+           offset = gen_rtx_PLUS (SImode, tmp, offset);
          else
            offset = tmp;
          break;
@@ -728,7 +1378,7 @@ print_operand_address (file, addr)
           (disp(sb)) (MEM ...)
           */
       case REG:
-       fprintf (file, "(%s)", reg_names[REGNO (base)]);
+       fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
        break;
       case SYMBOL_REF:
        if (! flag_pic)
@@ -739,7 +1389,7 @@ print_operand_address (file, addr)
        fprintf (file, "(sb))");
         break;
       case MEM:
-       addr = XEXP(base,0);
+       addr = XEXP (base,0);
        base = NULL;
        offset = NULL;
        while (addr != NULL)
@@ -772,7 +1422,7 @@ print_operand_address (file, addr)
              case SYMBOL_REF:
              case LABEL_REF:
                if (offset)
-                 offset = gen_rtx (PLUS, SImode, tmp, offset);
+                 offset = gen_rtx_PLUS (SImode, tmp, offset);
                else
                  offset = tmp;
                break;
@@ -785,7 +1435,7 @@ print_operand_address (file, addr)
        fprintf (file, "(");
        output_addr_const (file, offset);
        if (base)
-         fprintf (file, "(%s)", reg_names[REGNO (base)]);
+         fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
        else if (TARGET_SB)
          fprintf (file, "(sb)");
        else
@@ -816,16 +1466,16 @@ print_operand_address (file, addr)
        }
       else
        scale = 0;
-      if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= 8)
+      if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= F0_REGNUM)
        abort ();
 
 #ifdef UTEK_ASM
       fprintf (file, "[%c`%s]",
               scales[scale],
-              reg_names[REGNO (indexexp)]);
+              ns32k_out_reg_names[REGNO (indexexp)]);
 #else
       fprintf (file, "[%s:%c]",
-              reg_names[REGNO (indexexp)],
+              ns32k_out_reg_names[REGNO (indexexp)],
               scales[scale]);
 #endif
     }
@@ -834,58 +1484,60 @@ print_operand_address (file, addr)
 /* National 32032 shifting is so bad that we can get
    better performance in many common cases by using other
    techniques.  */
-char *
+const char *
 output_shift_insn (operands)
      rtx *operands;
 {
   if (GET_CODE (operands[2]) == CONST_INT
       && INTVAL (operands[2]) > 0
       && INTVAL (operands[2]) <= 3)
-    if (GET_CODE (operands[0]) == REG)
-      {
-       if (GET_CODE (operands[1]) == REG)
-         {
-           if (REGNO (operands[0]) == REGNO (operands[1]))
-             {
-               if (operands[2] == const1_rtx)
-                 return "addd %0,%0";
-               else if (INTVAL (operands[2]) == 2)
-                 return "addd %0,%0\n\taddd %0,%0";
-             }
-           if (operands[2] == const1_rtx)
-             return "movd %1,%0\n\taddd %0,%0";
+    {
+      if (GET_CODE (operands[0]) == REG)
+       {
+         if (GET_CODE (operands[1]) == REG)
+           {
+             if (REGNO (operands[0]) == REGNO (operands[1]))
+               {
+                 if (operands[2] == const1_rtx)
+                   return "addd %0,%0";
+                 else if (INTVAL (operands[2]) == 2)
+                   return "addd %0,%0\n\taddd %0,%0";
+               }
+             if (operands[2] == const1_rtx)
+               return "movd %1,%0\n\taddd %0,%0";
            
-           operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
-           return "addr %a1,%0";
-         }
-       if (operands[2] == const1_rtx)
-         return "movd %1,%0\n\taddd %0,%0";
-      }
-    else if (GET_CODE (operands[1]) == REG)
-      {
-       operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
-       return "addr %a1,%0";
-      }
-    else if (INTVAL (operands[2]) == 1
-            && GET_CODE (operands[1]) == MEM
-            && rtx_equal_p (operands [0], operands[1]))
-      {
-       rtx temp = XEXP (operands[1], 0);
+             operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
+             return "addr %a1,%0";
+           }
+         if (operands[2] == const1_rtx)
+           return "movd %1,%0\n\taddd %0,%0";
+       }
+      else if (GET_CODE (operands[1]) == REG)
+       {
+         operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
+         return "addr %a1,%0";
+       }
+      else if (INTVAL (operands[2]) == 1
+              && GET_CODE (operands[1]) == MEM
+              && rtx_equal_p (operands [0], operands[1]))
+       {
+         rtx temp = XEXP (operands[1], 0);
        
-       if (GET_CODE (temp) == REG
-           || (GET_CODE (temp) == PLUS
-               && GET_CODE (XEXP (temp, 0)) == REG
-               && GET_CODE (XEXP (temp, 1)) == CONST_INT))
-         return "addd %0,%0";
-      }
-    else return "ashd %2,%0";
+         if (GET_CODE (temp) == REG
+             || (GET_CODE (temp) == PLUS
+                 && GET_CODE (XEXP (temp, 0)) == REG
+                 && GET_CODE (XEXP (temp, 1)) == CONST_INT))
+           return "addd %0,%0";
+       }
+      else return "ashd %2,%0";
+    }
   return "ashd %2,%0";
 }
 
-char *
+const char *
 output_move_dconst (n, s)
        int n;
-       char *s;
+       const char *s;
 {
   static char r[32];