This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[RFC] Fix PR target/14454
- From: Eric Botcazou <ebotcazou at libertysurf dot fr>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Richard Earnshaw <rearnsha at arm dot com>,Dave Anglin <dave dot anglin at nrc dot ca>
- Date: Sun, 10 Oct 2004 20:10:53 +0200
- Subject: [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>