public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C++/cgraph] Middle-end thunk support
@ 2009-11-21 19:52 Jan Hubicka
  2009-11-22 18:09 ` Jan Hubicka
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Hubicka @ 2009-11-21 19:52 UTC (permalink / raw)
  To: gcc-patches, rguenther, jakub, dnovillo, mjambor, mark

Hi,
this patch adds cgraph interface for thunks that fixes ASM thunks with LTO and
also fix code quality regression (and ABI regression for thunks without local
aliases where we can now output some but not all) we brought in with LTO merge
where unreachable functions are output just because they do have unreachable
thunks because Diego moved emitting of thunks into early compilation stages.
Note that current version of patch breaks Generic thunks with LTO for reasons
bellow.

The basic idea of patch is to represent thunks as alternate entry points for
functions same way as Jakub's code deals with aliases (this is different from
Diego's proposal of adding new gimple statement for thunked calls and
representing them as functions calling the function being thunked).  This is
done by extending cgraph node by data about thunk and placing them to same body
alias lists via new cgraph_add_thunk interface.  It is also desirable to have
both Generic and ASM thunks represented same way so we don't need to worry
about this implementation detail at IPA stage.

Frontend produce thunks early for all functions passed to backend and for
functions really reachable cgraphunit is then responsible for emitting them
from cgraph code.

The callgraph is now build with only single node for function including all
"entry points". Edges points to this node so code mapping call arguments to
parameters of function will need to use interface to apply transforms done by
thunks.  I intend to add for this purpose function returning for given call
site a list of mappings argument<->value and entry BB so in future real
alternative entry points can be handled by same API.  This is not needed in
current since we never actually use thunks dirrectly until we fix the
devirtualization with multiple inheritance as per Martin's patch.  In longer
run we such need interface here even if we never support real alternative entry
points; with LTO calls and callees don't really need to match that we currently
partly work around by marking them uninlinable in inliner and "variadic and
unoptimizable" in IPA-CP/indirect inlining code. 

The patch moves ASM thunks to middle end completely since it is very easy to
do.  I decided to keep Generic thunks in FE in the first iteration of patch and
handle them incrementally; it requires rewrite into direct gimple thunk codegen
(since current code use C++ FE infrastrucutre and gimplifier that is not
available at linktime) that is not completely straighforward and the patch is
tricky already.  At the moment bodies are produced by C++ FE and only remainder
of work (i.e. turning thunk to function and making it compiled) is done by cgraph.

This means that generic thunks are broken with LTO now (as opposed to ASM
thunks before my patch) since the bodies are produced by FE but not steamed
since they are not in callgraph as proper function.  Writting generic codegen
should be easy and if that fails, we can always stream the bodies of the alias
nodes that is trivial to implement, but it is more ugly to do so.

Another incremental change I would like to do is to add enum to the end of
cgraph node for different kind of nodes, so thunk_info can overlap with other
stuff we record for real nodes.  Adding the union will introduce need for
accessors that will add a bit noise to the patch. Because of GGC rounding
adding thunk info does not really increase memory usage, just is bit ugly.

Jakub, I dropped COMDAT checking from cgraph_same_body_alias function so I can
use them for aliases used by thunks too. Please check if it is safe, I believe
so since we don't do unification for DECL_ONE_ONLY. This is needed because I
need to use same body aliases for local aliases used by thunk code.  Wihtout
doing so, every function referred via assemble_alias is forced to be output
that cause some undefined symbols in the testsuite.  As next iteration, I would
like to move to same API Java aliases too (in C++&Java): it should make
possible to drop unreachable functions in Java mode.


Bootstrapped/regtested x86_64-linux.  Are C++ bits OK?

	* cgraph.c (same_body_alias_1): Break out of
	(same_body_alias): ... here; remove comdat check; it is handled
	in cp already.
	(cgraph_add_thunk): New.
	(dump_cgraph_node): Dump aliases and thunks.
	* cgraph.h (cgraph_thunk_info): New structure.
	(struct cgraph_node): Add thunk info.
	(cgraph_add_thunk): New.
	* cgraphunit.c (cgraph_emit_thunks): Remove.
	(cgraph_finalize_compilation_unit): Do not call cgraph_emit_thunks.
	(assemble_thunk): New function.
	(cgraph_expand_function): Handle thunks.

	* optimize.c (maybe_clone_body): Emit thunks associated to alias.
	* Make-lang.in (method.o): Add dependency on gimple.h.
	* method.c: Include gimple.h
	(make_alias_for_thunk): Use same body alias instead of assemble_alias.
	(use_thunk): Drop ASM thunks code; use cgraph_add_thunk; gimplify
	generic thunks.
	* semantics.c (expand_or_defer_fn): Emit associated thunks.
	* cp-objcp-common.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove.
	* lto-cgraph.c (lto_output_node): Stream thunk info.
	(input_node): Likewise.
	* langhooks.h (lang_hooks_for_callgraph): Remove emit_associated_thunks.
	* langhooks-def.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove.
	(LANG_HOOKS_CALLGRAPH_INITIALIZER): Update.
Index: cgraph.c
===================================================================
*** cgraph.c	(revision 154387)
--- cgraph.c	(working copy)
*************** cgraph_node (tree decl)
*** 507,535 ****
    return node;
  }
  
! /* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
!    Same body aliases are output whenever the body of DECL is output,
!    and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).  */
  
! bool
! cgraph_same_body_alias (tree alias, tree decl)
  {
    struct cgraph_node key, *alias_node, *decl_node, **slot;
  
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
-   gcc_assert (!assembler_name_hash);
- 
- #ifndef ASM_OUTPUT_DEF
-   /* If aliases aren't supported by the assembler, fail.  */
-   return false;
- #endif
- 
-   /* Comdat same body aliases are only supported when comdat groups
-      are supported and the symbols are weak.  */
-   if (DECL_ONE_ONLY (decl) && (!HAVE_COMDAT_GROUP || !DECL_WEAK (decl)))
-     return false;
- 
    decl_node = cgraph_node (decl);
  
    key.decl = alias;
--- 507,521 ----
    return node;
  }
  
! /* Mark ALIAS as an alias to DECL.  */
  
! static struct cgraph_node *
! cgraph_same_body_alias_1 (tree alias, tree decl)
  {
    struct cgraph_node key, *alias_node, *decl_node, **slot;
  
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
    decl_node = cgraph_node (decl);
  
    key.decl = alias;
*************** cgraph_same_body_alias (tree alias, tree
*** 538,544 ****
  
    /* If the cgraph_node has been already created, fail.  */
    if (*slot)
!     return false;
  
    alias_node = cgraph_allocate_node ();
    alias_node->decl = alias;
--- 524,530 ----
  
    /* If the cgraph_node has been already created, fail.  */
    if (*slot)
!     return NULL;
  
    alias_node = cgraph_allocate_node ();
    alias_node->decl = alias;
*************** cgraph_same_body_alias (tree alias, tree
*** 548,556 ****
    if (decl_node->same_body)
      decl_node->same_body->previous = alias_node;
    alias_node->next = decl_node->same_body;
    decl_node->same_body = alias_node;
    *slot = alias_node;
!   return true;
  }
  
  /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
--- 534,579 ----
    if (decl_node->same_body)
      decl_node->same_body->previous = alias_node;
    alias_node->next = decl_node->same_body;
+   alias_node->thunk.alias = decl;
    decl_node->same_body = alias_node;
    *slot = alias_node;
!   return alias_node;
! }
! 
! /* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
!    Same body aliases are output whenever the body of DECL is output,
!    and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).   */
! 
! bool
! cgraph_same_body_alias (tree alias, tree decl)
! {
! #ifndef ASM_OUTPUT_DEF
!   /* If aliases aren't supported by the assembler, fail.  */
!   return false;
! #endif
! 
!   gcc_assert (!assembler_name_hash);
! 
!   return cgraph_same_body_alias_1 (alias, decl) != NULL;
! }
! 
! void
! cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
! 		  HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
! 		  tree real_alias)
! {
!   struct cgraph_node *node = cgraph_get_node (alias);
! 
!   if (node)
!     cgraph_remove_node (node);
!   
!   node = cgraph_same_body_alias_1 (alias, decl);
!   gcc_assert (node);
!   node->thunk.fixed_offset = fixed_offset;
!   node->thunk.this_adjusting = this_adjusting;
!   node->thunk.virtual_value = virtual_value;
!   node->thunk.alias = real_alias;
!   node->thunk.thunk_p = true;
  }
  
  /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
*************** dump_cgraph_node (FILE *f, struct cgraph
*** 1646,1651 ****
--- 1669,1692 ----
  	fprintf(f, "(can throw external) ");
      }
    fprintf (f, "\n");
+ 
+   if (node->same_body)
+     {
+       struct cgraph_node *n;
+       fprintf (f, "  aliases & thunks:");
+       for (n = node->same_body; n; n = n->next)
+         {
+           fprintf (f, " %s", cgraph_node_name (n));
+ 	  if (n->thunk.thunk_p)
+ 	    {
+ 	      fprintf (f, " (thunk of %s fixed ofset %i virtual value %i)",
+ 	      	       lang_hooks.decl_printable_name (n->thunk.alias, 2),
+ 		       (int)n->thunk.fixed_offset,
+ 		       (int)n->thunk.virtual_value);
+ 	    }
+ 	}
+       fprintf (f, "\n");
+     }
  }
  
  
Index: cgraph.h
===================================================================
*** cgraph.h	(revision 154387)
--- cgraph.h	(working copy)
*************** struct GTY(()) inline_summary
*** 69,74 ****
--- 69,86 ----
    int time_inlining_benefit;
  };
  
+ /* Information about thunk, used only for same body aliases.  */
+ 
+ struct GTY(()) cgraph_thunk_info {
+   /* Information about the thunk.  */
+   HOST_WIDE_INT fixed_offset;
+   HOST_WIDE_INT virtual_value;
+   tree alias;
+   bool this_adjusting;
+   /* Set to true when alias node is thunk.  */
+   bool thunk_p;
+ };
+ 
  /* Information about the function collected locally.
     Available after function is analyzed.  */
  
*************** struct GTY((chain_next ("%h.next"), chai
*** 184,191 ****
    struct cgraph_node *prev_sibling_clone;
    struct cgraph_node *clones;
    struct cgraph_node *clone_of;
!   /* For normal nodes pointer to the list of alias nodes, in alias
!      nodes pointer to the normal node.  */
    struct cgraph_node *same_body;
    /* For functions with many calls sites it holds map from call expression
       to the edge to speed up cgraph_edge function.  */
--- 196,203 ----
    struct cgraph_node *prev_sibling_clone;
    struct cgraph_node *clones;
    struct cgraph_node *clone_of;
!   /* For normal nodes pointer to the list of alias and thunk nodes,
!      in alias/thunk nodes pointer to the normal node.  */
    struct cgraph_node *same_body;
    /* For functions with many calls sites it holds map from call expression
       to the edge to speed up cgraph_edge function.  */
*************** struct GTY((chain_next ("%h.next"), chai
*** 202,207 ****
--- 214,220 ----
    struct cgraph_global_info global;
    struct cgraph_rtl_info rtl;
    struct cgraph_clone_info clone;
+   struct cgraph_thunk_info thunk;
  
    /* Expected number of executions: calculated in profile.c.  */
    gcov_type count;
*************** struct GTY((chain_next ("%h.next"), chai
*** 244,251 ****
    unsigned alias : 1;
    /* Set for nodes that was constructed and finalized by frontend.  */
    unsigned finalized_by_frontend : 1;
!   /* Set for alias nodes, same_body points to the node they are alias of
!      and they are linked through the next/previous pointers.  */
    unsigned same_body_alias : 1;
  };
  
--- 257,264 ----
    unsigned alias : 1;
    /* Set for nodes that was constructed and finalized by frontend.  */
    unsigned finalized_by_frontend : 1;
!   /* Set for alias and thunk nodes, same_body points to the node they are alias
!      of and they are linked through the next/previous pointers.  */
    unsigned same_body_alias : 1;
  };
  
*************** struct cgraph_edge *cgraph_create_edge (
*** 423,428 ****
--- 436,442 ----
  struct cgraph_node * cgraph_get_node (tree);
  struct cgraph_node *cgraph_node (tree);
  bool cgraph_same_body_alias (tree, tree);
+ void cgraph_add_thunk (tree, tree, bool, HOST_WIDE_INT, HOST_WIDE_INT, tree);
  void cgraph_remove_same_body_alias (struct cgraph_node *);
  struct cgraph_node *cgraph_node_for_asm (tree);
  struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple);
Index: cgraphunit.c
===================================================================
*** cgraphunit.c	(revision 154387)
--- cgraphunit.c	(working copy)
*************** cgraph_analyze_functions (void)
*** 1034,1068 ****
  }
  
  
- /* Emit thunks for every node in the cgraph.
-    FIXME: We really ought to emit thunks only for functions that are needed.  */
- 
- static void
- cgraph_emit_thunks (void)
- {
-   struct cgraph_node *n, *alias;
- 
-   for (n = cgraph_nodes; n; n = n->next)
-     {
-       /* Only emit thunks on functions defined in this TU.
- 	 Note that this may emit more thunks than strictly necessary.
- 	 During optimization some nodes may disappear.  It would be
- 	 nice to only emit thunks only for the functions that will be
- 	 emitted, but we cannot know that until the inliner and other
- 	 IPA passes have run (see the sequencing of the call to
- 	 cgraph_mark_functions_to_output in cgraph_optimize).  */
-       if (n->reachable
- 	  && !DECL_EXTERNAL (n->decl))
- 	{
- 	  lang_hooks.callgraph.emit_associated_thunks (n->decl);
- 	  for (alias = n->same_body; alias; alias = alias->next)
- 	    if (!DECL_EXTERNAL (alias->decl))
- 	      lang_hooks.callgraph.emit_associated_thunks (alias->decl);
- 	}
-     }
- }
- 
- 
  /* Analyze the whole compilation unit once it is parsed completely.  */
  
  void
--- 1034,1039 ----
*************** cgraph_finalize_compilation_unit (void)
*** 1093,1102 ****
       remove unreachable nodes.  */
    cgraph_analyze_functions ();
  
-   /* Emit thunks for reachable nodes, if needed.  */
-   if (lang_hooks.callgraph.emit_associated_thunks)
-     cgraph_emit_thunks ();
- 
    /* Mark alias targets necessary and emit diagnostics.  */
    finish_aliases_1 ();
  
--- 1064,1069 ----
*************** cgraph_mark_functions_to_output (void)
*** 1159,1164 ****
--- 1126,1185 ----
      }
  }
  
+ /* Produce assembler for thunk NODE.  */
+ 
+ static void
+ assemble_thunk (struct cgraph_node *node)
+ {
+   bool this_adjusting = node->thunk.this_adjusting;
+   HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
+   HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
+   tree alias = node->thunk.alias;
+   tree thunk_fndecl = node->decl;
+   tree a = DECL_ARGUMENTS (thunk_fndecl);
+ 
+   if (this_adjusting
+       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
+ 					      virtual_value, alias))
+     {
+       const char *fnname;
+       tree fn_block;
+       
+       current_function_decl = thunk_fndecl;
+       DECL_RESULT (thunk_fndecl)
+ 	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
+ 		      RESULT_DECL, 0, integer_type_node);
+       fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
+       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
+ 	 create one.  */
+       fn_block = make_node (BLOCK);
+       BLOCK_VARS (fn_block) = a;
+       DECL_INITIAL (thunk_fndecl) = fn_block;
+       init_function_start (thunk_fndecl);
+       cfun->is_thunk = 1;
+       assemble_start_function (thunk_fndecl, fnname);
+ 
+       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
+ 				       fixed_offset, virtual_value, alias);
+ 
+       assemble_end_function (thunk_fndecl, fnname);
+       init_insn_lengths ();
+       free_after_compilation (cfun);
+       current_function_decl = 0;
+       set_cfun (NULL);
+       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+     }
+   else
+     {
+       cgraph_remove_same_body_alias (node);
+       /* Since we want to emit the thunk, we explicitly mark its name as
+ 	 referenced.  */
+       mark_decl_referenced (thunk_fndecl);
+       DECL_STRUCT_FUNCTION (thunk_fndecl)->curr_properties |= PROP_gimple_any;
+       cgraph_add_new_function (thunk_fndecl, false);
+     }
+ }
+ 
  /* Expand function specified by NODE.  */
  
  static void
*************** cgraph_expand_function (struct cgraph_no
*** 1182,1191 ****
    current_function_decl = NULL;
    if (node->same_body)
      {
!       struct cgraph_node *alias;
        bool saved_alias = node->alias;
!       for (alias = node->same_body; alias; alias = alias->next)
! 	assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl));
        node->alias = saved_alias;
      }
    gcc_assert (!cgraph_preserve_function_body_p (decl));
--- 1203,1224 ----
    current_function_decl = NULL;
    if (node->same_body)
      {
!       struct cgraph_node *alias, *next;
        bool saved_alias = node->alias;
!       for (alias = node->same_body;
!       	   alias && alias->next; alias = alias->next)
!         ;
!       /* Walk aliases in the order they were created; it is possible that
!          thunks reffers to the aliases made earlier.  */
!       for (; alias; alias = next)
!         {
! 	  next = alias->previous;
! 	  if (!alias->thunk.thunk_p)
! 	    assemble_alias (alias->decl,
! 			    DECL_ASSEMBLER_NAME (alias->thunk.alias));
! 	  else
! 	    assemble_thunk (alias);
! 	}
        node->alias = saved_alias;
      }
    gcc_assert (!cgraph_preserve_function_body_p (decl));
Index: cp/optimize.c
===================================================================
*** cp/optimize.c	(revision 154387)
--- cp/optimize.c	(working copy)
*************** maybe_clone_body (tree fn)
*** 250,256 ****
  	  && DECL_INTERFACE_KNOWN (fns[0])
  	  && !DECL_ONE_ONLY (fns[0])
  	  && cgraph_same_body_alias (clone, fns[0]))
! 	alias = true;
  
        /* Build the delete destructor by calling complete destructor
           and delete function.  */
--- 250,259 ----
  	  && DECL_INTERFACE_KNOWN (fns[0])
  	  && !DECL_ONE_ONLY (fns[0])
  	  && cgraph_same_body_alias (clone, fns[0]))
! 	{
! 	  alias = true;
! 	  emit_associated_thunks (clone);
! 	}
  
        /* Build the delete destructor by calling complete destructor
           and delete function.  */
Index: cp/Make-lang.in
===================================================================
*** cp/Make-lang.in	(revision 154387)
--- cp/Make-lang.in	(working copy)
*************** cp/friend.o: cp/friend.c $(CXX_TREE_H) $
*** 275,281 ****
  cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \
    toplev.h except.h $(TARGET_H)
  cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \
!   $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h
  cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \
    convert.h $(TARGET_H)
  cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H)
--- 275,281 ----
  cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \
    toplev.h except.h $(TARGET_H)
  cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \
!   $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h $(GIMPLE_H)
  cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \
    convert.h $(TARGET_H)
  cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H)
Index: cp/method.c
===================================================================
*** cp/method.c	(revision 154387)
--- cp/method.c	(working copy)
*************** along with GCC; see the file COPYING3.  
*** 39,44 ****
--- 39,45 ----
  #include "tree-pass.h"
  #include "diagnostic.h"
  #include "cgraph.h"
+ #include "gimple.h"
  
  /* Various flags to control the mangling process.  */
  
*************** make_alias_for_thunk (tree function)
*** 303,308 ****
    alias = make_alias_for (function, get_identifier (buf));
  
    if (!flag_syntax_only)
!     assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
  
    return alias;
  }
--- 304,314 ----
    alias = make_alias_for (function, get_identifier (buf));
  
    if (!flag_syntax_only)
!     {
!       bool ok = cgraph_same_body_alias (alias, function);
!       gcc_assert (ok);
!     }
  
    return alias;
  }
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 416,454 ****
      }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
  
!   if (this_adjusting
!       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					      virtual_value, alias))
!     {
!       const char *fnname;
!       tree fn_block;
!       
!       current_function_decl = thunk_fndecl;
!       DECL_RESULT (thunk_fndecl)
! 	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
! 		      RESULT_DECL, 0, integer_type_node);
!       fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
!       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
! 	 create one.  */
!       fn_block = make_node (BLOCK);
!       BLOCK_VARS (fn_block) = a;
!       DECL_INITIAL (thunk_fndecl) = fn_block;
!       init_function_start (thunk_fndecl);
!       cfun->is_thunk = 1;
!       assemble_start_function (thunk_fndecl, fnname);
! 
!       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
! 				       fixed_offset, virtual_value, alias);
! 
!       assemble_end_function (thunk_fndecl, fnname);
!       init_insn_lengths ();
!       free_after_compilation (cfun);
!       current_function_decl = 0;
!       set_cfun (NULL);
!       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
!     }
!   else
      {
        int i;
        tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree));
--- 421,432 ----
      }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
+   cgraph_add_thunk (thunk_fndecl, function,
+ 		    this_adjusting, fixed_offset, virtual_value, alias);
  
!   if (!this_adjusting
!       || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					       virtual_value, alias))
      {
        int i;
        tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree));
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 511,520 ****
  	  finish_return_stmt (t);
  	}
  
-       /* Since we want to emit the thunk, we explicitly mark its name as
- 	 referenced.  */
-       mark_decl_referenced (thunk_fndecl);
- 
        /* But we don't want debugging information about it.  */
        DECL_IGNORED_P (thunk_fndecl) = 1;
  
--- 489,494 ----
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 522,528 ****
        pop_deferring_access_checks ();
  
        thunk_fndecl = finish_function (0);
!       cgraph_add_new_function (thunk_fndecl, false);
      }
  
    pop_from_top_level ();
--- 496,502 ----
        pop_deferring_access_checks ();
  
        thunk_fndecl = finish_function (0);
!       gimplify_function_tree (thunk_fndecl);
      }
  
    pop_from_top_level ();
Index: cp/semantics.c
===================================================================
*** cp/semantics.c	(revision 154387)
--- cp/semantics.c	(working copy)
*************** expand_or_defer_fn (tree fn)
*** 3404,3409 ****
--- 3404,3410 ----
  
        /* Expand or defer, at the whim of the compilation unit manager.  */
        cgraph_finalize_function (fn, function_depth > 1);
+       emit_associated_thunks (fn);
  
        function_depth--;
      }
Index: cp/cp-objcp-common.h
===================================================================
*** cp/cp-objcp-common.h	(revision 154387)
--- cp/cp-objcp-common.h	(working copy)
*************** extern bool cp_function_decl_explicit_p 
*** 104,111 ****
  
  #undef LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR cxx_callgraph_analyze_expr
- #undef LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS
- #define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS emit_associated_thunks
  
  #undef LANG_HOOKS_MAKE_TYPE
  #define LANG_HOOKS_MAKE_TYPE cxx_make_type
--- 104,109 ----
Index: lto-cgraph.c
===================================================================
*** lto-cgraph.c	(revision 154387)
--- lto-cgraph.c	(working copy)
*************** lto_output_node (struct lto_simple_outpu
*** 317,322 ****
--- 317,333 ----
  	{
  	  lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
  				    alias->decl);
+ 	  if (alias->thunk.thunk_p)
+ 	    {
+               lto_output_uleb128_stream (ob->main_stream,
+ 	      				 alias->thunk.this_adjusting ? 2 : 1);
+ 	      lto_output_uleb128_stream (ob->main_stream, alias->thunk.fixed_offset);
+ 	      lto_output_uleb128_stream (ob->main_stream, alias->thunk.virtual_value);
+ 	      lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
+ 					alias->thunk.alias);
+ 	    }
+ 	  else
+             lto_output_uleb128_stream (ob->main_stream, 0);
  	  alias = alias->previous;
  	}
        while (alias);
*************** input_node (struct lto_file_decl_data *f
*** 575,583 ****
    while (same_body_count-- > 0)
      {
        tree alias_decl;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       cgraph_same_body_alias (alias_decl, fn_decl);
      }
    return node;
  }
--- 586,607 ----
    while (same_body_count-- > 0)
      {
        tree alias_decl;
+       int type;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       type = lto_input_uleb128 (ib);
!       if (!type)
!         cgraph_same_body_alias (alias_decl, fn_decl);
!       else
!         {
! 	  HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib);
! 	  HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib);
! 	  tree real_alias;
! 	  decl_index = lto_input_uleb128 (ib);
! 	  real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
! 	  cgraph_add_thunk (alias_decl, fn_decl, type == 2, fixed_offset,
! 	  		    virtual_value, real_alias);
! 	}
      }
    return node;
  }
Index: langhooks.h
===================================================================
*** langhooks.h	(revision 154387)
--- langhooks.h	(working copy)
*************** struct lang_hooks_for_callgraph
*** 49,57 ****
    /* The node passed is a language-specific tree node.  If its contents
       are relevant to use of other declarations, mark them.  */
    tree (*analyze_expr) (tree *, int *);
- 
-   /* Emit thunks associated to function.  */
-   void (*emit_associated_thunks) (tree);
  };
  
  /* The following hooks are used by tree-dump.c.  */
--- 49,54 ----
Index: langhooks-def.h
===================================================================
*** langhooks-def.h	(revision 154387)
--- langhooks-def.h	(working copy)
*************** extern void lhd_omp_firstprivatize_type_
*** 126,136 ****
  }
  
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
- #define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS NULL
  
  #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
!   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR, \
!   LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS, \
  }
  
  /* Hooks for tree gimplification.  */
--- 126,134 ----
  }
  
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
  
  #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
!   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \
  }
  
  /* Hooks for tree gimplification.  */

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-21 19:52 [C++/cgraph] Middle-end thunk support Jan Hubicka
@ 2009-11-22 18:09 ` Jan Hubicka
  2009-11-23 15:47   ` Mark Mitchell
  2009-11-25 14:00   ` Martin Jambor
  0 siblings, 2 replies; 12+ messages in thread
From: Jan Hubicka @ 2009-11-22 18:09 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, rguenther, jakub, dnovillo, mjambor, mark

Hi,
to avoid uncomplette transition, here is updated patch with generic
thunk codegen.  Thunks are now generated as lowered gimple functions.
(it might be bit easier to adjust existing code to use C++ FE
independent functions and gimplifier, but it seems cleaner at this
stage to operate at gimple. Plus I seem to generate more optimized IL
saving some instructions even when thunks are optimized with -O2)

I've tested on i386 with thunks disabled that g++ testsuite and
libstdc++ testsuite passes (with the obvious failures on testcase
testing variadic thunks that does not work in the generic code).

Bootstrapped/regtested x86_64-linux, OK for C++ bits?


	* cgraph.c (same_body_alias_1): Break out of
	(same_body_alias): ... here; remove comdat check; it is handled
	in cp already.
	(cgraph_add_thunk): New.
	(dump_cgraph_node): Dump aliases and thunks.
	* cgraph.h (cgraph_thunk_info): New structure.
	(struct cgraph_node): Add thunk info.
	(cgraph_add_thunk): New.
	* cgraphunit.c (cgraph_emit_thunks): Remove.
	(cgraph_finalize_compilation_unit): Do not call cgraph_emit_thunks.
	(assemble_thunk): New function.
	(cgraph_expand_function): Handle thunks.
	(thunk_adjust): New.
	(init_lowered_empty_function): New.

	* optimize.c (maybe_clone_body): Emit thunks associated to alias.
	* Make-lang.in (method.o): Add dependency on gimple.h.
	* method.c: Include gimple.h
	(make_alias_for_thunk): Use same body alias instead of assemble_alias.
	(use_thunk): Drop codegen; use cgraph_add_thunk; gimplify
	generic thunks.
	* semantics.c (expand_or_defer_fn): Emit associated thunks.
	* cp-objcp-common.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove.
	* lto-cgraph.c (lto_output_node): Stream thunk info.
	(input_node): Likewise.
	* langhooks.h (lang_hooks_for_callgraph): Remove emit_associated_thunks.
	* langhooks-def.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove.
	(LANG_HOOKS_CALLGRAPH_INITIALIZER): Update.

	* i386.c (x86_output_mi_thunk): Make output prettier.

Index: cgraph.c
===================================================================
*** cgraph.c	(revision 154387)
--- cgraph.c	(working copy)
*************** The callgraph:
*** 85,90 ****
--- 85,91 ----
  #include "tree-flow.h"
  #include "value-prof.h"
  #include "except.h"
+ #include "diagnostic.h"
  
  static void cgraph_node_remove_callers (struct cgraph_node *node);
  static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
*************** cgraph_node (tree decl)
*** 507,535 ****
    return node;
  }
  
! /* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
!    Same body aliases are output whenever the body of DECL is output,
!    and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).  */
  
! bool
! cgraph_same_body_alias (tree alias, tree decl)
  {
    struct cgraph_node key, *alias_node, *decl_node, **slot;
  
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
-   gcc_assert (!assembler_name_hash);
- 
- #ifndef ASM_OUTPUT_DEF
-   /* If aliases aren't supported by the assembler, fail.  */
-   return false;
- #endif
- 
-   /* Comdat same body aliases are only supported when comdat groups
-      are supported and the symbols are weak.  */
-   if (DECL_ONE_ONLY (decl) && (!HAVE_COMDAT_GROUP || !DECL_WEAK (decl)))
-     return false;
- 
    decl_node = cgraph_node (decl);
  
    key.decl = alias;
--- 508,522 ----
    return node;
  }
  
! /* Mark ALIAS as an alias to DECL.  */
  
! static struct cgraph_node *
! cgraph_same_body_alias_1 (tree alias, tree decl)
  {
    struct cgraph_node key, *alias_node, *decl_node, **slot;
  
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
    decl_node = cgraph_node (decl);
  
    key.decl = alias;
*************** cgraph_same_body_alias (tree alias, tree
*** 538,544 ****
  
    /* If the cgraph_node has been already created, fail.  */
    if (*slot)
!     return false;
  
    alias_node = cgraph_allocate_node ();
    alias_node->decl = alias;
--- 525,531 ----
  
    /* If the cgraph_node has been already created, fail.  */
    if (*slot)
!     return NULL;
  
    alias_node = cgraph_allocate_node ();
    alias_node->decl = alias;
*************** cgraph_same_body_alias (tree alias, tree
*** 548,556 ****
    if (decl_node->same_body)
      decl_node->same_body->previous = alias_node;
    alias_node->next = decl_node->same_body;
    decl_node->same_body = alias_node;
    *slot = alias_node;
!   return true;
  }
  
  /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
--- 535,590 ----
    if (decl_node->same_body)
      decl_node->same_body->previous = alias_node;
    alias_node->next = decl_node->same_body;
+   alias_node->thunk.alias = decl;
    decl_node->same_body = alias_node;
    *slot = alias_node;
!   return alias_node;
! }
! 
! /* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
!    Same body aliases are output whenever the body of DECL is output,
!    and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).   */
! 
! bool
! cgraph_same_body_alias (tree alias, tree decl)
! {
! #ifndef ASM_OUTPUT_DEF
!   /* If aliases aren't supported by the assembler, fail.  */
!   return false;
! #endif
! 
!   /*gcc_assert (!assembler_name_hash);*/
! 
!   return cgraph_same_body_alias_1 (alias, decl) != NULL;
! }
! 
! void
! cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
! 		  HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
! 		  tree virtual_offset,
! 		  tree real_alias)
! {
!   struct cgraph_node *node = cgraph_get_node (alias);
! 
!   if (node)
!     {
!       gcc_assert (node->local.finalized);
!       gcc_assert (!node->same_body);
!       cgraph_remove_node (node);
!     }
!   
!   node = cgraph_same_body_alias_1 (alias, decl);
!   gcc_assert (node);
! #ifdef ENABLE_CHECKING
!   gcc_assert (!virtual_offset
!   	      || tree_int_cst_equal (virtual_offset, size_int (virtual_value)));
! #endif
!   node->thunk.fixed_offset = fixed_offset;
!   node->thunk.this_adjusting = this_adjusting;
!   node->thunk.virtual_value = virtual_value;
!   node->thunk.virtual_offset_p = virtual_offset != NULL;
!   node->thunk.alias = real_alias;
!   node->thunk.thunk_p = true;
  }
  
  /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
*************** dump_cgraph_node (FILE *f, struct cgraph
*** 1646,1651 ****
--- 1680,1705 ----
  	fprintf(f, "(can throw external) ");
      }
    fprintf (f, "\n");
+ 
+   if (node->same_body)
+     {
+       struct cgraph_node *n;
+       fprintf (f, "  aliases & thunks:");
+       for (n = node->same_body; n; n = n->next)
+         {
+           fprintf (f, " %s", cgraph_node_name (n));
+ 	  if (n->thunk.thunk_p)
+ 	    {
+ 	      fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has virtual offset %i",
+ 	      	       lang_hooks.decl_printable_name (n->thunk.alias, 2),
+ 		       (int)n->thunk.fixed_offset,
+ 		       (int)n->thunk.virtual_value,
+ 		       (int)n->thunk.virtual_offset_p);
+ 	      fprintf (f, ")");
+ 	    }
+ 	}
+       fprintf (f, "\n");
+     }
  }
  
  
Index: cgraph.h
===================================================================
*** cgraph.h	(revision 154387)
--- cgraph.h	(working copy)
*************** struct GTY(()) inline_summary
*** 69,74 ****
--- 69,87 ----
    int time_inlining_benefit;
  };
  
+ /* Information about thunk, used only for same body aliases.  */
+ 
+ struct GTY(()) cgraph_thunk_info {
+   /* Information about the thunk.  */
+   HOST_WIDE_INT fixed_offset;
+   HOST_WIDE_INT virtual_value;
+   tree alias;
+   bool this_adjusting;
+   bool virtual_offset_p;
+   /* Set to true when alias node is thunk.  */
+   bool thunk_p;
+ };
+ 
  /* Information about the function collected locally.
     Available after function is analyzed.  */
  
*************** struct GTY((chain_next ("%h.next"), chai
*** 184,191 ****
    struct cgraph_node *prev_sibling_clone;
    struct cgraph_node *clones;
    struct cgraph_node *clone_of;
!   /* For normal nodes pointer to the list of alias nodes, in alias
!      nodes pointer to the normal node.  */
    struct cgraph_node *same_body;
    /* For functions with many calls sites it holds map from call expression
       to the edge to speed up cgraph_edge function.  */
--- 197,204 ----
    struct cgraph_node *prev_sibling_clone;
    struct cgraph_node *clones;
    struct cgraph_node *clone_of;
!   /* For normal nodes pointer to the list of alias and thunk nodes,
!      in alias/thunk nodes pointer to the normal node.  */
    struct cgraph_node *same_body;
    /* For functions with many calls sites it holds map from call expression
       to the edge to speed up cgraph_edge function.  */
*************** struct GTY((chain_next ("%h.next"), chai
*** 202,207 ****
--- 215,221 ----
    struct cgraph_global_info global;
    struct cgraph_rtl_info rtl;
    struct cgraph_clone_info clone;
+   struct cgraph_thunk_info thunk;
  
    /* Expected number of executions: calculated in profile.c.  */
    gcov_type count;
*************** struct GTY((chain_next ("%h.next"), chai
*** 244,251 ****
    unsigned alias : 1;
    /* Set for nodes that was constructed and finalized by frontend.  */
    unsigned finalized_by_frontend : 1;
!   /* Set for alias nodes, same_body points to the node they are alias of
!      and they are linked through the next/previous pointers.  */
    unsigned same_body_alias : 1;
  };
  
--- 258,265 ----
    unsigned alias : 1;
    /* Set for nodes that was constructed and finalized by frontend.  */
    unsigned finalized_by_frontend : 1;
!   /* Set for alias and thunk nodes, same_body points to the node they are alias
!      of and they are linked through the next/previous pointers.  */
    unsigned same_body_alias : 1;
  };
  
*************** struct cgraph_edge *cgraph_create_edge (
*** 423,428 ****
--- 437,443 ----
  struct cgraph_node * cgraph_get_node (tree);
  struct cgraph_node *cgraph_node (tree);
  bool cgraph_same_body_alias (tree, tree);
+ void cgraph_add_thunk (tree, tree, bool, HOST_WIDE_INT, HOST_WIDE_INT, tree, tree);
  void cgraph_remove_same_body_alias (struct cgraph_node *);
  struct cgraph_node *cgraph_node_for_asm (tree);
  struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple);
Index: cgraphunit.c
===================================================================
*** cgraphunit.c	(revision 154387)
--- cgraphunit.c	(working copy)
*************** cgraph_analyze_functions (void)
*** 1034,1068 ****
  }
  
  
- /* Emit thunks for every node in the cgraph.
-    FIXME: We really ought to emit thunks only for functions that are needed.  */
- 
- static void
- cgraph_emit_thunks (void)
- {
-   struct cgraph_node *n, *alias;
- 
-   for (n = cgraph_nodes; n; n = n->next)
-     {
-       /* Only emit thunks on functions defined in this TU.
- 	 Note that this may emit more thunks than strictly necessary.
- 	 During optimization some nodes may disappear.  It would be
- 	 nice to only emit thunks only for the functions that will be
- 	 emitted, but we cannot know that until the inliner and other
- 	 IPA passes have run (see the sequencing of the call to
- 	 cgraph_mark_functions_to_output in cgraph_optimize).  */
-       if (n->reachable
- 	  && !DECL_EXTERNAL (n->decl))
- 	{
- 	  lang_hooks.callgraph.emit_associated_thunks (n->decl);
- 	  for (alias = n->same_body; alias; alias = alias->next)
- 	    if (!DECL_EXTERNAL (alias->decl))
- 	      lang_hooks.callgraph.emit_associated_thunks (alias->decl);
- 	}
-     }
- }
- 
- 
  /* Analyze the whole compilation unit once it is parsed completely.  */
  
  void
--- 1034,1039 ----
*************** cgraph_finalize_compilation_unit (void)
*** 1093,1102 ****
       remove unreachable nodes.  */
    cgraph_analyze_functions ();
  
-   /* Emit thunks for reachable nodes, if needed.  */
-   if (lang_hooks.callgraph.emit_associated_thunks)
-     cgraph_emit_thunks ();
- 
    /* Mark alias targets necessary and emit diagnostics.  */
    finish_aliases_1 ();
  
--- 1064,1069 ----
*************** cgraph_mark_functions_to_output (void)
*** 1159,1164 ****
--- 1126,1470 ----
      }
  }
  
+ /* DECL is FUNCTION_DECL.  Initialize datastructures so DECL is a function
+    in lowered gimple form.
+    
+    Set current_function_decl and cfun to newly constructed empty function body.
+    return basic block in the function body.  */
+ 
+ static basic_block
+ init_lowered_empty_function (tree decl)
+ {
+   basic_block bb;
+ 
+   current_function_decl = decl;
+   allocate_struct_function (decl, false);
+   gimple_register_cfg_hooks ();
+   init_empty_tree_cfg ();
+   init_tree_ssa (cfun);
+   init_ssa_operands ();
+   cfun->gimple_df->in_ssa_p = true;
+   DECL_INITIAL (decl) = make_node (BLOCK);
+ 
+   DECL_SAVED_TREE (decl) = error_mark_node;
+   cfun->curr_properties |=
+     (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars |
+      PROP_ssa);
+ 
+   /* Create BB for body of the function and connect it properly.  */
+   bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
+   make_edge (ENTRY_BLOCK_PTR, bb, 0);
+   make_edge (bb, EXIT_BLOCK_PTR, 0);
+ 
+   return bb;
+ }
+ 
+ /* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
+    offset indicated by VIRTUAL_OFFSET, if that is
+    non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
+    zero for a result adjusting thunk.  */
+ 
+ static tree
+ thunk_adjust (gimple_stmt_iterator * bsi,
+ 	      tree ptr, bool this_adjusting,
+ 	      HOST_WIDE_INT fixed_offset, tree virtual_offset)
+ {
+   gimple stmt;
+   tree ret;
+ 
+   if (this_adjusting)
+     {
+       stmt = gimple_build_assign (ptr,
+ 				  fold_build2_loc (input_location,
+ 						   POINTER_PLUS_EXPR,
+ 						   TREE_TYPE (ptr), ptr,
+ 						   size_int (fixed_offset)));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+     }
+ 
+   /* If there's a virtual offset, look up that value in the vtable and
+      adjust the pointer again.  */
+   if (virtual_offset)
+     {
+       tree vtabletmp;
+       tree vtabletmp2;
+       tree vtabletmp3;
+       tree vtable_entry_type;
+       tree offsettmp;
+ 
+       tree vfunc_type = make_node (FUNCTION_TYPE);
+       TREE_TYPE (vfunc_type) = integer_type_node;
+       TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
+       layout_type (vfunc_type);
+ 
+       vtable_entry_type = build_pointer_type (vfunc_type);
+ 
+       vtabletmp =
+ 	create_tmp_var (build_pointer_type
+ 			(build_pointer_type (vtable_entry_type)), "vptr");
+ 
+       /* The vptr is always at offset zero in the object.  */
+       stmt = gimple_build_assign (vtabletmp,
+ 				  build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
+ 					  ptr));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Form the vtable address.  */
+       vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)),
+ 				   "vtableaddr");
+       stmt = gimple_build_assign (vtabletmp2,
+ 				  build1 (INDIRECT_REF,
+ 					  TREE_TYPE (vtabletmp2), vtabletmp));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Find the entry with the vcall offset.  */
+       stmt = gimple_build_assign (vtabletmp2,
+ 				  fold_build2_loc (input_location,
+ 						   POINTER_PLUS_EXPR,
+ 						   TREE_TYPE (vtabletmp2),
+ 						   vtabletmp2,
+ 						   fold_convert (sizetype,
+ 								 virtual_offset)));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+ 
+       /* Get the offset itself.  */
+       vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
+ 				   "vcalloffset");
+       stmt = gimple_build_assign (vtabletmp3,
+ 				  build1 (INDIRECT_REF,
+ 					  TREE_TYPE (vtabletmp3),
+ 					  vtabletmp2));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Cast to sizetype.  */
+       offsettmp = create_tmp_var (sizetype, "offset");
+       stmt = gimple_build_assign (offsettmp, fold_convert (sizetype, vtabletmp3));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Adjust the `this' pointer.  */
+       ptr = fold_build2_loc (input_location,
+ 			     POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
+ 			     offsettmp);
+     }
+ 
+   if (!this_adjusting)
+     /* Adjust the pointer by the constant.  */
+     {
+       tree ptrtmp;
+ 
+       if (TREE_CODE (ptr) == VAR_DECL)
+         ptrtmp = ptr;
+       else
+         {
+           ptrtmp = create_tmp_var (TREE_TYPE (ptr), "ptr");
+           stmt = gimple_build_assign (ptrtmp, ptr);
+ 	  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+ 	  mark_symbols_for_renaming (stmt);
+ 	  find_referenced_vars_in (stmt);
+ 	}
+       ptr = fold_build2_loc (input_location,
+ 			     POINTER_PLUS_EXPR, TREE_TYPE (ptrtmp), ptrtmp,
+ 			     size_int (fixed_offset));
+     }
+ 
+   /* Emit the statement and gimplify the adjustment expression.  */
+   ret = create_tmp_var (TREE_TYPE (ptr), "adjusted_this");
+   stmt = gimple_build_assign (ret, ptr);
+   mark_symbols_for_renaming (stmt);
+   find_referenced_vars_in (stmt);
+   gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+ 
+   return ret;
+ }
+ 
+ /* Produce assembler for thunk NODE.  */
+ 
+ static void
+ assemble_thunk (struct cgraph_node *node)
+ {
+   bool this_adjusting = node->thunk.this_adjusting;
+   HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
+   HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
+   tree virtual_offset = NULL;
+   tree alias = node->thunk.alias;
+   tree thunk_fndecl = node->decl;
+   tree a = DECL_ARGUMENTS (thunk_fndecl);
+ 
+   current_function_decl = thunk_fndecl;
+ 
+   if (this_adjusting
+       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
+ 					      virtual_value, alias))
+     {
+       const char *fnname;
+       tree fn_block;
+       
+       DECL_RESULT (thunk_fndecl)
+ 	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
+ 		      RESULT_DECL, 0, integer_type_node);
+       fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
+ 
+       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
+ 	 create one.  */
+       fn_block = make_node (BLOCK);
+       BLOCK_VARS (fn_block) = a;
+       DECL_INITIAL (thunk_fndecl) = fn_block;
+       init_function_start (thunk_fndecl);
+       cfun->is_thunk = 1;
+       assemble_start_function (thunk_fndecl, fnname);
+ 
+       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
+ 				       fixed_offset, virtual_value, alias);
+ 
+       assemble_end_function (thunk_fndecl, fnname);
+       init_insn_lengths ();
+       free_after_compilation (cfun);
+       set_cfun (NULL);
+       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+     }
+   else
+     {
+       tree restype;
+       basic_block bb, then_bb, else_bb, return_bb;
+       gimple_stmt_iterator bsi;
+       int nargs = 0;
+       tree arg;
+       int i;
+       tree resdecl;
+       tree restmp = NULL;
+       VEC(tree, heap) *vargs;
+ 
+       gimple call;
+       gimple ret;
+ 
+       DECL_IGNORED_P (thunk_fndecl) = 1;
+       bitmap_obstack_initialize (NULL);
+ 
+       if (node->thunk.virtual_offset_p)
+         virtual_offset = size_int (virtual_value);
+ 
+       /* Build the return declaration for the function.  */
+       restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
+       if (DECL_RESULT (thunk_fndecl) == NULL_TREE)
+ 	{
+ 	  resdecl = build_decl (input_location, RESULT_DECL, 0, restype);
+ 	  DECL_ARTIFICIAL (resdecl) = 1;
+ 	  DECL_IGNORED_P (resdecl) = 1;
+ 	  DECL_RESULT (thunk_fndecl) = resdecl;
+ 	}
+       else
+ 	resdecl = DECL_RESULT (thunk_fndecl);
+ 
+       bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl);
+ 
+       bsi = gsi_start_bb (bb);
+ 
+       /* Build call to the function being thunked.  */
+       if (!VOID_TYPE_P (restype))
+ 	{
+ 	  if (!is_gimple_reg_type (restype))
+ 	    {
+ 	      restmp = resdecl;
+ 	      cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls);
+ 	      BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+ 	    }
+ 	  else
+             restmp = create_tmp_var_raw (restype, "retval");
+ 	}
+ 
+       for (arg = a; arg; arg = TREE_CHAIN (arg))
+         nargs++;
+       vargs = VEC_alloc (tree, heap, nargs);
+       if (this_adjusting)
+         VEC_quick_push (tree, vargs,
+ 			thunk_adjust (&bsi,
+ 				      a, 1, fixed_offset,
+ 				      virtual_offset));
+       else
+         VEC_quick_push (tree, vargs, a);
+       for (i = 1, arg = TREE_CHAIN (a); i < nargs; i++, arg = TREE_CHAIN (arg))
+         VEC_quick_push (tree, vargs, arg);
+       call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
+       VEC_free (tree, heap, vargs);
+       gimple_call_set_cannot_inline (call, true);
+       gimple_call_set_from_thunk (call, true);
+       if (restmp)
+         gimple_call_set_lhs (call, restmp);
+       gsi_insert_after (&bsi, call, GSI_NEW_STMT);
+       mark_symbols_for_renaming (call);
+       find_referenced_vars_in (call);
+       update_stmt (call);
+ 
+       if (restmp && !this_adjusting)
+         {
+ 	  tree true_label = NULL_TREE, false_label = NULL_TREE;
+ 
+ 	  if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
+ 	    {
+ 	      gimple stmt;
+ 	      /* If the return type is a pointer, we need to
+ 		 protect against NULL.  We know there will be an
+ 		 adjustment, because that's why we're emitting a
+ 		 thunk.  */
+ 	      then_bb = create_basic_block (NULL, (void *) 0, bb);
+ 	      return_bb = create_basic_block (NULL, (void *) 0, then_bb);
+ 	      else_bb = create_basic_block (NULL, (void *) 0, else_bb);
+ 	      remove_edge (single_succ_edge (bb));
+ 	      true_label = gimple_block_label (then_bb);
+ 	      false_label = gimple_block_label (else_bb);
+ 	      stmt = gimple_build_cond (NE_EXPR, restmp,
+ 	      				fold_convert (TREE_TYPE (restmp),
+ 						      integer_zero_node),
+ 	      			        NULL_TREE, NULL_TREE);
+ 	      gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+ 	      make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+ 	      make_edge (bb, else_bb, EDGE_FALSE_VALUE);
+ 	      make_edge (return_bb, EXIT_BLOCK_PTR, 0);
+ 	      make_edge (then_bb, return_bb, EDGE_FALLTHRU);
+ 	      make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+ 	      bsi = gsi_last_bb (then_bb);
+ 	    }
+ 
+ 	  restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
+ 			         fixed_offset, virtual_offset);
+ 	  if (true_label)
+ 	    {
+ 	      gimple stmt;
+ 	      bsi = gsi_last_bb (else_bb);
+ 	      stmt = gimple_build_assign (restmp, fold_convert (TREE_TYPE (restmp),
+ 								integer_zero_node));
+ 	      gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+ 	      bsi = gsi_last_bb (return_bb);
+ 	    }
+ 	}
+       else
+         gimple_call_set_tail (call, true);
+ 
+       /* Build return value.  */
+       ret = gimple_build_return (restmp);
+       gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
+ 
+       delete_unreachable_blocks ();
+       update_ssa (TODO_update_ssa);
+ 
+       cgraph_remove_same_body_alias (node);
+       /* Since we want to emit the thunk, we explicitly mark its name as
+ 	 referenced.  */
+       mark_decl_referenced (thunk_fndecl);
+       cgraph_add_new_function (thunk_fndecl, true);
+       bitmap_obstack_release (NULL);
+     }
+   current_function_decl = NULL;
+ }
+ 
  /* Expand function specified by NODE.  */
  
  static void
*************** cgraph_expand_function (struct cgraph_no
*** 1182,1191 ****
    current_function_decl = NULL;
    if (node->same_body)
      {
!       struct cgraph_node *alias;
        bool saved_alias = node->alias;
!       for (alias = node->same_body; alias; alias = alias->next)
! 	assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl));
        node->alias = saved_alias;
      }
    gcc_assert (!cgraph_preserve_function_body_p (decl));
--- 1488,1509 ----
    current_function_decl = NULL;
    if (node->same_body)
      {
!       struct cgraph_node *alias, *next;
        bool saved_alias = node->alias;
!       for (alias = node->same_body;
!       	   alias && alias->next; alias = alias->next)
!         ;
!       /* Walk aliases in the order they were created; it is possible that
!          thunks reffers to the aliases made earlier.  */
!       for (; alias; alias = next)
!         {
! 	  next = alias->previous;
! 	  if (!alias->thunk.thunk_p)
! 	    assemble_alias (alias->decl,
! 			    DECL_ASSEMBLER_NAME (alias->thunk.alias));
! 	  else
! 	    assemble_thunk (alias);
! 	}
        node->alias = saved_alias;
      }
    gcc_assert (!cgraph_preserve_function_body_p (decl));
Index: cp/optimize.c
===================================================================
*** cp/optimize.c	(revision 154387)
--- cp/optimize.c	(working copy)
*************** maybe_clone_body (tree fn)
*** 250,256 ****
  	  && DECL_INTERFACE_KNOWN (fns[0])
  	  && !DECL_ONE_ONLY (fns[0])
  	  && cgraph_same_body_alias (clone, fns[0]))
! 	alias = true;
  
        /* Build the delete destructor by calling complete destructor
           and delete function.  */
--- 250,259 ----
  	  && DECL_INTERFACE_KNOWN (fns[0])
  	  && !DECL_ONE_ONLY (fns[0])
  	  && cgraph_same_body_alias (clone, fns[0]))
! 	{
! 	  alias = true;
! 	  emit_associated_thunks (clone);
! 	}
  
        /* Build the delete destructor by calling complete destructor
           and delete function.  */
Index: cp/Make-lang.in
===================================================================
*** cp/Make-lang.in	(revision 154387)
--- cp/Make-lang.in	(working copy)
*************** cp/friend.o: cp/friend.c $(CXX_TREE_H) $
*** 275,281 ****
  cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \
    toplev.h except.h $(TARGET_H)
  cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \
!   $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h
  cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \
    convert.h $(TARGET_H)
  cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H)
--- 275,281 ----
  cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \
    toplev.h except.h $(TARGET_H)
  cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \
!   $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h $(GIMPLE_H)
  cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \
    convert.h $(TARGET_H)
  cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H)
Index: cp/method.c
===================================================================
*** cp/method.c	(revision 154387)
--- cp/method.c	(working copy)
*************** along with GCC; see the file COPYING3.  
*** 39,44 ****
--- 39,45 ----
  #include "tree-pass.h"
  #include "diagnostic.h"
  #include "cgraph.h"
+ #include "gimple.h"
  
  /* Various flags to control the mangling process.  */
  
*************** enum mangling_flags
*** 58,64 ****
  
  typedef enum mangling_flags mangling_flags;
  
- static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree);
  static void do_build_assign_ref (tree);
  static void do_build_copy_constructor (tree);
  static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *);
--- 59,64 ----
*************** finish_thunk (tree thunk)
*** 205,260 ****
    SET_DECL_ASSEMBLER_NAME (thunk, name);
  }
  
- /* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
-    offset indicated by VIRTUAL_OFFSET, if that is
-    non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
-    zero for a result adjusting thunk.  */
- 
- static tree
- thunk_adjust (tree ptr, bool this_adjusting,
- 	      HOST_WIDE_INT fixed_offset, tree virtual_offset)
- {
-   if (this_adjusting)
-     /* Adjust the pointer by the constant.  */
-     ptr = fold_build2_loc (input_location,
- 		       POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- 		       size_int (fixed_offset));
- 
-   /* If there's a virtual offset, look up that value in the vtable and
-      adjust the pointer again.  */
-   if (virtual_offset)
-     {
-       tree vtable;
- 
-       ptr = save_expr (ptr);
-       /* The vptr is always at offset zero in the object.  */
-       vtable = build1 (NOP_EXPR,
- 		       build_pointer_type (build_pointer_type
- 					   (vtable_entry_type)),
- 		       ptr);
-       /* Form the vtable address.  */
-       vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
-       /* Find the entry with the vcall offset.  */
-       vtable = fold_build2_loc (input_location,
- 			    POINTER_PLUS_EXPR, TREE_TYPE (vtable), vtable,
- 			    fold_convert (sizetype, virtual_offset));
-       /* Get the offset itself.  */
-       vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
-       /* Adjust the `this' pointer.  */
-       ptr = fold_build2_loc (input_location,
- 			 POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- 			 fold_convert (sizetype, vtable));
-     }
- 
-   if (!this_adjusting)
-     /* Adjust the pointer by the constant.  */
-     ptr = fold_build2_loc (input_location,
- 		       POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- 		       size_int (fixed_offset));
- 
-   return ptr;
- }
- 
  static GTY (()) int thunk_labelno;
  
  /* Create a static alias to function.  */
--- 205,210 ----
*************** make_alias_for_thunk (tree function)
*** 303,309 ****
    alias = make_alias_for (function, get_identifier (buf));
  
    if (!flag_syntax_only)
!     assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
  
    return alias;
  }
--- 253,263 ----
    alias = make_alias_for (function, get_identifier (buf));
  
    if (!flag_syntax_only)
!     {
!       bool ok = cgraph_same_body_alias (alias, function);
!       DECL_ASSEMBLER_NAME (function);
!       gcc_assert (ok);
!     }
  
    return alias;
  }
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 416,457 ****
      }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
! 
!   if (this_adjusting
!       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					      virtual_value, alias))
!     {
!       const char *fnname;
!       tree fn_block;
!       
!       current_function_decl = thunk_fndecl;
!       DECL_RESULT (thunk_fndecl)
! 	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
! 		      RESULT_DECL, 0, integer_type_node);
!       fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
!       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
! 	 create one.  */
!       fn_block = make_node (BLOCK);
!       BLOCK_VARS (fn_block) = a;
!       DECL_INITIAL (thunk_fndecl) = fn_block;
!       init_function_start (thunk_fndecl);
!       cfun->is_thunk = 1;
!       assemble_start_function (thunk_fndecl, fnname);
! 
!       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
! 				       fixed_offset, virtual_value, alias);
! 
!       assemble_end_function (thunk_fndecl, fnname);
!       init_insn_lengths ();
!       free_after_compilation (cfun);
!       current_function_decl = 0;
!       set_cfun (NULL);
!       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
!     }
!   else
      {
-       int i;
-       tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree));
        /* If this is a covariant thunk, or we don't have the necessary
  	 code for efficient thunks, generate a thunk function that
  	 just makes a call to the real function.  Unfortunately, this
--- 370,384 ----
      }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
!   TREE_ASM_WRITTEN (thunk_fndecl) = 1;
!   cgraph_add_thunk (thunk_fndecl, function,
! 		    this_adjusting, fixed_offset, virtual_value,
! 		    virtual_offset, alias);
! 
!   if (!this_adjusting
!       || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					       virtual_value, alias))
      {
        /* If this is a covariant thunk, or we don't have the necessary
  	 code for efficient thunks, generate a thunk function that
  	 just makes a call to the real function.  Unfortunately, this
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 460,528 ****
        if (varargs_function_p (function))
  	error ("generic thunk code fails for method %q#D which uses %<...%>",
  	       function);
- 
-       DECL_RESULT (thunk_fndecl) = NULL_TREE;
- 
-       start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
-       /* We don't bother with a body block for thunks.  */
- 
-       /* There's no need to check accessibility inside the thunk body.  */
-       push_deferring_access_checks (dk_no_check);
- 
-       t = a;
-       if (this_adjusting)
- 	t = thunk_adjust (t, /*this_adjusting=*/1,
- 			  fixed_offset, virtual_offset);
- 
-       /* Build up the call to the real function.  */
-       argarray[0] = t;
-       for (i = 1, a = TREE_CHAIN (a); a; a = TREE_CHAIN (a), i++)
- 	argarray[i] = a;
-       t = build_call_a (alias, i, argarray);
-       CALL_FROM_THUNK_P (t) = 1;
-       CALL_CANNOT_INLINE_P (t) = 1;
- 
-       if (VOID_TYPE_P (TREE_TYPE (t)))
- 	finish_expr_stmt (t);
-       else
- 	{
- 	  if (!this_adjusting)
- 	    {
- 	      tree cond = NULL_TREE;
- 
- 	      if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
- 		{
- 		  /* If the return type is a pointer, we need to
- 		     protect against NULL.  We know there will be an
- 		     adjustment, because that's why we're emitting a
- 		     thunk.  */
- 		  t = save_expr (t);
- 		  cond = cp_convert (boolean_type_node, t);
- 		}
- 
- 	      t = thunk_adjust (t, /*this_adjusting=*/0,
- 				fixed_offset, virtual_offset);
- 	      if (cond)
- 		t = build3 (COND_EXPR, TREE_TYPE (t), cond, t,
- 			    cp_convert (TREE_TYPE (t), integer_zero_node));
- 	    }
- 	  if (MAYBE_CLASS_TYPE_P (TREE_TYPE (t)))
- 	    t = build_cplus_new (TREE_TYPE (t), t);
- 	  finish_return_stmt (t);
- 	}
- 
-       /* Since we want to emit the thunk, we explicitly mark its name as
- 	 referenced.  */
-       mark_decl_referenced (thunk_fndecl);
- 
-       /* But we don't want debugging information about it.  */
-       DECL_IGNORED_P (thunk_fndecl) = 1;
- 
-       /* Re-enable access control.  */
-       pop_deferring_access_checks ();
- 
-       thunk_fndecl = finish_function (0);
-       cgraph_add_new_function (thunk_fndecl, false);
      }
  
    pop_from_top_level ();
--- 387,392 ----
Index: cp/semantics.c
===================================================================
*** cp/semantics.c	(revision 154387)
--- cp/semantics.c	(working copy)
*************** expand_or_defer_fn (tree fn)
*** 3404,3409 ****
--- 3404,3410 ----
  
        /* Expand or defer, at the whim of the compilation unit manager.  */
        cgraph_finalize_function (fn, function_depth > 1);
+       emit_associated_thunks (fn);
  
        function_depth--;
      }
Index: cp/cp-objcp-common.h
===================================================================
*** cp/cp-objcp-common.h	(revision 154387)
--- cp/cp-objcp-common.h	(working copy)
*************** extern bool cp_function_decl_explicit_p 
*** 104,111 ****
  
  #undef LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR cxx_callgraph_analyze_expr
- #undef LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS
- #define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS emit_associated_thunks
  
  #undef LANG_HOOKS_MAKE_TYPE
  #define LANG_HOOKS_MAKE_TYPE cxx_make_type
--- 104,109 ----
Index: lto-cgraph.c
===================================================================
*** lto-cgraph.c	(revision 154387)
--- lto-cgraph.c	(working copy)
*************** lto_output_node (struct lto_simple_outpu
*** 317,322 ****
--- 317,337 ----
  	{
  	  lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
  				    alias->decl);
+ 	  if (alias->thunk.thunk_p)
+ 	    {
+               lto_output_uleb128_stream
+ 	         (ob->main_stream,
+ 	      	  1 + (alias->thunk.this_adjusting != 0) * 2
+ 		  + (alias->thunk.virtual_offset_p != 0) * 4);
+ 	      lto_output_uleb128_stream (ob->main_stream,
+ 	      				 alias->thunk.fixed_offset);
+ 	      lto_output_uleb128_stream (ob->main_stream,
+ 	      				 alias->thunk.virtual_value);
+ 	      lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
+ 					alias->thunk.alias);
+ 	    }
+ 	  else
+             lto_output_uleb128_stream (ob->main_stream, 0);
  	  alias = alias->previous;
  	}
        while (alias);
*************** input_node (struct lto_file_decl_data *f
*** 575,583 ****
    while (same_body_count-- > 0)
      {
        tree alias_decl;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       cgraph_same_body_alias (alias_decl, fn_decl);
      }
    return node;
  }
--- 590,613 ----
    while (same_body_count-- > 0)
      {
        tree alias_decl;
+       int type;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       type = lto_input_uleb128 (ib);
!       if (!type)
!         cgraph_same_body_alias (alias_decl, fn_decl);
!       else
!         {
! 	  HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib);
! 	  HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib);
! 	  tree real_alias;
! 	  decl_index = lto_input_uleb128 (ib);
! 	  real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
! 	  cgraph_add_thunk (alias_decl, fn_decl, type & 2, fixed_offset,
! 	  		    virtual_value,
! 			    (type & 4) ? size_int (virtual_value) : NULL_TREE,
! 			    real_alias);
! 	}
      }
    return node;
  }
Index: langhooks.h
===================================================================
*** langhooks.h	(revision 154387)
--- langhooks.h	(working copy)
*************** struct lang_hooks_for_callgraph
*** 49,57 ****
    /* The node passed is a language-specific tree node.  If its contents
       are relevant to use of other declarations, mark them.  */
    tree (*analyze_expr) (tree *, int *);
- 
-   /* Emit thunks associated to function.  */
-   void (*emit_associated_thunks) (tree);
  };
  
  /* The following hooks are used by tree-dump.c.  */
--- 49,54 ----
Index: ira.c
===================================================================
*** ira.c	(revision 154387)
--- ira.c	(working copy)
*************** ira (FILE *f)
*** 3172,3177 ****
--- 3172,3178 ----
    
    ira_assert (current_loops == NULL);
    flow_loops_find (&ira_loops);
+   record_loop_exits ();
    current_loops = &ira_loops;
        
    if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
*************** ira (FILE *f)
*** 3215,3220 ****
--- 3216,3222 ----
  	  df_analyze ();
  	  
  	  flow_loops_find (&ira_loops);
+ 	  record_loop_exits ();
  	  current_loops = &ira_loops;
  
  	  setup_allocno_assignment_flags ();
Index: config/i386/i386.c
===================================================================
*** config/i386/i386.c	(revision 154387)
--- config/i386/i386.c	(working copy)
*************** x86_output_mi_thunk (FILE *file ATTRIBUT
*** 27464,27470 ****
    /* Adjust the this parameter by a fixed constant.  */
    if (delta)
      {
!       xops[0] = GEN_INT (delta);
        xops[1] = this_reg ? this_reg : this_param;
        if (TARGET_64BIT)
  	{
--- 27464,27473 ----
    /* Adjust the this parameter by a fixed constant.  */
    if (delta)
      {
!       /* Make things pretty and `subl $4,%eax' rather than `addl $-4,%eax'.
!          Exceptions: -128 encodes smaller than 128, so swap sign and op.  */
!       bool sub = delta < 0 || delta == 128;
!       xops[0] = GEN_INT (sub ? -delta : delta);
        xops[1] = this_reg ? this_reg : this_param;
        if (TARGET_64BIT)
  	{
*************** x86_output_mi_thunk (FILE *file ATTRIBUT
*** 27476,27483 ****
  	      xops[0] = tmp;
  	      xops[1] = this_param;
  	    }
! 	  output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
  	}
        else
  	output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
      }
--- 27479,27491 ----
  	      xops[0] = tmp;
  	      xops[1] = this_param;
  	    }
! 	  if (sub)
! 	    output_asm_insn ("sub{q}\t{%0, %1|%1, %0}", xops);
! 	  else
! 	    output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
  	}
+       else if (sub)
+ 	output_asm_insn ("sub{l}\t{%0, %1|%1, %0}", xops);
        else
  	output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
      }
Index: langhooks-def.h
===================================================================
*** langhooks-def.h	(revision 154387)
--- langhooks-def.h	(working copy)
*************** extern void lhd_omp_firstprivatize_type_
*** 126,136 ****
  }
  
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
- #define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS NULL
  
  #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
!   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR, \
!   LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS, \
  }
  
  /* Hooks for tree gimplification.  */
--- 126,134 ----
  }
  
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
  
  #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
!   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \
  }
  
  /* Hooks for tree gimplification.  */

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-22 18:09 ` Jan Hubicka
@ 2009-11-23 15:47   ` Mark Mitchell
  2009-11-23 15:55     ` Jan Hubicka
  2009-11-25 14:00   ` Martin Jambor
  1 sibling, 1 reply; 12+ messages in thread
From: Mark Mitchell @ 2009-11-23 15:47 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, rguenther, jakub, dnovillo, mjambor

Jan Hubicka wrote:

> +       tree vfunc_type = make_node (FUNCTION_TYPE);
> +       TREE_TYPE (vfunc_type) = integer_type_node;
> +       TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
> +       layout_type (vfunc_type);

I like the overall direction of these patches.  But, this code in
thunk_adjust looks like it will create a new type every time.  Can't we
remember the type and use it again, or use the higher-level
type-creation functions so that we have  ashared type?

Thanks,

-- 
Mark Mitchell
CodeSourcery
mark@codesourcery.com
(650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-23 15:47   ` Mark Mitchell
@ 2009-11-23 15:55     ` Jan Hubicka
  2009-11-23 16:02       ` Jan Hubicka
  2009-11-23 16:04       ` Mark Mitchell
  0 siblings, 2 replies; 12+ messages in thread
From: Jan Hubicka @ 2009-11-23 15:55 UTC (permalink / raw)
  To: Mark Mitchell
  Cc: Jan Hubicka, gcc-patches, rguenther, jakub, dnovillo, mjambor

> Jan Hubicka wrote:
> 
> > +       tree vfunc_type = make_node (FUNCTION_TYPE);
> > +       TREE_TYPE (vfunc_type) = integer_type_node;
> > +       TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
> > +       layout_type (vfunc_type);
> 
> I like the overall direction of these patches.  But, this code in
> thunk_adjust looks like it will create a new type every time.  Can't we

Yes, it creates new type, I considered it harmless given that thunks are not
inlined and whole functions are short lived.

Currently vfunc_type is in C++ only datastructures, so we would have to move it
to middle end and LTO it, or just initialize lazilly static vfunc_type var
first time we output thunk?  Or can I grab it somewhere conveniently from
thunk_fndecl?

> remember the type and use it again, or use the higher-level
> type-creation functions so that we have  ashared type?

By higher-level you mean stuff like integer_type_node?

Honza
> 
> Thanks,
> 
> -- 
> Mark Mitchell
> CodeSourcery
> mark@codesourcery.com
> (650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-23 15:55     ` Jan Hubicka
@ 2009-11-23 16:02       ` Jan Hubicka
  2009-11-23 16:04       ` Mark Mitchell
  1 sibling, 0 replies; 12+ messages in thread
From: Jan Hubicka @ 2009-11-23 16:02 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Mark Mitchell, gcc-patches, rguenther, jakub, dnovillo, mjambor

> > Jan Hubicka wrote:
> > 
> > > +       tree vfunc_type = make_node (FUNCTION_TYPE);
> > > +       TREE_TYPE (vfunc_type) = integer_type_node;
> > > +       TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
> > > +       layout_type (vfunc_type);
> > 
> > I like the overall direction of these patches.  But, this code in
> > thunk_adjust looks like it will create a new type every time.  Can't we
> 
> Yes, it creates new type, I considered it harmless given that thunks are not
> inlined and whole functions are short lived.
> 
> Currently vfunc_type is in C++ only datastructures, so we would have to move it
> to middle end and LTO it, or just initialize lazilly static vfunc_type var
> first time we output thunk?  Or can I grab it somewhere conveniently from
> thunk_fndecl?

Well, if we are concerned only about memory, it would be easiest to do something like

static tree GTY(()) vfunc_type

if (!vfunc_type)
  ....

Honza
> 
> > remember the type and use it again, or use the higher-level
> > type-creation functions so that we have  ashared type?
> 
> By higher-level you mean stuff like integer_type_node?
> 
> Honza
> > 
> > Thanks,
> > 
> > -- 
> > Mark Mitchell
> > CodeSourcery
> > mark@codesourcery.com
> > (650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-23 15:55     ` Jan Hubicka
  2009-11-23 16:02       ` Jan Hubicka
@ 2009-11-23 16:04       ` Mark Mitchell
  2009-11-23 16:14         ` Jan Hubicka
  1 sibling, 1 reply; 12+ messages in thread
From: Mark Mitchell @ 2009-11-23 16:04 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, rguenther, jakub, dnovillo, mjambor

Jan Hubicka wrote:

>> I like the overall direction of these patches.  But, this code in
>> thunk_adjust looks like it will create a new type every time.  Can't we
> 
> Yes, it creates new type, I considered it harmless given that thunks are not
> inlined and whole functions are short lived.

Well, in the best case, we're making work for the GC that we don't need
to make.

> Currently vfunc_type is in C++ only datastructures, so we would have to move it
> to middle end and LTO it, or just initialize lazilly static vfunc_type var
> first time we output thunk?  Or can I grab it somewhere conveniently from
> thunk_fndecl?

I don't think you can get it from thunk_fndecl; if I understand
correctly here, you want the type of an entry in the virtual table, not
the type of the thunk, right?  If I'm right, then, there's no terribly
convenient place to get it at present.  If we think that C++ virtual
tables are useful language-independent infrastructure, then moving the
type to the middle end makes sense, but having a static variable would
solve the problem too.

> By higher-level you mean stuff like integer_type_node?

No, I meant things like build_function_type.  I believe these functions
automatically use a canonical version of the type if it can be found in
a hash table.  But, just saving the type in a static variable is more
efficient.

-- 
Mark Mitchell
CodeSourcery
mark@codesourcery.com
(650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-23 16:04       ` Mark Mitchell
@ 2009-11-23 16:14         ` Jan Hubicka
  2009-11-24 17:05           ` Mark Mitchell
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Hubicka @ 2009-11-23 16:14 UTC (permalink / raw)
  To: Mark Mitchell
  Cc: Jan Hubicka, gcc-patches, rguenther, jakub, dnovillo, mjambor

> No, I meant things like build_function_type.  I believe these functions
> automatically use a canonical version of the type if it can be found in
> a hash table.  But, just saving the type in a static variable is more
> efficient.

OK, 
I will update my local version with the static variable and re-test then.

Honza
> 
> -- 
> Mark Mitchell
> CodeSourcery
> mark@codesourcery.com
> (650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-23 16:14         ` Jan Hubicka
@ 2009-11-24 17:05           ` Mark Mitchell
  2009-11-24 20:45             ` Jan Hubicka
  0 siblings, 1 reply; 12+ messages in thread
From: Mark Mitchell @ 2009-11-24 17:05 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, rguenther, jakub, dnovillo, mjambor

Jan Hubicka wrote:

> I will update my local version with the static variable and re-test then.

Can you confirm that we will still follow the invariant that if we put
out the main function we will always emit all thunks, even if
unreferenced locally, now that we're depending more heavily on cgraph?
If so, the C++ part of the patch is OK.

Thanks,

-- 
Mark Mitchell
CodeSourcery
mark@codesourcery.com
(650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-24 17:05           ` Mark Mitchell
@ 2009-11-24 20:45             ` Jan Hubicka
  0 siblings, 0 replies; 12+ messages in thread
From: Jan Hubicka @ 2009-11-24 20:45 UTC (permalink / raw)
  To: Mark Mitchell
  Cc: Jan Hubicka, gcc-patches, rguenther, jakub, dnovillo, mjambor

> Jan Hubicka wrote:
> 
> > I will update my local version with the static variable and re-test then.
> 
> Can you confirm that we will still follow the invariant that if we put
> out the main function we will always emit all thunks, even if
> unreferenced locally, now that we're depending more heavily on cgraph?

Yes, the code works by adding thunks into list of "aliases" of the
function.  This list hangs on cgraph nodes and is removed only when the
function node is removed (or at conflict in lto-symtab symbol table when
i.e. two units define thunk of same name).

> If so, the C++ part of the patch is OK.

Thanks!  I am leaving for 2 days off mail, so I will proceed with the
patch after returning.

Honza
> 
> Thanks,
> 
> -- 
> Mark Mitchell
> CodeSourcery
> mark@codesourcery.com
> (650) 331-3385 x713

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-22 18:09 ` Jan Hubicka
  2009-11-23 15:47   ` Mark Mitchell
@ 2009-11-25 14:00   ` Martin Jambor
  2009-11-27 16:25     ` Jan Hubicka
  1 sibling, 1 reply; 12+ messages in thread
From: Martin Jambor @ 2009-11-25 14:00 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches

Hi,

On Sun, Nov 22, 2009 at 07:01:11PM +0100, Jan Hubicka wrote:
> Hi,
> to avoid uncomplette transition, here is updated patch with generic
> thunk codegen.  Thunks are now generated as lowered gimple functions.
> (it might be bit easier to adjust existing code to use C++ FE
> independent functions and gimplifier, but it seems cleaner at this
> stage to operate at gimple. Plus I seem to generate more optimized IL
> saving some instructions even when thunks are optimized with -O2)

Nice.  Just three rather minor comments:

> Index: cgraph.c
> ===================================================================
> *** cgraph.c	(revision 154387)
> --- cgraph.c	(working copy)

...

> *************** dump_cgraph_node (FILE *f, struct cgraph
> *** 1646,1651 ****
> --- 1680,1705 ----
>   	fprintf(f, "(can throw external) ");
>       }
>     fprintf (f, "\n");
> + 
> +   if (node->same_body)
> +     {
> +       struct cgraph_node *n;
> +       fprintf (f, "  aliases & thunks:");
> +       for (n = node->same_body; n; n = n->next)
> +         {
> +           fprintf (f, " %s", cgraph_node_name (n));

Can you please dump also the UID like you do everywhere else?  With
long C++ names, uids are the only hope to quickly identify the nodes I
am after.

> + 	  if (n->thunk.thunk_p)
> + 	    {
> + 	      fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has virtual offset %i",
> + 	      	       lang_hooks.decl_printable_name (n->thunk.alias, 2),
> + 		       (int)n->thunk.fixed_offset,
> + 		       (int)n->thunk.virtual_value,
> + 		       (int)n->thunk.virtual_offset_p);
> + 	      fprintf (f, ")");
> + 	    }
> + 	}
> +       fprintf (f, "\n");
> +     }
>   }

...

>   
>   
> Index: cgraphunit.c
> ===================================================================
> *** cgraphunit.c	(revision 154387)
> --- cgraphunit.c	(working copy)

...

> + static tree
> + thunk_adjust (gimple_stmt_iterator * bsi,
> + 	      tree ptr, bool this_adjusting,
> + 	      HOST_WIDE_INT fixed_offset, tree virtual_offset)
> + {
> +   gimple stmt;
> +   tree ret;
> + 
> +   if (this_adjusting)
> +     {
> +       stmt = gimple_build_assign (ptr,
> + 				  fold_build2_loc (input_location,
> + 						   POINTER_PLUS_EXPR,
> + 						   TREE_TYPE (ptr), ptr,
> + 						   size_int (fixed_offset)));
> +       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> +     }
> + 
> +   /* If there's a virtual offset, look up that value in the vtable and
> +      adjust the pointer again.  */
> +   if (virtual_offset)
> +     {
> +       tree vtabletmp;
> +       tree vtabletmp2;
> +       tree vtabletmp3;
> +       tree vtable_entry_type;
> +       tree offsettmp;
> + 
> +       tree vfunc_type = make_node (FUNCTION_TYPE);
> +       TREE_TYPE (vfunc_type) = integer_type_node;
> +       TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
> +       layout_type (vfunc_type);
> + 
> +       vtable_entry_type = build_pointer_type (vfunc_type);
> + 
> +       vtabletmp =
> + 	create_tmp_var (build_pointer_type
> + 			(build_pointer_type (vtable_entry_type)), "vptr");
> + 
> +       /* The vptr is always at offset zero in the object.  */
> +       stmt = gimple_build_assign (vtabletmp,
> + 				  build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
> + 					  ptr));
> +       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> +       mark_symbols_for_renaming (stmt);
> +       find_referenced_vars_in (stmt);
> + 
> +       /* Form the vtable address.  */
> +       vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)),
> + 				   "vtableaddr");
> +       stmt = gimple_build_assign (vtabletmp2,
> + 				  build1 (INDIRECT_REF,
> + 					  TREE_TYPE (vtabletmp2), vtabletmp));
> +       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> +       mark_symbols_for_renaming (stmt);
> +       find_referenced_vars_in (stmt);
> + 
> +       /* Find the entry with the vcall offset.  */
> +       stmt = gimple_build_assign (vtabletmp2,
> + 				  fold_build2_loc (input_location,
> + 						   POINTER_PLUS_EXPR,
> + 						   TREE_TYPE (vtabletmp2),
> + 						   vtabletmp2,
> + 						   fold_convert (sizetype,
> + 								 virtual_offset)));
> +       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> + 
> +       /* Get the offset itself.  */
> +       vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
> + 				   "vcalloffset");
> +       stmt = gimple_build_assign (vtabletmp3,
> + 				  build1 (INDIRECT_REF,
> + 					  TREE_TYPE (vtabletmp3),
> + 					  vtabletmp2));
> +       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> +       mark_symbols_for_renaming (stmt);
> +       find_referenced_vars_in (stmt);
> + 
> +       /* Cast to sizetype.  */
> +       offsettmp = create_tmp_var (sizetype, "offset");
> +       stmt = gimple_build_assign (offsettmp, fold_convert (sizetype, vtabletmp3));
> +       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> +       mark_symbols_for_renaming (stmt);
> +       find_referenced_vars_in (stmt);
> + 
> +       /* Adjust the `this' pointer.  */
> +       ptr = fold_build2_loc (input_location,
> + 			     POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
> + 			     offsettmp);
> +     }
> + 
> +   if (!this_adjusting)
> +     /* Adjust the pointer by the constant.  */
> +     {
> +       tree ptrtmp;
> + 
> +       if (TREE_CODE (ptr) == VAR_DECL)
> +         ptrtmp = ptr;
> +       else
> +         {
> +           ptrtmp = create_tmp_var (TREE_TYPE (ptr), "ptr");
> +           stmt = gimple_build_assign (ptrtmp, ptr);
> + 	  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> + 	  mark_symbols_for_renaming (stmt);
> + 	  find_referenced_vars_in (stmt);
> + 	}

I think that force_gimple_operand_gsi is what you want to use instead
of the whole if statement above.


> +       ptr = fold_build2_loc (input_location,
> + 			     POINTER_PLUS_EXPR, TREE_TYPE (ptrtmp), ptrtmp,
> + 			     size_int (fixed_offset));
> +     }
> + 
> +   /* Emit the statement and gimplify the adjustment expression.  */
> +   ret = create_tmp_var (TREE_TYPE (ptr), "adjusted_this");
> +   stmt = gimple_build_assign (ret, ptr);
> +   mark_symbols_for_renaming (stmt);
> +   find_referenced_vars_in (stmt);
> +   gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> + 
> +   return ret;
> + }

...

> Index: ira.c
> ===================================================================
> *** ira.c	(revision 154387)
> --- ira.c	(working copy)
> *************** ira (FILE *f)
> *** 3172,3177 ****
> --- 3172,3178 ----
>     
>     ira_assert (current_loops == NULL);
>     flow_loops_find (&ira_loops);
> +   record_loop_exits ();
>     current_loops = &ira_loops;
>         
>     if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
> *************** ira (FILE *f)
> *** 3215,3220 ****
> --- 3216,3222 ----
>   	  df_analyze ();
>   	  
>   	  flow_loops_find (&ira_loops);
> + 	  record_loop_exits ();
>   	  current_loops = &ira_loops;
>   
>   	  setup_allocno_assignment_flags ();

These changes look rather unrelated and are not in the Changelog so I
guess it is also unintentional?

Thanks,

Martin

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-25 14:00   ` Martin Jambor
@ 2009-11-27 16:25     ` Jan Hubicka
  2009-11-29 11:43       ` Jan Hubicka
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Hubicka @ 2009-11-27 16:25 UTC (permalink / raw)
  To: Jan Hubicka, gcc-patches

> > +           fprintf (f, " %s", cgraph_node_name (n));
> 
> Can you please dump also the UID like you do everywhere else?  With
> long C++ names, uids are the only hope to quickly identify the nodes I
> am after.

Will do even if thunks come out in mangled form for whatever reason, so they are
easier to handle then your average C++ function name.
> > +           ptrtmp = create_tmp_var (TREE_TYPE (ptr), "ptr");
> > +           stmt = gimple_build_assign (ptrtmp, ptr);
> > + 	  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
> > + 	  mark_symbols_for_renaming (stmt);
> > + 	  find_referenced_vars_in (stmt);
> > + 	}
> 
> I think that force_gimple_operand_gsi is what you want to use instead
> of the whole if statement above.

Yes, it would be more compact equivalent, will give it a try ;)
> 
> These changes look rather unrelated and are not in the Changelog so I
> guess it is also unintentional?

Yes, they was just change to IRA to avoid enumerating loop boides over and over
I sent to other thread.

Honza
> 
> Thanks,
> 
> Martin

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [C++/cgraph] Middle-end thunk support
  2009-11-27 16:25     ` Jan Hubicka
@ 2009-11-29 11:43       ` Jan Hubicka
  0 siblings, 0 replies; 12+ messages in thread
From: Jan Hubicka @ 2009-11-29 11:43 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches

Hi,
this is variant of patch I comitted after additional testing on SPEC2K6 LTO. It has all
the changes disucssed except for force_gimple_operand_gsi that introduces extra moves to
thunks.  I guess we should add some more convenient API to output specific gimple
statement in possibly folded form without need to go to trees back and forth, I will
look into this at next stage1.

Honza

	* cgraph.c (same_body_alias_1): Break out of
	(same_body_alias): ... here; remove comdat check; it is handled
	in cp already.
	(cgraph_add_thunk): New.
	(dump_cgraph_node): Dump aliases and thunks.
	* cgraph.h (cgraph_thunk_info): New structure.
	(struct cgraph_node): Add thunk info.
	(cgraph_add_thunk): New.
	* cgraphunit.c (cgraph_emit_thunks): Remove.
	(cgraph_finalize_compilation_unit): Do not call cgraph_emit_thunks.
	(assemble_thunk): New function.
	(cgraph_expand_function): Handle thunks.
	(thunk_adjust): New.
	(init_lowered_empty_function): New.
	* lto-cgraph.c (lto_output_node): Stream thunk info.
	(input_node): Likewise.
	* langhooks.h (lang_hooks_for_callgraph): Remove emit_associated_thunks.
	* langhooks-def.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove.
	(LANG_HOOKS_CALLGRAPH_INITIALIZER): Update.
	* i386.c (x86_output_mi_thunk): Make output prettier.

	* optimize.c (maybe_clone_body): Emit thunks associated to alias.
	* Make-lang.in (method.o): Add dependency on gimple.h.
	* method.c: Include gimple.h
	(make_alias_for_thunk): Use same body alias instead of assemble_alias.
	(use_thunk): Drop codegen; use cgraph_add_thunk; gimplify
	generic thunks.
	* semantics.c (expand_or_defer_fn): Emit associated thunks.
	* cp-objcp-common.h (LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS): Remove.
Index: cgraph.c
===================================================================
*** cgraph.c	(revision 154719)
--- cgraph.c	(working copy)
*************** The callgraph:
*** 85,90 ****
--- 85,91 ----
  #include "tree-flow.h"
  #include "value-prof.h"
  #include "except.h"
+ #include "diagnostic.h"
  
  static void cgraph_node_remove_callers (struct cgraph_node *node);
  static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
*************** cgraph_node (tree decl)
*** 507,535 ****
    return node;
  }
  
! /* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
!    Same body aliases are output whenever the body of DECL is output,
!    and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).  */
  
! bool
! cgraph_same_body_alias (tree alias, tree decl)
  {
    struct cgraph_node key, *alias_node, *decl_node, **slot;
  
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
-   gcc_assert (!assembler_name_hash);
- 
- #ifndef ASM_OUTPUT_DEF
-   /* If aliases aren't supported by the assembler, fail.  */
-   return false;
- #endif
- 
-   /* Comdat same body aliases are only supported when comdat groups
-      are supported and the symbols are weak.  */
-   if (DECL_ONE_ONLY (decl) && (!HAVE_COMDAT_GROUP || !DECL_WEAK (decl)))
-     return false;
- 
    decl_node = cgraph_node (decl);
  
    key.decl = alias;
--- 508,522 ----
    return node;
  }
  
! /* Mark ALIAS as an alias to DECL.  */
  
! static struct cgraph_node *
! cgraph_same_body_alias_1 (tree alias, tree decl)
  {
    struct cgraph_node key, *alias_node, *decl_node, **slot;
  
    gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
    gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
    decl_node = cgraph_node (decl);
  
    key.decl = alias;
*************** cgraph_same_body_alias (tree alias, tree
*** 538,544 ****
  
    /* If the cgraph_node has been already created, fail.  */
    if (*slot)
!     return false;
  
    alias_node = cgraph_allocate_node ();
    alias_node->decl = alias;
--- 525,531 ----
  
    /* If the cgraph_node has been already created, fail.  */
    if (*slot)
!     return NULL;
  
    alias_node = cgraph_allocate_node ();
    alias_node->decl = alias;
*************** cgraph_same_body_alias (tree alias, tree
*** 548,556 ****
    if (decl_node->same_body)
      decl_node->same_body->previous = alias_node;
    alias_node->next = decl_node->same_body;
    decl_node->same_body = alias_node;
    *slot = alias_node;
!   return true;
  }
  
  /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
--- 535,590 ----
    if (decl_node->same_body)
      decl_node->same_body->previous = alias_node;
    alias_node->next = decl_node->same_body;
+   alias_node->thunk.alias = decl;
    decl_node->same_body = alias_node;
    *slot = alias_node;
!   return alias_node;
! }
! 
! /* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
!    Same body aliases are output whenever the body of DECL is output,
!    and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).   */
! 
! bool
! cgraph_same_body_alias (tree alias, tree decl)
! {
! #ifndef ASM_OUTPUT_DEF
!   /* If aliases aren't supported by the assembler, fail.  */
!   return false;
! #endif
! 
!   /*gcc_assert (!assembler_name_hash);*/
! 
!   return cgraph_same_body_alias_1 (alias, decl) != NULL;
! }
! 
! void
! cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
! 		  HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
! 		  tree virtual_offset,
! 		  tree real_alias)
! {
!   struct cgraph_node *node = cgraph_get_node (alias);
! 
!   if (node)
!     {
!       gcc_assert (node->local.finalized);
!       gcc_assert (!node->same_body);
!       cgraph_remove_node (node);
!     }
!   
!   node = cgraph_same_body_alias_1 (alias, decl);
!   gcc_assert (node);
! #ifdef ENABLE_CHECKING
!   gcc_assert (!virtual_offset
!   	      || tree_int_cst_equal (virtual_offset, size_int (virtual_value)));
! #endif
!   node->thunk.fixed_offset = fixed_offset;
!   node->thunk.this_adjusting = this_adjusting;
!   node->thunk.virtual_value = virtual_value;
!   node->thunk.virtual_offset_p = virtual_offset != NULL;
!   node->thunk.alias = real_alias;
!   node->thunk.thunk_p = true;
  }
  
  /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
*************** dump_cgraph_node (FILE *f, struct cgraph
*** 1646,1651 ****
--- 1680,1706 ----
  	fprintf(f, "(can throw external) ");
      }
    fprintf (f, "\n");
+ 
+   if (node->same_body)
+     {
+       struct cgraph_node *n;
+       fprintf (f, "  aliases & thunks:");
+       for (n = node->same_body; n; n = n->next)
+         {
+           fprintf (f, " %s/%i", cgraph_node_name (n), n->uid);
+ 	  if (n->thunk.thunk_p)
+ 	    {
+ 	      fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has "
+ 		       "virtual offset %i",
+ 	      	       lang_hooks.decl_printable_name (n->thunk.alias, 2),
+ 		       (int)n->thunk.fixed_offset,
+ 		       (int)n->thunk.virtual_value,
+ 		       (int)n->thunk.virtual_offset_p);
+ 	      fprintf (f, ")");
+ 	    }
+ 	}
+       fprintf (f, "\n");
+     }
  }
  
  
Index: cgraph.h
===================================================================
*** cgraph.h	(revision 154719)
--- cgraph.h	(working copy)
*************** struct GTY(()) inline_summary
*** 69,74 ****
--- 69,87 ----
    int time_inlining_benefit;
  };
  
+ /* Information about thunk, used only for same body aliases.  */
+ 
+ struct GTY(()) cgraph_thunk_info {
+   /* Information about the thunk.  */
+   HOST_WIDE_INT fixed_offset;
+   HOST_WIDE_INT virtual_value;
+   tree alias;
+   bool this_adjusting;
+   bool virtual_offset_p;
+   /* Set to true when alias node is thunk.  */
+   bool thunk_p;
+ };
+ 
  /* Information about the function collected locally.
     Available after function is analyzed.  */
  
*************** struct GTY((chain_next ("%h.next"), chai
*** 184,191 ****
    struct cgraph_node *prev_sibling_clone;
    struct cgraph_node *clones;
    struct cgraph_node *clone_of;
!   /* For normal nodes pointer to the list of alias nodes, in alias
!      nodes pointer to the normal node.  */
    struct cgraph_node *same_body;
    /* For functions with many calls sites it holds map from call expression
       to the edge to speed up cgraph_edge function.  */
--- 197,204 ----
    struct cgraph_node *prev_sibling_clone;
    struct cgraph_node *clones;
    struct cgraph_node *clone_of;
!   /* For normal nodes pointer to the list of alias and thunk nodes,
!      in alias/thunk nodes pointer to the normal node.  */
    struct cgraph_node *same_body;
    /* For functions with many calls sites it holds map from call expression
       to the edge to speed up cgraph_edge function.  */
*************** struct GTY((chain_next ("%h.next"), chai
*** 202,207 ****
--- 215,221 ----
    struct cgraph_global_info global;
    struct cgraph_rtl_info rtl;
    struct cgraph_clone_info clone;
+   struct cgraph_thunk_info thunk;
  
    /* Expected number of executions: calculated in profile.c.  */
    gcov_type count;
*************** struct GTY((chain_next ("%h.next"), chai
*** 244,251 ****
    unsigned alias : 1;
    /* Set for nodes that was constructed and finalized by frontend.  */
    unsigned finalized_by_frontend : 1;
!   /* Set for alias nodes, same_body points to the node they are alias of
!      and they are linked through the next/previous pointers.  */
    unsigned same_body_alias : 1;
  };
  
--- 258,265 ----
    unsigned alias : 1;
    /* Set for nodes that was constructed and finalized by frontend.  */
    unsigned finalized_by_frontend : 1;
!   /* Set for alias and thunk nodes, same_body points to the node they are alias
!      of and they are linked through the next/previous pointers.  */
    unsigned same_body_alias : 1;
  };
  
*************** struct cgraph_edge *cgraph_create_edge (
*** 423,428 ****
--- 437,443 ----
  struct cgraph_node * cgraph_get_node (tree);
  struct cgraph_node *cgraph_node (tree);
  bool cgraph_same_body_alias (tree, tree);
+ void cgraph_add_thunk (tree, tree, bool, HOST_WIDE_INT, HOST_WIDE_INT, tree, tree);
  void cgraph_remove_same_body_alias (struct cgraph_node *);
  struct cgraph_node *cgraph_node_for_asm (tree);
  struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple);
Index: cgraphunit.c
===================================================================
*** cgraphunit.c	(revision 154719)
--- cgraphunit.c	(working copy)
*************** static GTY (()) VEC(tree, gc) *static_ct
*** 149,154 ****
--- 149,157 ----
  /* A vector of FUNCTION_DECLs declared as static destructors.  */
  static GTY (()) VEC(tree, gc) *static_dtors;
  
+ /* Used for vtable lookup in thunk adjusting.  */
+ static GTY (()) tree vtable_entry_type;
+ 
  /* When target does not have ctors and dtors, we call all constructor
     and destructor by special initialization/destruction function
     recognized by collect2.
*************** verify_cgraph_node (struct cgraph_node *
*** 741,748 ****
  			    debug_gimple_stmt (stmt);
  			    error_found = true;
  			  }
! 			if (!clone_of_p (cgraph_node (decl), e->callee)
! 			    && !e->callee->global.inlined_to)
  			  {
  			    error ("edge points to wrong declaration:");
  			    debug_tree (e->callee->decl);
--- 744,756 ----
  			    debug_gimple_stmt (stmt);
  			    error_found = true;
  			  }
! 			if (e->callee->same_body_alias)
! 			  {
! 			    error ("edge points to same body alias:");
! 			    debug_tree (e->callee->decl);
! 			  }
! 			else if (!clone_of_p (cgraph_node (decl), e->callee)
! 			         && !e->callee->global.inlined_to)
  			  {
  			    error ("edge points to wrong declaration:");
  			    debug_tree (e->callee->decl);
*************** cgraph_analyze_functions (void)
*** 1034,1068 ****
  }
  
  
- /* Emit thunks for every node in the cgraph.
-    FIXME: We really ought to emit thunks only for functions that are needed.  */
- 
- static void
- cgraph_emit_thunks (void)
- {
-   struct cgraph_node *n, *alias;
- 
-   for (n = cgraph_nodes; n; n = n->next)
-     {
-       /* Only emit thunks on functions defined in this TU.
- 	 Note that this may emit more thunks than strictly necessary.
- 	 During optimization some nodes may disappear.  It would be
- 	 nice to only emit thunks only for the functions that will be
- 	 emitted, but we cannot know that until the inliner and other
- 	 IPA passes have run (see the sequencing of the call to
- 	 cgraph_mark_functions_to_output in cgraph_optimize).  */
-       if (n->reachable
- 	  && !DECL_EXTERNAL (n->decl))
- 	{
- 	  lang_hooks.callgraph.emit_associated_thunks (n->decl);
- 	  for (alias = n->same_body; alias; alias = alias->next)
- 	    if (!DECL_EXTERNAL (alias->decl))
- 	      lang_hooks.callgraph.emit_associated_thunks (alias->decl);
- 	}
-     }
- }
- 
- 
  /* Analyze the whole compilation unit once it is parsed completely.  */
  
  void
--- 1042,1047 ----
*************** cgraph_finalize_compilation_unit (void)
*** 1093,1102 ****
       remove unreachable nodes.  */
    cgraph_analyze_functions ();
  
-   /* Emit thunks for reachable nodes, if needed.  */
-   if (lang_hooks.callgraph.emit_associated_thunks)
-     cgraph_emit_thunks ();
- 
    /* Mark alias targets necessary and emit diagnostics.  */
    finish_aliases_1 ();
  
--- 1072,1077 ----
*************** cgraph_mark_functions_to_output (void)
*** 1159,1164 ****
--- 1134,1480 ----
      }
  }
  
+ /* DECL is FUNCTION_DECL.  Initialize datastructures so DECL is a function
+    in lowered gimple form.
+    
+    Set current_function_decl and cfun to newly constructed empty function body.
+    return basic block in the function body.  */
+ 
+ static basic_block
+ init_lowered_empty_function (tree decl)
+ {
+   basic_block bb;
+ 
+   current_function_decl = decl;
+   allocate_struct_function (decl, false);
+   gimple_register_cfg_hooks ();
+   init_empty_tree_cfg ();
+   init_tree_ssa (cfun);
+   init_ssa_operands ();
+   cfun->gimple_df->in_ssa_p = true;
+   DECL_INITIAL (decl) = make_node (BLOCK);
+ 
+   DECL_SAVED_TREE (decl) = error_mark_node;
+   cfun->curr_properties |=
+     (PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_referenced_vars |
+      PROP_ssa);
+ 
+   /* Create BB for body of the function and connect it properly.  */
+   bb = create_basic_block (NULL, (void *) 0, ENTRY_BLOCK_PTR);
+   make_edge (ENTRY_BLOCK_PTR, bb, 0);
+   make_edge (bb, EXIT_BLOCK_PTR, 0);
+ 
+   return bb;
+ }
+ 
+ /* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
+    offset indicated by VIRTUAL_OFFSET, if that is
+    non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
+    zero for a result adjusting thunk.  */
+ 
+ static tree
+ thunk_adjust (gimple_stmt_iterator * bsi,
+ 	      tree ptr, bool this_adjusting,
+ 	      HOST_WIDE_INT fixed_offset, tree virtual_offset)
+ {
+   gimple stmt;
+   tree ret;
+ 
+   if (this_adjusting)
+     {
+       stmt = gimple_build_assign (ptr,
+ 				  fold_build2_loc (input_location,
+ 						   POINTER_PLUS_EXPR,
+ 						   TREE_TYPE (ptr), ptr,
+ 						   size_int (fixed_offset)));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+     }
+ 
+   /* If there's a virtual offset, look up that value in the vtable and
+      adjust the pointer again.  */
+   if (virtual_offset)
+     {
+       tree vtabletmp;
+       tree vtabletmp2;
+       tree vtabletmp3;
+       tree offsettmp;
+ 
+       if (!vtable_entry_type)
+ 	{
+ 	  tree vfunc_type = make_node (FUNCTION_TYPE);
+ 	  TREE_TYPE (vfunc_type) = integer_type_node;
+ 	  TYPE_ARG_TYPES (vfunc_type) = NULL_TREE;
+ 	  layout_type (vfunc_type);
+ 
+ 	  vtable_entry_type = build_pointer_type (vfunc_type);
+ 	}
+ 
+       vtabletmp =
+ 	create_tmp_var (build_pointer_type
+ 			(build_pointer_type (vtable_entry_type)), "vptr");
+ 
+       /* The vptr is always at offset zero in the object.  */
+       stmt = gimple_build_assign (vtabletmp,
+ 				  build1 (NOP_EXPR, TREE_TYPE (vtabletmp),
+ 					  ptr));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Form the vtable address.  */
+       vtabletmp2 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp)),
+ 				   "vtableaddr");
+       stmt = gimple_build_assign (vtabletmp2,
+ 				  build1 (INDIRECT_REF,
+ 					  TREE_TYPE (vtabletmp2), vtabletmp));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Find the entry with the vcall offset.  */
+       stmt = gimple_build_assign (vtabletmp2,
+ 				  fold_build2_loc (input_location,
+ 						   POINTER_PLUS_EXPR,
+ 						   TREE_TYPE (vtabletmp2),
+ 						   vtabletmp2,
+ 						   fold_convert (sizetype,
+ 								 virtual_offset)));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+ 
+       /* Get the offset itself.  */
+       vtabletmp3 = create_tmp_var (TREE_TYPE (TREE_TYPE (vtabletmp2)),
+ 				   "vcalloffset");
+       stmt = gimple_build_assign (vtabletmp3,
+ 				  build1 (INDIRECT_REF,
+ 					  TREE_TYPE (vtabletmp3),
+ 					  vtabletmp2));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Cast to sizetype.  */
+       offsettmp = create_tmp_var (sizetype, "offset");
+       stmt = gimple_build_assign (offsettmp, fold_convert (sizetype, vtabletmp3));
+       gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+       mark_symbols_for_renaming (stmt);
+       find_referenced_vars_in (stmt);
+ 
+       /* Adjust the `this' pointer.  */
+       ptr = fold_build2_loc (input_location,
+ 			     POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
+ 			     offsettmp);
+     }
+ 
+   if (!this_adjusting)
+     /* Adjust the pointer by the constant.  */
+     {
+       tree ptrtmp;
+ 
+       if (TREE_CODE (ptr) == VAR_DECL)
+         ptrtmp = ptr;
+       else
+         {
+           ptrtmp = create_tmp_var (TREE_TYPE (ptr), "ptr");
+           stmt = gimple_build_assign (ptrtmp, ptr);
+ 	  gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+ 	  mark_symbols_for_renaming (stmt);
+ 	  find_referenced_vars_in (stmt);
+ 	}
+       ptr = fold_build2_loc (input_location,
+ 			     POINTER_PLUS_EXPR, TREE_TYPE (ptrtmp), ptrtmp,
+ 			     size_int (fixed_offset));
+     }
+ 
+   /* Emit the statement and gimplify the adjustment expression.  */
+   ret = create_tmp_var (TREE_TYPE (ptr), "adjusted_this");
+   stmt = gimple_build_assign (ret, ptr);
+   mark_symbols_for_renaming (stmt);
+   find_referenced_vars_in (stmt);
+   gsi_insert_after (bsi, stmt, GSI_NEW_STMT);
+ 
+   return ret;
+ }
+ 
+ /* Produce assembler for thunk NODE.  */
+ 
+ static void
+ assemble_thunk (struct cgraph_node *node)
+ {
+   bool this_adjusting = node->thunk.this_adjusting;
+   HOST_WIDE_INT fixed_offset = node->thunk.fixed_offset;
+   HOST_WIDE_INT virtual_value = node->thunk.virtual_value;
+   tree virtual_offset = NULL;
+   tree alias = node->thunk.alias;
+   tree thunk_fndecl = node->decl;
+   tree a = DECL_ARGUMENTS (thunk_fndecl);
+ 
+   current_function_decl = thunk_fndecl;
+ 
+   if (this_adjusting
+       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
+ 					      virtual_value, alias))
+     {
+       const char *fnname;
+       tree fn_block;
+       
+       DECL_RESULT (thunk_fndecl)
+ 	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
+ 		      RESULT_DECL, 0, integer_type_node);
+       fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
+ 
+       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
+ 	 create one.  */
+       fn_block = make_node (BLOCK);
+       BLOCK_VARS (fn_block) = a;
+       DECL_INITIAL (thunk_fndecl) = fn_block;
+       init_function_start (thunk_fndecl);
+       cfun->is_thunk = 1;
+       assemble_start_function (thunk_fndecl, fnname);
+ 
+       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
+ 				       fixed_offset, virtual_value, alias);
+ 
+       assemble_end_function (thunk_fndecl, fnname);
+       init_insn_lengths ();
+       free_after_compilation (cfun);
+       set_cfun (NULL);
+       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
+     }
+   else
+     {
+       tree restype;
+       basic_block bb, then_bb, else_bb, return_bb;
+       gimple_stmt_iterator bsi;
+       int nargs = 0;
+       tree arg;
+       int i;
+       tree resdecl;
+       tree restmp = NULL;
+       VEC(tree, heap) *vargs;
+ 
+       gimple call;
+       gimple ret;
+ 
+       DECL_IGNORED_P (thunk_fndecl) = 1;
+       bitmap_obstack_initialize (NULL);
+ 
+       if (node->thunk.virtual_offset_p)
+         virtual_offset = size_int (virtual_value);
+ 
+       /* Build the return declaration for the function.  */
+       restype = TREE_TYPE (TREE_TYPE (thunk_fndecl));
+       if (DECL_RESULT (thunk_fndecl) == NULL_TREE)
+ 	{
+ 	  resdecl = build_decl (input_location, RESULT_DECL, 0, restype);
+ 	  DECL_ARTIFICIAL (resdecl) = 1;
+ 	  DECL_IGNORED_P (resdecl) = 1;
+ 	  DECL_RESULT (thunk_fndecl) = resdecl;
+ 	}
+       else
+ 	resdecl = DECL_RESULT (thunk_fndecl);
+ 
+       bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl);
+ 
+       bsi = gsi_start_bb (bb);
+ 
+       /* Build call to the function being thunked.  */
+       if (!VOID_TYPE_P (restype))
+ 	{
+ 	  if (!is_gimple_reg_type (restype))
+ 	    {
+ 	      restmp = resdecl;
+ 	      cfun->local_decls = tree_cons (NULL_TREE, restmp, cfun->local_decls);
+ 	      BLOCK_VARS (DECL_INITIAL (current_function_decl)) = restmp;
+ 	    }
+ 	  else
+             restmp = create_tmp_var_raw (restype, "retval");
+ 	}
+ 
+       for (arg = a; arg; arg = TREE_CHAIN (arg))
+         nargs++;
+       vargs = VEC_alloc (tree, heap, nargs);
+       if (this_adjusting)
+         VEC_quick_push (tree, vargs,
+ 			thunk_adjust (&bsi,
+ 				      a, 1, fixed_offset,
+ 				      virtual_offset));
+       else
+         VEC_quick_push (tree, vargs, a);
+       for (i = 1, arg = TREE_CHAIN (a); i < nargs; i++, arg = TREE_CHAIN (arg))
+         VEC_quick_push (tree, vargs, arg);
+       call = gimple_build_call_vec (build_fold_addr_expr_loc (0, alias), vargs);
+       VEC_free (tree, heap, vargs);
+       gimple_call_set_cannot_inline (call, true);
+       gimple_call_set_from_thunk (call, true);
+       if (restmp)
+         gimple_call_set_lhs (call, restmp);
+       gsi_insert_after (&bsi, call, GSI_NEW_STMT);
+       mark_symbols_for_renaming (call);
+       find_referenced_vars_in (call);
+       update_stmt (call);
+ 
+       if (restmp && !this_adjusting)
+         {
+ 	  tree true_label = NULL_TREE, false_label = NULL_TREE;
+ 
+ 	  if (TREE_CODE (TREE_TYPE (restmp)) == POINTER_TYPE)
+ 	    {
+ 	      gimple stmt;
+ 	      /* If the return type is a pointer, we need to
+ 		 protect against NULL.  We know there will be an
+ 		 adjustment, because that's why we're emitting a
+ 		 thunk.  */
+ 	      then_bb = create_basic_block (NULL, (void *) 0, bb);
+ 	      return_bb = create_basic_block (NULL, (void *) 0, then_bb);
+ 	      else_bb = create_basic_block (NULL, (void *) 0, else_bb);
+ 	      remove_edge (single_succ_edge (bb));
+ 	      true_label = gimple_block_label (then_bb);
+ 	      false_label = gimple_block_label (else_bb);
+ 	      stmt = gimple_build_cond (NE_EXPR, restmp,
+ 	      				fold_convert (TREE_TYPE (restmp),
+ 						      integer_zero_node),
+ 	      			        NULL_TREE, NULL_TREE);
+ 	      gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+ 	      make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+ 	      make_edge (bb, else_bb, EDGE_FALSE_VALUE);
+ 	      make_edge (return_bb, EXIT_BLOCK_PTR, 0);
+ 	      make_edge (then_bb, return_bb, EDGE_FALLTHRU);
+ 	      make_edge (else_bb, return_bb, EDGE_FALLTHRU);
+ 	      bsi = gsi_last_bb (then_bb);
+ 	    }
+ 
+ 	  restmp = thunk_adjust (&bsi, restmp, /*this_adjusting=*/0,
+ 			         fixed_offset, virtual_offset);
+ 	  if (true_label)
+ 	    {
+ 	      gimple stmt;
+ 	      bsi = gsi_last_bb (else_bb);
+ 	      stmt = gimple_build_assign (restmp, fold_convert (TREE_TYPE (restmp),
+ 								integer_zero_node));
+ 	      gsi_insert_after (&bsi, stmt, GSI_NEW_STMT);
+ 	      bsi = gsi_last_bb (return_bb);
+ 	    }
+ 	}
+       else
+         gimple_call_set_tail (call, true);
+ 
+       /* Build return value.  */
+       ret = gimple_build_return (restmp);
+       gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
+ 
+       delete_unreachable_blocks ();
+       update_ssa (TODO_update_ssa);
+ 
+       cgraph_remove_same_body_alias (node);
+       /* Since we want to emit the thunk, we explicitly mark its name as
+ 	 referenced.  */
+       mark_decl_referenced (thunk_fndecl);
+       cgraph_add_new_function (thunk_fndecl, true);
+       bitmap_obstack_release (NULL);
+     }
+   current_function_decl = NULL;
+ }
+ 
  /* Expand function specified by NODE.  */
  
  static void
*************** cgraph_expand_function (struct cgraph_no
*** 1182,1191 ****
    current_function_decl = NULL;
    if (node->same_body)
      {
!       struct cgraph_node *alias;
        bool saved_alias = node->alias;
!       for (alias = node->same_body; alias; alias = alias->next)
! 	assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl));
        node->alias = saved_alias;
      }
    gcc_assert (!cgraph_preserve_function_body_p (decl));
--- 1498,1519 ----
    current_function_decl = NULL;
    if (node->same_body)
      {
!       struct cgraph_node *alias, *next;
        bool saved_alias = node->alias;
!       for (alias = node->same_body;
!       	   alias && alias->next; alias = alias->next)
!         ;
!       /* Walk aliases in the order they were created; it is possible that
!          thunks reffers to the aliases made earlier.  */
!       for (; alias; alias = next)
!         {
! 	  next = alias->previous;
! 	  if (!alias->thunk.thunk_p)
! 	    assemble_alias (alias->decl,
! 			    DECL_ASSEMBLER_NAME (alias->thunk.alias));
! 	  else
! 	    assemble_thunk (alias);
! 	}
        node->alias = saved_alias;
      }
    gcc_assert (!cgraph_preserve_function_body_p (decl));
Index: cp/optimize.c
===================================================================
*** cp/optimize.c	(revision 154719)
--- cp/optimize.c	(working copy)
*************** maybe_clone_body (tree fn)
*** 250,256 ****
  	  && DECL_INTERFACE_KNOWN (fns[0])
  	  && !DECL_ONE_ONLY (fns[0])
  	  && cgraph_same_body_alias (clone, fns[0]))
! 	alias = true;
  
        /* Build the delete destructor by calling complete destructor
           and delete function.  */
--- 250,259 ----
  	  && DECL_INTERFACE_KNOWN (fns[0])
  	  && !DECL_ONE_ONLY (fns[0])
  	  && cgraph_same_body_alias (clone, fns[0]))
! 	{
! 	  alias = true;
! 	  emit_associated_thunks (clone);
! 	}
  
        /* Build the delete destructor by calling complete destructor
           and delete function.  */
Index: cp/Make-lang.in
===================================================================
*** cp/Make-lang.in	(revision 154719)
--- cp/Make-lang.in	(working copy)
*************** cp/friend.o: cp/friend.c $(CXX_TREE_H) $
*** 275,281 ****
  cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \
    toplev.h except.h $(TARGET_H)
  cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \
!   $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h
  cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \
    convert.h $(TARGET_H)
  cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H)
--- 275,281 ----
  cp/init.o: cp/init.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) $(EXPR_H) \
    toplev.h except.h $(TARGET_H)
  cp/method.o: cp/method.c $(CXX_TREE_H) $(TM_H) toplev.h $(RTL_H) $(EXPR_H) \
!   $(TM_P_H) $(TARGET_H) $(DIAGNOSTIC_H) gt-cp-method.h $(GIMPLE_H)
  cp/cvt.o: cp/cvt.c $(CXX_TREE_H) $(TM_H) cp/decl.h $(FLAGS_H) toplev.h \
    convert.h $(TARGET_H)
  cp/search.o: cp/search.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) toplev.h $(RTL_H)
Index: cp/method.c
===================================================================
*** cp/method.c	(revision 154719)
--- cp/method.c	(working copy)
*************** along with GCC; see the file COPYING3.  
*** 39,44 ****
--- 39,45 ----
  #include "tree-pass.h"
  #include "diagnostic.h"
  #include "cgraph.h"
+ #include "gimple.h"
  
  /* Various flags to control the mangling process.  */
  
*************** enum mangling_flags
*** 58,64 ****
  
  typedef enum mangling_flags mangling_flags;
  
- static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree);
  static void do_build_assign_ref (tree);
  static void do_build_copy_constructor (tree);
  static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *);
--- 59,64 ----
*************** finish_thunk (tree thunk)
*** 205,260 ****
    SET_DECL_ASSEMBLER_NAME (thunk, name);
  }
  
- /* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
-    offset indicated by VIRTUAL_OFFSET, if that is
-    non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
-    zero for a result adjusting thunk.  */
- 
- static tree
- thunk_adjust (tree ptr, bool this_adjusting,
- 	      HOST_WIDE_INT fixed_offset, tree virtual_offset)
- {
-   if (this_adjusting)
-     /* Adjust the pointer by the constant.  */
-     ptr = fold_build2_loc (input_location,
- 		       POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- 		       size_int (fixed_offset));
- 
-   /* If there's a virtual offset, look up that value in the vtable and
-      adjust the pointer again.  */
-   if (virtual_offset)
-     {
-       tree vtable;
- 
-       ptr = save_expr (ptr);
-       /* The vptr is always at offset zero in the object.  */
-       vtable = build1 (NOP_EXPR,
- 		       build_pointer_type (build_pointer_type
- 					   (vtable_entry_type)),
- 		       ptr);
-       /* Form the vtable address.  */
-       vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
-       /* Find the entry with the vcall offset.  */
-       vtable = fold_build2_loc (input_location,
- 			    POINTER_PLUS_EXPR, TREE_TYPE (vtable), vtable,
- 			    fold_convert (sizetype, virtual_offset));
-       /* Get the offset itself.  */
-       vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
-       /* Adjust the `this' pointer.  */
-       ptr = fold_build2_loc (input_location,
- 			 POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- 			 fold_convert (sizetype, vtable));
-     }
- 
-   if (!this_adjusting)
-     /* Adjust the pointer by the constant.  */
-     ptr = fold_build2_loc (input_location,
- 		       POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr,
- 		       size_int (fixed_offset));
- 
-   return ptr;
- }
- 
  static GTY (()) int thunk_labelno;
  
  /* Create a static alias to function.  */
--- 205,210 ----
*************** make_alias_for_thunk (tree function)
*** 303,309 ****
    alias = make_alias_for (function, get_identifier (buf));
  
    if (!flag_syntax_only)
!     assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
  
    return alias;
  }
--- 253,263 ----
    alias = make_alias_for (function, get_identifier (buf));
  
    if (!flag_syntax_only)
!     {
!       bool ok = cgraph_same_body_alias (alias, function);
!       DECL_ASSEMBLER_NAME (function);
!       gcc_assert (ok);
!     }
  
    return alias;
  }
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 416,457 ****
      }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
! 
!   if (this_adjusting
!       && targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					      virtual_value, alias))
!     {
!       const char *fnname;
!       tree fn_block;
!       
!       current_function_decl = thunk_fndecl;
!       DECL_RESULT (thunk_fndecl)
! 	= build_decl (DECL_SOURCE_LOCATION (thunk_fndecl),
! 		      RESULT_DECL, 0, integer_type_node);
!       fnname = IDENTIFIER_POINTER (DECL_NAME (thunk_fndecl));
!       /* The back end expects DECL_INITIAL to contain a BLOCK, so we
! 	 create one.  */
!       fn_block = make_node (BLOCK);
!       BLOCK_VARS (fn_block) = a;
!       DECL_INITIAL (thunk_fndecl) = fn_block;
!       init_function_start (thunk_fndecl);
!       cfun->is_thunk = 1;
!       assemble_start_function (thunk_fndecl, fnname);
! 
!       targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
! 				       fixed_offset, virtual_value, alias);
! 
!       assemble_end_function (thunk_fndecl, fnname);
!       init_insn_lengths ();
!       free_after_compilation (cfun);
!       current_function_decl = 0;
!       set_cfun (NULL);
!       TREE_ASM_WRITTEN (thunk_fndecl) = 1;
!     }
!   else
      {
-       int i;
-       tree *argarray = (tree *) alloca (list_length (a) * sizeof (tree));
        /* If this is a covariant thunk, or we don't have the necessary
  	 code for efficient thunks, generate a thunk function that
  	 just makes a call to the real function.  Unfortunately, this
--- 370,384 ----
      }
    a = nreverse (t);
    DECL_ARGUMENTS (thunk_fndecl) = a;
!   TREE_ASM_WRITTEN (thunk_fndecl) = 1;
!   cgraph_add_thunk (thunk_fndecl, function,
! 		    this_adjusting, fixed_offset, virtual_value,
! 		    virtual_offset, alias);
! 
!   if (!this_adjusting
!       || !targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
! 					       virtual_value, alias))
      {
        /* If this is a covariant thunk, or we don't have the necessary
  	 code for efficient thunks, generate a thunk function that
  	 just makes a call to the real function.  Unfortunately, this
*************** use_thunk (tree thunk_fndecl, bool emit_
*** 460,528 ****
        if (varargs_function_p (function))
  	error ("generic thunk code fails for method %q#D which uses %<...%>",
  	       function);
- 
-       DECL_RESULT (thunk_fndecl) = NULL_TREE;
- 
-       start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
-       /* We don't bother with a body block for thunks.  */
- 
-       /* There's no need to check accessibility inside the thunk body.  */
-       push_deferring_access_checks (dk_no_check);
- 
-       t = a;
-       if (this_adjusting)
- 	t = thunk_adjust (t, /*this_adjusting=*/1,
- 			  fixed_offset, virtual_offset);
- 
-       /* Build up the call to the real function.  */
-       argarray[0] = t;
-       for (i = 1, a = TREE_CHAIN (a); a; a = TREE_CHAIN (a), i++)
- 	argarray[i] = a;
-       t = build_call_a (alias, i, argarray);
-       CALL_FROM_THUNK_P (t) = 1;
-       CALL_CANNOT_INLINE_P (t) = 1;
- 
-       if (VOID_TYPE_P (TREE_TYPE (t)))
- 	finish_expr_stmt (t);
-       else
- 	{
- 	  if (!this_adjusting)
- 	    {
- 	      tree cond = NULL_TREE;
- 
- 	      if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
- 		{
- 		  /* If the return type is a pointer, we need to
- 		     protect against NULL.  We know there will be an
- 		     adjustment, because that's why we're emitting a
- 		     thunk.  */
- 		  t = save_expr (t);
- 		  cond = cp_convert (boolean_type_node, t);
- 		}
- 
- 	      t = thunk_adjust (t, /*this_adjusting=*/0,
- 				fixed_offset, virtual_offset);
- 	      if (cond)
- 		t = build3 (COND_EXPR, TREE_TYPE (t), cond, t,
- 			    cp_convert (TREE_TYPE (t), integer_zero_node));
- 	    }
- 	  if (MAYBE_CLASS_TYPE_P (TREE_TYPE (t)))
- 	    t = build_cplus_new (TREE_TYPE (t), t);
- 	  finish_return_stmt (t);
- 	}
- 
-       /* Since we want to emit the thunk, we explicitly mark its name as
- 	 referenced.  */
-       mark_decl_referenced (thunk_fndecl);
- 
-       /* But we don't want debugging information about it.  */
-       DECL_IGNORED_P (thunk_fndecl) = 1;
- 
-       /* Re-enable access control.  */
-       pop_deferring_access_checks ();
- 
-       thunk_fndecl = finish_function (0);
-       cgraph_add_new_function (thunk_fndecl, false);
      }
  
    pop_from_top_level ();
--- 387,392 ----
Index: cp/semantics.c
===================================================================
*** cp/semantics.c	(revision 154719)
--- cp/semantics.c	(working copy)
*************** expand_or_defer_fn (tree fn)
*** 3419,3424 ****
--- 3419,3425 ----
  
        /* Expand or defer, at the whim of the compilation unit manager.  */
        cgraph_finalize_function (fn, function_depth > 1);
+       emit_associated_thunks (fn);
  
        function_depth--;
      }
Index: cp/cp-objcp-common.h
===================================================================
*** cp/cp-objcp-common.h	(revision 154719)
--- cp/cp-objcp-common.h	(working copy)
*************** extern bool cp_function_decl_explicit_p 
*** 104,111 ****
  
  #undef LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR cxx_callgraph_analyze_expr
- #undef LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS
- #define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS emit_associated_thunks
  
  #undef LANG_HOOKS_MAKE_TYPE
  #define LANG_HOOKS_MAKE_TYPE cxx_make_type
--- 104,109 ----
Index: lto-cgraph.c
===================================================================
*** lto-cgraph.c	(revision 154719)
--- lto-cgraph.c	(working copy)
*************** lto_output_node (struct lto_simple_outpu
*** 317,322 ****
--- 317,337 ----
  	{
  	  lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
  				    alias->decl);
+ 	  if (alias->thunk.thunk_p)
+ 	    {
+               lto_output_uleb128_stream
+ 	         (ob->main_stream,
+ 	      	  1 + (alias->thunk.this_adjusting != 0) * 2
+ 		  + (alias->thunk.virtual_offset_p != 0) * 4);
+ 	      lto_output_uleb128_stream (ob->main_stream,
+ 	      				 alias->thunk.fixed_offset);
+ 	      lto_output_uleb128_stream (ob->main_stream,
+ 	      				 alias->thunk.virtual_value);
+ 	      lto_output_fn_decl_index (ob->decl_state, ob->main_stream,
+ 					alias->thunk.alias);
+ 	    }
+ 	  else
+             lto_output_uleb128_stream (ob->main_stream, 0);
  	  alias = alias->previous;
  	}
        while (alias);
*************** input_node (struct lto_file_decl_data *f
*** 575,583 ****
    while (same_body_count-- > 0)
      {
        tree alias_decl;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       cgraph_same_body_alias (alias_decl, fn_decl);
      }
    return node;
  }
--- 590,613 ----
    while (same_body_count-- > 0)
      {
        tree alias_decl;
+       int type;
        decl_index = lto_input_uleb128 (ib);
        alias_decl = lto_file_decl_data_get_fn_decl (file_data, decl_index);
!       type = lto_input_uleb128 (ib);
!       if (!type)
!         cgraph_same_body_alias (alias_decl, fn_decl);
!       else
!         {
! 	  HOST_WIDE_INT fixed_offset = lto_input_uleb128 (ib);
! 	  HOST_WIDE_INT virtual_value = lto_input_uleb128 (ib);
! 	  tree real_alias;
! 	  decl_index = lto_input_uleb128 (ib);
! 	  real_alias = lto_file_decl_data_get_fn_decl (file_data, decl_index);
! 	  cgraph_add_thunk (alias_decl, fn_decl, type & 2, fixed_offset,
! 	  		    virtual_value,
! 			    (type & 4) ? size_int (virtual_value) : NULL_TREE,
! 			    real_alias);
! 	}
      }
    return node;
  }
Index: langhooks.h
===================================================================
*** langhooks.h	(revision 154719)
--- langhooks.h	(working copy)
*************** struct lang_hooks_for_callgraph
*** 49,57 ****
    /* The node passed is a language-specific tree node.  If its contents
       are relevant to use of other declarations, mark them.  */
    tree (*analyze_expr) (tree *, int *);
- 
-   /* Emit thunks associated to function.  */
-   void (*emit_associated_thunks) (tree);
  };
  
  /* The following hooks are used by tree-dump.c.  */
--- 49,54 ----
Index: ira.c
===================================================================
*** ira.c	(revision 154719)
--- ira.c	(working copy)
*************** ira (FILE *f)
*** 3172,3177 ****
--- 3172,3178 ----
  
    ira_assert (current_loops == NULL);
    flow_loops_find (&ira_loops);
+   record_loop_exits ();
    current_loops = &ira_loops;
  
    if (internal_flag_ira_verbose > 0 && ira_dump_file != NULL)
*************** ira (FILE *f)
*** 3215,3220 ****
--- 3216,3222 ----
  	  df_analyze ();
  
  	  flow_loops_find (&ira_loops);
+ 	  record_loop_exits ();
  	  current_loops = &ira_loops;
  
  	  setup_allocno_assignment_flags ();
Index: tree-inline.c
===================================================================
*** tree-inline.c	(revision 154719)
--- tree-inline.c	(working copy)
*************** expand_call_inline (basic_block bb, gimp
*** 3843,3852 ****
    (*debug_hooks->outlining_inline_function) (cg_edge->callee->decl);
  
    /* Update callgraph if needed.  */
-   if (cg_edge->callee->clone_of
-       && !cg_edge->callee->clone_of->next_sibling_clone
-       && !cg_edge->callee->analyzed)
-     cgraph_remove_node (cg_edge->callee);
    cgraph_remove_node (cg_edge->callee);
  
    id->block = NULL_TREE;
--- 3843,3848 ----
Index: config/i386/i386.c
===================================================================
*** config/i386/i386.c	(revision 154719)
--- config/i386/i386.c	(working copy)
*************** x86_output_mi_thunk (FILE *file ATTRIBUT
*** 26002,26008 ****
    /* Adjust the this parameter by a fixed constant.  */
    if (delta)
      {
!       xops[0] = GEN_INT (delta);
        xops[1] = this_reg ? this_reg : this_param;
        if (TARGET_64BIT)
  	{
--- 26002,26011 ----
    /* Adjust the this parameter by a fixed constant.  */
    if (delta)
      {
!       /* Make things pretty and `subl $4,%eax' rather than `addl $-4,%eax'.
!          Exceptions: -128 encodes smaller than 128, so swap sign and op.  */
!       bool sub = delta < 0 || delta == 128;
!       xops[0] = GEN_INT (sub ? -delta : delta);
        xops[1] = this_reg ? this_reg : this_param;
        if (TARGET_64BIT)
  	{
*************** x86_output_mi_thunk (FILE *file ATTRIBUT
*** 26014,26021 ****
  	      xops[0] = tmp;
  	      xops[1] = this_param;
  	    }
! 	  output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
  	}
        else
  	output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
      }
--- 26017,26029 ----
  	      xops[0] = tmp;
  	      xops[1] = this_param;
  	    }
! 	  if (sub)
! 	    output_asm_insn ("sub{q}\t{%0, %1|%1, %0}", xops);
! 	  else
! 	    output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
  	}
+       else if (sub)
+ 	output_asm_insn ("sub{l}\t{%0, %1|%1, %0}", xops);
        else
  	output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
      }
Index: langhooks-def.h
===================================================================
*** langhooks-def.h	(revision 154719)
--- langhooks-def.h	(working copy)
*************** extern void lhd_omp_firstprivatize_type_
*** 126,136 ****
  }
  
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
- #define LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS NULL
  
  #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
!   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR, \
!   LANG_HOOKS_CALLGRAPH_EMIT_ASSOCIATED_THUNKS, \
  }
  
  /* Hooks for tree gimplification.  */
--- 126,134 ----
  }
  
  #define LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR lhd_callgraph_analyze_expr
  
  #define LANG_HOOKS_CALLGRAPH_INITIALIZER { \
!   LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \
  }
  
  /* Hooks for tree gimplification.  */

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2009-11-29 10:35 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-21 19:52 [C++/cgraph] Middle-end thunk support Jan Hubicka
2009-11-22 18:09 ` Jan Hubicka
2009-11-23 15:47   ` Mark Mitchell
2009-11-23 15:55     ` Jan Hubicka
2009-11-23 16:02       ` Jan Hubicka
2009-11-23 16:04       ` Mark Mitchell
2009-11-23 16:14         ` Jan Hubicka
2009-11-24 17:05           ` Mark Mitchell
2009-11-24 20:45             ` Jan Hubicka
2009-11-25 14:00   ` Martin Jambor
2009-11-27 16:25     ` Jan Hubicka
2009-11-29 11:43       ` Jan Hubicka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).