OSDN Git Service

* cgraph.c (same_body_alias_1): Break out of
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 29 Nov 2009 10:32:08 +0000 (10:32 +0000)
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 29 Nov 2009 10:32:08 +0000 (10:32 +0000)
(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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@154736 138bc75d-0d04-0410-961f-82ee72b054a4

16 files changed:
gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphunit.c
gcc/config/i386/i386.c
gcc/cp/ChangeLog
gcc/cp/Make-lang.in
gcc/cp/cp-objcp-common.h
gcc/cp/method.c
gcc/cp/optimize.c
gcc/cp/semantics.c
gcc/ira.c
gcc/langhooks-def.h
gcc/langhooks.h
gcc/lto-cgraph.c
gcc/tree-inline.c

index e68cf95..aedf2ae 100644 (file)
@@ -1,3 +1,27 @@
+2009-11-28  Jan Hubicka  <jh@suse.cz>
+
+       * 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.
+       * 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.
+
 2009-11-28  Richard Guenther  <rguenther@suse.de>
 
        PR tree-optimization/42183
index 8fd5e7f..651618c 100644 (file)
@@ -85,6 +85,7 @@ The callgraph:
 #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);
@@ -507,29 +508,15 @@ cgraph_node (tree decl)
   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).  */
+/* Mark ALIAS as an alias to DECL.  */
 
-bool
-cgraph_same_body_alias (tree alias, tree 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);
-  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;
@@ -538,7 +525,7 @@ cgraph_same_body_alias (tree alias, tree decl)
 
   /* If the cgraph_node has been already created, fail.  */
   if (*slot)
-    return false;
+    return NULL;
 
   alias_node = cgraph_allocate_node ();
   alias_node->decl = alias;
@@ -548,9 +535,56 @@ cgraph_same_body_alias (tree alias, tree decl)
   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 true;
+  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
@@ -1646,6 +1680,27 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
        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 a683c5b..1017176 100644 (file)
@@ -69,6 +69,19 @@ struct GTY(()) inline_summary
   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.  */
 
@@ -184,8 +197,8 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node {
   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.  */
+  /* 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.  */
@@ -202,6 +215,7 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node {
   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;
@@ -244,8 +258,8 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%h.previous"))) cgraph_node {
   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.  */
+  /* 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;
 };
 
@@ -423,6 +437,7 @@ struct cgraph_edge *cgraph_create_edge (struct cgraph_node *,
 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 4a4e6a4..e382543 100644 (file)
@@ -149,6 +149,9 @@ static GTY (()) VEC(tree, gc) *static_ctors;
 /* 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.
@@ -741,8 +744,13 @@ verify_cgraph_node (struct cgraph_node *node)
                            debug_gimple_stmt (stmt);
                            error_found = true;
                          }
-                       if (!clone_of_p (cgraph_node (decl), e->callee)
-                           && !e->callee->global.inlined_to)
+                       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);
@@ -1034,35 +1042,6 @@ cgraph_analyze_functions (void)
 }
 
 
-/* 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
@@ -1093,10 +1072,6 @@ cgraph_finalize_compilation_unit (void)
      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 ();
 
@@ -1159,6 +1134,347 @@ cgraph_mark_functions_to_output (void)
     }
 }
 
+/* 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
@@ -1182,10 +1498,22 @@ cgraph_expand_function (struct cgraph_node *node)
   current_function_decl = NULL;
   if (node->same_body)
     {
-      struct cgraph_node *alias;
+      struct cgraph_node *alias, *next;
       bool saved_alias = node->alias;
-      for (alias = node->same_body; alias; alias = alias->next)
-       assemble_alias (alias->decl, DECL_ASSEMBLER_NAME (decl));
+      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 443b4eb..37fe24f 100644 (file)
@@ -26002,7 +26002,10 @@ x86_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
   /* Adjust the this parameter by a fixed constant.  */
   if (delta)
     {
-      xops[0] = GEN_INT (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)
        {
@@ -26014,8 +26017,13 @@ x86_output_mi_thunk (FILE *file ATTRIBUTE_UNUSED,
              xops[0] = tmp;
              xops[1] = this_param;
            }
-         output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
+         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 4f58453..ab252ea 100644 (file)
@@ -1,3 +1,13 @@
+2009-11-29  Jan Hubicka  <jh@suse.cz>
+
+       * 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.
+
 2009-11-28  Dodji Seketeli  <dodji@redhat.com>
 
        PR c++/36408
index 6e10296..ef44201 100644 (file)
@@ -275,7 +275,7 @@ cp/friend.o: cp/friend.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) $(RTL_H) toplev.h \
 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
+  $(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 8f0cdc3..1a97ad2 100644 (file)
@@ -104,8 +104,6 @@ extern bool cp_function_decl_explicit_p (tree decl);
 
 #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
index 47f9e42..cda7ed3 100644 (file)
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "diagnostic.h"
 #include "cgraph.h"
+#include "gimple.h"
 
 /* Various flags to control the mangling process.  */
 
@@ -58,7 +59,6 @@ enum mangling_flags
 
 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 *);
@@ -205,56 +205,6 @@ finish_thunk (tree thunk)
   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.  */
@@ -303,7 +253,11 @@ make_alias_for_thunk (tree function)
   alias = make_alias_for (function, get_identifier (buf));
 
   if (!flag_syntax_only)
-    assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
+    {
+      bool ok = cgraph_same_body_alias (alias, function);
+      DECL_ASSEMBLER_NAME (function);
+      gcc_assert (ok);
+    }
 
   return alias;
 }
@@ -416,42 +370,15 @@ use_thunk (tree thunk_fndecl, bool emit_p)
     }
   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
+  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))
     {
-      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
@@ -460,69 +387,6 @@ use_thunk (tree thunk_fndecl, bool emit_p)
       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 ();
index c691f0b..838a730 100644 (file)
@@ -250,7 +250,10 @@ maybe_clone_body (tree fn)
          && DECL_INTERFACE_KNOWN (fns[0])
          && !DECL_ONE_ONLY (fns[0])
          && cgraph_same_body_alias (clone, fns[0]))
-       alias = true;
+       {
+         alias = true;
+         emit_associated_thunks (clone);
+       }
 
       /* Build the delete destructor by calling complete destructor
          and delete function.  */
index 5253df8..0a1a547 100644 (file)
@@ -3445,6 +3445,7 @@ expand_or_defer_fn (tree fn)
 
       /* 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 7165caa..e5a6171 100644 (file)
--- a/gcc/ira.c
+++ b/gcc/ira.c
@@ -3172,6 +3172,7 @@ ira (FILE *f)
 
   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)
@@ -3215,6 +3216,7 @@ ira (FILE *f)
          df_analyze ();
 
          flow_loops_find (&ira_loops);
+         record_loop_exits ();
          current_loops = &ira_loops;
 
          setup_allocno_assignment_flags ();
index 4f01483..f6ef3f1 100644 (file)
@@ -126,11 +126,9 @@ extern void lhd_omp_firstprivatize_type_sizes (struct gimplify_omp_ctx *,
 }
 
 #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, \
+  LANG_HOOKS_CALLGRAPH_ANALYZE_EXPR \
 }
 
 /* Hooks for tree gimplification.  */
index 81f2436..faac14a 100644 (file)
@@ -49,9 +49,6 @@ struct lang_hooks_for_callgraph
   /* 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.  */
index 0f77df1..309a1e6 100644 (file)
@@ -317,6 +317,21 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node,
        {
          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);
@@ -575,9 +590,24 @@ input_node (struct lto_file_decl_data *file_data,
   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);
-      cgraph_same_body_alias (alias_decl, fn_decl);
+      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 a70b5b1..10baf62 100644 (file)
@@ -3832,10 +3832,6 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id)
   (*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;