This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
fix g++.jason/thunk3.C for alpha
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Mon, 4 Feb 2002 09:53:45 -0800
- Subject: fix g++.jason/thunk3.C for alpha
Done by implementing ASM_OUTPUT_MI_THUNK.
That's done in an interesting way though -- I actually emit rtl
and then call into final to get that emitted as assembly. The
number of different cases I would have had to handle were large
otherwise.
The cp/method.c change avoids a crash in final.c when debugging
is enabled.
The cp/semantics.c change allows thunks to be done via sibcalls
on Alpha. We have a restriction there in which sibcalls can only
be done within a single object file. By delaying the thunks until
after the function, the code generators know that the function is
local to this object file.
r~
* config/alpha/alpha.c (current_function_is_thunk): Don't check
current_function_is_thunk.
(alpha_sa_mask): Distinguish between current_function_is_thunk
called from ASM_OUTPUT_MI_THUNK and not.
(alpha_does_function_need_gp): Thunks always need gp.
(alpha_start_function, alpha_output_function_end_prologue): Likewise.
(alpha_output_mi_thunk_osf): New.
* config/alpha/alpha-protos.h: Update.
* config/alpha/alpha.h (ASM_OUTPUT_MI_THUNK): New.
cp/
* method.c (use_thunk): Always initialize the block tree. Reindent.
* semantics.c (expand_body): Emit thunks after function, not before.
Index: cp/method.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/method.c,v
retrieving revision 1.218
diff -c -p -d -r1.218 method.c
*** method.c 2002/01/24 03:27:30 1.218
--- method.c 2002/02/04 17:35:18
*************** use_thunk (thunk_fndecl, emit_p)
*** 392,397 ****
--- 392,403 ----
push_to_top_level ();
+ /* The back-end expects DECL_INITIAL to contain a BLOCK, so we
+ create one. */
+ 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)
{
*************** use_thunk (thunk_fndecl, emit_p)
*** 411,498 ****
}
else
#endif /* ASM_OUTPUT_MI_THUNK */
! {
! /* If we don't have the necessary macro for efficient thunks, generate a
! thunk function that just makes a call to the real function.
! Unfortunately, this doesn't work for varargs. */
! tree a, t;
! if (varargs_function_p (function))
! error ("generic thunk code fails for method `%#D' which uses `...'",
! function);
! /* Set up clone argument trees for the thunk. */
! t = NULL_TREE;
! for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
! {
! tree x = copy_node (a);
! TREE_CHAIN (x) = t;
! DECL_CONTEXT (x) = thunk_fndecl;
! t = x;
! }
! a = nreverse (t);
! DECL_ARGUMENTS (thunk_fndecl) = a;
! DECL_RESULT (thunk_fndecl) = NULL_TREE;
! start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
! /* We don't bother with a body block for thunks. */
! /* Adjust the this pointer by the constant. */
! t = ssize_int (delta);
! t = fold (build (PLUS_EXPR, TREE_TYPE (a), a, t));
! /* If there's a vcall offset, look up that value in the vtable and
! adjust the `this' pointer again. */
! if (vcall_offset && !integer_zerop (vcall_offset))
! {
! tree orig_this;
! t = save_expr (t);
! orig_this = t;
! /* The vptr is always at offset zero in the object. */
! t = build1 (NOP_EXPR,
! build_pointer_type (build_pointer_type
! (vtable_entry_type)),
! t);
! /* Form the vtable address. */
! t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
! /* Find the entry with the vcall offset. */
! t = build (PLUS_EXPR, TREE_TYPE (t), t, vcall_offset);
! /* Calculate the offset itself. */
! t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
! /* Adjust the `this' pointer. */
! t = fold (build (PLUS_EXPR,
! TREE_TYPE (orig_this),
! orig_this,
! t));
! }
! /* Build up the call to the real function. */
! t = tree_cons (NULL_TREE, t, NULL_TREE);
! for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
! t = tree_cons (NULL_TREE, a, t);
! t = nreverse (t);
! t = build_call (function, t);
! if (VOID_TYPE_P (TREE_TYPE (t)))
! finish_expr_stmt (t);
! else
! finish_return_stmt (t);
! /* The back-end expects DECL_INITIAL to contain a BLOCK, so we
! create one. */
! DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
! BLOCK_VARS (DECL_INITIAL (thunk_fndecl))
! = DECL_ARGUMENTS (thunk_fndecl);
! /* Since we want to emit the thunk, we explicitly mark its name as
! referenced. */
! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (thunk_fndecl)) = 1;
! /* But we don't want debugging information about it. */
! DECL_IGNORED_P (thunk_fndecl) = 1;
! expand_body (finish_function (0));
! }
pop_from_top_level ();
}
--- 417,499 ----
}
else
#endif /* ASM_OUTPUT_MI_THUNK */
! {
! /* If we don't have the necessary macro for efficient thunks, generate
! a thunk function that just makes a call to the real function.
! Unfortunately, this doesn't work for varargs. */
! tree a, t;
! if (varargs_function_p (function))
! error ("generic thunk code fails for method `%#D' which uses `...'",
! function);
! /* Set up clone argument trees for the thunk. */
! t = NULL_TREE;
! for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
! {
! tree x = copy_node (a);
! TREE_CHAIN (x) = t;
! DECL_CONTEXT (x) = thunk_fndecl;
! t = x;
! }
! a = nreverse (t);
! DECL_ARGUMENTS (thunk_fndecl) = a;
! DECL_RESULT (thunk_fndecl) = NULL_TREE;
! start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
! /* We don't bother with a body block for thunks. */
! /* Adjust the this pointer by the constant. */
! t = ssize_int (delta);
! t = fold (build (PLUS_EXPR, TREE_TYPE (a), a, t));
! /* If there's a vcall offset, look up that value in the vtable and
! adjust the `this' pointer again. */
! if (vcall_offset && !integer_zerop (vcall_offset))
! {
! tree orig_this;
! t = save_expr (t);
! orig_this = t;
! /* The vptr is always at offset zero in the object. */
! t = build1 (NOP_EXPR,
! build_pointer_type (build_pointer_type
! (vtable_entry_type)),
! t);
! /* Form the vtable address. */
! t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
! /* Find the entry with the vcall offset. */
! t = build (PLUS_EXPR, TREE_TYPE (t), t, vcall_offset);
! /* Calculate the offset itself. */
! t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t);
! /* Adjust the `this' pointer. */
! t = fold (build (PLUS_EXPR,
! TREE_TYPE (orig_this),
! orig_this,
! t));
! }
! /* Build up the call to the real function. */
! t = tree_cons (NULL_TREE, t, NULL_TREE);
! for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
! t = tree_cons (NULL_TREE, a, t);
! t = nreverse (t);
! t = build_call (function, t);
! if (VOID_TYPE_P (TREE_TYPE (t)))
! finish_expr_stmt (t);
! else
! finish_return_stmt (t);
! /* Since we want to emit the thunk, we explicitly mark its name as
! referenced. */
! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (thunk_fndecl)) = 1;
! /* But we don't want debugging information about it. */
! DECL_IGNORED_P (thunk_fndecl) = 1;
! expand_body (finish_function (0));
! }
pop_from_top_level ();
}
Index: cp/semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/semantics.c,v
retrieving revision 1.249
diff -c -p -d -r1.249 semantics.c
*** semantics.c 2002/02/04 08:55:43 1.249
--- semantics.c 2002/02/04 17:35:19
*************** expand_body (fn)
*** 2397,2405 ****
if (DECL_EXTERNAL (fn))
return;
- /* Emit any thunks that should be emitted at the same time as FN. */
- emit_associated_thunks (fn);
-
timevar_push (TV_INTEGRATION);
/* Optimize the body of the function before expanding it. */
--- 2397,2402 ----
*************** expand_body (fn)
*** 2452,2457 ****
--- 2449,2457 ----
extract_interface_info ();
timevar_pop (TV_EXPAND);
+
+ /* Emit any thunks that should be emitted at the same time as FN. */
+ emit_associated_thunks (fn);
}
/* Helper function for walk_tree, used by finish_function to override all
Index: config/alpha/alpha-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/alpha/alpha-protos.h,v
retrieving revision 1.28
diff -c -p -d -r1.28 alpha-protos.h
*** alpha-protos.h 2002/01/04 08:15:21 1.28
--- alpha-protos.h 2002/02/04 17:36:03
*************** extern rtx function_arg PARAMS ((CUMULAT
*** 163,168 ****
--- 163,170 ----
#endif
extern void alpha_start_function PARAMS ((FILE *, const char *, tree));
extern void alpha_end_function PARAMS ((FILE *, const char *, tree));
+ extern void alpha_output_mi_thunk_osf PARAMS ((FILE *, tree,
+ HOST_WIDE_INT, tree));
extern void alpha_encode_section_info PARAMS ((tree));
#endif /* TREE CODE */
Index: config/alpha/alpha.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/alpha/alpha.c,v
retrieving revision 1.228
diff -c -p -d -r1.228 alpha.c
*** alpha.c 2002/01/27 00:38:07 1.228
--- alpha.c 2002/02/04 17:36:06
*************** Boston, MA 02111-1307, USA. */
*** 46,51 ****
--- 46,52 ----
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
+ #include "debug.h"
/* External data. */
extern int rtx_equal_function_value_matters;
*************** alpha_ra_ever_killed ()
*** 4993,5002 ****
{
rtx top;
- #ifdef ASM_OUTPUT_MI_THUNK
- if (current_function_is_thunk)
- return 0;
- #endif
if (!has_hard_reg_initial_val (Pmode, REG_RA))
return regs_ever_live[REG_RA];
--- 4994,4999 ----
*************** alpha_sa_mask (imaskP, fmaskP)
*** 5859,5901 ****
unsigned long fmask = 0;
unsigned int i;
! #ifdef ASM_OUTPUT_MI_THUNK
! if (!current_function_is_thunk)
! #endif
{
! if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure)
! imask |= (1L << HARD_FRAME_POINTER_REGNUM);
! /* One for every register we have to save. */
! for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
! if (! fixed_regs[i] && ! call_used_regs[i]
! && regs_ever_live[i] && i != REG_RA
! && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM))
! {
! if (i < 32)
! imask |= (1L << i);
! else
! fmask |= (1L << (i - 32));
! }
! /* We need to restore these for the handler. */
! if (current_function_calls_eh_return)
! {
! for (i = 0; ; ++i)
! {
! unsigned regno = EH_RETURN_DATA_REGNO (i);
! if (regno == INVALID_REGNUM)
! break;
! imask |= 1L << regno;
! }
! }
! /* If any register spilled, then spill the return address also. */
! /* ??? This is required by the Digital stack unwind specification
! and isn't needed if we're doing Dwarf2 unwinding. */
! if (imask || fmask || alpha_ra_ever_killed ())
! imask |= (1L << REG_RA);
! }
*imaskP = imask;
*fmaskP = fmask;
--- 5856,5903 ----
unsigned long fmask = 0;
unsigned int i;
! /* Irritatingly, there are two kinds of thunks -- those created with
! ASM_OUTPUT_MI_THUNK and those with DECL_THUNK_P that go through
! the regular part of the compiler. In the ASM_OUTPUT_MI_THUNK case
! we don't have valid register life info, but assemble_start_function
! wants to output .frame and .mask directives. */
! if (current_function_is_thunk && rtx_equal_function_value_matters)
{
! *imaskP = 0;
! *fmaskP = 0;
! return;
! }
! if (TARGET_ABI_OPEN_VMS && alpha_is_stack_procedure)
! imask |= (1L << HARD_FRAME_POINTER_REGNUM);
! /* One for every register we have to save. */
! for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
! if (! fixed_regs[i] && ! call_used_regs[i]
! && regs_ever_live[i] && i != REG_RA
! && (!TARGET_ABI_UNICOSMK || i != HARD_FRAME_POINTER_REGNUM))
! {
! if (i < 32)
! imask |= (1L << i);
! else
! fmask |= (1L << (i - 32));
! }
!
! /* We need to restore these for the handler. */
! if (current_function_calls_eh_return)
! for (i = 0; ; ++i)
! {
! unsigned regno = EH_RETURN_DATA_REGNO (i);
! if (regno == INVALID_REGNUM)
! break;
! imask |= 1L << regno;
! }
! /* If any register spilled, then spill the return address also. */
! /* ??? This is required by the Digital stack unwind specification
! and isn't needed if we're doing Dwarf2 unwinding. */
! if (imask || fmask || alpha_ra_ever_killed ())
! imask |= (1L << REG_RA);
*imaskP = imask;
*fmaskP = fmask;
*************** alpha_does_function_need_gp ()
*** 6043,6052 ****
if (TARGET_PROFILING_NEEDS_GP && current_function_profile)
return 1;
- #ifdef ASM_OUTPUT_MI_THUNK
if (current_function_is_thunk)
return 1;
- #endif
/* If we need a GP (we have a LDSYM insn or a CALL_INSN), load it first.
Even if we are a static function, we still need to do this in case
--- 6045,6052 ----
*************** alpha_start_function (file, fnname, decl
*** 6512,6518 ****
/* If the function needs GP, we'll write the "..ng" label there.
Otherwise, do it here. */
! if (TARGET_ABI_OSF && ! alpha_function_needs_gp)
{
putc ('$', file);
assemble_name (file, fnname);
--- 6512,6520 ----
/* If the function needs GP, we'll write the "..ng" label there.
Otherwise, do it here. */
! if (TARGET_ABI_OSF
! && ! alpha_function_needs_gp
! && ! current_function_is_thunk)
{
putc ('$', file);
assemble_name (file, fnname);
*************** alpha_output_function_end_prologue (file
*** 6646,6652 ****
else if (TARGET_ABI_WINDOWS_NT)
fputs ("\t.prologue 0\n", file);
else if (!flag_inhibit_size_directive)
! fprintf (file, "\t.prologue %d\n", alpha_function_needs_gp);
}
/* Write function epilogue. */
--- 6648,6655 ----
else if (TARGET_ABI_WINDOWS_NT)
fputs ("\t.prologue 0\n", file);
else if (!flag_inhibit_size_directive)
! fprintf (file, "\t.prologue %d\n",
! alpha_function_needs_gp || current_function_is_thunk);
}
/* Write function epilogue. */
*************** alpha_end_function (file, fnname, decl)
*** 6945,6950 ****
--- 6948,7023 ----
unicosmk_output_ssib (file, fnname);
unicosmk_output_deferred_case_vectors (file);
}
+ }
+
+ /* Emit a tail call to FUNCTION after adjusting THIS by DELTA.
+
+ In order to avoid the hordes of differences between generated code
+ with and without TARGET_EXPLICIT_RELOCS, and to avoid duplicating
+ lots of code loading up large constants, generate rtl and emit it
+ instead of going straight to text.
+
+ Not sure why this idea hasn't been explored before... */
+
+ void
+ alpha_output_mi_thunk_osf (file, thunk_fndecl, delta, function)
+ FILE *file;
+ tree thunk_fndecl ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT delta;
+ tree function;
+ {
+ HOST_WIDE_INT hi, lo;
+ rtx this, insn, funexp;
+
+ /* We always require a valid GP. */
+ emit_insn (gen_prologue_ldgp ());
+ emit_note (NULL, NOTE_INSN_PROLOGUE_END);
+
+ /* Find the "this" pointer. If the function returns a structure,
+ the structure return pointer is in $16. */
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
+ this = gen_rtx_REG (Pmode, 17);
+ else
+ this = gen_rtx_REG (Pmode, 16);
+
+ /* Add DELTA. When possible we use ldah+lda. Otherwise load the
+ entire constant for the add. */
+ lo = ((delta & 0xffff) ^ 0x8000) - 0x8000;
+ hi = (((delta - lo) & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ if (hi + lo == delta)
+ {
+ if (hi)
+ emit_insn (gen_adddi3 (this, this, GEN_INT (hi)));
+ if (lo)
+ emit_insn (gen_adddi3 (this, this, GEN_INT (lo)));
+ }
+ else
+ {
+ rtx tmp = alpha_emit_set_long_const (gen_rtx_REG (Pmode, 0),
+ delta, -(delta < 0));
+ emit_insn (gen_adddi3 (this, this, tmp));
+ }
+
+ /* Generate a tail call to the target function. */
+ if (! TREE_USED (function))
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ }
+ funexp = XEXP (DECL_RTL (function), 0);
+ funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
+ insn = emit_call_insn (gen_sibcall (funexp, const0_rtx));
+ SIBLING_CALL_P (insn) = 1;
+
+ /* Run just enough of rest_of_compilation to get the insns emitted.
+ There's not really enough bulk here to make other passes such as
+ instruction scheduling worth while. Note that use_thunk calls
+ assemble_start_function and assemble_end_function. */
+ insn = get_insns ();
+ shorten_branches (insn);
+ final_start_function (insn, file, 1);
+ final (insn, file, 1, 0);
+ final_end_function ();
}
/* Debugging support. */
Index: config/alpha/alpha.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/alpha/alpha.h,v
retrieving revision 1.153
diff -c -p -d -r1.153 alpha.h
*** alpha.h 2002/02/02 18:56:35 1.153
--- alpha.h 2002/02/04 17:36:07
*************** do { \
*** 2236,2238 ****
--- 2236,2243 ----
/* Generate calls to memcpy, etc., not bcopy, etc. */
#define TARGET_MEM_FUNCTIONS 1
+
+ /* 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) \
+ alpha_output_mi_thunk_osf (FILE, THUNK_FNDECL, DELTA, FUNCTION)