Makefile.in (TARGET_H, [...]): New.
[gcc.git] / gcc / config / m68k / m68k.c
index d41eb0ea1c3a7ca0e52b05f7955e018e51a58587..e377e77c15efec964e107db9430383d33efb1462 100644 (file)
@@ -1,5 +1,6 @@
 /* Subroutines for insn-output.c for Motorola 68000 family.
-   Copyright (C) 1987, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
+   Copyright (C) 1987, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+   Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -18,19 +19,23 @@ 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 m68k.md need these.  */
-#include <stdio.h>
 #include "config.h"
+#include "system.h"
+#include "tree.h"
 #include "rtl.h"
+#include "function.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 "recog.h"
+#include "toplev.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
 
 /* Needed for use_return_insn.  */
 #include "flags.h"
@@ -49,17 +54,17 @@ enum reg_class regno_reg_class[]
    if SGS_SWITCH_TABLE.  */
 int switch_table_difference_label_flag;
 
-static rtx find_addr_reg ();
-rtx legitimize_pic_address ();
+static rtx find_addr_reg PARAMS ((rtx));
+static const char *singlemove_string PARAMS ((rtx *));
 \f
 
 /* Alignment to use for loops and jumps */
 /* Specify power of two alignment used for loops. */
-char *m68k_align_loops_string;
+const char *m68k_align_loops_string;
 /* Specify power of two alignment used for non-loop jumps. */
-char *m68k_align_jumps_string;
+const char *m68k_align_jumps_string;
 /* Specify power of two alignment used for functions. */
-char *m68k_align_funcs_string;
+const char *m68k_align_funcs_string;
 
 /* Specify power of two alignment used for loops. */
 int m68k_align_loops;
@@ -72,7 +77,11 @@ int m68k_align_funcs;
    sCC expanders peek at this to determine what to do for the
    68060, which has no fsCC instructions.  */
 int m68k_last_compare_had_fp_operands;
+\f
+/* Initialize the GCC target structure.  */
 
+struct gcc_target target = TARGET_INITIALIZER;
+\f
 /* Sometimes certain combinations of command options do not make
    sense on a particular target machine.  You can define a macro
    `OVERRIDE_OPTIONS' to take account of this.  This macro, if
@@ -86,60 +95,44 @@ void
 override_options ()
 {
   int def_align;
+  int i;
 
   def_align = 1;
 
   /* Validate -malign-loops= value, or provide default */
+  m68k_align_loops = def_align;
   if (m68k_align_loops_string)
     {
-      m68k_align_loops = atoi (m68k_align_loops_string);
-      if (m68k_align_loops < 1 || m68k_align_loops > MAX_CODE_ALIGN)
-       fatal ("-malign-loops=%d is not between 1 and %d",
-              m68k_align_loops, MAX_CODE_ALIGN);
+      i = atoi (m68k_align_loops_string);
+      if (i < 1 || i > MAX_CODE_ALIGN)
+       error ("-malign-loops=%d is not between 1 and %d", i, MAX_CODE_ALIGN);
+      else
+       m68k_align_loops = i;
     }
-  else
-    m68k_align_loops = def_align;
 
   /* Validate -malign-jumps= value, or provide default */
+  m68k_align_jumps = def_align;
   if (m68k_align_jumps_string)
     {
-      m68k_align_jumps = atoi (m68k_align_jumps_string);
-      if (m68k_align_jumps < 1 || m68k_align_jumps > MAX_CODE_ALIGN)
-       fatal ("-malign-jumps=%d is not between 1 and %d",
-              m68k_align_jumps, MAX_CODE_ALIGN);
+      i = atoi (m68k_align_jumps_string);
+      if (i < 1 || i > MAX_CODE_ALIGN)
+       error ("-malign-jumps=%d is not between 1 and %d", i, MAX_CODE_ALIGN);
+      else
+       m68k_align_jumps = i;
     }
-  else
-    m68k_align_jumps = def_align;
 
   /* Validate -malign-functions= value, or provide default */
+  m68k_align_funcs = def_align;
   if (m68k_align_funcs_string)
     {
-      m68k_align_funcs = atoi (m68k_align_funcs_string);
-      if (m68k_align_funcs < 1 || m68k_align_funcs > MAX_CODE_ALIGN)
-       fatal ("-malign-functions=%d is not between 1 and %d",
-              m68k_align_funcs, MAX_CODE_ALIGN);
+      i = atoi (m68k_align_funcs_string);
+      if (i < 1 || i > MAX_CODE_ALIGN)
+       error ("-malign-functions=%d is not between 1 and %d",
+              i, MAX_CODE_ALIGN);
+      else
+       m68k_align_funcs = i;
     }
-  else
-    m68k_align_funcs = def_align;
 }
-\f
-/* Emit a (use pic_offset_table_rtx) if we used PIC relocation in the 
-   function at any time during the compilation process.  In the future 
-   we should try and eliminate the USE if we can easily determine that 
-   all PIC references were deleted from the current function.  That would 
-   save an address register */
-   
-void
-finalize_pic ()
-{
-  if (flag_pic && current_function_uses_pic_offset_table)
-    {
-      rtx insn = gen_rtx (USE, VOIDmode, pic_offset_table_rtx);
-      emit_insn_after (insn, get_insns ());
-      emit_insn (insn);
-    }
-}
-
 \f
 /* This function generates the assembly code for function entry.
    STREAM is a stdio stream to output the code to.
@@ -164,7 +157,21 @@ output_function_prologue (stream, size)
   int num_saved_regs = 0;
   extern char call_used_regs[];
   int fsize = (size + 3) & -4;
+  int cfa_offset = INCOMING_FRAME_SP_OFFSET, cfa_store_offset = cfa_offset;
   
+  /* If the stack limit is a symbol, we can check it here,
+     before actually allocating the space.  */
+  if (current_function_limit_stack
+      && GET_CODE (stack_limit_rtx) == SYMBOL_REF)
+    {
+#if defined (MOTOROLA)
+      asm_fprintf (stream, "\tcmp.l %0I%s+%d,%Rsp\n\ttrapcs\n",
+                  XSTR (stack_limit_rtx, 0), fsize + 4);
+#else
+      asm_fprintf (stream, "\tcmpl %0I%s+%d,%Rsp\n\ttrapcs\n",
+                  XSTR (stack_limit_rtx, 0), fsize + 4);
+#endif
+    }
 
   if (frame_pointer_needed)
     {
@@ -212,30 +219,40 @@ output_function_prologue (stream, size)
                       reg_names[FRAME_POINTER_REGNUM], -fsize);
 #endif
        }
+      if (dwarf2out_do_frame ())
+       {
+         char *l;
+          l = (char *) dwarf2out_cfi_label ();   
+         cfa_store_offset += 4;
+         cfa_offset = cfa_store_offset;
+         dwarf2out_reg_save (l, FRAME_POINTER_REGNUM, -cfa_store_offset);
+         dwarf2out_def_cfa (l, FRAME_POINTER_REGNUM, cfa_offset);
+         cfa_store_offset += fsize;
+       }
     }
   else if (fsize)
     {
       if (fsize + 4 < 0x8000)
        {
-#ifdef NO_ADDSUB_Q
+#ifndef NO_ADDSUB_Q
          if (fsize + 4 <= 8)
            {
              if (!TARGET_5200)
                {
                  /* asm_fprintf() cannot handle %. */
 #ifdef MOTOROLA
-                 asm_fprintf (stream, "\tsubq.w %OI%d,%Rsp\n", fsize + 4);
+                 asm_fprintf (stream, "\tsubq.w %0I%d,%Rsp\n", fsize + 4);
 #else
-                 asm_fprintf (stream, "\tsubqw %OI%d,%Rsp\n", fsize + 4);
+                 asm_fprintf (stream, "\tsubqw %0I%d,%Rsp\n", fsize + 4);
 #endif
                }
              else
                {
                  /* asm_fprintf() cannot handle %. */
 #ifdef MOTOROLA
-                 asm_fprintf (stream, "\tsubq.l %OI%d,%Rsp\n", fsize + 4);
+                 asm_fprintf (stream, "\tsubq.l %0I%d,%Rsp\n", fsize + 4);
 #else
-                 asm_fprintf (stream, "\tsubql %OI%d,%Rsp\n", fsize + 4);
+                 asm_fprintf (stream, "\tsubql %0I%d,%Rsp\n", fsize + 4);
 #endif
                }
            }
@@ -245,15 +262,15 @@ output_function_prologue (stream, size)
                 subtract a small integer (8 < N <= 16) to a register. */
              /* asm_fprintf() cannot handle %. */
 #ifdef MOTOROLA
-             asm_fprintf (stream, "\tsubq.w %OI8,%Rsp\n\tsubq.w %OI%d,%Rsp\n",
-                          fsize + 4);
+             asm_fprintf (stream, "\tsubq.w %0I8,%Rsp\n\tsubq.w %0I%d,%Rsp\n",
+                          fsize + 4 - 8);
 #else
-             asm_fprintf (stream, "\tsubqw %OI8,%Rsp\n\tsubqw %OI%d,%Rsp\n",
-                          fsize + 4);
+             asm_fprintf (stream, "\tsubqw %0I8,%Rsp\n\tsubqw %0I%d,%Rsp\n",
+                          fsize + 4 - 8);
 #endif
            }
          else 
-#endif /* NO_ADDSUB_Q */
+#endif /* not NO_ADDSUB_Q */
          if (TARGET_68040)
            {
              /* Adding negative number is faster on the 68040.  */
@@ -282,6 +299,12 @@ output_function_prologue (stream, size)
          asm_fprintf (stream, "\taddl %0I%d,%Rsp\n", - (fsize + 4));
 #endif
        }
+      if (dwarf2out_do_frame ())
+       {
+         cfa_store_offset += fsize;
+         cfa_offset = cfa_store_offset;
+         dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, cfa_offset);
+       }
     }
 #ifdef SUPPORT_SUN_FPA
   for (regno = 24; regno < 56; regno++)
@@ -294,13 +317,28 @@ output_function_prologue (stream, size)
        asm_fprintf (stream, "\tfpmoved %s,%Rsp@-\n",
                     reg_names[regno]);
 #endif
+       if (dwarf2out_do_frame ())
+         {
+           char *l = dwarf2out_cfi_label ();
+
+           cfa_store_offset += 8;
+           if (! frame_pointer_needed)
+             {
+               cfa_offset = cfa_store_offset;
+               dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+             }
+           dwarf2out_reg_save (l, regno, -cfa_store_offset);
+         }
       }
 #endif
   if (TARGET_68881)
     {
       for (regno = 16; regno < 24; regno++)
        if (regs_ever_live[regno] && ! call_used_regs[regno])
-          mask |= 1 << (regno - 16);
+         {
+           mask |= 1 << (regno - 16);
+           num_saved_regs++;
+         }
       if ((mask & 0xff) != 0)
        {
 #ifdef MOTOROLA
@@ -308,8 +346,25 @@ output_function_prologue (stream, size)
 #else
          asm_fprintf (stream, "\tfmovem %0I0x%x,%Rsp@-\n", mask & 0xff);
 #endif
+         if (dwarf2out_do_frame ())
+           {
+             char *l = (char *) dwarf2out_cfi_label ();
+             int n_regs;
+
+             cfa_store_offset += num_saved_regs * 12;
+             if (! frame_pointer_needed)
+               {
+                 cfa_offset = cfa_store_offset;
+                 dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+               }
+             for (regno = 16, n_regs = 0; regno < 24; regno++)
+               if (mask & (1 << (regno - 16)))
+                 dwarf2out_reg_save (l, regno,
+                                     -cfa_store_offset + n_regs++ * 12);
+           }
        }
       mask = 0;
+      num_saved_regs = 0;
     }
   for (regno = 0; regno < 16; regno++)
     if (regs_ever_live[regno] && ! call_used_regs[regno])
@@ -322,6 +377,11 @@ output_function_prologue (stream, size)
       mask &= ~ (1 << (15 - FRAME_POINTER_REGNUM));
       num_saved_regs--;
     }
+  if (flag_pic && current_function_uses_pic_offset_table)
+    {
+      mask |= 1 << (15 - PIC_OFFSET_TABLE_REGNUM);
+      num_saved_regs++;
+    }
 
 #if NEED_PROBE
 #ifdef MOTOROLA
@@ -335,6 +395,24 @@ output_function_prologue (stream, size)
 #endif
 #endif
 
+  /* If the stack limit is not a symbol, check it here.  
+     This has the disadvantage that it may be too late...  */
+  if (current_function_limit_stack)
+    {
+      if (REG_P (stack_limit_rtx))
+       {
+#if defined (MOTOROLA)
+         asm_fprintf (stream, "\tcmp.l %s,%Rsp\n\ttrapcs\n",
+                      reg_names[REGNO (stack_limit_rtx)]);
+#else
+         asm_fprintf (stream, "\tcmpl %s,%Rsp\n\ttrapcs\n",
+                      reg_names[REGNO (stack_limit_rtx)]);
+#endif
+       }
+      else if (GET_CODE (stack_limit_rtx) != SYMBOL_REF)
+       warning ("stack limit expression is not supported");
+    }
+  
   if (num_saved_regs <= 2)
     {
       /* Store each separately in the same order moveml uses.
@@ -347,13 +425,27 @@ output_function_prologue (stream, size)
       /* Undo the work from above. */
       for (i = 0; i< 16; i++)
         if (mask & (1 << i))
-          asm_fprintf (stream,
+         {
+           asm_fprintf (stream,
 #ifdef MOTOROLA
-                      "\t%Omove.l %s,-(%Rsp)\n",
+                        "\t%Omove.l %s,-(%Rsp)\n",
 #else
-                      "\tmovel %s,%Rsp@-\n",
+                        "\tmovel %s,%Rsp@-\n",
 #endif
-                      reg_names[15 - i]);
+                        reg_names[15 - i]);
+           if (dwarf2out_do_frame ())
+             {
+               char *l = (char *) dwarf2out_cfi_label ();
+
+               cfa_store_offset += 4;
+               if (! frame_pointer_needed)
+                 {
+                   cfa_offset = cfa_store_offset;
+                   dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+                 }
+               dwarf2out_reg_save (l, 15 - i, -cfa_store_offset);
+             }
+         }
     }
   else if (mask)
     {
@@ -392,6 +484,22 @@ output_function_prologue (stream, size)
          asm_fprintf (stream, "\tmoveml %0I0x%x,%Rsp@-\n", mask);
 #endif
        }
+      if (dwarf2out_do_frame ())
+       {
+         char *l = (char *) dwarf2out_cfi_label ();
+         int n_regs;
+
+         cfa_store_offset += num_saved_regs * 4;
+         if (! frame_pointer_needed)
+           {
+             cfa_offset = cfa_store_offset;
+             dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, cfa_offset);
+           }
+         for (regno = 0, n_regs = 0; regno < 16; regno++)
+           if (mask & (1 << (15 - regno)))
+             dwarf2out_reg_save (l, regno,
+                                 -cfa_store_offset + n_regs++ * 4);
+       }
     }
   if (flag_pic && current_function_uses_pic_offset_table)
     {
@@ -424,7 +532,10 @@ use_return_insn ()
   for (regno = 0 ; regno < FIRST_PSEUDO_REGISTER ; regno++)
     if (regs_ever_live[regno] && ! call_used_regs[regno])
       return 0;
-  
+
+  if (flag_pic && current_function_uses_pic_offset_table)
+    return 0;
+
   return 1;
 }
 
@@ -499,6 +610,11 @@ output_function_epilogue (stream, size)
         nregs++;
        mask |= 1 << regno;
       }
+  if (flag_pic && current_function_uses_pic_offset_table)
+    {
+      nregs++;
+      mask |= 1 << PIC_OFFSET_TABLE_REGNUM;
+    }
   offset = foffset + nregs * 4;
   /* FIXME : leaf_function_p below is too strong.
      What we really need to know there is if there could be pending
@@ -689,23 +805,23 @@ output_function_epilogue (stream, size)
             reg_names[FRAME_POINTER_REGNUM]);
   else if (fsize)
     {
-#ifdef NO_ADDSUB_Q
+#ifndef NO_ADDSUB_Q
       if (fsize + 4 <= 8) 
        {
          if (!TARGET_5200)
            {
 #ifdef MOTOROLA
-             asm_fprintf (stream, "\taddq.w %OI%d,%Rsp\n", fsize + 4);
+             asm_fprintf (stream, "\taddq.w %0I%d,%Rsp\n", fsize + 4);
 #else
-             asm_fprintf (stream, "\taddqw %OI%d,%Rsp\n", fsize + 4);
+             asm_fprintf (stream, "\taddqw %0I%d,%Rsp\n", fsize + 4);
 #endif
            }
          else
            {
 #ifdef MOTOROLA
-             asm_fprintf (stream, "\taddq.l %OI%d,%Rsp\n", fsize + 4);
+             asm_fprintf (stream, "\taddq.l %0I%d,%Rsp\n", fsize + 4);
 #else
-             asm_fprintf (stream, "\taddql %OI%d,%Rsp\n", fsize + 4);
+             asm_fprintf (stream, "\taddql %0I%d,%Rsp\n", fsize + 4);
 #endif
            }
        }
@@ -715,15 +831,15 @@ output_function_epilogue (stream, size)
             add a small integer (8 < N <= 16) to a register. */
          /* asm_fprintf() cannot handle %. */
 #ifdef MOTOROLA
-         asm_fprintf (stream, "\taddq.w %OI8,%Rsp\n\taddq.w %OI%d,%Rsp\n",
-                      fsize + 4);
+         asm_fprintf (stream, "\taddq.w %0I8,%Rsp\n\taddq.w %0I%d,%Rsp\n",
+                      fsize + 4 - 8);
 #else
-         asm_fprintf (stream, "\taddqw %OI8,%Rsp\n\taddqw %OI%d,%Rsp\n",
-                      fsize + 4);
+         asm_fprintf (stream, "\taddqw %0I8,%Rsp\n\taddqw %0I%d,%Rsp\n",
+                      fsize + 4 - 8);
 #endif
        }
       else
-#endif /* NO_ADDSUB_Q */
+#endif /* not NO_ADDSUB_Q */
       if (fsize + 4 < 0x8000)
        {
          if (TARGET_68040)
@@ -781,7 +897,7 @@ not_sp_operand (op, mode)
 int
 valid_dbcc_comparison_p (x, mode)
      rtx x;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   switch (GET_CODE (x))
     {
@@ -812,6 +928,7 @@ flags_in_68881 ()
    set.  It is assumed that valid_dbcc_comparison_p and flags_in_68881 will
    kick those out before we get here.  */
 
+void
 output_dbcc_and_branch (operands)
      rtx *operands;
 {
@@ -921,7 +1038,7 @@ output_dbcc_and_branch (operands)
     }
 }
 
-char *
+const char *
 output_scc_di(op, operand1, operand2, dest)
      rtx op;
      rtx operand1;
@@ -947,38 +1064,65 @@ output_scc_di(op, operand1, operand2, dest)
     }
   loperands[0] = operand1;
   if (GET_CODE (operand1) == REG)
-    loperands[1] = gen_rtx (REG, SImode, REGNO (operand1) + 1);
+    loperands[1] = gen_rtx_REG (SImode, REGNO (operand1) + 1);
   else
     loperands[1] = adj_offsettable_operand (operand1, 4);
   if (operand2 != const0_rtx)
     {
       loperands[2] = operand2;
       if (GET_CODE (operand2) == REG)
-       loperands[3] = gen_rtx (REG, SImode, REGNO (operand2) + 1);
+       loperands[3] = gen_rtx_REG (SImode, REGNO (operand2) + 1);
       else
        loperands[3] = adj_offsettable_operand (operand2, 4);
     }
   loperands[4] = gen_label_rtx();
   if (operand2 != const0_rtx)
+    {
 #ifdef MOTOROLA
 #ifdef SGS_CMP_ORDER
-    output_asm_insn ("cmp%.l %0,%2\n\tjbne %l4\n\tcmp%.l %1,%3", loperands);
+      output_asm_insn ("cmp%.l %0,%2\n\tjbne %l4\n\tcmp%.l %1,%3", loperands);
 #else
-    output_asm_insn ("cmp%.l %2,%0\n\tjbne %l4\n\tcmp%.l %3,%1", loperands);
+      output_asm_insn ("cmp%.l %2,%0\n\tjbne %l4\n\tcmp%.l %3,%1", loperands);
 #endif
 #else
 #ifdef SGS_CMP_ORDER
-    output_asm_insn ("cmp%.l %0,%2\n\tjne %l4\n\tcmp%.l %1,%3", loperands);
+      output_asm_insn ("cmp%.l %0,%2\n\tjne %l4\n\tcmp%.l %1,%3", loperands);
 #else
-    output_asm_insn ("cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1", loperands);
+      output_asm_insn ("cmp%.l %2,%0\n\tjne %l4\n\tcmp%.l %3,%1", loperands);
 #endif
 #endif
+    }
   else
+    {
+      if (TARGET_68020 || TARGET_5200 || ! ADDRESS_REG_P (loperands[0]))
+       output_asm_insn ("tst%.l %0", loperands);
+      else
+       {
+#ifdef SGS_CMP_ORDER
+         output_asm_insn ("cmp%.w %0,%#0", loperands);
+#else
+         output_asm_insn ("cmp%.w %#0,%0", loperands);
+#endif
+       }
+
 #ifdef MOTOROLA
-    output_asm_insn ("tst%.l %0\n\tjbne %l4\n\ttst%.l %1", loperands);
+      output_asm_insn ("jbne %l4", loperands);
 #else
-    output_asm_insn ("tst%.l %0\n\tjne %l4\n\ttst%.l %1", loperands);
+      output_asm_insn ("jne %l4", loperands);
 #endif
+
+      if (TARGET_68020 || TARGET_5200 || ! ADDRESS_REG_P (loperands[1]))
+       output_asm_insn ("tst%.l %1", loperands);
+      else
+       {
+#ifdef SGS_CMP_ORDER
+         output_asm_insn ("cmp%.w %1,%#0", loperands);
+#else
+         output_asm_insn ("cmp%.w %#0,%1", loperands);
+#endif
+       }
+    }
+
   loperands[5] = dest;
   
   switch (op_code)
@@ -1081,7 +1225,7 @@ output_scc_di(op, operand1, operand2, dest)
   return "";
 }
 
-char *
+const char *
 output_btst (operands, countop, dataop, insn, signpos)
      rtx *operands;
      rtx countop, dataop;
@@ -1130,7 +1274,7 @@ output_btst (operands, countop, dataop, insn, signpos)
 int
 symbolic_operand (op, mode)
      register rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   switch (GET_CODE (op))
     {
@@ -1218,7 +1362,7 @@ extend_operator(x, mode)
 rtx
 legitimize_pic_address (orig, mode, reg)
      rtx orig, reg;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   rtx pic_ref = orig;
 
@@ -1228,9 +1372,9 @@ legitimize_pic_address (orig, mode, reg)
       if (reg == 0)
        abort ();
 
-      pic_ref = gen_rtx (MEM, Pmode,
-                        gen_rtx (PLUS, Pmode,
-                                 pic_offset_table_rtx, orig));
+      pic_ref = gen_rtx_MEM (Pmode,
+                            gen_rtx_PLUS (Pmode,
+                                          pic_offset_table_rtx, orig));
       current_function_uses_pic_offset_table = 1;
       RTX_UNCHANGING_P (pic_ref) = 1;
       emit_move_insn (reg, pic_ref);
@@ -1238,7 +1382,7 @@ legitimize_pic_address (orig, mode, reg)
     }
   else if (GET_CODE (orig) == CONST)
     {
-      rtx base, offset;
+      rtx base;
 
       /* Make sure this is CONST has not already been legitimized */
       if (GET_CODE (XEXP (orig, 0)) == PLUS
@@ -1259,7 +1403,7 @@ legitimize_pic_address (orig, mode, reg)
 
       if (GET_CODE (orig) == CONST_INT)
        return plus_constant_for_output (base, INTVAL (orig));
-      pic_ref = gen_rtx (PLUS, Pmode, base, orig);
+      pic_ref = gen_rtx_PLUS (Pmode, base, orig);
       /* Likewise, should we set special REG_NOTEs here?  */
     }
   return pic_ref;
@@ -1268,9 +1412,11 @@ legitimize_pic_address (orig, mode, reg)
 \f
 typedef enum { MOVL, SWAP, NEGW, NOTW, NOTB, MOVQ } CONST_METHOD;
 
+static CONST_METHOD const_method PARAMS ((rtx));
+
 #define USE_MOVQ(i)    ((unsigned)((i) + 128) <= 255)
 
-CONST_METHOD
+static CONST_METHOD
 const_method (constant)
      rtx constant;
 {
@@ -1304,6 +1450,7 @@ const_method (constant)
   return MOVL;
 }
 
+int
 const_int_cost (constant)
      rtx constant;
 {
@@ -1325,7 +1472,7 @@ const_int_cost (constant)
     }
 }
 
-char *
+const char *
 output_move_const_into_data_reg (operands)
      rtx *operands;
 {
@@ -1341,14 +1488,14 @@ output_move_const_into_data_reg (operands)
       return "moveq %1,%0";
 #endif
     case NOTB :
-      operands[1] = gen_rtx (CONST_INT, VOIDmode, i ^ 0xff);
+      operands[1] = GEN_INT (i ^ 0xff);
 #if defined (MOTOROLA) && !defined (CRDS)
       return "moveq%.l %1,%0\n\tnot%.b %0";
 #else
       return "moveq %1,%0\n\tnot%.b %0";
 #endif  
     case NOTW :
-      operands[1] = gen_rtx (CONST_INT, VOIDmode, i ^ 0xffff);
+      operands[1] = GEN_INT (i ^ 0xffff);
 #if defined (MOTOROLA) && !defined (CRDS)
       return "moveq%.l %1,%0\n\tnot%.w %0";
 #else
@@ -1364,7 +1511,7 @@ output_move_const_into_data_reg (operands)
       {
        unsigned u = i;
 
-       operands[1] = gen_rtx (CONST_INT, VOIDmode, (u << 16) | (u >> 16));
+       operands[1] = GEN_INT ((u << 16) | (u >> 16));
 #if defined (MOTOROLA) && !defined (CRDS)
        return "moveq%.l %1,%0\n\tswap %0";
 #else
@@ -1378,7 +1525,7 @@ output_move_const_into_data_reg (operands)
     }
 }
 
-char *
+const char *
 output_move_simode_const (operands)
      rtx *operands;
 {
@@ -1391,6 +1538,9 @@ output_move_simode_const (operands)
          || !(GET_CODE (operands[0]) == MEM
               && MEM_VOLATILE_P (operands[0]))))
     return "clr%.l %0";
+  else if (operands[1] == const0_rtx
+          && ADDRESS_REG_P (operands[0]))
+    return "sub%.l %0,%0";
   else if (DATA_REG_P (operands[0]))
     return output_move_const_into_data_reg (operands);
   else if (ADDRESS_REG_P (operands[0])
@@ -1406,10 +1556,196 @@ output_move_simode_const (operands)
   return "move%.l %1,%0";
 }
 
+const char *
+output_move_simode (operands)
+     rtx *operands;
+{
+  if (GET_CODE (operands[1]) == CONST_INT)
+    return output_move_simode_const (operands);
+  else if ((GET_CODE (operands[1]) == SYMBOL_REF
+           || GET_CODE (operands[1]) == CONST)
+          && push_operand (operands[0], SImode))
+    return "pea %a1";
+  else if ((GET_CODE (operands[1]) == SYMBOL_REF
+           || GET_CODE (operands[1]) == CONST)
+          && ADDRESS_REG_P (operands[0]))
+    return "lea %a1,%0";
+  return "move%.l %1,%0";
+}
+
+const char *
+output_move_himode (operands)
+     rtx *operands;
+{
+ if (GET_CODE (operands[1]) == CONST_INT)
+    {
+      if (operands[1] == const0_rtx
+         && (DATA_REG_P (operands[0])
+             || GET_CODE (operands[0]) == MEM)
+         /* clr insns on 68000 read before writing.
+            This isn't so on the 68010, but we have no TARGET_68010.  */
+         && ((TARGET_68020 || TARGET_5200)
+             || !(GET_CODE (operands[0]) == MEM
+                  && MEM_VOLATILE_P (operands[0]))))
+       return "clr%.w %0";
+      else if (operands[1] == const0_rtx
+              && ADDRESS_REG_P (operands[0]))
+       return "sub%.l %0,%0";
+      else if (DATA_REG_P (operands[0])
+              && INTVAL (operands[1]) < 128
+              && INTVAL (operands[1]) >= -128)
+       {
+#if defined(MOTOROLA) && !defined(CRDS)
+         return "moveq%.l %1,%0";
+#else
+         return "moveq %1,%0";
+#endif
+       }
+      else if (INTVAL (operands[1]) < 0x8000
+              && INTVAL (operands[1]) >= -0x8000)
+       return "move%.w %1,%0";
+    }
+  else if (CONSTANT_P (operands[1]))
+    return "move%.l %1,%0";
+#ifndef SGS_NO_LI
+  /* Recognize the insn before a tablejump, one that refers
+     to a table of offsets.  Such an insn will need to refer
+     to a label on the insn.  So output one.  Use the label-number
+     of the table of offsets to generate this label.  This code,
+     and similar code below, assumes that there will be at most one
+     reference to each table.  */
+  if (GET_CODE (operands[1]) == MEM
+      && GET_CODE (XEXP (operands[1], 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == LABEL_REF
+      && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != PLUS)
+    {
+      rtx labelref = XEXP (XEXP (operands[1], 0), 1);
+#if defined (MOTOROLA) && !defined (SGS_SWITCH_TABLES)
+#ifdef SGS
+      asm_fprintf (asm_out_file, "\tset %LLI%d,.+2\n",
+                  CODE_LABEL_NUMBER (XEXP (labelref, 0)));
+#else /* not SGS */
+      asm_fprintf (asm_out_file, "\t.set %LLI%d,.+2\n",
+                  CODE_LABEL_NUMBER (XEXP (labelref, 0)));
+#endif /* not SGS */
+#else /* SGS_SWITCH_TABLES or not MOTOROLA */
+      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LI",
+                                CODE_LABEL_NUMBER (XEXP (labelref, 0)));
+#ifdef SGS_SWITCH_TABLES
+      /* Set flag saying we need to define the symbol
+        LD%n (with value L%n-LI%n) at the end of the switch table.  */
+      switch_table_difference_label_flag = 1;
+#endif /* SGS_SWITCH_TABLES */
+#endif /* SGS_SWITCH_TABLES or not MOTOROLA */
+    }
+#endif /* SGS_NO_LI */
+  return "move%.w %1,%0";
+}
+
+const char *
+output_move_qimode (operands)
+     rtx *operands;
+{
+  rtx xoperands[4];
+
+  /* This is probably useless, since it loses for pushing a struct
+     of several bytes a byte at a time.         */
+  /* 68k family always modifies the stack pointer by at least 2, even for
+     byte pushes.  The 5200 (coldfire) does not do this.  */
+  if (GET_CODE (operands[0]) == MEM
+      && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
+      && XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx
+      && ! ADDRESS_REG_P (operands[1])
+      && ! TARGET_5200)
+    {
+      xoperands[1] = operands[1];
+      xoperands[2]
+       = gen_rtx_MEM (QImode,
+                      gen_rtx_PLUS (VOIDmode, stack_pointer_rtx, const1_rtx));
+      /* Just pushing a byte puts it in the high byte of the halfword. */
+      /* We must put it in the low-order, high-numbered byte.  */
+      if (!reg_mentioned_p (stack_pointer_rtx, operands[1]))
+       {
+         xoperands[3] = stack_pointer_rtx;
+#ifndef NO_ADDSUB_Q
+         output_asm_insn ("subq%.l %#2,%3\n\tmove%.b %1,%2", xoperands);
+#else
+         output_asm_insn ("sub%.l %#2,%3\n\tmove%.b %1,%2", xoperands);
+#endif
+       }
+      else
+       output_asm_insn ("move%.b %1,%-\n\tmove%.b %@,%2", xoperands);
+      return "";
+    }
+
+  /* clr and st insns on 68000 read before writing.
+     This isn't so on the 68010, but we have no TARGET_68010.  */
+  if (!ADDRESS_REG_P (operands[0])
+      && ((TARGET_68020 || TARGET_5200)
+         || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+    {
+      if (operands[1] == const0_rtx)
+       return "clr%.b %0";
+      if ((!TARGET_5200 || DATA_REG_P (operands[0]))
+         && GET_CODE (operands[1]) == CONST_INT
+         && (INTVAL (operands[1]) & 255) == 255)
+       {
+         CC_STATUS_INIT;
+         return "st %0";
+       }
+    }
+  if (GET_CODE (operands[1]) == CONST_INT
+      && DATA_REG_P (operands[0])
+      && INTVAL (operands[1]) < 128
+      && INTVAL (operands[1]) >= -128)
+    {
+#if defined(MOTOROLA) && !defined(CRDS)
+      return "moveq%.l %1,%0";
+#else
+      return "moveq %1,%0";
+#endif
+    }
+  if (operands[1] == const0_rtx && ADDRESS_REG_P (operands[0]))
+    return "sub%.l %0,%0";
+  if (GET_CODE (operands[1]) != CONST_INT && CONSTANT_P (operands[1]))
+    return "move%.l %1,%0";
+  /* 68k family (including the 5200 coldfire) does not support byte moves to
+     from address registers.  */
+  if (ADDRESS_REG_P (operands[0]) || ADDRESS_REG_P (operands[1]))
+    return "move%.w %1,%0";
+  return "move%.b %1,%0";
+}
+
+const char *
+output_move_stricthi (operands)
+     rtx *operands;
+{
+  if (operands[1] == const0_rtx
+      /* clr insns on 68000 read before writing.
+        This isn't so on the 68010, but we have no TARGET_68010.  */
+      && ((TARGET_68020 || TARGET_5200)
+         || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+    return "clr%.w %0";
+  return "move%.w %1,%0";
+}
+
+const char *
+output_move_strictqi (operands)
+     rtx *operands;
+{
+  if (operands[1] == const0_rtx
+      /* clr insns on 68000 read before writing.
+         This isn't so on the 68010, but we have no TARGET_68010.  */
+      && ((TARGET_68020 || TARGET_5200)
+          || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
+    return "clr%.b %0";
+  return "move%.b %1,%0";
+}
+
 /* 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;
 {
@@ -1426,7 +1762,7 @@ singlemove_string (operands)
 /* Output assembler code to perform a doubleword move insn
    with operands OPERANDS.  */
 
-char *
+const char *
 output_move_double (operands)
      rtx *operands;
 {
@@ -1493,11 +1829,11 @@ output_move_double (operands)
       else
         output_asm_insn ("subq%.l %#8,%0", operands);
       if (GET_MODE (operands[1]) == XFmode)
-       operands[0] = gen_rtx (MEM, XFmode, operands[0]);
+       operands[0] = gen_rtx_MEM (XFmode, operands[0]);
       else if (GET_MODE (operands[0]) == DFmode)
-       operands[0] = gen_rtx (MEM, DFmode, operands[0]);
+       operands[0] = gen_rtx_MEM (DFmode, operands[0]);
       else
-       operands[0] = gen_rtx (MEM, DImode, operands[0]);
+       operands[0] = gen_rtx_MEM (DImode, operands[0]);
       optype0 = OFFSOP;
     }
   if (optype0 == POPOP && optype1 == PUSHOP)
@@ -1508,11 +1844,11 @@ output_move_double (operands)
       else
         output_asm_insn ("subq%.l %#8,%1", operands);
       if (GET_MODE (operands[1]) == XFmode)
-       operands[1] = gen_rtx (MEM, XFmode, operands[1]);
+       operands[1] = gen_rtx_MEM (XFmode, operands[1]);
       else if (GET_MODE (operands[1]) == DFmode)
-       operands[1] = gen_rtx (MEM, DFmode, operands[1]);
+       operands[1] = gen_rtx_MEM (DFmode, operands[1]);
       else
-       operands[1] = gen_rtx (MEM, DImode, operands[1]);
+       operands[1] = gen_rtx_MEM (DImode, operands[1]);
       optype1 = OFFSOP;
     }
 
@@ -1538,8 +1874,8 @@ output_move_double (operands)
     {
       if (optype0 == REGOP)
        {
-         latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2);
-         middlehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
+         latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
+         middlehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
        }
       else if (optype0 == OFFSOP)
        {
@@ -1554,8 +1890,8 @@ output_move_double (operands)
 
       if (optype1 == REGOP)
        {
-         latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
-         middlehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
+         latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
+         middlehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
        }
       else if (optype1 == OFFSOP)
        {
@@ -1596,14 +1932,14 @@ output_move_double (operands)
     /* size is not 12: */
     {
       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], size - 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], size - 4);
       else if (optype1 == CNSTOP)
@@ -1628,7 +1964,7 @@ output_move_double (operands)
   if (optype0 == REGOP
       && (optype1 == OFFSOP || optype1 == MEMOP))
     {
-      rtx testlow = gen_rtx (REG, SImode, REGNO (operands[0]));
+      rtx testlow = gen_rtx_REG (SImode, REGNO (operands[0]));
 
       if (reg_overlap_mentioned_p (testlow, XEXP (operands[1], 0))
          && reg_overlap_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
@@ -1642,13 +1978,13 @@ compadr:
          output_asm_insn ("lea %a1,%0", xops);
          if( GET_MODE (operands[1]) == XFmode )
            {
-             operands[1] = gen_rtx (MEM, XFmode, latehalf[0]);
+             operands[1] = gen_rtx_MEM (XFmode, latehalf[0]);
              middlehalf[1] = adj_offsettable_operand (operands[1], size-8);
              latehalf[1] = adj_offsettable_operand (operands[1], size-4);
            }
          else
            {
-             operands[1] = gen_rtx (MEM, DImode, latehalf[0]);
+             operands[1] = gen_rtx_MEM (DImode, latehalf[0]);
              latehalf[1] = adj_offsettable_operand (operands[1], size-4);
            }
        }
@@ -1801,6 +2137,94 @@ find_addr_reg (addr)
     return addr;
   abort ();
 }
+
+/* Output assembler code to perform a 32 bit 3 operand add.  */
+
+const char *
+output_addsi3 (operands)
+     rtx *operands;
+{
+  if (! operands_match_p (operands[0], operands[1]))
+    {
+      if (!ADDRESS_REG_P (operands[1]))
+       {
+         rtx tmp = operands[1];
+
+         operands[1] = operands[2];
+         operands[2] = tmp;
+       }
+
+      /* These insns can result from reloads to access
+        stack slots over 64k from the frame pointer.  */
+      if (GET_CODE (operands[2]) == CONST_INT
+         && INTVAL (operands[2]) + 0x8000 >= (unsigned) 0x10000)
+        return "move%.l %2,%0\n\tadd%.l %1,%0";
+#ifdef SGS
+      if (GET_CODE (operands[2]) == REG)
+       return "lea 0(%1,%2.l),%0";
+      else
+       return "lea %c2(%1),%0";
+#else /* not SGS */
+#ifdef MOTOROLA
+      if (GET_CODE (operands[2]) == REG)
+       return "lea (%1,%2.l),%0";
+      else
+       return "lea (%c2,%1),%0";
+#else /* not MOTOROLA (MIT syntax) */
+      if (GET_CODE (operands[2]) == REG)
+       return "lea %1@(0,%2:l),%0";
+      else
+       return "lea %1@(%c2),%0";
+#endif /* not MOTOROLA */
+#endif /* not SGS */
+    }
+  if (GET_CODE (operands[2]) == CONST_INT)
+    {
+#ifndef NO_ADDSUB_Q
+      if (INTVAL (operands[2]) > 0
+         && INTVAL (operands[2]) <= 8)
+       return "addq%.l %2,%0";
+      if (INTVAL (operands[2]) < 0
+         && INTVAL (operands[2]) >= -8)
+        {
+         operands[2] = GEN_INT (- INTVAL (operands[2]));
+         return "subq%.l %2,%0";
+       }
+      /* On the CPU32 it is faster to use two addql instructions to
+        add a small integer (8 < N <= 16) to a register.
+        Likewise for subql. */
+      if (TARGET_CPU32 && REG_P (operands[0]))
+       {
+         if (INTVAL (operands[2]) > 8
+             && INTVAL (operands[2]) <= 16)
+           {
+             operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+             return "addq%.l %#8,%0\n\taddq%.l %2,%0";
+           }
+         if (INTVAL (operands[2]) < -8
+             && INTVAL (operands[2]) >= -16)
+           {
+             operands[2] = GEN_INT (- INTVAL (operands[2]) - 8);
+             return "subq%.l %#8,%0\n\tsubq%.l %2,%0";
+           }
+       }
+#endif
+      if (ADDRESS_REG_P (operands[0])
+         && INTVAL (operands[2]) >= -0x8000
+         && INTVAL (operands[2]) < 0x8000)
+       {
+         if (TARGET_68040)
+           return "add%.w %2,%0";
+         else
+#ifdef MOTOROLA  
+           return "lea (%c2,%0),%0";
+#else
+           return "lea %0@(%c2),%0";
+#endif
+       }
+    }
+  return "add%.l %2,%0";
+}
 \f
 /* Store in cc_status the expressions that the condition codes will
    describe after execution of an instruction whose pattern is EXP.
@@ -1811,6 +2235,7 @@ find_addr_reg (addr)
    possibly invalid to use the saved cc's.  In those cases we clear out
    some or all of the saved cc's so they won't be used.  */
 
+void
 notice_update_cc (exp, insn)
      rtx exp;
      rtx insn;
@@ -1834,11 +2259,9 @@ notice_update_cc (exp, insn)
        }
       else if (ADDRESS_REG_P (SET_DEST (exp)))
        {
-         if (cc_status.value1
-             && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
+         if (cc_status.value1 && modified_in_p (cc_status.value1, insn))
            cc_status.value1 = 0;
-         if (cc_status.value2
-             && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
+         if (cc_status.value2 && modified_in_p (cc_status.value2, insn))
            cc_status.value2 = 0; 
        }
       else if (!FP_REG_P (SET_DEST (exp))
@@ -1908,6 +2331,9 @@ notice_update_cc (exp, insn)
           Thus, the cc's are set for r2.
           This can set N bit spuriously. */
        cc_status.flags |= CC_NOT_NEGATIVE; 
+
+      default:
+       break;
       }
   if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
       && cc_status.value2
@@ -1920,7 +2346,7 @@ notice_update_cc (exp, insn)
     cc_status.flags = CC_IN_68881;
 }
 \f
-char *
+const char *
 output_move_const_double (operands)
      rtx *operands;
 {
@@ -1954,7 +2380,7 @@ output_move_const_double (operands)
     }
 }
 
-char *
+const char *
 output_move_const_single (operands)
      rtx *operands;
 {
@@ -1997,7 +2423,7 @@ output_move_const_single (operands)
   
 static int inited_68881_table = 0;
 
-char *strings_68881[7] = {
+static const char *const strings_68881[7] = {
   "0.0",
   "1.0",
   "10.0",
@@ -2046,14 +2472,14 @@ standard_68881_constant_p (x)
 {
   REAL_VALUE_TYPE r;
   int i;
-  enum machine_mode mode;
 
 #ifdef NO_ASM_FMOVECR
   return 0;
 #endif
 
-  /* fmovecr must be emulated on the 68040, so it shouldn't be used at all. */
-  if (TARGET_68040)
+  /* fmovecr must be emulated on the 68040 and 68060, so it shouldn't be
+     used at all on those chips. */
+  if (TARGET_68040 || TARGET_68060)
     return 0;
 
 #ifndef REAL_ARITHMETIC
@@ -2068,9 +2494,11 @@ standard_68881_constant_p (x)
 
   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
 
+  /* Use REAL_VALUES_IDENTICAL instead of REAL_VALUES_EQUAL so that -0.0
+     is rejected.  */
   for (i = 0; i < 6; i++)
     {
-      if (REAL_VALUES_EQUAL (r, values_68881[i]))
+      if (REAL_VALUES_IDENTICAL (r, values_68881[i]))
         return (codes_68881[i]);
     }
   
@@ -2127,7 +2555,7 @@ floating_exact_log2 (x)
 
 static int inited_FPA_table = 0;
 
-char *strings_FPA[38] = {
+static const char *const strings_FPA[38] = {
 /* small rationals */
   "0.0",
   "1.0",
@@ -2323,6 +2751,8 @@ standard_sun_fpa_constant_p (x)
    'b' for byte insn (no effect, on the Sun; this is for the ISI).
    'd' to force memory addressing to be absolute, not relative.
    'f' for float insn (print a CONST_DOUBLE as a float rather than in hex)
+   'o' for operands to go directly to output_operand_address (bypassing
+       print_operand_address--used only for SYMBOL_REFs under TARGET_PCREL)
    'w' for FPA insn (print a CONST_DOUBLE as a SunFPA constant rather
        than directly).  Second part of 'y' below.
    'x' for float insn (print a CONST_DOUBLE as a float rather than in hex),
@@ -2339,7 +2769,9 @@ print_operand (file, op, letter)
      rtx op;                   /* operand to print */
      int letter;               /* %<letter> or 0 */
 {
+#ifdef SUPPORT_SUN_FPA
   int i;
+#endif
 
   if (letter == '.')
     {
@@ -2397,6 +2829,14 @@ print_operand (file, op, letter)
     {
       asm_fprintf (file, "%R");
     }
+  else if (letter == 'o')
+    {
+      /* This is only for direct addresses with TARGET_PCREL */
+      if (GET_CODE (op) != MEM || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
+          || !TARGET_PCREL) 
+       abort ();
+      output_addr_const (file, XEXP (op, 0));
+    }
   else if (GET_CODE (op) == REG)
     {
 #ifdef SUPPORT_SUN_FPA
@@ -2462,7 +2902,14 @@ print_operand (file, op, letter)
     }
   else
     {
-      asm_fprintf (file, "%0I"); output_addr_const (file, op);
+      /* Use `print_operand_address' instead of `output_addr_const'
+        to ensure that we print relevant PIC stuff.  */
+      asm_fprintf (file, "%0I");
+      if (TARGET_PCREL
+         && (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST))
+       print_operand_address (file, op);
+      else
+       output_addr_const (file, op);
     }
 }
 
@@ -2696,7 +3143,11 @@ print_operand_address (file, addr)
              {
                output_addr_const (file, addr);
                if (flag_pic && (breg == pic_offset_table_rtx))
-                 fprintf (file, "@GOT");
+                 {
+                   fprintf (file, "@GOT");
+                   if (flag_pic == 1)
+                     fprintf (file, ".w");
+                 }
              }
            fprintf (file, "(%s", reg_names[REGNO (breg)]);
            if (ireg != 0)
@@ -2759,7 +3210,7 @@ print_operand_address (file, addr)
            fprintf (file, "l)");
            break;
          }
-       /* FALL-THROUGH (is this really what we want? */
+       /* FALL-THROUGH (is this really what we want? */
       default:
         if (GET_CODE (addr) == CONST_INT
            && INTVAL (addr) < 0x8000
@@ -2776,9 +3227,40 @@ print_operand_address (file, addr)
            fprintf (file, "%d:w", INTVAL (addr));
 #endif
          }
-       else
+       else if (GET_CODE (addr) == CONST_INT)
+         {
+           fprintf (file,
+#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
+                    "%d",
+#else
+                    "%ld",
+#endif
+                    INTVAL (addr));
+         }
+       else if (TARGET_PCREL)
          {
+           fputc ('(', file);
            output_addr_const (file, addr);
+           if (flag_pic == 1)
+             asm_fprintf (file, ":w,%Rpc)");
+           else
+             asm_fprintf (file, ":l,%Rpc)");
+         }
+       else
+         {
+           /* Special case for SYMBOL_REF if the symbol name ends in
+              `.<letter>', this can be mistaken as a size suffix.  Put
+              the name in parentheses.  */
+           if (GET_CODE (addr) == SYMBOL_REF
+               && strlen (XSTR (addr, 0)) > 2
+               && XSTR (addr, 0)[strlen (XSTR (addr, 0)) - 2] == '.')
+             {
+               putc ('(', file);
+               output_addr_const (file, addr);
+               putc (')', file);
+             }
+           else
+             output_addr_const (file, addr);
          }
        break;
     }
@@ -2850,14 +3332,14 @@ strict_low_part_peephole_ok (mode, first_insn, target)
 int
 const_uint32_operand (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
 #if HOST_BITS_PER_WIDE_INT > 32
   /* All allowed constants will fit a CONST_INT.  */
   return (GET_CODE (op) == CONST_INT
          && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL));
 #else
-  return ((GET_CODE (op) == CONST_INT && INTVAL (op) >= 0)
+  return (GET_CODE (op) == CONST_INT
          || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0));
 #endif
 }
@@ -2869,9 +3351,249 @@ const_uint32_operand (op, mode)
 int
 const_sint32_operand (op, mode)
      rtx op;
-     enum machine_mode mode;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
 {
   /* All allowed constants will fit a CONST_INT.  */
   return (GET_CODE (op) == CONST_INT
          && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff));
 }
+
+/* Operand predicates for implementing asymmetric pc-relative addressing
+   on m68k.  The m68k supports pc-relative addressing (mode 7, register 2)
+   when used as a source operand, but not as a destintation operand.
+
+   We model this by restricting the meaning of the basic predicates
+   (general_operand, memory_operand, etc) to forbid the use of this
+   addressing mode, and then define the following predicates that permit
+   this addressing mode.  These predicates can then be used for the
+   source operands of the appropriate instructions.
+
+   n.b.  While it is theoretically possible to change all machine patterns
+   to use this addressing more where permitted by the architecture,
+   it has only been implemented for "common" cases: SImode, HImode, and
+   QImode operands, and only for the principle operations that would
+   require this addressing mode: data movement and simple integer operations.
+
+   In parallel with these new predicates, two new constraint letters
+   were defined: 'S' and 'T'.  'S' is the -mpcrel analog of 'm'.
+   'T' replaces 's' in the non-pcrel case.  It is a no-op in the pcrel case.
+   In the pcrel case 's' is only valid in combination with 'a' registers.
+   See addsi3, subsi3, cmpsi, and movsi patterns for a better understanding
+   of how these constraints are used.
+
+   The use of these predicates is strictly optional, though patterns that
+   don't will cause an extra reload register to be allocated where one
+   was not necessary:
+
+       lea (abc:w,%pc),%a0     ; need to reload address
+       moveq &1,%d1            ; since write to pc-relative space
+       movel %d1,%a0@          ; is not allowed
+       ...
+       lea (abc:w,%pc),%a1     ; no need to reload address here
+       movel %a1@,%d0          ; since "movel (abc:w,%pc),%d0" is ok
+
+   For more info, consult tiemann@cygnus.com.
+
+
+   All of the ugliness with predicates and constraints is due to the
+   simple fact that the m68k does not allow a pc-relative addressing
+   mode as a destination.  gcc does not distinguish between source and
+   destination addresses.  Hence, if we claim that pc-relative address
+   modes are valid, e.g. GO_IF_LEGITIMATE_ADDRESS accepts them, then we
+   end up with invalid code.  To get around this problem, we left
+   pc-relative modes as invalid addresses, and then added special
+   predicates and constraints to accept them.
+
+   A cleaner way to handle this is to modify gcc to distinguish
+   between source and destination addresses.  We can then say that
+   pc-relative is a valid source address but not a valid destination
+   address, and hopefully avoid a lot of the predicate and constraint
+   hackery.  Unfortunately, this would be a pretty big change.  It would
+   be a useful change for a number of ports, but there aren't any current
+   plans to undertake this.
+
+   ***************************************************************************/
+
+
+/* Special case of a general operand that's used as a source operand.
+   Use this to permit reads from PC-relative memory when -mpcrel
+   is specified.  */
+
+int
+general_src_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (TARGET_PCREL
+      && GET_CODE (op) == MEM
+      && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+         || GET_CODE (XEXP (op, 0)) == LABEL_REF
+         || GET_CODE (XEXP (op, 0)) == CONST))
+    return 1;
+  return general_operand (op, mode);
+}
+
+/* Special case of a nonimmediate operand that's used as a source.
+   Use this to permit reads from PC-relative memory when -mpcrel
+   is specified.  */
+
+int
+nonimmediate_src_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (TARGET_PCREL && GET_CODE (op) == MEM
+      && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+         || GET_CODE (XEXP (op, 0)) == LABEL_REF
+         || GET_CODE (XEXP (op, 0)) == CONST))
+    return 1;
+  return nonimmediate_operand (op, mode);
+}
+
+/* Special case of a memory operand that's used as a source.
+   Use this to permit reads from PC-relative memory when -mpcrel
+   is specified.  */
+
+int
+memory_src_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (TARGET_PCREL && GET_CODE (op) == MEM
+      && (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+         || GET_CODE (XEXP (op, 0)) == LABEL_REF
+         || GET_CODE (XEXP (op, 0)) == CONST))
+    return 1;
+  return memory_operand (op, mode);
+}
+
+/* Predicate that accepts only a pc-relative address.  This is needed
+   because pc-relative addresses don't satisfy the predicate
+   "general_src_operand".  */
+
+int
+pcrel_address (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF
+         || GET_CODE (op) == CONST);
+}
+
+const char *
+output_andsi3 (operands)
+     rtx *operands;
+{
+  int logval;
+  if (GET_CODE (operands[2]) == CONST_INT
+      && (INTVAL (operands[2]) | 0xffff) == 0xffffffff
+      && (DATA_REG_P (operands[0])
+         || offsettable_memref_p (operands[0]))
+      && !TARGET_5200)
+    {
+      if (GET_CODE (operands[0]) != REG)
+        operands[0] = adj_offsettable_operand (operands[0], 2);
+      operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+      /* Do not delete a following tstl %0 insn; that would be incorrect.  */
+      CC_STATUS_INIT;
+      if (operands[2] == const0_rtx)
+        return "clr%.w %0";
+      return "and%.w %2,%0";
+    }
+  if (GET_CODE (operands[2]) == CONST_INT
+      && (logval = exact_log2 (~ INTVAL (operands[2]))) >= 0
+      && (DATA_REG_P (operands[0])
+          || offsettable_memref_p (operands[0])))
+    {
+      if (DATA_REG_P (operands[0]))
+        {
+          operands[1] = GEN_INT (logval);
+        }
+      else
+        {
+         operands[0] = adj_offsettable_operand (operands[0], 3 - (logval / 8));
+         operands[1] = GEN_INT (logval % 8);
+        }
+      /* This does not set condition codes in a standard way.  */
+      CC_STATUS_INIT;
+      return "bclr %1,%0";
+    }
+  return "and%.l %2,%0";
+}
+
+const char *
+output_iorsi3 (operands)
+     rtx *operands;
+{
+  register int logval;
+  if (GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) >> 16 == 0
+      && (DATA_REG_P (operands[0])
+         || offsettable_memref_p (operands[0]))
+      && !TARGET_5200)
+    {
+      if (GET_CODE (operands[0]) != REG)
+        operands[0] = adj_offsettable_operand (operands[0], 2);
+      /* Do not delete a following tstl %0 insn; that would be incorrect.  */
+      CC_STATUS_INIT;
+      if (INTVAL (operands[2]) == 0xffff)
+       return "mov%.w %2,%0";
+      return "or%.w %2,%0";
+    }
+  if (GET_CODE (operands[2]) == CONST_INT
+      && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
+      && (DATA_REG_P (operands[0])
+         || offsettable_memref_p (operands[0])))
+    {
+      if (DATA_REG_P (operands[0]))
+       {
+         operands[1] = GEN_INT (logval);
+       }
+      else
+        {
+         operands[0] = adj_offsettable_operand (operands[0], 3 - (logval / 8));
+         operands[1] = GEN_INT (logval % 8);
+       }
+      CC_STATUS_INIT;
+      return "bset %1,%0";
+    }
+  return "or%.l %2,%0";
+}
+
+const char *
+output_xorsi3 (operands)
+     rtx *operands;
+{
+  register int logval;
+  if (GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) >> 16 == 0
+      && (offsettable_memref_p (operands[0]) || DATA_REG_P (operands[0]))
+      && !TARGET_5200)
+    {
+      if (! DATA_REG_P (operands[0]))
+       operands[0] = adj_offsettable_operand (operands[0], 2);
+      /* Do not delete a following tstl %0 insn; that would be incorrect.  */
+      CC_STATUS_INIT;
+      if (INTVAL (operands[2]) == 0xffff)
+       return "not%.w %0";
+      return "eor%.w %2,%0";
+    }
+  if (GET_CODE (operands[2]) == CONST_INT
+      && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
+      && (DATA_REG_P (operands[0])
+         || offsettable_memref_p (operands[0])))
+    {
+      if (DATA_REG_P (operands[0]))
+       {
+         operands[1] = GEN_INT (logval);
+       }
+      else
+        {
+         operands[0] = adj_offsettable_operand (operands[0], 3 - (logval / 8));
+         operands[1] = GEN_INT (logval % 8);
+       }
+      CC_STATUS_INIT;
+      return "bchg %1,%0";
+    }
+  return "eor%.l %2,%0";
+}