testcase for hppa64 gcc bug

John David Anglin dave@hiauly1.hia.nrc.ca
Thu, 9 Nov 2000 12:39:57 -0500 (EST)


> > So I went down the path of trying to fix things properly by defining
> > ELIMINABLE_REGS and so on, but I ended in a maze of twisty little passages
> > labelled "Unrecognizable instruction", like this one:
> > 
> > /src/parisc/gcc/gcc/libgcc2.c: In function `__moddi3':
> > /src/parisc/gcc/gcc/libgcc2.c:601: Unrecognizable insn:
> > (insn 1289 209 1298 (set (reg:SI 50 %fr22)
> >         (subreg:SI (plus:DI (reg:DI 30 %r30)
> >                 (const_int -272 [0xfffffef0])) 0)) -1 (nil)
> >     (nil))
> > /src/parisc/gcc/gcc/libgcc2.c:601: Internal compiler error in
> > extract_insn, at recog.c:2134
> 
> I am making progress in trying to make the arg_pointer register eliminable.
> I have fixed the above problem.  What was happening was that reload_as_needed
> was incorrectly trying to eliminate the return from millicode calls which
> is also register r29.  I have figured out how to hide it from reload with
> unspec.
> 
> However, the compiler is now too good at eliminating the arg_pointer.  At
> -O3, it completely eliminates the arg_pointer.  However, as I read the ABI,
> the call must always set the arg_pointer before calls.

For the record, here is my final patch regarding making the arg_pointer
eliminable for TARGET_64BIT.  I think the code it generates is correct but
it hasn't been extensively tested.  However, I don't recommend it for
installation since in comparing the assembler code generated with and
without elimination for a couple of test cases, I didn't observe any
significant improvement in the code with the patch.  Possibly, the patch
implicitly disables elimination when the arg_pointer is needed.

I do find that Alan Modra's ARG_POINTER_INVARIANT patch needs to be installed
to get correct code with his test case.

There is one part of the patch below which I think needs to be installed.
That is

	(call, call_value): Always USE the arg_pointer for TARGET_64BIT.

The use for the arg_pointer needs to be pulled out of the `if (flag_pic)'.

Dave
-- 
J. David Anglin                                  dave.anglin@nrc.ca
National Research Council of Canada              (613) 990-0752 (FAX: 952-6605)

2000-11-07  John David Anglin  <dave@hiauly1.hia.nrc.ca>

	* pa-linux64.h (ARG_POINTER_INVARIANT): Define even when the
	arg_pointer is being eliminated.
	(ELIMINABLE_REGS): Enable elimination of the arg_pointer.
	(INITIAL_ELIMINATION_OFFSET): Revise offsets for arg_pointer.
	* pa.md (mulsi3, divsi3, udivsi3, modsi3, umodsi3 and
	canonicalize_funcptr_for_compare): Put "(reg:SI 26)" inside
	unspec to prevent elimination.
	(call, call_value): Always USE the arg_pointer for TARGET_64BIT.
	Use the new addmovdi3 insn to load the arg_pointer register.
	(addmovdi3 and mov_from_r29_si): New insn and expand which prevent
	r29 from being eliminated in call setups and millicode returns.

--- pa-linux64.h.orig	Tue Oct 31 18:38:24 2000
+++ pa-linux64.h	Tue Nov  7 12:17:12 2000
@@ -209,21 +209,18 @@
    that grow to lower addresses.  What fun.  */
 #undef ARGS_GROW_DOWNWARD
 #undef ARG_POINTER_REGNUM
-#define ARG_POINTER_INVARIANT 0
 #define ARG_POINTER_REGNUM 29
+#define ARG_POINTER_INVARIANT 0
 #undef STATIC_CHAIN_REGNUM
 #define STATIC_CHAIN_REGNUM 31
 
-#if 1
-#define ARG_POINTER_INVARIANT 0
-#else
-/* If defined, this macro specifies a table of register pairs used to eliminate
-   unneeded registers that point into the stack frame.  */
+/* If defined, this macro specifies a table of register pairs used to
+   eliminate unneeded registers that point into the stack frame.  */
 
 #define ELIMINABLE_REGS							\
 {									\
-  {ARG_POINTER_REGNUM,	 STACK_POINTER_REGNUM},				\
   {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},				\
+  {ARG_POINTER_REGNUM,	 STACK_POINTER_REGNUM},				\
   {ARG_POINTER_REGNUM,	 FRAME_POINTER_REGNUM},				\
 }
 
@@ -240,19 +237,18 @@
 #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
   do								\
     {								\
-      int fsize;						\
+      int fsize = compute_frame_size (get_frame_size (), 0);	\
 								\
       if ((TO) == FRAME_POINTER_REGNUM				\
 	  && (FROM) == ARG_POINTER_REGNUM)			\
 	{							\
-	  (OFFSET) = - current_function_pretend_args_size - 16;	\
+	  (OFFSET) = fsize + 48 - current_function_outgoing_args_size;	\
 	  break;						\
 	}							\
 								\
       if ((TO) != STACK_POINTER_REGNUM)				\
 	abort ();						\
 								\
-      fsize = compute_frame_size (get_frame_size (), 0);	\
       switch (FROM)						\
 	{							\
 	case FRAME_POINTER_REGNUM:				\
@@ -260,14 +256,13 @@
 	  break;						\
 								\
 	case ARG_POINTER_REGNUM:				\
-	  (OFFSET) = - fsize - current_function_pretend_args_size - 16;	\
+	  (OFFSET) = 48 - current_function_outgoing_args_size;  \
 	  break;						\
 								\
 	default:						\
 	  abort ();						\
 	}							\
     } while (0)
-#endif
 
 #undef SELECT_RTX_SECTION
 #define SELECT_RTX_SECTION(MODE,RTX)	\
--- pa.md.orig	Tue Nov  7 13:50:34 2000
+++ pa.md.work	Wed Nov  8 14:06:05 2000
@@ -3993,7 +3993,7 @@
 	      (clobber (reg:SI 26))
 	      (clobber (reg:SI 25))
 	      (clobber (reg:SI 31))])
-   (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))]
+   (set (match_operand:SI 0 "general_operand" "") (unspec:SI [(reg:SI 29)] 0))]
   ""
   "
 {
@@ -4139,7 +4139,7 @@
 	      (clobber (reg:SI 26))
 	      (clobber (reg:SI 25))
 	      (clobber (reg:SI 31))])
-   (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))]
+   (set (match_operand:SI 0 "general_operand" "") (unspec:SI [(reg:SI 29)] 0))]
   ""
   "
 {
@@ -4197,7 +4197,7 @@
 	      (clobber (reg:SI 26))
 	      (clobber (reg:SI 25))
 	      (clobber (reg:SI 31))])
-   (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))]
+   (set (match_operand:SI 0 "general_operand" "") (unspec:SI [(reg:SI 29)] 0))]
   ""
   "
 {
@@ -4255,7 +4255,7 @@
 	      (clobber (reg:SI 26))
 	      (clobber (reg:SI 25))
 	      (clobber (reg:SI 31))])
-   (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))]
+   (set (match_operand:SI 0 "general_operand" "") (unspec:SI [(reg:SI 29)] 0))]
   ""
   "
 {
@@ -4310,7 +4310,7 @@
 	      (clobber (reg:SI 26))
 	      (clobber (reg:SI 25))
 	      (clobber (reg:SI 31))])
-   (set (match_operand:SI 0 "general_operand" "") (reg:SI 29))]
+   (set (match_operand:SI 0 "general_operand" "") (unspec:SI [(reg:SI 29)] 0))]
   ""
   "
 {
@@ -5785,9 +5785,9 @@
     op = XEXP (operands[0], 0);
 
   if (TARGET_64BIT)
-    emit_move_insn (arg_pointer_rtx,
-		    gen_rtx_PLUS (word_mode, virtual_outgoing_args_rtx,
-				  GEN_INT (64)));
+    emit_insn (gen_addmovdi3 (arg_pointer_rtx,
+			      virtual_outgoing_args_rtx,
+			      GEN_INT (64)));
 
   /* Use two different patterns for calls to explicitly named functions
      and calls through function pointers.  This is necessary as these two
@@ -5809,13 +5809,14 @@
       call_insn = emit_call_insn (gen_call_internal_reg (operands[1]));
     }
 
+  if (TARGET_64BIT)
+    use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
+
   if (flag_pic)
     {
       use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
       use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn),
 	       gen_rtx_REG (word_mode, PIC_OFFSET_TABLE_REGNUM_SAVED));
-      if (TARGET_64BIT)
-	use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
 
       /* After each call we must restore the PIC register, even if it
 	 doesn't appear to be used.
@@ -5961,9 +5962,9 @@
     op = XEXP (operands[1], 0);
 
   if (TARGET_64BIT)
-    emit_move_insn (arg_pointer_rtx,
-		    gen_rtx_PLUS (word_mode, virtual_outgoing_args_rtx,
-				  GEN_INT (64)));
+    emit_insn (gen_addmovdi3 (arg_pointer_rtx,
+			      virtual_outgoing_args_rtx,
+			      GEN_INT (64)));
 
   /* Use two different patterns for calls to explicitly named functions
      and calls through function pointers.  This is necessary as these two
@@ -5989,6 +5990,10 @@
       call_insn = emit_call_insn (gen_call_value_internal_reg (operands[0],
 							       operands[2]));
     }
+
+  if (TARGET_64BIT)
+    use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
+
   if (flag_pic)
     {
       use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
@@ -7124,7 +7129,7 @@
 	      (clobber (reg:SI 22))
 	      (clobber (reg:SI 31))])
    (set (match_operand:SI 0 "register_operand" "")
-	(reg:SI 29))]
+	(unspec:SI [(reg:SI 29)] 0))]
   "! TARGET_PORTABLE_RUNTIME && !TARGET_64BIT && !TARGET_ELF32"
   "
 {
@@ -7236,3 +7241,48 @@
   emit_insn (gen_blockage ());
   DONE;
 }")
+
+;; For TARGET_64BIT, the arg_pointer register is also used for millicode
+;; returns.  The ABI requires that the arg_pointer be set for all calls.
+;; When the arg_pointer is made an eliminable register, eliminate_regs
+;; will eliminate the arg_pointer register from the function call setup and
+;; millicode returns unless the arg_pointer is hidden in a use, clobber or
+;; unspec.
+
+;; This is for loading the arg_pointer in function calls.
+(define_insn "addmovdi3"
+  [(set (unspec:DI [(match_operand:DI 0 "register_operand" "=r,r")] 0)
+        (plus:DI (match_operand:DI 1 "register_operand" "r,r")
+		 (match_operand 2 "const_int_operand" "J,i")))
+   (set (match_dup 0) (match_dup 0))]
+  "TARGET_64BIT"
+  "@
+  ldo %2(%1),%0
+  ldil L'%G2,%0\;add,l %0,%1,%0"
+  [(set_attr "type" "binary,binary")
+   (set_attr "pa_combine_type" "addmove,none")
+   (set_attr "length" "4,8")])
+
+;; This is for millicode return.
+(define_expand "mov_from_r29_si"
+  [(set (match_operand:SI 0 "" "")
+        (unspec:SI [(reg:SI 29)] 0))]
+  ""
+  "
+{
+  if (!TARGET_64BIT)
+    {
+      rtx tmp = gen_rtx_REG (SImode, 29);
+      emit_insn (gen_movsi (operands[0], tmp));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=r")
+	(unspec:SI [(reg:SI 29)] 0))]
+  ""
+  "copy %%r29,%0"
+  [(set_attr "type" "multi")
+   (set_attr "length" "4")])
+