This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [C++/cgraph] Middle-end thunk support


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.  */


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