OSDN Git Service

don't use TYPE_ARG_TYPES in the Ada frontend
[pf3gnuchains/gcc-fork.git] / gcc / cgraph.c
index 89e431a..91805e3 100644 (file)
@@ -94,9 +94,24 @@ The callgraph:
 #include "tree-flow.h"
 #include "value-prof.h"
 #include "except.h"
-#include "diagnostic.h"
+#include "diagnostic-core.h"
 #include "rtl.h"
 #include "ipa-utils.h"
+#include "lto-streamer.h"
+#include "ipa-inline.h"
+
+const char * const ld_plugin_symbol_resolution_names[]=
+{
+  "",
+  "undef",
+  "prevailing_def",
+  "prevailing_def_ironly",
+  "preempted_reg",
+  "preempted_ir",
+  "resolved_ir",
+  "resolved_exec",
+  "resolved_dyn"
+};
 
 static void cgraph_node_remove_callers (struct cgraph_node *node);
 static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
@@ -127,9 +142,6 @@ int cgraph_max_uid;
 /* Maximal uid used in cgraph edges.  */
 int cgraph_edge_max_uid;
 
-/* Maximal pid used for profiling */
-int cgraph_max_pid;
-
 /* Set when whole unit has been analyzed so we can access global info.  */
 bool cgraph_global_info_ready = false;
 
@@ -150,28 +162,28 @@ static GTY(()) struct cgraph_asm_node *cgraph_asm_last_node;
    them, to support -fno-toplevel-reorder.  */
 int cgraph_order;
 
-/* List of hooks trigerred on cgraph_edge events.  */
+/* List of hooks triggered on cgraph_edge events.  */
 struct cgraph_edge_hook_list {
   cgraph_edge_hook hook;
   void *data;
   struct cgraph_edge_hook_list *next;
 };
 
-/* List of hooks trigerred on cgraph_node events.  */
+/* List of hooks triggered on cgraph_node events.  */
 struct cgraph_node_hook_list {
   cgraph_node_hook hook;
   void *data;
   struct cgraph_node_hook_list *next;
 };
 
-/* List of hooks trigerred on events involving two cgraph_edges.  */
+/* List of hooks triggered on events involving two cgraph_edges.  */
 struct cgraph_2edge_hook_list {
   cgraph_2edge_hook hook;
   void *data;
   struct cgraph_2edge_hook_list *next;
 };
 
-/* List of hooks trigerred on events involving two cgraph_nodes.  */
+/* List of hooks triggered on events involving two cgraph_nodes.  */
 struct cgraph_2node_hook_list {
   cgraph_2node_hook hook;
   void *data;
@@ -442,7 +454,7 @@ cgraph_allocate_node (void)
     }
   else
     {
-      node = GGC_CNEW (struct cgraph_node);
+      node = ggc_alloc_cleared_cgraph_node ();
       node->uid = cgraph_max_uid++;
     }
 
@@ -452,18 +464,17 @@ cgraph_allocate_node (void)
 /* Allocate new callgraph node and insert it into basic data structures.  */
 
 static struct cgraph_node *
-cgraph_create_node (void)
+cgraph_create_node_1 (void)
 {
   struct cgraph_node *node = cgraph_allocate_node ();
 
   node->next = cgraph_nodes;
-  node->pid = -1;
   node->order = cgraph_order++;
   if (cgraph_nodes)
     cgraph_nodes->previous = node;
   node->previous = NULL;
-  node->global.estimated_growth = INT_MIN;
   node->frequency = NODE_FREQUENCY_NORMAL;
+  node->count_materialization_scale = REG_BR_PROB_BASE;
   ipa_empty_ref_list (&node->ref_list);
   cgraph_nodes = node;
   cgraph_n_nodes++;
@@ -473,7 +484,7 @@ cgraph_create_node (void)
 /* Return cgraph node assigned to DECL.  Create new one when needed.  */
 
 struct cgraph_node *
-cgraph_node (tree decl)
+cgraph_create_node (tree decl)
 {
   struct cgraph_node key, *node, **slot;
 
@@ -483,23 +494,15 @@ cgraph_node (tree decl)
     cgraph_hash = htab_create_ggc (10, hash_node, eq_node, NULL);
 
   key.decl = decl;
-
   slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
+  gcc_assert (!*slot);
 
-  if (*slot)
-    {
-      node = *slot;
-      if (node->same_body_alias)
-       node = node->same_body;
-      return node;
-    }
-
-  node = cgraph_create_node ();
+  node = cgraph_create_node_1 ();
   node->decl = decl;
   *slot = node;
   if (DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
     {
-      node->origin = cgraph_node (DECL_CONTEXT (decl));
+      node->origin = cgraph_get_create_node (DECL_CONTEXT (decl));
       node->next_nested = node->origin->nested;
       node->origin->nested = node;
     }
@@ -521,16 +524,31 @@ cgraph_node (tree decl)
   return node;
 }
 
-/* Mark ALIAS as an alias to DECL.  */
+/* Try to find a call graph node for declaration DECL and if it does not exist,
+   create it.  */
+
+struct cgraph_node *
+cgraph_get_create_node (tree decl)
+{
+  struct cgraph_node *node;
+
+  node = cgraph_get_node (decl);
+  if (node)
+    return node;
+
+  return cgraph_create_node (decl);
+}
+
+/* Mark ALIAS as an alias to DECL.  DECL_NODE is cgraph node representing
+   the function body is associated with (not neccesarily cgraph_node (DECL).  */
 
 static struct cgraph_node *
-cgraph_same_body_alias_1 (tree alias, tree decl)
+cgraph_same_body_alias_1 (struct cgraph_node *decl_node, tree alias, tree decl)
 {
-  struct cgraph_node key, *alias_node, *decl_node, **slot;
+  struct cgraph_node key, *alias_node, **slot;
 
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
   gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
-  decl_node = cgraph_node (decl);
 
   key.decl = alias;
 
@@ -554,25 +572,31 @@ cgraph_same_body_alias_1 (tree alias, tree decl)
   return alias_node;
 }
 
-/* Attempt to mark ALIAS as an alias to DECL.  Return TRUE if successful.
+/* Attempt to mark ALIAS as an alias to DECL.  Return alias node if successful
+   and NULL otherwise.
    Same body aliases are output whenever the body of DECL is output,
-   and cgraph_node (ALIAS) transparently returns cgraph_node (DECL).   */
+   and cgraph_get_node (ALIAS) transparently returns cgraph_get_node (DECL).  */
 
-bool
-cgraph_same_body_alias (tree alias, tree decl)
+struct cgraph_node *
+cgraph_same_body_alias (struct cgraph_node *decl_node, tree alias, tree decl)
 {
 #ifndef ASM_OUTPUT_DEF
   /* If aliases aren't supported by the assembler, fail.  */
-  return false;
+  return NULL;
 #endif
 
   /*gcc_assert (!assembler_name_hash);*/
 
-  return cgraph_same_body_alias_1 (alias, decl) != NULL;
+  return cgraph_same_body_alias_1 (decl_node, alias, decl);
 }
 
-void
-cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
+/* Add thunk alias into callgraph.  The alias declaration is ALIAS and it
+   aliases DECL with an adjustments made into the first parameter.
+   See comments in thunk_adjust for detail on the parameters.  */
+
+struct cgraph_node *
+cgraph_add_thunk (struct cgraph_node *decl_node, tree alias, tree decl,
+                 bool this_adjusting,
                  HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
                  tree virtual_offset,
                  tree real_alias)
@@ -586,25 +610,26 @@ cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
       cgraph_remove_node (node);
     }
   
-  node = cgraph_same_body_alias_1 (alias, decl);
+  node = cgraph_same_body_alias_1 (decl_node, alias, decl);
   gcc_assert (node);
-#ifdef ENABLE_CHECKING
-  gcc_assert (!virtual_offset
-             || tree_int_cst_equal (virtual_offset, size_int (virtual_value)));
-#endif
+  gcc_checking_assert (!virtual_offset
+                      || double_int_equal_p
+                           (tree_to_double_int (virtual_offset),
+                            shwi_to_double_int (virtual_value)));
   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;
+  return node;
 }
 
 /* Returns the cgraph node assigned to DECL or NULL if no cgraph node
    is assigned.  */
 
 struct cgraph_node *
-cgraph_get_node (tree decl)
+cgraph_get_node_or_alias (const_tree decl)
 {
   struct cgraph_node key, *node = NULL, **slot;
 
@@ -613,7 +638,30 @@ cgraph_get_node (tree decl)
   if (!cgraph_hash)
     return NULL;
 
-  key.decl = decl;
+  key.decl = CONST_CAST2 (tree, const_tree, decl);
+
+  slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key,
+                                                NO_INSERT);
+
+  if (slot && *slot)
+    node = *slot;
+  return node;
+}
+
+/* Returns the cgraph node assigned to DECL or NULL if no cgraph node
+   is assigned.  */
+
+struct cgraph_node *
+cgraph_get_node (const_tree decl)
+{
+  struct cgraph_node key, *node = NULL, **slot;
+
+  gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+  if (!cgraph_hash)
+    return NULL;
+
+  key.decl = CONST_CAST2 (tree, const_tree, decl);
 
   slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key,
                                                 NO_INSERT);
@@ -815,9 +863,10 @@ cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
     {
       /* Constant propagation (and possibly also inlining?) can turn an
         indirect call into a direct one.  */
-      struct cgraph_node *new_callee = cgraph_node (decl);
+      struct cgraph_node *new_callee = cgraph_get_node (decl);
 
-      cgraph_make_edge_direct (e, new_callee);
+      gcc_checking_assert (new_callee);
+      cgraph_make_edge_direct (e, new_callee, 0);
     }
 
   push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
@@ -873,7 +922,7 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
                                     struct cgraph_node *callee,
                                     gimple old_stmt,
                                     gimple stmt, gcov_type count,
-                                    int freq, int loop_depth,
+                                    int freq,
                                     cgraph_inline_failed_t reason)
 {
   struct cgraph_node *node;
@@ -881,7 +930,7 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
 
   if (!cgraph_edge (orig, stmt))
     {
-      edge = cgraph_create_edge (orig, callee, stmt, count, freq, loop_depth);
+      edge = cgraph_create_edge (orig, callee, stmt, count, freq);
       edge->inline_failed = reason;
     }
 
@@ -894,13 +943,13 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
         /* It is possible that clones already contain the edge while
           master didn't.  Either we promoted indirect call into direct
           call in the clone or we are processing clones of unreachable
-          master where edges has been rmeoved.  */
+          master where edges has been removed.  */
        if (edge)
          cgraph_set_call_stmt (edge, stmt);
        else if (!cgraph_edge (node, stmt))
          {
            edge = cgraph_create_edge (node, callee, stmt, count,
-                                      freq, loop_depth);
+                                      freq);
            edge->inline_failed = reason;
          }
 
@@ -918,35 +967,13 @@ cgraph_create_edge_including_clones (struct cgraph_node *orig,
       }
 }
 
-/* Give initial reasons why inlining would fail on EDGE.  This gets either
-   nullified or usually overwritten by more precise reasons later.  */
-
-static void
-initialize_inline_failed (struct cgraph_edge *e)
-{
-  struct cgraph_node *callee = e->callee;
-
-  if (e->indirect_unknown_callee)
-    e->inline_failed = CIF_INDIRECT_UNKNOWN_CALL;
-  else if (!callee->analyzed)
-    e->inline_failed = CIF_BODY_NOT_AVAILABLE;
-  else if (callee->local.redefined_extern_inline)
-    e->inline_failed = CIF_REDEFINED_EXTERN_INLINE;
-  else if (!callee->local.inlinable)
-    e->inline_failed = CIF_FUNCTION_NOT_INLINABLE;
-  else if (e->call_stmt && gimple_call_cannot_inline_p (e->call_stmt))
-    e->inline_failed = CIF_MISMATCHED_ARGUMENTS;
-  else
-    e->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
-}
-
 /* Allocate a cgraph_edge structure and fill it with data according to the
    parameters of which only CALLEE can be NULL (when creating an indirect call
    edge).  */
 
 static struct cgraph_edge *
 cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
-                      gimple call_stmt, gcov_type count, int freq, int nest)
+                      gimple call_stmt, gcov_type count, int freq)
 {
   struct cgraph_edge *edge;
 
@@ -954,11 +981,9 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
      have not been loaded yet.  */
   if (call_stmt)
     {
-#ifdef ENABLE_CHECKING
-      /* This is rather pricely check possibly trigerring construction of
-        call stmt hashtable.  */
-      gcc_assert (!cgraph_edge (caller, call_stmt));
-#endif
+      /* This is a rather expensive check possibly triggering
+        construction of call stmt hashtable.  */
+      gcc_checking_assert (!cgraph_edge (caller, call_stmt));
 
       gcc_assert (is_gimple_call (call_stmt));
     }
@@ -970,7 +995,7 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
     }
   else
     {
-      edge = GGC_NEW (struct cgraph_edge);
+      edge = ggc_alloc_cgraph_edge ();
       edge->uid = cgraph_edge_max_uid++;
     }
 
@@ -987,7 +1012,6 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
   edge->frequency = freq;
   gcc_assert (freq >= 0);
   gcc_assert (freq <= CGRAPH_FREQ_MAX);
-  edge->loop_nest = nest;
 
   edge->call_stmt = call_stmt;
   push_cfun (DECL_STRUCT_FUNCTION (caller->decl));
@@ -1009,10 +1033,10 @@ cgraph_create_edge_1 (struct cgraph_node *caller, struct cgraph_node *callee,
 
 struct cgraph_edge *
 cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
-                   gimple call_stmt, gcov_type count, int freq, int nest)
+                   gimple call_stmt, gcov_type count, int freq)
 {
   struct cgraph_edge *edge = cgraph_create_edge_1 (caller, callee, call_stmt,
-                                                  count, freq, nest);
+                                                  count, freq);
 
   edge->indirect_unknown_callee = 0;
   initialize_inline_failed (edge);
@@ -1029,6 +1053,17 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
   return edge;
 }
 
+/* Allocate cgraph_indirect_call_info and set its fields to default values. */
+
+struct cgraph_indirect_call_info *
+cgraph_allocate_init_indirect_info (void)
+{
+  struct cgraph_indirect_call_info *ii;
+
+  ii = ggc_alloc_cleared_cgraph_indirect_call_info ();
+  ii->param_index = -1;
+  return ii;
+}
 
 /* Create an indirect edge with a yet-undetermined callee where the call
    statement destination is a formal parameter of the caller with index
@@ -1037,16 +1072,15 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
 struct cgraph_edge *
 cgraph_create_indirect_edge (struct cgraph_node *caller, gimple call_stmt,
                             int ecf_flags,
-                            gcov_type count, int freq, int nest)
+                            gcov_type count, int freq)
 {
   struct cgraph_edge *edge = cgraph_create_edge_1 (caller, NULL, call_stmt,
-                                                  count, freq, nest);
+                                                  count, freq);
 
   edge->indirect_unknown_callee = 1;
   initialize_inline_failed (edge);
 
-  edge->indirect_info = GGC_CNEW (struct cgraph_indirect_call_info);
-  edge->indirect_info->param_index = -1;
+  edge->indirect_info = cgraph_allocate_init_indirect_info ();
   edge->indirect_info->ecf_flags = ecf_flags;
 
   edge->next_callee = caller->indirect_calls;
@@ -1154,12 +1188,15 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
 }
 
 /* Make an indirect EDGE with an unknown callee an ordinary edge leading to
-   CALLEE.  */
+   CALLEE.  DELTA is an integer constant that is to be added to the this
+   pointer (first parameter) to compensate for skipping a thunk adjustment.  */
 
 void
-cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee)
+cgraph_make_edge_direct (struct cgraph_edge *edge, struct cgraph_node *callee,
+                        HOST_WIDE_INT delta)
 {
   edge->indirect_unknown_callee = 0;
+  edge->indirect_info->thunk_delta = delta;
 
   /* Get the edge out of the indirect edge list. */
   if (edge->prev_callee)
@@ -1198,29 +1235,36 @@ cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node,
   if (!new_call && !old_call)
     return;
   /* See if we turned indirect call into direct call or folded call to one builtin
-     into different bultin.  */
+     into different builtin.  */
   if (old_call != new_call)
     {
       struct cgraph_edge *e = cgraph_edge (node, old_stmt);
       struct cgraph_edge *ne = NULL;
       gcov_type count;
       int frequency;
-      int loop_nest;
 
       if (e)
        {
          /* See if the edge is already there and has the correct callee.  It
             might be so because of indirect inlining has already updated
-            it.  */
-         if (new_call && e->callee && e->callee->decl == new_call)
-           return;
+            it.  We also might've cloned and redirected the edge.  */
+         if (new_call && e->callee)
+           {
+             struct cgraph_node *callee = e->callee;
+             while (callee)
+               {
+                 if (callee->decl == new_call
+                     || callee->former_clone_of == new_call)
+                   return;
+                 callee = callee->clone_of;
+               }
+           }
 
          /* Otherwise remove edge and create new one; we can't simply redirect
             since function has changed, so inline plan and other information
             attached to edge is invalid.  */
          count = e->count;
          frequency = e->frequency;
-         loop_nest = e->loop_nest;
          cgraph_remove_edge (e);
        }
       else
@@ -1230,14 +1274,12 @@ cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node,
          count = bb->count;
          frequency = compute_call_stmt_bb_frequency (current_function_decl,
                                                      bb);
-         loop_nest = bb->loop_depth;
        }
 
       if (new_call)
        {
-         ne = cgraph_create_edge (node, cgraph_node (new_call),
-                                  new_stmt, count, frequency,
-                                  loop_nest);
+         ne = cgraph_create_edge (node, cgraph_get_create_node (new_call),
+                                  new_stmt, count, frequency);
          gcc_assert (ne->inline_failed);
        }
     }
@@ -1253,9 +1295,10 @@ cgraph_update_edges_for_call_stmt_node (struct cgraph_node *node,
 void
 cgraph_update_edges_for_call_stmt (gimple old_stmt, tree old_decl, gimple new_stmt)
 {
-  struct cgraph_node *orig = cgraph_node (cfun->decl);
+  struct cgraph_node *orig = cgraph_get_node (cfun->decl);
   struct cgraph_node *node;
 
+  gcc_checking_assert (orig);
   cgraph_update_edges_for_call_stmt_node (orig, old_stmt, old_decl, new_stmt);
   if (orig->clones)
     for (node = orig->clones; node != orig;)
@@ -1659,7 +1702,8 @@ cgraph_mark_reachable_node (struct cgraph_node *node)
          /* Verify that function does not appear to be needed out of blue
             during the optimization process.  This can happen for extern
             inlines when bodies was removed after inlining.  */
-         gcc_assert ((node->analyzed || DECL_EXTERNAL (node->decl)));
+         gcc_assert ((node->analyzed || node->in_other_partition
+                      || DECL_EXTERNAL (node->decl)));
        }
       else
         notice_global_symbol (node->decl);
@@ -1686,6 +1730,7 @@ cgraph_mark_needed_node (struct cgraph_node *node)
 void
 cgraph_mark_address_taken_node (struct cgraph_node *node)
 {
+  gcc_assert (!node->global.inlined_to);
   cgraph_mark_reachable_node (node);
   node->address_taken = 1;
 }
@@ -1698,7 +1743,9 @@ cgraph_local_info (tree decl)
   struct cgraph_node *node;
 
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
-  node = cgraph_node (decl);
+  node = cgraph_get_node (decl);
+  if (!node)
+    return NULL;
   return &node->local;
 }
 
@@ -1710,7 +1757,9 @@ cgraph_global_info (tree decl)
   struct cgraph_node *node;
 
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL && cgraph_global_info_ready);
-  node = cgraph_node (decl);
+  node = cgraph_get_node (decl);
+  if (!node)
+    return NULL;
   return &node->global;
 }
 
@@ -1722,9 +1771,10 @@ cgraph_rtl_info (tree decl)
   struct cgraph_node *node;
 
   gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
-  node = cgraph_node (decl);
-  if (decl != current_function_decl
-      && !TREE_ASM_WRITTEN (node->decl))
+  node = cgraph_get_node (decl);
+  if (!node
+      || (decl != current_function_decl
+         && !TREE_ASM_WRITTEN (node->decl)))
     return NULL;
   return &node->rtl;
 }
@@ -1767,8 +1817,7 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
   struct cgraph_edge *edge;
   int indirect_calls_count = 0;
 
-  fprintf (f, "%s/%i(%i)", cgraph_node_name (node), node->uid,
-          node->pid);
+  fprintf (f, "%s/%i", cgraph_node_name (node), node->uid);
   dump_addr (f, " @", (void *)node);
   if (DECL_ASSEMBLER_NAME_SET_P (node->decl))
     fprintf (f, " (asm: %s)", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (node->decl)));
@@ -1776,6 +1825,10 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
     fprintf (f, " (inline copy in %s/%i)",
             cgraph_node_name (node->global.inlined_to),
             node->global.inlined_to->uid);
+  if (node->same_comdat_group)
+    fprintf (f, " (same comdat group as %s/%i)",
+            cgraph_node_name (node->same_comdat_group),
+            node->same_comdat_group->uid);
   if (node->clone_of)
     fprintf (f, " (clone of %s/%i)",
             cgraph_node_name (node->clone_of),
@@ -1790,22 +1843,6 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
   if (node->count)
     fprintf (f, " executed "HOST_WIDEST_INT_PRINT_DEC"x",
             (HOST_WIDEST_INT)node->count);
-  if (node->local.inline_summary.self_time)
-    fprintf (f, " %i time, %i benefit", node->local.inline_summary.self_time,
-                                       node->local.inline_summary.time_inlining_benefit);
-  if (node->global.time && node->global.time
-      != node->local.inline_summary.self_time)
-    fprintf (f, " (%i after inlining)", node->global.time);
-  if (node->local.inline_summary.self_size)
-    fprintf (f, " %i size, %i benefit", node->local.inline_summary.self_size,
-                                       node->local.inline_summary.size_inlining_benefit);
-  if (node->global.size && node->global.size
-      != node->local.inline_summary.self_size)
-    fprintf (f, " (%i after inlining)", node->global.size);
-  if (node->local.inline_summary.estimated_self_stack_size)
-    fprintf (f, " %i bytes stack usage", (int)node->local.inline_summary.estimated_self_stack_size);
-  if (node->global.estimated_stack_size != node->local.inline_summary.estimated_self_stack_size)
-    fprintf (f, " %i bytes after inlining", (int)node->global.estimated_stack_size);
   if (node->origin)
     fprintf (f, " nested in: %s", cgraph_node_name (node->origin));
   if (node->needed)
@@ -1824,18 +1861,19 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
     fprintf (f, " local");
   if (node->local.externally_visible)
     fprintf (f, " externally_visible");
+  if (node->resolution != LDPR_UNKNOWN)
+    fprintf (f, " %s",
+            ld_plugin_symbol_resolution_names[(int)node->resolution]);
   if (node->local.finalized)
     fprintf (f, " finalized");
-  if (node->local.disregard_inline_limits)
-    fprintf (f, " always_inline");
-  else if (node->local.inlinable)
-    fprintf (f, " inlinable");
-  else if (node->local.versionable)
-    fprintf (f, " versionable");
   if (node->local.redefined_extern_inline)
     fprintf (f, " redefined_extern_inline");
   if (TREE_ASM_WRITTEN (node->decl))
     fprintf (f, " asm_written");
+  if (node->only_called_at_startup)
+    fprintf (f, " only_called_at_startup");
+  if (node->only_called_at_exit)
+    fprintf (f, " only_called_at_exit");
 
   fprintf (f, "\n  called by: ");
   for (edge = node->callers; edge; edge = edge->next_caller)
@@ -1871,8 +1909,6 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
       if (edge->frequency)
        fprintf (f, "(%.2f per call) ",
                 edge->frequency / (double)CGRAPH_FREQ_BASE);
-      if (edge->loop_nest)
-       fprintf (f, "(nested in %i loops) ", edge->loop_nest);
       if (edge->can_throw_external)
        fprintf(f, "(can throw external) ");
     }
@@ -1897,7 +1933,7 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
           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 "
+             fprintf (f, " (thunk of %s fixed offset %i virtual value %i has "
                       "virtual offset %i",
                       lang_hooks.decl_printable_name (n->thunk.alias, 2),
                       (int)n->thunk.fixed_offset,
@@ -1915,7 +1951,7 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
 
 /* Dump call graph node NODE to stderr.  */
 
-void
+DEBUG_FUNCTION void
 debug_cgraph_node (struct cgraph_node *node)
 {
   dump_cgraph_node (stderr, node);
@@ -1937,7 +1973,7 @@ dump_cgraph (FILE *f)
 
 /* Dump the call graph to stderr.  */
 
-void
+DEBUG_FUNCTION void
 debug_cgraph (void)
 {
   dump_cgraph (stderr);
@@ -1949,20 +1985,43 @@ debug_cgraph (void)
 void
 change_decl_assembler_name (tree decl, tree name)
 {
-  gcc_assert (!assembler_name_hash);
+  struct cgraph_node *node;
+  void **slot;
   if (!DECL_ASSEMBLER_NAME_SET_P (decl))
+    SET_DECL_ASSEMBLER_NAME (decl, name);
+  else
     {
-      SET_DECL_ASSEMBLER_NAME (decl, name);
-      return;
-    }
-  if (name == DECL_ASSEMBLER_NAME (decl))
-    return;
+      if (name == DECL_ASSEMBLER_NAME (decl))
+       return;
 
-  if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
-      && DECL_RTL_SET_P (decl))
-    warning (0, "%D renamed after being referenced in assembly", decl);
+      if (assembler_name_hash
+         && TREE_CODE (decl) == FUNCTION_DECL
+         && (node = cgraph_get_node_or_alias (decl)) != NULL)
+       {
+         tree old_name = DECL_ASSEMBLER_NAME (decl);
+         slot = htab_find_slot_with_hash (assembler_name_hash, old_name,
+                                          decl_assembler_name_hash (old_name),
+                                          NO_INSERT);
+         /* Inline clones are not hashed.  */
+         if (slot && *slot == node)
+           htab_clear_slot (assembler_name_hash, slot);
+       }
+      if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+         && DECL_RTL_SET_P (decl))
+       warning (0, "%D renamed after being referenced in assembly", decl);
 
-  SET_DECL_ASSEMBLER_NAME (decl, name);
+      SET_DECL_ASSEMBLER_NAME (decl, name);
+    }
+  if (assembler_name_hash
+      && TREE_CODE (decl) == FUNCTION_DECL
+      && (node = cgraph_get_node_or_alias (decl)) != NULL)
+    {
+      slot = htab_find_slot_with_hash (assembler_name_hash, name,
+                                      decl_assembler_name_hash (name),
+                                      INSERT);
+      gcc_assert (!*slot);
+      *slot = node;
+    }
 }
 
 /* Add a top-level asm statement to the list.  */
@@ -1972,7 +2031,7 @@ cgraph_add_asm_node (tree asm_str)
 {
   struct cgraph_asm_node *node;
 
-  node = GGC_CNEW (struct cgraph_asm_node);
+  node = ggc_alloc_cleared_cgraph_asm_node ();
   node->asm_str = asm_str;
   node->order = cgraph_order++;
   node->next = NULL;
@@ -1997,7 +2056,7 @@ cgraph_function_possibly_inlined_p (tree decl)
 struct cgraph_edge *
 cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
                   gimple call_stmt, unsigned stmt_uid, gcov_type count_scale,
-                  int freq_scale, int loop_nest, bool update_original)
+                  int freq_scale, bool update_original)
 {
   struct cgraph_edge *new_edge;
   gcov_type count = e->count * count_scale / REG_BR_PROB_BASE;
@@ -2016,26 +2075,35 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
 
       if (call_stmt && (decl = gimple_call_fndecl (call_stmt)))
        {
-         struct cgraph_node *callee = cgraph_node (decl);
-         new_edge = cgraph_create_edge (n, callee, call_stmt, count, freq,
-                                        e->loop_nest + loop_nest);
+         struct cgraph_node *callee = cgraph_get_node (decl);
+         gcc_checking_assert (callee);
+         new_edge = cgraph_create_edge (n, callee, call_stmt, count, freq);
        }
       else
        {
          new_edge = cgraph_create_indirect_edge (n, call_stmt,
                                                  e->indirect_info->ecf_flags,
-                                                 count, freq,
-                                                 e->loop_nest + loop_nest);
+                                                 count, freq);
          *new_edge->indirect_info = *e->indirect_info;
        }
     }
   else
-    new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq,
-                                  e->loop_nest + loop_nest);
+    {
+      new_edge = cgraph_create_edge (n, e->callee, call_stmt, count, freq);
+      if (e->indirect_info)
+       {
+         new_edge->indirect_info
+           = ggc_alloc_cleared_cgraph_indirect_call_info ();
+         *new_edge->indirect_info = *e->indirect_info;
+       }
+    }
 
   new_edge->inline_failed = e->inline_failed;
   new_edge->indirect_inlining_edge = e->indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  /* Clone flags that depend on call_stmt availability manually.  */
+  new_edge->can_throw_external = e->can_throw_external;
+  new_edge->call_stmt_cannot_inline_p = e->call_stmt_cannot_inline_p;
   if (update_original)
     {
       e->count -= new_edge->count;
@@ -2056,10 +2124,10 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
    by node.  */
 struct cgraph_node *
 cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
-                  int loop_nest, bool update_original,
+                  bool update_original,
                   VEC(cgraph_edge_p,heap) *redirect_callers)
 {
-  struct cgraph_node *new_node = cgraph_create_node ();
+  struct cgraph_node *new_node = cgraph_create_node_1 ();
   struct cgraph_edge *e;
   gcov_type count_scale;
   unsigned i;
@@ -2075,7 +2143,6 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
   new_node->local = n->local;
   new_node->local.externally_visible = false;
   new_node->local.local = true;
-  new_node->local.vtable_method = false;
   new_node->global = n->global;
   new_node->rtl = n->rtl;
   new_node->count = count;
@@ -2098,7 +2165,7 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
        n->count = 0;
     }
 
-  for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+  FOR_EACH_VEC_ELT (cgraph_edge_p, redirect_callers, i, e)
     {
       /* Redirect calls to the old version node to point to its new
         version.  */
@@ -2108,11 +2175,11 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
 
   for (e = n->callees;e; e=e->next_callee)
     cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
-                      count_scale, freq, loop_nest, update_original);
+                      count_scale, freq, update_original);
 
   for (e = n->indirect_calls; e; e = e->next_callee)
     cgraph_clone_edge (e, new_node, e->call_stmt, e->lto_stmt_uid,
-                      count_scale, freq, loop_nest, update_original);
+                      count_scale, freq, update_original);
   ipa_clone_references (new_node, NULL, &n->ref_list);
 
   new_node->next_sibling_clone = n->clones;
@@ -2143,24 +2210,26 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
   return new_node;
 }
 
-/* Create a new name for omp child function.  Returns an identifier.  */
+/* Create a new name for clone of DECL, add SUFFIX.  Returns an identifier.  */
 
 static GTY(()) unsigned int clone_fn_id_num;
 
-static tree
-clone_function_name (tree decl)
+tree
+clone_function_name (tree decl, const char *suffix)
 {
   tree name = DECL_ASSEMBLER_NAME (decl);
   size_t len = IDENTIFIER_LENGTH (name);
   char *tmp_name, *prefix;
 
-  prefix = XALLOCAVEC (char, len + strlen ("_clone") + 1);
+  prefix = XALLOCAVEC (char, len + strlen (suffix) + 2);
   memcpy (prefix, IDENTIFIER_POINTER (name), len);
-  strcpy (prefix + len, "_clone");
+  strcpy (prefix + len + 1, suffix);
 #ifndef NO_DOT_IN_LABEL
   prefix[len] = '.';
 #elif !defined NO_DOLLAR_IN_LABEL
   prefix[len] = '$';
+#else
+  prefix[len] = '_';
 #endif
   ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
   return get_identifier (tmp_name);
@@ -2176,7 +2245,8 @@ struct cgraph_node *
 cgraph_create_virtual_clone (struct cgraph_node *old_node,
                             VEC(cgraph_edge_p,heap) *redirect_callers,
                             VEC(ipa_replace_map_p,gc) *tree_map,
-                            bitmap args_to_skip)
+                            bitmap args_to_skip,
+                            const char * suffix)
 {
   tree old_decl = old_node->decl;
   struct cgraph_node *new_node = NULL;
@@ -2184,7 +2254,10 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
   size_t i;
   struct ipa_replace_map *map;
 
-  gcc_assert  (tree_versionable_function_p (old_decl));
+  if (!flag_wpa)
+    gcc_checking_assert  (tree_versionable_function_p (old_decl));
+
+  gcc_assert (old_node->local.can_change_signature || !args_to_skip);
 
   /* Make a new FUNCTION_DECL tree node */
   if (!args_to_skip)
@@ -2194,12 +2267,12 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
   DECL_STRUCT_FUNCTION (new_decl) = NULL;
 
   /* Generate a new name for the new version. */
-  DECL_NAME (new_decl) = clone_function_name (old_decl);
+  DECL_NAME (new_decl) = clone_function_name (old_decl, suffix);
   SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
   SET_DECL_RTL (new_decl, NULL);
 
   new_node = cgraph_clone_node (old_node, new_decl, old_node->count,
-                               CGRAPH_FREQ_BASE, 0, false,
+                               CGRAPH_FREQ_BASE, false,
                                redirect_callers);
   /* Update the properties.
      Make clone visible only within this translation unit.  Make sure
@@ -2207,13 +2280,17 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
      ??? We cannot use COMDAT linkage because there is no
      ABI support for this.  */
   DECL_EXTERNAL (new_node->decl) = 0;
+  if (DECL_ONE_ONLY (old_decl))
+    DECL_SECTION_NAME (new_node->decl) = NULL;
   DECL_COMDAT_GROUP (new_node->decl) = 0;
   TREE_PUBLIC (new_node->decl) = 0;
   DECL_COMDAT (new_node->decl) = 0;
   DECL_WEAK (new_node->decl) = 0;
+  DECL_STATIC_CONSTRUCTOR (new_node->decl) = 0;
+  DECL_STATIC_DESTRUCTOR (new_node->decl) = 0;
   new_node->clone.tree_map = tree_map;
   new_node->clone.args_to_skip = args_to_skip;
-  for (i = 0; VEC_iterate (ipa_replace_map_p, tree_map, i, map); i++)
+  FOR_EACH_VEC_ELT (ipa_replace_map_p, tree_map, i, map)
     {
       tree var = map->new_tree;
 
@@ -2227,8 +2304,12 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
       /* Record references of the future statement initializing the constant
         argument.  */
       if (TREE_CODE (var) == FUNCTION_DECL)
-       ipa_record_reference (new_node, NULL, cgraph_node (var),
-                             NULL, IPA_REF_ADDR, NULL);
+       {
+         struct cgraph_node *ref_node = cgraph_get_node (var);
+         gcc_checking_assert (ref_node);
+         ipa_record_reference (new_node, NULL, ref_node, NULL, IPA_REF_ADDR,
+                               NULL);
+       }
       else if (TREE_CODE (var) == VAR_DECL)
        ipa_record_reference (new_node, NULL, NULL, varpool_node (var),
                              IPA_REF_ADDR, NULL);
@@ -2243,7 +2324,7 @@ cgraph_create_virtual_clone (struct cgraph_node *old_node,
       struct cgraph_node *orig_node;
       for (orig_node = old_node; orig_node->clone_of; orig_node = orig_node->clone_of)
         ;
-      for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = TREE_CHAIN (arg), oldi++)
+      for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = DECL_CHAIN (arg), oldi++)
        {
          if (bitmap_bit_p (old_node->clone.combined_args_to_skip, oldi))
            {
@@ -2293,8 +2374,8 @@ cgraph_function_body_availability (struct cgraph_node *node)
     avail = AVAIL_LOCAL;
   else if (!node->local.externally_visible)
     avail = AVAIL_AVAILABLE;
-  /* Inline functions are safe to be analyzed even if their sybol can
-     be overwritten at runtime.  It is not meaningful to enfore any sane
+  /* Inline functions are safe to be analyzed even if their symbol can
+     be overwritten at runtime.  It is not meaningful to enforce any sane
      behaviour on replacing inline function by different body.  */
   else if (DECL_DECLARED_INLINE_P (node->decl))
     avail = AVAIL_AVAILABLE;
@@ -2309,7 +2390,7 @@ cgraph_function_body_availability (struct cgraph_node *node)
      AVAIL_AVAILABLE here?  That would be good reason to preserve this
      bit.  */
 
-  else if (DECL_REPLACEABLE_P (node->decl) && !DECL_EXTERNAL (node->decl))
+  else if (decl_replaceable_p (node->decl) && !DECL_EXTERNAL (node->decl))
     avail = AVAIL_OVERWRITABLE;
   else avail = AVAIL_AVAILABLE;
 
@@ -2336,7 +2417,7 @@ cgraph_add_new_function (tree fndecl, bool lowered)
     {
       case CGRAPH_STATE_CONSTRUCTION:
        /* Just enqueue function to be processed at nearest occurrence.  */
-       node = cgraph_node (fndecl);
+       node = cgraph_create_node (fndecl);
        node->next_needed = cgraph_new_nodes;
        if (lowered)
          node->lowered = true;
@@ -2348,7 +2429,7 @@ cgraph_add_new_function (tree fndecl, bool lowered)
       case CGRAPH_STATE_EXPANSION:
        /* Bring the function into finalized state and enqueue for later
           analyzing and compilation.  */
-       node = cgraph_node (fndecl);
+       node = cgraph_get_create_node (fndecl);
        node->local.local = false;
        node->local.finalized = true;
        node->reachable = node->needed = true;
@@ -2376,11 +2457,13 @@ cgraph_add_new_function (tree fndecl, bool lowered)
       case CGRAPH_STATE_FINISHED:
        /* At the very end of compilation we have to do all the work up
           to expansion.  */
+       node = cgraph_create_node (fndecl);
+       if (lowered)
+         node->lowered = true;
+       cgraph_analyze_function (node);
        push_cfun (DECL_STRUCT_FUNCTION (fndecl));
        current_function_decl = fndecl;
        gimple_register_cfg_hooks ();
-       if (!lowered)
-          tree_lowering_passes (fndecl);
        bitmap_obstack_initialize (NULL);
        if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (fndecl)))
          execute_pass_list (pass_early_local_passes.pass.sub);
@@ -2419,15 +2502,50 @@ cgraph_make_decl_local (tree decl)
 
   if (TREE_CODE (decl) == VAR_DECL)
     DECL_COMMON (decl) = 0;
-  else if (TREE_CODE (decl) == FUNCTION_DECL)
+  else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+  if (DECL_COMDAT (decl))
     {
+      /* It is possible that we are linking against library defining same COMDAT
+        function.  To avoid conflict we need to rename our local name of the
+        function just in the case WHOPR partitioning decide to make it hidden
+        to avoid cross partition references.  */
+      if (flag_wpa)
+       {
+         const char *old_name;
+
+         old_name  = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+         if (TREE_CODE (decl) == FUNCTION_DECL)
+           {
+             struct cgraph_node *node = cgraph_get_node_or_alias (decl);
+             change_decl_assembler_name (decl,
+                                         clone_function_name (decl, "local"));
+             if (node->local.lto_file_data)
+               lto_record_renamed_decl (node->local.lto_file_data,
+                                        old_name,
+                                        IDENTIFIER_POINTER
+                                          (DECL_ASSEMBLER_NAME (decl)));
+           }
+         else if (TREE_CODE (decl) == VAR_DECL)
+           {
+             struct varpool_node *vnode = varpool_get_node (decl);
+             /* change_decl_assembler_name will warn here on vtables because
+                C++ frontend still sets TREE_SYMBOL_REFERENCED on them.  */
+             SET_DECL_ASSEMBLER_NAME (decl,
+                                      clone_function_name (decl, "local"));
+             if (vnode->lto_file_data)
+               lto_record_renamed_decl (vnode->lto_file_data,
+                                        old_name,
+                                        IDENTIFIER_POINTER
+                                          (DECL_ASSEMBLER_NAME (decl)));
+           }
+       }
+      DECL_SECTION_NAME (decl) = 0;
       DECL_COMDAT (decl) = 0;
-      DECL_COMDAT_GROUP (decl) = 0;
-      DECL_WEAK (decl) = 0;
-      DECL_EXTERNAL (decl) = 0;
     }
-  else
-    gcc_unreachable ();
+  DECL_COMDAT_GROUP (decl) = 0;
+  DECL_WEAK (decl) = 0;
+  DECL_EXTERNAL (decl) = 0;
   TREE_PUBLIC (decl) = 0;
   if (!DECL_RTL_SET_P (decl))
     return;
@@ -2461,6 +2579,7 @@ cgraph_make_node_local (struct cgraph_node *node)
 
       node->local.externally_visible = false;
       node->local.local = true;
+      node->resolution = LDPR_PREVAILING_DEF_IRONLY;
       gcc_assert (cgraph_function_body_availability (node) == AVAIL_LOCAL);
     }
 }
@@ -2481,37 +2600,50 @@ cgraph_set_nothrow_flag (struct cgraph_node *node, bool nothrow)
    if any to READONLY.  */
 
 void
-cgraph_set_readonly_flag (struct cgraph_node *node, bool readonly)
+cgraph_set_const_flag (struct cgraph_node *node, bool readonly, bool looping)
 {
   struct cgraph_node *alias;
+  /* Static constructors and destructors without a side effect can be
+     optimized out.  */
+  if (!looping && readonly)
+    {
+      if (DECL_STATIC_CONSTRUCTOR (node->decl))
+       DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+      if (DECL_STATIC_DESTRUCTOR (node->decl))
+       DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+    }
   TREE_READONLY (node->decl) = readonly;
+  DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
   for (alias = node->same_body; alias; alias = alias->next)
-    TREE_READONLY (alias->decl) = readonly;
+    {
+      TREE_READONLY (alias->decl) = readonly;
+      DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping;
+    }
 }
 
 /* Set DECL_PURE_P on NODE's decl and on same_body aliases of NODE
    if any to PURE.  */
 
 void
-cgraph_set_pure_flag (struct cgraph_node *node, bool pure)
+cgraph_set_pure_flag (struct cgraph_node *node, bool pure, bool looping)
 {
   struct cgraph_node *alias;
+  /* Static constructors and destructors without a side effect can be
+     optimized out.  */
+  if (!looping && pure)
+    {
+      if (DECL_STATIC_CONSTRUCTOR (node->decl))
+       DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+      if (DECL_STATIC_DESTRUCTOR (node->decl))
+       DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+    }
   DECL_PURE_P (node->decl) = pure;
+  DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
   for (alias = node->same_body; alias; alias = alias->next)
-    DECL_PURE_P (alias->decl) = pure;
-}
-
-/* Set DECL_LOOPING_CONST_OR_PURE_P on NODE's decl and on
-   same_body aliases of NODE if any to LOOPING_CONST_OR_PURE.  */
-
-void
-cgraph_set_looping_const_or_pure_flag (struct cgraph_node *node,
-                                      bool looping_const_or_pure)
-{
-  struct cgraph_node *alias;
-  DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping_const_or_pure;
-  for (alias = node->same_body; alias; alias = alias->next)
-    DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping_const_or_pure;
+    {
+      DECL_PURE_P (alias->decl) = pure;
+      DECL_LOOPING_CONST_OR_PURE_P (alias->decl) = looping;
+    }
 }
 
 /* See if the frequency of NODE can be updated based on frequencies of its
@@ -2520,20 +2652,32 @@ bool
 cgraph_propagate_frequency (struct cgraph_node *node)
 {
   bool maybe_unlikely_executed = true, maybe_executed_once = true;
+  bool only_called_at_startup = true;
+  bool only_called_at_exit = true;
+  bool changed = false;
   struct cgraph_edge *edge;
+
   if (!node->local.local)
     return false;
   gcc_assert (node->analyzed);
-  if (node->frequency == NODE_FREQUENCY_HOT)
-    return false;
-  if (node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED)
-    return false;
   if (dump_file && (dump_flags & TDF_DETAILS))
     fprintf (dump_file, "Processing frequency %s\n", cgraph_node_name (node));
+
   for (edge = node->callers;
-       edge && (maybe_unlikely_executed || maybe_executed_once);
+       edge && (maybe_unlikely_executed || maybe_executed_once
+               || only_called_at_startup || only_called_at_exit);
        edge = edge->next_caller)
     {
+      if (edge->caller != node)
+       {
+          only_called_at_startup &= edge->caller->only_called_at_startup;
+         /* It makes sense to put main() together with the static constructors.
+            It will be executed for sure, but rest of functions called from
+            main are definitely not at startup only.  */
+         if (MAIN_NAME_P (DECL_NAME (edge->caller->decl)))
+           only_called_at_startup = 0;
+          only_called_at_exit &= edge->caller->only_called_at_exit;
+       }
       if (!edge->frequency)
        continue;
       switch (edge->caller->frequency)
@@ -2542,9 +2686,10 @@ cgraph_propagate_frequency (struct cgraph_node *node)
          break;
        case NODE_FREQUENCY_EXECUTED_ONCE:
          if (dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "  Called by %s that is executed once\n", cgraph_node_name (node));
+           fprintf (dump_file, "  Called by %s that is executed once\n",
+                    cgraph_node_name (edge->caller));
          maybe_unlikely_executed = false;
-         if (edge->loop_nest)
+         if (inline_edge_summary (edge)->loop_depth)
            {
              maybe_executed_once = false;
              if (dump_file && (dump_flags & TDF_DETAILS))
@@ -2554,27 +2699,175 @@ cgraph_propagate_frequency (struct cgraph_node *node)
        case NODE_FREQUENCY_HOT:
        case NODE_FREQUENCY_NORMAL:
          if (dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "  Called by %s that is normal or hot\n", cgraph_node_name (node));
+           fprintf (dump_file, "  Called by %s that is normal or hot\n",
+                    cgraph_node_name (edge->caller));
          maybe_unlikely_executed = false;
          maybe_executed_once = false;
          break;
        }
     }
-   if (maybe_unlikely_executed)
-     {
-       node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED;
+  if ((only_called_at_startup && !only_called_at_exit)
+      && !node->only_called_at_startup)
+    {
+       node->only_called_at_startup = true;
        if (dump_file)
-         fprintf (dump_file, "Node %s promoted to unlikely executed.\n", cgraph_node_name (node));
-       return true;
-     }
-   if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE)
-     {
-       node->frequency = NODE_FREQUENCY_EXECUTED_ONCE;
+         fprintf (dump_file, "Node %s promoted to only called at startup.\n",
+                 cgraph_node_name (node));
+       changed = true;
+    }
+  if ((only_called_at_exit && !only_called_at_startup)
+      && !node->only_called_at_exit)
+    {
+       node->only_called_at_exit = true;
        if (dump_file)
-         fprintf (dump_file, "Node %s promoted to executed once.\n", cgraph_node_name (node));
-       return true;
-     }
-   return false;
+         fprintf (dump_file, "Node %s promoted to only called at exit.\n",
+                 cgraph_node_name (node));
+       changed = true;
+    }
+  /* These come either from profile or user hints; never update them.  */
+  if (node->frequency == NODE_FREQUENCY_HOT
+      || node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED)
+    return changed;
+  if (maybe_unlikely_executed)
+    {
+      node->frequency = NODE_FREQUENCY_UNLIKELY_EXECUTED;
+      if (dump_file)
+       fprintf (dump_file, "Node %s promoted to unlikely executed.\n",
+                cgraph_node_name (node));
+      changed = true;
+    }
+  else if (maybe_executed_once && node->frequency != NODE_FREQUENCY_EXECUTED_ONCE)
+    {
+      node->frequency = NODE_FREQUENCY_EXECUTED_ONCE;
+      if (dump_file)
+       fprintf (dump_file, "Node %s promoted to executed once.\n",
+                cgraph_node_name (node));
+      changed = true;
+    }
+  return changed;
+}
+
+/* Return true when NODE can not return or throw and thus
+   it is safe to ignore its side effects for IPA analysis.  */
+
+bool
+cgraph_node_cannot_return (struct cgraph_node *node)
+{
+  int flags = flags_from_decl_or_type (node->decl);
+  if (!flag_exceptions)
+    return (flags & ECF_NORETURN) != 0;
+  else
+    return ((flags & (ECF_NORETURN | ECF_NOTHROW))
+            == (ECF_NORETURN | ECF_NOTHROW));
+}
+
+/* Return true when call of E can not lead to return from caller
+   and thus it is safe to ignore its side effects for IPA analysis
+   when computing side effects of the caller.
+   FIXME: We could actually mark all edges that have no reaching
+   patch to EXIT_BLOCK_PTR or throw to get better results.  */
+bool
+cgraph_edge_cannot_lead_to_return (struct cgraph_edge *e)
+{
+  if (cgraph_node_cannot_return (e->caller))
+    return true;
+  if (e->indirect_unknown_callee)
+    {
+      int flags = e->indirect_info->ecf_flags;
+      if (!flag_exceptions)
+       return (flags & ECF_NORETURN) != 0;
+      else
+       return ((flags & (ECF_NORETURN | ECF_NOTHROW))
+                == (ECF_NORETURN | ECF_NOTHROW));
+    }
+  else
+    return cgraph_node_cannot_return (e->callee);
+}
+
+/* Return true when function NODE can be removed from callgraph
+   if all direct calls are eliminated.  */
+
+bool
+cgraph_can_remove_if_no_direct_calls_and_refs_p (struct cgraph_node *node)
+{
+  gcc_assert (!node->global.inlined_to);
+  /* Extern inlines can always go, we will use the external definition.  */
+  if (DECL_EXTERNAL (node->decl))
+    return true;
+  /* When function is needed, we can not remove it.  */
+  if (node->needed || node->reachable_from_other_partition)
+    return false;
+  if (DECL_STATIC_CONSTRUCTOR (node->decl)
+      || DECL_STATIC_DESTRUCTOR (node->decl))
+    return false;
+  /* Only COMDAT functions can be removed if externally visible.  */
+  if (node->local.externally_visible
+      && (!DECL_COMDAT (node->decl)
+         || cgraph_used_from_object_file_p (node)))
+    return false;
+  return true;
+}
+
+/* Return true when function NODE can be expected to be removed
+   from program when direct calls in this compilation unit are removed.
+
+   As a special case COMDAT functions are
+   cgraph_can_remove_if_no_direct_calls_p while the are not
+   cgraph_only_called_directly_p (it is possible they are called from other
+   unit)
+
+   This function behaves as cgraph_only_called_directly_p because eliminating
+   all uses of COMDAT function does not make it necessarily disappear from
+   the program unless we are compiling whole program or we do LTO.  In this
+   case we know we win since dynamic linking will not really discard the
+   linkonce section.  */
+
+bool
+cgraph_will_be_removed_from_program_if_no_direct_calls (struct cgraph_node *node)
+{
+  gcc_assert (!node->global.inlined_to);
+  if (cgraph_used_from_object_file_p (node))
+    return false;
+  if (!in_lto_p && !flag_whole_program)
+    return cgraph_only_called_directly_p (node);
+  else
+    {
+       if (DECL_EXTERNAL (node->decl))
+         return true;
+      return cgraph_can_remove_if_no_direct_calls_p (node);
+    }
+}
+
+/* Return true when RESOLUTION indicate that linker will use
+   the symbol from non-LTO object files.  */
+
+bool
+resolution_used_from_other_file_p (enum ld_plugin_symbol_resolution resolution)
+{
+  return (resolution == LDPR_PREVAILING_DEF
+          || resolution == LDPR_PREEMPTED_REG
+          || resolution == LDPR_RESOLVED_EXEC
+          || resolution == LDPR_RESOLVED_DYN);
+}
+
+/* Return true when NODE is known to be used from other (non-LTO) object file.
+   Known only when doing LTO via linker plugin.  */
+
+bool
+cgraph_used_from_object_file_p (struct cgraph_node *node)
+{
+  struct cgraph_node *alias;
+
+  gcc_assert (!node->global.inlined_to);
+  if (!TREE_PUBLIC (node->decl) || DECL_EXTERNAL (node->decl))
+    return false;
+  if (resolution_used_from_other_file_p (node->resolution))
+    return true;
+  for (alias = node->same_body; alias; alias = alias->next)
+    if (TREE_PUBLIC (alias->decl)
+       && resolution_used_from_other_file_p (alias->resolution))
+      return true;
+  return false;
 }
 
 #include "gt-cgraph.h"