diff mbox series

[pdp11] Bugfixes from test suite

Message ID DF968F22-B0D6-4456-912A-80363B2E6092@comcast.net
State New
Headers show
Series [pdp11] Bugfixes from test suite | expand

Commit Message

Paul Koning Nov. 8, 2018, 7 p.m. UTC
This patch corrects a large number of test suite failures.  I'm now down to about 1100 failures out of over 60k total, from at least 4000 before.  

Committed.

	paul

ChangeLog:

2018-11-08  Paul Koning  <ni1d@arrl.net>

	* config/pdp11/constraints.md: Add "Z" series constraints for use
	with pre-dec and post-inc addressing.
	* config/pdp11/pdp11-protos.m (expand_block_move): Delete.
	(pdp11_expand_operands): Add int argument (word count).
	(pdp11_sp_frame_offset): Delete.
	(pdp11_cmp_length): New function.
	(pushpop_regeq): New function.
	* config/pdp11/pdp11.c (TARGET_STACK_PROTECT_RUNTIME_ENABLED_P):
	Add hook.
	(pdp11_expand_prologue, pdp11_expand_epilogue): Rewrite for new
	frame layout.
	(pdp11_initial_elimination_offset): Ditto.
	(pdp11_expand_operands): Add word count argument.  Bugfixes.
	(output_move_multiple): Change how pointer adjustment is done.
	(pdp11_gen_int_label): Correct format.
	(output_ascii): Ditto.
	(pdp11_asm_output_var): Add code for DEC assembler case.
	(pdp11_asm_print_operand): Bugfix for CONST_DOUBLE holding integer
	value.
	(legitimate_const_double_p): Ditto.
	(pdp11_register_move_cost): Adjust for new register classes.
	(pdp11_regno_reg_class): Ditto.
	(expand_block_move): Delete.
	(pushpop_regeq): New function.
	(pdp11_legitimate_address_p): Bugfix in check for constant
	offset.
	(pdp11_sp_frame_offset): Delete.
	(pdp11_reg_save_size): New helper function for new frame layout.
	(output_addr_const_pdp11): Remove CONST_DOUBLE case.
	(pdp11_expand_shift): Bugfix in check for constant shift count.
	(pdp11_shift_length): Ditto.
	(pdp11_assemble_shift): Copy input to pdp11_expand_operands.
	(pdp11_cmp_length): New function.
	* config/pdp11/pdp11.h (TARGET_CPU_CPP_BUILTINS): Add macros for
	some compile options.
	(FIXED_REGISTERS): Remove HARD_FRAME_POINTER_REGNUM.
	(CALL_USED_REGISTERS): Ditto.
	(ELIMINABLE_REGS): Ditto.
	(REGISTER_NAMES): Ditto.
	(reg_class): Add classes NOTR0_REG through NOTSP_REG for use by Z
	constraints.
	(REG_CLASS_NAMES): Ditto.
	(REG_CLASS_CONTENTS): Ditto.  Also remove
	HARD_FRAME_POINTER_REGNUM.
	(CPU_REG_CLASS): New macro.
	(CLASS_MAX_NREGS): Adjust for new register classes.
	(FUNCTION_PROFILER): Make no-op.
	(may_call_alloca): Remove unused declaration.
	(ASM_OUTPUT_ALIGN): Add workaround for PR87795.
	(ASM_OUTPUT_SKIP): Fix format.
	* config/pdp11/pdp11.md (unspecv): Add UNSPECV_MOVMEM.
	(HARD_FRAME_POINTER_REGNUM): Remove.
	(return): Delete.
	(*rts): Rename.  Remove epilogue related checks.
	(cmpsi, cmpdi): New insn.
	(cbranch<mode>4): Change to apply to SI and DI modes as well.
	(mov<mode>): Change constraints to enforce that push/pop
	destination cannot use the same register as source.
	(*mov<mode><cc_cc>): Ditto.
	(movmemhi, movmemhi1, movmemhi_nocc): Change to expand block move
	at assembly output rather than as RTL expander.
	(zero_extendqihi2): Bugfix in check for same registers.
	(adddi3_nocc): Bugfix in check for constant operand.
	(addsi3_nocc): Ditto.
	(subdi3_nocc): Ditto.
	(subsi3_nocc): Ditto.
	(negdi2_nocc): Copy input to pdp11_expand_operands.
	(negsi2_nocc): Ditto.
	(bswap2_nocc): Ditto.
	* config/pdp11/pdp11.opt (mlra): Fix documentation.
	* config/pdp11/t-pdp11: Use -Os.
diff mbox series

Patch

Index: config/pdp11/constraints.md
===================================================================
--- config/pdp11/constraints.md	(revision 265931)
+++ config/pdp11/constraints.md	(working copy)
@@ -88,3 +88,32 @@ 
        (match_test "memory_address_p (GET_MODE (op), XEXP (op, 0))
                     && no_side_effect_operand (op, GET_MODE (op))")))
 
+;; What follows is a set of constraints used to prevent the generation
+;; of insns that have a register as source, and an auto-increment or
+;; auto-decrement memory reference as the destination where the register
+;; is the same as the source.  On the PDP11, such instructions are not
+;; implemented consistently across the models and often do something
+;; different from what the RTL intends.
+(define_register_constraint "Z0" "NOTR0_REG" "Register other than 0")
+(define_register_constraint "Z1" "NOTR1_REG" "Register other than 1")
+(define_register_constraint "Z2" "NOTR2_REG" "Register other than 2")
+(define_register_constraint "Z3" "NOTR3_REG" "Register other than 3")
+(define_register_constraint "Z4" "NOTR4_REG" "Register other than 4")
+(define_register_constraint "Z5" "NOTR5_REG" "Register other than 5")
+(define_register_constraint "Z6" "NOTSP_REG"
+  "Register other than stack pointer (register 6)")
+(define_memory_constraint "Za" "R0 push/pop"
+  (match_test "pushpop_regeq (op, 0)"))
+(define_memory_constraint "Zb" "R1 push/pop"
+  (match_test "pushpop_regeq (op, 1)"))
+(define_memory_constraint "Zc" "R2 push/pop"
+  (match_test "pushpop_regeq (op, 2)"))
+(define_memory_constraint "Zd" "R3 push/pop"
+  (match_test "pushpop_regeq (op, 3)"))
+(define_memory_constraint "Ze" "R4 push/pop"
+  (match_test "pushpop_regeq (op, 4)"))
+(define_memory_constraint "Zf" "R5 push/pop"
+  (match_test "pushpop_regeq (op, 5)"))
+(define_memory_constraint "Zg" "SP push/pop"
+  (match_test "pushpop_regeq (op, 6)"))
+  
Index: config/pdp11/pdp11-protos.h
===================================================================
--- config/pdp11/pdp11-protos.h	(revision 265931)
+++ config/pdp11/pdp11-protos.h	(working copy)
@@ -26,14 +26,12 @@  extern int legitimate_const_double_p (rtx);
 extern void notice_update_cc_on_set (rtx, rtx);
 extern void output_addr_const_pdp11 (FILE *, rtx);
 extern const char *output_move_multiple (rtx *);
-extern void expand_block_move (rtx *);
 extern const char *output_jump (rtx *, int, int);
 extern void print_operand_address (FILE *, rtx);
 typedef enum { no_action, dec_before, inc_after } pdp11_action;
 typedef enum { little, either, big } pdp11_partorder;
-extern bool pdp11_expand_operands (rtx *, rtx [][2], int,
+extern bool pdp11_expand_operands (rtx *, rtx [][2], int, int,
 				   pdp11_action *, pdp11_partorder);
-extern int pdp11_sp_frame_offset (void);
 extern int pdp11_initial_elimination_offset (int, int);
 extern enum reg_class pdp11_regno_reg_class (int);
 extern bool pdp11_fixed_cc_regs (unsigned int *, unsigned int *);
@@ -42,6 +40,8 @@  extern bool pdp11_expand_shift (rtx *, rtx (*) (rt
 				rtx (*) (rtx, rtx, rtx));
 extern const char * pdp11_assemble_shift (rtx *, machine_mode, int);
 extern int pdp11_shift_length (rtx *, machine_mode, int, bool);
+extern int pdp11_cmp_length (rtx *, int);
+extern bool pushpop_regeq (rtx, int);
 extern bool pdp11_small_shift (int);
 
 #endif /* RTX_CODE */
Index: config/pdp11/pdp11.c
===================================================================
--- config/pdp11/pdp11.c	(revision 265931)
+++ config/pdp11/pdp11.c	(working copy)
@@ -304,6 +304,9 @@  static bool pdp11_scalar_mode_supported_p (scalar_
 
 #undef TARGET_HAVE_SPECULATION_SAFE_VALUE
 #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+
+#undef  TARGET_STACK_PROTECT_RUNTIME_ENABLED_P
+#define TARGET_STACK_PROTECT_RUNTIME_ENABLED_P hook_bool_void_false
 
 /* A helper function to determine if REGNO should be saved in the
    current function's stack frame.  */
@@ -316,6 +319,13 @@  pdp11_saved_regno (unsigned regno)
 
 /* Expand the function prologue.  */
 
+/* Frame layout, from high to low memory (stack push order):
+   return address (from jsr instruction)
+   saved CPU registers, lowest number first
+   saved FPU registers, lowest number first, always 64 bit mode
+   *** frame pointer points here ***
+   local variables
+   alloca storage if any.  */
 void
 pdp11_expand_prologue (void)
 {							       
@@ -331,31 +341,9 @@  pdp11_expand_prologue (void)
       emit_insn (gen_seti ());
     }
     
-  if (frame_pointer_needed) 					
-    {								
-      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)
-    {
-      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 ());
-    }
-
   /* Save CPU registers.  */
   for (regno = R0_REGNUM; regno <= PC_REGNUM; regno++)
-    if (pdp11_saved_regno (regno)
-	&& (regno != HARD_FRAME_POINTER_REGNUM || !frame_pointer_needed))
+    if (pdp11_saved_regno (regno))
       {
 	x = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
 	x = gen_frame_mem (Pmode, x);
@@ -383,26 +371,22 @@  pdp11_expand_prologue (void)
 	x = gen_frame_mem (DFmode, x);
 	emit_move_insn (x, via_ac);
       }
-}
 
-/* 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.  */
+  if (frame_pointer_needed)
+    emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
 
-/* 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!) 
+  /* Make local variable space.  */
+  if (fsize)
+    emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
+			   GEN_INT (-fsize)));
+}
 
-   set/clear bit 11 (dec. 2048) of status word for switching register files - 
-   but how can we do this? the pdp11/45 manual says bit may only 
-   be set (p.24), but not cleared!
+/* Generate epilogue.  This uses the frame pointer to pop the local
+   variables and any alloca data off the stack.  If there is no alloca
+   and frame pointer elimination hasn't been disabled, there is no
+   frame pointer and the local variables are popped by adjusting the
+   stack pointer instead.  */
 
-   switching to kernel is probably more expensive, so we'll leave it 
-   like this and not use the second set of registers... 
-
-   maybe as option if you want to generate code for kernel mode? */
-
 void
 pdp11_expand_epilogue (void)
 {								
@@ -410,6 +394,20 @@  pdp11_expand_epilogue (void)
   unsigned regno;
   rtx x, reg, via_ac = NULL;
 
+  /* Deallocate the local variables.  */
+  if (fsize)
+    {
+      if (frame_pointer_needed)
+	{
+	  /* We can deallocate the frame with a single move.  */
+	  emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+	}
+      else
+	emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx,
+			       GEN_INT (fsize)));
+    }
+
+  /* Restore the FPU registers.  */
   if (pdp11_saved_regno (AC4_REGNUM) || pdp11_saved_regno (AC5_REGNUM))
     {
       /* Find a temporary with which to restore AC4/5.  */
@@ -421,109 +419,33 @@  pdp11_expand_epilogue (void)
 	  }
     }
 
-  /* If possible, restore registers via pops.  */
-  if (!frame_pointer_needed || crtl->sp_is_unchanging)
-    {
-      /* Restore registers via pops.  */
+  /* 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);
+  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))
+	if (LOAD_FPU_REG_P (regno))
+	  emit_move_insn (reg, x);
+	else
 	  {
-	    x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
-	    x = gen_frame_mem (Pmode, x);
-	    emit_move_insn (gen_rtx_REG (Pmode, regno), x);
+	    emit_move_insn (via_ac, x);
+	    emit_move_insn (reg, via_ac);
 	  }
-    }
-  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.  */
+  for (regno = PC_REGNUM; regno >= R0_REGNUM + 2; regno--)
+    if (pdp11_saved_regno (regno))
+      {
+	x = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
+	x = gen_frame_mem (Pmode, x);
+	emit_move_insn (gen_rtx_REG (Pmode, regno), x);
+      }
 
-      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)
-	{
-	  /* 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);
-    }
-
-  emit_jump_insn (gen_return ());
+  emit_jump_insn (gen_rtspc ());
 }
 
 /* Return the best assembler insn template
@@ -539,21 +461,23 @@  singlemove_string (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.  */
+   corresponding HImode operands.  The number of operands is given as
+   the third argument, the word count for the mode as the fourth
+   argument, and the required order of parts as the sixth argument.
+   The word count is explicit because sometimes we're asked to compare
+   two constants, both of which have mode VOIDmode, so we can't always
+   rely on the input operand mode to imply the operand size.  */
 bool
-pdp11_expand_operands (rtx *operands, rtx exops[][2], int opcount, 
+pdp11_expand_operands (rtx *operands, rtx exops[][2],
+		       int opcount, int words,
 		       pdp11_action *action, pdp11_partorder order)
 {
-  int words, op, w, i, sh;
+  int op, w, i, sh;
   pdp11_partorder useorder;
   bool sameoff = false;
   enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype;
   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
@@ -566,19 +490,16 @@  bool
   useorder = either;
   if (opcount == 2)
     {
-      if (!REG_P (operands[0]) && !REG_P (operands[1]) &&
-	  !(CONSTANT_P (operands[1]) || 
-	    GET_CODE (operands[1]) == CONST_DOUBLE) &&
+      if (GET_CODE (operands[0]) == MEM &&
+	  GET_CODE (operands[1]) == MEM &&
 	  ((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]) &&
+      else if ((GET_CODE (operands[0]) == MEM &&
 		GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ||
-	       (!REG_P (operands[1]) &&
-		!(CONSTANT_P (operands[1]) || 
-		  GET_CODE (operands[1]) == CONST_DOUBLE) &&
+	       (GET_CODE (operands[1]) == MEM &&
 		GET_CODE (XEXP (operands[1], 0)) == PRE_DEC))
 	useorder = little;
       else if (REG_P (operands[0]) && REG_P (operands[1]) &&
@@ -615,7 +536,7 @@  bool
       /* First classify the operand.  */
       if (REG_P (operands[op]))
 	optype = REGOP;
-      else if (CONSTANT_P (operands[op])
+      else if (CONST_INT_P (operands[op])
 	       || GET_CODE (operands[op]) == CONST_DOUBLE)
 	optype = CNSTOP;
       else if (GET_CODE (XEXP (operands[op], 0)) == POST_INC)
@@ -663,8 +584,11 @@  bool
 	}
 
       if (GET_CODE (operands[op]) == CONST_DOUBLE)
-	REAL_VALUE_TO_TARGET_DOUBLE
-	  (*CONST_DOUBLE_REAL_VALUE (operands[op]), sval);
+	{
+	  gcc_assert (GET_MODE (operands[op]) != VOIDmode);
+	  REAL_VALUE_TO_TARGET_DOUBLE
+	    (*CONST_DOUBLE_REAL_VALUE (operands[op]), sval);
+	}
       
       for (i = 0; i < words; i++)
 	{
@@ -707,24 +631,31 @@  bool
 const char *
 output_move_multiple (rtx *operands)
 {
+  rtx inops[2];
   rtx exops[4][2];
+  rtx adjops[2];
+  
   pdp11_action action[2];
   int i, words;
   
   words = GET_MODE_BITSIZE (GET_MODE (operands[0])) / 16;
+  adjops[1] = gen_rtx_CONST_INT (HImode, words * 2);
 
-  pdp11_expand_operands (operands, exops, 2, action, either);
+  inops[0] = operands[0];
+  inops[1] = operands[1];
   
+  pdp11_expand_operands (inops, exops, 2, words, action, either);
+  
   /* Check for explicit decrement before.  */
   if (action[0] == dec_before)
     {
-      operands[0] = XEXP (operands[0], 0);
-      output_asm_insn ("sub\t%#4,%0", operands);
+      adjops[0] = XEXP (XEXP (operands[0], 0), 0);
+      output_asm_insn ("sub\t%1,%0", adjops);
     }
   if (action[1] == dec_before)
     {
-      operands[1] = XEXP (operands[1], 0);
-      output_asm_insn ("sub\t%#4,%1", operands);
+      adjops[0] = XEXP (XEXP (operands[1], 0), 0);
+      output_asm_insn ("sub\t%1,%0", adjops);
     }
 
   /* Do the words.  */
@@ -734,13 +665,13 @@  output_move_multiple (rtx *operands)
   /* Check for increment after.  */
   if (action[0] == inc_after)
     {
-      operands[0] = XEXP (operands[0], 0);
-      output_asm_insn ("add\t%#4,%0", operands);
+      adjops[0] = XEXP (XEXP (operands[0], 0), 0);
+      output_asm_insn ("add\t%1,%0", adjops);
     }
   if (action[1] == inc_after)
     {
-      operands[1] = XEXP (operands[1], 0);
-      output_asm_insn ("add\t%#4,%1", operands);
+      adjops[0] = XEXP (XEXP (operands[1], 0), 0);
+      output_asm_insn ("add\t%1,%0", adjops);
     }
 
   return "";
@@ -752,9 +683,9 @@  pdp11_gen_int_label (char *label, const char *pref
 {
   if (TARGET_DEC_ASM)
     /* +1 because GCC numbers labels starting at zero.  */
-    sprintf (label, "*%lu$", num + 1);
+    sprintf (label, "*%u$", num + 1);
   else
-    sprintf (label, "*%s_%lu", prefix, num);
+    sprintf (label, "*%s_%u", prefix, num);
 }
   
 /* Output an ascii string.  */
@@ -780,7 +711,7 @@  output_ascii (FILE *file, const char *p, int size)
 	    {
 	      if (delim)
 		putc ('"', file);
-	      fprintf (file, "<%o%>", c);
+	      fprintf (file, "<%o>", c);
 	      delim = false;
 	    }
 	  else
@@ -815,15 +746,30 @@  pdp11_asm_output_var (FILE *file, const char *name
 {
   if (align > 8)
     fprintf (file, "\t.even\n");
-  if (global)
+  if (TARGET_DEC_ASM)
     {
-      fprintf (file, ".globl ");
       assemble_name (file, name);
+      if (global)
+	fputs ("::", file);
+      else
+	fputs (":", file);
+      if (align > 8)
+	fprintf (file, "\t.blkw\t%o\n", (size & 0xffff) / 2);
+      else
+	fprintf (file, "\t.blkb\t%o\n", size & 0xffff);
     }
-  fprintf (file, "\n");
-  assemble_name (file, name);
-  fputs (":", file);
-  ASM_OUTPUT_SKIP (file, size);
+  else
+    {
+      if (global)
+	{
+	  fprintf (file, ".globl ");
+	  assemble_name (file, name);
+	}
+      fprintf (file, "\n");
+      assemble_name (file, name);
+      fputs (":", file);
+      ASM_OUTPUT_SKIP (file, size);
+    }  
 }
 
 /* Special format operators handled here:
@@ -855,7 +801,7 @@  pdp11_asm_print_operand (FILE *file, rtx x, int co
     fprintf (file, "%s", reg_names[REGNO (x)]);
   else if (GET_CODE (x) == MEM)
     output_address (GET_MODE (x), XEXP (x, 0));
-  else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != SImode)
+  else if (GET_CODE (x) == CONST_DOUBLE && FLOAT_MODE_P (GET_MODE (x)))
     {
       REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (x), sval);
       if (TARGET_DEC_ASM)
@@ -1013,8 +959,7 @@  static int
 pdp11_register_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
 			  reg_class_t c1, reg_class_t c2)
 {
-  if (((c1 == MUL_REGS || c1 == GENERAL_REGS) &&
-       (c2 == MUL_REGS || c2 == GENERAL_REGS)))
+  if (CPU_REG_CLASS (c1) && CPU_REG_CLASS (c2))
     return 2;
   else if ((c1 >= LOAD_FPU_REGS && c1 <= FPU_REGS && c2 == LOAD_FPU_REGS) ||
 	   (c2 >= LOAD_FPU_REGS && c2 <= FPU_REGS && c1 == LOAD_FPU_REGS))
@@ -1512,50 +1457,32 @@  no_side_effect_operand(rtx op, machine_mode mode A
   return FALSE;
 }
 
-
-/*
- * expand a block move:
- *
- * operands[0]	... to
- * operands[1]  ... from
- * operands[2]  ... length
- * operands[3]  ... alignment
- */
-
-void
-expand_block_move(rtx *operands)
+/* Return TRUE if op is a push or pop using the register "regno".  */
+bool
+pushpop_regeq (rtx op, int regno)
 {
-    rtx lb, test;
-    rtx fromop, toop, counter;
-    int count;
+  rtx addr;
+  
+  /* False if not memory reference.  */
+  if (GET_CODE (op) != MEM)
+    return FALSE;
+  
+  /* Get the address of the memory reference.  */
+  addr = XEXP (op, 0);
 
-    /* Transform BLKmode MEM reference into a (reg)+ operand.  */
-    toop = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
-    toop = gen_rtx_POST_INC (Pmode, toop);
-    fromop = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
-    fromop = gen_rtx_POST_INC (Pmode, fromop);
-
-    count = INTVAL (operands[2]);
-    if (INTVAL (operands [3]) >= 2 && (count & 1) == 0)
-      {
-	count >>= 1;
-	toop = gen_rtx_MEM (HImode, toop);
-	fromop = gen_rtx_MEM (HImode, fromop);
-      }
-    else
-      {
-	toop = gen_rtx_MEM (QImode, toop);
-	fromop = gen_rtx_MEM (QImode, fromop);
-      }
-    counter = copy_to_mode_reg (HImode, gen_rtx_CONST_INT (HImode, count));
-
-    /* Label at top of loop */
-    lb = gen_label_rtx ();
-    emit_label (lb);
-    emit_move_insn (toop, fromop);
-    emit_insn (gen_subhi3 (counter, counter, const1_rtx));
-    test = gen_rtx_NE (HImode, counter, const0_rtx);
-    emit_jump_insn (gen_cbranchhi4 (test, counter, const0_rtx, lb));
+  if (GET_CODE (addr) == MEM)
+    addr = XEXP (addr, 0);
+    
+  switch (GET_CODE (addr))
+    {
+    case PRE_DEC:
+    case POST_INC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      return REGNO (XEXP (addr, 0)) == regno;
+    default:
+      return FALSE;
+    }
 }
 
 /* This function checks whether a real value can be encoded as
@@ -1565,7 +1492,12 @@  int
 legitimate_const_double_p (rtx address)
 {
   long sval[2];
+
+  /* If it's too big for HOST_WIDE_INT, it's definitely to big here.  */
+  if (GET_MODE (address) == VOIDmode)
+    return 0;
   REAL_VALUE_TO_TARGET_DOUBLE (*CONST_DOUBLE_REAL_VALUE (address), sval);
+
   if ((sval[0] & 0xffff) == 0 && sval[1] == 0)
     return 1;
   return 0;
@@ -1723,7 +1655,7 @@  pdp11_legitimate_address_p (machine_mode mode,
 	  && GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS
 	  && GET_CODE (XEXP (xfoob, 0)) == REG
 	  && REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM
-	  && CONSTANT_P (XEXP (xfoob, 1))
+	  && CONST_INT_P (XEXP (xfoob, 1))
 	  && INTVAL (XEXP (xfoob,1)) == -2;
 
       case POST_MODIFY:
@@ -1733,7 +1665,7 @@  pdp11_legitimate_address_p (machine_mode mode,
 	  && GET_CODE ((xfoob = XEXP (operand, 1))) == PLUS
 	  && GET_CODE (XEXP (xfoob, 0)) == REG
 	  && REGNO (XEXP (xfoob, 0)) == STACK_POINTER_REGNUM
-	  && CONSTANT_P (XEXP (xfoob, 1))
+	  && CONST_INT_P (XEXP (xfoob, 1))
 	  && INTVAL (XEXP (xfoob,1)) == 2;
 
       case MEM:
@@ -1792,8 +1724,8 @@  pdp11_legitimate_address_p (machine_mode mode,
 enum reg_class
 pdp11_regno_reg_class (int regno)
 { 
-  if (regno == FRAME_POINTER_REGNUM || regno == ARG_POINTER_REGNUM)
-    return GENERAL_REGS;
+  if (regno == ARG_POINTER_REGNUM)
+    return NOTSP_REG;
   else if (regno == CC_REGNUM || regno == FCC_REGNUM)
     return CC_REGS;
   else if (regno > AC3_REGNUM)
@@ -1800,8 +1732,10 @@  pdp11_regno_reg_class (int regno)
     return NO_LOAD_FPU_REGS;
   else if (regno >= AC0_REGNUM)
     return LOAD_FPU_REGS;
-  else if (regno & 1)
-    return MUL_REGS;
+  else if (regno == 6)
+    return NOTR0_REG;
+  else if (regno < 6)
+    return NOTSP_REG;
   else
     return GENERAL_REGS;
 }
@@ -1815,11 +1749,11 @@  pdp11_fixed_cc_regs (unsigned int *p1, unsigned in
   return true;
 }
 
-int
-pdp11_sp_frame_offset (void)
+static int
+pdp11_reg_save_size (void)
 {
   int offset = 0, regno;
-  offset = get_frame_size();
+
   for (regno = 0; regno <= PC_REGNUM; regno++)
     if (pdp11_saved_regno (regno))
       offset += 2;
@@ -1836,33 +1770,19 @@  pdp11_fixed_cc_regs (unsigned int *p1, unsigned in
 int
 pdp11_initial_elimination_offset (int from, int to)
 {
+  /* Get the size of the register save area.  */
   int spoff;
   
-  if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
-    return 4;
-  else if (from == FRAME_POINTER_REGNUM
-	   && to == HARD_FRAME_POINTER_REGNUM)
-    return 0;
+  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+    return get_frame_size ();
+  else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+    return pdp11_reg_save_size () + 2;
+  else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+    return pdp11_reg_save_size () + 2 + get_frame_size ();
   else
-    {
-      gcc_assert (to == STACK_POINTER_REGNUM);
+    gcc_assert (0);
+}
 
-      /* Get the size of the register save area.  */
-      spoff = pdp11_sp_frame_offset ();
-      if (from == FRAME_POINTER_REGNUM)
-	return spoff;
-
-      gcc_assert (from == ARG_POINTER_REGNUM);
-
-      /* If there is a frame pointer, that is saved too.  */
-      if (frame_pointer_needed)
-	spoff += 2;
-      
-      /* Account for the saved PC in the function call.  */
-      return spoff + 2;
-    }
-}    
-
 /* A copy of output_addr_const modified for pdp11 expression syntax.
    output_addr_const also gets called for %cDIGIT and %nDIGIT, which we don't
    use, and for debugging output, which we don't support with this port either.
@@ -1913,21 +1833,6 @@  output_addr_const_pdp11 (FILE *file, rtx x)
       output_addr_const_pdp11 (file, XEXP (x, 0));
       break;
 
-    case CONST_DOUBLE:
-      if (GET_MODE (x) == VOIDmode)
-	{
-	  /* We can use %o if the number is one word and positive.  */
-	  if (TARGET_DEC_ASM)
-	    fprintf (file, "%o", (int) CONST_DOUBLE_LOW (x) & 0xffff);
-	  else
-	    fprintf (file, "%#o", (int) CONST_DOUBLE_LOW (x) & 0xffff);
-	}
-      else
-	/* We can't handle floating point constants;
-	   PRINT_OPERAND must handle them.  */
-	output_operand_lossage ("floating constant misused");
-      break;
-
     case PLUS:
       /* Some assemblers need integer constants to appear last (e.g. masm).  */
       if (GET_CODE (XEXP (x, 0)) == CONST_INT)
@@ -2033,7 +1938,7 @@  pdp11_expand_shift (rtx *operands, rtx (*shift_sc)
   rtx r, test;
   rtx_code_label *lb;
   
-  if (CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
+  if (CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
     emit_insn ((*shift_sc) (operands[0], operands[1], operands[2]));
   else if (TARGET_40_PLUS)
     return false;
@@ -2043,7 +1948,7 @@  pdp11_expand_shift (rtx *operands, rtx (*shift_sc)
       r = gen_reg_rtx (HImode);
       emit_move_insn (operands[0], operands[1]);
       emit_move_insn (r, operands[2]);
-      if (!CONSTANT_P (operands[2]))
+      if (!CONST_INT_P (operands[2]))
 	{
 	  test = gen_rtx_LE (HImode, r, const0_rtx);
 	  emit_jump_insn (gen_cbranchhi4 (test, r, const0_rtx, lb));
@@ -2053,7 +1958,7 @@  pdp11_expand_shift (rtx *operands, rtx (*shift_sc)
 	 optimizer and it doesn't appreciate flow changes happening
 	 while it's doing things.  */
       emit_insn ((*shift_base) (operands[0], operands[1], r));
-      if (!CONSTANT_P (operands[2]))
+      if (!CONST_INT_P (operands[2]))
 	{
 	  emit_label (lb);
 
@@ -2072,16 +1977,20 @@  const char *
 pdp11_assemble_shift (rtx *operands, machine_mode m, int code)
 {
   int i, n;
-  rtx exops[4][2];
+  rtx inops[2];
+  rtx exops[2][2];
   rtx lb[1];
   pdp11_action action[2];
-  const bool small = CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]));
+  const bool small = CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2]));
 
   gcc_assert (small || !TARGET_40_PLUS);
 
   if (m == E_SImode)
-      pdp11_expand_operands (operands, exops, 1, action, either);
-
+    {
+      inops[0] = operands[0];
+      pdp11_expand_operands (inops, exops, 1, 2, action, either);
+    }
+  
   if (!small)
     {
       /* Loop case, generate the top of loop label.  */
@@ -2179,7 +2088,7 @@  pdp11_shift_length (rtx *operands, machine_mode m,
   /* If shifting by a small constant, the loop is unrolled by the
      shift count.  Otherwise, account for the size of the decrement
      and branch.  */
-  if (CONSTANT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
+  if (CONST_INT_P (operands[2]) && pdp11_small_shift (INTVAL (operands[2])))
     shift_size *= INTVAL (operands[2]);
   else
     shift_size += 4;
@@ -2191,6 +2100,39 @@  pdp11_shift_length (rtx *operands, machine_mode m,
   return shift_size;
 }
 
+/* Return the length of 2 or 4 word integer compares.  */
+int
+pdp11_cmp_length (rtx *operands, int words)
+{
+  rtx inops[2];
+  rtx exops[4][2];
+  rtx lb[1];
+  int i, len = 0;
+
+  if (!reload_completed)
+    return 2;
+  
+  inops[0] = operands[0];
+  inops[1] = operands[1];
+  
+  pdp11_expand_operands (inops, exops, 2, words, NULL, big);
+
+  for (i = 0; i < words; i++)
+    {
+      len += 4;    /* cmp instruction word and branch that follows.  */
+      if (!REG_P (exops[i][0]) &&
+	  !simple_memory_operand (exops[i][0], HImode))
+	len += 2;  /* first operand extra word.  */
+      if (!REG_P (exops[i][1]) &&
+	  !simple_memory_operand (exops[i][1], HImode) &&
+	  !(CONST_INT_P (exops[i][1]) && INTVAL (exops[i][1]) == 0))
+	len += 2;  /* second operand extra word.  */
+    }
+
+  /* Deduct one word because there is no branch at the end.  */
+  return len - 2;
+}
+
 /* Prepend to CLOBBERS hard registers that are automatically clobbered
    for an asm We do this for CC_REGNUM and FCC_REGNUM (on FPU target)
    to maintain source compatibility with the original cc0-based
Index: config/pdp11/pdp11.h
===================================================================
--- config/pdp11/pdp11.h	(revision 265931)
+++ config/pdp11/pdp11.h	(working copy)
@@ -32,6 +32,20 @@  along with GCC; see the file COPYING3.  If not see
   do						\
     {						\
       builtin_define_std ("pdp11");		\
+      if (TARGET_INT16) 					\
+	builtin_define_with_int_value ("__pdp11_int", 16);	\
+      else							\
+	builtin_define_with_int_value ("__pdp11_int", 32);	\
+      if (TARGET_40)						\
+	builtin_define_with_int_value ("__pdp11_model", 40);	\
+      else if (TARGET_45)					\
+	builtin_define_with_int_value ("__pdp11_model", 45);	\
+      else							\
+	builtin_define_with_int_value ("__pdp11_model", 10);	\
+      if (TARGET_FPU)						\
+	builtin_define ("__pdp11_fpu");				\
+      if (TARGET_AC0)						\
+	builtin_define ("__pdp11_ac0");				\
     }						\
   while (0)
 
@@ -153,7 +167,7 @@  extern const struct real_format pdp11_d_format;
 #define FIXED_REGISTERS  \
 {0, 0, 0, 0, 0, 0, 1, 1, \
  0, 0, 0, 0, 0, 0, 1, 1, \
- 1, 1 }
+ 1 }
 
 
 
@@ -168,7 +182,7 @@  extern const struct real_format pdp11_d_format;
 #define CALL_USED_REGISTERS  \
 {1, 1, 0, 0, 0, 0, 1, 1, \
  0, 0, 0, 0, 0, 0, 1, 1, \
- 1, 1 }
+ 1 }
 
 
 /* Specify the registers used for certain standard purposes.
@@ -211,6 +225,13 @@  CC_REGS is the condition codes (CPU and FPU)
 
 enum reg_class
   { NO_REGS,
+    NOTR0_REG,
+    NOTR1_REG,
+    NOTR2_REG,
+    NOTR3_REG,
+    NOTR4_REG,
+    NOTR5_REG,
+    NOTSP_REG,
     MUL_REGS,
     GENERAL_REGS,
     LOAD_FPU_REGS,
@@ -229,6 +250,13 @@  enum reg_class
 
 #define REG_CLASS_NAMES  \
   { "NO_REGS",		 \
+    "NOTR0_REG",	 \
+    "NOTR1_REG",	 \
+    "NOTR2_REG",	 \
+    "NOTR3_REG",	 \
+    "NOTR4_REG",	 \
+    "NOTR5_REG",	 \
+    "SP_REG",		 \
     "MUL_REGS", 	 \
     "GENERAL_REGS",	 \
     "LOAD_FPU_REGS",	 \
@@ -243,13 +271,20 @@  enum reg_class
 
 #define REG_CLASS_CONTENTS \
   { {0x00000},	/* NO_REGS */		\
-    {0x000aa},	/* MUL_REGS */		\
-    {0x0c0ff},	/* GENERAL_REGS */	\
+    {0x000fe},	/* NOTR0_REG */		\
+    {0x000fd},	/* NOTR1_REG */		\
+    {0x000fb},	/* NOTR2_REG */		\
+    {0x000f7},	/* NOTR3_REG */		\
+    {0x000ef},	/* NOTR4_REG */		\
+    {0x000df},	/* NOTR5_REG */		\
+    {0x000bf},	/* NOTSP_REG */		\
+    {0x0002a},	/* MUL_REGS */		\
+    {0x040ff},	/* GENERAL_REGS */	\
     {0x00f00},	/* LOAD_FPU_REGS */	\
     {0x03000},	/* NO_LOAD_FPU_REGS */ 	\
     {0x03f00},	/* FPU_REGS */		\
-    {0x30000},	/* CC_REGS */		\
-    {0x3ffff}}	/* ALL_REGS */
+    {0x18000},	/* CC_REGS */		\
+    {0x1ffff}}	/* ALL_REGS */
 
 /* The same information, inverted:
    Return the class number of the smallest class containing
@@ -262,13 +297,17 @@  enum reg_class
 #define INDEX_REG_CLASS GENERAL_REGS
 #define BASE_REG_CLASS GENERAL_REGS
 
+/* Return TRUE if the class is a CPU register.  */
+#define CPU_REG_CLASS(CLASS) \
+  (CLASS >= NOTR0_REG && CLASS <= GENERAL_REGS)
+  
 /* Return the maximum number of consecutive registers
    needed to represent mode MODE in a register of class CLASS.  */
 #define CLASS_MAX_NREGS(CLASS, MODE)	\
-((CLASS == GENERAL_REGS || CLASS == MUL_REGS)?				\
-  ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD):	\
-  1									\
-)
+  (CPU_REG_CLASS (CLASS) ?	\
+   ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD):	\
+   1									\
+  )
 
 /* Stack layout; function entry, exit and calling.  */
 
@@ -328,8 +367,7 @@  extern int current_first_parm_offset;
 /* Output assembler code to FILE to increment profiler label # LABELNO
    for profiling a function entry.  */
 
-#define FUNCTION_PROFILER(FILE, LABELNO)  \
-   gcc_unreachable ();
+#define FUNCTION_PROFILER(FILE, LABELNO)
 
 /* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
    the stack pointer does not matter.  The value is tested only in
@@ -336,8 +374,6 @@  extern int current_first_parm_offset;
    functions that have frame pointers.
    No definition is equivalent to always zero.  */
 
-extern int may_call_alloca;
-
 #define EXIT_IGNORE_STACK	1
 
 /* Definitions for register eliminations.
@@ -347,17 +383,14 @@  extern int current_first_parm_offset;
    followed by "to".  Eliminations of the same "from" register are listed
    in order of preference.
 
-   There are two registers that can always be eliminated on the pdp11.
-   The frame pointer and the arg pointer can be replaced by either the
-   hard frame pointer or to the stack pointer, depending upon the
-   circumstances.  The hard frame pointer is not used before reload and
-   so it is not eligible for elimination.  */
+   There are two registers that can be eliminated on the pdp11.  The
+   arg pointer can be replaced by the frame pointer; the frame pointer
+   can often be replaced by the stack pointer.  */
 
 #define ELIMINABLE_REGS					\
 {{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM},		\
- { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM},	\
- { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},		\
- { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
+ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM},		\
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
 
 #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
   ((OFFSET) = pdp11_initial_elimination_offset ((FROM), (TO)))
@@ -514,8 +547,8 @@  extern int current_first_parm_offset;
 
 #define REGISTER_NAMES \
 {"r0", "r1", "r2", "r3", "r4", "r5", "sp", "pc",     \
- "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "fp", "ap", \
- "cc", "fcc" }
+ "ac0", "ac1", "ac2", "ac3", "ac4", "ac5", "ap", "cc", \
+ "fcc" }
 
 /* Globalizing directive for a label.  */
 #define GLOBAL_ASM_OP "\t.globl\t"
@@ -568,28 +601,22 @@  extern int current_first_parm_offset;
 #define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE)  \
   pdp11_output_addr_vec_elt (FILE, VALUE)
 
-/* This is how to output an assembler line
-   that says to advance the location counter
-   to a multiple of 2**LOG bytes. 
+/* This is how to output an assembler line that says to advance the
+   location counter to a multiple of 2**LOG bytes.  Only values 0 and
+   1 should appear, but due to PR87795 larger values (which are not
+   supported) can also appear.  So we treat all alignment of LOG >= 1
+   as word (2 byte) alignment.
 */
 
 #define ASM_OUTPUT_ALIGN(FILE,LOG)	\
-  switch (LOG)				\
-    {					\
-      case 0:				\
-	break;				\
-      case 1:				\
-	fprintf (FILE, "\t.even\n");	\
-	break;				\
-      default:				\
-	gcc_unreachable ();		\
-    }
+  if (LOG != 0)				\
+    fprintf (FILE, "\t.even\n")
 
 #define ASM_OUTPUT_SKIP(FILE,SIZE)  \
   if (TARGET_DEC_ASM) \
-    fprintf (FILE, "\t.blkb\t%ho\n", (SIZE) & 0xffff);	\
+    fprintf (FILE, "\t.blkb\t%o\n", (SIZE) & 0xffff);	\
   else							\
-    fprintf (FILE, "\t.=.+ %#ho\n", (SIZE) & 0xffff);
+    fprintf (FILE, "\t.=.+ %#o\n", (SIZE) & 0xffff);
 
 /* This says how to output an assembler line
    to define a global common symbol.  */
@@ -597,7 +624,6 @@  extern int current_first_parm_offset;
 #define ASM_OUTPUT_ALIGNED_COMMON(FILE, NAME, SIZE, ALIGN)  \
   pdp11_asm_output_var (FILE, NAME, SIZE, ALIGN, true)
 
-
 /* This says how to output an assembler line
    to define a local common symbol.  */
 
Index: config/pdp11/pdp11.md
===================================================================
--- config/pdp11/pdp11.md	(revision 265931)
+++ config/pdp11/pdp11.md	(working copy)
@@ -26,6 +26,7 @@ 
     UNSPECV_BLOCKAGE
     UNSPECV_SETD
     UNSPECV_SETI
+    UNSPECV_MOVMEM
   ])
 
 (define_constants
@@ -33,7 +34,7 @@ 
    ;; Register numbers
    (R0_REGNUM     	  0)
    (RETVAL_REGNUM     	  0)
-   (HARD_FRAME_POINTER_REGNUM  5)
+   (FRAME_POINTER_REGNUM  5)
    (STACK_POINTER_REGNUM  6)
    (PC_REGNUM             7)
    (AC0_REGNUM            8)
@@ -40,15 +41,14 @@ 
    (AC3_REGNUM            11)
    (AC4_REGNUM            12)
    (AC5_REGNUM            13)
-   ;; The next two are not physical registers but are used for addressing
-   ;; arguments.
-   (FRAME_POINTER_REGNUM  14)
-   (ARG_POINTER_REGNUM    15)
+   ;; The next one is not a physical register but is used for
+   ;; addressing arguments.
+   (ARG_POINTER_REGNUM    14)
    ;; Condition code registers
-   (CC_REGNUM             16)
-   (FCC_REGNUM            17)
+   (CC_REGNUM             15)
+   (FCC_REGNUM            16)
    ;; End of hard registers
-   (FIRST_PSEUDO_REGISTER 18)
+   (FIRST_PSEUDO_REGISTER 17)
    
    ;; Branch offset limits, as byte offsets from (pc).  That is NOT
    ;; the same thing as "instruction address" -- it is for backward
@@ -178,13 +178,8 @@ 
   DONE;
 })
 
-(define_expand "return"
+(define_insn "rtspc"
   [(return)]
-  "reload_completed && !frame_pointer_needed && pdp11_sp_frame_offset () == 0"
-  "")
-
-(define_insn "*rts"
-  [(return)]
   ""
   "rts\tpc")
 
@@ -249,6 +244,78 @@ 
    cmp<PDPint:isfx>\t%0,%1"
   [(set_attr "length" "2,2,4,4,4,6")])
 
+;; Two word compare
+(define_insn "cmpsi"
+  [(set (reg:CC CC_REGNUM)
+	(compare:CC (match_operand:SI 0 "general_operand" "rDQi")
+		    (match_operand:SI 1 "general_operand" "rDQi")))]
+  ""
+{
+  rtx inops[2];
+  rtx exops[2][2];
+  rtx lb[1];
+  
+  inops[0] = operands[0];
+  inops[1] = operands[1];
+  pdp11_expand_operands (inops, exops, 2, 2, NULL, big);
+  lb[0] = gen_label_rtx ();
+  
+  if (CONST_INT_P (exops[0][1]) && INTVAL (exops[0][1]) == 0)
+   output_asm_insn ("tst\t%0", exops[0]);
+  else
+   output_asm_insn ("cmp\t%0,%1", exops[0]);
+  output_asm_insn ("bne\t%l0", lb);
+  if (CONST_INT_P (exops[1][1]) && INTVAL (exops[1][1]) == 0)
+   output_asm_insn ("tst\t%0", exops[1]);
+  else
+   output_asm_insn ("cmp\t%0,%1", exops[1]);
+  output_asm_label (lb[0]);
+  fputs (":\n", asm_out_file);
+
+  return "";
+}
+  [(set (attr "length")
+	(symbol_ref "pdp11_cmp_length (operands, 2)"))
+   (set_attr "base_cost" "0")])
+
+;; Four word compare
+(define_insn "cmpdi"
+  [(set (reg:CC CC_REGNUM)
+	(compare:CC (match_operand:DI 0 "general_operand" "rDQi")
+		    (match_operand:DI 1 "general_operand" "rDQi")))]
+  ""
+{
+  rtx inops[4];
+  rtx exops[4][2];
+  rtx lb[1];
+  int i;
+  
+  inops[0] = operands[0];
+  inops[1] = operands[1];
+  pdp11_expand_operands (inops, exops, 2, 4, NULL, big);
+  lb[0] = gen_label_rtx ();
+
+  for (i = 0; i < 3; i++)
+    {
+      if (CONST_INT_P (exops[i][1]) && INTVAL (exops[i][1]) == 0)
+        output_asm_insn ("tst\t%0", exops[i]);
+      else
+        output_asm_insn ("cmp\t%0,%1", exops[i]);
+       output_asm_insn ("bne\t%l0", lb);
+     }
+  if (CONST_INT_P (exops[3][1]) && INTVAL (exops[3][1]) == 0)
+   output_asm_insn ("tst\t%0", exops[3]);
+  else
+   output_asm_insn ("cmp\t%0,%1", exops[3]);
+  output_asm_label (lb[0]);
+   fputs (":\n", asm_out_file);
+
+  return "";
+}
+  [(set (attr "length")
+	(symbol_ref "pdp11_cmp_length (operands, 2)"))
+   (set_attr "base_cost" "0")])
+
 ;; sob instruction
 ;;
 ;; This expander has to check for mode match because the doloop pass
@@ -368,8 +435,8 @@ 
 (define_insn_and_split "cbranch<mode>4"
   [(set (pc)
 	(if_then_else (match_operator 0 "ordered_comparison_operator"
-		       [(match_operand:PDPint 1 "general_operand" "g")
-			(match_operand:PDPint 2 "general_operand" "g")])
+		       [(match_operand:QHSDint 1 "general_operand" "g")
+			(match_operand:QHSDint 2 "general_operand" "g")])
 		      (label_ref (match_operand 3 "" ""))
 		      (pc)))]
   ""
@@ -473,12 +540,19 @@ 
   "* return output_move_multiple (operands);"
   [(set_attr "length" "4,6,8,16")])
 
+;; That long string of "Z" constraints enforces the restriction that
+;; a register source and auto increment or decrement destination must
+;; not use the same register, because that case is not consistently
+;; implemented across the PDP11 models.
+;; TODO: the same should be applied to insn like add, but this is not
+;; necessary yet because the incdec optimization pass does not apply
+;; that optimization to 3-operand insns at the moment.
 (define_insn "mov<mode>"
-  [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
-	(match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))]
+  [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Za,Zb,Zc,Zd,Ze,Zf,Zg,rD,rR,Q,Q")
+	(match_operand:PDPint 1 "general_operand" "RN,Z0,Z1,Z2,Z3,Z4,Z5,Z6,r,Qi,rRN,Qi"))]
   ""
   ""
-  [(set_attr "length" "2,4,4,6")])
+  [(set_attr "length" "2,2,2,2,2,2,2,2,2,4,4,6")])
 
 ;; This splits all the integer moves: DI and SI modes as well as
 ;; the simple machine operations.
@@ -493,8 +567,8 @@ 
   
 ;; MOV clears V
 (define_insn "*mov<mode>_<cc_cc>"
-  [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,rR,Q,Q")
-	(match_operand:PDPint 1 "general_operand" "rRN,Qi,rRN,Qi"))
+  [(set (match_operand:PDPint 0 "nonimmediate_operand" "=rR,Za,Zb,Zc,Zd,Ze,Zf,Zg,rD,rR,Q,Q")
+	(match_operand:PDPint 1 "general_operand" "RN,Z0,Z1,Z2,Z3,Z4,Z5,Z6,r,Qi,rRN,Qi"))
    (clobber (reg:CC CC_REGNUM))]
   "reload_completed"
   "*
@@ -504,7 +578,7 @@ 
 
   return \"mov<PDPint:isfx>\t%1,%0\";
 }"
-  [(set_attr "length" "2,4,4,6")])
+  [(set_attr "length" "2,2,2,2,2,2,2,2,2,4,4,6")])
 
 ;; movdf has unusually complicated condition code handling, because
 ;; load (into float register) updates the FCC, while store (from
@@ -591,18 +665,98 @@ 
 
 ;; Expand a block move.  We turn this into a move loop.
 (define_expand "movmemhi"
-  [(match_operand:BLK 0 "general_operand" "=g")
-   (match_operand:BLK 1 "general_operand" "g")
-   (match_operand:HI 2 "immediate_operand" "i")
-   (match_operand:HI 3 "immediate_operand" "i")]
+  [(parallel [(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
+	      (match_operand:BLK 0 "general_operand" "=g")
+	      (match_operand:BLK 1 "general_operand" "g")
+	      (match_operand:HI 2 "immediate_operand" "i")
+	      (match_operand:HI 3 "immediate_operand" "i")
+	      (clobber (mem:BLK (scratch)))
+	      (clobber (match_dup 0))
+	      (clobber (match_dup 1))
+	      (clobber (match_dup 2))])]
   ""
   "
 {
-  if (INTVAL (operands[2]) != 0)
-    expand_block_move (operands);
-  DONE;
+  int count;
+  count = INTVAL (operands[2]);
+  if (count == 0)
+    DONE;
+  if (INTVAL (operands [3]) >= 2 && (count & 1) == 0)
+    count >>= 1;
+  else
+    operands[3] = const1_rtx;
+  operands[2] = copy_to_mode_reg (HImode,
+                                  gen_rtx_CONST_INT (HImode, count));
+
+  /* Load BLKmode MEM addresses into scratch registers.  */
+  operands[0] = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
+  operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
 }")
 
+;; Expand a block move.  We turn this into a move loop.
+(define_insn_and_split "movmemhi1"
+  [(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
+   (match_operand:HI 0 "register_operand" "+r")
+   (match_operand:HI 1 "register_operand" "+r")
+   (match_operand:HI 2 "register_operand" "+r")
+   (match_operand:HI 3 "immediate_operand" "i")
+   (clobber (mem:BLK (scratch)))
+   (clobber (match_dup 0))
+   (clobber (match_dup 1))
+   (clobber (match_dup 2))]
+  ""
+  "#"
+  "reload_completed"
+  [(parallel [(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
+	      (match_dup 0)
+	      (match_dup 1)
+	      (match_dup 2)
+	      (match_dup 3)
+	      (clobber (mem:BLK (scratch)))
+	      (clobber (match_dup 0))
+	      (clobber (match_dup 1))
+	      (clobber (match_dup 2))
+	      (clobber (reg:CC CC_REGNUM))])]
+  "")
+
+(define_insn "movmemhi_nocc"
+  [(unspec_volatile [(const_int 0)] UNSPECV_MOVMEM)
+   (match_operand:HI 0 "register_operand" "+r")
+   (match_operand:HI 1 "register_operand" "+r")
+   (match_operand:HI 2 "register_operand" "+r")
+   (match_operand:HI 3 "immediate_operand" "i")
+   (clobber (mem:BLK (scratch)))
+   (clobber (match_dup 0))
+   (clobber (match_dup 1))
+   (clobber (match_dup 2))
+   (clobber (reg:CC CC_REGNUM))]
+  "reload_completed"
+  "*
+{
+  rtx lb[2];
+  
+  lb[0] = operands[2];
+  lb[1] = gen_label_rtx ();
+  
+  output_asm_label (lb[1]);
+  fputs (\":\n\", asm_out_file);
+  if (INTVAL (operands[3]) > 1)
+    output_asm_insn (\"mov\t(%1)+,(%0)+\", operands);
+  else
+    output_asm_insn (\"movb\t(%1)+,(%0)+\", operands);
+  if (TARGET_40_PLUS)
+    output_asm_insn (\"sob\t%0,%l1\", lb);
+  else
+    {
+      output_asm_insn (\"dec\t%0\", lb);
+      output_asm_insn (\"bne\t%l1\", lb);
+    }
+  return \"\";
+}"
+  [(set (attr "length")
+	(if_then_else (match_test "TARGET_40_PLUS")
+		      (const_int 4)
+		      (const_int 6)))])
 
 ;;- truncation instructions
 
@@ -659,7 +813,8 @@ 
         emit_move_insn (r, const0_rtx);
         DONE;
       }
-    else if (!rtx_equal_p (operands[0], operands[1]))
+    else if (!REG_P (operands[1]) ||
+             REGNO (operands[0]) != REGNO (operands[1]))
       {
         /* Alternatives 2 and 3 */
         emit_move_insn (operands[0], const0_rtx);
@@ -975,22 +1130,22 @@ 
   
   inops[0] = operands[0];
   inops[1] = operands[2];
-  pdp11_expand_operands (inops, exops, 2, NULL, either);
+  pdp11_expand_operands (inops, exops, 2, 4, NULL, big);
   
-  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+  if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
     output_asm_insn (\"add\t%1,%0\", exops[0]);
-  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
   {
     output_asm_insn (\"add\t%1,%0\", exops[1]);
     output_asm_insn (\"adc\t%0\", exops[0]);
   }
-  if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
+  if (!CONST_INT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
   {
     output_asm_insn (\"add\t%1,%0\", exops[2]);
     output_asm_insn (\"adc\t%0\", exops[1]);
     output_asm_insn (\"adc\t%0\", exops[0]);
   }
-  if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
+  if (!CONST_INT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
   {
     output_asm_insn (\"add\t%1,%0\", exops[3]);
     output_asm_insn (\"adc\t%0\", exops[2]);
@@ -1037,11 +1192,11 @@ 
   
   inops[0] = operands[0];
   inops[1] = operands[2];
-  pdp11_expand_operands (inops, exops, 2, NULL, either);
+  pdp11_expand_operands (inops, exops, 2, 2, NULL, big);
   
-  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+  if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
     output_asm_insn (\"add\t%1,%0\", exops[0]);
-  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
   {
     output_asm_insn (\"add\t%1,%0\", exops[1]);
     output_asm_insn (\"adc\t%0\", exops[0]);
@@ -1169,22 +1324,22 @@ 
   
   inops[0] = operands[0];
   inops[1] = operands[2];
-  pdp11_expand_operands (inops, exops, 2, NULL, either);
+  pdp11_expand_operands (inops, exops, 2, 4, NULL, big);
   
-  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+  if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
     output_asm_insn (\"sub\t%1,%0\", exops[0]);
-  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
   {
     output_asm_insn (\"sub\t%1,%0\", exops[1]);
     output_asm_insn (\"sbc\t%0\", exops[0]);
   }
-  if (!CONSTANT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
+  if (!CONST_INT_P (exops[2][1]) || INTVAL (exops[2][1]) != 0)
   {
     output_asm_insn (\"sub\t%1,%0\", exops[2]);
     output_asm_insn (\"sbc\t%0\", exops[1]);
     output_asm_insn (\"sbc\t%0\", exops[0]);
   }
-  if (!CONSTANT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
+  if (!CONST_INT_P (exops[3][1]) || INTVAL (exops[3][1]) != 0)
   {
     output_asm_insn (\"sub\t%1,%0\", exops[3]);
     output_asm_insn (\"sbc\t%0\", exops[2]);
@@ -1222,11 +1377,11 @@ 
   
   inops[0] = operands[0];
   inops[1] = operands[2];
-  pdp11_expand_operands (inops, exops, 2, NULL, either);
+  pdp11_expand_operands (inops, exops, 2, 2, NULL, big);
   
-  if (!CONSTANT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
+  if (!CONST_INT_P (exops[0][1]) || INTVAL (exops[0][1]) != 0)
     output_asm_insn (\"sub\t%1,%0\", exops[0]);
-  if (!CONSTANT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
+  if (!CONST_INT_P (exops[1][1]) || INTVAL (exops[1][1]) != 0)
   {
     output_asm_insn (\"sub\t%1,%0\", exops[1]);
     output_asm_insn (\"sbc\t%0\", exops[0]);
@@ -1702,9 +1857,11 @@ 
    (clobber (reg:CC CC_REGNUM))]
   "reload_completed"
   {
+    rtx inops[2];
     rtx exops[4][2];
-    
-    pdp11_expand_operands (operands, exops, 1, NULL, either);
+
+    inops[0] = operands[0];
+    pdp11_expand_operands (inops, exops, 1, 4, NULL, big);
   
     output_asm_insn (\"com\t%0\", exops[3]);
     output_asm_insn (\"com\t%0\", exops[2]);
@@ -1738,9 +1895,11 @@ 
    (clobber (reg:CC CC_REGNUM))]
   "reload_completed"
   {
-    rtx exops[2][2];
-    
-    pdp11_expand_operands (operands, exops, 1, NULL, either);
+    rtx inops[2];
+    rtx exops[4][2];
+
+    inops[0] = operands[0];
+    pdp11_expand_operands (inops, exops, 1, 2, NULL, big);
   
     output_asm_insn (\"com\t%0\", exops[1]);
     output_asm_insn (\"com\t%0\", exops[0]);
@@ -2046,11 +2205,14 @@ 
    (clobber (reg:CC CC_REGNUM))]
   ""
   {
+    rtx inops[2];
     rtx exops[2][2];
     rtx t;
-  
-    pdp11_expand_operands (operands, exops, 2, NULL, either);
 
+    inops[0] = operands[0];
+    inops[1] = operands[1];
+    pdp11_expand_operands (inops, exops, 2, 2, NULL, either);
+
     t = exops[0][0];
     exops[0][0] = exops[1][0];
     exops[1][0] = t;
Index: config/pdp11/pdp11.opt
===================================================================
--- config/pdp11/pdp11.opt	(revision 265931)
+++ config/pdp11/pdp11.opt	(working copy)
@@ -68,4 +68,4 @@  Use UNIX assembler syntax.
 
 mlra
 Target Report Mask(LRA)
-Use LRA register allocator
+Use LRA register allocator.
Index: config/pdp11/t-pdp11
===================================================================
--- config/pdp11/t-pdp11	(revision 265931)
+++ config/pdp11/t-pdp11	(working copy)
@@ -18,6 +18,10 @@ 
 
 MULTILIB_OPTIONS = msoft-float
 
+# Optimize for space
+LIBGCC2_CFLAGS = -Os
+CRTSTUFF_T_CFLAGS = -Os
+
 # Because the pdp11 POINTER_SIZE is only 16, in dwarf2out.c,
 # DWARF_ARANGES_PAD_SIZE is 0, thus a loop in output_aranges that checks
 # (i < (unsigned) DWARF_ARANGES_PAD_SIZE) elicits a warning that the