This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Fix PR target/14454


Hi,

Any attempt to compile C++ code using virtual vararg methods in the context 
of multiple inheritance will fail on the SPARC with the message:

virtual.cpp:23: error: generic thunk code fails for method `virtual void 
   Foo::log(int, const char*, ...)' which uses `...'

This is a regression from 2.95.3 present on all active branches.  It appears 
that the SPARC back-end was not upgraded two years ago when the new 
mechanism for thunks was devised (new proto for TARGET_ASM_OUTPUT_MI_THUNK 
and new target hook TARGET_ASM_CAN_OUTPUT_MI_THUNK).  Part of the problem 
might be that this new interface has never been documented in tm.texi.

[Other ports might be in the same situation, e.g. ARM and PA.]


This patch implements the long-overdue upgrade for the SPARC.  The default 
method used in the generic case when VCALL_OFFSET is non-null is based on 
estimates provided by Nathan (thanks!).  Tested on sparc64-sun-solaris2.9 
and sparc-sun-solaris2.8.

I'd appreciate comments from other SPARC maintainers.  Thanks in advance.


2004-10-10  Eric Botcazou  <ebotcazou@libertysurf.fr>

	PR target/14454
	* config/sparc/sparc.c (TARGET_ASM_CAN_OUTPUT_MI_THUNK): Set to true.
	(sparc_output_mi_thunk): Simplify handling of delta offset.  Add
	handling of vcall offset.
	* doc/tm.texi (TARGET_ASM_OUTPUT_MI_THUNK): Document VCALL_OFFSET.
	(TARGET_ASM_OUTPUT_MI_VCALL_THUNK): Delete.
	(TARGET_ASM_CAN_OUTPUT_MI_THUNK): New target hook.


2004-10-10  Eric Botcazou  <ebotcazou@libertysurf.fr>

	* g++.dg/inherit/thunk1.C: Run on the SPARC.


-- 
Eric Botcazou
Index: config/sparc/sparc.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/sparc/sparc.c,v
retrieving revision 1.271.4.21
diff -c -p -r1.271.4.21 sparc.c
*** config/sparc/sparc.c	8 Oct 2004 13:41:22 -0000	1.271.4.21
--- config/sparc/sparc.c	10 Oct 2004 14:49:28 -0000
*************** enum processor_type sparc_cpu;
*** 270,276 ****
  #undef TARGET_ASM_OUTPUT_MI_THUNK
  #define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk
  #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
! #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
  
  #undef TARGET_RTX_COSTS
  #define TARGET_RTX_COSTS sparc_rtx_costs
--- 270,276 ----
  #undef TARGET_ASM_OUTPUT_MI_THUNK
  #define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk
  #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
! #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
  
  #undef TARGET_RTX_COSTS
  #define TARGET_RTX_COSTS sparc_rtx_costs
*************** sparc_rtx_costs (rtx x, int code, int ou
*** 9286,9301 ****
      }
  }
  
! /* Output code to add DELTA to the first argument, and then jump to FUNCTION.
!    Used for C++ multiple inheritance.  */
  
  static void
  sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
! 		       HOST_WIDE_INT delta,
! 		       HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
  		       tree function)
  {
!   rtx this, insn, funexp, delta_rtx, tmp;
  
    reload_completed = 1;
    epilogue_completed = 1;
--- 9286,9303 ----
      }
  }
  
! /* Output the assembler code for a thunk function.  THUNK_DECL is the
!    declaration for the thunk function itself, FUNCTION is the decl for
!    the target function.  DELTA is an immediate constant offset to be
!    added to THIS.  If VCALL_OFFSET is nonzero, the word at address
!    (*THIS + VCALL_OFFSET) should be additionally added to THIS.  */
  
  static void
  sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
! 		       HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
  		       tree function)
  {
!   rtx this, insn, funexp;
  
    reload_completed = 1;
    epilogue_completed = 1;
*************** sparc_output_mi_thunk (FILE *file, tree 
*** 9313,9338 ****
  
    /* Add DELTA.  When possible use a plain add, otherwise load it into
       a register first.  */
!   delta_rtx = GEN_INT (delta);
!   if (!SPARC_SIMM13_P (delta))
      {
        rtx scratch = gen_rtx_REG (Pmode, 1);
  
!       if (input_operand (delta_rtx, GET_MODE (scratch)))
! 	emit_insn (gen_rtx_SET (VOIDmode, scratch, delta_rtx));
        else
  	{
! 	  if (TARGET_ARCH64)
! 	    sparc_emit_set_const64 (scratch, delta_rtx);
! 	  else
! 	    sparc_emit_set_const32 (scratch, delta_rtx);
  	}
  
!       delta_rtx = scratch;
!     }
  
!   tmp = gen_rtx_PLUS (Pmode, this, delta_rtx);
!   emit_insn (gen_rtx_SET (VOIDmode, this, tmp));
  
    /* Generate a tail call to the target function.  */
    if (! TREE_USED (function))
--- 9315,9387 ----
  
    /* Add DELTA.  When possible use a plain add, otherwise load it into
       a register first.  */
!   if (delta)
      {
+       rtx delta_rtx = GEN_INT (delta);
+ 
+       if (! SPARC_SIMM13_P (delta))
+ 	{
+ 	  rtx scratch = gen_rtx_REG (Pmode, 1);
+ 	  emit_move_insn (scratch, delta_rtx);
+ 	  delta_rtx = scratch;
+ 	}
+ 
+       /* THIS += DELTA.  */
+       emit_insn (gen_add2_insn (this, delta_rtx));
+     }
+ 
+   /* Add the word at address (*THIS + VCALL_OFFSET).  */
+   if (vcall_offset)
+     {
+       rtx vcall_offset_rtx = GEN_INT (vcall_offset);
        rtx scratch = gen_rtx_REG (Pmode, 1);
  
!       if (vcall_offset >= 0)
! 	abort ();
! 
!       /* SCRATCH = *THIS.  */
!       emit_move_insn (scratch, gen_rtx_MEM (Pmode, this));
! 
!       /* Prepare for adding VCALL_OFFSET.  The difficulty is that we
! 	 may not have any available scratch register at this point.  */
!       if (SPARC_SIMM13_P (vcall_offset))
! 	;
!       /* This is the case if ARCH64 (unless -ffixed-g5 is passed).  */
!       else if (! fixed_regs[5]
! 	       /* The below sequence is made up of at least 2 insns,
! 		  while the default method may need only one.  */
! 	       && vcall_offset < -8192)
! 	{
! 	  rtx scratch2 = gen_rtx_REG (Pmode, 5);
! 	  emit_move_insn (scratch2, vcall_offset_rtx);
! 	  vcall_offset_rtx = scratch2;
! 	}
        else
  	{
! 	  rtx increment = GEN_INT (-4096);
! 
! 	  /* VCALL_OFFSET is a negative number whose typical range can be
! 	     estimated as -32768..0 in 32-bit mode.  In almost all cases
! 	     it is therefore cheaper to emit multiple add insns than
! 	     spilling and loading the constant into a register (at least
! 	     6 insns).  */
! 	  while (! SPARC_SIMM13_P (vcall_offset))
! 	    {
! 	      emit_insn (gen_add2_insn (scratch, increment));
! 	      vcall_offset += 4096;
! 	    }
! 	  vcall_offset_rtx = GEN_INT (vcall_offset); /* cannot be 0 */
  	}
  
!       /* SCRATCH = *(*THIS + VCALL_OFFSET).  */
!       emit_move_insn (scratch, gen_rtx_MEM (Pmode,
! 					    gen_rtx_PLUS (Pmode,
! 							  scratch,
! 							  vcall_offset_rtx)));
  
!       /* THIS += *(*THIS + VCALL_OFFSET).  */
!       emit_insn (gen_add2_insn (this, scratch));
!     }
  
    /* Generate a tail call to the target function.  */
    if (! TREE_USED (function))
Index: doc/tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.281.2.17
diff -c -p -r1.281.2.17 tm.texi
*** doc/tm.texi	30 Sep 2004 17:45:49 -0000	1.281.2.17
--- doc/tm.texi	10 Oct 2004 16:53:53 -0000
*************** You need not define this macro if you di
*** 4314,4320 ****
  @code{DELAY_SLOTS_FOR_EPILOGUE}.
  @end defmac
  
! @deftypefn {Target Hook} void TARGET_ASM_OUTPUT_MI_THUNK (FILE *@var{file}, tree @var{thunk_fndecl}, HOST_WIDE_INT @var{delta}, tree @var{function})
  A function that outputs the assembler code for a thunk
  function, used to implement C++ virtual function calls with multiple
  inheritance.  The thunk acts as a wrapper around a virtual function,
--- 4314,4320 ----
  @code{DELAY_SLOTS_FOR_EPILOGUE}.
  @end defmac
  
! @deftypefn {Target Hook} void TARGET_ASM_OUTPUT_MI_THUNK (FILE *@var{file}, tree @var{thunk_fndecl}, HOST_WIDE_INT @var{delta}, HOST_WIDE_INT @var{vcall_offset}, tree @var{function})
  A function that outputs the assembler code for a thunk
  function, used to implement C++ virtual function calls with multiple
  inheritance.  The thunk acts as a wrapper around a virtual function,
*************** in C++.  This is the incoming argument @
*** 4328,4334 ****
  e.g.@: @samp{%o0} on a sparc.  The addition must preserve the values of
  all other incoming arguments.
  
! After the addition, emit code to jump to @var{function}, which is a
  @code{FUNCTION_DECL}.  This is a direct pure jump, not a call, and does
  not touch the return address.  Hence returning from @var{FUNCTION} will
  return to whoever called the current @samp{thunk}.
--- 4328,4342 ----
  e.g.@: @samp{%o0} on a sparc.  The addition must preserve the values of
  all other incoming arguments.
  
! Then, if @var{vcall_offset} is nonzero, an additional adjustment should be
! made after adding @code{delta}.  In particular, if @var{p} is the
! adjusted pointer, the following adjustment should be made:
! 
! @smallexample
! p += (*((ptrdiff_t **)p))[vcall_offset/sizeof(ptrdiff_t)]
! @end smallexample
! 
! After the additions, emit code to jump to @var{function}, which is a
  @code{FUNCTION_DECL}.  This is a direct pure jump, not a call, and does
  not touch the return address.  Hence returning from @var{FUNCTION} will
  return to whoever called the current @samp{thunk}.
*************** front end will generate a less efficient
*** 4348,4366 ****
  not support varargs.
  @end deftypefn
  
! @deftypefn {Target Hook} void TARGET_ASM_OUTPUT_MI_VCALL_THUNK (FILE *@var{file}, tree @var{thunk_fndecl}, HOST_WIDE_INT @var{delta}, int @var{vcall_offset}, tree @var{function})
! A function like @code{TARGET_ASM_OUTPUT_MI_THUNK}, except that if
! @var{vcall_offset} is nonzero, an additional adjustment should be made
! after adding @code{delta}.  In particular, if @var{p} is the
! adjusted pointer, the following adjustment should be made:
! 
! @smallexample
! p += (*((ptrdiff_t **)p))[vcall_offset/sizeof(ptrdiff_t)]
! @end smallexample
! 
! @noindent
! If this function is defined, it will always be used in place of
! @code{TARGET_ASM_OUTPUT_MI_THUNK}.
  @end deftypefn
  
  @node Profiling
--- 4356,4367 ----
  not support varargs.
  @end deftypefn
  
! @deftypefn {Target Hook} bool TARGET_ASM_CAN_OUTPUT_MI_THUNK (FILE *@var{file}, tree @var{thunk_fndecl}, HOST_WIDE_INT @var{delta}, HOST_WIDE_INT @var{vcall_offset}, tree @var{function})
! A function that returns true if TARGET_ASM_OUTPUT_MI_THUNK would be able
! to output the assembler code for the thunk function specified by the
! arguments it is passed, and false otherwise.  In the latter case, the
! generic approach will be used by the C++ front end, with the limitations
! previously exposed.
  @end deftypefn
  
  @node Profiling
Index: g++.dg/inherit/thunk1.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.dg/inherit/thunk1.C,v
retrieving revision 1.5
diff -u -r1.5 thunk1.C
--- g++.dg/inherit/thunk1.C	24 Oct 2002 09:16:36 -0000	1.5
+++ g++.dg/inherit/thunk1.C	10 Oct 2004 18:02:17 -0000
@@ -1,4 +1,4 @@
-// { dg-do run { target i?86-*-* x86_64-*-* s390*-*-* alpha*-*-* ia64-*-* } }
+// { dg-do run { target i?86-*-* x86_64-*-* s390*-*-* alpha*-*-* ia64-*-* sparc*-*-* } }
 
 #include <stdarg.h>
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]