]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/config/pdp11/pdp11.c
Factor unrelated declarations out of tree.h.
[thirdparty/gcc.git] / gcc / config / pdp11 / pdp11.c
index 35a76fadc22b72a2f223c60ffa3a4e47861102b7..42237b5d798ca1c47658c78e60cbdbfdf051af31 100644 (file)
@@ -1,6 +1,5 @@
 /* Subroutines for gcc2 for pdp11.
-   Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2004, 2005,
-   2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1994-2013 Free Software Foundation, Inc.
    Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at).
 
 This file is part of GCC.
@@ -34,12 +33,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 #include "recog.h"
 #include "tree.h"
+#include "stor-layout.h"
+#include "varasm.h"
+#include "calls.h"
 #include "expr.h"
 #include "diagnostic-core.h"
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
 #include "df.h"
+#include "opts.h"
+#include "dbxout.h"
 
 /* this is the current value returned by the macro FIRST_PARM_OFFSET 
    defined in tm.h */
@@ -138,32 +142,20 @@ decode_pdp11_d (const struct real_format *fmt ATTRIBUTE_UNUSED,
 /* This is where the condition code register lives.  */
 /* rtx cc0_reg_rtx; - no longer needed? */
 
-static bool pdp11_handle_option (size_t, const char *, int);
-static void pdp11_option_init_struct (struct gcc_options *);
-static rtx find_addr_reg (rtx); 
 static const char *singlemove_string (rtx *);
 static bool pdp11_assemble_integer (rtx, unsigned int, int);
-static void pdp11_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void pdp11_output_function_epilogue (FILE *, HOST_WIDE_INT);
-static bool pdp11_rtx_costs (rtx, int, int, int *, bool);
+static bool pdp11_rtx_costs (rtx, int, int, int, int *, bool);
 static bool pdp11_return_in_memory (const_tree, const_tree);
 static rtx pdp11_function_value (const_tree, const_tree, bool);
 static rtx pdp11_libcall_value (enum machine_mode, const_rtx);
 static bool pdp11_function_value_regno_p (const unsigned int);
 static void pdp11_trampoline_init (rtx, tree, rtx);
-static rtx pdp11_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+static rtx pdp11_function_arg (cumulative_args_t, enum machine_mode,
                               const_tree, bool);
-static void pdp11_function_arg_advance (CUMULATIVE_ARGS *,
+static void pdp11_function_arg_advance (cumulative_args_t,
                                        enum machine_mode, const_tree, bool);
 static void pdp11_conditional_register_usage (void);
-
-/* Implement TARGET_OPTION_OPTIMIZATION_TABLE.  */
-
-static const struct default_options pdp11_option_optimization_table[] =
-  {
-    { OPT_LEVELS_3_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
-    { OPT_LEVELS_NONE, 0, NULL, 0 }
-  };
+static bool pdp11_legitimate_constant_p (enum machine_mode, rtx);
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_BYTE_OP
@@ -175,26 +167,11 @@ static const struct default_options pdp11_option_optimization_table[] =
 #undef TARGET_ASM_INTEGER
 #define TARGET_ASM_INTEGER pdp11_assemble_integer
 
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE pdp11_output_function_prologue
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE pdp11_output_function_epilogue
-
 #undef TARGET_ASM_OPEN_PAREN
 #define TARGET_ASM_OPEN_PAREN "["
 #undef TARGET_ASM_CLOSE_PAREN
 #define TARGET_ASM_CLOSE_PAREN "]"
 
-#undef TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS \
-  (MASK_FPU | MASK_45 | TARGET_UNIX_ASM_DEFAULT)
-#undef TARGET_HANDLE_OPTION
-#define TARGET_HANDLE_OPTION pdp11_handle_option
-#undef TARGET_OPTION_OPTIMIZATION_TABLE
-#define TARGET_OPTION_OPTIMIZATION_TABLE pdp11_option_optimization_table
-#undef TARGET_OPTION_INIT_STRUCT
-#define TARGET_OPTION_INIT_STRUCT pdp11_option_init_struct
-
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS pdp11_rtx_costs
 
@@ -233,123 +210,105 @@ static const struct default_options pdp11_option_optimization_table[] =
 
 #undef  TARGET_CONDITIONAL_REGISTER_USAGE
 #define TARGET_CONDITIONAL_REGISTER_USAGE pdp11_conditional_register_usage
-\f
-/* Implement TARGET_HANDLE_OPTION.  */
 
-static bool
-pdp11_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
-                    int value ATTRIBUTE_UNUSED)
-{
-  switch (code)
-    {
-    case OPT_m10:
-      target_flags &= ~(MASK_40 | MASK_45);
-      return true;
+#undef  TARGET_ASM_FUNCTION_SECTION
+#define TARGET_ASM_FUNCTION_SECTION pdp11_function_section
 
-    default:
-      return true;
-    }
-}
+#undef  TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND pdp11_asm_print_operand
 
-/* Implement TARGET_OPTION_INIT_STRUCT.  */
+#undef  TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P pdp11_asm_print_operand_punct_valid_p
 
-static void
-pdp11_option_init_struct (struct gcc_options *opts)
+#undef  TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P pdp11_legitimate_constant_p
+\f
+/* A helper function to determine if REGNO should be saved in the
+   current function's stack frame.  */
+
+static inline bool
+pdp11_saved_regno (unsigned regno)
 {
-  opts->x_flag_finite_math_only = 0;
-  opts->x_flag_trapping_math = 0;
-  opts->x_flag_signaling_nans = 0;
+  return !call_used_regs[regno] && df_regs_ever_live_p (regno);
 }
 
-/*
-   stream 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 macro is responsible for
-   knowing which registers should not be saved even if used.  
-*/
+/* Expand the function prologue.  */
 
-static void
-pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size)
+void
+pdp11_expand_prologue (void)
 {                                                             
-    HOST_WIDE_INT fsize = ((size) + 1) & ~1;
-    int regno;
-    int via_ac = -1;
+  HOST_WIDE_INT fsize = get_frame_size ();
+  unsigned regno;
+  rtx x, via_ac = NULL;
 
-    fprintf (stream,
-            "\n\t;     /* function prologue %s*/\n",
-            current_function_name ());
-
-    /* if we are outputting code for main, 
-       the switch FPU to right mode if TARGET_FPU */
-    if (MAIN_NAME_P (DECL_NAME (current_function_decl)) && TARGET_FPU)
+  /* If we are outputting code for main, the switch FPU to the
+     right mode if TARGET_FPU.  */
+  if (MAIN_NAME_P (DECL_NAME (current_function_decl)) && TARGET_FPU)
     {
-       fprintf(stream,
-               "\t;/* switch cpu to double float, single integer */\n");
-       fprintf(stream, "\tsetd\n");
-       fprintf(stream, "\tseti\n\n");
+      emit_insn (gen_setd ());
+      emit_insn (gen_seti ());
     }
     
-    if (frame_pointer_needed)                                  
-    {                                                          
-       fprintf(stream, "\tmov r5, -(sp)\n");                   
-       fprintf(stream, "\tmov sp, r5\n");                              
-    }                                                          
-    else                                                               
+  if (frame_pointer_needed)                                    
     {                                                          
-       /* DON'T SAVE FP */
+      x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
+      x = gen_frame_mem (Pmode, x);
+      emit_move_insn (x, hard_frame_pointer_rtx);
+
+      emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
     }                                                          
 
-    /* make frame */
-    if (fsize)                                                 
-       asm_fprintf (stream, "\tsub $%#wo, sp\n", fsize);
-
-    /* save CPU registers  */
-    for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++)                               
-      if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])      
-           if (! ((regno == FRAME_POINTER_REGNUM)                      
-                  && frame_pointer_needed))                            
-               fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]);        
-    /* fpu regs saving */
-    
-    /* via_ac specifies the ac to use for saving ac4, ac5 */
-    via_ac = -1;
-    
-    for (regno = AC0_REGNUM; regno <= AC5_REGNUM ; regno++) 
+  /* Make frame.  */
+  if (fsize)
     {
-       /* ac0 - ac3 */                                         
-       if (LOAD_FPU_REG_P(regno)
-           && df_regs_ever_live_p (regno) 
-           && ! call_used_regs[regno])
-       {
-           fprintf (stream, "\tstd %s, -(sp)\n", reg_names[regno]);
-           via_ac = regno;
-       }
-       
-       /* maybe make ac4, ac5 call used regs?? */
-       /* ac4 - ac5 */
-       if (NO_LOAD_FPU_REG_P(regno)
-           && df_regs_ever_live_p (regno)
-           && ! call_used_regs[regno])
-       {
-         gcc_assert (via_ac != -1);
-         fprintf (stream, "\tldd %s, %s\n",
-                  reg_names[regno], reg_names[via_ac]);
-         fprintf (stream, "\tstd %s, -(sp)\n", reg_names[via_ac]);
-       }
+      emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
+                            GEN_INT (-fsize)));
+
+      /* Prevent frame references via the frame pointer from being
+        scheduled before the frame is allocated.  */
+      if (frame_pointer_needed)
+       emit_insn (gen_blockage ());
     }
 
-    fprintf (stream, "\t;/* end of prologue */\n\n");          
+  /* Save CPU registers.  */
+  for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++)
+    if (pdp11_saved_regno (regno)
+       && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
+      {
+       x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
+       x = gen_frame_mem (Pmode, x);
+       emit_move_insn (x, gen_rtx_REG (Pmode, regno));
+      }
+
+  /* Save FPU registers.  */
+  for (regno = AC0_REGNUM; regno <= AC3_REGNUM; regno++) 
+    if (pdp11_saved_regno (regno))
+      {
+       x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
+       x = gen_frame_mem (DFmode, x);
+       via_ac = gen_rtx_REG (DFmode, regno);
+       emit_move_insn (x, via_ac);
+      }
+
+  /* ??? Maybe make ac4, ac5 call used regs?? */
+  for (regno = AC4_REGNUM; regno <= AC5_REGNUM; regno++)
+    if (pdp11_saved_regno (regno))
+      {
+       gcc_assert (via_ac != NULL);
+       emit_move_insn (via_ac, gen_rtx_REG (DFmode, regno));
+
+       x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
+       x = gen_frame_mem (DFmode, x);
+       emit_move_insn (x, via_ac);
+      }
 }
 
-/*
-   The function epilogue should not depend on the current stack pointer!
+/* The function epilogue should not depend on the current stack pointer!
    It should use the frame pointer only.  This is mandatory because
    of alloca; we also take advantage of it to omit stack adjustments
    before returning.  */
 
-/* maybe we can make leaf functions faster by switching to the
+/* Maybe we can make leaf functions faster by switching to the
    second register file - this way we don't have to save regs!
    leaf functions are ~ 50% of all functions (dynamically!) 
 
@@ -362,109 +321,127 @@ pdp11_output_function_prologue (FILE *stream, HOST_WIDE_INT size)
 
    maybe as option if you want to generate code for kernel mode? */
 
-static void
-pdp11_output_function_epilogue (FILE *stream, HOST_WIDE_INT size)
+void
+pdp11_expand_epilogue (void)
 {                                                              
-    HOST_WIDE_INT fsize = ((size) + 1) & ~1;
-    int i, j, k;
+  HOST_WIDE_INT fsize = get_frame_size ();
+  unsigned regno;
+  rtx x, reg, via_ac = NULL;
 
-    int via_ac;
-    
-    fprintf (stream, "\n\t;    /*function epilogue */\n");             
+  if (pdp11_saved_regno (AC4_REGNUM) || pdp11_saved_regno (AC5_REGNUM))
+    {
+      /* Find a temporary with which to restore AC4/5.  */
+      for (regno = AC0_REGNUM; regno <= AC3_REGNUM; regno++)
+       if (pdp11_saved_regno (regno))
+         {
+           via_ac = gen_rtx_REG (DFmode, regno);
+           break;
+         }
+    }
 
-    if (frame_pointer_needed)                                  
-    {                                                          
-       /* hope this is safe - m68k does it also .... */                
-        df_set_regs_ever_live (FRAME_POINTER_REGNUM, false);
-                                                               
-       for (i = PC_REGNUM, j = 0 ; i >= 0 ; i--)                               
-         if (df_regs_ever_live_p (i) && ! call_used_regs[i])           
-               j++;
-       
-       /* remember # of pushed bytes for CPU regs */
-       k = 2*j;
-       
-       /* change fp -> r5 due to the compile error on libgcc2.c */
-       for (i = PC_REGNUM ; i >= R0_REGNUM ; i--)                                      
-         if (df_regs_ever_live_p (i) && ! call_used_regs[i])           
-               fprintf(stream, "\tmov %#" HOST_WIDE_INT_PRINT "o(r5), %s\n",
-                       (-fsize-2*j--)&0xffff, reg_names[i]);
-
-       /* get ACs */                                           
-       via_ac = AC5_REGNUM;
-       
-       for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
-         if (df_regs_ever_live_p (i) && ! call_used_regs[i])
-           {
-               via_ac = i;
-               k += 8;
-           }
-       
-       for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
-       {
-           if (LOAD_FPU_REG_P(i)
-               && df_regs_ever_live_p (i)
-               && ! call_used_regs[i])
-           {
-               fprintf(stream, "\tldd %#" HOST_WIDE_INT_PRINT "o(r5), %s\n",
-                       (-fsize-k)&0xffff, reg_names[i]);
-               k -= 8;
-           }
-           
-           if (NO_LOAD_FPU_REG_P(i)
-               && df_regs_ever_live_p (i)
-               && ! call_used_regs[i])
-           {
-               gcc_assert (LOAD_FPU_REG_P(via_ac));
-                   
-               fprintf(stream, "\tldd %#" HOST_WIDE_INT_PRINT "o(r5), %s\n",
-                       (-fsize-k)&0xffff, reg_names[via_ac]);
-               fprintf(stream, "\tstd %s, %s\n", reg_names[via_ac], reg_names[i]);
-               k -= 8;
-           }
-       }
-       
-       fprintf(stream, "\tmov r5, sp\n");                              
-       fprintf (stream, "\tmov (sp)+, r5\n");                          
-    }                                                          
-    else                                                               
-    {             
-      via_ac = AC5_REGNUM;
-       
-       /* get ACs */
-       for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
-         if (df_regs_ever_live_p (i) && ! call_used_regs[i])
-               via_ac = i;
-       
-       for (i = AC5_REGNUM; i >= AC0_REGNUM; i--)
+  /* If possible, restore registers via pops.  */
+  if (!frame_pointer_needed || crtl->sp_is_unchanging)
+    {
+      /* Restore registers via pops.  */
+
+      for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--)
+       if (pdp11_saved_regno (regno))
+         {
+           x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
+           x = gen_frame_mem (DFmode, x);
+           reg = gen_rtx_REG (DFmode, regno);
+
+           if (LOAD_FPU_REG_P (regno))
+             emit_move_insn (reg, x);
+           else
+             {
+               emit_move_insn (via_ac, x);
+               emit_move_insn (reg, via_ac);
+             }
+         }
+
+      for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--)
+       if (pdp11_saved_regno (regno)
+           && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
+         {
+           x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
+           x = gen_frame_mem (Pmode, x);
+           emit_move_insn (gen_rtx_REG (Pmode, regno), x);
+         }
+    }
+  else
+    {
+      /* Restore registers via moves.  */
+      /* ??? If more than a few registers need to be restored, it's smaller
+        to generate a pointer through which we can emit pops.  Consider
+        that moves cost 2*NREG words and pops cost NREG+3 words.  This
+        means that the crossover is NREG=3.
+
+        Possible registers to use are:
+         (1) The first call-saved general register.  This register will
+               be restored with the last pop.
+         (2) R1, if it's not used as a return register.
+         (3) FP itself.  This option may result in +4 words, since we
+               may need two add imm,rn instructions instead of just one.
+               This also has the downside that we're not representing
+               the unwind info in any way, so during the epilogue the
+               debugger may get lost.  */
+
+      HOST_WIDE_INT ofs = -pdp11_sp_frame_offset ();
+
+      for (regno = AC5_REGNUM; regno >= AC0_REGNUM; regno--)
+       if (pdp11_saved_regno (regno))
+         {
+           x = plus_constant (Pmode, hard_frame_pointer_rtx, ofs);
+           x = gen_frame_mem (DFmode, x);
+           reg = gen_rtx_REG (DFmode, regno);
+
+           if (LOAD_FPU_REG_P (regno))
+             emit_move_insn (reg, x);
+           else
+             {
+               emit_move_insn (via_ac, x);
+               emit_move_insn (reg, via_ac);
+             }
+           ofs += 8;
+         }
+
+      for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--)
+       if (pdp11_saved_regno (regno)
+           && (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
+         {
+           x = plus_constant (Pmode, hard_frame_pointer_rtx, ofs);
+           x = gen_frame_mem (Pmode, x);
+           emit_move_insn (gen_rtx_REG (Pmode, regno), x);
+           ofs += 2;
+         }
+    }
+
+  /* Deallocate the stack frame.  */
+  if (fsize)
+    {
+      /* Prevent frame references via any pointer from being
+        scheduled after the frame is deallocated.  */
+      emit_insn (gen_blockage ());
+
+      if (frame_pointer_needed)
        {
-           if (LOAD_FPU_REG_P(i)
-               && df_regs_ever_live_p (i)
-               && ! call_used_regs[i])
-             fprintf(stream, "\tldd (sp)+, %s\n", reg_names[i]);
-           
-           if (NO_LOAD_FPU_REG_P(i)
-               && df_regs_ever_live_p (i)
-               && ! call_used_regs[i])
-           {
-               gcc_assert (LOAD_FPU_REG_P(via_ac));
-                   
-               fprintf(stream, "\tldd (sp)+, %s\n", reg_names[via_ac]);
-               fprintf(stream, "\tstd %s, %s\n", reg_names[via_ac], reg_names[i]);
-           }
+         /* We can deallocate the frame with a single move.  */
+         emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
        }
+      else
+       emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
+                              GEN_INT (fsize)));
+    }
+
+  if (frame_pointer_needed)
+    {
+      x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
+      x = gen_frame_mem (Pmode, x);
+      emit_move_insn (hard_frame_pointer_rtx, x);
+    }
 
-       for (i = PC_REGNUM; i >= 0; i--)                                        
-         if (df_regs_ever_live_p (i) && !call_used_regs[i])            
-               fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]);     
-                                                               
-       if (fsize)                                              
-           fprintf((stream), "\tadd $%#" HOST_WIDE_INT_PRINT "o, sp\n",
-                   (fsize)&0xffff);                    
-    }                  
-                                       
-    fprintf (stream, "\trts pc\n");                                    
-    fprintf (stream, "\t;/* end of epilogue*/\n\n\n");         
+  emit_jump_insn (gen_return ());
 }
 
 /* Return the best assembler insn template
@@ -479,408 +456,216 @@ singlemove_string (rtx *operands)
 }
 
 \f
-/* Output assembler code to perform a doubleword move insn
-   with operands OPERANDS.  */
-
-const char *
-output_move_double (rtx *operands)
+/* Expand multi-word operands (SImode or DImode) into the 2 or 4
+   corresponding HImode operands.  The number of operands is given
+   as the third argument, and the required order of the parts as
+   the fourth argument.  */
+bool
+pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount, 
+                      pdp11_action *action, pdp11_partorder order)
 {
-  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
-  rtx latehalf[2];
-  rtx addreg0 = 0, addreg1 = 0;
-
-  /* First classify both operands.  */
-
-  if (REG_P (operands[0]))
-    optype0 = REGOP;
-  else if (offsettable_memref_p (operands[0]))
-    optype0 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
-    optype0 = POPOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
-    optype0 = PUSHOP;
-  else if (GET_CODE (operands[0]) == MEM)
-    optype0 = MEMOP;
-  else
-    optype0 = RNDOP;
-
-  if (REG_P (operands[1]))
-    optype1 = REGOP;
-  else if (CONSTANT_P (operands[1])
-#if 0
-          || GET_CODE (operands[1]) == CONST_DOUBLE
-#endif
-          )
-    optype1 = CNSTOP;
-  else if (offsettable_memref_p (operands[1]))
-    optype1 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
-    optype1 = POPOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
-    optype1 = PUSHOP;
-  else if (GET_CODE (operands[1]) == MEM)
-    optype1 = MEMOP;
-  else
-    optype1 = RNDOP;
-
-  /* Check for the cases that the operand constraints are not
-     supposed to allow to happen.  Abort if we get one,
-     because generating code for these cases is painful.  */
-
-  gcc_assert (optype0 != RNDOP && optype1 != RNDOP);
-
-  /* If one operand is decrementing and one is incrementing
-     decrement the former register explicitly
-     and change that operand into ordinary indexing.  */
-
-  if (optype0 == PUSHOP && optype1 == POPOP)
-    {
-      operands[0] = XEXP (XEXP (operands[0], 0), 0);
-      output_asm_insn ("sub $4,%0", operands);
-      operands[0] = gen_rtx_MEM (SImode, operands[0]);
-      optype0 = OFFSOP;
-    }
-  if (optype0 == POPOP && optype1 == PUSHOP)
+  int words, op, w, i, sh;
+  pdp11_partorder useorder;
+  bool sameoff = false;
+  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype;
+  REAL_VALUE_TYPE r;
+  long sval[2];
+  
+  words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
+  
+  /* If either piece order is accepted and one is pre-decrement
+     while the other is post-increment, set order to be high order
+     word first.  That will force the pre-decrement to be turned
+     into a pointer adjust, then offset addressing.
+     Otherwise, if either operand uses pre-decrement, that means
+     the order is low order first. 
+     Otherwise, if both operands are registers and destination is
+     higher than source and they overlap, do low order word (highest
+     register number) first.  */
+  useorder = either;
+  if (opcount == 2)
     {
-      operands[1] = XEXP (XEXP (operands[1], 0), 0);
-      output_asm_insn ("sub $4,%1", operands);
-      operands[1] = gen_rtx_MEM (SImode, operands[1]);
-      optype1 = OFFSOP;
+      if (!REG_P (operands[0]) && !REG_P (operands[1]) &&
+         !(CONSTANT_P (operands[1]) || 
+           GET_CODE (operands[1]) == CONST_DOUBLE) &&
+         ((GET_CODE (XEXP (operands[0], 0)) == POST_INC &&
+           GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ||
+          (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC &&
+           GET_CODE (XEXP (operands[1], 0)) == POST_INC)))
+           useorder = big;
+      else if ((!REG_P (operands[0]) &&
+               GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ||
+              (!REG_P (operands[1]) &&
+               !(CONSTANT_P (operands[1]) || 
+                 GET_CODE (operands[1]) == CONST_DOUBLE) &&
+               GET_CODE (XEXP (operands[1], 0)) == PRE_DEC))
+       useorder = little;
+      else if (REG_P (operands[0]) && REG_P (operands[1]) &&
+              REGNO (operands[0]) > REGNO (operands[1]) &&
+              REGNO (operands[0]) < REGNO (operands[1]) + words)
+           useorder = little;
+
+      /* Check for source == offset from register and dest == push of
+        the same register.  In that case, we have to use the same
+        offset (the one for the low order word) for all words, because
+        the push increases the offset to each source word.
+        In theory there are other cases like this, for example dest == pop,
+        but those don't occur in real life so ignore those.  */
+      if (GET_CODE (operands[0]) ==  MEM 
+         && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
+         && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
+         && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
+       sameoff = true;
     }
 
-  /* If an operand is an unoffsettable memory ref, find a register
-     we can increment temporarily to make it refer to the second word.  */
-
-  if (optype0 == MEMOP)
-    addreg0 = find_addr_reg (XEXP (operands[0], 0));
-
-  if (optype1 == MEMOP)
-    addreg1 = find_addr_reg (XEXP (operands[1], 0));
-
-  /* Ok, we can do one word at a time.
-     Normally we do the low-numbered word first,
-     but if either operand is autodecrementing then we
-     do the high-numbered word first.
-
-     In either case, set up in LATEHALF the operands to use
-     for the high-numbered word and in some cases alter the
-     operands in OPERANDS to be suitable for the low-numbered word.  */
-
-  if (optype0 == REGOP)
-    latehalf[0] = gen_rtx_REG (HImode, REGNO (operands[0]) + 1);
-  else if (optype0 == OFFSOP)
-    latehalf[0] = adjust_address (operands[0], HImode, 2);
+  /* If the caller didn't specify order, use the one we computed,
+     or high word first if we don't care either.  If the caller did
+     specify, verify we don't have a problem with that order.
+     (If it matters to the caller, constraints need to be used to
+     ensure this case doesn't occur).  */
+  if (order == either)
+    order = (useorder == either) ? big : useorder;
   else
-    latehalf[0] = operands[0];
+    gcc_assert (useorder == either || useorder == order);
 
-  if (optype1 == REGOP)
-    latehalf[1] = gen_rtx_REG (HImode, REGNO (operands[1]) + 1);
-  else if (optype1 == OFFSOP)
-    latehalf[1] = adjust_address (operands[1], HImode, 2);
-  else if (optype1 == CNSTOP)
+  
+  for (op = 0; op < opcount; op++)
     {
-       if (CONSTANT_P (operands[1]))
+      /* First classify the operand.  */
+      if (REG_P (operands[op]))
+       optype = REGOP;
+      else if (CONSTANT_P (operands[op])
+              || GET_CODE (operands[op]) == CONST_DOUBLE)
+       optype = CNSTOP;
+      else if (GET_CODE (XEXP (operands[op], 0)) == POST_INC)
+       optype = POPOP;
+      else if (GET_CODE (XEXP (operands[op], 0)) == PRE_DEC)
+       optype = PUSHOP;
+      else if (!reload_in_progress || offsettable_memref_p (operands[op]))
+       optype = OFFSOP;
+      else if (GET_CODE (operands[op]) == MEM)
+       optype = MEMOP;
+      else
+       optype = RNDOP;
+
+      /* Check for the cases that the operand constraints are not
+        supposed to allow to happen. Return failure for such cases.  */
+      if (optype == RNDOP)
+       return false;
+      
+      if (action != NULL)
+       action[op] = no_action;
+      
+      /* If the operand uses pre-decrement addressing but we
+        want to get the parts high order first,
+        decrement the former register explicitly
+        and change the operand into ordinary indexing.  */
+      if (optype == PUSHOP && order == big)
+       {
+         gcc_assert (action != NULL);
+         action[op] = dec_before;
+         operands[op] = gen_rtx_MEM (GET_MODE (operands[op]),
+                                     XEXP (XEXP (operands[op], 0), 0));
+         optype = OFFSOP;
+       }
+      /* If the operand uses post-increment mode but we want 
+        to get the parts low order first, change the operand
+        into ordinary indexing and remember to increment
+        the register explicitly when we're done.  */
+      else if (optype == POPOP && order == little)
        {
-           /* now the mess begins, high word is in lower word??? 
+         gcc_assert (action != NULL);
+         action[op] = inc_after;
+         operands[op] = gen_rtx_MEM (GET_MODE (operands[op]),
+                                     XEXP (XEXP (operands[op], 0), 0));
+         optype = OFFSOP;
+       }
 
-              that's what ashc makes me think, but I don't remember :-( */
-           latehalf[1] = GEN_INT (INTVAL(operands[1]) >> 16);
-           operands[1] = GEN_INT (INTVAL(operands[1]) & 0xff);
+      if (GET_CODE (operands[op]) == CONST_DOUBLE)
+       {
+         REAL_VALUE_FROM_CONST_DOUBLE (r, operands[op]);
+         REAL_VALUE_TO_TARGET_DOUBLE (r, sval);
+       }
+      
+      for (i = 0; i < words; i++)
+       {
+         if (order == big)
+           w = i;
+         else if (sameoff)
+           w = words - 1;
+         else
+           w = words - 1 - i;
+
+         /* Set the output operand to be word "w" of the input.  */
+         if (optype == REGOP)
+           exops[i][op] = gen_rtx_REG (HImode, REGNO (operands[op]) + w);
+         else if (optype == OFFSOP)
+           exops[i][op] = adjust_address (operands[op], HImode, w * 2);
+         else if (optype == CNSTOP)
+           {
+             if (GET_CODE (operands[op]) == CONST_DOUBLE)
+               {
+                 sh = 16 - (w & 1) * 16;
+                 exops[i][op] = gen_rtx_CONST_INT (HImode, (sval[w / 2] >> sh) & 0xffff);
+               }
+             else
+               {
+                 sh = ((words - 1 - w) * 16);
+                 exops[i][op] = gen_rtx_CONST_INT (HImode, trunc_int_for_mode (INTVAL(operands[op]) >> sh, HImode));
+               }
+           }
+         else
+           exops[i][op] = operands[op];
        }
-       else
-         /* immediate 32-bit values not allowed */
-         gcc_assert (GET_CODE (operands[1]) != CONST_DOUBLE);
-    }
-  else
-    latehalf[1] = operands[1];
-
-  /* If insn is effectively movd N(sp),-(sp) then we will do the
-     high word first.  We should use the adjusted operand 1 (which is N+4(sp))
-     for the low word as well, to compensate for the first decrement of sp.  */
-  if (optype0 == PUSHOP
-      && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
-      && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
-    operands[1] = latehalf[1];
-
-  /* If one or both operands autodecrementing,
-     do the two words, high-numbered first.  */
-
-  /* Likewise,  the first move would clobber the source of the second one,
-     do them in the other order.  This happens only for registers;
-     such overlap can't happen in memory unless the user explicitly
-     sets it up, and that is an undefined circumstance.  */
-
-  if (optype0 == PUSHOP || optype1 == PUSHOP
-      || (optype0 == REGOP && optype1 == REGOP
-         && REGNO (operands[0]) == REGNO (latehalf[1])))
-    {
-      /* Make any unoffsettable addresses point at high-numbered word.  */
-      if (addreg0)
-       output_asm_insn ("add $2,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("add $2,%0", &addreg1);
-
-      /* Do that word.  */
-      output_asm_insn (singlemove_string (latehalf), latehalf);
-
-      /* Undo the adds we just did.  */
-      if (addreg0)
-       output_asm_insn ("sub $2,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("sub $2,%0", &addreg1);
-
-      /* Do low-numbered word.  */
-      return singlemove_string (operands);
     }
-
-  /* Normal case: do the two words, low-numbered first.  */
-
-  output_asm_insn (singlemove_string (operands), operands);
-
-  /* Make any unoffsettable addresses point at high-numbered word.  */
-  if (addreg0)
-    output_asm_insn ("add $2,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add $2,%0", &addreg1);
-
-  /* Do that word.  */
-  output_asm_insn (singlemove_string (latehalf), latehalf);
-
-  /* Undo the adds we just did.  */
-  if (addreg0)
-    output_asm_insn ("sub $2,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("sub $2,%0", &addreg1);
-
-  return "";
+  return true;
 }
-/* Output assembler code to perform a quadword move insn
-   with operands OPERANDS.  */
+
+/* Output assembler code to perform a multiple-word move insn
+   with operands OPERANDS.  This moves 2 or 4 words depending
+   on the machine mode of the operands.  */
 
 const char *
-output_move_quad (rtx *operands)
+output_move_multiple (rtx *operands)
 {
-  enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
-  rtx latehalf[2];
-  rtx addreg0 = 0, addreg1 = 0;
-
-  output_asm_insn(";/* movdi/df: %1 -> %0 */", operands);
+  rtx exops[4][2];
+  pdp11_action action[2];
+  int i, words;
   
-  if (REG_P (operands[0]))
-    optype0 = REGOP;
-  else if (offsettable_memref_p (operands[0]))
-    optype0 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
-    optype0 = POPOP;
-  else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
-    optype0 = PUSHOP;
-  else if (GET_CODE (operands[0]) == MEM)
-    optype0 = MEMOP;
-  else
-    optype0 = RNDOP;
-
-  if (REG_P (operands[1]))
-    optype1 = REGOP;
-  else if (CONSTANT_P (operands[1])
-          || GET_CODE (operands[1]) == CONST_DOUBLE)
-    optype1 = CNSTOP;
-  else if (offsettable_memref_p (operands[1]))
-    optype1 = OFFSOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
-    optype1 = POPOP;
-  else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
-    optype1 = PUSHOP;
-  else if (GET_CODE (operands[1]) == MEM)
-    optype1 = MEMOP;
-  else
-    optype1 = RNDOP;
+  words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
 
-  /* Check for the cases that the operand constraints are not
-     supposed to allow to happen.  Abort if we get one,
-     because generating code for these cases is painful.  */
-
-  gcc_assert (optype0 != RNDOP && optype1 != RNDOP);
+  pdp11_expand_operands (operands, exops, 2, action, either);
   
-  if (optype0 == REGOP || optype1 == REGOP)
-  {
-      /* check for use of clrd???? 
-         if you ever allow ac4 and ac5 (now we require secondary load) 
-        you must check whether 
-        you want to load into them or store from them - 
-        then dump ac0 into $help$ movce ac4/5 to ac0, do the 
-        store from ac0, and restore ac0 - if you can find 
-        an unused ac[0-3], use that and you save a store and a load!*/
-
-      if (FPU_REG_P(REGNO(operands[0])))
-      {
-         if (GET_CODE(operands[1]) == CONST_DOUBLE)
-         {
-             REAL_VALUE_TYPE r;
-             REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
-
-             if (REAL_VALUES_EQUAL (r, dconst0))
-                 return "{clrd|clrf} %0";
-         }
-             
-         return "{ldd|movf} %1, %0";
-      }
-      
-      if (FPU_REG_P(REGNO(operands[1])))
-         return "{std|movf} %1, %0";
-  }
-      
-  /* If one operand is decrementing and one is incrementing
-     decrement the former register explicitly
-     and change that operand into ordinary indexing.  */
-
-  if (optype0 == PUSHOP && optype1 == POPOP)
+  /* Check for explicit decrement before.  */
+  if (action[0] == dec_before)
     {
-      operands[0] = XEXP (XEXP (operands[0], 0), 0);
-      output_asm_insn ("sub $8,%0", operands);
-      operands[0] = gen_rtx_MEM (DImode, operands[0]);
-      optype0 = OFFSOP;
+      operands[0] = XEXP (operands[0], 0);
+      output_asm_insn ("sub $4,%0", operands);
     }
-  if (optype0 == POPOP && optype1 == PUSHOP)
+  if (action[1] == dec_before)
     {
-      operands[1] = XEXP (XEXP (operands[1], 0), 0);
-      output_asm_insn ("sub $8,%1", operands);
-      operands[1] = gen_rtx_MEM (SImode, operands[1]);
-      optype1 = OFFSOP;
+      operands[1] = XEXP (operands[1], 0);
+      output_asm_insn ("sub $4,%1", operands);
     }
 
-  /* If an operand is an unoffsettable memory ref, find a register
-     we can increment temporarily to make it refer to the second word.  */
-
-  if (optype0 == MEMOP)
-    addreg0 = find_addr_reg (XEXP (operands[0], 0));
-
-  if (optype1 == MEMOP)
-    addreg1 = find_addr_reg (XEXP (operands[1], 0));
+  /* Do the words.  */
+  for (i = 0; i < words; i++)
+    output_asm_insn (singlemove_string (exops[i]), exops[i]);
 
-  /* Ok, we can do one word at a time.
-     Normally we do the low-numbered word first,
-     but if either operand is autodecrementing then we
-     do the high-numbered word first.
-
-     In either case, set up in LATEHALF the operands to use
-     for the high-numbered word and in some cases alter the
-     operands in OPERANDS to be suitable for the low-numbered word.  */
-
-  if (optype0 == REGOP)
-    latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
-  else if (optype0 == OFFSOP)
-    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]) + 2);
-  else if (optype1 == OFFSOP)
-    latehalf[1] = adjust_address (operands[1], SImode, 4);
-  else if (optype1 == CNSTOP)
+  /* Check for increment after.  */
+  if (action[0] == inc_after)
     {
-      if (GET_CODE (operands[1]) == CONST_DOUBLE)
-       {
-         REAL_VALUE_TYPE r;
-         long dval[2];
-         REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
-         REAL_VALUE_TO_TARGET_DOUBLE (r, dval);
-         latehalf[1] = GEN_INT (dval[1]);
-         operands[1] = GEN_INT (dval[0]);
-       }
-      else if (GET_CODE(operands[1]) == CONST_INT)
-       {
-         latehalf[1] = const0_rtx;
-       }
-      else
-       gcc_unreachable ();
+      operands[0] = XEXP (operands[0], 0);
+      output_asm_insn ("add $4,%0", operands);
     }
-  else
-    latehalf[1] = operands[1];
-
-  /* If insn is effectively movd N(sp),-(sp) then we will do the
-     high word first.  We should use the adjusted operand 1 (which is N+4(sp))
-     for the low word as well, to compensate for the first decrement of sp.  */
-  if (optype0 == PUSHOP
-      && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
-      && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
-    operands[1] = latehalf[1];
-
-  /* If one or both operands autodecrementing,
-     do the two words, high-numbered first.  */
-
-  /* Likewise,  the first move would clobber the source of the second one,
-     do them in the other order.  This happens only for registers;
-     such overlap can't happen in memory unless the user explicitly
-     sets it up, and that is an undefined circumstance.  */
-
-  if (optype0 == PUSHOP || optype1 == PUSHOP
-      || (optype0 == REGOP && optype1 == REGOP
-         && REGNO (operands[0]) == REGNO (latehalf[1])))
+  if (action[1] == inc_after)
     {
-      /* Make any unoffsettable addresses point at high-numbered word.  */
-      if (addreg0)
-       output_asm_insn ("add $4,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("add $4,%0", &addreg1);
-
-      /* Do that word.  */
-      output_asm_insn(output_move_double(latehalf), latehalf);
-
-      /* Undo the adds we just did.  */
-      if (addreg0)
-       output_asm_insn ("sub $4,%0", &addreg0);
-      if (addreg1)
-       output_asm_insn ("sub $4,%0", &addreg1);
-
-      /* Do low-numbered word.  */
-      return output_move_double (operands);
+      operands[1] = XEXP (operands[1], 0);
+      output_asm_insn ("add $4,%1", operands);
     }
 
-  /* Normal case: do the two words, low-numbered first.  */
-
-  output_asm_insn (output_move_double (operands), operands);
-
-  /* Make any unoffsettable addresses point at high-numbered word.  */
-  if (addreg0)
-    output_asm_insn ("add $4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("add $4,%0", &addreg1);
-
-  /* Do that word.  */
-  output_asm_insn (output_move_double (latehalf), latehalf);
-
-  /* Undo the adds we just did.  */
-  if (addreg0)
-    output_asm_insn ("sub $4,%0", &addreg0);
-  if (addreg1)
-    output_asm_insn ("sub $4,%0", &addreg1);
-
   return "";
 }
-
-\f
-/* Return a REG that occurs in ADDR with coefficient 1.
-   ADDR can be effectively incremented by incrementing REG.  */
-
-static rtx
-find_addr_reg (rtx addr)
-{
-  while (GET_CODE (addr) == PLUS)
-    {
-      if (GET_CODE (XEXP (addr, 0)) == REG)
-       addr = XEXP (addr, 0);
-      if (GET_CODE (XEXP (addr, 1)) == REG)
-       addr = XEXP (addr, 1);
-      if (CONSTANT_P (XEXP (addr, 0)))
-       addr = XEXP (addr, 1);
-      if (CONSTANT_P (XEXP (addr, 1)))
-       addr = XEXP (addr, 0);
-    }
-  if (GET_CODE (addr) == REG)
-    return addr;
-  return 0;
-}
 \f
 /* Output an ascii string.  */
 void
@@ -905,6 +690,60 @@ output_ascii (FILE *file, const char *p, int size)
 }
 
 
+void
+pdp11_asm_output_var (FILE *file, const char *name, int size,
+                     int align, bool global)
+{
+  if (align > 8)
+    fprintf (file, "\n\t.even\n");
+  if (global)
+    {
+      fprintf (file, ".globl ");
+      assemble_name (file, name);
+    }
+  fprintf (file, "\n");
+  assemble_name (file, name);
+  fprintf (file, ": .=.+ %#ho\n", (unsigned short)size);
+}
+
+static void
+pdp11_asm_print_operand (FILE *file, rtx x, int code)
+{
+  REAL_VALUE_TYPE r;
+  long sval[2];
+  if (code == '#')
+    fprintf (file, "#");
+  else if (code == '@')
+    {
+      if (TARGET_UNIX_ASM)
+       fprintf (file, "*");
+      else
+       fprintf (file, "@");
+    }
+  else if (GET_CODE (x) == REG)
+    fprintf (file, "%s", reg_names[REGNO (x)]);
+  else if (GET_CODE (x) == MEM)
+    output_address (XEXP (x, 0));
+  else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != SImode)
+    {
+      REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+      REAL_VALUE_TO_TARGET_DOUBLE (r, sval);
+      fprintf (file, "$%#lo", sval[0] >> 16);
+    }
+  else
+    {
+      putc ('$', file);
+      output_addr_const_pdp11 (file, x);
+    }
+}
+
+static bool
+pdp11_asm_print_operand_punct_valid_p (unsigned char c)
+{
+  return (c == '#' || c == '@');
+}
+
 void
 print_operand_address (FILE *file, register rtx addr)
 {
@@ -1052,7 +891,8 @@ pdp11_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
 }
 
 static bool
-pdp11_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total,
+pdp11_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED,
+                int opno ATTRIBUTE_UNUSED, int *total,
                 bool speed ATTRIBUTE_UNUSED)
 {
   switch (code)
@@ -1167,6 +1007,17 @@ output_jump (enum rtx_code code, int inv, int length)
     static char buf[1000];
     const char *pos, *neg;
 
+    if (cc_prev_status.flags & CC_NO_OVERFLOW)
+      {
+       switch (code)
+         {
+         case GTU: code = GT; break;
+         case LTU: code = LT; break;
+         case GEU: code = GE; break;
+         case LEU: code = LE; break;
+         default: ;
+         }
+      }
     switch (code)
       {
       case EQ: pos = "beq", neg = "bne"; break;
@@ -1220,68 +1071,38 @@ notice_update_cc_on_set(rtx exp, rtx insn ATTRIBUTE_UNUSED)
 {
     if (GET_CODE (SET_DEST (exp)) == CC0)
     { 
-       cc_status.flags = 0;                                    
-       cc_status.value1 = SET_DEST (exp);                      
-       cc_status.value2 = SET_SRC (exp);                       
-
-/*
-       if (GET_MODE(SET_SRC(exp)) == DFmode)
-           cc_status.flags |= CC_IN_FPU;
-*/     
-    }                                                  
-    else if ((GET_CODE (SET_DEST (exp)) == REG         
-             || GET_CODE (SET_DEST (exp)) == MEM)              
-            && GET_CODE (SET_SRC (exp)) != PC          
-            && (GET_MODE (SET_DEST(exp)) == HImode             
-                || GET_MODE (SET_DEST(exp)) == QImode) 
-               && (GET_CODE (SET_SRC(exp)) == PLUS             
-                   || GET_CODE (SET_SRC(exp)) == MINUS 
-                   || GET_CODE (SET_SRC(exp)) == AND   
-                   || GET_CODE (SET_SRC(exp)) == IOR   
-                   || GET_CODE (SET_SRC(exp)) == XOR   
-                   || GET_CODE (SET_SRC(exp)) == NOT   
-                   || GET_CODE (SET_SRC(exp)) == NEG   
-                       || GET_CODE (SET_SRC(exp)) == REG       
-                   || GET_CODE (SET_SRC(exp)) == MEM)) 
-    { 
-       cc_status.flags = 0;                                    
-       cc_status.value1 = SET_SRC (exp);                       
-       cc_status.value2 = SET_DEST (exp);                      
-       
-       if (cc_status.value1 && GET_CODE (cc_status.value1) == REG      
-           && cc_status.value2                                 
-           && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
-           cc_status.value2 = 0;                                       
-       if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM      
-           && cc_status.value2                                 
-           && GET_CODE (cc_status.value2) == MEM)                      
-           cc_status.value2 = 0;                                       
+      cc_status.flags = 0;                                     
+      cc_status.value1 = SET_DEST (exp);                       
+      cc_status.value2 = SET_SRC (exp);                        
     }                                                  
     else if (GET_CODE (SET_SRC (exp)) == CALL)         
     { 
-       CC_STATUS_INIT; 
+      CC_STATUS_INIT; 
     }
-    else if (GET_CODE (SET_DEST (exp)) == REG)                 
-       /* what's this ? */                                     
-    { 
-       if ((cc_status.value1                                   
-            && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1)))
-           cc_status.value1 = 0;                               
-       if ((cc_status.value2                                   
-            && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2)))
-           cc_status.value2 = 0;                               
-    }                                                  
     else if (SET_DEST(exp) == pc_rtx)
     { 
-       /* jump */
-    }
-    else /* if (GET_CODE (SET_DEST (exp)) == MEM)      */      
-    {  
-       /* the last else is a bit paranoiac, but since nearly all instructions 
-          play with condition codes, it's reasonable! */
-
-       CC_STATUS_INIT; /* paranoia*/ 
+      /* jump */
+    }  
+    else if (GET_MODE (SET_DEST(exp)) == HImode                
+            || GET_MODE (SET_DEST(exp)) == QImode)
+    { 
+      cc_status.flags = GET_CODE (SET_SRC(exp)) == MINUS ? 0 : CC_NO_OVERFLOW;
+      cc_status.value1 = SET_SRC (exp);                        
+      cc_status.value2 = SET_DEST (exp);                       
+       
+      if (cc_status.value1 && GET_CODE (cc_status.value1) == REG       
+         && cc_status.value2                                   
+         && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
+       cc_status.value2 = 0;                                   
+      if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM       
+         && cc_status.value2                                   
+         && GET_CODE (cc_status.value2) == MEM)                        
+       cc_status.value2 = 0;                                   
     }                  
+    else
+    { 
+      CC_STATUS_INIT; 
+    }
 }
 
 
@@ -1363,236 +1184,157 @@ output_block_move(rtx *operands)
 {
     static int count = 0;
     char buf[200];
+    int unroll;
+    int lastbyte = 0;
+    
+    /* Move of zero bytes is a NOP.  */
+    if (operands[2] == const0_rtx)
+      return "";
     
-    if (GET_CODE(operands[2]) == CONST_INT
-       && ! optimize_size)
+    /* Look for moves by small constant byte counts, those we'll
+       expand to straight line code.  */
+    if (CONSTANT_P (operands[2]))
     {
-       if (INTVAL(operands[2]) < 16
-           && INTVAL(operands[3]) == 1)
+       if (INTVAL (operands[2]) < 16
+           && (!optimize_size || INTVAL (operands[2]) < 5)
+           && INTVAL (operands[3]) == 1)
        {
            register int i;
            
-           for (i = 1; i <= INTVAL(operands[2]); i++)
+           for (i = 1; i <= INTVAL (operands[2]); i++)
                output_asm_insn("movb (%1)+, (%0)+", operands);
 
            return "";
        }
-       else if (INTVAL(operands[2]) < 32)
+       else if (INTVAL(operands[2]) < 32
+                && (!optimize_size || INTVAL (operands[2]) < 9)
+                && INTVAL (operands[3]) >= 2)
        {
            register int i;
            
-           for (i = 1; i <= INTVAL(operands[2])/2; i++)
-               output_asm_insn("mov (%1)+, (%0)+", operands);
+           for (i = 1; i <= INTVAL (operands[2]) / 2; i++)
+               output_asm_insn ("mov (%1)+, (%0)+", operands);
+           if (INTVAL (operands[2]) & 1)
+             output_asm_insn ("movb (%1), (%0)", operands);
            
-           /* may I assume that moved quantity is 
-              multiple of alignment ???
-
-              I HOPE SO !
-           */
-
            return "";
        }
-       
-
-       /* can do other clever things, maybe... */
     }
 
-    if (CONSTANT_P(operands[2]) )
+    /* Ideally we'd look for moves that are multiples of 4 or 8
+       bytes and handle those by unrolling the move loop.  That
+       makes for a lot of code if done at run time, but it's ok
+       for constant counts.  Also, for variable counts we have
+       to worry about odd byte count with even aligned pointers.
+       On 11/40 and up we handle that case; on older machines
+       we don't and just use byte-wise moves all the time.  */
+
+    if (CONSTANT_P (operands[2]) )
     {
-       /* just move count to scratch */
-       output_asm_insn("mov %2, %4", operands);
+      if (INTVAL (operands[3]) < 2)
+       unroll = 0;
+      else
+       {
+         lastbyte = INTVAL (operands[2]) & 1;
+
+         if (optimize_size || INTVAL (operands[2]) & 2)
+           unroll = 1;
+         else if (INTVAL (operands[2]) & 4)
+           unroll = 2;
+         else
+           unroll = 3;
+       }
+      
+      /* Loop count is byte count scaled by unroll.  */
+      operands[2] = GEN_INT (INTVAL (operands[2]) >> unroll);
+      output_asm_insn ("mov %2, %4", operands);
     }
     else
     {
-       /* just clobber the register */
+       /* Variable byte count; use the input register
+          as the scratch.  */
        operands[4] = operands[2];
-    }
-    
 
-    /* switch over alignment */
-    switch (INTVAL(operands[3]))
-    {
-      case 1:
-       
-       /* 
-         x:
-         movb (%1)+, (%0)+
-         
-         if (TARGET_45)
-            sob %4,x
-         else
-            dec %4
-            bgt x
+       /* Decide whether to move by words, and check
+          the byte count for zero.  */
+       if (TARGET_40_PLUS && INTVAL (operands[3]) > 1)
+         {
+           unroll = 1;
+           output_asm_insn ("asr %4", operands);
+         }
+       else
+         {
+           unroll = 0;
+           output_asm_insn ("tst %4", operands);
+         }
+       sprintf (buf, "beq movestrhi%d", count + 1);
+       output_asm_insn (buf, NULL);
+    }
 
-       */
+    /* Output the loop label.  */
+    sprintf (buf, "\nmovestrhi%d:", count);
+    output_asm_insn (buf, NULL);
 
-       sprintf(buf, "\nmovestrhi%d:", count);
-       output_asm_insn(buf, NULL);
-       
-       output_asm_insn("movb (%1)+, (%0)+", operands);
-       
-       if (TARGET_45)
-       {
-           sprintf(buf, "sob %%4, movestrhi%d", count);
-           output_asm_insn(buf, operands);
-       }
-       else
-       {
-           output_asm_insn("dec %4", operands);
-           
-           sprintf(buf, "bgt movestrhi%d", count);
-           output_asm_insn(buf, NULL);
-       }
+    /* Output the appropriate move instructions.  */
+    switch (unroll)
+    {
+      case 0:
+       output_asm_insn ("movb (%1)+, (%0)+", operands);
+       break;
        
-       count ++;
+      case 1:
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
        break;
        
       case 2:
-       
-       /* 
-          asr %4
-
-          x:
-
-          mov (%1)+, (%0)+
-
-          if (TARGET_45)
-            sob %4, x
-          else
-            dec %4
-            bgt x
-       */
-
-      generate_compact_code:
-
-       output_asm_insn("asr %4", operands);
-
-       sprintf(buf, "\nmovestrhi%d:", count);
-       output_asm_insn(buf, NULL);
-       
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       
-       if (TARGET_45)
-       {
-           sprintf(buf, "sob %%4, movestrhi%d", count);
-           output_asm_insn(buf, operands);
-       }
-       else
-       {
-           output_asm_insn("dec %4", operands);
-           
-           sprintf(buf, "bgt movestrhi%d", count);
-           output_asm_insn(buf, NULL);
-       }
-       
-       count ++;
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
        break;
-
-      case 4:
-       
-       /*
-
-          asr %4
-          asr %4
-
-          x:
-
-          mov (%1)+, (%0)+
-          mov (%1)+, (%0)+
-
-          if (TARGET_45)
-            sob %4, x
-          else
-            dec %4
-            bgt x
-       */
-
-       if (optimize_size)
-           goto generate_compact_code;
-       
-       output_asm_insn("asr %4", operands);
-       output_asm_insn("asr %4", operands);
-
-       sprintf(buf, "\nmovestrhi%d:", count);
-       output_asm_insn(buf, NULL);
        
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       
-       if (TARGET_45)
-       {
-           sprintf(buf, "sob %%4, movestrhi%d", count);
-           output_asm_insn(buf, operands);
-       }
-       else
-       {
-           output_asm_insn("dec %4", operands);
-           
-           sprintf(buf, "bgt movestrhi%d", count);
-           output_asm_insn(buf, NULL);
-       }
-       
-       count ++;
-       break;
-       
       default:
-       
-       /*
-          
-          asr %4
-          asr %4
-          asr %4
-
-          x:
-
-          mov (%1)+, (%0)+
-          mov (%1)+, (%0)+
-          mov (%1)+, (%0)+
-          mov (%1)+, (%0)+
-          
-          if (TARGET_45)
-            sob %4, x
-          else
-            dec %4
-            bgt x
-       */
-
-
-       if (optimize_size)
-           goto generate_compact_code;
-       
-       output_asm_insn("asr %4", operands);
-       output_asm_insn("asr %4", operands);
-       output_asm_insn("asr %4", operands);
-
-       sprintf(buf, "\nmovestrhi%d:", count);
-       output_asm_insn(buf, NULL);
-       
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       output_asm_insn("mov (%1)+, (%0)+", operands);
-       
-       if (TARGET_45)
-       {
-           sprintf(buf, "sob %%4, movestrhi%d", count);
-           output_asm_insn(buf, operands);
-       }
-       else
-       {
-           output_asm_insn("dec %4", operands);
-           
-           sprintf(buf, "bgt movestrhi%d", count);
-           output_asm_insn(buf, NULL);
-       }
-       
-       count ++;
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
+       output_asm_insn ("mov (%1)+, (%0)+", operands);
        break;
-       
-       ;
-       
     }
+
+    /* Output the decrement and test.  */
+    if (TARGET_40_PLUS)
+      {
+       sprintf (buf, "sob %%4, movestrhi%d", count);
+       output_asm_insn (buf, operands);
+      }
+    else
+      {
+       output_asm_insn ("dec %4", operands);
+       sprintf (buf, "bgt movestrhi%d", count);
+       output_asm_insn (buf, NULL);
+      }
+    count ++;
+
+    /* If constant odd byte count, move the last byte.  */
+    if (lastbyte)
+      output_asm_insn ("movb (%1), (%0)", operands);
+    else if (!CONSTANT_P (operands[2]))
+      {
+       /* Output the destination label for the zero byte count check.  */
+       sprintf (buf, "\nmovestrhi%d:", count);
+       output_asm_insn (buf, NULL);
+       count++;
     
+       /* If we did word moves, check for trailing last byte. */
+       if (unroll)
+         {
+           sprintf (buf, "bcc movestrhi%d", count);
+           output_asm_insn (buf, NULL);
+           output_asm_insn ("movb (%1), (%0)", operands);
+           sprintf (buf, "\nmovestrhi%d:", count);
+           output_asm_insn (buf, NULL);
+           count++;
+         }
+      }
+            
     return "";
 }
 
@@ -1821,6 +1563,7 @@ pdp11_legitimate_address_p (enum machine_mode mode,
        return false;
       }
 }
+
 /* Return the class number of the smallest class containing
    reg number REGNO.  */
 enum reg_class
@@ -1839,16 +1582,16 @@ pdp11_regno_reg_class (int regno)
 }
 
 
-static int
+int
 pdp11_sp_frame_offset (void)
 {
   int offset = 0, regno;
   offset = get_frame_size();
   for (regno = 0; regno <= PC_REGNUM; regno++)
-    if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])
+    if (pdp11_saved_regno (regno))
       offset += 2;
   for (regno = AC0_REGNUM; regno <= AC5_REGNUM; regno++)
-    if (df_regs_ever_live_p (regno) && ! call_used_regs[regno])
+    if (pdp11_saved_regno (regno))
       offset += 8;
   
   return offset;
@@ -1896,7 +1639,8 @@ void
 output_addr_const_pdp11 (FILE *file, rtx x)
 {
   char buf[256];
-
+  int i;
+  
  restart:
   switch (GET_CODE (x))
     {
@@ -1920,7 +1664,13 @@ output_addr_const_pdp11 (FILE *file, rtx x)
       break;
 
     case CONST_INT:
-      fprintf (file, "%#o", (int) INTVAL (x) & 0xffff);
+      i = INTVAL (x);
+      if (i < 0)
+       {
+         i = -i;
+         fprintf (file, "-");
+       }
+      fprintf (file, "%#o", i & 0xffff);
       break;
 
     case CONST:
@@ -1968,16 +1718,10 @@ output_addr_const_pdp11 (FILE *file, rtx x)
        goto restart;
 
       output_addr_const_pdp11 (file, XEXP (x, 0));
-      fprintf (file, "-");
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && INTVAL (XEXP (x, 1)) < 0)
-       {
-         fprintf (file, targetm.asm_out.open_paren);
-         output_addr_const_pdp11 (file, XEXP (x, 1));
-         fprintf (file, targetm.asm_out.close_paren);
-       }
-      else
-       output_addr_const_pdp11 (file, XEXP (x, 1));
+      if (GET_CODE (XEXP (x, 1)) != CONST_INT
+         || INTVAL (XEXP (x, 1)) >= 0)
+       fprintf (file, "-");
+      output_addr_const_pdp11 (file, XEXP (x, 1));
       break;
 
     case ZERO_EXTEND:
@@ -2081,7 +1825,7 @@ pdp11_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
     (otherwise it is an extra parameter matching an ellipsis).  */
 
 static rtx
-pdp11_function_arg (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+pdp11_function_arg (cumulative_args_t cum ATTRIBUTE_UNUSED,
                    enum machine_mode mode ATTRIBUTE_UNUSED,
                    const_tree type ATTRIBUTE_UNUSED,
                    bool named ATTRIBUTE_UNUSED)
@@ -2096,9 +1840,11 @@ pdp11_function_arg (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
    may not be available.)  */
 
 static void
-pdp11_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+pdp11_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
                            const_tree type, bool named ATTRIBUTE_UNUSED)
 {
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
   *cum += (mode != BLKmode
           ? GET_MODE_SIZE (mode)
           : int_size_in_bytes (type));
@@ -2137,4 +1883,21 @@ pdp11_conditional_register_usage (void)
     }
 }
 
+static section *
+pdp11_function_section (tree decl ATTRIBUTE_UNUSED,
+                       enum node_frequency freq ATTRIBUTE_UNUSED,
+                       bool startup ATTRIBUTE_UNUSED,
+                       bool exit ATTRIBUTE_UNUSED)
+{
+  return NULL;
+}
+
+/* Implement TARGET_LEGITIMATE_CONSTANT_P.  */
+
+static bool
+pdp11_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+  return GET_CODE (x) != CONST_DOUBLE || legitimate_const_double_p (x);
+}
+
 struct gcc_target targetm = TARGET_INITIALIZER;