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]

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)


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