This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++/i86 thunk patch
- From: Nathan Sidwell <nathan at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: rth at redhat dot com, mark at codesourcery dot com
- Date: Mon, 15 Jul 2002 09:46:37 +0100
- Subject: C++/i86 thunk patch
- Organization: Codesourcery LLC
Hi,
this patch implements a new macro ASM_OUTPUT_VMI_THUNK, and
obsoletes ASM_OUTPUT_MI_THUNK. I have implemented that
macro for i86 targets.
The new macro incorporates the 3.0 ABI's vcall offset. The thunk
is passed both the fixed delta and the vcall offset, and it is
responsible for generating code to do both adjusts.
Here's the x86 code for a thunk which requires both adjusts, compiled
with -mregparms=2.
_ZTvn8_n16_N9Overrider2FnEv:
pushl %edx
addl $-8, %eax
movl (%eax), %edx
addl -16(%edx), %eax
popl %edx
jmp _ZN9Overrider2FnEv
Without -mregparms, eax & edx are scratch registers. Instead
of the push & pop, we have a load and store of eax to obtain the
this parm. I have not tested the hammer code path
(though it is similar to the 32 bit regparms path).
This patch fixes PR 7306, which is marked as a regression. It
appears that previously we did not require a thunk in that particular
case, but now we do. My inclination is to not treat it as a
regression, so this patch is inappropriate for the 3.[12] branch.
Your call Mark.
The new ABI also requires return value adjusting thunks,
for covariant return types. I can't see how to do those without
cloning the thunked function.
booted & tested on i686-pc-linux-gnu, ok?
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
2002-07-14 Nathan Sidwell <nathan@codesourcery.com>
* doc/tm.texi (ASM_OUTPUT_VMI_THUNK): Document.
(ASM_OUTPUT_MI_THUNK): Deprecate.
* config/i386/i386-protos.h (x86_output_mi_thunk): Remove.
(x86_output_vmi_thunk): Declare.
* config/i386/i386.c (x86_output_mi_thunk): Remove.
(x86_output_vmi_thunk): Implement
* config/i386/unix.h (ASM_OUTPUT_MI_THUNK): Remove.
(ASM_OUTPUT_VMI_THUNK): Define.
2002-07-14 Nathan Sidwell <nathan@codesourcery.com>
* method.c (use_thunk): Use ASM_OUTPUT_VMI_THUNK, if available.
Index: doc/tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.143
diff -c -3 -p -r1.143 tm.texi
*** doc/tm.texi 27 Jun 2002 17:19:04 -0000 1.143
--- doc/tm.texi 15 Jul 2002 08:22:46 -0000
*************** outputting the insns in this list, usual
*** 4093,4098 ****
--- 4093,4132 ----
You need not define this macro if you did not define
@code{DELAY_SLOTS_FOR_EPILOGUE}.
+ @findex ASM_OUTPUT_VMI_THUNK
+ @item ASM_OUTPUT_VMI_THUNK (@var{file}, @var{thunk_fndecl}, @var{function}, @var{delta}, @var{vcall_offset})
+ A C compound statement that outputs the assembler code for a 3.0 ABI VMI
+ thunk function, used to implement C++ virtual function calls with
+ virtual and/or multiple inheritance. The thunk acts as a wrapper around
+ a virtual function, adjusting the implicit object parameter before
+ handing control off to the real function.
+
+ Emit code to adjust the passed this pointer by adding @var{delta} to it,
+ then use @var{vcall_offset} to index into the now pointed to vtable to
+ obtain the virtual base adjustment. Add the value found to the this
+ pointer. Finally emit code to jump to @var{function}, a FUNCTION_DECL.
+ The effect must be as if @var{function} had been called directly with
+ the adjusted first argument.
+
+ The thunk is a stub function with no function prologue and, of course,
+ no epilogue. The parameters will be in the registers the caller placed
+ them (such as @samp{%o0} on a sparc). You must not corrupt any callee
+ save registers. This macro is responsible for emitting all of the code
+ for a thunk function; @code{TARGET_ASM_FUNCTION_PROLOGUE} and
+ @code{TARGET_ASM_FUNCTION_EPILOGUE} are not invoked.
+
+ It is possible that @var{delta} or @var{vcall_offset} is zero, in which
+ case the associated adjustment should not be done.
+
+ The @var{thunk_fndecl} is redundant. (@var{delta} and @var{function}
+ have already been extracted from it.) It might possibly be useful on
+ some targets, but probably not.
+
+ If you do not define this macro, (nor defined ASM_OUTPUT_MI_THUNK) the
+ target-independent code in the C++ front end will generate a less
+ efficient heavyweight thunk that calls @var{function} instead of jumping
+ to it. The generic approach does not support varargs.
+
@findex ASM_OUTPUT_MI_THUNK
@item ASM_OUTPUT_MI_THUNK (@var{file}, @var{thunk_fndecl}, @var{delta}, @var{function})
A C compound statement that outputs the assembler code for a thunk
*************** If you do not define this macro, the tar
*** 4126,4131 ****
--- 4160,4167 ----
front end will generate a less efficient heavyweight thunk that calls
@var{function} instead of jumping to it. The generic approach does
not support varargs.
+
+ This macro is deprecated. Use ASM_OUTPUT_VMI_THUNK instead.
@end table
@node Profiling
Index: cp/method.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/method.c,v
retrieving revision 1.224
diff -c -3 -p -r1.224 method.c
*** cp/method.c 29 Apr 2002 14:56:24 -0000 1.224
--- cp/method.c 15 Jul 2002 08:22:46 -0000
*************** use_thunk (thunk_fndecl, emit_p)
*** 397,403 ****
DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
BLOCK_VARS (DECL_INITIAL (thunk_fndecl))
= DECL_ARGUMENTS (thunk_fndecl);
!
#ifdef ASM_OUTPUT_MI_THUNK
if (!vcall_offset)
{
--- 397,423 ----
DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
BLOCK_VARS (DECL_INITIAL (thunk_fndecl))
= DECL_ARGUMENTS (thunk_fndecl);
! #ifdef ASM_OUTPUT_VMI_THUNK
! {
! const char *fname;
!
! current_function_decl = thunk_fndecl;
! DECL_RESULT (thunk_fndecl)
! = build_decl (RESULT_DECL, 0, integer_type_node);
! fname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0);
! init_function_start (thunk_fndecl, input_filename, lineno);
! current_function_is_thunk = 1;
! assemble_start_function (thunk_fndecl, fname);
! ASM_OUTPUT_VMI_THUNK (asm_out_file,
! thunk_fndecl, function,
! delta,
! vcall_offset ? TREE_INT_CST_LOW (vcall_offset) : 0);
! assemble_end_function (thunk_fndecl, fname);
! current_function_decl = NULL_TREE;
! cfun = 0;
! TREE_ASM_WRITTEN (thunk_fndecl) = 1;
! }
! #else
#ifdef ASM_OUTPUT_MI_THUNK
if (!vcall_offset)
{
*************** use_thunk (thunk_fndecl, emit_p)
*** 494,500 ****
expand_body (finish_function (0));
}
!
pop_from_top_level ();
}
--- 514,520 ----
expand_body (finish_function (0));
}
! #endif /* !defined (ASM_OUTPUT_VMI_THUNK) */
pop_from_top_level ();
}
Index: config/i386/i386-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386-protos.h,v
retrieving revision 1.75
diff -c -3 -p -r1.75 i386-protos.h
*** config/i386/i386-protos.h 16 Jun 2002 20:18:24 -0000 1.75
--- config/i386/i386-protos.h 15 Jul 2002 08:22:46 -0000
*************** extern tree ix86_handle_shared_attribute
*** 204,210 ****
extern unsigned int i386_pe_section_type_flags PARAMS ((tree, const char *,
int));
extern void i386_pe_asm_named_section PARAMS ((const char *, unsigned int));
! extern void x86_output_mi_thunk PARAMS ((FILE *, int, tree));
extern int x86_field_alignment PARAMS ((tree, int));
#endif
--- 204,210 ----
extern unsigned int i386_pe_section_type_flags PARAMS ((tree, const char *,
int));
extern void i386_pe_asm_named_section PARAMS ((const char *, unsigned int));
! extern void x86_output_vmi_thunk PARAMS ((FILE *, tree, int, int));
extern int x86_field_alignment PARAMS ((tree, int));
#endif
Index: config/i386/i386.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.c,v
retrieving revision 1.429
diff -c -3 -p -r1.429 i386.c
*** config/i386/i386.c 3 Jul 2002 14:15:44 -0000 1.429
--- config/i386/i386.c 15 Jul 2002 08:22:48 -0000
*************** x86_order_regs_for_local_alloc ()
*** 13574,13602 ****
reg_alloc_order [pos++] = 0;
}
void
! x86_output_mi_thunk (file, delta, function)
FILE *file;
- int delta;
tree function;
{
! tree parm;
! rtx xops[3];
! if (ix86_regparm > 0)
! parm = TYPE_ARG_TYPES (TREE_TYPE (function));
else
! parm = NULL_TREE;
! for (; parm; parm = TREE_CHAIN (parm))
! if (TREE_VALUE (parm) == void_type_node)
! break;
! xops[0] = GEN_INT (delta);
if (TARGET_64BIT)
{
- int n = aggregate_value_p (TREE_TYPE (TREE_TYPE (function))) != 0;
- xops[1] = gen_rtx_REG (DImode, x86_64_int_parameter_registers[n]);
- output_asm_insn ("add{q} {%0, %1|%1, %0}", xops);
if (flag_pic)
{
fprintf (file, "\tjmp *");
--- 13574,13686 ----
reg_alloc_order [pos++] = 0;
}
+ /* Output thunk for 3.0 ABI general case inheritance. CALL_DELTA is a
+ fixed delta to add to the this pointer. VCALL_OFFSET is an offset
+ into the vtable. The value there is to be added to the this
+ pointer. CALL_DELTA is applied first. If CALL_DELTA is zero, there
+ is no fixed adjusment. If VCALL_OFFSET is zero, there is no virtual
+ adjustment. FUNCTION is the thunked function. */
+
void
! x86_output_vmi_thunk (file, function, call_delta, vcall_offset)
FILE *file;
tree function;
+ int call_delta, vcall_offset;
{
! rtx xops[7]; /*0-this, 1-delta, 2-vcall, 3-reg-this, 4-tmp, 5-tmp, 6-tmp*/
! int on_stack = 0;
! if (TARGET_64BIT)
! {
! int n = aggregate_value_p (TREE_TYPE (TREE_TYPE (function))) != 0;
!
! xops[0] = gen_rtx_REG (DImode, x86_64_int_parameter_registers[n]);
! xops[4] = gen_rtx_REG (DImode, x86_64_int_parameter_registers[1 - n]);
! }
else
! {
! tree parm;
!
! if (ix86_regparm > 0)
! parm = TYPE_ARG_TYPES (TREE_TYPE (function));
! else
! parm = NULL_TREE;
! for (; parm; parm = TREE_CHAIN (parm))
! if (TREE_VALUE (parm) == void_type_node)
! break;
! if (parm)
! xops[0] = gen_rtx_REG (SImode, 0);
! else
! {
! on_stack = 1;
! xops[0] = gen_rtx_MEM
! (SImode, plus_constant
! (stack_pointer_rtx, aggregate_value_p
! (TREE_TYPE (TREE_TYPE (function))) ? 8 : 4));
! }
! xops[4] = gen_rtx_REG (SImode, 1);
! }
!
! xops[1] = GEN_INT (call_delta);
!
! if (vcall_offset)
! {
! /* Add vcall offset from vtable, and possibly fixed delta. */
! if (TARGET_64BIT)
! {
! xops[3] = xops[0];
! xops[2] = gen_rtx_MEM (DImode,
! plus_constant (xops[4], vcall_offset));
! xops[5] = gen_rtx_MEM (DImode, xops[3]);
!
! /* We might be able to do better by looking for a
! caller-save register that is not passing a parameter. */
! output_asm_insn ("push{q} %4", xops); /* save tmp */
!
! if (call_delta)
! output_asm_insn ("add{q} {%1, %3|%3, %1}", xops);
! output_asm_insn ("mov{q} %5, %4", xops); /* get vtable ptr */
! output_asm_insn ("add{q} %2, %3", xops); /* index & add */
!
! output_asm_insn ("pop{q} %4", xops); /* restore tmp */
! }
! else
! {
! xops[3] = on_stack ? gen_rtx_REG (SImode, 0) : xops[0];
! xops[2] = gen_rtx_MEM (SImode,
! plus_constant (xops[4], vcall_offset));
! xops[5] = gen_rtx_MEM (SImode, xops[3]);
!
! if (on_stack)
! output_asm_insn ("mov{l} %0, %3", xops); /* load this parm */
! else
! /* We might be able to do better by looking for a
! caller-save register that is not passing a parameter. */
! output_asm_insn ("push{l} %4", xops); /* save tmp */
!
! if (call_delta)
! output_asm_insn ("add{l} {%1, %3|%3, %1}", xops);
! output_asm_insn ("mov{l} %5, %4", xops); /* get vtable ptr */
! output_asm_insn ("add{l} %2, %3", xops); /* index & add */
!
! if (on_stack)
! output_asm_insn ("mov{l} %3, %0", xops); /* store this parm */
! else
! output_asm_insn ("pop{l} %4", xops); /* restore tmp */
! }
! }
! else if (call_delta)
! {
! /* Add fixed offset */
! if (TARGET_64BIT)
! output_asm_insn ("add{q} {%1, %0|%0, %1}", xops);
! else
! output_asm_insn ("add{l} {%1, %0|%0, %1}", xops);
! }
! /* Jump to the thunked function. */
if (TARGET_64BIT)
{
if (flag_pic)
{
fprintf (file, "\tjmp *");
*************** x86_output_mi_thunk (file, delta, functi
*** 13612,13631 ****
}
else
{
- if (parm)
- xops[1] = gen_rtx_REG (SImode, 0);
- else if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
- xops[1] = gen_rtx_MEM (SImode, plus_constant (stack_pointer_rtx, 8));
- else
- xops[1] = gen_rtx_MEM (SImode, plus_constant (stack_pointer_rtx, 4));
- output_asm_insn ("add{l} {%0, %1|%1, %0}", xops);
-
if (flag_pic)
{
xops[0] = pic_offset_table_rtx;
xops[1] = gen_label_rtx ();
xops[2] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
!
if (ix86_regparm > 2)
abort ();
output_asm_insn ("push{l}\t%0", xops);
--- 13696,13707 ----
}
else
{
if (flag_pic)
{
xops[0] = pic_offset_table_rtx;
xops[1] = gen_label_rtx ();
xops[2] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
!
if (ix86_regparm > 2)
abort ();
output_asm_insn ("push{l}\t%0", xops);
Index: config/i386/unix.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/unix.h,v
retrieving revision 1.20
diff -c -3 -p -r1.20 unix.h
*** config/i386/unix.h 15 May 2002 09:00:25 -0000 1.20
--- config/i386/unix.h 15 Jul 2002 08:22:48 -0000
*************** Boston, MA 02111-1307, USA. */
*** 77,83 ****
: (MODE) == TImode || VECTOR_MODE_P (MODE) ? FIRST_SSE_REG \
: 0)
! /* Output code to add DELTA to the first argument, and then jump to FUNCTION.
! Used for C++ multiple inheritance. */
! #define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \
! x86_output_mi_thunk (FILE, DELTA, FUNCTION);
--- 77,82 ----
: (MODE) == TImode || VECTOR_MODE_P (MODE) ? FIRST_SSE_REG \
: 0)
! /* Output code for general case thunk using the 3.0 ABI layout */
! #define ASM_OUTPUT_VMI_THUNK(FILE, THUNK_FNDECL, FUNCTION, DELTA, VCALL_OFFSET) \
! x86_output_vmi_thunk (FILE, FUNCTION, DELTA, VCALL_OFFSET)
// { dg-do run }
// Copyright (C) 2002 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 14 Jul 2002 <nathan@codesourcery.com>
// Implement ASM_OUTPUT_VMI_THUNK. Bug 7306
struct Base1
{
int i;
// varadic thunks can't be synthesized by the front-end alone
virtual int vmi (int, ...)
{
return 1;
}
};
struct Base2
{
int i;
virtual int vi (int, ...)
{
return 2;
};
};
struct Vbase : Base2 , Base1
{
};
struct Overrider: virtual Vbase
{
int vmi (int, ...)
{
return 3;
};
int vi (int, ...)
{
return 4;
};
};
int VMI (Base1 *p)
{
return p->vmi (0);
}
int VI (Base2 *p)
{
return p->vi (0);
}
int main ()
{
Overrider o;
int r;
r = VMI (&o);
if (r != 3)
return r;
r = VI (&o);
if (r != 4)
return r;
return 0;
}