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


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