OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / cgraphunit.c
index 999b238..ae30605 100644 (file)
@@ -1,6 +1,6 @@
 /* Callgraph based interprocedural optimizations.
    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-   2011 Free Software Foundation, Inc.
+   2011, 2012 Free Software Foundation, Inc.
    Contributed by Jan Hubicka
 
 This file is part of GCC.
@@ -195,6 +195,7 @@ cgraph_decide_is_function_needed (struct cgraph_node *node, tree decl)
      to change the behavior here.  */
   if (((TREE_PUBLIC (decl)
        || (!optimize
+           && !node->same_body_alias
            && !DECL_DISREGARD_INLINE_LIMITS (decl)
            && !DECL_DECLARED_INLINE_P (decl)
            && !(DECL_CONTEXT (decl)
@@ -426,7 +427,9 @@ verify_edge_count_and_frequency (struct cgraph_edge *e)
         Remove this once edges are actualy removed from the function at that time.  */
       && (e->frequency
          || (inline_edge_summary_vec
-             && !inline_edge_summary (e)->predicate))
+             && ((VEC_length(inline_edge_summary_t, inline_edge_summary_vec)
+                 <= (unsigned) e->uid)
+                 || !inline_edge_summary (e)->predicate)))
       && (e->frequency
          != compute_call_stmt_bb_frequency (e->caller->decl,
                                             gimple_bb (e->call_stmt))))
@@ -468,11 +471,17 @@ verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
     return false;
   node = cgraph_function_or_thunk_node (node, NULL);
 
-  if ((e->callee->former_clone_of != node->decl)
+  if ((e->callee->former_clone_of != node->decl
+       && (!node->same_body_alias
+          || e->callee->former_clone_of != node->thunk.alias))
       /* IPA-CP sometimes redirect edge to clone and then back to the former
-        function.  This ping-pong has to go, eventaully.  */
+        function.  This ping-pong has to go, eventually.  */
       && (node != cgraph_function_or_thunk_node (e->callee, NULL))
-      && !clone_of_p (node, e->callee))
+      && !clone_of_p (node, e->callee)
+      /* If decl is a same body alias of some other decl, allow e->callee to be
+        a clone of a clone of that other decl too.  */
+      && (!node->same_body_alias
+         || !clone_of_p (cgraph_get_node (node->thunk.alias), e->callee)))
     return true;
   else
     return false;
@@ -664,7 +673,7 @@ verify_cgraph_node (struct cgraph_node *node)
       for (i = 0; ipa_ref_list_reference_iterate (&node->ref_list, i, ref); i++)
        if (ref->use != IPA_REF_ALIAS)
          {
-           error ("Alias has non-alias refernece");
+           error ("Alias has non-alias reference");
            error_found = true;
          }
        else if (ref_found)
@@ -833,6 +842,16 @@ cgraph_analyze_function (struct cgraph_node *node)
   if (node->alias && node->thunk.alias)
     {
       struct cgraph_node *tgt = cgraph_get_node (node->thunk.alias);
+      struct cgraph_node *n;
+
+      for (n = tgt; n && n->alias;
+          n = n->analyzed ? cgraph_alias_aliased_node (n) : NULL)
+       if (n == node)
+         {
+           error ("function %q+D part of alias cycle", node->decl);
+           node->alias = false;
+           return;
+         }
       if (!VEC_length (ipa_ref_t, node->ref_list.references))
         ipa_record_reference (node, NULL, tgt, NULL, IPA_REF_ALIAS, NULL);
       if (node->same_body_alias)
@@ -1217,7 +1236,6 @@ handle_alias_pairs (void)
   for (i = 0; VEC_iterate (alias_pair, alias_pairs, i, p);)
     {
       if (TREE_CODE (p->decl) == FUNCTION_DECL
-          && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl))
          && (target_node = cgraph_node_for_asm (p->target)) != NULL)
        {
          src_node = cgraph_get_node (p->decl);
@@ -1229,12 +1247,12 @@ handle_alias_pairs (void)
             However for weakref we insist on EXTERNAL flag being set.
             See gcc.dg/attr-alias-5.c  */
          if (DECL_EXTERNAL (p->decl))
-           DECL_EXTERNAL (p->decl) = 0;
+           DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref",
+                                                       DECL_ATTRIBUTES (p->decl)) != NULL;
          cgraph_create_function_alias (p->decl, target_node->decl);
          VEC_unordered_remove (alias_pair, alias_pairs, i);
        }
       else if (TREE_CODE (p->decl) == VAR_DECL
-              && !lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl))
               && (target_vnode = varpool_node_for_asm (p->target)) != NULL)
        {
          /* Normally EXTERNAL flag is used to mark external inlines,
@@ -1243,10 +1261,26 @@ handle_alias_pairs (void)
             However for weakref we insist on EXTERNAL flag being set.
             See gcc.dg/attr-alias-5.c  */
          if (DECL_EXTERNAL (p->decl))
-           DECL_EXTERNAL (p->decl) = 0;
+           DECL_EXTERNAL (p->decl) = lookup_attribute ("weakref",
+                                                       DECL_ATTRIBUTES (p->decl)) != NULL;
          varpool_create_variable_alias (p->decl, target_vnode->decl);
          VEC_unordered_remove (alias_pair, alias_pairs, i);
        }
+      /* Weakrefs with target not defined in current unit are easy to handle; they
+        behave just as external variables except we need to note the alias flag
+        to later output the weakref pseudo op into asm file.  */
+      else if (lookup_attribute ("weakref", DECL_ATTRIBUTES (p->decl)) != NULL
+              && (TREE_CODE (p->decl) == FUNCTION_DECL
+                  ? (varpool_node_for_asm (p->target) == NULL)
+                  : (cgraph_node_for_asm (p->target) == NULL)))
+       {
+         if (TREE_CODE (p->decl) == FUNCTION_DECL)
+           cgraph_get_create_node (p->decl)->alias = true;
+         else
+           varpool_get_node (p->decl)->alias = true;
+         DECL_EXTERNAL (p->decl) = 1;
+         VEC_unordered_remove (alias_pair, alias_pairs, i);
+       }
       else
        {
          if (dump_file)
@@ -1402,14 +1436,16 @@ cgraph_mark_functions_to_output (void)
          tree decl = node->decl;
          if (!node->global.inlined_to
              && gimple_has_body_p (decl)
-             /* FIXME: in ltrans unit when offline copy is outside partition but inline copies
-                are inside partition, we can end up not removing the body since we no longer
-                have analyzed node pointing to it.  */
+             /* FIXME: in an ltrans unit when the offline copy is outside a
+                partition but inline copies are inside a partition, we can
+                end up not removing the body since we no longer have an
+                analyzed node pointing to it.  */
              && !node->in_other_partition
              && !DECL_EXTERNAL (decl))
            {
              dump_cgraph_node (stderr, node);
-             internal_error ("failed to reclaim unneeded functionin same comdat group");
+             internal_error ("failed to reclaim unneeded function in same "
+                             "comdat group");
            }
        }
 #endif
@@ -1677,7 +1713,6 @@ assemble_thunk (struct cgraph_node *node)
         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);
@@ -1796,7 +1831,6 @@ cgraph_expand_function (struct cgraph_node *node)
 
   announce_function (decl);
   node->process = 0;
-  assemble_thunks_and_aliases (node);
   gcc_assert (node->lowered);
 
   /* Generate RTL for the body of DECL.  */
@@ -1806,6 +1840,14 @@ cgraph_expand_function (struct cgraph_node *node)
   gcc_assert (TREE_ASM_WRITTEN (decl));
   current_function_decl = NULL;
   gcc_assert (!cgraph_preserve_function_body_p (node));
+
+  /* It would make a lot more sense to output thunks before function body to get more
+     forward and lest backwarding jumps.  This is however would need solving problem
+     with comdats. See PR48668.  Also aliases must come after function itself to
+     make one pass assemblers, like one on AIX happy.  See PR 50689.
+     FIXME: Perhaps thunks should be move before function IFF they are not in comdat
+     groups.  */
+  assemble_thunks_and_aliases (node);
   cgraph_release_function_body (node);
   /* Eliminate all call edges.  This is important so the GIMPLE_CALL no longer
      points to the dead function body.  */
@@ -2011,6 +2053,12 @@ ipa_passes (void)
        return;
     }
 
+  /* We never run removal of unreachable nodes after early passes.  This is
+     because TODO is run before the subpasses.  It is important to remove
+     the unreachable functions to save works at IPA level and to get LTO
+     symbol tables right.  */
+  cgraph_remove_unreachable_nodes (true, cgraph_dump_file);
+
   /* If pass_all_early_optimizations was not scheduled, the state of
      the cgraph will not be properly updated.  Update it now.  */
   if (cgraph_state < CGRAPH_STATE_IPA_SSA)
@@ -2050,6 +2098,42 @@ ipa_passes (void)
 }
 
 
+/* Return string alias is alias of.  */
+
+static tree
+get_alias_symbol (tree decl)
+{
+  tree alias = lookup_attribute ("alias", DECL_ATTRIBUTES (decl));
+  return get_identifier (TREE_STRING_POINTER
+                         (TREE_VALUE (TREE_VALUE (alias))));
+}
+
+
+/* Weakrefs may be associated to external decls and thus not output
+   at expansion time.  Emit all neccesary aliases.  */
+
+static void
+output_weakrefs (void)
+{
+  struct cgraph_node *node;
+  struct varpool_node *vnode;
+  for (node = cgraph_nodes; node; node = node->next)
+    if (node->alias && DECL_EXTERNAL (node->decl)
+        && !TREE_ASM_WRITTEN (node->decl)
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->decl)))
+      assemble_alias (node->decl,
+                     node->thunk.alias ? DECL_ASSEMBLER_NAME (node->thunk.alias)
+                     : get_alias_symbol (node->decl));
+  for (vnode = varpool_nodes; vnode; vnode = vnode->next)
+    if (vnode->alias && DECL_EXTERNAL (vnode->decl)
+        && !TREE_ASM_WRITTEN (vnode->decl)
+       && lookup_attribute ("weakref", DECL_ATTRIBUTES (vnode->decl)))
+      assemble_alias (vnode->decl,
+                     vnode->alias_of ? DECL_ASSEMBLER_NAME (vnode->alias_of)
+                     : get_alias_symbol (vnode->decl));
+}
+
+
 /* Perform simple optimizations based on callgraph.  */
 
 void
@@ -2122,6 +2206,7 @@ cgraph_optimize (void)
 #endif
   bitmap_obstack_release (NULL);
   cgraph_mark_functions_to_output ();
+  output_weakrefs ();
 
   cgraph_state = CGRAPH_STATE_EXPANSION;
   if (!flag_toplevel_reorder)
@@ -2135,6 +2220,7 @@ cgraph_optimize (void)
 
       varpool_assemble_pending_decls ();
     }
+
   cgraph_process_new_functions ();
   cgraph_state = CGRAPH_STATE_FINISHED;
 
@@ -2206,7 +2292,7 @@ update_call_expr (struct cgraph_node *new_version)
    was copied to prevent duplications of calls that are dead
    in the clone.  */
 
-static struct cgraph_node *
+struct cgraph_node *
 cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
                                 tree new_decl,
                                 VEC(cgraph_edge_p,heap) *redirect_callers,
@@ -2220,7 +2306,7 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
 
    new_version = cgraph_create_node (new_decl);
 
-   new_version->analyzed = true;
+   new_version->analyzed = old_version->analyzed;
    new_version->local = old_version->local;
    new_version->local.externally_visible = false;
    new_version->local.local = true;
@@ -2250,6 +2336,8 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
        cgraph_redirect_edge_callee (e, new_version);
      }
 
+   cgraph_call_node_duplication_hooks (old_version, new_version);
+
    return new_version;
  }
 
@@ -2264,17 +2352,21 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
     TREE_MAP is a mapping of tree nodes we want to replace with
     new ones (according to results of prior analysis).
     OLD_VERSION_NODE is the node that is versioned.
-    It returns the new version's cgraph node.
+
     If non-NULL ARGS_TO_SKIP determine function parameters to remove
     from new version.
+    If SKIP_RETURN is true, the new version will return void.
     If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
-    If non_NULL NEW_ENTRY determine new entry BB of the clone.  */
+    If non_NULL NEW_ENTRY determine new entry BB of the clone.
+
+    Return the new version's cgraph node.  */
 
 struct cgraph_node *
 cgraph_function_versioning (struct cgraph_node *old_version_node,
                            VEC(cgraph_edge_p,heap) *redirect_callers,
                            VEC (ipa_replace_map_p,gc)* tree_map,
                            bitmap args_to_skip,
+                           bool skip_return,
                            bitmap bbs_to_copy,
                            basic_block new_entry_block,
                            const char *clone_name)
@@ -2288,18 +2380,22 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
 
   gcc_assert (old_version_node->local.can_change_signature || !args_to_skip);
 
-  /* Make a new FUNCTION_DECL tree node for the
-     new version. */
-  if (!args_to_skip)
+  /* Make a new FUNCTION_DECL tree node for the new version. */
+  if (!args_to_skip && !skip_return)
     new_decl = copy_node (old_decl);
   else
-    new_decl = build_function_decl_skip_args (old_decl, args_to_skip);
+    new_decl
+      = build_function_decl_skip_args (old_decl, args_to_skip, skip_return);
 
   /* Generate a new name for the new version. */
   DECL_NAME (new_decl) = clone_function_name (old_decl, clone_name);
   SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
   SET_DECL_RTL (new_decl, NULL);
 
+  /* When the old decl was a con-/destructor make sure the clone isn't.  */
+  DECL_STATIC_CONSTRUCTOR(new_decl) = 0;
+  DECL_STATIC_DESTRUCTOR(new_decl) = 0;
+
   /* Create the new version's call-graph node.
      and update the edges of the new node. */
   new_version_node =
@@ -2308,7 +2404,7 @@ cgraph_function_versioning (struct cgraph_node *old_version_node,
 
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
   tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
-                           bbs_to_copy, new_entry_block);
+                           skip_return, bbs_to_copy, new_entry_block);
 
   /* Update the new version's properties.
      Make The new version visible only within this translation unit.  Make sure
@@ -2339,7 +2435,8 @@ cgraph_materialize_clone (struct cgraph_node *node)
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
   tree_function_versioning (node->clone_of->decl, node->decl,
                            node->clone.tree_map, true,
-                           node->clone.args_to_skip, NULL, NULL);
+                           node->clone.args_to_skip, false,
+                           NULL, NULL);
   if (cgraph_dump_file)
     {
       dump_function_to_file (node->clone_of->decl, cgraph_dump_file, dump_flags);
@@ -2393,8 +2490,8 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
   if (cgraph_dump_file)
     {
       fprintf (cgraph_dump_file, "updating call of %s/%i -> %s/%i: ",
-              cgraph_node_name (e->caller), e->caller->uid,
-              cgraph_node_name (e->callee), e->callee->uid);
+              xstrdup (cgraph_node_name (e->caller)), e->caller->uid,
+              xstrdup (cgraph_node_name (e->callee)), e->callee->uid);
       print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags);
       if (e->callee->clone.combined_args_to_skip)
        {
@@ -2480,8 +2577,8 @@ cgraph_materialize_all_clones (void)
                  if (cgraph_dump_file)
                    {
                      fprintf (cgraph_dump_file, "cloning %s to %s\n",
-                              cgraph_node_name (node->clone_of),
-                              cgraph_node_name (node));
+                              xstrdup (cgraph_node_name (node->clone_of)),
+                              xstrdup (cgraph_node_name (node)));
                      if (node->clone.tree_map)
                        {
                          unsigned int i;
@@ -2494,9 +2591,11 @@ cgraph_materialize_all_clones (void)
                              replace_info = VEC_index (ipa_replace_map_p,
                                                        node->clone.tree_map,
                                                        i);
-                             print_generic_expr (cgraph_dump_file, replace_info->old_tree, 0);
+                             print_generic_expr (cgraph_dump_file,
+                                                 replace_info->old_tree, 0);
                              fprintf (cgraph_dump_file, " -> ");
-                             print_generic_expr (cgraph_dump_file, replace_info->new_tree, 0);
+                             print_generic_expr (cgraph_dump_file,
+                                                 replace_info->new_tree, 0);
                              fprintf (cgraph_dump_file, "%s%s;",
                                       replace_info->replace_p ? "(replace)":"",
                                       replace_info->ref_p ? "(ref)":"");
@@ -2506,12 +2605,15 @@ cgraph_materialize_all_clones (void)
                      if (node->clone.args_to_skip)
                        {
                          fprintf (cgraph_dump_file, "   args_to_skip: ");
-                         dump_bitmap (cgraph_dump_file, node->clone.args_to_skip);
+                         dump_bitmap (cgraph_dump_file,
+                                      node->clone.args_to_skip);
                        }
                      if (node->clone.args_to_skip)
                        {
-                         fprintf (cgraph_dump_file, "   combined_args_to_skip:");
-                         dump_bitmap (cgraph_dump_file, node->clone.combined_args_to_skip);
+                         fprintf (cgraph_dump_file,
+                                  "   combined_args_to_skip:");
+                         dump_bitmap (cgraph_dump_file,
+                                      node->clone.combined_args_to_skip);
                        }
                    }
                  cgraph_materialize_clone (node);