re PR debug/66691 (ICE on valid code at -O3 with -g enabled in simplify_subreg, at...
[gcc.git] / gcc / ipa.c
index 76815648d898ce0902df2a5398d25e8a5079858f..fdfb88072da6652a47309f1071ae5c5696ae6b70 100644 (file)
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -1,5 +1,5 @@
 /* Basic IPA optimizations and utilities.
-   Copyright (C) 2003-2014 Free Software Foundation, Inc.
+   Copyright (C) 2003-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,19 +21,28 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "alias.h"
+#include "symtab.h"
+#include "options.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "calls.h"
 #include "stringpool.h"
+#include "predict.h"
+#include "basic-block.h"
+#include "hard-reg-set.h"
+#include "function.h"
 #include "cgraph.h"
 #include "tree-pass.h"
-#include "hash-map.h"
-#include "pointer-set.h"
 #include "gimple-expr.h"
 #include "gimplify.h"
 #include "flags.h"
 #include "target.h"
 #include "tree-iterator.h"
 #include "ipa-utils.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
+#include "ipa-prop.h"
 #include "ipa-inline.h"
 #include "tree-inline.h"
 #include "profile.h"
@@ -84,14 +93,14 @@ update_inlined_to_pointer (struct cgraph_node *node, struct cgraph_node *inlined
 
 static void
 enqueue_node (symtab_node *node, symtab_node **first,
-             struct pointer_set_t *reachable)
+             hash_set<symtab_node *> *reachable)
 {
   /* Node is still in queue; do nothing.  */
   if (node->aux && node->aux != (void *) 2)
     return;
   /* Node was already processed as unreachable, re-enqueue
      only if it became reachable now.  */
-  if (node->aux == (void *)2 && !pointer_set_contains (reachable, node))
+  if (node->aux == (void *)2 && !reachable->contains (node))
     return;
   node->aux = *first;
   *first = node;
@@ -103,28 +112,42 @@ static void
 process_references (symtab_node *snode,
                    symtab_node **first,
                    bool before_inlining_p,
-                   struct pointer_set_t *reachable)
+                   hash_set<symtab_node *> *reachable)
 {
   int i;
   struct ipa_ref *ref = NULL;
   for (i = 0; snode->iterate_reference (i, ref); i++)
     {
       symtab_node *node = ref->referred;
+      symtab_node *body = node->ultimate_alias_target ();
 
       if (node->definition && !node->in_other_partition
          && ((!DECL_EXTERNAL (node->decl) || node->alias)
              || (((before_inlining_p
-                   && (cgraph_state < CGRAPH_STATE_IPA_SSA
-                       || !lookup_attribute ("always_inline",
-                                             DECL_ATTRIBUTES (node->decl)))))
-                 /* We use variable constructors during late complation for
+                   && ((TREE_CODE (node->decl) != FUNCTION_DECL
+                        && optimize)
+                       || (TREE_CODE (node->decl) == FUNCTION_DECL
+                           && opt_for_fn (body->decl, optimize))
+                       || (symtab->state < IPA_SSA
+                           && lookup_attribute
+                                ("always_inline",
+                                 DECL_ATTRIBUTES (body->decl))))))
+                 /* We use variable constructors during late compilation for
                     constant folding.  Keep references alive so partitioning
                     knows about potential references.  */
                  || (TREE_CODE (node->decl) == VAR_DECL
                      && flag_wpa
                      && ctor_for_folding (node->decl)
                         != error_mark_node))))
-       pointer_set_insert (reachable, node);
+       {
+         /* Be sure that we will not optimize out alias target
+            body.  */
+         if (DECL_EXTERNAL (node->decl)
+             && node->alias
+             && before_inlining_p)
+           reachable->add (body);
+         reachable->add (node);
+       }
       enqueue_node (node, first, reachable);
     }
 }
@@ -138,10 +161,11 @@ process_references (symtab_node *snode,
    possible.  */
 
 static void
-walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
+walk_polymorphic_call_targets (hash_set<void *> *reachable_call_targets,
                               struct cgraph_edge *edge,
                               symtab_node **first,
-                              pointer_set_t *reachable, bool before_inlining_p)
+                              hash_set<symtab_node *> *reachable,
+                              bool before_inlining_p)
 {
   unsigned int i;
   void *cache_token;
@@ -150,8 +174,7 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
     = possible_polymorphic_call_targets
        (edge, &final, &cache_token);
 
-  if (!pointer_set_insert (reachable_call_targets,
-                          cache_token))
+  if (!reachable_call_targets->add (cache_token))
     {
       for (i = 0; i < targets.length (); i++)
        {
@@ -162,18 +185,26 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
             unused.  */
          if (TREE_CODE (TREE_TYPE (n->decl)) == METHOD_TYPE
              && type_in_anonymous_namespace_p
-                   (method_class_type (TREE_TYPE (n->decl))))
+                   (TYPE_METHOD_BASETYPE (TREE_TYPE (n->decl))))
            continue;
 
+          symtab_node *body = n->function_symbol ();
+
          /* Prior inlining, keep alive bodies of possible targets for
             devirtualization.  */
           if (n->definition
               && (before_inlining_p
-                  && (cgraph_state < CGRAPH_STATE_IPA_SSA
-                      || !lookup_attribute ("always_inline",
-                                            DECL_ATTRIBUTES (n->decl)))))
-            pointer_set_insert (reachable, n);
-
+                  && opt_for_fn (body->decl, optimize)
+                  && opt_for_fn (body->decl, flag_devirtualize)))
+             {
+                /* Be sure that we will not optimize out alias target
+                   body.  */
+                if (DECL_EXTERNAL (n->decl)
+                    && n->alias
+                    && before_inlining_p)
+                  reachable->add (body);
+               reachable->add (n);
+             }
          /* Even after inlining we want to keep the possible targets in the
             boundary, so late passes can still produce direct call even if
             the chance for inlining is lost.  */
@@ -193,23 +224,33 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
          if (targets.length () == 1)
            target = targets[0];
          else
-           target = cgraph_get_create_node
+           target = cgraph_node::get_create
                       (builtin_decl_implicit (BUILT_IN_UNREACHABLE));
 
          if (dump_enabled_p ())
             {
-              location_t locus = gimple_location_safe (edge->call_stmt);
-              dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
+             location_t locus;
+             if (edge->call_stmt)
+               locus = gimple_location (edge->call_stmt);
+             else
+               locus = UNKNOWN_LOCATION;
+             dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, locus,
                                "devirtualizing call in %s/%i to %s/%i\n",
                                edge->caller->name (), edge->caller->order,
                                target->name (),
                                target->order);
            }
-         edge = cgraph_make_edge_direct (edge, target);
-         if (inline_summary_vec)
+         edge = edge->make_direct (target);
+         if (inline_summaries)
            inline_update_overall_summary (node);
          else if (edge->call_stmt)
-           cgraph_redirect_edge_call_stmt_to_callee (edge);
+           {
+             edge->redirect_call_stmt_to_callee ();
+
+             /* Call to __builtin_unreachable shouldn't be instrumented.  */
+             if (!targets.length ())
+               gimple_call_set_with_bounds (edge->call_stmt, false);
+           }
        }
     }
 }
@@ -223,8 +264,6 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
      After inlining we release their bodies and turn them into unanalyzed
      nodes even when they are reachable.
 
-     BEFORE_INLINING_P specify whether we are before or after inlining.
-
    - virtual functions are kept in callgraph even if they seem unreachable in
      hope calls to them will be devirtualized. 
 
@@ -270,19 +309,20 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
    we set AUX pointer of processed symbols in the boundary to constant 2.  */
 
 bool
-symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
+symbol_table::remove_unreachable_nodes (FILE *file)
 {
   symtab_node *first = (symtab_node *) (void *) 1;
   struct cgraph_node *node, *next;
   varpool_node *vnode, *vnext;
   bool changed = false;
-  struct pointer_set_t *reachable = pointer_set_create ();
-  struct pointer_set_t *body_needed_for_clonning = pointer_set_create ();
-  struct pointer_set_t *reachable_call_targets = pointer_set_create ();
+  hash_set<symtab_node *> reachable;
+  hash_set<tree> body_needed_for_clonning;
+  hash_set<void *> reachable_call_targets;
+  bool before_inlining_p = symtab->state < (!optimize ? IPA_SSA
+                                           : IPA_SSA_AFTER_INLINING);
 
   timevar_push (TV_IPA_UNREACHABLE);
-  if (optimize && flag_devirtualize)
-    build_type_inheritance_graph ();
+  build_type_inheritance_graph ();
   if (file)
     fprintf (file, "\nReclaiming functions:");
 #ifdef ENABLE_CHECKING
@@ -301,11 +341,11 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
       if (node->definition
          && !node->global.inlined_to
          && !node->in_other_partition
-         && !cgraph_can_remove_if_no_direct_calls_and_refs_p (node))
+         && !node->can_remove_if_no_direct_calls_and_refs_p ())
        {
          gcc_assert (!node->global.inlined_to);
-         pointer_set_insert (reachable, node);
-         enqueue_node (node, &first, reachable);
+         reachable.add (node);
+         enqueue_node (node, &first, &reachable);
        }
       else
        gcc_assert (!node->aux);
@@ -313,17 +353,17 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
 
   /* Mark variables that are obviously needed.  */
   FOR_EACH_DEFINED_VARIABLE (vnode)
-    if (!varpool_can_remove_if_no_refs (vnode)
+    if (!vnode->can_remove_if_no_refs_p()
        && !vnode->in_other_partition)
       {
-       pointer_set_insert (reachable, vnode);
-       enqueue_node (vnode, &first, reachable);
+       reachable.add (vnode);
+       enqueue_node (vnode, &first, &reachable);
       }
 
   /* Perform reachability analysis.  */
   while (first != (symtab_node *) (void *) 1)
     {
-      bool in_boundary_p = !pointer_set_contains (reachable, first);
+      bool in_boundary_p = !reachable.contains (first);
       symtab_node *node = first;
 
       first = (symtab_node *)first->aux;
@@ -331,16 +371,28 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
       /* If we are processing symbol in boundary, mark its AUX pointer for
         possible later re-processing in enqueue_node.  */
       if (in_boundary_p)
-       node->aux = (void *)2;
+       {
+         node->aux = (void *)2;
+         if (node->alias && node->analyzed)
+           enqueue_node (node->get_alias_target (), &first, &reachable);
+       }
       else
        {
          if (TREE_CODE (node->decl) == FUNCTION_DECL
              && DECL_ABSTRACT_ORIGIN (node->decl))
            {
              struct cgraph_node *origin_node
-             = cgraph_get_create_node (DECL_ABSTRACT_ORIGIN (node->decl));
-             origin_node->used_as_abstract_origin = true;
-             enqueue_node (origin_node, &first, reachable);
+             = cgraph_node::get (DECL_ABSTRACT_ORIGIN (node->decl));
+             if (origin_node && !origin_node->used_as_abstract_origin)
+               {
+                 origin_node->used_as_abstract_origin = true;
+                 gcc_assert (!origin_node->prev_sibling_clone);
+                 gcc_assert (!origin_node->next_sibling_clone);
+                 for (cgraph_node *n = origin_node->clones; n;
+                      n = n->next_sibling_clone)
+                   if (n->decl == DECL_ABSTRACT_ORIGIN (node->decl))
+                     n->used_as_abstract_origin = true;
+               }
            }
          /* If any symbol in a comdat group is reachable, force
             all externally visible symbols in the same comdat
@@ -352,12 +404,12 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
              for (next = node->same_comdat_group;
                   next != node;
                   next = next->same_comdat_group)
-               if (!symtab_comdat_local_p (next)
-                   && !pointer_set_insert (reachable, next))
-                 enqueue_node (next, &first, reachable);
+               if (!next->comdat_local_p ()
+                   && !reachable.add (next))
+                 enqueue_node (next, &first, &reachable);
            }
          /* Mark references as reachable.  */
-         process_references (node, &first, before_inlining_p, reachable);
+         process_references (node, &first, before_inlining_p, &reachable);
        }
 
       if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node))
@@ -368,45 +420,63 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
            {
              struct cgraph_edge *e;
              /* Keep alive possible targets for devirtualization.  */
-             if (optimize && flag_devirtualize)
+             if (opt_for_fn (cnode->decl, optimize)
+                 && opt_for_fn (cnode->decl, flag_devirtualize))
                {
                  struct cgraph_edge *next;
                  for (e = cnode->indirect_calls; e; e = next)
                    {
                      next = e->next_callee;
                      if (e->indirect_info->polymorphic)
-                       walk_polymorphic_call_targets (reachable_call_targets,
-                                                      e, &first, reachable,
+                       walk_polymorphic_call_targets (&reachable_call_targets,
+                                                      e, &first, &reachable,
                                                       before_inlining_p);
                    }
                }
              for (e = cnode->callees; e; e = e->next_callee)
                {
+                 symtab_node *body = e->callee->function_symbol ();
                  if (e->callee->definition
                      && !e->callee->in_other_partition
                      && (!e->inline_failed
                          || !DECL_EXTERNAL (e->callee->decl)
                          || e->callee->alias
-                         || before_inlining_p))
+                         || (before_inlining_p
+                             && (opt_for_fn (body->decl, optimize)
+                                 || (symtab->state < IPA_SSA
+                                     && lookup_attribute
+                                         ("always_inline",
+                                          DECL_ATTRIBUTES (body->decl)))))))
                    {
                      /* Be sure that we will not optimize out alias target
                         body.  */
                      if (DECL_EXTERNAL (e->callee->decl)
                          && e->callee->alias
                          && before_inlining_p)
-                       {
-                         pointer_set_insert (reachable,
-                                             cgraph_function_node (e->callee));
-                       }
-                     pointer_set_insert (reachable, e->callee);
+                       reachable.add (body);
+                     reachable.add (e->callee);
                    }
-                 enqueue_node (e->callee, &first, reachable);
+                 enqueue_node (e->callee, &first, &reachable);
                }
 
              /* When inline clone exists, mark body to be preserved so when removing
                 offline copy of the function we don't kill it.  */
              if (cnode->global.inlined_to)
-               pointer_set_insert (body_needed_for_clonning, cnode->decl);
+               body_needed_for_clonning.add (cnode->decl);
+
+             /* For instrumentation clones we always need original
+                function node for proper LTO privatization.  */
+             if (cnode->instrumentation_clone
+                 && cnode->definition)
+               {
+                 gcc_assert (cnode->instrumented_version || in_lto_p);
+                 if (cnode->instrumented_version)
+                   {
+                     enqueue_node (cnode->instrumented_version, &first,
+                                   &reachable);
+                     reachable.add (cnode->instrumented_version);
+                   }
+               }
 
              /* For non-inline clones, force their origins to the boundary and ensure
                 that body is not removed.  */
@@ -416,12 +486,15 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                  cnode = cnode->clone_of;
                  if (noninline)
                    {
-                     pointer_set_insert (body_needed_for_clonning, cnode->decl);
-                     enqueue_node (cnode, &first, reachable);
+                     body_needed_for_clonning.add (cnode->decl);
+                     enqueue_node (cnode, &first, &reachable);
                    }
                }
 
            }
+         else if (cnode->thunk.thunk_p)
+           enqueue_node (cnode->callees->callee, &first, &reachable);
+
          /* If any reachable function has simd clones, mark them as
             reachable as well.  */
          if (cnode->simd_clones)
@@ -431,8 +504,8 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                   next;
                   next = next->simdclone->next_clone)
                if (in_boundary_p
-                   || !pointer_set_insert (reachable, next))
-                 enqueue_node (next, &first, reachable);
+                   || !reachable.add (next))
+                 enqueue_node (next, &first, &reachable);
            }
        }
       /* When we see constructor of external variable, keep referred nodes in the
@@ -446,31 +519,37 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
        {
          struct ipa_ref *ref = NULL;
          for (int i = 0; node->iterate_reference (i, ref); i++)
-           enqueue_node (ref->referred, &first, reachable);
+           enqueue_node (ref->referred, &first, &reachable);
        }
     }
 
   /* Remove unreachable functions.   */
-  for (node = cgraph_first_function (); node; node = next)
+  for (node = first_function (); node; node = next)
     {
-      next = cgraph_next_function (node);
+      next = next_function (node);
 
       /* If node is not needed at all, remove it.  */
       if (!node->aux)
        {
          if (file)
            fprintf (file, " %s/%i", node->name (), node->order);
-         cgraph_remove_node (node);
+         node->remove ();
          changed = true;
        }
       /* If node is unreachable, remove its body.  */
-      else if (!pointer_set_contains (reachable, node))
+      else if (!reachable.contains (node))
         {
-         if (!pointer_set_contains (body_needed_for_clonning, node->decl))
-           cgraph_release_function_body (node);
+         /* We keep definitions of thunks and aliases in the boundary so
+            we can walk to the ultimate alias targets and function symbols
+            reliably.  */
+         if (node->alias || node->thunk.thunk_p)
+           ;
+         else if (!body_needed_for_clonning.contains (node->decl)
+             && !node->alias && !node->thunk.thunk_p)
+           node->release_body ();
          else if (!node->clone_of)
            gcc_assert (in_lto_p || DECL_RESULT (node->decl));
-         if (node->definition)
+         if (node->definition && !node->alias && !node->thunk.thunk_p)
            {
              if (file)
                fprintf (file, " %s/%i", node->name (), node->order);
@@ -489,14 +568,19 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                                    DECL_ATTRIBUTES (node->decl));
              if (!node->in_other_partition)
                node->local.local = false;
-             cgraph_node_remove_callees (node);
-             symtab_remove_from_same_comdat_group (node);
+             node->remove_callees ();
              node->remove_all_references ();
              changed = true;
+             if (node->thunk.thunk_p
+                 && node->thunk.add_pointer_bounds_args)
+               {
+                 node->thunk.thunk_p = false;
+                 node->thunk.add_pointer_bounds_args = false;
+               }
            }
        }
       else
-       gcc_assert (node->clone_of || !cgraph_function_with_gimple_body_p (node)
+       gcc_assert (node->clone_of || !node->has_gimple_body_p ()
                    || in_lto_p || DECL_RESULT (node->decl));
     }
 
@@ -518,21 +602,33 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
   /* Remove unreachable variables.  */
   if (file)
     fprintf (file, "\nReclaiming variables:");
-  for (vnode = varpool_first_variable (); vnode; vnode = vnext)
+  for (vnode = first_variable (); vnode; vnode = vnext)
     {
-      vnext = varpool_next_variable (vnode);
+      vnext = next_variable (vnode);
       if (!vnode->aux
          /* For can_refer_decl_in_current_unit_p we want to track for
             all external variables if they are defined in other partition
             or not.  */
          && (!flag_ltrans || !DECL_EXTERNAL (vnode->decl)))
        {
+         struct ipa_ref *ref = NULL;
+
+         /* First remove the aliases, so varpool::remove can possibly lookup
+            the constructor and save it for future use.  */
+         while (vnode->iterate_direct_aliases (0, ref))
+           {
+             if (file)
+               fprintf (file, " %s/%i", ref->referred->name (),
+                        ref->referred->order);
+             ref->referring->remove ();
+           }
          if (file)
            fprintf (file, " %s/%i", vnode->name (), vnode->order);
-         varpool_remove_node (vnode);
+          vnext = next_variable (vnode);
+         vnode->remove ();
          changed = true;
        }
-      else if (!pointer_set_contains (reachable, vnode))
+      else if (!reachable.contains (vnode) && !vnode->alias)
         {
          tree init;
          if (vnode->definition)
@@ -541,28 +637,25 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
                fprintf (file, " %s", vnode->name ());
              changed = true;
            }
+         /* Keep body if it may be useful for constant folding.  */
+         if ((init = ctor_for_folding (vnode->decl)) == error_mark_node
+             && !POINTER_BOUNDS_P (vnode->decl))
+           vnode->remove_initializer ();
+         else
+           DECL_INITIAL (vnode->decl) = init;
          vnode->body_removed = true;
          vnode->definition = false;
          vnode->analyzed = false;
          vnode->aux = NULL;
 
-         symtab_remove_from_same_comdat_group (vnode);
+         vnode->remove_from_same_comdat_group ();
 
-         /* Keep body if it may be useful for constant folding.  */
-         if ((init = ctor_for_folding (vnode->decl)) == error_mark_node)
-           varpool_remove_initializer (vnode);
-         else
-           DECL_INITIAL (vnode->decl) = init;
          vnode->remove_all_references ();
        }
       else
        vnode->aux = NULL;
     }
 
-  pointer_set_destroy (reachable);
-  pointer_set_destroy (body_needed_for_clonning);
-  pointer_set_destroy (reachable_call_targets);
-
   /* Now update address_taken flags and try to promote functions to be local.  */
   if (file)
     fprintf (file, "\nClearing address taken flags:");
@@ -570,13 +663,17 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
     if (node->address_taken
        && !node->used_from_other_partition)
       {
-       if (!cgraph_for_node_and_aliases (node, has_addr_references_p, NULL, true))
+       if (!node->call_for_symbol_and_aliases
+           (has_addr_references_p, NULL, true)
+           && (!node->instrumentation_clone
+               || !node->instrumented_version
+               || !node->instrumented_version->address_taken))
          {
            if (file)
              fprintf (file, " %s", node->name ());
            node->address_taken = false;
            changed = true;
-           if (cgraph_local_node_p (node))
+           if (node->local_p ())
              {
                node->local.local = true;
                if (file)
@@ -588,7 +685,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
     fprintf (file, "\n");
 
 #ifdef ENABLE_CHECKING
-  verify_symtab ();
+  symtab_node::verify_symtab_nodes ();
 #endif
 
   /* If we removed something, perhaps profile could be improved.  */
@@ -612,7 +709,7 @@ process_references (varpool_node *vnode,
   int i;
   struct ipa_ref *ref;
 
-  if (!varpool_all_refs_explicit_p (vnode)
+  if (!vnode->all_refs_explicit_p ()
       || TREE_THIS_VOLATILE (vnode->decl))
     *explicit_refs = false;
 
@@ -630,9 +727,11 @@ process_references (varpool_node *vnode,
        *written = true;
        break;
       case IPA_REF_ALIAS:
-       process_references (varpool (ref->referring), written, address_taken,
-                           read, explicit_refs);
+       process_references (dyn_cast<varpool_node *> (ref->referring), written,
+                           address_taken, read, explicit_refs);
        break;
+      case IPA_REF_CHKP:
+       gcc_unreachable ();
       }
 }
 
@@ -648,14 +747,18 @@ set_readonly_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
 /* Set writeonly bit and clear the initalizer, since it will not be needed.  */
 
 bool
-set_writeonly_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
+set_writeonly_bit (varpool_node *vnode, void *data)
 {
   vnode->writeonly = true;
   if (optimize)
     {
       DECL_INITIAL (vnode->decl) = NULL;
       if (!vnode->alias)
-       vnode->remove_all_references ();
+       {
+         if (vnode->num_references ())
+           *(bool *)data = true;
+         vnode->remove_all_references ();
+       }
     }
   return false;
 }
@@ -673,15 +776,18 @@ clear_addressable_bit (varpool_node *vnode, void *data ATTRIBUTE_UNUSED)
 /* Discover variables that have no longer address taken or that are read only
    and update their flags.
 
+   Return true when unreachable symbol removan should be done.
+
    FIXME: This can not be done in between gimplify and omp_expand since
    readonly flag plays role on what is shared and what is not.  Currently we do
    this transformation as part of whole program visibility and re-do at
    ipa-reference pass (to take into account clonning), but it would
    make sense to do it before early optimizations.  */
 
-void
+bool
 ipa_discover_readonly_nonaddressable_vars (void)
 {
+  bool remove_p = false;
   varpool_node *vnode;
   if (dump_file)
     fprintf (dump_file, "Clearing variable flags:");
@@ -696,14 +802,16 @@ ipa_discover_readonly_nonaddressable_vars (void)
        bool read = false;
        bool explicit_refs = true;
 
-       process_references (vnode, &written, &address_taken, &read, &explicit_refs);
+       process_references (vnode, &written, &address_taken, &read,
+                           &explicit_refs);
        if (!explicit_refs)
          continue;
        if (!address_taken)
          {
            if (TREE_ADDRESSABLE (vnode->decl) && dump_file)
              fprintf (dump_file, " %s (non-addressable)", vnode->name ());
-           varpool_for_node_and_aliases (vnode, clear_addressable_bit, NULL, true);
+           vnode->call_for_symbol_and_aliases (clear_addressable_bit, NULL,
+                                               true);
          }
        if (!address_taken && !written
            /* Making variable in explicit section readonly can cause section
@@ -713,17 +821,19 @@ ipa_discover_readonly_nonaddressable_vars (void)
          {
            if (!TREE_READONLY (vnode->decl) && dump_file)
              fprintf (dump_file, " %s (read-only)", vnode->name ());
-           varpool_for_node_and_aliases (vnode, set_readonly_bit, NULL, true);
+           vnode->call_for_symbol_and_aliases (set_readonly_bit, NULL, true);
          }
        if (!vnode->writeonly && !read && !address_taken && written)
          {
            if (dump_file)
              fprintf (dump_file, " %s (write-only)", vnode->name ());
-           varpool_for_node_and_aliases (vnode, set_writeonly_bit, NULL, true);
+           vnode->call_for_symbol_and_aliases (set_writeonly_bit, &remove_p, 
+                                               true);
          }
       }
   if (dump_file)
     fprintf (dump_file, "\n");
+  return remove_p;
 }
 
 /* Free inline summary.  */
@@ -733,15 +843,17 @@ namespace {
 const pass_data pass_data_ipa_free_inline_summary =
 {
   SIMPLE_IPA_PASS, /* type */
-  "*free_inline_summary", /* name */
+  "free-inline-summary", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
-  true, /* has_execute */
   TV_IPA_FREE_INLINE_SUMMARY, /* tv_id */
   0, /* properties_required */
   0, /* properties_provided */
   0, /* properties_destroyed */
   0, /* todo_flags_start */
-  0, /* todo_flags_finish */
+  /* Early optimizations may make function unreachable.  We can not
+     remove unreachable functions as part of the ealry opts pass because
+     TODOs are run before subpasses.  Do it here.  */
+  ( TODO_remove_functions | TODO_dump_symtab ), /* todo_flags_finish */
 };
 
 class pass_ipa_free_inline_summary : public simple_ipa_opt_pass
@@ -769,9 +881,11 @@ make_pass_ipa_free_inline_summary (gcc::context *ctxt)
 }
 
 /* Generate and emit a static constructor or destructor.  WHICH must
-   be one of 'I' (for a constructor) or 'D' (for a destructor).  BODY
-   is a STATEMENT_LIST containing GENERIC statements.  PRIORITY is the
-   initialization priority for this constructor or destructor. 
+   be one of 'I' (for a constructor), 'D' (for a destructor), 'P'
+   (for chp static vars constructor) or 'B' (for chkp static bounds
+   constructor).  BODY is a STATEMENT_LIST containing GENERIC
+   statements.  PRIORITY is the initialization priority for this
+   constructor or destructor.
 
    FINAL specify whether the externally visible name for collect2 should
    be produced. */
@@ -830,6 +944,20 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final)
       DECL_STATIC_CONSTRUCTOR (decl) = 1;
       decl_init_priority_insert (decl, priority);
       break;
+    case 'P':
+      DECL_STATIC_CONSTRUCTOR (decl) = 1;
+      DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("chkp ctor"),
+                                         NULL,
+                                         NULL_TREE);
+      decl_init_priority_insert (decl, priority);
+      break;
+    case 'B':
+      DECL_STATIC_CONSTRUCTOR (decl) = 1;
+      DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("bnd_legacy"),
+                                         NULL,
+                                         NULL_TREE);
+      decl_init_priority_insert (decl, priority);
+      break;
     case 'D':
       DECL_STATIC_DESTRUCTOR (decl) = 1;
       decl_fini_priority_insert (decl, priority);
@@ -840,16 +968,18 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final)
 
   gimplify_function_tree (decl);
 
-  cgraph_add_new_function (decl, false);
+  cgraph_node::add_new_function (decl, false);
 
   set_cfun (NULL);
   current_function_decl = NULL;
 }
 
 /* Generate and emit a static constructor or destructor.  WHICH must
-   be one of 'I' (for a constructor) or 'D' (for a destructor).  BODY
-   is a STATEMENT_LIST containing GENERIC statements.  PRIORITY is the
-   initialization priority for this constructor or destructor.  */
+   be one of 'I' (for a constructor), 'D' (for a destructor), 'P'
+   (for chkp static vars constructor) or 'B' (for chkp static bounds
+   constructor).  BODY is a STATEMENT_LIST containing GENERIC
+   statements.  PRIORITY is the initialization priority for this
+   constructor or destructor.  */
 
 void
 cgraph_build_static_cdtor (char which, tree body, int priority)
@@ -876,7 +1006,7 @@ record_cdtor_fn (struct cgraph_node *node)
     static_ctors.safe_push (node->decl);
   if (DECL_STATIC_DESTRUCTOR (node->decl))
     static_dtors.safe_push (node->decl);
-  node = cgraph_get_node (node->decl);
+  node = cgraph_node::get (node->decl);
   DECL_DISREGARD_INLINE_LIMITS (node->decl) = 1;
 }
 
@@ -1047,7 +1177,6 @@ const pass_data pass_data_ipa_cdtor_merge =
   IPA_PASS, /* type */
   "cdtor", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
-  true, /* has_execute */
   TV_CGRAPHOPT, /* tv_id */
   0, /* properties_required */
   0, /* properties_provided */
@@ -1146,12 +1275,10 @@ propagate_single_user (varpool_node *vnode, cgraph_node *function,
 
   /* If node is an alias, first meet with its target.  */
   if (vnode->alias)
-    function = meet (function, varpool_alias_target (vnode), single_user_map);
+    function = meet (function, vnode->get_alias_target (), single_user_map);
 
   /* Check all users and see if they correspond to a single function.  */
-  for (i = 0;
-       vnode->iterate_referring (i, ref)
-       && function != BOTTOM; i++)
+  for (i = 0; vnode->iterate_referring (i, ref) && function != BOTTOM; i++)
     {
       struct cgraph_node *cnode = dyn_cast <cgraph_node *> (ref->referring);
       if (cnode)
@@ -1164,14 +1291,15 @@ propagate_single_user (varpool_node *vnode, cgraph_node *function,
            function = BOTTOM;
        }
       else
-        function = meet (function, dyn_cast <varpool_node *> (ref->referring), single_user_map);
+       function = meet (function, dyn_cast <varpool_node *> (ref->referring),
+                        single_user_map);
     }
   return function;
 }
 
 /* Pass setting used_by_single_function flag.
-   This flag is set on variable when there is only one function that may possibly
-   referr to it.  */
+   This flag is set on variable when there is only one function that may
+   possibly referr to it.  */
 
 static unsigned int
 ipa_single_use (void)
@@ -1181,7 +1309,7 @@ ipa_single_use (void)
   hash_map<varpool_node *, cgraph_node *> single_user_map;
 
   FOR_EACH_DEFINED_VARIABLE (var)
-    if (!varpool_all_refs_explicit_p (var))
+    if (!var->all_refs_explicit_p ())
       var->aux = BOTTOM;
     else
       {
@@ -1217,17 +1345,14 @@ ipa_single_use (void)
          single_user_map.put (var, user);
 
          /* Enqueue all aliases for re-processing.  */
-         for (i = 0;
-              var->iterate_referring (i, ref); i++)
-           if (ref->use == IPA_REF_ALIAS
-               && !ref->referring->aux)
+         for (i = 0; var->iterate_direct_aliases (i, ref); i++)
+           if (!ref->referring->aux)
              {
                ref->referring->aux = first;
                first = dyn_cast <varpool_node *> (ref->referring);
              }
          /* Enqueue all users for re-processing.  */
-         for (i = 0;
-              var->iterate_reference (i, ref); i++)
+         for (i = 0; var->iterate_reference (i, ref); i++)
            if (!ref->referred->aux
                && ref->referred->definition
                && is_a <varpool_node *> (ref->referred))
@@ -1251,7 +1376,10 @@ ipa_single_use (void)
       if (var->aux != BOTTOM)
        {
 #ifdef ENABLE_CHECKING
-         if (!single_user_map.get (var))
+         /* Not having the single user known means that the VAR is
+            unreachable.  Either someone forgot to remove unreachable
+            variables or the reachability here is wrong.  */
+
           gcc_assert (single_user_map.get (var));
 #endif
          if (dump_file)
@@ -1273,7 +1401,6 @@ const pass_data pass_data_ipa_single_use =
   IPA_PASS, /* type */
   "single-use", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
-  true, /* has_execute */
   TV_CGRAPHOPT, /* tv_id */
   0, /* properties_required */
   0, /* properties_provided */